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