diff options
Diffstat (limited to 'sql/sql_insert.cc')
-rw-r--r-- | sql/sql_insert.cc | 261 |
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= ©->def_read_set; copy->write_set= ©->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; |