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-1301 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 */
32extern 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
47static my_bool
48write_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
88static 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
103static 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
137static my_bool
138write_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
249my_bool
250sql_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);
336err_w_cache:
337 end_io_cache(&file);
338err_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*/
357my_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
401File_parser *
402sql_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
480frm_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
506static const char *
507parse_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
537my_bool
538read_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
598const char *
599parse_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
628static const char *
629parse_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
669bool 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
699end_of_nlist:
700 if (*(ptr++) != '\n')
701 goto nlist_err;
702 return FALSE;
703
704nlist_err_w_message:
705 my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0), parameter->name.str, line);
706nlist_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
733my_bool
734File_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
874end_of_list:
875 if (*(ptr++) != '\n')
876 goto list_err;
877 break;
878
879list_err_w_message:
880 my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0),
881 parameter->name.str, line);
882list_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
944bool
945File_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