diff options
Diffstat (limited to 'sql/sql_update.cc')
-rw-r--r-- | sql/sql_update.cc | 460 |
1 files changed, 315 insertions, 145 deletions
diff --git a/sql/sql_update.cc b/sql/sql_update.cc index e01fe0926dd..0499ab53c6d 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -20,9 +20,8 @@ Multi-table updates were introduced by Sinisa & Monty */ -#include "my_global.h" /* NO_EMBEDDED_ACCESS_CHECKS */ +#include <my_global.h> /* NO_EMBEDDED_ACCESS_CHECKS */ #include "sql_priv.h" -#include "unireg.h" // REQUIRED: for other includes #include "sql_update.h" #include "sql_cache.h" // query_cache_* #include "sql_base.h" // close_tables_for_reopen @@ -32,6 +31,7 @@ #include "sql_view.h" // check_key_in_view #include "sp_head.h" #include "sql_trigger.h" +#include "sql_statistics.h" #include "probes_mysql.h" #include "debug_sync.h" #include "key.h" // is_key_used @@ -137,7 +137,7 @@ static bool check_fields(THD *thd, List<Item> &items) while ((item= it++)) { - if (!(field= item->filed_for_view_update())) + if (!(field= item->field_for_view_update())) { /* item has name, because it comes from VIEW SELECT list */ my_error(ER_NONUPDATEABLE_COLUMN, MYF(0), item->name); @@ -190,7 +190,7 @@ static void prepare_record_for_error_message(int error, TABLE *table) DBUG_VOID_RETURN; /* Create unique_map with all fields used by that index. */ - bitmap_init(&unique_map, unique_map_buf, table->s->fields, FALSE); + my_bitmap_init(&unique_map, unique_map_buf, table->s->fields, FALSE); table->mark_columns_used_by_index_no_reset(keynr, &unique_map); /* Subtract read_set and write_set. */ @@ -254,12 +254,12 @@ int mysql_update(THD *thd, ha_rows *found_return, ha_rows *updated_return) { bool using_limit= limit != HA_POS_ERROR; - bool safe_update= test(thd->variables.option_bits & OPTION_SAFE_UPDATES); + bool safe_update= MY_TEST(thd->variables.option_bits & OPTION_SAFE_UPDATES); bool used_key_is_modified= FALSE, transactional_table, will_batch; bool can_compare_record; int res; int error, loc_error; - uint used_index, dup_key_found; + uint dup_key_found; bool need_sort= TRUE; bool reverse= FALSE; #ifndef NO_EMBEDDED_ACCESS_CHECKS @@ -269,14 +269,18 @@ int mysql_update(THD *thd, ha_rows updated, found; key_map old_covering_keys; TABLE *table; - SQL_SELECT *select; + SQL_SELECT *select= NULL; READ_RECORD info; SELECT_LEX *select_lex= &thd->lex->select_lex; ulonglong id; List<Item> all_fields; killed_state killed_status= NOT_KILLED; + Update_plan query_plan(thd->mem_root); + query_plan.index= MAX_KEY; + query_plan.using_filesort= FALSE; DBUG_ENTER("mysql_update"); + create_explain_query(thd->lex, thd->mem_root); if (open_tables(thd, &table_list, &table_count, 0)) DBUG_RETURN(1); @@ -301,7 +305,7 @@ int mysql_update(THD *thd, if (table_list->handle_derived(thd->lex, DT_PREPARE)) DBUG_RETURN(1); - thd_proc_info(thd, "init"); + THD_STAGE_INFO(thd, stage_init); table= table_list->table; if (!table_list->single_table_updatable()) @@ -309,10 +313,14 @@ int mysql_update(THD *thd, my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "UPDATE"); DBUG_RETURN(1); } + query_plan.updating_a_view= MY_TEST(table_list->view); + /* Calculate "table->covering_keys" based on the WHERE */ table->covering_keys= table->s->keys_in_use; table->quick_keys.clear_all(); + query_plan.select_lex= &thd->lex->select_lex; + query_plan.table= table; #ifndef NO_EMBEDDED_ACCESS_CHECKS /* Force privilege re-checking for views after they have been opened. */ want_privilege= (table_list->view ? UPDATE_ACL : @@ -342,19 +350,8 @@ int mysql_update(THD *thd, my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias, "UPDATE"); DBUG_RETURN(1); } - if (table->timestamp_field) - { - // Don't set timestamp column if this is modified - if (bitmap_is_set(table->write_set, - table->timestamp_field->field_index)) - table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; - else - { - if (((uint) table->timestamp_field_type) & TIMESTAMP_AUTO_SET_ON_UPDATE) - bitmap_set_bit(table->write_set, - table->timestamp_field->field_index); - } - } + if (table->default_field) + table->mark_default_fields_for_write(); #ifndef NO_EMBEDDED_ACCESS_CHECKS /* Check values */ @@ -383,7 +380,12 @@ int mysql_update(THD *thd, Item::cond_result cond_value; conds= remove_eq_conds(thd, conds, &cond_value); if (cond_value == Item::COND_FALSE) + { limit= 0; // Impossible WHERE + query_plan.set_impossible_where(); + if (thd->lex->describe) + goto exit_without_my_ok; + } } /* @@ -392,7 +394,7 @@ int mysql_update(THD *thd, to compare records and detect data change. */ if ((table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ) && - (((uint) table->timestamp_field_type) & TIMESTAMP_AUTO_SET_ON_UPDATE)) + table->default_field && table->has_default_function(true)) bitmap_union(table->read_set, table->write_set); // Don't count on usage of 'only index' when calculating which key to use table->covering_keys.clear_all(); @@ -401,17 +403,27 @@ int mysql_update(THD *thd, if (prune_partitions(thd, table, conds)) { free_underlaid_joins(thd, select_lex); + + query_plan.set_no_partitions(); + if (thd->lex->describe) + goto exit_without_my_ok; + my_ok(thd); // No matching records DBUG_RETURN(0); } #endif /* Update the table->file->stats.records number */ table->file->info(HA_STATUS_VARIABLE | HA_STATUS_NO_LOCK); + set_statistics_for_table(thd, table); select= make_select(table, 0, 0, conds, 0, &error); if (error || !limit || thd->is_error() || (select && select->check_quick(thd, safe_update, limit))) { + query_plan.set_impossible_where(); + if (thd->lex->describe) + goto exit_without_my_ok; + delete select; free_underlaid_joins(thd, select_lex); /* @@ -432,7 +444,7 @@ int mysql_update(THD *thd, /* If running in safe sql mode, don't allow updates without keys */ if (table->quick_keys.is_clear_all()) { - thd->server_status|=SERVER_QUERY_NO_INDEX_USED; + thd->set_status_no_index_used(); if (safe_update && !using_limit) { my_message(ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE, @@ -446,55 +458,95 @@ int mysql_update(THD *thd, table->update_const_key_parts(conds); order= simple_remove_const(order, conds); + query_plan.scanned_rows= select? select->records: table->file->stats.records; if (select && select->quick && select->quick->unique_key_range()) - { // Single row select (always "ordered"): Ok to use with key field UPDATE + { + /* Single row select (always "ordered"): Ok to use with key field UPDATE */ need_sort= FALSE; - used_index= MAX_KEY; + query_plan.index= MAX_KEY; used_key_is_modified= FALSE; } else { - used_index= get_index_for_order(order, table, select, limit, - &need_sort, &reverse); + ha_rows scanned_limit= query_plan.scanned_rows; + query_plan.index= get_index_for_order(order, table, select, limit, + &scanned_limit, &need_sort, + &reverse); + if (!need_sort) + query_plan.scanned_rows= scanned_limit; + if (select && select->quick) { - DBUG_ASSERT(need_sort || used_index == select->quick->index); + DBUG_ASSERT(need_sort || query_plan.index == select->quick->index); used_key_is_modified= (!select->quick->unique_key_range() && select->quick->is_keys_used(table->write_set)); } else { if (need_sort) - { // Assign table scan index to check below for modified key fields: - used_index= table->file->key_used_on_scan; + { + /* Assign table scan index to check below for modified key fields: */ + query_plan.index= table->file->key_used_on_scan; } - if (used_index != MAX_KEY) - { // Check if we are modifying a key that we are used to search with: - used_key_is_modified= is_key_used(table, used_index, table->write_set); + if (query_plan.index != MAX_KEY) + { + /* Check if we are modifying a key that we are used to search with: */ + used_key_is_modified= is_key_used(table, query_plan.index, + table->write_set); } } } - + + /* + Query optimization is finished at this point. + - Save the decisions in the query plan + - if we're running EXPLAIN UPDATE, get out + */ + query_plan.select= select; + query_plan.possible_keys= select? select->possible_keys: key_map(0); + if (used_key_is_modified || order || partition_key_modified(table, table->write_set)) { + if (order && need_sort) + query_plan.using_filesort= true; + else + query_plan.using_io_buffer= true; + } + + + /* + Ok, we have generated a query plan for the UPDATE. + - if we're running EXPLAIN UPDATE, goto produce explain output + - otherwise, execute the query plan + */ + if (thd->lex->describe) + goto exit_without_my_ok; + query_plan.save_explain_data(thd->lex->explain); + + DBUG_EXECUTE_IF("show_explain_probe_update_exec_start", + dbug_serve_apcs(thd, 1);); + + if (!(select && select->quick)) + status_var_increment(thd->status_var.update_scan_count); + + if (query_plan.using_filesort || query_plan.using_io_buffer) + { /* We can't update table directly; We must first search after all matching rows before updating the table! */ + MY_BITMAP *save_read_set= table->read_set; + MY_BITMAP *save_write_set= table->write_set; - // Verify that table->restore_column_maps_after_mark_index() will work - DBUG_ASSERT(table->read_set == &table->def_read_set); - DBUG_ASSERT(table->write_set == &table->def_write_set); - - if (used_index < MAX_KEY && old_covering_keys.is_set(used_index)) - table->add_read_columns_used_by_index(used_index); + if (query_plan.index < MAX_KEY && old_covering_keys.is_set(query_plan.index)) + table->add_read_columns_used_by_index(query_plan.index); else table->use_all_columns(); /* note: We avoid sorting if we sort on the used index */ - if (order && (need_sort || used_key_is_modified)) + if (query_plan.using_filesort) { /* Doing an ORDER BY; Let filesort find and sort the rows we are going @@ -504,18 +556,21 @@ int mysql_update(THD *thd, uint length= 0; SORT_FIELD *sortorder; ha_rows examined_rows; + ha_rows found_rows; table->sort.io_cache = (IO_CACHE *) my_malloc(sizeof(IO_CACHE), - MYF(MY_FAE | MY_ZEROFILL)); + MYF(MY_FAE | MY_ZEROFILL | + MY_THREAD_SPECIFIC)); if (!(sortorder=make_unireg_sortorder(order, &length, NULL)) || (table->sort.found_records= filesort(thd, table, sortorder, length, - select, limit, 1, - &examined_rows)) + select, limit, + true, + &examined_rows, &found_rows)) == HA_POS_ERROR) { goto err; } - thd->examined_row_count+= examined_rows; + thd->inc_examined_row_count(examined_rows); /* Filesort has already found and selected the rows we want to update, so we don't need the where clause @@ -542,38 +597,43 @@ int mysql_update(THD *thd, close_cached_file(&tempfile); goto err; } + table->file->try_semi_consistent_read(1); /* When we get here, we have one of the following options: - A. used_index == MAX_KEY + A. query_plan.index == MAX_KEY This means we should use full table scan, and start it with init_read_record call - B. used_index != MAX_KEY + B. query_plan.index != MAX_KEY B.1 quick select is used, start the scan with init_read_record B.2 quick select is not used, this is full index scan (with LIMIT) - Full index scan must be started with init_read_record_idx + Full index scan must be started with init_read_record_idx */ - if (used_index == MAX_KEY || (select && select->quick)) + if (query_plan.index == MAX_KEY || (select && select->quick)) + error= init_read_record(&info, thd, table, select, 0, 1, FALSE); + else + error= init_read_record_idx(&info, thd, table, 1, query_plan.index, + reverse); + + if (error) { - if (init_read_record(&info, thd, table, select, 0, 1, FALSE)) - goto err; + close_cached_file(&tempfile); + goto err; } - else - init_read_record_idx(&info, thd, table, 1, used_index, reverse); - thd_proc_info(thd, "Searching rows for update"); + THD_STAGE_INFO(thd, stage_searching_rows_for_update); ha_rows tmp_limit= limit; while (!(error=info.read_record(&info)) && !thd->killed) { if (table->vfield) update_virtual_fields(thd, table, VCOL_UPDATE_FOR_READ); - thd->examined_row_count++; + thd->inc_examined_row_count(1); if (!select || (error= select->skip_record(thd)) > 0) { - if (table->file->was_semi_consistent_read()) + if (table->file->ha_was_semi_consistent_read()) continue; /* repeat the read of the same row if it still exists */ table->file->position(table->record[0]); @@ -625,6 +685,7 @@ int mysql_update(THD *thd, select= new SQL_SELECT; select->head=table; } + //psergey-todo: disable SHOW EXPLAIN because the plan was deleted? if (reinit_io_cache(&tempfile,READ_CACHE,0L,0,0)) error=1; /* purecov: inspected */ select->file=tempfile; // Read row ptrs from this file @@ -633,11 +694,8 @@ int mysql_update(THD *thd, if (error >= 0) goto err; } - /* - This restore bitmaps, works for add_read_columns_used_by_index() and - use_all_columns(): - */ - table->restore_column_maps_after_mark_index(); + table->disable_keyread(); + table->column_bitmaps_set(save_read_set, save_write_set); } if (ignore) @@ -656,23 +714,12 @@ int mysql_update(THD *thd, */ thd->count_cuted_fields= CHECK_FIELD_WARN; thd->cuted_fields=0L; - thd_proc_info(thd, "Updating"); + THD_STAGE_INFO(thd, stage_updating); transactional_table= table->file->has_transactions(); - thd->abort_on_warning= test(!ignore && - (thd->variables.sql_mode & - (MODE_STRICT_TRANS_TABLES | - MODE_STRICT_ALL_TABLES))); - if (table->triggers && - table->triggers->has_triggers(TRG_EVENT_UPDATE, - TRG_ACTION_AFTER)) + thd->abort_on_warning= !ignore && thd->is_strict_mode(); + if (table->prepare_triggers_for_update_stmt_or_event()) { - /* - The table has AFTER UPDATE triggers that might access to subject - table and therefore might need update to be done immediately. - So we turn-off the batching. - */ - (void) table->file->extra(HA_EXTRA_UPDATE_CANNOT_BATCH); will_batch= FALSE; } else @@ -685,6 +732,8 @@ int mysql_update(THD *thd, if (table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ) table->prepare_for_position(); + table->reset_default_fields(); + /* We can use compare_record() to optimize away updates if the table handler is returning all columns OR if @@ -696,15 +745,14 @@ int mysql_update(THD *thd, { if (table->vfield) update_virtual_fields(thd, table, VCOL_UPDATE_FOR_READ); - thd->examined_row_count++; + thd->inc_examined_row_count(1); if (!select || select->skip_record(thd) > 0) { - if (table->file->was_semi_consistent_read()) + if (table->file->ha_was_semi_consistent_read()) continue; /* repeat the read of the same row if it still exists */ store_record(table,record[1]); - if (fill_record_n_invoke_before_triggers(thd, fields, values, 0, - table->triggers, + if (fill_record_n_invoke_before_triggers(thd, table, fields, values, 0, TRG_EVENT_UPDATE)) break; /* purecov: inspected */ @@ -712,6 +760,11 @@ int mysql_update(THD *thd, if (!can_compare_record || compare_record(table)) { + if (table->default_field && table->update_default_fields()) + { + error= 1; + break; + } if ((res= table_list->view_check_option(thd, ignore)) != VIEW_CHECK_OK) { @@ -852,7 +905,7 @@ int mysql_update(THD *thd, error= 1; break; } - thd->warning_info->inc_current_row_for_warning(); + thd->get_stmt_da()->inc_current_row_for_warning(); if (thd->is_error()) { error= 1; @@ -906,7 +959,7 @@ int mysql_update(THD *thd, end_read_record(&info); delete select; - thd_proc_info(thd, "end"); + THD_STAGE_INFO(thd, stage_end); (void) table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY); /* @@ -960,7 +1013,7 @@ int mysql_update(THD *thd, char buff[MYSQL_ERRMSG_SIZE]; my_snprintf(buff, sizeof(buff), ER(ER_UPDATE_INFO), (ulong) found, (ulong) updated, - (ulong) thd->warning_info->statement_warn_count()); + (ulong) thd->get_stmt_da()->current_statement_warn_count()); my_ok(thd, (thd->client_capabilities & CLIENT_FOUND_ROWS) ? found : updated, id, buff); DBUG_PRINT("info",("%ld records updated", (long) updated)); @@ -977,11 +1030,21 @@ int mysql_update(THD *thd, DBUG_RETURN((error >= 0 || thd->is_error()) ? 1 : 0); err: + delete select; free_underlaid_joins(thd, select_lex); table->disable_keyread(); thd->abort_on_warning= 0; DBUG_RETURN(1); + +exit_without_my_ok: + query_plan.save_explain_data(thd->lex->explain); + + int err2= thd->lex->explain->send_explain(thd); + + delete select; + free_underlaid_joins(thd, select_lex); + DBUG_RETURN((err2 || thd->is_error()) ? 1 : 0); } /* @@ -1119,7 +1182,7 @@ bool unsafe_key_update(List<TABLE_LIST> leaves, table_map tables_for_update) while ((tl= it++)) { - if (tl->table->map & tables_for_update) + if (!tl->is_jtbm() && (tl->table->map & tables_for_update)) { TABLE *table1= tl->table; bool primkey_clustered= (table1->file->primary_key_is_clustered() && @@ -1136,6 +1199,8 @@ bool unsafe_key_update(List<TABLE_LIST> leaves, table_map tables_for_update) it2.rewind(); while ((tl2= it2++)) { + if (tl2->is_jtbm()) + continue; /* Look at "next" tables only since all previous tables have already been checked @@ -1163,7 +1228,7 @@ bool unsafe_key_update(List<TABLE_LIST> leaves, table_map tables_for_update) // The primary key can cover multiple columns KEY key_info= table1->key_info[table1->s->primary_key]; KEY_PART_INFO *key_part= key_info.key_part; - KEY_PART_INFO *key_part_end= key_part + key_info.key_parts; + KEY_PART_INFO *key_part_end= key_part + key_info.user_defined_key_parts; for (;key_part != key_part_end; ++key_part) { @@ -1187,6 +1252,87 @@ bool unsafe_key_update(List<TABLE_LIST> leaves, table_map tables_for_update) return false; } +/** + Check if there is enough privilege on specific table used by the + main select list of multi-update directly or indirectly (through + a view). + + @param[in] thd Thread context. + @param[in] table Table list element for the table. + @param[in] tables_for_update Bitmap with tables being updated. + @param[in/out] updated_arg Set to true if table in question is + updated, also set to true if it is + a view and one of its underlying + tables is updated. Should be + initialized to false by the caller + before a sequence of calls to this + function. + + @note To determine which tables/views are updated we have to go from + leaves to root since tables_for_update contains map of leaf + tables being updated and doesn't include non-leaf tables + (fields are already resolved to leaf tables). + + @retval false - Success, all necessary privileges on all tables are + present or might be present on column-level. + @retval true - Failure, some necessary privilege on some table is + missing. +*/ + +static bool multi_update_check_table_access(THD *thd, TABLE_LIST *table, + table_map tables_for_update, + bool *updated_arg) +{ + if (table->view) + { + bool updated= false; + /* + If it is a mergeable view then we need to check privileges on its + underlying tables being merged (including views). We also need to + check if any of them is updated in order to find if this view is + updated. + If it is a non-mergeable view then it can't be updated. + */ + DBUG_ASSERT(table->merge_underlying_list || + (!table->updatable && + !(table->table->map & tables_for_update))); + + for (TABLE_LIST *tbl= table->merge_underlying_list; tbl; + tbl= tbl->next_local) + { + if (multi_update_check_table_access(thd, tbl, tables_for_update, + &updated)) + { + tbl->hide_view_error(thd); + return true; + } + } + if (check_table_access(thd, updated ? UPDATE_ACL: SELECT_ACL, table, + FALSE, 1, FALSE)) + return true; + *updated_arg|= updated; + /* We only need SELECT privilege for columns in the values list. */ + table->grant.want_privilege= SELECT_ACL & ~table->grant.privilege; + } + else + { + /* Must be a base or derived table. */ + const bool updated= table->table->map & tables_for_update; + if (check_table_access(thd, updated ? UPDATE_ACL : SELECT_ACL, table, + FALSE, 1, FALSE)) + return true; + *updated_arg|= updated; + /* We only need SELECT privilege for columns in the values list. */ + if (!table->derived) + { + table->grant.want_privilege= SELECT_ACL & ~table->grant.privilege; + table->table->grant.want_privilege= (SELECT_ACL & + ~table->table->grant.privilege); + } + } + return false; +} + class Multiupdate_prelocking_strategy : public DML_prelocking_strategy { @@ -1266,10 +1412,9 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd) while ((tl= ti++)) { TABLE *table= tl->table; - /* Only set timestamp column if this is not modified */ - if (table->timestamp_field && - bitmap_is_set(table->write_set, table->timestamp_field->field_index)) - table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET; + + if (tl->is_jtbm()) + continue; /* if table will be updated then check that it is unique */ if (table->map & tables_for_update) @@ -1303,23 +1448,47 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd) another table instance used by this statement which is going to be write-locked (for example, trigger to be invoked might try to update this table). + Last argument routine_modifies_data for read_lock_type_for_table() + is ignored, as prelocking placeholder will never be set here. */ + DBUG_ASSERT(tl->prelocking_placeholder == false); + thr_lock_type lock_type= read_lock_type_for_table(thd, lex, tl, true); if (using_lock_tables) - tl->lock_type= read_lock_type_for_table(thd, lex, tl); + tl->lock_type= lock_type; else - tl->set_lock_type(thd, read_lock_type_for_table(thd, lex, tl)); + tl->set_lock_type(thd, lock_type); } } + /* + Check access privileges for tables being updated or read. + Note that unlike in the above loop we need to iterate here not only + through all leaf tables but also through all view hierarchy. + */ + + for (tl= table_list; tl; tl= tl->next_local) + { + bool not_used= false; + if (tl->is_jtbm()) + continue; + if (multi_update_check_table_access(thd, tl, tables_for_update, ¬_used)) + DBUG_RETURN(TRUE); + } + /* check single table update for view compound from several tables */ for (tl= table_list; tl; tl= tl->next_local) { - TABLE_LIST *for_update= 0; - if (tl->is_merged_derived() && - tl->check_single_table(&for_update, tables_for_update, tl)) + if (tl->is_jtbm()) + continue; + if (tl->is_merged_derived()) { - my_error(ER_VIEW_MULTIUPDATE, MYF(0), tl->view_db.str, tl->view_name.str); - DBUG_RETURN(1); + TABLE_LIST *for_update= 0; + if (tl->check_single_table(&for_update, tables_for_update, tl)) + { + my_error(ER_VIEW_MULTIUPDATE, MYF(0), + tl->view_db.str, tl->view_name.str); + DBUG_RETURN(-1); + } } } @@ -1370,19 +1539,6 @@ int mysql_multi_update_prepare(THD *thd) DBUG_RETURN(TRUE); } - for (tl= table_list; tl; tl= tl->next_local) - { - /* Check access privileges for table */ - if (!tl->is_derived()) - { - uint want_privilege= tl->updating ? UPDATE_ACL : SELECT_ACL; - if (check_access(thd, want_privilege, tl->db, &tl->grant.privilege, - &tl->grant.m_internal, 0, 0) || - check_grant(thd, want_privilege, tl, FALSE, 1, FALSE)) - DBUG_RETURN(TRUE); - } - } - /* now lock and fill tables */ if (!thd->stmt_arena->is_stmt_prepare() && lock_tables(thd, table_list, table_count, 0)) @@ -1400,6 +1556,8 @@ int mysql_multi_update_prepare(THD *thd) List_iterator<TABLE_LIST> ti(lex->select_lex.leaf_tables); while ((tl= ti++)) { + if (tl->is_jtbm()) + continue; TABLE *table= tl->table; TABLE_LIST *tlist; if (!(tlist= tl->top_table())->derived) @@ -1446,18 +1604,15 @@ bool mysql_multi_update(THD *thd, TABLE_LIST *table_list, List<Item> *fields, { bool res; DBUG_ENTER("mysql_multi_update"); - + if (!(*result= new multi_update(table_list, - &thd->lex->select_lex.leaf_tables, - fields, values, handle_duplicates, ignore))) + &thd->lex->select_lex.leaf_tables, + fields, values, handle_duplicates, ignore))) { DBUG_RETURN(TRUE); } - thd->abort_on_warning= test(thd->variables.sql_mode & - (MODE_STRICT_TRANS_TABLES | - MODE_STRICT_ALL_TABLES)); - + thd->abort_on_warning= !ignore && thd->is_strict_mode(); List<Item> total_list; res= mysql_select(thd, &select_lex->ref_pointer_array, table_list, @@ -1471,6 +1626,11 @@ bool mysql_multi_update(THD *thd, TABLE_LIST *table_list, List<Item> *fields, res|= thd->is_error(); if (unlikely(res)) (*result)->abort_result_set(); + else + { + if (thd->lex->describe) + res= thd->lex->explain->send_explain(thd); + } thd->abort_on_warning= 0; DBUG_RETURN(res); } @@ -1485,7 +1645,7 @@ multi_update::multi_update(TABLE_LIST *table_list, tmp_tables(0), updated(0), found(0), fields(field_list), values(value_list), table_count(0), copy_field(0), handle_duplicates(handle_duplicates_arg), do_update(1), trans_safe(1), - transactional_tables(0), ignore(ignore_arg), error_handled(0) + transactional_tables(0), ignore(ignore_arg), error_handled(0), prepared(0) {} @@ -1508,9 +1668,13 @@ int multi_update::prepare(List<Item> ¬_used_values, List_iterator<TABLE_LIST> ti(*leaves); DBUG_ENTER("multi_update::prepare"); + if (prepared) + DBUG_RETURN(0); + prepared= true; + thd->count_cuted_fields= CHECK_FIELD_WARN; thd->cuted_fields=0L; - thd_proc_info(thd, "updating main table"); + THD_STAGE_INFO(thd, stage_updating_main_table); tables_to_update= get_table_map(fields); @@ -1527,6 +1691,9 @@ int multi_update::prepare(List<Item> ¬_used_values, */ while ((table_ref= ti++)) { + if (table_ref->is_jtbm()) + continue; + TABLE *table= table_ref->table; if (tables_to_update & table->map) { @@ -1546,6 +1713,9 @@ int multi_update::prepare(List<Item> ¬_used_values, ti.rewind(); while ((table_ref= ti++)) { + if (table_ref->is_jtbm()) + continue; + TABLE *table= table_ref->table; if (tables_to_update & table->map) { @@ -1557,8 +1727,7 @@ int multi_update::prepare(List<Item> ¬_used_values, to compare records and detect data change. */ if ((table->file->ha_table_flags() & HA_PARTIAL_COLUMN_READ) && - (((uint) table->timestamp_field_type) & - TIMESTAMP_AUTO_SET_ON_UPDATE)) + table->default_field && table->has_default_function(true)) bitmap_union(table->read_set, table->write_set); } } @@ -1577,6 +1746,8 @@ int multi_update::prepare(List<Item> ¬_used_values, while ((table_ref= ti++)) { /* TODO: add support of view of join support */ + if (table_ref->is_jtbm()) + continue; TABLE *table=table_ref->table; leaf_table_count++; if (tables_to_update & table->map) @@ -1590,17 +1761,8 @@ int multi_update::prepare(List<Item> ¬_used_values, table->no_keyread=1; table->covering_keys.clear_all(); table->pos_in_table_list= tl; - if (table->triggers && - table->triggers->has_triggers(TRG_EVENT_UPDATE, - TRG_ACTION_AFTER)) - { - /* - The table has AFTER UPDATE triggers that might access to subject - table and therefore might need update to be done immediately. - So we turn-off the batching. - */ - (void) table->file->extra(HA_EXTRA_UPDATE_CANNOT_BATCH); - } + table->prepare_triggers_for_update_stmt_or_event(); + table->reset_default_fields(); } } @@ -1836,6 +1998,13 @@ loop_end: TABLE *tbl= table; do { + /* + Signal each table (including tables referenced by WITH CHECK OPTION + clause) for which we will store row position in the temporary table + that we need a position to be read first. + */ + tbl->prepare_for_position(); + Field_string *field= new Field_string(tbl->file->ref_length, 0, tbl->alias.c_ptr(), &my_charset_bin); @@ -1946,9 +2115,8 @@ int multi_update::send_data(List<Item> ¬_used_values) table->status|= STATUS_UPDATED; store_record(table,record[1]); - if (fill_record_n_invoke_before_triggers(thd, *fields_for_table[offset], + if (fill_record_n_invoke_before_triggers(thd, table, *fields_for_table[offset], *values_for_table[offset], 0, - table->triggers, TRG_EVENT_UPDATE)) DBUG_RETURN(1); @@ -1961,6 +2129,10 @@ int multi_update::send_data(List<Item> ¬_used_values) if (!can_compare_record || compare_record(table)) { int error; + + if (table->default_field && table->update_default_fields()) + DBUG_RETURN(1); + if ((error= cur_table->view_check_option(thd, ignore)) != VIEW_CHECK_OK) { @@ -2050,7 +2222,7 @@ int multi_update::send_data(List<Item> ¬_used_values) } while ((tbl= tbl_it++)); /* Store regular updated fields in the row. */ - fill_record(thd, + fill_record(thd, tmp_table, tmp_table->field + 1 + unupdated_check_opt_tables.elements, *values_for_table[offset], TRUE, FALSE); @@ -2075,13 +2247,6 @@ int multi_update::send_data(List<Item> ¬_used_values) } -void multi_update::send_error(uint errcode,const char *err) -{ - /* First send error what ever it is ... */ - my_error(errcode, MYF(0), err); -} - - void multi_update::abort_result_set() { /* the error was handled or nothing deleted and no side effects return */ @@ -2168,7 +2333,7 @@ int multi_update::do_updates() check_opt_it.rewind(); while(TABLE *tbl= check_opt_it++) { - if ((local_error= tbl->file->ha_rnd_init(1))) + if ((local_error= tbl->file->ha_rnd_init(0))) { err_table= tbl; goto err; @@ -2240,7 +2405,10 @@ int multi_update::do_updates() for (copy_field_ptr=copy_field; copy_field_ptr != copy_field_end; copy_field_ptr++) + { (*copy_field_ptr->do_copy)(copy_field_ptr); + copy_field_ptr->to_field->set_has_explicit_value(); + } if (table->triggers && table->triggers->process_triggers(thd, TRG_EVENT_UPDATE, @@ -2250,6 +2418,8 @@ int multi_update::do_updates() if (!can_compare_record || compare_record(table)) { int error; + if (table->default_field && (error= table->update_default_fields())) + goto err2; if (table->vfield && update_virtual_fields(thd, table, VCOL_UPDATE_FOR_WRITE)) goto err2; @@ -2346,7 +2516,7 @@ bool multi_update::send_eof() ulonglong id; killed_state killed_status= NOT_KILLED; DBUG_ENTER("multi_update::send_eof"); - thd_proc_info(thd, "updating reference tables"); + THD_STAGE_INFO(thd, stage_updating_reference_tables); /* Does updates for the last n - 1 tables, returns 0 if ok; @@ -2360,7 +2530,7 @@ bool multi_update::send_eof() later carried out killing should not affect binlogging. */ killed_status= (local_error == 0) ? NOT_KILLED : thd->killed; - thd_proc_info(thd, "end"); + THD_STAGE_INFO(thd, stage_end); /* We must invalidate the query cache before binlog writing and ha_autocommit_... */ @@ -2402,12 +2572,12 @@ bool multi_update::send_eof() thd->transaction.stmt.modified_non_trans_table); if (local_error != 0) - error_handled= TRUE; // to force early leave from ::send_error() + error_handled= TRUE; // to force early leave from ::abort_result_set() if (local_error > 0) // if the above log write did not fail ... { /* Safety: If we haven't got an error before (can happen in do_updates) */ - my_message(ER_UNKNOWN_ERROR, "An error occured in multi-table update", + my_message(ER_UNKNOWN_ERROR, "An error occurred in multi-table update", MYF(0)); DBUG_RETURN(TRUE); } |