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, ¬_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, ¬_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