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