mirror of
https://github.com/MariaDB/server.git
synced 2025-01-29 02:05:57 +01:00
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).
This commit is contained in:
parent
b9895d46c1
commit
c20afa6d49
12 changed files with 790 additions and 418 deletions
|
@ -1474,10 +1474,26 @@ int setup_ftfuncs(SELECT_LEX* select);
|
|||
int init_ftfuncs(THD *thd, SELECT_LEX* select, bool no_order);
|
||||
void wait_for_condition(THD *thd, pthread_mutex_t *mutex,
|
||||
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 */
|
||||
int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived,
|
||||
uint flags);
|
||||
int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables,
|
||||
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 */
|
||||
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_head.h"
|
||||
#include "sp_cache.h"
|
||||
#include "sql_trigger.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,
|
||||
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
|
||||
used by statement.
|
||||
|
||||
In case when statement uses stored routines but does not need
|
||||
prelocking (i.e. it does not use any tables) we will access the
|
||||
elements of LEX::sroutines set on prepared statement re-execution.
|
||||
Because of this we have to allocate memory for both hash element
|
||||
and copy of its key in persistent arena.
|
||||
elements of Query_tables_list::sroutines set on prepared statement
|
||||
re-execution. Because of this we have to allocate memory for both
|
||||
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
|
||||
allocated
|
||||
@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)
|
||||
|
||||
@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
|
||||
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).
|
||||
*/
|
||||
|
||||
static bool add_used_routine(LEX *lex, Query_arena *arena,
|
||||
const LEX_STRING *key,
|
||||
TABLE_LIST *belong_to_view)
|
||||
bool sp_add_used_routine(Query_tables_list *prelocking_ctx, Query_arena *arena,
|
||||
const LEX_STRING *key, 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,
|
||||
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 *)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.str= (char *)rn + sizeof(Sroutine_hash_entry);
|
||||
memcpy(rn->key.str, key->str, key->length + 1);
|
||||
my_hash_insert(&lex->sroutines, (uchar *)rn);
|
||||
lex->sroutines_list.link_in_list((uchar *)rn, (uchar **)&rn->next);
|
||||
my_hash_insert(&prelocking_ctx->sroutines, (uchar *)rn);
|
||||
prelocking_ctx->sroutines_list.link_in_list((uchar *)rn, (uchar **)&rn->next);
|
||||
rn->belong_to_view= belong_to_view;
|
||||
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
|
||||
persistent arena as second argument.
|
||||
|
||||
@param lex LEX representing statement
|
||||
@param arena arena in which memory for new element of the set
|
||||
will be allocated
|
||||
@param rt routine name
|
||||
@param rt_type routine type (one of TYPE_ENUM_PROCEDURE/...)
|
||||
@param prelocking_ctx Prelocking context of the statement
|
||||
@param arena Arena in which memory for new element of the set
|
||||
will be allocated
|
||||
@param rt Routine name
|
||||
@param rt_type Routine type (one of TYPE_ENUM_PROCEDURE/...)
|
||||
|
||||
@note
|
||||
Will also add element to end of 'LEX::sroutines_list' list (and will
|
||||
take into account that this is explicitly used routine).
|
||||
Will also add element to end of 'Query_tables_list::sroutines_list' list
|
||||
(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)
|
||||
{
|
||||
rt->set_routine_type(rt_type);
|
||||
(void)add_used_routine(lex, arena, &rt->m_sroutines_key, 0);
|
||||
lex->sroutines_list_own_last= lex->sroutines_list.next;
|
||||
lex->sroutines_list_own_elements= lex->sroutines_list.elements;
|
||||
(void)sp_add_used_routine(prelocking_ctx, arena, &rt->m_sroutines_key, 0);
|
||||
prelocking_ctx->sroutines_list_own_last= prelocking_ctx->sroutines_list.next;
|
||||
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
|
||||
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;
|
||||
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)
|
||||
{
|
||||
/*
|
||||
|
@ -1564,12 +1504,13 @@ void sp_remove_not_own_routines(LEX *lex)
|
|||
but we want to be more future-proof.
|
||||
*/
|
||||
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;
|
||||
lex->sroutines_list.next= lex->sroutines_list_own_last;
|
||||
lex->sroutines_list.elements= lex->sroutines_list_own_elements;
|
||||
*(Sroutine_hash_entry **)prelocking_ctx->sroutines_list_own_last= NULL;
|
||||
prelocking_ctx->sroutines_list.next= prelocking_ctx->sroutines_list_own_last;
|
||||
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.
|
||||
|
||||
@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
|
||||
be added
|
||||
@param belong_to_view Uppermost view which uses these routines, 0 if none
|
||||
|
||||
@note
|
||||
It will also add elements to end of 'LEX::sroutines_list' list.
|
||||
@note It will also add elements to end of
|
||||
'Query_tables_list::sroutines_list' list.
|
||||
*/
|
||||
|
||||
static void
|
||||
sp_update_stmt_used_routines(THD *thd, LEX *lex, HASH *src,
|
||||
TABLE_LIST *belong_to_view)
|
||||
void
|
||||
sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx,
|
||||
HASH *src, TABLE_LIST *belong_to_view)
|
||||
{
|
||||
for (uint i=0 ; i < src->records ; 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.
|
||||
|
||||
@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
|
||||
be added
|
||||
@param belong_to_view Uppermost view which uses these routines, 0 if none
|
||||
|
||||
@note
|
||||
It will also add elements to end of 'LEX::sroutines_list' list.
|
||||
@note It will also add elements to end of
|
||||
'Query_tables_list::sroutines_list' list.
|
||||
*/
|
||||
|
||||
static void sp_update_stmt_used_routines(THD *thd, LEX *lex, SQL_LIST *src,
|
||||
TABLE_LIST *belong_to_view)
|
||||
void sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx,
|
||||
SQL_LIST *src, TABLE_LIST *belong_to_view)
|
||||
{
|
||||
for (Sroutine_hash_entry *rt= (Sroutine_hash_entry *)src->first;
|
||||
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
|
||||
routines to statement table list. Do the same for all routines used
|
||||
by these routines.
|
||||
Ensure that routine is present in cache by loading it from the mysql.proc
|
||||
table if needed. Emit an appropriate error if there was a problem during
|
||||
loading.
|
||||
|
||||
@param thd thread context
|
||||
@param lex LEX representing statement
|
||||
@param start first routine from the list of routines to be cached
|
||||
(this list defines mentioned sub-set).
|
||||
@param first_no_prelock If true, don't add tables or cache routines used by
|
||||
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
|
||||
@param[in] thd Thread context.
|
||||
@param[in] type Type of object (TYPE_ENUM_FUNCTION or TYPE_ENUM_PROCEDURE).
|
||||
@param[in] name Name of routine.
|
||||
@param[out] sp Pointer to sp_head object for routine, NULL if routine was
|
||||
not found,
|
||||
|
||||
@note
|
||||
If some function is missing this won't be reported here.
|
||||
Instead this fact will be discovered during query execution.
|
||||
|
||||
@retval
|
||||
0 success
|
||||
@retval
|
||||
non-0 failure
|
||||
@retval 0 Either routine is found and was succesfully loaded into cache
|
||||
or it does not exist.
|
||||
@retval non-0 Error while loading routine from mysql,proc table.
|
||||
*/
|
||||
|
||||
static int
|
||||
sp_cache_routines_and_add_tables_aux(THD *thd, LEX *lex,
|
||||
Sroutine_hash_entry *start,
|
||||
bool first_no_prelock)
|
||||
int sp_cache_routine(THD *thd, int type, sp_name *name, sp_head **sp)
|
||||
{
|
||||
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)
|
||||
{
|
||||
sp_name name(thd, rt->key.str, rt->key.length);
|
||||
int type= rt->key.str[0];
|
||||
sp_head *sp;
|
||||
DBUG_ENTER("sp_cache_routine");
|
||||
|
||||
/*
|
||||
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);
|
||||
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),
|
||||
&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
|
||||
with the mysql.proc table, or a parse error because the contents
|
||||
has been tampered with (in which case we clear that 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.)
|
||||
*/
|
||||
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())
|
||||
{
|
||||
/*
|
||||
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];
|
||||
char n[NAME_LEN*2+2];
|
||||
|
||||
/* m_qname.str is not always \0 terminated */
|
||||
memcpy(n, name.m_qname.str, name.m_qname.length);
|
||||
n[name.m_qname.length]= '\0';
|
||||
my_error(ER_SP_PROC_TABLE_CORRUPT, MYF(0), n, ret);
|
||||
}
|
||||
break;
|
||||
/* m_qname.str is not always \0 terminated */
|
||||
memcpy(n, name->m_qname.str, name->m_qname.length);
|
||||
n[name->m_qname.length]= '\0';
|
||||
my_error(ER_SP_PROC_TABLE_CORRUPT, MYF(0), n, ret);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
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.
|
||||
|
||||
|
|
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_cache **cp, bool cache_only);
|
||||
|
||||
int
|
||||
sp_cache_routine(THD *thd, int type, sp_name *name, sp_head **sp);
|
||||
|
||||
bool
|
||||
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
|
||||
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);
|
||||
void sp_add_used_routine(LEX *lex, Query_arena *arena,
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
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);
|
||||
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);
|
||||
int sp_cache_routines_and_add_tables(THD *thd, LEX *lex,
|
||||
bool first_no_prelock);
|
||||
int sp_cache_routines_and_add_tables_for_view(THD *thd, LEX *lex,
|
||||
TABLE_LIST *view);
|
||||
int sp_cache_routines_and_add_tables_for_triggers(THD *thd, LEX *lex,
|
||||
TABLE_LIST *table);
|
||||
void sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx,
|
||||
HASH *src, TABLE_LIST *belong_to_view);
|
||||
void sp_update_stmt_used_routines(THD *thd, Query_tables_list *prelocking_ctx,
|
||||
SQL_LIST *src, TABLE_LIST *belong_to_view);
|
||||
|
||||
extern "C" uchar* sp_sroutine_key(const uchar *ptr, size_t *plen,
|
||||
my_bool first);
|
||||
|
|
|
@ -454,10 +454,10 @@ public:
|
|||
|
||||
/*
|
||||
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
|
||||
sp_head needs to "taint" the caller).
|
||||
to propagate upwards to the Query_tables_list of the caller (when
|
||||
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
|
||||
|
@ -466,7 +466,7 @@ public:
|
|||
the substatements not).
|
||||
*/
|
||||
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
|
||||
|
||||
SYNOPSIS
|
||||
open_tables()
|
||||
thd - thread handler
|
||||
start - list of tables in/out
|
||||
counter - number of opened tables will be return using this parameter
|
||||
flags - bitmap of flags to modify how the tables will be open:
|
||||
MYSQL_LOCK_IGNORE_FLUSH - open table even if someone has
|
||||
done a flush or namelock on it.
|
||||
@param[in] thd Thread context.
|
||||
@param[in,out] start List of tables to be open (it can be adjusted for
|
||||
statement that uses tables only implicitly, e.g.
|
||||
for "SELECT f1()").
|
||||
@param[out] counter Number of tables which were open.
|
||||
@param[in] flags Bitmap of flags to modify how the tables will be
|
||||
open, see open_table() description for details.
|
||||
@param[in] prelocking_strategy Strategy which specifies how prelocking
|
||||
algorithm should work for this statement.
|
||||
|
||||
NOTE
|
||||
Unless we are already in prelocked mode, this function will also precache
|
||||
all SP/SFs explicitly or implicitly (via views and triggers) used by the
|
||||
query and add tables needed for their execution to table list. If resulting
|
||||
tables list will be non empty it will mark query as requiring precaching.
|
||||
@note
|
||||
Unless we are already in prelocked mode and prelocking strategy prescribes
|
||||
so this function will also precache all SP/SFs explicitly or implicitly
|
||||
(via views and triggers) used by the query and add tables needed for their
|
||||
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.
|
||||
|
||||
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
|
||||
which is already built.
|
||||
|
||||
RETURN
|
||||
0 - OK
|
||||
-1 - error
|
||||
@retval 0 OK
|
||||
@retval -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;
|
||||
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->uses_stored_routines())
|
||||
{
|
||||
bool first_no_prelocking, need_prelocking;
|
||||
bool need_prelocking= FALSE;
|
||||
TABLE_LIST **save_query_tables_last= thd->lex->query_tables_last;
|
||||
|
||||
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.
|
||||
|
@ -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
|
||||
yet built and we have trigger for table being opened then we should
|
||||
cache all routines used by its triggers and add their tables to
|
||||
prelocking list.
|
||||
If we lock table for reading we won't update it so there is no need to
|
||||
process its triggers since they never will be activated.
|
||||
yet built we might have to build the prelocking set for this statement.
|
||||
|
||||
Since currently no prelocking strategy prescribes doing anything for
|
||||
tables which are only read, we do below checks only if table is going
|
||||
to be changed.
|
||||
*/
|
||||
if (thd->locked_tables_mode <= LTM_LOCK_TABLES &&
|
||||
!thd->lex->requires_prelocking() &&
|
||||
tables->trg_event_map && tables->table->triggers &&
|
||||
tables->lock_type >= TL_WRITE_ALLOW_WRITE)
|
||||
{
|
||||
if (!query_tables_last_own)
|
||||
query_tables_last_own= thd->lex->query_tables_last;
|
||||
if (sp_cache_routines_and_add_tables_for_triggers(thd, thd->lex,
|
||||
tables))
|
||||
bool need_prelocking= FALSE;
|
||||
bool not_used;
|
||||
TABLE_LIST **save_query_tables_last= thd->lex->query_tables_last;
|
||||
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;
|
||||
goto err;
|
||||
}
|
||||
|
@ -4039,13 +4132,27 @@ process_view_routines:
|
|||
*/
|
||||
if (tables->view &&
|
||||
thd->locked_tables_mode <= LTM_LOCK_TABLES &&
|
||||
!thd->lex->requires_prelocking() &&
|
||||
tables->view->uses_stored_routines())
|
||||
!thd->lex->requires_prelocking())
|
||||
{
|
||||
/* We have at least one table in TL here. */
|
||||
if (!query_tables_last_own)
|
||||
query_tables_last_own= thd->lex->query_tables_last;
|
||||
if (sp_cache_routines_and_add_tables_for_view(thd, thd->lex, tables))
|
||||
bool need_prelocking= FALSE;
|
||||
bool not_used;
|
||||
TABLE_LIST **save_query_tables_last= thd->lex->query_tables_last;
|
||||
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.
|
||||
|
@ -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
|
||||
|
||||
|
@ -4312,34 +4633,35 @@ end:
|
|||
}
|
||||
|
||||
|
||||
/*
|
||||
/**
|
||||
Open all tables in list, locks them and optionally process derived tables.
|
||||
|
||||
SYNOPSIS
|
||||
open_and_lock_tables_derived()
|
||||
thd - thread handler
|
||||
tables - list of tables for open&locking
|
||||
flags - set of options to be used to open and lock tables (see
|
||||
open_tables() and mysql_lock_tables() for details).
|
||||
derived - if to handle derived tables
|
||||
@param thd Thread context.
|
||||
@param tables List of tables for open and locking.
|
||||
@param derived If to handle derived tables.
|
||||
@param flags Bitmap of options to be used to open and lock
|
||||
tables (see open_tables() and mysql_lock_tables()
|
||||
for details).
|
||||
@param prelocking_strategy Strategy which specifies how prelocking algorithm
|
||||
should work for this statement.
|
||||
|
||||
RETURN
|
||||
FALSE - ok
|
||||
TRUE - error
|
||||
|
||||
NOTE
|
||||
@note
|
||||
The lock will automaticaly be freed by close_thread_tables()
|
||||
|
||||
NOTE
|
||||
There are two convenience functions:
|
||||
@note
|
||||
There are several convenience functions, e.g. :
|
||||
- simple_open_n_lock_tables(thd, tables) without derived handling
|
||||
- open_and_lock_tables(thd, tables) with derived handling
|
||||
Both inline functions call open_and_lock_tables_derived() with
|
||||
the third argument set appropriately.
|
||||
|
||||
@retval FALSE OK.
|
||||
@retval TRUE Error
|
||||
*/
|
||||
|
||||
int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived,
|
||||
uint flags)
|
||||
int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables,
|
||||
bool derived, uint flags,
|
||||
Prelocking_strategy *prelocking_strategy)
|
||||
{
|
||||
uint counter;
|
||||
bool need_reopen;
|
||||
|
@ -4349,7 +4671,7 @@ int open_and_lock_tables_derived(THD *thd, TABLE_LIST *tables, bool derived,
|
|||
|
||||
for ( ; ; )
|
||||
{
|
||||
if (open_tables(thd, &tables, &counter, flags))
|
||||
if (open_tables(thd, &tables, &counter, flags, prelocking_strategy))
|
||||
DBUG_RETURN(-1);
|
||||
|
||||
DBUG_EXECUTE_IF("sleep_open_and_lock_after_open", {
|
||||
|
|
|
@ -38,6 +38,7 @@ class sp_rcontext;
|
|||
class sp_cache;
|
||||
class Parser_state;
|
||||
class Rows_log_event;
|
||||
class Sroutine_hash_entry;
|
||||
|
||||
enum enum_enable_or_disable { LEAVE_AS_IS, ENABLE, DISABLE };
|
||||
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
|
||||
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?
|
||||
*/
|
||||
|
@ -1751,6 +1762,12 @@ struct LEX: public Query_tables_list
|
|||
bool subqueries, ignore;
|
||||
st_parsing_options parsing_options;
|
||||
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:*/
|
||||
LEX_STRING prepared_stmt_name; /* Statement name (in all queries) */
|
||||
/*
|
||||
|
|
|
@ -3342,9 +3342,14 @@ end_with_restore_list:
|
|||
thd->options|= OPTION_TABLE_LOCK;
|
||||
thd->in_lock_tables=1;
|
||||
|
||||
res= (open_and_lock_tables_derived(thd, all_tables, FALSE,
|
||||
MYSQL_OPEN_TAKE_UPGRADABLE_MDL) ||
|
||||
thd->locked_tables_list.init_locked_tables(thd));
|
||||
{
|
||||
Lock_tables_prelocking_strategy lock_tables_prelocking_strategy;
|
||||
|
||||
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;
|
||||
|
||||
|
|
|
@ -6560,9 +6560,26 @@ view_err:
|
|||
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);
|
||||
}
|
||||
|
||||
table= table_list->table;
|
||||
table->use_all_columns();
|
||||
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
|
||||
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) &&
|
||||
(table->s->tmp_table == NO_TMP_TABLE))
|
||||
{
|
||||
|
@ -6806,7 +6824,8 @@ view_err:
|
|||
table_list->table= NULL; // For query cache
|
||||
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
|
||||
|
@ -7290,7 +7309,9 @@ view_err:
|
|||
if (table->s->tmp_table != NO_TMP_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);
|
||||
thd->lock=0;
|
||||
|
@ -7492,7 +7513,8 @@ view_err:
|
|||
table_list->table=0; // For query cache
|
||||
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))
|
||||
{
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "sp_head.h"
|
||||
#include "sql_trigger.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
|
||||
as such.
|
||||
|
|
|
@ -144,8 +144,10 @@ public:
|
|||
void mark_fields_used(trg_event_type event);
|
||||
|
||||
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:
|
||||
bool prepare_record1_accessors(TABLE *table);
|
||||
|
|
|
@ -1766,6 +1766,7 @@ create:
|
|||
lex->create_info.default_table_charset= NULL;
|
||||
lex->name.str= 0;
|
||||
lex->name.length= 0;
|
||||
lex->create_last_non_select_table= lex->last_table();
|
||||
}
|
||||
create2
|
||||
{
|
||||
|
@ -1788,7 +1789,8 @@ create:
|
|||
lex->sql_command= SQLCOM_CREATE_INDEX;
|
||||
if (!lex->current_select->add_table_to_list(lex->thd, $7,
|
||||
NULL,
|
||||
TL_OPTION_UPDATING))
|
||||
TL_OPTION_UPDATING,
|
||||
TL_WRITE_ALLOW_READ))
|
||||
MYSQL_YYABORT;
|
||||
lex->alter_info.reset();
|
||||
lex->alter_info.flags= ALTER_ADD_INDEX;
|
||||
|
@ -3952,7 +3954,7 @@ create2:
|
|||
;
|
||||
|
||||
create2a:
|
||||
field_list ')' opt_create_table_options
|
||||
create_field_list ')' opt_create_table_options
|
||||
opt_partitioning
|
||||
create3 {}
|
||||
| opt_partitioning
|
||||
|
@ -4802,19 +4804,30 @@ create_table_option:
|
|||
Lex->create_info.row_type= $3;
|
||||
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;
|
||||
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.elements--;
|
||||
lex->create_info.merge_list.first=
|
||||
(uchar*) (table_list->next_local);
|
||||
lex->select_lex.table_list.elements=1;
|
||||
lex->select_lex.table_list.next=
|
||||
(uchar**) &(table_list->next_local);
|
||||
table_list->next_local= 0;
|
||||
lex->select_lex.table_list= lex->save_list;
|
||||
/*
|
||||
When excluding union list from the global list we assume that
|
||||
elements of the former immediately follow elements which represent
|
||||
table being created/altered and parent tables.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
| default_charset
|
||||
|
@ -4952,6 +4965,14 @@ udf_type:
|
|||
| INT_SYM {$$ = (int) INT_RESULT; }
|
||||
;
|
||||
|
||||
|
||||
create_field_list:
|
||||
field_list
|
||||
{
|
||||
Lex->create_last_non_select_table= Lex->last_table();
|
||||
}
|
||||
;
|
||||
|
||||
field_list:
|
||||
field_list_item
|
||||
| field_list ',' field_list_item
|
||||
|
@ -5743,7 +5764,8 @@ alter:
|
|||
lex->sql_command= SQLCOM_ALTER_TABLE;
|
||||
lex->duplicates= DUP_ERROR;
|
||||
if (!lex->select_lex.add_table_to_list(thd, $4, NULL,
|
||||
TL_OPTION_UPDATING))
|
||||
TL_OPTION_UPDATING,
|
||||
TL_WRITE_ALLOW_READ))
|
||||
MYSQL_YYABORT;
|
||||
lex->col_list.empty();
|
||||
lex->select_lex.init_order();
|
||||
|
@ -5756,6 +5778,7 @@ alter:
|
|||
lex->alter_info.reset();
|
||||
lex->no_write_to_binlog= 0;
|
||||
lex->create_info.storage_media= HA_SM_DEFAULT;
|
||||
lex->create_last_non_select_table= lex->last_table();
|
||||
}
|
||||
alter_commands
|
||||
{}
|
||||
|
@ -6139,12 +6162,16 @@ add_column:
|
|||
;
|
||||
|
||||
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
|
||||
{
|
||||
Lex->create_last_non_select_table= Lex->last_table();
|
||||
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;
|
||||
}
|
||||
|
@ -6155,6 +6182,9 @@ alter_list_item:
|
|||
lex->alter_info.flags|= ALTER_CHANGE_COLUMN;
|
||||
}
|
||||
field_spec opt_place
|
||||
{
|
||||
Lex->create_last_non_select_table= Lex->last_table();
|
||||
}
|
||||
| MODIFY_SYM opt_column field_ident
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
|
@ -6177,6 +6207,9 @@ alter_list_item:
|
|||
MYSQL_YYABORT;
|
||||
}
|
||||
opt_place
|
||||
{
|
||||
Lex->create_last_non_select_table= Lex->last_table();
|
||||
}
|
||||
| DROP opt_column field_ident opt_restrict
|
||||
{
|
||||
LEX *lex=Lex;
|
||||
|
@ -9607,7 +9640,8 @@ drop:
|
|||
lex->alter_info.flags= ALTER_DROP_INDEX;
|
||||
lex->alter_info.drop_list.push_back(ad);
|
||||
if (!lex->current_select->add_table_to_list(lex->thd, $5, NULL,
|
||||
TL_OPTION_UPDATING))
|
||||
TL_OPTION_UPDATING,
|
||||
TL_WRITE_ALLOW_READ))
|
||||
MYSQL_YYABORT;
|
||||
}
|
||||
| DROP DATABASE if_exists ident
|
||||
|
|
Loading…
Add table
Reference in a new issue