summaryrefslogtreecommitdiff
path: root/sql/sql_insert.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_insert.cc')
-rw-r--r--sql/sql_insert.cc261
1 files changed, 168 insertions, 93 deletions
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc
index 65af14b62f6..13943ce8d3c 100644
--- a/sql/sql_insert.cc
+++ b/sql/sql_insert.cc
@@ -1,6 +1,6 @@
/*
- Copyright (c) 2000, 2015, Oracle and/or its affiliates.
- Copyright (c) 2010, 2015, MariaDB
+ Copyright (c) 2000, 2016, Oracle and/or its affiliates.
+ Copyright (c) 2010, 2016, MariaDB
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
@@ -282,12 +282,7 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list,
my_error(ER_FIELD_SPECIFIED_TWICE, MYF(0), thd->dup_field->field_name);
DBUG_RETURN(-1);
}
- if (table->default_field)
- table->mark_default_fields_for_write();
}
- /* Mark virtual columns used in the insert statement */
- if (table->vfield)
- table->mark_virtual_columns_for_write(TRUE);
// For the values we need select_priv
#ifndef NO_EMBEDDED_ACCESS_CHECKS
table->grant.want_privilege= (SELECT_ACL & ~table->grant.privilege);
@@ -359,7 +354,7 @@ static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list,
return -1;
if (table->default_field)
- table->mark_default_fields_for_write();
+ table->mark_default_fields_for_write(FALSE);
if (table->found_next_number_field)
{
@@ -801,6 +796,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
info.update_fields= &update_fields;
info.update_values= &update_values;
info.view= (table_list->view ? table_list : 0);
+ info.table_list= table_list;
/*
Count warnings for all inserts.
@@ -864,15 +860,14 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
thd->abort_on_warning= !ignore && thd->is_strict_mode();
+ table->reset_default_fields();
table->prepare_triggers_for_insert_stmt_or_event();
table->mark_columns_needed_for_insert();
-
if (table_list->prepare_where(thd, 0, TRUE) ||
table_list->prepare_check_option(thd))
error= 1;
- table->reset_default_fields();
switch_to_nullable_trigger_fields(fields, table);
switch_to_nullable_trigger_fields(update_fields, table);
switch_to_nullable_trigger_fields(update_values, table);
@@ -901,6 +896,7 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
INSERT INTO t1 VALUES ()
*/
restore_record(table,s->default_values); // Get empty record
+ table->reset_default_fields();
if (fill_record_n_invoke_before_triggers(thd, table, fields, *values, 0,
TRG_EVENT_INSERT))
{
@@ -961,11 +957,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list,
break;
}
}
- if (table->default_field && table->update_default_fields())
- {
- error= 1;
- break;
- }
if ((res= table_list->view_check_option(thd,
(values_list.elements == 1 ?
@@ -1504,18 +1495,8 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list,
if (!table)
table= table_list->table;
-
- if (!fields.elements && table->vfield)
- {
- for (Field **vfield_ptr= table->vfield; *vfield_ptr; vfield_ptr++)
- {
- if ((*vfield_ptr)->vcol_info->stored_in_db)
- {
- thd->lex->unit.insert_table_with_stored_vcol= table;
- break;
- }
- }
- }
+ if (table->s->virtual_stored_fields)
+ thd->lex->unit.insert_table_with_stored_vcol= table;
if (!select_insert)
{
@@ -1626,9 +1607,10 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
else
table->file->insert_id_for_cur_row= insert_id_for_cur_row;
bool is_duplicate_key_error;
- if (table->file->is_fatal_error(error, HA_CHECK_DUP))
+ if (table->file->is_fatal_error(error, HA_CHECK_ALL))
goto err;
- is_duplicate_key_error= table->file->is_fatal_error(error, 0);
+ is_duplicate_key_error=
+ table->file->is_fatal_error(error, HA_CHECK_ALL & ~HA_CHECK_DUP);
if (!is_duplicate_key_error)
{
/*
@@ -1702,16 +1684,17 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
DBUG_ASSERT(table->insert_values != NULL);
store_record(table,insert_values);
restore_record(table,record[1]);
+ table->reset_default_fields();
/*
in INSERT ... ON DUPLICATE KEY UPDATE the set of modified fields can
change per row. Thus, we have to do reset_default_fields() per row.
Twice (before insert and before update).
*/
- table->reset_default_fields();
DBUG_ASSERT(info->update_fields->elements ==
info->update_values->elements);
- if (fill_record_n_invoke_before_triggers(thd, table, *info->update_fields,
+ if (fill_record_n_invoke_before_triggers(thd, table,
+ *info->update_fields,
*info->update_values,
info->ignore,
TRG_EVENT_UPDATE))
@@ -1728,20 +1711,13 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
*/
if (different_records && table->default_field)
{
- bool res;
- enum_sql_command cmd= thd->lex->sql_command;
- thd->lex->sql_command= SQLCOM_UPDATE;
- res= table->update_default_fields();
- thd->lex->sql_command= cmd;
- if (res)
+ if (table->update_default_fields(1, info->ignore))
goto err;
}
- table->reset_default_fields();
/* CHECK OPTION for VIEW ... ON DUPLICATE KEY UPDATE ... */
- if (info->view &&
- (res= info->view->view_check_option(current_thd, info->ignore)) ==
- VIEW_CHECK_SKIP)
+ res= info->table_list->view_check_option(table->in_use, info->ignore);
+ if (res == VIEW_CHECK_SKIP)
goto ok_or_after_trg_err;
if (res == VIEW_CHECK_ERROR)
goto before_trg_err;
@@ -1755,7 +1731,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
error != HA_ERR_RECORD_IS_THE_SAME)
{
if (info->ignore &&
- !table->file->is_fatal_error(error, HA_CHECK_DUP_KEY))
+ !table->file->is_fatal_error(error, HA_CHECK_ALL))
{
if (!(thd->variables.old_behavior &
OLD_MODE_NO_DUP_KEY_WARNINGS_WITH_IGNORE))
@@ -1885,7 +1861,7 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info)
{
DEBUG_SYNC(thd, "write_row_noreplace");
if (!info->ignore ||
- table->file->is_fatal_error(error, HA_CHECK_DUP))
+ table->file->is_fatal_error(error, HA_CHECK_ALL))
goto err;
if (!(thd->variables.old_behavior &
OLD_MODE_NO_DUP_KEY_WARNINGS_WITH_IGNORE))
@@ -2015,7 +1991,7 @@ public:
TABLE *table;
mysql_mutex_t mutex;
mysql_cond_t cond, cond_client;
- volatile uint tables_in_use,stacked_inserts;
+ uint tables_in_use, stacked_inserts;
volatile bool status;
bool retry;
/**
@@ -2041,7 +2017,8 @@ public:
MDL_request grl_protection;
Delayed_insert(SELECT_LEX *current_select)
- :locks_in_memory(0), table(0),tables_in_use(0),stacked_inserts(0),
+ :locks_in_memory(0), thd(next_thread_id()),
+ table(0),tables_in_use(0), stacked_inserts(0),
status(0), retry(0), handler_thread_initialized(FALSE), group_count(0)
{
DBUG_ENTER("Delayed_insert constructor");
@@ -2072,7 +2049,6 @@ public:
delayed_lock= global_system_variables.low_priority_updates ?
TL_WRITE_LOW_PRIORITY : TL_WRITE;
mysql_mutex_unlock(&LOCK_thread_count);
- thread_safe_increment32(&thread_count);
DBUG_VOID_RETURN;
}
~Delayed_insert()
@@ -2103,7 +2079,6 @@ public:
my_free(thd.query());
thd.security_ctx->user= 0;
thd.security_ctx->host= 0;
- dec_thread_count();
}
/* The following is for checking when we can delete ourselves */
@@ -2362,11 +2337,12 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
{
my_ptrdiff_t adjust_ptrs;
Field **field,**org_field, *found_next_number_field;
- Field **UNINIT_VAR(vfield), **UNINIT_VAR(dfield_ptr);
+ Field **vfield= 0, **dfield_ptr= 0;
TABLE *copy;
TABLE_SHARE *share;
uchar *bitmap;
char *copy_tmp;
+ uint bitmaps_used;
DBUG_ENTER("Delayed_insert::get_local_table");
/* First request insert thread to get a lock */
@@ -2420,13 +2396,14 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
copy_tmp= (char*) client_thd->alloc(sizeof(*copy)+
(share->fields+1)*sizeof(Field**)+
share->reclength +
- share->column_bitmap_size*3);
+ share->column_bitmap_size*4);
if (!copy_tmp)
goto error;
- if (share->vfields)
+ if (share->virtual_fields)
{
- vfield= (Field **) client_thd->alloc((share->vfields+1)*sizeof(Field*));
+ vfield= (Field **) client_thd->alloc((share->virtual_fields+1)*
+ sizeof(Field*));
if (!vfield)
goto error;
}
@@ -2434,16 +2411,19 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
/* Copy the TABLE object. */
copy= new (copy_tmp) TABLE;
*copy= *table;
+
/* We don't need to change the file handler here */
/* Assign the pointers for the field pointers array and the record. */
field= copy->field= (Field**) (copy + 1);
bitmap= (uchar*) (field + share->fields + 1);
- copy->record[0]= (bitmap + share->column_bitmap_size*3);
+ copy->record[0]= (bitmap + share->column_bitmap_size*4);
memcpy((char*) copy->record[0], (char*) table->record[0], share->reclength);
- if (share->default_fields)
+ if (share->default_fields || share->default_expressions)
{
- copy->default_field= (Field**) client_thd->alloc((share->default_fields+1)*
- sizeof(Field**));
+ copy->default_field= (Field**)
+ client_thd->alloc((share->default_fields +
+ share->default_expressions + 1)*
+ sizeof(Field*));
if (!copy->default_field)
goto error;
dfield_ptr= copy->default_field;
@@ -2466,48 +2446,60 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
if (!(*field= (*org_field)->make_new_field(client_thd->mem_root, copy,
1)))
goto error;
+ (*field)->unireg_check= (*org_field)->unireg_check;
(*field)->orig_table= copy; // Remove connection
(*field)->move_field_offset(adjust_ptrs); // Point at copy->record[0]
if (*org_field == found_next_number_field)
(*field)->table->found_next_number_field= *field;
- if (share->default_fields &&
- ((*org_field)->has_insert_default_function() ||
- (*org_field)->has_update_default_function()))
- {
- /* Put the newly copied field into the set of default fields. */
- *dfield_ptr= *field;
- (*dfield_ptr)->unireg_check= (*org_field)->unireg_check;
- dfield_ptr++;
- }
}
*field=0;
- if (share->vfields)
+ if (share->virtual_fields || share->default_expressions ||
+ share->default_fields)
{
+ bool error_reported= FALSE;
if (!(copy->def_vcol_set= (MY_BITMAP*) alloc_root(client_thd->mem_root,
sizeof(MY_BITMAP))))
goto error;
copy->vfield= vfield;
for (field= copy->field; *field; field++)
{
+ Virtual_column_info *vcol;
if ((*field)->vcol_info)
{
- bool error_reported= FALSE;
- if (unpack_vcol_info_from_frm(client_thd,
- client_thd->mem_root,
- copy,
- *field,
- &(*field)->vcol_info->expr_str,
- &error_reported))
+ if (!(vcol= unpack_vcol_info_from_frm(client_thd,
+ client_thd->mem_root,
+ copy,
+ 0,
+ (*field)->vcol_info,
+ &error_reported)))
goto error;
+ (*field)->vcol_info= vcol;
*vfield++= *field;
}
+ if ((*field)->default_value)
+ {
+ if (!(vcol= unpack_vcol_info_from_frm(client_thd,
+ client_thd->mem_root,
+ copy,
+ 0,
+ (*field)->default_value,
+ &error_reported)))
+ goto error;
+ (*field)->default_value= vcol;
+ *dfield_ptr++= *field;
+ }
+ else
+ if ((*field)->has_update_default_function())
+ *dfield_ptr++= *field;
}
- *vfield= 0;
+ if (vfield)
+ *vfield= 0;
+ if (dfield_ptr)
+ *dfield_ptr= 0;
}
- if (share->default_fields)
- *dfield_ptr= NULL;
+ switch_defaults_to_nullable_trigger_fields(copy);
/* Adjust in_use for pointing to client thread */
copy->in_use= client_thd;
@@ -2519,15 +2511,28 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd)
copy->def_read_set.bitmap= (my_bitmap_map*) bitmap;
copy->def_write_set.bitmap= ((my_bitmap_map*)
(bitmap + share->column_bitmap_size));
- if (share->vfields)
+ bitmaps_used= 2;
+ if (share->virtual_fields)
{
my_bitmap_init(copy->def_vcol_set,
- (my_bitmap_map*) (bitmap + 2*share->column_bitmap_size),
+ (my_bitmap_map*) (bitmap +
+ bitmaps_used*share->column_bitmap_size),
share->fields, FALSE);
+ bitmaps_used++;
copy->vcol_set= copy->def_vcol_set;
}
+ if (share->default_fields)
+ {
+ if (!(copy->has_value_set= (MY_BITMAP*) alloc_root(client_thd->mem_root,
+ sizeof(MY_BITMAP))))
+ goto error;
+ my_bitmap_init(copy->has_value_set,
+ (my_bitmap_map*) (bitmap +
+ bitmaps_used*share->column_bitmap_size),
+ share->fields, FALSE);
+ }
copy->tmp_set.bitmap= 0; // To catch errors
- bzero((char*) bitmap, share->column_bitmap_size * (share->vfields ? 3 : 2));
+ bzero((char*) bitmap, share->column_bitmap_size * bitmaps_used);
copy->read_set= &copy->def_read_set;
copy->write_set= &copy->def_write_set;
@@ -2672,14 +2677,16 @@ static void end_delayed_insert(THD *thd)
void kill_delayed_threads(void)
{
+ DBUG_ENTER("kill_delayed_threads");
mysql_mutex_lock(&LOCK_delayed_insert); // For unlink from list
I_List_iterator<Delayed_insert> it(delayed_threads);
Delayed_insert *di;
while ((di= it++))
{
- di->thd.killed= KILL_CONNECTION;
mysql_mutex_lock(&di->thd.LOCK_thd_data);
+ if (di->thd.killed < KILL_CONNECTION)
+ di->thd.killed= KILL_CONNECTION;
if (di->thd.mysys_var)
{
mysql_mutex_lock(&di->thd.mysys_var->mutex);
@@ -2700,6 +2707,7 @@ void kill_delayed_threads(void)
mysql_mutex_unlock(&di->thd.LOCK_thd_data);
}
mysql_mutex_unlock(&LOCK_delayed_insert); // For unlink from list
+ DBUG_VOID_RETURN;
}
@@ -2794,11 +2802,11 @@ bool Delayed_insert::open_and_lock_table()
return TRUE;
}
- if (table->triggers)
+ if (table->triggers || table->check_constraints)
{
/*
- Table has triggers. This is not an error, but we do
- not support triggers with delayed insert. Terminate the delayed
+ Table has triggers or check constraints. This is not an error, but we do
+ not support these with delayed insert. Terminate the delayed
thread without an error and thus request lock upgrade.
*/
return TRUE;
@@ -2819,7 +2827,6 @@ pthread_handler_t handle_delayed_insert(void *arg)
pthread_detach_this_thread();
/* Add thread to THD list so that's it's visible in 'show processlist' */
- thd->thread_id= thd->variables.pseudo_thread_id= next_thread_id();
thd->set_current_time();
add_to_active_threads(thd);
if (abort_loop)
@@ -2902,6 +2909,12 @@ pthread_handler_t handle_delayed_insert(void *arg)
/* Tell client that the thread is initialized */
mysql_cond_signal(&di->cond_client);
+ /*
+ Inform mdl that it needs to call mysql_lock_abort to abort locks
+ for delayed insert.
+ */
+ thd->mdl_context.set_needs_thr_lock_abort(TRUE);
+
di->table->mark_columns_needed_for_insert();
/* Now wait until we get an insert or lock to handle */
@@ -2912,6 +2925,7 @@ pthread_handler_t handle_delayed_insert(void *arg)
if (thd->killed)
{
uint lock_count;
+ DBUG_PRINT("delayed", ("Insert delayed killed"));
/*
Remove this from delay insert list so that no one can request a
table from this
@@ -2922,11 +2936,15 @@ pthread_handler_t handle_delayed_insert(void *arg)
lock_count=di->lock_count();
mysql_mutex_unlock(&LOCK_delayed_insert);
mysql_mutex_lock(&di->mutex);
- if (!lock_count && !di->tables_in_use && !di->stacked_inserts)
+ if (!lock_count && !di->tables_in_use && !di->stacked_inserts &&
+ !thd->lock)
break; // Time to die
}
/* Shouldn't wait if killed or an insert is waiting. */
+ DBUG_PRINT("delayed",
+ ("thd->killed: %d di->status: %d di->stacked_inserts: %d",
+ thd->killed, di->status, di->stacked_inserts));
if (!thd->killed && !di->status && !di->stacked_inserts)
{
struct timespec abstime;
@@ -2966,6 +2984,9 @@ pthread_handler_t handle_delayed_insert(void *arg)
mysql_mutex_unlock(&di->thd.mysys_var->mutex);
mysql_mutex_lock(&di->mutex);
}
+ DBUG_PRINT("delayed",
+ ("thd->killed: %d di->tables_in_use: %d thd->lock: %d",
+ thd->killed, di->tables_in_use, thd->lock != 0));
if (di->tables_in_use && ! thd->lock && !thd->killed)
{
@@ -3026,9 +3047,19 @@ pthread_handler_t handle_delayed_insert(void *arg)
{
DBUG_ENTER("handle_delayed_insert-cleanup");
di->table=0;
- thd->killed= KILL_CONNECTION; // If error
mysql_mutex_unlock(&di->mutex);
+ /*
+ Protect against mdl_locks trying to access open tables
+ We use KILL_CONNECTION_HARD here to ensure that
+ THD::notify_shared_lock() dosn't try to access open tables after
+ this.
+ */
+ mysql_mutex_lock(&thd->LOCK_thd_data);
+ thd->killed= KILL_CONNECTION_HARD; // If error
+ thd->mdl_context.set_needs_thr_lock_abort(0);
+ mysql_mutex_unlock(&thd->LOCK_thd_data);
+
close_thread_tables(thd); // Free the table
thd->mdl_context.release_transactional_locks();
mysql_cond_broadcast(&di->cond_client); // Safety
@@ -3427,8 +3458,8 @@ select_insert::select_insert(THD *thd_arg, TABLE_LIST *table_list_par,
info.ignore= ignore_check_option_errors;
info.update_fields= update_fields;
info.update_values= update_values;
- if (table_list_par)
- info.view= (table_list_par->view ? table_list_par : 0);
+ info.view= (table_list_par->view ? table_list_par : 0);
+ info.table_list= table_list_par;
}
@@ -3659,7 +3690,7 @@ int select_insert::send_data(List<Item> &values)
thd->count_cuted_fields= CHECK_FIELD_WARN; // Calculate cuted fields
store_values(values);
- if (table->default_field && table->update_default_fields())
+ if (table->default_field && table->update_default_fields(0, info.ignore))
DBUG_RETURN(1);
thd->count_cuted_fields= CHECK_FIELD_ERROR_FOR_NULL;
if (thd->is_error())
@@ -3903,10 +3934,10 @@ void select_insert::abort_result_set() {
CREATE TABLE (SELECT) ...
***************************************************************************/
-Field *Item::create_field_for_create_select(THD *thd, TABLE *table)
+Field *Item::create_field_for_create_select(TABLE *table)
{
Field *def_field, *tmp_field;
- return ::create_tmp_field(thd, table, this, type(),
+ return ::create_tmp_field(table->in_use, table, this, type(),
(Item ***) 0, &tmp_field, &def_field, 0, 0, 0, 0);
}
@@ -3981,7 +4012,7 @@ static TABLE *create_table_from_items(THD *thd,
while ((item=it++))
{
- Field *tmp_field= item->create_field_for_create_select(thd, &tmp_table);
+ Field *tmp_field= item->create_field_for_create_select(&tmp_table);
if (!tmp_field)
DBUG_RETURN(NULL);
@@ -4080,7 +4111,12 @@ static TABLE *create_table_from_items(THD *thd,
}
else
{
- if (open_temporary_table(thd, create_table))
+ /*
+ The pointer to the newly created temporary table has been stored in
+ table->create_info.
+ */
+ create_table->table= create_info->table;
+ if (!create_table->table)
{
/*
This shouldn't happen as creation of temporary table should make
@@ -4089,7 +4125,6 @@ static TABLE *create_table_from_items(THD *thd,
*/
DBUG_ASSERT(0);
}
- DBUG_ASSERT(create_table->table == create_info->table);
}
}
else
@@ -4224,6 +4259,18 @@ select_create::prepare(List<Item> &values, SELECT_LEX_UNIT *u)
/* abort() deletes table */
DBUG_RETURN(-1);
+ if (create_info->tmp_table())
+ {
+ /*
+ When the temporary table was created & opened in create_table_impl(),
+ the table's TABLE_SHARE (and thus TABLE) object was also linked to THD
+ temporary tables lists. So, we must temporarily remove it from the
+ list to keep them inaccessible from inner statements.
+ e.g. CREATE TEMPORARY TABLE `t1` AS SELECT * FROM `t1`;
+ */
+ saved_tmp_table_share= thd->save_tmp_table_share(create_table->table);
+ }
+
if (extra_lock)
{
DBUG_ASSERT(m_plock == NULL);
@@ -4339,6 +4386,27 @@ bool select_create::send_eof()
DBUG_RETURN(true);
}
+ if (table->s->tmp_table)
+ {
+ /*
+ Now is good time to add the new table to THD temporary tables list.
+ But, before that we need to check if same table got created by the sub-
+ statement.
+ */
+ if (thd->find_tmp_table_share(table->s->table_cache_key.str,
+ table->s->table_cache_key.length))
+ {
+ my_error(ER_TABLE_EXISTS_ERROR, MYF(0), table->alias.c_ptr());
+ abort_result_set();
+ DBUG_RETURN(true);
+ }
+ else
+ {
+ DBUG_ASSERT(saved_tmp_table_share);
+ thd->restore_tmp_table_share(saved_tmp_table_share);
+ }
+ }
+
/*
Do an implicit commit at end of statement for non-temporary
tables. This can fail, but we should unlock the table
@@ -4463,6 +4531,13 @@ void select_create::abort_result_set()
if (table)
{
bool tmp_table= table->s->tmp_table;
+
+ if (tmp_table)
+ {
+ DBUG_ASSERT(saved_tmp_table_share);
+ thd->restore_tmp_table_share(saved_tmp_table_share);
+ }
+
table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY);
table->file->extra(HA_EXTRA_WRITE_CANNOT_REPLACE);
table->auto_increment_field_not_null= FALSE;