Loading sql/handler.h +79 −0 Original line number Diff line number Diff line Loading @@ -567,6 +567,85 @@ class handler :public Sql_alloc { return HA_ERR_WRONG_COMMAND; } virtual int delete_row(const byte * buf) { return HA_ERR_WRONG_COMMAND; } /* SYNOPSIS start_bulk_update() RETURN 0 Bulk update used by handler 1 Bulk update not used, normal operation used */ virtual bool start_bulk_update() { return 1; } /* SYNOPSIS start_bulk_delete() RETURN 0 Bulk delete used by handler 1 Bulk delete not used, normal operation used */ virtual bool start_bulk_delete() { return 1; } /* SYNOPSIS This method is similar to update_row, however the handler doesn't need to execute the updates at this point in time. The handler can be certain that another call to bulk_update_row will occur OR a call to exec_bulk_update before the set of updates in this query is concluded. bulk_update_row() old_data Old record new_data New record dup_key_found Number of duplicate keys found RETURN 0 Bulk delete used by handler 1 Bulk delete not used, normal operation used */ virtual int bulk_update_row(const byte *old_data, byte *new_data, uint *dup_key_found) { DBUG_ASSERT(FALSE); return HA_ERR_WRONG_COMMAND; } /* SYNOPSIS After this call all outstanding updates must be performed. The number of duplicate key errors are reported in the duplicate key parameter. It is allowed to continue to the batched update after this call, the handler has to wait until end_bulk_update with changing state. exec_bulk_update() dup_key_found Number of duplicate keys found RETURN 0 Success >0 Error code */ virtual int exec_bulk_update(uint *dup_key_found) { DBUG_ASSERT(FALSE); return HA_ERR_WRONG_COMMAND; } /* SYNOPSIS Perform any needed clean-up, no outstanding updates are there at the moment. end_bulk_update() RETURN Nothing */ virtual void end_bulk_update() { return; } /* SYNOPSIS Execute all outstanding deletes and close down the bulk delete. end_bulk_delete() RETURN 0 Success >0 Error code */ virtual int end_bulk_delete() { DBUG_ASSERT(FALSE); return HA_ERR_WRONG_COMMAND; } virtual int index_read(byte * buf, const byte * key, uint key_len, enum ha_rkey_function find_flag) { return HA_ERR_WRONG_COMMAND; } Loading sql/sql_delete.cc +23 −5 Original line number Diff line number Diff line Loading @@ -32,7 +32,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, SQL_LIST *order, ha_rows limit, ulong options) { int error; bool will_batch; int error, loc_error; TABLE *table; SQL_SELECT *select=0; READ_RECORD info; Loading Loading @@ -171,6 +172,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, deleted=0L; init_ftfuncs(thd, select_lex, 1); thd->proc_info="updating"; will_batch= !table->file->start_bulk_delete(); while (!(error=info.read_record(&info)) && !thd->killed && !thd->net.report_error) { Loading Loading @@ -215,6 +217,12 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, } if (thd->killed && !error) error= 1; // Aborted if (will_batch && (loc_error= table->file->end_bulk_delete())) { if (error != 1) table->file->print_error(loc_error,MYF(0)); error=1; } thd->proc_info= "end"; end_read_record(&info); free_io_cache(table); // Will not do any harm Loading Loading @@ -579,7 +587,8 @@ void multi_delete::send_error(uint errcode,const char *err) int multi_delete::do_deletes(bool from_send_error) { int local_error= 0, counter= 0; int local_error= 0, counter= 0, error; bool will_batch; DBUG_ENTER("do_deletes"); if (from_send_error) Loading Loading @@ -614,6 +623,7 @@ int multi_delete::do_deletes(bool from_send_error) been deleted by foreign key handling */ info.ignore_not_found_rows= 1; will_batch= !table->file->start_bulk_delete(); while (!(local_error=info.read_record(&info)) && !thd->killed) { if ((local_error= table->file->delete_row(table->record[0]))) Loading @@ -623,6 +633,14 @@ int multi_delete::do_deletes(bool from_send_error) } deleted++; } if (will_batch && (error= table->file->end_bulk_delete())) { if (!local_error) { local_error= error; table->file->print_error(local_error,MYF(0)); } } end_read_record(&info); if (thd->killed && !local_error) local_error= 1; Loading sql/sql_update.cc +106 −12 Original line number Diff line number Diff line Loading @@ -117,10 +117,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=0; uint used_index; int error=0, loc_error; uint used_index, dup_key_found; #ifndef NO_EMBEDDED_ACCESS_CHECKS uint want_privilege; #endif Loading Loading @@ -392,7 +392,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())) Loading @@ -404,7 +404,8 @@ int mysql_update(THD *thd, found++; if (table->triggers) table->triggers->process_triggers(thd, TRG_EVENT_UPDATE, TRG_ACTION_BEFORE); table->triggers->process_triggers(thd, TRG_EVENT_UPDATE, TRG_ACTION_BEFORE); if (compare_record(table, query_id)) { Loading @@ -420,8 +421,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[0], table->record[1], &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; Loading @@ -434,22 +474,76 @@ int mysql_update(THD *thd, break; } } if (table->triggers) table->triggers->process_triggers(thd, TRG_EVENT_UPDATE, TRG_ACTION_AFTER); table->triggers->process_triggers(thd, TRG_EVENT_UPDATE, TRG_ACTION_AFTER); if (!--limit && using_limit) { /* 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; Loading Loading
sql/handler.h +79 −0 Original line number Diff line number Diff line Loading @@ -567,6 +567,85 @@ class handler :public Sql_alloc { return HA_ERR_WRONG_COMMAND; } virtual int delete_row(const byte * buf) { return HA_ERR_WRONG_COMMAND; } /* SYNOPSIS start_bulk_update() RETURN 0 Bulk update used by handler 1 Bulk update not used, normal operation used */ virtual bool start_bulk_update() { return 1; } /* SYNOPSIS start_bulk_delete() RETURN 0 Bulk delete used by handler 1 Bulk delete not used, normal operation used */ virtual bool start_bulk_delete() { return 1; } /* SYNOPSIS This method is similar to update_row, however the handler doesn't need to execute the updates at this point in time. The handler can be certain that another call to bulk_update_row will occur OR a call to exec_bulk_update before the set of updates in this query is concluded. bulk_update_row() old_data Old record new_data New record dup_key_found Number of duplicate keys found RETURN 0 Bulk delete used by handler 1 Bulk delete not used, normal operation used */ virtual int bulk_update_row(const byte *old_data, byte *new_data, uint *dup_key_found) { DBUG_ASSERT(FALSE); return HA_ERR_WRONG_COMMAND; } /* SYNOPSIS After this call all outstanding updates must be performed. The number of duplicate key errors are reported in the duplicate key parameter. It is allowed to continue to the batched update after this call, the handler has to wait until end_bulk_update with changing state. exec_bulk_update() dup_key_found Number of duplicate keys found RETURN 0 Success >0 Error code */ virtual int exec_bulk_update(uint *dup_key_found) { DBUG_ASSERT(FALSE); return HA_ERR_WRONG_COMMAND; } /* SYNOPSIS Perform any needed clean-up, no outstanding updates are there at the moment. end_bulk_update() RETURN Nothing */ virtual void end_bulk_update() { return; } /* SYNOPSIS Execute all outstanding deletes and close down the bulk delete. end_bulk_delete() RETURN 0 Success >0 Error code */ virtual int end_bulk_delete() { DBUG_ASSERT(FALSE); return HA_ERR_WRONG_COMMAND; } virtual int index_read(byte * buf, const byte * key, uint key_len, enum ha_rkey_function find_flag) { return HA_ERR_WRONG_COMMAND; } Loading
sql/sql_delete.cc +23 −5 Original line number Diff line number Diff line Loading @@ -32,7 +32,8 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, SQL_LIST *order, ha_rows limit, ulong options) { int error; bool will_batch; int error, loc_error; TABLE *table; SQL_SELECT *select=0; READ_RECORD info; Loading Loading @@ -171,6 +172,7 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, deleted=0L; init_ftfuncs(thd, select_lex, 1); thd->proc_info="updating"; will_batch= !table->file->start_bulk_delete(); while (!(error=info.read_record(&info)) && !thd->killed && !thd->net.report_error) { Loading Loading @@ -215,6 +217,12 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, } if (thd->killed && !error) error= 1; // Aborted if (will_batch && (loc_error= table->file->end_bulk_delete())) { if (error != 1) table->file->print_error(loc_error,MYF(0)); error=1; } thd->proc_info= "end"; end_read_record(&info); free_io_cache(table); // Will not do any harm Loading Loading @@ -579,7 +587,8 @@ void multi_delete::send_error(uint errcode,const char *err) int multi_delete::do_deletes(bool from_send_error) { int local_error= 0, counter= 0; int local_error= 0, counter= 0, error; bool will_batch; DBUG_ENTER("do_deletes"); if (from_send_error) Loading Loading @@ -614,6 +623,7 @@ int multi_delete::do_deletes(bool from_send_error) been deleted by foreign key handling */ info.ignore_not_found_rows= 1; will_batch= !table->file->start_bulk_delete(); while (!(local_error=info.read_record(&info)) && !thd->killed) { if ((local_error= table->file->delete_row(table->record[0]))) Loading @@ -623,6 +633,14 @@ int multi_delete::do_deletes(bool from_send_error) } deleted++; } if (will_batch && (error= table->file->end_bulk_delete())) { if (!local_error) { local_error= error; table->file->print_error(local_error,MYF(0)); } } end_read_record(&info); if (thd->killed && !local_error) local_error= 1; Loading
sql/sql_update.cc +106 −12 Original line number Diff line number Diff line Loading @@ -117,10 +117,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=0; uint used_index; int error=0, loc_error; uint used_index, dup_key_found; #ifndef NO_EMBEDDED_ACCESS_CHECKS uint want_privilege; #endif Loading Loading @@ -392,7 +392,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())) Loading @@ -404,7 +404,8 @@ int mysql_update(THD *thd, found++; if (table->triggers) table->triggers->process_triggers(thd, TRG_EVENT_UPDATE, TRG_ACTION_BEFORE); table->triggers->process_triggers(thd, TRG_EVENT_UPDATE, TRG_ACTION_BEFORE); if (compare_record(table, query_id)) { Loading @@ -420,8 +421,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[0], table->record[1], &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; Loading @@ -434,22 +474,76 @@ int mysql_update(THD *thd, break; } } if (table->triggers) table->triggers->process_triggers(thd, TRG_EVENT_UPDATE, TRG_ACTION_AFTER); table->triggers->process_triggers(thd, TRG_EVENT_UPDATE, TRG_ACTION_AFTER); if (!--limit && using_limit) { /* 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; Loading