summaryrefslogtreecommitdiff
path: root/sql/sql_update.cc
diff options
context:
space:
mode:
Diffstat (limited to 'sql/sql_update.cc')
-rw-r--r--sql/sql_update.cc460
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, &not_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> &not_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> &not_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> &not_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> &not_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> &not_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> &not_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> &not_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> &not_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> &not_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> &not_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);
}