mirror of
				https://github.com/MariaDB/server.git
				synced 2025-10-24 07:13:33 +03:00 
			
		
		
		
	into vajra.(none):/opt/local/work/mysql-5.1-runtime sql/parse_file.cc: Auto merged sql/sp_head.cc: Auto merged
		
			
				
	
	
		
			973 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			973 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* Copyright (C) 2004 MySQL AB
 | |
| 
 | |
|    This program is free software; you can redistribute it and/or modify
 | |
|    it under the terms of the GNU General Public License as published by
 | |
|    the Free Software Foundation; version 2 of the License.
 | |
| 
 | |
|    This program is distributed in the hope that it will be useful,
 | |
|    but WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | |
|    GNU General Public License for more details.
 | |
| 
 | |
|    You should have received a copy of the GNU General Public License
 | |
|    along with this program; if not, write to the Free Software
 | |
|    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
 | |
| 
 | |
| // Text .frm files management routines
 | |
| 
 | |
| #include "mysql_priv.h"
 | |
| #include <errno.h>
 | |
| #include <m_ctype.h>
 | |
| #include <my_sys.h>
 | |
| #include <my_dir.h>
 | |
| 
 | |
| 
 | |
| /*
 | |
|   write string with escaping
 | |
| 
 | |
|   SYNOPSIS
 | |
|     write_escaped_string()
 | |
|     file	- IO_CACHE for record
 | |
|     val_s	- string for writing
 | |
| 
 | |
|   RETURN
 | |
|     FALSE - OK
 | |
|     TRUE  - error
 | |
| */
 | |
| 
 | |
| static my_bool
 | |
| write_escaped_string(IO_CACHE *file, LEX_STRING *val_s)
 | |
| {
 | |
|   char *eos= val_s->str + val_s->length;
 | |
|   char *ptr= val_s->str;
 | |
| 
 | |
|   for (; ptr < eos; ptr++)
 | |
|   {
 | |
|     /*
 | |
|       Should be in sync with read_escaped_string() and
 | |
|       parse_quoted_escaped_string()
 | |
|     */
 | |
|     switch(*ptr) {
 | |
|     case '\\': // escape character
 | |
|       if (my_b_append(file, (const byte *)STRING_WITH_LEN("\\\\")))
 | |
| 	return TRUE;
 | |
|       break;
 | |
|     case '\n': // parameter value delimiter
 | |
|       if (my_b_append(file, (const byte *)STRING_WITH_LEN("\\n")))
 | |
| 	return TRUE;
 | |
|       break;
 | |
|     case '\0': // problem for some string processing utilities
 | |
|       if (my_b_append(file, (const byte *)STRING_WITH_LEN("\\0")))
 | |
| 	return TRUE;
 | |
|       break;
 | |
|     case 26: // problem for windows utilities (Ctrl-Z)
 | |
|       if (my_b_append(file, (const byte *)STRING_WITH_LEN("\\z")))
 | |
| 	return TRUE;
 | |
|       break;
 | |
|     case '\'': // list of string delimiter
 | |
|       if (my_b_append(file, (const byte *)STRING_WITH_LEN("\\\'")))
 | |
| 	return TRUE;
 | |
|       break;
 | |
|     default:
 | |
|       if (my_b_append(file, (const byte *)ptr, 1))
 | |
| 	return TRUE;
 | |
|     }
 | |
|   }
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   write parameter value to IO_CACHE
 | |
| 
 | |
|   SYNOPSIS
 | |
|     write_parameter()
 | |
|     file	pointer to IO_CACHE structure for writing
 | |
|     base	pointer to data structure
 | |
|     parameter	pointer to parameter descriptor
 | |
|     old_version	for returning back old version number value
 | |
| 
 | |
|   RETURN
 | |
|     FALSE - OK
 | |
|     TRUE  - error
 | |
| */
 | |
| 
 | |
| static my_bool
 | |
| write_parameter(IO_CACHE *file, gptr base, File_option *parameter,
 | |
| 		ulonglong *old_version)
 | |
| {
 | |
|   char num_buf[20];			// buffer for numeric operations
 | |
|   // string for numeric operations
 | |
|   String num(num_buf, sizeof(num_buf), &my_charset_bin);
 | |
|   DBUG_ENTER("write_parameter");
 | |
| 
 | |
|   switch (parameter->type) {
 | |
|   case FILE_OPTIONS_STRING:
 | |
|   {
 | |
|     LEX_STRING *val_s= (LEX_STRING *)(base + parameter->offset);
 | |
|     if (my_b_append(file, (const byte *)val_s->str, val_s->length))
 | |
|       DBUG_RETURN(TRUE);
 | |
|     break;
 | |
|   }
 | |
|   case FILE_OPTIONS_ESTRING:
 | |
|   {
 | |
|     if (write_escaped_string(file, (LEX_STRING *)(base + parameter->offset)))
 | |
|       DBUG_RETURN(TRUE);
 | |
|     break;
 | |
|   }
 | |
|   case FILE_OPTIONS_ULONGLONG:
 | |
|   {
 | |
|     num.set(*((ulonglong *)(base + parameter->offset)), &my_charset_bin);
 | |
|     if (my_b_append(file, (const byte *)num.ptr(), num.length()))
 | |
|       DBUG_RETURN(TRUE);
 | |
|     break;
 | |
|   }
 | |
|   case FILE_OPTIONS_REV:
 | |
|   {
 | |
|     ulonglong *val_i= (ulonglong *)(base + parameter->offset);
 | |
|     *old_version= (*val_i)++;
 | |
|     num.set(*val_i, &my_charset_bin);
 | |
|     if (my_b_append(file, (const byte *)num.ptr(), num.length()))
 | |
|       DBUG_RETURN(TRUE);
 | |
|     break;
 | |
|   }
 | |
|   case FILE_OPTIONS_TIMESTAMP:
 | |
|   {
 | |
|     /* string have to be allocated already */
 | |
|     LEX_STRING *val_s= (LEX_STRING *)(base + parameter->offset);
 | |
|     time_t tm= time(NULL);
 | |
| 
 | |
|     get_date(val_s->str, GETDATE_DATE_TIME|GETDATE_GMT|GETDATE_FIXEDLENGTH,
 | |
| 	     tm);
 | |
|     val_s->length= PARSE_FILE_TIMESTAMPLENGTH;
 | |
|     if (my_b_append(file, (const byte *)val_s->str,
 | |
|                     PARSE_FILE_TIMESTAMPLENGTH))
 | |
|       DBUG_RETURN(TRUE);
 | |
|     break;
 | |
|   }
 | |
|   case FILE_OPTIONS_STRLIST:
 | |
|   {
 | |
|     List_iterator_fast<LEX_STRING> it(*((List<LEX_STRING>*)
 | |
| 					(base + parameter->offset)));
 | |
|     bool first= 1;
 | |
|     LEX_STRING *str;
 | |
|     while ((str= it++))
 | |
|     {
 | |
|       // We need ' ' after string to detect list continuation
 | |
|       if ((!first && my_b_append(file, (const byte *)STRING_WITH_LEN(" "))) ||
 | |
| 	  my_b_append(file, (const byte *)STRING_WITH_LEN("\'")) ||
 | |
|           write_escaped_string(file, str) ||
 | |
| 	  my_b_append(file, (const byte *)STRING_WITH_LEN("\'")))
 | |
|       {
 | |
| 	DBUG_RETURN(TRUE);
 | |
|       }
 | |
|       first= 0;
 | |
|     }
 | |
|     break;
 | |
|   }
 | |
|   case FILE_OPTIONS_ULLLIST:
 | |
|   {
 | |
|     List_iterator_fast<ulonglong> it(*((List<ulonglong>*)
 | |
|                                        (base + parameter->offset)));
 | |
|     bool first= 1;
 | |
|     ulonglong *val;
 | |
|     while ((val= it++))
 | |
|     {
 | |
|       num.set(*val, &my_charset_bin);
 | |
|       // We need ' ' after string to detect list continuation
 | |
|       if ((!first && my_b_append(file, (const byte *)STRING_WITH_LEN(" "))) ||
 | |
|           my_b_append(file, (const byte *)num.ptr(), num.length()))
 | |
|       {
 | |
|         DBUG_RETURN(TRUE);
 | |
|       }
 | |
|       first= 0;
 | |
|     }
 | |
|     break;
 | |
|   }
 | |
|   default:
 | |
|     DBUG_ASSERT(0); // never should happened
 | |
|   }
 | |
|   DBUG_RETURN(FALSE);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   write new .frm
 | |
| 
 | |
|   SYNOPSIS
 | |
|     sql_create_definition_file()
 | |
|     dir			directory where put .frm
 | |
|     file		.frm file name
 | |
|     type		.frm type string (VIEW, TABLE)
 | |
|     base		base address for parameter reading (structure like
 | |
| 			TABLE)
 | |
|     parameters		parameters description
 | |
|     max_versions	number of versions to save
 | |
| 
 | |
|   RETURN
 | |
|     FALSE - OK
 | |
|     TRUE - error
 | |
| */
 | |
| 
 | |
| my_bool
 | |
| sql_create_definition_file(const LEX_STRING *dir, const LEX_STRING *file_name,
 | |
| 			   const LEX_STRING *type,
 | |
| 			   gptr base, File_option *parameters,
 | |
| 			   uint max_versions)
 | |
| {
 | |
|   File handler;
 | |
|   IO_CACHE file;
 | |
|   char path[FN_REFLEN+1];	// +1 to put temporary file name for sure
 | |
|   ulonglong old_version= ULONGLONG_MAX;
 | |
|   int path_end;
 | |
|   File_option *param;
 | |
|   DBUG_ENTER("sql_create_definition_file");
 | |
|   DBUG_PRINT("enter", ("Dir: %s, file: %s, base 0x%lx",
 | |
| 		       dir ? dir->str : "(null)",
 | |
|                        file_name->str, (ulong) base));
 | |
| 
 | |
|   if (dir)
 | |
|   {
 | |
|     fn_format(path, file_name->str, dir->str, "", MY_UNPACK_FILENAME);
 | |
|     path_end= strlen(path);
 | |
|   }
 | |
|   else
 | |
|   {
 | |
|     /*
 | |
|       if not dir is passed, it means file_name is a full path,
 | |
|       including dir name, file name itself, and an extension,
 | |
|       and with unpack_filename() executed over it.
 | |
|     */    
 | |
|     path_end= strxnmov(path, FN_REFLEN, file_name->str, NullS) - path;
 | |
|   }
 | |
| 
 | |
|   // temporary file name
 | |
|   path[path_end]='~';
 | |
|   path[path_end+1]= '\0';
 | |
|   if ((handler= my_create(path, CREATE_MODE, O_RDWR | O_TRUNC,
 | |
| 			  MYF(MY_WME))) <= 0)
 | |
|   {
 | |
|     DBUG_RETURN(TRUE);
 | |
|   }
 | |
| 
 | |
|   if (init_io_cache(&file, handler, 0, SEQ_READ_APPEND, 0L, 0, MYF(MY_WME)))
 | |
|     goto err_w_file;
 | |
| 
 | |
|   // write header (file signature)
 | |
|   if (my_b_append(&file, (const byte *)STRING_WITH_LEN("TYPE=")) ||
 | |
|       my_b_append(&file, (const byte *)type->str, type->length) ||
 | |
|       my_b_append(&file, (const byte *)STRING_WITH_LEN("\n")))
 | |
|     goto err_w_file;
 | |
| 
 | |
|   // write parameters to temporary file
 | |
|   for (param= parameters; param->name.str; param++)
 | |
|   {
 | |
|     if (my_b_append(&file, (const byte *)param->name.str,
 | |
|                     param->name.length) ||
 | |
| 	my_b_append(&file, (const byte *)STRING_WITH_LEN("=")) ||
 | |
| 	write_parameter(&file, base, param, &old_version) ||
 | |
| 	my_b_append(&file, (const byte *)STRING_WITH_LEN("\n")))
 | |
|       goto err_w_cache;
 | |
|   }
 | |
| 
 | |
|   if (end_io_cache(&file))
 | |
|     goto err_w_file;
 | |
| 
 | |
|   if (my_close(handler, MYF(MY_WME)))
 | |
|   {
 | |
|     DBUG_RETURN(TRUE);
 | |
|   }
 | |
| 
 | |
|   // archive copies management
 | |
|   path[path_end]='\0';
 | |
|   if (!access(path, F_OK))
 | |
|   {
 | |
|     if (old_version != ULONGLONG_MAX && max_versions != 0)
 | |
|     {
 | |
|       // save backup
 | |
|       char path_arc[FN_REFLEN];
 | |
|       // backup old version
 | |
|       char path_to[FN_REFLEN];
 | |
| 
 | |
|       // check archive directory existence
 | |
|       fn_format(path_arc, "arc", dir->str, "", MY_UNPACK_FILENAME);
 | |
|       if (access(path_arc, F_OK))
 | |
|       {
 | |
| 	if (my_mkdir(path_arc, 0777, MYF(MY_WME)))
 | |
| 	{
 | |
| 	  DBUG_RETURN(TRUE);
 | |
| 	}
 | |
|       }
 | |
| 
 | |
|       my_snprintf(path_to, FN_REFLEN, "%s/%s-%04lu",
 | |
| 		  path_arc, file_name->str, (ulong) old_version);
 | |
|       if (my_rename(path, path_to, MYF(MY_WME)))
 | |
|       {
 | |
| 	DBUG_RETURN(TRUE);
 | |
|       }
 | |
| 
 | |
|       // remove very old version
 | |
|       if (old_version > max_versions)
 | |
|       {
 | |
| 	my_snprintf(path_to, FN_REFLEN, "%s/%s-%04lu",
 | |
| 		    path_arc, file_name->str,
 | |
| 		    (ulong)(old_version - max_versions));
 | |
| 	if (!access(path_arc, F_OK) && my_delete(path_to, MYF(MY_WME)))
 | |
| 	{
 | |
| 	  DBUG_RETURN(TRUE);
 | |
| 	}
 | |
|       }
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|       if (my_delete(path, MYF(MY_WME)))	// no backups
 | |
|       {
 | |
| 	DBUG_RETURN(TRUE);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   {
 | |
|     // rename temporary file
 | |
|     char path_to[FN_REFLEN];
 | |
|     memcpy(path_to, path, path_end+1);
 | |
|     path[path_end]='~';
 | |
|     if (my_rename(path, path_to, MYF(MY_WME)))
 | |
|     {
 | |
|       DBUG_RETURN(TRUE);
 | |
|     }
 | |
|   }
 | |
|   DBUG_RETURN(FALSE);
 | |
| err_w_cache:
 | |
|   end_io_cache(&file);
 | |
| err_w_file:
 | |
|   my_close(handler, MYF(MY_WME));
 | |
|   DBUG_RETURN(TRUE);
 | |
| }
 | |
| 
 | |
| /*
 | |
|   Renames a frm file (including backups) in same schema
 | |
| 
 | |
|   SYNOPSIS
 | |
|     rename_in_schema_file
 | |
|     schema            name of given schema           
 | |
|     old_name          original file name
 | |
|     new_name          new file name
 | |
|     revision          revision number
 | |
|     num_view_backups  number of backups
 | |
| 
 | |
|   RETURN
 | |
|     0 - OK 
 | |
|     1 - Error (only if renaming of frm failed)
 | |
| 
 | |
| */
 | |
| my_bool rename_in_schema_file(const char *schema, const char *old_name, 
 | |
|                               const char *new_name, ulonglong revision, 
 | |
|                               uint num_view_backups)
 | |
| {
 | |
|   char old_path[FN_REFLEN], new_path[FN_REFLEN], arc_path[FN_REFLEN];
 | |
| 
 | |
|   build_table_filename(old_path, sizeof(old_path) - 1,
 | |
|                        schema, old_name, reg_ext, 0);
 | |
|   build_table_filename(new_path, sizeof(new_path) - 1,
 | |
|                        schema, new_name, reg_ext, 0);
 | |
| 
 | |
|   if (my_rename(old_path, new_path, MYF(MY_WME)))
 | |
|     return 1;
 | |
| 
 | |
|   /* check if arc_dir exists */
 | |
|   build_table_filename(arc_path, sizeof(arc_path) - 1, schema, "arc", "", 0);
 | |
|   
 | |
|   if (revision > 0 && !access(arc_path, F_OK))
 | |
|   {
 | |
|     char old_name_buf[FN_REFLEN], new_name_buf[FN_REFLEN];
 | |
|     ulonglong limit= ((revision > num_view_backups) ?
 | |
|                       revision - num_view_backups : 0);
 | |
| 
 | |
|     VOID(tablename_to_filename(old_name, old_name_buf, sizeof(old_name_buf)));
 | |
|     VOID(tablename_to_filename(new_name, new_name_buf, sizeof(new_name_buf)));
 | |
| 
 | |
|     for (; revision > limit ; revision--)
 | |
|     {
 | |
|       my_snprintf(old_path, FN_REFLEN, "%s/%s%s-%04lu",
 | |
| 		  arc_path, old_name_buf, reg_ext, (ulong) revision);
 | |
|       (void) unpack_filename(old_path, old_path);
 | |
|       my_snprintf(new_path, FN_REFLEN, "%s/%s%s-%04lu",
 | |
| 		  arc_path, new_name_buf, reg_ext, (ulong) revision);
 | |
|       (void) unpack_filename(new_path, new_path);
 | |
|       my_rename(old_path, new_path, MYF(0));
 | |
|     }
 | |
|   }
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|   Prepare frm to parse (read to memory)
 | |
| 
 | |
|   SYNOPSIS
 | |
|     sql_parse_prepare()
 | |
|     file_name		- path & filename to .frm file
 | |
|     mem_root		- MEM_ROOT for buffer allocation
 | |
|     bad_format_errors	- send errors on bad content
 | |
| 
 | |
|   RETURN
 | |
|     0 - error
 | |
|     parser object
 | |
| 
 | |
|   NOTE
 | |
|     returned pointer + 1 will be type of .frm
 | |
| */
 | |
| 
 | |
| File_parser * 
 | |
| sql_parse_prepare(const LEX_STRING *file_name, MEM_ROOT *mem_root,
 | |
| 		  bool bad_format_errors)
 | |
| {
 | |
|   MY_STAT stat_info;
 | |
|   uint len;
 | |
|   char *end, *sign;
 | |
|   File_parser *parser;
 | |
|   File file;
 | |
|   DBUG_ENTER("sql_parse_prepare");
 | |
| 
 | |
|   if (!my_stat(file_name->str, &stat_info, MYF(MY_WME)))
 | |
|   {
 | |
|     DBUG_RETURN(0);
 | |
|   }
 | |
| 
 | |
|   if (stat_info.st_size > INT_MAX-1)
 | |
|   {
 | |
|     my_error(ER_FPARSER_TOO_BIG_FILE, MYF(0), file_name->str);
 | |
|     DBUG_RETURN(0);
 | |
|   }
 | |
| 
 | |
|   if (!(parser= new(mem_root) File_parser))
 | |
|   {
 | |
|     DBUG_RETURN(0);
 | |
|   }
 | |
| 
 | |
|   if (!(parser->buff= alloc_root(mem_root, stat_info.st_size+1)))
 | |
|   {
 | |
|     DBUG_RETURN(0);
 | |
|   }
 | |
| 
 | |
|   if ((file= my_open(file_name->str, O_RDONLY | O_SHARE, MYF(MY_WME))) < 0)
 | |
|   {
 | |
|     DBUG_RETURN(0);
 | |
|   }
 | |
|   
 | |
|   if ((len= my_read(file, (byte *)parser->buff,
 | |
|                     stat_info.st_size, MYF(MY_WME))) ==
 | |
|       MY_FILE_ERROR)
 | |
|   {
 | |
|     my_close(file, MYF(MY_WME));
 | |
|     DBUG_RETURN(0);
 | |
|   }
 | |
| 
 | |
|   if (my_close(file, MYF(MY_WME)))
 | |
|   {
 | |
|     DBUG_RETURN(0);
 | |
|   }
 | |
| 
 | |
|   end= parser->end= parser->buff + len;
 | |
|   *end= '\0'; // barrier for more simple parsing
 | |
| 
 | |
|   // 7 = 5 (TYPE=) + 1 (letter at least of type name) + 1 ('\n')
 | |
|   if (len < 7 ||
 | |
|       parser->buff[0] != 'T' ||
 | |
|       parser->buff[1] != 'Y' ||
 | |
|       parser->buff[2] != 'P' ||
 | |
|       parser->buff[3] != 'E' ||
 | |
|       parser->buff[4] != '=')
 | |
|     goto frm_error;
 | |
| 
 | |
|   // skip signature;
 | |
|   parser->file_type.str= sign= parser->buff + 5;
 | |
|   while (*sign >= 'A' && *sign <= 'Z' && sign < end)
 | |
|     sign++;
 | |
|   if (*sign != '\n')
 | |
|     goto frm_error;
 | |
|   parser->file_type.length= sign - parser->file_type.str;
 | |
|   // EOS for file signature just for safety
 | |
|   *sign= '\0';
 | |
| 
 | |
|   parser->start= sign + 1;
 | |
|   parser->content_ok= 1;
 | |
| 
 | |
|   DBUG_RETURN(parser);
 | |
| 
 | |
| frm_error:
 | |
|   if (bad_format_errors)
 | |
|   {
 | |
|     my_error(ER_FPARSER_BAD_HEADER, MYF(0), file_name->str);
 | |
|     DBUG_RETURN(0);
 | |
|   }
 | |
|   else
 | |
|     DBUG_RETURN(parser); // upper level have to check parser->ok()
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   parse LEX_STRING
 | |
| 
 | |
|   SYNOPSIS
 | |
|     parse_string()
 | |
|     ptr		- pointer on string beginning
 | |
|     end		- pointer on symbol after parsed string end (still owned
 | |
| 		  by buffer and can be accessed
 | |
|     mem_root	- MEM_ROOT for parameter allocation
 | |
|     str		- pointer on string, where results should be stored
 | |
| 
 | |
|   RETURN
 | |
|     0	- error
 | |
|     #	- pointer on symbol after string
 | |
| */
 | |
| 
 | |
| static char *
 | |
| parse_string(char *ptr, char *end, MEM_ROOT *mem_root, LEX_STRING *str)
 | |
| {
 | |
|   // get string length
 | |
|   char *eol= strchr(ptr, '\n');
 | |
| 
 | |
|   if (eol >= end)
 | |
|     return 0;
 | |
| 
 | |
|   str->length= eol - ptr;
 | |
| 
 | |
|   if (!(str->str= alloc_root(mem_root, str->length+1)))
 | |
|     return 0;
 | |
| 
 | |
|   memcpy(str->str, ptr, str->length);
 | |
|   str->str[str->length]= '\0'; // just for safety
 | |
|   return eol+1;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   read escaped string from ptr to eol in already allocated str
 | |
| 
 | |
|   SYNOPSIS
 | |
|     read_escaped_string()
 | |
|     ptr		- pointer on string beginning
 | |
|     eol		- pointer on character after end of string
 | |
|     str		- target string
 | |
| 
 | |
|   RETURN
 | |
|     FALSE - OK
 | |
|     TRUE  - error
 | |
| */
 | |
| 
 | |
| my_bool
 | |
| read_escaped_string(char *ptr, char *eol, LEX_STRING *str)
 | |
| {
 | |
|   char *write_pos= str->str;
 | |
| 
 | |
|   for (; ptr < eol; ptr++, write_pos++)
 | |
|   {
 | |
|     char c= *ptr;
 | |
|     if (c == '\\')
 | |
|     {
 | |
|       ptr++;
 | |
|       if (ptr >= eol)
 | |
| 	return TRUE;
 | |
|       /*
 | |
| 	Should be in sync with write_escaped_string() and
 | |
| 	parse_quoted_escaped_string()
 | |
|       */
 | |
|       switch(*ptr) {
 | |
|       case '\\':
 | |
| 	*write_pos= '\\';
 | |
| 	break;
 | |
|       case 'n':
 | |
| 	*write_pos= '\n';
 | |
| 	break;
 | |
|       case '0':
 | |
| 	*write_pos= '\0';
 | |
| 	break;
 | |
|       case 'z':
 | |
| 	*write_pos= 26;
 | |
| 	break;
 | |
|       case '\'':
 | |
| 	*write_pos= '\'';
 | |
|         break;
 | |
|       default:
 | |
| 	return TRUE;
 | |
|       }
 | |
|     }
 | |
|     else
 | |
|       *write_pos= c;
 | |
|   }
 | |
|   str->str[str->length= write_pos-str->str]= '\0'; // just for safety
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   parse \n delimited escaped string
 | |
| 
 | |
|   SYNOPSIS
 | |
|     parse_escaped_string()
 | |
|     ptr		- pointer on string beginning
 | |
|     end		- pointer on symbol after parsed string end (still owned
 | |
| 		  by buffer and can be accessed
 | |
|     mem_root	- MEM_ROOT for parameter allocation
 | |
|     str		- pointer on string, where results should be stored
 | |
| 
 | |
|   RETURN
 | |
|     0	- error
 | |
|     #	- pointer on symbol after string
 | |
| */
 | |
| 
 | |
| char *
 | |
| parse_escaped_string(char *ptr, char *end, MEM_ROOT *mem_root, LEX_STRING *str)
 | |
| {
 | |
|   char *eol= strchr(ptr, '\n');
 | |
| 
 | |
|   if (eol == 0 || eol >= end ||
 | |
|       !(str->str= alloc_root(mem_root, (eol - ptr) + 1)) ||
 | |
|       read_escaped_string(ptr, eol, str))
 | |
|     return 0;
 | |
|     
 | |
|   return eol+1;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   parse '' delimited escaped string
 | |
| 
 | |
|   SYNOPSIS
 | |
|     parse_quoted_escaped_string()
 | |
|     ptr		- pointer on string beginning
 | |
|     end		- pointer on symbol after parsed string end (still owned
 | |
| 		  by buffer and can be accessed
 | |
|     mem_root	- MEM_ROOT for parameter allocation
 | |
|     str		- pointer on string, where results should be stored
 | |
| 
 | |
|   RETURN
 | |
|     0	- error
 | |
|     #	- pointer on symbol after string
 | |
| */
 | |
| 
 | |
| static char *
 | |
| parse_quoted_escaped_string(char *ptr, char *end,
 | |
| 			    MEM_ROOT *mem_root, LEX_STRING *str)
 | |
| {
 | |
|   char *eol;
 | |
|   uint result_len= 0;
 | |
|   bool escaped= 0;
 | |
| 
 | |
|   // starting '
 | |
|   if (*(ptr++) != '\'')
 | |
|     return 0;
 | |
| 
 | |
|   // find ending '
 | |
|   for (eol= ptr; (*eol != '\'' || escaped) && eol < end; eol++)
 | |
|   {
 | |
|     if (!(escaped= (*eol == '\\' && !escaped)))
 | |
|       result_len++;
 | |
|   }
 | |
| 
 | |
|   // process string
 | |
|   if (eol >= end ||
 | |
|       !(str->str= alloc_root(mem_root, result_len + 1)) ||
 | |
|       read_escaped_string(ptr, eol, str))
 | |
|     return 0;
 | |
| 
 | |
|   return eol+1;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Parser for FILE_OPTIONS_ULLLIST type value.
 | |
| 
 | |
|   SYNOPSIS
 | |
|     get_file_options_ulllist()
 | |
|     ptr                  [in/out] pointer to parameter
 | |
|     end                  [in] end of the configuration
 | |
|     line                 [in] pointer to the line begining
 | |
|     base                 [in] base address for parameter writing (structure
 | |
|                               like TABLE)
 | |
|     parameter            [in] description
 | |
|     mem_root             [in] MEM_ROOT for parameters allocation
 | |
| */
 | |
| 
 | |
| bool get_file_options_ulllist(char *&ptr, char *end, char *line,
 | |
|                               gptr base, File_option *parameter,
 | |
|                               MEM_ROOT *mem_root)
 | |
| {
 | |
|   List<ulonglong> *nlist= (List<ulonglong>*)(base + parameter->offset);
 | |
|   ulonglong *num;
 | |
|   nlist->empty();
 | |
|   // list parsing
 | |
|   while (ptr < end)
 | |
|   {
 | |
|     int not_used;
 | |
|     char *num_end= end;
 | |
|     if (!(num= (ulonglong*)alloc_root(mem_root, sizeof(ulonglong))) ||
 | |
|         nlist->push_back(num, mem_root))
 | |
|       goto nlist_err;
 | |
|     *num= my_strtoll10(ptr, &num_end, ¬_used);
 | |
|     ptr= num_end;
 | |
|     switch (*ptr) {
 | |
|     case '\n':
 | |
|       goto end_of_nlist;
 | |
|     case ' ':
 | |
|       // we cant go over buffer bounds, because we have \0 at the end
 | |
|       ptr++;
 | |
|       break;
 | |
|     default:
 | |
|       goto nlist_err_w_message;
 | |
|     }
 | |
|   }
 | |
| 
 | |
| end_of_nlist:
 | |
|   if (*(ptr++) != '\n')
 | |
|     goto nlist_err;
 | |
|   return FALSE;
 | |
| 
 | |
| nlist_err_w_message:
 | |
|   my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0), parameter->name.str, line);
 | |
| nlist_err:
 | |
|   return TRUE;
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   parse parameters
 | |
| 
 | |
|   SYNOPSIS
 | |
|     File_parser::parse()
 | |
|     base                base address for parameter writing (structure like
 | |
|                         TABLE)
 | |
|     mem_root            MEM_ROOT for parameters allocation
 | |
|     parameters          parameters description
 | |
|     required            number of parameters in the above list. If the file
 | |
|                         contains more parameters than "required", they will
 | |
|                         be ignored. If the file contains less parameters
 | |
|                         then "required", non-existing parameters will
 | |
|                         remain their values.
 | |
|     hook                hook called for unknown keys
 | |
|     hook_data           some data specific for the hook
 | |
| 
 | |
|   RETURN
 | |
|     FALSE - OK
 | |
|     TRUE - error
 | |
| */
 | |
| 
 | |
| my_bool
 | |
| File_parser::parse(gptr base, MEM_ROOT *mem_root,
 | |
|                    struct File_option *parameters, uint required,
 | |
|                    Unknown_key_hook *hook)
 | |
| {
 | |
|   uint first_param= 0, found= 0;
 | |
|   char *ptr= start;
 | |
|   char *eol;
 | |
|   LEX_STRING *str;
 | |
|   List<LEX_STRING> *list;
 | |
|   DBUG_ENTER("File_parser::parse");
 | |
| 
 | |
|   while (ptr < end && found < required)
 | |
|   {
 | |
|     char *line= ptr;
 | |
|     if (*ptr == '#')
 | |
|     {
 | |
|       // it is comment
 | |
|       if (!(ptr= strchr(ptr, '\n')))
 | |
|       {
 | |
| 	my_error(ER_FPARSER_EOF_IN_COMMENT, MYF(0), line);
 | |
| 	DBUG_RETURN(TRUE);
 | |
|       }
 | |
|       ptr++;
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|       File_option *parameter= parameters+first_param,
 | |
| 	*parameters_end= parameters+required;
 | |
|       int len= 0;
 | |
|       for (; parameter < parameters_end; parameter++)
 | |
|       {
 | |
| 	len= parameter->name.length;
 | |
| 	// check length
 | |
| 	if (len < (end-ptr) && ptr[len] != '=')
 | |
| 	  continue;
 | |
| 	// check keyword
 | |
| 	if (memcmp(parameter->name.str, ptr, len) == 0)
 | |
| 	  break;
 | |
|       }
 | |
| 
 | |
|       if (parameter < parameters_end)
 | |
|       {
 | |
| 	found++;
 | |
| 	/*
 | |
| 	  if we found first parameter, start search from next parameter
 | |
| 	  next time.
 | |
| 	  (this small optimisation should work, because they should be
 | |
| 	  written in same order)
 | |
| 	*/
 | |
| 	if (parameter == parameters+first_param)
 | |
| 	  first_param++;
 | |
| 
 | |
| 	// get value
 | |
| 	ptr+= (len+1);
 | |
| 	switch (parameter->type) {
 | |
| 	case FILE_OPTIONS_STRING:
 | |
| 	{
 | |
| 	  if (!(ptr= parse_string(ptr, end, mem_root,
 | |
| 				  (LEX_STRING *)(base +
 | |
| 						 parameter->offset))))
 | |
| 	  {
 | |
| 	    my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0),
 | |
|                      parameter->name.str, line);
 | |
| 	    DBUG_RETURN(TRUE);
 | |
| 	  }
 | |
| 	  break;
 | |
| 	}
 | |
| 	case FILE_OPTIONS_ESTRING:
 | |
| 	{
 | |
| 	  if (!(ptr= parse_escaped_string(ptr, end, mem_root,
 | |
| 					  (LEX_STRING *)
 | |
| 					  (base + parameter->offset))))
 | |
| 	  {
 | |
| 	    my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0),
 | |
|                      parameter->name.str, line);
 | |
| 	    DBUG_RETURN(TRUE);
 | |
| 	  }
 | |
| 	  break;
 | |
| 	}
 | |
| 	case FILE_OPTIONS_ULONGLONG:
 | |
| 	case FILE_OPTIONS_REV:
 | |
| 	  if (!(eol= strchr(ptr, '\n')))
 | |
| 	  {
 | |
| 	    my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0),
 | |
|                      parameter->name.str, line);
 | |
| 	    DBUG_RETURN(TRUE);
 | |
| 	  }
 | |
|           {
 | |
|             int not_used;
 | |
| 	    *((ulonglong*)(base + parameter->offset))=
 | |
|               my_strtoll10(ptr, 0, ¬_used);
 | |
|           }
 | |
| 	  ptr= eol+1;
 | |
| 	  break;
 | |
| 	case FILE_OPTIONS_TIMESTAMP:
 | |
| 	{
 | |
| 	  /* string have to be allocated already */
 | |
| 	  LEX_STRING *val= (LEX_STRING *)(base + parameter->offset);
 | |
| 	  /* yyyy-mm-dd HH:MM:SS = 19(PARSE_FILE_TIMESTAMPLENGTH) characters */
 | |
| 	  if (ptr[PARSE_FILE_TIMESTAMPLENGTH] != '\n')
 | |
| 	  {
 | |
| 	    my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0),
 | |
|                      parameter->name.str, line);
 | |
| 	    DBUG_RETURN(TRUE);
 | |
| 	  }
 | |
| 	  memcpy(val->str, ptr, PARSE_FILE_TIMESTAMPLENGTH);
 | |
| 	  val->str[val->length= PARSE_FILE_TIMESTAMPLENGTH]= '\0';
 | |
| 	  ptr+= (PARSE_FILE_TIMESTAMPLENGTH+1);
 | |
| 	  break;
 | |
| 	}
 | |
| 	case FILE_OPTIONS_STRLIST:
 | |
| 	{
 | |
|           list= (List<LEX_STRING>*)(base + parameter->offset);
 | |
| 
 | |
| 	  list->empty();
 | |
| 	  // list parsing
 | |
| 	  while (ptr < end)
 | |
| 	  {
 | |
| 	    if (!(str= (LEX_STRING*)alloc_root(mem_root,
 | |
| 					       sizeof(LEX_STRING))) ||
 | |
| 		list->push_back(str, mem_root))
 | |
| 	      goto list_err;
 | |
| 	    if (!(ptr= parse_quoted_escaped_string(ptr, end, mem_root, str)))
 | |
| 	      goto list_err_w_message;
 | |
| 	    switch (*ptr) {
 | |
| 	    case '\n':
 | |
| 	      goto end_of_list;
 | |
| 	    case ' ':
 | |
| 	      // we cant go over buffer bounds, because we have \0 at the end
 | |
| 	      ptr++;
 | |
| 	      break;
 | |
| 	    default:
 | |
| 	      goto list_err_w_message;
 | |
| 	    }
 | |
| 	  }
 | |
| 
 | |
| end_of_list:
 | |
| 	  if (*(ptr++) != '\n')
 | |
| 	    goto list_err;
 | |
| 	  break;
 | |
| 
 | |
| list_err_w_message:
 | |
| 	  my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0),
 | |
|                    parameter->name.str, line);
 | |
| list_err:
 | |
| 	  DBUG_RETURN(TRUE);
 | |
| 	}
 | |
|         case FILE_OPTIONS_ULLLIST:
 | |
|           if (get_file_options_ulllist(ptr, end, line, base,
 | |
|                                        parameter, mem_root))
 | |
|             DBUG_RETURN(TRUE);
 | |
|           break;
 | |
| 	default:
 | |
| 	  DBUG_ASSERT(0); // never should happened
 | |
| 	}
 | |
|       }
 | |
|       else
 | |
|       {
 | |
|         ptr= line;
 | |
|         if (hook->process_unknown_string(ptr, base, mem_root, end))
 | |
|         {
 | |
|           DBUG_RETURN(TRUE);
 | |
|         }
 | |
|         // skip unknown parameter
 | |
|         if (!(ptr= strchr(ptr, '\n')))
 | |
|         {
 | |
|           my_error(ER_FPARSER_EOF_IN_UNKNOWN_PARAMETER, MYF(0), line);
 | |
|           DBUG_RETURN(TRUE);
 | |
|         }
 | |
|         ptr++;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   /*
 | |
|     NOTE: if we read less than "required" parameters, it is still Ok.
 | |
|     Probably, we've just read the file of the previous version, which
 | |
|     contains less parameters.
 | |
|   */
 | |
| 
 | |
|   DBUG_RETURN(FALSE);
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|   Dummy unknown key hook
 | |
| 
 | |
|   SYNOPSIS
 | |
|     File_parser_dummy_hook::process_unknown_string()
 | |
|     unknown_key          [in/out] reference on the line with unknown
 | |
|                                   parameter and the parsing point
 | |
|     base                 [in] base address for parameter writing (structure like
 | |
|                               TABLE)
 | |
|     mem_root             [in] MEM_ROOT for parameters allocation
 | |
|     end                  [in] the end of the configuration
 | |
| 
 | |
|   NOTE
 | |
|     This hook used to catch no longer supported keys and process them for
 | |
|     backward compatibility, but it will not slow down processing of modern
 | |
|     format files.
 | |
|     This hook does nothing except debug output.
 | |
| 
 | |
|   RETURN
 | |
|     FALSE OK
 | |
|     TRUE  Error
 | |
| */
 | |
| 
 | |
| bool
 | |
| File_parser_dummy_hook::process_unknown_string(char *&unknown_key,
 | |
|                                                gptr base, MEM_ROOT *mem_root,
 | |
|                                                char *end)
 | |
| {
 | |
|   DBUG_ENTER("file_parser_dummy_hook::process_unknown_string");
 | |
|   DBUG_PRINT("info", ("Unknown key: '%60s'", unknown_key));
 | |
|   DBUG_RETURN(FALSE);
 | |
| }
 |