From 9a4110aa5799d63faced5f986fab9035785a9310 Mon Sep 17 00:00:00 2001 From: Monty Date: Wed, 28 Dec 2022 05:41:41 +0200 Subject: [PATCH] MDEV-30256 Wrong result (missing rows) upon join with empty table The problem was an assignment in test_quick_select() that flagged empty tables with "Impossible where". This test was however wrong as it didn't work correctly for left join. Removed the test, but added checking of empty tables in DELETE and UPDATE to get similar EXPLAIN as before. The new tests is a bit more strict (better) than before as it catches all cases of empty tables in single table DELETE/UPDATE. --- mysql-test/main/analyze_stmt.result | 2 +- mysql-test/main/derived_cond_pushdown.result | 2 +- mysql-test/main/explain_non_select.result | 2 +- mysql-test/main/filesort_debug.result | 1 + mysql-test/main/filesort_debug.test | 2 ++ mysql-test/main/join.result | 22 ++++++++++++++++++- mysql-test/main/join.test | 17 +++++++++++++- .../main/myisam_explain_non_select_all.result | 5 ++--- mysql-test/main/ps.result | 6 ++--- .../suite/binlog/r/binlog_unsafe.result | 4 ---- sql/opt_range.cc | 3 +-- sql/sql_delete.cc | 4 +++- sql/sql_update.cc | 6 +++-- .../spider/bg/r/spider_fixes.result | 1 - .../mysql-test/spider/r/spider_fixes.result | 1 - 15 files changed, 55 insertions(+), 23 deletions(-) diff --git a/mysql-test/main/analyze_stmt.result b/mysql-test/main/analyze_stmt.result index 89375146730..8ba089d8d31 100644 --- a/mysql-test/main/analyze_stmt.result +++ b/mysql-test/main/analyze_stmt.result @@ -258,7 +258,7 @@ drop table t1; create table t1 (i int); analyze delete from t1 returning *; id select_type table type possible_keys key key_len ref rows r_rows filtered r_filtered Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 0 0.00 100.00 100.00 +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE drop table t1; # # MDEV-6396: ANALYZE INSERT/REPLACE is accepted, but does not produce a plan diff --git a/mysql-test/main/derived_cond_pushdown.result b/mysql-test/main/derived_cond_pushdown.result index 2e07179ba73..acc52daed43 100644 --- a/mysql-test/main/derived_cond_pushdown.result +++ b/mysql-test/main/derived_cond_pushdown.result @@ -21096,7 +21096,7 @@ id select_type table type possible_keys key key_len ref rows filtered Extra 1 PRIMARY NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables 3 DERIVED NULL NULL NULL NULL NULL NULL NULL NULL no matching row in const table Warnings: -Note 1003 /* select#1 */ select NULL AS `f`,NULL AS `f` from `test`.`t1` `a` straight_join `test`.`t1` `b` join `test`.`v2` where 0 +Note 1003 /* select#1 */ select NULL AS `f`,`v2`.`f` AS `f` from `test`.`t1` `a` straight_join `test`.`t1` `b` join `test`.`v2` where 0 DROP VIEW v1,v2; DROP TABLE t1; # diff --git a/mysql-test/main/explain_non_select.result b/mysql-test/main/explain_non_select.result index d1e7af6afde..7dea232f260 100644 --- a/mysql-test/main/explain_non_select.result +++ b/mysql-test/main/explain_non_select.result @@ -145,7 +145,7 @@ DROP TABLE t1; create table t1 (i int); explain partitions update t1 set i = 3; id select_type table partitions type possible_keys key key_len ref rows Extra -1 SIMPLE t1 NULL ALL NULL NULL NULL NULL 0 +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE create table t2 (a int, b int) partition by hash(a) partitions 5; insert into t2 values (0,0),(1,1),(2,2),(3,3),(4,4); explain partitions update t2 set b=3 where a in (3,4); diff --git a/mysql-test/main/filesort_debug.result b/mysql-test/main/filesort_debug.result index 4aa40592be7..0280d378778 100644 --- a/mysql-test/main/filesort_debug.result +++ b/mysql-test/main/filesort_debug.result @@ -63,6 +63,7 @@ c27 TEXT, c28 TEXT, primary key (pk) ); +insert into t1 (pk) values (1),(2),(3); CALL mtr.add_suppression("Out of sort memory"); DELETE IGNORE FROM t1 ORDER BY c26,c7,c23,c4,c25,c5,c20, c19,c21,c8,c1,c27,c28,c3,c9,c22,c24,c6,c2,pk LIMIT 2; diff --git a/mysql-test/main/filesort_debug.test b/mysql-test/main/filesort_debug.test index a8833617c09..d5dc6a89507 100644 --- a/mysql-test/main/filesort_debug.test +++ b/mysql-test/main/filesort_debug.test @@ -86,6 +86,8 @@ CREATE TABLE t1 ( primary key (pk) ); +insert into t1 (pk) values (1),(2),(3); + CALL mtr.add_suppression("Out of sort memory"); --error ER_OUT_OF_SORTMEMORY diff --git a/mysql-test/main/join.result b/mysql-test/main/join.result index a156feeeea9..cb295c3528f 100644 --- a/mysql-test/main/join.result +++ b/mysql-test/main/join.result @@ -3435,4 +3435,24 @@ SELECT COUNT(*) FROM t1 LEFT JOIN (t2 LEFT JOIN t3 ON t2.b = t3.c) ON t1.a = t2. COUNT(*) 2 DROP TABLE t1, t2, t3; -# End of 10.5 tests +# +# MDEV-30256 Wrong result (missing rows) upon join with empty table +# +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (1),(2); +CREATE TABLE t2 (b INT); +INSERT INTO t2 VALUES (3),(4); +CREATE TABLE t3 (c INT PRIMARY KEY); +EXPLAIN SELECT * FROM t1 LEFT JOIN t2 JOIN t3 ON (t2.b >= t3.c) ON (t1.a < t2.b); +id select_type table type possible_keys key key_len ref rows Extra +1 SIMPLE t1 ALL NULL NULL NULL NULL 2 +1 SIMPLE t3 index PRIMARY PRIMARY 4 NULL 0 Using index; Using join buffer (flat, BNL join) +1 SIMPLE t2 ALL NULL NULL NULL NULL 2 Using where; Using join buffer (incremental, BNL join) +SELECT * FROM t1 LEFT JOIN t2 JOIN t3 ON (t2.b >= t3.c) ON (t1.a < t2.b); +a b c +1 NULL NULL +2 NULL NULL +DROP TABLE t1,t2,t3; +# +# End of 11.0 tests +# diff --git a/mysql-test/main/join.test b/mysql-test/main/join.test index dfe92ff9561..7e53a33099e 100644 --- a/mysql-test/main/join.test +++ b/mysql-test/main/join.test @@ -1838,4 +1838,19 @@ SELECT * FROM t1 LEFT JOIN (t2 LEFT JOIN t3 ON t2.b = t3.c) ON t1.a = t2.b; SELECT COUNT(*) FROM t1 LEFT JOIN (t2 LEFT JOIN t3 ON t2.b = t3.c) ON t1.a = t2.b; DROP TABLE t1, t2, t3; ---echo # End of 10.5 tests +--echo # +--echo # MDEV-30256 Wrong result (missing rows) upon join with empty table +--echo # + +CREATE TABLE t1 (a INT); +INSERT INTO t1 VALUES (1),(2); +CREATE TABLE t2 (b INT); +INSERT INTO t2 VALUES (3),(4); +CREATE TABLE t3 (c INT PRIMARY KEY); +EXPLAIN SELECT * FROM t1 LEFT JOIN t2 JOIN t3 ON (t2.b >= t3.c) ON (t1.a < t2.b); +SELECT * FROM t1 LEFT JOIN t2 JOIN t3 ON (t2.b >= t3.c) ON (t1.a < t2.b); +DROP TABLE t1,t2,t3; + +--echo # +--echo # End of 11.0 tests +--echo # diff --git a/mysql-test/main/myisam_explain_non_select_all.result b/mysql-test/main/myisam_explain_non_select_all.result index bb6fa785b7f..82d494be01c 100644 --- a/mysql-test/main/myisam_explain_non_select_all.result +++ b/mysql-test/main/myisam_explain_non_select_all.result @@ -2978,8 +2978,7 @@ JOIN t1 AS a12 ON a12.c1 = a11.c1 ) d1 ); id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY t3 ALL NULL NULL NULL NULL 0 100.00 -2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE DROP TABLE t1, t2, t3; #73 CREATE TABLE t1 (id INT); @@ -3106,7 +3105,7 @@ CALL p10(); DROP PROCEDURE p10; CALL p9(); id select_type table type possible_keys key key_len ref rows Extra -1 SIMPLE t1 ALL NULL NULL NULL NULL 0 +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL Impossible WHERE DROP PROCEDURE p9; CALL p8(); DROP PROCEDURE p8; diff --git a/mysql-test/main/ps.result b/mysql-test/main/ps.result index 5ddcf7974ae..d60fa28fc96 100644 --- a/mysql-test/main/ps.result +++ b/mysql-test/main/ps.result @@ -5552,13 +5552,11 @@ CREATE TABLE t2 (c2 INT) ENGINE=MyISAM; CREATE TABLE t3 (c3 INT) ENGINE=MyISAM; EXPLAIN EXTENDED UPDATE t3 SET c3 = ( SELECT COUNT(d1.c1) FROM ( SELECT a11.c1 FROM t1 AS a11 STRAIGHT_JOIN t2 AS a21 ON a21.c2 = a11.c1 JOIN t1 AS a12 ON a12.c1 = a11.c1 ) d1 ); id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY t3 ALL NULL NULL NULL NULL 0 100.00 -2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE PREPARE stmt FROM "EXPLAIN EXTENDED UPDATE t3 SET c3 = ( SELECT COUNT(d1.c1) FROM ( SELECT a11.c1 FROM t1 AS a11 STRAIGHT_JOIN t2 AS a21 ON a21.c2 = a11.c1 JOIN t1 AS a12 ON a12.c1 = a11.c1 ) d1 )"; EXECUTE stmt; id select_type table type possible_keys key key_len ref rows filtered Extra -1 PRIMARY t3 ALL NULL NULL NULL NULL 0 100.00 -2 SUBQUERY NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE noticed after reading const tables +1 SIMPLE NULL NULL NULL NULL NULL NULL NULL NULL Impossible WHERE DEALLOCATE PREPARE stmt; DROP TABLE t1, t2, t3; # diff --git a/mysql-test/suite/binlog/r/binlog_unsafe.result b/mysql-test/suite/binlog/r/binlog_unsafe.result index 0c0b0e77915..67849fc7fd1 100644 --- a/mysql-test/suite/binlog/r/binlog_unsafe.result +++ b/mysql-test/suite/binlog/r/binlog_unsafe.result @@ -2338,11 +2338,7 @@ Warnings: Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. The statement is unsafe because it uses a LIMIT clause. This is unsafe because the set of rows included cannot be predicted Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. REPLACE... SELECT is unsafe because the order in which rows are retrieved by the SELECT determines which (if any) rows are replaced. This order cannot be predicted and may differ on master and the slave UPDATE t1 SET a=1 LIMIT 1; -Warnings: -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. The statement is unsafe because it uses a LIMIT clause. This is unsafe because the set of rows included cannot be predicted DELETE FROM t1 LIMIT 1; -Warnings: -Note 1592 Unsafe statement written to the binary log using statement format since BINLOG_FORMAT = STATEMENT. The statement is unsafe because it uses a LIMIT clause. This is unsafe because the set of rows included cannot be predicted CREATE PROCEDURE p1() BEGIN INSERT INTO t1 SELECT * FROM t1 LIMIT 1; diff --git a/sql/opt_range.cc b/sql/opt_range.cc index 5130622ec04..93e37d732d4 100644 --- a/sql/opt_range.cc +++ b/sql/opt_range.cc @@ -2716,8 +2716,7 @@ int SQL_SELECT::test_quick_select(THD *thd, key_map keys_to_use, SEL_ARG **backup_keys= 0; ha_rows table_records= head->stat_records(); handler *file= head->file; - /* We trust that if stat_records() is 0 the table is really empty! */ - bool impossible_range= table_records == 0; + bool impossible_range= 0; DBUG_ENTER("SQL_SELECT::test_quick_select"); DBUG_PRINT("enter",("keys_to_use: %lu prev_tables: %lu const_tables: %lu", (ulong) keys_to_use.to_ulonglong(), (ulong) prev_tables, diff --git a/sql/sql_delete.cc b/sql/sql_delete.cc index d31d0b949ba..0c0f05aab07 100644 --- a/sql/sql_delete.cc +++ b/sql/sql_delete.cc @@ -526,7 +526,9 @@ bool mysql_delete(THD *thd, TABLE_LIST *table_list, COND *conds, select=make_select(table, 0, 0, conds, (SORT_INFO*) 0, 0, &error); if (unlikely(error)) DBUG_RETURN(TRUE); - if ((select && select->check_quick(thd, safe_update, limit)) || !limit) + if (unlikely((select && select->check_quick(thd, safe_update, limit)) || + table->stat_records() == 0 || + !limit)) { query_plan.set_impossible_where(); if (thd->lex->describe || thd->lex->analyze_stmt) diff --git a/sql/sql_update.cc b/sql/sql_update.cc index 5327e1f1095..4173d6d82ec 100644 --- a/sql/sql_update.cc +++ b/sql/sql_update.cc @@ -581,8 +581,10 @@ int mysql_update(THD *thd, set_statistics_for_table(thd, table); select= make_select(table, 0, 0, conds, (SORT_INFO*) 0, 0, &error); - if (unlikely(error || !limit || thd->is_error() || - (select && select->check_quick(thd, safe_update, limit)))) + if (unlikely(error || thd->is_error() || !limit || + (select && select->check_quick(thd, safe_update, limit)) || + table->stat_records() == 0)) + { query_plan.set_impossible_where(); if (thd->lex->describe || thd->lex->analyze_stmt) diff --git a/storage/spider/mysql-test/spider/bg/r/spider_fixes.result b/storage/spider/mysql-test/spider/bg/r/spider_fixes.result index a6a7588b014..2f54ef93a13 100644 --- a/storage/spider/mysql-test/spider/bg/r/spider_fixes.result +++ b/storage/spider/mysql-test/spider/bg/r/spider_fixes.result @@ -481,7 +481,6 @@ DELETE FROM t1; Warnings: Error 12702 Remote table 'auto_test_remote.ter1_1' is not found Error 12702 Remote table 'auto_test_remote.ter1_1' is not found -Error 1146 Table 'auto_test_remote.ter1_1' doesn't exist TRUNCATE t1; Warnings: Error 1146 Table 'auto_test_remote.ter1_1' doesn't exist diff --git a/storage/spider/mysql-test/spider/r/spider_fixes.result b/storage/spider/mysql-test/spider/r/spider_fixes.result index 3b9d939393a..5e17e83618e 100644 --- a/storage/spider/mysql-test/spider/r/spider_fixes.result +++ b/storage/spider/mysql-test/spider/r/spider_fixes.result @@ -481,7 +481,6 @@ DELETE FROM t1; Warnings: Error 12702 Remote table 'auto_test_remote.ter1_1' is not found Error 12702 Remote table 'auto_test_remote.ter1_1' is not found -Error 1146 Table 'auto_test_remote.ter1_1' doesn't exist TRUNCATE t1; Warnings: Error 1146 Table 'auto_test_remote.ter1_1' doesn't exist