mirror of
https://github.com/MariaDB/server.git
synced 2025-07-27 18:02:13 +03:00
merge of mysql-5.5 into mysql-5.5-wl1054
This commit is contained in:
@ -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;
|
||||
|
Reference in New Issue
Block a user