1/* Copyright (C) 2010, 2017, MariaDB Corporation Ab
2
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; version 2 of the License.
6
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY; without even the implied warranty of
9 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 GNU General Public License for more details.
11
12 You should have received a copy of the GNU General Public License
13 along with this program; if not, write to the Free Software
14 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA */
15
16/**
17 @file
18
19 Engine defined options of tables/fields/keys in CREATE/ALTER TABLE.
20*/
21
22#include "mariadb.h"
23#include "create_options.h"
24#include <my_getopt.h>
25#include "set_var.h"
26
27#define FRM_QUOTED_VALUE 0x8000U
28
29/**
30 Links this item to the given list end
31
32 @param start The list beginning or NULL
33 @param end The list last element or does not matter
34*/
35
36void engine_option_value::link(engine_option_value **start,
37 engine_option_value **end)
38{
39 DBUG_ENTER("engine_option_value::link");
40 DBUG_PRINT("enter", ("name: '%s' (%u) value: '%s' (%u)",
41 name.str, (uint) name.length,
42 value.str, (uint) value.length));
43 engine_option_value *opt;
44 /* check duplicates to avoid writing them to frm*/
45 for(opt= *start;
46 opt && ((opt->parsed && !opt->value.str) ||
47 my_strnncoll(system_charset_info,
48 (uchar *)name.str, name.length,
49 (uchar*)opt->name.str, opt->name.length));
50 opt= opt->next) /* no-op */;
51 if (opt)
52 {
53 opt->value.str= NULL; /* remove previous value */
54 opt->parsed= TRUE; /* and don't issue warnings for it anymore */
55 }
56 /*
57 Add this option to the end of the list
58
59 @note: We add even if it is opt->value.str == NULL because it can be
60 ALTER TABLE to remove the option.
61 */
62 if (*start)
63 {
64 (*end)->next= this;
65 *end= this;
66 }
67 else
68 {
69 /*
70 note that is *start == 0, the value of *end does not matter,
71 it can be uninitialized.
72 */
73 *start= *end= this;
74 }
75 DBUG_VOID_RETURN;
76}
77
78static bool report_wrong_value(THD *thd, const char *name, const char *val,
79 bool suppress_warning)
80{
81 if (suppress_warning)
82 return 0;
83
84 if (!(thd->variables.sql_mode & MODE_IGNORE_BAD_TABLE_OPTIONS) &&
85 !thd->slave_thread)
86 {
87 my_error(ER_BAD_OPTION_VALUE, MYF(0), val, name);
88 return 1;
89 }
90
91 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, ER_BAD_OPTION_VALUE,
92 ER_THD(thd, ER_BAD_OPTION_VALUE), val, name);
93 return 0;
94}
95
96static bool report_unknown_option(THD *thd, engine_option_value *val,
97 bool suppress_warning)
98{
99 DBUG_ENTER("report_unknown_option");
100
101 if (val->parsed || suppress_warning)
102 {
103 DBUG_PRINT("info", ("parsed => exiting"));
104 DBUG_RETURN(FALSE);
105 }
106
107 if (!(thd->variables.sql_mode & MODE_IGNORE_BAD_TABLE_OPTIONS) &&
108 !thd->slave_thread)
109 {
110 my_error(ER_UNKNOWN_OPTION, MYF(0), val->name.str);
111 DBUG_RETURN(TRUE);
112 }
113
114 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN,
115 ER_UNKNOWN_OPTION, ER_THD(thd, ER_UNKNOWN_OPTION),
116 val->name.str);
117 DBUG_RETURN(FALSE);
118}
119
120#define value_ptr(STRUCT,OPT) ((char*)(STRUCT) + (OPT)->offset)
121
122static bool set_one_value(ha_create_table_option *opt,
123 THD *thd, const LEX_CSTRING *value, void *base,
124 bool suppress_warning,
125 MEM_ROOT *root)
126{
127 DBUG_ENTER("set_one_value");
128 DBUG_PRINT("enter", ("opt: %p type: %u name '%s' value: '%s'",
129 opt,
130 opt->type, opt->name,
131 (value->str ? value->str : "<DEFAULT>")));
132 switch (opt->type)
133 {
134 case HA_OPTION_TYPE_SYSVAR:
135 DBUG_ASSERT(0); // HA_OPTION_TYPE_SYSVAR's are replaced in resolve_sysvars()
136 case HA_OPTION_TYPE_ULL:
137 {
138 ulonglong *val= (ulonglong*)value_ptr(base, opt);
139 if (!value->str)
140 {
141 *val= opt->def_value;
142 DBUG_RETURN(0);
143 }
144
145 my_option optp=
146 { opt->name, 1, 0, (uchar **)val, 0, 0, GET_ULL,
147 REQUIRED_ARG, (longlong)opt->def_value, (longlong)opt->min_value,
148 opt->max_value, 0, (long) opt->block_size, 0};
149
150 ulonglong orig_val= strtoull(value->str, NULL, 10);
151 my_bool unused;
152 *val= orig_val;
153 *val= getopt_ull_limit_value(*val, &optp, &unused);
154 if (*val == orig_val)
155 DBUG_RETURN(0);
156
157 DBUG_RETURN(report_wrong_value(thd, opt->name, value->str,
158 suppress_warning));
159 }
160 case HA_OPTION_TYPE_STRING:
161 {
162 char **val= (char **)value_ptr(base, opt);
163 if (!value->str)
164 {
165 *val= 0;
166 DBUG_RETURN(0);
167 }
168
169 if (!(*val= strmake_root(root, value->str, value->length)))
170 DBUG_RETURN(1);
171 DBUG_RETURN(0);
172 }
173 case HA_OPTION_TYPE_ENUM:
174 {
175 uint *val= (uint *)value_ptr(base, opt), num;
176
177 *val= (uint) opt->def_value;
178 if (!value->str)
179 DBUG_RETURN(0);
180
181 const char *start= opt->values, *end;
182
183 num= 0;
184 while (*start)
185 {
186 for (end=start;
187 *end && *end != ',';
188 end++) /* no-op */;
189 if (!my_strnncoll(system_charset_info,
190 (uchar*)start, end-start,
191 (uchar*)value->str, value->length))
192 {
193 *val= num;
194 DBUG_RETURN(0);
195 }
196 if (*end)
197 end++;
198 start= end;
199 num++;
200 }
201
202 DBUG_RETURN(report_wrong_value(thd, opt->name, value->str,
203 suppress_warning));
204 }
205 case HA_OPTION_TYPE_BOOL:
206 {
207 bool *val= (bool *)value_ptr(base, opt);
208 *val= opt->def_value;
209
210 if (!value->str)
211 DBUG_RETURN(0);
212
213 if (!my_strnncoll(system_charset_info,
214 (const uchar*)"NO", 2,
215 (uchar *)value->str, value->length) ||
216 !my_strnncoll(system_charset_info,
217 (const uchar*)"OFF", 3,
218 (uchar *)value->str, value->length) ||
219 !my_strnncoll(system_charset_info,
220 (const uchar*)"0", 1,
221 (uchar *)value->str, value->length))
222 {
223 *val= FALSE;
224 DBUG_RETURN(FALSE);
225 }
226
227 if (!my_strnncoll(system_charset_info,
228 (const uchar*)"YES", 3,
229 (uchar *)value->str, value->length) ||
230 !my_strnncoll(system_charset_info,
231 (const uchar*)"ON", 2,
232 (uchar *)value->str, value->length) ||
233 !my_strnncoll(system_charset_info,
234 (const uchar*)"1", 1,
235 (uchar *)value->str, value->length))
236 {
237 *val= TRUE;
238 DBUG_RETURN(FALSE);
239 }
240
241 DBUG_RETURN(report_wrong_value(thd, opt->name, value->str,
242 suppress_warning));
243 }
244 }
245 DBUG_ASSERT(0);
246 my_error(ER_UNKNOWN_ERROR, MYF(0));
247 DBUG_RETURN(1);
248}
249
250static const size_t ha_option_type_sizeof[]=
251{ sizeof(ulonglong), sizeof(char *), sizeof(uint), sizeof(bool)};
252
253/**
254 Creates option structure and parses list of options in it
255
256 @param thd thread handler
257 @param option_struct where to store pointer on the option struct
258 @param option_list list of options given by user
259 @param rules list of option description by engine
260 @param suppress_warning second parse so we do not need warnings
261 @param root MEM_ROOT where allocate memory
262
263 @retval TRUE Error
264 @retval FALSE OK
265*/
266
267bool parse_option_list(THD* thd, handlerton *hton, void *option_struct_arg,
268 engine_option_value **option_list,
269 ha_create_table_option *rules,
270 bool suppress_warning, MEM_ROOT *root)
271{
272 ha_create_table_option *opt;
273 size_t option_struct_size= 0;
274 engine_option_value *val, *last;
275 void **option_struct= (void**)option_struct_arg;
276 DBUG_ENTER("parse_option_list");
277 DBUG_PRINT("enter",
278 ("struct: %p list: %p rules: %p suppress_warning: %u root: %p",
279 *option_struct, *option_list, rules,
280 (uint) suppress_warning, root));
281
282 if (rules)
283 {
284 for (opt= rules; opt->name; opt++)
285 set_if_bigger(option_struct_size, opt->offset +
286 ha_option_type_sizeof[opt->type]);
287
288 *option_struct= alloc_root(root, option_struct_size);
289 }
290
291 for (opt= rules; rules && opt->name; opt++)
292 {
293 bool seen=false;
294 for (val= *option_list; val; val= val->next)
295 {
296 last= val;
297 if (my_strnncoll(system_charset_info,
298 (uchar*)opt->name, opt->name_length,
299 (uchar*)val->name.str, val->name.length))
300 continue;
301
302 /* skip duplicates (see engine_option_value constructor above) */
303 if (val->parsed && !val->value.str)
304 continue;
305
306 if (set_one_value(opt, thd, &val->value,
307 *option_struct, suppress_warning || val->parsed, root))
308 DBUG_RETURN(TRUE);
309 val->parsed= true;
310 seen=true;
311 break;
312 }
313 if (!seen || (opt->var && !last->value.str))
314 {
315 LEX_CSTRING default_val= null_clex_str;
316
317 /*
318 Okay, here's the logic for sysvar options:
319 1. When we parse CREATE TABLE and sysvar option was not explicitly
320 mentioned we add it to the list as if it was specified with the
321 *current* value of the underlying sysvar.
322 2. But only if the underlying sysvar value is different from the
323 sysvar's default.
324 3. If it's ALTER TABLE or CREATE_SEQUENCE and the sysvar option was
325 not explicitly mentioned - do nothing, do not add it to the list.
326 4. But if it was ALTER TABLE with sysvar option = DEFAULT, we
327 add it to the list (under the same condition #2).
328 5. If we're here parsing the option list from the .frm file
329 for a normal open_table() and the sysvar option was not there -
330 do not add it to the list (makes no sense anyway) and
331 use the *default* value of the underlying sysvar. Because
332 sysvar value can change, but it should not affect existing tables.
333 This is how it's implemented: the current sysvar value is added
334 to the list if suppress_warning is FALSE (meaning a table is created,
335 that is CREATE TABLE or ALTER TABLE) and it's actually a CREATE TABLE
336 command or it's an ALTER TABLE and the option was seen (=DEFAULT).
337
338 Note that if the option was set explicitly (not =DEFAULT) it wouldn't
339 have passes the if() condition above.
340 */
341 if (!suppress_warning && opt->var &&
342 (thd->lex->sql_command == SQLCOM_CREATE_TABLE || seen))
343 {
344 // take a value from the variable and add it to the list
345 sys_var *sysvar= find_hton_sysvar(hton, opt->var);
346 DBUG_ASSERT(sysvar);
347
348 if (!sysvar->session_is_default(thd))
349 {
350 char buf[256];
351 String sbuf(buf, sizeof(buf), system_charset_info), *str;
352 if ((str= sysvar->val_str(&sbuf, thd, OPT_SESSION, &null_clex_str)))
353 {
354 LEX_CSTRING name= { opt->name, opt->name_length };
355 default_val.str= strmake_root(root, str->ptr(), str->length());
356 default_val.length= str->length();
357 val= new (root) engine_option_value(name, default_val,
358 opt->type != HA_OPTION_TYPE_ULL, option_list, &last);
359 val->parsed= true;
360 }
361 }
362 }
363 set_one_value(opt, thd, &default_val, *option_struct,
364 suppress_warning, root);
365 }
366 }
367
368 for (val= *option_list; val; val= val->next)
369 {
370 if (report_unknown_option(thd, val, suppress_warning))
371 DBUG_RETURN(TRUE);
372 val->parsed= true;
373 }
374
375 DBUG_RETURN(FALSE);
376}
377
378
379/**
380 Resolves all HA_OPTION_TYPE_SYSVAR elements.
381
382 This is done when an engine is loaded.
383*/
384static bool resolve_sysvars(handlerton *hton, ha_create_table_option *rules)
385{
386 for (ha_create_table_option *opt= rules; rules && opt->name; opt++)
387 {
388 if (opt->type == HA_OPTION_TYPE_SYSVAR)
389 {
390 struct my_option optp;
391 plugin_opt_set_limits(&optp, opt->var);
392 switch(optp.var_type) {
393 case GET_ULL:
394 case GET_ULONG:
395 case GET_UINT:
396 opt->type= HA_OPTION_TYPE_ULL;
397 opt->def_value= (ulonglong)optp.def_value;
398 opt->min_value= (ulonglong)optp.min_value;
399 opt->max_value= (ulonglong)optp.max_value;
400 opt->block_size= (ulonglong)optp.block_size;
401 break;
402 case GET_STR:
403 case GET_STR_ALLOC:
404 opt->type= HA_OPTION_TYPE_STRING;
405 break;
406 case GET_BOOL:
407 opt->type= HA_OPTION_TYPE_BOOL;
408 opt->def_value= optp.def_value;
409 break;
410 case GET_ENUM:
411 {
412 opt->type= HA_OPTION_TYPE_ENUM;
413 opt->def_value= optp.def_value;
414
415 char buf[256];
416 String str(buf, sizeof(buf), system_charset_info);
417 str.length(0);
418 for (const char **s= optp.typelib->type_names; *s; s++)
419 {
420 if (str.append(*s) || str.append(','))
421 return 1;
422 }
423 DBUG_ASSERT(str.length());
424 opt->values= my_strndup(str.ptr(), str.length()-1, MYF(MY_WME));
425 if (!opt->values)
426 return 1;
427 break;
428 }
429 default:
430 DBUG_ASSERT(0);
431 }
432 }
433 }
434 return 0;
435}
436
437bool resolve_sysvar_table_options(handlerton *hton)
438{
439 return resolve_sysvars(hton, hton->table_options) ||
440 resolve_sysvars(hton, hton->field_options) ||
441 resolve_sysvars(hton, hton->index_options);
442}
443
444/*
445 Restore HA_OPTION_TYPE_SYSVAR options back as they were
446 before resolve_sysvars().
447
448 This is done when the engine is unloaded, so that we could
449 call resolve_sysvars() if the engine is installed again.
450*/
451static void free_sysvars(handlerton *hton, ha_create_table_option *rules)
452{
453 for (ha_create_table_option *opt= rules; rules && opt->name; opt++)
454 {
455 if (opt->var)
456 {
457 my_free(const_cast<char*>(opt->values));
458 opt->type= HA_OPTION_TYPE_SYSVAR;
459 opt->def_value= 0;
460 opt->min_value= 0;
461 opt->max_value= 0;
462 opt->block_size= 0;
463 opt->values= 0;
464 }
465 }
466}
467
468void free_sysvar_table_options(handlerton *hton)
469{
470 free_sysvars(hton, hton->table_options);
471 free_sysvars(hton, hton->field_options);
472 free_sysvars(hton, hton->index_options);
473}
474
475
476/**
477 Parses all table/fields/keys options
478
479 @param thd thread handler
480 @param file handler of the table
481 @parem share descriptor of the table
482
483 @retval TRUE Error
484 @retval FALSE OK
485*/
486
487bool parse_engine_table_options(THD *thd, handlerton *ht, TABLE_SHARE *share)
488{
489 MEM_ROOT *root= &share->mem_root;
490 DBUG_ENTER("parse_engine_table_options");
491
492 if (parse_option_list(thd, ht, &share->option_struct, & share->option_list,
493 ht->table_options, TRUE, root))
494 DBUG_RETURN(TRUE);
495
496 for (Field **field= share->field; *field; field++)
497 {
498 if (parse_option_list(thd, ht, &(*field)->option_struct,
499 & (*field)->option_list,
500 ht->field_options, TRUE, root))
501 DBUG_RETURN(TRUE);
502 }
503
504 for (uint index= 0; index < share->keys; index ++)
505 {
506 if (parse_option_list(thd, ht, &share->key_info[index].option_struct,
507 & share->key_info[index].option_list,
508 ht->index_options, TRUE, root))
509 DBUG_RETURN(TRUE);
510 }
511
512 DBUG_RETURN(FALSE);
513}
514
515
516bool engine_options_differ(void *old_struct, void *new_struct,
517 ha_create_table_option *rules)
518{
519 ha_create_table_option *opt;
520 for (opt= rules; rules && opt->name; opt++)
521 {
522 char **old_val= (char**)value_ptr(old_struct, opt);
523 char **new_val= (char**)value_ptr(new_struct, opt);
524 int neq;
525 if (opt->type == HA_OPTION_TYPE_STRING)
526 neq= (*old_val && *new_val) ? strcmp(*old_val, *new_val) : *old_val != *new_val;
527 else
528 neq= memcmp(old_val, new_val, ha_option_type_sizeof[opt->type]);
529 if (neq)
530 return true;
531 }
532 return false;
533}
534
535
536/**
537 Returns representation length of key and value in the frm file
538*/
539
540uint engine_option_value::frm_length()
541{
542 /*
543 1 byte - name length
544 2 bytes - value length
545
546 if value.str is NULL, this option is not written to frm (=DEFAULT)
547 */
548 return value.str ? (uint)(1 + name.length + 2 + value.length) : 0;
549}
550
551
552/**
553 Returns length of representation of option list in the frm file
554*/
555
556static uint option_list_frm_length(engine_option_value *opt)
557{
558 uint res= 0;
559
560 for (; opt; opt= opt->next)
561 res+= opt->frm_length();
562
563 return res;
564}
565
566
567/**
568 Calculates length of options image in the .frm
569
570 @param table_option_list list of table options
571 @param create_fields field descriptors list
572 @param keys number of keys
573 @param key_info array of key descriptors
574
575 @returns length of image in frm
576*/
577
578uint engine_table_options_frm_length(engine_option_value *table_option_list,
579 List<Create_field> &create_fields,
580 uint keys, KEY *key_info)
581{
582 List_iterator<Create_field> it(create_fields);
583 Create_field *field;
584 uint res, index;
585 DBUG_ENTER("engine_table_options_frm_length");
586
587 res= option_list_frm_length(table_option_list);
588
589 while ((field= it++))
590 res+= option_list_frm_length(field->option_list);
591
592 for (index= 0; index < keys; index++, key_info++)
593 res+= option_list_frm_length(key_info->option_list);
594
595 /*
596 if there's at least one option somewhere (res > 0)
597 we write option lists for all fields and keys, zero-terminated.
598 If there're no options we write nothing at all (backward compatibility)
599 */
600 DBUG_RETURN(res ? res + 1 + create_fields.elements + keys : 0);
601}
602
603
604/**
605 Writes image of the key and value to the frm image buffer
606
607 @param buff pointer to the buffer free space beginning
608
609 @returns pointer to byte after last recorded in the buffer
610*/
611
612uchar *engine_option_value::frm_image(uchar *buff)
613{
614 if (value.str)
615 {
616 DBUG_ASSERT(name.length <= 0xff);
617 *buff++= (uchar)name.length;
618 memcpy(buff, name.str, name.length);
619 buff+= name.length;
620 int2store(buff, value.length | (quoted_value ? FRM_QUOTED_VALUE : 0));
621 buff+= 2;
622 memcpy(buff, (const uchar *) value.str, value.length);
623 buff+= value.length;
624 }
625 return buff;
626}
627
628/**
629 Writes image of the key and value to the frm image buffer
630
631 @param buff pointer to the buffer to store the options in
632 @param opt list of options;
633
634 @returns pointer to the end of the stored data in the buffer
635*/
636static uchar *option_list_frm_image(uchar *buff, engine_option_value *opt)
637{
638 for (; opt; opt= opt->next)
639 buff= opt->frm_image(buff);
640
641 *buff++= 0;
642 return buff;
643}
644
645
646/**
647 Writes options image in the .frm buffer
648
649 @param buff pointer to the buffer
650 @param table_option_list list of table options
651 @param create_fields field descriptors list
652 @param keys number of keys
653 @param key_info array of key descriptors
654
655 @returns pointer to byte after last recorded in the buffer
656*/
657
658uchar *engine_table_options_frm_image(uchar *buff,
659 engine_option_value *table_option_list,
660 List<Create_field> &create_fields,
661 uint keys, KEY *key_info)
662{
663 List_iterator<Create_field> it(create_fields);
664 Create_field *field;
665 KEY *key_info_end= key_info + keys;
666 DBUG_ENTER("engine_table_options_frm_image");
667
668 buff= option_list_frm_image(buff, table_option_list);
669
670 while ((field= it++))
671 buff= option_list_frm_image(buff, field->option_list);
672
673 while (key_info < key_info_end)
674 buff= option_list_frm_image(buff, (key_info++)->option_list);
675
676 DBUG_RETURN(buff);
677}
678
679/**
680 Reads name and value from buffer, then link it in the list
681
682 @param buff the buffer to read from
683 @param start The list beginning or NULL
684 @param end The list last element or does not matter
685 @param root MEM_ROOT for allocating
686
687 @returns pointer to byte after last recorded in the buffer
688*/
689uchar *engine_option_value::frm_read(const uchar *buff, const uchar *buff_end,
690 engine_option_value **start,
691 engine_option_value **end, MEM_ROOT *root)
692{
693 LEX_CSTRING name, value;
694 uint len;
695#define need_buff(N) if (buff + (N) >= buff_end) return NULL
696
697 need_buff(3);
698 name.length= buff[0];
699 buff++;
700 need_buff(name.length + 2);
701 if (!(name.str= strmake_root(root, (const char*)buff, name.length)))
702 return NULL;
703 buff+= name.length;
704 len= uint2korr(buff);
705 value.length= len & ~FRM_QUOTED_VALUE;
706 buff+= 2;
707 need_buff(value.length);
708 if (!(value.str= strmake_root(root, (const char*)buff, value.length)))
709 return NULL;
710 buff+= value.length;
711
712 engine_option_value *ptr=new (root)
713 engine_option_value(name, value, len & FRM_QUOTED_VALUE, start, end);
714 if (!ptr)
715 return NULL;
716
717 return (uchar *)buff;
718}
719
720
721/**
722 Reads options from this buffer
723
724 @param buff the buffer to read from
725 @param length buffer length
726 @param share table descriptor
727 @param root MEM_ROOT for allocating
728
729 @retval TRUE Error
730 @retval FALSE OK
731*/
732
733bool engine_table_options_frm_read(const uchar *buff, size_t length,
734 TABLE_SHARE *share)
735{
736 const uchar *buff_end= buff + length;
737 engine_option_value *UNINIT_VAR(end);
738 MEM_ROOT *root= &share->mem_root;
739 uint count;
740 DBUG_ENTER("engine_table_options_frm_read");
741
742 while (buff < buff_end && *buff)
743 {
744 if (!(buff= engine_option_value::frm_read(buff, buff_end,
745 &share->option_list, &end, root)))
746 DBUG_RETURN(TRUE);
747 }
748 buff++;
749
750 for (count=0; count < share->fields; count++)
751 {
752 while (buff < buff_end && *buff)
753 {
754 if (!(buff= engine_option_value::frm_read(buff, buff_end,
755 &share->field[count]->option_list,
756 &end, root)))
757 DBUG_RETURN(TRUE);
758 }
759 buff++;
760 }
761
762 for (count=0; count < share->keys; count++)
763 {
764 while (buff < buff_end && *buff)
765 {
766 if (!(buff= engine_option_value::frm_read(buff, buff_end,
767 &share->key_info[count].option_list,
768 &end, root)))
769 DBUG_RETURN(TRUE);
770 }
771 buff++;
772 }
773
774 if (buff < buff_end)
775 sql_print_warning("Table '%s' was created in a later MariaDB version - "
776 "unknown table attributes were ignored",
777 share->table_name.str);
778
779 DBUG_RETURN(buff > buff_end);
780}
781
782/**
783 Merges two lists of engine_option_value's with duplicate removal.
784*/
785
786engine_option_value *merge_engine_table_options(engine_option_value *first,
787 engine_option_value *second,
788 MEM_ROOT *root)
789{
790 engine_option_value *UNINIT_VAR(end), *opt;
791 DBUG_ENTER("merge_engine_table_options");
792
793 /* Create copy of first list */
794 for (opt= first, first= 0; opt; opt= opt->next)
795 new (root) engine_option_value(opt, &first, &end);
796
797 for (opt= second; opt; opt= opt->next)
798 new (root) engine_option_value(opt->name, opt->value, opt->quoted_value,
799 &first, &end);
800 DBUG_RETURN(first);
801}
802
803bool is_engine_option_known(engine_option_value *opt,
804 ha_create_table_option *rules)
805{
806 if (!rules)
807 return false;
808
809 for (; rules->name; rules++)
810 {
811 if (!my_strnncoll(system_charset_info,
812 (uchar*)rules->name, rules->name_length,
813 (uchar*)opt->name.str, opt->name.length))
814 return true;
815 }
816 return false;
817}
818
819