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