1 /* Copyright (C) 2010, 2019, MariaDB Corporation. 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 02110-1335 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 36 void 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 78 static 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 96 static 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 || thd->slave_thread) 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 { 109 my_error(ER_UNKNOWN_OPTION, MYF(0), val->name.str); 110 DBUG_RETURN(TRUE); 111 } 112 113 push_warning_printf(thd, Sql_condition::WARN_LEVEL_WARN, 114 ER_UNKNOWN_OPTION, ER_THD(thd, ER_UNKNOWN_OPTION), 115 val->name.str); 116 DBUG_RETURN(FALSE); 117 } 118 119 #define value_ptr(STRUCT,OPT) ((char*)(STRUCT) + (OPT)->offset) 120 121 static bool set_one_value(ha_create_table_option *opt, 122 THD *thd, const LEX_CSTRING *value, void *base, 123 bool suppress_warning, 124 MEM_ROOT *root) 125 { 126 DBUG_ENTER("set_one_value"); 127 DBUG_PRINT("enter", ("opt: %p type: %u name '%s' value: '%s'", 128 opt, 129 opt->type, opt->name, 130 (value->str ? value->str : "<DEFAULT>"))); 131 switch (opt->type) 132 { 133 case HA_OPTION_TYPE_SYSVAR: 134 // HA_OPTION_TYPE_SYSVAR's are replaced in resolve_sysvars() 135 break; // to DBUG_ASSERT(0) 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 250 static 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 267 bool 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 */ 384 static 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 437 bool 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 */ 451 static 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 468 void 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 487 bool 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 516 bool 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 540 uint 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 556 static 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 578 uint 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 612 uchar *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 */ 636 static 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 658 uchar *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 */ 689 uchar *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 733 bool 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 786 engine_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 803 bool 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