3876 Mattias Jonsson 2012-04-08
WL#4443 - Prune partition locks.
Updated with dlenev's review patch chunk 1.
modified:
sql/opt_explain.cc
sql/rpl_info_table_access.cc
sql/sp_head.cc
sql/sql_acl.cc
sql/sql_base.cc
sql/sql_base.h
sql/sql_delete.cc
sql/sql_insert.cc
sql/sql_lex.cc
sql/sql_lex.h
sql/sql_load.cc
sql/sql_parse.cc
sql/sql_parse.h
sql/sql_prepare.cc
sql/sql_select.cc
sql/sql_union.cc
sql/sql_update.cc
sql/sql_view.cc
3875 Mattias Jonsson 2012-03-30
WL#4443:
Updated some result files.
removed debug runs from push.
added fix for preventing stored functions to execute during prepare
(to avoid using tables before open/lock).
modified:
mysql-test/collections/mysql-trunk-wl4443.push
mysql-test/r/innodb_explain_json_non_select_all.result
mysql-test/r/innodb_explain_json_non_select_none.result
mysql-test/r/myisam_explain_json_non_select_all.result
mysql-test/r/myisam_explain_json_non_select_none.result
mysql-test/suite/rpl/r/rpl_parallel_change_master.result
mysql-test/t/partition_locking.test
sql/item.cc
=== modified file 'sql/opt_explain.cc'
--- a/sql/opt_explain.cc revid:mattias.jonsson@stripped
+++ b/sql/opt_explain.cc revid:mattias.jonsson@stripped
@@ -21,7 +21,7 @@
#include "sql_partition.h" // for make_used_partitions_str()
#include "sql_join_buffer.h" // JOIN_CACHE
#include "opt_explain_format.h"
-#include "sql_base.h" // open_query_tables, lock_query_tables
+#include "sql_base.h" // lock_tables
typedef qep_row::extra extra;
@@ -1963,17 +1963,23 @@ bool mysql_explain_unit(THD *thd, SELECT
unit->fake_select_lex->select_number= UINT_MAX; // just for initialization
unit->fake_select_lex->options|= SELECT_DESCRIBE;
- if (open_query_tables(thd))
- DBUG_RETURN(true);
-
res= unit->prepare(thd, result, SELECT_NO_UNLOCK | SELECT_DESCRIBE);
if (res)
DBUG_RETURN(res);
- if (lock_query_tables(thd))
+ /*
+ If tables are not locked at this point, it means that we have delayed
+ this step until after prepare stage (now), in order to do better
+ partition pruning.
+
+ We need to lock tables now in order to proceed with the remaning
+ stages of query optimization.
+ */
+ if (! thd->lex->is_query_tables_locked() &&
+ lock_tables(thd, thd->lex->query_tables, thd->lex->table_count, 0))
DBUG_RETURN(true);
-
+
res= unit->optimize();
if (!res)
=== modified file 'sql/rpl_info_table_access.cc'
--- a/sql/rpl_info_table_access.cc revid:mattias.jonsson@stripped
+++ b/sql/rpl_info_table_access.cc revid:mattias.jonsson@stripped
@@ -52,6 +52,7 @@ bool Rpl_info_table_access::open_table(T
Open_tables_backup* backup)
{
TABLE_LIST tables;
+ Query_tables_list query_tables_list_backup;
uint flags= (MYSQL_OPEN_IGNORE_GLOBAL_READ_LOCK |
MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY |
@@ -71,6 +72,15 @@ bool Rpl_info_table_access::open_table(T
mysql_reset_thd_for_next_command(thd);
}
+ /*
+ We need to use new Open_tables_state in order not to be affected
+ by LOCK TABLES/prelocked mode.
+ Also in order not to break execution of current statement we also
+ have to backup/reset/restore Query_tables_list part of LEX, which
+ is accessed and updated in the process of opening and locking
+ tables.
+ */
+ thd->lex->reset_n_backup_query_tables_list(&query_tables_list_backup);
thd->reset_n_backup_open_tables_state(backup);
tables.init_one_table(dbstr.str, dbstr.length, tbstr.str, tbstr.length,
@@ -80,6 +90,7 @@ bool Rpl_info_table_access::open_table(T
{
close_thread_tables(thd);
thd->restore_backup_open_tables_state(backup);
+ thd->lex->restore_backup_query_tables_list(&query_tables_list_backup);
my_error(ER_NO_SUCH_TABLE, MYF(0), dbstr.str, tbstr.str);
DBUG_RETURN(TRUE);
}
@@ -95,12 +106,15 @@ bool Rpl_info_table_access::open_table(T
ha_rollback_trans(thd, FALSE);
close_thread_tables(thd);
thd->restore_backup_open_tables_state(backup);
+ thd->lex->restore_backup_query_tables_list(&query_tables_list_backup);
my_error(ER_COL_COUNT_DOESNT_MATCH_CORRUPTED_V2, MYF(0),
tables.table->s->db.str, tables.table->s->table_name.str,
max_num_field, tables.table->s->fields);
DBUG_RETURN(TRUE);
}
+ thd->lex->restore_backup_query_tables_list(&query_tables_list_backup);
+
*table= tables.table;
tables.table->use_all_columns();
DBUG_RETURN(FALSE);
@@ -132,6 +146,8 @@ bool Rpl_info_table_access::close_table(
Open_tables_backup *backup,
bool error)
{
+ Query_tables_list query_tables_list_backup;
+
DBUG_ENTER("Rpl_info_table_access::close_table");
if (table)
@@ -148,7 +164,14 @@ bool Rpl_info_table_access::close_table(
else
ha_commit_trans(thd, TRUE);
}
+ /*
+ In order not to break execution of current statement we have to
+ backup/reset/restore Query_tables_list part of LEX, which is
+ accessed and updated in the process of closing tables.
+ */
+ thd->lex->reset_n_backup_query_tables_list(&query_tables_list_backup);
close_thread_tables(thd);
+ thd->lex->restore_backup_query_tables_list(&query_tables_list_backup);
thd->restore_backup_open_tables_state(backup);
}
=== modified file 'sql/sp_head.cc'
--- a/sql/sp_head.cc revid:mattias.jonsson@stripped
+++ b/sql/sp_head.cc revid:mattias.jonsson@stripped
@@ -2952,7 +2952,7 @@ sp_lex_keeper::reset_lex_and_exec_core(T
UINT_MAX, false));
if (!res)
- res= open_query_tables(thd) || lock_query_tables(thd);
+ res= open_and_lock_tables(thd, m_lex->query_tables, true, 0);
if (!res)
{
=== modified file 'sql/sql_acl.cc'
--- a/sql/sql_acl.cc revid:mattias.jonsson@stripped
+++ b/sql/sql_acl.cc revid:mattias.jonsson@stripped
@@ -3556,7 +3556,7 @@ int mysql_table_grant(THD *thd, TABLE_LI
class LEX_COLUMN *column;
List_iterator <LEX_COLUMN> column_iter(columns);
- if (open_query_tables(thd))
+ if (open_normal_and_derived_tables(thd, table_list, 0))
DBUG_RETURN(TRUE);
while ((column = column_iter++))
=== modified file 'sql/sql_base.cc'
--- a/sql/sql_base.cc revid:mattias.jonsson@stripped
+++ b/sql/sql_base.cc revid:mattias.jonsson@stripped
@@ -1567,8 +1567,13 @@ void close_thread_tables(THD *thd)
/* Ensure we are calling ha_reset() for all used tables */
mark_used_tables_as_free_for_reuse(thd, thd->open_tables);
- /* Set the tables_state to be able to reopen/reuse the tables */
- thd->lex->tables_state= Query_tables_list::TABLES_STATE_REUSABLE;
+ /*
+ Mark this statement as one that has "unlocked" its tables.
+ For purposes of Query_tables_list::lock_tables_state we treat
+ any statement which passed through close_thread_tables() as
+ such.
+ */
+ thd->lex->lock_tables_state= Query_tables_list::LTS_NOT_LOCKED;
/*
We are under simple LOCK TABLES or we're inside a sub-statement
@@ -1611,9 +1616,11 @@ void close_thread_tables(THD *thd)
*/
(void)thd->binlog_flush_pending_rows_event(TRUE);
mysql_unlock_tables(thd, thd->lock);
- thd->lex->tables_state= Query_tables_list::TABLES_STATE_UNLOCKED;
thd->lock=0;
}
+
+ thd->lex->lock_tables_state= Query_tables_list::LTS_NOT_LOCKED;
+
/*
Closing a MERGE child before the parent would be fatal if the
other thread tries to abort the MERGE lock in between.
@@ -1621,8 +1628,6 @@ void close_thread_tables(THD *thd)
if (thd->open_tables)
close_open_tables(thd);
- thd->lex->tables_state= Query_tables_list::TABLES_STATE_CLOSED;
-
DBUG_VOID_RETURN;
}
@@ -5662,101 +5667,6 @@ void open_and_lock_tables_cleanup(THD *t
/**
- Cleanup failed open or lock tables.
-
- @param thd Thread context.
- @param mdl_savepoint Savepoint for rollback.
-*/
-
-void open_and_lock_query_tables_cleanup(THD *thd)
-{
- if (thd->lex->is_query_tables_opened())
- open_and_lock_tables_cleanup(thd, thd->lex->mdl_open_savepoint);
- thd->lex->tables_state= Query_tables_list::TABLES_STATE_CLOSED;
-}
-
-
-/**
- Open query tables, open derived tables and prepares them.
-
- @param thd Thread context.
-
- @return Operation status
- @retval false OK
- @retval true Error
-*/
-
-bool open_query_tables(THD *thd)
-{
- DBUG_ENTER("open_query_tables");
-
- if (thd->lex->is_query_tables_opened())
- {
- DBUG_PRINT("info", ("Query tables already opened"));
- DBUG_RETURN(false);
- }
-
-
- thd->lex->mdl_open_savepoint= thd->mdl_context.mdl_savepoint();
-
- /* if prepare, then we must use shared mdl. */
- if (open_tables(thd,
- &thd->lex->query_tables,
- &thd->lex->tables_lock_count,
- (thd->stmt_arena->is_stmt_prepare()
- ? MYSQL_OPEN_FORCE_SHARED_MDL : 0)))
- DBUG_RETURN(true);
-
- thd->lex->tables_state= Query_tables_list::TABLES_STATE_OPENED;
-
- if (mysql_handle_derived(thd->lex, &mysql_derived_prepare))
- {
- open_and_lock_query_tables_cleanup(thd);
- DBUG_RETURN(true);
- }
-
- thd->lex->tables_state= Query_tables_list::TABLES_STATE_DERIVED_PREPARED;
-
- DBUG_RETURN(false);
-}
-
-
-/**
- Lock query tables.
-
- @param thd Thread context.
-
- @return Operation status
- @retval false OK
- @retval true Error
-*/
-
-bool lock_query_tables(THD *thd)
-{
- DBUG_ENTER("lock_query_tables");
-
- if (thd->lex->is_query_tables_locked())
- {
- DBUG_PRINT("info", ("Query tables already locked"));
- DBUG_RETURN(false);
- }
-
- DBUG_ASSERT(thd->lex->tables_state >=
- Query_tables_list::TABLES_STATE_OPENED);
-
- if (lock_tables(thd,
- thd->lex->query_tables,
- thd->lex->tables_lock_count, 0))
- {
- open_and_lock_query_tables_cleanup(thd);
- DBUG_RETURN(true);
- }
- thd->lex->tables_state= Query_tables_list::TABLES_STATE_LOCKED;
- DBUG_RETURN(false);
-}
-
-
-/**
Open all tables in list, locks them and optionally process derived tables.
@param thd Thread context.
@@ -5808,34 +5718,33 @@ err:
}
-/*
+/**
Open all tables in list and process derived tables
- SYNOPSIS
- open_normal_and_derived_tables
- thd - thread handler
- tables - list of tables for open
- 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 on it.
+ @param thd thread handler
+ @param tables list of tables for open
+ @param 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 on it.
- RETURN
- FALSE - ok
- TRUE - error
+ @retval FALSE - ok
+ @retval TRUE - error
- NOTE
+ @note
This is to be used on prepare stage when you don't read any
data from the tables.
+
+ @note
+ Updates Query_tables_list::table_count as side-effect.
*/
bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags)
{
DML_prelocking_strategy prelocking_strategy;
- uint counter;
MDL_savepoint mdl_savepoint= thd->mdl_context.mdl_savepoint();
DBUG_ENTER("open_normal_and_derived_tables");
- DBUG_ASSERT(!thd->fill_derived_tables());
- if (open_tables(thd, &tables, &counter, flags, &prelocking_strategy) ||
+ if (open_tables(thd, &tables, &thd->lex->table_count, flags,
+ &prelocking_strategy) ||
mysql_handle_derived(thd->lex, &mysql_derived_prepare))
goto end;
@@ -5925,8 +5834,23 @@ bool lock_tables(THD *thd, TABLE_LIST *t
DBUG_ASSERT(thd->locked_tables_mode <= LTM_LOCK_TABLES ||
!thd->lex->requires_prelocking());
+ /*
+ lock_tables() should not be called if this statement has
+ already locked its tables.
+ */
+ DBUG_ASSERT(thd->lex->lock_tables_state == Query_tables_list::LTS_NOT_LOCKED);
+
if (!tables && !thd->lex->requires_prelocking())
+ {
+ /*
+ Even though we are not really locking any tables mark this
+ statement as one that has locked its tables, so we won't
+ call this function second time for the same execution of
+ the same statement.
+ */
+ thd->lex->lock_tables_state= Query_tables_list::LTS_LOCKED;
DBUG_RETURN(thd->decide_logging_format(tables));
+ }
/*
Check for thd->locked_tables_mode to avoid a redundant
@@ -6079,6 +6003,13 @@ bool lock_tables(THD *thd, TABLE_LIST *t
}
}
+ /*
+ Mark the statement as having tables locked. For purposes
+ of Query_tables_list::lock_tables_state we treat any
+ statement which passes through lock_tables() as such.
+ */
+ thd->lex->lock_tables_state= Query_tables_list::LTS_LOCKED;
+
DBUG_RETURN(thd->decide_logging_format(tables));
}
@@ -9568,7 +9499,6 @@ open_system_tables_for_read(THD *thd, TA
we also have to backup and reset/and then restore part of LEX
which is accessed by open_tables() in order to determine if
prelocking is needed and what tables should be added for it.
- close_system_tables() doesn't require such treatment.
*/
lex->reset_n_backup_query_tables_list(&query_tables_list_backup);
thd->reset_n_backup_open_tables_state(backup);
@@ -9607,7 +9537,16 @@ open_system_tables_for_read(THD *thd, TA
void
close_system_tables(THD *thd, Open_tables_backup *backup)
{
+ Query_tables_list query_tables_list_backup;
+
+ /*
+ In order not affect execution of current statement we have to
+ backup/reset/restore Query_tables_list part of LEX, which is
+ accessed and updated in the process of closing tables.
+ */
+ thd->lex->reset_n_backup_query_tables_list(&query_tables_list_backup);
close_thread_tables(thd);
+ thd->lex->restore_backup_query_tables_list(&query_tables_list_backup);
thd->restore_backup_open_tables_state(backup);
}
=== modified file 'sql/sql_base.h'
--- a/sql/sql_base.h revid:mattias.jonsson@stripped
+++ b/sql/sql_base.h revid:mattias.jonsson@stripped
@@ -244,8 +244,6 @@ bool open_tables(THD *thd, TABLE_LIST **
Prelocking_strategy *prelocking_strategy);
void open_and_lock_tables_cleanup(THD *thd,
const MDL_savepoint &mdl_savepoint);
-void open_and_lock_query_tables_cleanup(THD *thd);
-bool open_query_tables(THD *thd);
/* open_and_lock_tables with optional derived handling */
bool open_and_lock_tables(THD *thd, TABLE_LIST *tables,
bool derived, uint flags,
@@ -256,7 +254,6 @@ TABLE *open_n_lock_single_table(THD *thd
Prelocking_strategy *prelocking_strategy);
bool open_normal_and_derived_tables(THD *thd, TABLE_LIST *tables, uint flags);
bool lock_tables(THD *thd, TABLE_LIST *tables, uint counter, uint flags);
-bool lock_query_tables(THD *thd);
void free_io_cache(TABLE *entry);
void intern_close_table(TABLE *entry);
bool close_thread_table(THD *thd, TABLE **table_ptr);
=== modified file 'sql/sql_delete.cc'
--- a/sql/sql_delete.cc revid:mattias.jonsson@stripped
+++ b/sql/sql_delete.cc revid:mattias.jonsson@stripped
@@ -71,7 +71,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *
THD::enum_binlog_query_type query_type= THD::ROW_QUERY_TYPE;
DBUG_ENTER("mysql_delete");
- if (open_query_tables(thd))
+ if (open_normal_and_derived_tables(thd, table_list, 0))
DBUG_RETURN(TRUE);
if (!(table= table_list->table))
@@ -118,7 +118,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *
goto exit_all_parts_pruned_away;
#endif
- if (lock_query_tables(thd))
+ if (lock_tables(thd, table_list, thd->lex->table_count, 0))
DBUG_RETURN(true);
const_cond= (!conds || conds->const_item());
@@ -556,19 +556,17 @@ extern "C" int refpos_order_cmp(const vo
return file->cmp_ref((const uchar*)a, (const uchar*)b);
}
-/*
- make delete specific preparation and checks after opening tables
+/**
+ Make delete specific preparation and checks after opening tables.
- SYNOPSIS
- mysql_multi_delete_prepare()
- thd thread handler
+ @param thd Thread context.
+ @param[out] table_count Number of tables to be deleted from.
- RETURN
- FALSE OK
- TRUE Error
+ @retval FALST - success.
+ @retval TRUE - error.
*/
-int mysql_multi_delete_prepare(THD *thd)
+int mysql_multi_delete_prepare(THD *thd, uint *table_count)
{
LEX *lex= thd->lex;
TABLE_LIST *aux_tables= lex->auxiliary_table_list.first;
@@ -588,6 +586,7 @@ int mysql_multi_delete_prepare(THD *thd)
DELETE_ACL, SELECT_ACL))
DBUG_RETURN(TRUE);
+ *table_count= 0;
/*
Multi-delete can't be constructed over-union => we always have
@@ -599,6 +598,8 @@ int mysql_multi_delete_prepare(THD *thd)
target_tbl;
target_tbl= target_tbl->next_local)
{
+ ++(*table_count);
+
if (!(target_tbl->table= target_tbl->correspondent_table->table))
{
DBUG_ASSERT(target_tbl->correspondent_table->view &&
=== modified file 'sql/sql_insert.cc'
--- a/sql/sql_insert.cc revid:mattias.jonsson@stripped
+++ b/sql/sql_insert.cc revid:mattias.jonsson@stripped
@@ -743,7 +743,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *t
}
else
{
- if (open_query_tables(thd))
+ if (open_normal_and_derived_tables(thd, table_list, 0))
DBUG_RETURN(true);
}
@@ -912,7 +912,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *t
/* Lock the tables now if not delayed/already locked. */
if (!is_locked &&
- lock_query_tables(thd))
+ lock_tables(thd, table_list, thd->lex->table_count, 0))
DBUG_RETURN(true);
/*
=== modified file 'sql/sql_lex.cc'
--- a/sql/sql_lex.cc revid:mattias.jonsson@stripped
+++ b/sql/sql_lex.cc revid:mattias.jonsson@stripped
@@ -2778,8 +2778,8 @@ void Query_tables_list::reset_query_tabl
sroutines_list_own_elements= 0;
binlog_stmt_flags= 0;
stmt_accessed_table_flag= 0;
- tables_state= TABLES_STATE_NONE;
- tables_lock_count= 0;
+ lock_tables_state= LTS_NOT_LOCKED;
+ table_count= 0;
}
=== modified file 'sql/sql_lex.h'
--- a/sql/sql_lex.h revid:mattias.jonsson@stripped
+++ b/sql/sql_lex.h revid:mattias.jonsson@stripped
@@ -46,7 +46,6 @@ class sys_var;
class Item_func_match;
class File_parser;
class Key_part_spec;
-class MDL_savepoint;
#ifdef MYSQL_SERVER
/*
@@ -1069,50 +1068,36 @@ public:
uint sroutines_list_own_elements;
/**
- Enum to track the state of the tables used by the query.
- */
- enum enum_tables_state {
- /** The tables is not yet opened. */
- TABLES_STATE_NONE = 0,
- /**
- The tables are already opened and locked in thd->open_tables,
- but not marked as used.
- */
- TABLES_STATE_REUSABLE,
- /** Base tables are opened. */
- TABLES_STATE_OPENED,
- /** Derived tables are prepared. */
- TABLES_STATE_DERIVED_PREPARED,
- /** Base tables are locked. */
- TABLES_STATE_LOCKED,
- /** Derived tables are optimized. */
- TABLES_STATE_DERIVED_OPTIMIZED,
- /** Derived tables are created for materialization. */
- TABLES_STATE_DERIVED_CREATED,
- /** Derived tables are materialized. */
- TABLES_STATE_DERIVED_MATERIALIZED,
- /** Base tables are unlocked. */
- TABLES_STATE_UNLOCKED,
- /** Base tables are closed or marked for reuse. */
- TABLES_STATE_CLOSED
+ Locking state of tables in this particular statement.
+
+ If we under LOCK TABLES or in prelocked mode we consider tables
+ for the statement to be "locked" if there was a call to lock_tables()
+ (which called handler::start_stmt()) for tables of this statement
+ and there was no matching close_thread_tables() call.
+
+ As result this state may differ significantly from one represented
+ by Open_tables_state::lock/locked_tables_mode more, which are always
+ "on" under LOCK TABLES or in prelocked mode.
+ */
+ enum enum_lock_tables_state {
+ LTS_NOT_LOCKED = 0,
+ LTS_LOCKED
};
- enum_tables_state tables_state;
- bool is_query_tables_opened()
- {
- if (tables_state >= TABLES_STATE_OPENED &&
- tables_state < TABLES_STATE_CLOSED)
- return true;
- return false;
- }
+ enum_lock_tables_state lock_tables_state;
bool is_query_tables_locked()
{
- if (tables_state >= TABLES_STATE_LOCKED &&
- tables_state < TABLES_STATE_UNLOCKED)
- return true;
- return false;
+ return (lock_tables_state == LTS_LOCKED);
}
- uint tables_lock_count;
- MDL_savepoint mdl_open_savepoint;
+
+ /**
+ Number of tables which were open by open_tables() and to be locked
+ by lock_tables().
+ Note that we set this member only in some cases, when this value
+ needs to be passed from open_tables() to lock_tables() which are
+ separated by some amount of code.
+ */
+ uint table_count;
+
/*
These constructor and destructor serve for creation/destruction
of Query_tables_list instances which are used as backup storage.
=== modified file 'sql/sql_load.cc'
--- a/sql/sql_load.cc revid:mattias.jonsson@stripped
+++ b/sql/sql_load.cc revid:mattias.jonsson@stripped
@@ -228,9 +228,8 @@ int mysql_load(THD *thd,sql_exchange *ex
ER(WARN_NON_ASCII_SEPARATOR_NOT_IMPLEMENTED));
}
- if (open_query_tables(thd) || lock_query_tables(thd))
+ if (open_and_lock_tables(thd, table_list, TRUE, 0))
DBUG_RETURN(TRUE);
-
if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context,
&thd->lex->select_lex.top_join_list,
table_list,
=== modified file 'sql/sql_parse.cc'
--- a/sql/sql_parse.cc revid:mattias.jonsson@stripped
+++ b/sql/sql_parse.cc revid:mattias.jonsson@stripped
@@ -2461,8 +2461,7 @@ case SQLCOM_PREPARE:
}
case SQLCOM_DO:
if (check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE)
- || open_query_tables(thd)
- || lock_query_tables(thd))
+ || open_and_lock_tables(thd, all_tables, TRUE, 0))
goto error;
res= mysql_do(thd, *lex->insert_list);
@@ -2777,7 +2776,7 @@ case SQLCOM_PREPARE:
goto end_with_restore_list;
}
- if (!(res= open_query_tables(thd)))
+ if (!(res= open_normal_and_derived_tables(thd, all_tables, 0)))
{
/* The table already exists */
if (create_table->table || create_table->view)
@@ -3291,7 +3290,7 @@ end_with_restore_list:
unit->set_limit(select_lex);
- if (!(res= open_query_tables(thd)))
+ if (!(res= open_normal_and_derived_tables(thd, all_tables, 0)))
{
MYSQL_INSERT_SELECT_START(thd->query());
/* Skip first table, which is the table we are inserting in */
@@ -3367,6 +3366,7 @@ end_with_restore_list:
{
DBUG_ASSERT(first_table == all_tables && first_table != 0);
TABLE_LIST *aux_tables= thd->lex->auxiliary_table_list.first;
+ uint del_table_count;
multi_delete *del_result;
if ((res= multi_delete_precheck(thd, all_tables)))
@@ -3379,18 +3379,18 @@ end_with_restore_list:
goto error;
THD_STAGE_INFO(thd, stage_init);
- if ((res= open_query_tables(thd)))
+ if ((res= open_normal_and_derived_tables(thd, all_tables, 0)))
break;
MYSQL_MULTI_DELETE_START(thd->query());
- if ((res= mysql_multi_delete_prepare(thd)))
+ if ((res= mysql_multi_delete_prepare(thd, &del_table_count)))
{
MYSQL_MULTI_DELETE_DONE(1, 0);
goto error;
}
if (!thd->is_fatal_error &&
- (del_result= new multi_delete(aux_tables, lex->tables_lock_count)))
+ (del_result= new multi_delete(aux_tables, del_table_count)))
{
if (lex->describe)
res= explain_multi_table_modification(thd, del_result);
@@ -3506,9 +3506,8 @@ end_with_restore_list:
{
List<set_var_base> *lex_var_list= &lex->var_list;
- if (check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE)
- || open_query_tables(thd)
- || lock_query_tables(thd))
+ if ((check_table_access(thd, SELECT_ACL, all_tables, FALSE, UINT_MAX, FALSE)
+ || open_and_lock_tables(thd, all_tables, TRUE, 0)))
goto error;
if (!(res= sql_set_variables(thd, lex_var_list)))
{
@@ -4293,8 +4292,7 @@ create_sp_error:
*/
if (check_table_access(thd, SELECT_ACL, all_tables, FALSE,
UINT_MAX, FALSE) ||
- open_query_tables(thd) ||
- lock_query_tables(thd))
+ open_and_lock_tables(thd, all_tables, TRUE, 0))
goto error;
/*
@@ -4865,7 +4863,7 @@ static bool execute_sqlcom_select(THD *t
param->select_limit=
new Item_int((ulonglong) thd->variables.select_limit);
}
- if (!(res= open_query_tables(thd)))
+ if (!(res= open_normal_and_derived_tables(thd, all_tables, 0)))
{
if (lex->describe)
{
=== modified file 'sql/sql_parse.h'
--- a/sql/sql_parse.h revid:mattias.jonsson@stripped
+++ b/sql/sql_parse.h revid:mattias.jonsson@stripped
@@ -42,7 +42,7 @@ bool select_precheck(THD *thd, LEX *lex,
bool multi_update_precheck(THD *thd, TABLE_LIST *tables);
bool multi_delete_precheck(THD *thd, TABLE_LIST *tables);
int mysql_multi_update_prepare(THD *thd);
-int mysql_multi_delete_prepare(THD *thd);
+int mysql_multi_delete_prepare(THD *thd, uint *table_count);
bool mysql_insert_select_prepare(THD *thd);
bool update_precheck(THD *thd, TABLE_LIST *tables);
bool delete_precheck(THD *thd, TABLE_LIST *tables);
=== modified file 'sql/sql_prepare.cc'
--- a/sql/sql_prepare.cc revid:mattias.jonsson@stripped
+++ b/sql/sql_prepare.cc revid:mattias.jonsson@stripped
@@ -90,7 +90,7 @@ When one supplies long data for a placeh
#include "set_var.h"
#include "sql_prepare.h"
#include "sql_parse.h" // insert_precheck, update_precheck, delete_precheck
-#include "sql_base.h" // open_query_tables
+#include "sql_base.h" // open_normal_and_derived_tables
#include "sql_cache.h" // query_cache_*
#include "sql_view.h" // create_view_precheck
#include "sql_delete.h" // mysql_prepare_delete
@@ -1269,7 +1269,8 @@ static bool mysql_test_insert(Prepared_s
If we would use locks, then we have to ensure we are not using
TL_WRITE_DELAYED as having two such locks can cause table corruption.
*/
- if (open_query_tables(thd))
+ if (open_normal_and_derived_tables(thd, table_list,
+ MYSQL_OPEN_FORCE_SHARED_MDL))
goto error;
if ((values= its++))
@@ -1349,7 +1350,8 @@ static int mysql_test_update(Prepared_st
DBUG_ENTER("mysql_test_update");
if (update_precheck(thd, table_list) ||
- open_query_tables(thd))
+ open_normal_and_derived_tables(thd, table_list,
+ MYSQL_OPEN_FORCE_SHARED_MDL))
goto error;
if (table_list->multitable_view)
@@ -1426,7 +1428,8 @@ static bool mysql_test_delete(Prepared_s
DBUG_ASSERT(stmt->is_stmt_prepare());
if (delete_precheck(thd, table_list) ||
- open_query_tables(thd))
+ open_normal_and_derived_tables(thd, table_list,
+ MYSQL_OPEN_FORCE_SHARED_MDL))
goto error;
if (!table_list->table)
@@ -1478,7 +1481,7 @@ static int mysql_test_select(Prepared_st
goto error;
}
- if (open_query_tables(thd))
+ if (open_normal_and_derived_tables(thd, tables, MYSQL_OPEN_FORCE_SHARED_MDL))
goto error;
thd->lex->used_tables= 0; // Updated by setup_fields
@@ -1540,7 +1543,7 @@ static bool mysql_test_do_fields(Prepare
UINT_MAX, FALSE))
DBUG_RETURN(TRUE);
- if (open_query_tables(thd))
+ if (open_normal_and_derived_tables(thd, tables, MYSQL_OPEN_FORCE_SHARED_MDL))
DBUG_RETURN(TRUE);
DBUG_RETURN(setup_fields(thd, Ref_ptr_array(),
*values, MARK_COLUMNS_NONE, 0, 0));
@@ -1572,7 +1575,7 @@ static bool mysql_test_set_fields(Prepar
if ((tables && check_table_access(thd, SELECT_ACL, tables, FALSE,
UINT_MAX, FALSE)) ||
- open_query_tables(thd))
+ open_normal_and_derived_tables(thd, tables, MYSQL_OPEN_FORCE_SHARED_MDL))
goto error;
while ((var= it++))
@@ -1609,7 +1612,7 @@ static bool mysql_test_call_fields(Prepa
if ((tables && check_table_access(thd, SELECT_ACL, tables, FALSE,
UINT_MAX, FALSE)) ||
- open_query_tables(thd))
+ open_normal_and_derived_tables(thd, tables, MYSQL_OPEN_FORCE_SHARED_MDL))
goto err;
while ((item= it++))
@@ -1689,11 +1692,12 @@ select_like_stmt_test_with_open(Prepared
/*
We should not call LEX::unit.cleanup() after this
- open_query_tables() call because we don't allow
+ open_normal_and_derived_tables() call because we don't allow
prepared EXPLAIN yet so derived tables will clean up after
themself.
*/
- if (open_query_tables(stmt->thd))
+ if (open_normal_and_derived_tables(stmt->thd, tables,
+ MYSQL_OPEN_FORCE_SHARED_MDL))
DBUG_RETURN(TRUE);
DBUG_RETURN(select_like_stmt_test(stmt, specific_prepare,
@@ -1734,7 +1738,8 @@ static bool mysql_test_create_table(Prep
if (!(lex->create_info.options & HA_LEX_CREATE_TMP_TABLE))
create_table->open_type= OT_BASE_ONLY;
- if (open_query_tables(stmt->thd))
+ if (open_normal_and_derived_tables(stmt->thd, lex->query_tables,
+ MYSQL_OPEN_FORCE_SHARED_MDL))
DBUG_RETURN(TRUE);
select_lex->context.resolve_in_select_list= TRUE;
@@ -1753,7 +1758,8 @@ static bool mysql_test_create_table(Prep
we validate metadata of all CREATE TABLE statements,
which keeps metadata validation code simple.
*/
- if (open_query_tables(stmt->thd))
+ if (open_normal_and_derived_tables(stmt->thd, lex->query_tables,
+ MYSQL_OPEN_FORCE_SHARED_MDL))
DBUG_RETURN(TRUE);
}
@@ -1794,7 +1800,7 @@ static bool mysql_test_create_view(Prepa
if (open_temporary_tables(thd, tables))
goto err;
- if (open_query_tables(thd))
+ if (open_normal_and_derived_tables(thd, tables, MYSQL_OPEN_FORCE_SHARED_MDL))
goto err;
lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_VIEW;
@@ -1834,6 +1840,18 @@ static bool mysql_test_multiupdate(Prepa
/**
+ Wrapper for mysql_multi_delete_prepare() function which makes
+ it compatible with select_like_stmt_test_with_open().
+*/
+
+static int mysql_multi_delete_prepare_tester(THD *thd)
+{
+ uint table_count;
+ return mysql_multi_delete_prepare(thd, &table_count);
+}
+
+
+/**
Validate and prepare for execution a multi delete statement.
@param stmt prepared statement
@@ -1857,7 +1875,7 @@ static bool mysql_test_multidelete(Prepa
if (multi_delete_precheck(stmt->thd, tables) ||
select_like_stmt_test_with_open(stmt, tables,
- &mysql_multi_delete_prepare,
+ &mysql_multi_delete_prepare_tester,
OPTION_SETUP_TABLES_DONE))
goto error;
if (!tables->table)
@@ -1874,13 +1892,13 @@ error:
/**
Wrapper for mysql_insert_select_prepare, to make change of local tables
- after open_query_tables() call.
+ after open_normal_and_derived_tables() call.
@param thd thread handle
@note
We need to remove the first local table after
- open_query_tables(), because mysql_handle_derived
+ open_normal_and_derived_tables(), because mysql_handle_derived
uses local tables lists.
*/
=== modified file 'sql/sql_select.cc'
--- a/sql/sql_select.cc revid:mattias.jonsson@stripped
+++ b/sql/sql_select.cc revid:mattias.jonsson@stripped
@@ -1166,16 +1166,12 @@ mysql_select(THD *thd,
SELECT_LEX *select_lex)
{
bool free_join= 1;
- bool store_in_query_cache= false;
uint og_num= 0;
ORDER *first_order= NULL;
ORDER *first_group= NULL;
JOIN *join= NULL;
DBUG_ENTER("mysql_select");
- if (open_query_tables(thd))
- DBUG_RETURN(true);
-
if (order)
{
og_num= order->elements;
@@ -1187,10 +1183,6 @@ mysql_select(THD *thd,
first_group= group->first;
}
- /* Only register the query if it was opened above. */
- if (thd->lex->tables_state < Query_tables_list::TABLES_STATE_LOCKED)
- store_in_query_cache= true;
-
if (mysql_prepare_select(thd, tables, wild_num, fields,
conds, og_num, first_order, first_group, having,
proc_param, select_options, result, unit,
@@ -1204,23 +1196,36 @@ mysql_select(THD *thd,
DBUG_RETURN(true);
}
- if (lock_query_tables(thd))
+ if (! thd->lex->is_query_tables_locked())
{
- if (free_join)
+ /*
+ If tables are not locked at this point, it means that we have delayed
+ this step until after prepare stage (i.e. this moment). This allows to
+ do better partition pruning and avoid locking unused partitions.
+ As a consequence, in such a case, prepare stage can rely only on
+ metadata about tables used and not data from them.
+ We need to lock tables now in order to proceed with the remaning
+ stages of query optimization and execution.
+ */
+ if (lock_tables(thd, thd->lex->query_tables, thd->lex->table_count, 0))
{
- thd_proc_info(thd, "end");
- (void) select_lex->cleanup();
+ if (free_join)
+ {
+ thd_proc_info(thd, "end");
+ (void) select_lex->cleanup();
+ }
+ DBUG_RETURN(true);
}
- DBUG_RETURN(true);
- }
- /*
- Tables must be locked before storing the query in the query cache.
- Transactional engines must been signalled that the statement started,
- which external_lock signals.
- */
- if (store_in_query_cache)
+ /*
+ Only register query in cache if it tables were locked above.
+
+ Tables must be locked before storing the query in the query cache.
+ Transactional engines must been signalled that the statement started,
+ which external_lock signals.
+ */
query_cache_store_query(thd, thd->lex->query_tables);
+ }
if (mysql_execute_select(thd, select_lex, free_join, join))
DBUG_RETURN(true);
=== modified file 'sql/sql_union.cc'
--- a/sql/sql_union.cc revid:mattias.jonsson@stripped
+++ b/sql/sql_union.cc revid:mattias.jonsson@stripped
@@ -34,31 +34,36 @@ bool mysql_union(THD *thd, LEX *lex, sel
SELECT_LEX_UNIT *unit, ulong setup_tables_done_option)
{
bool res;
- bool store_in_query_cache= false;
DBUG_ENTER("mysql_union");
- if (open_query_tables(thd))
- DBUG_RETURN(true);
-
- /* Only register the query if it was opened above */
- if (thd->lex->tables_state < Query_tables_list::TABLES_STATE_LOCKED)
- store_in_query_cache= true;
-
res= unit->prepare(thd, result,
SELECT_NO_UNLOCK | setup_tables_done_option);
if (res)
goto err;
- if (lock_query_tables(thd))
- goto err;
+ if (! thd->lex->is_query_tables_locked())
+ {
+ /*
+ If tables are not locked at this point, it means that we have delayed
+ this step until after prepare stage (i.e. this moment). This allows to
+ do better partition pruning and avoid locking unused partitions.
+ As a consequence, in such a case, prepare stage can rely only on
+ metadata about tables used and not data from them.
+ We need to lock tables now in order to proceed with the remaning
+ stages of query optimization and execution.
+ */
+ if (lock_tables(thd, lex->query_tables, lex->table_count, 0))
+ goto err;
- /*
- Tables must be locked before storing the query in the query cache.
- Transactional engines must been signalled that the statement started,
- which external_lock signals.
- */
- if (store_in_query_cache)
+ /*
+ Only register query in cache if it tables were locked above.
+
+ Tables must be locked before storing the query in the query cache.
+ Transactional engines must been signalled that the statement started,
+ which external_lock signals.
+ */
query_cache_store_query(thd, thd->lex->query_tables);
+ }
res= unit->optimize() || unit->exec();
res|= unit->cleanup();
=== modified file 'sql/sql_update.cc'
--- a/sql/sql_update.cc revid:mattias.jonsson@stripped
+++ b/sql/sql_update.cc revid:mattias.jonsson@stripped
@@ -288,7 +288,7 @@ int mysql_update(THD *thd,
DBUG_ENTER("mysql_update");
- if (open_query_tables(thd))
+ if (open_normal_and_derived_tables(thd, table_list, 0))
DBUG_RETURN(1);
if (table_list->multitable_view)
@@ -438,7 +438,7 @@ int mysql_update(THD *thd,
bitmap_copy(&table->part_info->lock_partitions, &lock_partitions);
}
#endif
- if (lock_query_tables(thd))
+ if (lock_tables(thd, table_list, thd->lex->table_count, 0))
DBUG_RETURN(1);
/* Update the table->file->stats.records number */
@@ -1294,7 +1294,10 @@ int mysql_multi_update_prepare(THD *thd)
keep prepare of multi-UPDATE compatible with concurrent LOCK TABLES WRITE
and global read lock.
*/
- if (original_multiupdate && open_query_tables(thd))
+ if (original_multiupdate &&
+ open_normal_and_derived_tables(thd, table_list,
+ (thd->stmt_arena->is_stmt_prepare() ?
+ MYSQL_OPEN_FORCE_SHARED_MDL : 0)))
DBUG_RETURN(TRUE);
/*
setup_tables() need for VIEWs. JOIN::prepare() will call setup_tables()
=== modified file 'sql/sql_view.cc'
--- a/sql/sql_view.cc revid:mattias.jonsson@stripped
+++ b/sql/sql_view.cc revid:mattias.jonsson@stripped
@@ -449,7 +449,7 @@ bool mysql_create_view(THD *thd, TABLE_L
}
/* Not required to lock any tables. */
- if (open_query_tables(thd))
+ if (open_normal_and_derived_tables(thd, lex->query_tables, 0))
{
view= lex->unlink_first_table(&link_to_local);
res= TRUE;
No bundle (reason: useless for push emails).
| Thread |
|---|
| • bzr push into mysql-trunk branch (mattias.jonsson:3875 to 3876) WL#4443 | Mattias Jonsson | 10 Apr |