diff options
Diffstat (limited to 'sql/sql_update.cc')
-rw-r--r-- | sql/sql_update.cc | 757 |
1 files changed, 357 insertions, 400 deletions
diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 6acea2d5..589bcc9c 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -45,6 +45,9 @@ #include "sql_insert.h" // For vers_insert_history_row() that may be // needed for System Versioning. +#ifdef WITH_WSREP +#include "wsrep_mysqld.h" +#endif /** True if the table's input and output record buffers are comparable using @@ -197,22 +200,11 @@ static bool check_fields(THD *thd, TABLE_LIST *table, List<Item> &items, return true; } - DBUG_ASSERT(thd->lex->sql_command == SQLCOM_UPDATE); - for (List_iterator_fast<Item> it(items); (item=it++);) - { - Field *f= item->field_for_view_update()->field; - vers_select_conds_t &period= table->period_conditions; - if (period.field_start->field == f || period.field_end->field == f) - { - my_error(ER_PERIOD_COLUMNS_UPDATED, MYF(0), - item->name.str, period.name.str); - return true; - } - } } return FALSE; } + bool TABLE::vers_check_update(List<Item> &items) { List_iterator<Item> it(items); @@ -344,36 +336,25 @@ int cut_fields_for_portion_of_time(THD *thd, TABLE *table, return res; } -/* - Process usual UPDATE - - SYNOPSIS - mysql_update() - thd thread handler - fields fields for update - values values of fields for update - conds WHERE clause expression - order_num number of elemen in ORDER BY clause - order ORDER BY clause list - limit limit clause +/** + @brief Special handling of single-table updates after prepare phase - RETURN - 0 - OK - 2 - privilege check and openning table passed, but we need to convert to - multi-update because of view substitution - 1 - error + @param thd global context the processed statement + @returns false on success, true on error */ -int mysql_update(THD *thd, - TABLE_LIST *table_list, - List<Item> &fields, - List<Item> &values, - COND *conds, - uint order_num, ORDER *order, - ha_rows limit, - bool ignore, - ha_rows *found_return, ha_rows *updated_return) +bool Sql_cmd_update::update_single_table(THD *thd) { + SELECT_LEX_UNIT *unit = &lex->unit; + SELECT_LEX *select_lex= unit->first_select(); + TABLE_LIST *const table_list = select_lex->get_table_list(); + List<Item> *fields= &select_lex->item_list; + List<Item> *values= &lex->value_list; + COND *conds= select_lex->where_cond_after_prepare; + ORDER *order= select_lex->order_list.first; + ha_rows limit= unit->lim.get_select_limit(); + bool ignore= lex->ignore; + bool using_limit= limit != HA_POS_ERROR; bool safe_update= (thd->variables.option_bits & OPTION_SAFE_UPDATES) && !thd->lex->describe; @@ -385,76 +366,38 @@ int mysql_update(THD *thd, ha_rows dup_key_found; bool need_sort= TRUE; bool reverse= FALSE; -#ifndef NO_EMBEDDED_ACCESS_CHECKS - privilege_t want_privilege(NO_ACL); -#endif - uint table_count= 0; ha_rows updated, updated_or_same, found; key_map old_covering_keys; TABLE *table; SQL_SELECT *select= NULL; SORT_INFO *file_sort= 0; READ_RECORD info; - SELECT_LEX *select_lex= thd->lex->first_select_lex(); ulonglong id; List<Item> all_fields; killed_state killed_status= NOT_KILLED; bool has_triggers, binlog_is_row, do_direct_update= FALSE; Update_plan query_plan(thd->mem_root); Explain_update *explain; - TABLE_LIST *update_source_table; query_plan.index= MAX_KEY; query_plan.using_filesort= FALSE; // For System Versioning (may need to insert new fields to a table). ha_rows rows_inserted= 0; - DBUG_ENTER("mysql_update"); + DBUG_ENTER("Sql_cmd_update::update_single_table"); - create_explain_query(thd->lex, thd->mem_root); - if (open_tables(thd, &table_list, &table_count, 0)) - DBUG_RETURN(1); - - /* Prepare views so they are handled correctly */ - if (mysql_handle_derived(thd->lex, DT_INIT)) - DBUG_RETURN(1); - - if (table_list->has_period() && table_list->is_view_or_derived()) - { - my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); - DBUG_RETURN(TRUE); - } - - if (((update_source_table=unique_table(thd, table_list, - table_list->next_global, 0)) || - table_list->is_multitable())) - { - DBUG_ASSERT(update_source_table || table_list->view != 0); - DBUG_PRINT("info", ("Switch to multi-update")); - /* pass counter value */ - thd->lex->table_count_update= table_count; - if (thd->lex->period_conditions.is_set()) - { - my_error(ER_NOT_SUPPORTED_YET, MYF(0), - "updating and querying the same temporal periods table"); - - DBUG_RETURN(1); - } + THD_STAGE_INFO(thd, stage_init_update); - /* convert to multiupdate */ - DBUG_RETURN(2); - } - if (lock_tables(thd, table_list, table_count, 0)) - DBUG_RETURN(1); + thd->table_map_for_update= 0; - (void) read_statistics_for_tables_if_needed(thd, table_list); - - THD_STAGE_INFO(thd, stage_init_update); if (table_list->handle_derived(thd->lex, DT_MERGE_FOR_INSERT)) DBUG_RETURN(1); if (table_list->handle_derived(thd->lex, DT_PREPARE)) DBUG_RETURN(1); + if (setup_ftfuncs(select_lex)) + DBUG_RETURN(1); + table= table_list->table; if (!table_list->single_table_updatable()) @@ -463,89 +406,26 @@ int mysql_update(THD *thd, DBUG_RETURN(1); } - /* Calculate "table->covering_keys" based on the WHERE */ - table->covering_keys= table->s->keys_in_use; table->opt_range_keys.clear_all(); query_plan.select_lex= thd->lex->first_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 : - table_list->grant.want_privilege); -#endif thd->lex->promote_select_describe_flag_if_needed(); - if (mysql_prepare_update(thd, table_list, &conds, order_num, order)) - DBUG_RETURN(1); - - if (table_list->has_period()) - { - if (!table_list->period_conditions.start.item->const_item() - || !table_list->period_conditions.end.item->const_item()) - { - my_error(ER_NOT_CONSTANT_EXPRESSION, MYF(0), "FOR PORTION OF"); - DBUG_RETURN(true); - } - table->no_cache= true; - } - old_covering_keys= table->covering_keys; // Keys used in WHERE - /* Check the fields we are going to modify */ -#ifndef NO_EMBEDDED_ACCESS_CHECKS - table_list->grant.want_privilege= table->grant.want_privilege= want_privilege; - table_list->register_want_access(want_privilege); -#endif - /* 'Unfix' fields to allow correct marking by the setup_fields function. */ - if (table_list->is_view()) - unfix_fields(fields); - if (setup_fields_with_no_wrap(thd, Ref_ptr_array(), - fields, MARK_COLUMNS_WRITE, 0, 0)) - DBUG_RETURN(1); /* purecov: inspected */ - if (check_fields(thd, table_list, fields, table_list->view)) - { - DBUG_RETURN(1); - } - bool has_vers_fields= table->vers_check_update(fields); - if (check_key_in_view(thd, table_list)) - { - my_error(ER_NON_UPDATABLE_TABLE, MYF(0), table_list->alias.str, "UPDATE"); - DBUG_RETURN(1); - } + bool has_vers_fields= table->vers_check_update(*fields); if (table->default_field) table->mark_default_fields_for_write(false); -#ifndef NO_EMBEDDED_ACCESS_CHECKS - /* Check values */ - table_list->grant.want_privilege= table->grant.want_privilege= - (SELECT_ACL & ~table->grant.privilege); -#endif - if (setup_fields(thd, Ref_ptr_array(), values, MARK_COLUMNS_READ, 0, NULL, 0)) - { - free_underlaid_joins(thd, select_lex); - DBUG_RETURN(1); /* purecov: inspected */ - } - - if (table_list->table->check_assignability_explicit_fields(fields, values, - ignore)) - DBUG_RETURN(true); - - if (check_unique_table(thd, table_list)) - DBUG_RETURN(TRUE); - - switch_to_nullable_trigger_fields(fields, table); - switch_to_nullable_trigger_fields(values, table); + switch_to_nullable_trigger_fields(*fields, table); + switch_to_nullable_trigger_fields(*values, table); /* Apply the IN=>EXISTS transformation to all subqueries and optimize them */ if (select_lex->optimize_unflattened_subqueries(false)) DBUG_RETURN(TRUE); - if (select_lex->inner_refs_list.elements && - fix_inner_refs(thd, all_fields, select_lex, select_lex->ref_pointer_array)) - DBUG_RETURN(1); - if (conds) { Item::cond_result cond_value; @@ -558,6 +438,18 @@ int mysql_update(THD *thd, goto produce_explain_and_leave; } } + if (conds && thd->lex->are_date_funcs_used()) + { + /* Rewrite datetime comparison conditions into sargable */ + conds= conds->top_level_transform(thd, &Item::date_conds_transformer, + (uchar *) 0); + } + + if (conds && optimizer_flag(thd, OPTIMIZER_SWITCH_SARGABLE_CASEFOLD)) + { + conds= conds->top_level_transform(thd, &Item::varchar_upper_cmp_transformer, + (uchar *) 0); + } // Don't count on usage of 'only index' when calculating which key to use table->covering_keys.clear_all(); @@ -586,9 +478,9 @@ int mysql_update(THD *thd, set_statistics_for_table(thd, table); select= make_select(table, 0, 0, conds, (SORT_INFO*) 0, 0, &error); - if (unlikely(error || !limit || thd->is_error() || - (select && select->check_quick(thd, safe_update, limit, - Item_func::BITMAP_ALL)))) + if (error || !limit || thd->is_error() || table->stat_records() == 0 || + (select && select->check_quick(thd, safe_update, limit, + Item_func::BITMAP_ALL))) { query_plan.set_impossible_where(); if (thd->lex->describe || thd->lex->analyze_stmt) @@ -786,9 +678,9 @@ int mysql_update(THD *thd, } if (use_direct_update && - !table->file->info_push(INFO_KIND_UPDATE_FIELDS, &fields) && - !table->file->info_push(INFO_KIND_UPDATE_VALUES, &values) && - !table->file->direct_update_rows_init(&fields)) + !table->file->info_push(INFO_KIND_UPDATE_FIELDS, fields) && + !table->file->info_push(INFO_KIND_UPDATE_VALUES, values) && + !table->file->direct_update_rows_init(fields)) { do_direct_update= TRUE; @@ -819,7 +711,6 @@ int mysql_update(THD *thd, if (!(file_sort= filesort(thd, table, &fsort, fs_tracker))) goto err; - thd->inc_examined_row_count(file_sort->examined_rows); /* Filesort has already found and selected the rows we want to update, @@ -839,15 +730,15 @@ int mysql_update(THD *thd, table->use_all_columns(); /* - We are doing a search on a key that is updated. In this case - we go trough the matching rows, save a pointer to them and - update these in a separate loop based on the pointer. + We are doing a search on a key that is updated. In this case + we go trough the matching rows, save a pointer to them and + update these in a separate loop based on the pointer. */ explain->buf_tracker.on_scan_init(); IO_CACHE tempfile; if (open_cached_file(&tempfile, mysql_tmpdir,TEMP_PREFIX, - DISK_BUFFER_SIZE, MYF(MY_WME))) - goto err; + DISK_CHUNK_SIZE, MYF(MY_WME))) + goto err; /* If quick select is used, initialize it before retrieving rows. */ if (select && select->quick && select->quick->reset()) @@ -887,7 +778,7 @@ int mysql_update(THD *thd, while (likely(!(error=info.read_record())) && likely(!thd->killed)) { explain->buf_tracker.on_record_read(); - thd->inc_examined_row_count(1); + thd->inc_examined_row_count(); if (!select || (error= select->skip_record(thd)) > 0) { if (table->file->ha_was_semi_consistent_read()) @@ -1024,7 +915,7 @@ update_begin: while (!(error=info.read_record()) && !thd->killed) { explain->tracker.on_record_read(); - thd->inc_examined_row_count(1); + thd->inc_examined_row_count(); if (!select || select->skip_record(thd) > 0) { if (table->file->ha_was_semi_consistent_read()) @@ -1037,7 +928,7 @@ update_begin: cut_fields_for_portion_of_time(thd, table, table_list->period_conditions); - if (fill_record_n_invoke_before_triggers(thd, table, fields, values, 0, + if (fill_record_n_invoke_before_triggers(thd, table, *fields, *values, 0, TRG_EVENT_UPDATE)) break; /* purecov: inspected */ @@ -1370,9 +1261,9 @@ update_end: thd->lex->current_select->save_leaf_tables(thd); thd->lex->current_select->first_cond_optimization= 0; } - *found_return= found; - *updated_return= updated; - + ((multi_update *)result)->set_found(found); + ((multi_update *)result)->set_updated(updated); + if (unlikely(thd->lex->analyze_stmt)) goto emit_explain_and_leave; @@ -1405,77 +1296,6 @@ emit_explain_and_leave: DBUG_RETURN((err2 || thd->is_error()) ? 1 : 0); } -/* - Prepare items in UPDATE statement - - SYNOPSIS - mysql_prepare_update() - thd - thread handler - table_list - global/local table list - conds - conditions - order_num - number of ORDER BY list entries - order - ORDER BY clause list - - RETURN VALUE - FALSE OK - TRUE error -*/ -bool mysql_prepare_update(THD *thd, TABLE_LIST *table_list, - Item **conds, uint order_num, ORDER *order) -{ - Item *fake_conds= 0; -#ifndef NO_EMBEDDED_ACCESS_CHECKS - TABLE *table= table_list->table; -#endif - List<Item> all_fields; - SELECT_LEX *select_lex= thd->lex->first_select_lex(); - DBUG_ENTER("mysql_prepare_update"); - -#ifndef NO_EMBEDDED_ACCESS_CHECKS - table_list->grant.want_privilege= table->grant.want_privilege= - (SELECT_ACL & ~table->grant.privilege); - table_list->register_want_access(SELECT_ACL); -#endif - - thd->lex->allow_sum_func.clear_all(); - - if (table_list->has_period() && - select_lex->period_setup_conds(thd, table_list)) - DBUG_RETURN(true); - - DBUG_ASSERT(table_list->table); - // conds could be cached from previous SP call - DBUG_ASSERT(!table_list->vers_conditions.need_setup() || - !*conds || thd->stmt_arena->is_stmt_execute()); - if (select_lex->vers_setup_conds(thd, table_list)) - DBUG_RETURN(TRUE); - - *conds= select_lex->where; - - /* - We do not call DT_MERGE_FOR_INSERT because it has no sense for simple - (not multi-) update - */ - if (mysql_handle_derived(thd->lex, DT_PREPARE)) - DBUG_RETURN(TRUE); - - 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, TRUE) || - setup_conds(thd, table_list, select_lex->leaf_tables, conds) || - select_lex->setup_ref_array(thd, order_num) || - setup_order(thd, select_lex->ref_pointer_array, - table_list, all_fields, all_fields, order) || - setup_ftfuncs(select_lex)) - DBUG_RETURN(TRUE); - - - select_lex->fix_prepare_information(thd, conds, &fake_conds); - if (!thd->lex->upd_del_where) - thd->lex->upd_del_where= *conds; - DBUG_RETURN(FALSE); -} /** Check that we are not using table that we are updating in a sub select @@ -1686,6 +1506,14 @@ static bool multi_update_check_table_access(THD *thd, TABLE_LIST *table, else { /* Must be a base or derived table. */ + /* + Derived tables do not need the check below. + Besides one have take into account that for mergeable derived tables + TABLE_LIST::TABLE is set to NULL after the first execution of the query. + */ + if (table->is_derived()) + return false; + const bool updated= table->table->map & tables_for_update; if (check_table_access(thd, updated ? UPDATE_ACL : SELECT_ACL, table, FALSE, 1, FALSE)) @@ -1703,15 +1531,6 @@ static bool multi_update_check_table_access(THD *thd, TABLE_LIST *table, } -class Multiupdate_prelocking_strategy : public DML_prelocking_strategy -{ - bool done; - bool has_prelocking_list; -public: - void reset(THD *thd); - bool handle_end(THD *thd); -}; - void Multiupdate_prelocking_strategy::reset(THD *thd) { done= false; @@ -1741,7 +1560,13 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd) mysql_handle_derived(lex, DT_PREPARE)) DBUG_RETURN(1); - /* + if (table_list->has_period() && table_list->is_view_or_derived()) + { + my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); + DBUG_RETURN(TRUE); + } + + /* setup_tables() need for VIEWs. JOIN::prepare() will call setup_tables() second time, but this call will do nothing (there are check for second call in setup_tables()). @@ -1752,6 +1577,10 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd) FALSE, UPDATE_ACL, SELECT_ACL, TRUE)) DBUG_RETURN(1); + if (table_list->has_period() && + select_lex->period_setup_conds(thd, table_list)) + DBUG_RETURN(true); + List<Item> *fields= &lex->first_select_lex()->item_list; if (setup_fields_with_no_wrap(thd, Ref_ptr_array(), *fields, MARK_COLUMNS_WRITE, 0, 0)) @@ -1860,156 +1689,6 @@ bool Multiupdate_prelocking_strategy::handle_end(THD *thd) DBUG_RETURN(0); } -/* - make update specific preparation and checks after opening tables - - SYNOPSIS - mysql_multi_update_prepare() - thd thread handler - - RETURN - FALSE OK - TRUE Error -*/ - -int mysql_multi_update_prepare(THD *thd) -{ - LEX *lex= thd->lex; - TABLE_LIST *table_list= lex->query_tables; - TABLE_LIST *tl; - Multiupdate_prelocking_strategy prelocking_strategy; - uint table_count= lex->table_count_update; - DBUG_ENTER("mysql_multi_update_prepare"); - - /* - Open tables and create derived ones, but do not lock and fill them yet. - - 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, - thd->stmt_arena->is_stmt_prepare() ? MYSQL_OPEN_FORCE_SHARED_MDL : 0, - &prelocking_strategy)) - DBUG_RETURN(TRUE); - } - else - { - /* following need for prepared statements, to run next time multi-update */ - thd->lex->sql_command= SQLCOM_UPDATE_MULTI; - prelocking_strategy.reset(thd); - if (prelocking_strategy.handle_end(thd)) - DBUG_RETURN(TRUE); - } - - /* now lock and fill tables */ - if (!thd->stmt_arena->is_stmt_prepare() && - lock_tables(thd, table_list, table_count, 0)) - 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. */ - - /* - Check that we are not using table that we are updating, but we should - skip all tables of UPDATE SELECT itself - */ - lex->first_select_lex()->exclude_from_table_unique_test= TRUE; - /* We only need SELECT privilege for columns in the values list */ - List_iterator<TABLE_LIST> ti(lex->first_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) - { - tlist->grant.want_privilege= - (SELECT_ACL & ~tlist->grant.privilege); - table->grant.want_privilege= (SELECT_ACL & ~table->grant.privilege); - } - DBUG_PRINT("info", ("table: %s want_privilege: %llx", tl->alias.str, - (longlong) table->grant.want_privilege)); - } - /* - Set exclude_from_table_unique_test value back to FALSE. It is needed for - further check in multi_update::prepare whether to use record cache. - */ - lex->first_select_lex()->exclude_from_table_unique_test= FALSE; - - if (lex->save_prep_leaf_tables()) - DBUG_RETURN(TRUE); - - DBUG_RETURN (FALSE); -} - - -/* - Setup multi-update handling and call SELECT to do the join -*/ - -bool mysql_multi_update(THD *thd, TABLE_LIST *table_list, List<Item> *fields, - List<Item> *values, COND *conds, ulonglong options, - enum enum_duplicates handle_duplicates, - bool ignore, SELECT_LEX_UNIT *unit, - SELECT_LEX *select_lex, multi_update **result) -{ - bool res; - DBUG_ENTER("mysql_multi_update"); - - if (!(*result= new (thd->mem_root) multi_update(thd, table_list, - &thd->lex->first_select_lex()->leaf_tables, - fields, values, handle_duplicates, ignore))) - { - 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); - - res= mysql_select(thd, - table_list, total_list, conds, - select_lex->order_list.elements, - select_lex->order_list.first, NULL, NULL, NULL, - options | SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | - OPTION_SETUP_TABLES_DONE, - *result, unit, select_lex); - - DBUG_PRINT("info",("res: %d report_error: %d", res, (int) thd->is_error())); - res|= thd->is_error(); - if (unlikely(res)) - (*result)->abort_result_set(); - else - { - if (thd->lex->describe || thd->lex->analyze_stmt) - { - bool extended= thd->lex->describe & DESCRIBE_EXTENDED; - res= thd->lex->explain->send_explain(thd, extended); - } - } - thd->abort_on_warning= 0; - DBUG_RETURN(res); -} - multi_update::multi_update(THD *thd_arg, TABLE_LIST *table_list, List<TABLE_LIST> *leaves_list, @@ -2045,6 +1724,19 @@ bool multi_update::init(THD *thd) } +bool multi_update::init_for_single_table(THD *thd) +{ + List_iterator_fast<TABLE_LIST> li(*leaves); + TABLE_LIST *tbl; + while ((tbl =li++)) + { + 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 */ @@ -2120,7 +1812,8 @@ int multi_update::prepare(List<Item> ¬_used_values, { table->read_set= &table->def_read_set; bitmap_union(table->read_set, &table->tmp_set); - table->file->prepare_for_insert(1); + if (!(thd->lex->context_analysis_only & CONTEXT_ANALYSIS_ONLY_PREPARE)) + table->file->prepare_for_insert(1); } } if (unlikely(error)) @@ -2287,6 +1980,7 @@ static bool safe_update_on_fly(THD *thd, JOIN_TAB *join_tab, case JT_REF: case JT_REF_OR_NULL: return !is_key_used(table, join_tab->ref.key, table->write_set); + case JT_RANGE: case JT_ALL: if (bitmap_is_overlapping(&table->tmp_set, table->write_set)) return FALSE; @@ -3006,8 +2700,7 @@ int multi_update::do_updates() (void) tmp_table->file->ha_rnd_end(); check_opt_it.rewind(); while (TABLE *tbl= check_opt_it++) - tbl->file->ha_rnd_end(); - + tbl->file->ha_rnd_end(); } DBUG_RETURN(0); @@ -3146,3 +2839,267 @@ bool multi_update::send_eof() } DBUG_RETURN(FALSE); } + + +/** + @brief Check whether conversion to multi-table update is prohibited + + @param thd global context the processed statement + @returns true if conversion is prohibited, false otherwise + + @todo + Introduce handler level flag for storage engines that would prohibit + such conversion for any single-table update. +*/ + +bool Sql_cmd_update::processing_as_multitable_update_prohibited(THD *thd) +{ + SELECT_LEX *const select_lex = thd->lex->first_select_lex(); + return + (select_lex->order_list.elements && + select_lex->limit_params.select_limit); +} + + +/** + @brief Perform precheck of table privileges for update statements + + @param thd global context the processed statement + @returns false on success, true on error +*/ + +bool Sql_cmd_update::precheck(THD *thd) +{ + if (!multitable) + { + if (update_precheck(thd, lex->query_tables)) + return true; + } + else + { + if (multi_update_precheck(thd, lex->query_tables)) + return true; + } + +#ifdef WITH_WSREP + WSREP_SYNC_WAIT(thd, WSREP_SYNC_WAIT_BEFORE_UPDATE_DELETE); +#endif + + return false; + +#ifdef WITH_WSREP +wsrep_error_label: +#endif + return true; +} + + +/** + @brief Perform context analysis for update statements + + @param thd global context the processed statement + @returns false on success, true on error + + @note + The main bulk of the context analysis actions for and update statement + is performed by a call of JOIN::prepare(). +*/ + +bool Sql_cmd_update::prepare_inner(THD *thd) +{ + JOIN *join; + int err= 0; + SELECT_LEX *const select_lex = thd->lex->first_select_lex(); + TABLE_LIST *const table_list = select_lex->get_table_list(); + ulonglong select_options= select_lex->options; + bool free_join= 1; + DBUG_ENTER("Sql_cmd_update::prepare_inner"); + + (void) read_statistics_for_tables_if_needed(thd, table_list); + + THD_STAGE_INFO(thd, stage_init_update); + + if (!multitable) + { + if (mysql_handle_derived(lex, DT_INIT)) + DBUG_RETURN(TRUE); + } + + if (table_list->has_period() && table_list->is_view_or_derived()) + { + my_error(ER_IT_IS_A_VIEW, MYF(0), table_list->table_name.str); + DBUG_RETURN(TRUE); + } + + if (!multitable) + { + TABLE_LIST *update_source_table= 0; + + if (((update_source_table=unique_table(thd, table_list, + table_list->next_global, 0)) || + table_list->is_multitable())) + { + DBUG_ASSERT(update_source_table || table_list->view != 0); + if (thd->lex->period_conditions.is_set()) + { + my_error(ER_NOT_SUPPORTED_YET, MYF(0), + "updating and querying the same temporal periods table"); + DBUG_RETURN(TRUE); + } + if (!table_list->is_multitable() && + !processing_as_multitable_update_prohibited(thd)) + multitable= true; + } + } + + if(!multitable) + { + if (table_list->is_view_or_derived() && + select_lex->leaf_tables.elements > 1) + multitable = true; + } + + if (!multitable) + { + if (lex->ignore) + lex->set_stmt_unsafe(LEX::BINLOG_STMT_UNSAFE_UPDATE_IGNORE); + } + + if (!(result= new (thd->mem_root) multi_update(thd, table_list, + &select_lex->leaf_tables, + &select_lex->item_list, + &lex->value_list, + lex->duplicates, + lex->ignore))) + { + DBUG_RETURN(TRUE); + } + + if (((multi_update *)result)->init(thd)) + DBUG_RETURN(TRUE); + + if (setup_tables(thd, &select_lex->context, &select_lex->top_join_list, + table_list, select_lex->leaf_tables, false, false)) + DBUG_RETURN(TRUE); + + if (select_lex->vers_setup_conds(thd, table_list)) + DBUG_RETURN(TRUE); + + { + if (thd->lex->describe) + select_options|= SELECT_DESCRIBE; + + /* + When in EXPLAIN, delay deleting the joins so that they are still + available when we're producing EXPLAIN EXTENDED warning text. + */ + if (select_options & SELECT_DESCRIBE) + free_join= 0; + + select_options|= + SELECT_NO_JOIN_CACHE | SELECT_NO_UNLOCK | OPTION_SETUP_TABLES_DONE; + + if (!(join= new (thd->mem_root) JOIN(thd, select_lex->item_list, + select_options, result))) + DBUG_RETURN(TRUE); + THD_STAGE_INFO(thd, stage_init); + select_lex->join= join; + thd->lex->used_tables=0; + select_lex->item_list_usage= MARK_COLUMNS_WRITE; + if ((err= join->prepare(table_list, select_lex->where, + select_lex->order_list.elements, + select_lex->order_list.first, + false, NULL, NULL, NULL, + select_lex, &lex->unit))) + { + goto err; + } + if (!multitable && + select_lex->sj_subselects.elements) + multitable= true; + } + + if (table_list->has_period()) + { + Item *item; + for (List_iterator_fast<Item> it(select_lex->item_list); (item=it++);) + { + Field *f= item->field_for_view_update()->field; + vers_select_conds_t &period= table_list->period_conditions; + if (period.field_start->field == f || period.field_end->field == f) + { + my_error(ER_PERIOD_COLUMNS_UPDATED, MYF(0), + item->name.str, period.name.str); + DBUG_RETURN(true); + } + } + + if (!table_list->period_conditions.start.item->const_item() + || !table_list->period_conditions.end.item->const_item()) + { + my_error(ER_NOT_CONSTANT_EXPRESSION, MYF(0), "FOR PORTION OF"); + DBUG_RETURN(true); + } + table_list->table->no_cache= true; + } + + + free_join= false; + +err: + + if (free_join) + { + THD_STAGE_INFO(thd, stage_end); + err|= (int)(select_lex->cleanup()); + DBUG_RETURN(err || thd->is_error()); + } + DBUG_RETURN(err); + +} + + +/** + @brief Perform optimization and execution actions needed for updates + + @param thd global context the processed statement + @returns false on success, true on error +*/ + +bool Sql_cmd_update::execute_inner(THD *thd) +{ + bool res= 0; + + thd->get_stmt_da()->reset_current_row_for_warning(1); + if (!multitable) + res= update_single_table(thd); + else + { + thd->abort_on_warning= !thd->lex->ignore && thd->is_strict_mode(); + res= Sql_cmd_dml::execute_inner(thd); + } + + res|= thd->is_error(); + if (multitable) + { + if (unlikely(res)) + result->abort_result_set(); + else + { + if (thd->lex->describe || thd->lex->analyze_stmt) + { + bool extended= thd->lex->describe & DESCRIBE_EXTENDED; + res= thd->lex->explain->send_explain(thd, extended); + } + } + } + + if (result) + { + res= false; + delete result; + } + + status_var_add(thd->status_var.rows_sent, thd->get_sent_row_count()); + return res; +} |