1/* -*- c-basic-offset: 2; indent-tabs-mode: nil -*- */
2/*
3 Copyright(C) 2015 Naoya Murakami <naoya@createfield.com>
4 Copyright(C) 2017 Kouhei Sutou <kou@clear-code.com>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19*/
20
21#include <mrn_mysql.h>
22#include <mrn_mysql_compat.h>
23#include <mrn_encoding.hpp>
24#include <mrn_windows.hpp>
25#include <mrn_table.hpp>
26#include <mrn_macro.hpp>
27#include <mrn_database_manager.hpp>
28#include <mrn_context_pool.hpp>
29#include <mrn_variables.hpp>
30#include <mrn_current_thread.hpp>
31
32MRN_BEGIN_DECLS
33
34extern mrn::DatabaseManager *mrn_db_manager;
35extern mrn::ContextPool *mrn_context_pool;
36
37#define DEFAULT_NORMALIZER_NAME "NormalizerAuto"
38
39struct st_mrn_normalize_info
40{
41 grn_ctx *ctx;
42 grn_obj *db;
43 bool use_shared_db;
44 grn_obj *normalizer;
45 int flags;
46 String result_str;
47};
48
49MRN_API my_bool mroonga_normalize_init(UDF_INIT *init, UDF_ARGS *args,
50 char *message)
51{
52 st_mrn_normalize_info *info = NULL;
53 String *result_str = NULL;
54
55 init->ptr = NULL;
56 if (!(1 <= args->arg_count && args->arg_count <= 2)) {
57 sprintf(message,
58 "mroonga_normalize(): Incorrect number of arguments: %u for 1..2",
59 args->arg_count);
60 goto error;
61 }
62 if (args->arg_type[0] != STRING_RESULT) {
63 strcpy(message,
64 "mroonga_normalize(): The 1st argument must be query as string");
65 goto error;
66 }
67 if (args->arg_count == 2) {
68 if (args->arg_type[1] != STRING_RESULT) {
69 strcpy(message,
70 "mroonga_normalize(): "
71 "The 2st argument must be normalizer name as string");
72 goto error;
73 }
74 }
75
76 init->maybe_null = 1;
77
78 info = (st_mrn_normalize_info *)mrn_my_malloc(sizeof(st_mrn_normalize_info),
79 MYF(MY_WME | MY_ZEROFILL));
80 if (!info) {
81 strcpy(message, "mroonga_normalize(): out of memory");
82 goto error;
83 }
84
85 info->ctx = mrn_context_pool->pull();
86 {
87 const char *current_db_path = MRN_THD_DB_PATH(current_thd);
88 const char *action;
89 if (current_db_path) {
90 action = "open database";
91 mrn::Database *db;
92 int error = mrn_db_manager->open(current_db_path, &db);
93 if (error == 0) {
94 info->db = db->get();
95 grn_ctx_use(info->ctx, info->db);
96 info->use_shared_db = true;
97 }
98 } else {
99 action = "create anonymous database";
100 info->db = grn_db_create(info->ctx, NULL, NULL);
101 info->use_shared_db = false;
102 }
103 if (!info->db) {
104 sprintf(message,
105 "mroonga_normalize(): failed to %s: %s",
106 action,
107 info->ctx->errbuf);
108 goto error;
109 }
110 }
111
112 if (args->arg_count == 1) {
113 info->normalizer = grn_ctx_get(info->ctx, DEFAULT_NORMALIZER_NAME, -1);
114 } else {
115 info->normalizer = grn_ctx_get(info->ctx, args->args[1], args->lengths[1]);
116 }
117 if (!info->normalizer) {
118 sprintf(message, "mroonga_normalize(): nonexistent normalizer %.*s",
119 (int)args->lengths[1], args->args[1]);
120 goto error;
121 }
122 info->flags = 0;
123
124 result_str = &(info->result_str);
125 mrn::encoding::set_raw(info->ctx, system_charset_info);
126 result_str->set_charset(system_charset_info);
127
128 init->ptr = (char *)info;
129
130 return FALSE;
131
132error:
133 if (info) {
134 if (!info->use_shared_db) {
135 grn_obj_close(info->ctx, info->db);
136 }
137 mrn_context_pool->release(info->ctx);
138 my_free(info);
139 }
140 return TRUE;
141}
142
143MRN_API char *mroonga_normalize(UDF_INIT *init, UDF_ARGS *args, char *result,
144 unsigned long *length, char *is_null, char *error)
145{
146 st_mrn_normalize_info *info = (st_mrn_normalize_info *)init->ptr;
147 grn_ctx *ctx = info->ctx;
148 String *result_str = &(info->result_str);
149
150 if (!args->args[0]) {
151 *is_null = 1;
152 return NULL;
153 }
154
155 result_str->length(0);
156 {
157 char *target = args->args[0];
158 unsigned int target_length = args->lengths[0];
159 grn_obj *grn_string;
160 const char *normalized;
161 unsigned int normalized_length_in_bytes;
162 unsigned int normalized_n_characters;
163
164 grn_string = grn_string_open(ctx,
165 target, target_length,
166 info->normalizer, info->flags);
167 grn_string_get_normalized(ctx, grn_string,
168 &normalized,
169 &normalized_length_in_bytes,
170 &normalized_n_characters);
171 if (result_str->reserve(normalized_length_in_bytes)) {
172 my_error(ER_OUT_OF_RESOURCES, MYF(0), HA_ERR_OUT_OF_MEM);
173 goto error;
174 }
175 result_str->q_append(normalized, normalized_length_in_bytes);
176 result_str->length(normalized_length_in_bytes);
177 grn_obj_unlink(ctx, grn_string);
178 }
179 *is_null = 0;
180
181 if (ctx->rc) {
182 my_message(ER_ERROR_ON_WRITE, ctx->errbuf, MYF(0));
183 goto error;
184 }
185
186 *length = result_str->length();
187 return (char *)result_str->ptr();
188
189error:
190 *is_null = 1;
191 *error = 1;
192 return NULL;
193}
194
195MRN_API void mroonga_normalize_deinit(UDF_INIT *init)
196{
197 st_mrn_normalize_info *info = (st_mrn_normalize_info *)init->ptr;
198
199 if (info) {
200 MRN_STRING_FREE(info->result_str);
201 if (info->normalizer) {
202 grn_obj_unlink(info->ctx, info->normalizer);
203 }
204 if (!info->use_shared_db) {
205 grn_obj_close(info->ctx, info->db);
206 }
207 mrn_context_pool->release(info->ctx);
208 my_free(info);
209 }
210}
211
212MRN_END_DECLS
213