diff options
Diffstat (limited to 'sql/sql_update.cc')
-rw-r--r-- | sql/sql_update.cc | 167 |
1 files changed, 103 insertions, 64 deletions
diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 9514b193407..41ea52ef3c3 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -1,5 +1,5 @@ /* Copyright (c) 2000, 2016, Oracle and/or its affiliates. - Copyright (c) 2011, 2020, MariaDB + Copyright (c) 2011, 2021, 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 @@ -52,8 +52,9 @@ compare_record(TABLE*). */ bool records_are_comparable(const TABLE *table) { - return ((table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ) == 0) || - bitmap_is_subset(table->write_set, table->read_set); + return !table->versioned(VERS_TRX_ID) && + (((table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ) == 0) || + bitmap_is_subset(table->write_set, table->read_set)); } @@ -1081,20 +1082,9 @@ update_begin: } else if (likely(!error)) { - if (has_vers_fields && table->versioned()) - { - if (table->versioned(VERS_TIMESTAMP)) - { - store_record(table, record[2]); - table->mark_columns_per_binlog_row_image(); - error= vers_insert_history_row(table); - restore_record(table, record[2]); - } - if (likely(!error)) - rows_inserted++; - } - if (likely(!error)) - updated++; + if (has_vers_fields && table->versioned(VERS_TRX_ID)) + rows_inserted++; + updated++; } if (likely(!error) && !record_was_same && table_list->has_period()) @@ -1110,6 +1100,20 @@ update_begin: if (unlikely(error) && (!ignore || table->file->is_fatal_error(error, HA_CHECK_ALL))) { + goto error; + } + } + + if (likely(!error) && has_vers_fields && table->versioned(VERS_TIMESTAMP)) + { + store_record(table, record[2]); + table->mark_columns_per_binlog_row_image(); + table->clone_handler_for_update(); + error= vers_insert_history_row(table); + restore_record(table, record[2]); + if (unlikely(error)) + { +error: /* If (ignore && error is ignorable) we don't have to do anything; otherwise... @@ -1120,10 +1124,11 @@ update_begin: flags|= ME_FATAL; /* Other handler errors are fatal */ prepare_record_for_error_message(error, table); - table->file->print_error(error,MYF(flags)); - error= 1; - break; - } + table->file->print_error(error,MYF(flags)); + error= 1; + break; + } + rows_inserted++; } if (table->triggers && @@ -1705,12 +1710,8 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd) call in setup_tables()). */ - if (setup_tables_and_check_access(thd, &select_lex->context, - &select_lex->top_join_list, table_list, select_lex->leaf_tables, - FALSE, UPDATE_ACL, SELECT_ACL, FALSE)) - DBUG_RETURN(1); - - if (select_lex->handle_derived(thd->lex, DT_MERGE)) + if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list, + table_list, select_lex->leaf_tables, FALSE, TRUE)) DBUG_RETURN(1); List<Item> *fields= &lex->first_select_lex()->item_list; @@ -1848,7 +1849,11 @@ int mysql_multi_update_prepare(THD *thd) During prepare phase acquire only S metadata locks instead of SW locks to keep prepare of multi-UPDATE compatible with concurrent LOCK TABLES WRITE and global read lock. + + Don't evaluate any subqueries even if constant, because + tables aren't locked yet. */ + lex->context_analysis_only|= CONTEXT_ANALYSIS_ONLY_DERIVED; if (thd->lex->sql_command == SQLCOM_UPDATE_MULTI) { if (open_tables(thd, &table_list, &table_count, @@ -1871,6 +1876,9 @@ int mysql_multi_update_prepare(THD *thd) { DBUG_RETURN(TRUE); } + + lex->context_analysis_only&= ~CONTEXT_ANALYSIS_ONLY_DERIVED; + (void) read_statistics_for_tables_if_needed(thd, table_list); /* @todo: downgrade the metadata locks here. */ @@ -1929,9 +1937,16 @@ bool mysql_multi_update(THD *thd, TABLE_LIST *table_list, List<Item> *fields, DBUG_RETURN(TRUE); } + if ((*result)->init(thd)) + DBUG_RETURN(1); + thd->abort_on_warning= !ignore && thd->is_strict_mode(); List<Item> total_list; + if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list, + table_list, select_lex->leaf_tables, FALSE, FALSE)) + DBUG_RETURN(1); + if (select_lex->vers_setup_conds(thd, table_list)) DBUG_RETURN(1); @@ -1973,6 +1988,24 @@ multi_update::multi_update(THD *thd_arg, TABLE_LIST *table_list, } +bool multi_update::init(THD *thd) +{ + table_map tables_to_update= get_table_map(fields); + List_iterator_fast<TABLE_LIST> li(*leaves); + TABLE_LIST *tbl; + while ((tbl =li++)) + { + if (tbl->is_jtbm()) + continue; + if (!(tbl->table->map & tables_to_update)) + continue; + if (updated_leaves.push_back(tbl, thd->mem_root)) + return true; + } + return false; +} + + /* Connect fields with tables and create list of tables that are updated */ @@ -1989,7 +2022,7 @@ int multi_update::prepare(List<Item> ¬_used_values, List_iterator_fast<Item> value_it(*values); uint i, max_fields; uint leaf_table_count= 0; - List_iterator<TABLE_LIST> ti(*leaves); + List_iterator<TABLE_LIST> ti(updated_leaves); DBUG_ENTER("multi_update::prepare"); if (prepared) @@ -2477,6 +2510,7 @@ int multi_update::send_data(List<Item> ¬_used_values) for (cur_table= update_tables; cur_table; cur_table= cur_table->next_local) { + int error= 0; TABLE *table= cur_table->table; uint offset= cur_table->shared; /* @@ -2520,7 +2554,6 @@ int multi_update::send_data(List<Item> ¬_used_values) found++; if (!can_compare_record || compare_record(table)) { - int error; if ((error= cur_table->view_check_option(thd, ignore)) != VIEW_CHECK_OK) @@ -2547,20 +2580,7 @@ int multi_update::send_data(List<Item> ¬_used_values) updated--; if (!ignore || table->file->is_fatal_error(error, HA_CHECK_ALL)) - { - /* - If (ignore && error == is ignorable) we don't have to - do anything; otherwise... - */ - myf flags= 0; - - if (table->file->is_fatal_error(error, HA_CHECK_ALL)) - flags|= ME_FATAL; /* Other handler errors are fatal */ - - prepare_record_for_error_message(error, table); - table->file->print_error(error,MYF(flags)); - DBUG_RETURN(1); - } + goto error; } else { @@ -2569,19 +2589,8 @@ int multi_update::send_data(List<Item> ¬_used_values) error= 0; updated--; } - else if (has_vers_fields && table->versioned()) + else if (has_vers_fields && table->versioned(VERS_TRX_ID)) { - if (table->versioned(VERS_TIMESTAMP)) - { - store_record(table, record[2]); - if (vers_insert_history_row(table)) - { - restore_record(table, record[2]); - error= 1; - break; - } - restore_record(table, record[2]); - } updated_sys_ver++; } /* non-transactional or transactional table got modified */ @@ -2595,6 +2604,18 @@ int multi_update::send_data(List<Item> ¬_used_values) } } } + if (has_vers_fields && table->versioned(VERS_TIMESTAMP)) + { + store_record(table, record[2]); + table->clone_handler_for_update(); + if (unlikely(error= vers_insert_history_row(table))) + { + restore_record(table, record[2]); + goto error; + } + restore_record(table, record[2]); + updated_sys_ver++; + } if (table->triggers && unlikely(table->triggers->process_triggers(thd, TRG_EVENT_UPDATE, TRG_ACTION_AFTER, TRUE))) @@ -2602,7 +2623,6 @@ int multi_update::send_data(List<Item> ¬_used_values) } else { - int error; TABLE *tmp_table= tmp_tables[offset]; if (copy_funcs(tmp_table_param[offset].items_to_copy, thd)) DBUG_RETURN(1); @@ -2637,7 +2657,22 @@ int multi_update::send_data(List<Item> ¬_used_values) } } } - } + continue; +error: + DBUG_ASSERT(error > 0); + /* + If (ignore && error == is ignorable) we don't have to + do anything; otherwise... + */ + myf flags= 0; + + if (table->file->is_fatal_error(error, HA_CHECK_ALL)) + flags|= ME_FATAL; /* Other handler errors are fatal */ + + prepare_record_for_error_message(error, table); + table->file->print_error(error,MYF(flags)); + DBUG_RETURN(1); + } // for (cur_table) DBUG_RETURN(0); } @@ -3034,14 +3069,18 @@ bool multi_update::send_eof() DBUG_ASSERT(trans_safe || !updated || thd->transaction.stmt.modified_non_trans_table); - if (likely(local_error != 0)) - error_handled= TRUE; // to force early leave from ::abort_result_set() - - if (unlikely(local_error > 0)) // if the above log write did not fail ... + if (unlikely(local_error)) { - /* Safety: If we haven't got an error before (can happen in do_updates) */ - my_message(ER_UNKNOWN_ERROR, "An error occurred in multi-table update", - MYF(0)); + error_handled= TRUE; // to force early leave from ::abort_result_set() + if (thd->killed == NOT_KILLED && !thd->get_stmt_da()->is_set()) + { + /* + No error message was sent and query was not killed (in which case + mysql_execute_command() will send the error mesage). + */ + my_message(ER_UNKNOWN_ERROR, "An error occurred in multi-table update", + MYF(0)); + } DBUG_RETURN(TRUE); } |