From e549c52fcaa5ab374c272d0ff110f7ceca2aec92 Mon Sep 17 00:00:00 2001 From: tpp <146148086+terry1purcell@users.noreply.github.com> Date: Thu, 20 Nov 2025 00:20:50 -0800 Subject: [PATCH] planner: out of range col stats only if loaded (#64582) close pingcap/tidb#64572 --- pkg/planner/cardinality/row_count_index.go | 2 +- .../cbotest/testdata/analyze_suite_out.json | 4 ++-- .../cbotest/testdata/analyze_suite_xut.json | 4 ++-- tests/integrationtest/r/imdbload.result | 24 +++++++++---------- 4 files changed, 17 insertions(+), 17 deletions(-) diff --git a/pkg/planner/cardinality/row_count_index.go b/pkg/planner/cardinality/row_count_index.go index 4d5ab875d8..3326cffe66 100644 --- a/pkg/planner/cardinality/row_count_index.go +++ b/pkg/planner/cardinality/row_count_index.go @@ -362,7 +362,7 @@ func getIndexRowCountForStatsV2(sctx planctx.PlanContext, idx *statistics.Index, // If this is single column predicate - use the column's information rather than index. // Index histograms are converted to string. Column uses original type - which can be more accurate for out of range isSingleColRange := len(indexRange.LowVal) == len(indexRange.HighVal) && len(indexRange.LowVal) == 1 - if isSingleColRange && c != nil && c.Histogram.NDV > 0 { + if isSingleColRange && c != nil && c.Histogram.NDV > 0 && c.Histogram.Len() > 0 { histNDV = c.Histogram.NDV - int64(c.TopN.Num()) count.Add(c.Histogram.OutOfRangeRowCount(sctx, &indexRange.LowVal[0], &indexRange.HighVal[0], realtimeRowCount, modifyCount, histNDV)) } else { diff --git a/pkg/planner/core/casetest/cbotest/testdata/analyze_suite_out.json b/pkg/planner/core/casetest/cbotest/testdata/analyze_suite_out.json index e81f3032ce..46dfb346bc 100644 --- a/pkg/planner/core/casetest/cbotest/testdata/analyze_suite_out.json +++ b/pkg/planner/core/casetest/cbotest/testdata/analyze_suite_out.json @@ -238,8 +238,8 @@ "TopN 1.00 root test.t.a, offset:0, count:1", "└─IndexReader 1.00 root index:TopN", " └─TopN 1.00 cop[tikv] test.t.a, offset:0, count:1", - " └─Selection 6.00 cop[tikv] gt(test.t.c, 0)", - " └─IndexRangeScan 6.00 cop[tikv] table:t, index:idx(b, d, a, c) range:[2,2], keep order:false" + " └─Selection 7.00 cop[tikv] gt(test.t.c, 0)", + " └─IndexRangeScan 7.00 cop[tikv] table:t, index:idx(b, d, a, c) range:[2,2], keep order:false" ] } ] diff --git a/pkg/planner/core/casetest/cbotest/testdata/analyze_suite_xut.json b/pkg/planner/core/casetest/cbotest/testdata/analyze_suite_xut.json index e81f3032ce..46dfb346bc 100644 --- a/pkg/planner/core/casetest/cbotest/testdata/analyze_suite_xut.json +++ b/pkg/planner/core/casetest/cbotest/testdata/analyze_suite_xut.json @@ -238,8 +238,8 @@ "TopN 1.00 root test.t.a, offset:0, count:1", "└─IndexReader 1.00 root index:TopN", " └─TopN 1.00 cop[tikv] test.t.a, offset:0, count:1", - " └─Selection 6.00 cop[tikv] gt(test.t.c, 0)", - " └─IndexRangeScan 6.00 cop[tikv] table:t, index:idx(b, d, a, c) range:[2,2], keep order:false" + " └─Selection 7.00 cop[tikv] gt(test.t.c, 0)", + " └─IndexRangeScan 7.00 cop[tikv] table:t, index:idx(b, d, a, c) range:[2,2], keep order:false" ] } ] diff --git a/tests/integrationtest/r/imdbload.result b/tests/integrationtest/r/imdbload.result index 07b755a827..e99b3ce2ce 100644 --- a/tests/integrationtest/r/imdbload.result +++ b/tests/integrationtest/r/imdbload.result @@ -295,12 +295,12 @@ IndexLookUp 1.00 root └─TableRowIDScan(Probe) 1.00 cop[tikv] table:char_name keep order:false explain format = 'brief' select * from char_name where imdb_index > 'V'; id estRows task access object operator info -IndexLookUp 1.00 root -├─IndexRangeScan(Build) 1.00 cop[tikv] table:char_name, index:itest2(imdb_index, surname_pcode, name_pcode_nf) range:("V",+inf], keep order:false -└─TableRowIDScan(Probe) 1.00 cop[tikv] table:char_name keep order:false +IndexLookUp 2.32 root +├─IndexRangeScan(Build) 2.32 cop[tikv] table:char_name, index:itest2(imdb_index, surname_pcode, name_pcode_nf) range:("V",+inf], keep order:false +└─TableRowIDScan(Probe) 2.32 cop[tikv] table:char_name keep order:false trace plan target = 'estimation' select * from char_name where imdb_index > 'V'; CE_trace -[{"table_name":"char_name","type":"Column Stats-Range","expr":"((id >= -9223372036854775808 and id <= 9223372036854775807))","row_count":4314864},{"table_name":"char_name","type":"Column Stats-Range","expr":"((imdb_index > 'V' and true))","row_count":1},{"table_name":"char_name","type":"Index Stats-Range","expr":"((imdb_index > 'V' and true))","row_count":1},{"table_name":"char_name","type":"Table Stats-Expression-CNF","expr":"`gt`(imdbload.char_name.imdb_index, 'V')","row_count":1}] +[{"table_name":"char_name","type":"Column Stats-Range","expr":"((id >= -9223372036854775808 and id <= 9223372036854775807))","row_count":4314864},{"table_name":"char_name","type":"Column Stats-Range","expr":"((imdb_index > 'V' and true))","row_count":1},{"table_name":"char_name","type":"Index Stats-Range","expr":"((imdb_index > 'V' and true))","row_count":2},{"table_name":"char_name","type":"Table Stats-Expression-CNF","expr":"`gt`(imdbload.char_name.imdb_index, 'V')","row_count":2}] explain format = 'brief' select * from movie_companies where company_type_id > 2; id estRows task access object operator info @@ -313,21 +313,21 @@ CE_trace explain format = 'brief' select * from char_name where imdb_index > 'I' and imdb_index < 'II'; id estRows task access object operator info -IndexLookUp 1.00 root -├─IndexRangeScan(Build) 1.00 cop[tikv] table:char_name, index:itest2(imdb_index, surname_pcode, name_pcode_nf) range:("I","II"), keep order:false -└─TableRowIDScan(Probe) 1.00 cop[tikv] table:char_name keep order:false +IndexLookUp 2.32 root +├─IndexRangeScan(Build) 2.32 cop[tikv] table:char_name, index:itest2(imdb_index, surname_pcode, name_pcode_nf) range:("I","II"), keep order:false +└─TableRowIDScan(Probe) 2.32 cop[tikv] table:char_name keep order:false trace plan target = 'estimation' select * from char_name where imdb_index > 'I' and imdb_index < 'II'; CE_trace -[{"table_name":"char_name","type":"Column Stats-Range","expr":"((id >= -9223372036854775808 and id <= 9223372036854775807))","row_count":4314864},{"table_name":"char_name","type":"Column Stats-Range","expr":"((imdb_index > 'I' and imdb_index < 'II'))","row_count":1},{"table_name":"char_name","type":"Index Stats-Range","expr":"((imdb_index > 'I' and imdb_index < 'II'))","row_count":1},{"table_name":"char_name","type":"Table Stats-Expression-CNF","expr":"`and`(`gt`(imdbload.char_name.imdb_index, 'I'), `lt`(imdbload.char_name.imdb_index, 'II'))","row_count":1}] +[{"table_name":"char_name","type":"Column Stats-Range","expr":"((id >= -9223372036854775808 and id <= 9223372036854775807))","row_count":4314864},{"table_name":"char_name","type":"Column Stats-Range","expr":"((imdb_index > 'I' and imdb_index < 'II'))","row_count":1},{"table_name":"char_name","type":"Index Stats-Range","expr":"((imdb_index > 'I' and imdb_index < 'II'))","row_count":2},{"table_name":"char_name","type":"Table Stats-Expression-CNF","expr":"`and`(`gt`(imdbload.char_name.imdb_index, 'I'), `lt`(imdbload.char_name.imdb_index, 'II'))","row_count":2}] explain format = 'brief' select * from char_name where imdb_index > 'I'; id estRows task access object operator info -IndexLookUp 1.00 root -├─IndexRangeScan(Build) 1.00 cop[tikv] table:char_name, index:itest2(imdb_index, surname_pcode, name_pcode_nf) range:("I",+inf], keep order:false -└─TableRowIDScan(Probe) 1.00 cop[tikv] table:char_name keep order:false +IndexLookUp 2.32 root +├─IndexRangeScan(Build) 2.32 cop[tikv] table:char_name, index:itest2(imdb_index, surname_pcode, name_pcode_nf) range:("I",+inf], keep order:false +└─TableRowIDScan(Probe) 2.32 cop[tikv] table:char_name keep order:false trace plan target = 'estimation' select * from char_name where imdb_index > 'I'; CE_trace -[{"table_name":"char_name","type":"Column Stats-Range","expr":"((id >= -9223372036854775808 and id <= 9223372036854775807))","row_count":4314864},{"table_name":"char_name","type":"Column Stats-Range","expr":"((imdb_index > 'I' and true))","row_count":1},{"table_name":"char_name","type":"Index Stats-Range","expr":"((imdb_index > 'I' and true))","row_count":1},{"table_name":"char_name","type":"Table Stats-Expression-CNF","expr":"`gt`(imdbload.char_name.imdb_index, 'I')","row_count":1}] +[{"table_name":"char_name","type":"Column Stats-Range","expr":"((id >= -9223372036854775808 and id <= 9223372036854775807))","row_count":4314864},{"table_name":"char_name","type":"Column Stats-Range","expr":"((imdb_index > 'I' and true))","row_count":1},{"table_name":"char_name","type":"Index Stats-Range","expr":"((imdb_index > 'I' and true))","row_count":2},{"table_name":"char_name","type":"Table Stats-Expression-CNF","expr":"`gt`(imdbload.char_name.imdb_index, 'I')","row_count":2}] explain format = 'brief' select * from cast_info where nr_order < -2068070866; id estRows task access object operator info