diff options
author | Georgi Kodinov <Georgi.Kodinov@Oracle.com> | 2010-09-20 17:17:32 +0300 |
---|---|---|
committer | Georgi Kodinov <Georgi.Kodinov@Oracle.com> | 2010-09-20 17:17:32 +0300 |
commit | 353739fdef95fb1765dbbadf14ce1a9849a36137 (patch) | |
tree | 4b2b7364d1e622b3b02db3fb9d0c898fb9293a87 /sql/sql_insert.cc | |
parent | 9fee5866beed47b55c5196acfa1a03503a13a2d7 (diff) | |
parent | f4444c0016c68feb3f300c4b30b886cd407f5824 (diff) | |
download | mariadb-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.cc | 171 |
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; |