Skip to content

Slice bounds out of range panic in cascades planner when generating plan for indexed table #68455

@ZyanNo1

Description

@ZyanNo1

Bug Report

1. Minimal reproduce step (Required)

Save the following SQL to a file (e.g., repro.sql) and execute it against a fresh TiDB instance:

USE test;
DROP DATABASE IF EXISTS database20;
CREATE DATABASE database20;
USE database20;
CREATE TABLE IF NOT EXISTS idx_t2 (c0 INT NOT NULL, c1 FLOAT NOT NULL, c2 VARCHAR(64), c3 INT NOT NULL, c4 FLOAT, c5 VARCHAR(64) NOT NULL, c6 INT, c7 FLOAT NOT NULL);
CREATE INDEX IF NOT EXISTS idx_2_c0 ON idx_t2 (c0);
CREATE INDEX IF NOT EXISTS idx_2_c1 ON idx_t2 (c1);
CREATE INDEX IF NOT EXISTS idx_2_c0c1 ON idx_t2 (c0, c1);
CREATE INDEX IF NOT EXISTS idx_2_c5 ON idx_t2 (c5);
CREATE INDEX IF NOT EXISTS idx_2_c1desc ON idx_t2 (c1 DESC);
CREATE INDEX IF NOT EXISTS idx_2_c4 ON idx_t2 (c4);
CREATE INDEX IF NOT EXISTS idx_2_c7 ON idx_t2 (c7);
INSERT IGNORE INTO idx_t2(c0, c1, c2, c3, c4, c5, c6, c7) VALUES (43, 352.9813385747763, '-406092536', 20, 213.9958336986293, 'mG', 57, 1.108112066E12);
CREATE INDEX cover_idx_3120 ON idx_t2 (c3, c4, c6);
SET SESSION tidb_opt_enable_hash_join=0;
CREATE UNIQUE INDEX uniq_idx_3124 ON idx_t2 (c7);
CREATE INDEX range_idx_3118 ON idx_t2 (c1);
CREATE UNIQUE INDEX uniq_idx_3117 ON idx_t2 (c0);
CREATE INDEX expr_idx_9610 ON idx_t2 ((LOWER(c2)));
CREATE INDEX imerge_a_3117 ON idx_t2 (c0);
CREATE INDEX imerge_b_3118 ON idx_t2 (c1);
CREATE INDEX imerge_a_3117 ON idx_t2 (c0);
CREATE INDEX imerge_b_3123 ON idx_t2 (c6);
DROP INDEX idx_2_c0 ON idx_t2;
ANALYZE TABLE idx_t2;
CREATE INDEX imerge_a_3118 ON idx_t2 (c1);
CREATE INDEX imerge_b_3124 ON idx_t2 (c7);
CREATE INDEX imerge_a_3118 ON idx_t2 (c1);
CREATE INDEX imerge_b_3120 ON idx_t2 (c3);
CREATE INDEX range_idx_3124 ON idx_t2 (c7);
CREATE INDEX ordlim_idx_3124 ON idx_t2 (c7);
CREATE INDEX cover_idx_3121 ON idx_t2 (c4, c1);
CREATE UNIQUE INDEX uniq_idx_3123 ON idx_t2 (c6);
SET SESSION tidb_opt_insubq_to_join_and_agg=0;
SET SESSION tidb_opt_agg_push_down=1;
CREATE INDEX sq_in_3123 ON idx_t2 (c6);
SET SESSION tidb_enable_inl_join=0;
CREATE INDEX ordlim_idx_3121 ON idx_t2 (c4);
SET SESSION tidb_enable_index_fast_plan=0;
CREATE INDEX range_idx_3118 ON idx_t2 (c1);
CREATE INDEX prefix_idx_3122 ON idx_t2 (c5(1));
CREATE UNIQUE INDEX uniq_idx_3124 ON idx_t2 (c7);
CREATE INDEX range_idx_3120 ON idx_t2 (c3);
ANALYZE TABLE idx_t2;
CREATE INDEX imerge_a_3124 ON idx_t2 (c7);
CREATE INDEX imerge_b_3120 ON idx_t2 (c3);
DROP INDEX idx_2_c1 ON idx_t2;
CREATE INDEX cover_idx_3120 ON idx_t2 (c3, c7);
CREATE INDEX imerge_a_3124 ON idx_t2 (c7);
CREATE INDEX imerge_b_3121 ON idx_t2 (c4);
CREATE INDEX cover_idx_3123 ON idx_t2 (c6, c0, c1);
CREATE INDEX range_idx_3120 ON idx_t2 (c3);
SET SESSION tidb_enable_index_fast_plan=0;
CREATE INDEX cover_idx_3118 ON idx_t2 (c1, c3, c4);
CREATE INDEX sq_out_3121 ON idx_t2 (c4);
CREATE INDEX ordlim_idx_3124 ON idx_t2 (c7);
CREATE INDEX ordlim_idx_3121 ON idx_t2 (c4);
CREATE UNIQUE INDEX uniq_idx_3118 ON idx_t2 (c1);
CREATE INDEX ordlim_idx_3117 ON idx_t2 (c0);
ANALYZE TABLE idx_t2;
CREATE INDEX ordlim_idx_3120 ON idx_t2 (c3);
CREATE INDEX loose_idx_3123 ON idx_t2 (c6, c7);
CREATE INDEX prefix_idx_3119 ON idx_t2 (c2(2));
CREATE INDEX imerge_a_3124 ON idx_t2 (c7);
CREATE INDEX imerge_b_3120 ON idx_t2 (c3);
CREATE INDEX range_idx_3123 ON idx_t2 (c6);
CREATE INDEX expr_idx_8962 ON idx_t2 ((ABS(c6)));
CREATE INDEX imerge_a_3118 ON idx_t2 (c1);
CREATE INDEX imerge_b_3120 ON idx_t2 (c3);
CREATE INDEX prefix_idx_3119 ON idx_t2 (c2(10));
CREATE INDEX range_idx_3121 ON idx_t2 (c4);
CREATE INDEX null_idx_3123 ON idx_t2 (c6);
CREATE INDEX prefix_idx_3122 ON idx_t2 (c5(2));
ANALYZE TABLE idx_t2;
SET SESSION tidb_enable_ordered_index_mode=0;
CREATE INDEX prefix_idx_3119 ON idx_t2 (c2(2));
CREATE INDEX imerge_a_3117 ON idx_t2 (c0);
CREATE INDEX imerge_b_3121 ON idx_t2 (c4);
SET SESSION tidb_enable_index_merge=0;
CREATE INDEX imerge_a_3117 ON idx_t2 (c0);
CREATE INDEX imerge_b_3121 ON idx_t2 (c4);
SET SESSION tidb_opt_prefer_range_scan=1;
CREATE INDEX null_idx_3121 ON idx_t2 (c4);
CREATE INDEX loose_idx_3118 ON idx_t2 (c1, c7);
CREATE INDEX imerge_a_3124 ON idx_t2 (c7);
CREATE INDEX imerge_b_3118 ON idx_t2 (c1);
CREATE INDEX sq_out_3124 ON idx_t2 (c7);
SET SESSION tidb_opt_prefer_range_scan=1;
CREATE INDEX prefix_idx_3122 ON idx_t2 (c5(2));
CREATE INDEX ordlim_idx_3118 ON idx_t2 (c1);
CREATE UNIQUE INDEX uniq_idx_3117 ON idx_t2 (c0);
CREATE INDEX loose_idx_3117 ON idx_t2 (c0, c7);
CREATE INDEX range_idx_3118 ON idx_t2 (c1);
CREATE INDEX ordlim_idx_3118 ON idx_t2 (c1);
CREATE UNIQUE INDEX uniq_idx_3124 ON idx_t2 (c7);
CREATE INDEX sq_in_3117 ON idx_t2 (c0);
ANALYZE TABLE idx_t2;
CREATE INDEX null_idx_3123 ON idx_t2 (c6);
CREATE INDEX expr_idx_8947 ON idx_t2 ((c6 + 0));
CREATE INDEX sq_out_3123 ON idx_t2 (c6);
SET SESSION tidb_enable_inl_join=0;
CREATE INDEX expr_idx_9222 ON idx_t2 ((UPPER(c2)));
DROP INDEX idx_2_c0c1 ON idx_t2;
CREATE INDEX imerge_a_3123 ON idx_t2 (c6);
CREATE INDEX imerge_b_3118 ON idx_t2 (c1);
CREATE INDEX imerge_a_3118 ON idx_t2 (c1);
CREATE INDEX imerge_b_3120 ON idx_t2 (c3);
CREATE INDEX null_idx_3123 ON idx_t2 (c6);
CREATE INDEX range_idx_3124 ON idx_t2 (c7);
CREATE INDEX sq_in_3121 ON idx_t2 (c4);
SET SESSION tidb_opt_agg_push_down=1;
CREATE INDEX sq_out_3123 ON idx_t2 (c6);
ANALYZE TABLE idx_t2;
CREATE INDEX ordlim_idx_3121 ON idx_t2 (c4);
CREATE INDEX ordlim_idx_3120 ON idx_t2 (c3);
CREATE INDEX loose_idx_3117 ON idx_t2 (c0, c4);
CREATE INDEX null_idx_3123 ON idx_t2 (c6);
SET SESSION tidb_opt_enable_correlation_adjustment=0;
CREATE INDEX sq_out_3121 ON idx_t2 (c4);
SET SESSION tidb_opt_insubq_to_join_and_agg=0;
CREATE INDEX expr_idx_0 ON idx_t2 ((LOWER(c5)));
CREATE INDEX null_idx_3121 ON idx_t2 (c4);
CREATE INDEX sq_in_3118 ON idx_t2 (c1);
CREATE UNIQUE INDEX uniq_idx_3117 ON idx_t2 (c0);
ANALYZE TABLE idx_t2;
CREATE INDEX loose_idx_3117 ON idx_t2 (c0, c6);
CREATE UNIQUE INDEX uniq_idx_3118 ON idx_t2 (c1);
CREATE INDEX loose_idx_3124 ON idx_t2 (c7, c0);
CREATE INDEX sq_in_3124 ON idx_t2 (c7);
CREATE INDEX expr_idx_1389 ON idx_t2 ((UPPER(c2)));
SET SESSION tidb_enable_index_merge_join=0;
SET SESSION tidb_enable_cascades_planner=1;
SET SESSION tidb_enable_ordered_index_mode=0;
CREATE INDEX sq_in_3119 ON idx_t2 (c2);
CREATE UNIQUE INDEX uniq_idx_3118 ON idx_t2 (c1);
CREATE INDEX prefix_idx_3122 ON idx_t2 (c5(10));
ANALYZE TABLE idx_t2;
CREATE INDEX expr_idx_323 ON idx_t2 ((c6 + 0));
SELECT c3 FROM idx_t2 WHERE c3 IS NOT NULL AND c6 IS NOT NULL ORDER BY c3 LIMIT 70;

Quick reproduce with Docker:

docker run -d --name tidb-test -p 4000:4000 pingcap/tidb:v8.5.6 --store=unistore
sleep 15
mysql -h 127.0.0.1 -P 4000 -u root -f < repro.sql

The last line of output will show:

ERROR 1105 (HY000) at line 139: runtime error: slice bounds out of range [8:4]

2. What did you expect to see? (Required)

The SELECT query should execute successfully and return the following result:

+------+
| c3   |
+------+
|   20 |
+------+
1 row in set

The table has only 1 row where c3 = 20 and c6 = 57 (both NOT NULL), so the query should return that single row. TiDB should handle the query gracefully even with many indexes (including expression indexes) created on the table.

3. What did you see instead (Required)

TiDB panics with a Go runtime error: slice bounds out of range [8:4], which is caught by TiDB's panic recovery mechanism and returned to the client as:

ERROR 1105 (HY000): runtime error: slice bounds out of range [8:4]

The TiDB server log shows:

runtime error: slice bounds out of range [8:4]

This error is deterministic and reproduces every time the script is executed.

Key observations:

  • The bug is triggered by the cascades planner. Setting SET SESSION tidb_enable_cascades_planner=1 is required to reproduce the bug. Without it, the same query returns the correct result normally. This confirms the bug is in the cascades planner code path.
  • The bug requires a sequence of repeated CREATE INDEX + ANALYZE TABLE operations (including expression indexes like (c6 + 0), (LOWER(c2)), (UPPER(c2)), (LOWER(c5))) to build up internal state
  • The bug triggers on the final SELECT query with ORDER BY c3 LIMIT 70
  • The slice bounds value varies depending on the number of rows/operations: [8:4] with 1 row, [256:128] with 50 rows
  • Reducing the number of index operations below ~125 makes the bug disappear, suggesting an internal array/slice overflow in statistics or index handling

4. What is your TiDB version? (Required)

Release Version: v8.5.6
Edition: Community
Git Commit Hash: ae18096e023780bb56bfce33698abec0d4640d0a
Git Branch: HEAD
UTC Build Time: 2026-04-24 09:13:10
GoVersion: go1.25.8
Race Enabled: false
Check Table Before Drop: false
Store: unistore

Metadata

Metadata

Assignees

No one assigned

    Labels

    contributionThis PR is from a community contributor.type/bugThe issue is confirmed as a bug.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions