MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
parse_file.cc
Go to the documentation of this file.
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 Foundation,
14  51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA */
15 
23 #include "sql_priv.h"
24 #include "parse_file.h"
25 #include "unireg.h" // CREATE_MODE
26 #include "sql_table.h" // build_table_filename
27 #include <errno.h>
28 #include <m_ctype.h>
29 #include <my_sys.h>
30 #include <my_dir.h>
31 
32 /* from sql_db.cc */
33 extern long mysql_rm_arc_files(THD *thd, MY_DIR *dirp, const char *org_path);
34 
35 
48 static my_bool
49 write_escaped_string(IO_CACHE *file, LEX_STRING *val_s)
50 {
51  char *eos= val_s->str + val_s->length;
52  char *ptr= val_s->str;
53 
54  for (; ptr < eos; ptr++)
55  {
56  /*
57  Should be in sync with read_escaped_string() and
58  parse_quoted_escaped_string()
59  */
60  switch(*ptr) {
61  case '\\': // escape character
62  if (my_b_append(file, (const uchar *)STRING_WITH_LEN("\\\\")))
63  return TRUE;
64  break;
65  case '\n': // parameter value delimiter
66  if (my_b_append(file, (const uchar *)STRING_WITH_LEN("\\n")))
67  return TRUE;
68  break;
69  case '\0': // problem for some string processing utilities
70  if (my_b_append(file, (const uchar *)STRING_WITH_LEN("\\0")))
71  return TRUE;
72  break;
73  case 26: // problem for windows utilities (Ctrl-Z)
74  if (my_b_append(file, (const uchar *)STRING_WITH_LEN("\\z")))
75  return TRUE;
76  break;
77  case '\'': // list of string delimiter
78  if (my_b_append(file, (const uchar *)STRING_WITH_LEN("\\\'")))
79  return TRUE;
80  break;
81  default:
82  if (my_b_append(file, (const uchar *)ptr, 1))
83  return TRUE;
84  }
85  }
86  return FALSE;
87 }
88 
89 
104 static my_bool
105 write_parameter(IO_CACHE *file, uchar* base, File_option *parameter)
106 {
107  char num_buf[20]; // buffer for numeric operations
108  // string for numeric operations
109  String num(num_buf, sizeof(num_buf), &my_charset_bin);
110  DBUG_ENTER("write_parameter");
111 
112  switch (parameter->type) {
113  case FILE_OPTIONS_STRING:
114  {
115  LEX_STRING *val_s= (LEX_STRING *)(base + parameter->offset);
116  if (my_b_append(file, (const uchar *)val_s->str, val_s->length))
117  DBUG_RETURN(TRUE);
118  break;
119  }
120  case FILE_OPTIONS_ESTRING:
121  {
122  if (write_escaped_string(file, (LEX_STRING *)(base + parameter->offset)))
123  DBUG_RETURN(TRUE);
124  break;
125  }
126  case FILE_OPTIONS_ULONGLONG:
127  {
128  num.set(*((ulonglong *)(base + parameter->offset)), &my_charset_bin);
129  if (my_b_append(file, (const uchar *)num.ptr(), num.length()))
130  DBUG_RETURN(TRUE);
131  break;
132  }
133  case FILE_OPTIONS_TIMESTAMP:
134  {
135  /* string have to be allocated already */
136  LEX_STRING *val_s= (LEX_STRING *)(base + parameter->offset);
137  time_t tm= my_time(0);
138 
139  get_date(val_s->str, GETDATE_DATE_TIME|GETDATE_GMT|GETDATE_FIXEDLENGTH,
140  tm);
141  val_s->length= PARSE_FILE_TIMESTAMPLENGTH;
142  if (my_b_append(file, (const uchar *)val_s->str,
143  PARSE_FILE_TIMESTAMPLENGTH))
144  DBUG_RETURN(TRUE);
145  break;
146  }
147  case FILE_OPTIONS_STRLIST:
148  {
150  (base + parameter->offset)));
151  bool first= 1;
152  LEX_STRING *str;
153  while ((str= it++))
154  {
155  // We need ' ' after string to detect list continuation
156  if ((!first && my_b_append(file, (const uchar *)STRING_WITH_LEN(" "))) ||
157  my_b_append(file, (const uchar *)STRING_WITH_LEN("\'")) ||
158  write_escaped_string(file, str) ||
159  my_b_append(file, (const uchar *)STRING_WITH_LEN("\'")))
160  {
161  DBUG_RETURN(TRUE);
162  }
163  first= 0;
164  }
165  break;
166  }
167  case FILE_OPTIONS_ULLLIST:
168  {
170  (base + parameter->offset)));
171  bool first= 1;
172  ulonglong *val;
173  while ((val= it++))
174  {
175  num.set(*val, &my_charset_bin);
176  // We need ' ' after string to detect list continuation
177  if ((!first && my_b_append(file, (const uchar *)STRING_WITH_LEN(" "))) ||
178  my_b_append(file, (const uchar *)num.ptr(), num.length()))
179  {
180  DBUG_RETURN(TRUE);
181  }
182  first= 0;
183  }
184  break;
185  }
186  default:
187  DBUG_ASSERT(0); // never should happened
188  }
189  DBUG_RETURN(FALSE);
190 }
191 
192 
210 my_bool
211 sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name,
212  const LEX_STRING *type,
213  uchar* base, File_option *parameters)
214 {
215  File handler;
216  IO_CACHE file;
217  char path[FN_REFLEN+1]; // +1 to put temporary file name for sure
218  int path_end;
219  File_option *param;
220  DBUG_ENTER("sql_create_definition_file");
221  DBUG_PRINT("enter", ("Dir: %s, file: %s, base 0x%lx",
222  dir ? dir->str : "(null)",
223  file_name->str, (ulong) base));
224 
225  if (dir)
226  {
227  fn_format(path, file_name->str, dir->str, "", MY_UNPACK_FILENAME);
228  path_end= strlen(path);
229  }
230  else
231  {
232  /*
233  if not dir is passed, it means file_name is a full path,
234  including dir name, file name itself, and an extension,
235  and with unpack_filename() executed over it.
236  */
237  path_end= strxnmov(path, sizeof(path) - 1, file_name->str, NullS) - path;
238  }
239 
240  // temporary file name
241  path[path_end]='~';
242  path[path_end+1]= '\0';
243  if ((handler= mysql_file_create(key_file_fileparser,
244  path, CREATE_MODE, O_RDWR | O_TRUNC,
245  MYF(MY_WME))) <= 0)
246  {
247  DBUG_RETURN(TRUE);
248  }
249 
250  if (init_io_cache(&file, handler, 0, SEQ_READ_APPEND, 0L, 0, MYF(MY_WME)))
251  goto err_w_file;
252 
253  // write header (file signature)
254  if (my_b_append(&file, (const uchar *)STRING_WITH_LEN("TYPE=")) ||
255  my_b_append(&file, (const uchar *)type->str, type->length) ||
256  my_b_append(&file, (const uchar *)STRING_WITH_LEN("\n")))
257  goto err_w_file;
258 
259  // write parameters to temporary file
260  for (param= parameters; param->name.str; param++)
261  {
262  if (my_b_append(&file, (const uchar *)param->name.str,
263  param->name.length) ||
264  my_b_append(&file, (const uchar *)STRING_WITH_LEN("=")) ||
265  write_parameter(&file, base, param) ||
266  my_b_append(&file, (const uchar *)STRING_WITH_LEN("\n")))
267  goto err_w_cache;
268  }
269 
270  if (end_io_cache(&file))
271  goto err_w_file;
272 
273  if (opt_sync_frm) {
274  if (mysql_file_sync(handler, MYF(MY_WME)))
275  goto err_w_file;
276  }
277 
278  if (mysql_file_close(handler, MYF(MY_WME)))
279  {
280  DBUG_RETURN(TRUE);
281  }
282 
283  path[path_end]='\0';
284 
285  {
286  // rename temporary file
287  char path_to[FN_REFLEN];
288  memcpy(path_to, path, path_end+1);
289  path[path_end]='~';
290  if (mysql_file_rename(key_file_fileparser, path, path_to, MYF(MY_WME)))
291  {
292  DBUG_RETURN(TRUE);
293  }
294  }
295  DBUG_RETURN(FALSE);
296 err_w_cache:
297  end_io_cache(&file);
298 err_w_file:
299  mysql_file_close(handler, MYF(MY_WME));
300  DBUG_RETURN(TRUE);
301 }
302 
317 my_bool rename_in_schema_file(THD *thd,
318  const char *schema, const char *old_name,
319  const char *new_db, const char *new_name)
320 {
321  char old_path[FN_REFLEN + 1], new_path[FN_REFLEN + 1], arc_path[FN_REFLEN + 1];
322 
323  build_table_filename(old_path, sizeof(old_path) - 1,
324  schema, old_name, reg_ext, 0);
325  build_table_filename(new_path, sizeof(new_path) - 1,
326  new_db, new_name, reg_ext, 0);
327 
328  if (mysql_file_rename(key_file_frm, old_path, new_path, MYF(MY_WME)))
329  return 1;
330 
331  /* check if arc_dir exists: disabled unused feature (see bug #17823). */
332  build_table_filename(arc_path, sizeof(arc_path) - 1, schema, "arc", "", 0);
333 
334  { // remove obsolete 'arc' directory and files if any
335  MY_DIR *new_dirp;
336  if ((new_dirp = my_dir(arc_path, MYF(MY_DONT_SORT))))
337  {
338  DBUG_PRINT("my",("Archive subdir found: %s", arc_path));
339  (void) mysql_rm_arc_files(thd, new_dirp, arc_path);
340  }
341  }
342  return 0;
343 }
344 
361 File_parser *
362 sql_parse_prepare(const LEX_STRING *file_name, MEM_ROOT *mem_root,
363  bool bad_format_errors)
364 {
365  MY_STAT stat_info;
366  size_t len;
367  char *buff, *end, *sign;
368  File_parser *parser;
369  File file;
370  DBUG_ENTER("sql_parse_prepare");
371 
372  if (!mysql_file_stat(key_file_fileparser,
373  file_name->str, &stat_info, MYF(MY_WME)))
374  {
375  DBUG_RETURN(0);
376  }
377 
378  if (stat_info.st_size > INT_MAX-1)
379  {
380  my_error(ER_FPARSER_TOO_BIG_FILE, MYF(0), file_name->str);
381  DBUG_RETURN(0);
382  }
383 
384  if (!(parser= new(mem_root) File_parser))
385  {
386  DBUG_RETURN(0);
387  }
388 
389  if (!(buff= (char*) alloc_root(mem_root, stat_info.st_size+1)))
390  {
391  DBUG_RETURN(0);
392  }
393 
394  if ((file= mysql_file_open(key_file_fileparser, file_name->str,
395  O_RDONLY | O_SHARE, MYF(MY_WME))) < 0)
396  {
397  DBUG_RETURN(0);
398  }
399 
400  if ((len= mysql_file_read(file, (uchar *)buff, stat_info.st_size,
401  MYF(MY_WME))) == MY_FILE_ERROR)
402  {
403  mysql_file_close(file, MYF(MY_WME));
404  DBUG_RETURN(0);
405  }
406 
407  if (mysql_file_close(file, MYF(MY_WME)))
408  {
409  DBUG_RETURN(0);
410  }
411 
412  end= buff + len;
413  *end= '\0'; // barrier for more simple parsing
414 
415  // 7 = 5 (TYPE=) + 1 (letter at least of type name) + 1 ('\n')
416  if (len < 7 ||
417  buff[0] != 'T' ||
418  buff[1] != 'Y' ||
419  buff[2] != 'P' ||
420  buff[3] != 'E' ||
421  buff[4] != '=')
422  goto frm_error;
423 
424  // skip signature;
425  parser->file_type.str= sign= buff + 5;
426  while (*sign >= 'A' && *sign <= 'Z' && sign < end)
427  sign++;
428  if (*sign != '\n')
429  goto frm_error;
430  parser->file_type.length= sign - parser->file_type.str;
431  // EOS for file signature just for safety
432  *sign= '\0';
433 
434  parser->end= end;
435  parser->start= sign + 1;
436  parser->content_ok= 1;
437 
438  DBUG_RETURN(parser);
439 
440 frm_error:
441  if (bad_format_errors)
442  {
443  my_error(ER_FPARSER_BAD_HEADER, MYF(0), file_name->str);
444  DBUG_RETURN(0);
445  }
446  else
447  DBUG_RETURN(parser); // upper level have to check parser->ok()
448 }
449 
450 
467 static const char *
468 parse_string(const char *ptr, const char *end, MEM_ROOT *mem_root,
469  LEX_STRING *str)
470 {
471  // get string length
472  const char *eol= strchr(ptr, '\n');
473 
474  if (eol >= end)
475  return 0;
476 
477  str->length= eol - ptr;
478 
479  if (!(str->str= strmake_root(mem_root, ptr, str->length)))
480  return 0;
481  return eol+1;
482 }
483 
484 
498 my_bool
499 read_escaped_string(const char *ptr, const char *eol, LEX_STRING *str)
500 {
501  char *write_pos= str->str;
502 
503  for (; ptr < eol; ptr++, write_pos++)
504  {
505  char c= *ptr;
506  if (c == '\\')
507  {
508  ptr++;
509  if (ptr >= eol)
510  return TRUE;
511  /*
512  Should be in sync with write_escaped_string() and
513  parse_quoted_escaped_string()
514  */
515  switch(*ptr) {
516  case '\\':
517  *write_pos= '\\';
518  break;
519  case 'n':
520  *write_pos= '\n';
521  break;
522  case '0':
523  *write_pos= '\0';
524  break;
525  case 'z':
526  *write_pos= 26;
527  break;
528  case '\'':
529  *write_pos= '\'';
530  break;
531  default:
532  return TRUE;
533  }
534  }
535  else
536  *write_pos= c;
537  }
538  str->str[str->length= write_pos-str->str]= '\0'; // just for safety
539  return FALSE;
540 }
541 
542 
559 const char *
560 parse_escaped_string(const char *ptr, const char *end, MEM_ROOT *mem_root,
561  LEX_STRING *str)
562 {
563  const char *eol= strchr(ptr, '\n');
564 
565  if (eol == 0 || eol >= end ||
566  !(str->str= (char*) alloc_root(mem_root, (eol - ptr) + 1)) ||
567  read_escaped_string(ptr, eol, str))
568  return 0;
569 
570  return eol+1;
571 }
572 
573 
589 static const char *
590 parse_quoted_escaped_string(const char *ptr, const char *end,
591  MEM_ROOT *mem_root, LEX_STRING *str)
592 {
593  const char *eol;
594  uint result_len= 0;
595  bool escaped= 0;
596 
597  // starting '
598  if (*(ptr++) != '\'')
599  return 0;
600 
601  // find ending '
602  for (eol= ptr; (*eol != '\'' || escaped) && eol < end; eol++)
603  {
604  if (!(escaped= (*eol == '\\' && !escaped)))
605  result_len++;
606  }
607 
608  // process string
609  if (eol >= end ||
610  !(str->str= (char*) alloc_root(mem_root, result_len + 1)) ||
611  read_escaped_string(ptr, eol, str))
612  return 0;
613 
614  return eol+1;
615 }
616 
617 
630 bool get_file_options_ulllist(const char *&ptr, const char *end, const char *line,
631  uchar* base, File_option *parameter,
632  MEM_ROOT *mem_root)
633 {
634  List<ulonglong> *nlist= (List<ulonglong>*)(base + parameter->offset);
635  ulonglong *num;
636  nlist->empty();
637  // list parsing
638  while (ptr < end)
639  {
640  int not_used;
641  char *num_end= const_cast<char *>(end);
642  if (!(num= (ulonglong*)alloc_root(mem_root, sizeof(ulonglong))) ||
643  nlist->push_back(num, mem_root))
644  goto nlist_err;
645  *num= my_strtoll10(ptr, &num_end, &not_used);
646  ptr= num_end;
647  switch (*ptr) {
648  case '\n':
649  goto end_of_nlist;
650  case ' ':
651  // we cant go over buffer bounds, because we have \0 at the end
652  ptr++;
653  break;
654  default:
655  goto nlist_err_w_message;
656  }
657  }
658 
659 end_of_nlist:
660  if (*(ptr++) != '\n')
661  goto nlist_err;
662  return FALSE;
663 
664 nlist_err_w_message:
665  my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0), parameter->name.str, line);
666 nlist_err:
667  return TRUE;
668 }
669 
670 
693 my_bool
694 File_parser::parse(uchar* base, MEM_ROOT *mem_root,
695  struct File_option *parameters, uint required,
696  Unknown_key_hook *hook) const
697 {
698  uint first_param= 0, found= 0;
699  const char *ptr= start;
700  const char *eol;
701  LEX_STRING *str;
702  List<LEX_STRING> *list;
703  DBUG_ENTER("File_parser::parse");
704 
705  while (ptr < end && found < required)
706  {
707  const char *line= ptr;
708  if (*ptr == '#')
709  {
710  // it is comment
711  if (!(ptr= strchr(ptr, '\n')))
712  {
713  my_error(ER_FPARSER_EOF_IN_COMMENT, MYF(0), line);
714  DBUG_RETURN(TRUE);
715  }
716  ptr++;
717  }
718  else
719  {
720  File_option *parameter= parameters+first_param,
721  *parameters_end= parameters+required;
722  int len= 0;
723  for (; parameter < parameters_end; parameter++)
724  {
725  len= parameter->name.length;
726  // check length
727  if (len < (end-ptr) && ptr[len] != '=')
728  continue;
729  // check keyword
730  if (memcmp(parameter->name.str, ptr, len) == 0)
731  break;
732  }
733 
734  if (parameter < parameters_end)
735  {
736  found++;
737  /*
738  if we found first parameter, start search from next parameter
739  next time.
740  (this small optimisation should work, because they should be
741  written in same order)
742  */
743  if (parameter == parameters+first_param)
744  first_param++;
745 
746  // get value
747  ptr+= (len+1);
748  switch (parameter->type) {
749  case FILE_OPTIONS_STRING:
750  {
751  if (!(ptr= parse_string(ptr, end, mem_root,
752  (LEX_STRING *)(base +
753  parameter->offset))))
754  {
755  my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0),
756  parameter->name.str, line);
757  DBUG_RETURN(TRUE);
758  }
759  break;
760  }
761  case FILE_OPTIONS_ESTRING:
762  {
763  if (!(ptr= parse_escaped_string(ptr, end, mem_root,
764  (LEX_STRING *)
765  (base + parameter->offset))))
766  {
767  my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0),
768  parameter->name.str, line);
769  DBUG_RETURN(TRUE);
770  }
771  break;
772  }
773  case FILE_OPTIONS_ULONGLONG:
774  if (!(eol= strchr(ptr, '\n')))
775  {
776  my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0),
777  parameter->name.str, line);
778  DBUG_RETURN(TRUE);
779  }
780  {
781  int not_used;
782  *((ulonglong*)(base + parameter->offset))=
783  my_strtoll10(ptr, 0, &not_used);
784  }
785  ptr= eol+1;
786  break;
787  case FILE_OPTIONS_TIMESTAMP:
788  {
789  /* string have to be allocated already */
790  LEX_STRING *val= (LEX_STRING *)(base + parameter->offset);
791  /* yyyy-mm-dd HH:MM:SS = 19(PARSE_FILE_TIMESTAMPLENGTH) characters */
792  if (ptr[PARSE_FILE_TIMESTAMPLENGTH] != '\n')
793  {
794  my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0),
795  parameter->name.str, line);
796  DBUG_RETURN(TRUE);
797  }
798  memcpy(val->str, ptr, PARSE_FILE_TIMESTAMPLENGTH);
799  val->str[val->length= PARSE_FILE_TIMESTAMPLENGTH]= '\0';
800  ptr+= (PARSE_FILE_TIMESTAMPLENGTH+1);
801  break;
802  }
803  case FILE_OPTIONS_STRLIST:
804  {
805  list= (List<LEX_STRING>*)(base + parameter->offset);
806 
807  list->empty();
808  // list parsing
809  while (ptr < end)
810  {
811  if (!(str= (LEX_STRING*)alloc_root(mem_root,
812  sizeof(LEX_STRING))) ||
813  list->push_back(str, mem_root))
814  goto list_err;
815  if (!(ptr= parse_quoted_escaped_string(ptr, end, mem_root, str)))
816  goto list_err_w_message;
817  switch (*ptr) {
818  case '\n':
819  goto end_of_list;
820  case ' ':
821  // we cant go over buffer bounds, because we have \0 at the end
822  ptr++;
823  break;
824  default:
825  goto list_err_w_message;
826  }
827  }
828 
829 end_of_list:
830  if (*(ptr++) != '\n')
831  goto list_err;
832  break;
833 
834 list_err_w_message:
835  my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0),
836  parameter->name.str, line);
837 list_err:
838  DBUG_RETURN(TRUE);
839  }
840  case FILE_OPTIONS_ULLLIST:
841  if (get_file_options_ulllist(ptr, end, line, base,
842  parameter, mem_root))
843  DBUG_RETURN(TRUE);
844  break;
845  default:
846  DBUG_ASSERT(0); // never should happened
847  }
848  }
849  else
850  {
851  ptr= line;
852  if (hook->process_unknown_string(ptr, base, mem_root, end))
853  {
854  DBUG_RETURN(TRUE);
855  }
856  // skip unknown parameter
857  if (!(ptr= strchr(ptr, '\n')))
858  {
859  my_error(ER_FPARSER_EOF_IN_UNKNOWN_PARAMETER, MYF(0), line);
860  DBUG_RETURN(TRUE);
861  }
862  ptr++;
863  }
864  }
865  }
866 
867  /*
868  NOTE: if we read less than "required" parameters, it is still Ok.
869  Probably, we've just read the file of the previous version, which
870  contains less parameters.
871  */
872 
873  DBUG_RETURN(FALSE);
874 }
875 
876 
899 bool
901  uchar* base, MEM_ROOT *mem_root,
902  const char *end)
903 {
904  DBUG_ENTER("file_parser_dummy_hook::process_unknown_string");
905  DBUG_PRINT("info", ("Unknown key: '%60s'", unknown_key));
906  DBUG_RETURN(FALSE);
907 }