diff --git a/mysql-test/main/delete_multi_order_by.result b/mysql-test/main/delete_multi_order_by.result index 5b5fb98802e..20f3f1b3d30 100644 --- a/mysql-test/main/delete_multi_order_by.result +++ b/mysql-test/main/delete_multi_order_by.result @@ -237,3 +237,157 @@ update t2, t1 set t2.field=t1.field where t1.id1=t2.id2 and 0=1; delete t1, t2 from t2 inner join t1 on t1.id1=t2.id2 where 0=1; drop table t1, t2; set session sql_buffer_result=default; +create table t1 (id int primary key, v int); +create table t2 (id int primary key, v int); +insert into t1 (id, v) values (2,3),(1,4); +insert into t2 (id, v) values (5,5),(6,6); +select * from t1; +id v +2 3 +1 4 +select * from t2; +id v +5 5 +6 6 +select t1.*, t2.* from t1, t2 order by t1.id, t2.id limit 1; +id v id v +1 4 5 5 +delete t1.*, t2.* from t1, t2 order by t1.id, t2.id limit 1; +select * from t1; +id v +2 3 +select * from t2; +id v +6 6 +drop table t1, t2; +create table t1 (id int primary key, v int); +create table t2 (id int primary key, v int); +insert into t1 (id, v) values (2,3),(1,4); +insert into t2 (id, v) values (5,5),(6,6); +select * from t1; +id v +2 3 +1 4 +select * from t2; +id v +5 5 +6 6 +select t1.*, t2.* from t1, t2 order by t1.id, t2.id limit 2; +id v id v +1 4 5 5 +1 4 6 6 +delete t1.*, t2.* from t1, t2 order by t1.id, t2.id limit 2; +select * from t1; +id v +2 3 +select * from t2; +id v +drop table t1, t2; +create table t1 (id int primary key, v int); +create table t2 (id int primary key, v int); +insert into t1 (id, v) values (2,3),(1,4); +insert into t2 (id, v) values (5,5),(6,6); +select * from t1; +id v +2 3 +1 4 +select * from t2; +id v +5 5 +6 6 +select t1.*, t2.* from t1, t2 order by t1.id, t2.id limit 3; +id v id v +1 4 5 5 +1 4 6 6 +2 3 5 5 +delete t1.*, t2.* from t1, t2 order by t1.id, t2.id limit 3; +select * from t1; +id v +select * from t2; +id v +drop table t1, t2; +create table t1 (id int primary key, v int); +create table t2 (id int primary key, v int); +create table t3 (id int primary key, v int); +insert into t1 (id, v) values (1, 1000), (2, 2000), (3, 3000), (4, 4000), (5, 5000); +insert into t2 (id, v) values (10, 100), (20, 200), (30, 300), (40, 400), (50, 500); +insert into t3 (id, v) values (11, 111), (22, 222), (33, 333), (44, 444), (55, 555); +select * from t1; +id v +1 1000 +2 2000 +3 3000 +4 4000 +5 5000 +select * from t2; +id v +10 100 +20 200 +30 300 +40 400 +50 500 +select * from t3; +id v +11 111 +22 222 +33 333 +44 444 +55 555 +select t1.*, t2.*, t3.* from t1, t2, t3 order by t1.id, t2.id, t3.id limit 3; +id v id v id v +1 1000 10 100 11 111 +1 1000 10 100 22 222 +1 1000 10 100 33 333 +delete t1.*, t2.*, t3.* from t1, t2, t3 order by t1.id, t2.id, t3.id limit 3; +select * from t1; +id v +2 2000 +3 3000 +4 4000 +5 5000 +select * from t2; +id v +20 200 +30 300 +40 400 +50 500 +select * from t3; +id v +44 444 +55 555 +drop table t1, t2, t3; +create table t1 (id int primary key, v int); +create table t2 (id int primary key, v int); +create table t3 (id int primary key, v int); +insert into t1 (id, v) values (1, 1000), (2, 2000), (3, 3000), (4, 4000), (5, 5000); +insert into t2 (id, v) values (10, 100), (20, 200), (30, 300), (40, 400), (50, 500); +insert into t3 (id, v) values (11, 111), (22, 222), (33, 333), (44, 444), (55, 555); +select * from t1; +id v +1 1000 +2 2000 +3 3000 +4 4000 +5 5000 +select * from t2; +id v +10 100 +20 200 +30 300 +40 400 +50 500 +select * from t3; +id v +11 111 +22 222 +33 333 +44 444 +55 555 +delete t1.*, t2.*, t3.* from t1, t2, t3; +select * from t1; +id v +select * from t2; +id v +select * from t3; +id v +drop table t1, t2, t3; diff --git a/mysql-test/main/delete_multi_order_by.test b/mysql-test/main/delete_multi_order_by.test index 77c330d76df..801d324c61b 100644 --- a/mysql-test/main/delete_multi_order_by.test +++ b/mysql-test/main/delete_multi_order_by.test @@ -1,6 +1,9 @@ # # MDEV-30469 Support ORDER BY and LIMIT for multi-table DELETE, index hints for single-table DELETE. # + +--source include/have_innodb.inc + create table t1 (id int primary key, v int); create table t2 (id int primary key, v int); insert into t1 (id, v) values (4,1),(3,2),(2,3),(1,4); @@ -129,3 +132,74 @@ delete t1, t2 from t2 inner join t1 on t1.id1=t2.id2 where 0=1; drop table t1, t2; set session sql_buffer_result=default; + +create table t1 (id int primary key, v int); +create table t2 (id int primary key, v int); +insert into t1 (id, v) values (2,3),(1,4); +insert into t2 (id, v) values (5,5),(6,6); +select * from t1; +select * from t2; +select t1.*, t2.* from t1, t2 order by t1.id, t2.id limit 1; +delete t1.*, t2.* from t1, t2 order by t1.id, t2.id limit 1; +select * from t1; +select * from t2; + +drop table t1, t2; + +create table t1 (id int primary key, v int); +create table t2 (id int primary key, v int); +insert into t1 (id, v) values (2,3),(1,4); +insert into t2 (id, v) values (5,5),(6,6); +select * from t1; +select * from t2; +select t1.*, t2.* from t1, t2 order by t1.id, t2.id limit 2; +delete t1.*, t2.* from t1, t2 order by t1.id, t2.id limit 2; +select * from t1; +select * from t2; + +drop table t1, t2; + +create table t1 (id int primary key, v int); +create table t2 (id int primary key, v int); +insert into t1 (id, v) values (2,3),(1,4); +insert into t2 (id, v) values (5,5),(6,6); +select * from t1; +select * from t2; +select t1.*, t2.* from t1, t2 order by t1.id, t2.id limit 3; +delete t1.*, t2.* from t1, t2 order by t1.id, t2.id limit 3; +select * from t1; +select * from t2; + +drop table t1, t2; +create table t1 (id int primary key, v int); +create table t2 (id int primary key, v int); +create table t3 (id int primary key, v int); +insert into t1 (id, v) values (1, 1000), (2, 2000), (3, 3000), (4, 4000), (5, 5000); +insert into t2 (id, v) values (10, 100), (20, 200), (30, 300), (40, 400), (50, 500); +insert into t3 (id, v) values (11, 111), (22, 222), (33, 333), (44, 444), (55, 555); +select * from t1; +select * from t2; +select * from t3; +select t1.*, t2.*, t3.* from t1, t2, t3 order by t1.id, t2.id, t3.id limit 3; +delete t1.*, t2.*, t3.* from t1, t2, t3 order by t1.id, t2.id, t3.id limit 3; +select * from t1; +select * from t2; +select * from t3; + +drop table t1, t2, t3; + +create table t1 (id int primary key, v int); +create table t2 (id int primary key, v int); +create table t3 (id int primary key, v int); +insert into t1 (id, v) values (1, 1000), (2, 2000), (3, 3000), (4, 4000), (5, 5000); +insert into t2 (id, v) values (10, 100), (20, 200), (30, 300), (40, 400), (50, 500); +insert into t3 (id, v) values (11, 111), (22, 222), (33, 333), (44, 444), (55, 555); +select * from t1; +select * from t2; +select * from t3; +delete t1.*, t2.*, t3.* from t1, t2, t3; +select * from t1; +select * from t2; +select * from t3; + +drop table t1, t2, t3; diff --git a/mysql-test/main/delete_single_to_multi.result b/mysql-test/main/delete_single_to_multi.result index 3b43f553fdc..fb8b2c0ceae 100644 --- a/mysql-test/main/delete_single_to_multi.result +++ b/mysql-test/main/delete_single_to_multi.result @@ -3394,12 +3394,12 @@ o_custkey in (select c_custkey from customer where c_nationkey in (1,2)); select o_orderkey, o_totalprice from t; o_orderkey o_totalprice -1856 189361.42 324 26868.85 +1856 189361.42 1221 117397.16 3139 40975.96 -1925 146382.71 1344 43809.37 +1925 146382.71 4903 34363.63 5607 24660.06 delete from orders where o_orderDATE between '1992-01-01' and '1992-06-30' and diff --git a/mysql-test/main/myisam_explain_non_select_all.result b/mysql-test/main/myisam_explain_non_select_all.result index c3d3247fb29..537d30fb12e 100644 --- a/mysql-test/main/myisam_explain_non_select_all.result +++ b/mysql-test/main/myisam_explain_non_select_all.result @@ -120,8 +120,7 @@ Handler_read_rnd_next 4 Variable_name Value Handler_delete 1 Handler_read_key 2 -Handler_read_rnd 1 -Handler_read_rnd_next 6 +Handler_read_rnd_next 4 DROP TABLE t1; #4 @@ -928,9 +927,9 @@ Variable_name Value Handler_delete 8 Handler_read_key 19 Handler_read_next 3 -Handler_read_rnd 8 +Handler_read_rnd 5 Handler_read_rnd_deleted 1 -Handler_read_rnd_next 15 +Handler_read_rnd_next 11 DROP TABLE t1, t2, t3; #20 @@ -1066,8 +1065,7 @@ Handler_read_rnd_next 12 Variable_name Value Handler_delete 3 Handler_read_key 4 -Handler_read_rnd 3 -Handler_read_rnd_next 34 +Handler_read_rnd_next 30 DROP TABLE t1, t2; #22 diff --git a/mysql-test/suite/perfschema/r/multi_table_io.result b/mysql-test/suite/perfschema/r/multi_table_io.result index e10cfc87a0a..27effe010f4 100644 --- a/mysql-test/suite/perfschema/r/multi_table_io.result +++ b/mysql-test/suite/perfschema/r/multi_table_io.result @@ -69,7 +69,6 @@ wait/io/table/sql/handler TABLE test1 t2 update 1 wait/io/table/sql/handler TABLE test marker insert 1 wait/io/table/sql/handler TABLE test t1 fetch 1 wait/io/table/sql/handler TABLE test1 t2 fetch 1 -wait/io/table/sql/handler TABLE test t1 fetch 1 wait/io/table/sql/handler TABLE test t1 delete 1 wait/io/table/sql/handler TABLE test1 t2 fetch 1 wait/io/table/sql/handler TABLE test1 t2 delete 1 diff --git a/sql/sql_class.h b/sql/sql_class.h index 0caaf873007..debe56f714c 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -7564,7 +7564,6 @@ class multi_delete :public select_result_interceptor TABLE_LIST *delete_tables, *table_being_deleted; TMP_TABLE_PARAM *tmp_table_param; TABLE **tmp_tables, *main_table; - Unique **tempfiles; ha_rows deleted, found; uint table_count; int error; @@ -7573,6 +7572,7 @@ class multi_delete :public select_result_interceptor bool transactional_tables; /* True if at least one table we delete from is not transactional */ bool normal_tables; + bool delete_while_scanning; /* error handling (rollback and binlogging) can happen in send_eof() so that afterward abort_result_set() needs to find out that. diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index d4438c082d3..9201e2a42f5 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1112,7 +1112,6 @@ multi_delete::multi_delete(THD *thd_arg, normal_tables(0), error_handled(0) { - tempfiles= thd_arg->calloc(table_count); tmp_tables = thd->calloc(table_count); tmp_table_param = thd->calloc(table_count); } @@ -1154,7 +1153,7 @@ int multi_delete::prepare2(JOIN *join) { if (!join->need_tmp || !join->tmp_table_keep_current_rowid) return 0; - + delete_while_scanning= false; JOIN_TAB *tmptab= join->join_tab + join->exec_join_tab_cnt(); for (Item **it= tmptab->tmp_table_param->items_to_copy; *it ; it++) @@ -1195,7 +1194,6 @@ bool multi_delete::initialize_tables(JOIN *join) { TABLE_LIST *walk; - Unique **tempfiles_ptr; DBUG_ENTER("initialize_tables"); if (unlikely((thd->variables.option_bits & OPTION_SAFE_UPDATES) && @@ -1203,7 +1201,8 @@ multi_delete::initialize_tables(JOIN *join) DBUG_RETURN(1); main_table=join->join_tab->table; - table_map tables_to_delete_from=0; + table_map tables_to_delete_from= 0; + delete_while_scanning= true; for (walk= delete_tables; walk; walk= walk->next_local) { TABLE_LIST *tbl= walk->correspondent_table->find_table_for_update(); @@ -1214,13 +1213,24 @@ multi_delete::initialize_tables(JOIN *join) delete is called. */ join->map2table[tbl->table->tablenr]->keep_current_rowid= true; + + if (delete_while_scanning && + unique_table(thd, tbl, join->tables_list, 0)) + { + /* + If the table we are going to delete from appears + in join, we need to defer delete. So the delete + doesn't interfers with the scaning of results. + */ + delete_while_scanning= false; + } } walk= delete_tables; uint index= 0; for (JOIN_TAB *tab= first_linear_tab(join, WITHOUT_BUSH_ROOTS, WITH_CONST_TABLES); - tab; + tab; tab= next_linear_tab(join, tab, WITHOUT_BUSH_ROOTS)) { if (!tab->bush_children && tab->table->map & tables_to_delete_from) @@ -1273,16 +1283,19 @@ multi_delete::initialize_tables(JOIN *join) tmp_tables[index]->file->extra(HA_EXTRA_WRITE_CACHE); ++index; } + else if ((tab->type != JT_SYSTEM && tab->type != JT_CONST) && + walk == delete_tables) + { + /* + We are not deleting from the table we are scanning. In this + case send_data() shouldn't delete any rows a we may touch + the rows in the deleted table many times + */ + delete_while_scanning= false; + } } - walk= delete_tables; - tempfiles_ptr= tempfiles; - for (;walk ;walk= walk->next_local) - { - TABLE *table=walk->table; - *tempfiles_ptr++= new (thd->mem_root) Unique (refpos_order_cmp, table->file, - table->file->ref_length, - MEM_STRIP_BUF_SIZE); - } + if (delete_while_scanning) + table_being_deleted= delete_tables; if (init_ftfuncs(thd, thd->lex->current_select, 1)) DBUG_RETURN(true); @@ -1293,23 +1306,15 @@ multi_delete::initialize_tables(JOIN *join) multi_delete::~multi_delete() { - for (table_being_deleted= delete_tables; - table_being_deleted; - table_being_deleted= table_being_deleted->next_local) + for (TABLE_LIST *walk= delete_tables; walk; walk= walk->next_local) { - TABLE *table= table_being_deleted->table; + TABLE *table= walk->table; if (!table) continue; table->no_keyread=0; table->no_cache= 0; } - for (uint counter= 0; counter < table_count; counter++) - { - if (tempfiles[counter]) - delete tempfiles[counter]; - } - if (tmp_tables) { for (uint cnt = 0; cnt < table_count; cnt++) @@ -1326,12 +1331,15 @@ multi_delete::~multi_delete() int multi_delete::send_data(List &values) { + int secure_counter= delete_while_scanning ? -1 : 0; TABLE_LIST *del_table; DBUG_ENTER("multi_delete::send_data"); + bool ignore= thd->lex->ignore; + for (del_table= delete_tables; del_table; - del_table= del_table->next_local) + del_table= del_table->next_local, ++secure_counter) { TABLE *table= del_table->table; // DELETE and TRUNCATE don't affect SEQUENCE, so bail early @@ -1342,31 +1350,68 @@ int multi_delete::send_data(List &values) if (table->status & (STATUS_NULL_ROW | STATUS_DELETED)) continue; - found++; - const uint offset= del_table->shared; - TABLE *tmp_table= tmp_tables[offset]; - if (copy_funcs(tmp_table_param[offset].items_to_copy, thd)) - DBUG_RETURN(1); - /* rowid field is NULL if join tmp table has null row from outer join */ - if (tmp_table->field[0]->is_null()) - continue; - error= tmp_table->file->ha_write_tmp_row(tmp_table->record[0]); - if (error) + table->file->position(table->record[0]); + ++found; + + if (secure_counter < 0) { - --found; - if (error != HA_ERR_FOUND_DUPP_KEY && - error != HA_ERR_FOUND_DUPP_UNIQUE) - { - if (create_internal_tmp_table_from_heap(thd, tmp_table, - tmp_table_param[offset].start_recinfo, - &tmp_table_param[offset].recinfo, - error, 1, NULL)) - { - do_delete= 0; - DBUG_RETURN(1); // Not a table_is_full error - } - found++; - } + /* We are scanning the current table */ + DBUG_ASSERT(del_table == table_being_deleted); + if (table->triggers && + table->triggers->process_triggers(thd, TRG_EVENT_DELETE, + TRG_ACTION_BEFORE, false)) + DBUG_RETURN(1); + + table->status|= STATUS_DELETED; + + error= table->delete_row(); + if (likely(!error)) + { + deleted++; + if (!table->file->has_transactions()) + thd->transaction->stmt.modified_non_trans_table= TRUE; + if (table->triggers && + table->triggers->process_triggers(thd, TRG_EVENT_DELETE, + TRG_ACTION_AFTER, false)) + DBUG_RETURN(1); + } + else if (!ignore) + { + /* + If the IGNORE option is used errors caused by ha_delete_row don't + have to stop the iteration. + */ + table->file->print_error(error,MYF(0)); + DBUG_RETURN(1); + } + } + else + { + const uint offset= del_table->shared; + TABLE *tmp_table= tmp_tables[offset]; + if (copy_funcs(tmp_table_param[offset].items_to_copy, thd)) + DBUG_RETURN(1); + /* rowid field is NULL if join tmp table has null row from outer join */ + if (tmp_table->field[0]->is_null()) + continue; + error= tmp_table->file->ha_write_tmp_row(tmp_table->record[0]); + if (error) + { + --found; + if (error != HA_ERR_FOUND_DUPP_KEY && + error != HA_ERR_FOUND_DUPP_UNIQUE) + { + if (create_internal_tmp_table_from_heap(thd, tmp_table, + tmp_table_param[offset].start_recinfo, + &tmp_table_param[offset].recinfo, + error, 1, NULL)) + { + do_delete= 0; + DBUG_RETURN(1); // Not a table_is_full error + } + found++; + } + } } } DBUG_RETURN(0); @@ -1452,21 +1497,18 @@ int multi_delete::do_deletes() if (!found) DBUG_RETURN(0); - table_being_deleted= delete_tables; - - for (uint counter= 0; table_being_deleted; - table_being_deleted= table_being_deleted->next_local, counter++) - { + table_being_deleted= (delete_while_scanning ? delete_tables->next_local : + delete_tables); + + for (; table_being_deleted; + table_being_deleted= table_being_deleted->next_local) + { TABLE *table = table_being_deleted->table; // DELETE and TRUNCATE don't affect SEQUENCE, so bail early if (table->file->ht->db_type == DB_TYPE_SEQUENCE) continue; - int local_error; - if (tempfiles[counter] && unlikely(tempfiles[counter]->get(table))) - DBUG_RETURN(1); - - local_error= rowid_table_deletes(table, thd->lex->ignore); + int local_error= rowid_table_deletes(table, thd->lex->ignore); if (unlikely(thd->killed) && likely(!local_error)) DBUG_RETURN(1); @@ -1556,7 +1598,7 @@ int multi_delete::rowid_table_deletes(TABLE *table, bool ignore) table->file->print_error(local_error, MYF(0)); break; } - + /* Increase the reported number of deleted rows only if no error occurred during ha_delete_row.