Commit 30235272 authored by Konstantin Osipov's avatar Konstantin Osipov

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.
parent 97d2a923
......@@ -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)
{
......
......@@ -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,251 +1573,101 @@ 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.
@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
@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
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[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,
@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;
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;
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())
{
/*
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];
/* 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
char n[NAME_LEN*2+2];
@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;
}
}
/* 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;
}
}
return 0;
DBUG_RETURN(ret);
}
......
......@@ -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);
/**
Structure that represents element in the set of stored routines
used by statement or routine.
*/
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 pre-caching of stored routines and building table list
for prelocking.
Procedures for handling sets 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,
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();
}
......
......@@ -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,
&not_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,
&not_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
RETURN
FALSE - ok
TRUE - error
@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.
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
{
Lex->select_lex.table_list.save_and_clear(&Lex->save_list);
}
'(' opt_table_list ')'
{
/* Move the union list to the merge_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
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment