mirror of
https://github.com/MariaDB/server.git
synced 2025-08-01 03:47:19 +03:00
Bug#27430 "Crash in subquery code when in PS and table DDL changed after
PREPARE": rename members, methods, classes to follow the spec (a code review request)
This commit is contained in:
@ -700,14 +700,14 @@ const char *set_thd_proc_info(THD *thd, const char *info,
|
|||||||
@sa Prepared_statement::reprepare()
|
@sa Prepared_statement::reprepare()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
enum enum_metadata_type
|
enum enum_table_ref_type
|
||||||
{
|
{
|
||||||
/** Initial value set by the parser */
|
/** Initial value set by the parser */
|
||||||
METADATA_NULL= 0,
|
TABLE_REF_NULL= 0,
|
||||||
METADATA_VIEW,
|
TABLE_REF_VIEW,
|
||||||
METADATA_BASE_TABLE,
|
TABLE_REF_BASE_TABLE,
|
||||||
METADATA_I_S_TABLE,
|
TABLE_REF_I_S_TABLE,
|
||||||
METADATA_TMP_TABLE
|
TABLE_REF_TMP_TABLE
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1068,7 +1068,7 @@ sp_head::execute(THD *thd)
|
|||||||
LEX *old_lex;
|
LEX *old_lex;
|
||||||
Item_change_list old_change_list;
|
Item_change_list old_change_list;
|
||||||
String old_packet;
|
String old_packet;
|
||||||
Metadata_version_observer *save_metadata_observer= thd->m_metadata_observer;
|
Reprepare_observer *save_reprepare_observer= thd->m_reprepare_observer;
|
||||||
|
|
||||||
Object_creation_ctx *saved_creation_ctx;
|
Object_creation_ctx *saved_creation_ctx;
|
||||||
|
|
||||||
@ -1154,7 +1154,7 @@ sp_head::execute(THD *thd)
|
|||||||
of substatements (Bug#12257, Bug#27011, Bug#32868, Bug#33000),
|
of substatements (Bug#12257, Bug#27011, Bug#32868, Bug#33000),
|
||||||
but it's not implemented yet.
|
but it's not implemented yet.
|
||||||
*/
|
*/
|
||||||
thd->m_metadata_observer= 0;
|
thd->m_reprepare_observer= 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
It is also more efficient to save/restore current thd->lex once when
|
It is also more efficient to save/restore current thd->lex once when
|
||||||
@ -1317,7 +1317,7 @@ sp_head::execute(THD *thd)
|
|||||||
thd->derived_tables= old_derived_tables;
|
thd->derived_tables= old_derived_tables;
|
||||||
thd->variables.sql_mode= save_sql_mode;
|
thd->variables.sql_mode= save_sql_mode;
|
||||||
thd->abort_on_warning= save_abort_on_warning;
|
thd->abort_on_warning= save_abort_on_warning;
|
||||||
thd->m_metadata_observer= save_metadata_observer;
|
thd->m_reprepare_observer= save_reprepare_observer;
|
||||||
|
|
||||||
thd->stmt_arena= old_arena;
|
thd->stmt_arena= old_arena;
|
||||||
state= EXECUTED;
|
state= EXECUTED;
|
||||||
|
@ -3737,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_id_equal()
|
@sa TABLE_LIST::is_table_ref_id_equal()
|
||||||
@sa TABLE_SHARE::get_metadata_id()
|
@sa TABLE_SHARE::get_table_ref_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
|
||||||
@ -3754,10 +3754,10 @@ 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_id_equal(table_share))
|
if (! tables->is_table_ref_id_equal(table_share))
|
||||||
{
|
{
|
||||||
if (thd->m_metadata_observer &&
|
if (thd->m_reprepare_observer &&
|
||||||
thd->m_metadata_observer->report_error(thd))
|
thd->m_reprepare_observer->report_error(thd))
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
Version of the table share is different from the
|
Version of the table share is different from the
|
||||||
@ -3768,14 +3768,14 @@ check_and_update_table_version(THD *thd,
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
/* Always maintain the latest version and type */
|
/* Always maintain the latest version and type */
|
||||||
tables->set_metadata_id(table_share);
|
tables->set_table_ref_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_reprepare_observer && thd->stmt_arena->is_reprepared == FALSE)
|
||||||
{
|
{
|
||||||
thd->m_metadata_observer->report_error(thd);
|
thd->m_reprepare_observer->report_error(thd);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -3865,7 +3865,7 @@ retry:
|
|||||||
Note, the assert below is known to fail inside stored
|
Note, the assert below is known to fail inside stored
|
||||||
procedures (Bug#27011).
|
procedures (Bug#27011).
|
||||||
*/
|
*/
|
||||||
DBUG_ASSERT(thd->m_metadata_observer);
|
DBUG_ASSERT(thd->m_reprepare_observer);
|
||||||
check_and_update_table_version(thd, table_list, share);
|
check_and_update_table_version(thd, table_list, share);
|
||||||
/* Always an error. */
|
/* Always an error. */
|
||||||
DBUG_ASSERT(thd->is_error());
|
DBUG_ASSERT(thd->is_error());
|
||||||
|
@ -198,6 +198,19 @@ bool foreign_key_prefix(Key *a, Key *b)
|
|||||||
** Thread specific functions
|
** Thread specific functions
|
||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
|
|
||||||
|
/** Push an error to the error stack and return TRUE for now. */
|
||||||
|
|
||||||
|
bool
|
||||||
|
Reprepare_observer::report_error(THD *thd)
|
||||||
|
{
|
||||||
|
my_error(ER_NEED_REPREPARE, MYF(ME_NO_WARNING_FOR_ERROR|ME_NO_SP_HANDLER));
|
||||||
|
|
||||||
|
m_invalidated= TRUE;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Open_tables_state::Open_tables_state(ulong version_arg)
|
Open_tables_state::Open_tables_state(ulong version_arg)
|
||||||
:version(version_arg), state_flags(0U)
|
:version(version_arg), state_flags(0U)
|
||||||
{
|
{
|
||||||
@ -360,10 +373,6 @@ char *thd_security_context(THD *thd, char *buffer, unsigned int length,
|
|||||||
return thd->strmake(str.ptr(), str.length());
|
return thd->strmake(str.ptr(), str.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
Metadata_version_observer::~Metadata_version_observer()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Clear this diagnostics area.
|
Clear this diagnostics area.
|
||||||
|
|
||||||
@ -2774,7 +2783,7 @@ void THD::restore_backup_open_tables_state(Open_tables_state *backup)
|
|||||||
handler_tables == 0 && derived_tables == 0 &&
|
handler_tables == 0 && derived_tables == 0 &&
|
||||||
lock == 0 && locked_tables == 0 &&
|
lock == 0 && locked_tables == 0 &&
|
||||||
prelocked_mode == NON_PRELOCKED &&
|
prelocked_mode == NON_PRELOCKED &&
|
||||||
m_metadata_observer == NULL);
|
m_reprepare_observer == NULL);
|
||||||
set_open_tables_state(backup);
|
set_open_tables_state(backup);
|
||||||
DBUG_VOID_RETURN;
|
DBUG_VOID_RETURN;
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,7 @@
|
|||||||
#include "rpl_tblmap.h"
|
#include "rpl_tblmap.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
An abstract interface that can be used to take an action when
|
An interface that is used to take an action when
|
||||||
the locking module notices that a table version has changed
|
the locking module notices that a table version has changed
|
||||||
since the last execution. "Table" here may refer to any kind of
|
since the last execution. "Table" here may refer to any kind of
|
||||||
table -- a base table, a temporary table, a view or an
|
table -- a base table, a temporary table, a view or an
|
||||||
@ -36,36 +36,35 @@
|
|||||||
parse tree *may* be no longer valid, e.g. in case it contains
|
parse tree *may* be no longer valid, e.g. in case it contains
|
||||||
optimizations that depend on table metadata.
|
optimizations that depend on table metadata.
|
||||||
|
|
||||||
This class provides an abstract interface (a method) that is
|
This class provides an interface (a method) that is
|
||||||
invoked when such a situation takes place.
|
invoked when such a situation takes place.
|
||||||
The implementation of the interface in most cases simply
|
The implementation of the method simply reports an error, but
|
||||||
reports an error, but the exact details depend on the nature of
|
the exact details depend on the nature of the SQL statement.
|
||||||
the SQL statement.
|
|
||||||
|
|
||||||
At most 1 instance of this class is active at a time, in which
|
At most 1 instance of this class is active at a time, in which
|
||||||
case THD::m_metadata_observer is not NULL.
|
case THD::m_reprepare_observer is not NULL.
|
||||||
|
|
||||||
@sa check_and_update_table_version() for details of the
|
@sa check_and_update_table_version() for details of the
|
||||||
version tracking algorithm
|
version tracking algorithm
|
||||||
|
|
||||||
@sa Execute_observer for details of how we detect that
|
@sa Open_tables_state::m_reprepare_observer for the life cycle
|
||||||
a metadata change is fatal and a re-prepare is necessary
|
|
||||||
|
|
||||||
@sa Open_tables_state::m_metadata_observer for the life cycle
|
|
||||||
of metadata observers.
|
of metadata observers.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class Metadata_version_observer
|
class Reprepare_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 report_error(THD *thd)= 0;
|
bool report_error(THD *thd);
|
||||||
|
bool is_invalidated() const { return m_invalidated; }
|
||||||
|
void reset_reprepare_observer() { m_invalidated= FALSE; }
|
||||||
|
private:
|
||||||
|
bool m_invalidated;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -848,7 +847,7 @@ public:
|
|||||||
tracking.
|
tracking.
|
||||||
@sa check_and_update_table_version()
|
@sa check_and_update_table_version()
|
||||||
*/
|
*/
|
||||||
Metadata_version_observer *m_metadata_observer;
|
Reprepare_observer *m_reprepare_observer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
List of regular tables in use by this thread. Contains temporary and
|
List of regular tables in use by this thread. Contains temporary and
|
||||||
@ -953,7 +952,7 @@ public:
|
|||||||
extra_lock= lock= locked_tables= 0;
|
extra_lock= lock= locked_tables= 0;
|
||||||
prelocked_mode= NON_PRELOCKED;
|
prelocked_mode= NON_PRELOCKED;
|
||||||
state_flags= 0U;
|
state_flags= 0U;
|
||||||
m_metadata_observer= NULL;
|
m_reprepare_observer= NULL;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -116,37 +116,6 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
If a metadata changed, report a respective error to trigger
|
|
||||||
re-prepare of a prepared statement.
|
|
||||||
*/
|
|
||||||
|
|
||||||
class Execute_observer: public Metadata_version_observer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual bool report_error(THD *thd);
|
|
||||||
/** Set to TRUE if metadata of some used table has changed since prepare */
|
|
||||||
bool m_invalidated;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
Push an error to the error stack and return TRUE for now.
|
|
||||||
In future we must take special care of statements like CREATE
|
|
||||||
TABLE ... SELECT. Should we re-prepare such statements every
|
|
||||||
time?
|
|
||||||
*/
|
|
||||||
|
|
||||||
bool
|
|
||||||
Execute_observer::report_error(THD *thd)
|
|
||||||
{
|
|
||||||
DBUG_ENTER("Execute_observer::report_error");
|
|
||||||
|
|
||||||
my_error(ER_NEED_REPREPARE, MYF(ME_NO_WARNING_FOR_ERROR|ME_NO_SP_HANDLER));
|
|
||||||
|
|
||||||
m_invalidated= TRUE;
|
|
||||||
DBUG_RETURN(TRUE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/****************************************************************************/
|
/****************************************************************************/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -3219,7 +3188,7 @@ Prepared_statement::execute_loop(String *expanded_query,
|
|||||||
uchar *packet_end)
|
uchar *packet_end)
|
||||||
{
|
{
|
||||||
const int MAX_REPREPARE_ATTEMPTS= 3;
|
const int MAX_REPREPARE_ATTEMPTS= 3;
|
||||||
Execute_observer execute_observer;
|
Reprepare_observer reprepare_observer;
|
||||||
bool error;
|
bool error;
|
||||||
int reprepare_attempt= 0;
|
int reprepare_attempt= 0;
|
||||||
|
|
||||||
@ -3227,7 +3196,7 @@ Prepared_statement::execute_loop(String *expanded_query,
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
reexecute:
|
reexecute:
|
||||||
execute_observer.m_invalidated= FALSE;
|
reprepare_observer.reset_reprepare_observer();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
If the free_list is not empty, we'll wrongly free some externally
|
If the free_list is not empty, we'll wrongly free some externally
|
||||||
@ -3245,8 +3214,8 @@ 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);
|
DBUG_ASSERT(thd->m_reprepare_observer == NULL);
|
||||||
thd->m_metadata_observer= &execute_observer;
|
thd->m_reprepare_observer = &reprepare_observer;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(specialflag & SPECIAL_NO_PRIOR))
|
if (!(specialflag & SPECIAL_NO_PRIOR))
|
||||||
@ -3257,10 +3226,10 @@ 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= NULL;
|
thd->m_reprepare_observer= NULL;
|
||||||
|
|
||||||
if (error && !thd->is_fatal_error && !thd->killed &&
|
if (error && !thd->is_fatal_error && !thd->killed &&
|
||||||
execute_observer.m_invalidated &&
|
reprepare_observer.is_invalidated() &&
|
||||||
reprepare_attempt++ < MAX_REPREPARE_ATTEMPTS)
|
reprepare_attempt++ < MAX_REPREPARE_ATTEMPTS)
|
||||||
{
|
{
|
||||||
DBUG_ASSERT(thd->main_da.sql_errno() == ER_NEED_REPREPARE);
|
DBUG_ASSERT(thd->main_da.sql_errno() == ER_NEED_REPREPARE);
|
||||||
|
36
sql/table.h
36
sql/table.h
@ -440,21 +440,21 @@ typedef struct st_table_share
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
Convert unrelated members of TABLE_SHARE to one enum
|
Convert unrelated members of TABLE_SHARE to one enum
|
||||||
representing its metadata type.
|
representing its type.
|
||||||
|
|
||||||
@todo perhaps we need to have a member instead of a function.
|
@todo perhaps we need to have a member instead of a function.
|
||||||
*/
|
*/
|
||||||
enum enum_metadata_type get_metadata_type() const
|
enum enum_table_ref_type get_table_ref_type() const
|
||||||
{
|
{
|
||||||
if (is_view)
|
if (is_view)
|
||||||
return METADATA_VIEW;
|
return TABLE_REF_VIEW;
|
||||||
switch (tmp_table) {
|
switch (tmp_table) {
|
||||||
case NO_TMP_TABLE:
|
case NO_TMP_TABLE:
|
||||||
return METADATA_BASE_TABLE;
|
return TABLE_REF_BASE_TABLE;
|
||||||
case SYSTEM_TMP_TABLE:
|
case SYSTEM_TMP_TABLE:
|
||||||
return METADATA_I_S_TABLE;
|
return TABLE_REF_I_S_TABLE;
|
||||||
default:
|
default:
|
||||||
return METADATA_TMP_TABLE;
|
return TABLE_REF_TMP_TABLE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
@ -479,7 +479,7 @@ typedef struct st_table_share
|
|||||||
to validate prepared statements.
|
to validate prepared statements.
|
||||||
|
|
||||||
Firstly, sets (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 table 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, and vice versa.
|
a version id of a view, and vice versa.
|
||||||
|
|
||||||
@ -525,14 +525,14 @@ typedef struct st_table_share
|
|||||||
When this is done, views will be handled in the same fashion
|
When this is done, views will be handled in the same fashion
|
||||||
as the base tables.
|
as the base tables.
|
||||||
|
|
||||||
Finally, by taking into account metadata type, we always
|
Finally, by taking into account table type, we always
|
||||||
track that a change has taken place when a view is replaced
|
track that a change has taken place when a view is replaced
|
||||||
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_id_equal()
|
@sa TABLE_LIST::is_table_ref_id_equal()
|
||||||
*/
|
*/
|
||||||
ulong get_metadata_version() const
|
ulong get_table_ref_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;
|
||||||
}
|
}
|
||||||
@ -1340,10 +1340,10 @@ struct TABLE_LIST
|
|||||||
@sa check_and_update_table_version()
|
@sa check_and_update_table_version()
|
||||||
*/
|
*/
|
||||||
inline
|
inline
|
||||||
bool is_metadata_id_equal(TABLE_SHARE *s) const
|
bool is_table_ref_id_equal(TABLE_SHARE *s) const
|
||||||
{
|
{
|
||||||
return (m_metadata_type == s->get_metadata_type() &&
|
return (m_table_ref_type == s->get_table_ref_type() &&
|
||||||
m_metadata_version == s->get_metadata_version());
|
m_table_ref_version == s->get_table_ref_version());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1353,10 +1353,10 @@ struct TABLE_LIST
|
|||||||
@sa check_and_update_table_version()
|
@sa check_and_update_table_version()
|
||||||
*/
|
*/
|
||||||
inline
|
inline
|
||||||
void set_metadata_id(TABLE_SHARE *s)
|
void set_table_ref_id(TABLE_SHARE *s)
|
||||||
{
|
{
|
||||||
m_metadata_type= s->get_metadata_type();
|
m_table_ref_type= s->get_table_ref_type();
|
||||||
m_metadata_version= s->get_metadata_version();
|
m_table_ref_version= s->get_table_ref_version();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@ -1370,9 +1370,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_id() */
|
/** See comments for set_metadata_id() */
|
||||||
enum enum_metadata_type m_metadata_type;
|
enum enum_table_ref_type m_table_ref_type;
|
||||||
/** See comments for set_metadata_id() */
|
/** See comments for set_metadata_id() */
|
||||||
ulong m_metadata_version;
|
ulong m_table_ref_version;
|
||||||
};
|
};
|
||||||
|
|
||||||
class Item;
|
class Item;
|
||||||
|
Reference in New Issue
Block a user