summaryrefslogtreecommitdiffstats
path: root/sql/parse_file.cc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:24:36 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:24:36 +0000
commit06eaf7232e9a920468c0f8d74dcf2fe8b555501c (patch)
treee2c7b5777f728320e5b5542b6213fd3591ba51e2 /sql/parse_file.cc
parentInitial commit. (diff)
downloadmariadb-06eaf7232e9a920468c0f8d74dcf2fe8b555501c.tar.xz
mariadb-06eaf7232e9a920468c0f8d74dcf2fe8b555501c.zip
Adding upstream version 1:10.11.6.upstream/1%10.11.6
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sql/parse_file.cc')
-rw-r--r--sql/parse_file.cc1004
1 files changed, 1004 insertions, 0 deletions
diff --git a/sql/parse_file.cc b/sql/parse_file.cc
new file mode 100644
index 00000000..f4aae130
--- /dev/null
+++ b/sql/parse_file.cc
@@ -0,0 +1,1004 @@
+/* Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
+
+ 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
+
+/**
+ @file
+
+ @brief
+ Text .frm files management routines
+*/
+
+#include "mariadb.h"
+#include "sql_priv.h"
+#include "parse_file.h"
+#include "unireg.h" // CREATE_MODE
+#include "sql_table.h" // build_table_filename
+#include "debug.h"
+#include <mysys_err.h> // EE_WRITE
+#include <m_ctype.h>
+#include <my_dir.h>
+
+/* from sql_db.cc */
+extern long mysql_rm_arc_files(THD *thd, MY_DIR *dirp, const char *org_path);
+
+
+/**
+ Write string with escaping.
+
+ @param file IO_CACHE for record
+ @param val_s string for writing
+
+ @retval
+ FALSE OK
+ @retval
+ 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_write(file, (const uchar *)STRING_WITH_LEN("\\\\")))
+ return TRUE;
+ break;
+ case '\n': // parameter value delimiter
+ if (my_b_write(file, (const uchar *)STRING_WITH_LEN("\\n")))
+ return TRUE;
+ break;
+ case '\0': // problem for some string processing utilities
+ if (my_b_write(file, (const uchar *)STRING_WITH_LEN("\\0")))
+ return TRUE;
+ break;
+ case 26: // problem for windows utilities (Ctrl-Z)
+ if (my_b_write(file, (const uchar *)STRING_WITH_LEN("\\z")))
+ return TRUE;
+ break;
+ case '\'': // list of string delimiter
+ if (my_b_write(file, (const uchar *)STRING_WITH_LEN("\\\'")))
+ return TRUE;
+ break;
+ default:
+ if (my_b_write(file, (const uchar *)ptr, 1))
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static ulonglong view_algo_to_frm(ulonglong val)
+{
+ switch(val)
+ {
+ case VIEW_ALGORITHM_UNDEFINED:
+ return VIEW_ALGORITHM_UNDEFINED_FRM;
+ case VIEW_ALGORITHM_MERGE:
+ return VIEW_ALGORITHM_MERGE_FRM;
+ case VIEW_ALGORITHM_TMPTABLE:
+ return VIEW_ALGORITHM_TMPTABLE_FRM;
+ }
+ DBUG_ASSERT(0); /* Should never happen */
+ return VIEW_ALGORITHM_UNDEFINED;
+}
+
+static ulonglong view_algo_from_frm(ulonglong val)
+{
+ switch(val)
+ {
+ case VIEW_ALGORITHM_UNDEFINED_FRM:
+ return VIEW_ALGORITHM_UNDEFINED;
+ case VIEW_ALGORITHM_MERGE_FRM:
+ return VIEW_ALGORITHM_MERGE;
+ case VIEW_ALGORITHM_TMPTABLE_FRM:
+ return VIEW_ALGORITHM_TMPTABLE;
+ }
+
+ /*
+ Early versions of MariaDB 5.2/5.3 had identical in-memory and frm values
+ Return input value.
+ */
+ return val;
+}
+
+
+/**
+ Write parameter value to IO_CACHE.
+
+ @param file pointer to IO_CACHE structure for writing
+ @param base pointer to data structure
+ @param parameter pointer to parameter descriptor
+
+ @retval
+ FALSE OK
+ @retval
+ TRUE error
+*/
+
+
+static my_bool
+write_parameter(IO_CACHE *file, const uchar* base, File_option *parameter)
+{
+ 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_write(file, (const uchar *)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:
+ case FILE_OPTIONS_VIEW_ALGO:
+ {
+ ulonglong val= *(ulonglong *)(base + parameter->offset);
+
+ if (parameter->type == FILE_OPTIONS_VIEW_ALGO)
+ val= view_algo_to_frm(val);
+
+ num.set(val, &my_charset_bin);
+ if (my_b_write(file, (const uchar *)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);
+ // number of microseconds since Epoch, timezone-independent
+ my_hrtime_t tm= my_hrtime();
+ // Paded to 19 characters for compatibility
+ val_s->length= snprintf(val_s->str, MICROSECOND_TIMESTAMP_BUFFER_SIZE,
+ "%019lld", tm.val);
+ DBUG_ASSERT(val_s->length == MICROSECOND_TIMESTAMP_BUFFER_SIZE-1);
+ if (my_b_write(file, (const uchar *)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_write(file, (const uchar *)STRING_WITH_LEN(" "))) ||
+ my_b_write(file, (const uchar *)STRING_WITH_LEN("\'")) ||
+ write_escaped_string(file, str) ||
+ my_b_write(file, (const uchar *)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_write(file, (const uchar *)STRING_WITH_LEN(" "))) ||
+ my_b_write(file, (const uchar *)num.ptr(), num.length()))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ first= 0;
+ }
+ break;
+ }
+ default:
+ DBUG_ASSERT(0); // never should happened
+ }
+ DBUG_RETURN(FALSE);
+}
+
+
+/**
+ Write new .frm.
+
+ @param dir directory where put .frm
+ @param file_name .frm file name
+ @param type .frm type string (VIEW, TABLE)
+ @param base base address for parameter reading (structure like
+ TABLE)
+ @param parameters parameters description
+
+ @retval
+ FALSE OK
+ @retval
+ TRUE error
+*/
+
+my_bool
+sql_create_definition_file(const LEX_CSTRING *dir,
+ const LEX_CSTRING *file_name,
+ const LEX_CSTRING *type,
+ uchar* base, File_option *parameters)
+{
+ File handler;
+ IO_CACHE file;
+ char path[FN_REFLEN+1]; // +1 to put temporary file name for sure
+ size_t path_end;
+ File_option *param;
+ DBUG_ENTER("sql_create_definition_file");
+ DBUG_PRINT("enter", ("Dir: %s, file: %s, base %p",
+ dir ? dir->str : "",
+ file_name->str, 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, sizeof(path) - 1, file_name->str, NullS) - path;
+ }
+
+ // temporary file name
+ path[path_end]='~';
+ path[path_end+1]= '\0';
+ if ((handler= mysql_file_create(key_file_fileparser,
+ path, CREATE_MODE, O_RDWR | O_TRUNC,
+ MYF(MY_WME))) < 0)
+ {
+ DBUG_RETURN(TRUE);
+ }
+
+ debug_crash_here("definition_file_after_create");
+
+ if (init_io_cache(&file, handler, 0, WRITE_CACHE, 0L, 0, MYF(MY_WME)))
+ goto err_w_file;
+
+ // write header (file signature)
+ if (my_b_write(&file, (const uchar *)STRING_WITH_LEN("TYPE=")) ||
+ my_b_write(&file, (const uchar *)type->str, type->length) ||
+ my_b_write(&file, (const uchar *)STRING_WITH_LEN("\n")))
+ goto err_w_cache;
+
+ if (debug_simulate_error("definition_file_simulate_write_error", EE_WRITE))
+ goto err_w_cache;
+
+ // write parameters to temporary file
+ for (param= parameters; param->name.str; param++)
+ {
+ if (my_b_write(&file, (const uchar *)param->name.str,
+ param->name.length) ||
+ my_b_write(&file, (const uchar *)STRING_WITH_LEN("=")) ||
+ write_parameter(&file, base, param) ||
+ my_b_write(&file, (const uchar *)STRING_WITH_LEN("\n")))
+ goto err_w_cache;
+ }
+
+ if (end_io_cache(&file))
+ goto err_w_file;
+
+ if (opt_sync_frm) {
+ if (mysql_file_sync(handler, MYF(MY_WME)))
+ goto err_w_file;
+ }
+
+ if (mysql_file_close(handler, MYF(MY_WME)))
+ {
+ DBUG_RETURN(TRUE);
+ }
+
+ path[path_end]='\0';
+
+ {
+ // rename temporary file
+ char path_to[FN_REFLEN];
+ memcpy(path_to, path, path_end+1);
+ path[path_end]='~';
+ if (mysql_file_rename(key_file_fileparser, path, path_to, MYF(MY_WME)))
+ {
+ DBUG_RETURN(TRUE);
+ }
+ }
+ DBUG_RETURN(FALSE);
+err_w_cache:
+ end_io_cache(&file);
+err_w_file:
+ mysql_file_close(handler, MYF(MY_WME));
+ mysql_file_delete(key_file_fileparser, path, MYF(MY_WME));
+ DBUG_RETURN(TRUE);
+}
+
+
+/*
+ Make a copy of a definition file with '-' added to the name
+
+ @param org_name Original file name
+ @param new_name Pointer to a buff of FN_REFLEN. Will be updated to name of
+ backup file
+ @return 0 ok
+ @return 1 error
+*/
+
+int sql_backup_definition_file(const LEX_CSTRING *org_name,
+ LEX_CSTRING *new_name)
+{
+ char *new_name_buff= (char*) new_name->str;
+ new_name->length= org_name->length+1;
+
+ memcpy(new_name_buff, org_name->str, org_name->length+1);
+ new_name_buff[org_name->length]= '-';
+ new_name_buff[org_name->length+1]= 0;
+ return my_copy(org_name->str, new_name->str, MYF(MY_WME));
+}
+
+/*
+ Restore copy of a definition file
+
+ @param org_name Name of backup file (ending with '-' or '~')
+
+ @return 0 ok
+ @return 1 error
+*/
+
+int sql_restore_definition_file(const LEX_CSTRING *name)
+{
+ char new_name[FN_REFLEN+1];
+ memcpy(new_name, name->str, name->length-1);
+ new_name[name->length-1]= 0;
+ return mysql_file_rename(key_file_fileparser, name->str, new_name,
+ MYF(MY_WME));
+}
+
+
+/**
+ Renames a frm file (including backups) in same schema.
+
+ @thd thread handler
+ @param schema name of given schema
+ @param old_name original file name
+ @param new_db new schema
+ @param new_name new file name
+
+ @retval
+ 0 OK
+ @retval
+ 1 Error (only if renaming of frm failed)
+*/
+my_bool rename_in_schema_file(THD *thd,
+ const char *schema, const char *old_name,
+ const char *new_db, const char *new_name)
+{
+ char old_path[FN_REFLEN + 1], new_path[FN_REFLEN + 1], arc_path[FN_REFLEN + 1];
+
+ build_table_filename(old_path, sizeof(old_path) - 1,
+ schema, old_name, reg_ext, 0);
+ build_table_filename(new_path, sizeof(new_path) - 1,
+ new_db, new_name, reg_ext, 0);
+
+ if (mysql_file_rename(key_file_frm, old_path, new_path, MYF(MY_WME)))
+ return 1;
+
+ /* check if arc_dir exists: disabled unused feature (see bug #17823). */
+ build_table_filename(arc_path, sizeof(arc_path) - 1, schema, "arc", "", 0);
+
+ { // remove obsolete 'arc' directory and files if any
+ MY_DIR *new_dirp;
+ if ((new_dirp = my_dir(arc_path, MYF(MY_DONT_SORT))))
+ {
+ DBUG_PRINT("my",("Archive subdir found: %s", arc_path));
+ (void) mysql_rm_arc_files(thd, new_dirp, arc_path);
+ }
+ }
+ return 0;
+}
+
+/**
+ Prepare frm to parse (read to memory).
+
+ @param file_name path & filename to .frm file
+ @param mem_root MEM_ROOT for buffer allocation
+ @param bad_format_errors send errors on bad content
+
+ @note
+ returned pointer + 1 will be type of .frm
+
+ @return
+ 0 - error
+ @return
+ parser object
+*/
+
+File_parser *
+sql_parse_prepare(const LEX_CSTRING *file_name, MEM_ROOT *mem_root,
+ bool bad_format_errors)
+{
+ MY_STAT stat_info;
+ size_t len;
+ char *buff, *end, *sign;
+ File_parser *parser;
+ File file;
+ DBUG_ENTER("sql_parse_prepare");
+
+ if (!mysql_file_stat(key_file_fileparser,
+ file_name->str, &stat_info, MYF(MY_WME)))
+ {
+ DBUG_RETURN(0);
+ }
+
+ MSAN_STAT_WORKAROUND(&stat_info);
+
+ 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 (!(buff= (char*) alloc_root(mem_root, (size_t)(stat_info.st_size+1))))
+ {
+ DBUG_RETURN(0);
+ }
+
+ if ((file= mysql_file_open(key_file_fileparser, file_name->str,
+ O_RDONLY | O_SHARE, MYF(MY_WME))) < 0)
+ {
+ DBUG_RETURN(0);
+ }
+
+ if ((len= mysql_file_read(file, (uchar *)buff, (size_t)stat_info.st_size,
+ MYF(MY_WME))) == MY_FILE_ERROR)
+ {
+ mysql_file_close(file, MYF(MY_WME));
+ DBUG_RETURN(0);
+ }
+
+ if (mysql_file_close(file, MYF(MY_WME)))
+ {
+ DBUG_RETURN(0);
+ }
+
+ end= buff + len;
+ *end= '\0'; // barrier for more simple parsing
+
+ // 7 = 5 (TYPE=) + 1 (letter at least of type name) + 1 ('\n')
+ if (len < 7 ||
+ buff[0] != 'T' ||
+ buff[1] != 'Y' ||
+ buff[2] != 'P' ||
+ buff[3] != 'E' ||
+ buff[4] != '=')
+ goto frm_error;
+
+ // skip signature;
+ parser->file_type.str= sign= 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->end= end;
+ 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);
+ }
+ DBUG_RETURN(parser); // upper level have to check parser->ok()
+}
+
+
+/**
+ parse LEX_STRING.
+
+ @param ptr pointer on string beginning
+ @param end pointer on symbol after parsed string end (still owned
+ by buffer and can be accessed
+ @param mem_root MEM_ROOT for parameter allocation
+ @param str pointer on string, where results should be stored
+
+ @retval
+ 0 error
+ @retval
+ \# pointer on symbol after string
+*/
+
+
+static const char *
+parse_string(const char *ptr, const char *end, MEM_ROOT *mem_root,
+ LEX_STRING *str)
+{
+ // get string length
+ const char *eol= strchr(ptr, '\n');
+
+ if (eol >= end)
+ return 0;
+
+ str->length= eol - ptr;
+
+ if (!(str->str= strmake_root(mem_root, ptr, str->length)))
+ return 0;
+ return eol+1;
+}
+
+
+/**
+ read escaped string from ptr to eol in already allocated str.
+
+ @param ptr pointer on string beginning
+ @param eol pointer on character after end of string
+ @param str target string
+
+ @retval
+ FALSE OK
+ @retval
+ TRUE error
+*/
+
+my_bool
+read_escaped_string(const char *ptr, const 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.
+
+ @param ptr pointer on string beginning
+ @param end pointer on symbol after parsed string end (still owned
+ by buffer and can be accessed
+ @param mem_root MEM_ROOT for parameter allocation
+ @param str pointer on string, where results should be stored
+
+ @retval
+ 0 error
+ @retval
+ \# pointer on symbol after string
+*/
+
+
+const char *
+parse_escaped_string(const char *ptr, const char *end, MEM_ROOT *mem_root,
+ LEX_CSTRING *str)
+{
+ const char *eol= strchr(ptr, '\n');
+
+ if (eol == 0 || eol >= end ||
+ !(str->str= (char*) alloc_root(mem_root, (eol - ptr) + 1)) ||
+ read_escaped_string(ptr, eol, (LEX_STRING*) str))
+ return 0;
+
+ return eol+1;
+}
+
+
+/**
+ parse '' delimited escaped string.
+
+ @param ptr pointer on string beginning
+ @param end pointer on symbol after parsed string end (still owned
+ by buffer and can be accessed
+ @param mem_root MEM_ROOT for parameter allocation
+ @param str pointer on string, where results should be stored
+
+ @retval
+ 0 error
+ @retval
+ \# pointer on symbol after string
+*/
+
+static const char *
+parse_quoted_escaped_string(const char *ptr, const char *end,
+ MEM_ROOT *mem_root, LEX_STRING *str)
+{
+ const 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= (char*) 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.
+
+ @param[in,out] ptr pointer to parameter
+ @param[in] end end of the configuration
+ @param[in] line pointer to the line beginning
+ @param[in] base base address for parameter writing (structure
+ like TABLE)
+ @param[in] parameter description
+ @param[in] mem_root MEM_ROOT for parameters allocation
+*/
+
+bool get_file_options_ulllist(const char *&ptr, const char *end,
+ const char *line,
+ uchar* 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= const_cast<char *>(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, &not_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.
+
+ @param base base address for parameter writing (structure like
+ TABLE)
+ @param mem_root MEM_ROOT for parameters allocation
+ @param parameters parameters description
+ @param required number of required parameters in 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.
+ @param hook hook called for unknown keys
+ @param hook_data some data specific for the hook
+
+ @retval
+ FALSE OK
+ @retval
+ TRUE error
+*/
+
+
+my_bool
+File_parser::parse(uchar* base, MEM_ROOT *mem_root,
+ struct File_option *parameters, uint required,
+ Unknown_key_hook *hook) const
+{
+ uint first_param= 0, found= 0;
+ const char *ptr= start;
+ const char *eol;
+ LEX_STRING *str;
+ List<LEX_STRING> *list;
+ DBUG_ENTER("File_parser::parse");
+
+ while (ptr < end && found < required)
+ {
+ const 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;
+ size_t len= 0;
+ for (; parameter < parameters_end; parameter++)
+ {
+ len= parameter->name.length;
+ // check length
+ if (len < (size_t)(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_CSTRING *)
+ (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_VIEW_ALGO:
+ 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 val= (ulonglong)my_strtoll10(ptr, 0, &not_used);
+
+ if (parameter->type == FILE_OPTIONS_VIEW_ALGO)
+ val= view_algo_from_frm(val);
+
+ *((ulonglong*)(base + parameter->offset))= val;
+ }
+ ptr= eol+1;
+ break;
+ case FILE_OPTIONS_TIMESTAMP:
+ {
+ /* string have to be allocated already */
+ LEX_STRING *val= (LEX_STRING *)(base + parameter->offset);
+ /* 19 characters of timestamp */
+ if (ptr[MICROSECOND_TIMESTAMP_BUFFER_SIZE-1] != '\n')
+ {
+ my_error(ER_FPARSER_ERROR_IN_PARAMETER, MYF(0),
+ parameter->name.str, line);
+ DBUG_RETURN(TRUE);
+ }
+ memcpy(val->str, ptr, MICROSECOND_TIMESTAMP_BUFFER_SIZE-1);
+ val->str[val->length= MICROSECOND_TIMESTAMP_BUFFER_SIZE-1]= '\0';
+ ptr+= MICROSECOND_TIMESTAMP_BUFFER_SIZE;
+ 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.
+
+ @param[in,out] unknown_key reference on the line with unknown
+ parameter and the parsing point
+ @param[in] base base address for parameter writing
+ (structure like TABLE)
+ @param[in] mem_root MEM_ROOT for parameters allocation
+ @param[in] end 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.
+
+ @retval
+ FALSE OK
+ @retval
+ TRUE Error
+*/
+
+bool
+File_parser_dummy_hook::process_unknown_string(const char *&unknown_key,
+ uchar* base, MEM_ROOT *mem_root,
+ const char *end)
+{
+ DBUG_ENTER("file_parser_dummy_hook::process_unknown_string");
+ DBUG_PRINT("info", ("Unknown key: '%60s'", unknown_key));
+ DBUG_RETURN(FALSE);
+}