From 03d2328785db2ec1467a9c465712b2c559bcd6e6 Mon Sep 17 00:00:00 2001 From: Sergei Golubchik Date: Mon, 27 Jan 2025 19:22:20 +0100 Subject: [PATCH] MDEV-35944 DELETE fails to notice transaction abort, violating ACID Process errors of read_record(). Also, add an assert that Marko requested --- mysql-test/main/delete_innodb.result | 27 +++++++++++++++++++ mysql-test/main/delete_innodb.test | 39 +++++++++++++++++++++++++++ sql/sql_delete.cc | 18 +++++-------- storage/innobase/handler/ha_innodb.cc | 1 + 4 files changed, 73 insertions(+), 12 deletions(-) diff --git a/mysql-test/main/delete_innodb.result b/mysql-test/main/delete_innodb.result index b9f4c8bdaf5..ebee36d6ce7 100644 --- a/mysql-test/main/delete_innodb.result +++ b/mysql-test/main/delete_innodb.result @@ -24,3 +24,30 @@ SELECT * FROM t1; c1 SET sort_buffer_size=@save_sort_buffer_size; DROP TABLE t1; +# +# MDEV-35944 DELETE fails to notice transaction abort, violating ACID +# +CREATE TABLE t1 (id INT PRIMARY KEY, col_varchar VARCHAR(8)) ENGINE=InnoDB; +INSERT INTO t1 (id) VALUES (1),(2); +CREATE TABLE t2 (id INT, f INT, s DATE, e DATE, PERIOD FOR p(s,e), PRIMARY KEY(id, p WITHOUT OVERLAPS)) ENGINE=InnoDB; +INSERT INTO t2 VALUES (1,0,'2000-01-01','2000-01-02'); +CREATE TABLE t3 (id INT, f BLOB, UNIQUE(f)) ENGINE=InnoDB; +connection default; +SET innodb_lock_wait_timeout=1; +START TRANSACTION; +DELETE FROM t1; +connect con1,localhost,root,,; +START TRANSACTION; +UPDATE t2 SET f = 20; +connection default; +DELETE FROM t2 FOR PORTION OF p FROM '2000-01-01' TO '2000-01-02'; +connection con1; +INSERT INTO t3 (id) VALUES (1), (2); +UPDATE t1 SET col_varchar = 'bar'; +COMMIT; +connection default; +ERROR 40001: Deadlock found when trying to get lock; try restarting transaction +COMMIT; +UPDATE t3 SET f = 'foo' ORDER BY f LIMIT 1; +DROP TABLE t1, t2, t3; +# End of 10.5 tests diff --git a/mysql-test/main/delete_innodb.test b/mysql-test/main/delete_innodb.test index c5c5c5d0172..e654b8fe691 100644 --- a/mysql-test/main/delete_innodb.test +++ b/mysql-test/main/delete_innodb.test @@ -20,3 +20,42 @@ SELECT * FROM t1; SET sort_buffer_size=@save_sort_buffer_size; DROP TABLE t1; + +--echo # +--echo # MDEV-35944 DELETE fails to notice transaction abort, violating ACID +--echo # + +CREATE TABLE t1 (id INT PRIMARY KEY, col_varchar VARCHAR(8)) ENGINE=InnoDB; +INSERT INTO t1 (id) VALUES (1),(2); +CREATE TABLE t2 (id INT, f INT, s DATE, e DATE, PERIOD FOR p(s,e), PRIMARY KEY(id, p WITHOUT OVERLAPS)) ENGINE=InnoDB; +INSERT INTO t2 VALUES (1,0,'2000-01-01','2000-01-02'); +CREATE TABLE t3 (id INT, f BLOB, UNIQUE(f)) ENGINE=InnoDB; + +--connection default +SET innodb_lock_wait_timeout=1; +START TRANSACTION; +DELETE FROM t1; + +--connect (con1,localhost,root,,) +START TRANSACTION; +UPDATE t2 SET f = 20; + +--connection default +--send + DELETE FROM t2 FOR PORTION OF p FROM '2000-01-01' TO '2000-01-02'; + +--connection con1 +INSERT INTO t3 (id) VALUES (1), (2); +UPDATE t1 SET col_varchar = 'bar'; +COMMIT; + +--connection default +--error ER_LOCK_DEADLOCK +--reap +COMMIT; +UPDATE t3 SET f = 'foo' ORDER BY f LIMIT 1; + +# Cleanup +DROP TABLE t1, t2, t3; + +--echo # End of 10.5 tests diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index 2e77e32aed9..dc7b3fc00c4 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -773,27 +773,21 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, MEM_STRIP_BUF_SIZE); THD_STAGE_INFO(thd, stage_searching_rows_for_update); - while (!(error=info.read_record()) && !thd->killed && - ! thd->is_error()) + while (!(error=info.read_record()) && !thd->killed && !thd->is_error()) { if (record_should_be_deleted(thd, table, select, explain, delete_history)) { table->file->position(table->record[0]); - if (unlikely((error= - deltempfile->unique_add((char*) table->file->ref)))) - { - error= 1; - goto terminate_delete; - } + if ((error= deltempfile->unique_add((char*) table->file->ref))) + break; if (!--tmplimit && using_limit) break; } } end_read_record(&info); - if (unlikely(deltempfile->get(table)) || - unlikely(table->file->ha_index_or_rnd_end()) || - unlikely(init_read_record(&info, thd, table, 0, &deltempfile->sort, 0, - 1, false))) + if (table->file->ha_index_or_rnd_end() || error > 0 || + deltempfile->get(table) || + init_read_record(&info, thd, table, 0, &deltempfile->sort, 0, 1, 0)) { error= 1; goto terminate_delete; diff --git a/storage/innobase/handler/ha_innodb.cc b/storage/innobase/handler/ha_innodb.cc index 612a4f640b4..33ebb9f1291 100644 --- a/storage/innobase/handler/ha_innodb.cc +++ b/storage/innobase/handler/ha_innodb.cc @@ -16313,6 +16313,7 @@ ha_innobase::external_lock( case F_UNLCK: DEBUG_SYNC_C("ha_innobase_end_statement"); m_mysql_has_locked = false; + ut_a(trx->n_mysql_tables_in_use); if (--trx->n_mysql_tables_in_use) { break;