pingcap / pingcap/tidb

Wrong result: HAVING clause with `NOT(non-aggregate-pred OR aggregate-pred)` produces incorrect rows due to non-aggregate predicate being pushed down to cop[tikv] Selection

Open
#68,616 7 comments 0 reactions 0 assignees View on GitHub
ai-bugfix contribution may-affects-7.5 may-affects-8.1 may-affects-8.5 severity/critical sig/planner type/bug
Dominant language
Go
Stars
40.5k
Forks
6.2k
PR merge metrics
PR metrics pending

Description

## Bug Report

Please answer these questions before submitting your issue. Thanks!

### 1. Minimal reproduce step (Required)

**Reproduction 1 — FLOAT UNSIGNED with REGEXP / CASE in HAVING**
```sql
DROP DATABASE IF EXISTS database7;
CREATE DATABASE database7;
USE database7;

CREATE TABLE t0(c0 FLOAT UNSIGNED);
INSERT INTO t0 VALUES (0.698059195919747);
INSERT INTO t0(c0) VALUES (0.09950191164007238);
INSERT INTO t0(c0) VALUES (1.142487236E9), (0.031526090981527366);
INSERT INTO t0 VALUES (0.5389515701949363);
INSERT INTO t0 VALUES (0.7725500190030953), (0.22799797955785361);
INSERT INTO t0 VALUES (0.3389213598518491);
INSERT INTO t0(c0) VALUES (0.8960697137515322);
INSERT INTO t0 VALUES (0.6513303344278238);
INSERT INTO t0(c0) VALUES (0.09950191164007238);
INSERT INTO t0 VALUES (0.691189401027172);
INSERT INTO t0(c0) VALUES (0.4875978418275635);
INSERT INTO t0 VALUES (0.7637389704839178);

-- Q1 (original, with HAVING)
SELECT CONCAT(IFNULL(t0.c0, '__NULL__'))
FROM t0
GROUP BY t0.c0
HAVING (NOT (((((COUNT(t0.c0))<(AVG(t0.c0))))
OR(((t0.c0)NOT REGEXP((CASE t0.c0 WHEN t0.c0 THEN t0.c0
ELSE ((t0.c0)NOT REGEXP(t0.c0)) END )))))));

-- Q2 (semantically equivalent: same expression as a derived column, filtered by outer WHERE)
SELECT ref0
FROM (
SELECT CONCAT(IFNULL(t0.c0, '__NULL__')) AS ref0,
(NOT (((((COUNT(t0.c0))<(AVG(t0.c0))))
OR(((t0.c0)NOT REGEXP((CASE t0.c0 WHEN t0.c0 THEN t0.c0
ELSE ((t0.c0)NOT REGEXP(t0.c0)) END )))))) ) AS ref1
FROM t0
GROUP BY t0.c0
) AS s
WHERE ref1;

```

**Reproduction 2 — NUMERIC UNSIGNED with CAST AS DATETIME / BIT_OR in HAVING**
```sql
DROP DATABASE IF EXISTS database13;
CREATE DATABASE database13;
USE database13;

CREATE TABLE t0(c0 NUMERIC UNSIGNED);
INSERT IGNORE INTO t0 VALUES (-349599311); -- clamped to 0

-- Q1 (original, with HAVING)
SELECT CONCAT(IFNULL(t0.c0, '__NULL__'))
FROM t0
WHERE (((((NOT ('B!c喽\nM)\n')))AND(2023372831)))OR(((t0.c0) IS NULL)))
GROUP BY t0.c0
HAVING (NOT (((((t0.c0)!=(CAST((CASE t0.c0 WHEN -1285358684 THEN t0.c0
ELSE CAST(t0.c0 AS DATETIME) END ) AS DATETIME))))
OR(((BIT_OR(t0.c0))>(COUNT(t0.c0)))))));

-- Q2 (semantically equivalent)
SELECT ref0
FROM (
SELECT CONCAT(IFNULL(t0.c0, '__NULL__')) AS ref0,
(NOT (((((t0.c0)!=(CAST((CASE t0.c0 WHEN -1285358684 THEN t0.c0
ELSE CAST(t0.c0 AS DATETIME) END ) AS DATETIME))))
OR(((BIT_OR(t0.c0))>(COUNT(t0.c0))))))) AS ref1
FROM t0
WHERE (((((NOT ('B!c喽\nM)\n')))AND(2023372831)))OR(((t0.c0) IS NULL)))
GROUP BY t0.c0
) AS s
WHERE ref1;
```

### 2. What did you expect to see? (Required)
Q1 and Q2 are semantically equivalent, so they must return the same set of rows. MySQL 8.0 confirms this:

- Reproduction 1: both Q1 and Q2 return 12 rows.

- Reproduction 2: both Q1 and Q2 return 0 rows (Empty set).
### 3. What did you see instead (Required)
**Case 1:**
```sql

SELECT CONCAT(IFNULL(t0.c0, '__NULL__')) FROM t0 GROUP BY t0.c0 HAVING (NOT (((((COUNT(t0.c0))<(AVG(t0.c0))))OR(((t0.c0)NOT REGEXP((CASE t0.c0 WHEN t0.c0 THEN t0.c0 ELSE ((t0.c0)NOT REGEXP(t0.c0)) END )))))));-- cardinality: 0
Empty set (0.00 sec)

SELECT ref0 FROM (SELECT CONCAT(IFNULL(t0.c0, '__NULL__')) AS ref0, (NOT (((((COUNT(t0.c0))<(AVG(t0.c0))))OR(((t0.c0)NOT REGEXP((CASE t0.c0 WHEN t0.c0 THEN t0.c0 ELSE ((t0.c0)NOT REGEXP(t0.c0)) END ))))))) AS ref1 FROM t0 GROUP BY t0.c0) AS s WHERE ref1;-- cardinality: 12
+-------------+
| ref0 |
+-------------+
| 0.6980592 |
| 0.031526092 |
| 0.33892137 |
| 0.763739 |
| 0.5389516 |
| 0.8960697 |
| 0.6911894 |
| 0.77255005 |
| 0.22799797 |
| 0.65133035 |
| 0.48759785 |
| 0.099501915 |
+-------------+
12 rows in set (0.00 sec)
```

**Case 2:**
```sql
SELECT CONCAT(IFNULL(t0.c0, '__NULL__')) FROM t0 WHERE (((((NOT ('B!c喽\nM)\n')))AND(2023372831)))OR(((t0.c0) IS NULL))) GROUP BY t0.c0 HAVING (NOT (((((t0.c0)!=(CAST((CASE t0.c0 WHEN -1285358684 THEN t0.c0 ELSE CAST(t0.c0 AS DATETIME) END ) AS DATETIME))))OR(((BIT_OR(t0.c0))>(COUNT(t0.c0)))))));-- cardinality: 1
+-----------------------------------+
| CONCAT(IFNULL(t0.c0, '__NULL__')) |
+-----------------------------------+
| 0 |
+-----------------------------------+
1 row in set, 1 warning (0.00 sec)

SELECT ref0 FROM (SELECT CONCAT(IFNULL(t0.c0, '__NULL__')) AS ref0, (NOT (((((t0.c0)!=(CAST((CASE t0.c0 WHEN -1285358684 THEN t0.c0 ELSE CAST(t0.c0 AS DATETIME) END ) AS DATETIME))))OR(((BIT_OR(t0.c0))>(COUNT(t0.c0))))))) AS ref1 FROM t0 WHERE (((((NOT ('B!c喽\nM)\n')))AND(2023372831)))OR(((t0.c0) IS NULL))) GROUP BY t0.c0) AS s WHERE ref1;-- cardinality: 0
Empty set, 2 warnings (0.01 sec)
```
### 4. What is your TiDB version? (Required)
Two version:
```sql

mysql> select tidb_version();
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| tidb_version() |
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Release Version: v8.5.5
Edition: Community
Git Commit Hash: 1fa258b833ff113883beeba40bc130be7ce66610
Git Branch: HEAD
UTC Build Time: 2026-01-14 22:20:57
GoVersion: go1.25.5
Race Enabled: false
Check Table Before Drop: false
Store: unistore |
+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> select tidb_version();
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| tidb_version() |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Release Version: v9.0.0-beta.2.pre-1651-g33ae9e3cb5
Edition: Community
Git Commit Hash: 33ae9e3cb52f4891ff35407ed736107aa34e84b9
Git Branch: HEAD
UTC Build Time: 2026-05-01 06:07:45
GoVersion: go1.25.9
Race Enabled: false
Check Table Before Drop: false
Store: unistore
Kernel Type: Classic |
+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)

```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.