Bug Report
1. Minimal reproduce step (Required)
I can reproduce this on a TiDB + TiKV deployment with the following minimal case:
CREATE DATABASE IF NOT EXISTS verify_ilp;
USE verify_ilp;
CREATE TABLE touter (a INT, b INT, KEY idx_a(a));
CREATE TABLE tinner (a INT, b INT, KEY idx_a(a));
INSERT INTO touter VALUES (10,1),(20,5),(30,9);
INSERT INTO tinner VALUES (1,100),(3,200),(6,300),(10,400),(12,500),(15,600),(25,700);
SELECT *
FROM touter
WHERE touter.a > (
SELECT /*+ INDEX_LOOKUP_PUSHDOWN(tinner, idx_a) */ SUM(tinner.b)
FROM tinner USE INDEX(idx_a)
WHERE tinner.a > touter.b
);
EXPLAIN FORMAT='brief' for the query shows a correlated Apply whose inner side contains LocalIndexLookUp.
2. What did you expect to see? (Required)
The query should execute successfully.
3. What did you see instead (Required)
The query fails with the following error from TiKV batch executor:
other error: [components/tidb_query_executors/src/runner.rs:569]: Unexpected non-first executor TypeTableScan
I can also reproduce the same class of issue with a larger business query that contains correlated subqueries under Apply and INDEX_LOOKUP_PUSHDOWN.
A few observations from local debugging:
- Removing
INDEX_LOOKUP_PUSHDOWN makes the query succeed.
- Using a non-correlated subquery with
INDEX_LOOKUP_PUSHDOWN works.
- Using a correlated subquery that does not need row lookup (for example, only reading index columns) also works.
- The problematic shape seems to be: correlated subquery under
Apply + pushed-down LocalIndexLookUp + row lookup needed.
From code inspection, the planner-side IndexPlansUnNatureOrders looks correct, but IndexLookUpExecutor.open() rebuilds e.dagPB.Executors when corColInIdxSide is true. In that rebuild path, the ParentIdx information for the pushed-down LocalIndexLookUp branch appears to be lost, so TiKV later sees a non-first TypeTableScan on the main list-DAG path.
The relevant code path seems to be:
pkg/executor/distsql.go: IndexLookUpExecutor.open()
pkg/executor/internal/builder/builder_utils.go: list-DAG construction
A local fix that preserves the un-natural parent relationship when rebuilding the index-side DAG under corColInIdxSide makes the minimal repro pass.
4. What is your TiDB version? (Required)
Release Version: v8.5.5-20260214-791f51e-3-g68d0945d8d
Edition: Community
Git Commit Hash: 68d0945d8dd1b70232742bb259e95d5e830af645
Git Branch: codex/backup-disable_aa_asynccommit-20260313
UTC Build Time: 2026-03-13 07:09:20
GoVersion: go1.25.5
Race Enabled: false
Check Table Before Drop: false
Store: tikv
Workaround
Removing the INDEX_LOOKUP_PUSHDOWN hint or forcing NO_INDEX_LOOKUP_PUSHDOWN avoids the issue.
Bug Report
1. Minimal reproduce step (Required)
I can reproduce this on a TiDB + TiKV deployment with the following minimal case:
EXPLAIN FORMAT='brief'for the query shows a correlatedApplywhose inner side containsLocalIndexLookUp.2. What did you expect to see? (Required)
The query should execute successfully.
3. What did you see instead (Required)
The query fails with the following error from TiKV batch executor:
I can also reproduce the same class of issue with a larger business query that contains correlated subqueries under
ApplyandINDEX_LOOKUP_PUSHDOWN.A few observations from local debugging:
INDEX_LOOKUP_PUSHDOWNmakes the query succeed.INDEX_LOOKUP_PUSHDOWNworks.Apply+ pushed-downLocalIndexLookUp+ row lookup needed.From code inspection, the planner-side
IndexPlansUnNatureOrderslooks correct, butIndexLookUpExecutor.open()rebuildse.dagPB.ExecutorswhencorColInIdxSideis true. In that rebuild path, theParentIdxinformation for the pushed-downLocalIndexLookUpbranch appears to be lost, so TiKV later sees a non-firstTypeTableScanon the main list-DAG path.The relevant code path seems to be:
pkg/executor/distsql.go:IndexLookUpExecutor.open()pkg/executor/internal/builder/builder_utils.go: list-DAG constructionA local fix that preserves the un-natural parent relationship when rebuilding the index-side DAG under
corColInIdxSidemakes the minimal repro pass.4. What is your TiDB version? (Required)
Workaround
Removing the
INDEX_LOOKUP_PUSHDOWNhint or forcingNO_INDEX_LOOKUP_PUSHDOWNavoids the issue.