diff options
Diffstat (limited to 'sql/sql_insert.cc')
-rw-r--r-- | sql/sql_insert.cc | 535 |
1 files changed, 313 insertions, 222 deletions
diff --git a/sql/sql_insert.cc b/sql/sql_insert.cc index 1745c6b4aaa..c623336fdba 100644 --- a/sql/sql_insert.cc +++ b/sql/sql_insert.cc @@ -77,6 +77,8 @@ #include "transaction.h" #include "sql_audit.h" #include "sql_derived.h" // mysql_handle_derived +#include "sql_prepare.h" +#include <my_bit.h> #include "debug_sync.h" @@ -87,7 +89,7 @@ static int write_delayed(THD *thd, TABLE *table, enum_duplicates duplic, LEX_STRING query, bool ignore, bool log_on); static void end_delayed_insert(THD *thd); pthread_handler_t handle_delayed_insert(void *arg); -static void unlink_blobs(register TABLE *table); +static void unlink_blobs(TABLE *table); #endif static bool check_view_insertability(THD *thd, TABLE_LIST *view); @@ -126,6 +128,14 @@ static bool check_view_single_update(List<Item> &fields, List<Item> *values, while ((item= it++)) tables|= item->used_tables(); + /* + Check that table is only one + (we can not rely on check_single_table because it skips some + types of tables) + */ + if (my_count_bits(tables) > 1) + goto error; + if (values) { it.init(*values); @@ -258,7 +268,8 @@ static int check_insert_fields(THD *thd, TABLE_LIST *table_list, if (table_list->is_view()) unfix_fields(fields); - res= setup_fields(thd, 0, fields, MARK_COLUMNS_WRITE, 0, NULL, 0); + res= setup_fields(thd, Ref_ptr_array(), + fields, MARK_COLUMNS_WRITE, 0, NULL, 0); /* Restore the current context. */ ctx_state.restore_state(context, table_list); @@ -282,12 +293,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); @@ -372,7 +378,8 @@ static int check_update_fields(THD *thd, TABLE_LIST *insert_table_list, } /* Check the fields we are going to modify */ - if (setup_fields(thd, 0, update_fields, MARK_COLUMNS_WRITE, 0, NULL, 0)) + if (setup_fields(thd, Ref_ptr_array(), + update_fields, MARK_COLUMNS_WRITE, 0, NULL, 0)) return -1; if (insert_table_list->is_view() && @@ -384,7 +391,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) { @@ -691,6 +698,8 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, bool using_bulk_insert= 0; uint value_count; ulong counter = 1; + /* counter of iteration in bulk PS operation*/ + ulonglong iteration= 0; ulonglong id; COPY_INFO info; TABLE *table= 0; @@ -755,6 +764,8 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, THD_STAGE_INFO(thd, stage_init); thd->lex->used_tables=0; values= its++; + if (bulk_parameters_set(thd)) + DBUG_RETURN(TRUE); value_count= values->elements; if (mysql_prepare_insert(thd, table_list, table, fields, values, @@ -794,7 +805,8 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, my_error(ER_WRONG_VALUE_COUNT_ON_ROW, MYF(0), counter); goto abort; } - if (setup_fields(thd, 0, *values, MARK_COLUMNS_READ, 0, NULL, 0)) + if (setup_fields(thd, Ref_ptr_array(), + *values, MARK_COLUMNS_READ, 0, NULL, 0)) goto abort; switch_to_nullable_trigger_fields(*values, table); } @@ -823,6 +835,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. @@ -886,6 +899,7 @@ 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(); @@ -907,7 +921,6 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, 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); @@ -925,122 +938,130 @@ bool mysql_insert(THD *thd,TABLE_LIST *table_list, goto values_loop_end; } } - - while ((values= its++)) + do { - if (fields.elements || !value_count) + DBUG_PRINT("info", ("iteration %llu", iteration)); + if (iteration && bulk_parameters_set(thd)) + goto abort; + + while ((values= its++)) { - /* - There are possibly some default values: - INSERT INTO t1 (fields) VALUES ... - INSERT INTO t1 VALUES () - */ - restore_record(table,s->default_values); // Get empty record - if (fill_record_n_invoke_before_triggers(thd, table, fields, *values, 0, - TRG_EVENT_INSERT)) + if (fields.elements || !value_count) { - if (values_list.elements != 1 && ! thd->is_error()) - { - info.records++; - continue; - } - /* - TODO: set thd->abort_on_warning if values_list.elements == 1 - and check that all items return warning in case of problem with - storing field. + /* + There are possibly some default values: + INSERT INTO t1 (fields) VALUES ... + INSERT INTO t1 VALUES () */ - error=1; - break; + 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)) + { + if (values_list.elements != 1 && ! thd->is_error()) + { + info.records++; + continue; + } + /* + TODO: set thd->abort_on_warning if values_list.elements == 1 + and check that all items return warning in case of problem with + storing field. + */ + error=1; + break; + } } - } - else - { - /* - No field list, all fields are set explicitly: - INSERT INTO t1 VALUES (values) - */ - if (thd->lex->used_tables) // Column used in values() - restore_record(table,s->default_values); // Get empty record else { - TABLE_SHARE *share= table->s; - /* - Fix delete marker. No need to restore rest of record since it will - be overwritten by fill_record() anyway (and fill_record() does not - use default values in this case). + No field list, all fields are set explicitly: + INSERT INTO t1 VALUES (values) */ - table->record[0][0]= share->default_values[0]; + if (thd->lex->used_tables) // Column used in values() + restore_record(table,s->default_values); // Get empty record + else + { + TABLE_SHARE *share= table->s; - /* Fix undefined null_bits. */ - if (share->null_bytes > 1 && share->last_null_bit_pos) + /* + Fix delete marker. No need to restore rest of record since it will + be overwritten by fill_record() anyway (and fill_record() does not + use default values in this case). + */ + table->record[0][0]= share->default_values[0]; + + /* Fix undefined null_bits. */ + if (share->null_bytes > 1 && share->last_null_bit_pos) + { + table->record[0][share->null_bytes - 1]= + share->default_values[share->null_bytes - 1]; + } + } + table->reset_default_fields(); + if (fill_record_n_invoke_before_triggers(thd, table, + table->field_to_fill(), + *values, 0, TRG_EVENT_INSERT)) { - table->record[0][share->null_bytes - 1]= - share->default_values[share->null_bytes - 1]; + if (values_list.elements != 1 && ! thd->is_error()) + { + info.records++; + continue; + } + error=1; + break; } } - if (fill_record_n_invoke_before_triggers(thd, table, table->field_to_fill(), - *values, 0, TRG_EVENT_INSERT)) - { - if (values_list.elements != 1 && ! thd->is_error()) - { - info.records++; - continue; - } - error=1; - break; - } - } - if (table->default_field && table->update_default_fields()) - { - error= 1; - break; - } - /* - with triggers a field can get a value *conditionally*, so we have to repeat - has_no_default_value() check for every row - */ - if (table->triggers && - table->triggers->has_triggers(TRG_EVENT_INSERT, TRG_ACTION_BEFORE)) - { - for (Field **f=table->field ; *f ; f++) + + /* + with triggers a field can get a value *conditionally*, so we have to repeat + has_no_default_value() check for every row + */ + if (table->triggers && + table->triggers->has_triggers(TRG_EVENT_INSERT, TRG_ACTION_BEFORE)) { - if (!((*f)->flags & HAS_EXPLICIT_VALUE) && has_no_default_value(thd, *f, table_list)) + for (Field **f=table->field ; *f ; f++) { - error= 1; - goto values_loop_end; + if (!(*f)->has_explicit_value() && + has_no_default_value(thd, *f, table_list)) + { + error= 1; + goto values_loop_end; + } } - (*f)->flags &= ~HAS_EXPLICIT_VALUE; } - } - if ((res= table_list->view_check_option(thd, - (values_list.elements == 1 ? - 0 : - ignore))) == - VIEW_CHECK_SKIP) - continue; - else if (res == VIEW_CHECK_ERROR) - { - error= 1; - break; - } + if ((res= table_list->view_check_option(thd, + (values_list.elements == 1 ? + 0 : + ignore))) == + VIEW_CHECK_SKIP) + continue; + else if (res == VIEW_CHECK_ERROR) + { + error= 1; + break; + } + #ifndef EMBEDDED_LIBRARY - if (lock_type == TL_WRITE_DELAYED) - { - LEX_STRING const st_query = { query, thd->query_length() }; - DEBUG_SYNC(thd, "before_write_delayed"); - error=write_delayed(thd, table, duplic, st_query, ignore, log_on); - DEBUG_SYNC(thd, "after_write_delayed"); - query=0; - } - else + if (lock_type == TL_WRITE_DELAYED) + { + LEX_STRING const st_query = { query, thd->query_length() }; + DEBUG_SYNC(thd, "before_write_delayed"); + error=write_delayed(thd, table, duplic, st_query, ignore, log_on); + DEBUG_SYNC(thd, "after_write_delayed"); + query=0; + } + else #endif - error=write_record(thd, table ,&info); - if (error) - break; - thd->get_stmt_da()->inc_current_row_for_warning(); - } + error=write_record(thd, table ,&info); + if (error) + break; + thd->get_stmt_da()->inc_current_row_for_warning(); + } + its.rewind(); + iteration++; + } while (bulk_parameters_iterations(thd)); values_loop_end: free_underlaid_joins(thd, &thd->lex->select_lex); @@ -1187,7 +1208,7 @@ values_loop_end: retval= thd->lex->explain->send_explain(thd); goto abort; } - if (values_list.elements == 1 && (!(thd->variables.option_bits & OPTION_WARNINGS) || + if ((iteration * values_list.elements) == 1 && (!(thd->variables.option_bits & OPTION_WARNINGS) || !thd->cuted_fields)) { my_ok(thd, info.copied + info.deleted + @@ -1451,8 +1472,8 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, bool res= 0; table_map map= 0; DBUG_ENTER("mysql_prepare_insert"); - DBUG_PRINT("enter", ("table_list: 0x%lx table: 0x%lx view: %d", - (ulong)table_list, (ulong)table, + DBUG_PRINT("enter", ("table_list: %p table: %p view: %d", + table_list, table, (int)insert_into_view)); /* INSERT should have a SELECT or VALUES clause */ DBUG_ASSERT (!select_insert || !values); @@ -1509,12 +1530,14 @@ bool mysql_prepare_insert(THD *thd, TABLE_LIST *table_list, table_list->next_local= 0; context->resolve_in_table_list_only(table_list); - res= (setup_fields(thd, 0, *values, MARK_COLUMNS_READ, 0, NULL, 0) || + res= (setup_fields(thd, Ref_ptr_array(), + *values, MARK_COLUMNS_READ, 0, NULL, 0) || check_insert_fields(thd, context->table_list, fields, *values, !insert_into_view, 0, &map)); if (!res) - res= setup_fields(thd, 0, update_values, MARK_COLUMNS_READ, 0, NULL, 0); + res= setup_fields(thd, Ref_ptr_array(), + update_values, MARK_COLUMNS_READ, 0, NULL, 0); if (!res && duplic == DUP_UPDATE) { @@ -1534,18 +1557,6 @@ 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)->stored_in_db) - { - thd->lex->unit.insert_table_with_stored_vcol= table; - break; - } - } - } - if (!select_insert) { Item *fake_conds= 0; @@ -1721,6 +1732,16 @@ int write_record(THD *thd, TABLE *table,COPY_INFO *info) HA_READ_KEY_EXACT)))) goto err; } + if (table->vfield) + { + /* + We have not yet called update_virtual_fields(VOL_UPDATE_FOR_READ) + in handler methods for the just read row in record[1]. + */ + table->move_fields(table->field, table->record[1], table->record[0]); + table->update_virtual_fields(table->file, VCOL_UPDATE_FOR_REPLACE); + table->move_fields(table->field, table->record[0], table->record[1]); + } if (info->handle_duplicates == DUP_UPDATE) { int res= 0; @@ -1732,16 +1753,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)) @@ -1758,20 +1780,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; @@ -1981,7 +1996,7 @@ public: enum_duplicates dup; my_time_t start_time; ulong start_time_sec_part; - ulonglong sql_mode; + sql_mode_t sql_mode; bool auto_increment_field_not_null; bool query_start_used, ignore, log_query, query_start_sec_part_used; bool stmt_depends_on_first_successful_insert_id_in_prev_stmt; @@ -2045,7 +2060,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"); @@ -2089,17 +2105,23 @@ public: close_thread_tables(&thd); thd.mdl_context.release_transactional_locks(); } - mysql_mutex_lock(&LOCK_thread_count); mysql_mutex_destroy(&mutex); mysql_cond_destroy(&cond); mysql_cond_destroy(&cond_client); + + /* + We could use unlink_not_visible_threads() here, but as + delayed_insert_threads also needs to be protected by + the LOCK_thread_count mutex, we open code this. + */ + mysql_mutex_lock(&LOCK_thread_count); thd.unlink(); // Must be unlinked under lock - my_free(thd.query()); - thd.security_ctx->user= thd.security_ctx->host=0; delayed_insert_threads--; mysql_mutex_unlock(&LOCK_thread_count); - thread_safe_decrement32(&thread_count); - mysql_cond_broadcast(&COND_thread_count); /* Tell main we are ready */ + + my_free(thd.query()); + thd.security_ctx->user= 0; + thd.security_ctx->host= 0; } /* The following is for checking when we can delete ourselves */ @@ -2234,8 +2256,6 @@ bool delayed_get_table(THD *thd, MDL_request *grl_protection_request, if (!(di= new Delayed_insert(thd->lex->current_select))) goto end_create; - thread_safe_increment32(&thread_count); - /* Annotating delayed inserts is not supported. */ @@ -2339,6 +2359,12 @@ end_create: DBUG_RETURN(thd->is_error()); } +#define memdup_vcol(thd, vcol) \ + if (vcol) \ + { \ + (vcol)= (Virtual_column_info*)(thd)->memdup((vcol), sizeof(*(vcol))); \ + (vcol)->expr= NULL; \ + } /** As we can't let many client threads modify the same TABLE @@ -2360,11 +2386,11 @@ 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); 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 */ @@ -2418,35 +2444,39 @@ 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) - { - vfield= (Field **) client_thd->alloc((share->vfields+1)*sizeof(Field*)); - if (!vfield) - goto error; - } - /* 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; } + if (share->virtual_fields) + { + copy->vfield= (Field **) client_thd->alloc((share->virtual_fields+1)* + sizeof(Field*)); + if (!copy->vfield) + goto error; + } + copy->expr_arena= NULL; + /* Ensure we don't use the table list of the original table */ copy->pos_in_table_list= 0; @@ -2461,51 +2491,32 @@ TABLE *Delayed_insert::get_local_table(THD* client_thd) found_next_number_field= table->found_next_number_field; for (org_field= table->field; *org_field; org_field++, field++) { - if (!(*field= (*org_field)->make_new_field(client_thd->mem_root, copy, - 1))) + 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] + memdup_vcol(client_thd, (*field)->vcol_info); + memdup_vcol(client_thd, (*field)->default_value); + memdup_vcol(client_thd, (*field)->check_constraint); 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++) - { - 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)) - goto error; - *vfield++= *field; - } - } - *vfield= 0; + + if (parse_vcol_defs(client_thd, client_thd->mem_root, copy, &error_reported)) + goto error; } - 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; @@ -2517,15 +2528,25 @@ 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 || share->default_expressions) + { + my_bitmap_init(©->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; @@ -2795,11 +2816,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; @@ -2820,15 +2841,12 @@ 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' */ - mysql_mutex_lock(&LOCK_thread_count); - thd->thread_id= thd->variables.pseudo_thread_id= thread_id++; thd->set_current_time(); - threads.append(thd); + add_to_active_threads(thd); if (abort_loop) thd->set_killed(KILL_CONNECTION); else thd->reset_killed(); - mysql_mutex_unlock(&LOCK_thread_count); mysql_thread_set_psi_id(thd->thread_id); @@ -2912,6 +2930,8 @@ pthread_handler_t handle_delayed_insert(void *arg) thd->mdl_context.set_needs_thr_lock_abort(TRUE); di->table->mark_columns_needed_for_insert(); + /* Mark all columns for write as we don't know which columns we get from user */ + bitmap_set_all(di->table->write_set); /* Now wait until we get an insert or lock to handle */ /* We will not abort as long as a client thread uses this thread */ @@ -3079,9 +3099,9 @@ pthread_handler_t handle_delayed_insert(void *arg) } -/* Remove pointers from temporary fields to allocated values */ +/* Remove all pointers to data for blob fields so that original table doesn't try to free them */ -static void unlink_blobs(register TABLE *table) +static void unlink_blobs(TABLE *table) { for (Field **ptr=table->field ; *ptr ; ptr++) { @@ -3092,16 +3112,28 @@ static void unlink_blobs(register TABLE *table) /* Free blobs stored in current row */ -static void free_delayed_insert_blobs(register TABLE *table) +static void free_delayed_insert_blobs(TABLE *table) +{ + for (Field **ptr=table->field ; *ptr ; ptr++) + { + if ((*ptr)->flags & BLOB_FLAG) + ((Field_blob *) *ptr)->free(); + } +} + + +/* set value field for blobs to point to data in record */ + +static void set_delayed_insert_blobs(TABLE *table) { for (Field **ptr=table->field ; *ptr ; ptr++) { if ((*ptr)->flags & BLOB_FLAG) { - uchar *str; - ((Field_blob *) (*ptr))->get_ptr(&str); - my_free(str); - ((Field_blob *) (*ptr))->reset(); + Field_blob *blob= ((Field_blob *) *ptr); + uchar *data= blob->get_ptr(); + if (data) + blob->set_value(data); // Set value.ptr() to point to data } } } @@ -3156,9 +3188,12 @@ bool Delayed_insert::handle_inserts(void) while ((row=rows.get())) { + int tmp_error; stacked_inserts--; mysql_mutex_unlock(&mutex); memcpy(table->record[0],row->record,table->s->reclength); + if (table->s->blob_fields) + set_delayed_insert_blobs(table); thd.start_time=row->start_time; thd.query_start_used=row->query_start_used; @@ -3229,7 +3264,20 @@ bool Delayed_insert::handle_inserts(void) if (info.handle_duplicates == DUP_UPDATE) table->file->extra(HA_EXTRA_INSERT_WITH_UPDATE); thd.clear_error(); // reset error for binlog - if (write_record(&thd, table, &info)) + + tmp_error= 0; + if (table->vfield) + { + /* + Virtual fields where not calculated by caller as the temporary + TABLE object used had vcol_set empty. Better to calculate them + here to make the caller faster. + */ + tmp_error= table->update_virtual_fields(table->file, + VCOL_UPDATE_FOR_WRITE); + } + + if (tmp_error || write_record(&thd, table, &info)) { info.error_count++; // Ignore errors thread_safe_increment(delayed_insert_errors,&LOCK_delayed_status); @@ -3350,6 +3398,7 @@ bool Delayed_insert::handle_inserts(void) if (table->s->blob_fields) { memcpy(table->record[0],row->record,table->s->reclength); + set_delayed_insert_blobs(table); free_delayed_insert_blobs(table); } delete row; @@ -3453,8 +3502,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; } @@ -3476,7 +3525,8 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) */ lex->current_select= &lex->select_lex; - res= (setup_fields(thd, 0, values, MARK_COLUMNS_READ, 0, NULL, 0) || + res= (setup_fields(thd, Ref_ptr_array(), + values, MARK_COLUMNS_READ, 0, NULL, 0) || check_insert_fields(thd, table_list, *fields, values, !insert_into_view, 1, &map)); @@ -3528,7 +3578,7 @@ select_insert::prepare(List<Item> &values, SELECT_LEX_UNIT *u) table_list->next_name_resolution_table= ctx_state.get_first_name_resolution_table(); - res= res || setup_fields(thd, 0, *info.update_values, + res= res || setup_fields(thd, Ref_ptr_array(), *info.update_values, MARK_COLUMNS_READ, 0, NULL, 0); if (!res) { @@ -3657,7 +3707,7 @@ void select_insert::cleanup() select_insert::~select_insert() { DBUG_ENTER("~select_insert"); - if (table && table->created) + if (table && table->is_created()) { table->next_number_field=0; table->auto_increment_field_not_null= FALSE; @@ -3684,7 +3734,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()) @@ -3702,9 +3752,6 @@ int select_insert::send_data(List<Item> &values) } } - // Release latches in case bulk insert takes a long time - ha_release_temporary_latches(thd); - error= write_record(thd, table, &info); table->auto_increment_field_not_null= FALSE; @@ -3992,7 +4039,6 @@ static TABLE *create_table_from_items(THD *thd, Item *item; DBUG_ENTER("create_table_from_items"); - tmp_table.alias= 0; tmp_table.s= &share; init_tmp_table_share(thd, &share, "", 0, "", ""); @@ -4105,7 +4151,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 @@ -4114,7 +4165,6 @@ static TABLE *create_table_from_items(THD *thd, */ DBUG_ASSERT(0); } - DBUG_ASSERT(create_table->table == create_info->table); } } else @@ -4249,6 +4299,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); @@ -4371,6 +4433,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 @@ -4419,8 +4502,9 @@ bool select_create::send_eof() mysql_mutex_lock(&thd->LOCK_thd_data); if (thd->wsrep_conflict_state != NO_CONFLICT) { - WSREP_DEBUG("select_create commit failed, thd: %lu err: %d %s", - thd->thread_id, thd->wsrep_conflict_state, thd->query()); + WSREP_DEBUG("select_create commit failed, thd: %lld err: %d %s", + (longlong) thd->thread_id, thd->wsrep_conflict_state, + thd->query()); mysql_mutex_unlock(&thd->LOCK_thd_data); abort_result_set(); DBUG_RETURN(true); @@ -4526,6 +4610,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; |