summaryrefslogtreecommitdiff
path: root/sql/sql_insert.cc
diff options
context:
space:
mode:
authorGeorgi Kodinov <Georgi.Kodinov@Oracle.com>2010-09-20 17:17:32 +0300
committerGeorgi Kodinov <Georgi.Kodinov@Oracle.com>2010-09-20 17:17:32 +0300
commit353739fdef95fb1765dbbadf14ce1a9849a36137 (patch)
tree4b2b7364d1e622b3b02db3fb9d0c898fb9293a87 /sql/sql_insert.cc
parent9fee5866beed47b55c5196acfa1a03503a13a2d7 (diff)
parentf4444c0016c68feb3f300c4b30b886cd407f5824 (diff)
downloadmariadb-git-353739fdef95fb1765dbbadf14ce1a9849a36137.tar.gz
merge of mysql-5.5 into mysql-5.5-wl1054
Diffstat (limited to 'sql/sql_insert.cc')
-rw-r--r--sql/sql_insert.cc171
1 files changed, 105 insertions, 66 deletions
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 85c7b807ea6..7d2a6ad864e 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -548,10 +548,25 @@ bool open_and_lock_for_insert_delayed(THD *thd, TABLE_LIST *table_list)
DBUG_RETURN(TRUE);
}
- if (delayed_get_table(thd, table_list))
+ /*
+ In order for the deadlock detector to be able to find any deadlocks
+ caused by the handler thread locking this table, we take the metadata
+ lock inside the connection thread. If this goes ok, the ticket is cloned
+ and added to the list of granted locks held by the handler thread.
+ */
+ MDL_ticket *mdl_savepoint= thd->mdl_context.mdl_savepoint();
+ if (thd->mdl_context.acquire_lock(&table_list->mdl_request,
+ thd->variables.lock_wait_timeout))
+ /*
+ If a lock can't be acquired, it makes no sense to try normal insert.
+ Therefore we just abort the statement.
+ */
DBUG_RETURN(TRUE);
- if (table_list->table)
+ bool error= FALSE;
+ if (delayed_get_table(thd, table_list))
+ error= TRUE;
+ else if (table_list->table)
{
/*
Open tables used for sub-selects or in stored functions, will also
@@ -560,16 +575,36 @@ bool open_and_lock_for_insert_delayed(THD *thd, TABLE_LIST *table_list)
if (open_and_lock_tables(thd, table_list->next_global, TRUE, 0))
{
end_delayed_insert(thd);
- DBUG_RETURN(TRUE);
+ error= TRUE;
+ }
+ else
+ {
+ /*
+ First table was not processed by open_and_lock_tables(),
+ we need to set updatability flag "by hand".
+ */
+ if (!table_list->derived && !table_list->view)
+ table_list->updatable= 1; // usual table
}
- /*
- First table was not processed by open_and_lock_tables(),
- we need to set updatability flag "by hand".
- */
- if (!table_list->derived && !table_list->view)
- table_list->updatable= 1; // usual table
- DBUG_RETURN(FALSE);
}
+
+ /*
+ If a lock was acquired above, we should release it after
+ handle_delayed_insert() has cloned the ticket. Note that acquire_lock() can
+ succeed because the connection already has the lock. In this case the ticket
+ will be before the mdl_savepoint and we should not release it here.
+ */
+ if (!thd->mdl_context.has_lock(mdl_savepoint, table_list->mdl_request.ticket))
+ thd->mdl_context.release_lock(table_list->mdl_request.ticket);
+
+ /*
+ Reset the ticket in case we end up having to use normal insert and
+ therefore will reopen the table and reacquire the metadata lock.
+ */
+ table_list->mdl_request.ticket= NULL;
+
+ if (error || table_list->table)
+ DBUG_RETURN(error);
#endif
/*
* This is embedded library and we don't have auxiliary
@@ -1801,14 +1836,25 @@ public:
mysql_cond_t cond, cond_client;
volatile uint tables_in_use,stacked_inserts;
volatile bool status;
+ /*
+ When the handler thread starts, it clones a metadata lock ticket
+ for the table to be inserted. This is done to allow the deadlock
+ detector to detect deadlocks resulting from this lock.
+ Before this is done, the connection thread cannot safely exit
+ without causing problems for clone_ticket().
+ Once handler_thread_initialized has been set, it is safe for the
+ connection thread to exit.
+ Access to handler_thread_initialized is protected by di->mutex.
+ */
+ bool handler_thread_initialized;
COPY_INFO info;
I_List<delayed_row> rows;
ulong group_count;
TABLE_LIST table_list; // Argument
Delayed_insert()
- :locks_in_memory(0),
- table(0),tables_in_use(0),stacked_inserts(0), status(0), group_count(0)
+ :locks_in_memory(0), table(0),tables_in_use(0),stacked_inserts(0),
+ status(0), handler_thread_initialized(FALSE), group_count(0)
{
DBUG_ENTER("Delayed_insert constructor");
thd.security_ctx->user=(char*) delayed_user;
@@ -2027,6 +2073,10 @@ bool delayed_get_table(THD *thd, TABLE_LIST *table_list)
/* Replace volatile strings with local copies */
di->table_list.alias= di->table_list.table_name= di->thd.query();
di->table_list.db= di->thd.db;
+ /* We need the ticket so that it can be cloned in handle_delayed_insert */
+ init_mdl_requests(&di->table_list);
+ di->table_list.mdl_request.ticket= table_list->mdl_request.ticket;
+
di->lock();
mysql_mutex_lock(&di->mutex);
if ((error= mysql_thread_create(key_thread_delayed_insert,
@@ -2043,9 +2093,15 @@ bool delayed_get_table(THD *thd, TABLE_LIST *table_list)
goto end_create;
}
- /* Wait until table is open */
+ /*
+ Wait until table is open unless the handler thread or the connection
+ thread has been killed. Note that we in all cases must wait until the
+ handler thread has been properly initialized before exiting. Otherwise
+ we risk doing clone_ticket() on a ticket that is no longer valid.
+ */
thd_proc_info(thd, "waiting for handler open");
- while (!di->thd.killed && !di->table && !thd->killed)
+ while (!di->handler_thread_initialized ||
+ (!di->thd.killed && !di->table && !thd->killed))
{
mysql_cond_wait(&di->cond_client, &di->mutex);
}
@@ -2473,6 +2529,7 @@ pthread_handler_t handle_delayed_insert(void *arg)
/* Can't use my_error since store_globals has not yet been called */
thd->stmt_da->set_error_status(thd, ER_OUT_OF_RESOURCES,
ER(ER_OUT_OF_RESOURCES), NULL);
+ di->handler_thread_initialized= TRUE;
}
else
{
@@ -2483,6 +2540,7 @@ pthread_handler_t handle_delayed_insert(void *arg)
/* Can't use my_error since store_globals has perhaps failed */
thd->stmt_da->set_error_status(thd, ER_OUT_OF_RESOURCES,
ER(ER_OUT_OF_RESOURCES), NULL);
+ di->handler_thread_initialized= TRUE;
thd->fatal_error();
goto err;
}
@@ -2495,7 +2553,24 @@ pthread_handler_t handle_delayed_insert(void *arg)
thd->lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_INSERT_DELAYED);
thd->set_current_stmt_binlog_format_row_if_mixed();
- init_mdl_requests(&di->table_list);
+ /*
+ Clone the ticket representing the lock on the target table for
+ the insert and add it to the list of granted metadata locks held by
+ the handler thread. This is safe since the handler thread is
+ not holding nor waiting on any metadata locks.
+ */
+ if (thd->mdl_context.clone_ticket(&di->table_list.mdl_request))
+ {
+ di->handler_thread_initialized= TRUE;
+ goto err;
+ }
+
+ /*
+ Now that the ticket has been cloned, it is safe for the connection
+ thread to exit.
+ */
+ di->handler_thread_initialized= TRUE;
+ di->table_list.mdl_request.ticket= NULL;
if (di->open_and_lock_table())
goto err;
@@ -2707,7 +2782,7 @@ bool Delayed_insert::handle_inserts(void)
thd_proc_info(&thd, "insert");
max_rows= delayed_insert_limit;
- if (thd.killed || table->s->needs_reopen())
+ if (thd.killed || table->s->has_old_version())
{
thd.killed= THD::KILL_CONNECTION;
max_rows= ULONG_MAX; // Do as much as possible
@@ -3045,6 +3120,9 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
we are fixing fields from insert list.
*/
lex->current_select= &lex->select_lex;
+
+ /* Errors during check_insert_fields() should not be ignored. */
+ lex->current_select->no_error= FALSE;
res= (setup_fields(thd, 0, values, MARK_COLUMNS_READ, 0, 0) ||
check_insert_fields(thd, table_list, *fields, values,
!insert_into_view, 1, &map));
@@ -3578,19 +3656,8 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
if (!mysql_create_table_no_lock(thd, create_table->db,
create_table->table_name,
create_info, alter_info, 0,
- select_field_count))
+ select_field_count, NULL))
{
- if (create_info->table_existed)
- {
- /*
- This means that someone created table underneath server
- or it was created via different mysqld front-end to the
- cluster. We don't have much options but throw an error.
- */
- my_error(ER_TABLE_EXISTS_ERROR, MYF(0), create_table->table_name);
- DBUG_RETURN(0);
- }
-
DBUG_EXECUTE_IF("sleep_create_select_before_open", my_sleep(6000000););
if (!(create_info->options & HA_LEX_CREATE_TMP_TABLE))
@@ -3602,11 +3669,9 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
*/
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,
table_case_name(create_info, create_table->table_name),
0);
- mysql_mutex_unlock(&LOCK_open);
}
else
table= create_table->table;
@@ -3621,7 +3686,7 @@ static TABLE *create_table_from_items(THD *thd, HA_CREATE_INFO *create_info,
it preparable for open. But let us do close_temporary_table() here
just in case.
*/
- drop_temporary_table(thd, create_table);
+ drop_temporary_table(thd, create_table, NULL);
}
else
table= create_table->table;
@@ -3708,15 +3773,13 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
TABLE const *const table = *tables;
if (thd->is_current_stmt_binlog_format_row() &&
- !table->s->tmp_table &&
- !ptr->get_create_info()->table_existed)
+ !table->s->tmp_table)
{
if (int error= ptr->binlog_show_create_table(tables, count))
return error;
}
return 0;
}
-
select_create *ptr;
TABLE_LIST *create_table;
TABLE_LIST *select_tables;
@@ -3739,34 +3802,15 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
thd->binlog_start_trans_and_stmt();
}
+ DBUG_ASSERT(create_table->table == NULL);
+
DBUG_EXECUTE_IF("sleep_create_select_before_check_if_exists", my_sleep(6000000););
- if (create_table->table)
- {
- /* Table already exists and was open at open_and_lock_tables() stage. */
- if (create_info->options & HA_LEX_CREATE_IF_NOT_EXISTS)
- {
- /* Mark that table existed */
- create_info->table_existed= 1;
- push_warning_printf(thd, MYSQL_ERROR::WARN_LEVEL_NOTE,
- ER_TABLE_EXISTS_ERROR, ER(ER_TABLE_EXISTS_ERROR),
- create_table->table_name);
- if (thd->is_current_stmt_binlog_format_row())
- binlog_show_create_table(&(create_table->table), 1);
- table= create_table->table;
- }
- else
- {
- my_error(ER_TABLE_EXISTS_ERROR, MYF(0), create_table->table_name);
- DBUG_RETURN(-1);
- }
- }
- else
- if (!(table= create_table_from_items(thd, create_info, create_table,
- alter_info, &values,
- &extra_lock, hook_ptr)))
- /* abort() deletes table */
- DBUG_RETURN(-1);
+ if (!(table= create_table_from_items(thd, create_info, create_table,
+ alter_info, &values,
+ &extra_lock, hook_ptr)))
+ /* abort() deletes table */
+ DBUG_RETURN(-1);
if (extra_lock)
{
@@ -3886,10 +3930,6 @@ void select_create::send_error(uint errcode,const char *err)
("Current table (at 0x%lu) %s a temporary (or non-existant) table",
(ulong) table,
table && !table->s->tmp_table ? "is NOT" : "is"));
- DBUG_PRINT("info",
- ("Table %s prior to executing this statement",
- get_create_info()->table_existed ? "existed" : "did not exist"));
-
/*
This will execute any rollbacks that are necessary before writing
the transcation cache.
@@ -3978,8 +4018,7 @@ void select_create::abort_result_set()
table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
table->auto_increment_field_not_null= FALSE;
- if (!create_info->table_existed)
- drop_open_table(thd, table, create_table->db, create_table->table_name);
+ drop_open_table(thd, table, create_table->db, create_table->table_name);
table=0; // Safety
}
DBUG_VOID_RETURN;