Bug #120051 Wrong result for derived-table rewrite of GROUP BY ... HAVING query
Submitted: 13 Mar 9:31 Modified: 13 Mar 14:54
Reporter: 策 吕 Email Updates:
Status: Verified Impact on me:
None 
Category:MySQL Server: Optimizer Severity:S1 (Critical)
Version:8.0.45 OS:Linux (ubuntu 20.04)
Assigned to: CPU Architecture:Any

[13 Mar 9:31] 策 吕
Description:
A query using GROUP BY ... HAVING returns one row, but an equivalent derived-table rewrite that materializes the HAVING predicate as a projected column and filters it in an outer WHERE returns an empty set.

This indicates a wrong-result bug in the optimizer or executor when handling a materialized HAVING predicate across a derived table boundary.

This behavior was observed on MySQL, whereas TiDB v8.5.5 and MariaDB 11.8.5  handles both queries consistently, returning 1 row (null) for both.

How to repeat:
-------------------------------- To Reproduce --------------------------------

CREATE TABLE t1(c0 FLOAT, c1 FLOAT) ;

INSERT INTO t1(c0) VALUES(0.6809136682939025);
INSERT INTO t1(c0) VALUES(NULL);
INSERT INTO t1(c0) VALUES(0);

CREATE UNIQUE INDEX i1 USING HASH ON t1(c1, c0);
-- cardinality: 1
SELECT DISTINCT CONCAT(t1.c1) FROM t1 WHERE (NOT ((t1.c0) IS NOT FALSE)) GROUP BY t1.c1 HAVING ((MAX(DISTINCT t1.c0)) >= (-1214742575));
-- cardinality: 0
SELECT DISTINCT ref0 FROM (SELECT CONCAT(t1.c1) AS ref0, (((MAX(DISTINCT t1.c0)) >= (-1214742575))) AS ref1 FROM t1 WHERE (NOT ((t1.c0) IS NOT FALSE)) GROUP BY t1.c1) AS s WHERE ref1;

-------------------------------- What did I expect to see? --------------------------------

mysql> SELECT DISTINCT CONCAT(t1.c1)
    -> FROM t1
    -> WHERE (NOT ((t1.c0) IS NOT FALSE))
    -> GROUP BY t1.c1
    -> HAVING ((MAX(DISTINCT t1.c0)) >= (-1214742575))
    ->     || ((MAX(DISTINCT t1.c0)) < (-506330848));
Empty set, 1 warning (0.00 sec)

mysql>
mysql> SELECT DISTINCT ref0
    -> FROM (
    ->     SELECT CONCAT(t1.c1) AS ref0,
    ->            (((MAX(DISTINCT t1.c0)) >= (-1214742575))
    ->              || ((MAX(DISTINCT t1.c0)) < (-506330848))) AS ref1
    ->     FROM t1
    ->     WHERE (NOT ((t1.c0) IS NOT FALSE))
    ->     GROUP BY t1.c1
    -> ) AS s
    -> WHERE ref1;
Empty set, 1 warning (0.00 sec)

-------------------------------- What did I see instead --------------------------------
mysql> -- Query 1: returns 1 row
mysql> SELECT DISTINCT CONCAT(t1.c1)
    -> FROM t1
    -> WHERE (NOT ((t1.c0) IS NOT FALSE))
    -> GROUP BY t1.c1
    -> HAVING ((MAX(DISTINCT t1.c0)) >= (-1214742575))
    ->     || ((MAX(DISTINCT t1.c0)) < (-506330848));
+---------------+
| CONCAT(t1.c1) |
+---------------+
| NULL          |
+---------------+
1 row in set, 1 warning (0.00 sec)

mysql>
mysql> -- Query 2: unexpectedly returns 0 rows
mysql> SELECT DISTINCT ref0
    -> FROM (
    ->     SELECT CONCAT(t1.c1) AS ref0,
    ->            (((MAX(DISTINCT t1.c0)) >= (-1214742575))
    ->              || ((MAX(DISTINCT t1.c0)) < (-506330848))) AS ref1
    ->     FROM t1
    ->     WHERE (NOT ((t1.c0) IS NOT FALSE))
    ->     GROUP BY t1.c1
    -> ) AS s
    -> WHERE ref1;
Empty set, 1 warning (0.00 sec)
[13 Mar 14:13] Roy Lyseng
Thank you for the bug report.
Verified as described.
[13 Mar 14:54] 策 吕
Thanks for verifying. I noticed the Version field was changed from 9.6.0 to 8.0.45.
Just to confirm: does this mean the issue was also reproduced on 8.0.45, or that 8.0.45 is considered the earliest affected version?
I originally observed it on 9.6.0, so I want to understand the affected version range more precisely.
[13 Mar 21:16] Roy Lyseng
We verify on earlier versions to clarify whether the problem is due to a recent regression or a legacy issue. But we rarely go back earlier than 8.0 for this check. Thus, 8.0 is the earliest known release with this problem.