From 847b21f7b423bbf4c01d94e1919e55e1e94f8be5 Mon Sep 17 00:00:00 2001 From: Dave Gosselin Date: Fri, 31 Jul 2026 09:06:03 -0400 Subject: [PATCH] MDEV-40573: Crash on multi-table DELETE with an impossible WHERE A DELETE containing a single table, an index hint, an impossible WHERE condition, and a window function will take the multi-delete code path but never initialize tables for deletion, leading to a crash. Such a statement would never delete rows from the target table. Record in the multi_delete whether it was ever initialized for execution, and don't attempt to delete anything if it wasn't initialized. The index hint forces the single table DELETE to take the multi-table codepath. Since this case has an impossible WHERE condition, we set subq_exit_fl which later causes JOIN::optimize_stage2 to skip the multi-delete table initialization. It's not safe to attempt initialization when trying to find a "tableless" subquery plan, so defend against this case with the new multi-delete flag added by this commit. --- mysql-test/main/delete_multi_order_by.result | 9 +++++++++ mysql-test/main/delete_multi_order_by.test | 10 ++++++++++ sql/sql_class.h | 1 + sql/sql_delete.cc | 5 +++++ 4 files changed, 25 insertions(+) diff --git a/mysql-test/main/delete_multi_order_by.result b/mysql-test/main/delete_multi_order_by.result index 656090040ef5d..eddc3f55f2538 100644 --- a/mysql-test/main/delete_multi_order_by.result +++ b/mysql-test/main/delete_multi_order_by.result @@ -423,5 +423,14 @@ COUNT(*) 0 DROP TABLE t1; # +# MDEV-40573 Assertion `del_table == table_being_deleted' failed in +# multi_delete::send_data on DELETE w/ index hint + window function +# +CREATE TABLE t1 (c INT PRIMARY KEY); +INSERT INTO t1 (c) VALUES (1),(2),(3),(4); +DELETE FROM t1 USE KEY() WHERE 0 +ORDER BY PERCENTILE_CONT(COUNT(*)) WITHIN GROUP(ORDER BY c) OVER(); +DROP TABLE t1; +# # End 11.8 tests # diff --git a/mysql-test/main/delete_multi_order_by.test b/mysql-test/main/delete_multi_order_by.test index c2c2b28155353..a57b6b9144c2f 100644 --- a/mysql-test/main/delete_multi_order_by.test +++ b/mysql-test/main/delete_multi_order_by.test @@ -228,6 +228,16 @@ DELETE FROM a3,a1 USING t1 AS a1 JOIN t1 AS a2 JOIN t1 AS a3; SELECT COUNT(*) from t1; DROP TABLE t1; +--echo # +--echo # MDEV-40573 Assertion `del_table == table_being_deleted' failed in +--echo # multi_delete::send_data on DELETE w/ index hint + window function +--echo # +CREATE TABLE t1 (c INT PRIMARY KEY); +INSERT INTO t1 (c) VALUES (1),(2),(3),(4); +DELETE FROM t1 USE KEY() WHERE 0 + ORDER BY PERCENTILE_CONT(COUNT(*)) WITHIN GROUP(ORDER BY c) OVER(); +DROP TABLE t1; + --echo # --echo # End 11.8 tests --echo # diff --git a/sql/sql_class.h b/sql/sql_class.h index 5ca2eac6f4403..788f88023e0f7 100644 --- a/sql/sql_class.h +++ b/sql/sql_class.h @@ -7717,6 +7717,7 @@ class multi_delete :public select_result_interceptor /* True if at least one table we delete from is not transactional */ bool normal_tables; bool delete_while_scanning; + bool tables_initialized; /* 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 37495077b74f5..dbb9e31d689f1 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -1165,6 +1165,7 @@ multi_delete::multi_delete(THD *thd_arg, do_delete(0), transactional_tables(0), normal_tables(0), + tables_initialized(false), error_handled(0) { tmp_tables = thd->calloc(table_count); @@ -1355,6 +1356,7 @@ multi_delete::initialize_tables(JOIN *join) DBUG_RETURN(true); join->tmp_table_keep_current_rowid= TRUE; + tables_initialized= true; DBUG_RETURN(thd->is_fatal_error); } @@ -1386,6 +1388,9 @@ multi_delete::~multi_delete() int multi_delete::send_data(List &values) { + if (!tables_initialized) + return 0; + int secure_counter= delete_while_scanning ? -1 : 0; TABLE_LIST *del_table; DBUG_ENTER("multi_delete::send_data");