WHERE` Expression with `CASE`/`DATE_FORMAT`/`CAST` Evaluates Differently When Pushed to TiKV vs. Evaluated as MergeJoin `other cond` at Root
- 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)
```sql
DROP DATABASE IF EXISTS repro_tidb615_db7_min;
CREATE DATABASE repro_tidb615_db7_min;
USE repro_tidb615_db7_min;
SET SESSION sql_mode='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION';
SET time_zone='+00:00';
CREATE TABLE src(
id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
c0 DOUBLE NULL
);
INSERT INTO src(c0) VALUES (0.25);
CREATE TABLE l(
id BIGINT NOT NULL PRIMARY KEY,
c0 DOUBLE NULL
);
CREATE TABLE r(
id BIGINT NOT NULL PRIMARY KEY,
c0 DOUBLE NULL
);
INSERT INTO l SELECT id, c0 FROM src;
INSERT INTO r SELECT id, c0 FROM src;
-- Single-table: returns 1 row
SELECT 1 AS ok
FROM src
WHERE DATE_FORMAT(
CASE 0
WHEN CAST(id AS CHAR) THEN NULL
WHEN TRUE THEN c0
ELSE CAST(c0 AS DATE)
END,
(c0 OR 1) NOT LIKE TRUE
) | id;
-- Split/join via derived table: incorrectly returns empty set
SELECT 1 AS ok
FROM (
SELECT l.id, r.c0
FROM l JOIN r ON l.id = r.id
) v
WHERE DATE_FORMAT(
CASE 0
WHEN CAST(v.id AS CHAR) THEN NULL
WHEN TRUE THEN v.c0
ELSE CAST(v.c0 AS DATE)
END,
(v.c0 OR 1) NOT LIKE TRUE
) | v.id;
```
### 2. What Did You Expect to See? (Required)
Both queries operate on the same single row (`id=1, c0=0.25`). The `WHERE` condition should evaluate identically:
- `CASE 0 WHEN CAST(id AS CHAR) THEN NULL WHEN TRUE THEN c0 ELSE CAST(c0 AS DATE) END` → all branches fail, so `ELSE CAST(c0 AS DATE)` is returned.
- `(c0 OR 1) NOT LIKE TRUE` → `(0.25 OR 1)` = `1`, `1 NOT LIKE TRUE` → `0` (format string `'0'`).
- `DATE_FORMAT(cast_result, '0')` → returns the string `'0'` if the date is non‑NULL.
- `'0' | id` → bitwise OR yields a non‑zero number, which is truthy.
Thus the condition should be `TRUE` and both queries should return `1 row`.
### 3. What Did You See Instead? (Required)
- Single‑table query: returns `1 row` (correct).
- Join query: returns `Empty set` (wrong).
### 4. What Is Your TiDB Version? (Required)
Version: TiDB‑v9.0.0
### 5. Execution Plan Differences
**Single‑table (correct)** — entire `WHERE` clause pushed down to TiKV as a coprocessor selection:
```
Selection_6 [cop[tikv]] bitor(cast(date_format(cast(case(... src.c0 ...), datetime(6)), ...), bigint), src.id)
```
All expression evaluation (including `CAST(c0 AS DATE)` under `NO_ZERO_DATE` mode) happens inside TiKV.
**Join query (wrong)** — `WHERE` becomes `MergeJoin` `other cond`, evaluated at TiDB root:
```
MergeJoin_10 [root] other cond: bitor(cast(date_format(cast(case(... r.c0 ...), datetime(6)), ...), bigint), l.id)
```
The identical expression is now evaluated at the TiDB root layer after the join.
Contributor guide
Assessment
This issue has not been assessed yet.