From 73ef86d4ef9f2d225501f8cc153b737a4177ff1e Mon Sep 17 00:00:00 2001 From: Oleg Smirnov Date: Tue, 19 Sep 2023 19:20:21 +0700 Subject: [PATCH] MDEV-29731 Assertion failure when HAVING in a correlated subquery references columns in the outer query When resolving a column from the HAVING clause, a new Item_field object may be created inside Item_ref::fix_fields(). But the object is created with an empty name resolution context, which then leads to debug assertion failure during Item_field::fix_fields(). The solution is to pass the correct name resolution context when creating the Item_field object. Reviewer: Oleksandr Byelkin (sanja@mariadb.com) --- mysql-test/main/having.result | 49 +++++++++++++++++++++++++++++++++++ mysql-test/main/having.test | 47 ++++++++++++++++++++++++++++++++- sql/item.cc | 2 +- 3 files changed, 96 insertions(+), 2 deletions(-) diff --git a/mysql-test/main/having.result b/mysql-test/main/having.result index 5f75c847993..95497a59c20 100644 --- a/mysql-test/main/having.result +++ b/mysql-test/main/having.result @@ -904,5 +904,54 @@ SELECT * FROM t HAVING f = 'foo'; f DROP TABLE t; # +# MDEV-29731 Crash when HAVING in a correlated subquery references +# columns in the outer query +# +CREATE TABLE t (a INT, b INT); +SELECT 1 FROM t +WHERE b = (SELECT 1 FROM t GROUP BY a HAVING b = a+1); +1 +DROP TABLE t; +CREATE TABLE t (a INT, b INT, c INT); +SELECT 1 FROM t +WHERE (b,c) = (SELECT 1,1 FROM t GROUP BY a HAVING b = a+1 and c = a-1); +1 +DROP TABLE t; +CREATE TABLE t (a TEXT, b INT UNIQUE); +SELECT 1 FROM t +WHERE b IN (SELECT 1 FROM t +GROUP BY '', a +HAVING (CASE b WHEN 1 +'' THEN 3 ELSE a END) +ORDER BY b) +GROUP BY b HAVING b = 1; +1 +Warnings: +Warning 1292 Truncated incorrect DOUBLE value: '' +DROP TABLE t; +CREATE TABLE t (a INT, b CHAR KEY UNIQUE); +CREATE VIEW v AS SELECT * FROM t WHERE a LIKE '' GROUP BY b HAVING a > a; +SELECT * FROM v AS v1 NATURAL JOIN v AS v5 NATURAL JOIN v +WHERE a LIKE '' AND b IN (SELECT a FROM t +WHERE a LIKE '' + GROUP BY a +HAVING b LIKE (b < +1 OR a > 1) >= b); +a b +DROP VIEW v; +DROP TABLE t; +EXECUTE IMMEDIATE 'SELECT LEAD(c) OVER (ORDER BY c) + FROM (SELECT 0 AS c) AS a NATURAL JOIN (SELECT 0 AS c) AS b;'; +LEAD(c) OVER (ORDER BY c) +NULL +CREATE TABLE t (a INT); +UPDATE t SET a = '' + WHERE 1 IN (SELECT * FROM +(SELECT * FROM +(SELECT * FROM t AS v5 NATURAL JOIN t AS v4 NATURAL JOIN t) AS v3 +NATURAL JOIN t +GROUP BY a) AS v2 +WHERE (0, a) IN ((0,-1),(+1,0)) +ORDER BY 1+AVG(a) OVER (ORDER BY a)) ORDER BY a; +DROP TABLE t; +# # End of 10.4 tests # diff --git a/mysql-test/main/having.test b/mysql-test/main/having.test index 618fcb57250..f5212d441d5 100644 --- a/mysql-test/main/having.test +++ b/mysql-test/main/having.test @@ -950,8 +950,53 @@ DROP TABLE t1,t2; CREATE TABLE t (f VARCHAR(512)); INSERT INTO t VALUES ('a'),('b'); SELECT * FROM t HAVING f = 'foo'; +DROP TABLE t; -# Cleanup +--echo # +--echo # MDEV-29731 Crash when HAVING in a correlated subquery references +--echo # columns in the outer query +--echo # +CREATE TABLE t (a INT, b INT); +SELECT 1 FROM t + WHERE b = (SELECT 1 FROM t GROUP BY a HAVING b = a+1); +DROP TABLE t; + +CREATE TABLE t (a INT, b INT, c INT); +SELECT 1 FROM t + WHERE (b,c) = (SELECT 1,1 FROM t GROUP BY a HAVING b = a+1 and c = a-1); +DROP TABLE t; + +CREATE TABLE t (a TEXT, b INT UNIQUE); +SELECT 1 FROM t + WHERE b IN (SELECT 1 FROM t + GROUP BY '', a + HAVING (CASE b WHEN 1 +'' THEN 3 ELSE a END) + ORDER BY b) + GROUP BY b HAVING b = 1; +DROP TABLE t; + +CREATE TABLE t (a INT, b CHAR KEY UNIQUE); +CREATE VIEW v AS SELECT * FROM t WHERE a LIKE '' GROUP BY b HAVING a > a; +SELECT * FROM v AS v1 NATURAL JOIN v AS v5 NATURAL JOIN v + WHERE a LIKE '' AND b IN (SELECT a FROM t + WHERE a LIKE '' + GROUP BY a + HAVING b LIKE (b < +1 OR a > 1) >= b); +DROP VIEW v; +DROP TABLE t; + +EXECUTE IMMEDIATE 'SELECT LEAD(c) OVER (ORDER BY c) + FROM (SELECT 0 AS c) AS a NATURAL JOIN (SELECT 0 AS c) AS b;'; + +CREATE TABLE t (a INT); +UPDATE t SET a = '' + WHERE 1 IN (SELECT * FROM + (SELECT * FROM + (SELECT * FROM t AS v5 NATURAL JOIN t AS v4 NATURAL JOIN t) AS v3 + NATURAL JOIN t + GROUP BY a) AS v2 + WHERE (0, a) IN ((0,-1),(+1,0)) + ORDER BY 1+AVG(a) OVER (ORDER BY a)) ORDER BY a; DROP TABLE t; --echo # diff --git a/sql/item.cc b/sql/item.cc index 472cf3ce2fc..500d9f73a08 100644 --- a/sql/item.cc +++ b/sql/item.cc @@ -8041,7 +8041,7 @@ bool Item_ref::fix_fields(THD *thd, Item **reference) if (from_field != not_found_field) { Item_field* fld; - if (!(fld= new (thd->mem_root) Item_field(thd, from_field))) + if (!(fld= new (thd->mem_root) Item_field(thd, context, from_field))) goto error; thd->change_item_tree(reference, fld); mark_as_dependent(thd, last_checked_context->select_lex,