1/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
2/*
3 Copyright(C) 2010 Tetsuro IKEDA
4 Copyright(C) 2010-2013 Kentoku SHIBA
5 Copyright(C) 2011-2017 Kouhei Sutou <kou@clear-code.com>
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
11
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20*/
21
22#include <mrn_mysql.h>
23#include <mrn_mysql_compat.h>
24#include <mrn_err.h>
25#include <mrn_encoding.hpp>
26#include <mrn_windows.hpp>
27#include <mrn_table.hpp>
28#include <mrn_macro.hpp>
29#include <mrn_database_manager.hpp>
30#include <mrn_context_pool.hpp>
31#include <mrn_variables.hpp>
32#include <mrn_current_thread.hpp>
33
34MRN_BEGIN_DECLS
35
36extern mrn::DatabaseManager *mrn_db_manager;
37extern mrn::ContextPool *mrn_context_pool;
38
39struct st_mrn_snip_info
40{
41 grn_ctx *ctx;
42 grn_obj *db;
43 bool use_shared_db;
44 grn_obj *snippet;
45 String result_str;
46};
47
48static my_bool mrn_snippet_prepare(st_mrn_snip_info *snip_info, UDF_ARGS *args,
49 char *message, grn_obj **snippet)
50{
51 unsigned int i;
52 CHARSET_INFO *cs;
53 grn_ctx *ctx = snip_info->ctx;
54 long long snip_max_len;
55 long long snip_max_num;
56 long long skip_leading_spaces;
57 long long html_escape;
58 int flags = GRN_SNIP_COPY_TAG;
59 grn_snip_mapping *mapping = NULL;
60 grn_rc rc;
61 String *result_str = &snip_info->result_str;
62
63 *snippet = NULL;
64 snip_max_len = *((long long *) args->args[1]);
65 snip_max_num = *((long long *) args->args[2]);
66
67 if (args->arg_type[3] == STRING_RESULT) {
68 if (!(cs = get_charset_by_name(args->args[3], MYF(0)))) {
69 snprintf(message, MYSQL_ERRMSG_SIZE,
70 "Unknown charset: <%s>", args->args[3]);
71 goto error;
72 }
73 } else {
74 uint charset_id = static_cast<uint>(*((long long *) args->args[3]));
75 if (!(cs = get_charset(charset_id, MYF(0)))) {
76 snprintf(message, MYSQL_ERRMSG_SIZE,
77 "Unknown charset ID: <%u>", charset_id);
78 goto error;
79 }
80 }
81 if (!mrn::encoding::set_raw(ctx, cs)) {
82 snprintf(message, MYSQL_ERRMSG_SIZE,
83 "Unsupported charset: <%s>", cs->name);
84 goto error;
85 }
86
87 if (!(cs->state & (MY_CS_BINSORT | MY_CS_CSSORT))) {
88 flags |= GRN_SNIP_NORMALIZE;
89 }
90
91 skip_leading_spaces = *((long long *) args->args[4]);
92 if (skip_leading_spaces) {
93 flags |= GRN_SNIP_SKIP_LEADING_SPACES;
94 }
95
96 html_escape = *((long long *) args->args[5]);
97 if (html_escape) {
98 mapping = (grn_snip_mapping *) -1;
99 }
100
101 *snippet = grn_snip_open(ctx, flags, static_cast<unsigned int>(snip_max_len),
102 static_cast<unsigned int>(snip_max_num),
103 "", 0, "", 0, mapping);
104 if (ctx->rc) {
105 snprintf(message, MYSQL_ERRMSG_SIZE,
106 "Failed to open grn_snip: <%s>", ctx->errbuf);
107 goto error;
108 }
109
110 for (i = 8; i < args->arg_count; i += 3) {
111 rc = grn_snip_add_cond(ctx, *snippet,
112 args->args[i], args->lengths[i],
113 args->args[i + 1], args->lengths[i + 1],
114 args->args[i + 2], args->lengths[i + 2]);
115 if (rc) {
116 snprintf(message, MYSQL_ERRMSG_SIZE,
117 "Failed to add a condition to grn_snip: <%s>", ctx->errbuf);
118 goto error;
119 }
120 }
121
122 result_str->set_charset(cs);
123 return FALSE;
124
125error:
126 if (*snippet) {
127 grn_obj_close(ctx, *snippet);
128 }
129 return TRUE;
130}
131
132MRN_API my_bool mroonga_snippet_init(UDF_INIT *init, UDF_ARGS *args, char *message)
133{
134 uint i;
135 st_mrn_snip_info *snip_info = NULL;
136 bool can_open_snippet = TRUE;
137 init->ptr = NULL;
138 if (args->arg_count < 11 || (args->arg_count - 11) % 3)
139 {
140 sprintf(message, "Incorrect number of arguments for mroonga_snippet(): %u",
141 args->arg_count);
142 goto error;
143 }
144 if (args->arg_type[0] != STRING_RESULT) {
145 strcpy(message, "mroonga_snippet() requires string for 1st argument");
146 goto error;
147 }
148 if (args->arg_type[1] != INT_RESULT) {
149 strcpy(message, "mroonga_snippet() requires int for 2nd argument");
150 goto error;
151 }
152 if (args->arg_type[2] != INT_RESULT) {
153 strcpy(message, "mroonga_snippet() requires int for 3rd argument");
154 goto error;
155 }
156 if (
157 args->arg_type[3] != STRING_RESULT &&
158 args->arg_type[3] != INT_RESULT
159 ) {
160 strcpy(message,
161 "mroonga_snippet() requires string or int for 4th argument");
162 goto error;
163 }
164 if (args->arg_type[4] != INT_RESULT) {
165 strcpy(message, "mroonga_snippet() requires int for 5th argument");
166 goto error;
167 }
168 if (args->arg_type[5] != INT_RESULT) {
169 strcpy(message, "mroonga_snippet() requires int for 6th argument");
170 goto error;
171 }
172 for (i = 6; i < args->arg_count; i++) {
173 if (args->arg_type[i] != STRING_RESULT) {
174 sprintf(message, "mroonga_snippet() requires string for %uth argument",
175 i);
176 goto error;
177 }
178 }
179 init->maybe_null = 1;
180
181 if (!(snip_info = (st_mrn_snip_info *) mrn_my_malloc(sizeof(st_mrn_snip_info),
182 MYF(MY_WME | MY_ZEROFILL))))
183 {
184 strcpy(message, "mroonga_snippet() out of memory");
185 goto error;
186 }
187 snip_info->ctx = mrn_context_pool->pull();
188 {
189 const char *current_db_path = MRN_THD_DB_PATH(current_thd);
190 const char *action;
191 if (current_db_path) {
192 action = "open database";
193 mrn::Database *db;
194 int error = mrn_db_manager->open(current_db_path, &db);
195 if (error == 0) {
196 snip_info->db = db->get();
197 grn_ctx_use(snip_info->ctx, snip_info->db);
198 snip_info->use_shared_db = true;
199 }
200 } else {
201 action = "create anonymous database";
202 snip_info->db = grn_db_create(snip_info->ctx, NULL, NULL);
203 snip_info->use_shared_db = false;
204 }
205 if (!snip_info->db) {
206 sprintf(message,
207 "mroonga_snippet(): failed to %s: %s",
208 action,
209 snip_info->ctx->errbuf);
210 goto error;
211 }
212 }
213
214 for (i = 1; i < args->arg_count; i++) {
215 if (!args->args[i]) {
216 can_open_snippet = FALSE;
217 break;
218 }
219 }
220 if (can_open_snippet) {
221 if (mrn_snippet_prepare(snip_info, args, message, &snip_info->snippet)) {
222 goto error;
223 }
224 }
225 init->ptr = (char *) snip_info;
226
227 return FALSE;
228
229error:
230 if (snip_info) {
231 if (!snip_info->use_shared_db) {
232 grn_obj_close(snip_info->ctx, snip_info->db);
233 }
234 mrn_context_pool->release(snip_info->ctx);
235 my_free(snip_info);
236 }
237 return TRUE;
238}
239
240MRN_API char *mroonga_snippet(UDF_INIT *init, UDF_ARGS *args, char *result,
241 unsigned long *length, char *is_null, char *error)
242{
243 st_mrn_snip_info *snip_info = (st_mrn_snip_info *) init->ptr;
244 grn_ctx *ctx = snip_info->ctx;
245 String *result_str = &snip_info->result_str;
246 char *target;
247 unsigned int target_length;
248 grn_obj *snippet = NULL;
249 grn_rc rc;
250 unsigned int i, n_results, max_tagged_length, result_length;
251
252 if (!args->args[0]) {
253 *is_null = 1;
254 return NULL;
255 }
256 *is_null = 0;
257 target = args->args[0];
258 target_length = args->lengths[0];
259
260 if (!snip_info->snippet) {
261 for (i = 1; i < args->arg_count; i++) {
262 if (!args->args[i]) {
263 my_printf_error(ER_MRN_INVALID_NULL_VALUE_NUM,
264 ER_MRN_INVALID_NULL_VALUE_STR, MYF(0),
265 "mroonga_snippet() arguments");
266 goto error;
267 }
268 }
269
270 if (mrn_snippet_prepare(snip_info, args, NULL, &snippet)) {
271 goto error;
272 }
273 } else {
274 snippet = snip_info->snippet;
275 }
276
277 rc = grn_snip_exec(ctx, snippet, target, target_length,
278 &n_results, &max_tagged_length);
279 if (rc) {
280 my_printf_error(ER_MRN_ERROR_FROM_GROONGA_NUM,
281 ER_MRN_ERROR_FROM_GROONGA_STR, MYF(0), ctx->errbuf);
282 goto error;
283 }
284
285 result_str->length(0);
286 if (result_str->reserve((args->lengths[6] + args->lengths[7] +
287 max_tagged_length) * n_results)) {
288 my_error(ER_OUT_OF_RESOURCES, MYF(0), HA_ERR_OUT_OF_MEM);
289 goto error;
290 }
291 for (i = 0; i < n_results; i++) {
292 result_str->q_append(args->args[6], args->lengths[6]);
293 rc = grn_snip_get_result(ctx, snippet, i,
294 (char *) result_str->ptr() + result_str->length(),
295 &result_length);
296 if (rc) {
297 my_printf_error(ER_MRN_ERROR_FROM_GROONGA_NUM,
298 ER_MRN_ERROR_FROM_GROONGA_STR, MYF(0), ctx->errbuf);
299 goto error;
300 }
301 result_str->length(result_str->length() + result_length);
302 result_str->q_append(args->args[7], args->lengths[7]);
303 }
304
305 if (!snip_info->snippet) {
306 rc = grn_obj_close(ctx, snippet);
307 if (rc) {
308 my_printf_error(ER_MRN_ERROR_FROM_GROONGA_NUM,
309 ER_MRN_ERROR_FROM_GROONGA_STR, MYF(0), ctx->errbuf);
310 goto error;
311 }
312 }
313
314 *length = result_str->length();
315 return (char *) result_str->ptr();
316
317error:
318 *error = 1;
319 return NULL;
320}
321
322MRN_API void mroonga_snippet_deinit(UDF_INIT *init)
323{
324 st_mrn_snip_info *snip_info = (st_mrn_snip_info *) init->ptr;
325 if (snip_info) {
326 if (snip_info->snippet) {
327 grn_obj_close(snip_info->ctx, snip_info->snippet);
328 }
329 MRN_STRING_FREE(snip_info->result_str);
330 if (!snip_info->use_shared_db) {
331 grn_obj_close(snip_info->ctx, snip_info->db);
332 }
333 mrn_context_pool->release(snip_info->ctx);
334 my_free(snip_info);
335 }
336}
337
338MRN_END_DECLS
339