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 | |
31 | MRN_BEGIN_DECLS |
32 | |
33 | extern mrn::DatabaseManager *mrn_db_manager; |
34 | extern mrn::ContextPool *mrn_context_pool; |
35 | |
36 | namespace 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 | |
45 | static 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 | |
68 | MRN_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 | |
193 | error: |
194 | mrn_query_expand_info_free(info); |
195 | DBUG_RETURN(TRUE); |
196 | } |
197 | |
198 | static 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 | |
233 | MRN_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 | |
268 | error: |
269 | *error = 1; |
270 | DBUG_RETURN(NULL); |
271 | } |
272 | |
273 | MRN_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 | |
282 | MRN_END_DECLS |
283 | |