1 /* Copyright (c) 2004, 2021, Oracle and/or its affiliates.
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, version 2.0,
5    as published by the Free Software Foundation.
6 
7    This program is also distributed with certain software (including
8    but not limited to OpenSSL) that is licensed under separate terms,
9    as designated in a particular file or component or in included license
10    documentation.  The authors of MySQL hereby grant you an additional
11    permission to link the program and your derivative works with the
12    separately licensed software that they have included with MySQL.
13 
14    This program is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License, version 2.0, for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with this program; if not, write to the Free Software
21    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA */
22 
23 /**
24   @file
25 
26   @brief
27   Text .frm files management routines
28 */
29 
30 #include "parse_file.h"
31 #include "sql_table.h"                        // build_table_filename
32 #include <errno.h>
33 #include <m_ctype.h>
34 #include <my_sys.h>
35 #include <my_dir.h>
36 #include "mysqld.h"                           // reg_ext
37 #include "mysqld_error.h"                     // ER_*
38 #include "sql_const.h"                        // CREATE_MODE
39 #include "sql_list.h"                         // List_iterator_fast
40 
41 #include "pfs_file_provider.h"
42 #include "mysql/psi/mysql_file.h"
43 
44 /* from sql_db.cc */
45 extern long mysql_rm_arc_files(THD *thd, MY_DIR *dirp, const char *org_path);
46 
47 
48 /**
49   Write string with escaping.
50 
51   @param file	  IO_CACHE for record
52   @param val_s	  string for writing
53 
54   @retval
55     FALSE   OK
56   @retval
57     TRUE    error
58 */
59 
60 static my_bool
write_escaped_string(IO_CACHE * file,LEX_STRING * val_s)61 write_escaped_string(IO_CACHE *file, LEX_STRING *val_s)
62 {
63   char *eos= val_s->str + val_s->length;
64   char *ptr= val_s->str;
65 
66   for (; ptr < eos; ptr++)
67   {
68     /*
69       Should be in sync with read_escaped_string() and
70       parse_quoted_escaped_string()
71     */
72     switch(*ptr) {
73     case '\\': // escape character
74       if (my_b_append(file, (const uchar *)STRING_WITH_LEN("\\\\")))
75 	return TRUE;
76       break;
77     case '\n': // parameter value delimiter
78       if (my_b_append(file, (const uchar *)STRING_WITH_LEN("\\n")))
79 	return TRUE;
80       break;
81     case '\0': // problem for some string processing utilities
82       if (my_b_append(file, (const uchar *)STRING_WITH_LEN("\\0")))
83 	return TRUE;
84       break;
85     case 26: // problem for windows utilities (Ctrl-Z)
86       if (my_b_append(file, (const uchar *)STRING_WITH_LEN("\\z")))
87 	return TRUE;
88       break;
89     case '\'': // list of string delimiter
90       if (my_b_append(file, (const uchar *)STRING_WITH_LEN("\\\'")))
91 	return TRUE;
92       break;
93     default:
94       if (my_b_append(file, (const uchar *)ptr, 1))
95 	return TRUE;
96     }
97   }
98   return FALSE;
99 }
100 
101 
102 /**
103   Write parameter value to IO_CACHE.
104 
105   @param file          pointer to IO_CACHE structure for writing
106   @param base          pointer to data structure
107   @param parameter     pointer to parameter descriptor
108 
109   @retval
110     FALSE   OK
111   @retval
112     TRUE    error
113 */
114 
115 
116 static my_bool
write_parameter(IO_CACHE * file,uchar * base,File_option * parameter)117 write_parameter(IO_CACHE *file, uchar* base, File_option *parameter)
118 {
119   char num_buf[20];			// buffer for numeric operations
120   // string for numeric operations
121   String num(num_buf, sizeof(num_buf), &my_charset_bin);
122   DBUG_ENTER("write_parameter");
123 
124   switch (parameter->type) {
125   case FILE_OPTIONS_STRING:
126   {
127     LEX_STRING *val_s= (LEX_STRING *)(base + parameter->offset);
128     if (my_b_append(file, (const uchar *)val_s->str, val_s->length))
129       DBUG_RETURN(TRUE);
130     break;
131   }
132   case FILE_OPTIONS_ESTRING:
133   {
134     if (write_escaped_string(file, (LEX_STRING *)(base + parameter->offset)))
135       DBUG_RETURN(TRUE);
136     break;
137   }
138   case FILE_OPTIONS_ULONGLONG:
139   {
140     num.set(*((ulonglong *)(base + parameter->offset)), &my_charset_bin);
141     if (my_b_append(file, (const uchar *)num.ptr(), num.length()))
142       DBUG_RETURN(TRUE);
143     break;
144   }
145   case FILE_OPTIONS_TIMESTAMP:
146   {
147     /* string have to be allocated already */
148     LEX_STRING *val_s= (LEX_STRING *)(base + parameter->offset);
149     time_t tm= my_time(0);
150 
151     get_date(val_s->str, GETDATE_DATE_TIME|GETDATE_GMT|GETDATE_FIXEDLENGTH,
152 	     tm);
153     val_s->length= PARSE_FILE_TIMESTAMPLENGTH;
154     if (my_b_append(file, (const uchar *)val_s->str,
155                     PARSE_FILE_TIMESTAMPLENGTH))
156       DBUG_RETURN(TRUE);
157     break;
158   }
159   case FILE_OPTIONS_STRLIST:
160   {
161     List_iterator_fast<LEX_STRING> it(*((List<LEX_STRING>*)
162 					(base + parameter->offset)));
163     bool first= 1;
164     LEX_STRING *str;
165     while ((str= it++))
166     {
167       // We need ' ' after string to detect list continuation
168       if ((!first && my_b_append(file, (const uchar *)STRING_WITH_LEN(" "))) ||
169 	  my_b_append(file, (const uchar *)STRING_WITH_LEN("\'")) ||
170           write_escaped_string(file, str) ||
171 	  my_b_append(file, (const uchar *)STRING_WITH_LEN("\'")))
172       {
173 	DBUG_RETURN(TRUE);
174       }
175       first= 0;
176     }
177     break;
178   }
179   case FILE_OPTIONS_ULLLIST:
180   {
181     List_iterator_fast<ulonglong> it(*((List<ulonglong>*)
182                                        (base + parameter->offset)));
183     bool first= 1;
184     ulonglong *val;
185     while ((val= it++))
186     {
187       num.set(*val, &my_charset_bin);
188       // We need ' ' after string to detect list continuation
189       if ((!first && my_b_append(file, (const uchar *)STRING_WITH_LEN(" "))) ||
190           my_b_append(file, (const uchar *)num.ptr(), num.length()))
191       {
192         DBUG_RETURN(TRUE);
193       }
194       first= 0;
195     }
196     break;
197   }
198   default:
199     assert(0); // never should happened
200   }
201   DBUG_RETURN(FALSE);
202 }
203 
204 
205 /**
206   Write new .frm.
207 
208   @param dir           directory where put .frm
209   @param file_name     .frm file name
210   @param type          .frm type string (VIEW, TABLE)
211   @param base          base address for parameter reading (structure like
212                        TABLE)
213   @param parameters    parameters description
214 
215   @retval
216     FALSE   OK
217   @retval
218     TRUE    error
219 */
220 
221 
222 my_bool
sql_create_definition_file(const LEX_STRING * dir,const LEX_STRING * file_name,const LEX_STRING * type,uchar * base,File_option * parameters)223 sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name,
224 			   const LEX_STRING *type,
225 			   uchar* base, File_option *parameters)
226 {
227   File handler;
228   IO_CACHE file;
229   char path[FN_REFLEN+1];	// +1 to put temporary file name for sure
230   size_t path_end;
231   File_option *param;
232   DBUG_ENTER("sql_create_definition_file");
233   DBUG_PRINT("enter", ("Dir: %s, file: %s, base 0x%lx",
234 		       dir ? dir->str : "(null)",
235                        file_name->str, (ulong) base));
236 
237   if (dir)
238   {
239     fn_format(path, file_name->str, dir->str, "", MY_UNPACK_FILENAME);
240     path_end= strlen(path);
241   }
242   else
243   {
244     /*
245       if not dir is passed, it means file_name is a full path,
246       including dir name, file name itself, and an extension,
247       and with unpack_filename() executed over it.
248     */
249     path_end= strxnmov(path, sizeof(path) - 1, file_name->str, NullS) - path;
250   }
251 
252   // temporary file name
253   path[path_end]='~';
254   path[path_end+1]= '\0';
255   if ((handler= mysql_file_create(key_file_fileparser,
256                                   path, CREATE_MODE, O_RDWR | O_TRUNC,
257                                   MYF(MY_WME))) <= 0)
258   {
259     DBUG_RETURN(TRUE);
260   }
261 
262   if (init_io_cache(&file, handler, 0, SEQ_READ_APPEND, 0L, 0, MYF(MY_WME)))
263     goto err_w_file;
264 
265   // write header (file signature)
266   if (my_b_append(&file, (const uchar *)STRING_WITH_LEN("TYPE=")) ||
267       my_b_append(&file, (const uchar *)type->str, type->length) ||
268       my_b_append(&file, (const uchar *)STRING_WITH_LEN("\n")))
269     goto err_w_file;
270 
271   // write parameters to temporary file
272   for (param= parameters; param->name.str; param++)
273   {
274     if (my_b_append(&file, (const uchar *)param->name.str,
275                     param->name.length) ||
276 	my_b_append(&file, (const uchar *)STRING_WITH_LEN("=")) ||
277 	write_parameter(&file, base, param) ||
278 	my_b_append(&file, (const uchar *)STRING_WITH_LEN("\n")))
279       goto err_w_cache;
280   }
281 
282   if (end_io_cache(&file))
283     goto err_w_file;
284 
285   if (opt_sync_frm) {
286     if (mysql_file_sync(handler, MYF(MY_WME)))
287       goto err_w_file;
288   }
289 
290   if (mysql_file_close(handler, MYF(MY_WME)))
291   {
292     DBUG_RETURN(TRUE);
293   }
294 
295   path[path_end]='\0';
296 
297   {
298     // rename temporary file
299     char path_to[FN_REFLEN];
300     memcpy(path_to, path, path_end+1);
301     path[path_end]='~';
302     if (mysql_file_rename(key_file_fileparser, path, path_to, MYF(MY_WME)))
303     {
304       DBUG_RETURN(TRUE);
305     }
306   }
307   DBUG_RETURN(FALSE);
308 err_w_cache:
309   end_io_cache(&file);
310 err_w_file:
311   mysql_file_close(handler, MYF(MY_WME));
312   DBUG_RETURN(TRUE);
313 }
314 
315 /**
316   Renames a frm file (including backups) in same schema.
317 
318   @thd                     thread handler
319   @param schema            name of given schema
320   @param old_name          original file name
321   @param new_db            new schema
322   @param new_name          new file name
323 
324   @retval
325     0   OK
326   @retval
327     1   Error (only if renaming of frm failed)
328 */
rename_in_schema_file(THD * thd,const char * schema,const char * old_name,const char * new_db,const char * new_name)329 my_bool rename_in_schema_file(THD *thd,
330                               const char *schema, const char *old_name,
331                               const char *new_db, const char *new_name)
332 {
333   char old_path[FN_REFLEN + 1], new_path[FN_REFLEN + 1], arc_path[FN_REFLEN + 1];
334 
335   build_table_filename(old_path, sizeof(old_path) - 1,
336                        schema, old_name, reg_ext, 0);
337   build_table_filename(new_path, sizeof(new_path) - 1,
338                        new_db, new_name, reg_ext, 0);
339 
340   if (mysql_file_rename(key_file_frm, old_path, new_path, MYF(MY_WME)))
341     return 1;
342 
343   /* check if arc_dir exists: disabled unused feature (see bug #17823). */
344   build_table_filename(arc_path, sizeof(arc_path) - 1, schema, "arc", "", 0);
345 
346   { // remove obsolete 'arc' directory and files if any
347     MY_DIR *new_dirp;
348     if ((new_dirp = my_dir(arc_path, MYF(MY_DONT_SORT))))
349     {
350       DBUG_PRINT("my",("Archive subdir found: %s", arc_path));
351       (void) mysql_rm_arc_files(thd, new_dirp, arc_path);
352     }
353   }
354   return 0;
355 }
356 
357 /**
358   Prepare frm to parse (read to memory).
359 
360   @param file_name		  path & filename to .frm file
361   @param mem_root		  MEM_ROOT for buffer allocation
362   @param bad_format_errors	  send errors on bad content
363 
364   @note
365     returned pointer + 1 will be type of .frm
366 
367   @return
368     0 - error
369   @return
370     parser object
371 */
372 
373 File_parser *
sql_parse_prepare(const LEX_STRING * file_name,MEM_ROOT * mem_root,bool bad_format_errors)374 sql_parse_prepare(const LEX_STRING *file_name, MEM_ROOT *mem_root,
375 		  bool bad_format_errors)
376 {
377   MY_STAT stat_info;
378   size_t len;
379   char *buff, *end, *sign;
380   File_parser *parser;
381   File file;
382   DBUG_ENTER("sql_parse_prepare");
383 
384   if (!mysql_file_stat(key_file_fileparser,
385                        file_name->str, &stat_info, MYF(MY_WME)))
386   {
387     DBUG_RETURN(0);
388   }
389 
390   if (stat_info.st_size > INT_MAX-1)
391   {
392     my_error(ER_FPARSER_TOO_BIG_FILE, MYF(0), file_name->str);
393     DBUG_RETURN(0);
394   }
395 
396   if (!(parser= new(mem_root) File_parser))
397   {
398     DBUG_RETURN(0);
399   }
400 
401   if (!(buff= (char*) alloc_root(mem_root,
402                                  static_cast<size_t>(stat_info.st_size)+1)))
403   {
404     DBUG_RETURN(0);
405   }
406 
407   if ((file= mysql_file_open(key_file_fileparser, file_name->str,
408                              O_RDONLY | O_SHARE, MYF(MY_WME))) < 0)
409   {
410     DBUG_RETURN(0);
411   }
412 
413   if ((len= mysql_file_read(file, (uchar *)buff,
414                             static_cast<size_t>(stat_info.st_size),
415                             MYF(MY_WME))) == MY_FILE_ERROR)
416   {
417     mysql_file_close(file, MYF(MY_WME));
418     DBUG_RETURN(0);
419   }
420 
421   if (mysql_file_close(file, MYF(MY_WME)))
422   {
423     DBUG_RETURN(0);
424   }
425 
426   end= buff + len;
427   *end= '\0'; // barrier for more simple parsing
428 
429   // 7 = 5 (TYPE=) + 1 (letter at least of type name) + 1 ('\n')
430   if (len < 7 ||
431       buff[0] != 'T' ||
432       buff[1] != 'Y' ||
433       buff[2] != 'P' ||
434       buff[3] != 'E' ||
435       buff[4] != '=')
436     goto frm_error;
437 
438   // skip signature;
439   parser->file_type.str= sign= buff + 5;
440   while (*sign >= 'A' && *sign <= 'Z' && sign < end)
441     sign++;
442   if (*sign != '\n')
443     goto frm_error;
444   parser->file_type.length= sign - parser->file_type.str;
445   // EOS for file signature just for safety
446   *sign= '\0';
447 
448   parser->end= end;
449   parser->start= sign + 1;
450   parser->content_ok= 1;
451 
452   DBUG_RETURN(parser);
453 
454 frm_error:
455   if (bad_format_errors)
456   {
457     my_error(ER_FPARSER_BAD_HEADER, MYF(0), file_name->str);
458     DBUG_RETURN(0);
459   }
460   else
461     DBUG_RETURN(parser); // upper level have to check parser->ok()
462 }
463 
464 
465 /**
466   parse LEX_STRING.
467 
468   @param ptr		  pointer on string beginning
469   @param end		  pointer on symbol after parsed string end (still owned
470                          by buffer and can be accessed
471   @param mem_root	  MEM_ROOT for parameter allocation
472   @param str		  pointer on string, where results should be stored
473 
474   @retval
475     0	  error
476   @retval
477     \#	  pointer on symbol after string
478 */
479 
480 
481 static const char *
parse_string(const char * ptr,const char * end,MEM_ROOT * mem_root,LEX_STRING * str)482 parse_string(const char *ptr, const char *end, MEM_ROOT *mem_root,
483              LEX_STRING *str)
484 {
485   // get string length
486   const char *eol= strchr(ptr, '\n');
487 
488   if (eol >= end)
489     return 0;
490 
491   str->length= eol - ptr;
492 
493   if (!(str->str= strmake_root(mem_root, ptr, str->length)))
494     return 0;
495   return eol+1;
496 }
497 
498 
499 /**
500   read escaped string from ptr to eol in already allocated str.
501 
502   @param ptr		  pointer on string beginning
503   @param eol		  pointer on character after end of string
504   @param str		  target string
505 
506   @retval
507     FALSE   OK
508   @retval
509     TRUE    error
510 */
511 
512 my_bool
read_escaped_string(const char * ptr,const char * eol,LEX_STRING * str)513 read_escaped_string(const char *ptr, const char *eol, LEX_STRING *str)
514 {
515   char *write_pos= str->str;
516 
517   for (; ptr < eol; ptr++, write_pos++)
518   {
519     char c= *ptr;
520     if (c == '\\')
521     {
522       ptr++;
523       if (ptr >= eol)
524 	return TRUE;
525       /*
526 	Should be in sync with write_escaped_string() and
527 	parse_quoted_escaped_string()
528       */
529       switch(*ptr) {
530       case '\\':
531 	*write_pos= '\\';
532 	break;
533       case 'n':
534 	*write_pos= '\n';
535 	break;
536       case '0':
537 	*write_pos= '\0';
538 	break;
539       case 'z':
540 	*write_pos= 26;
541 	break;
542       case '\'':
543 	*write_pos= '\'';
544         break;
545       default:
546 	return TRUE;
547       }
548     }
549     else
550       *write_pos= c;
551   }
552   str->str[str->length= write_pos-str->str]= '\0'; // just for safety
553   return FALSE;
554 }
555 
556 
557 /**
558   parse \\n delimited escaped string.
559 
560   @param ptr		  pointer on string beginning
561   @param end		  pointer on symbol after parsed string end (still owned
562                          by buffer and can be accessed
563   @param mem_root	  MEM_ROOT for parameter allocation
564   @param str		  pointer on string, where results should be stored
565 
566   @retval
567     0	  error
568   @retval
569     \#	  pointer on symbol after string
570 */
571 
572 
573 const char *
parse_escaped_string(const char * ptr,const char * end,MEM_ROOT * mem_root,LEX_STRING * str)574 parse_escaped_string(const char *ptr, const char *end, MEM_ROOT *mem_root,
575                      LEX_STRING *str)
576 {
577   const char *eol= strchr(ptr, '\n');
578 
579   if (eol == 0 || eol >= end ||
580       !(str->str= (char*) alloc_root(mem_root, (eol - ptr) + 1)) ||
581       read_escaped_string(ptr, eol, str))
582     return 0;
583 
584   return eol+1;
585 }
586 
587 
588 /**
589   parse '' delimited escaped string.
590 
591   @param ptr		  pointer on string beginning
592   @param end		  pointer on symbol after parsed string end (still owned
593                          by buffer and can be accessed
594   @param mem_root	  MEM_ROOT for parameter allocation
595   @param str		  pointer on string, where results should be stored
596 
597   @retval
598     0	  error
599   @retval
600     \#	  pointer on symbol after string
601 */
602 
603 static const char *
parse_quoted_escaped_string(const char * ptr,const char * end,MEM_ROOT * mem_root,LEX_STRING * str)604 parse_quoted_escaped_string(const char *ptr, const char *end,
605 			    MEM_ROOT *mem_root, LEX_STRING *str)
606 {
607   const char *eol;
608   uint result_len= 0;
609   bool escaped= 0;
610 
611   // starting '
612   if (*(ptr++) != '\'')
613     return 0;
614 
615   // find ending '
616   for (eol= ptr; (*eol != '\'' || escaped) && eol < end; eol++)
617   {
618     if (!(escaped= (*eol == '\\' && !escaped)))
619       result_len++;
620   }
621 
622   // process string
623   if (eol >= end ||
624       !(str->str= (char*) alloc_root(mem_root, result_len + 1)) ||
625       read_escaped_string(ptr, eol, str))
626     return 0;
627 
628   return eol+1;
629 }
630 
631 
632 /**
633   Parser for FILE_OPTIONS_ULLLIST type value.
634 
635   @param[in,out] ptr          pointer to parameter
636   @param[in] end              end of the configuration
637   @param[in] line             pointer to the line begining
638   @param[in] base             base address for parameter writing (structure
639     like TABLE)
640   @param[in] parameter        description
641   @param[in] mem_root         MEM_ROOT for parameters allocation
642 */
643 
get_file_options_ulllist(const char * & ptr,const char * end,const char * line,uchar * base,File_option * parameter,MEM_ROOT * mem_root)644 bool get_file_options_ulllist(const char *&ptr, const char *end, const char *line,
645                               uchar* base, File_option *parameter,
646                               MEM_ROOT *mem_root)
647 {
648   List<ulonglong> *nlist= (List<ulonglong>*)(base + parameter->offset);
649   ulonglong *num;
650   nlist->empty();
651   // list parsing
652   while (ptr < end)
653   {
654     int not_used;
655     char *num_end= const_cast<char *>(end);
656     if (!(num= (ulonglong*)alloc_root(mem_root, sizeof(ulonglong))) ||
657         nlist->push_back(num, mem_root))
658       goto nlist_err;
659     *num= my_strtoll10(ptr, &num_end, &not_used);
660     ptr= num_end;
661     switch (*ptr) {
662     case '\n':
663       goto end_of_nlist;
664     case ' ':
665       // we cant go over buffer bounds, because we have \0 at the end
666       ptr++;
667       break;
668     default:
669       goto nlist_err_w_message;
670     }
671   }
672 
673 end_of_nlist:
674   if (*(ptr++) != '\n')
675     goto nlist_err;
676   return FALSE;
677 
678 nlist_err_w_message:
679   my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0), parameter->name.str, line);
680 nlist_err:
681   return TRUE;
682 }
683 
684 
685 /**
686   parse parameters.
687 
688   @param base                base address for parameter writing (structure like
689                              TABLE)
690   @param mem_root            MEM_ROOT for parameters allocation
691   @param parameters          parameters description
692   @param required            number of required parameters in above list. If the file
693                              contains more parameters than "required", they will
694                              be ignored. If the file contains less parameters
695                              then "required", non-existing parameters will
696                              remain their values.
697   @param hook                hook called for unknown keys
698   @param hook_data           some data specific for the hook
699 
700   @retval
701     FALSE   OK
702   @retval
703     TRUE    error
704 */
705 
706 
707 my_bool
parse(uchar * base,MEM_ROOT * mem_root,struct File_option * parameters,uint required,Unknown_key_hook * hook) const708 File_parser::parse(uchar* base, MEM_ROOT *mem_root,
709                    struct File_option *parameters, uint required,
710                    Unknown_key_hook *hook) const
711 {
712   uint first_param= 0, found= 0;
713   const char *ptr= start;
714   const char *eol;
715   LEX_STRING *str;
716   List<LEX_STRING> *list;
717   DBUG_ENTER("File_parser::parse");
718 
719   while (ptr < end && found < required)
720   {
721     const char *line= ptr;
722     if (*ptr == '#')
723     {
724       // it is comment
725       if (!(ptr= strchr(ptr, '\n')))
726       {
727 	my_error(ER_FPARSER_EOF_IN_COMMENT, MYF(0), line);
728 	DBUG_RETURN(TRUE);
729       }
730       ptr++;
731     }
732     else
733     {
734       File_option *parameter= parameters+first_param,
735 	*parameters_end= parameters+required;
736       size_t len= 0;
737       for (; parameter < parameters_end; parameter++)
738       {
739 	len= parameter->name.length;
740 	// check length
741 	if (len < static_cast<size_t>(end-ptr) && ptr[len] != '=')
742 	  continue;
743 	// check keyword
744 	if (memcmp(parameter->name.str, ptr, len) == 0)
745 	  break;
746       }
747 
748       if (parameter < parameters_end)
749       {
750 	found++;
751 	/*
752 	  if we found first parameter, start search from next parameter
753 	  next time.
754 	  (this small optimisation should work, because they should be
755 	  written in same order)
756 	*/
757 	if (parameter == parameters+first_param)
758 	  first_param++;
759 
760 	// get value
761 	ptr+= (len+1);
762 	switch (parameter->type) {
763 	case FILE_OPTIONS_STRING:
764 	{
765 	  if (!(ptr= parse_string(ptr, end, mem_root,
766 				  (LEX_STRING *)(base +
767 						 parameter->offset))))
768 	  {
769 	    my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0),
770                      parameter->name.str, line);
771 	    DBUG_RETURN(TRUE);
772 	  }
773 	  break;
774 	}
775 	case FILE_OPTIONS_ESTRING:
776 	{
777 	  if (!(ptr= parse_escaped_string(ptr, end, mem_root,
778 					  (LEX_STRING *)
779 					  (base + parameter->offset))))
780 	  {
781 	    my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0),
782                      parameter->name.str, line);
783 	    DBUG_RETURN(TRUE);
784 	  }
785 	  break;
786 	}
787 	case FILE_OPTIONS_ULONGLONG:
788 	  if (!(eol= strchr(ptr, '\n')))
789 	  {
790 	    my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0),
791                      parameter->name.str, line);
792 	    DBUG_RETURN(TRUE);
793 	  }
794           {
795             int not_used;
796 	    *((ulonglong*)(base + parameter->offset))=
797               my_strtoll10(ptr, 0, &not_used);
798           }
799 	  ptr= eol+1;
800 	  break;
801 	case FILE_OPTIONS_TIMESTAMP:
802 	{
803 	  /* string have to be allocated already */
804 	  LEX_STRING *val= (LEX_STRING *)(base + parameter->offset);
805 	  /* yyyy-mm-dd HH:MM:SS = 19(PARSE_FILE_TIMESTAMPLENGTH) characters */
806 	  if (ptr[PARSE_FILE_TIMESTAMPLENGTH] != '\n')
807 	  {
808 	    my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0),
809                      parameter->name.str, line);
810 	    DBUG_RETURN(TRUE);
811 	  }
812 	  memcpy(val->str, ptr, PARSE_FILE_TIMESTAMPLENGTH);
813 	  val->str[val->length= PARSE_FILE_TIMESTAMPLENGTH]= '\0';
814 	  ptr+= (PARSE_FILE_TIMESTAMPLENGTH+1);
815 	  break;
816 	}
817 	case FILE_OPTIONS_STRLIST:
818 	{
819           list= (List<LEX_STRING>*)(base + parameter->offset);
820 
821 	  list->empty();
822 	  // list parsing
823 	  while (ptr < end)
824 	  {
825 	    if (!(str= (LEX_STRING*)alloc_root(mem_root,
826 					       sizeof(LEX_STRING))) ||
827 		list->push_back(str, mem_root))
828 	      goto list_err;
829 	    if (!(ptr= parse_quoted_escaped_string(ptr, end, mem_root, str)))
830 	      goto list_err_w_message;
831 	    switch (*ptr) {
832 	    case '\n':
833 	      goto end_of_list;
834 	    case ' ':
835 	      // we cant go over buffer bounds, because we have \0 at the end
836 	      ptr++;
837 	      break;
838 	    default:
839 	      goto list_err_w_message;
840 	    }
841 	  }
842 
843 end_of_list:
844 	  if (*(ptr++) != '\n')
845 	    goto list_err;
846 	  break;
847 
848 list_err_w_message:
849 	  my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0),
850                    parameter->name.str, line);
851 list_err:
852 	  DBUG_RETURN(TRUE);
853 	}
854         case FILE_OPTIONS_ULLLIST:
855           if (get_file_options_ulllist(ptr, end, line, base,
856                                        parameter, mem_root))
857             DBUG_RETURN(TRUE);
858           break;
859 	default:
860 	  assert(0); // never should happened
861 	}
862       }
863       else
864       {
865         ptr= line;
866         if (hook->process_unknown_string(ptr, base, mem_root, end))
867         {
868           DBUG_RETURN(TRUE);
869         }
870         // skip unknown parameter
871         if (!(ptr= strchr(ptr, '\n')))
872         {
873           my_error(ER_FPARSER_EOF_IN_UNKNOWN_PARAMETER, MYF(0), line);
874           DBUG_RETURN(TRUE);
875         }
876         ptr++;
877       }
878     }
879   }
880 
881   /*
882     NOTE: if we read less than "required" parameters, it is still Ok.
883     Probably, we've just read the file of the previous version, which
884     contains less parameters.
885   */
886 
887   DBUG_RETURN(FALSE);
888 }
889 
890 
891 /**
892   Dummy unknown key hook.
893 
894   @param[in,out] unknown_key       reference on the line with unknown
895     parameter and the parsing point
896   @param[in] base                  base address for parameter writing
897     (structure like TABLE)
898   @param[in] mem_root              MEM_ROOT for parameters allocation
899   @param[in] end                   the end of the configuration
900 
901   @note
902     This hook used to catch no longer supported keys and process them for
903     backward compatibility, but it will not slow down processing of modern
904     format files.
905     This hook does nothing except debug output.
906 
907   @retval
908     FALSE OK
909   @retval
910     TRUE  Error
911 */
912 
913 bool
process_unknown_string(const char * & unknown_key,uchar * base,MEM_ROOT * mem_root,const char * end)914 File_parser_dummy_hook::process_unknown_string(const char *&unknown_key,
915                                                uchar* base, MEM_ROOT *mem_root,
916                                                const char *end)
917 {
918   DBUG_ENTER("file_parser_dummy_hook::process_unknown_string");
919   DBUG_PRINT("info", ("Unknown key: '%60s'", unknown_key));
920   DBUG_RETURN(FALSE);
921 }
922