mirror of
https://github.com/MariaDB/server.git
synced 2026-05-10 09:04:29 +02:00
Implement some code review fixes for the fix for Bug#27430
"Crash in subquery code when in PS and table DDL changed after PREPARE"
include/my_sys.h:
Add two new flags for my_error(). These flags help parameterize
behavoiur of my_message_sql()
sql/item.cc:
Update comments.
Fix a typo in Item_param::set_param_type_and_swap_value()
sql/mysqld.cc:
Implement two additional flags for my_error():
- if ME_NO_SP_HANDLER is specified, ignore stored procedure continue/
exit handlers
- if ME_NO_WARNING_FOR_ERROR is specified, do not push warning
sql/sql_base.cc:
Update comments.
Rename a few methods.
sql/sql_class.h:
Update and improve comments.
sql/sql_prepare.cc:
Update comments.
Style changes.
sql/table.h:
Update comments.
Style changes.
Rename a few methods.
tests/mysql_client_test.c:
Zero the bind array, to follow C API requirements.
This commit is contained in:
parent
954787376f
commit
1cf0b5cb89
8 changed files with 92 additions and 71 deletions
|
|
@ -90,6 +90,9 @@ extern int NEAR my_errno; /* Last error in mysys */
|
||||||
#define ME_COLOUR1 ((1 << ME_HIGHBYTE)) /* Possibly error-colours */
|
#define ME_COLOUR1 ((1 << ME_HIGHBYTE)) /* Possibly error-colours */
|
||||||
#define ME_COLOUR2 ((2 << ME_HIGHBYTE))
|
#define ME_COLOUR2 ((2 << ME_HIGHBYTE))
|
||||||
#define ME_COLOUR3 ((3 << ME_HIGHBYTE))
|
#define ME_COLOUR3 ((3 << ME_HIGHBYTE))
|
||||||
|
#define ME_FATALERROR 1024 /* Fatal statement error */
|
||||||
|
#define ME_NO_WARNING_FOR_ERROR 2048 /* Don't push a warning for error */
|
||||||
|
#define ME_NO_SP_HANDLER 4096 /* Don't call stored routine error handlers */
|
||||||
|
|
||||||
/* Bits in last argument to fn_format */
|
/* Bits in last argument to fn_format */
|
||||||
#define MY_REPLACE_DIR 1 /* replace dir in name with 'dir' */
|
#define MY_REPLACE_DIR 1 /* replace dir in name with 'dir' */
|
||||||
|
|
|
||||||
12
sql/item.cc
12
sql/item.cc
|
|
@ -3165,15 +3165,19 @@ void Item_param::print(String *str, enum_query_type query_type)
|
||||||
Preserve the original parameter types and values
|
Preserve the original parameter types and values
|
||||||
when re-preparing a prepared statement.
|
when re-preparing a prepared statement.
|
||||||
|
|
||||||
Copy parameter type information and conversion function
|
@details Copy parameter type information and conversion
|
||||||
pointers from a parameter of the old statement to the
|
function pointers from a parameter of the old statement
|
||||||
corresponding parameter of the new one.
|
to the corresponding parameter of the new one.
|
||||||
|
|
||||||
Move parameter values from the old parameters to the new
|
Move parameter values from the old parameters to the new
|
||||||
one. We simply "exchange" the values, which allows
|
one. We simply "exchange" the values, which allows
|
||||||
to save on allocation and character set conversion in
|
to save on allocation and character set conversion in
|
||||||
case a parameter is a string or a blob/clob.
|
case a parameter is a string or a blob/clob.
|
||||||
|
|
||||||
|
The old parameter gets the value of this one, which
|
||||||
|
ensures that all memory of this parameter is freed
|
||||||
|
correctly.
|
||||||
|
|
||||||
@param[in] src parameter item of the original
|
@param[in] src parameter item of the original
|
||||||
prepared statement
|
prepared statement
|
||||||
*/
|
*/
|
||||||
|
|
@ -3187,7 +3191,7 @@ Item_param::set_param_type_and_swap_value(Item_param *src)
|
||||||
item_type= src->item_type;
|
item_type= src->item_type;
|
||||||
item_result_type= src->item_result_type;
|
item_result_type= src->item_result_type;
|
||||||
|
|
||||||
collation.set(src->collation.collation);
|
collation.set(src->collation);
|
||||||
maybe_null= src->maybe_null;
|
maybe_null= src->maybe_null;
|
||||||
null_value= src->null_value;
|
null_value= src->null_value;
|
||||||
max_length= src->max_length;
|
max_length= src->max_length;
|
||||||
|
|
|
||||||
|
|
@ -2843,6 +2843,7 @@ int my_message_sql(uint error, const char *str, myf MyFlags)
|
||||||
by the stored procedures code.
|
by the stored procedures code.
|
||||||
*/
|
*/
|
||||||
if (thd->spcont &&
|
if (thd->spcont &&
|
||||||
|
! (MyFlags & ME_NO_SP_HANDLER) &&
|
||||||
thd->spcont->handle_error(error, MYSQL_ERROR::WARN_LEVEL_ERROR, thd))
|
thd->spcont->handle_error(error, MYSQL_ERROR::WARN_LEVEL_ERROR, thd))
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
|
@ -2852,7 +2853,8 @@ int my_message_sql(uint error, const char *str, myf MyFlags)
|
||||||
DBUG_RETURN(0);
|
DBUG_RETURN(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!thd->no_warnings_for_error)
|
if (!thd->no_warnings_for_error &&
|
||||||
|
!(MyFlags & ME_NO_WARNING_FOR_ERROR))
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
Suppress infinite recursion if there a memory allocation error
|
Suppress infinite recursion if there a memory allocation error
|
||||||
|
|
|
||||||
|
|
@ -3722,9 +3722,8 @@ void assign_new_table_id(TABLE_SHARE *share)
|
||||||
Compare metadata versions of an element obtained from the table
|
Compare metadata versions of an element obtained from the table
|
||||||
definition cache and its corresponding node in the parse tree.
|
definition cache and its corresponding node in the parse tree.
|
||||||
|
|
||||||
If the new and the old values mismatch, invoke
|
@details If the new and the old values mismatch, invoke
|
||||||
Metadata_version_observer.
|
Metadata_version_observer.
|
||||||
|
|
||||||
At prepared statement prepare, all TABLE_LIST version values are
|
At prepared statement prepare, all TABLE_LIST version values are
|
||||||
NULL and we always have a mismatch. But there is no observer set
|
NULL and we always have a mismatch. But there is no observer set
|
||||||
in THD, and therefore no error is reported. Instead, we update
|
in THD, and therefore no error is reported. Instead, we update
|
||||||
|
|
@ -3738,8 +3737,8 @@ void assign_new_table_id(TABLE_SHARE *share)
|
||||||
|
|
||||||
@sa Execute_observer
|
@sa Execute_observer
|
||||||
@sa check_prepared_statement() to see cases when an observer is installed
|
@sa check_prepared_statement() to see cases when an observer is installed
|
||||||
@sa TABLE_LIST::is_metadata_version_equal()
|
@sa TABLE_LIST::is_metadata_id_equal()
|
||||||
@sa TABLE_SHARE::get_metadata_version()
|
@sa TABLE_SHARE::get_metadata_id()
|
||||||
|
|
||||||
@param[in] thd used to report errors
|
@param[in] thd used to report errors
|
||||||
@param[in,out] tables TABLE_LIST instance created by the parser
|
@param[in,out] tables TABLE_LIST instance created by the parser
|
||||||
|
|
@ -3755,27 +3754,28 @@ bool
|
||||||
check_and_update_table_version(THD *thd,
|
check_and_update_table_version(THD *thd,
|
||||||
TABLE_LIST *tables, TABLE_SHARE *table_share)
|
TABLE_LIST *tables, TABLE_SHARE *table_share)
|
||||||
{
|
{
|
||||||
if (! tables->is_metadata_version_equal(table_share))
|
if (! tables->is_metadata_id_equal(table_share))
|
||||||
{
|
{
|
||||||
if (thd->m_metadata_observer &&
|
if (thd->m_metadata_observer &&
|
||||||
thd->m_metadata_observer->check_metadata_change(thd))
|
thd->m_metadata_observer->report_error(thd))
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
Version of the table share is different from the
|
Version of the table share is different from the
|
||||||
previous execution of the prepared statement, and it is
|
previous execution of the prepared statement, and it is
|
||||||
unacceptable for this SQLCOM. Error has been reported.
|
unacceptable for this SQLCOM. Error has been reported.
|
||||||
*/
|
*/
|
||||||
|
DBUG_ASSERT(thd->is_error());
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
/* Always maintain the latest version */
|
/* Always maintain the latest version and type */
|
||||||
tables->set_metadata_version(table_share);
|
tables->set_metadata_id(table_share);
|
||||||
}
|
}
|
||||||
#if 0
|
#if 0
|
||||||
#ifndef DBUG_OFF
|
#ifndef DBUG_OFF
|
||||||
/* Spuriously reprepare each statement. */
|
/* Spuriously reprepare each statement. */
|
||||||
if (thd->m_metadata_observer && thd->stmt_arena->is_reprepared == FALSE)
|
if (thd->m_metadata_observer && thd->stmt_arena->is_reprepared == FALSE)
|
||||||
{
|
{
|
||||||
thd->m_metadata_observer->check_metadata_change(thd);
|
thd->m_metadata_observer->report_error(thd);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -3828,6 +3828,10 @@ retry:
|
||||||
|
|
||||||
if (share->is_view)
|
if (share->is_view)
|
||||||
{
|
{
|
||||||
|
/*
|
||||||
|
This table is a view. Validate its metadata version: in particular,
|
||||||
|
that it was a view when the statement was prepared.
|
||||||
|
*/
|
||||||
if (check_and_update_table_version(thd, table_list, share))
|
if (check_and_update_table_version(thd, table_list, share))
|
||||||
goto err;
|
goto err;
|
||||||
if (table_list->i_s_requested_object & OPEN_TABLE_ONLY)
|
if (table_list->i_s_requested_object & OPEN_TABLE_ONLY)
|
||||||
|
|
@ -4622,6 +4626,7 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
|
||||||
}
|
}
|
||||||
tables->table->grant= tables->grant;
|
tables->table->grant= tables->grant;
|
||||||
|
|
||||||
|
/* Check and update metadata version of a base table. */
|
||||||
if (check_and_update_table_version(thd, tables, tables->table->s))
|
if (check_and_update_table_version(thd, tables, tables->table->s))
|
||||||
{
|
{
|
||||||
result= -1;
|
result= -1;
|
||||||
|
|
|
||||||
|
|
@ -57,16 +57,15 @@
|
||||||
|
|
||||||
class Metadata_version_observer
|
class Metadata_version_observer
|
||||||
{
|
{
|
||||||
protected:
|
|
||||||
virtual ~Metadata_version_observer();
|
|
||||||
public:
|
public:
|
||||||
|
virtual ~Metadata_version_observer();
|
||||||
/**
|
/**
|
||||||
Check if a change of metadata is OK. In future
|
Check if a change of metadata is OK. In future
|
||||||
the signature of this method may be extended to accept the old
|
the signature of this method may be extended to accept the old
|
||||||
and the new versions, but since currently the check is very
|
and the new versions, but since currently the check is very
|
||||||
simple, we only need the THD to report an error.
|
simple, we only need the THD to report an error.
|
||||||
*/
|
*/
|
||||||
virtual bool check_metadata_change(THD *thd)= 0;
|
virtual bool report_error(THD *thd)= 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -2842,6 +2841,19 @@ public:
|
||||||
#define CF_STATUS_COMMAND 4
|
#define CF_STATUS_COMMAND 4
|
||||||
#define CF_SHOW_TABLE_COMMAND 8
|
#define CF_SHOW_TABLE_COMMAND 8
|
||||||
#define CF_WRITE_LOGS_COMMAND 16
|
#define CF_WRITE_LOGS_COMMAND 16
|
||||||
|
/**
|
||||||
|
Must be set for SQL statements that may contain
|
||||||
|
Item expressions and/or use joins and tables.
|
||||||
|
Indicates that the parse tree of such statement may
|
||||||
|
contain rule-based optimizations that depend on metadata
|
||||||
|
(i.e. number of columns in a table), and consequently
|
||||||
|
that the statement must be re-prepared whenever
|
||||||
|
referenced metadata changes. Must not be set for
|
||||||
|
statements that themselves change metadata, e.g. RENAME,
|
||||||
|
ALTER and other DDL, since otherwise will trigger constant
|
||||||
|
reprepare. Consequently, complex item expressions and
|
||||||
|
joins are currently prohibited in these statements.
|
||||||
|
*/
|
||||||
#define CF_REEXECUTION_FRAGILE 32
|
#define CF_REEXECUTION_FRAGILE 32
|
||||||
|
|
||||||
/* Functions in sql_class.cc */
|
/* Functions in sql_class.cc */
|
||||||
|
|
|
||||||
|
|
@ -124,7 +124,7 @@ public:
|
||||||
class Execute_observer: public Metadata_version_observer
|
class Execute_observer: public Metadata_version_observer
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual bool check_metadata_change(THD *thd);
|
virtual bool report_error(THD *thd);
|
||||||
/** Set to TRUE if metadata of some used table has changed since prepare */
|
/** Set to TRUE if metadata of some used table has changed since prepare */
|
||||||
bool m_invalidated;
|
bool m_invalidated;
|
||||||
};
|
};
|
||||||
|
|
@ -137,14 +137,12 @@ public:
|
||||||
*/
|
*/
|
||||||
|
|
||||||
bool
|
bool
|
||||||
Execute_observer::check_metadata_change(THD *thd)
|
Execute_observer::report_error(THD *thd)
|
||||||
{
|
{
|
||||||
bool save_thd_no_warnings_for_error= thd->no_warnings_for_error;
|
DBUG_ENTER("Execute_observer::report_error");
|
||||||
DBUG_ENTER("Execute_observer::notify_about_metadata_change");
|
|
||||||
|
my_error(ER_NEED_REPREPARE, MYF(ME_NO_WARNING_FOR_ERROR|ME_NO_SP_HANDLER));
|
||||||
|
|
||||||
thd->no_warnings_for_error= TRUE;
|
|
||||||
my_error(ER_NEED_REPREPARE, MYF(0));
|
|
||||||
thd->no_warnings_for_error= save_thd_no_warnings_for_error;
|
|
||||||
m_invalidated= TRUE;
|
m_invalidated= TRUE;
|
||||||
DBUG_RETURN(TRUE);
|
DBUG_RETURN(TRUE);
|
||||||
}
|
}
|
||||||
|
|
@ -984,7 +982,7 @@ static bool emb_insert_params_with_log(Prepared_statement *stmt,
|
||||||
/**
|
/**
|
||||||
Setup data conversion routines using an array of parameter
|
Setup data conversion routines using an array of parameter
|
||||||
markers from the original prepared statement.
|
markers from the original prepared statement.
|
||||||
Move the parameter data of the original prepared
|
Swap the parameter data of the original prepared
|
||||||
statement to the new one.
|
statement to the new one.
|
||||||
|
|
||||||
Used only when we re-prepare a prepared statement.
|
Used only when we re-prepare a prepared statement.
|
||||||
|
|
@ -2844,7 +2842,7 @@ Select_fetch_protocol_binary::send_data(List<Item> &fields)
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
Prepared_statement::Prepared_statement(THD *thd_arg, Protocol *protocol_arg)
|
Prepared_statement::Prepared_statement(THD *thd_arg, Protocol *protocol_arg)
|
||||||
:Statement(0, &main_mem_root,
|
:Statement(NULL, &main_mem_root,
|
||||||
INITIALIZED, ++thd_arg->statement_id_counter),
|
INITIALIZED, ++thd_arg->statement_id_counter),
|
||||||
thd(thd_arg),
|
thd(thd_arg),
|
||||||
result(thd_arg),
|
result(thd_arg),
|
||||||
|
|
@ -3153,38 +3151,36 @@ Prepared_statement::set_parameters(String *expanded_query,
|
||||||
uchar *packet, uchar *packet_end)
|
uchar *packet, uchar *packet_end)
|
||||||
{
|
{
|
||||||
bool is_sql_ps= packet == NULL;
|
bool is_sql_ps= packet == NULL;
|
||||||
|
bool res;
|
||||||
|
|
||||||
if (is_sql_ps)
|
if (is_sql_ps)
|
||||||
{
|
{
|
||||||
/* SQL prepared statement */
|
/* SQL prepared statement */
|
||||||
if (set_params_from_vars(this, thd->lex->prepared_stmt_params,
|
res= set_params_from_vars(this, thd->lex->prepared_stmt_params,
|
||||||
expanded_query))
|
expanded_query);
|
||||||
goto set_params_data_err;
|
|
||||||
}
|
}
|
||||||
else if (param_count)
|
else if (param_count)
|
||||||
{
|
{
|
||||||
#ifndef EMBEDDED_LIBRARY
|
#ifndef EMBEDDED_LIBRARY
|
||||||
uchar *null_array= packet;
|
uchar *null_array= packet;
|
||||||
if (setup_conversion_functions(this, &packet, packet_end) ||
|
res= (setup_conversion_functions(this, &packet, packet_end) ||
|
||||||
set_params(this, null_array, packet, packet_end,
|
set_params(this, null_array, packet, packet_end, expanded_query));
|
||||||
expanded_query))
|
|
||||||
goto set_params_data_err;
|
|
||||||
#else
|
#else
|
||||||
/*
|
/*
|
||||||
In embedded library we re-install conversion routines each time
|
In embedded library we re-install conversion routines each time
|
||||||
we set params, and also we don't need to parse packet.
|
we set parameters, and also we don't need to parse packet.
|
||||||
So we do it in one function.
|
So we do it in one function.
|
||||||
*/
|
*/
|
||||||
if (set_params_data(this, expanded_query))
|
res= set_params_data(this, expanded_query);
|
||||||
goto set_params_data_err;
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
return FALSE;
|
if (res)
|
||||||
set_params_data_err:
|
{
|
||||||
my_error(ER_WRONG_ARGUMENTS, MYF(0),
|
my_error(ER_WRONG_ARGUMENTS, MYF(0),
|
||||||
is_sql_ps ? "EXECUTE" : "mysql_stmt_execute");
|
is_sql_ps ? "EXECUTE" : "mysql_stmt_execute");
|
||||||
reset_stmt_params(this);
|
reset_stmt_params(this);
|
||||||
return TRUE;
|
}
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -3249,6 +3245,7 @@ reexecute:
|
||||||
if (sql_command_flags[lex->sql_command] &
|
if (sql_command_flags[lex->sql_command] &
|
||||||
CF_REEXECUTION_FRAGILE)
|
CF_REEXECUTION_FRAGILE)
|
||||||
{
|
{
|
||||||
|
DBUG_ASSERT(thd->m_metadata_observer == NULL);
|
||||||
thd->m_metadata_observer= &execute_observer;
|
thd->m_metadata_observer= &execute_observer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3260,12 +3257,13 @@ reexecute:
|
||||||
if (!(specialflag & SPECIAL_NO_PRIOR))
|
if (!(specialflag & SPECIAL_NO_PRIOR))
|
||||||
my_pthread_setprio(pthread_self(), WAIT_PRIOR);
|
my_pthread_setprio(pthread_self(), WAIT_PRIOR);
|
||||||
|
|
||||||
thd->m_metadata_observer= 0;
|
thd->m_metadata_observer= NULL;
|
||||||
|
|
||||||
if (error && !thd->is_fatal_error && !thd->killed &&
|
if (error && !thd->is_fatal_error && !thd->killed &&
|
||||||
execute_observer.m_invalidated &&
|
execute_observer.m_invalidated &&
|
||||||
reprepare_attempt++ < MAX_REPREPARE_ATTEMPTS)
|
reprepare_attempt++ < MAX_REPREPARE_ATTEMPTS)
|
||||||
{
|
{
|
||||||
|
DBUG_ASSERT(thd->main_da.sql_errno() == ER_NEED_REPREPARE);
|
||||||
thd->clear_error();
|
thd->clear_error();
|
||||||
|
|
||||||
error= reprepare();
|
error= reprepare();
|
||||||
|
|
@ -3299,44 +3297,40 @@ Prepared_statement::reprepare()
|
||||||
LEX_STRING saved_cur_db_name=
|
LEX_STRING saved_cur_db_name=
|
||||||
{ saved_cur_db_name_buf, sizeof(saved_cur_db_name_buf) };
|
{ saved_cur_db_name_buf, sizeof(saved_cur_db_name_buf) };
|
||||||
LEX_STRING stmt_db_name= { db, db_length };
|
LEX_STRING stmt_db_name= { db, db_length };
|
||||||
Prepared_statement *copy;
|
|
||||||
bool cur_db_changed;
|
bool cur_db_changed;
|
||||||
bool error= TRUE;
|
bool error;
|
||||||
|
|
||||||
|
Prepared_statement copy(thd, &thd->protocol_text);
|
||||||
|
|
||||||
status_var_increment(thd->status_var.com_stmt_reprepare);
|
status_var_increment(thd->status_var.com_stmt_reprepare);
|
||||||
|
|
||||||
copy= new Prepared_statement(thd, &thd->protocol_text);
|
|
||||||
|
|
||||||
if (! copy)
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
if (mysql_opt_change_db(thd, &stmt_db_name, &saved_cur_db_name, TRUE,
|
if (mysql_opt_change_db(thd, &stmt_db_name, &saved_cur_db_name, TRUE,
|
||||||
&cur_db_changed))
|
&cur_db_changed))
|
||||||
goto end;
|
return TRUE;
|
||||||
|
|
||||||
error= (name.str && copy->set_name(&name) ||
|
error= (name.str && copy.set_name(&name) ||
|
||||||
copy->prepare(query, query_length) ||
|
copy.prepare(query, query_length) ||
|
||||||
validate_metadata(copy));
|
validate_metadata(©));
|
||||||
|
|
||||||
if (cur_db_changed)
|
if (cur_db_changed)
|
||||||
mysql_change_db(thd, &saved_cur_db_name, TRUE);
|
mysql_change_db(thd, &saved_cur_db_name, TRUE);
|
||||||
|
|
||||||
if (! error)
|
if (! error)
|
||||||
{
|
{
|
||||||
swap_prepared_statement(copy);
|
swap_prepared_statement(©);
|
||||||
swap_parameter_array(param_array, copy->param_array, param_count);
|
swap_parameter_array(param_array, copy.param_array, param_count);
|
||||||
#ifndef DBUG_OFF
|
#ifndef DBUG_OFF
|
||||||
is_reprepared= TRUE;
|
is_reprepared= TRUE;
|
||||||
#endif
|
#endif
|
||||||
/*
|
/*
|
||||||
Clear possible warnigns during re-prepare, it has to be completely
|
Clear possible warnings during reprepare, it has to be completely
|
||||||
transparent to the user. We use mysql_reset_errors() since
|
transparent to the user. We use mysql_reset_errors() since
|
||||||
there were no separate query id issued for re-prepare.
|
there were no separate query id issued for re-prepare.
|
||||||
|
Sic: we can't simply silence warnings during reprepare, because if
|
||||||
|
it's failed, we need to return all the warnings to the user.
|
||||||
*/
|
*/
|
||||||
mysql_reset_errors(thd, TRUE);
|
mysql_reset_errors(thd, TRUE);
|
||||||
}
|
}
|
||||||
end:
|
|
||||||
delete copy;
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
16
sql/table.h
16
sql/table.h
|
|
@ -478,10 +478,10 @@ typedef struct st_table_share
|
||||||
Let's try to explain why and how this limited solution allows
|
Let's try to explain why and how this limited solution allows
|
||||||
to validate prepared statements.
|
to validate prepared statements.
|
||||||
|
|
||||||
Firstly, spaces (in mathematical sense) of version numbers
|
Firstly, sets (in mathematical sense) of version numbers
|
||||||
never intersect for different metadata types. Therefore,
|
never intersect for different metadata types. Therefore,
|
||||||
version id of a temporary table is never compared with
|
version id of a temporary table is never compared with
|
||||||
a version id of a view or a temporary table, and vice versa.
|
a version id of a view, and vice versa.
|
||||||
|
|
||||||
Secondly, for base tables, we know that each DDL flushes the
|
Secondly, for base tables, we know that each DDL flushes the
|
||||||
respective share from the TDC. This ensures that whenever
|
respective share from the TDC. This ensures that whenever
|
||||||
|
|
@ -530,11 +530,11 @@ typedef struct st_table_share
|
||||||
with a base table, a base table is replaced with a temporary
|
with a base table, a base table is replaced with a temporary
|
||||||
table and so on.
|
table and so on.
|
||||||
|
|
||||||
@sa TABLE_LIST::is_metadata_version_equal()
|
@sa TABLE_LIST::is_metadata_id_equal()
|
||||||
*/
|
*/
|
||||||
ulong get_metadata_version() const
|
ulong get_metadata_version() const
|
||||||
{
|
{
|
||||||
return tmp_table == SYSTEM_TMP_TABLE || is_view ? 0 : table_map_id;
|
return (tmp_table == SYSTEM_TMP_TABLE || is_view) ? 0 : table_map_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
} TABLE_SHARE;
|
} TABLE_SHARE;
|
||||||
|
|
@ -1340,7 +1340,7 @@ struct TABLE_LIST
|
||||||
@sa check_and_update_table_version()
|
@sa check_and_update_table_version()
|
||||||
*/
|
*/
|
||||||
inline
|
inline
|
||||||
bool is_metadata_version_equal(TABLE_SHARE *s) const
|
bool is_metadata_id_equal(TABLE_SHARE *s) const
|
||||||
{
|
{
|
||||||
return (m_metadata_type == s->get_metadata_type() &&
|
return (m_metadata_type == s->get_metadata_type() &&
|
||||||
m_metadata_version == s->get_metadata_version());
|
m_metadata_version == s->get_metadata_version());
|
||||||
|
|
@ -1353,7 +1353,7 @@ struct TABLE_LIST
|
||||||
@sa check_and_update_table_version()
|
@sa check_and_update_table_version()
|
||||||
*/
|
*/
|
||||||
inline
|
inline
|
||||||
void set_metadata_version(TABLE_SHARE *s)
|
void set_metadata_id(TABLE_SHARE *s)
|
||||||
{
|
{
|
||||||
m_metadata_type= s->get_metadata_type();
|
m_metadata_type= s->get_metadata_type();
|
||||||
m_metadata_version= s->get_metadata_version();
|
m_metadata_version= s->get_metadata_version();
|
||||||
|
|
@ -1369,9 +1369,9 @@ private:
|
||||||
|
|
||||||
/* Remembered MERGE child def version. See top comment in ha_myisammrg.cc */
|
/* Remembered MERGE child def version. See top comment in ha_myisammrg.cc */
|
||||||
ulong child_def_version;
|
ulong child_def_version;
|
||||||
/** See comments for set_metadata_version() */
|
/** See comments for set_metadata_id() */
|
||||||
enum enum_metadata_type m_metadata_type;
|
enum enum_metadata_type m_metadata_type;
|
||||||
/** See comments for set_metadata_version() */
|
/** See comments for set_metadata_id() */
|
||||||
ulong m_metadata_version;
|
ulong m_metadata_version;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17418,6 +17418,7 @@ static void test_wl4166()
|
||||||
|
|
||||||
verify_param_count(stmt, 7);
|
verify_param_count(stmt, 7);
|
||||||
|
|
||||||
|
bzero(my_bind, sizeof(my_bind));
|
||||||
/* tinyint */
|
/* tinyint */
|
||||||
my_bind[0].buffer_type= MYSQL_TYPE_TINY;
|
my_bind[0].buffer_type= MYSQL_TYPE_TINY;
|
||||||
my_bind[0].buffer= (void *)&tiny_data;
|
my_bind[0].buffer= (void *)&tiny_data;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue