Skip to content

correlated subquery with INDEX_LOOKUP_PUSHDOWN may fail with Unexpected non-first executor TypeTableScan #67546

@lcwangchao

Description

@lcwangchao

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions