mirror of
https://github.com/MariaDB/server.git
synced 2025-08-01 03:47:19 +03:00
Better approach for prelocking of tables for stored routines execution
and some SP-related cleanups. - We don't have separate stage for calculation of list of tables to be prelocked and doing implicit LOCK/UNLOCK any more. Instead we calculate this list at open_tables() and do implicit LOCK in lock_tables() (and UNLOCK in close_thread_tables()). Also now we support cases when same table (with same alias) is used several times in the same query in SP. - Cleaned up execution of SP. Moved all common code which handles LEX and does preparations before statement execution or complex expression evaluation to auxilary sp_lex_keeper class. Now all statements in SP (and corresponding instructions) that evaluate expression which can contain subquery have their own LEX.
This commit is contained in:
373
sql/sql_base.cc
373
sql/sql_base.cc
@ -20,6 +20,7 @@
|
||||
#include "mysql_priv.h"
|
||||
#include "sql_select.h"
|
||||
#include "sp_head.h"
|
||||
#include "sp.h"
|
||||
#include "sql_trigger.h"
|
||||
#include <m_ctype.h>
|
||||
#include <my_dir.h>
|
||||
@ -359,7 +360,30 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh,
|
||||
|
||||
|
||||
/*
|
||||
Close all tables used by thread
|
||||
Mark all tables in the list which were used by current substatement
|
||||
as free for reuse.
|
||||
|
||||
SYNOPSIS
|
||||
mark_used_tables_as_free_for_reuse()
|
||||
thd - thread context
|
||||
table - head of the list of tables
|
||||
|
||||
DESCRIPTION
|
||||
Marks all tables in the list which were used by current substatement
|
||||
(they are marked by its query_id) as free for reuse.
|
||||
*/
|
||||
|
||||
static void mark_used_tables_as_free_for_reuse(THD *thd, TABLE *table)
|
||||
{
|
||||
for (; table ; table= table->next)
|
||||
if (table->query_id == thd->query_id)
|
||||
table->query_id= 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Close all tables used by the current substatement, or all tables
|
||||
used by this thread if we are on the upper level.
|
||||
|
||||
SYNOPSIS
|
||||
close_thread_tables()
|
||||
@ -372,14 +396,31 @@ bool close_cached_tables(THD *thd, bool if_wait_for_refresh,
|
||||
IMPLEMENTATION
|
||||
Unlocks tables and frees derived tables.
|
||||
Put all normal tables used by thread in free list.
|
||||
|
||||
When in prelocked mode it will only close/mark as free for reuse
|
||||
tables opened by this substatement, it will also check if we are
|
||||
closing tables after execution of complete query (i.e. we are on
|
||||
upper level) and will leave prelocked mode if needed.
|
||||
*/
|
||||
|
||||
void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived,
|
||||
TABLE *stopper)
|
||||
{
|
||||
bool found_old_table;
|
||||
prelocked_mode_type prelocked_mode= thd->prelocked_mode;
|
||||
DBUG_ENTER("close_thread_tables");
|
||||
|
||||
/*
|
||||
We are assuming here that thd->derived_tables contains ONLY derived
|
||||
tables for this substatement. i.e. instead of approach which uses
|
||||
query_id matching for determining which of the derived tables belong
|
||||
to this substatement we rely on the ability of substatements to
|
||||
save/restore thd->derived_tables during their execution.
|
||||
|
||||
TODO: Probably even better approach is to simply associate list of
|
||||
derived tables with (sub-)statement instead of thread and destroy
|
||||
them at the end of its execution.
|
||||
*/
|
||||
if (thd->derived_tables && !skip_derived)
|
||||
{
|
||||
TABLE *table, *next;
|
||||
@ -394,10 +435,50 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived,
|
||||
}
|
||||
thd->derived_tables= 0;
|
||||
}
|
||||
if (thd->locked_tables)
|
||||
|
||||
if (prelocked_mode)
|
||||
{
|
||||
ha_commit_stmt(thd); // If select statement
|
||||
DBUG_VOID_RETURN; // LOCK TABLES in use
|
||||
/*
|
||||
Mark all temporary tables used by this substatement as free for reuse.
|
||||
*/
|
||||
mark_used_tables_as_free_for_reuse(thd, thd->temporary_tables);
|
||||
}
|
||||
|
||||
if (thd->locked_tables || prelocked_mode)
|
||||
{
|
||||
/*
|
||||
TODO: It is not 100% clear whenever we should do ha_commit_stmt() for
|
||||
sub-statements. This issue needs additional investigation.
|
||||
*/
|
||||
ha_commit_stmt(thd);
|
||||
|
||||
/* We are under simple LOCK TABLES so should not do anything else. */
|
||||
if (!prelocked_mode)
|
||||
DBUG_VOID_RETURN;
|
||||
|
||||
if (!thd->lex->requires_prelocking())
|
||||
{
|
||||
/*
|
||||
If we are executing one of substatements we have to mark
|
||||
all tables which it used as free for reuse.
|
||||
*/
|
||||
mark_used_tables_as_free_for_reuse(thd, thd->open_tables);
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
DBUG_ASSERT(prelocked_mode);
|
||||
/*
|
||||
We are in prelocked mode, so we have to leave it now with doing
|
||||
implicit UNLOCK TABLES if need.
|
||||
*/
|
||||
thd->prelocked_mode= NON_PRELOCKED;
|
||||
|
||||
if (prelocked_mode == PRELOCKED_UNDER_LOCK_TABLES)
|
||||
DBUG_VOID_RETURN;
|
||||
|
||||
thd->lock= thd->locked_tables;
|
||||
thd->locked_tables= 0;
|
||||
/* Fallthrough */
|
||||
}
|
||||
|
||||
if (thd->lock)
|
||||
@ -429,6 +510,17 @@ void close_thread_tables(THD *thd, bool lock_in_use, bool skip_derived,
|
||||
if (!lock_in_use)
|
||||
VOID(pthread_mutex_unlock(&LOCK_open));
|
||||
/* VOID(pthread_sigmask(SIG_SETMASK,&thd->signals,NULL)); */
|
||||
|
||||
if (prelocked_mode == PRELOCKED)
|
||||
{
|
||||
/*
|
||||
If we are here then we are leaving normal prelocked mode, so it is
|
||||
good idea to turn off OPTION_TABLE_LOCK flag.
|
||||
*/
|
||||
DBUG_ASSERT(thd->lex->requires_prelocking());
|
||||
thd->options&= ~(ulong) (OPTION_TABLE_LOCK);
|
||||
}
|
||||
|
||||
DBUG_VOID_RETURN;
|
||||
}
|
||||
|
||||
@ -898,7 +990,8 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
|
||||
!memcmp(table->s->table_cache_key, key,
|
||||
key_length + TMP_TABLE_KEY_EXTRA))
|
||||
{
|
||||
if (table->query_id == thd->query_id)
|
||||
if (table->query_id == thd->query_id ||
|
||||
thd->prelocked_mode && table->query_id)
|
||||
{
|
||||
my_error(ER_CANT_REOPEN_TABLE, MYF(0), table->alias);
|
||||
DBUG_RETURN(0);
|
||||
@ -912,16 +1005,17 @@ TABLE *open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
|
||||
}
|
||||
}
|
||||
|
||||
if (thd->locked_tables)
|
||||
if (thd->locked_tables || thd->prelocked_mode)
|
||||
{ // Using table locks
|
||||
for (table=thd->open_tables; table ; table=table->next)
|
||||
{
|
||||
if (table->s->key_length == key_length &&
|
||||
!memcmp(table->s->table_cache_key,key,key_length) &&
|
||||
!my_strcasecmp(system_charset_info, table->alias, alias))
|
||||
!memcmp(table->s->table_cache_key, key, key_length) &&
|
||||
!my_strcasecmp(system_charset_info, table->alias, alias) &&
|
||||
table->query_id != thd->query_id && /* skip tables already used by this query */
|
||||
!(thd->prelocked_mode && table->query_id))
|
||||
{
|
||||
if (table->query_id != thd->query_id)
|
||||
table->query_id=thd->query_id;
|
||||
table->query_id= thd->query_id;
|
||||
DBUG_PRINT("info",("Using locked table"));
|
||||
goto reset;
|
||||
}
|
||||
@ -1613,21 +1707,34 @@ err:
|
||||
SYNOPSIS
|
||||
open_tables()
|
||||
thd - thread handler
|
||||
start - list of tables
|
||||
start - list of tables in/out
|
||||
counter - number of opened tables will be return using this parameter
|
||||
|
||||
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.
|
||||
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
|
||||
*/
|
||||
|
||||
int open_tables(THD *thd, TABLE_LIST *start, uint *counter)
|
||||
int open_tables(THD *thd, TABLE_LIST **start, uint *counter)
|
||||
{
|
||||
TABLE_LIST *tables;
|
||||
bool refresh;
|
||||
int result=0;
|
||||
DBUG_ENTER("open_tables");
|
||||
MEM_ROOT new_frm_mem;
|
||||
/* Also used for indicating that prelocking is need */
|
||||
TABLE_LIST **query_tables_last_own;
|
||||
DBUG_ENTER("open_tables");
|
||||
/*
|
||||
temporary mem_root for new .frm parsing.
|
||||
TODO: variables for size
|
||||
@ -1637,8 +1744,51 @@ int open_tables(THD *thd, TABLE_LIST *start, uint *counter)
|
||||
thd->current_tablenr= 0;
|
||||
restart:
|
||||
*counter= 0;
|
||||
query_tables_last_own= 0;
|
||||
thd->proc_info="Opening tables";
|
||||
for (tables= start; tables ;tables= tables->next_global)
|
||||
|
||||
/*
|
||||
If we are not already executing prelocked statement and don't have
|
||||
statement for which table list for prelocking is already built, let
|
||||
us cache routines and try to build such table list.
|
||||
|
||||
NOTE: If we want queries with functions to work under explicit
|
||||
LOCK TABLES we have to additionaly lock mysql.proc table in it.
|
||||
At least until Monty will fix SP loading :)
|
||||
|
||||
NOTE: We can't delay prelocking until we will met some sub-statement
|
||||
which really uses tables, since this will imply that we have to restore
|
||||
its table list to be able execute it in some other context.
|
||||
And current views implementation assumes that view tables are added to
|
||||
global table list only once during PS preparing/first SP execution.
|
||||
Also locking at earlier stage is probably faster altough may decrease
|
||||
concurrency a bit.
|
||||
|
||||
NOTE: We will mark statement as requiring prelocking only if we will
|
||||
have non empty table list. But this does not guarantee that in prelocked
|
||||
mode we will have some locked tables, because queries which use only
|
||||
derived/information schema tables and views possible. Thus "counter"
|
||||
may be still zero for prelocked statement...
|
||||
*/
|
||||
if (!thd->prelocked_mode && !thd->lex->requires_prelocking() &&
|
||||
(thd->lex->spfuns.records || thd->lex->spprocs.records))
|
||||
{
|
||||
TABLE_LIST **save_query_tables_last;
|
||||
|
||||
sp_cache_routines(thd, thd->lex);
|
||||
save_query_tables_last= thd->lex->query_tables_last;
|
||||
|
||||
DBUG_ASSERT(thd->lex->query_tables == *start);
|
||||
|
||||
if (sp_add_sp_tables_to_table_list(thd, thd->lex, thd->lex) ||
|
||||
*start)
|
||||
{
|
||||
query_tables_last_own= save_query_tables_last;
|
||||
*start= thd->lex->query_tables;
|
||||
}
|
||||
}
|
||||
|
||||
for (tables= *start; tables ;tables= tables->next_global)
|
||||
{
|
||||
/*
|
||||
Ignore placeholders for derived tables. After derived tables
|
||||
@ -1659,8 +1809,27 @@ int open_tables(THD *thd, TABLE_LIST *start, uint *counter)
|
||||
free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC));
|
||||
if (tables->view)
|
||||
{
|
||||
/* VIEW placeholder */
|
||||
(*counter)--;
|
||||
continue; //VIEW placeholder
|
||||
/*
|
||||
Again if needed we have to get cache all routines used by this view
|
||||
and add tables used by them to table list.
|
||||
*/
|
||||
if (!thd->prelocked_mode && !thd->lex->requires_prelocking() &&
|
||||
(tables->view->spfuns.records || tables->view->spprocs.records))
|
||||
{
|
||||
// FIXME We should catch recursion for both views and funcs here
|
||||
sp_cache_routines(thd, tables->view);
|
||||
|
||||
/* We have at least one table in TL here */
|
||||
if (!query_tables_last_own)
|
||||
query_tables_last_own= thd->lex->query_tables_last;
|
||||
sp_add_sp_tables_to_table_list(thd, thd->lex, tables->view);
|
||||
}
|
||||
/* Cleanup hashes because destructo for this LEX is never called */
|
||||
hash_free(&tables->view->spfuns);
|
||||
hash_free(&tables->view->spprocs);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (refresh) // Refresh in progress
|
||||
@ -1672,7 +1841,12 @@ int open_tables(THD *thd, TABLE_LIST *start, uint *counter)
|
||||
thd->version=refresh_version;
|
||||
TABLE **prev_table= &thd->open_tables;
|
||||
bool found=0;
|
||||
for (TABLE_LIST *tmp= start; tmp; tmp= tmp->next_global)
|
||||
/*
|
||||
QQ: What we should do if we have started building of table list
|
||||
for prelocking ??? Probably throw it away ? But before we should
|
||||
mark all temporary tables as free? How about locked ?
|
||||
*/
|
||||
for (TABLE_LIST *tmp= *start; tmp; tmp= tmp->next_global)
|
||||
{
|
||||
/* Close normal (not temporary) changed tables */
|
||||
if (tmp->table && ! tmp->table->s->tmp_table)
|
||||
@ -1701,7 +1875,27 @@ int open_tables(THD *thd, TABLE_LIST *start, uint *counter)
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
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.
|
||||
|
||||
FIXME Now we are simply turning on prelocking. Proper integration
|
||||
and testing is to be done later.
|
||||
*/
|
||||
if (!thd->prelocked_mode && !thd->lex->requires_prelocking() &&
|
||||
tables->table->triggers &&
|
||||
tables->lock_type >= TL_WRITE_ALLOW_WRITE)
|
||||
{
|
||||
if (!query_tables_last_own)
|
||||
query_tables_last_own= thd->lex->query_tables_last;
|
||||
}
|
||||
free_root(&new_frm_mem, MYF(MY_KEEP_PREALLOC));
|
||||
}
|
||||
|
||||
if (tables->lock_type != TL_UNLOCK && ! thd->locked_tables)
|
||||
tables->table->reginfo.lock_type=tables->lock_type;
|
||||
@ -1709,6 +1903,10 @@ int open_tables(THD *thd, TABLE_LIST *start, uint *counter)
|
||||
}
|
||||
thd->proc_info=0;
|
||||
free_root(&new_frm_mem, MYF(0)); // Free pre-alloced block
|
||||
|
||||
if (query_tables_last_own)
|
||||
thd->lex->mark_as_requiring_prelocking(query_tables_last_own);
|
||||
|
||||
DBUG_RETURN(result);
|
||||
}
|
||||
|
||||
@ -1757,6 +1955,11 @@ static bool check_lock_and_start_stmt(THD *thd, TABLE *table,
|
||||
table_list Table to open is first table in this list
|
||||
lock_type Lock to use for open
|
||||
|
||||
NOTE
|
||||
This function don't do anything like SP/SF/views/triggers analysis done
|
||||
in open_tables(). It is intended for opening of only one concrete table.
|
||||
And used only in special contexts.
|
||||
|
||||
RETURN VALUES
|
||||
table Opened table
|
||||
0 Error
|
||||
@ -1831,7 +2034,7 @@ int simple_open_n_lock_tables(THD *thd, TABLE_LIST *tables)
|
||||
{
|
||||
DBUG_ENTER("simple_open_n_lock_tables");
|
||||
uint counter;
|
||||
if (open_tables(thd, tables, &counter) || lock_tables(thd, tables, counter))
|
||||
if (open_tables(thd, &tables, &counter) || lock_tables(thd, tables, counter))
|
||||
DBUG_RETURN(-1); /* purecov: inspected */
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
@ -1858,7 +2061,7 @@ bool open_and_lock_tables(THD *thd, TABLE_LIST *tables)
|
||||
{
|
||||
uint counter;
|
||||
DBUG_ENTER("open_and_lock_tables");
|
||||
if (open_tables(thd, tables, &counter) ||
|
||||
if (open_tables(thd, &tables, &counter) ||
|
||||
lock_tables(thd, tables, counter) ||
|
||||
mysql_handle_derived(thd->lex, &mysql_derived_prepare) ||
|
||||
(thd->fill_derived_tables() &&
|
||||
@ -1891,7 +2094,7 @@ bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables)
|
||||
uint counter;
|
||||
DBUG_ENTER("open_normal_and_derived_tables");
|
||||
DBUG_ASSERT(!thd->fill_derived_tables());
|
||||
if (open_tables(thd, tables, &counter) ||
|
||||
if (open_tables(thd, &tables, &counter) ||
|
||||
mysql_handle_derived(thd->lex, &mysql_derived_prepare))
|
||||
DBUG_RETURN(TRUE); /* purecov: inspected */
|
||||
relink_tables_for_multidelete(thd); // Not really needed, but
|
||||
@ -1924,6 +2127,27 @@ static void relink_tables_for_multidelete(THD *thd)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Mark all real tables in the list as free for reuse.
|
||||
|
||||
SYNOPSIS
|
||||
mark_real_tables_as_free_for_reuse()
|
||||
thd - thread context
|
||||
table - head of the list of tables
|
||||
|
||||
DESCRIPTION
|
||||
Marks all real tables in the list (i.e. not views, derived
|
||||
or schema tables) as free for reuse.
|
||||
*/
|
||||
|
||||
static void mark_real_tables_as_free_for_reuse(TABLE_LIST *table)
|
||||
{
|
||||
for (; table; table= table->next_global)
|
||||
if (!table->placeholder() && !table->schema_table)
|
||||
table->table->query_id= 0;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Lock all tables in list
|
||||
|
||||
@ -1938,6 +2162,10 @@ static void relink_tables_for_multidelete(THD *thd)
|
||||
handling thr_lock gives us. You most always get all needed locks at
|
||||
once.
|
||||
|
||||
If query for which we are calling this function marked as requring
|
||||
prelocking, this function will do implicit LOCK TABLES and change
|
||||
thd::prelocked_mode accordingly.
|
||||
|
||||
RETURN VALUES
|
||||
0 ok
|
||||
-1 Error
|
||||
@ -1946,36 +2174,125 @@ static void relink_tables_for_multidelete(THD *thd)
|
||||
int lock_tables(THD *thd, TABLE_LIST *tables, uint count)
|
||||
{
|
||||
TABLE_LIST *table;
|
||||
if (!tables)
|
||||
return 0;
|
||||
|
||||
if (!thd->locked_tables)
|
||||
DBUG_ENTER("lock_tables");
|
||||
/*
|
||||
We can't meet statement requiring prelocking if we already
|
||||
in prelocked mode.
|
||||
*/
|
||||
DBUG_ASSERT(!thd->prelocked_mode || !thd->lex->requires_prelocking());
|
||||
/*
|
||||
If statement requires prelocking then it has non-empty table list.
|
||||
So it is safe to shortcut.
|
||||
*/
|
||||
DBUG_ASSERT(!thd->lex->requires_prelocking() || tables);
|
||||
|
||||
if (!tables)
|
||||
DBUG_RETURN(0);
|
||||
|
||||
/*
|
||||
We need this extra check for thd->prelocked_mode because we want to avoid
|
||||
attempts to lock tables in substatements. Checking for thd->locked_tables
|
||||
is not enough in some situations. For example for SP containing
|
||||
"drop table t3; create temporary t3 ..; insert into t3 ...;"
|
||||
thd->locked_tables may be 0 after drop tables, and without this extra
|
||||
check insert will try to lock temporary table t3, that will lead
|
||||
to memory leak...
|
||||
*/
|
||||
if (!thd->locked_tables && !thd->prelocked_mode)
|
||||
{
|
||||
DBUG_ASSERT(thd->lock == 0); // You must lock everything at once
|
||||
TABLE **start,**ptr;
|
||||
|
||||
if (!(ptr=start=(TABLE**) thd->alloc(sizeof(TABLE*)*count)))
|
||||
return -1;
|
||||
DBUG_RETURN(-1);
|
||||
for (table= tables; table; table= table->next_global)
|
||||
{
|
||||
if (!table->placeholder() && !table->schema_table)
|
||||
*(ptr++)= table->table;
|
||||
}
|
||||
|
||||
/* We have to emulate LOCK TABLES if we are statement needs prelocking. */
|
||||
if (thd->lex->requires_prelocking())
|
||||
{
|
||||
thd->in_lock_tables=1;
|
||||
thd->options|= OPTION_TABLE_LOCK;
|
||||
}
|
||||
|
||||
if (!(thd->lock=mysql_lock_tables(thd,start, (uint) (ptr - start))))
|
||||
return -1; /* purecov: inspected */
|
||||
{
|
||||
if (thd->lex->requires_prelocking())
|
||||
{
|
||||
thd->options&= ~(ulong) (OPTION_TABLE_LOCK);
|
||||
thd->in_lock_tables=0;
|
||||
}
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
if (thd->lex->requires_prelocking() &&
|
||||
thd->lex->sql_command != SQLCOM_LOCK_TABLES)
|
||||
{
|
||||
TABLE_LIST *first_not_own= thd->lex->first_not_own_table();
|
||||
/*
|
||||
We just have done implicit LOCK TABLES, and now we have
|
||||
to emulate first open_and_lock_tables() after it.
|
||||
|
||||
Note that "LOCK TABLES" can also be marked as requiring prelocking
|
||||
(e.g. if one locks view which uses functions). We should not emulate
|
||||
such open_and_lock_tables() in this case. We also should not set
|
||||
THD::prelocked_mode or first close_thread_tables() call will do
|
||||
"UNLOCK TABLES".
|
||||
*/
|
||||
thd->locked_tables= thd->lock;
|
||||
thd->lock= 0;
|
||||
thd->in_lock_tables=0;
|
||||
|
||||
for (table= tables; table != first_not_own; table= table->next_global)
|
||||
{
|
||||
if (!table->placeholder() && !table->schema_table)
|
||||
{
|
||||
table->table->query_id= thd->query_id;
|
||||
if (check_lock_and_start_stmt(thd, table->table, table->lock_type))
|
||||
{
|
||||
ha_rollback_stmt(thd);
|
||||
mysql_unlock_tables(thd, thd->locked_tables);
|
||||
thd->locked_tables= 0;
|
||||
thd->options&= ~(ulong) (OPTION_TABLE_LOCK);
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
Let us mark all tables which don't belong to the statement itself,
|
||||
and was marked as occupied during open_tables() as free for reuse.
|
||||
*/
|
||||
mark_real_tables_as_free_for_reuse(first_not_own);
|
||||
thd->prelocked_mode= PRELOCKED;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (table= tables; table; table= table->next_global)
|
||||
TABLE_LIST *first_not_own= thd->lex->first_not_own_table();
|
||||
for (table= tables; table != first_not_own; table= table->next_global)
|
||||
{
|
||||
if (!table->placeholder() &&
|
||||
if (!table->placeholder() && !table->schema_table &&
|
||||
check_lock_and_start_stmt(thd, table->table, table->lock_type))
|
||||
{
|
||||
ha_rollback_stmt(thd);
|
||||
return -1;
|
||||
DBUG_RETURN(-1);
|
||||
}
|
||||
}
|
||||
/*
|
||||
If we are under explicit LOCK TABLES and our statement requires
|
||||
prelocking, we should mark all "additional" tables as free for use
|
||||
and enter prelocked mode.
|
||||
*/
|
||||
if (thd->lex->requires_prelocking())
|
||||
{
|
||||
mark_real_tables_as_free_for_reuse(first_not_own);
|
||||
thd->prelocked_mode= PRELOCKED_UNDER_LOCK_TABLES;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
DBUG_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user