/* 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_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();
// Padded 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, 0 L, 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 );
}
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()
}
/**
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, ¬_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_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 , ¬_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 );
}
Messung V0.5 in Prozent C=94 H=84 G=88
¤ Dauer der Verarbeitung: 0.6 Sekunden
(vorverarbeitet am 2026-10-08)
¤
*© Formatika GbR, Deutschland