1/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
2/*
3 Copyright(C) 2017 Kouhei Sutou <kou@clear-code.com>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18*/
19
20#include <mrn_mysql.h>
21#include <mrn_mysql_compat.h>
22#include <mrn_path_mapper.hpp>
23#include <mrn_windows.hpp>
24#include <mrn_macro.hpp>
25#include <mrn_variables.hpp>
26#include <mrn_database_manager.hpp>
27#include <mrn_context_pool.hpp>
28#include <mrn_current_thread.hpp>
29#include <mrn_query_parser.hpp>
30
31MRN_BEGIN_DECLS
32
33extern mrn::DatabaseManager *mrn_db_manager;
34extern mrn::ContextPool *mrn_context_pool;
35
36namespace mrn {
37 struct QueryExpandInfo {
38 grn_ctx *ctx;
39 grn_obj expanded_query;
40 grn_obj *term_column;
41 grn_obj *expanded_term_column;
42 };
43}
44
45static void mrn_query_expand_info_free(mrn::QueryExpandInfo *info)
46{
47 MRN_DBUG_ENTER_FUNCTION();
48
49 if (!info) {
50 DBUG_VOID_RETURN;
51 }
52
53 if (info->ctx) {
54 GRN_OBJ_FIN(info->ctx, &(info->expanded_query));
55 if (grn_obj_is_accessor(info->ctx, info->expanded_term_column)) {
56 grn_obj_unlink(info->ctx, info->expanded_term_column);
57 }
58 if (grn_obj_is_accessor(info->ctx, info->term_column)) {
59 grn_obj_unlink(info->ctx, info->term_column);
60 }
61 mrn_context_pool->release(info->ctx);
62 }
63 my_free(info);
64
65 DBUG_VOID_RETURN;
66}
67
68MRN_API my_bool mroonga_query_expand_init(UDF_INIT *init,
69 UDF_ARGS *args,
70 char *message)
71{
72 mrn::QueryExpandInfo *info = NULL;
73
74 MRN_DBUG_ENTER_FUNCTION();
75
76 init->ptr = NULL;
77 if (args->arg_count != 4) {
78 sprintf(message,
79 "mroonga_query_expand(): wrong number of arguments: %u for 4",
80 args->arg_count);
81 goto error;
82 }
83 if (args->arg_type[0] != STRING_RESULT) {
84 strcpy(message,
85 "mroonga_query_expand(): "
86 "the 1st argument must be table name as string");
87 goto error;
88 }
89 if (args->arg_type[1] != STRING_RESULT) {
90 strcpy(message,
91 "mroonga_query_expand(): "
92 "the 2nd argument must be term column name as string");
93 goto error;
94 }
95 if (args->arg_type[2] != STRING_RESULT) {
96 strcpy(message,
97 "mroonga_query_expand(): "
98 "the 3nd argument must be expanded term column name as string");
99 goto error;
100 }
101 if (args->arg_type[3] != STRING_RESULT) {
102 strcpy(message,
103 "mroonga_query_expand(): "
104 "the 4th argument must be query as string");
105 goto error;
106 }
107
108 init->maybe_null = 1;
109
110 info = static_cast<mrn::QueryExpandInfo *>(
111 mrn_my_malloc(sizeof(mrn::QueryExpandInfo),
112 MYF(MY_WME | MY_ZEROFILL)));
113 if (!info) {
114 snprintf(message, MYSQL_ERRMSG_SIZE,
115 "mroonga_query_expand(): failed to allocate memory");
116 goto error;
117 }
118
119 {
120 const char *current_db_path = MRN_THD_DB_PATH(current_thd);
121 if (!current_db_path) {
122 snprintf(message, MYSQL_ERRMSG_SIZE,
123 "mroonga_query_expand(): no current database");
124 goto error;
125 }
126
127 mrn::Database *db;
128 int error = mrn_db_manager->open(current_db_path, &db);
129 if (error != 0) {
130 snprintf(message, MYSQL_ERRMSG_SIZE,
131 "mroonga_query_expand(): failed to open database: %s",
132 mrn_db_manager->error_message());
133 goto error;
134 }
135 info->ctx = mrn_context_pool->pull();
136 grn_ctx_use(info->ctx, db->get());
137 }
138
139 GRN_TEXT_INIT(&(info->expanded_query), 0);
140
141 {
142 const char *table_name = args->args[0];
143 unsigned int table_name_length = args->lengths[0];
144 grn_obj *table = grn_ctx_get(info->ctx,
145 table_name,
146 table_name_length);
147 if (!table) {
148 snprintf(message, MYSQL_ERRMSG_SIZE,
149 "mroonga_query_expand(): table doesn't exist: <%.*s>",
150 static_cast<int>(table_name_length),
151 table_name);
152 goto error;
153 }
154
155 const char *term_column_name = args->args[1];
156 unsigned int term_column_name_length = args->lengths[1];
157 info->term_column = grn_obj_column(info->ctx,
158 table,
159 term_column_name,
160 term_column_name_length);
161 if (!info->term_column) {
162 snprintf(message, MYSQL_ERRMSG_SIZE,
163 "mroonga_query_expand(): term column doesn't exist: <%.*s.%.*s>",
164 static_cast<int>(table_name_length),
165 table_name,
166 static_cast<int>(term_column_name_length),
167 term_column_name);
168 goto error;
169 }
170
171 const char *expanded_term_column_name = args->args[2];
172 unsigned int expanded_term_column_name_length = args->lengths[2];
173 info->expanded_term_column = grn_obj_column(info->ctx,
174 table,
175 expanded_term_column_name,
176 expanded_term_column_name_length);
177 if (!info->expanded_term_column) {
178 snprintf(message, MYSQL_ERRMSG_SIZE,
179 "mroonga_query_expand(): "
180 "expanded term column doesn't exist: <%.*s.%.*s>",
181 static_cast<int>(table_name_length),
182 table_name,
183 static_cast<int>(expanded_term_column_name_length),
184 expanded_term_column_name);
185 goto error;
186 }
187 }
188
189 init->ptr = reinterpret_cast<char *>(info);
190
191 DBUG_RETURN(FALSE);
192
193error:
194 mrn_query_expand_info_free(info);
195 DBUG_RETURN(TRUE);
196}
197
198static void query_expand(mrn::QueryExpandInfo *info, UDF_ARGS *args)
199{
200 grn_ctx *ctx = info->ctx;
201 const char *query = args->args[3];
202 unsigned int query_length = args->lengths[3];
203
204 mrn::QueryParser query_parser(info->ctx,
205 current_thd,
206 NULL,
207 NULL,
208 0,
209 NULL);
210 const char *raw_query;
211 size_t raw_query_length;
212 grn_operator default_operator;
213 grn_expr_flags flags;
214 query_parser.parse_pragma(query,
215 query_length,
216 &raw_query,
217 &raw_query_length,
218 &default_operator,
219 &flags);
220 GRN_TEXT_SET(info->ctx,
221 &(info->expanded_query),
222 query,
223 raw_query - query);
224 grn_expr_syntax_expand_query_by_table(ctx,
225 raw_query,
226 raw_query_length,
227 flags,
228 info->term_column,
229 info->expanded_term_column,
230 &(info->expanded_query));
231}
232
233MRN_API char *mroonga_query_expand(UDF_INIT *init,
234 UDF_ARGS *args,
235 char *result,
236 unsigned long *length,
237 char *is_null,
238 char *error)
239{
240 MRN_DBUG_ENTER_FUNCTION();
241
242 mrn::QueryExpandInfo *info =
243 reinterpret_cast<mrn::QueryExpandInfo *>(init->ptr);
244 grn_ctx *ctx = info->ctx;
245
246 if (!args->args[3]) {
247 *is_null = 1;
248 DBUG_RETURN(NULL);
249 }
250
251 *is_null = 0;
252
253 query_expand(info, args);
254
255 if (ctx->rc) {
256 char message[MYSQL_ERRMSG_SIZE];
257 snprintf(message, MYSQL_ERRMSG_SIZE,
258 "mroonga_query_expand(): "
259 "failed to expand: %s",
260 ctx->errbuf);
261 my_message(ER_ERROR_ON_WRITE, message, MYF(0));
262 goto error;
263 }
264
265 *length = GRN_TEXT_LEN(&(info->expanded_query));
266 DBUG_RETURN(GRN_TEXT_VALUE(&(info->expanded_query)));
267
268error:
269 *error = 1;
270 DBUG_RETURN(NULL);
271}
272
273MRN_API void mroonga_query_expand_deinit(UDF_INIT *init)
274{
275 MRN_DBUG_ENTER_FUNCTION();
276 mrn::QueryExpandInfo *info =
277 reinterpret_cast<mrn::QueryExpandInfo *>(init->ptr);
278 mrn_query_expand_info_free(info);
279 DBUG_VOID_RETURN;
280}
281
282MRN_END_DECLS
283