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