1/*****************************************************************************
2
3Copyright (c) 2007, 2016, Oracle and/or its affiliates. All Rights Reserved.
4Copyright (c) 2017, MariaDB Corporation.
5
6This program is free software; you can redistribute it and/or modify it under
7the terms of the GNU General Public License as published by the Free Software
8Foundation; version 2 of the License.
9
10This program is distributed in the hope that it will be useful, but WITHOUT
11ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
13
14You should have received a copy of the GNU General Public License along with
15this program; if not, write to the Free Software Foundation, Inc.,
1651 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
17
18*****************************************************************************/
19
20/******************************************************************//**
21@file fts/fts0config.cc
22Full Text Search configuration table.
23
24Created 2007/5/9 Sunny Bains
25***********************************************************************/
26
27#include "trx0roll.h"
28#include "row0sel.h"
29
30#include "fts0priv.h"
31
32/******************************************************************//**
33Callback function for fetching the config value.
34@return always returns TRUE */
35static
36ibool
37fts_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/******************************************************************//**
65Get value from the config table. The caller must ensure that enough
66space is allocated for value to hold the column contents.
67@return DB_SUCCESS or error code */
68dberr_t
69fts_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/*********************************************************************//**
131Create the config table name for retrieving index specific value.
132@return index config parameter name */
133char*
134fts_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/******************************************************************//**
159Get value specific to an FTS index from the config table. The caller
160must ensure that enough space is allocated for value to hold the
161column contents.
162@return DB_SUCCESS or error code */
163dberr_t
164fts_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/******************************************************************//**
191Set the value in the config table for name.
192@return DB_SUCCESS or error code */
193dberr_t
194fts_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/******************************************************************//**
266Set the value specific to an FTS index in the config table.
267@return DB_SUCCESS or error code */
268dberr_t
269fts_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/******************************************************************//**
297Get an ulint value from the config table.
298@return DB_SUCCESS if all OK else error code */
299dberr_t
300fts_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/******************************************************************//**
331Set an ulint value in the config table.
332@return DB_SUCCESS if all OK else error code */
333dberr_t
334fts_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/******************************************************************//**
370Get an ulint value from the config table.
371@return DB_SUCCESS if all OK else error code */
372dberr_t
373fts_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/******************************************************************//**
404Set an ulint value in the config table.
405@return DB_SUCCESS if all OK else error code */
406dberr_t
407fts_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