1 | /***************************************************************************** |
2 | |
3 | Copyright (c) 2007, 2016, Oracle and/or its affiliates. All Rights Reserved. |
4 | Copyright (c) 2017, MariaDB Corporation. |
5 | |
6 | This program is free software; you can redistribute it and/or modify it under |
7 | the terms of the GNU General Public License as published by the Free Software |
8 | Foundation; version 2 of the License. |
9 | |
10 | This program is distributed in the hope that it will be useful, but WITHOUT |
11 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
12 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU General Public License along with |
15 | this program; if not, write to the Free Software Foundation, Inc., |
16 | 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA |
17 | |
18 | *****************************************************************************/ |
19 | |
20 | /******************************************************************//** |
21 | @file fts/fts0config.cc |
22 | Full Text Search configuration table. |
23 | |
24 | Created 2007/5/9 Sunny Bains |
25 | ***********************************************************************/ |
26 | |
27 | #include "trx0roll.h" |
28 | #include "row0sel.h" |
29 | |
30 | #include "fts0priv.h" |
31 | |
32 | /******************************************************************//** |
33 | Callback function for fetching the config value. |
34 | @return always returns TRUE */ |
35 | static |
36 | ibool |
37 | fts_config_fetch_value( |
38 | /*===================*/ |
39 | void* row, /*!< in: sel_node_t* */ |
40 | void* user_arg) /*!< in: pointer to |
41 | ib_vector_t */ |
42 | { |
43 | sel_node_t* node = static_cast<sel_node_t*>(row); |
44 | fts_string_t* value = static_cast<fts_string_t*>(user_arg); |
45 | |
46 | dfield_t* dfield = que_node_get_val(node->select_list); |
47 | dtype_t* type = dfield_get_type(dfield); |
48 | ulint len = dfield_get_len(dfield); |
49 | void* data = dfield_get_data(dfield); |
50 | |
51 | ut_a(dtype_get_mtype(type) == DATA_VARCHAR); |
52 | |
53 | if (len != UNIV_SQL_NULL) { |
54 | ulint max_len = ut_min(value->f_len - 1, len); |
55 | |
56 | memcpy(value->f_str, data, max_len); |
57 | value->f_len = max_len; |
58 | value->f_str[value->f_len] = '\0'; |
59 | } |
60 | |
61 | return(TRUE); |
62 | } |
63 | |
64 | /******************************************************************//** |
65 | Get value from the config table. The caller must ensure that enough |
66 | space is allocated for value to hold the column contents. |
67 | @return DB_SUCCESS or error code */ |
68 | dberr_t |
69 | fts_config_get_value( |
70 | /*=================*/ |
71 | trx_t* trx, /*!< transaction */ |
72 | fts_table_t* fts_table, /*!< in: the indexed |
73 | FTS table */ |
74 | const char* name, /*!< in: get config value for |
75 | this parameter name */ |
76 | fts_string_t* value) /*!< out: value read from |
77 | config table */ |
78 | { |
79 | pars_info_t* info; |
80 | que_t* graph; |
81 | dberr_t error; |
82 | ulint name_len = strlen(name); |
83 | char table_name[MAX_FULL_NAME_LEN]; |
84 | |
85 | info = pars_info_create(); |
86 | |
87 | *value->f_str = '\0'; |
88 | ut_a(value->f_len > 0); |
89 | |
90 | pars_info_bind_function(info, "my_func" , fts_config_fetch_value, |
91 | value); |
92 | |
93 | /* The len field of value must be set to the max bytes that |
94 | it can hold. On a successful read, the len field will be set |
95 | to the actual number of bytes copied to value. */ |
96 | pars_info_bind_varchar_literal(info, "name" , (byte*) name, name_len); |
97 | |
98 | fts_table->suffix = "CONFIG" ; |
99 | fts_get_table_name(fts_table, table_name); |
100 | pars_info_bind_id(info, true, "table_name" , table_name); |
101 | |
102 | graph = fts_parse_sql( |
103 | fts_table, |
104 | info, |
105 | "DECLARE FUNCTION my_func;\n" |
106 | "DECLARE CURSOR c IS SELECT value FROM $table_name" |
107 | " WHERE key = :name;\n" |
108 | "BEGIN\n" |
109 | "" |
110 | "OPEN c;\n" |
111 | "WHILE 1 = 1 LOOP\n" |
112 | " FETCH c INTO my_func();\n" |
113 | " IF c % NOTFOUND THEN\n" |
114 | " EXIT;\n" |
115 | " END IF;\n" |
116 | "END LOOP;\n" |
117 | "CLOSE c;" ); |
118 | |
119 | trx->op_info = "getting FTS config value" ; |
120 | |
121 | error = fts_eval_sql(trx, graph); |
122 | |
123 | mutex_enter(&dict_sys->mutex); |
124 | que_graph_free(graph); |
125 | mutex_exit(&dict_sys->mutex); |
126 | |
127 | return(error); |
128 | } |
129 | |
130 | /*********************************************************************//** |
131 | Create the config table name for retrieving index specific value. |
132 | @return index config parameter name */ |
133 | char* |
134 | fts_config_create_index_param_name( |
135 | /*===============================*/ |
136 | const char* param, /*!< in: base name of param */ |
137 | const dict_index_t* index) /*!< in: index for config */ |
138 | { |
139 | ulint len; |
140 | char* name; |
141 | |
142 | /* The format of the config name is: name_<index_id>. */ |
143 | len = strlen(param); |
144 | |
145 | /* Caller is responsible for deleting name. */ |
146 | name = static_cast<char*>(ut_malloc_nokey( |
147 | len + FTS_AUX_MIN_TABLE_ID_LENGTH + 2)); |
148 | ::strcpy(name, param); |
149 | name[len] = '_'; |
150 | |
151 | fts_write_object_id(index->id, name + len + 1, |
152 | DICT_TF2_FLAG_IS_SET(index->table, |
153 | DICT_TF2_FTS_AUX_HEX_NAME)); |
154 | |
155 | return(name); |
156 | } |
157 | |
158 | /******************************************************************//** |
159 | Get value specific to an FTS index from the config table. The caller |
160 | must ensure that enough space is allocated for value to hold the |
161 | column contents. |
162 | @return DB_SUCCESS or error code */ |
163 | dberr_t |
164 | fts_config_get_index_value( |
165 | /*=======================*/ |
166 | trx_t* trx, /*!< transaction */ |
167 | dict_index_t* index, /*!< in: index */ |
168 | const char* param, /*!< in: get config value for |
169 | this parameter name */ |
170 | fts_string_t* value) /*!< out: value read from |
171 | config table */ |
172 | { |
173 | char* name; |
174 | dberr_t error; |
175 | fts_table_t fts_table; |
176 | |
177 | FTS_INIT_FTS_TABLE(&fts_table, "CONFIG" , FTS_COMMON_TABLE, |
178 | index->table); |
179 | |
180 | /* We are responsible for free'ing name. */ |
181 | name = fts_config_create_index_param_name(param, index); |
182 | |
183 | error = fts_config_get_value(trx, &fts_table, name, value); |
184 | |
185 | ut_free(name); |
186 | |
187 | return(error); |
188 | } |
189 | |
190 | /******************************************************************//** |
191 | Set the value in the config table for name. |
192 | @return DB_SUCCESS or error code */ |
193 | dberr_t |
194 | fts_config_set_value( |
195 | /*=================*/ |
196 | trx_t* trx, /*!< transaction */ |
197 | fts_table_t* fts_table, /*!< in: the indexed |
198 | FTS table */ |
199 | const char* name, /*!< in: get config value for |
200 | this parameter name */ |
201 | const fts_string_t* |
202 | value) /*!< in: value to update */ |
203 | { |
204 | pars_info_t* info; |
205 | que_t* graph; |
206 | dberr_t error; |
207 | undo_no_t undo_no; |
208 | undo_no_t n_rows_updated; |
209 | ulint name_len = strlen(name); |
210 | char table_name[MAX_FULL_NAME_LEN]; |
211 | |
212 | info = pars_info_create(); |
213 | |
214 | pars_info_bind_varchar_literal(info, "name" , (byte*) name, name_len); |
215 | pars_info_bind_varchar_literal(info, "value" , |
216 | value->f_str, value->f_len); |
217 | |
218 | fts_table->suffix = "CONFIG" ; |
219 | fts_get_table_name(fts_table, table_name); |
220 | pars_info_bind_id(info, true, "table_name" , table_name); |
221 | |
222 | graph = fts_parse_sql( |
223 | fts_table, info, |
224 | "BEGIN UPDATE $table_name SET value = :value" |
225 | " WHERE key = :name;" ); |
226 | |
227 | trx->op_info = "setting FTS config value" ; |
228 | |
229 | undo_no = trx->undo_no; |
230 | |
231 | error = fts_eval_sql(trx, graph); |
232 | |
233 | fts_que_graph_free_check_lock(fts_table, NULL, graph); |
234 | |
235 | n_rows_updated = trx->undo_no - undo_no; |
236 | |
237 | /* Check if we need to do an insert. */ |
238 | if (n_rows_updated == 0) { |
239 | info = pars_info_create(); |
240 | |
241 | pars_info_bind_varchar_literal( |
242 | info, "name" , (byte*) name, name_len); |
243 | |
244 | pars_info_bind_varchar_literal( |
245 | info, "value" , value->f_str, value->f_len); |
246 | |
247 | fts_get_table_name(fts_table, table_name); |
248 | pars_info_bind_id(info, true, "table_name" , table_name); |
249 | |
250 | graph = fts_parse_sql( |
251 | fts_table, info, |
252 | "BEGIN\n" |
253 | "INSERT INTO $table_name VALUES(:name, :value);" ); |
254 | |
255 | trx->op_info = "inserting FTS config value" ; |
256 | |
257 | error = fts_eval_sql(trx, graph); |
258 | |
259 | fts_que_graph_free_check_lock(fts_table, NULL, graph); |
260 | } |
261 | |
262 | return(error); |
263 | } |
264 | |
265 | /******************************************************************//** |
266 | Set the value specific to an FTS index in the config table. |
267 | @return DB_SUCCESS or error code */ |
268 | dberr_t |
269 | fts_config_set_index_value( |
270 | /*=======================*/ |
271 | trx_t* trx, /*!< transaction */ |
272 | dict_index_t* index, /*!< in: index */ |
273 | const char* param, /*!< in: get config value for |
274 | this parameter name */ |
275 | fts_string_t* value) /*!< out: value read from |
276 | config table */ |
277 | { |
278 | char* name; |
279 | dberr_t error; |
280 | fts_table_t fts_table; |
281 | |
282 | FTS_INIT_FTS_TABLE(&fts_table, "CONFIG" , FTS_COMMON_TABLE, |
283 | index->table); |
284 | |
285 | /* We are responsible for free'ing name. */ |
286 | name = fts_config_create_index_param_name(param, index); |
287 | |
288 | error = fts_config_set_value(trx, &fts_table, name, value); |
289 | |
290 | ut_free(name); |
291 | |
292 | return(error); |
293 | } |
294 | |
295 | #ifdef FTS_OPTIMIZE_DEBUG |
296 | /******************************************************************//** |
297 | Get an ulint value from the config table. |
298 | @return DB_SUCCESS if all OK else error code */ |
299 | dberr_t |
300 | fts_config_get_index_ulint( |
301 | /*=======================*/ |
302 | trx_t* trx, /*!< in: transaction */ |
303 | dict_index_t* index, /*!< in: FTS index */ |
304 | const char* name, /*!< in: param name */ |
305 | ulint* int_value) /*!< out: value */ |
306 | { |
307 | dberr_t error; |
308 | fts_string_t value; |
309 | |
310 | /* We set the length of value to the max bytes it can hold. This |
311 | information is used by the callback that reads the value.*/ |
312 | value.f_len = FTS_MAX_CONFIG_VALUE_LEN; |
313 | value.f_str = static_cast<byte*>(ut_malloc_nokey(value.f_len + 1)); |
314 | |
315 | error = fts_config_get_index_value(trx, index, name, &value); |
316 | |
317 | if (UNIV_UNLIKELY(error != DB_SUCCESS)) { |
318 | |
319 | ib::error() << "(" << ut_strerr(error) << ") reading `" |
320 | << name << "'" ; |
321 | } else { |
322 | *int_value = strtoul((char*) value.f_str, NULL, 10); |
323 | } |
324 | |
325 | ut_free(value.f_str); |
326 | |
327 | return(error); |
328 | } |
329 | |
330 | /******************************************************************//** |
331 | Set an ulint value in the config table. |
332 | @return DB_SUCCESS if all OK else error code */ |
333 | dberr_t |
334 | fts_config_set_index_ulint( |
335 | /*=======================*/ |
336 | trx_t* trx, /*!< in: transaction */ |
337 | dict_index_t* index, /*!< in: FTS index */ |
338 | const char* name, /*!< in: param name */ |
339 | ulint int_value) /*!< in: value */ |
340 | { |
341 | dberr_t error; |
342 | fts_string_t value; |
343 | |
344 | /* We set the length of value to the max bytes it can hold. This |
345 | information is used by the callback that reads the value.*/ |
346 | value.f_len = FTS_MAX_CONFIG_VALUE_LEN; |
347 | value.f_str = static_cast<byte*>(ut_malloc_nokey(value.f_len + 1)); |
348 | |
349 | // FIXME: Get rid of snprintf |
350 | ut_a(FTS_MAX_INT_LEN < FTS_MAX_CONFIG_VALUE_LEN); |
351 | |
352 | value.f_len = snprintf( |
353 | (char*) value.f_str, FTS_MAX_INT_LEN, ULINTPF, int_value); |
354 | |
355 | error = fts_config_set_index_value(trx, index, name, &value); |
356 | |
357 | if (UNIV_UNLIKELY(error != DB_SUCCESS)) { |
358 | |
359 | ib::error() << "(" << ut_strerr(error) << ") writing `" |
360 | << name << "'" ; |
361 | } |
362 | |
363 | ut_free(value.f_str); |
364 | |
365 | return(error); |
366 | } |
367 | #endif /* FTS_OPTIMIZE_DEBUG */ |
368 | |
369 | /******************************************************************//** |
370 | Get an ulint value from the config table. |
371 | @return DB_SUCCESS if all OK else error code */ |
372 | dberr_t |
373 | fts_config_get_ulint( |
374 | /*=================*/ |
375 | trx_t* trx, /*!< in: transaction */ |
376 | fts_table_t* fts_table, /*!< in: the indexed |
377 | FTS table */ |
378 | const char* name, /*!< in: param name */ |
379 | ulint* int_value) /*!< out: value */ |
380 | { |
381 | dberr_t error; |
382 | fts_string_t value; |
383 | |
384 | /* We set the length of value to the max bytes it can hold. This |
385 | information is used by the callback that reads the value.*/ |
386 | value.f_len = FTS_MAX_CONFIG_VALUE_LEN; |
387 | value.f_str = static_cast<byte*>(ut_malloc_nokey(value.f_len + 1)); |
388 | |
389 | error = fts_config_get_value(trx, fts_table, name, &value); |
390 | |
391 | if (UNIV_UNLIKELY(error != DB_SUCCESS)) { |
392 | ib::error() << "(" << ut_strerr(error) << ") reading `" |
393 | << name << "'" ; |
394 | } else { |
395 | *int_value = strtoul((char*) value.f_str, NULL, 10); |
396 | } |
397 | |
398 | ut_free(value.f_str); |
399 | |
400 | return(error); |
401 | } |
402 | |
403 | /******************************************************************//** |
404 | Set an ulint value in the config table. |
405 | @return DB_SUCCESS if all OK else error code */ |
406 | dberr_t |
407 | fts_config_set_ulint( |
408 | /*=================*/ |
409 | trx_t* trx, /*!< in: transaction */ |
410 | fts_table_t* fts_table, /*!< in: the indexed |
411 | FTS table */ |
412 | const char* name, /*!< in: param name */ |
413 | ulint int_value) /*!< in: value */ |
414 | { |
415 | dberr_t error; |
416 | fts_string_t value; |
417 | |
418 | /* We set the length of value to the max bytes it can hold. This |
419 | information is used by the callback that reads the value.*/ |
420 | value.f_len = FTS_MAX_CONFIG_VALUE_LEN; |
421 | value.f_str = static_cast<byte*>(ut_malloc_nokey(value.f_len + 1)); |
422 | |
423 | ut_a(FTS_MAX_INT_LEN < FTS_MAX_CONFIG_VALUE_LEN); |
424 | |
425 | value.f_len = (ulint) snprintf( |
426 | (char*) value.f_str, FTS_MAX_INT_LEN, ULINTPF, int_value); |
427 | |
428 | error = fts_config_set_value(trx, fts_table, name, &value); |
429 | |
430 | if (UNIV_UNLIKELY(error != DB_SUCCESS)) { |
431 | ib::error() << "(" << ut_strerr(error) << ") writing `" |
432 | << name << "'" ; |
433 | } |
434 | |
435 | ut_free(value.f_str); |
436 | |
437 | return(error); |
438 | } |
439 | |