Backport of:
---------------------------------------------------------- revno: 2617.69.24 committer: Konstantin Osipov <kostja@sun.com> branch nick: 5.4-42546 timestamp: Fri 2009-08-14 19:22:05 +0400 message: A pre-requisite for a fix for Bug#42546 "Backup: RESTORE fails, thinking it finds an existing table" Back-port from WL 148 "Foreign keys" feature tree a patch that introduced Prelocking_strategy class -- a way to parameterize open_tables() behaviour, implemented by Dmitry Lenev. (Part of WL#4284). sql/sql_base.cc: Implement different prelocking strategies. Use an instance of prelocking_strategy in open_tables(). sql/sql_class.h: Add declarations for class Prelocking_strategy. sql/sql_lex.h: Add a helper method to access last table of the global table list (lex->query_tables). sql/sql_parse.cc: Use a special prelocking strategy when locking tables for LOCK TABLES. sql/sql_table.cc: Use normal open_and_lock_tables_derived() in ALTER TABLE. sql/sql_yacc.yy: Modify the grammar to not pollute the global table list with tables that should not be opened.
This commit is contained in:
parent
97d2a9233b
commit
302352723e
@ -1474,10 +1474,26 @@ int setup_ftfuncs(SELECT_LEX* select);
|
|||||||
int init_ftfuncs(THD *thd, SELECT_LEX* select, bool no_order);
|
int init_ftfuncs(THD *thd, SELECT_LEX* select, bool no_order);
|
||||||
void wait_for_condition(THD *thd, pthread_mutex_t *mutex,
|
void wait_for_condition(THD *thd, pthread_mutex_t *mutex,
|
||||||
pthread_cond_t *cond);
|
pthread_cond_t *cond);
|
||||||
int open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags);
|
int open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags,
|
||||||
|
Prelocking_strategy *prelocking_strategy);
|
||||||
|
inline int open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags)
|
||||||
|
{
|
||||||
|
DML_prelocking_strategy prelocking_strategy;
|
||||||
|
|
||||||
|
return open_tables(thd, tables, counter, flags, &prelocking_strategy);
|
||||||
|
}
|
||||||
/* open_and_lock_tables with optional derived handling */
|
/* open_and_lock_tables with optional derived handling */
|
||||||
int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived,
|
int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables,
|
||||||
uint flags);
|
bool derived, uint flags,
|
||||||
|
Prelocking_strategy *prelocking_strategy);
|
||||||
|
inline int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables,
|
||||||
|
bool derived, uint flags)
|
||||||
|
{
|
||||||
|
DML_prelocking_strategy prelocking_strategy;
|
||||||
|
|
||||||
|
return open_and_lock_tables_derived(thd, tables, derived, flags,
|
||||||
|
&prelocking_strategy);
|
||||||
|
}
|
||||||
/* simple open_and_lock_tables without derived handling */
|
/* simple open_and_lock_tables without derived handling */
|
||||||
inline int simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables)
|
inline int simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables)
|
||||||
{
|
{
|
||||||
|
416
sql/sp.cc
416
sql/sp.cc
@ -17,7 +17,6 @@
|
|||||||
#include "sp.h"
|
#include "sp.h"
|
||||||
#include "sp_head.h"
|
#include "sp_head.h"
|
||||||
#include "sp_cache.h"
|
#include "sp_cache.h"
|
||||||
#include "sql_trigger.h"
|
|
||||||
|
|
||||||
#include <my_user.h>
|
#include <my_user.h>
|
||||||
|
|
||||||
@ -1388,32 +1387,6 @@ sp_routine_exists_in_table(THD *thd, int type, sp_name *name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
Structure that represents element in the set of stored routines
|
|
||||||
used by statement or routine.
|
|
||||||
*/
|
|
||||||
struct Sroutine_hash_entry;
|
|
||||||
|
|
||||||
struct Sroutine_hash_entry
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
Set key consisting of one-byte routine type and quoted routine name.
|
|
||||||
*/
|
|
||||||
LEX_STRING key;
|
|
||||||
/**
|
|
||||||
Next element in list linking all routines in set. See also comments
|
|
||||||
for LEX::sroutine/sroutine_list and sp_head::m_sroutines.
|
|
||||||
*/
|
|
||||||
Sroutine_hash_entry *next;
|
|
||||||
/**
|
|
||||||
Uppermost view which directly or indirectly uses this routine.
|
|
||||||
0 if routine is not used in view. Note that it also can be 0 if
|
|
||||||
statement uses routine both via view and directly.
|
|
||||||
*/
|
|
||||||
TABLE_LIST *belong_to_view;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
extern "C" uchar* sp_sroutine_key(const uchar *ptr, size_t *plen,
|
extern "C" uchar* sp_sroutine_key(const uchar *ptr, size_t *plen,
|
||||||
my_bool first)
|
my_bool first)
|
||||||
{
|
{
|
||||||
@ -1423,52 +1396,17 @@ extern "C" uchar* sp_sroutine_key(const uchar *ptr, size_t *plen,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
Check if
|
|
||||||
- current statement (the one in thd->lex) needs table prelocking
|
|
||||||
- first routine in thd->lex->sroutines_list needs to execute its body in
|
|
||||||
prelocked mode.
|
|
||||||
|
|
||||||
@param thd Current thread, thd->lex is the statement to be
|
|
||||||
checked.
|
|
||||||
@param[out] need_prelocking TRUE - prelocked mode should be activated
|
|
||||||
before executing the statement;
|
|
||||||
FALSE - Don't activate prelocking
|
|
||||||
@param[out] first_no_prelocking TRUE - Tables used by first routine in
|
|
||||||
thd->lex->sroutines_list should be
|
|
||||||
prelocked. FALSE - Otherwise.
|
|
||||||
|
|
||||||
@note
|
|
||||||
This function assumes that for any "CALL proc(...)" statement routines_list
|
|
||||||
will have 'proc' as first element (it may have several, consider e.g.
|
|
||||||
"proc(sp_func(...)))". This property is currently guaranted by the parser.
|
|
||||||
*/
|
|
||||||
|
|
||||||
void sp_get_prelocking_info(THD *thd, bool *need_prelocking,
|
|
||||||
bool *first_no_prelocking)
|
|
||||||
{
|
|
||||||
Sroutine_hash_entry *routine;
|
|
||||||
routine= (Sroutine_hash_entry*)thd->lex->sroutines_list.first;
|
|
||||||
|
|
||||||
DBUG_ASSERT(routine);
|
|
||||||
bool first_is_procedure= (routine->key.str[0] == TYPE_ENUM_PROCEDURE);
|
|
||||||
|
|
||||||
*first_no_prelocking= first_is_procedure;
|
|
||||||
*need_prelocking= !first_is_procedure || test(routine->next);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Auxilary function that adds new element to the set of stored routines
|
Auxilary function that adds new element to the set of stored routines
|
||||||
used by statement.
|
used by statement.
|
||||||
|
|
||||||
In case when statement uses stored routines but does not need
|
In case when statement uses stored routines but does not need
|
||||||
prelocking (i.e. it does not use any tables) we will access the
|
prelocking (i.e. it does not use any tables) we will access the
|
||||||
elements of LEX::sroutines set on prepared statement re-execution.
|
elements of Query_tables_list::sroutines set on prepared statement
|
||||||
Because of this we have to allocate memory for both hash element
|
re-execution. Because of this we have to allocate memory for both
|
||||||
and copy of its key in persistent arena.
|
hash element and copy of its key in persistent arena.
|
||||||
|
|
||||||
@param lex LEX representing statement
|
@param prelocking_ctx Prelocking context of the statement
|
||||||
@param arena Arena in which memory for new element will be
|
@param arena Arena in which memory for new element will be
|
||||||
allocated
|
allocated
|
||||||
@param key Key for the hash representing set
|
@param key Key for the hash representing set
|
||||||
@ -1476,7 +1414,7 @@ void sp_get_prelocking_info(THD *thd, bool *need_prelocking,
|
|||||||
(0 if routine is not used by view)
|
(0 if routine is not used by view)
|
||||||
|
|
||||||
@note
|
@note
|
||||||
Will also add element to end of 'LEX::sroutines_list' list.
|
Will also add element to end of 'Query_tables_list::sroutines_list' list.
|
||||||
|
|
||||||
@todo
|
@todo
|
||||||
When we will got rid of these accesses on re-executions we will be
|
When we will got rid of these accesses on re-executions we will be
|
||||||
@ -1491,15 +1429,15 @@ void sp_get_prelocking_info(THD *thd, bool *need_prelocking,
|
|||||||
the set).
|
the set).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static bool add_used_routine(LEX *lex, Query_arena *arena,
|
bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena,
|
||||||
const LEX_STRING *key,
|
const LEX_STRING *key, TABLE_LIST *belong_to_view)
|
||||||
TABLE_LIST *belong_to_view)
|
|
||||||
{
|
{
|
||||||
my_hash_init_opt(&lex->sroutines, system_charset_info,
|
my_hash_init_opt(&prelocking_ctx->sroutines, system_charset_info,
|
||||||
Query_tables_list::START_SROUTINES_HASH_SIZE,
|
Query_tables_list::START_SROUTINES_HASH_SIZE,
|
||||||
0, 0, sp_sroutine_key, 0, 0);
|
0, 0, sp_sroutine_key, 0, 0);
|
||||||
|
|
||||||
if (!my_hash_search(&lex->sroutines, (uchar *)key->str, key->length))
|
if (!my_hash_search(&prelocking_ctx->sroutines, (uchar *)key->str,
|
||||||
|
key->length))
|
||||||
{
|
{
|
||||||
Sroutine_hash_entry *rn=
|
Sroutine_hash_entry *rn=
|
||||||
(Sroutine_hash_entry *)arena->alloc(sizeof(Sroutine_hash_entry) +
|
(Sroutine_hash_entry *)arena->alloc(sizeof(Sroutine_hash_entry) +
|
||||||
@ -1509,8 +1447,8 @@ static bool add_used_routine(LEX *lex, Query_arena *arena,
|
|||||||
rn->key.length= key->length;
|
rn->key.length= key->length;
|
||||||
rn->key.str= (char *)rn + sizeof(Sroutine_hash_entry);
|
rn->key.str= (char *)rn + sizeof(Sroutine_hash_entry);
|
||||||
memcpy(rn->key.str, key->str, key->length + 1);
|
memcpy(rn->key.str, key->str, key->length + 1);
|
||||||
my_hash_insert(&lex->sroutines, (uchar *)rn);
|
my_hash_insert(&prelocking_ctx->sroutines, (uchar *)rn);
|
||||||
lex->sroutines_list.link_in_list((uchar *)rn, (uchar **)&rn->next);
|
prelocking_ctx->sroutines_list.link_in_list((uchar *)rn, (uchar **)&rn->next);
|
||||||
rn->belong_to_view= belong_to_view;
|
rn->belong_to_view= belong_to_view;
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
@ -1525,24 +1463,25 @@ static bool add_used_routine(LEX *lex, Query_arena *arena,
|
|||||||
To be friendly towards prepared statements one should pass
|
To be friendly towards prepared statements one should pass
|
||||||
persistent arena as second argument.
|
persistent arena as second argument.
|
||||||
|
|
||||||
@param lex LEX representing statement
|
@param prelocking_ctx Prelocking context of the statement
|
||||||
@param arena arena in which memory for new element of the set
|
@param arena Arena in which memory for new element of the set
|
||||||
will be allocated
|
will be allocated
|
||||||
@param rt routine name
|
@param rt Routine name
|
||||||
@param rt_type routine type (one of TYPE_ENUM_PROCEDURE/...)
|
@param rt_type Routine type (one of TYPE_ENUM_PROCEDURE/...)
|
||||||
|
|
||||||
@note
|
@note
|
||||||
Will also add element to end of 'LEX::sroutines_list' list (and will
|
Will also add element to end of 'Query_tables_list::sroutines_list' list
|
||||||
take into account that this is explicitly used routine).
|
(and will take into account that this is an explicitly used routine).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void sp_add_used_routine(LEX *lex, Query_arena *arena,
|
void sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena,
|
||||||
sp_name *rt, char rt_type)
|
sp_name *rt, char rt_type)
|
||||||
{
|
{
|
||||||
rt->set_routine_type(rt_type);
|
rt->set_routine_type(rt_type);
|
||||||
(void)add_used_routine(lex, arena, &rt->m_sroutines_key, 0);
|
(void)sp_add_used_routine(prelocking_ctx, arena, &rt->m_sroutines_key, 0);
|
||||||
lex->sroutines_list_own_last= lex->sroutines_list.next;
|
prelocking_ctx->sroutines_list_own_last= prelocking_ctx->sroutines_list.next;
|
||||||
lex->sroutines_list_own_elements= lex->sroutines_list.elements;
|
prelocking_ctx->sroutines_list_own_elements=
|
||||||
|
prelocking_ctx->sroutines_list.elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1550,13 +1489,14 @@ void sp_add_used_routine(LEX *lex, Query_arena *arena,
|
|||||||
Remove routines which are only indirectly used by statement from
|
Remove routines which are only indirectly used by statement from
|
||||||
the set of routines used by this statement.
|
the set of routines used by this statement.
|
||||||
|
|
||||||
@param lex LEX representing statement
|
@param prelocking_ctx Prelocking context of the statement
|
||||||
*/
|
*/
|
||||||
|
|
||||||
void sp_remove_not_own_routines(LEX *lex)
|
void sp_remove_not_own_routines(Query_tables_list *prelocking_ctx)
|
||||||
{
|
{
|
||||||
Sroutine_hash_entry *not_own_rt, *next_rt;
|
Sroutine_hash_entry *not_own_rt, *next_rt;
|
||||||
for (not_own_rt= *(Sroutine_hash_entry **)lex->sroutines_list_own_last;
|
for (not_own_rt=
|
||||||
|
*(Sroutine_hash_entry **)prelocking_ctx->sroutines_list_own_last;
|
||||||
not_own_rt; not_own_rt= next_rt)
|
not_own_rt; not_own_rt= next_rt)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@ -1564,12 +1504,13 @@ void sp_remove_not_own_routines(LEX *lex)
|
|||||||
but we want to be more future-proof.
|
but we want to be more future-proof.
|
||||||
*/
|
*/
|
||||||
next_rt= not_own_rt->next;
|
next_rt= not_own_rt->next;
|
||||||
my_hash_delete(&lex->sroutines, (uchar *)not_own_rt);
|
my_hash_delete(&prelocking_ctx->sroutines, (uchar *)not_own_rt);
|
||||||
}
|
}
|
||||||
|
|
||||||
*(Sroutine_hash_entry **)lex->sroutines_list_own_last= NULL;
|
*(Sroutine_hash_entry **)prelocking_ctx->sroutines_list_own_last= NULL;
|
||||||
lex->sroutines_list.next= lex->sroutines_list_own_last;
|
prelocking_ctx->sroutines_list.next= prelocking_ctx->sroutines_list_own_last;
|
||||||
lex->sroutines_list.elements= lex->sroutines_list_own_elements;
|
prelocking_ctx->sroutines_list.elements=
|
||||||
|
prelocking_ctx->sroutines_list_own_elements;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1605,23 +1546,24 @@ void sp_update_sp_used_routines(HASH *dst, HASH *src)
|
|||||||
routines used by statement.
|
routines used by statement.
|
||||||
|
|
||||||
@param thd Thread context
|
@param thd Thread context
|
||||||
@param lex LEX representing statement
|
@param prelocking_ctx Prelocking context of the statement
|
||||||
@param src Hash representing set from which routines will
|
@param src Hash representing set from which routines will
|
||||||
be added
|
be added
|
||||||
@param belong_to_view Uppermost view which uses these routines, 0 if none
|
@param belong_to_view Uppermost view which uses these routines, 0 if none
|
||||||
|
|
||||||
@note
|
@note It will also add elements to end of
|
||||||
It will also add elements to end of 'LEX::sroutines_list' list.
|
'Query_tables_list::sroutines_list' list.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void
|
void
|
||||||
sp_update_stmt_used_routines(THD *thd, LEX *lex, HASH *src,
|
sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx,
|
||||||
TABLE_LIST *belong_to_view)
|
HASH *src, TABLE_LIST *belong_to_view)
|
||||||
{
|
{
|
||||||
for (uint i=0 ; i < src->records ; i++)
|
for (uint i=0 ; i < src->records ; i++)
|
||||||
{
|
{
|
||||||
Sroutine_hash_entry *rt= (Sroutine_hash_entry *)my_hash_element(src, i);
|
Sroutine_hash_entry *rt= (Sroutine_hash_entry *)my_hash_element(src, i);
|
||||||
(void)add_used_routine(lex, thd->stmt_arena, &rt->key, belong_to_view);
|
(void)sp_add_used_routine(prelocking_ctx, thd->stmt_arena, &rt->key,
|
||||||
|
belong_to_view);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1631,254 +1573,104 @@ sp_update_stmt_used_routines(THD *thd, LEX *lex, HASH *src,
|
|||||||
routines used by statement.
|
routines used by statement.
|
||||||
|
|
||||||
@param thd Thread context
|
@param thd Thread context
|
||||||
@param lex LEX representing statement
|
@param prelocking_ctx Prelocking context of the statement
|
||||||
@param src List representing set from which routines will
|
@param src List representing set from which routines will
|
||||||
be added
|
be added
|
||||||
@param belong_to_view Uppermost view which uses these routines, 0 if none
|
@param belong_to_view Uppermost view which uses these routines, 0 if none
|
||||||
|
|
||||||
@note
|
@note It will also add elements to end of
|
||||||
It will also add elements to end of 'LEX::sroutines_list' list.
|
'Query_tables_list::sroutines_list' list.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void sp_update_stmt_used_routines(THD *thd, LEX *lex, SQL_LIST *src,
|
void sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx,
|
||||||
TABLE_LIST *belong_to_view)
|
SQL_LIST *src, TABLE_LIST *belong_to_view)
|
||||||
{
|
{
|
||||||
for (Sroutine_hash_entry *rt= (Sroutine_hash_entry *)src->first;
|
for (Sroutine_hash_entry *rt= (Sroutine_hash_entry *)src->first;
|
||||||
rt; rt= rt->next)
|
rt; rt= rt->next)
|
||||||
(void)add_used_routine(lex, thd->stmt_arena, &rt->key, belong_to_view);
|
(void)sp_add_used_routine(prelocking_ctx, thd->stmt_arena, &rt->key,
|
||||||
|
belong_to_view);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Cache sub-set of routines used by statement, add tables used by these
|
Ensure that routine is present in cache by loading it from the mysql.proc
|
||||||
routines to statement table list. Do the same for all routines used
|
table if needed. Emit an appropriate error if there was a problem during
|
||||||
by these routines.
|
loading.
|
||||||
|
|
||||||
@param thd thread context
|
@param[in] thd Thread context.
|
||||||
@param lex LEX representing statement
|
@param[in] type Type of object (TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE).
|
||||||
@param start first routine from the list of routines to be cached
|
@param[in] name Name of routine.
|
||||||
(this list defines mentioned sub-set).
|
@param[out] sp Pointer to sp_head object for routine, NULL if routine was
|
||||||
@param first_no_prelock If true, don't add tables or cache routines used by
|
not found,
|
||||||
the body of the first routine (i.e. *start)
|
|
||||||
will be executed in non-prelocked mode.
|
|
||||||
@param tabs_changed Set to TRUE some tables were added, FALSE otherwise
|
|
||||||
|
|
||||||
@note
|
@retval 0 Either routine is found and was succesfully loaded into cache
|
||||||
If some function is missing this won't be reported here.
|
or it does not exist.
|
||||||
Instead this fact will be discovered during query execution.
|
@retval non-0 Error while loading routine from mysql,proc table.
|
||||||
|
|
||||||
@retval
|
|
||||||
0 success
|
|
||||||
@retval
|
|
||||||
non-0 failure
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int
|
int sp_cache_routine(THD *thd, int type, sp_name *name, sp_head **sp)
|
||||||
sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex,
|
|
||||||
Sroutine_hash_entry *start,
|
|
||||||
bool first_no_prelock)
|
|
||||||
{
|
{
|
||||||
int ret= 0;
|
int ret= 0;
|
||||||
bool first= TRUE;
|
|
||||||
DBUG_ENTER("sp_cache_routines_and_add_tables_aux");
|
|
||||||
|
|
||||||
for (Sroutine_hash_entry *rt= start; rt; rt= rt->next)
|
DBUG_ENTER("sp_cache_routine");
|
||||||
{
|
|
||||||
sp_name name(thd, rt->key.str, rt->key.length);
|
|
||||||
int type= rt->key.str[0];
|
|
||||||
sp_head *sp;
|
|
||||||
|
|
||||||
/*
|
DBUG_ASSERT(type == TYPE_ENUM_FUNCTION || type == TYPE_ENUM_PROCEDURE);
|
||||||
Triggers can't happen here: their bodies are always processed
|
|
||||||
in sp_cache_routines_and_add_tables_for_triggers().
|
|
||||||
*/
|
|
||||||
DBUG_ASSERT(type == TYPE_ENUM_FUNCTION || type == TYPE_ENUM_PROCEDURE);
|
|
||||||
|
|
||||||
if (!(sp= sp_cache_lookup((type == TYPE_ENUM_FUNCTION ?
|
if (!(*sp= sp_cache_lookup((type == TYPE_ENUM_FUNCTION ?
|
||||||
&thd->sp_func_cache : &thd->sp_proc_cache),
|
&thd->sp_func_cache : &thd->sp_proc_cache),
|
||||||
&name)))
|
name)))
|
||||||
|
{
|
||||||
|
switch ((ret= db_find_routine(thd, type, name, sp)))
|
||||||
{
|
{
|
||||||
switch ((ret= db_find_routine(thd, type, &name, &sp)))
|
case SP_OK:
|
||||||
|
if (type == TYPE_ENUM_FUNCTION)
|
||||||
|
sp_cache_insert(&thd->sp_func_cache, *sp);
|
||||||
|
else
|
||||||
|
sp_cache_insert(&thd->sp_proc_cache, *sp);
|
||||||
|
break;
|
||||||
|
case SP_KEY_NOT_FOUND:
|
||||||
|
ret= SP_OK;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* Query might have been killed, don't set error. */
|
||||||
|
if (thd->killed)
|
||||||
|
break;
|
||||||
|
/*
|
||||||
|
Any error when loading an existing routine is either some problem
|
||||||
|
with the mysql.proc table, or a parse error because the contents
|
||||||
|
has been tampered with (in which case we clear that error).
|
||||||
|
*/
|
||||||
|
if (ret == SP_PARSE_ERROR)
|
||||||
|
thd->clear_error();
|
||||||
|
/*
|
||||||
|
If we cleared the parse error, or when db_find_routine() flagged
|
||||||
|
an error with it's return value without calling my_error(), we
|
||||||
|
set the generic "mysql.proc table corrupt" error here.
|
||||||
|
*/
|
||||||
|
if (! thd->is_error())
|
||||||
{
|
{
|
||||||
case SP_OK:
|
|
||||||
{
|
|
||||||
if (type == TYPE_ENUM_FUNCTION)
|
|
||||||
sp_cache_insert(&thd->sp_func_cache, sp);
|
|
||||||
else
|
|
||||||
sp_cache_insert(&thd->sp_proc_cache, sp);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case SP_KEY_NOT_FOUND:
|
|
||||||
ret= SP_OK;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
/* Query might have been killed, don't set error. */
|
|
||||||
if (thd->killed)
|
|
||||||
break;
|
|
||||||
/*
|
/*
|
||||||
Any error when loading an existing routine is either some problem
|
SP allows full NAME_LEN chars thus he have to allocate enough
|
||||||
with the mysql.proc table, or a parse error because the contents
|
size in bytes. Otherwise there is stack overrun could happen
|
||||||
has been tampered with (in which case we clear that error).
|
if multibyte sequence is `name`. `db` is still safe because the
|
||||||
|
rest of the server checks agains NAME_LEN bytes and not chars.
|
||||||
|
Hence, the overrun happens only if the name is in length > 32 and
|
||||||
|
uses multibyte (cyrillic, greek, etc.)
|
||||||
*/
|
*/
|
||||||
if (ret == SP_PARSE_ERROR)
|
char n[NAME_LEN*2+2];
|
||||||
thd->clear_error();
|
|
||||||
/*
|
|
||||||
If we cleared the parse error, or when db_find_routine() flagged
|
|
||||||
an error with it's return value without calling my_error(), we
|
|
||||||
set the generic "mysql.proc table corrupt" error here.
|
|
||||||
*/
|
|
||||||
if (! thd->is_error())
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
SP allows full NAME_LEN chars thus he have to allocate enough
|
|
||||||
size in bytes. Otherwise there is stack overrun could happen
|
|
||||||
if multibyte sequence is `name`. `db` is still safe because the
|
|
||||||
rest of the server checks agains NAME_LEN bytes and not chars.
|
|
||||||
Hence, the overrun happens only if the name is in length > 32 and
|
|
||||||
uses multibyte (cyrillic, greek, etc.)
|
|
||||||
*/
|
|
||||||
char n[NAME_LEN*2+2];
|
|
||||||
|
|
||||||
/* m_qname.str is not always \0 terminated */
|
/* m_qname.str is not always \0 terminated */
|
||||||
memcpy(n, name.m_qname.str, name.m_qname.length);
|
memcpy(n, name->m_qname.str, name->m_qname.length);
|
||||||
n[name.m_qname.length]= '\0';
|
n[name->m_qname.length]= '\0';
|
||||||
my_error(ER_SP_PROC_TABLE_CORRUPT, MYF(0), n, ret);
|
my_error(ER_SP_PROC_TABLE_CORRUPT, MYF(0), n, ret);
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (sp)
|
|
||||||
{
|
|
||||||
if (!(first && first_no_prelock))
|
|
||||||
{
|
|
||||||
sp_update_stmt_used_routines(thd, lex, &sp->m_sroutines,
|
|
||||||
rt->belong_to_view);
|
|
||||||
(void)sp->add_used_tables_to_table_list(thd, &lex->query_tables_last,
|
|
||||||
rt->belong_to_view);
|
|
||||||
}
|
|
||||||
sp->propagate_attributes(lex);
|
|
||||||
}
|
|
||||||
first= FALSE;
|
|
||||||
}
|
}
|
||||||
DBUG_RETURN(ret);
|
DBUG_RETURN(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
Cache all routines from the set of used by statement, add tables used
|
|
||||||
by those routines to statement table list. Do the same for all routines
|
|
||||||
used by those routines.
|
|
||||||
|
|
||||||
@param thd thread context
|
|
||||||
@param lex LEX representing statement
|
|
||||||
@param first_no_prelock If true, don't add tables or cache routines used by
|
|
||||||
the body of the first routine (i.e. *start)
|
|
||||||
|
|
||||||
@retval
|
|
||||||
0 success
|
|
||||||
@retval
|
|
||||||
non-0 failure
|
|
||||||
*/
|
|
||||||
|
|
||||||
int
|
|
||||||
sp_cache_routines_and_add_tables(THD *thd, LEX *lex, bool first_no_prelock)
|
|
||||||
{
|
|
||||||
return sp_cache_routines_and_add_tables_aux(thd, lex,
|
|
||||||
(Sroutine_hash_entry *)lex->sroutines_list.first,
|
|
||||||
first_no_prelock);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
Add all routines used by view to the set of routines used by
|
|
||||||
statement.
|
|
||||||
|
|
||||||
Add tables used by those routines to statement table list. Do the same
|
|
||||||
for all routines used by these routines.
|
|
||||||
|
|
||||||
@param thd Thread context
|
|
||||||
@param lex LEX representing statement
|
|
||||||
@param view Table list element representing view
|
|
||||||
|
|
||||||
@retval
|
|
||||||
0 success
|
|
||||||
@retval
|
|
||||||
non-0 failure
|
|
||||||
*/
|
|
||||||
|
|
||||||
int
|
|
||||||
sp_cache_routines_and_add_tables_for_view(THD *thd, LEX *lex, TABLE_LIST *view)
|
|
||||||
{
|
|
||||||
Sroutine_hash_entry **last_cached_routine_ptr=
|
|
||||||
(Sroutine_hash_entry **)lex->sroutines_list.next;
|
|
||||||
sp_update_stmt_used_routines(thd, lex, &view->view->sroutines_list,
|
|
||||||
view->top_table());
|
|
||||||
return sp_cache_routines_and_add_tables_aux(thd, lex,
|
|
||||||
*last_cached_routine_ptr, FALSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
Add triggers for table to the set of routines used by statement.
|
|
||||||
Add tables used by them to statement table list. Do the same for
|
|
||||||
all implicitly used routines.
|
|
||||||
|
|
||||||
@param thd thread context
|
|
||||||
@param lex LEX respresenting statement
|
|
||||||
@param table Table list element for table with trigger
|
|
||||||
|
|
||||||
@retval
|
|
||||||
0 success
|
|
||||||
@retval
|
|
||||||
non-0 failure
|
|
||||||
*/
|
|
||||||
|
|
||||||
int
|
|
||||||
sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
|
|
||||||
TABLE_LIST *table)
|
|
||||||
{
|
|
||||||
if (static_cast<int>(table->lock_type) >=
|
|
||||||
static_cast<int>(TL_WRITE_ALLOW_WRITE))
|
|
||||||
{
|
|
||||||
for (int i= 0; i < (int)TRG_EVENT_MAX; i++)
|
|
||||||
{
|
|
||||||
if (table->trg_event_map &
|
|
||||||
static_cast<uint8>(1 << static_cast<int>(i)))
|
|
||||||
{
|
|
||||||
for (int j= 0; j < (int)TRG_ACTION_MAX; j++)
|
|
||||||
{
|
|
||||||
/* We can have only one trigger per action type currently */
|
|
||||||
sp_head *trigger= table->table->triggers->bodies[i][j];
|
|
||||||
if (trigger &&
|
|
||||||
add_used_routine(lex, thd->stmt_arena, &trigger->m_sroutines_key,
|
|
||||||
table->belong_to_view))
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
/* Sic: excludes the trigger key from processing */
|
|
||||||
Sroutine_hash_entry **last_cached_routine_ptr=
|
|
||||||
(Sroutine_hash_entry **)lex->sroutines_list.next;
|
|
||||||
|
|
||||||
trigger->add_used_tables_to_table_list(thd, &lex->query_tables_last,
|
|
||||||
table->belong_to_view);
|
|
||||||
trigger->propagate_attributes(lex);
|
|
||||||
sp_update_stmt_used_routines(thd, lex,
|
|
||||||
&trigger->m_sroutines,
|
|
||||||
table->belong_to_view);
|
|
||||||
|
|
||||||
ret= sp_cache_routines_and_add_tables_aux(thd, lex,
|
|
||||||
*last_cached_routine_ptr,
|
|
||||||
FALSE);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Generates the CREATE... string from the table information.
|
Generates the CREATE... string from the table information.
|
||||||
|
|
||||||
|
52
sql/sp.h
52
sql/sp.h
@ -42,6 +42,9 @@ sp_head *
|
|||||||
sp_find_routine(THD *thd, int type, sp_name *name,
|
sp_find_routine(THD *thd, int type, sp_name *name,
|
||||||
sp_cache **cp, bool cache_only);
|
sp_cache **cp, bool cache_only);
|
||||||
|
|
||||||
|
int
|
||||||
|
sp_cache_routine(THD *thd, int type, sp_name *name, sp_head **sp);
|
||||||
|
|
||||||
bool
|
bool
|
||||||
sp_exist_routines(THD *thd, TABLE_LIST *procs, bool any);
|
sp_exist_routines(THD *thd, TABLE_LIST *procs, bool any);
|
||||||
|
|
||||||
@ -60,22 +63,45 @@ sp_update_routine(THD *thd, int type, sp_name *name, st_sp_chistics *chistics);
|
|||||||
int
|
int
|
||||||
sp_drop_routine(THD *thd, int type, sp_name *name);
|
sp_drop_routine(THD *thd, int type, sp_name *name);
|
||||||
|
|
||||||
/*
|
|
||||||
Procedures for pre-caching of stored routines and building table list
|
/**
|
||||||
for prelocking.
|
Structure that represents element in the set of stored routines
|
||||||
|
used by statement or routine.
|
||||||
*/
|
*/
|
||||||
void sp_get_prelocking_info(THD *thd, bool *need_prelocking,
|
|
||||||
bool *first_no_prelocking);
|
struct Sroutine_hash_entry
|
||||||
void sp_add_used_routine(LEX *lex, Query_arena *arena,
|
{
|
||||||
|
/**
|
||||||
|
Set key consisting of one-byte routine type and quoted routine name.
|
||||||
|
*/
|
||||||
|
LEX_STRING key;
|
||||||
|
/**
|
||||||
|
Next element in list linking all routines in set. See also comments
|
||||||
|
for LEX::sroutine/sroutine_list and sp_head::m_sroutines.
|
||||||
|
*/
|
||||||
|
Sroutine_hash_entry *next;
|
||||||
|
/**
|
||||||
|
Uppermost view which directly or indirectly uses this routine.
|
||||||
|
0 if routine is not used in view. Note that it also can be 0 if
|
||||||
|
statement uses routine both via view and directly.
|
||||||
|
*/
|
||||||
|
TABLE_LIST *belong_to_view;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Procedures for handling sets of stored routines used by statement or routine.
|
||||||
|
*/
|
||||||
|
void sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena,
|
||||||
sp_name *rt, char rt_type);
|
sp_name *rt, char rt_type);
|
||||||
void sp_remove_not_own_routines(LEX *lex);
|
bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena,
|
||||||
|
const LEX_STRING *key, TABLE_LIST *belong_to_view);
|
||||||
|
void sp_remove_not_own_routines(Query_tables_list *prelocking_ctx);
|
||||||
void sp_update_sp_used_routines(HASH *dst, HASH *src);
|
void sp_update_sp_used_routines(HASH *dst, HASH *src);
|
||||||
int sp_cache_routines_and_add_tables(THD *thd, LEX *lex,
|
void sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx,
|
||||||
bool first_no_prelock);
|
HASH *src, TABLE_LIST *belong_to_view);
|
||||||
int sp_cache_routines_and_add_tables_for_view(THD *thd, LEX *lex,
|
void sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx,
|
||||||
TABLE_LIST *view);
|
SQL_LIST *src, TABLE_LIST *belong_to_view);
|
||||||
int sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
|
|
||||||
TABLE_LIST *table);
|
|
||||||
|
|
||||||
extern "C" uchar* sp_sroutine_key(const uchar *ptr, size_t *plen,
|
extern "C" uchar* sp_sroutine_key(const uchar *ptr, size_t *plen,
|
||||||
my_bool first);
|
my_bool first);
|
||||||
|
@ -454,10 +454,10 @@ public:
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
This method is intended for attributes of a routine which need
|
This method is intended for attributes of a routine which need
|
||||||
to propagate upwards to the LEX of the caller (when a property of a
|
to propagate upwards to the Query_tables_list of the caller (when
|
||||||
sp_head needs to "taint" the caller).
|
a property of a sp_head needs to "taint" the calling statement).
|
||||||
*/
|
*/
|
||||||
void propagate_attributes(LEX *lex)
|
void propagate_attributes(Query_tables_list *prelocking_ctx)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
If this routine needs row-based binary logging, the entire top statement
|
If this routine needs row-based binary logging, the entire top statement
|
||||||
@ -466,7 +466,7 @@ public:
|
|||||||
the substatements not).
|
the substatements not).
|
||||||
*/
|
*/
|
||||||
if (m_flags & BINLOG_ROW_BASED_IF_MIXED)
|
if (m_flags & BINLOG_ROW_BASED_IF_MIXED)
|
||||||
lex->set_stmt_unsafe();
|
prelocking_ctx->set_stmt_unsafe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
440
sql/sql_base.cc
440
sql/sql_base.cc
@ -3714,34 +3714,99 @@ thr_lock_type read_lock_type_for_table(THD *thd, TABLE *table)
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Perform steps of prelocking algorithm for elements of the
|
||||||
|
prelocking set other than tables. E.g. cache routines and, if
|
||||||
|
prelocking strategy prescribes so, extend the prelocking set with
|
||||||
|
tables and routines used by them.
|
||||||
|
|
||||||
|
@param[in] thd Thread context.
|
||||||
|
@param[in] prelocking_ctx Prelocking context.
|
||||||
|
@param[in] start First element in the list representing
|
||||||
|
subset of the prelocking set to be
|
||||||
|
processed.
|
||||||
|
@param[in] prelocking_strategy Strategy which specifies how the
|
||||||
|
prelocking set should be extended when
|
||||||
|
one of its elements is processed.
|
||||||
|
@param[out] need_prelocking Set to TRUE if it was detected that this
|
||||||
|
statement will require prelocked mode for
|
||||||
|
its execution, not touched otherwise.
|
||||||
|
|
||||||
|
@retval FALSE Success.
|
||||||
|
@retval TRUE Failure (Conflicting metadata lock, OOM, other errors).
|
||||||
|
*/
|
||||||
|
|
||||||
|
static bool
|
||||||
|
open_routines(THD *thd, Query_tables_list *prelocking_ctx,
|
||||||
|
Sroutine_hash_entry *start,
|
||||||
|
Prelocking_strategy *prelocking_strategy,
|
||||||
|
bool *need_prelocking)
|
||||||
|
{
|
||||||
|
DBUG_ENTER("open_routines");
|
||||||
|
|
||||||
|
for (Sroutine_hash_entry *rt= start; rt; rt= rt->next)
|
||||||
|
{
|
||||||
|
int type= rt->key.str[0];
|
||||||
|
|
||||||
|
switch (type)
|
||||||
|
{
|
||||||
|
case TYPE_ENUM_FUNCTION:
|
||||||
|
case TYPE_ENUM_PROCEDURE:
|
||||||
|
{
|
||||||
|
sp_name name(thd, rt->key.str, rt->key.length);
|
||||||
|
sp_head *sp;
|
||||||
|
|
||||||
|
if (sp_cache_routine(thd, type, &name, &sp))
|
||||||
|
DBUG_RETURN(TRUE);
|
||||||
|
|
||||||
|
if (sp)
|
||||||
|
{
|
||||||
|
prelocking_strategy->handle_routine(thd, prelocking_ctx, rt, sp,
|
||||||
|
need_prelocking);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TYPE_ENUM_TRIGGER:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
/* Impossible type value. */
|
||||||
|
DBUG_ASSERT(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DBUG_RETURN(FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
Open all tables in list
|
Open all tables in list
|
||||||
|
|
||||||
SYNOPSIS
|
@param[in] thd Thread context.
|
||||||
open_tables()
|
@param[in,out] start List of tables to be open (it can be adjusted for
|
||||||
thd - thread handler
|
statement that uses tables only implicitly, e.g.
|
||||||
start - list of tables in/out
|
for "SELECT f1()").
|
||||||
counter - number of opened tables will be return using this parameter
|
@param[out] counter Number of tables which were open.
|
||||||
flags - bitmap of flags to modify how the tables will be open:
|
@param[in] flags Bitmap of flags to modify how the tables will be
|
||||||
MYSQL_LOCK_IGNORE_FLUSH - open table even if someone has
|
open, see open_table() description for details.
|
||||||
done a flush or namelock on it.
|
@param[in] prelocking_strategy Strategy which specifies how prelocking
|
||||||
|
algorithm should work for this statement.
|
||||||
|
|
||||||
NOTE
|
@note
|
||||||
Unless we are already in prelocked mode, this function will also precache
|
Unless we are already in prelocked mode and prelocking strategy prescribes
|
||||||
all SP/SFs explicitly or implicitly (via views and triggers) used by the
|
so this function will also precache all SP/SFs explicitly or implicitly
|
||||||
query and add tables needed for their execution to table list. If resulting
|
(via views and triggers) used by the query and add tables needed for their
|
||||||
tables list will be non empty it will mark query as requiring precaching.
|
execution to table list. Statement that uses SFs, invokes triggers or
|
||||||
|
requires foreign key checks will be marked as requiring prelocking.
|
||||||
Prelocked mode will be enabled for such query during lock_tables() call.
|
Prelocked mode will be enabled for such query during lock_tables() call.
|
||||||
|
|
||||||
If query for which we are opening tables is already marked as requiring
|
If query for which we are opening tables is already marked as requiring
|
||||||
prelocking it won't do such precaching and will simply reuse table list
|
prelocking it won't do such precaching and will simply reuse table list
|
||||||
which is already built.
|
which is already built.
|
||||||
|
|
||||||
RETURN
|
@retval 0 OK
|
||||||
0 - OK
|
@retval -1 Error.
|
||||||
-1 - error
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
|
int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags,
|
||||||
|
Prelocking_strategy *prelocking_strategy)
|
||||||
{
|
{
|
||||||
TABLE_LIST *tables= NULL;
|
TABLE_LIST *tables= NULL;
|
||||||
Open_table_context ot_ctx(thd);
|
Open_table_context ot_ctx(thd);
|
||||||
@ -3786,13 +3851,14 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
|
|||||||
!thd->lex->requires_prelocking() &&
|
!thd->lex->requires_prelocking() &&
|
||||||
thd->lex->uses_stored_routines())
|
thd->lex->uses_stored_routines())
|
||||||
{
|
{
|
||||||
bool first_no_prelocking, need_prelocking;
|
bool need_prelocking= FALSE;
|
||||||
TABLE_LIST **save_query_tables_last= thd->lex->query_tables_last;
|
TABLE_LIST **save_query_tables_last= thd->lex->query_tables_last;
|
||||||
|
|
||||||
DBUG_ASSERT(thd->lex->query_tables == *start);
|
DBUG_ASSERT(thd->lex->query_tables == *start);
|
||||||
sp_get_prelocking_info(thd, &need_prelocking, &first_no_prelocking);
|
|
||||||
|
|
||||||
if (sp_cache_routines_and_add_tables(thd, thd->lex, first_no_prelocking))
|
if (open_routines(thd, thd->lex,
|
||||||
|
(Sroutine_hash_entry *)thd->lex->sroutines_list.first,
|
||||||
|
prelocking_strategy, &need_prelocking))
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
Serious error during reading stored routines from mysql.proc table.
|
Serious error during reading stored routines from mysql.proc table.
|
||||||
@ -3973,27 +4039,54 @@ int open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
If we are not already in prelocked mode and extended table list is not
|
If we are not already in prelocked mode and extended table list is not
|
||||||
yet built and we have trigger for table being opened then we should
|
yet built we might have to build the prelocking set for this statement.
|
||||||
cache all routines used by its triggers and add their tables to
|
|
||||||
prelocking list.
|
Since currently no prelocking strategy prescribes doing anything for
|
||||||
If we lock table for reading we won't update it so there is no need to
|
tables which are only read, we do below checks only if table is going
|
||||||
process its triggers since they never will be activated.
|
to be changed.
|
||||||
*/
|
*/
|
||||||
if (thd->locked_tables_mode <= LTM_LOCK_TABLES &&
|
if (thd->locked_tables_mode <= LTM_LOCK_TABLES &&
|
||||||
!thd->lex->requires_prelocking() &&
|
!thd->lex->requires_prelocking() &&
|
||||||
tables->trg_event_map && tables->table->triggers &&
|
|
||||||
tables->lock_type >= TL_WRITE_ALLOW_WRITE)
|
tables->lock_type >= TL_WRITE_ALLOW_WRITE)
|
||||||
{
|
{
|
||||||
if (!query_tables_last_own)
|
bool need_prelocking= FALSE;
|
||||||
query_tables_last_own= thd->lex->query_tables_last;
|
bool not_used;
|
||||||
if (sp_cache_routines_and_add_tables_for_triggers(thd, thd->lex,
|
TABLE_LIST **save_query_tables_last= thd->lex->query_tables_last;
|
||||||
tables))
|
Sroutine_hash_entry **sroutines_next=
|
||||||
|
(Sroutine_hash_entry **)thd->lex->sroutines_list.next;
|
||||||
|
|
||||||
|
/*
|
||||||
|
Extend statement's table list and the prelocking set with
|
||||||
|
tables and routines according to the current prelocking
|
||||||
|
strategy.
|
||||||
|
|
||||||
|
For example, for DML statements we need to add tables and routines
|
||||||
|
used by triggers which are going to be invoked for this element of
|
||||||
|
table list and also add tables required for handling of foreign keys.
|
||||||
|
*/
|
||||||
|
error= prelocking_strategy->handle_table(thd, thd->lex, tables,
|
||||||
|
&need_prelocking);
|
||||||
|
|
||||||
|
if (need_prelocking && ! query_tables_last_own)
|
||||||
|
query_tables_last_own= save_query_tables_last;
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
result= -1;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Process elements of the prelocking set which were added
|
||||||
|
by the above invocation of Prelocking_strategy method.
|
||||||
|
|
||||||
|
For example, if new element is a routine, cache it and then, if
|
||||||
|
prelocking strategy prescribes so, add tables it uses to the table
|
||||||
|
list and routines it might invoke to the prelocking set.
|
||||||
|
*/
|
||||||
|
if (open_routines(thd, thd->lex, *sroutines_next, prelocking_strategy,
|
||||||
|
¬_used))
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
Serious error during reading stored routines from mysql.proc table.
|
|
||||||
Something's wrong with the table or its contents, and an error has
|
|
||||||
been emitted; we must abort.
|
|
||||||
*/
|
|
||||||
result= -1;
|
result= -1;
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
@ -4039,13 +4132,27 @@ process_view_routines:
|
|||||||
*/
|
*/
|
||||||
if (tables->view &&
|
if (tables->view &&
|
||||||
thd->locked_tables_mode <= LTM_LOCK_TABLES &&
|
thd->locked_tables_mode <= LTM_LOCK_TABLES &&
|
||||||
!thd->lex->requires_prelocking() &&
|
!thd->lex->requires_prelocking())
|
||||||
tables->view->uses_stored_routines())
|
|
||||||
{
|
{
|
||||||
/* We have at least one table in TL here. */
|
bool need_prelocking= FALSE;
|
||||||
if (!query_tables_last_own)
|
bool not_used;
|
||||||
query_tables_last_own= thd->lex->query_tables_last;
|
TABLE_LIST **save_query_tables_last= thd->lex->query_tables_last;
|
||||||
if (sp_cache_routines_and_add_tables_for_view(thd, thd->lex, tables))
|
Sroutine_hash_entry **sroutines_next=
|
||||||
|
(Sroutine_hash_entry **)thd->lex->sroutines_list.next;
|
||||||
|
|
||||||
|
error= prelocking_strategy->handle_view(thd, thd->lex, tables,
|
||||||
|
&need_prelocking);
|
||||||
|
|
||||||
|
if (need_prelocking && ! query_tables_last_own)
|
||||||
|
query_tables_last_own= save_query_tables_last;
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
|
result= -1;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
if (open_routines(thd, thd->lex, *sroutines_next, prelocking_strategy,
|
||||||
|
¬_used))
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
Serious error during reading stored routines from mysql.proc table.
|
Serious error during reading stored routines from mysql.proc table.
|
||||||
@ -4097,6 +4204,220 @@ process_view_routines:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Defines how prelocking algorithm for DML statements should handle routines:
|
||||||
|
- For CALL statements we do unrolling (i.e. open and lock tables for each
|
||||||
|
sub-statement individually). So for such statements prelocking is enabled
|
||||||
|
only if stored functions are used in parameter list and only for period
|
||||||
|
during which we calculate values of parameters. Thus in this strategy we
|
||||||
|
ignore procedure which is directly called by such statement and extend
|
||||||
|
the prelocking set only with tables/functions used by SF called from the
|
||||||
|
parameter list.
|
||||||
|
- For any other statement any routine which is directly or indirectly called
|
||||||
|
by statement is going to be executed in prelocked mode. So in this case we
|
||||||
|
simply add all tables and routines used by it to the prelocking set.
|
||||||
|
|
||||||
|
@param[in] thd Thread context.
|
||||||
|
@param[in] prelocking_ctx Prelocking context of the statement.
|
||||||
|
@param[in] rt Prelocking set element describing routine.
|
||||||
|
@param[in] sp Routine body.
|
||||||
|
@param[out] need_prelocking Set to TRUE if method detects that prelocking
|
||||||
|
required, not changed otherwise.
|
||||||
|
|
||||||
|
@retval FALSE Success.
|
||||||
|
@retval TRUE Failure (OOM).
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool DML_prelocking_strategy::
|
||||||
|
handle_routine(THD *thd, Query_tables_list *prelocking_ctx,
|
||||||
|
Sroutine_hash_entry *rt, sp_head *sp, bool *need_prelocking)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
We assume that for any "CALL proc(...)" statement sroutines_list will
|
||||||
|
have 'proc' as first element (it may have several, consider e.g.
|
||||||
|
"proc(sp_func(...)))". This property is currently guaranted by the
|
||||||
|
parser.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (rt != (Sroutine_hash_entry*)prelocking_ctx->sroutines_list.first ||
|
||||||
|
rt->key.str[0] != TYPE_ENUM_PROCEDURE)
|
||||||
|
{
|
||||||
|
*need_prelocking= TRUE;
|
||||||
|
sp_update_stmt_used_routines(thd, prelocking_ctx, &sp->m_sroutines,
|
||||||
|
rt->belong_to_view);
|
||||||
|
(void)sp->add_used_tables_to_table_list(thd,
|
||||||
|
&prelocking_ctx->query_tables_last,
|
||||||
|
rt->belong_to_view);
|
||||||
|
}
|
||||||
|
sp->propagate_attributes(prelocking_ctx);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Defines how prelocking algorithm for DML statements should handle table list
|
||||||
|
elements:
|
||||||
|
- If table has triggers we should add all tables and routines
|
||||||
|
used by them to the prelocking set.
|
||||||
|
|
||||||
|
We do not need to acquire metadata locks on trigger names
|
||||||
|
in DML statements, since all DDL statements
|
||||||
|
that change trigger metadata always lock their
|
||||||
|
subject tables.
|
||||||
|
|
||||||
|
@param[in] thd Thread context.
|
||||||
|
@param[in] prelocking_ctx Prelocking context of the statement.
|
||||||
|
@param[in] table_list Table list element for table.
|
||||||
|
@param[in] sp Routine body.
|
||||||
|
@param[out] need_prelocking Set to TRUE if method detects that prelocking
|
||||||
|
required, not changed otherwise.
|
||||||
|
|
||||||
|
@retval FALSE Success.
|
||||||
|
@retval TRUE Failure (OOM).
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool DML_prelocking_strategy::
|
||||||
|
handle_table(THD *thd, Query_tables_list *prelocking_ctx,
|
||||||
|
TABLE_LIST *table_list, bool *need_prelocking)
|
||||||
|
{
|
||||||
|
/* We rely on a caller to check that table is going to be changed. */
|
||||||
|
DBUG_ASSERT(table_list->lock_type >= TL_WRITE_ALLOW_WRITE);
|
||||||
|
|
||||||
|
if (table_list->trg_event_map)
|
||||||
|
{
|
||||||
|
if (table_list->table->triggers)
|
||||||
|
{
|
||||||
|
*need_prelocking= TRUE;
|
||||||
|
|
||||||
|
if (table_list->table->triggers->
|
||||||
|
add_tables_and_routines_for_triggers(thd, prelocking_ctx, table_list))
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Defines how prelocking algorithm for DML statements should handle view -
|
||||||
|
all view routines should be added to the prelocking set.
|
||||||
|
|
||||||
|
@param[in] thd Thread context.
|
||||||
|
@param[in] prelocking_ctx Prelocking context of the statement.
|
||||||
|
@param[in] table_list Table list element for view.
|
||||||
|
@param[in] sp Routine body.
|
||||||
|
@param[out] need_prelocking Set to TRUE if method detects that prelocking
|
||||||
|
required, not changed otherwise.
|
||||||
|
|
||||||
|
@retval FALSE Success.
|
||||||
|
@retval TRUE Failure (OOM).
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool DML_prelocking_strategy::
|
||||||
|
handle_view(THD *thd, Query_tables_list *prelocking_ctx,
|
||||||
|
TABLE_LIST *table_list, bool *need_prelocking)
|
||||||
|
{
|
||||||
|
if (table_list->view->uses_stored_routines())
|
||||||
|
{
|
||||||
|
*need_prelocking= TRUE;
|
||||||
|
|
||||||
|
sp_update_stmt_used_routines(thd, prelocking_ctx,
|
||||||
|
&table_list->view->sroutines_list,
|
||||||
|
table_list->top_table());
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Defines how prelocking algorithm for LOCK TABLES statement should handle
|
||||||
|
table list elements.
|
||||||
|
|
||||||
|
@param[in] thd Thread context.
|
||||||
|
@param[in] prelocking_ctx Prelocking context of the statement.
|
||||||
|
@param[in] table_list Table list element for table.
|
||||||
|
@param[in] sp Routine body.
|
||||||
|
@param[out] need_prelocking Set to TRUE if method detects that prelocking
|
||||||
|
required, not changed otherwise.
|
||||||
|
|
||||||
|
@retval FALSE Success.
|
||||||
|
@retval TRUE Failure (OOM).
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool Lock_tables_prelocking_strategy::
|
||||||
|
handle_table(THD *thd, Query_tables_list *prelocking_ctx,
|
||||||
|
TABLE_LIST *table_list, bool *need_prelocking)
|
||||||
|
{
|
||||||
|
if (DML_prelocking_strategy::handle_table(thd, prelocking_ctx, table_list,
|
||||||
|
need_prelocking))
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
/* We rely on a caller to check that table is going to be changed. */
|
||||||
|
DBUG_ASSERT(table_list->lock_type >= TL_WRITE_ALLOW_WRITE);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Defines how prelocking algorithm for ALTER TABLE statement should handle
|
||||||
|
routines - do nothing as this statement is not supposed to call routines.
|
||||||
|
|
||||||
|
We still can end up in this method when someone tries
|
||||||
|
to define a foreign key referencing a view, and not just
|
||||||
|
a simple view, but one that uses stored routines.
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool Alter_table_prelocking_strategy::
|
||||||
|
handle_routine(THD *thd, Query_tables_list *prelocking_ctx,
|
||||||
|
Sroutine_hash_entry *rt, sp_head *sp, bool *need_prelocking)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Defines how prelocking algorithm for ALTER TABLE statement should handle
|
||||||
|
table list elements.
|
||||||
|
|
||||||
|
Unlike in DML, we do not process triggers here.
|
||||||
|
|
||||||
|
@param[in] thd Thread context.
|
||||||
|
@param[in] prelocking_ctx Prelocking context of the statement.
|
||||||
|
@param[in] table_list Table list element for table.
|
||||||
|
@param[in] sp Routine body.
|
||||||
|
@param[out] need_prelocking Set to TRUE if method detects that prelocking
|
||||||
|
required, not changed otherwise.
|
||||||
|
|
||||||
|
|
||||||
|
@retval FALSE Success.
|
||||||
|
@retval TRUE Failure (OOM).
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool Alter_table_prelocking_strategy::
|
||||||
|
handle_table(THD *thd, Query_tables_list *prelocking_ctx,
|
||||||
|
TABLE_LIST *table_list, bool *need_prelocking)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Defines how prelocking algorithm for ALTER TABLE statement
|
||||||
|
should handle view - do nothing. We don't need to add view
|
||||||
|
routines to the prelocking set in this case as view is not going
|
||||||
|
to be materialized.
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool Alter_table_prelocking_strategy::
|
||||||
|
handle_view(THD *thd, Query_tables_list *prelocking_ctx,
|
||||||
|
TABLE_LIST *table_list, bool *need_prelocking)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Check that lock is ok for tables; Call start stmt if ok
|
Check that lock is ok for tables; Call start stmt if ok
|
||||||
|
|
||||||
@ -4312,34 +4633,35 @@ end:
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/**
|
||||||
Open all tables in list, locks them and optionally process derived tables.
|
Open all tables in list, locks them and optionally process derived tables.
|
||||||
|
|
||||||
SYNOPSIS
|
@param thd Thread context.
|
||||||
open_and_lock_tables_derived()
|
@param tables List of tables for open and locking.
|
||||||
thd - thread handler
|
@param derived If to handle derived tables.
|
||||||
tables - list of tables for open&locking
|
@param flags Bitmap of options to be used to open and lock
|
||||||
flags - set of options to be used to open and lock tables (see
|
tables (see open_tables() and mysql_lock_tables()
|
||||||
open_tables() and mysql_lock_tables() for details).
|
for details).
|
||||||
derived - if to handle derived tables
|
@param prelocking_strategy Strategy which specifies how prelocking algorithm
|
||||||
|
should work for this statement.
|
||||||
|
|
||||||
RETURN
|
@note
|
||||||
FALSE - ok
|
|
||||||
TRUE - error
|
|
||||||
|
|
||||||
NOTE
|
|
||||||
The lock will automaticaly be freed by close_thread_tables()
|
The lock will automaticaly be freed by close_thread_tables()
|
||||||
|
|
||||||
NOTE
|
@note
|
||||||
There are two convenience functions:
|
There are several convenience functions, e.g. :
|
||||||
- simple_open_n_lock_tables(thd, tables) without derived handling
|
- simple_open_n_lock_tables(thd, tables) without derived handling
|
||||||
- open_and_lock_tables(thd, tables) with derived handling
|
- open_and_lock_tables(thd, tables) with derived handling
|
||||||
Both inline functions call open_and_lock_tables_derived() with
|
Both inline functions call open_and_lock_tables_derived() with
|
||||||
the third argument set appropriately.
|
the third argument set appropriately.
|
||||||
|
|
||||||
|
@retval FALSE OK.
|
||||||
|
@retval TRUE Error
|
||||||
*/
|
*/
|
||||||
|
|
||||||
int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived,
|
int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables,
|
||||||
uint flags)
|
bool derived, uint flags,
|
||||||
|
Prelocking_strategy *prelocking_strategy)
|
||||||
{
|
{
|
||||||
uint counter;
|
uint counter;
|
||||||
bool need_reopen;
|
bool need_reopen;
|
||||||
@ -4349,7 +4671,7 @@ int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived,
|
|||||||
|
|
||||||
for ( ; ; )
|
for ( ; ; )
|
||||||
{
|
{
|
||||||
if (open_tables(thd, &tables, &counter, flags))
|
if (open_tables(thd, &tables, &counter, flags, prelocking_strategy))
|
||||||
DBUG_RETURN(-1);
|
DBUG_RETURN(-1);
|
||||||
|
|
||||||
DBUG_EXECUTE_IF("sleep_open_and_lock_after_open", {
|
DBUG_EXECUTE_IF("sleep_open_and_lock_after_open", {
|
||||||
|
@ -38,6 +38,7 @@ class sp_rcontext;
|
|||||||
class sp_cache;
|
class sp_cache;
|
||||||
class Parser_state;
|
class Parser_state;
|
||||||
class Rows_log_event;
|
class Rows_log_event;
|
||||||
|
class Sroutine_hash_entry;
|
||||||
|
|
||||||
enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE };
|
enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE };
|
||||||
enum enum_ha_read_modes { RFIRST, RNEXT, RPREV, RLAST, RKEY, RNEXT_SAME };
|
enum enum_ha_read_modes { RFIRST, RNEXT, RPREV, RLAST, RKEY, RNEXT_SAME };
|
||||||
@ -1181,6 +1182,89 @@ private:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
An abstract class for a strategy specifying how the prelocking
|
||||||
|
algorithm should extend the prelocking set while processing
|
||||||
|
already existing elements in the set.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Prelocking_strategy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual ~Prelocking_strategy() { }
|
||||||
|
|
||||||
|
virtual bool handle_routine(THD *thd, Query_tables_list *prelocking_ctx,
|
||||||
|
Sroutine_hash_entry *rt, sp_head *sp,
|
||||||
|
bool *need_prelocking) = 0;
|
||||||
|
virtual bool handle_table(THD *thd, Query_tables_list *prelocking_ctx,
|
||||||
|
TABLE_LIST *table_list, bool *need_prelocking) = 0;
|
||||||
|
virtual bool handle_view(THD *thd, Query_tables_list *prelocking_ctx,
|
||||||
|
TABLE_LIST *table_list, bool *need_prelocking)= 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
A Strategy for prelocking algorithm suitable for DML statements.
|
||||||
|
|
||||||
|
Ensures that all tables used by all statement's SF/SP/triggers and
|
||||||
|
required for foreign key checks are prelocked and SF/SPs used are
|
||||||
|
cached.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class DML_prelocking_strategy : public Prelocking_strategy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual bool handle_routine(THD *thd, Query_tables_list *prelocking_ctx,
|
||||||
|
Sroutine_hash_entry *rt, sp_head *sp,
|
||||||
|
bool *need_prelocking);
|
||||||
|
virtual bool handle_table(THD *thd, Query_tables_list *prelocking_ctx,
|
||||||
|
TABLE_LIST *table_list, bool *need_prelocking);
|
||||||
|
virtual bool handle_view(THD *thd, Query_tables_list *prelocking_ctx,
|
||||||
|
TABLE_LIST *table_list, bool *need_prelocking);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
A strategy for prelocking algorithm to be used for LOCK TABLES
|
||||||
|
statement.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Lock_tables_prelocking_strategy : public DML_prelocking_strategy
|
||||||
|
{
|
||||||
|
virtual bool handle_table(THD *thd, Query_tables_list *prelocking_ctx,
|
||||||
|
TABLE_LIST *table_list, bool *need_prelocking);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Strategy for prelocking algorithm to be used for ALTER TABLE statements.
|
||||||
|
|
||||||
|
Unlike DML or LOCK TABLES strategy, it doesn't
|
||||||
|
prelock triggers, views or stored routines, since they are not
|
||||||
|
used during ALTER.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Alter_table_prelocking_strategy : public Prelocking_strategy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
Alter_table_prelocking_strategy(Alter_info *alter_info)
|
||||||
|
: m_alter_info(alter_info)
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual bool handle_routine(THD *thd, Query_tables_list *prelocking_ctx,
|
||||||
|
Sroutine_hash_entry *rt, sp_head *sp,
|
||||||
|
bool *need_prelocking);
|
||||||
|
virtual bool handle_table(THD *thd, Query_tables_list *prelocking_ctx,
|
||||||
|
TABLE_LIST *table_list, bool *need_prelocking);
|
||||||
|
virtual bool handle_view(THD *thd, Query_tables_list *prelocking_ctx,
|
||||||
|
TABLE_LIST *table_list, bool *need_prelocking);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Alter_info *m_alter_info;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
A context of open_tables() function, used to recover
|
A context of open_tables() function, used to recover
|
||||||
from a failed open_table() attempt.
|
from a failed open_table() attempt.
|
||||||
|
@ -1052,6 +1052,17 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Return a pointer to the last element in query table list. */
|
||||||
|
TABLE_LIST *last_table()
|
||||||
|
{
|
||||||
|
/* Don't use offsetof() macro in order to avoid warnings. */
|
||||||
|
return query_tables ?
|
||||||
|
(TABLE_LIST*) ((char*) query_tables_last -
|
||||||
|
((char*) &(query_tables->next_global) -
|
||||||
|
(char*) query_tables)) :
|
||||||
|
0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Has the parser/scanner detected that this statement is unsafe?
|
Has the parser/scanner detected that this statement is unsafe?
|
||||||
*/
|
*/
|
||||||
@ -1751,6 +1762,12 @@ struct LEX: public Query_tables_list
|
|||||||
bool subqueries, ignore;
|
bool subqueries, ignore;
|
||||||
st_parsing_options parsing_options;
|
st_parsing_options parsing_options;
|
||||||
Alter_info alter_info;
|
Alter_info alter_info;
|
||||||
|
/*
|
||||||
|
For CREATE TABLE statement last element of table list which is not
|
||||||
|
part of SELECT or LIKE part (i.e. either element for table we are
|
||||||
|
creating or last of tables referenced by foreign keys).
|
||||||
|
*/
|
||||||
|
TABLE_LIST *create_last_non_select_table;
|
||||||
/* Prepared statements SQL syntax:*/
|
/* Prepared statements SQL syntax:*/
|
||||||
LEX_STRING prepared_stmt_name; /* Statement name (in all queries) */
|
LEX_STRING prepared_stmt_name; /* Statement name (in all queries) */
|
||||||
/*
|
/*
|
||||||
|
@ -3342,9 +3342,14 @@ end_with_restore_list:
|
|||||||
thd->options|= OPTION_TABLE_LOCK;
|
thd->options|= OPTION_TABLE_LOCK;
|
||||||
thd->in_lock_tables=1;
|
thd->in_lock_tables=1;
|
||||||
|
|
||||||
res= (open_and_lock_tables_derived(thd, all_tables, FALSE,
|
{
|
||||||
MYSQL_OPEN_TAKE_UPGRADABLE_MDL) ||
|
Lock_tables_prelocking_strategy lock_tables_prelocking_strategy;
|
||||||
thd->locked_tables_list.init_locked_tables(thd));
|
|
||||||
|
res= (open_and_lock_tables_derived(thd, all_tables, FALSE,
|
||||||
|
MYSQL_OPEN_TAKE_UPGRADABLE_MDL,
|
||||||
|
&lock_tables_prelocking_strategy) ||
|
||||||
|
thd->locked_tables_list.init_locked_tables(thd));
|
||||||
|
}
|
||||||
|
|
||||||
thd->in_lock_tables= 0;
|
thd->in_lock_tables= 0;
|
||||||
|
|
||||||
|
@ -6560,9 +6560,26 @@ view_err:
|
|||||||
DBUG_RETURN(error);
|
DBUG_RETURN(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(table= open_n_lock_single_table(thd, table_list, TL_WRITE_ALLOW_READ,
|
|
||||||
MYSQL_OPEN_TAKE_UPGRADABLE_MDL)))
|
/*
|
||||||
|
Code below can handle only base tables so ensure that we won't open a view.
|
||||||
|
Note that RENAME TABLE the only ALTER clause which is supported for views
|
||||||
|
has been already processed.
|
||||||
|
*/
|
||||||
|
table_list->required_type= FRMTYPE_TABLE;
|
||||||
|
|
||||||
|
Alter_table_prelocking_strategy alter_prelocking_strategy(alter_info);
|
||||||
|
|
||||||
|
error= open_and_lock_tables_derived(thd, table_list, FALSE,
|
||||||
|
MYSQL_OPEN_TAKE_UPGRADABLE_MDL,
|
||||||
|
&alter_prelocking_strategy);
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
{
|
||||||
DBUG_RETURN(TRUE);
|
DBUG_RETURN(TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
table= table_list->table;
|
||||||
table->use_all_columns();
|
table->use_all_columns();
|
||||||
mdl_ticket= table->mdl_ticket;
|
mdl_ticket= table->mdl_ticket;
|
||||||
|
|
||||||
@ -6572,7 +6589,8 @@ view_err:
|
|||||||
set of tables from the old table or to open a new TABLE object for
|
set of tables from the old table or to open a new TABLE object for
|
||||||
an extended list and verify that they belong to locked tables.
|
an extended list and verify that they belong to locked tables.
|
||||||
*/
|
*/
|
||||||
if (thd->locked_tables_mode &&
|
if ((thd->locked_tables_mode == LTM_LOCK_TABLES ||
|
||||||
|
thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES) &&
|
||||||
(create_info->used_fields & HA_CREATE_USED_UNION) &&
|
(create_info->used_fields & HA_CREATE_USED_UNION) &&
|
||||||
(table->s->tmp_table == NO_TMP_TABLE))
|
(table->s->tmp_table == NO_TMP_TABLE))
|
||||||
{
|
{
|
||||||
@ -6806,7 +6824,8 @@ view_err:
|
|||||||
table_list->table= NULL; // For query cache
|
table_list->table= NULL; // For query cache
|
||||||
query_cache_invalidate3(thd, table_list, 0);
|
query_cache_invalidate3(thd, table_list, 0);
|
||||||
|
|
||||||
if (thd->locked_tables_mode)
|
if ((thd->locked_tables_mode == LTM_LOCK_TABLES ||
|
||||||
|
thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES))
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
Under LOCK TABLES we should adjust meta-data locks before finishing
|
Under LOCK TABLES we should adjust meta-data locks before finishing
|
||||||
@ -7290,7 +7309,9 @@ view_err:
|
|||||||
if (table->s->tmp_table != NO_TMP_TABLE)
|
if (table->s->tmp_table != NO_TMP_TABLE)
|
||||||
{
|
{
|
||||||
/* Close lock if this is a transactional table */
|
/* Close lock if this is a transactional table */
|
||||||
if (thd->lock && ! thd->locked_tables_mode)
|
if (thd->lock &&
|
||||||
|
! (thd->locked_tables_mode == LTM_LOCK_TABLES ||
|
||||||
|
thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES))
|
||||||
{
|
{
|
||||||
mysql_unlock_tables(thd, thd->lock);
|
mysql_unlock_tables(thd, thd->lock);
|
||||||
thd->lock=0;
|
thd->lock=0;
|
||||||
@ -7492,7 +7513,8 @@ view_err:
|
|||||||
table_list->table=0; // For query cache
|
table_list->table=0; // For query cache
|
||||||
query_cache_invalidate3(thd, table_list, 0);
|
query_cache_invalidate3(thd, table_list, 0);
|
||||||
|
|
||||||
if (thd->locked_tables_mode)
|
if (thd->locked_tables_mode == LTM_LOCK_TABLES ||
|
||||||
|
thd->locked_tables_mode == LTM_PRELOCKED_UNDER_LOCK_TABLES)
|
||||||
{
|
{
|
||||||
if ((new_name != table_name || new_db != db))
|
if ((new_name != table_name || new_db != db))
|
||||||
{
|
{
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include "sp_head.h"
|
#include "sp_head.h"
|
||||||
#include "sql_trigger.h"
|
#include "sql_trigger.h"
|
||||||
#include "parse_file.h"
|
#include "parse_file.h"
|
||||||
|
#include "sp.h"
|
||||||
|
|
||||||
/*************************************************************************/
|
/*************************************************************************/
|
||||||
|
|
||||||
@ -2017,6 +2018,57 @@ bool Table_triggers_list::process_triggers(THD *thd,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Add triggers for table to the set of routines used by statement.
|
||||||
|
Add tables used by them to statement table list. Do the same for
|
||||||
|
routines used by triggers.
|
||||||
|
|
||||||
|
@param thd Thread context.
|
||||||
|
@param prelocking_ctx Prelocking context of the statement.
|
||||||
|
@param table_list Table list element for table with trigger.
|
||||||
|
|
||||||
|
@retval FALSE Success.
|
||||||
|
@retval TRUE Failure.
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool
|
||||||
|
Table_triggers_list::
|
||||||
|
add_tables_and_routines_for_triggers(THD *thd,
|
||||||
|
Query_tables_list *prelocking_ctx,
|
||||||
|
TABLE_LIST *table_list)
|
||||||
|
{
|
||||||
|
DBUG_ASSERT(static_cast<int>(table_list->lock_type) >=
|
||||||
|
static_cast<int>(TL_WRITE_ALLOW_WRITE));
|
||||||
|
|
||||||
|
for (int i= 0; i < (int)TRG_EVENT_MAX; i++)
|
||||||
|
{
|
||||||
|
if (table_list->trg_event_map &
|
||||||
|
static_cast<uint8>(1 << static_cast<int>(i)))
|
||||||
|
{
|
||||||
|
for (int j= 0; j < (int)TRG_ACTION_MAX; j++)
|
||||||
|
{
|
||||||
|
/* We can have only one trigger per action type currently */
|
||||||
|
sp_head *trigger= table_list->table->triggers->bodies[i][j];
|
||||||
|
|
||||||
|
if (trigger && sp_add_used_routine(prelocking_ctx, thd->stmt_arena,
|
||||||
|
&trigger->m_sroutines_key,
|
||||||
|
table_list->belong_to_view))
|
||||||
|
{
|
||||||
|
trigger->add_used_tables_to_table_list(thd,
|
||||||
|
&prelocking_ctx->query_tables_last,
|
||||||
|
table_list->belong_to_view);
|
||||||
|
sp_update_stmt_used_routines(thd, prelocking_ctx,
|
||||||
|
&trigger->m_sroutines,
|
||||||
|
table_list->belong_to_view);
|
||||||
|
trigger->propagate_attributes(prelocking_ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Mark fields of subject table which we read/set in its triggers
|
Mark fields of subject table which we read/set in its triggers
|
||||||
as such.
|
as such.
|
||||||
|
@ -144,8 +144,10 @@ public:
|
|||||||
void mark_fields_used(trg_event_type event);
|
void mark_fields_used(trg_event_type event);
|
||||||
|
|
||||||
friend class Item_trigger_field;
|
friend class Item_trigger_field;
|
||||||
friend int sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
|
|
||||||
TABLE_LIST *table);
|
bool add_tables_and_routines_for_triggers(THD *thd,
|
||||||
|
Query_tables_list *prelocking_ctx,
|
||||||
|
TABLE_LIST *table_list);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool prepare_record1_accessors(TABLE *table);
|
bool prepare_record1_accessors(TABLE *table);
|
||||||
|
@ -1766,6 +1766,7 @@ create:
|
|||||||
lex->create_info.default_table_charset= NULL;
|
lex->create_info.default_table_charset= NULL;
|
||||||
lex->name.str= 0;
|
lex->name.str= 0;
|
||||||
lex->name.length= 0;
|
lex->name.length= 0;
|
||||||
|
lex->create_last_non_select_table= lex->last_table();
|
||||||
}
|
}
|
||||||
create2
|
create2
|
||||||
{
|
{
|
||||||
@ -1788,7 +1789,8 @@ create:
|
|||||||
lex->sql_command= SQLCOM_CREATE_INDEX;
|
lex->sql_command= SQLCOM_CREATE_INDEX;
|
||||||
if (!lex->current_select->add_table_to_list(lex->thd, $7,
|
if (!lex->current_select->add_table_to_list(lex->thd, $7,
|
||||||
NULL,
|
NULL,
|
||||||
TL_OPTION_UPDATING))
|
TL_OPTION_UPDATING,
|
||||||
|
TL_WRITE_ALLOW_READ))
|
||||||
MYSQL_YYABORT;
|
MYSQL_YYABORT;
|
||||||
lex->alter_info.reset();
|
lex->alter_info.reset();
|
||||||
lex->alter_info.flags= ALTER_ADD_INDEX;
|
lex->alter_info.flags= ALTER_ADD_INDEX;
|
||||||
@ -3952,7 +3954,7 @@ create2:
|
|||||||
;
|
;
|
||||||
|
|
||||||
create2a:
|
create2a:
|
||||||
field_list ')' opt_create_table_options
|
create_field_list ')' opt_create_table_options
|
||||||
opt_partitioning
|
opt_partitioning
|
||||||
create3 {}
|
create3 {}
|
||||||
| opt_partitioning
|
| opt_partitioning
|
||||||
@ -4802,19 +4804,30 @@ create_table_option:
|
|||||||
Lex->create_info.row_type= $3;
|
Lex->create_info.row_type= $3;
|
||||||
Lex->create_info.used_fields|= HA_CREATE_USED_ROW_FORMAT;
|
Lex->create_info.used_fields|= HA_CREATE_USED_ROW_FORMAT;
|
||||||
}
|
}
|
||||||
| UNION_SYM opt_equal '(' opt_table_list ')'
|
| UNION_SYM opt_equal
|
||||||
{
|
{
|
||||||
/* Move the union list to the merge_list */
|
Lex->select_lex.table_list.save_and_clear(&Lex->save_list);
|
||||||
|
}
|
||||||
|
'(' opt_table_list ')'
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
Move the union list to the merge_list and exclude its tables
|
||||||
|
from the global list.
|
||||||
|
*/
|
||||||
LEX *lex=Lex;
|
LEX *lex=Lex;
|
||||||
TABLE_LIST *table_list= lex->select_lex.get_table_list();
|
|
||||||
lex->create_info.merge_list= lex->select_lex.table_list;
|
lex->create_info.merge_list= lex->select_lex.table_list;
|
||||||
lex->create_info.merge_list.elements--;
|
lex->select_lex.table_list= lex->save_list;
|
||||||
lex->create_info.merge_list.first=
|
/*
|
||||||
(uchar*) (table_list->next_local);
|
When excluding union list from the global list we assume that
|
||||||
lex->select_lex.table_list.elements=1;
|
elements of the former immediately follow elements which represent
|
||||||
lex->select_lex.table_list.next=
|
table being created/altered and parent tables.
|
||||||
(uchar**) &(table_list->next_local);
|
*/
|
||||||
table_list->next_local= 0;
|
TABLE_LIST *last_non_sel_table= lex->create_last_non_select_table;
|
||||||
|
DBUG_ASSERT(last_non_sel_table->next_global ==
|
||||||
|
(TABLE_LIST *)lex->create_info.merge_list.first);
|
||||||
|
last_non_sel_table->next_global= 0;
|
||||||
|
Lex->query_tables_last= &last_non_sel_table->next_global;
|
||||||
|
|
||||||
lex->create_info.used_fields|= HA_CREATE_USED_UNION;
|
lex->create_info.used_fields|= HA_CREATE_USED_UNION;
|
||||||
}
|
}
|
||||||
| default_charset
|
| default_charset
|
||||||
@ -4952,6 +4965,14 @@ udf_type:
|
|||||||
| INT_SYM {$$ = (int) INT_RESULT; }
|
| INT_SYM {$$ = (int) INT_RESULT; }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
|
create_field_list:
|
||||||
|
field_list
|
||||||
|
{
|
||||||
|
Lex->create_last_non_select_table= Lex->last_table();
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
field_list:
|
field_list:
|
||||||
field_list_item
|
field_list_item
|
||||||
| field_list ',' field_list_item
|
| field_list ',' field_list_item
|
||||||
@ -5743,7 +5764,8 @@ alter:
|
|||||||
lex->sql_command= SQLCOM_ALTER_TABLE;
|
lex->sql_command= SQLCOM_ALTER_TABLE;
|
||||||
lex->duplicates= DUP_ERROR;
|
lex->duplicates= DUP_ERROR;
|
||||||
if (!lex->select_lex.add_table_to_list(thd, $4, NULL,
|
if (!lex->select_lex.add_table_to_list(thd, $4, NULL,
|
||||||
TL_OPTION_UPDATING))
|
TL_OPTION_UPDATING,
|
||||||
|
TL_WRITE_ALLOW_READ))
|
||||||
MYSQL_YYABORT;
|
MYSQL_YYABORT;
|
||||||
lex->col_list.empty();
|
lex->col_list.empty();
|
||||||
lex->select_lex.init_order();
|
lex->select_lex.init_order();
|
||||||
@ -5756,6 +5778,7 @@ alter:
|
|||||||
lex->alter_info.reset();
|
lex->alter_info.reset();
|
||||||
lex->no_write_to_binlog= 0;
|
lex->no_write_to_binlog= 0;
|
||||||
lex->create_info.storage_media= HA_SM_DEFAULT;
|
lex->create_info.storage_media= HA_SM_DEFAULT;
|
||||||
|
lex->create_last_non_select_table= lex->last_table();
|
||||||
}
|
}
|
||||||
alter_commands
|
alter_commands
|
||||||
{}
|
{}
|
||||||
@ -6139,12 +6162,16 @@ add_column:
|
|||||||
;
|
;
|
||||||
|
|
||||||
alter_list_item:
|
alter_list_item:
|
||||||
add_column column_def opt_place { }
|
add_column column_def opt_place
|
||||||
|
{
|
||||||
|
Lex->create_last_non_select_table= Lex->last_table();
|
||||||
|
}
|
||||||
| ADD key_def
|
| ADD key_def
|
||||||
{
|
{
|
||||||
|
Lex->create_last_non_select_table= Lex->last_table();
|
||||||
Lex->alter_info.flags|= ALTER_ADD_INDEX;
|
Lex->alter_info.flags|= ALTER_ADD_INDEX;
|
||||||
}
|
}
|
||||||
| add_column '(' field_list ')'
|
| add_column '(' create_field_list ')'
|
||||||
{
|
{
|
||||||
Lex->alter_info.flags|= ALTER_ADD_COLUMN | ALTER_ADD_INDEX;
|
Lex->alter_info.flags|= ALTER_ADD_COLUMN | ALTER_ADD_INDEX;
|
||||||
}
|
}
|
||||||
@ -6155,6 +6182,9 @@ alter_list_item:
|
|||||||
lex->alter_info.flags|= ALTER_CHANGE_COLUMN;
|
lex->alter_info.flags|= ALTER_CHANGE_COLUMN;
|
||||||
}
|
}
|
||||||
field_spec opt_place
|
field_spec opt_place
|
||||||
|
{
|
||||||
|
Lex->create_last_non_select_table= Lex->last_table();
|
||||||
|
}
|
||||||
| MODIFY_SYM opt_column field_ident
|
| MODIFY_SYM opt_column field_ident
|
||||||
{
|
{
|
||||||
LEX *lex=Lex;
|
LEX *lex=Lex;
|
||||||
@ -6177,6 +6207,9 @@ alter_list_item:
|
|||||||
MYSQL_YYABORT;
|
MYSQL_YYABORT;
|
||||||
}
|
}
|
||||||
opt_place
|
opt_place
|
||||||
|
{
|
||||||
|
Lex->create_last_non_select_table= Lex->last_table();
|
||||||
|
}
|
||||||
| DROP opt_column field_ident opt_restrict
|
| DROP opt_column field_ident opt_restrict
|
||||||
{
|
{
|
||||||
LEX *lex=Lex;
|
LEX *lex=Lex;
|
||||||
@ -9607,7 +9640,8 @@ drop:
|
|||||||
lex->alter_info.flags= ALTER_DROP_INDEX;
|
lex->alter_info.flags= ALTER_DROP_INDEX;
|
||||||
lex->alter_info.drop_list.push_back(ad);
|
lex->alter_info.drop_list.push_back(ad);
|
||||||
if (!lex->current_select->add_table_to_list(lex->thd, $5, NULL,
|
if (!lex->current_select->add_table_to_list(lex->thd, $5, NULL,
|
||||||
TL_OPTION_UPDATING))
|
TL_OPTION_UPDATING,
|
||||||
|
TL_WRITE_ALLOW_READ))
|
||||||
MYSQL_YYABORT;
|
MYSQL_YYABORT;
|
||||||
}
|
}
|
||||||
| DROP DATABASE if_exists ident
|
| DROP DATABASE if_exists ident
|
||||||
|
Loading…
x
Reference in New Issue
Block a user