1 /***********************************************************************
2
3 Copyright (c) 2011, 2020, Oracle and/or its affiliates. All rights reserved.
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License, version 2.0,
7 as published by the Free Software Foundation.
8
9 This program is also distributed with certain software (including
10 but not limited to OpenSSL) that is licensed under separate terms,
11 as designated in a particular file or component or in included license
12 documentation. The authors of MySQL hereby grant you an additional
13 permission to link the program and your derivative works with the
14 separately licensed software that they have included with MySQL.
15
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License, version 2.0, for more details.
20
21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24
25 ***********************************************************************/
26
27 /**************************************************/ /**
28 @file
29 InnoDB Memcached configurations
30
31 Created 04/12/2011 Jimmy Yang
32 *******************************************************/
33
34 #include <errno.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38
39 #include "my_compiler.h"
40
41 #include "innodb_api.h"
42 #include "innodb_cb_api.h"
43 #include "innodb_config.h"
44 #include "innodb_utility.h"
45
46 /** Configure options enum IDs, their "names" and their default value */
47 option_t config_option_names[] = {
48 {OPTION_ID_COL_SEP, COLUMN_SEPARATOR, {"|", 1}},
49 {OPTION_ID_TBL_MAP_SEP, TABLE_MAP_SEPARATOR, {".", 1}}};
50
51 /**********************************************************************/ /**
52 Makes a NUL-terminated copy of a nonterminated string.
53 @return own: a copy of the string, must be deallocated by caller */
my_strdupl(const char * str,int len)54 static char *my_strdupl(
55 /*=======*/
56 const char *str, /*!< in: string to be copied */
57 int len) /*!< in: length of str, in bytes */
58 {
59 char *s = (char *)malloc(len + 1);
60
61 if (!s) {
62 return (NULL);
63 }
64
65 s[len] = 0;
66
67 return ((char *)memcpy(s, str, len));
68 }
69
70 /**********************************************************************/ /**
71 This function frees meta info structures */
innodb_config_free(meta_cfg_info_t * item)72 void innodb_config_free(
73 /*===============*/
74 meta_cfg_info_t *item) /*!< in: meta info structure */
75 {
76 int i;
77
78 for (i = 0; i < CONTAINER_NUM_COLS; i++) {
79 if (item->col_info[i].col_name) {
80 free(item->col_info[i].col_name);
81 item->col_info[i].col_name = NULL;
82 }
83 }
84
85 if (item->index_info.idx_name) {
86 free(item->index_info.idx_name);
87 item->index_info.idx_name = NULL;
88 }
89
90 if (item->extra_col_info) {
91 for (i = 0; i < item->n_extra_col; i++) {
92 free(item->extra_col_info[i].col_name);
93 item->extra_col_info[i].col_name = NULL;
94 }
95
96 free(item->extra_col_info);
97 item->extra_col_info = NULL;
98 }
99 }
100
101 /**********************************************************************/ /**
102 This function parses possible multiple column name separated by ",", ";"
103 or " " in the input "str" for the memcached "value" field.
104 @return true if everything works out fine */
innodb_config_parse_value_col(meta_cfg_info_t * item,char * str,int len)105 static bool innodb_config_parse_value_col(
106 /*==========================*/
107 meta_cfg_info_t *item, /*!< in: meta info structure */
108 char *str, /*!< in: column name(s) string */
109 int len) /*!< in: length of above string */
110 {
111 static const char *sep = " ;,|\n";
112 char *last;
113 char *column_str;
114 int num_cols = 0;
115 char *my_str = my_strdupl(str, len);
116
117 /* Find out how many column names in the string */
118 for (column_str = strtok_r(my_str, sep, &last); column_str;
119 column_str = strtok_r(NULL, sep, &last)) {
120 num_cols++;
121 }
122
123 free(my_str);
124
125 my_str = str;
126
127 if (num_cols > 1) {
128 int i = 0;
129 item->extra_col_info =
130 (meta_column_t *)malloc(num_cols * sizeof(*item->extra_col_info));
131
132 if (!item->extra_col_info) {
133 return (false);
134 }
135
136 for (column_str = strtok_r(my_str, sep, &last); column_str;
137 column_str = strtok_r(NULL, sep, &last)) {
138 item->extra_col_info[i].col_name_len = strlen(column_str);
139 item->extra_col_info[i].col_name =
140 my_strdupl(column_str, item->extra_col_info[i].col_name_len);
141 item->extra_col_info[i].field_id = -1;
142 i++;
143 }
144
145 item->n_extra_col = num_cols;
146 } else {
147 item->extra_col_info = NULL;
148 item->n_extra_col = 0;
149 }
150
151 return (true);
152 }
153
154 /**********************************************************************/ /**
155 This function opens the cache_policy configuration table, and find the
156 table and column info that used for memcached data
157 @return true if everything works out fine */
innodb_read_cache_policy(meta_cfg_info_t * item,void * thd)158 static bool innodb_read_cache_policy(
159 /*=====================*/
160 meta_cfg_info_t *item, /*!< in: meta info structure */
161 void *thd) /*!< in/out: MySQL THD */
162 {
163 ib_trx_t ib_trx;
164 ib_crsr_t crsr = NULL;
165 ib_crsr_t idx_crsr = NULL;
166 ib_tpl_t tpl = NULL;
167 ib_err_t err = DB_SUCCESS;
168 int n_cols MY_ATTRIBUTE((unused));
169 int i;
170 ib_ulint_t data_len;
171 ib_col_meta_t col_meta;
172
173 ib_trx = ib_cb_trx_begin(IB_TRX_READ_COMMITTED, true, false, thd);
174
175 err = innodb_api_begin(NULL, MCI_CFG_DB_NAME, MCI_CFG_CACHE_POLICIES, NULL,
176 ib_trx, &crsr, &idx_crsr, IB_LOCK_S);
177
178 if (err != DB_SUCCESS) {
179 fprintf(stderr,
180 " InnoDB_Memcached: Cannot open config table"
181 "'%s' in database '%s'. Error %d\n",
182 MCI_CFG_CACHE_POLICIES, MCI_CFG_DB_NAME, err);
183 err = DB_ERROR;
184 goto func_exit;
185 }
186
187 tpl = innodb_cb_read_tuple_create(crsr);
188
189 /* Currently, we support one table per memcached setup.
190 We could extend that limit later */
191 err = innodb_cb_cursor_first(crsr);
192
193 if (err != DB_SUCCESS) {
194 fprintf(stderr,
195 " InnoDB_Memcached: failed to locate entry in"
196 " config table '%s' in database '%s' \n",
197 MCI_CFG_CACHE_POLICIES, MCI_CFG_DB_NAME);
198 err = DB_ERROR;
199 goto func_exit;
200 }
201
202 err = ib_cb_cursor_read_row(crsr, tpl, NULL, 0, NULL, NULL, NULL);
203
204 n_cols = innodb_cb_tuple_get_n_cols(tpl);
205
206 assert(n_cols >= CACHE_POLICY_NUM_COLS);
207
208 for (i = 0; i < CACHE_POLICY_NUM_COLS; ++i) {
209 char opt_name;
210 meta_cache_opt_t opt_val;
211
212 /* Skip cache policy name for now, We could have
213 different cache policy stored, and switch dynamically */
214 if (i == CACHE_POLICY_NAME) {
215 continue;
216 }
217
218 data_len = innodb_cb_col_get_meta(tpl, i, &col_meta);
219
220 if (data_len == IB_SQL_NULL) {
221 opt_val = META_CACHE_OPT_INNODB;
222 } else {
223 opt_name = *(char *)innodb_cb_col_get_value(tpl, i);
224
225 opt_val = (meta_cache_opt_t)opt_name;
226 }
227
228 if (opt_val >= META_CACHE_NUM_OPT || opt_val < META_CACHE_OPT_INNODB) {
229 fprintf(stderr,
230 " InnoDB_Memcached: Invalid Cache"
231 " Policy %d. Reset to innodb_only\n",
232 (int)opt_val);
233 opt_val = META_CACHE_OPT_INNODB;
234 }
235
236 switch (i) {
237 case CACHE_POLICY_GET:
238 item->get_option = opt_val;
239 break;
240 case CACHE_POLICY_SET:
241 item->set_option = opt_val;
242 break;
243 case CACHE_POLICY_DEL:
244 item->del_option = opt_val;
245 break;
246 case CACHE_POLICY_FLUSH:
247 item->flush_option = opt_val;
248 break;
249 default:
250 assert(0);
251 }
252 }
253
254 func_exit:
255
256 innodb_cb_cursor_close(crsr);
257
258 if (tpl) {
259 innodb_cb_tuple_delete(tpl);
260 }
261
262 innodb_cb_trx_commit(ib_trx);
263 ib_cb_trx_release(ib_trx);
264
265 return (err == DB_SUCCESS || err == DB_END_OF_INDEX);
266 }
267
268 /**********************************************************************/ /**
269 This function opens the config_options configuration table, and find the
270 table and column info that used for memcached data
271 @return true if everything works out fine */
innodb_read_config_option(meta_cfg_info_t * item,void * thd)272 static bool innodb_read_config_option(
273 /*======================*/
274 meta_cfg_info_t *item, /*!< in: meta info structure */
275 void *thd) /*!< in/out: MySQL THD */
276 {
277 ib_trx_t ib_trx;
278 ib_crsr_t crsr = NULL;
279 ib_crsr_t idx_crsr = NULL;
280 ib_tpl_t tpl = NULL;
281 ib_err_t err = DB_SUCCESS;
282 int n_cols MY_ATTRIBUTE((unused));
283 int i;
284 ib_ulint_t data_len;
285 ib_col_meta_t col_meta;
286 int current_option = -1;
287
288 ib_trx = ib_cb_trx_begin(IB_TRX_READ_COMMITTED, true, false, thd);
289 err = innodb_api_begin(NULL, MCI_CFG_DB_NAME, MCI_CFG_CONFIG_OPTIONS, NULL,
290 ib_trx, &crsr, &idx_crsr, IB_LOCK_S);
291
292 if (err != DB_SUCCESS) {
293 fprintf(stderr,
294 " InnoDB_Memcached: Cannot open config table"
295 "'%s' in database '%s'\n",
296 MCI_CFG_CONFIG_OPTIONS, MCI_CFG_DB_NAME);
297 err = DB_ERROR;
298 goto func_exit;
299 }
300
301 tpl = innodb_cb_read_tuple_create(crsr);
302
303 err = innodb_cb_cursor_first(crsr);
304
305 if (err != DB_SUCCESS) {
306 fprintf(stderr,
307 " InnoDB_Memcached: failed to locate entry in"
308 " config table '%s' in database '%s' \n",
309 MCI_CFG_CONFIG_OPTIONS, MCI_CFG_DB_NAME);
310 err = DB_ERROR;
311 goto func_exit;
312 }
313
314 do {
315 err = ib_cb_cursor_read_row(crsr, tpl, NULL, 0, NULL, NULL, NULL);
316
317 if (err != DB_SUCCESS) {
318 fprintf(stderr,
319 " InnoDB_Memcached: failed to read"
320 " row from config table '%s' in"
321 " database '%s' \n",
322 MCI_CFG_CONFIG_OPTIONS, MCI_CFG_DB_NAME);
323 err = DB_ERROR;
324 goto func_exit;
325 }
326
327 n_cols = innodb_cb_tuple_get_n_cols(tpl);
328
329 assert(n_cols >= CONFIG_OPT_NUM_COLS);
330
331 for (i = 0; i < CONFIG_OPT_NUM_COLS; ++i) {
332 char *key;
333
334 data_len = innodb_cb_col_get_meta(tpl, i, &col_meta);
335
336 assert(data_len != IB_SQL_NULL);
337
338 if (i == CONFIG_OPT_KEY) {
339 int j;
340 key = (char *)innodb_cb_col_get_value(tpl, i);
341 current_option = -1;
342
343 for (j = 0; j < OPTION_ID_NUM_OPTIONS; j++) {
344 /* Currently, we only support one
345 configure option, that is the string
346 "separator" */
347 if (strcmp(key, config_option_names[j].name) == 0) {
348 current_option = config_option_names[j].id;
349 break;
350 }
351 }
352 }
353
354 if (i == CONFIG_OPT_VALUE && current_option >= 0) {
355 int max_len;
356
357 /* The maximum length for delimiter is
358 MAX_DELIMITER_LEN */
359 max_len = (data_len > MAX_DELIMITER_LEN) ? MAX_DELIMITER_LEN : data_len;
360
361 memcpy(item->options[current_option].value,
362 innodb_cb_col_get_value(tpl, i), max_len);
363
364 item->options[current_option].value[max_len] = 0;
365
366 item->options[current_option].value_len = max_len;
367 }
368 }
369
370 err = ib_cb_cursor_next(crsr);
371
372 } while (err == DB_SUCCESS);
373
374 func_exit:
375
376 innodb_cb_cursor_close(crsr);
377
378 if (tpl) {
379 innodb_cb_tuple_delete(tpl);
380 }
381
382 innodb_cb_trx_commit(ib_trx);
383 ib_cb_trx_release(ib_trx);
384
385 return (err == DB_SUCCESS || err == DB_END_OF_INDEX);
386 }
387
388 /**********************************************************************/ /**
389 This function opens the "containers" configuration table, and find the
390 table and column info that used for memcached data, and instantiates
391 meta_cfg_info_t structure for such metadata.
392 @return instantiated configure info item if everything works out fine,
393 callers are responsible to free the memory returned by this function */
innodb_config_add_item(ib_tpl_t tpl,hash_table_t * eng_meta_hash,void * thd)394 static meta_cfg_info_t *innodb_config_add_item(
395 /*===================*/
396 ib_tpl_t tpl, /*!< in: container row we are fetching
397 row from */
398 hash_table_t *eng_meta_hash, /*!< in/out: hash table to insert
399 the row */
400 void *thd) /*!< in/out: MySQL THD */
401 {
402 ib_err_t err = DB_SUCCESS;
403 int n_cols;
404 int i;
405 ib_ulint_t data_len;
406 meta_cfg_info_t *item = NULL;
407 ib_col_meta_t col_meta;
408 int fold;
409
410 n_cols = innodb_cb_tuple_get_n_cols(tpl);
411
412 if (n_cols < CONTAINER_NUM_COLS) {
413 fprintf(stderr,
414 " InnoDB_Memcached: config table '%s' in"
415 " database '%s' has only %d column(s),"
416 " server is expecting %d columns\n",
417 MCI_CFG_CONTAINER_TABLE, MCI_CFG_DB_NAME, n_cols,
418 CONTAINER_NUM_COLS);
419 err = DB_ERROR;
420 goto func_exit;
421 }
422
423 item = (meta_cfg_info_t *)malloc(sizeof(*item));
424
425 memset(item, 0, sizeof(*item));
426
427 /* Get the column mappings (column for each memcached data */
428 for (i = 0; i < CONTAINER_NUM_COLS; ++i) {
429 data_len = innodb_cb_col_get_meta(tpl, i, &col_meta);
430
431 if (data_len == IB_SQL_NULL) {
432 fprintf(stderr,
433 " InnoDB_Memcached: column %d in"
434 " the entry for config table '%s' in"
435 " database '%s' has an invalid"
436 " NULL value\n",
437 i, MCI_CFG_CONTAINER_TABLE, MCI_CFG_DB_NAME);
438
439 err = DB_ERROR;
440 goto func_exit;
441 }
442
443 item->col_info[i].col_name_len = data_len;
444
445 item->col_info[i].col_name =
446 my_strdupl((char *)innodb_cb_col_get_value(tpl, i), data_len);
447
448 item->col_info[i].field_id = -1;
449
450 if (i == CONTAINER_VALUE) {
451 innodb_config_parse_value_col(item, item->col_info[i].col_name, data_len);
452 }
453 }
454
455 /* Last column is about the unique index name on key column */
456 data_len = innodb_cb_col_get_meta(tpl, i, &col_meta);
457
458 if (data_len == IB_SQL_NULL) {
459 fprintf(stderr,
460 " InnoDB_Memcached: There must be a unique"
461 " index on memcached table's key column\n");
462 err = DB_ERROR;
463 goto func_exit;
464 }
465
466 item->index_info.idx_name =
467 my_strdupl((char *)innodb_cb_col_get_value(tpl, i), data_len);
468
469 if (!innodb_verify(item, thd)) {
470 err = DB_ERROR;
471 goto func_exit;
472 }
473
474 fold = ut_fold_string(item->col_info[0].col_name);
475 HASH_INSERT(meta_cfg_info_t, name_hash, eng_meta_hash, fold, item);
476
477 func_exit:
478 if (err != DB_SUCCESS && item) {
479 free(item);
480 item = NULL;
481 }
482
483 return (item);
484 }
485
486 /**********************************************************************/ /**
487 This function opens the "containers" table, reads in all rows and
488 instantiates the metadata hash table.
489 @return the default configuration setting (whose mapping name is "default") */
innodb_config_meta_hash_init(hash_table_t * meta_hash,void * thd)490 meta_cfg_info_t *innodb_config_meta_hash_init(
491 /*=========================*/
492 hash_table_t *meta_hash, /*!< in/out: InnoDB Memcached engine */
493 void *thd) /*!< in/out: MySQL THD */
494 {
495 ib_trx_t ib_trx;
496 ib_crsr_t crsr = NULL;
497 ib_crsr_t idx_crsr = NULL;
498 ib_tpl_t tpl = NULL;
499 ib_err_t err = DB_SUCCESS;
500 meta_cfg_info_t *default_item = NULL;
501
502 ib_trx = ib_cb_trx_begin(IB_TRX_READ_COMMITTED, true, false, thd);
503 err = innodb_api_begin(NULL, MCI_CFG_DB_NAME, MCI_CFG_CONTAINER_TABLE, NULL,
504 ib_trx, &crsr, &idx_crsr, IB_LOCK_S);
505
506 if (err != DB_SUCCESS) {
507 fprintf(stderr,
508 " InnoDB_Memcached: Please create config table"
509 "'%s' in database '%s' by running"
510 " 'innodb_memcached_config.sql. error %s'\n",
511 MCI_CFG_CONTAINER_TABLE, MCI_CFG_DB_NAME, ib_cb_ut_strerr(err));
512 err = DB_ERROR;
513 goto func_exit;
514 }
515
516 tpl = innodb_cb_read_tuple_create(crsr);
517
518 /* If name field is NULL, just read the first row */
519 err = innodb_cb_cursor_first(crsr);
520
521 while (err == DB_SUCCESS) {
522 meta_cfg_info_t *item;
523
524 err = ib_cb_cursor_read_row(crsr, tpl, NULL, 0, NULL, NULL, NULL);
525
526 if (err != DB_SUCCESS) {
527 fprintf(stderr,
528 " InnoDB_Memcached: failed to read row"
529 " from config table '%s' in database"
530 " '%s' \n",
531 MCI_CFG_CONTAINER_TABLE, MCI_CFG_DB_NAME);
532 err = DB_ERROR;
533 goto func_exit;
534 }
535
536 item = innodb_config_add_item(tpl, meta_hash, thd);
537
538 /* First initialize default setting to be the first row
539 of the table */
540 /* If there are any setting whose name is "default",
541 then set default_item to point to this setting, otherwise
542 point it to the first row of the table */
543 if (default_item == NULL ||
544 (item && strcmp(item->col_info[0].col_name, "default") == 0)) {
545 default_item = item;
546 }
547
548 err = ib_cb_cursor_next(crsr);
549 }
550
551 if (err == DB_END_OF_INDEX) {
552 err = DB_SUCCESS;
553 }
554
555 if (err != DB_SUCCESS) {
556 fprintf(stderr,
557 " InnoDB_Memcached: failed to locate entry in"
558 " config table '%s' in database '%s' \n",
559 MCI_CFG_CONTAINER_TABLE, MCI_CFG_DB_NAME);
560 err = DB_ERROR;
561 }
562
563 func_exit:
564
565 innodb_cb_cursor_close(crsr);
566
567 if (tpl) {
568 innodb_cb_tuple_delete(tpl);
569 }
570
571 innodb_cb_trx_commit(ib_trx);
572 ib_cb_trx_release(ib_trx);
573
574 return (default_item);
575 }
576
577 /**********************************************************************/ /**
578 This function opens the "containers" configuration table, and find the
579 table and column info that used for memcached data
580 @return true if everything works out fine */
innodb_config_container(const char * name,size_t name_len,hash_table_t * meta_hash,void * thd)581 static meta_cfg_info_t *innodb_config_container(
582 /*====================*/
583 const char *name, /*!< in: option name to look for */
584 size_t name_len, /*!< in: option name length */
585 hash_table_t *meta_hash, /*!< in: engine hash table */
586 void *thd) /*!< in/out: MySQL THD */
587 {
588 ib_trx_t ib_trx;
589 ib_crsr_t crsr = NULL;
590 ib_crsr_t idx_crsr = NULL;
591 ib_tpl_t tpl = NULL;
592 ib_err_t err = DB_SUCCESS;
593 int n_cols;
594 int i;
595 ib_ulint_t data_len;
596 ib_col_meta_t col_meta;
597 ib_tpl_t read_tpl = NULL;
598 meta_cfg_info_t *item = NULL;
599
600 if (name != NULL) {
601 ib_ulint_t fold;
602
603 assert(meta_hash);
604
605 fold = ut_fold_string(name);
606 HASH_SEARCH(name_hash, meta_hash, fold, meta_cfg_info_t *, item,
607 (name_len == item->col_info[0].col_name_len &&
608 strcmp(name, item->col_info[0].col_name) == 0));
609
610 if (item) {
611 return (item);
612 }
613 }
614
615 ib_trx = ib_cb_trx_begin(IB_TRX_READ_COMMITTED, true, false, thd);
616 err = innodb_api_begin(NULL, MCI_CFG_DB_NAME, MCI_CFG_CONTAINER_TABLE, NULL,
617 ib_trx, &crsr, &idx_crsr, IB_LOCK_S);
618
619 if (err != DB_SUCCESS) {
620 fprintf(stderr,
621 " InnoDB_Memcached: Please create config table"
622 "'%s' in database '%s' by running"
623 " 'innodb_memcached_config.sql. error %d'\n",
624 MCI_CFG_CONTAINER_TABLE, MCI_CFG_DB_NAME, err);
625 err = DB_ERROR;
626 goto func_exit;
627 }
628
629 if (!name) {
630 tpl = innodb_cb_read_tuple_create(crsr);
631
632 /* If name field is NULL, just read the first row */
633 err = innodb_cb_cursor_first(crsr);
634 } else {
635 /* User supplied a config option name, find it */
636 tpl = ib_cb_sec_search_tuple_create(crsr);
637
638 err = ib_cb_col_set_value(tpl, 0, name, name_len, true);
639
640 ib_cb_cursor_set_match_mode(crsr, IB_EXACT_MATCH);
641 err = ib_cb_cursor_moveto(crsr, tpl, IB_CUR_GE, 0);
642 }
643
644 if (err != DB_SUCCESS) {
645 fprintf(stderr,
646 " InnoDB_Memcached: failed to locate entry in"
647 " config table '%s' in database '%s' \n",
648 MCI_CFG_CONTAINER_TABLE, MCI_CFG_DB_NAME);
649 err = DB_ERROR;
650 goto func_exit;
651 }
652
653 if (!name) {
654 read_tpl = tpl;
655 err = ib_cb_cursor_read_row(crsr, tpl, NULL, 0, NULL, NULL, NULL);
656 } else {
657 read_tpl = ib_cb_clust_read_tuple_create(crsr);
658
659 err = ib_cb_cursor_read_row(crsr, read_tpl, NULL, 0, NULL, NULL, NULL);
660 }
661
662 if (err != DB_SUCCESS) {
663 fprintf(stderr,
664 " InnoDB_Memcached: failed to read row from"
665 " config table '%s' in database '%s' \n",
666 MCI_CFG_CONTAINER_TABLE, MCI_CFG_DB_NAME);
667 err = DB_ERROR;
668 goto func_exit;
669 }
670
671 n_cols = innodb_cb_tuple_get_n_cols(read_tpl);
672
673 if (n_cols < CONTAINER_NUM_COLS) {
674 fprintf(stderr,
675 " InnoDB_Memcached: config table '%s' in"
676 " database '%s' has only %d column(s),"
677 " server is expecting %d columns\n",
678 MCI_CFG_CONTAINER_TABLE, MCI_CFG_DB_NAME, n_cols,
679 CONTAINER_NUM_COLS);
680 err = DB_ERROR;
681 goto func_exit;
682 }
683
684 item = (meta_cfg_info_t *)malloc(sizeof(*item));
685 memset(item, 0, sizeof(*item));
686
687 /* Get the column mappings (column for each memcached data */
688 for (i = 0; i < CONTAINER_NUM_COLS; ++i) {
689 data_len = innodb_cb_col_get_meta(read_tpl, i, &col_meta);
690
691 if (data_len == IB_SQL_NULL) {
692 fprintf(stderr,
693 " InnoDB_Memcached: column %d in"
694 " the entry for config table '%s' in"
695 " database '%s' has an invalid"
696 " NULL value\n",
697 i, MCI_CFG_CONTAINER_TABLE, MCI_CFG_DB_NAME);
698
699 err = DB_ERROR;
700 goto func_exit;
701 }
702
703 item->col_info[i].col_name_len = data_len;
704
705 item->col_info[i].col_name =
706 my_strdupl((char *)innodb_cb_col_get_value(read_tpl, i), data_len);
707
708 item->col_info[i].field_id = -1;
709
710 if (i == CONTAINER_VALUE) {
711 innodb_config_parse_value_col(item, item->col_info[i].col_name, data_len);
712 }
713 }
714
715 /* Last column is about the unique index name on key column */
716 data_len = innodb_cb_col_get_meta(read_tpl, i, &col_meta);
717
718 if (data_len == IB_SQL_NULL) {
719 fprintf(stderr,
720 " InnoDB_Memcached: There must be a unique"
721 " index on memcached table's key column\n");
722 err = DB_ERROR;
723 goto func_exit;
724 }
725
726 item->index_info.idx_name =
727 my_strdupl((char *)innodb_cb_col_get_value(read_tpl, i), data_len);
728
729 if (!innodb_verify(item, thd)) {
730 err = DB_ERROR;
731 }
732
733 func_exit:
734
735 innodb_cb_cursor_close(crsr);
736
737 if (tpl) {
738 innodb_cb_tuple_delete(tpl);
739 }
740
741 innodb_cb_trx_commit(ib_trx);
742 ib_cb_trx_release(ib_trx);
743
744 if (err != DB_SUCCESS) {
745 free(item);
746 item = NULL;
747 } else {
748 ib_ulint_t fold;
749
750 fold = ut_fold_string(item->col_info[0].col_name);
751 HASH_INSERT(meta_cfg_info_t, name_hash, meta_hash, fold, item);
752 }
753
754 return (item);
755 }
756
757 /**********************************************************************/ /**
758 This function verifies "value" column(s) specified by configure table are of
759 the correct type
760 @return DB_SUCCESS if everything is verified */
innodb_config_value_col_verify(const char * name,meta_cfg_info_t * meta_info,ib_col_meta_t * col_meta,int col_id,meta_column_t * col_verify)761 static ib_err_t innodb_config_value_col_verify(
762 /*===========================*/
763 const char *name, /*!< in: column name */
764 meta_cfg_info_t *meta_info, /*!< in: meta info structure */
765 ib_col_meta_t *col_meta, /*!< in: column metadata */
766 int col_id, /*!< in: column ID */
767 meta_column_t *col_verify) /*!< in: verify structure */
768 {
769 ib_err_t err = DB_NOT_FOUND;
770 char table_name[MAX_TABLE_NAME_LEN + MAX_DATABASE_NAME_LEN];
771 char *dbname;
772 char *tname;
773
774 /* Get table name. */
775 dbname = meta_info->col_info[CONTAINER_DB].col_name;
776 tname = meta_info->col_info[CONTAINER_TABLE].col_name;
777 #ifdef __WIN__
778 sprintf(table_name, "%s\%s", dbname, tname);
779 #else
780 snprintf(table_name, sizeof(table_name), "%s/%s", dbname, tname);
781 #endif
782
783 if (!meta_info->n_extra_col) {
784 meta_column_t *cinfo = meta_info->col_info;
785
786 /* "value" column must be of CHAR, VARCHAR or BLOB type */
787 if (strcmp(name, cinfo[CONTAINER_VALUE].col_name) == 0) {
788 if (col_meta->type != IB_VARCHAR && col_meta->type != IB_CHAR &&
789 col_meta->type != IB_BLOB && col_meta->type != IB_CHAR_ANYCHARSET &&
790 col_meta->type != IB_VARCHAR_ANYCHARSET && col_meta->type != IB_INT) {
791 fprintf(stderr,
792 " InnoDB_Memcached: the value"
793 " column %s in table %s"
794 " should be INTEGER, CHAR or"
795 " VARCHAR.\n",
796 name, table_name);
797 err = DB_DATA_MISMATCH;
798 }
799
800 cinfo[CONTAINER_VALUE].field_id = col_id;
801 cinfo[CONTAINER_VALUE].col_meta = *col_meta;
802 err = DB_SUCCESS;
803 }
804 } else {
805 int i;
806
807 for (i = 0; i < meta_info->n_extra_col; i++) {
808 if (strcmp(name, meta_info->extra_col_info[i].col_name) == 0) {
809 if (col_meta->type != IB_VARCHAR && col_meta->type != IB_CHAR &&
810 col_meta->type != IB_BLOB && col_meta->type != IB_CHAR_ANYCHARSET &&
811 col_meta->type != IB_VARCHAR_ANYCHARSET &&
812 col_meta->type != IB_INT) {
813 fprintf(stderr,
814 " InnoDB_Memcached: the value"
815 " column %s in table %s"
816 " should be INTEGER, CHAR or"
817 " VARCHAR.\n",
818 name, table_name);
819 err = DB_DATA_MISMATCH;
820 break;
821 }
822
823 meta_info->extra_col_info[i].field_id = col_id;
824 meta_info->extra_col_info[i].col_meta = *col_meta;
825
826 meta_info->col_info[CONTAINER_VALUE].field_id = col_id;
827 meta_info->col_info[CONTAINER_VALUE].col_meta = *col_meta;
828
829 if (col_verify) {
830 col_verify[i].field_id = col_id;
831 }
832
833 err = DB_SUCCESS;
834 }
835 }
836 }
837
838 return (err);
839 }
840
841 /**********************************************************************/ /**
842 This function verifies the table configuration information on an opened
843 table, and fills in columns used for memcached functionalities (cas, exp etc.)
844 @return true if everything works out fine */
innodb_verify_low(meta_cfg_info_t * info,ib_crsr_t crsr,bool runtime)845 ib_err_t innodb_verify_low(
846 /*==============*/
847 meta_cfg_info_t *info, /*!< in/out: meta info structure */
848 ib_crsr_t crsr, /*!< in: crsr */
849 bool runtime) /*!< in: verify at the runtime */
850 {
851 ib_crsr_t idx_crsr = NULL;
852 ib_tpl_t tpl = NULL;
853 ib_col_meta_t col_meta;
854 int n_cols;
855 int i;
856 bool is_key_col = false;
857 bool is_value_col = false;
858 bool is_flag_col = false;
859 bool is_cas_col = false;
860 bool is_exp_col = false;
861 int index_type;
862 ib_id_u64_t index_id;
863 ib_err_t err = DB_SUCCESS;
864 const char *name;
865 meta_column_t *cinfo = info->col_info;
866 meta_column_t *col_verify = NULL;
867 char table_name[MAX_TABLE_NAME_LEN + MAX_DATABASE_NAME_LEN];
868 char *dbname;
869 char *tname;
870
871 tpl = innodb_cb_read_tuple_create(crsr);
872
873 if (runtime && info->n_extra_col) {
874 col_verify =
875 (meta_column_t *)malloc(info->n_extra_col * sizeof(meta_column_t));
876
877 if (!col_verify) {
878 return (DB_ERROR);
879 }
880
881 for (i = 0; i < info->n_extra_col; i++) {
882 col_verify[i].field_id = -1;
883 }
884 }
885
886 /* Get table name. */
887 dbname = info->col_info[CONTAINER_DB].col_name;
888 tname = info->col_info[CONTAINER_TABLE].col_name;
889 #ifdef __WIN__
890 sprintf(table_name, "%s\%s", dbname, tname);
891 #else
892 snprintf(table_name, sizeof(table_name), "%s/%s", dbname, tname);
893 #endif
894
895 n_cols = innodb_cb_tuple_get_n_cols(tpl);
896
897 /* Verify each mapped column */
898 for (i = 0; i < n_cols; i++) {
899 ib_err_t result = DB_SUCCESS;
900
901 name = innodb_cb_col_get_name(crsr, i);
902 innodb_cb_col_get_meta(tpl, i, &col_meta);
903
904 result =
905 innodb_config_value_col_verify(name, info, &col_meta, i, col_verify);
906
907 if (result == DB_SUCCESS) {
908 is_value_col = true;
909
910 if (strcmp(name, cinfo[CONTAINER_KEY].col_name)) {
911 continue;
912 }
913 } else if (result == DB_DATA_MISMATCH) {
914 err = DB_DATA_MISMATCH;
915 goto func_exit;
916 }
917
918 if (strcmp(name, cinfo[CONTAINER_KEY].col_name) == 0) {
919 /* Key column must be CHAR or VARCHAR type */
920 if (col_meta.type != IB_VARCHAR && col_meta.type != IB_CHAR &&
921 col_meta.type != IB_VARCHAR_ANYCHARSET &&
922 col_meta.type != IB_CHAR_ANYCHARSET && col_meta.type != IB_INT) {
923 fprintf(stderr,
924 " InnoDB_Memcached: the key"
925 " column %s in table %s should"
926 " be INTEGER, CHAR or VARCHAR.\n",
927 name, table_name);
928 err = DB_DATA_MISMATCH;
929 goto func_exit;
930 }
931 cinfo[CONTAINER_KEY].field_id = i;
932 cinfo[CONTAINER_KEY].col_meta = col_meta;
933 is_key_col = true;
934 } else if (strcmp(name, cinfo[CONTAINER_FLAG].col_name) == 0) {
935 /* Flag column must be integer type */
936 if (col_meta.type != IB_INT) {
937 fprintf(stderr,
938 " InnoDB_Memcached: the flag"
939 " column %s in table %s should"
940 " be INTEGER.\n",
941 name, table_name);
942 err = DB_DATA_MISMATCH;
943 goto func_exit;
944 }
945 cinfo[CONTAINER_FLAG].field_id = i;
946 cinfo[CONTAINER_FLAG].col_meta = col_meta;
947 info->flag_enabled = true;
948 is_flag_col = true;
949 } else if (strcmp(name, cinfo[CONTAINER_CAS].col_name) == 0) {
950 /* CAS column must be integer type */
951 if (col_meta.type != IB_INT) {
952 fprintf(stderr,
953 " InnoDB_Memcached: the cas"
954 " column %s in table %s should"
955 " be INTEGER.\n",
956 name, table_name);
957 err = DB_DATA_MISMATCH;
958 goto func_exit;
959 }
960 cinfo[CONTAINER_CAS].field_id = i;
961 cinfo[CONTAINER_CAS].col_meta = col_meta;
962 info->cas_enabled = true;
963 is_cas_col = true;
964 } else if (strcmp(name, cinfo[CONTAINER_EXP].col_name) == 0) {
965 /* EXP column must be integer type */
966 if (col_meta.type != IB_INT) {
967 fprintf(stderr,
968 " InnoDB_Memcached: the expire"
969 " column %s in table %s should"
970 " be INTEGER.\n",
971 name, table_name);
972 err = DB_DATA_MISMATCH;
973 goto func_exit;
974 }
975 cinfo[CONTAINER_EXP].field_id = i;
976 cinfo[CONTAINER_EXP].col_meta = col_meta;
977 info->exp_enabled = true;
978 is_exp_col = true;
979 }
980 }
981
982 /* Key column and Value column must present */
983 if (!is_key_col || !is_value_col) {
984 fprintf(stderr,
985 " InnoDB_Memcached: failed to locate key"
986 " column or value column in table"
987 " as specified by config table \n");
988
989 err = DB_ERROR;
990 goto func_exit;
991 }
992
993 if (info->n_extra_col) {
994 meta_column_t *col_check;
995
996 col_check = (runtime && col_verify) ? col_verify : info->extra_col_info;
997
998 for (i = 0; i < info->n_extra_col; i++) {
999 if (col_check[i].field_id < 0) {
1000 fprintf(stderr,
1001 " InnoDB_Memcached: failed to"
1002 " locate value column %s"
1003 " as specified by config"
1004 " table \n",
1005 info->extra_col_info[i].col_name);
1006 err = DB_ERROR;
1007 goto func_exit;
1008 }
1009 }
1010 }
1011
1012 if (info->flag_enabled && !is_flag_col) {
1013 fprintf(stderr,
1014 " InnoDB_Memcached: failed to locate flag"
1015 " column as specified by config table \n");
1016 err = DB_ERROR;
1017 goto func_exit;
1018 }
1019
1020 if (info->cas_enabled && !is_cas_col) {
1021 fprintf(stderr,
1022 " InnoDB_Memcached: failed to locate cas"
1023 " column as specified by config table \n");
1024 err = DB_ERROR;
1025 goto func_exit;
1026 }
1027
1028 if (info->exp_enabled && !is_exp_col) {
1029 fprintf(stderr,
1030 " InnoDB_Memcached: failed to locate exp"
1031 " column as specified by config table \n");
1032 err = DB_ERROR;
1033 goto func_exit;
1034 }
1035
1036 /* Test the specified index */
1037 innodb_cb_cursor_open_index_using_name(crsr, info->index_info.idx_name,
1038 &idx_crsr, &index_type, &index_id);
1039
1040 if (index_type & IB_CLUSTERED) {
1041 info->index_info.srch_use_idx = META_USE_CLUSTER;
1042 } else if (!idx_crsr || !(index_type & IB_UNIQUE)) {
1043 fprintf(stderr,
1044 " InnoDB_Memcached: Index on key column"
1045 " must be a Unique index\n");
1046 info->index_info.srch_use_idx = META_USE_NO_INDEX;
1047 err = DB_ERROR;
1048 } else {
1049 info->index_info.idx_id = index_id;
1050 info->index_info.srch_use_idx = META_USE_SECONDARY;
1051 }
1052
1053 if (idx_crsr) {
1054 /* We use the same function even if index_type & IB_CLUSTERED,
1055 because the variant for _clust_ is not exposed via API, and it turns
1056 out that we don't really need it, as long as the clustered index
1057 has one column. */
1058 ib_tpl_t idx_tpl = ib_cb_sec_search_tuple_create(idx_crsr);
1059
1060 n_cols = ib_cb_tuple_get_n_user_cols(idx_tpl);
1061
1062 name = ib_cb_get_idx_field_name(idx_crsr, 0);
1063
1064 if (n_cols > 1) {
1065 fprintf(stderr,
1066 " InnoDB_Memcached: The unique_idx_name_on_key (%s)"
1067 " must be on key column (%s) only but it is on %d columns\n",
1068 info->index_info.idx_name, cinfo[CONTAINER_KEY].col_name, n_cols);
1069 info->index_info.srch_use_idx = META_USE_NO_INDEX;
1070 err = DB_ERROR;
1071 }
1072 if (strcmp(name, cinfo[CONTAINER_KEY].col_name)) {
1073 fprintf(stderr,
1074 " InnoDB_Memcached: The unique_idx_name_on_key (%s)"
1075 " must be on key column (%s) but it is on (%s)\n",
1076 info->index_info.idx_name, cinfo[CONTAINER_KEY].col_name, name);
1077 info->index_info.srch_use_idx = META_USE_NO_INDEX;
1078 err = DB_ERROR;
1079 }
1080
1081 innodb_cb_tuple_delete(idx_tpl);
1082 innodb_cb_cursor_close(idx_crsr);
1083 }
1084 func_exit:
1085
1086 if (runtime && col_verify) {
1087 free(col_verify);
1088 }
1089
1090 if (tpl) {
1091 innodb_cb_tuple_delete(tpl);
1092 }
1093
1094 return (err);
1095 }
1096
1097 /** This function verifies the table configuration information, and fills
1098 in columns used for memcached functionalities (cas, exp etc.)
1099 @param[in] info meta info structure
1100 @param[in,out] thd MySQL THD
1101 @return true if everything works out fine */
innodb_verify(meta_cfg_info_t * info,void * thd)1102 bool innodb_verify(
1103 /*==========*/
1104 meta_cfg_info_t *info, void *thd) {
1105 ib_crsr_t crsr = NULL;
1106 char table_name[MAX_TABLE_NAME_LEN + MAX_DATABASE_NAME_LEN];
1107 char *dbname;
1108 char *name;
1109 ib_err_t err = DB_SUCCESS;
1110 ib_trx_t ib_trx = ib_cb_trx_begin(IB_TRX_READ_COMMITTED, false, false, thd);
1111
1112 dbname = info->col_info[CONTAINER_DB].col_name;
1113 name = info->col_info[CONTAINER_TABLE].col_name;
1114 info->flag_enabled = false;
1115 info->cas_enabled = false;
1116 info->exp_enabled = false;
1117
1118 #ifdef _WIN32
1119 sprintf(table_name, "%s\%s", dbname, name);
1120 #else
1121 snprintf(table_name, sizeof(table_name), "%s/%s", dbname, name);
1122 #endif
1123
1124 err = innodb_cb_open_table(table_name, ib_trx, &crsr);
1125
1126 /* Mapped InnoDB table must be able to open */
1127 if (err != DB_SUCCESS) {
1128 fprintf(stderr,
1129 " InnoDB_Memcached: failed to open table"
1130 " '%s' \n",
1131 table_name);
1132 err = DB_ERROR;
1133 goto func_exit;
1134 }
1135
1136 if (ib_cb_is_virtual_table(crsr)) {
1137 fprintf(stderr,
1138 " InnoDB_Memcached: table '%s' cannot"
1139 " be mapped since it contains virtual"
1140 " columns. \n",
1141 table_name);
1142 err = DB_ERROR;
1143 goto func_exit;
1144 }
1145
1146 err = innodb_verify_low(info, crsr, false);
1147 func_exit:
1148 innodb_cb_cursor_close(crsr);
1149
1150 innodb_cb_trx_commit(ib_trx);
1151 ib_cb_trx_release(ib_trx);
1152
1153 return (err == DB_SUCCESS);
1154 }
1155
1156 /**********************************************************************/ /**
1157 If the hash table (meta_hash) is NULL, then initialise the hash table with
1158 data in the configure tables. And return the "default" item. If there is
1159 no setting named "default" then use the first row in the table. This is
1160 currently only used at the engine initialization time.
1161 If the hash table (meta_hash) is created, then look for the meta-data based on
1162 specified configuration name parameter. If such metadata does not exist in
1163 the hash table, then add such metadata into hash table.
1164 @return meta_cfg_info_t* structure if configure option found, otherwise NULL */
innodb_config(const char * name,size_t name_len,hash_table_t ** meta_hash)1165 meta_cfg_info_t *innodb_config(
1166 /*==========*/
1167 const char *name, /*!< in: config option name */
1168 size_t name_len, /*!< in: name length */
1169 hash_table_t **meta_hash) /*!< in/out: engine hash
1170 table. If NULL, it will be
1171 created and initialized */
1172 {
1173 meta_cfg_info_t *item;
1174 bool success;
1175 void *thd = handler_create_thd(false);
1176
1177 if (*meta_hash == NULL) {
1178 *meta_hash = hash_create(100);
1179 }
1180
1181 if (!name) {
1182 item = innodb_config_meta_hash_init(*meta_hash, thd);
1183 } else {
1184 ib_ulint_t fold;
1185
1186 fold = ut_fold_string(name);
1187 HASH_SEARCH(name_hash, *meta_hash, fold, meta_cfg_info_t *, item,
1188 (name_len == item->col_info[0].col_name_len &&
1189 strcmp(name, item->col_info[0].col_name) == 0));
1190
1191 if (item) {
1192 handler_close_thd(thd);
1193 return (item);
1194 }
1195
1196 item = innodb_config_container(name, name_len, *meta_hash, thd);
1197 }
1198
1199 if (!item) {
1200 handler_close_thd(thd);
1201 return (NULL);
1202 }
1203
1204 /* Following two configure operations are optional, and can be
1205 failed */
1206 success = innodb_read_cache_policy(item, thd);
1207
1208 if (!success) {
1209 handler_close_thd(thd);
1210 return (NULL);
1211 }
1212
1213 success = innodb_read_config_option(item, thd);
1214
1215 handler_close_thd(thd);
1216
1217 if (!success) {
1218 return (NULL);
1219 }
1220
1221 return (item);
1222 }
1223