diff options
Diffstat (limited to 'sql/sql_delete.cc')
-rw-r--r-- | sql/sql_delete.cc | 168 |
1 files changed, 143 insertions, 25 deletions
diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 0ab80fe7cb8..26646d5c73f 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -245,6 +245,50 @@ static bool record_should_be_deleted(THD *thd, TABLE *table, SQL_SELECT *sel, return false; } +static +int update_portion_of_time(THD *thd, TABLE *table, + const vers_select_conds_t &period_conds, + bool *inside_period) +{ + bool lcond= period_conds.field_start->val_datetime_packed(thd) + < period_conds.start.item->val_datetime_packed(thd); + bool rcond= period_conds.field_end->val_datetime_packed(thd) + > period_conds.end.item->val_datetime_packed(thd); + + *inside_period= !lcond && !rcond; + if (*inside_period) + return 0; + + DBUG_ASSERT(!table->triggers + || !table->triggers->has_triggers(TRG_EVENT_INSERT, + TRG_ACTION_BEFORE)); + + int res= 0; + Item *src= lcond ? period_conds.start.item : period_conds.end.item; + uint dst_fieldno= lcond ? table->s->period.end_fieldno + : table->s->period.start_fieldno; + + store_record(table, record[1]); + if (likely(!res)) + res= src->save_in_field(table->field[dst_fieldno], true); + + if (likely(!res)) + res= table->update_generated_fields(); + + if(likely(!res)) + res= table->file->ha_update_row(table->record[1], table->record[0]); + + if (likely(!res) && table->triggers) + res= table->triggers->process_triggers(thd, TRG_EVENT_INSERT, + TRG_ACTION_AFTER, true); + restore_record(table, record[1]); + + if (likely(!res) && lcond && rcond) + res= table->period_make_insert(period_conds.end.item, + table->field[table->s->period.start_fieldno]); + + return res; +} inline int TABLE::delete_row() @@ -282,10 +326,10 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, bool return_error= 0; ha_rows deleted= 0; bool reverse= FALSE; - bool has_triggers; + bool has_triggers= false; ORDER *order= (ORDER *) ((order_list && order_list->elements) ? order_list->first : NULL); - SELECT_LEX *select_lex= &thd->lex->select_lex; + SELECT_LEX *select_lex= thd->lex->first_select_lex(); killed_state killed_status= NOT_KILLED; THD::enum_binlog_query_type query_type= THD::ROW_QUERY_TYPE; bool binlog_is_row; @@ -293,7 +337,9 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, Explain_delete *explain; Delete_plan query_plan(thd->mem_root); Unique * deltempfile= NULL; - bool delete_record, delete_while_scanning; + bool delete_record= false; + bool delete_while_scanning; + bool portion_of_time_through_update; DBUG_ENTER("mysql_delete"); query_plan.index= MAX_KEY; @@ -308,6 +354,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, bool truncate_history= table_list->vers_conditions.is_set(); if (truncate_history) { + DBUG_ASSERT(!table_list->period_conditions.is_set()); + if (table_list->is_view_or_derived()) { my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); @@ -315,7 +363,6 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, } DBUG_ASSERT(table_list->table); - DBUG_ASSERT(!conds || thd->stmt_arena->is_stmt_execute()); // conds could be cached from previous SP call @@ -346,7 +393,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, DBUG_RETURN(TRUE); } table->map=1; - query_plan.select_lex= &thd->lex->select_lex; + query_plan.select_lex= thd->lex->first_select_lex(); query_plan.table= table; query_plan.updating_a_view= MY_TEST(table_list->view); @@ -378,7 +425,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, setup_order(thd, select_lex->ref_pointer_array, &tables, fields, all_fields, order)) { - free_underlaid_joins(thd, &thd->lex->select_lex); + free_underlaid_joins(thd, thd->lex->first_select_lex()); DBUG_RETURN(TRUE); } } @@ -419,12 +466,12 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, - there should be no delete triggers associated with the table. */ - has_triggers= (table->triggers && - table->triggers->has_delete_triggers()); + has_triggers= table->triggers && table->triggers->has_delete_triggers(); + if (!with_select && !using_limit && const_cond_result && (!thd->is_current_stmt_binlog_format_row() && !has_triggers) - && !table->versioned(VERS_TIMESTAMP)) + && !table->versioned(VERS_TIMESTAMP) && !table_list->has_period()) { /* Update the table->file->stats.records number */ table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); @@ -594,7 +641,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, */ if ((table->file->ha_table_flags() & HA_CAN_DIRECT_UPDATE_AND_DELETE) && - !has_triggers && !binlog_is_row && !with_select) + !has_triggers && !binlog_is_row && !with_select && + !table_list->has_period()) { table->mark_columns_needed_for_delete(); if (!table->check_virtual_columns_marked_for_read()) @@ -664,7 +712,15 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, if (unlikely(init_ftfuncs(thd, select_lex, 1))) goto got_error; - table->mark_columns_needed_for_delete(); + if (table_list->has_period()) + { + table->use_all_columns(); + table->rpl_write_set= table->write_set; + } + else + { + table->mark_columns_needed_for_delete(); + } if ((table->file->ha_table_flags() & HA_CAN_FORCE_BULK_DELETE) && !table->prepare_triggers_for_delete_stmt_or_event()) @@ -721,6 +777,24 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, delete_record= true; } + /* + From SQL2016, Part 2, 15.7 <Effect of deleting rows from base table>, + General Rules, 8), we can conclude that DELETE FOR PORTTION OF time performs + 0-2 INSERTS + DELETE. We can substitute INSERT+DELETE with one UPDATE, with + a condition of no side effects. The side effect is possible if there is a + BEFORE INSERT trigger, since it is the only one splitting DELETE and INSERT + operations. + Another possible side effect is related to tables of non-transactional + engines, since UPDATE is anyway atomic, and DELETE+INSERT is not. + + This optimization is not possible for system-versioned table. + */ + portion_of_time_through_update= + !(table->triggers && table->triggers->has_triggers(TRG_EVENT_INSERT, + TRG_ACTION_BEFORE)) + && !table->versioned() + && table->file->has_transactions(); + THD_STAGE_INFO(thd, stage_updating); while (likely(!(error=info.read_record())) && likely(!thd->killed) && likely(!thd->is_error())) @@ -744,7 +818,25 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, break; } - error= table->delete_row(); + if (table_list->has_period() && portion_of_time_through_update) + { + bool need_delete= true; + error= update_portion_of_time(thd, table, table_list->period_conditions, + &need_delete); + if (likely(!error) && need_delete) + error= table->delete_row(); + } + else + { + error= table->delete_row(); + + ha_rows rows_inserted; + if (likely(!error) && table_list->has_period() + && !portion_of_time_through_update) + error= table->insert_portion_of_time(thd, table_list->period_conditions, + &rows_inserted); + } + if (likely(!error)) { deleted++; @@ -764,7 +856,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, else { table->file->print_error(error, - MYF(thd->lex->ignore ? ME_JUST_WARNING : 0)); + MYF(thd->lex->ignore ? ME_WARNING : 0)); if (thd->is_error()) { error= 1; @@ -794,6 +886,8 @@ terminate_delete: } THD_STAGE_INFO(thd, stage_end); end_read_record(&info); + if (table_list->has_period()) + table->file->ha_release_auto_increment(); if (options & OPTION_QUICK) (void) table->file->extra(HA_EXTRA_NORMAL); ANALYZE_STOP_TRACKING(&explain->command_tracker); @@ -925,14 +1019,16 @@ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, bool *delete_while_scanning) { Item *fake_conds= 0; - SELECT_LEX *select_lex= &thd->lex->select_lex; + SELECT_LEX *select_lex= thd->lex->first_select_lex(); DBUG_ENTER("mysql_prepare_delete"); List<Item> all_fields; *delete_while_scanning= true; thd->lex->allow_sum_func.clear_all(); - if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context, - &thd->lex->select_lex.top_join_list, + if (setup_tables_and_check_access(thd, + &thd->lex->first_select_lex()->context, + &thd->lex->first_select_lex()-> + top_join_list, table_list, select_lex->leaf_tables, FALSE, DELETE_ACL, SELECT_ACL, TRUE)) @@ -947,6 +1043,20 @@ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, if (select_lex->vers_setup_conds(thd, table_list)) DBUG_RETURN(true); } + + if (table_list->has_period()) + { + if (table_list->is_view_or_derived()) + { + my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); + DBUG_RETURN(true); + } + + *conds= select_lex->period_setup_conds(thd, table_list, *conds); + if (!*conds) + DBUG_RETURN(true); + } + if ((wild_num && setup_wild(thd, table_list, field_list, NULL, wild_num, &select_lex->hidden_bit_fields)) || setup_fields(thd, Ref_ptr_array(), @@ -961,7 +1071,13 @@ int mysql_prepare_delete(THD *thd, TABLE_LIST *table_list, DBUG_RETURN(TRUE); } - if (unique_table(thd, table_list, table_list->next_global, 0)) + /* + Application-time periods: if FOR PORTION OF ... syntax used, DELETE + statement could issue delete_row's mixed with write_row's. This causes + problems for myisam and corrupts table, if deleting while scanning. + */ + if (table_list->has_period() + || unique_table(thd, table_list, table_list->next_global, 0)) *delete_while_scanning= false; if (select_lex->inner_refs_list.elements && @@ -1015,21 +1131,23 @@ int mysql_multi_delete_prepare(THD *thd) lex->query_tables also point on local list of DELETE SELECT_LEX */ - if (setup_tables_and_check_access(thd, &thd->lex->select_lex.context, - &thd->lex->select_lex.top_join_list, + if (setup_tables_and_check_access(thd, + &thd->lex->first_select_lex()->context, + &thd->lex->first_select_lex()-> + top_join_list, lex->query_tables, - lex->select_lex.leaf_tables, FALSE, - DELETE_ACL, SELECT_ACL, FALSE)) + lex->first_select_lex()->leaf_tables, + FALSE, DELETE_ACL, SELECT_ACL, FALSE)) DBUG_RETURN(TRUE); - if (lex->select_lex.handle_derived(thd->lex, DT_MERGE)) + if (lex->first_select_lex()->handle_derived(thd->lex, DT_MERGE)) DBUG_RETURN(TRUE); /* Multi-delete can't be constructed over-union => we always have single SELECT on top and have to check underlying SELECTs of it */ - lex->select_lex.exclude_from_table_unique_test= TRUE; + lex->first_select_lex()->exclude_from_table_unique_test= TRUE; /* Fix tables-to-be-deleted-from list to point at opened tables */ for (target_tbl= (TABLE_LIST*) aux_tables; target_tbl; @@ -1071,8 +1189,8 @@ int mysql_multi_delete_prepare(THD *thd) Reset the exclude flag to false so it doesn't interfare with further calls to unique_table */ - lex->select_lex.exclude_from_table_unique_test= FALSE; - + lex->first_select_lex()->exclude_from_table_unique_test= FALSE; + if (lex->save_prep_leaf_tables()) DBUG_RETURN(TRUE); |