summaryrefslogtreecommitdiff
path: root/sql
diff options
context:
space:
mode:
Diffstat (limited to 'sql')
-rwxr-xr-xsql/CMakeLists.txt2
-rw-r--r--sql/Makefile.am11
-rw-r--r--sql/datadict.cc161
-rw-r--r--sql/datadict.h40
-rw-r--r--sql/ha_ndbcluster.cc7
-rw-r--r--sql/ha_partition.cc2
-rw-r--r--sql/lock.cc11
-rw-r--r--sql/lock.h15
-rw-r--r--sql/log.cc4
-rw-r--r--sql/log.h4
-rw-r--r--sql/mdl.cc264
-rw-r--r--sql/mdl.h26
-rw-r--r--sql/sp_head.cc13
-rw-r--r--sql/sp_head.h4
-rw-r--r--sql/sql_base.cc376
-rw-r--r--sql/sql_base.h169
-rw-r--r--sql/sql_bitmap.h1
-rw-r--r--sql/sql_class.h156
-rw-r--r--sql/sql_db.cc8
-rw-r--r--sql/sql_delete.cc314
-rw-r--r--sql/sql_delete.h4
-rw-r--r--sql/sql_insert.cc10
-rw-r--r--sql/sql_lex.cc1
-rw-r--r--sql/sql_lex.h10
-rw-r--r--sql/sql_parse.cc27
-rw-r--r--sql/sql_parse.h5
-rw-r--r--sql/sql_prepare.cc2
-rw-r--r--sql/sql_rename.cc3
-rw-r--r--sql/sql_show.cc14
-rw-r--r--sql/sql_table.cc143
-rw-r--r--sql/sql_test.cc1
-rw-r--r--sql/sql_trigger.cc6
-rw-r--r--sql/sql_truncate.cc485
-rw-r--r--sql/sql_truncate.h23
-rw-r--r--sql/sql_view.cc53
-rw-r--r--sql/sql_view.h2
-rw-r--r--sql/sql_yacc.yy109
-rw-r--r--sql/table.cc23
-rw-r--r--sql/table.h30
39 files changed, 1531 insertions, 1008 deletions
diff --git a/sql/CMakeLists.txt b/sql/CMakeLists.txt
index a0e157b7806..44160c32225 100755
--- a/sql/CMakeLists.txt
+++ b/sql/CMakeLists.txt
@@ -75,7 +75,7 @@ SET (SQL_SOURCE
sql_connect.cc scheduler.cc
sql_profile.cc event_parse_data.cc
sql_signal.cc rpl_handler.cc mdl.cc
- transaction.cc sys_vars.cc
+ transaction.cc sys_vars.cc sql_truncate.cc datadict.cc
${GEN_SOURCES}
${MYSYS_LIBWRAP_SOURCE})
diff --git a/sql/Makefile.am b/sql/Makefile.am
index f57d1f3bc34..0616893a014 100644
--- a/sql/Makefile.am
+++ b/sql/Makefile.am
@@ -39,7 +39,9 @@ DTRACEFILES = filesort.o \
sql_connect.o \
sql_cursor.o \
sql_delete.o \
+ sql_truncate.o \
sql_insert.o \
+ datadict.o \
sql_parse.o \
sql_prepare.o \
sql_select.o \
@@ -56,7 +58,9 @@ DTRACEFILES_DEPEND = filesort.o \
sql_connect.o \
sql_cursor.o \
sql_delete.o \
+ sql_truncate.o \
sql_insert.o \
+ datadict.o \
sql_parse.o \
sql_prepare.o \
sql_select.o \
@@ -121,7 +125,8 @@ noinst_HEADERS = item.h item_func.h item_sum.h item_cmpfunc.h \
sql_audit.h \
contributors.h sql_servers.h sql_signal.h records.h \
sql_prepare.h rpl_handler.h replication.h mdl.h \
- sql_plist.h transaction.h sys_vars.h
+ sql_plist.h transaction.h sys_vars.h sql_truncate.h \
+ datadict.h
mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \
item.cc item_sum.cc item_buff.cc item_func.cc \
@@ -136,10 +141,10 @@ mysqld_SOURCES = sql_lex.cc sql_handler.cc sql_partition.cc \
sql_connect.cc scheduler.cc sql_parse.cc \
keycaches.cc set_var.cc sql_yacc.yy sys_vars.cc \
sql_base.cc table.cc sql_select.cc sql_insert.cc \
- sql_profile.cc \
+ datadict.cc sql_profile.cc \
sql_prepare.cc sql_error.cc sql_locale.cc \
sql_update.cc sql_delete.cc uniques.cc sql_do.cc \
- procedure.cc sql_test.cc \
+ procedure.cc sql_test.cc sql_truncate.cc \
log.cc init.cc derror.cc sql_acl.cc \
unireg.cc des_key_file.cc \
log_event.cc rpl_record.cc \
diff --git a/sql/datadict.cc b/sql/datadict.cc
new file mode 100644
index 00000000000..33c3b6bc700
--- /dev/null
+++ b/sql/datadict.cc
@@ -0,0 +1,161 @@
+/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "datadict.h"
+#include "sql_priv.h"
+#include "sql_class.h"
+#include "sql_table.h"
+
+
+/**
+ Check type of .frm if we are not going to parse it.
+
+ @param path path to FRM file
+
+ @retval FRMTYPE_ERROR error
+ @retval FRMTYPE_TABLE table
+ @retval FRMTYPE_VIEW view
+*/
+
+frm_type_enum dd_frm_type(THD *thd, char *path, enum legacy_db_type *dbt)
+{
+ File file;
+ uchar header[10]; //"TYPE=VIEW\n" it is 10 characters
+ size_t error;
+ DBUG_ENTER("dd_frm_type");
+
+ *dbt= DB_TYPE_UNKNOWN;
+
+ if ((file= mysql_file_open(key_file_frm, path, O_RDONLY | O_SHARE, MYF(0))) < 0)
+ DBUG_RETURN(FRMTYPE_ERROR);
+ error= mysql_file_read(file, (uchar*) header, sizeof(header), MYF(MY_NABP));
+ mysql_file_close(file, MYF(MY_WME));
+
+ if (error)
+ DBUG_RETURN(FRMTYPE_ERROR);
+ if (!strncmp((char*) header, "TYPE=VIEW\n", sizeof(header)))
+ DBUG_RETURN(FRMTYPE_VIEW);
+
+ /*
+ This is just a check for DB_TYPE. We'll return default unknown type
+ if the following test is true (arg #3). This should not have effect
+ on return value from this function (default FRMTYPE_TABLE)
+ */
+ if (header[0] != (uchar) 254 || header[1] != 1 ||
+ (header[2] != FRM_VER && header[2] != FRM_VER+1 &&
+ (header[2] < FRM_VER+3 || header[2] > FRM_VER+4)))
+ DBUG_RETURN(FRMTYPE_TABLE);
+
+ *dbt= (enum legacy_db_type) (uint) *(header + 3);
+
+ /* Probably a table. */
+ DBUG_RETURN(FRMTYPE_TABLE);
+}
+
+
+/**
+ Given a table name, check if the storage engine for the
+ table referred by this name supports an option 'flag'.
+ Return an error if the table does not exist or is not a
+ base table.
+
+ @pre Any metadata lock on the table.
+
+ @param[in] thd The current session.
+ @param[in] db Table schema.
+ @param[in] table_name Table database.
+ @param[in] flag The option to check.
+ @param[out] yes_no The result. Undefined if error.
+*/
+
+bool dd_check_storage_engine_flag(THD *thd,
+ const char *db, const char *table_name,
+ uint32 flag, bool *yes_no)
+{
+ char path[FN_REFLEN + 1];
+ enum legacy_db_type db_type;
+ handlerton *table_type;
+ LEX_STRING db_name = {(char *) db, strlen(db)};
+
+ if (check_db_name(&db_name))
+ {
+ my_error(ER_WRONG_DB_NAME, MYF(0), db_name.str);
+ return TRUE;
+ }
+
+ if (check_table_name(table_name, strlen(table_name), FALSE))
+ {
+ my_error(ER_WRONG_TABLE_NAME, MYF(0), table_name);
+ return TRUE;
+ }
+
+ /* There should be at least some lock on the table. */
+ DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, db,
+ table_name, MDL_SHARED));
+
+ (void) build_table_filename(path, sizeof(path) - 1, db,
+ table_name, reg_ext, 0);
+
+ dd_frm_type(thd, path, &db_type);
+
+ /* Type is unknown if the object is not found or is not a table. */
+ if (db_type == DB_TYPE_UNKNOWN)
+ {
+ my_error(ER_NO_SUCH_TABLE, MYF(0), db, table_name);
+ return TRUE;
+ }
+
+ table_type= ha_resolve_by_legacy_type(thd, db_type);
+ *yes_no= ha_check_storage_engine_flag(table_type, flag);
+
+ return FALSE;
+}
+
+
+/*
+ Regenerate a metadata locked table.
+
+ @param thd Thread context.
+ @param db Name of the database to which the table belongs to.
+ @param name Table name.
+
+ @retval FALSE Success.
+ @retval TRUE Error.
+*/
+
+bool dd_recreate_table(THD *thd, const char *db, const char *table_name)
+{
+ bool error= TRUE;
+ HA_CREATE_INFO create_info;
+ char path[FN_REFLEN + 1];
+ DBUG_ENTER("dd_recreate_table");
+
+ /* There should be a exclusive metadata lock on the table. */
+ DBUG_ASSERT(thd->mdl_context.is_lock_owner(MDL_key::TABLE, db, table_name,
+ MDL_EXCLUSIVE));
+
+ memset(&create_info, 0, sizeof(create_info));
+
+ /* Create a path to the table, but without a extension. */
+ build_table_filename(path, sizeof(path) - 1, db, table_name, "", 0);
+
+ /* Attempt to reconstruct the table. */
+ mysql_mutex_lock(&LOCK_open);
+ error= ha_create_table(thd, path, db, table_name, &create_info, TRUE);
+ mysql_mutex_unlock(&LOCK_open);
+
+ DBUG_RETURN(error);
+}
+
diff --git a/sql/datadict.h b/sql/datadict.h
new file mode 100644
index 00000000000..05b5a9bba4b
--- /dev/null
+++ b/sql/datadict.h
@@ -0,0 +1,40 @@
+#ifndef DATADICT_INCLUDED
+#define DATADICT_INCLUDED
+/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "handler.h"
+
+/*
+ Data dictionary API.
+*/
+
+enum frm_type_enum
+{
+ FRMTYPE_ERROR= 0,
+ FRMTYPE_TABLE,
+ FRMTYPE_VIEW
+};
+
+
+frm_type_enum dd_frm_type(THD *thd, char *path, enum legacy_db_type *dbt);
+
+bool dd_check_storage_engine_flag(THD *thd,
+ const char *db, const char *table_name,
+ uint32 flag,
+ bool *yes_no);
+bool dd_recreate_table(THD *thd, const char *db, const char *table_name);
+
+#endif // DATADICT_INCLUDED
diff --git a/sql/ha_ndbcluster.cc b/sql/ha_ndbcluster.cc
index 9f003174d2e..099763b3fe9 100644
--- a/sql/ha_ndbcluster.cc
+++ b/sql/ha_ndbcluster.cc
@@ -7408,9 +7408,10 @@ int ndbcluster_find_files(handlerton *hton, THD *thd,
DBUG_PRINT("info", ("Remove table %s/%s", db, file_name_str));
// Delete the table and all related files
TABLE_LIST table_list;
- bzero((char*) &table_list,sizeof(table_list));
- table_list.db= (char*) db;
- table_list.alias= table_list.table_name= (char*)file_name_str;
+ table_list.init_one_table(db, strlen(db), file_name_str,
+ strlen(file_name_str), file_name_str,
+ TL_WRITE);
+ table_list.mdl_request.set_type(MDL_EXCLUSIVE);
(void)mysql_rm_table_part2(thd, &table_list,
FALSE, /* if_exists */
FALSE, /* drop_temporary */
diff --git a/sql/ha_partition.cc b/sql/ha_partition.cc
index 2506e2fc8b3..9e06c0371d4 100644
--- a/sql/ha_partition.cc
+++ b/sql/ha_partition.cc
@@ -1306,7 +1306,7 @@ int ha_partition::prepare_new_partition(TABLE *tbl,
assumes that external_lock() is last call that may fail here.
Otherwise see description for cleanup_new_partition().
*/
- if ((error= file->ha_external_lock(ha_thd(), m_lock_type)))
+ if ((error= file->ha_external_lock(ha_thd(), F_WRLCK)))
goto error_external_lock;
DBUG_PRINT("info", ("partition %s external locked", part_name));
diff --git a/sql/lock.cc b/sql/lock.cc
index 3f13f15454a..d35f2c359fb 100644
--- a/sql/lock.cc
+++ b/sql/lock.cc
@@ -415,7 +415,7 @@ void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock)
THR_LOCK_DATA **lock=sql_lock->locks;
for (i=found=0 ; i < sql_lock->lock_count ; i++)
{
- if (sql_lock->locks[i]->type >= TL_WRITE_ALLOW_READ)
+ if (sql_lock->locks[i]->type > TL_WRITE_ALLOW_WRITE)
{
swap_variables(THR_LOCK_DATA *, *lock, sql_lock->locks[i]);
lock++;
@@ -435,7 +435,7 @@ void mysql_unlock_read_tables(THD *thd, MYSQL_LOCK *sql_lock)
for (i=found=0 ; i < sql_lock->table_count ; i++)
{
DBUG_ASSERT(sql_lock->table[i]->lock_position == i);
- if ((uint) sql_lock->table[i]->reginfo.lock_type >= TL_WRITE_ALLOW_READ)
+ if ((uint) sql_lock->table[i]->reginfo.lock_type > TL_WRITE_ALLOW_WRITE)
{
swap_variables(TABLE *, *table, sql_lock->table[i]);
table++;
@@ -866,6 +866,8 @@ static MYSQL_LOCK *get_lock_data(THD *thd, TABLE **table_ptr, uint count,
before calling it. Also it cannot be called while holding
LOCK_open mutex. Both these invariants are enforced by asserts
in MDL_context::acquire_locks().
+ @note Initialization of MDL_request members of TABLE_LIST elements
+ is a responsibility of the caller.
@retval FALSE Success.
@retval TRUE Failure (OOM or thread was killed).
@@ -880,12 +882,7 @@ bool lock_table_names(THD *thd, TABLE_LIST *table_list)
global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE);
for (lock_table= table_list; lock_table; lock_table= lock_table->next_local)
- {
- lock_table->mdl_request.init(MDL_key::TABLE,
- lock_table->db, lock_table->table_name,
- MDL_EXCLUSIVE);
mdl_requests.push_front(&lock_table->mdl_request);
- }
mdl_requests.push_front(&global_request);
diff --git a/sql/lock.h b/sql/lock.h
index f7c19913675..84c7bce0679 100644
--- a/sql/lock.h
+++ b/sql/lock.h
@@ -15,33 +15,32 @@ typedef struct st_mysql_lock MYSQL_LOCK;
#define MYSQL_OPEN_TEMPORARY_ONLY 0x0004
#define MYSQL_LOCK_IGNORE_GLOBAL_READ_ONLY 0x0008
#define MYSQL_LOCK_LOG_TABLE 0x0010
-#define MYSQL_OPEN_TAKE_UPGRADABLE_MDL 0x0020
/**
Do not try to acquire a metadata lock on the table: we
already have one.
*/
-#define MYSQL_OPEN_HAS_MDL_LOCK 0x0040
+#define MYSQL_OPEN_HAS_MDL_LOCK 0x0020
/**
If in locked tables mode, ignore the locked tables and get
a new instance of the table.
*/
-#define MYSQL_OPEN_GET_NEW_TABLE 0x0080
+#define MYSQL_OPEN_GET_NEW_TABLE 0x0040
/** Don't look up the table in the list of temporary tables. */
-#define MYSQL_OPEN_SKIP_TEMPORARY 0x0100
+#define MYSQL_OPEN_SKIP_TEMPORARY 0x0080
/** Fail instead of waiting when conficting metadata lock is discovered. */
-#define MYSQL_OPEN_FAIL_ON_MDL_CONFLICT 0x0200
+#define MYSQL_OPEN_FAIL_ON_MDL_CONFLICT 0x0100
/** Open tables using MDL_SHARED lock instead of one specified in parser. */
-#define MYSQL_OPEN_FORCE_SHARED_MDL 0x0400
+#define MYSQL_OPEN_FORCE_SHARED_MDL 0x0200
/**
Open tables using MDL_SHARED_HIGH_PRIO lock instead of one specified
in parser.
*/
-#define MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL 0x0800
+#define MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL 0x0400
/**
When opening or locking the table, use the maximum timeout
(LONG_TIMEOUT = 1 year) rather than the user-supplied timeout value.
*/
-#define MYSQL_LOCK_IGNORE_TIMEOUT 0x1000
+#define MYSQL_LOCK_IGNORE_TIMEOUT 0x0800
/** Please refer to the internals manual. */
#define MYSQL_OPEN_REOPEN (MYSQL_OPEN_IGNORE_FLUSH |\
diff --git a/sql/log.cc b/sql/log.cc
index fd17e04b212..680a56ec161 100644
--- a/sql/log.cc
+++ b/sql/log.cc
@@ -370,8 +370,8 @@ bool LOGGER::is_log_table_enabled(uint log_table_type)
/* Check if a given table is opened log table */
-int check_if_log_table(uint db_len, const char *db, uint table_name_len,
- const char *table_name, uint check_if_opened)
+int check_if_log_table(size_t db_len, const char *db, size_t table_name_len,
+ const char *table_name, bool check_if_opened)
{
if (db_len == 5 &&
!(lower_case_table_names ?
diff --git a/sql/log.h b/sql/log.h
index cd3faace598..d264f62fb64 100644
--- a/sql/log.h
+++ b/sql/log.h
@@ -490,8 +490,8 @@ public:
};
-int check_if_log_table(uint db_len, const char *db, uint table_name_len,
- const char *table_name, uint check_if_opened);
+int check_if_log_table(size_t db_len, const char *db, size_t table_name_len,
+ const char *table_name, bool check_if_opened);
class Log_to_csv_event_handler: public Log_event_handler
{
diff --git a/sql/mdl.cc b/sql/mdl.cc
index ddf518fbb1c..68ac0cc599d 100644
--- a/sql/mdl.cc
+++ b/sql/mdl.cc
@@ -105,23 +105,46 @@ enum enum_deadlock_weight
};
-
/**
A context of the recursive traversal through all contexts
in all sessions in search for deadlock.
*/
-class Deadlock_detection_context
+class Deadlock_detection_visitor
{
public:
- Deadlock_detection_context(MDL_context *start_arg)
- : start(start_arg),
- victim(NULL),
- current_search_depth(0)
- { }
- MDL_context *start;
- MDL_context *victim;
- uint current_search_depth;
+ Deadlock_detection_visitor(MDL_context *start_node_arg)
+ : m_start_node(start_node_arg),
+ m_victim(NULL),
+ m_current_search_depth(0)
+ {}
+ bool enter_node(MDL_context * /* unused */);
+ void leave_node(MDL_context * /* unused */);
+
+ bool inspect_edge(MDL_context *dest);
+
+ MDL_context *get_victim() const { return m_victim; }
+
+ /**
+ Change the deadlock victim to a new one if it has lower deadlock
+ weight.
+ */
+ MDL_context *opt_change_victim_to(MDL_context *new_victim);
+private:
+ /**
+ The context which has initiated the search. There
+ can be multiple searches happening in parallel at the same time.
+ */
+ MDL_context *m_start_node;
+ /** If a deadlock is found, the context that identifies the victim. */
+ MDL_context *m_victim;
+ /** Set to the 0 at start. Increased whenever
+ we descend into another MDL context (aka traverse to the next
+ wait-for graph node). When MAX_SEARCH_DEPTH is reached, we
+ assume that a deadlock is found, even if we have not found a
+ loop.
+ */
+ uint m_current_search_depth;
/**
Maximum depth for deadlock searches. After this depth is
achieved we will unconditionally declare that there is a
@@ -140,6 +163,74 @@ public:
/**
+ Enter a node of a wait-for graph. After
+ a node is entered, inspect_edge() will be called
+ for all wait-for destinations of this node. Then
+ leave_node() will be called.
+ We call "enter_node()" for all nodes we inspect,
+ including the starting node.
+
+ @retval TRUE Maximum search depth exceeded.
+ @retval FALSE OK.
+*/
+
+bool Deadlock_detection_visitor::enter_node(MDL_context * /* unused */)
+{
+ if (++m_current_search_depth >= MAX_SEARCH_DEPTH)
+ return TRUE;
+ return FALSE;
+}
+
+
+/**
+ Done inspecting this node. Decrease the search
+ depth. Clear the node for debug safety.
+*/
+
+void Deadlock_detection_visitor::leave_node(MDL_context * /* unused */)
+{
+ --m_current_search_depth;
+}
+
+
+/**
+ Inspect a wait-for graph edge from one MDL context to another.
+
+ @retval TRUE A loop is found.
+ @retval FALSE No loop is found.
+*/
+
+bool Deadlock_detection_visitor::inspect_edge(MDL_context *node)
+{
+ return node == m_start_node;
+}
+
+
+/**
+ Change the deadlock victim to a new one if it has lower deadlock
+ weight.
+
+ @retval new_victim Victim is not changed.
+ @retval !new_victim New victim became the current.
+*/
+
+MDL_context *
+Deadlock_detection_visitor::opt_change_victim_to(MDL_context *new_victim)
+{
+ if (m_victim == NULL ||
+ m_victim->get_deadlock_weight() >= new_victim->get_deadlock_weight())
+ {
+ /* Swap victims, unlock the old one. */
+ MDL_context *tmp= m_victim;
+ m_victim= new_victim;
+ return tmp;
+ }
+ /* No change, unlock the current context. */
+ return new_victim;
+}
+
+
+/**
Get a bit corresponding to enum_mdl_type value in a granted/waiting bitmaps
and compatibility matrices.
*/
@@ -272,7 +363,7 @@ public:
void remove_ticket(Ticket_list MDL_lock::*queue, MDL_ticket *ticket);
bool find_deadlock(MDL_ticket *waiting_ticket,
- Deadlock_detection_context *deadlock_ctx);
+ Deadlock_detection_visitor *dvisitor);
/** List of granted tickets for this lock. */
Ticket_list m_granted;
@@ -408,7 +499,7 @@ mdl_locks_key(const uchar *record, size_t *length,
statement, the design capitalizes on that to later save on
look ups in the table definition cache. This leads to reduced
contention overall and on LOCK_open in particular.
- Please see the description of MDL_context::acquire_shared_lock()
+ Please see the description of MDL_context::acquire_lock_impl()
for details.
*/
@@ -750,13 +841,6 @@ MDL_request::create(MDL_key::enum_mdl_namespace mdl_namespace, const char *db,
}
-uint MDL_request::get_deadlock_weight() const
-{
- return key.mdl_namespace() == MDL_key::GLOBAL ||
- type > MDL_SHARED_NO_WRITE ?
- MDL_DEADLOCK_WEIGHT_DDL : MDL_DEADLOCK_WEIGHT_DML;
-}
-
/**
Auxiliary functions needed for creation/destruction of MDL_lock objects.
@@ -805,6 +889,21 @@ void MDL_ticket::destroy(MDL_ticket *ticket)
/**
+ Return the 'weight' of this ticket for the
+ victim selection algorithm. Requests with
+ lower weight are preferred to requests
+ with higher weight when choosing a victim.
+*/
+
+uint MDL_ticket::get_deadlock_weight() const
+{
+ return (m_lock->key.mdl_namespace() == MDL_key::GLOBAL ||
+ m_type > MDL_SHARED_NO_WRITE ?
+ MDL_DEADLOCK_WEIGHT_DDL : MDL_DEADLOCK_WEIGHT_DML);
+}
+
+
+/**
Helper functions and macros to be used for killable waiting in metadata
locking subsystem.
@@ -1539,7 +1638,6 @@ bool MDL_context::acquire_lock_impl(MDL_request *mdl_request,
mysql_prlock_unlock(&lock->m_rwlock);
- set_deadlock_weight(mdl_request->get_deadlock_weight());
will_wait_for(ticket);
/* There is a shared or exclusive lock on the object. */
@@ -1689,9 +1787,8 @@ err:
shared mode).
@note There can be only one upgrader for a lock or we will have deadlock.
- This invariant is ensured by code outside of metadata subsystem usually
- by obtaining some sort of exclusive table-level lock (e.g. TL_WRITE,
- TL_WRITE_ALLOW_READ) before performing upgrade of metadata lock.
+ This invariant is ensured by the fact that upgradeable locks SNW
+ and SNRW are not compatible with each other and themselves.
@retval FALSE Success
@retval TRUE Failure (thread was killed)
@@ -1752,46 +1849,57 @@ MDL_context::upgrade_shared_lock_to_exclusive(MDL_ticket *mdl_ticket,
bool MDL_lock::find_deadlock(MDL_ticket *waiting_ticket,
- Deadlock_detection_context *deadlock_ctx)
+ Deadlock_detection_visitor *dvisitor)
{
MDL_ticket *ticket;
- bool result= FALSE;
+ MDL_context *src_ctx= waiting_ticket->get_ctx();
+ bool result= TRUE;
+
+ if (dvisitor->enter_node(src_ctx))
+ return TRUE;
mysql_prlock_rdlock(&m_rwlock);
+ /* Must be initialized after taking a read lock. */
Ticket_iterator granted_it(m_granted);
Ticket_iterator waiting_it(m_waiting);
+ /*
+ We do a breadth-first search first -- that is, inspect all
+ edges of the current node, and only then follow up to the next
+ node. In workloads that involve wait-for graph loops this
+ has proven to be a more efficient strategy [citation missing].
+ */
while ((ticket= granted_it++))
{
- if (ticket->is_incompatible_when_granted(waiting_ticket->get_type()) &&
- ticket->get_ctx() != waiting_ticket->get_ctx() &&
- ticket->get_ctx() == deadlock_ctx->start)
+ /* Filter out edges that point to the same node. */
+ if (ticket->get_ctx() != src_ctx &&
+ ticket->is_incompatible_when_granted(waiting_ticket->get_type()) &&
+ dvisitor->inspect_edge(ticket->get_ctx()))
{
- result= TRUE;
goto end;
}
}
while ((ticket= waiting_it++))
{
- if (ticket->is_incompatible_when_waiting(waiting_ticket->get_type()) &&
- ticket->get_ctx() != waiting_ticket->get_ctx() &&
- ticket->get_ctx() == deadlock_ctx->start)
+ /* Filter out edges that point to the same node. */
+ if (ticket->get_ctx() != src_ctx &&
+ ticket->is_incompatible_when_waiting(waiting_ticket->get_type()) &&
+ dvisitor->inspect_edge(ticket->get_ctx()))
{
- result= TRUE;
goto end;
}
}
+ /* Recurse and inspect all adjacent nodes. */
granted_it.rewind();
while ((ticket= granted_it++))
{
- if (ticket->is_incompatible_when_granted(waiting_ticket->get_type()) &&
- ticket->get_ctx() != waiting_ticket->get_ctx() &&
- ticket->get_ctx()->find_deadlock(deadlock_ctx))
+ if (ticket->get_ctx() != src_ctx &&
+ ticket->is_incompatible_when_granted(waiting_ticket->get_type()) &&
+ ticket->get_ctx()->find_deadlock(dvisitor))
{
- result= TRUE;
goto end;
}
}
@@ -1799,23 +1907,34 @@ bool MDL_lock::find_deadlock(MDL_ticket *waiting_ticket,
waiting_it.rewind();
while ((ticket= waiting_it++))
{
- if (ticket->is_incompatible_when_waiting(waiting_ticket->get_type()) &&
- ticket->get_ctx() != waiting_ticket->get_ctx() &&
- ticket->get_ctx()->find_deadlock(deadlock_ctx))
+ if (ticket->get_ctx() != src_ctx &&
+ ticket->is_incompatible_when_waiting(waiting_ticket->get_type()) &&
+ ticket->get_ctx()->find_deadlock(dvisitor))
{
- result= TRUE;
goto end;
}
}
+ result= FALSE;
end:
mysql_prlock_unlock(&m_rwlock);
+ dvisitor->leave_node(src_ctx);
return result;
}
-bool MDL_context::find_deadlock(Deadlock_detection_context *deadlock_ctx)
+/**
+ Recursively traverse the wait-for graph of MDL contexts
+ in search for deadlocks.
+
+ @retval TRUE A deadlock is found. A victim is remembered
+ by the visitor.
+ @retval FALSE
+*/
+
+bool MDL_context::find_deadlock(Deadlock_detection_visitor *dvisitor)
{
+ MDL_context *m_unlock_ctx= this;
bool result= FALSE;
mysql_prlock_rdlock(&m_waiting_for_lock);
@@ -1830,57 +1949,55 @@ bool MDL_context::find_deadlock(Deadlock_detection_context *deadlock_ctx)
*/
if (peek_signal() != VICTIM_WAKE_UP)
{
-
- if (++deadlock_ctx->current_search_depth >
- deadlock_ctx->MAX_SEARCH_DEPTH)
- result= TRUE;
- else
- result= m_waiting_for->m_lock->find_deadlock(m_waiting_for,
- deadlock_ctx);
- --deadlock_ctx->current_search_depth;
+ result= m_waiting_for->m_lock->find_deadlock(m_waiting_for, dvisitor);
+ if (result)
+ m_unlock_ctx= dvisitor->opt_change_victim_to(this);
}
}
-
- if (result)
- {
- if (! deadlock_ctx->victim)
- deadlock_ctx->victim= this;
- else if (deadlock_ctx->victim->m_deadlock_weight >= m_deadlock_weight)
- {
- mysql_prlock_unlock(&deadlock_ctx->victim->m_waiting_for_lock);
- deadlock_ctx->victim= this;
- }
- else
- mysql_prlock_unlock(&m_waiting_for_lock);
- }
- else
- mysql_prlock_unlock(&m_waiting_for_lock);
+ /*
+ We may recurse into the same MDL_context more than once
+ in case this is not the starting node. Make sure we release the
+ read lock as it's been taken, except for 1 read lock for
+ the deadlock victim.
+ */
+ if (m_unlock_ctx)
+ mysql_prlock_unlock(&m_unlock_ctx->m_waiting_for_lock);
return result;
}
+/**
+ Try to find a deadlock. This function produces no errors.
+
+ @retval TRUE A deadlock is found.
+ @retval FALSE No deadlock found.
+*/
+
bool MDL_context::find_deadlock()
{
while (1)
{
+ MDL_context *victim;
/*
- The fact that we use fresh instance of deadlock_ctx for each
+ The fact that we use fresh instance of dvisitor for each
search performed by find_deadlock() below is important, code
responsible for victim selection relies on this.
*/
- Deadlock_detection_context deadlock_ctx(this);
+ Deadlock_detection_visitor dvisitor(this);
- if (! find_deadlock(&deadlock_ctx))
+ if (! find_deadlock(&dvisitor))
{
/* No deadlocks are found! */
break;
}
- if (deadlock_ctx.victim != this)
+ victim= dvisitor.get_victim();
+
+ if (victim != this)
{
- deadlock_ctx.victim->awake(VICTIM_WAKE_UP);
- mysql_prlock_unlock(&deadlock_ctx.victim->m_waiting_for_lock);
+ victim->awake(VICTIM_WAKE_UP);
+ mysql_prlock_unlock(&victim->m_waiting_for_lock);
/*
After adding new arc to waiting graph we found that it participates
in some loop (i.e. there is a deadlock). We decided to destroy this
@@ -1892,8 +2009,8 @@ bool MDL_context::find_deadlock()
}
else
{
- DBUG_ASSERT(&deadlock_ctx.victim->m_waiting_for_lock == &m_waiting_for_lock);
- mysql_prlock_unlock(&deadlock_ctx.victim->m_waiting_for_lock);
+ DBUG_ASSERT(&victim->m_waiting_for_lock == &m_waiting_for_lock);
+ mysql_prlock_unlock(&victim->m_waiting_for_lock);
return TRUE;
}
}
@@ -1968,7 +2085,6 @@ MDL_context::wait_for_lock(MDL_request *mdl_request, ulong lock_wait_timeout)
wait_reset();
mysql_prlock_unlock(&lock->m_rwlock);
- set_deadlock_weight(MDL_DEADLOCK_WEIGHT_DML);
will_wait_for(pending_ticket);
bool is_deadlock= find_deadlock();
diff --git a/sql/mdl.h b/sql/mdl.h
index 89a679be264..ef133520140 100644
--- a/sql/mdl.h
+++ b/sql/mdl.h
@@ -34,7 +34,7 @@ class THD;
class MDL_context;
class MDL_lock;
class MDL_ticket;
-class Deadlock_detection_context;
+class Deadlock_detection_visitor;
/**
Type of metadata lock request.
@@ -308,6 +308,10 @@ public:
MDL_key key;
public:
+ static void *operator new(size_t size, MEM_ROOT *mem_root) throw ()
+ { return alloc_root(mem_root, size); }
+ static void operator delete(void *ptr, MEM_ROOT *mem_root) {}
+
void init(MDL_key::enum_mdl_namespace namespace_arg,
const char *db_arg, const char *name_arg,
enum_mdl_type mdl_type_arg);
@@ -318,8 +322,6 @@ public:
DBUG_ASSERT(ticket == NULL);
type= type_arg;
}
- uint get_deadlock_weight() const;
-
static MDL_request *create(MDL_key::enum_mdl_namespace mdl_namespace,
const char *db, const char *name,
enum_mdl_type mdl_type, MEM_ROOT *root);
@@ -413,6 +415,8 @@ public:
bool is_incompatible_when_granted(enum_mdl_type type) const;
bool is_incompatible_when_waiting(enum_mdl_type type) const;
+ /* A helper used to determine which lock request should be aborted. */
+ uint get_deadlock_weight() const;
private:
friend class MDL_context;
@@ -524,6 +528,9 @@ public:
inline THD *get_thd() const { return m_thd; }
+ /** @pre Only valid if we started waiting for lock. */
+ inline uint get_deadlock_weight() const
+ { return m_waiting_for->get_deadlock_weight(); }
/**
Wake up context which is waiting for a change of MDL_lock state.
*/
@@ -554,7 +561,7 @@ public:
return m_needs_thr_lock_abort;
}
- bool find_deadlock(Deadlock_detection_context *deadlock_ctx);
+ bool find_deadlock(Deadlock_detection_visitor *dvisitor);
private:
/**
All MDL tickets acquired by this connection.
@@ -665,17 +672,6 @@ private:
mysql_prlock_unlock(&m_waiting_for_lock);
}
- void set_deadlock_weight(uint weight)
- {
- /*
- m_deadlock_weight should not be modified while m_waiting_for is
- non-NULL as in this case this context might participate in deadlock
- and so m_deadlock_weight can be accessed from other threads.
- */
- DBUG_ASSERT(m_waiting_for == NULL);
- m_deadlock_weight= weight;
- }
-
void stop_waiting()
{
mysql_prlock_wrlock(&m_waiting_for_lock);
diff --git a/sql/sp_head.cc b/sql/sp_head.cc
index 9395146f18f..61f00fdf59a 100644
--- a/sql/sp_head.cc
+++ b/sql/sp_head.cc
@@ -4002,6 +4002,11 @@ sp_head::add_used_tables_to_table_list(THD *thd,
table->prelocking_placeholder= 1;
table->belong_to_view= belong_to_view;
table->trg_event_map= stab->trg_event_map;
+ /*
+ Since we don't allow DDL on base tables in prelocked mode it
+ is safe to infer the type of metadata lock from the type of
+ table lock.
+ */
table->mdl_request.init(MDL_key::TABLE, table->db, table->table_name,
table->lock_type >= TL_WRITE_ALLOW_WRITE ?
MDL_SHARED_WRITE : MDL_SHARED_READ);
@@ -4031,8 +4036,9 @@ sp_head::add_used_tables_to_table_list(THD *thd,
TABLE_LIST *
sp_add_to_query_tables(THD *thd, LEX *lex,
- const char *db, const char *name,
- thr_lock_type locktype)
+ const char *db, const char *name,
+ thr_lock_type locktype,
+ enum_mdl_type mdl_type)
{
TABLE_LIST *table;
@@ -4047,8 +4053,7 @@ sp_add_to_query_tables(THD *thd, LEX *lex,
table->select_lex= lex->current_select;
table->cacheable_table= 1;
table->mdl_request.init(MDL_key::TABLE, table->db, table->table_name,
- table->lock_type >= TL_WRITE_ALLOW_WRITE ?
- MDL_SHARED_WRITE : MDL_SHARED_READ);
+ mdl_type);
lex->add_to_query_tables(table);
return table;
diff --git a/sql/sp_head.h b/sql/sp_head.h
index 539a2da5f8c..9796c49fdfb 100644
--- a/sql/sp_head.h
+++ b/sql/sp_head.h
@@ -1342,7 +1342,9 @@ set_routine_security_ctx(THD *thd, sp_head *sp, bool is_proc,
TABLE_LIST *
sp_add_to_query_tables(THD *thd, LEX *lex,
const char *db, const char *name,
- thr_lock_type locktype);
+ thr_lock_type locktype,
+ enum_mdl_type mdl_type);
+
Item *
sp_prepare_func_item(THD* thd, Item **it_addr);
diff --git a/sql/sql_base.cc b/sql/sql_base.cc
index 325f054db02..bea10343eb6 100644
--- a/sql/sql_base.cc
+++ b/sql/sql_base.cc
@@ -16,18 +16,18 @@
/* Basic functions needed by many modules */
+#include "sql_base.h" // setup_table_map
#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
#include "unireg.h"
#include "debug_sync.h"
-#include "sql_base.h" // setup_table_map
#include "lock.h" // broadcast_refresh, mysql_lock_remove,
// mysql_unlock_tables,
// mysql_lock_have_duplicate
#include "sql_show.h" // append_identifier
#include "strfunc.h" // find_type
#include "parse_file.h" // sql_parse_prepare, File_parser
-#include "sql_view.h" // mysql_frm_type, mysql_make_view, VIEW_ANY_ACL
+#include "sql_view.h" // mysql_make_view, VIEW_ANY_ACL
#include "sql_parse.h" // check_table_access
#include "sql_insert.h" // kill_delayed_threads
#include "sql_acl.h" // *_ACL, check_grant_all_columns,
@@ -52,6 +52,7 @@
#include <hash.h>
#include "rpl_filter.h"
#include "sql_table.h" // build_table_filename
+#include "datadict.h" // dd_frm_type()
#ifdef __WIN__
#include <io.h>
#endif
@@ -2364,82 +2365,90 @@ void table_share_release_hook(void *share)
/**
- A helper function that acquires an MDL lock for a table
- being opened.
+ Try to acquire an MDL lock for a table being opened.
+
+ @param[in,out] thd Session context, to report errors.
+ @param[out] ot_ctx Open table context, to hold the back off
+ state. If we failed to acquire a lock
+ due to a lock conflict, we add the
+ failed request to the open table context.
+ @param[in,out] mdl_request A request for an MDL lock.
+ If we managed to acquire a ticket
+ (no errors or lock conflicts occurred),
+ contains a reference to it on
+ return. However, is not modified if MDL
+ lock type- modifying flags were provided.
+ @param[in] flags flags MYSQL_OPEN_FORCE_SHARED_MDL,
+ MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL or
+ MYSQL_OPEN_FAIL_ON_MDL_CONFLICT
+ @sa open_table().
+ @param[out] mdl_ticket Only modified if there was no error.
+ If we managed to acquire an MDL
+ lock, contains a reference to the
+ ticket, otherwise is set to NULL.
+
+ @retval TRUE An error occurred.
+ @retval FALSE No error, but perhaps a lock conflict, check mdl_ticket.
*/
static bool
-open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list,
+open_table_get_mdl_lock(THD *thd, Open_table_context *ot_ctx,
MDL_request *mdl_request,
- Open_table_context *ot_ctx,
- uint flags)
+ uint flags,
+ MDL_ticket **mdl_ticket)
{
- if (table_list->lock_strategy)
+ if (flags & (MYSQL_OPEN_FORCE_SHARED_MDL |
+ MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL))
{
- MDL_request_list mdl_requests;
- MDL_request *global_request;
/*
- In case of CREATE TABLE .. If NOT EXISTS .. SELECT, the table
- may not yet exist. Let's acquire an exclusive lock for that
- case. If later it turns out the table existsed, we will
- downgrade the lock to shared. Note that, according to the
- locking protocol, all exclusive locks must be acquired before
- shared locks. This invariant is preserved here and is also
- enforced by asserts in metadata locking subsystem.
+ MYSQL_OPEN_FORCE_SHARED_MDL flag means that we are executing
+ PREPARE for a prepared statement and want to override
+ the type-of-operation aware metadata lock which was set
+ in the parser/during view opening with a simple shared
+ metadata lock.
+ This is necessary to allow concurrent execution of PREPARE
+ and LOCK TABLES WRITE statement against the same table.
+
+ MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL flag means that we open
+ the table in order to get information about it for one of I_S
+ queries and also want to override the type-of-operation aware
+ shared metadata lock which was set earlier (e.g. during view
+ opening) with a high-priority shared metadata lock.
+ This is necessary to avoid unnecessary waiting and extra
+ ER_WARN_I_S_SKIPPED_TABLE warnings when accessing I_S tables.
+
+ These two flags are mutually exclusive.
*/
+ DBUG_ASSERT(!(flags & MYSQL_OPEN_FORCE_SHARED_MDL) ||
+ !(flags & MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL));
- mdl_request->set_type(MDL_EXCLUSIVE);
- DBUG_ASSERT(! thd->mdl_context.has_locks() ||
- thd->handler_tables_hash.records ||
- thd->global_read_lock.is_acquired());
-
- if (!(global_request= ot_ctx->get_global_mdl_request(thd)))
- return 1;
-
- mdl_requests.push_front(mdl_request);
- mdl_requests.push_front(global_request);
+ mdl_request= new (thd->mem_root) MDL_request(mdl_request);
+ if (mdl_request == NULL)
+ return TRUE;
- if (thd->mdl_context.acquire_locks(&mdl_requests, ot_ctx->get_timeout()))
- return 1;
+ mdl_request->set_type((flags & MYSQL_OPEN_FORCE_SHARED_MDL) ?
+ MDL_SHARED : MDL_SHARED_HIGH_PRIO);
}
- else
- {
- if (flags & MYSQL_OPEN_FORCE_SHARED_MDL)
- {
- /*
- While executing PREPARE for prepared statement we override
- type-of-operation aware type of shared metadata lock which
- was set in the parser with simple shared metadata lock.
- This is necessary to allow concurrent execution of PREPARE
- and LOCK TABLES WRITE statement which locks one of the tables
- used in the statement being prepared.
- */
- DBUG_ASSERT(!(flags & (MYSQL_OPEN_TAKE_UPGRADABLE_MDL |
- MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL)));
- mdl_request->set_type(MDL_SHARED);
- }
- else if (flags & MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL)
- {
- DBUG_ASSERT(!(flags & MYSQL_OPEN_TAKE_UPGRADABLE_MDL));
- mdl_request->set_type(MDL_SHARED_HIGH_PRIO);
- }
+ ot_ctx->add_request(mdl_request);
- ot_ctx->add_request(mdl_request);
-
- if (thd->mdl_context.try_acquire_lock(mdl_request))
- return 1;
+ if (thd->mdl_context.try_acquire_lock(mdl_request))
+ return TRUE;
- if (mdl_request->ticket == NULL)
+ if (mdl_request->ticket == NULL)
+ {
+ if (flags & MYSQL_OPEN_FAIL_ON_MDL_CONFLICT)
{
- if (flags & MYSQL_OPEN_FAIL_ON_MDL_CONFLICT)
- my_error(ER_WARN_I_S_SKIPPED_TABLE, MYF(0), table_list->db, table_list->table_name);
- else
- ot_ctx->request_backoff_action(Open_table_context::OT_WAIT_MDL_LOCK);
- return 1;
+ my_error(ER_WARN_I_S_SKIPPED_TABLE, MYF(0),
+ mdl_request->key.db_name(), mdl_request->key.name());
+ return TRUE;
}
+ if (ot_ctx->request_backoff_action(Open_table_context::OT_WAIT_MDL_LOCK,
+ mdl_request, NULL))
+ return TRUE;
}
- return 0;
+ *mdl_ticket= mdl_request->ticket;
+ return FALSE;
}
@@ -2474,11 +2483,9 @@ open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list,
is never opened. In both cases, metadata locks are always taken according
to the lock strategy.
- This function will take a exclusive metadata lock on the table if
- TABLE_LIST::lock_strategy is EXCLUSIVE_DOWNGRADABLE_MDL or EXCLUSIVE_MDL.
- If the lock strategy is EXCLUSIVE_DOWNGRADABLE_MDL and opening the table
- is successful, the exclusive metadata lock is downgraded to a shared
- lock.
+ If the lock strategy is OTLS_DOWNGRADE_IF_EXISTS and opening the table
+ is successful, the exclusive metadata lock acquired by the caller
+ is downgraded to a shared lock.
RETURN
TRUE Open failed. "action" parameter may contain type of action
@@ -2490,13 +2497,13 @@ open_table_get_mdl_lock(THD *thd, TABLE_LIST *table_list,
bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
- Open_table_context *ot_ctx, uint flags)
+ Open_table_context *ot_ctx)
{
reg1 TABLE *table;
char key[MAX_DBKEY_LENGTH];
uint key_length;
char *alias= table_list->alias;
- MDL_request *mdl_request;
+ uint flags= ot_ctx->get_flags();
MDL_ticket *mdl_ticket;
int error;
TABLE_SHARE *share;
@@ -2534,7 +2541,8 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
if (thd->version != refresh_version)
{
- (void) ot_ctx->request_backoff_action(Open_table_context::OT_WAIT_TDC);
+ (void) ot_ctx->request_backoff_action(Open_table_context::OT_WAIT_TDC,
+ NULL, NULL);
DBUG_RETURN(TRUE);
}
}
@@ -2678,7 +2686,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
during prelocking process (in this case in theory we still
should hold shared metadata lock on it).
*/
- if (mysql_frm_type(thd, path, &not_used) == FRMTYPE_VIEW)
+ if (dd_frm_type(thd, path, &not_used) == FRMTYPE_VIEW)
{
if (!tdc_open_view(thd, table_list, alias, key, key_length,
mem_root, 0))
@@ -2707,23 +2715,25 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
This is the normal use case.
*/
- mdl_request= &table_list->mdl_request;
if (! (flags & MYSQL_OPEN_HAS_MDL_LOCK))
{
- if (open_table_get_mdl_lock(thd, table_list, mdl_request, ot_ctx, flags))
+ if (open_table_get_mdl_lock(thd, ot_ctx, &table_list->mdl_request,
+ flags, &mdl_ticket) ||
+ mdl_ticket == NULL)
{
DEBUG_SYNC(thd, "before_open_table_wait_refresh");
DBUG_RETURN(TRUE);
}
DEBUG_SYNC(thd, "after_open_table_mdl_shared");
}
-
- /*
- Grab reference to the granted MDL lock ticket. Must be done after
- open_table_get_mdl_lock as the lock on the table might have been
- acquired previously (MYSQL_OPEN_HAS_MDL_LOCK).
- */
- mdl_ticket= mdl_request->ticket;
+ else
+ {
+ /*
+ Grab reference to the MDL lock ticket that was acquired
+ by the caller.
+ */
+ mdl_ticket= table_list->mdl_request.ticket;
+ }
hash_value= my_calc_hash(&table_def_cache, (uchar*) key, key_length);
mysql_mutex_lock(&LOCK_open);
@@ -2743,7 +2753,8 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
{
/* Someone did a refresh while thread was opening tables */
mysql_mutex_unlock(&LOCK_open);
- (void) ot_ctx->request_backoff_action(Open_table_context::OT_WAIT_TDC);
+ (void) ot_ctx->request_backoff_action(Open_table_context::OT_WAIT_TDC,
+ NULL, NULL);
DBUG_RETURN(TRUE);
}
@@ -2802,26 +2813,15 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
if (open_new_frm(thd, share, alias,
(uint) (HA_OPEN_KEYFILE | HA_OPEN_RNDFILE |
HA_GET_INDEX | HA_TRY_READ_ONLY),
- READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD |
- (flags & OPEN_VIEW_NO_PARSE), thd->open_options,
+ READ_KEYINFO | COMPUTE_TYPES | EXTRA_RECORD,
+ thd->open_options,
0, table_list, mem_root))
goto err_unlock;
/* TODO: Don't free this */
release_table_share(share);
- if (flags & OPEN_VIEW_NO_PARSE)
- {
- /*
- VIEW not really opened, only frm were read.
- Set 1 as a flag here
- */
- table_list->view= (LEX*)1;
- }
- else
- {
- DBUG_ASSERT(table_list->view);
- }
+ DBUG_ASSERT(table_list->view);
mysql_mutex_unlock(&LOCK_open);
DBUG_RETURN(FALSE);
@@ -2884,7 +2884,8 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
*/
release_table_share(share);
mysql_mutex_unlock(&LOCK_open);
- (void) ot_ctx->request_backoff_action(Open_table_context::OT_WAIT_TDC);
+ (void) ot_ctx->request_backoff_action(Open_table_context::OT_WAIT_TDC,
+ NULL, NULL);
DBUG_RETURN(TRUE);
}
/* Force close at once after usage */
@@ -2924,12 +2925,14 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
if (error == 7)
{
share->version= 0;
- (void) ot_ctx->request_backoff_action(Open_table_context::OT_DISCOVER);
+ (void) ot_ctx->request_backoff_action(Open_table_context::OT_DISCOVER,
+ NULL, table_list);
}
else if (share->crashed)
{
share->version= 0;
- (void) ot_ctx->request_backoff_action(Open_table_context::OT_REPAIR);
+ (void) ot_ctx->request_backoff_action(Open_table_context::OT_REPAIR,
+ NULL, table_list);
}
goto err_unlock;
@@ -2953,7 +2956,7 @@ bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
table exists now we should downgrade our exclusive metadata
lock on this table to SW metadata lock.
*/
- if (table_list->lock_strategy == TABLE_LIST::EXCLUSIVE_DOWNGRADABLE_MDL &&
+ if (table_list->lock_strategy == TABLE_LIST::OTLS_DOWNGRADE_IF_EXISTS &&
!(flags & MYSQL_OPEN_HAS_MDL_LOCK))
mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_WRITE);
@@ -3351,7 +3354,7 @@ unlink_all_closed_tables(THD *thd, MYSQL_LOCK *lock, size_t reopen_count)
bool
Locked_tables_list::reopen_tables(THD *thd)
{
- Open_table_context ot_ctx_unused(thd, LONG_TIMEOUT);
+ Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN);
size_t reopen_count= 0;
MYSQL_LOCK *lock;
MYSQL_LOCK *merged_lock;
@@ -3363,8 +3366,7 @@ Locked_tables_list::reopen_tables(THD *thd)
continue;
/* Links into thd->open_tables upon success */
- if (open_table(thd, table_list, thd->mem_root, &ot_ctx_unused,
- MYSQL_OPEN_REOPEN))
+ if (open_table(thd, table_list, thd->mem_root, &ot_ctx))
{
unlink_all_closed_tables(thd, 0, reopen_count);
return TRUE;
@@ -3786,14 +3788,18 @@ end_with_lock_open:
/** Open_table_context */
-Open_table_context::Open_table_context(THD *thd, ulong timeout)
- :m_action(OT_NO_ACTION),
+Open_table_context::Open_table_context(THD *thd, uint flags)
+ :m_failed_mdl_request(NULL),
+ m_failed_table(NULL),
m_start_of_statement_svp(thd->mdl_context.mdl_savepoint()),
+ m_global_mdl_request(NULL),
+ m_timeout(flags & MYSQL_LOCK_IGNORE_TIMEOUT ?
+ LONG_TIMEOUT : thd->variables.lock_wait_timeout),
+ m_flags(flags),
+ m_action(OT_NO_ACTION),
m_has_locks((thd->in_multi_stmt_transaction_mode() &&
thd->mdl_context.has_locks()) ||
- thd->mdl_context.trans_sentinel()),
- m_global_mdl_request(NULL),
- m_timeout(timeout)
+ thd->mdl_context.trans_sentinel())
{}
@@ -3807,10 +3813,8 @@ MDL_request *Open_table_context::get_global_mdl_request(THD *thd)
{
if (! m_global_mdl_request)
{
- char *buff;
- if ((buff= (char*)thd->alloc(sizeof(MDL_request))))
+ if ((m_global_mdl_request= new (thd->mem_root) MDL_request()))
{
- m_global_mdl_request= new (buff) MDL_request();
m_global_mdl_request->init(MDL_key::GLOBAL, "", "",
MDL_INTENTION_EXCLUSIVE);
}
@@ -3829,7 +3833,8 @@ MDL_request *Open_table_context::get_global_mdl_request(THD *thd)
bool
Open_table_context::
-request_backoff_action(enum_open_table_action action_arg)
+request_backoff_action(enum_open_table_action action_arg,
+ MDL_request *mdl_request, TABLE_LIST *table)
{
/*
We are inside a transaction that already holds locks and have
@@ -3853,6 +3858,19 @@ request_backoff_action(enum_open_table_action action_arg)
return TRUE;
}
m_action= action_arg;
+ /*
+ If waiting for metadata lock is requested, a pointer to
+ MDL_request object for which we were unable to acquire the
+ lock is required.
+ */
+ DBUG_ASSERT(m_action != OT_WAIT_MDL_LOCK || mdl_request);
+ m_failed_mdl_request= mdl_request;
+ /*
+ If auto-repair or discovery are requested, a pointer to table
+ list element must be provided.
+ */
+ DBUG_ASSERT((m_action != OT_DISCOVER && m_action != OT_REPAIR) || table);
+ m_failed_table= table;
return FALSE;
}
@@ -3861,10 +3879,6 @@ request_backoff_action(enum_open_table_action action_arg)
Recover from failed attempt of open table by performing requested action.
@param thd Thread context
- @param mdl_request MDL_request of the object that caused the problem.
- @param table Optional (can be NULL). Used only if action is OT_REPAIR.
- In that case a TABLE_LIST for the table to be repaired.
- @todo: It's unnecessary and should be removed.
@pre This function should be called only with "action" != OT_NO_ACTION
and after having called @sa close_tables_for_reopen().
@@ -3875,8 +3889,7 @@ request_backoff_action(enum_open_table_action action_arg)
bool
Open_table_context::
-recover_from_failed_open(THD *thd, MDL_request *mdl_request,
- TABLE_LIST *table)
+recover_from_failed_open(THD *thd)
{
bool result= FALSE;
/*
@@ -3888,7 +3901,8 @@ recover_from_failed_open(THD *thd, MDL_request *mdl_request,
switch (m_action)
{
case OT_WAIT_MDL_LOCK:
- result= thd->mdl_context.wait_for_lock(mdl_request, get_timeout());
+ result= thd->mdl_context.wait_for_lock(m_failed_mdl_request,
+ get_timeout());
break;
case OT_WAIT_TDC:
result= tdc_wait_for_old_versions(thd, &m_mdl_requests, get_timeout());
@@ -3897,7 +3911,7 @@ recover_from_failed_open(THD *thd, MDL_request *mdl_request,
case OT_DISCOVER:
{
MDL_request mdl_global_request;
- MDL_request mdl_xlock_request(mdl_request);
+ MDL_request mdl_xlock_request(&m_failed_table->mdl_request);
MDL_request_list mdl_requests;
mdl_global_request.init(MDL_key::GLOBAL, "", "",
@@ -3911,14 +3925,11 @@ recover_from_failed_open(THD *thd, MDL_request *mdl_request,
thd->mdl_context.acquire_locks(&mdl_requests, get_timeout())))
break;
- DBUG_ASSERT(mdl_request->key.mdl_namespace() == MDL_key::TABLE);
mysql_mutex_lock(&LOCK_open);
- tdc_remove_table(thd, TDC_RT_REMOVE_ALL,
- mdl_request->key.db_name(),
- mdl_request->key.name());
- ha_create_table_from_engine(thd,
- mdl_request->key.db_name(),
- mdl_request->key.name());
+ tdc_remove_table(thd, TDC_RT_REMOVE_ALL, m_failed_table->db,
+ m_failed_table->table_name);
+ ha_create_table_from_engine(thd, m_failed_table->db,
+ m_failed_table->table_name);
mysql_mutex_unlock(&LOCK_open);
thd->warning_info->clear_warning_info(thd->query_id);
@@ -3929,7 +3940,7 @@ recover_from_failed_open(THD *thd, MDL_request *mdl_request,
case OT_REPAIR:
{
MDL_request mdl_global_request;
- MDL_request mdl_xlock_request(mdl_request);
+ MDL_request mdl_xlock_request(&m_failed_table->mdl_request);
MDL_request_list mdl_requests;
mdl_global_request.init(MDL_key::GLOBAL, "", "",
@@ -3943,14 +3954,12 @@ recover_from_failed_open(THD *thd, MDL_request *mdl_request,
thd->mdl_context.acquire_locks(&mdl_requests, get_timeout())))
break;
- DBUG_ASSERT(mdl_request->key.mdl_namespace() == MDL_key::TABLE);
mysql_mutex_lock(&LOCK_open);
- tdc_remove_table(thd, TDC_RT_REMOVE_ALL,
- mdl_request->key.db_name(),
- mdl_request->key.name());
+ tdc_remove_table(thd, TDC_RT_REMOVE_ALL, m_failed_table->db,
+ m_failed_table->table_name);
mysql_mutex_unlock(&LOCK_open);
- result= auto_repair_table(thd, table);
+ result= auto_repair_table(thd, m_failed_table);
thd->mdl_context.release_transactional_locks();
break;
}
@@ -3959,6 +3968,13 @@ recover_from_failed_open(THD *thd, MDL_request *mdl_request,
}
/* Remove all old requests, they will be re-added. */
m_mdl_requests.empty();
+ /*
+ Reset the pointers to conflicting MDL request and the
+ TABLE_LIST element, set when we need auto-discovery or repair,
+ for safety.
+ */
+ m_failed_mdl_request= NULL;
+ m_failed_table= NULL;
/* Prepare for possible another back-off. */
m_action= OT_NO_ACTION;
return result;
@@ -4087,7 +4103,8 @@ open_and_process_routine(THD *thd, Query_tables_list *prelocking_ctx,
if (rt->mdl_request.ticket == NULL)
{
/* A lock conflict. Someone's trying to modify SP metadata. */
- ot_ctx->request_backoff_action(Open_table_context::OT_WAIT_MDL_LOCK);
+ ot_ctx->request_backoff_action(Open_table_context::OT_WAIT_MDL_LOCK,
+ &rt->mdl_request, NULL);
DBUG_RETURN(TRUE);
}
DEBUG_SYNC(thd, "after_shared_lock_pname");
@@ -4234,12 +4251,14 @@ open_and_process_table(THD *thd, LEX *lex, TABLE_LIST *tables,
*/
if (tables->view)
{
+ MDL_ticket *mdl_ticket;
/*
We still need to take a MDL lock on the merged view to protect
it from concurrent changes.
*/
- if (!open_table_get_mdl_lock(thd, tables, &tables->mdl_request,
- ot_ctx, flags))
+ if (!open_table_get_mdl_lock(thd, ot_ctx, &tables->mdl_request,
+ flags, &mdl_ticket) &&
+ mdl_ticket != NULL)
goto process_view_routines;
/* Fall-through to return error. */
}
@@ -4268,12 +4287,12 @@ open_and_process_table(THD *thd, LEX *lex, TABLE_LIST *tables,
*/
Prelock_error_handler prelock_handler;
thd->push_internal_handler(& prelock_handler);
- error= open_table(thd, tables, new_frm_mem, ot_ctx, flags);
+ error= open_table(thd, tables, new_frm_mem, ot_ctx);
thd->pop_internal_handler();
safe_to_ignore_table= prelock_handler.safely_trapped_errors();
}
else
- error= open_table(thd, tables, new_frm_mem, ot_ctx, flags);
+ error= open_table(thd, tables, new_frm_mem, ot_ctx);
free_root(new_frm_mem, MYF(MY_KEEP_PREALLOC));
@@ -4429,6 +4448,8 @@ end:
should be acquired.
@param tables_end End of list of tables.
@param ot_ctx Context of open_tables() operation.
+ @param flags Bitmap of flags to modify how the tables will be
+ open, see open_table() description for details.
@retval FALSE Success.
@retval TRUE Failure (e.g. connection was killed)
@@ -4437,31 +4458,30 @@ end:
static bool
open_tables_acquire_upgradable_mdl(THD *thd, TABLE_LIST *tables_start,
TABLE_LIST *tables_end,
- Open_table_context *ot_ctx)
+ Open_table_context *ot_ctx,
+ uint flags)
{
MDL_request_list mdl_requests;
TABLE_LIST *table;
DBUG_ASSERT(!thd->locked_tables_mode);
- DEBUG_SYNC(thd, "open_tables_acquire_upgradable_mdl");
for (table= tables_start; table && table != tables_end;
table= table->next_global)
{
- if (table->lock_type >= TL_WRITE_ALLOW_WRITE &&
+ if (table->mdl_request.type >= MDL_SHARED_NO_WRITE &&
!(table->open_type == OT_TEMPORARY_ONLY ||
+ (flags & MYSQL_OPEN_TEMPORARY_ONLY) ||
(table->open_type != OT_BASE_ONLY &&
+ ! (flags & MYSQL_OPEN_SKIP_TEMPORARY) &&
find_temporary_table(thd, table))))
- {
- table->mdl_request.set_type(table->lock_type > TL_WRITE_ALLOW_READ ?
- MDL_SHARED_NO_READ_WRITE :
- MDL_SHARED_NO_WRITE);
mdl_requests.push_front(&table->mdl_request);
- }
}
if (! mdl_requests.is_empty())
{
+ DEBUG_SYNC(thd, "open_tables_acquire_upgradable_mdl");
+
MDL_request *global_request= ot_ctx->get_global_mdl_request(thd);
if (global_request == NULL)
@@ -4475,11 +4495,8 @@ open_tables_acquire_upgradable_mdl(THD *thd, TABLE_LIST *tables_start,
for (table= tables_start; table && table != tables_end;
table= table->next_global)
{
- if (table->lock_type >= TL_WRITE_ALLOW_WRITE)
- {
+ if (table->mdl_request.type >= MDL_SHARED_NO_WRITE)
table->mdl_request.ticket= NULL;
- table->mdl_request.set_type(MDL_SHARED_WRITE);
- }
}
return FALSE;
@@ -4495,6 +4512,8 @@ open_tables_acquire_upgradable_mdl(THD *thd, TABLE_LIST *tables_start,
@param tables_start Start of list of tables on which upgradable locks
should be searched for.
@param tables_end End of list of tables.
+ @param flags Bitmap of flags to modify how the tables will be
+ open, see open_table() description for details.
@retval FALSE Success.
@retval TRUE Failure (e.g. connection was killed)
@@ -4502,7 +4521,7 @@ open_tables_acquire_upgradable_mdl(THD *thd, TABLE_LIST *tables_start,
static bool
open_tables_check_upgradable_mdl(THD *thd, TABLE_LIST *tables_start,
- TABLE_LIST *tables_end)
+ TABLE_LIST *tables_end, uint flags)
{
TABLE_LIST *table;
@@ -4511,9 +4530,11 @@ open_tables_check_upgradable_mdl(THD *thd, TABLE_LIST *tables_start,
for (table= tables_start; table && table != tables_end;
table= table->next_global)
{
- if (table->lock_type >= TL_WRITE_ALLOW_WRITE &&
+ if (table->mdl_request.type >= MDL_SHARED_NO_WRITE &&
!(table->open_type == OT_TEMPORARY_ONLY ||
+ (flags & MYSQL_OPEN_TEMPORARY_ONLY) ||
(table->open_type != OT_BASE_ONLY &&
+ ! (flags & MYSQL_OPEN_SKIP_TEMPORARY) &&
find_temporary_table(thd, table))))
{
/*
@@ -4525,8 +4546,14 @@ open_tables_check_upgradable_mdl(THD *thd, TABLE_LIST *tables_start,
lock, all other instances of TABLE for the same table will have the
same ticket.
- Note that find_table_for_mdl_upgrade() will report an error if a
- ticket is not found.
+ Note that this works OK even for CREATE TABLE statements which
+ request X type of metadata lock. This is because under LOCK TABLES
+ such statements don't create the table but only check if it exists
+ or, in most complex case, only insert into it.
+ Thus SNRW lock should be enough.
+
+ Note that find_table_for_mdl_upgrade() will report an error if
+ no suitable ticket is found.
*/
if (!find_table_for_mdl_upgrade(thd->open_tables, table->db,
table->table_name, FALSE))
@@ -4580,8 +4607,7 @@ bool open_tables(THD *thd, TABLE_LIST **start, uint *counter, uint flags,
TABLE_LIST **table_to_open;
Sroutine_hash_entry **sroutine_to_open;
TABLE_LIST *tables;
- Open_table_context ot_ctx(thd, (flags & MYSQL_LOCK_IGNORE_TIMEOUT) ?
- LONG_TIMEOUT : thd->variables.lock_wait_timeout);
+ Open_table_context ot_ctx(thd, flags);
bool error= FALSE;
MEM_ROOT new_frm_mem;
bool has_prelocking_list;
@@ -4618,21 +4644,19 @@ restart:
(in non-LOCK TABLES mode) we might have to acquire upgradable
semi-exclusive metadata locks (SNW or SNRW) on some of the
tables to be opened.
- So we acquire all such locks at once here as doing this in one
+ When executing CREATE TABLE .. If NOT EXISTS .. SELECT, the
+ table may not yet exist, in which case we acquire an exclusive
+ lock.
+ We acquire all such locks at once here as doing this in one
by one fashion may lead to deadlocks or starvation. Later when
we will be opening corresponding table pre-acquired metadata
lock will be reused (thanks to the fact that in recursive case
metadata locks are acquired without waiting).
*/
- if (flags & MYSQL_OPEN_TAKE_UPGRADABLE_MDL)
+ if (! (flags & (MYSQL_OPEN_HAS_MDL_LOCK |
+ MYSQL_OPEN_FORCE_SHARED_MDL |
+ MYSQL_OPEN_FORCE_SHARED_HIGH_PRIO_MDL)))
{
- /*
- open_tables_acquire_upgradable_mdl() does not currenly handle
- these two flags. At this point, that does not matter as they
- are not used together with MYSQL_OPEN_TAKE_UPGRADABLE_MDL.
- */
- DBUG_ASSERT(!(flags & (MYSQL_OPEN_SKIP_TEMPORARY |
- MYSQL_OPEN_TEMPORARY_ONLY)));
if (thd->locked_tables_mode)
{
/*
@@ -4640,7 +4664,8 @@ restart:
need to check if appropriate locks were pre-acquired.
*/
if (open_tables_check_upgradable_mdl(thd, *start,
- thd->lex->first_not_own_table()))
+ thd->lex->first_not_own_table(),
+ flags))
{
error= TRUE;
goto err;
@@ -4648,7 +4673,7 @@ restart:
}
else if (open_tables_acquire_upgradable_mdl(thd, *start,
thd->lex->first_not_own_table(),
- &ot_ctx))
+ &ot_ctx, flags))
{
error= TRUE;
goto err;
@@ -4694,7 +4719,6 @@ restart:
have failed to open since closing tables can trigger removal of
elements from the table list (if MERGE tables are involved),
*/
- TABLE_LIST *failed_table= *table_to_open;
close_tables_for_reopen(thd, start, ot_ctx.start_of_statement_svp());
/*
@@ -4702,8 +4726,7 @@ restart:
TABLE_LIST element. Altough currently this assumption is valid
it may change in future.
*/
- if (ot_ctx.recover_from_failed_open(thd, &failed_table->mdl_request,
- failed_table))
+ if (ot_ctx.recover_from_failed_open(thd))
goto err;
error= FALSE;
@@ -4747,7 +4770,7 @@ restart:
{
close_tables_for_reopen(thd, start,
ot_ctx.start_of_statement_svp());
- if (ot_ctx.recover_from_failed_open(thd, &rt->mdl_request, NULL))
+ if (ot_ctx.recover_from_failed_open(thd))
goto err;
error= FALSE;
@@ -5054,8 +5077,8 @@ static bool check_lock_and_start_stmt(THD *thd,
else
lock_type= table_list->lock_type;
- if ((int) lock_type >= (int) TL_WRITE_ALLOW_READ &&
- (int) table_list->table->reginfo.lock_type < (int) TL_WRITE_ALLOW_READ)
+ if ((int) lock_type > (int) TL_WRITE_ALLOW_WRITE &&
+ (int) table_list->table->reginfo.lock_type <= (int) TL_WRITE_ALLOW_WRITE)
{
my_error(ER_TABLE_NOT_LOCKED_FOR_WRITE, MYF(0), table_list->alias);
DBUG_RETURN(1);
@@ -5156,8 +5179,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type,
uint lock_flags)
{
TABLE *table;
- Open_table_context ot_ctx(thd, (lock_flags & MYSQL_LOCK_IGNORE_TIMEOUT) ?
- LONG_TIMEOUT : thd->variables.lock_wait_timeout);
+ Open_table_context ot_ctx(thd, lock_flags);
bool error;
DBUG_ENTER("open_ltable");
@@ -5169,7 +5191,10 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type,
/* open_ltable can be used only for BASIC TABLEs */
table_list->required_type= FRMTYPE_TABLE;
- while ((error= open_table(thd, table_list, thd->mem_root, &ot_ctx, lock_flags)) &&
+ /* This function can't properly handle requests for such metadata locks. */
+ DBUG_ASSERT(table_list->mdl_request.type < MDL_SHARED_NO_WRITE);
+
+ while ((error= open_table(thd, table_list, thd->mem_root, &ot_ctx)) &&
ot_ctx.can_recover_from_failed_open())
{
/*
@@ -5179,8 +5204,7 @@ TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type lock_type,
*/
thd->mdl_context.rollback_to_savepoint(ot_ctx.start_of_statement_svp());
table_list->mdl_request.ticket= 0;
- if (ot_ctx.recover_from_failed_open(thd, &table_list->mdl_request,
- table_list))
+ if (ot_ctx.recover_from_failed_open(thd))
break;
}
diff --git a/sql/sql_base.h b/sql/sql_base.h
index 0fe70e4bc9d..2abddb540b8 100644
--- a/sql/sql_base.h
+++ b/sql/sql_base.h
@@ -89,7 +89,7 @@ TABLE_SHARE *get_cached_table_share(const char *db, const char *table_name);
TABLE *open_ltable(THD *thd, TABLE_LIST *table_list, thr_lock_type update,
uint lock_flags);
bool open_table(THD *thd, TABLE_LIST *table_list, MEM_ROOT *mem_root,
- Open_table_context *ot_ctx, uint flags);
+ Open_table_context *ot_ctx);
bool name_lock_locked_table(THD *thd, TABLE_LIST *tables);
bool reopen_name_locked_table(THD* thd, TABLE_LIST* table_list, bool link_in);
TABLE *table_cache_insert_placeholder(THD *thd, const char *key,
@@ -322,6 +322,7 @@ inline TABLE_LIST *find_table_in_local_list(TABLE_LIST *table,
db_name, table_name);
}
+
inline bool setup_fields_with_no_wrap(THD *thd, Item **ref_pointer_array,
List<Item> &item,
enum_mark_columns mark_used_columns,
@@ -336,6 +337,89 @@ inline bool setup_fields_with_no_wrap(THD *thd, Item **ref_pointer_array,
return res;
}
+/**
+ 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;
+};
+
+
inline bool
open_tables(THD *thd, TABLE_LIST **tables, uint *counter, uint flags)
{
@@ -355,4 +439,87 @@ inline bool open_and_lock_tables(THD *thd, TABLE_LIST *tables,
&prelocking_strategy);
}
+
+/**
+ A context of open_tables() function, used to recover
+ from a failed open_table() or open_routine() attempt.
+*/
+
+class Open_table_context
+{
+public:
+ enum enum_open_table_action
+ {
+ OT_NO_ACTION= 0,
+ OT_WAIT_MDL_LOCK,
+ OT_WAIT_TDC,
+ OT_DISCOVER,
+ OT_REPAIR
+ };
+ Open_table_context(THD *thd, uint flags);
+
+ bool recover_from_failed_open(THD *thd);
+ bool request_backoff_action(enum_open_table_action action_arg,
+ MDL_request *mdl_request, TABLE_LIST *table);
+
+ void add_request(MDL_request *request)
+ { m_mdl_requests.push_front(request); }
+
+ bool can_recover_from_failed_open() const
+ { return m_action != OT_NO_ACTION; }
+
+ /**
+ When doing a back-off, we close all tables acquired by this
+ statement. Return an MDL savepoint taken at the beginning of
+ the statement, so that we can rollback to it before waiting on
+ locks.
+ */
+ MDL_ticket *start_of_statement_svp() const
+ {
+ return m_start_of_statement_svp;
+ }
+
+ MDL_request *get_global_mdl_request(THD *thd);
+
+ inline ulong get_timeout() const
+ {
+ return m_timeout;
+ }
+
+ uint get_flags() const { return m_flags; }
+private:
+ /** List of requests for all locks taken so far. Used for waiting on locks. */
+ MDL_request_list m_mdl_requests;
+ /** For OT_WAIT_MDL_LOCK action, the request for which we should wait. */
+ MDL_request *m_failed_mdl_request;
+ /**
+ For OT_DISCOVER and OT_REPAIR actions, the table list element for
+ the table which definition should be re-discovered or which
+ should be repaired.
+ */
+ TABLE_LIST *m_failed_table;
+ MDL_ticket *m_start_of_statement_svp;
+ /**
+ Request object for global intention exclusive lock which is acquired during
+ opening tables for statements which take upgradable shared metadata locks.
+ */
+ MDL_request *m_global_mdl_request;
+ /**
+ Lock timeout in seconds. Initialized to LONG_TIMEOUT when opening system
+ tables or to the "lock_wait_timeout" system variable for regular tables.
+ */
+ ulong m_timeout;
+ /* open_table() flags. */
+ uint m_flags;
+ /** Back off action. */
+ enum enum_open_table_action m_action;
+ /**
+ Whether we had any locks when this context was created.
+ If we did, they are from the previous statement of a transaction,
+ and we can't safely do back-off (and release them).
+ */
+ bool m_has_locks;
+};
+
+
#endif /* SQL_BASE_INCLUDED */
diff --git a/sql/sql_bitmap.h b/sql/sql_bitmap.h
index 80a4712dd69..8d00c984d14 100644
--- a/sql/sql_bitmap.h
+++ b/sql/sql_bitmap.h
@@ -22,6 +22,7 @@
#ifndef SQL_BITMAP_INCLUDED
#define SQL_BITMAP_INCLUDED
+#include <my_sys.h>
#include <my_bitmap.h>
template <uint default_width> class Bitmap
diff --git a/sql/sql_class.h b/sql/sql_class.h
index f1fce5ef472..8a25112b577 100644
--- a/sql/sql_class.h
+++ b/sql/sql_class.h
@@ -1229,162 +1229,6 @@ 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() or open_routine() attempt.
-
- Implemented in sql_base.cc.
-*/
-
-class Open_table_context
-{
-public:
- enum enum_open_table_action
- {
- OT_NO_ACTION= 0,
- OT_WAIT_MDL_LOCK,
- OT_WAIT_TDC,
- OT_DISCOVER,
- OT_REPAIR
- };
- Open_table_context(THD *thd, ulong timeout);
-
- bool recover_from_failed_open(THD *thd, MDL_request *mdl_request,
- TABLE_LIST *table);
- bool request_backoff_action(enum_open_table_action action_arg);
-
- void add_request(MDL_request *request)
- { m_mdl_requests.push_front(request); }
-
- bool can_recover_from_failed_open() const
- { return m_action != OT_NO_ACTION; }
-
- /**
- When doing a back-off, we close all tables acquired by this
- statement. Return an MDL savepoint taken at the beginning of
- the statement, so that we can rollback to it before waiting on
- locks.
- */
- MDL_ticket *start_of_statement_svp() const
- {
- return m_start_of_statement_svp;
- }
-
- MDL_request *get_global_mdl_request(THD *thd);
-
- inline ulong get_timeout() const
- {
- return m_timeout;
- }
-
-private:
- /** List of requests for all locks taken so far. Used for waiting on locks. */
- MDL_request_list m_mdl_requests;
- /** Back off action. */
- enum enum_open_table_action m_action;
- MDL_ticket *m_start_of_statement_svp;
- /**
- Whether we had any locks when this context was created.
- If we did, they are from the previous statement of a transaction,
- and we can't safely do back-off (and release them).
- */
- bool m_has_locks;
- /**
- Request object for global intention exclusive lock which is acquired during
- opening tables for statements which take upgradable shared metadata locks.
- */
- MDL_request *m_global_mdl_request;
- /**
- Lock timeout in seconds. Initialized to LONG_TIMEOUT when opening system
- tables or to the "lock_wait_timeout" system variable for regular tables.
- */
- uint m_timeout;
-};
-
-
-/**
Tables that were locked with LOCK TABLES statement.
Encapsulates a list of TABLE_LIST instances for tables
diff --git a/sql/sql_db.cc b/sql/sql_db.cc
index 15fdd842e34..2e48475f298 100644
--- a/sql/sql_db.cc
+++ b/sql/sql_db.cc
@@ -1203,6 +1203,8 @@ static long mysql_rm_known_files(THD *thd, MY_DIR *dirp, const char *db,
table_list->alias= table_list->table_name; // If lower_case_table_names=2
table_list->internal_tmp_table= is_prefix(file->name, tmp_file_prefix);
+ table_list->mdl_request.init(MDL_key::TABLE, table_list->db,
+ table_list->table_name, MDL_EXCLUSIVE);
/* Link into list */
(*tot_list_next)= table_list;
tot_list_next= &table_list->next_local;
@@ -1918,9 +1920,11 @@ bool mysql_upgrade_db(THD *thd, LEX_STRING *old_db)
Table_ident *new_ident= new Table_ident(thd, new_db, table_str, 0);
if (!old_ident || !new_ident ||
!sl->add_table_to_list(thd, old_ident, NULL,
- TL_OPTION_UPDATING, TL_IGNORE) ||
+ TL_OPTION_UPDATING, TL_IGNORE,
+ MDL_EXCLUSIVE) ||
!sl->add_table_to_list(thd, new_ident, NULL,
- TL_OPTION_UPDATING, TL_IGNORE))
+ TL_OPTION_UPDATING, TL_IGNORE,
+ MDL_EXCLUSIVE))
{
error= 1;
my_dirend(dirp);
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc
index 2e86315d072..ece72e97d0d 100644
--- a/sql/sql_delete.cc
+++ b/sql/sql_delete.cc
@@ -14,7 +14,7 @@
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
/*
- Delete of records and truncate of tables.
+ Delete of records tables.
Multi-table deletes were introduced by Monty and Sinisa
*/
@@ -47,8 +47,7 @@
*/
bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
- SQL_LIST *order, ha_rows limit, ulonglong options,
- bool reset_auto_increment)
+ SQL_LIST *order, ha_rows limit, ulonglong options)
{
bool will_batch;
int error, loc_error;
@@ -59,17 +58,11 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
bool transactional_table, safe_update, const_cond;
bool const_cond_result;
ha_rows deleted= 0;
- bool triggers_applicable;
uint usable_index= MAX_KEY;
SELECT_LEX *select_lex= &thd->lex->select_lex;
THD::killed_state killed_status= THD::NOT_KILLED;
+ THD::enum_binlog_query_type query_type= THD::ROW_QUERY_TYPE;
DBUG_ENTER("mysql_delete");
- bool save_binlog_row_based;
-
- THD::enum_binlog_query_type query_type=
- thd->lex->sql_command == SQLCOM_TRUNCATE ?
- THD::STMT_QUERY_TYPE :
- THD::ROW_QUERY_TYPE;
if (open_and_lock_tables(thd, table_list, TRUE, 0))
DBUG_RETURN(TRUE);
@@ -129,25 +122,20 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
any side-effects (because of triggers), so we can use optimized
handler::delete_all_rows() method.
- We implement fast TRUNCATE for InnoDB even if triggers are
- present. TRUNCATE ignores triggers.
-
We can use delete_all_rows() if and only if:
- We allow new functions (not using option --skip-new), and are
not in safe mode (not using option --safe-mode)
- There is no limit clause
- The condition is constant
- If there is a condition, then it it produces a non-zero value
- - If the current command is DELETE FROM with no where clause
- (i.e., not TRUNCATE) then:
- - We should not be binlogging this statement row-based, and
+ - If the current command is DELETE FROM with no where clause, then:
+ - We should not be binlogging this statement in row-based, and
- there should be no delete triggers associated with the table.
*/
if (!using_limit && const_cond_result &&
!(specialflag & (SPECIAL_NO_NEW_FUNC | SPECIAL_SAFE_MODE)) &&
- (thd->lex->sql_command == SQLCOM_TRUNCATE ||
(!thd->is_current_stmt_binlog_format_row() &&
- !(table->triggers && table->triggers->has_delete_triggers()))))
+ !(table->triggers && table->triggers->has_delete_triggers())))
{
/* Update the table->file->stats.records number */
table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
@@ -160,16 +148,14 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
query in row format, so we have to log it in statement format.
*/
query_type= THD::STMT_QUERY_TYPE;
- error= -1; // ok
+ error= -1;
deleted= maybe_deleted;
- save_binlog_row_based= thd->is_current_stmt_binlog_format_row();
goto cleanup;
}
if (error != HA_ERR_WRONG_COMMAND)
{
table->file->print_error(error,MYF(0));
error=0;
- save_binlog_row_based= thd->is_current_stmt_binlog_format_row();
goto cleanup;
}
/* Handler didn't support fast delete; Delete rows one by one */
@@ -212,11 +198,6 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
if (thd->is_error())
DBUG_RETURN(TRUE);
my_ok(thd, 0);
- /*
- We don't need to call reset_auto_increment in this case, because
- mysql_truncate always gives a NULL conds argument, hence we never
- get here.
- */
DBUG_RETURN(0); // Nothing to delete
}
@@ -287,12 +268,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
init_ftfuncs(thd, select_lex, 1);
thd_proc_info(thd, "updating");
- /* NOTE: TRUNCATE must not invoke triggers. */
-
- triggers_applicable= table->triggers &&
- thd->lex->sql_command != SQLCOM_TRUNCATE;
-
- if (triggers_applicable &&
+ if (table->triggers &&
table->triggers->has_triggers(TRG_EVENT_DELETE,
TRG_ACTION_AFTER))
{
@@ -310,11 +286,6 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
table->mark_columns_needed_for_delete();
- save_binlog_row_based= thd->is_current_stmt_binlog_format_row();
- if (thd->lex->sql_command == SQLCOM_TRUNCATE &&
- thd->is_current_stmt_binlog_format_row())
- thd->clear_current_stmt_binlog_format_row();
-
while (!(error=info.read_record(&info)) && !thd->killed &&
! thd->is_error())
{
@@ -323,7 +294,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
if (!(select && select->skip_record())&& ! thd->is_error() )
{
- if (triggers_applicable &&
+ if (table->triggers &&
table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
TRG_ACTION_BEFORE, FALSE))
{
@@ -334,7 +305,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
if (!(error= table->file->ha_delete_row(table->record[0])))
{
deleted++;
- if (triggers_applicable &&
+ if (table->triggers &&
table->triggers->process_triggers(thd, TRG_EVENT_DELETE,
TRG_ACTION_AFTER, FALSE))
{
@@ -379,21 +350,6 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
if (options & OPTION_QUICK)
(void) table->file->extra(HA_EXTRA_NORMAL);
- if (reset_auto_increment && (error < 0))
- {
- /*
- We're really doing a truncate and need to reset the table's
- auto-increment counter.
- */
- int error2= table->file->ha_reset_auto_increment(0);
-
- if (error2 && (error2 != HA_ERR_WRONG_COMMAND))
- {
- table->file->print_error(error2, MYF(0));
- error= 1;
- }
- }
-
cleanup:
/*
Invalidate the table in the query cache if something changed. This must
@@ -414,34 +370,24 @@ cleanup:
/* See similar binlogging code in sql_update.cc, for comments */
if ((error < 0) || thd->transaction.stmt.modified_non_trans_table)
{
- if (mysql_bin_log.is_open() &&
- !(thd->lex->sql_command == SQLCOM_TRUNCATE &&
- thd->is_current_stmt_binlog_format_row() &&
- find_temporary_table(thd, table_list)))
+ if (mysql_bin_log.is_open())
{
- bool const is_trans=
- thd->lex->sql_command == SQLCOM_TRUNCATE ?
- FALSE :
- transactional_table;
-
int errcode= 0;
if (error < 0)
thd->clear_error();
else
errcode= query_error_code(thd, killed_status == THD::NOT_KILLED);
-
+
/*
[binlog]: If 'handler::delete_all_rows()' was called and the
storage engine does not inject the rows itself, we replicate
statement-based; otherwise, 'ha_delete_row()' was used to
delete specific rows which we might log row-based.
-
- Note that TRUNCATE TABLE is not transactional and should
- therefore be treated as a DDL.
*/
int log_result= thd->binlog_query(query_type,
thd->query(), thd->query_length(),
- is_trans, FALSE, FALSE, errcode);
+ transactional_table, FALSE, FALSE,
+ errcode);
if (log_result)
{
@@ -449,18 +395,12 @@ cleanup:
}
}
}
- if (save_binlog_row_based)
- thd->set_current_stmt_binlog_format_row();
DBUG_ASSERT(transactional_table || !deleted || thd->transaction.stmt.modified_non_trans_table);
free_underlaid_joins(thd, select_lex);
if (error < 0 ||
(thd->lex->ignore && !thd->is_error() && !thd->is_fatal_error))
{
- /*
- If a TRUNCATE TABLE was issued, the number of rows should be reported as
- zero since the exact number is unknown.
- */
- my_ok(thd, reset_auto_increment ? 0 : deleted);
+ my_ok(thd, deleted);
DBUG_PRINT("info",("%ld records deleted",(long) deleted));
}
DBUG_RETURN(error >= 0 || thd->is_error());
@@ -1062,227 +1002,3 @@ bool multi_delete::send_eof()
return 0;
}
-
-/***************************************************************************
- TRUNCATE TABLE
-****************************************************************************/
-
-/*
- Row-by-row truncation if the engine does not support table recreation.
- Probably a InnoDB table.
-*/
-
-static bool mysql_truncate_by_delete(THD *thd, TABLE_LIST *table_list)
-{
- bool error;
- DBUG_ENTER("mysql_truncate_by_delete");
- table_list->lock_type= TL_WRITE;
- table_list->mdl_request.set_type(MDL_SHARED_WRITE);
- mysql_init_select(thd->lex);
- /* Delete all rows from table */
- error= mysql_delete(thd, table_list, NULL, NULL, HA_POS_ERROR, LL(0), TRUE);
- /*
- All effects of a TRUNCATE TABLE operation are rolled back if a row by row
- deletion fails. Otherwise, operation is automatically committed at the end.
- */
- if (error)
- {
- DBUG_ASSERT(thd->stmt_da->is_error());
- trans_rollback_stmt(thd);
- trans_rollback(thd);
- }
- DBUG_RETURN(error);
-}
-
-
-/*
- Optimize delete of all rows by doing a full generate of the table
- This will work even if the .ISM and .ISD tables are destroyed
-
- dont_send_ok should be set if:
- - We should always wants to generate the table (even if the table type
- normally can't safely do this.
- - We don't want an ok to be sent to the end user.
- - We don't want to log the truncate command
- - If we want to keep exclusive metadata lock on the table (obtained by
- caller) on exit without errors.
-*/
-
-bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok)
-{
- HA_CREATE_INFO create_info;
- char path[FN_REFLEN + 1];
- TABLE *table;
- bool error= TRUE;
- uint path_length;
- /*
- Is set if we're under LOCK TABLES, and used
- to downgrade the exclusive lock after the
- table was truncated.
- */
- MDL_ticket *mdl_ticket= NULL;
- bool has_mdl_lock= FALSE;
- bool is_temporary_table= false;
- DBUG_ENTER("mysql_truncate");
-
- bzero((char*) &create_info,sizeof(create_info));
-
- /* Remove tables from the HANDLER's hash. */
- mysql_ha_rm_tables(thd, table_list);
-
- /* If it is a temporary table, close and regenerate it */
- if (!dont_send_ok && (table= find_temporary_table(thd, table_list)))
- {
- is_temporary_table= true;
- handlerton *table_type= table->s->db_type();
- TABLE_SHARE *share= table->s;
- /* Note that a temporary table cannot be partitioned */
- if (!ha_check_storage_engine_flag(table_type, HTON_CAN_RECREATE))
- goto trunc_by_del;
-
- table->file->info(HA_STATUS_AUTO | HA_STATUS_NO_LOCK);
-
- close_temporary_table(thd, table, 0, 0); // Don't free share
- ha_create_table(thd, share->normalized_path.str,
- share->db.str, share->table_name.str, &create_info, 1);
- // We don't need to call invalidate() because this table is not in cache
- if ((error= (int) !(open_temporary_table(thd, share->path.str,
- share->db.str,
- share->table_name.str, 1))))
- (void) rm_temporary_table(table_type, path);
- else
- thd->thread_specific_used= TRUE;
-
- free_table_share(share);
- my_free((char*) table,MYF(0));
- /*
- If we return here we will not have logged the truncation to the bin log
- and we will not my_ok() to the client.
- */
- goto end;
- }
-
- path_length= build_table_filename(path, sizeof(path) - 1, table_list->db,
- table_list->table_name, reg_ext, 0);
-
- if (!dont_send_ok)
- {
- enum legacy_db_type table_type;
- /*
- FIXME: Code of TRUNCATE breaks the meta-data
- locking protocol since it tries to find out the table storage
- engine and therefore accesses table in some way without holding
- any kind of meta-data lock.
- */
- mysql_frm_type(thd, path, &table_type);
- if (table_type == DB_TYPE_UNKNOWN)
- {
- my_error(ER_NO_SUCH_TABLE, MYF(0),
- table_list->db, table_list->table_name);
- DBUG_RETURN(TRUE);
- }
-#ifdef WITH_PARTITION_STORAGE_ENGINE
- /*
- TODO: Add support for TRUNCATE PARTITION for NDB and other engines
- supporting native partitioning
- */
- if (table_type != DB_TYPE_PARTITION_DB &&
- thd->lex->alter_info.flags & ALTER_ADMIN_PARTITION)
- {
- my_error(ER_PARTITION_MGMT_ON_NONPARTITIONED, MYF(0));
- DBUG_RETURN(TRUE);
- }
-#endif
- if (!ha_check_storage_engine_flag(ha_resolve_by_legacy_type(thd,
- table_type),
- HTON_CAN_RECREATE) ||
- thd->lex->alter_info.flags & ALTER_ADMIN_PARTITION)
- goto trunc_by_del;
-
-
- if (thd->locked_tables_mode)
- {
- if (!(table= find_table_for_mdl_upgrade(thd->open_tables, table_list->db,
- table_list->table_name, FALSE)))
- DBUG_RETURN(TRUE);
- mdl_ticket= table->mdl_ticket;
- if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
- goto end;
- close_all_tables_for_name(thd, table->s, FALSE);
- }
- else
- {
- MDL_request mdl_global_request, mdl_request;
- MDL_request_list mdl_requests;
- /*
- Even though we could use the previous execution branch
- here just as well, we must not try to open the table:
- MySQL manual documents that TRUNCATE can be used to
- repair a damaged table, i.e. a table that can not be
- fully "opened". In particular MySQL manual says:
-
- As long as the table format file tbl_name.frm is valid,
- the table can be re-created as an empty table with TRUNCATE
- TABLE, even if the data or index files have become corrupted.
- */
-
- mdl_global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE);
- mdl_request.init(MDL_key::TABLE, table_list->db, table_list->table_name,
- MDL_EXCLUSIVE);
- mdl_requests.push_front(&mdl_request);
- mdl_requests.push_front(&mdl_global_request);
-
- if (thd->mdl_context.acquire_locks(&mdl_requests,
- thd->variables.lock_wait_timeout))
- DBUG_RETURN(TRUE);
-
- has_mdl_lock= TRUE;
- mysql_mutex_lock(&LOCK_open);
- tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table_list->db,
- table_list->table_name);
- mysql_mutex_unlock(&LOCK_open);
- }
- }
-
- /*
- Remove the .frm extension AIX 5.2 64-bit compiler bug (BUG#16155): this
- crashes, replacement works. *(path + path_length - reg_ext_length)=
- '\0';
- */
- path[path_length - reg_ext_length] = 0;
- mysql_mutex_lock(&LOCK_open);
- error= ha_create_table(thd, path, table_list->db, table_list->table_name,
- &create_info, 1);
- mysql_mutex_unlock(&LOCK_open);
- query_cache_invalidate3(thd, table_list, 0);
-
-end:
- if (!dont_send_ok)
- {
- if (thd->locked_tables_mode && thd->locked_tables_list.reopen_tables(thd))
- thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0);
- /*
- Even if we failed to reopen some tables,
- the operation itself succeeded, write the binlog.
- */
- if (!error)
- {
- /* In RBR, the statement is not binlogged if the table is temporary. */
- if (!is_temporary_table || !thd->is_current_stmt_binlog_format_row())
- error= write_bin_log(thd, TRUE, thd->query(), thd->query_length());
- if (!error)
- my_ok(thd); // This should return record count
- }
- if (has_mdl_lock)
- thd->mdl_context.release_transactional_locks();
- if (mdl_ticket)
- mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE);
- }
-
- DBUG_PRINT("exit", ("error: %d", error));
- DBUG_RETURN(error);
-
-trunc_by_del:
- error= mysql_truncate_by_delete(thd, table_list);
- DBUG_RETURN(error);
-}
diff --git a/sql/sql_delete.h b/sql/sql_delete.h
index d1c1b363abd..c718323ce1e 100644
--- a/sql/sql_delete.h
+++ b/sql/sql_delete.h
@@ -27,8 +27,6 @@ typedef struct st_sql_list SQL_LIST;
int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, Item **conds);
bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds,
- SQL_LIST *order, ha_rows rows, ulonglong options,
- bool reset_auto_increment);
-bool mysql_truncate(THD *thd, TABLE_LIST *table_list, bool dont_send_ok);
+ SQL_LIST *order, ha_rows rows, ulonglong options);
#endif /* SQL_DELETE_INCLUDED */
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index d40f0dcb410..998d3c53e04 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -3608,13 +3608,12 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
{
- Open_table_context ot_ctx_unused(thd, LONG_TIMEOUT);
+ Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN);
/*
Here we open the destination table, on which we already have
an exclusive metadata lock.
*/
- if (open_table(thd, create_table, thd->mem_root,
- &ot_ctx_unused, MYSQL_OPEN_REOPEN))
+ if (open_table(thd, create_table, thd->mem_root, &ot_ctx))
{
mysql_mutex_lock(&LOCK_open);
quick_rm_table(create_info->db_type, create_table->db,
@@ -3627,9 +3626,8 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
}
else
{
- Open_table_context ot_ctx_unused(thd, LONG_TIMEOUT);
- if (open_table(thd, create_table, thd->mem_root, &ot_ctx_unused,
- MYSQL_OPEN_TEMPORARY_ONLY))
+ Open_table_context ot_ctx(thd, MYSQL_OPEN_TEMPORARY_ONLY);
+ if (open_table(thd, create_table, thd->mem_root, &ot_ctx))
{
/*
This shouldn't happen as creation of temporary table should make
diff --git a/sql/sql_lex.cc b/sql/sql_lex.cc
index 88cba22d115..e87204ffd40 100644
--- a/sql/sql_lex.cc
+++ b/sql/sql_lex.cc
@@ -1978,6 +1978,7 @@ TABLE_LIST *st_select_lex_node::add_table_to_list (THD *thd, Table_ident *table,
LEX_STRING *alias,
ulong table_join_options,
thr_lock_type flags,
+ enum_mdl_type mdl_type,
List<Index_hint> *hints,
LEX_STRING *option)
{
diff --git a/sql/sql_lex.h b/sql/sql_lex.h
index 40bd3875793..2a1b1371975 100644
--- a/sql/sql_lex.h
+++ b/sql/sql_lex.h
@@ -502,6 +502,7 @@ public:
LEX_STRING *alias,
ulong table_options,
thr_lock_type flags= TL_UNLOCK,
+ enum_mdl_type mdl_type= MDL_SHARED_READ,
List<Index_hint> *hints= 0,
LEX_STRING *option= 0);
virtual void set_lock_for_tables(thr_lock_type lock_type) {}
@@ -799,6 +800,7 @@ public:
LEX_STRING *alias,
ulong table_options,
thr_lock_type flags= TL_UNLOCK,
+ enum_mdl_type mdl_type= MDL_SHARED_READ,
List<Index_hint> *hints= 0,
LEX_STRING *option= 0);
TABLE_LIST* get_table_list();
@@ -2250,6 +2252,7 @@ public:
yacc_yyvs= NULL;
m_set_signal_info.clear();
m_lock_type= TL_READ_DEFAULT;
+ m_mdl_type= MDL_SHARED_READ;
}
~Yacc_state();
@@ -2261,6 +2264,7 @@ public:
void reset_before_substatement()
{
m_lock_type= TL_READ_DEFAULT;
+ m_mdl_type= MDL_SHARED_READ;
}
/**
@@ -2300,6 +2304,12 @@ public:
*/
thr_lock_type m_lock_type;
+ /**
+ The type of requested metadata lock for tables added to
+ the statement table list.
+ */
+ enum_mdl_type m_mdl_type;
+
/*
TODO: move more attributes from the LEX structure here.
*/
diff --git a/sql/sql_parse.cc b/sql/sql_parse.cc
index 06bebe76842..3c4c2eb1050 100644
--- a/sql/sql_parse.cc
+++ b/sql/sql_parse.cc
@@ -49,6 +49,7 @@
// mysql_recreate_table,
// mysql_backup_table,
// mysql_restore_table
+#include "sql_truncate.h" // mysql_truncate_table
#include "sql_connect.h" // check_user,
// decrease_user_connections,
// thd_init_client_charset, check_mqh,
@@ -264,7 +265,7 @@ void init_update_queries(void)
sql_command_flags[SQLCOM_ALTER_TABLE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND |
CF_AUTO_COMMIT_TRANS | CF_PROTECT_AGAINST_GRL;
sql_command_flags[SQLCOM_TRUNCATE]= CF_CHANGES_DATA | CF_WRITE_LOGS_COMMAND |
- CF_AUTO_COMMIT_TRANS;
+ CF_AUTO_COMMIT_TRANS | CF_PROTECT_AGAINST_GRL;
sql_command_flags[SQLCOM_DROP_TABLE]= CF_CHANGES_DATA | CF_AUTO_COMMIT_TRANS;
sql_command_flags[SQLCOM_LOAD]= CF_CHANGES_DATA | CF_REEXECUTION_FRAGILE |
CF_PROTECT_AGAINST_GRL |
@@ -1650,7 +1651,8 @@ int prepare_schema_table(THD *thd, LEX *lex, Table_ident *table_ident,
/* 'parent_lex' is used in init_query() so it must be before it. */
schema_select_lex->parent_lex= lex;
schema_select_lex->init_query();
- if (!schema_select_lex->add_table_to_list(thd, table_ident, 0, 0, TL_READ))
+ if (!schema_select_lex->add_table_to_list(thd, table_ident, 0, 0, TL_READ,
+ MDL_SHARED_READ))
DBUG_RETURN(1);
lex->query_tables_last= query_tables_last;
break;
@@ -2588,7 +2590,7 @@ case SQLCOM_PREPARE:
/* Set strategies: reset default or 'prepared' values. */
create_table->open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
- create_table->lock_strategy= TABLE_LIST::EXCLUSIVE_DOWNGRADABLE_MDL;
+ create_table->lock_strategy= TABLE_LIST::OTLS_DOWNGRADE_IF_EXISTS;
/*
Close any open handlers for the table
@@ -3327,9 +3329,8 @@ end_with_restore_list:
ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
goto error;
}
- if (thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE))
- goto error;
- res= mysql_truncate(thd, first_table, 0);
+ if (! (res= mysql_truncate_table(thd, first_table)))
+ my_ok(thd);
break;
case SQLCOM_DELETE:
{
@@ -3342,8 +3343,7 @@ end_with_restore_list:
MYSQL_DELETE_START(thd->query());
res = mysql_delete(thd, all_tables, select_lex->where,
&select_lex->order_list,
- unit->select_limit_cnt, select_lex->options,
- FALSE);
+ unit->select_limit_cnt, select_lex->options);
MYSQL_DELETE_DONE(res, (ulong) thd->get_row_count_func());
break;
}
@@ -3550,16 +3550,13 @@ end_with_restore_list:
thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE))
goto error;
- init_mdl_requests(all_tables);
-
thd->variables.option_bits|= OPTION_TABLE_LOCK;
thd->in_lock_tables=1;
{
Lock_tables_prelocking_strategy lock_tables_prelocking_strategy;
- res= (open_and_lock_tables(thd, all_tables, FALSE,
- MYSQL_OPEN_TAKE_UPGRADABLE_MDL,
+ res= (open_and_lock_tables(thd, all_tables, FALSE, 0,
&lock_tables_prelocking_strategy) ||
thd->locked_tables_list.init_locked_tables(thd));
}
@@ -6062,6 +6059,7 @@ bool add_to_list(THD *thd, SQL_LIST &list,Item *item,bool asc)
- TL_OPTION_FORCE_INDEX : Force usage of index
- TL_OPTION_ALIAS : an alias in multi table DELETE
@param lock_type How table should be locked
+ @param mdl_type Type of metadata lock to acquire on the table.
@param use_index List of indexed used in USE INDEX
@param ignore_index List of indexed used in IGNORE INDEX
@@ -6076,6 +6074,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
LEX_STRING *alias,
ulong table_options,
thr_lock_type lock_type,
+ enum_mdl_type mdl_type,
List<Index_hint> *index_hints_arg,
LEX_STRING *option)
{
@@ -6223,9 +6222,7 @@ TABLE_LIST *st_select_lex::add_table_to_list(THD *thd,
ptr->next_name_resolution_table= NULL;
/* Link table in global list (all used tables) */
lex->add_to_query_tables(ptr);
- ptr->mdl_request.init(MDL_key::TABLE, ptr->db, ptr->table_name,
- (ptr->lock_type >= TL_WRITE_ALLOW_WRITE) ?
- MDL_SHARED_WRITE : MDL_SHARED_READ);
+ ptr->mdl_request.init(MDL_key::TABLE, ptr->db, ptr->table_name, mdl_type);
DBUG_RETURN(ptr);
}
diff --git a/sql/sql_parse.h b/sql/sql_parse.h
index 6d968033ccd..c9264b999b3 100644
--- a/sql/sql_parse.h
+++ b/sql/sql_parse.h
@@ -130,11 +130,6 @@ bool check_simple_select();
Item *negate_expression(THD *thd, Item *expr);
bool check_stack_overrun(THD *thd, long margin, uchar *dummy);
-bool begin_trans(THD *thd);
-bool end_active_trans(THD *thd);
-int end_trans(THD *thd, enum enum_mysql_completiontype completion);
-
-
/* Variables */
extern const char* any_db;
diff --git a/sql/sql_prepare.cc b/sql/sql_prepare.cc
index c2d3c595d95..e5d7514d9f5 100644
--- a/sql/sql_prepare.cc
+++ b/sql/sql_prepare.cc
@@ -1690,7 +1690,7 @@ static bool mysql_test_create_table(Prepared_statement *stmt)
for the prepare phase.
*/
create_table->open_strategy= TABLE_LIST::OPEN_IF_EXISTS;
- create_table->lock_strategy= TABLE_LIST::SHARED_MDL;
+ create_table->lock_strategy= TABLE_LIST::OTLS_NONE;
if (select_lex->item_list.elements)
{
diff --git a/sql/sql_rename.cc b/sql/sql_rename.cc
index ea95b59b0c2..130a99a374f 100644
--- a/sql/sql_rename.cc
+++ b/sql/sql_rename.cc
@@ -29,6 +29,7 @@
// start_waiting_global_read_lock
#include "sql_base.h" // tdc_remove_table
#include "sql_handler.h" // mysql_ha_rm_tables
+#include "datadict.h"
static TABLE_LIST *rename_tables(THD *thd, TABLE_LIST *table_list,
bool skip_error);
@@ -283,7 +284,7 @@ do_rename(THD *thd, TABLE_LIST *ren_table, char *new_db, char *new_table_name,
build_table_filename(name, sizeof(name) - 1,
ren_table->db, old_alias, reg_ext, 0);
- frm_type= mysql_frm_type(thd, name, &table_type);
+ frm_type= dd_frm_type(thd, name, &table_type);
switch (frm_type)
{
case FRMTYPE_TABLE:
diff --git a/sql/sql_show.cc b/sql/sql_show.cc
index 16a17744279..61b61106522 100644
--- a/sql/sql_show.cc
+++ b/sql/sql_show.cc
@@ -18,7 +18,6 @@
#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */
#include "sql_priv.h"
-#include "debug_sync.h"
#include "unireg.h"
#include "sql_acl.h" // fill_schema_*_privileges
#include "sql_select.h" // For select_describe
@@ -28,7 +27,6 @@
// primary_key_name,
// build_table_filename
#include "repl_failsafe.h"
-#include "sql_view.h" // mysql_frm_type
#include "sql_parse.h" // check_access, check_table_access
#include "sql_partition.h" // partition_element
#include "sql_db.h" // check_db_dir_existence, load_db_opt_by_name
@@ -50,8 +48,9 @@
#include "event_data_objects.h"
#endif
#include <my_dir.h>
-#include "debug_sync.h"
#include "lock.h" // MYSQL_OPEN_IGNORE_FLUSH
+#include "debug_sync.h"
+#include "datadict.h" // dd_frm_type()
#define STR_OR_NIL(S) ((S) ? (S) : "<nil>")
@@ -2366,7 +2365,7 @@ int make_table_list(THD *thd, SELECT_LEX *sel,
Table_ident *table_ident;
table_ident= new Table_ident(thd, *db_name, *table_name, 1);
sel->init_query();
- if (!sel->add_table_to_list(thd, table_ident, 0, 0, TL_READ))
+ if (!sel->add_table_to_list(thd, table_ident, 0, 0, TL_READ, MDL_SHARED_READ))
return 1;
return 0;
}
@@ -2967,6 +2966,9 @@ fill_schema_show_cols_or_idxs(THD *thd, TABLE_LIST *tables,
(can_deadlock ?
MYSQL_OPEN_FAIL_ON_MDL_CONFLICT : 0)));
lex->sql_command= save_sql_command;
+
+ DEBUG_SYNC(thd, "after_open_table_ignore_flush");
+
/*
get_all_tables() returns 1 on failure and 0 on success thus
return only these and not the result code of ::process_table()
@@ -3026,7 +3028,7 @@ static int fill_schema_table_names(THD *thd, TABLE *table,
char path[FN_REFLEN + 1];
(void) build_table_filename(path, sizeof(path) - 1, db_name->str,
table_name->str, reg_ext, 0);
- switch (mysql_frm_type(thd, path, &not_used)) {
+ switch (dd_frm_type(thd, path, &not_used)) {
case FRMTYPE_ERROR:
table->field[3]->store(STRING_WITH_LEN("ERROR"),
system_charset_info);
@@ -6569,7 +6571,7 @@ int make_schema_select(THD *thd, SELECT_LEX *sel,
strlen(schema_table->table_name), 0);
if (schema_table->old_format(thd, schema_table) || /* Handle old syntax */
!sel->add_table_to_list(thd, new Table_ident(thd, db, table, 0),
- 0, 0, TL_READ))
+ 0, 0, TL_READ, MDL_SHARED_READ))
{
DBUG_RETURN(1);
}
diff --git a/sql/sql_table.cc b/sql/sql_table.cc
index 5052e29ac30..6fbee4f856e 100644
--- a/sql/sql_table.cc
+++ b/sql/sql_table.cc
@@ -27,8 +27,8 @@
// start_waiting_global_read_lock,
// unlock_table_names, mysql_unlock_tables
#include "strfunc.h" // find_type2, find_set
-#include "sql_view.h" // mysql_frm_type, view_checksum, mysql_frm_type
-#include "sql_delete.h" // mysql_truncate
+#include "sql_view.h" // view_checksum
+#include "sql_truncate.h" // regenerate_locked_table
#include "sql_partition.h" // mem_alloc_error,
// generate_partition_syntax,
// partition_info
@@ -52,6 +52,7 @@
#include "sql_show.h"
#include "transaction.h"
#include "keycaches.h"
+#include "datadict.h" // dd_frm_type()
#ifdef __WIN__
#include <io.h>
@@ -2125,7 +2126,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
((access(path, F_OK) &&
ha_create_table_from_engine(thd, db, alias)) ||
(!drop_view &&
- mysql_frm_type(thd, path, &frm_db_type) != FRMTYPE_TABLE)))
+ dd_frm_type(thd, path, &frm_db_type) != FRMTYPE_TABLE)))
{
// Table was not found on disk and table can't be created from engine
if (if_exists)
@@ -2145,7 +2146,7 @@ int mysql_rm_table_part2(THD *thd, TABLE_LIST *tables, bool if_exists,
*/
if (frm_db_type == DB_TYPE_UNKNOWN)
{
- mysql_frm_type(thd, path, &frm_db_type);
+ dd_frm_type(thd, path, &frm_db_type);
DBUG_PRINT("info", ("frm_db_type %d from %s", frm_db_type, path));
}
table_type= ha_resolve_by_legacy_type(thd, frm_db_type);
@@ -4454,10 +4455,10 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
char from[FN_REFLEN],tmp[FN_REFLEN+32];
const char **ext;
MY_STAT stat_info;
- Open_table_context ot_ctx_unused(thd, LONG_TIMEOUT);
+ Open_table_context ot_ctx(thd, (MYSQL_OPEN_IGNORE_FLUSH |
+ MYSQL_OPEN_HAS_MDL_LOCK |
+ MYSQL_LOCK_IGNORE_TIMEOUT));
DBUG_ENTER("prepare_for_repair");
- uint reopen_for_repair_flags= (MYSQL_OPEN_IGNORE_FLUSH |
- MYSQL_OPEN_HAS_MDL_LOCK);
if (!(check_opt->sql_flags & TT_USEFRM))
DBUG_RETURN(0);
@@ -4586,12 +4587,18 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
"Failed renaming data file");
goto end;
}
- if (mysql_truncate(thd, table_list, 1))
+ if (dd_recreate_table(thd, table_list->db, table_list->table_name))
{
error= send_check_errmsg(thd, table_list, "repair",
"Failed generating table from .frm file");
goto end;
}
+ /*
+ 'FALSE' for 'using_transactions' means don't postpone
+ invalidation till the end of a transaction, but do it
+ immediately.
+ */
+ query_cache_invalidate3(thd, table_list, FALSE);
if (mysql_file_rename(key_file_misc, tmp, from, MYF(MY_WME)))
{
error= send_check_errmsg(thd, table_list, "repair",
@@ -4606,8 +4613,7 @@ static int prepare_for_repair(THD *thd, TABLE_LIST *table_list,
Now we should be able to open the partially repaired table
to finish the repair in the handler later on.
*/
- if (open_table(thd, table_list, thd->mem_root,
- &ot_ctx_unused, reopen_for_repair_flags))
+ if (open_table(thd, table_list, thd->mem_root, &ot_ctx))
{
error= send_check_errmsg(thd, table_list, "repair",
"Failed to open partially repaired table");
@@ -4685,6 +4691,14 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
strxmov(table_name, db, ".", table->table_name, NullS);
thd->open_options|= extra_open_options;
table->lock_type= lock_type;
+ /*
+ To make code safe for re-execution we need to reset type of MDL
+ request as code below may change it.
+ To allow concurrent execution of read-only operations we acquire
+ weak metadata lock for them.
+ */
+ table->mdl_request.set_type((lock_type >= TL_WRITE_ALLOW_WRITE) ?
+ MDL_SHARED_NO_READ_WRITE : MDL_SHARED_READ);
/* open only one table from local list of command */
{
TABLE_LIST *save_next_global, *save_next_local;
@@ -4706,8 +4720,7 @@ static bool mysql_admin_table(THD* thd, TABLE_LIST* tables,
if (view_operator_func == NULL)
table->required_type=FRMTYPE_TABLE;
- open_error= open_and_lock_tables(thd, table, TRUE,
- MYSQL_OPEN_TAKE_UPGRADABLE_MDL);
+ open_error= open_and_lock_tables(thd, table, TRUE, 0);
thd->no_warnings_for_error= 0;
table->next_global= save_next_global;
table->next_local= save_next_local;
@@ -5053,6 +5066,7 @@ send_result_message:
/* Clear the ticket released in close_thread_tables(). */
table->mdl_request.ticket= NULL;
DEBUG_SYNC(thd, "ha_admin_open_ltable");
+ table->mdl_request.set_type(MDL_SHARED_WRITE);
if ((table->table= open_ltable(thd, table, lock_type, 0)))
{
result_code= table->table->file->ha_analyze(thd, check_opt);
@@ -5388,7 +5402,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
char buf[2048];
String query(buf, sizeof(buf), system_charset_info);
query.length(0); // Have to zero it since constructor doesn't
- Open_table_context ot_ctx_unused(thd, LONG_TIMEOUT);
+ Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN);
/*
The condition avoids a crash as described in BUG#48506. Other
@@ -5403,8 +5417,7 @@ bool mysql_create_like_table(THD* thd, TABLE_LIST* table, TABLE_LIST* src_table,
to work. The table will be closed by close_thread_table() at
the end of this branch.
*/
- if (open_table(thd, table, thd->mem_root, &ot_ctx_unused,
- MYSQL_OPEN_REOPEN))
+ if (open_table(thd, table, thd->mem_root, &ot_ctx))
goto err;
int result __attribute__((unused))=
@@ -5490,6 +5503,7 @@ mysql_discard_or_import_tablespace(THD *thd,
not complain when we lock the table
*/
thd->tablespace_op= TRUE;
+ table_list->mdl_request.set_type(MDL_SHARED_WRITE);
if (!(table=open_ltable(thd, table_list, TL_WRITE, 0)))
{
thd->tablespace_op=FALSE;
@@ -6467,8 +6481,6 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
char reg_path[FN_REFLEN+1];
ha_rows copied,deleted;
handlerton *old_db_type, *new_db_type, *save_old_db_type;
- legacy_db_type table_type;
- frm_type_enum frm_type;
enum_alter_table_change_level need_copy_table= ALTER_TABLE_METADATA_ONLY;
#ifdef WITH_PARTITION_STORAGE_ENGINE
uint fast_alter_partition= 0;
@@ -6548,85 +6560,6 @@ bool mysql_alter_table(THD *thd,char *new_db, char *new_name,
/* Conditionally writes to binlog. */
DBUG_RETURN(mysql_discard_or_import_tablespace(thd,table_list,
alter_info->tablespace_op));
- strxnmov(new_name_buff, sizeof (new_name_buff) - 1, mysql_data_home, "/", db,
- "/", table_name, reg_ext, NullS);
- (void) unpack_filename(new_name_buff, new_name_buff);
- /*
- If this is just a rename of a view, short cut to the
- following scenario: 1) lock LOCK_open 2) do a RENAME
- 2) unlock LOCK_open.
- This is a copy-paste added to make sure
- ALTER (sic:) TABLE .. RENAME works for views. ALTER VIEW is handled
- as an independent branch in mysql_execute_command. The need
- for a copy-paste arose because the main code flow of ALTER TABLE
- ... RENAME tries to use open_ltable, which does not work for views
- (open_ltable was never modified to merge table lists of child tables
- into the main table list, like open_tables does).
- This code is wrong and will be removed, please do not copy.
- */
- frm_type= mysql_frm_type(thd, new_name_buff, &table_type);
- /* Rename a view */
- /* Sic: there is a race here */
- if (frm_type == FRMTYPE_VIEW && !(alter_info->flags & ~ALTER_RENAME))
- {
- /*
- The following branch handles "ALTER VIEW v1 /no arguments/;"
- This feature is not documented one.
- However, before "OPTIMIZE TABLE t1;" was implemented,
- ALTER TABLE with no alter_specifications was used to force-rebuild
- the table. That's why this grammar is allowed. That's why we ignore
- it for views. So just do nothing in such a case.
- */
- if (!new_name)
- {
- my_ok(thd);
- DBUG_RETURN(FALSE);
- }
-
- /*
- Avoid problems with a rename on a table that we have locked or
- if the user is trying to to do this in a transcation context
- */
-
- if (thd->locked_tables_mode || thd->in_active_multi_stmt_transaction())
- {
- my_message(ER_LOCK_OR_ACTIVE_TRANSACTION,
- ER(ER_LOCK_OR_ACTIVE_TRANSACTION), MYF(0));
- DBUG_RETURN(TRUE);
- }
-
- if (thd->global_read_lock.wait_if_global_read_lock(thd, FALSE, TRUE))
- DBUG_RETURN(TRUE);
- if (lock_table_names(thd, table_list))
- {
- error= 1;
- goto view_err;
- }
-
- mysql_mutex_lock(&LOCK_open);
-
- if (!do_rename(thd, table_list, new_db, new_name, new_name, 1))
- {
- if (mysql_bin_log.is_open())
- {
- thd->clear_error();
- Query_log_event qinfo(thd, thd->query(), thd->query_length(),
- FALSE, TRUE, FALSE, 0);
- if ((error= mysql_bin_log.write(&qinfo)))
- goto view_err_unlock;
- }
- my_ok(thd);
- }
-
-view_err_unlock:
- mysql_mutex_unlock(&LOCK_open);
- unlock_table_names(thd);
-
-view_err:
- thd->global_read_lock.start_waiting_global_read_lock(thd);
- DBUG_RETURN(error);
- }
-
/*
Code below can handle only base tables so ensure that we won't open a view.
@@ -6637,8 +6570,7 @@ view_err:
Alter_table_prelocking_strategy alter_prelocking_strategy(alter_info);
- error= open_and_lock_tables(thd, table_list, FALSE,
- MYSQL_OPEN_TAKE_UPGRADABLE_MDL,
+ error= open_and_lock_tables(thd, table_list, FALSE, 0,
&alter_prelocking_strategy);
if (error)
@@ -7241,14 +7173,14 @@ view_err:
{
if (table->s->tmp_table)
{
- Open_table_context ot_ctx_unused(thd, LONG_TIMEOUT);
+ Open_table_context ot_ctx(thd, (MYSQL_OPEN_IGNORE_FLUSH |
+ MYSQL_LOCK_IGNORE_TIMEOUT));
TABLE_LIST tbl;
bzero((void*) &tbl, sizeof(tbl));
tbl.db= new_db;
tbl.table_name= tbl.alias= tmp_name;
/* Table is in thd->temporary_tables */
- (void) open_table(thd, &tbl, thd->mem_root, &ot_ctx_unused,
- MYSQL_OPEN_IGNORE_FLUSH);
+ (void) open_table(thd, &tbl, thd->mem_root, &ot_ctx);
new_table= tbl.table;
}
else
@@ -7527,7 +7459,7 @@ view_err:
To do this we need to obtain a handler object for it.
NO need to tamper with MERGE tables. The real open is done later.
*/
- Open_table_context ot_ctx_unused(thd, LONG_TIMEOUT);
+ Open_table_context ot_ctx(thd, MYSQL_OPEN_REOPEN);
TABLE *t_table;
if (new_name != table_name || new_db != db)
{
@@ -7547,8 +7479,7 @@ view_err:
*/
table_list->mdl_request.ticket= mdl_ticket;
}
- if (open_table(thd, table_list, thd->mem_root,
- &ot_ctx_unused, MYSQL_OPEN_REOPEN))
+ if (open_table(thd, table_list, thd->mem_root, &ot_ctx))
{
goto err_with_mdl;
}
@@ -7940,6 +7871,10 @@ bool mysql_recreate_table(THD *thd, TABLE_LIST *table_list)
table_list->table= NULL;
/* Same applies to MDL ticket. */
table_list->mdl_request.ticket= NULL;
+ /* Set lock type which is appropriate for ALTER TABLE. */
+ table_list->lock_type= TL_READ_NO_INSERT;
+ /* Same applies to MDL request. */
+ table_list->mdl_request.set_type(MDL_SHARED_NO_WRITE);
bzero((char*) &create_info, sizeof(create_info));
create_info.row_type=ROW_TYPE_NOT_USED;
diff --git a/sql/sql_test.cc b/sql/sql_test.cc
index 43d203e6498..48fd5f9dff8 100644
--- a/sql/sql_test.cc
+++ b/sql/sql_test.cc
@@ -45,7 +45,6 @@ static const char *lock_descriptions[] =
/* TL_READ_HIGH_PRIORITY */ "High priority read lock",
/* TL_READ_NO_INSERT */ "Read lock without concurrent inserts",
/* TL_WRITE_ALLOW_WRITE */ "Write lock that allows other writers",
- /* TL_WRITE_ALLOW_READ */ "Write lock, but allow reading",
/* TL_WRITE_CONCURRENT_INSERT */ "Concurrent insert lock",
/* TL_WRITE_DELAYED */ "Lock used by delayed insert",
/* TL_WRITE_DEFAULT */ NULL,
diff --git a/sql/sql_trigger.cc b/sql/sql_trigger.cc
index 9ce62d9f2a4..e9330574b34 100644
--- a/sql/sql_trigger.cc
+++ b/sql/sql_trigger.cc
@@ -489,8 +489,7 @@ bool mysql_create_or_drop_trigger(THD *thd, TABLE_LIST *tables, bool create)
else
{
tables->table= open_n_lock_single_table(thd, tables,
- TL_WRITE_ALLOW_READ,
- MYSQL_OPEN_TAKE_UPGRADABLE_MDL);
+ TL_READ_NO_INSERT, 0);
if (! tables->table)
goto end;
tables->table->use_all_columns();
@@ -1667,7 +1666,8 @@ bool add_table_for_trigger(THD *thd,
DBUG_RETURN(TRUE);
*table= sp_add_to_query_tables(thd, lex, trg_name->m_db.str,
- tbl_name.str, TL_IGNORE);
+ tbl_name.str, TL_IGNORE,
+ MDL_SHARED_NO_WRITE);
DBUG_RETURN(*table ? FALSE : TRUE);
}
diff --git a/sql/sql_truncate.cc b/sql/sql_truncate.cc
new file mode 100644
index 00000000000..901ab8e987d
--- /dev/null
+++ b/sql/sql_truncate.cc
@@ -0,0 +1,485 @@
+/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "sql_truncate.h"
+#include "sql_priv.h"
+#include "transaction.h"
+#include "debug_sync.h"
+#include "records.h" // READ_RECORD
+#include "table.h" // TABLE
+#include "sql_class.h" // THD
+#include "sql_base.h" // open_and_lock_tables
+#include "sql_table.h" // write_bin_log
+#include "sql_handler.h" // mysql_ha_rm_tables
+#include "datadict.h" // dd_recreate_table()
+#include "lock.h" // MYSQL_OPEN_TEMPORARY_ONLY
+
+
+/*
+ Delete all rows of a locked table.
+
+ @param thd Thread context.
+ @param table_list Table list element for the table.
+ @param rows_deleted Whether rows might have been deleted.
+
+ @retval FALSE Success.
+ @retval TRUE Error.
+*/
+
+static bool
+delete_all_rows(THD *thd, TABLE *table)
+{
+ int error;
+ READ_RECORD info;
+ bool is_bulk_delete;
+ bool some_rows_deleted= FALSE;
+ bool save_binlog_row_based= thd->is_current_stmt_binlog_format_row();
+ DBUG_ENTER("delete_all_rows");
+
+ /* Replication of truncate table must be statement based. */
+ thd->clear_current_stmt_binlog_format_row();
+
+ /*
+ Update handler statistics (e.g. table->file->stats.records).
+ Might be used by the storage engine to aggregate information
+ necessary to allow deletion. Currently, this seems to be
+ meaningful only to the archive storage engine, which uses
+ the info method to set the number of records. Although
+ archive does not support deletion, it becomes necessary in
+ order to return a error if the table is not empty.
+ */
+ error= table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK);
+ if (error && error != HA_ERR_WRONG_COMMAND)
+ {
+ table->file->print_error(error, MYF(0));
+ goto end;
+ }
+
+ /*
+ Attempt to delete all rows in the table.
+ If it is unsupported, switch to row by row deletion.
+ */
+ if (! (error= table->file->ha_delete_all_rows()))
+ goto end;
+
+ if (error != HA_ERR_WRONG_COMMAND)
+ {
+ /*
+ If a transactional engine fails in the middle of deletion,
+ we expect it to be able to roll it back. Some reasons
+ for the engine to fail would be media failure or corrupted
+ data dictionary (i.e. in case of a partitioned table). We
+ have sufficiently strong metadata locks to rule out any
+ potential deadlocks.
+
+ If a non-transactional engine fails here (that would
+ not be MyISAM, since MyISAM does TRUNCATE by recreate),
+ and binlog is on, replication breaks, since nothing gets
+ written to the binary log. (XXX: is this a bug?)
+ */
+ table->file->print_error(error, MYF(0));
+ goto end;
+ }
+
+ /*
+ A workaround for Bug#53696 "Performance schema engine violates the
+ PSEA API by calling my_error()".
+ */
+ if (thd->is_error())
+ goto end;
+
+ /* Handler didn't support fast delete. Delete rows one by one. */
+
+ init_read_record(&info, thd, table, NULL, TRUE, TRUE, FALSE);
+
+ /*
+ Start bulk delete. If the engine does not support it, go on,
+ it's not an error.
+ */
+ is_bulk_delete= ! table->file->start_bulk_delete();
+
+ table->mark_columns_needed_for_delete();
+
+ while (!(error= info.read_record(&info)) && !thd->killed)
+ {
+ if ((error= table->file->ha_delete_row(table->record[0])))
+ {
+ table->file->print_error(error, MYF(0));
+ break;
+ }
+
+ some_rows_deleted= TRUE;
+ }
+
+ /* HA_ERR_END_OF_FILE */
+ if (error == -1)
+ error= 0;
+
+ /* Close down the bulk delete. */
+ if (is_bulk_delete)
+ {
+ int bulk_delete_error= table->file->end_bulk_delete();
+ if (bulk_delete_error && !error)
+ {
+ table->file->print_error(bulk_delete_error, MYF(0));
+ error= bulk_delete_error;
+ }
+ }
+
+ end_read_record(&info);
+
+ /*
+ Regardless of the error status, the query must be written to the
+ binary log if rows of the table is non-transactional.
+ */
+ if (some_rows_deleted && !table->file->has_transactions())
+ {
+ thd->transaction.stmt.modified_non_trans_table= TRUE;
+ thd->transaction.all.modified_non_trans_table= TRUE;
+ }
+
+ if (error || thd->killed)
+ goto end;
+
+ /* Truncate resets the auto-increment counter. */
+ error= table->file->ha_reset_auto_increment(0);
+ if (error)
+ {
+ if (error != HA_ERR_WRONG_COMMAND)
+ table->file->print_error(error, MYF(0));
+ else
+ error= 0;
+ }
+
+end:
+ if (save_binlog_row_based)
+ thd->set_current_stmt_binlog_format_row();
+
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Close and recreate a temporary table. In case of success,
+ write truncate statement into the binary log if in statement
+ mode.
+
+ @param thd Thread context.
+ @param table The temporary table.
+
+ @retval FALSE Success.
+ @retval TRUE Error.
+*/
+
+static bool recreate_temporary_table(THD *thd, TABLE *table)
+{
+ bool error= TRUE;
+ TABLE_SHARE *share= table->s;
+ HA_CREATE_INFO create_info;
+ handlerton *table_type= table->s->db_type();
+ DBUG_ENTER("recreate_temporary_table");
+
+ memset(&create_info, 0, sizeof(create_info));
+
+ table->file->info(HA_STATUS_AUTO | HA_STATUS_NO_LOCK);
+
+ /* Don't free share. */
+ close_temporary_table(thd, table, FALSE, FALSE);
+
+ /*
+ We must use share->normalized_path.str since for temporary tables it
+ differs from what dd_recreate_table() would generate based
+ on table and schema names.
+ */
+ ha_create_table(thd, share->normalized_path.str, share->db.str,
+ share->table_name.str, &create_info, 1);
+
+ if (open_temporary_table(thd, share->path.str, share->db.str,
+ share->table_name.str, 1))
+ {
+ error= FALSE;
+ thd->thread_specific_used= TRUE;
+ }
+ else
+ rm_temporary_table(table_type, share->path.str);
+
+ free_table_share(share);
+ my_free(table, MYF(0));
+
+ DBUG_RETURN(error);
+}
+
+
+/*
+ Handle opening and locking if a base table for truncate.
+
+ @param[in] thd Thread context.
+ @param[in] table_ref Table list element for the table to
+ be truncated.
+ @param[out] hton_can_recreate Set to TRUE if table can be dropped
+ and recreated.
+ @param[out] ticket_downgrade Set if a lock must be downgraded after
+ truncate is done.
+
+ @retval FALSE Success.
+ @retval TRUE Error.
+*/
+
+static bool open_and_lock_table_for_truncate(THD *thd, TABLE_LIST *table_ref,
+ bool *hton_can_recreate,
+ MDL_ticket **ticket_downgrade)
+{
+ TABLE *table= NULL;
+ MDL_ticket *mdl_ticket= NULL;
+ DBUG_ENTER("open_and_lock_table_for_truncate");
+
+ /*
+ Before doing anything else, acquire a metadata lock on the table,
+ or ensure we have one. We don't use open_and_lock_tables()
+ right away because we want to be able to truncate (and recreate)
+ corrupted tables, those that we can't fully open.
+
+ MySQL manual documents that TRUNCATE can be used to repair a
+ damaged table, i.e. a table that can not be fully "opened".
+ In particular MySQL manual says: As long as the table format
+ file tbl_name.frm is valid, the table can be re-created as
+ an empty table with TRUNCATE TABLE, even if the data or index
+ files have become corrupted.
+ */
+ if (thd->locked_tables_mode)
+ {
+ if (!(table= find_table_for_mdl_upgrade(thd->open_tables, table_ref->db,
+ table_ref->table_name, FALSE)))
+ DBUG_RETURN(TRUE);
+
+ *hton_can_recreate= ha_check_storage_engine_flag(table->s->db_type(),
+ HTON_CAN_RECREATE);
+ }
+ else
+ {
+ /*
+ Even though we could use the previous execution branch here just as
+ well, we must not try to open the table:
+ */
+ MDL_request mdl_global_request, mdl_request;
+ MDL_request_list mdl_requests;
+
+ mdl_global_request.init(MDL_key::GLOBAL, "", "", MDL_INTENTION_EXCLUSIVE);
+ mdl_request.init(MDL_key::TABLE, table_ref->db, table_ref->table_name,
+ MDL_SHARED_NO_READ_WRITE);
+ mdl_requests.push_front(&mdl_request);
+ mdl_requests.push_front(&mdl_global_request);
+
+ if (thd->mdl_context.acquire_locks(&mdl_requests,
+ thd->variables.lock_wait_timeout))
+ DBUG_RETURN(TRUE);
+
+ mdl_ticket= mdl_request.ticket;
+
+ if (dd_check_storage_engine_flag(thd, table_ref->db, table_ref->table_name,
+ HTON_CAN_RECREATE, hton_can_recreate))
+ DBUG_RETURN(TRUE);
+ }
+
+ DEBUG_SYNC(thd, "lock_table_for_truncate");
+
+ if (*hton_can_recreate)
+ {
+ /*
+ Acquire an exclusive lock. The storage engine can recreate the
+ table only if there are no references to it from anywhere, i.e.
+ no cached TABLE in the table cache. To remove the table from the
+ cache we need an exclusive lock.
+ */
+ if (thd->locked_tables_mode)
+ {
+ if (wait_while_table_is_used(thd, table, HA_EXTRA_FORCE_REOPEN))
+ DBUG_RETURN(TRUE);
+ *ticket_downgrade= table->mdl_ticket;
+ close_all_tables_for_name(thd, table->s, FALSE);
+ }
+ else
+ {
+ ulong timeout= thd->variables.lock_wait_timeout;
+ if (thd->mdl_context.upgrade_shared_lock_to_exclusive(mdl_ticket, timeout))
+ DBUG_RETURN(TRUE);
+ mysql_mutex_lock(&LOCK_open);
+ tdc_remove_table(thd, TDC_RT_REMOVE_ALL, table_ref->db,
+ table_ref->table_name);
+ mysql_mutex_unlock(&LOCK_open);
+ }
+ }
+ else
+ {
+ /*
+ Can't recreate, we must mechanically delete all rows in
+ the table. Our metadata lock guarantees that no transaction
+ is reading or writing into the table. Yet, to open a write
+ cursor we need a thr_lock lock. Use open_and_lock_tables()
+ to do the necessary job.
+ */
+
+ /* Allow to open base tables only. */
+ table_ref->required_type= FRMTYPE_TABLE;
+ /* We don't need to load triggers. */
+ DBUG_ASSERT(table_ref->trg_event_map == 0);
+ /* Work around partition parser rules using alter table's. */
+ if (thd->lex->alter_info.flags & ALTER_ADMIN_PARTITION)
+ {
+ table_ref->lock_type= TL_WRITE;
+ table_ref->mdl_request.set_type(MDL_SHARED_WRITE);
+ }
+ /* Ensure proper lock types (e.g. from the parser). */
+ DBUG_ASSERT(table_ref->lock_type == TL_WRITE);
+ DBUG_ASSERT(table_ref->mdl_request.type == MDL_SHARED_WRITE);
+
+ /*
+ Open the table as it will handle some required preparations.
+ Ignore pending FLUSH TABLES since we don't want to release
+ the MDL lock taken above and otherwise there is no way to
+ wait for FLUSH TABLES in deadlock-free fashion.
+ */
+ if (open_and_lock_tables(thd, table_ref, FALSE,
+ MYSQL_OPEN_IGNORE_FLUSH |
+ MYSQL_OPEN_SKIP_TEMPORARY))
+ DBUG_RETURN(TRUE);
+ }
+
+ DBUG_RETURN(FALSE);
+}
+
+
+/*
+ Optimized delete of all rows by doing a full generate of the table.
+
+ @remark Will work even if the .MYI and .MYD files are destroyed.
+ In other words, it works as long as the .FRM is intact and
+ the engine supports re-create.
+
+ @param thd Thread context.
+ @param table_ref Table list element for the table to be truncated.
+
+ @retval FALSE Success.
+ @retval TRUE Error.
+*/
+
+bool mysql_truncate_table(THD *thd, TABLE_LIST *table_ref)
+{
+ TABLE *table;
+ bool error= TRUE, binlog_stmt;
+ MDL_ticket *mdl_ticket= NULL;
+ DBUG_ENTER("mysql_truncate_table");
+
+ /* Remove tables from the HANDLER's hash. */
+ mysql_ha_rm_tables(thd, table_ref);
+
+ /* If it is a temporary table, no need to take locks. */
+ if ((table= find_temporary_table(thd, table_ref)))
+ {
+ /* In RBR, the statement is not binlogged if the table is temporary. */
+ binlog_stmt= !thd->is_current_stmt_binlog_format_row();
+
+ /* Note that a temporary table cannot be partitioned. */
+ if (ha_check_storage_engine_flag(table->s->db_type(), HTON_CAN_RECREATE))
+ {
+ if ((error= recreate_temporary_table(thd, table)))
+ binlog_stmt= FALSE; /* No need to binlog failed truncate-by-recreate. */
+
+ DBUG_ASSERT(! thd->transaction.stmt.modified_non_trans_table);
+ }
+ else
+ {
+ /*
+ The engine does not support truncate-by-recreate. Open the
+ table and delete all rows. In such a manner this can in fact
+ open several tables if it's a temporary MyISAMMRG table.
+ */
+ if (open_and_lock_tables(thd, table_ref, FALSE,
+ MYSQL_OPEN_TEMPORARY_ONLY))
+ DBUG_RETURN(TRUE);
+
+ error= delete_all_rows(thd, table_ref->table);
+ }
+
+ /*
+ No need to invalidate the query cache, queries with temporary
+ tables are not in the cache. No need to write to the binary
+ log a failed row-by-row delete even if under RBR as the table
+ might not exist on the slave.
+ */
+ }
+ else /* It's not a temporary table. */
+ {
+ bool hton_can_recreate;
+
+ if (open_and_lock_table_for_truncate(thd, table_ref,
+ &hton_can_recreate, &mdl_ticket))
+ DBUG_RETURN(TRUE);
+
+ if (hton_can_recreate)
+ {
+ /*
+ The storage engine can truncate the table by creating an
+ empty table with the same structure.
+ */
+ error= dd_recreate_table(thd, table_ref->db, table_ref->table_name);
+
+ if (thd->locked_tables_mode && thd->locked_tables_list.reopen_tables(thd))
+ thd->locked_tables_list.unlink_all_closed_tables(thd, NULL, 0);
+
+ /* No need to binlog a failed truncate-by-recreate. */
+ binlog_stmt= !error;
+ }
+ else
+ {
+ error= delete_all_rows(thd, table_ref->table);
+
+ /*
+ Regardless of the error status, the query must be written to the
+ binary log if rows of a non-transactional table were deleted.
+ */
+ binlog_stmt= !error || thd->transaction.stmt.modified_non_trans_table;
+ }
+
+ query_cache_invalidate3(thd, table_ref, FALSE);
+ }
+
+ /* DDL is logged in statement format, regardless of binlog format. */
+ if (binlog_stmt)
+ error|= write_bin_log(thd, !error, thd->query(), thd->query_length());
+
+ /*
+ All effects of a TRUNCATE TABLE operation are rolled back if a row
+ by row deletion fails. Otherwise, it is automatically committed at
+ the end.
+ */
+ if (error)
+ {
+ trans_rollback_stmt(thd);
+ trans_rollback(thd);
+ }
+
+ /*
+ A locked table ticket was upgraded to a exclusive lock. After the
+ the query has been written to the binary log, downgrade the lock
+ to a shared one.
+ */
+ if (mdl_ticket)
+ mdl_ticket->downgrade_exclusive_lock(MDL_SHARED_NO_READ_WRITE);
+
+ DBUG_PRINT("exit", ("error: %d", error));
+ DBUG_RETURN(test(error));
+}
+
diff --git a/sql/sql_truncate.h b/sql/sql_truncate.h
new file mode 100644
index 00000000000..11c07c7187c
--- /dev/null
+++ b/sql/sql_truncate.h
@@ -0,0 +1,23 @@
+#ifndef SQL_TRUNCATE_INCLUDED
+#define SQL_TRUNCATE_INCLUDED
+/* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
+
+class THD;
+struct TABLE_LIST;
+
+bool mysql_truncate_table(THD *thd, TABLE_LIST *table_ref);
+
+#endif
diff --git a/sql/sql_view.cc b/sql/sql_view.cc
index 3a6866f4a7e..3c8de0a253c 100644
--- a/sql/sql_view.cc
+++ b/sql/sql_view.cc
@@ -32,6 +32,7 @@
#include "sp.h"
#include "sp_head.h"
#include "sp_cache.h"
+#include "datadict.h" // dd_frm_type()
#define MD5_BUFF_LENGTH 33
@@ -433,7 +434,7 @@ bool mysql_create_view(THD *thd, TABLE_LIST *views,
lex->link_first_table_back(view, link_to_local);
view->open_strategy= TABLE_LIST::OPEN_STUB;
- view->lock_strategy= TABLE_LIST::EXCLUSIVE_MDL;
+ view->lock_strategy= TABLE_LIST::OTLS_NONE;
view->open_type= OT_BASE_ONLY;
if (open_and_lock_tables(thd, lex->query_tables, TRUE, 0))
@@ -1663,7 +1664,7 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
view->db, view->table_name, reg_ext, 0);
if (access(path, F_OK) ||
- FRMTYPE_VIEW != (type= mysql_frm_type(thd, path, &not_used)))
+ FRMTYPE_VIEW != (type= dd_frm_type(thd, path, &not_used)))
{
char name[FN_REFLEN];
my_snprintf(name, sizeof(name), "%s.%s", view->db, view->table_name);
@@ -1742,54 +1743,6 @@ bool mysql_drop_view(THD *thd, TABLE_LIST *views, enum_drop_mode drop_mode)
/*
- Check type of .frm if we are not going to parse it
-
- SYNOPSIS
- mysql_frm_type()
- path path to file
-
- RETURN
- FRMTYPE_ERROR error
- FRMTYPE_TABLE table
- FRMTYPE_VIEW view
-*/
-
-frm_type_enum mysql_frm_type(THD *thd, char *path, enum legacy_db_type *dbt)
-{
- File file;
- uchar header[10]; //"TYPE=VIEW\n" it is 10 characters
- size_t error;
- DBUG_ENTER("mysql_frm_type");
-
- *dbt= DB_TYPE_UNKNOWN;
-
- if ((file= mysql_file_open(key_file_frm,
- path, O_RDONLY | O_SHARE, MYF(0))) < 0)
- DBUG_RETURN(FRMTYPE_ERROR);
- error= mysql_file_read(file, (uchar*) header, sizeof(header), MYF(MY_NABP));
- mysql_file_close(file, MYF(MY_WME));
-
- if (error)
- DBUG_RETURN(FRMTYPE_ERROR);
- if (!strncmp((char*) header, "TYPE=VIEW\n", sizeof(header)))
- DBUG_RETURN(FRMTYPE_VIEW);
-
- /*
- This is just a check for DB_TYPE. We'll return default unknown type
- if the following test is true (arg #3). This should not have effect
- on return value from this function (default FRMTYPE_TABLE)
- */
- if (header[0] != (uchar) 254 || header[1] != 1 ||
- (header[2] != FRM_VER && header[2] != FRM_VER+1 &&
- (header[2] < FRM_VER+3 || header[2] > FRM_VER+4)))
- DBUG_RETURN(FRMTYPE_TABLE);
-
- *dbt= (enum legacy_db_type) (uint) *(header + 3);
- DBUG_RETURN(FRMTYPE_TABLE); // Is probably a .frm table
-}
-
-
-/*
check of key (primary or unique) presence in updatable view
SYNOPSIS
diff --git a/sql/sql_view.h b/sql/sql_view.h
index 7d06abb9068..c15ecffccb8 100644
--- a/sql/sql_view.h
+++ b/sql/sql_view.h
@@ -43,8 +43,6 @@ bool check_key_in_view(THD *thd, TABLE_LIST * view);
bool insert_view_fields(THD *thd, List<Item> *list, TABLE_LIST *view);
-frm_type_enum mysql_frm_type(THD *thd, char *path, enum legacy_db_type *dbt);
-
int view_checksum(THD *thd, TABLE_LIST *view);
extern TYPELIB updatable_views_with_limit_typelib;
diff --git a/sql/sql_yacc.yy b/sql/sql_yacc.yy
index 9f20a4ccf71..34e20374042 100644
--- a/sql/sql_yacc.yy
+++ b/sql/sql_yacc.yy
@@ -697,7 +697,8 @@ static bool add_create_index_prepare (LEX *lex, Table_ident *table)
lex->sql_command= SQLCOM_CREATE_INDEX;
if (!lex->current_select->add_table_to_list(lex->thd, table, NULL,
TL_OPTION_UPDATING,
- TL_WRITE_ALLOW_READ))
+ TL_READ_NO_INSERT,
+ MDL_SHARED_NO_WRITE))
return TRUE;
lex->alter_info.reset();
lex->alter_info.flags= ALTER_ADD_INDEX;
@@ -2023,7 +2024,7 @@ create:
lex->sql_command= SQLCOM_CREATE_TABLE;
if (!lex->select_lex.add_table_to_list(thd, $5, NULL,
TL_OPTION_UPDATING,
- TL_WRITE))
+ TL_WRITE, MDL_EXCLUSIVE))
MYSQL_YYABORT;
lex->alter_info.reset();
lex->col_list.empty();
@@ -4213,7 +4214,8 @@ create2:
lex->create_info.options|= HA_LEX_CREATE_TABLE_LIKE;
src_table= lex->select_lex.add_table_to_list(thd, $2, NULL, 0,
- TL_READ);
+ TL_READ,
+ MDL_SHARED_READ);
if (! src_table)
MYSQL_YYABORT;
/* CREATE TABLE ... LIKE is not allowed for views. */
@@ -4227,7 +4229,8 @@ create2:
lex->create_info.options|= HA_LEX_CREATE_TABLE_LIKE;
src_table= lex->select_lex.add_table_to_list(thd, $3, NULL, 0,
- TL_READ);
+ TL_READ,
+ MDL_SHARED_READ);
if (! src_table)
MYSQL_YYABORT;
/* CREATE TABLE ... LIKE is not allowed for views. */
@@ -6154,7 +6157,8 @@ alter:
lex->duplicates= DUP_ERROR;
if (!lex->select_lex.add_table_to_list(thd, $4, NULL,
TL_OPTION_UPDATING,
- TL_WRITE_ALLOW_READ))
+ TL_READ_NO_INSERT,
+ MDL_SHARED_NO_WRITE))
MYSQL_YYABORT;
lex->col_list.empty();
lex->select_lex.init_order();
@@ -6847,6 +6851,8 @@ checksum:
{
LEX *lex=Lex;
lex->sql_command = SQLCOM_CHECKSUM;
+ /* Will be overriden during execution. */
+ YYPS->m_lock_type= TL_UNLOCK;
}
table_list opt_checksum_type
{}
@@ -6866,6 +6872,8 @@ repair:
lex->no_write_to_binlog= $2;
lex->check_opt.init();
lex->alter_info.reset();
+ /* Will be overriden during execution. */
+ YYPS->m_lock_type= TL_UNLOCK;
}
table_list opt_mi_repair_type
{}
@@ -6895,6 +6903,8 @@ analyze:
lex->no_write_to_binlog= $2;
lex->check_opt.init();
lex->alter_info.reset();
+ /* Will be overriden during execution. */
+ YYPS->m_lock_type= TL_UNLOCK;
}
table_list
{}
@@ -6921,6 +6931,8 @@ check:
lex->sql_command = SQLCOM_CHECK;
lex->check_opt.init();
lex->alter_info.reset();
+ /* Will be overriden during execution. */
+ YYPS->m_lock_type= TL_UNLOCK;
}
table_list opt_mi_check_type
{}
@@ -6953,6 +6965,8 @@ optimize:
lex->no_write_to_binlog= $2;
lex->check_opt.init();
lex->alter_info.reset();
+ /* Will be overriden during execution. */
+ YYPS->m_lock_type= TL_UNLOCK;
}
table_list
{}
@@ -7001,9 +7015,9 @@ table_to_table:
LEX *lex=Lex;
SELECT_LEX *sl= lex->current_select;
if (!sl->add_table_to_list(lex->thd, $1,NULL,TL_OPTION_UPDATING,
- TL_IGNORE) ||
+ TL_IGNORE, MDL_EXCLUSIVE) ||
!sl->add_table_to_list(lex->thd, $3,NULL,TL_OPTION_UPDATING,
- TL_IGNORE))
+ TL_IGNORE, MDL_EXCLUSIVE))
MYSQL_YYABORT;
}
;
@@ -7034,7 +7048,8 @@ keycache_list:
assign_to_keycache:
table_ident cache_keys_spec
{
- if (!Select->add_table_to_list(YYTHD, $1, NULL, 0, TL_READ,
+ if (!Select->add_table_to_list(YYTHD, $1, NULL, 0, TL_READ,
+ MDL_SHARED_READ,
Select->pop_index_hints()))
MYSQL_YYABORT;
}
@@ -7044,6 +7059,7 @@ assign_to_keycache_parts:
table_ident adm_partition cache_keys_spec
{
if (!Select->add_table_to_list(YYTHD, $1, NULL, 0, TL_READ,
+ MDL_SHARED_READ,
Select->pop_index_hints()))
MYSQL_YYABORT;
}
@@ -7079,6 +7095,7 @@ preload_keys:
table_ident cache_keys_spec opt_ignore_leaves
{
if (!Select->add_table_to_list(YYTHD, $1, NULL, $3, TL_READ,
+ MDL_SHARED_READ,
Select->pop_index_hints()))
MYSQL_YYABORT;
}
@@ -7088,6 +7105,7 @@ preload_keys_parts:
table_ident adm_partition cache_keys_spec opt_ignore_leaves
{
if (!Select->add_table_to_list(YYTHD, $1, NULL, $4, TL_READ,
+ MDL_SHARED_READ,
Select->pop_index_hints()))
MYSQL_YYABORT;
}
@@ -9220,6 +9238,7 @@ table_factor:
if (!($$= Select->add_table_to_list(YYTHD, $2, $3,
Select->get_table_join_options(),
YYPS->m_lock_type,
+ YYPS->m_mdl_type,
Select->pop_index_hints())))
MYSQL_YYABORT;
Select->add_joined_table($$);
@@ -9291,7 +9310,7 @@ table_factor:
MYSQL_YYABORT;
if (!($$= sel->add_table_to_list(lex->thd,
new Table_ident(unit), $5, 0,
- TL_READ)))
+ TL_READ, MDL_SHARED_READ)))
MYSQL_YYABORT;
sel->add_joined_table($$);
@@ -10126,13 +10145,17 @@ do:
*/
drop:
- DROP opt_temporary table_or_tables if_exists table_list opt_restrict
+ DROP opt_temporary table_or_tables if_exists
{
LEX *lex=Lex;
lex->sql_command = SQLCOM_DROP_TABLE;
lex->drop_temporary= $2;
lex->drop_if_exists= $4;
+ YYPS->m_lock_type= TL_IGNORE;
+ YYPS->m_mdl_type= MDL_EXCLUSIVE;
}
+ table_list opt_restrict
+ {}
| DROP INDEX_SYM ident ON table_ident {}
{
LEX *lex=Lex;
@@ -10145,7 +10168,8 @@ drop:
lex->alter_info.drop_list.push_back(ad);
if (!lex->current_select->add_table_to_list(lex->thd, $5, NULL,
TL_OPTION_UPDATING,
- TL_WRITE_ALLOW_READ))
+ TL_READ_NO_INSERT,
+ MDL_SHARED_NO_WRITE))
MYSQL_YYABORT;
}
| DROP DATABASE if_exists ident
@@ -10215,12 +10239,16 @@ drop:
{
Lex->sql_command = SQLCOM_DROP_USER;
}
- | DROP VIEW_SYM if_exists table_list opt_restrict
+ | DROP VIEW_SYM if_exists
{
LEX *lex= Lex;
lex->sql_command= SQLCOM_DROP_VIEW;
lex->drop_if_exists= $3;
+ YYPS->m_lock_type= TL_IGNORE;
+ YYPS->m_mdl_type= MDL_EXCLUSIVE;
}
+ table_list opt_restrict
+ {}
| DROP EVENT_SYM if_exists sp_name
{
Lex->drop_if_exists= $3;
@@ -10261,7 +10289,10 @@ table_list:
table_name:
table_ident
{
- if (!Select->add_table_to_list(YYTHD, $1, NULL, TL_OPTION_UPDATING))
+ if (!Select->add_table_to_list(YYTHD, $1, NULL,
+ TL_OPTION_UPDATING,
+ YYPS->m_lock_type,
+ YYPS->m_mdl_type))
MYSQL_YYABORT;
}
;
@@ -10276,7 +10307,8 @@ table_alias_ref:
{
if (!Select->add_table_to_list(YYTHD, $1, NULL,
TL_OPTION_UPDATING | TL_OPTION_ALIAS,
- YYPS->m_lock_type))
+ YYPS->m_lock_type,
+ YYPS->m_mdl_type))
MYSQL_YYABORT;
}
;
@@ -10558,6 +10590,8 @@ delete:
lex->sql_command= SQLCOM_DELETE;
mysql_init_select(lex);
YYPS->m_lock_type= TL_WRITE_DEFAULT;
+ YYPS->m_mdl_type= MDL_SHARED_WRITE;
+
lex->ignore= 0;
lex->select_lex.init_order();
}
@@ -10568,9 +10602,11 @@ single_multi:
FROM table_ident
{
if (!Select->add_table_to_list(YYTHD, $2, NULL, TL_OPTION_UPDATING,
- YYPS->m_lock_type))
+ YYPS->m_lock_type,
+ YYPS->m_mdl_type))
MYSQL_YYABORT;
YYPS->m_lock_type= TL_READ_DEFAULT;
+ YYPS->m_mdl_type= MDL_SHARED_READ;
}
where_clause opt_order_clause
delete_limit_clause {}
@@ -10578,6 +10614,7 @@ single_multi:
{
mysql_init_multi_delete(Lex);
YYPS->m_lock_type= TL_READ_DEFAULT;
+ YYPS->m_mdl_type= MDL_SHARED_READ;
}
FROM join_table_list where_clause
{
@@ -10588,6 +10625,7 @@ single_multi:
{
mysql_init_multi_delete(Lex);
YYPS->m_lock_type= TL_READ_DEFAULT;
+ YYPS->m_mdl_type= MDL_SHARED_READ;
}
USING join_table_list where_clause
{
@@ -10611,7 +10649,8 @@ table_wild_one:
ti,
NULL,
TL_OPTION_UPDATING | TL_OPTION_ALIAS,
- YYPS->m_lock_type))
+ YYPS->m_lock_type,
+ YYPS->m_mdl_type))
MYSQL_YYABORT;
}
| ident '.' ident opt_wild
@@ -10623,7 +10662,8 @@ table_wild_one:
ti,
NULL,
TL_OPTION_UPDATING | TL_OPTION_ALIAS,
- YYPS->m_lock_type))
+ YYPS->m_lock_type,
+ YYPS->m_mdl_type))
MYSQL_YYABORT;
}
;
@@ -10645,7 +10685,7 @@ opt_delete_option:
;
truncate:
- TRUNCATE_SYM opt_table_sym table_name
+ TRUNCATE_SYM opt_table_sym
{
LEX* lex= Lex;
lex->sql_command= SQLCOM_TRUNCATE;
@@ -10653,7 +10693,11 @@ truncate:
lex->select_lex.options= 0;
lex->select_lex.sql_cache= SELECT_LEX::SQL_CACHE_UNSPECIFIED;
lex->select_lex.init_order();
+ YYPS->m_lock_type= TL_WRITE;
+ YYPS->m_mdl_type= MDL_SHARED_WRITE;
}
+ table_name
+ {}
;
opt_table_sym:
@@ -11137,7 +11181,15 @@ flush:
flush_options:
table_or_tables
- { Lex->type|= REFRESH_TABLES; }
+ {
+ Lex->type|= REFRESH_TABLES;
+ /*
+ Set type of metadata and table locks for
+ FLUSH TABLES table_list WITH READ LOCK.
+ */
+ YYPS->m_lock_type= TL_READ_NO_INSERT;
+ YYPS->m_mdl_type= MDL_EXCLUSIVE;
+ }
opt_table_list {}
opt_with_read_lock {}
| flush_options_list
@@ -11301,7 +11353,7 @@ load:
{
LEX *lex=Lex;
if (!Select->add_table_to_list(YYTHD, $12, NULL, TL_OPTION_UPDATING,
- $4))
+ $4, MDL_SHARED_WRITE))
MYSQL_YYABORT;
lex->field_list.empty();
lex->update_list.empty();
@@ -13007,10 +13059,14 @@ table_lock:
table_ident opt_table_alias lock_option
{
thr_lock_type lock_type= (thr_lock_type) $3;
- if (!Select->add_table_to_list(YYTHD, $1, $2, 0, lock_type))
+ bool lock_for_write= (lock_type >= TL_WRITE_ALLOW_WRITE);
+ if (!Select->add_table_to_list(YYTHD, $1, $2, 0, lock_type,
+ (lock_for_write ?
+ MDL_SHARED_NO_READ_WRITE :
+ MDL_SHARED_READ)))
MYSQL_YYABORT;
/* If table is to be write locked, protect from a impending GRL. */
- if (lock_type >= TL_WRITE_ALLOW_WRITE)
+ if (lock_for_write)
Lex->protect_against_global_read_lock= TRUE;
}
;
@@ -13765,6 +13821,7 @@ query_expression_option:
if (check_simple_select())
MYSQL_YYABORT;
YYPS->m_lock_type= TL_READ_HIGH_PRIORITY;
+ YYPS->m_mdl_type= MDL_SHARED_READ;
Select->options|= SELECT_HIGH_PRIORITY;
}
| DISTINCT { Select->options|= SELECT_DISTINCT; }
@@ -13894,7 +13951,10 @@ view_tail:
LEX *lex= thd->lex;
lex->sql_command= SQLCOM_CREATE_VIEW;
/* first table in list is target VIEW name */
- if (!lex->select_lex.add_table_to_list(thd, $3, NULL, TL_OPTION_UPDATING))
+ if (!lex->select_lex.add_table_to_list(thd, $3, NULL,
+ TL_OPTION_UPDATING,
+ TL_IGNORE,
+ MDL_EXCLUSIVE))
MYSQL_YYABORT;
}
view_list_opt AS view_select
@@ -14034,7 +14094,8 @@ trigger_tail:
if (!lex->select_lex.add_table_to_list(YYTHD, $9,
(LEX_STRING*) 0,
TL_OPTION_UPDATING,
- TL_IGNORE))
+ TL_READ_NO_INSERT,
+ MDL_SHARED_NO_WRITE))
MYSQL_YYABORT;
}
;
diff --git a/sql/table.cc b/sql/table.cc
index b104c212593..84153f6d9c5 100644
--- a/sql/table.cc
+++ b/sql/table.cc
@@ -2773,9 +2773,10 @@ bool check_db_name(LEX_STRING *org_name)
returns 1 on error
*/
-bool check_table_name(const char *name, uint length, bool check_for_path_chars)
+bool check_table_name(const char *name, size_t length, bool check_for_path_chars)
{
- uint name_length= 0; // name length in symbols
+ // name length in symbols
+ size_t name_length= 0;
const char *end= name+length;
if (!length || length > NAME_LEN)
return 1;
@@ -2808,18 +2809,19 @@ bool check_table_name(const char *name, uint length, bool check_for_path_chars)
name_length++;
}
#if defined(USE_MB) && defined(USE_MB_IDENT)
- return (last_char_is_space || name_length > NAME_CHAR_LEN) ;
+ return last_char_is_space || (name_length > NAME_CHAR_LEN);
#else
- return 0;
+ return FALSE;
#endif
}
bool check_column_name(const char *name)
{
- uint name_length= 0; // name length in symbols
+ // name length in symbols
+ size_t name_length= 0;
bool last_char_is_space= TRUE;
-
+
while (*name)
{
#if defined(USE_MB) && defined(USE_MB_IDENT)
@@ -2844,7 +2846,7 @@ bool check_column_name(const char *name)
name_length++;
}
/* Error if empty or too long column name */
- return last_char_is_space || (uint) name_length > NAME_CHAR_LEN;
+ return last_char_is_space || (name_length > NAME_CHAR_LEN);
}
@@ -4658,13 +4660,6 @@ void TABLE_LIST::reinit_before_use(THD *thd)
parent_embedding->nested_join->join_list.head() == embedded);
mdl_request.ticket= NULL;
- /*
- Since we manipulate with the metadata lock type in open_table(),
- we need to reset it to the parser default, to restore things back
- to first-execution state.
- */
- mdl_request.set_type((lock_type >= TL_WRITE_ALLOW_WRITE) ?
- MDL_SHARED_WRITE : MDL_SHARED_READ);
}
/*
diff --git a/sql/table.h b/sql/table.h
index 87044ac769b..52fc0f1a7d9 100644
--- a/sql/table.h
+++ b/sql/table.h
@@ -20,6 +20,7 @@
#include "sql_plist.h"
#include "sql_list.h" /* Sql_alloc */
#include "mdl.h"
+#include "datadict.h"
#ifndef MYSQL_CLIENT
@@ -305,14 +306,6 @@ enum tmp_table_type
NO_TMP_TABLE, NON_TRANSACTIONAL_TMP_TABLE, TRANSACTIONAL_TMP_TABLE,
INTERNAL_TMP_TABLE, SYSTEM_TMP_TABLE
};
-
-enum frm_type_enum
-{
- FRMTYPE_ERROR= 0,
- FRMTYPE_TABLE,
- FRMTYPE_VIEW
-};
-
enum release_type { RELEASE_NORMAL, RELEASE_WAIT_FOR_DROP };
typedef struct st_filesort_info
@@ -1599,20 +1592,21 @@ struct TABLE_LIST
OPEN_STUB
} open_strategy;
/**
- Indicates the locking strategy for the object being opened:
- whether the associated metadata lock is shared or exclusive.
+ Indicates the locking strategy for the object being opened.
*/
enum
{
- /* Take a shared metadata lock before the object is opened. */
- SHARED_MDL= 0,
/*
- Take a exclusive metadata lock before the object is opened.
- If opening is successful, downgrade to a shared lock.
+ Take metadata lock specified by 'mdl_request' member before
+ the object is opened. Do nothing after that.
+ */
+ OTLS_NONE= 0,
+ /*
+ Take (exclusive) metadata lock specified by 'mdl_request' member
+ before object is opened. If opening is successful, downgrade to
+ a shared lock.
*/
- EXCLUSIVE_DOWNGRADABLE_MDL,
- /* Take a exclusive metadata lock before the object is opened. */
- EXCLUSIVE_MDL
+ OTLS_DOWNGRADE_IF_EXISTS
} lock_strategy;
/* For transactional locking. */
int lock_timeout; /* NOWAIT or WAIT [X] */
@@ -2038,7 +2032,7 @@ void update_create_info_from_table(HA_CREATE_INFO *info, TABLE *form);
bool check_and_convert_db_name(LEX_STRING *db, bool preserve_lettercase);
bool check_db_name(LEX_STRING *db);
bool check_column_name(const char *name);
-bool check_table_name(const char *name, uint length, bool check_for_path_chars);
+bool check_table_name(const char *name, size_t length, bool check_for_path_chars);
int rename_file_ext(const char * from,const char * to,const char * ext);
char *get_field(MEM_ROOT *mem, Field *field);
bool get_field(MEM_ROOT *mem, Field *field, class String *res);