[planner] Alternative logical plan returns empty result for IN subquery with LIMIT
- Dominant language
- Go
- Stars
- 40.5k
- Forks
- 6.2k
- PR merge metrics
- PR metrics pending
Description
## Bug Report
### 1. Minimal reproduce step (Required)
```sql
DROP DATABASE IF EXISTS correlate_limit_repro;
CREATE DATABASE correlate_limit_repro;
USE correlate_limit_repro;
CREATE TABLE outer_t (
a INT NOT NULL PRIMARY KEY,
b INT,
KEY(b)
);
CREATE TABLE inner_t (
a INT NOT NULL PRIMARY KEY
);
INSERT INTO outer_t VALUES (1, 1), (2, 1), (3, 1), (4, 1);
INSERT INTO inner_t VALUES (2), (3), (4);
SET SESSION tidb_opt_enable_alternative_logical_plans = OFF;
SELECT a
FROM outer_t
WHERE b = 1 AND a IN (SELECT a FROM inner_t LIMIT 1)
ORDER BY a;
-- Returns one row (2 on the tested build)
SET SESSION tidb_opt_enable_alternative_logical_plans = ON;
SELECT a
FROM outer_t
WHERE b = 1 AND a IN (SELECT a FROM inner_t LIMIT 1)
ORDER BY a;
```
The query has no fault injection or concurrency requirements.
### 2. What did you expect to see? (Required)
Enabling alternative logical plans must not change the query result. The second query should
also return exactly one row. Since the inner query can return any one of `2`, `3`, or `4`
without an inner `ORDER BY`, the returned value may vary, but the result must not be empty.
### 3. What did you see instead (Required)
With `tidb_opt_enable_alternative_logical_plans = ON`, the query returns an empty result set.
`EXPLAIN FORMAT = 'brief'` shows the inner side of the Apply alternative as an empty dual:
```text
Apply CARTESIAN semi join
├─IndexReader(Build)
│ └─IndexRangeScan table:outer_t, index:b(b), range:[1,1]
└─Selection(Probe) eq(inner_t.a, outer_t.a)
└─Limit offset:0, count:1
└─TableDual rows:0
```
The inner table is non-empty, so replacing it with `TableDual(rows:0)` silently drops matching
outer rows. Removing the inner `LIMIT` makes the query return the expected matching rows with
alternative logical plans enabled.
This is related to [#69790](https://github.com/pingcap/tidb/issues/69790), which reports a
similar empty-result failure for an aggregate `IN` subquery, but this report covers the simpler
non-aggregate `LIMIT` shape.
### 4. What is your TiDB version? (Required)
```text
Release Version: v8.5.7
Edition: Community
Git Commit Hash: 202b7f47286a1109b5c957401d34c9358d130ae0
Git Branch: HEAD
```
The regression was introduced by [PR #68752](https://github.com/pingcap/tidb/pull/68752).
Contributor guide
Research direction
Start with the planner changes introduced by PR #68752 and run the minimal SQL reproduction with alternative logical plans both disabled and enabled. Use EXPLAIN FORMAT = 'brief' to inspect why the Apply alternative produces TableDual rows:0; done means the enabled query returns one matching row, with coverage for this LIMIT-shaped IN subquery.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go, sql
- Domain
- databases
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100