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.cc129
1 files changed, 117 insertions, 12 deletions
diff --git a/sql/sql_update.cc b/sql/sql_update.cc
index 039aa0f71fa..f6834117d24 100644
--- a/sql/sql_update.cc
+++ b/sql/sql_update.cc
@@ -119,10 +119,10 @@ int mysql_update(THD *thd,
{
bool using_limit= limit != HA_POS_ERROR;
bool safe_update= thd->options & OPTION_SAFE_UPDATES;
- bool used_key_is_modified, transactional_table;
+ bool used_key_is_modified, transactional_table, will_batch;
int res;
- int error;
- uint used_index= MAX_KEY;
+ int error, loc_error;
+ uint used_index= MAX_KEY, dup_key_found;
bool need_sort= TRUE;
#ifndef NO_EMBEDDED_ACCESS_CHECKS
uint want_privilege;
@@ -197,7 +197,11 @@ int mysql_update(THD *thd,
table_list->grant.want_privilege= table->grant.want_privilege= want_privilege;
table_list->register_want_access(want_privilege);
#endif
- if (setup_fields_with_no_wrap(thd, 0, fields, 1, 0, 0))
+ /*
+ Indicate that the set of fields is to be updated by passing 2 for
+ set_query_id.
+ */
+ if (setup_fields_with_no_wrap(thd, 0, fields, 2, 0, 0))
DBUG_RETURN(1); /* purecov: inspected */
if (table_list->view && check_fields(thd, fields))
{
@@ -214,7 +218,10 @@ int mysql_update(THD *thd,
if (table->timestamp_field->query_id == thd->query_id)
table->timestamp_field_type= TIMESTAMP_NO_AUTO_SET;
else
+ {
table->timestamp_field->query_id=timestamp_query_id;
+ table->file->ha_set_bit_in_write_set(table->timestamp_field->fieldnr);
+ }
}
#ifndef NO_EMBEDDED_ACCESS_CHECKS
@@ -284,13 +291,18 @@ int mysql_update(THD *thd,
used_key_is_modified= check_if_key_used(table, used_index, fields);
}
+#ifdef WITH_PARTITION_STORAGE_ENGINE
+ if (used_key_is_modified || order ||
+ partition_key_modified(table, fields))
+#else
if (used_key_is_modified || order)
+#endif
{
/*
We can't update table directly; We must first search after all
matching rows before updating the table!
*/
- table->file->extra(HA_EXTRA_RETRIEVE_ALL_COLS);
+ table->file->ha_retrieve_all_cols();
if (used_index < MAX_KEY && old_used_keys.is_set(used_index))
{
table->key_read=1;
@@ -422,7 +434,7 @@ int mysql_update(THD *thd,
(thd->variables.sql_mode &
(MODE_STRICT_TRANS_TABLES |
MODE_STRICT_ALL_TABLES)));
-
+ will_batch= !table->file->start_bulk_update();
while (!(error=info.read_record(&info)) && !thd->killed)
{
if (!(select && select->skip_record()))
@@ -449,8 +461,47 @@ int mysql_update(THD *thd,
break;
}
}
- if (!(error=table->file->update_row((byte*) table->record[1],
- (byte*) table->record[0])))
+ if (will_batch)
+ {
+ /*
+ Typically a batched handler can execute the batched jobs when:
+ 1) When specifically told to do so
+ 2) When it is not a good idea to batch anymore
+ 3) When it is necessary to send batch for other reasons
+ (One such reason is when READ's must be performed)
+
+ 1) is covered by exec_bulk_update calls.
+ 2) and 3) is handled by the bulk_update_row method.
+
+ bulk_update_row can execute the updates including the one
+ defined in the bulk_update_row or not including the row
+ in the call. This is up to the handler implementation and can
+ vary from call to call.
+
+ The dup_key_found reports the number of duplicate keys found
+ in those updates actually executed. It only reports those if
+ the extra call with HA_EXTRA_IGNORE_DUP_KEY have been issued.
+ If this hasn't been issued it returns an error code and can
+ ignore this number. Thus any handler that implements batching
+ for UPDATE IGNORE must also handle this extra call properly.
+
+ If a duplicate key is found on the record included in this
+ call then it should be included in the count of dup_key_found
+ and error should be set to 0 (only if these errors are ignored).
+ */
+ error= table->file->bulk_update_row(table->record[1],
+ table->record[0],
+ &dup_key_found);
+ limit+= dup_key_found;
+ updated-= dup_key_found;
+ }
+ else
+ {
+ /* Non-batched update */
+ error= table->file->update_row((byte*) table->record[1],
+ (byte*) table->record[0]);
+ }
+ if (!error)
{
updated++;
thd->no_trans_update= !transactional_table;
@@ -474,20 +525,74 @@ int mysql_update(THD *thd,
if (!--limit && using_limit)
{
- error= -1; // Simulate end of file
- break;
+ /*
+ We have reached end-of-file in most common situations where no
+ batching has occurred and if batching was supposed to occur but
+ no updates were made and finally when the batch execution was
+ performed without error and without finding any duplicate keys.
+ If the batched updates were performed with errors we need to
+ check and if no error but duplicate key's found we need to
+ continue since those are not counted for in limit.
+ */
+ if (will_batch &&
+ ((error= table->file->exec_bulk_update(&dup_key_found)) ||
+ !dup_key_found))
+ {
+ if (error)
+ {
+ /*
+ The handler should not report error of duplicate keys if they
+ are ignored. This is a requirement on batching handlers.
+ */
+ table->file->print_error(error,MYF(0));
+ error= 1;
+ break;
+ }
+ /*
+ Either an error was found and we are ignoring errors or there
+ were duplicate keys found. In both cases we need to correct
+ the counters and continue the loop.
+ */
+ limit= dup_key_found; //limit is 0 when we get here so need to +
+ updated-= dup_key_found;
+ }
+ else
+ {
+ error= -1; // Simulate end of file
+ break;
+ }
}
}
else
table->file->unlock_row();
thd->row_count++;
}
+ dup_key_found= 0;
if (thd->killed && !error)
error= 1; // Aborted
+ else if (will_batch &&
+ (loc_error= table->file->exec_bulk_update(&dup_key_found)))
+ /*
+ An error has occurred when a batched update was performed and returned
+ an error indication. It cannot be an allowed duplicate key error since
+ we require the batching handler to treat this as a normal behavior.
+
+ Otherwise we simply remove the number of duplicate keys records found
+ in the batched update.
+ */
+ {
+ thd->fatal_error();
+ table->file->print_error(loc_error,MYF(0));
+ error= 1;
+ }
+ else
+ updated-= dup_key_found;
+ if (will_batch)
+ table->file->end_bulk_update();
end_read_record(&info);
free_io_cache(table); // If ORDER BY
delete select;
- thd->proc_info="end";
+ thd->proc_info= "end";
VOID(table->file->extra(HA_EXTRA_NO_IGNORE_DUP_KEY));
/*
@@ -697,7 +802,7 @@ reopen_tables:
&lex->select_lex.leaf_tables, FALSE))
DBUG_RETURN(TRUE);
- if (setup_fields_with_no_wrap(thd, 0, *fields, 1, 0, 0))
+ if (setup_fields_with_no_wrap(thd, 0, *fields, 2, 0, 0))
DBUG_RETURN(TRUE);
for (tl= table_list; tl ; tl= tl->next_local)