Skip to content

Commit 3c70a28

Browse files
authored
planner: fix RANGE COLUMNS partition prune gives wrong result with special collation (#57344)
close #57261
1 parent afacd10 commit 3c70a28

File tree

3 files changed

+65
-17
lines changed

3 files changed

+65
-17
lines changed

pkg/planner/core/partition_pruning_test.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,60 @@ func TestPartitionRangeColumnsForExpr(t *testing.T) {
575575
}
576576
}
577577

578+
func TestPartitionRangeColumnsForExprWithSpecialCollation(t *testing.T) {
579+
tc := prepareTestCtx(t, "create table t (a varchar(255) COLLATE utf8mb4_0900_ai_ci, b varchar(255) COLLATE utf8mb4_unicode_ci)", "a,b")
580+
lessThan := make([][]*expression.Expression, 0, 6)
581+
partDefs := [][]string{
582+
{"'i'", "'i'"},
583+
{"MAXVALUE", "MAXVALUE"},
584+
}
585+
for i := range partDefs {
586+
l := make([]*expression.Expression, 0, 2)
587+
for j := range []int{0, 1} {
588+
v := partDefs[i][j]
589+
var e *expression.Expression
590+
if v == "MAXVALUE" {
591+
e = nil // MAXVALUE
592+
} else {
593+
expr, err := expression.ParseSimpleExpr(tc.sctx, v, expression.WithInputSchemaAndNames(tc.schema, tc.names, nil))
594+
require.NoError(t, err)
595+
e = &expr
596+
}
597+
l = append(l, e)
598+
}
599+
lessThan = append(lessThan, l)
600+
}
601+
pruner := &rangeColumnsPruner{lessThan, tc.columns[:2]}
602+
cases := []struct {
603+
input string
604+
result partitionRangeOR
605+
}{
606+
{"a = 'q'", partitionRangeOR{{1, 2}}},
607+
{"a = 'Q'", partitionRangeOR{{1, 2}}},
608+
{"a = 'a'", partitionRangeOR{{0, 1}}},
609+
{"a = 'A'", partitionRangeOR{{0, 1}}},
610+
{"a > 'a'", partitionRangeOR{{0, 2}}},
611+
{"a > 'q'", partitionRangeOR{{1, 2}}},
612+
{"a = 'i' and b = 'q'", partitionRangeOR{{1, 2}}},
613+
{"a = 'i' and b = 'Q'", partitionRangeOR{{1, 2}}},
614+
{"a = 'i' and b = 'a'", partitionRangeOR{{0, 1}}},
615+
{"a = 'i' and b = 'A'", partitionRangeOR{{0, 1}}},
616+
{"a = 'i' and b > 'a'", partitionRangeOR{{0, 2}}},
617+
{"a = 'i' and b > 'q'", partitionRangeOR{{1, 2}}},
618+
{"a = 'i' or a = 'h'", partitionRangeOR{{0, 2}}},
619+
{"a = 'h' and a = 'j'", partitionRangeOR{}},
620+
}
621+
622+
for _, ca := range cases {
623+
expr, err := expression.ParseSimpleExpr(tc.sctx, ca.input, expression.WithInputSchemaAndNames(tc.schema, tc.names, nil))
624+
require.NoError(t, err)
625+
result := fullRange(len(lessThan))
626+
e := expression.SplitCNFItems(expr)
627+
result = partitionRangeForCNFExpr(tc.sctx, e, pruner, result)
628+
require.Truef(t, equalPartitionRangeOR(ca.result, result), "unexpected: %v %v != %v", ca.input, ca.result, result)
629+
}
630+
}
631+
578632
func benchmarkRangeColumnsPruner(b *testing.B, parts int) {
579633
tc := prepareBenchCtx("create table t (a bigint unsigned, b int, c int)", "a")
580634
if tc == nil {

pkg/planner/core/rule_partition_processor.go

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1320,7 +1320,7 @@ func multiColumnRangeColumnsPruner(sctx base.PlanContext, exprs []expression.Exp
13201320
lens = append(lens, columnsPruner.partCols[i].RetType.GetFlen())
13211321
}
13221322

1323-
res, err := ranger.DetachCondAndBuildRangeForIndex(sctx.GetRangerCtx(), exprs, columnsPruner.partCols, lens, sctx.GetSessionVars().RangeMaxSize)
1323+
res, err := ranger.DetachCondAndBuildRangeForPartition(sctx.GetRangerCtx(), exprs, columnsPruner.partCols, lens, sctx.GetSessionVars().RangeMaxSize)
13241324
if err != nil {
13251325
return fullRange(len(columnsPruner.lessThan))
13261326
}
@@ -1335,16 +1335,12 @@ func multiColumnRangeColumnsPruner(sctx base.PlanContext, exprs []expression.Exp
13351335

13361336
rangeOr := make([]partitionRange, 0, len(res.Ranges))
13371337

1338-
comparer := make([]collate.Collator, 0, len(columnsPruner.partCols))
1339-
for i := range columnsPruner.partCols {
1340-
comparer = append(comparer, collate.GetCollator(columnsPruner.partCols[i].RetType.GetCollate()))
1341-
}
13421338
gotError := false
13431339
// Create a sort.Search where the compare loops over ColumnValues
13441340
// Loop over the different ranges and extend/include all the partitions found
13451341
for idx := range res.Ranges {
1346-
minComparer := minCmp(sctx, res.Ranges[idx].LowVal, columnsPruner, comparer, res.Ranges[idx].LowExclude, &gotError)
1347-
maxComparer := maxCmp(sctx, res.Ranges[idx].HighVal, columnsPruner, comparer, res.Ranges[idx].HighExclude, &gotError)
1342+
minComparer := minCmp(sctx, res.Ranges[idx].LowVal, columnsPruner, res.Ranges[idx].Collators, res.Ranges[idx].LowExclude, &gotError)
1343+
maxComparer := maxCmp(sctx, res.Ranges[idx].HighVal, columnsPruner, res.Ranges[idx].Collators, res.Ranges[idx].HighExclude, &gotError)
13481344
if gotError {
13491345
// the compare function returned error, use all partitions.
13501346
return fullRange(len(columnsPruner.lessThan))
@@ -1953,10 +1949,9 @@ func makeRangeColumnPruner(columns []*expression.Column, pi *model.PartitionInfo
19531949
if len(pi.Definitions) != len(from.LessThan) {
19541950
return nil, errors.Trace(fmt.Errorf("internal error len(pi.Definitions) != len(from.LessThan) %d != %d", len(pi.Definitions), len(from.LessThan)))
19551951
}
1956-
schema := expression.NewSchema(columns...)
19571952
partCols := make([]*expression.Column, len(offsets))
19581953
for i, offset := range offsets {
1959-
partCols[i] = schema.Columns[offset]
1954+
partCols[i] = columns[offset]
19601955
}
19611956
lessThan := make([][]*expression.Expression, 0, len(from.LessThan))
19621957
for i := range from.LessThan {

pkg/util/ranger/detacher.go

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -323,7 +323,7 @@ func extractBestCNFItemRanges(sctx *rangerctx.RangerContext, conds []expression.
323323
// We build ranges for `(a,b) in ((1,1),(1,2))` and get `[1 1, 1 1] [1 2, 1 2]`, which are point ranges and we can
324324
// append `c = 1` to the point ranges. However, if we choose to merge consecutive ranges here, we get `[1 1, 1 2]`,
325325
// which are not point ranges, and we cannot append `c = 1` anymore.
326-
res, err := detachCondAndBuildRangeWithoutMerging(sctx, tmpConds, cols, lengths, rangeMaxSize, convertToSortKey)
326+
res, err := detachCondAndBuildRange(sctx, tmpConds, cols, lengths, rangeMaxSize, convertToSortKey, false)
327327
if err != nil {
328328
return nil, nil, err
329329
}
@@ -479,7 +479,7 @@ func (d *rangeDetacher) detachCNFCondAndBuildRangeForIndex(conditions []expressi
479479
if eqOrInCount > 0 {
480480
newCols := d.cols[eqOrInCount:]
481481
newLengths := d.lengths[eqOrInCount:]
482-
tailRes, err := DetachCondAndBuildRangeForIndex(d.sctx, newConditions, newCols, newLengths, d.rangeMaxSize)
482+
tailRes, err := detachCondAndBuildRange(d.sctx, newConditions, newCols, newLengths, d.rangeMaxSize, d.convertToSortKey, d.mergeConsecutive)
483483
if err != nil {
484484
return nil, err
485485
}
@@ -1004,16 +1004,15 @@ func DetachCondAndBuildRangeForIndex(sctx *rangerctx.RangerContext, conditions [
10041004
return d.detachCondAndBuildRangeForCols()
10051005
}
10061006

1007-
// detachCondAndBuildRangeWithoutMerging detaches the index filters from table filters and uses them to build ranges.
1008-
// When building ranges, it doesn't merge consecutive ranges.
1009-
func detachCondAndBuildRangeWithoutMerging(sctx *rangerctx.RangerContext, conditions []expression.Expression, cols []*expression.Column,
1010-
lengths []int, rangeMaxSize int64, convertToSortKey bool) (*DetachRangeResult, error) {
1007+
// detachCondAndBuildRange detaches the index filters from table filters and uses them to build ranges.
1008+
func detachCondAndBuildRange(sctx *rangerctx.RangerContext, conditions []expression.Expression, cols []*expression.Column,
1009+
lengths []int, rangeMaxSize int64, convertToSortKey bool, mergeConsecutive bool) (*DetachRangeResult, error) {
10111010
d := &rangeDetacher{
10121011
sctx: sctx,
10131012
allConds: conditions,
10141013
cols: cols,
10151014
lengths: lengths,
1016-
mergeConsecutive: false,
1015+
mergeConsecutive: mergeConsecutive,
10171016
convertToSortKey: convertToSortKey,
10181017
rangeMaxSize: rangeMaxSize,
10191018
}
@@ -1026,7 +1025,7 @@ func detachCondAndBuildRangeWithoutMerging(sctx *rangerctx.RangerContext, condit
10261025
// The returned values are encapsulated into a struct DetachRangeResult, see its comments for explanation.
10271026
func DetachCondAndBuildRangeForPartition(sctx *rangerctx.RangerContext, conditions []expression.Expression, cols []*expression.Column,
10281027
lengths []int, rangeMaxSize int64) (*DetachRangeResult, error) {
1029-
return detachCondAndBuildRangeWithoutMerging(sctx, conditions, cols, lengths, rangeMaxSize, false)
1028+
return detachCondAndBuildRange(sctx, conditions, cols, lengths, rangeMaxSize, false, false)
10301029
}
10311030

10321031
type rangeDetacher struct {

0 commit comments

Comments
 (0)