diff --git a/pkg/planner/core/integration_test.go b/pkg/planner/core/integration_test.go index b572bec656c94..d420c615da60a 100644 --- a/pkg/planner/core/integration_test.go +++ b/pkg/planner/core/integration_test.go @@ -2221,14 +2221,14 @@ func TestPlanCacheForIndexJoinRangeFallback(t *testing.T) { tk.MustExec("drop table if exists t1, t2") tk.MustExec("create table t1(a int, b varchar(10), c varchar(10), index idx_a_b(a, b))") tk.MustExec("create table t2(d int)") - tk.MustExec("set @@tidb_opt_range_max_size=1275") - // 1275 is enough for [? a,? a], [? b,? b], [? c,? c] but is not enough for [? aaaaaa,? aaaaaa], [? bbbbbb,? bbbbbb], [? cccccc,? cccccc]. + tk.MustExec("set @@tidb_opt_range_max_size=1260") + // 1260 is enough for [? a,? a], [? b,? b], [? c,? c] but is not enough for [? aaaaaa,? aaaaaa], [? bbbbbb,? bbbbbb], [? cccccc,? cccccc]. rows := tk.MustQuery("explain format='brief' select /*+ inl_join(t1) */ * from t1 join t2 on t1.a = t2.d where t1.b in ('a', 'b', 'c')").Rows() require.True(t, strings.Contains(rows[6][4].(string), "range: decided by [eq(test.t1.a, test.t2.d) in(test.t1.b, a, b, c)]")) tk.MustQuery("show warnings").Check(testkit.Rows()) rows = tk.MustQuery("explain format='brief' select /*+ inl_join(t1) */ * from t1 join t2 on t1.a = t2.d where t1.b in ('aaaaaa', 'bbbbbb', 'cccccc');").Rows() - require.True(t, strings.Contains(rows[6][4].(string), "range: decided by [eq(test.t1.a, test.t2.d)]")) - tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 Memory capacity of 1275 bytes for 'tidb_opt_range_max_size' exceeded when building ranges. Less accurate ranges such as full range are chosen")) + require.Contains(t, rows[6][4].(string), "range: decided by [eq(test.t1.a, test.t2.d)]") + tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1105 Memory capacity of 1260 bytes for 'tidb_opt_range_max_size' exceeded when building ranges. Less accurate ranges such as full range are chosen")) tk.MustExec("prepare stmt1 from 'select /*+ inl_join(t1) */ * from t1 join t2 on t1.a = t2.d where t1.b in (?, ?, ?)'") tk.MustExec("set @a='a', @b='b', @c='c'") @@ -2243,13 +2243,13 @@ func TestPlanCacheForIndexJoinRangeFallback(t *testing.T) { tk.Session().SetSessionManager(&testkit.MockSessionManager{PS: ps}) rows = tk.MustQuery(fmt.Sprintf("explain for connection %d", tkProcess.ID)).Rows() // We don't limit range mem usage when rebuilding index join ranges for the cached plan. So [? aaaaaa,? aaaaaa], [? bbbbbb,? bbbbbb], [? cccccc,? cccccc] can be built. - require.True(t, strings.Contains(rows[6][4].(string), "range: decided by [eq(test.t1.a, test.t2.d) in(test.t1.b, aaaaaa, bbbbbb, cccccc)]")) + require.Contains(t, rows[6][4].(string), "range: decided by [eq(test.t1.a, test.t2.d) in(test.t1.b, aaaaaa, bbbbbb, cccccc)]") // Test the plan with range fallback would not be put into cache. tk.MustExec("prepare stmt2 from 'select /*+ inl_join(t1) */ * from t1 join t2 on t1.a = t2.d where t1.b in (?, ?, ?, ?, ?)'") tk.MustExec("set @a='a', @b='b', @c='c', @d='d', @e='e'") tk.MustExec("execute stmt2 using @a, @b, @c, @d, @e") - tk.MustQuery("show warnings").Sort().Check(testkit.Rows("Warning 1105 Memory capacity of 1275 bytes for 'tidb_opt_range_max_size' exceeded when building ranges. Less accurate ranges such as full range are chosen", + tk.MustQuery("show warnings").Sort().Check(testkit.Rows("Warning 1105 Memory capacity of 1260 bytes for 'tidb_opt_range_max_size' exceeded when building ranges. Less accurate ranges such as full range are chosen", "Warning 1105 skip prepared plan-cache: in-list is too long")) tk.MustExec("execute stmt2 using @a, @b, @c, @d, @e") tk.MustQuery("select @@last_plan_from_cache").Check(testkit.Rows("0")) diff --git a/pkg/planner/core/testdata/index_merge_suite_out.json b/pkg/planner/core/testdata/index_merge_suite_out.json index 3d67e5e372251..a5637cb31d009 100644 --- a/pkg/planner/core/testdata/index_merge_suite_out.json +++ b/pkg/planner/core/testdata/index_merge_suite_out.json @@ -131,8 +131,8 @@ "IndexMerge 0.00 root type: intersection", "├─IndexRangeScan(Build) 10.00 cop[tikv] table:t5, index:is1(s1) range:[\"Abc\",\"Abc\"], keep order:false, stats:pseudo", "├─IndexRangeScan(Build) 3333.33 cop[tikv] table:t5, index:is2(s2) range:(\"zzz\",+inf], keep order:false, stats:pseudo", - "├─IndexRangeScan(Build) 3323.33 cop[tikv] table:t5, index:is3(s3) range:[-inf,\"B啊a\"), keep order:false, stats:pseudo", - "├─IndexRangeScan(Build) 10.00 cop[tikv] table:t5, index:is4(s4) range:[\"CcC\",\"CcC\"], keep order:false, stats:pseudo", + "├─IndexRangeScan(Build) 3323.33 cop[tikv] table:t5, index:is3(s3) range:[-inf,\"\\x0eJ\\xfb@\\xd5J\\x0e3\"), keep order:false, stats:pseudo", + "├─IndexRangeScan(Build) 10.00 cop[tikv] table:t5, index:is4(s4) range:[\"CCC\",\"CCC\"], keep order:false, stats:pseudo", "└─TableRowIDScan(Probe) 0.00 cop[tikv] table:t5 keep order:false, stats:pseudo" ], "Result": [ @@ -144,7 +144,7 @@ "Plan": [ "IndexMerge 0.03 root type: intersection", "├─IndexRangeScan(Build) 33.33 cop[tikv] table:t6, index:PRIMARY(s1, s2) range:(\"Abc\" \"zzz\",\"Abc\" +inf], keep order:false, stats:pseudo", - "├─IndexRangeScan(Build) 10.00 cop[tikv] table:t6, index:is3(s3) range:[\"A啊a\",\"A啊a\"], keep order:false, stats:pseudo", + "├─IndexRangeScan(Build) 10.00 cop[tikv] table:t6, index:is3(s3) range:[\"\\x0e3\\xfb@\\xd5J\\x0e3\",\"\\x0e3\\xfb@\\xd5J\\x0e3\"], keep order:false, stats:pseudo", "└─Selection(Probe) 0.03 cop[tikv] gt(test.t6.s2, \"zzz\"), not(like(test.t6.s4, \"Cd_\", 92))", " └─TableRowIDScan 0.03 cop[tikv] table:t6 keep order:false, stats:pseudo" ], @@ -172,13 +172,21 @@ { "SQL": "select /*+ use_index_merge(t8, primary,is2,is3,is4,is5) */ * from t8 where s1 like '啊A%' and s2 > 'abc' and s3 > 'cba' and s4 in ('aA', '??') and s5 = 'test,2'", "Plan": [ +<<<<<<< HEAD "Selection 1.42 root eq(test.t8.s5, \"test,2\")", "└─IndexMerge 0.59 root type: intersection", " ├─IndexRangeScan(Build) 3333.33 cop[tikv] table:t8, index:is2(s2) range:(0x616263,+inf], keep order:false, stats:pseudo", " ├─IndexRangeScan(Build) 3333.33 cop[tikv] table:t8, index:is3(s3) range:(0x636261,+inf], keep order:false, stats:pseudo", +======= + "Selection 0.04 root eq(test.t8.s5, \"test,2\")", + "└─IndexMerge 0.06 root type: intersection", + " ├─IndexRangeScan(Build) 250.00 cop[tikv] table:t8, index:PRIMARY(s1) range:[\"UJ\\x00A\",\"UJ\\x00B\"), keep order:false, stats:pseudo", + " ├─IndexRangeScan(Build) 3333.33 cop[tikv] table:t8, index:is2(s2) range:(\"abc\",+inf], keep order:false, stats:pseudo", + " ├─IndexRangeScan(Build) 3333.33 cop[tikv] table:t8, index:is3(s3) range:(\"cba\",+inf], keep order:false, stats:pseudo", +>>>>>>> e053c27f068 (util/ranger: support use `like` to build range for new collation columns (#48522)) " ├─IndexRangeScan(Build) 20.00 cop[tikv] table:t8, index:is4(s4) range:[\"aA\",\"aA\"], [\"??\",\"??\"], keep order:false, stats:pseudo", - " └─Selection(Probe) 0.59 cop[tikv] gt(test.t8.s3, \"cba\"), like(test.t8.s1, \"啊A%\", 92)", - " └─TableRowIDScan 2.22 cop[tikv] table:t8 keep order:false, stats:pseudo" + " └─Selection(Probe) 0.06 cop[tikv] gt(test.t8.s3, \"cba\"), like(test.t8.s1, \"啊A%\", 92)", + " └─TableRowIDScan 0.06 cop[tikv] table:t8 keep order:false, stats:pseudo" ], "Result": [ "啊aabbccdd abcc cccc aA tEsT,2" diff --git a/pkg/util/ranger/BUILD.bazel b/pkg/util/ranger/BUILD.bazel index b0e45e7cfca1d..93d3f35277944 100644 --- a/pkg/util/ranger/BUILD.bazel +++ b/pkg/util/ranger/BUILD.bazel @@ -30,7 +30,11 @@ go_library( "//pkg/util/codec", "//pkg/util/collate", "//pkg/util/dbterror", +<<<<<<< HEAD "//pkg/util/mathutil", +======= + "//pkg/util/hack", +>>>>>>> e053c27f068 (util/ranger: support use `like` to build range for new collation columns (#48522)) "@com_github_pingcap_errors//:errors", ], ) diff --git a/pkg/util/ranger/checker.go b/pkg/util/ranger/checker.go index 7d769a029b2fd..86c16a3703140 100644 --- a/pkg/util/ranger/checker.go +++ b/pkg/util/ranger/checker.go @@ -139,16 +139,6 @@ func (c *conditionChecker) checkScalarFunction(scalar *expression.ScalarFunction func (c *conditionChecker) checkLikeFunc(scalar *expression.ScalarFunction) (isAccessCond, shouldReserve bool) { _, collation := scalar.CharsetAndCollation() - if collate.NewCollationEnabled() && !collate.IsBinCollation(collation) { - // The algorithm constructs the range in byte-level: for example, ab% is mapped to [ab, ac] by adding 1 to the last byte. - // However, this is incorrect for non-binary collation strings because the sort key order is not the same as byte order. - // For example, "`%" is mapped to the range [`, a](where ` is 0x60 and a is 0x61). - // Because the collation utf8_general_ci is case-insensitive, a and A have the same sort key. - // Finally, the range comes to be [`, A], which is actually an empty range. - // See https://github.com/pingcap/tidb/issues/31174 for more details. - // In short, when the column type is non-binary collation string, we cannot use `like` expressions to generate the range. - return false, true - } if !collate.CompatibleCollate(scalar.GetArgs()[0].GetType().GetCollate(), collation) { return false, true } diff --git a/pkg/util/ranger/detacher.go b/pkg/util/ranger/detacher.go index 954bbcee5254e..6d4f3cf328f66 100644 --- a/pkg/util/ranger/detacher.go +++ b/pkg/util/ranger/detacher.go @@ -242,7 +242,7 @@ func compareCNFItemRangeResult(curResult, bestResult *cnfItemRangeResult) (curIs // e.g, for input CNF expressions ((a,b) in ((1,1),(2,2))) and a > 1 and ((a,b,c) in (1,1,1),(2,2,2)) // ((a,b,c) in (1,1,1),(2,2,2)) would be extracted. func extractBestCNFItemRanges(sctx sessionctx.Context, conds []expression.Expression, cols []*expression.Column, - lengths []int, rangeMaxSize int64) (*cnfItemRangeResult, []*valueInfo, error) { + lengths []int, rangeMaxSize int64, convertToSortKey bool) (*cnfItemRangeResult, []*valueInfo, error) { if len(conds) < 2 { return nil, nil, nil } @@ -261,7 +261,7 @@ func extractBestCNFItemRanges(sctx sessionctx.Context, conds []expression.Expres // 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 // append `c = 1` to the point ranges. However, if we choose to merge consecutive ranges here, we get `[1 1, 1 2]`, // which are not point ranges, and we cannot append `c = 1` anymore. - res, err := detachCondAndBuildRangeWithoutMerging(sctx, tmpConds, cols, lengths, rangeMaxSize) + res, err := detachCondAndBuildRangeWithoutMerging(sctx, tmpConds, cols, lengths, rangeMaxSize, convertToSortKey) if err != nil { return nil, nil, err } @@ -376,7 +376,7 @@ func (d *rangeDetacher) detachCNFCondAndBuildRangeForIndex(conditions []expressi optPrefixIndexSingleScan: d.sctx.GetSessionVars().OptPrefixIndexSingleScan, } if considerDNF { - bestCNFItemRes, columnValues, err := extractBestCNFItemRanges(d.sctx, conditions, d.cols, d.lengths, d.rangeMaxSize) + bestCNFItemRes, columnValues, err := extractBestCNFItemRanges(d.sctx, conditions, d.cols, d.lengths, d.rangeMaxSize, d.convertToSortKey) if err != nil { return nil, err } @@ -627,12 +627,22 @@ func ExtractEqAndInCondition(sctx sessionctx.Context, conditions []expression.Ex } // Multiple Eq/In conditions for one column in CNF, apply intersection on them // Lazily compute the points for the previously visited Eq/In + newTp := newFieldType(cols[offset].GetType()) collator := collate.GetCollator(cols[offset].GetType().GetCollate()) if mergedAccesses[offset] == nil { mergedAccesses[offset] = accesses[offset] +<<<<<<< HEAD points[offset] = rb.build(accesses[offset], collator) } points[offset] = rb.intersection(points[offset], rb.build(cond, collator), collator) +======= + // Note that this is a relatively special usage of build(). We will restore the points back to Expression for + // later use and may build the Expression to points again. + // We need to keep the original value here, which means we neither cut prefix nor convert to sort key. + points[offset] = rb.build(accesses[offset], newTp, types.UnspecifiedLength, false) + } + points[offset] = rb.intersection(points[offset], rb.build(cond, newTp, types.UnspecifiedLength, false), collator) +>>>>>>> e053c27f068 (util/ranger: support use `like` to build range for new collation columns (#48522)) if len(points[offset]) == 0 { // Early termination if false expression found if expression.MaybeOverOptimized4PlanCache(sctx, conditions) { // `a>@x and a<@y` --> `invalid-range if @x>=@y` @@ -772,9 +782,14 @@ func (d *rangeDetacher) detachDNFCondAndBuildRangeForIndex(condition *expression if shouldReserve { hasResidual = true } +<<<<<<< HEAD points := rb.build(item, collate.GetCollator(newTpSlice[0].GetCollate())) +======= + points := rb.build(item, newTpSlice[0], d.lengths[0], d.convertToSortKey) + tmpNewTp := convertStringFTToBinaryCollate(newTpSlice[0]) +>>>>>>> e053c27f068 (util/ranger: support use `like` to build range for new collation columns (#48522)) // TODO: restrict the mem usage of ranges - ranges, rangeFallback, err := points2Ranges(d.sctx, points, newTpSlice[0], d.rangeMaxSize) + ranges, rangeFallback, err := points2Ranges(d.sctx, points, tmpNewTp, d.rangeMaxSize) if err != nil { return nil, nil, nil, false, errors.Trace(err) } @@ -870,6 +885,7 @@ func DetachCondAndBuildRangeForIndex(sctx sessionctx.Context, conditions []expre cols: cols, lengths: lengths, mergeConsecutive: true, + convertToSortKey: true, rangeMaxSize: rangeMaxSize, } return d.detachCondAndBuildRangeForCols() @@ -878,13 +894,14 @@ func DetachCondAndBuildRangeForIndex(sctx sessionctx.Context, conditions []expre // detachCondAndBuildRangeWithoutMerging detaches the index filters from table filters and uses them to build ranges. // When building ranges, it doesn't merge consecutive ranges. func detachCondAndBuildRangeWithoutMerging(sctx sessionctx.Context, conditions []expression.Expression, cols []*expression.Column, - lengths []int, rangeMaxSize int64) (*DetachRangeResult, error) { + lengths []int, rangeMaxSize int64, convertToSortKey bool) (*DetachRangeResult, error) { d := &rangeDetacher{ sctx: sctx, allConds: conditions, cols: cols, lengths: lengths, mergeConsecutive: false, + convertToSortKey: convertToSortKey, rangeMaxSize: rangeMaxSize, } return d.detachCondAndBuildRangeForCols() @@ -896,7 +913,7 @@ func detachCondAndBuildRangeWithoutMerging(sctx sessionctx.Context, conditions [ // The returned values are encapsulated into a struct DetachRangeResult, see its comments for explanation. func DetachCondAndBuildRangeForPartition(sctx sessionctx.Context, conditions []expression.Expression, cols []*expression.Column, lengths []int, rangeMaxSize int64) (*DetachRangeResult, error) { - return detachCondAndBuildRangeWithoutMerging(sctx, conditions, cols, lengths, rangeMaxSize) + return detachCondAndBuildRangeWithoutMerging(sctx, conditions, cols, lengths, rangeMaxSize, false) } type rangeDetacher struct { @@ -905,6 +922,7 @@ type rangeDetacher struct { cols []*expression.Column lengths []int mergeConsecutive bool + convertToSortKey bool rangeMaxSize int64 } @@ -951,6 +969,7 @@ func DetachSimpleCondAndBuildRangeForIndex(sctx sessionctx.Context, conditions [ cols: cols, lengths: lengths, mergeConsecutive: true, + convertToSortKey: true, rangeMaxSize: rangeMaxSize, } res, err := d.detachCNFCondAndBuildRangeForIndex(conditions, newTpSlice, false) diff --git a/pkg/util/ranger/points.go b/pkg/util/ranger/points.go index 7503aa4a4858e..68df1af1325a6 100644 --- a/pkg/util/ranger/points.go +++ b/pkg/util/ranger/points.go @@ -31,6 +31,7 @@ import ( "github.com/pingcap/tidb/pkg/util/chunk" "github.com/pingcap/tidb/pkg/util/collate" "github.com/pingcap/tidb/pkg/util/dbterror" + "github.com/pingcap/tidb/pkg/util/hack" ) // Error instances. @@ -135,6 +136,50 @@ func rangePointEqualValueLess(a, b *point) bool { return a.excl && !b.excl } +func pointsConvertToSortKey(sctx sessionctx.Context, inputPs []*point, newTp *types.FieldType) ([]*point, error) { + // Only handle normal string type here. + // Currently, set won't be pushed down and it shouldn't reach here in theory. + // For enum, we have separate logic for it, like handleEnumFromBinOp(). For now, it only supports point range, + // intervals are not supported. So we also don't need to handle enum here. + if newTp.EvalType() != types.ETString || + newTp.GetType() == mysql.TypeEnum || + newTp.GetType() == mysql.TypeSet { + return inputPs, nil + } + ps := make([]*point, 0, len(inputPs)) + for _, p := range inputPs { + np, err := pointConvertToSortKey(sctx, p, newTp, true) + if err != nil { + return nil, err + } + ps = append(ps, np) + } + return ps, nil +} + +func pointConvertToSortKey( + sctx sessionctx.Context, + inputP *point, + newTp *types.FieldType, + trimTrailingSpace bool, +) (*point, error) { + p, err := convertPoint(sctx, inputP, newTp) + if err != nil { + return nil, err + } + if p.value.Kind() != types.KindString || newTp.GetCollate() == charset.CollationBin || !collate.NewCollationEnabled() { + return p, nil + } + sortKey := p.value.GetBytes() + if !trimTrailingSpace { + sortKey = collate.GetCollator(newTp.GetCollate()).KeyWithoutTrimRightSpace(string(hack.String(sortKey))) + } else { + sortKey = collate.GetCollator(newTp.GetCollate()).Key(string(hack.String(sortKey))) + } + + return &point{value: types.NewBytesDatum(sortKey), excl: p.excl, start: p.start}, nil +} + func (r *pointSorter) Swap(i, j int) { r.points[i], r.points[j] = r.points[j], r.points[i] } @@ -187,12 +232,32 @@ type builder struct { sc *stmtctx.StatementContext } +<<<<<<< HEAD func (r *builder) build(expr expression.Expression, collator collate.Collator) []*point { +======= +// build converts Expression on one column into point, which can be further built into Range. +// If the input prefixLen is not types.UnspecifiedLength, it means it's for a prefix column in a prefix index. In such +// cases, we should cut the prefix and adjust the exclusiveness. Ref: cutPrefixForPoints(). +// convertToSortKey indicates whether the string values should be converted to sort key. +// Converting to sort key can make `like` function be built into Range for new collation column. But we can't restore +// the original value from the sort key, so the usage of the result may be limited, like when you need to restore the +// result points back to Expression. +func (r *builder) build( + expr expression.Expression, + newTp *types.FieldType, + prefixLen int, + convertToSortKey bool, +) []*point { +>>>>>>> e053c27f068 (util/ranger: support use `like` to build range for new collation columns (#48522)) switch x := expr.(type) { case *expression.Column: return r.buildFromColumn() case *expression.ScalarFunction: +<<<<<<< HEAD return r.buildFromScalarFunc(x, collator) +======= + return r.buildFromScalarFunc(x, newTp, prefixLen, convertToSortKey) +>>>>>>> e053c27f068 (util/ranger: support use `like` to build range for new collation columns (#48522)) case *expression.Constant: return r.buildFromConstant(x) } @@ -233,7 +298,16 @@ func (*builder) buildFromColumn() []*point { return []*point{startPoint1, endPoint1, startPoint2, endPoint2} } +<<<<<<< HEAD func (r *builder) buildFromBinOp(expr *expression.ScalarFunction) []*point { +======= +func (r *builder) buildFromBinOp( + expr *expression.ScalarFunction, + newTp *types.FieldType, + prefixLen int, + convertToSortKey bool, +) []*point { +>>>>>>> e053c27f068 (util/ranger: support use `like` to build range for new collation columns (#48522)) // This has been checked that the binary operation is comparison operation, and one of // the operand is column name expression. var ( @@ -372,7 +446,19 @@ func (r *builder) buildFromBinOp(expr *expression.ScalarFunction) []*point { endPoint := &point{value: types.MaxValueDatum()} return []*point{startPoint, endPoint} } +<<<<<<< HEAD return nil +======= + cutPrefixForPoints(res, prefixLen, ft) + if convertToSortKey { + res, err = pointsConvertToSortKey(r.sctx, res, newTp) + if err != nil { + r.err = err + return getFullRange() + } + } + return res +>>>>>>> e053c27f068 (util/ranger: support use `like` to build range for new collation columns (#48522)) } // handleUnsignedCol handles the case when unsigned column meets negative value. @@ -554,7 +640,16 @@ func (*builder) buildFromIsFalse(_ *expression.ScalarFunction, isNot int) []*poi return []*point{startPoint, endPoint} } +<<<<<<< HEAD func (r *builder) buildFromIn(expr *expression.ScalarFunction) ([]*point, bool) { +======= +func (r *builder) buildFromIn( + expr *expression.ScalarFunction, + newTp *types.FieldType, + prefixLen int, + convertToSortKey bool, +) ([]*point, bool) { +>>>>>>> e053c27f068 (util/ranger: support use `like` to build range for new collation columns (#48522)) list := expr.GetArgs()[1:] rangePoints := make([]*point, 0, len(list)*2) hasNull := false @@ -630,10 +725,32 @@ func (r *builder) buildFromIn(expr *expression.ScalarFunction) ([]*point, bool) if curPos > 0 { curPos++ } +<<<<<<< HEAD return rangePoints[:curPos], hasNull } func (r *builder) newBuildFromPatternLike(expr *expression.ScalarFunction) []*point { +======= + rangePoints = rangePoints[:curPos] + cutPrefixForPoints(rangePoints, prefixLen, ft) + var err error + if convertToSortKey { + rangePoints, err = pointsConvertToSortKey(r.sctx, rangePoints, newTp) + if err != nil { + r.err = err + return getFullRange(), false + } + } + return rangePoints, hasNull +} + +func (r *builder) newBuildFromPatternLike( + expr *expression.ScalarFunction, + newTp *types.FieldType, + prefixLen int, + convertToSortKey bool, +) []*point { +>>>>>>> e053c27f068 (util/ranger: support use `like` to build range for new collation columns (#48522)) _, collation := expr.CharsetAndCollation() if !collate.CompatibleCollate(expr.GetArgs()[0].GetType().GetCollate(), collation) { return getFullRange() @@ -649,10 +766,23 @@ func (r *builder) newBuildFromPatternLike(expr *expression.ScalarFunction) []*po r.err = errors.Trace(err) return getFullRange() } + // non-exceptional return case 1: empty pattern if pattern == "" { startPoint := &point{value: types.NewStringDatum(""), start: true} endPoint := &point{value: types.NewStringDatum("")} +<<<<<<< HEAD return []*point{startPoint, endPoint} +======= + res := []*point{startPoint, endPoint} + if convertToSortKey { + res, err = pointsConvertToSortKey(r.sctx, res, newTp) + if err != nil { + r.err = err + return getFullRange() + } + } + return res +>>>>>>> e053c27f068 (util/ranger: support use `like` to build range for new collation columns (#48522)) } lowValue := make([]byte, 0, len(pattern)) edt, err := expr.GetArgs()[2].(*expression.Constant).Eval(chunk.Row{}) @@ -693,11 +823,14 @@ func (r *builder) newBuildFromPatternLike(expr *expression.ScalarFunction) []*po } lowValue = append(lowValue, pattern[i]) } + // non-exceptional return case 2: no characters before the wildcard if len(lowValue) == 0 { return []*point{{value: types.MinNotNullDatum(), start: true}, {value: types.MaxValueDatum()}} } + // non-exceptional return case 3: pattern contains valid characters and doesn't contain the wildcard if isExactMatch { val := types.NewCollationStringDatum(string(lowValue), tpOfPattern.GetCollate()) +<<<<<<< HEAD return []*point{{value: val, start: true}, {value: val}} } startPoint := &point{start: true, excl: exclude} @@ -706,15 +839,67 @@ func (r *builder) newBuildFromPatternLike(expr *expression.ScalarFunction) []*po copy(highValue, lowValue) endPoint := &point{excl: true} for i := len(highValue) - 1; i >= 0; i-- { +======= + startPoint := &point{value: val, start: true} + endPoint := &point{value: val} + res := []*point{startPoint, endPoint} + cutPrefixForPoints(res, prefixLen, tpOfPattern) + if convertToSortKey { + res, err = pointsConvertToSortKey(r.sctx, res, newTp) + if err != nil { + r.err = err + return getFullRange() + } + } + return res + } + + // non-exceptional return case 4: pattern contains valid characters and contains the wildcard + + // non-exceptional return case 4-1 + // If it's not a _bin or binary collation, and we don't convert the value to the sort key, we can't build + // a range for the wildcard. + if !convertToSortKey && + !collate.IsBinCollation(tpOfPattern.GetCollate()) { + return []*point{{value: types.MinNotNullDatum(), start: true}, {value: types.MaxValueDatum()}} + } + + // non-exceptional return case 4-2: build a range for the wildcard + // the end_key is sortKey(start_value) + 1 + originalStartPoint := &point{start: true, excl: exclude} + originalStartPoint.value.SetBytesAsString(lowValue, tpOfPattern.GetCollate(), uint32(tpOfPattern.GetFlen())) + cutPrefixForPoints([]*point{originalStartPoint}, prefixLen, tpOfPattern) + + // If we don't trim the trailing spaces, which means using KeyWithoutTrimRightSpace() instead of Key(), we can build + // a smaller range for better performance, e.g., LIKE ' %'. + // However, if it's a PAD SPACE collation, we must trim the trailing spaces for the start point to ensure the correctness. + // Because the trailing spaces are trimmed in the stored index key. For example, for LIKE 'abc %' on utf8mb4_bin + // column, the start key should be 'abd' instead of 'abc ', but the end key can be 'abc!'. ( ' ' is 32 and '!' is 33 + // in ASCII) + shouldTrimTrailingSpace := isPadSpaceCollation(collation) + startPoint, err := pointConvertToSortKey(r.sctx, originalStartPoint, newTp, shouldTrimTrailingSpace) + if err != nil { + r.err = errors.Trace(err) + return getFullRange() + } + sortKeyPointWithoutTrim, err := pointConvertToSortKey(r.sctx, originalStartPoint, newTp, false) + if err != nil { + r.err = errors.Trace(err) + return getFullRange() + } + sortKeyWithoutTrim := append([]byte{}, sortKeyPointWithoutTrim.value.GetBytes()...) + endPoint := &point{value: types.MaxValueDatum(), excl: true} + for i := len(sortKeyWithoutTrim) - 1; i >= 0; i-- { +>>>>>>> e053c27f068 (util/ranger: support use `like` to build range for new collation columns (#48522)) // Make the end point value more than the start point value, // and the length of the end point value is the same as the length of the start point value. // e.g., the start point value is "abc", so the end point value is "abd". - highValue[i]++ - if highValue[i] != 0 { - endPoint.value.SetBytesAsString(highValue, tpOfPattern.GetCollate(), uint32(tpOfPattern.GetFlen())) + sortKeyWithoutTrim[i]++ + if sortKeyWithoutTrim[i] != 0 { + endPoint.value.SetBytes(sortKeyWithoutTrim) break } - // If highValue[i] is 255 and highValue[i]++ is 0, then the end point value is max value. + // If sortKeyWithoutTrim[i] is 255 and sortKeyWithoutTrim[i]++ is 0, then the end point value is max value. if i == 0 { endPoint.value = types.MaxValueDatum() } @@ -730,7 +915,16 @@ func isPadSpaceCollation(collation string) bool { return collation != charset.CollationBin } +<<<<<<< HEAD func (r *builder) buildFromNot(expr *expression.ScalarFunction) []*point { +======= +func (r *builder) buildFromNot( + expr *expression.ScalarFunction, + newTp *types.FieldType, + prefixLen int, + convertToSortKey bool, +) []*point { +>>>>>>> e053c27f068 (util/ranger: support use `like` to build range for new collation columns (#48522)) switch n := expr.FuncName.L; n { case ast.IsTruthWithoutNull: return r.buildFromIsTrue(expr, 1, false) @@ -743,7 +937,18 @@ func (r *builder) buildFromNot(expr *expression.ScalarFunction) []*point { isUnsignedIntCol bool nonNegativePos int ) +<<<<<<< HEAD rangePoints, hasNull := r.buildFromIn(expr) +======= + // Note that we must handle the cutting prefix and converting to sort key in buildFromNot, because if we cut the + // prefix inside buildFromIn(), the inversion logic here would make an incomplete and wrong range. + // For example, for index col(1), col NOT IN ('aaa', 'bbb'), if we cut the prefix in buildFromIn(), we would get + // ['a', 'a'], ['b', 'b'] from there. Then after in this function we would get ['' 'a'), ('a', 'b'), ('b', +inf] + // as the result. This is wrong because data like 'ab' would be missed. Actually we are unable to build a range + // for this case. + // So we must cut the prefix in this function, therefore converting to sort key must also be done here. + rangePoints, hasNull := r.buildFromIn(expr, newTp, types.UnspecifiedLength, false) +>>>>>>> e053c27f068 (util/ranger: support use `like` to build range for new collation columns (#48522)) if hasNull { return nil } @@ -769,6 +974,18 @@ func (r *builder) buildFromNot(expr *expression.ScalarFunction) []*point { // Append the interval (last element, max value]. retRangePoints = append(retRangePoints, &point{value: previousValue, start: true, excl: true}) retRangePoints = append(retRangePoints, &point{value: types.MaxValueDatum()}) +<<<<<<< HEAD +======= + cutPrefixForPoints(retRangePoints, prefixLen, expr.GetArgs()[0].GetType()) + if convertToSortKey { + var err error + retRangePoints, err = pointsConvertToSortKey(r.sctx, retRangePoints, newTp) + if err != nil { + r.err = err + return getFullRange() + } + } +>>>>>>> e053c27f068 (util/ranger: support use `like` to build range for new collation columns (#48522)) return retRangePoints case ast.Like: // Pattern not like is not supported. @@ -785,6 +1002,7 @@ func (r *builder) buildFromNot(expr *expression.ScalarFunction) []*point { return getFullRange() } +<<<<<<< HEAD func (r *builder) buildFromScalarFunc(expr *expression.ScalarFunction, collator collate.Collator) []*point { switch op := expr.FuncName.L; op { case ast.GE, ast.GT, ast.LT, ast.LE, ast.EQ, ast.NE, ast.NullEQ: @@ -793,6 +1011,29 @@ func (r *builder) buildFromScalarFunc(expr *expression.ScalarFunction, collator return r.intersection(r.build(expr.GetArgs()[0], collator), r.build(expr.GetArgs()[1], collator), collator) case ast.LogicOr: return r.union(r.build(expr.GetArgs()[0], collator), r.build(expr.GetArgs()[1], collator), collator) +======= +func (r *builder) buildFromScalarFunc( + expr *expression.ScalarFunction, + newTp *types.FieldType, + prefixLen int, + convertToSortKey bool, +) []*point { + switch op := expr.FuncName.L; op { + case ast.GE, ast.GT, ast.LT, ast.LE, ast.EQ, ast.NE, ast.NullEQ: + return r.buildFromBinOp(expr, newTp, prefixLen, convertToSortKey) + case ast.LogicAnd: + collator := collate.GetCollator(newTp.GetCollate()) + if convertToSortKey { + collator = collate.GetCollator(charset.CollationBin) + } + return r.intersection(r.build(expr.GetArgs()[0], newTp, prefixLen, convertToSortKey), r.build(expr.GetArgs()[1], newTp, prefixLen, convertToSortKey), collator) + case ast.LogicOr: + collator := collate.GetCollator(newTp.GetCollate()) + if convertToSortKey { + collator = collate.GetCollator(charset.CollationBin) + } + return r.union(r.build(expr.GetArgs()[0], newTp, prefixLen, convertToSortKey), r.build(expr.GetArgs()[1], newTp, prefixLen, convertToSortKey), collator) +>>>>>>> e053c27f068 (util/ranger: support use `like` to build range for new collation columns (#48522)) case ast.IsTruthWithoutNull: return r.buildFromIsTrue(expr, 0, false) case ast.IsTruthWithNull: @@ -800,25 +1041,42 @@ func (r *builder) buildFromScalarFunc(expr *expression.ScalarFunction, collator case ast.IsFalsity: return r.buildFromIsFalse(expr, 0) case ast.In: +<<<<<<< HEAD retPoints, _ := r.buildFromIn(expr) return retPoints case ast.Like: return r.newBuildFromPatternLike(expr) +======= + retPoints, _ := r.buildFromIn(expr, newTp, prefixLen, convertToSortKey) + return retPoints + case ast.Like: + return r.newBuildFromPatternLike(expr, newTp, prefixLen, convertToSortKey) +>>>>>>> e053c27f068 (util/ranger: support use `like` to build range for new collation columns (#48522)) case ast.IsNull: startPoint := &point{start: true} endPoint := &point{} return []*point{startPoint, endPoint} case ast.UnaryNot: +<<<<<<< HEAD return r.buildFromNot(expr.GetArgs()[0].(*expression.ScalarFunction)) +======= + return r.buildFromNot(expr.GetArgs()[0].(*expression.ScalarFunction), newTp, prefixLen, convertToSortKey) +>>>>>>> e053c27f068 (util/ranger: support use `like` to build range for new collation columns (#48522)) } return nil } +// We need an input collator because our (*Datum).Compare(), which is used in this method, needs an explicit collator +// input to handle comparison for string and bytes. +// Note that if the points are converted to sort key, the collator should be set to charset.CollationBin. func (r *builder) intersection(a, b []*point, collator collate.Collator) []*point { return r.merge(a, b, false, collator) } +// We need an input collator because our (*Datum).Compare(), which is used in this method, needs an explicit collator +// input to handle comparison for string and bytes. +// Note that if the points are converted to sort key, the collator should be set to charset.CollationBin. func (r *builder) union(a, b []*point, collator collate.Collator) []*point { return r.merge(a, b, true, collator) } diff --git a/pkg/util/ranger/ranger.go b/pkg/util/ranger/ranger.go index 10fa1cc70f6ae..52820bb56e5d7 100644 --- a/pkg/util/ranger/ranger.go +++ b/pkg/util/ranger/ranger.go @@ -410,11 +410,20 @@ func points2TableRanges(sctx sessionctx.Context, rangePoints []*point, tp *types // The second return value is the conditions used to build ranges and the third return value is the remained conditions. func buildColumnRange(accessConditions []expression.Expression, sctx sessionctx.Context, tp *types.FieldType, tableRange bool, colLen int, rangeMaxSize int64) (Ranges, []expression.Expression, []expression.Expression, error) { +<<<<<<< HEAD rb := builder{sc: sctx.GetSessionVars().StmtCtx} rangePoints := getFullRange() for _, cond := range accessConditions { collator := collate.GetCollator(tp.GetCollate()) rangePoints = rb.intersection(rangePoints, rb.build(cond, collator), collator) +======= + rb := builder{sctx: sctx} + newTp := newFieldType(tp) + rangePoints := getFullRange() + for _, cond := range accessConditions { + collator := collate.GetCollator(charset.CollationBin) + rangePoints = rb.intersection(rangePoints, rb.build(cond, newTp, colLen, true), collator) +>>>>>>> e053c27f068 (util/ranger: support use `like` to build range for new collation columns (#48522)) if rb.err != nil { return nil, nil, nil, errors.Trace(rb.err) } @@ -424,7 +433,7 @@ func buildColumnRange(accessConditions []expression.Expression, sctx sessionctx. rangeFallback bool err error ) - newTp := newFieldType(tp) + newTp = convertStringFTToBinaryCollate(newTp) if tableRange { ranges, rangeFallback, err = points2TableRanges(sctx, rangePoints, newTp, rangeMaxSize) } else { @@ -492,14 +501,19 @@ func (d *rangeDetacher) buildRangeOnColsByCNFCond(newTp []*types.FieldType, eqAn ) for i := 0; i < eqAndInCount; i++ { // Build ranges for equal or in access conditions. +<<<<<<< HEAD point := rb.build(accessConds[i], collate.GetCollator(newTp[i].GetCollate())) +======= + point := rb.build(accessConds[i], newTp[i], d.lengths[i], d.convertToSortKey) +>>>>>>> e053c27f068 (util/ranger: support use `like` to build range for new collation columns (#48522)) if rb.err != nil { return nil, nil, nil, errors.Trace(rb.err) } + tmpNewTp := convertStringFTToBinaryCollate(newTp[i]) if i == 0 { - ranges, rangeFallback, err = points2Ranges(d.sctx, point, newTp[i], d.rangeMaxSize) + ranges, rangeFallback, err = points2Ranges(d.sctx, point, tmpNewTp, d.rangeMaxSize) } else { - ranges, rangeFallback, err = appendPoints2Ranges(d.sctx, ranges, point, newTp[i], d.rangeMaxSize) + ranges, rangeFallback, err = appendPoints2Ranges(d.sctx, ranges, point, tmpNewTp, d.rangeMaxSize) } if err != nil { return nil, nil, nil, errors.Trace(err) @@ -513,15 +527,30 @@ func (d *rangeDetacher) buildRangeOnColsByCNFCond(newTp []*types.FieldType, eqAn // Build rangePoints for non-equal access conditions. for i := eqAndInCount; i < len(accessConds); i++ { collator := collate.GetCollator(newTp[eqAndInCount].GetCollate()) +<<<<<<< HEAD rangePoints = rb.intersection(rangePoints, rb.build(accessConds[i], collator), collator) +======= + if d.convertToSortKey { + collator = collate.GetCollator(charset.CollationBin) + } + rangePoints = rb.intersection(rangePoints, rb.build(accessConds[i], newTp[eqAndInCount], d.lengths[eqAndInCount], d.convertToSortKey), collator) +>>>>>>> e053c27f068 (util/ranger: support use `like` to build range for new collation columns (#48522)) if rb.err != nil { return nil, nil, nil, errors.Trace(rb.err) } } + var tmpNewTp *types.FieldType + if eqAndInCount == 0 || eqAndInCount < len(accessConds) { + if d.convertToSortKey { + tmpNewTp = convertStringFTToBinaryCollate(newTp[eqAndInCount]) + } else { + tmpNewTp = newTp[eqAndInCount] + } + } if eqAndInCount == 0 { - ranges, rangeFallback, err = points2Ranges(d.sctx, rangePoints, newTp[0], d.rangeMaxSize) + ranges, rangeFallback, err = points2Ranges(d.sctx, rangePoints, tmpNewTp, d.rangeMaxSize) } else if eqAndInCount < len(accessConds) { - ranges, rangeFallback, err = appendPoints2Ranges(d.sctx, ranges, rangePoints, newTp[eqAndInCount], d.rangeMaxSize) + ranges, rangeFallback, err = appendPoints2Ranges(d.sctx, ranges, rangePoints, tmpNewTp, d.rangeMaxSize) } if err != nil { return nil, nil, nil, errors.Trace(err) @@ -533,6 +562,18 @@ func (d *rangeDetacher) buildRangeOnColsByCNFCond(newTp []*types.FieldType, eqAn return ranges, accessConds, nil, nil } +func convertStringFTToBinaryCollate(ft *types.FieldType) *types.FieldType { + if ft.EvalType() != types.ETString || + ft.GetType() == mysql.TypeEnum || + ft.GetType() == mysql.TypeSet { + return ft + } + newTp := ft.Clone() + newTp.SetCharset(charset.CharsetBin) + newTp.SetCollate(charset.CollationBin) + return newTp +} + // buildCNFIndexRange builds the range for index where the top layer is CNF. func (d *rangeDetacher) buildCNFIndexRange(newTp []*types.FieldType, eqAndInCount int, accessConds []expression.Expression) (Ranges, []expression.Expression, []expression.Expression, error) { diff --git a/pkg/util/ranger/ranger_test.go b/pkg/util/ranger/ranger_test.go index 7635900770245..0bf34bf2c1bd1 100644 --- a/pkg/util/ranger/ranger_test.go +++ b/pkg/util/ranger/ranger_test.go @@ -1330,21 +1330,21 @@ create table t( exprStr: "f >= 'a' and f <= 'B'", accessConds: "[ge(test.t.f, a) le(test.t.f, B)]", filterConds: "[]", - resultStr: "[[\"a\",\"B\"]]", + resultStr: "[[\"\\x00A\",\"\\x00B\"]]", }, { indexPos: 4, exprStr: "f in ('a', 'B')", accessConds: "[in(test.t.f, a, B)]", filterConds: "[]", - resultStr: "[[\"a\",\"a\"] [\"B\",\"B\"]]", + resultStr: "[[\"\\x00A\",\"\\x00A\"] [\"\\x00B\",\"\\x00B\"]]", }, { indexPos: 4, exprStr: "f = 'a' and f = 'B' collate utf8mb4_bin", accessConds: "[eq(test.t.f, a)]", filterConds: "[eq(test.t.f, B)]", - resultStr: "[[\"a\",\"a\"]]", + resultStr: "[[\"\\x00A\",\"\\x00A\"]]", }, { indexPos: 4, diff --git a/tests/integrationtest/r/expression/charset_and_collation.result b/tests/integrationtest/r/expression/charset_and_collation.result index 551c015572d6f..2a71e1397079a 100644 --- a/tests/integrationtest/r/expression/charset_and_collation.result +++ b/tests/integrationtest/r/expression/charset_and_collation.result @@ -1898,7 +1898,7 @@ c 1 explain format="brief" select v from t where v < 'b' order by v; id estRows task access object operator info IndexReader 3323.33 root index:IndexRangeScan -└─IndexRangeScan 3323.33 cop[tikv] table:t, index:v(v) range:[-inf,"b"), keep order:true, stats:pseudo +└─IndexRangeScan 3323.33 cop[tikv] table:t, index:v(v) range:[-inf,"\x00B"), keep order:true, stats:pseudo select v from t where v < 'b' order by v; v @@ -1909,7 +1909,7 @@ a explain format="brief" select v from t where v < 'b' and v > ' ' order by v; id estRows task access object operator info IndexReader 250.00 root index:IndexRangeScan -└─IndexRangeScan 250.00 cop[tikv] table:t, index:v(v) range:(" ","b"), keep order:true, stats:pseudo +└─IndexRangeScan 250.00 cop[tikv] table:t, index:v(v) range:("","\x00B"), keep order:true, stats:pseudo select v from t where v < 'b' and v > ' ' order by v; v a @@ -1935,7 +1935,7 @@ explain format="brief" select id from t use index(v) where v < 'b'; id estRows task access object operator info Projection 3323.33 root expression__charset_and_collation.t.id └─IndexLookUp 3323.33 root - ├─IndexRangeScan(Build) 3323.33 cop[tikv] table:t, index:v(v) range:[-inf,"b"), keep order:false, stats:pseudo + ├─IndexRangeScan(Build) 3323.33 cop[tikv] table:t, index:v(v) range:[-inf,"\x00B"), keep order:false, stats:pseudo └─TableRowIDScan(Probe) 3323.33 cop[tikv] table:t keep order:false, stats:pseudo select id from t use index(v) where v < 'b'; id @@ -1948,7 +1948,7 @@ explain format="brief" select id from t use index(v) where v < 'b' and v > ' '; id estRows task access object operator info Projection 250.00 root expression__charset_and_collation.t.id └─IndexLookUp 250.00 root - ├─IndexRangeScan(Build) 250.00 cop[tikv] table:t, index:v(v) range:(" ","b"), keep order:false, stats:pseudo + ├─IndexRangeScan(Build) 250.00 cop[tikv] table:t, index:v(v) range:("","\x00B"), keep order:false, stats:pseudo └─TableRowIDScan(Probe) 250.00 cop[tikv] table:t keep order:false, stats:pseudo select id from t use index(v) where v < 'b' and v > ' '; id diff --git a/tests/integrationtest/r/expression/issues.result b/tests/integrationtest/r/expression/issues.result index 6683ecc626934..99a4cbe9d34bb 100644 --- a/tests/integrationtest/r/expression/issues.result +++ b/tests/integrationtest/r/expression/issues.result @@ -2101,9 +2101,9 @@ create table t(a char(4) collate utf8_general_ci primary key /*T![clustered_inde insert into t values('`?'); explain format='brief' select * from t where a like '`%'; id estRows task access object operator info -TableReader 8000.00 root data:Selection -└─Selection 8000.00 cop[tikv] like(expression__issues.t.a, "`%", 92) - └─TableFullScan 10000.00 cop[tikv] table:t keep order:false, stats:pseudo +TableReader 250.00 root data:Selection +└─Selection 250.00 cop[tikv] like(expression__issues.t.a, "`%", 92) + └─TableRangeScan 250.00 cop[tikv] table:t range:["\x00`","\x00a"), keep order:false, stats:pseudo select * from t where a like '`%'; a `? diff --git a/tests/integrationtest/r/planner/core/issuetest/planner_issue.result b/tests/integrationtest/r/planner/core/issuetest/planner_issue.result index c9e1ffcafc287..c21d6c0eb6bb7 100644 --- a/tests/integrationtest/r/planner/core/issuetest/planner_issue.result +++ b/tests/integrationtest/r/planner/core/issuetest/planner_issue.result @@ -187,8 +187,15 @@ insert into t1 value('测试'),('测试 '),('xxx '); explain format = brief select *,length(a) from t1 where a like '测试 %'; id estRows task access object operator info Projection 250.00 root planner__core__issuetest__planner_issue.t1.a, length(planner__core__issuetest__planner_issue.t1.a)->Column#3 +<<<<<<< HEAD └─IndexReader 250.00 root index:IndexRangeScan └─IndexRangeScan 250.00 cop[tikv] table:t1, index:ia(a) range:["测试 ","测试!"), keep order:false, stats:pseudo +======= +└─UnionScan 250.00 root like(planner__core__issuetest__planner_issue.t1.a, "测试 %", 92) + └─IndexReader 250.00 root index:Selection + └─Selection 250.00 cop[tikv] like(planner__core__issuetest__planner_issue.t1.a, "测试 %", 92) + └─IndexRangeScan 250.00 cop[tikv] table:t1, index:ia(a) range:["测试","测试!"), keep order:false, stats:pseudo +>>>>>>> e053c27f068 (util/ranger: support use `like` to build range for new collation columns (#48522)) explain format = brief select *,length(a) from t1 where a like '测试'; id estRows task access object operator info Projection 10.00 root planner__core__issuetest__planner_issue.t1.a, length(planner__core__issuetest__planner_issue.t1.a)->Column#3 @@ -210,3 +217,28 @@ IndexReader 250.00 root index:Selection select * from t1 use index (ia) where a like 'xxx_'; a xxx +<<<<<<< HEAD +======= +create table t2(a varchar(20) collate gbk_chinese_ci, index ia(a)); +insert into t2 value('测试'),('测试 '); +explain format = brief select *,length(a) from t2 where a like '测试 %'; +id estRows task access object operator info +Projection 250.00 root planner__core__issuetest__planner_issue.t2.a, length(to_binary(planner__core__issuetest__planner_issue.t2.a))->Column#3 +└─UnionScan 250.00 root like(planner__core__issuetest__planner_issue.t2.a, "测试 %", 92) + └─IndexReader 250.00 root index:Selection + └─Selection 250.00 cop[tikv] like(planner__core__issuetest__planner_issue.t2.a, "测试 %", 92) + └─IndexRangeScan 250.00 cop[tikv] table:t2, index:ia(a) range:["\x89\a\xba%","\x89\a\xba%!"), keep order:false, stats:pseudo +explain format = brief select *,length(a) from t2 where a like '测试'; +id estRows task access object operator info +Projection 10.00 root planner__core__issuetest__planner_issue.t2.a, length(to_binary(planner__core__issuetest__planner_issue.t2.a))->Column#3 +└─UnionScan 10.00 root like(planner__core__issuetest__planner_issue.t2.a, "测试", 92) + └─IndexReader 10.00 root index:Selection + └─Selection 10.00 cop[tikv] like(planner__core__issuetest__planner_issue.t2.a, "测试", 92) + └─IndexRangeScan 10.00 cop[tikv] table:t2, index:ia(a) range:["\x89\a\xba%","\x89\a\xba%"], keep order:false, stats:pseudo +select *,length(a) from t2 where a like '测试 %'; +a length(a) +测试 6 +select *,length(a) from t2 where a like '测试'; +a length(a) +测试 4 +>>>>>>> e053c27f068 (util/ranger: support use `like` to build range for new collation columns (#48522)) diff --git a/tests/integrationtest/r/planner/core/range_scan_for_like.result b/tests/integrationtest/r/planner/core/range_scan_for_like.result new file mode 100644 index 0000000000000..058de729e4589 --- /dev/null +++ b/tests/integrationtest/r/planner/core/range_scan_for_like.result @@ -0,0 +1,1035 @@ +create table t(a varchar(20) collate utf8mb4_general_ci, index ia(a)); +insert into t value('测试'),('测试Abc'),('测试 '),('你好'),('aABBccdd'),('Aa'),(''),(' '),(' '),(' 语言'),(' 语 言 '),('测测试 '),('测测试 '),(NULL); +explain select *, length(a) from t use index (ia) where a like '测试%' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Projection_13 250.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexReader_16 250.00 root index:Selection_15 + └─Selection_15 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, "测试%", 92) + └─IndexRangeScan_14 250.00 cop[tikv] table:t, index:ia(a) range:["mK\x8b\xd5","mK\x8b\xd6"), keep order:true, stats:pseudo +select *, length(a) from t use index (ia) where a like '测试%' order by a,_tidb_rowid; +a length(a) +测试 6 +测试 11 +测试Abc 9 +explain select *, length(a) from t use index (ia) where a like '测%%' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Projection_13 250.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexReader_16 250.00 root index:Selection_15 + └─Selection_15 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, "测%%", 92) + └─IndexRangeScan_14 250.00 cop[tikv] table:t, index:ia(a) range:["mK","mL"), keep order:true, stats:pseudo +select *, length(a) from t use index (ia) where a like '测%%' order by a,_tidb_rowid; +a length(a) +测测试 10 +测测试 13 +测试 6 +测试 11 +测试Abc 9 +explain select *, length(a) from t use index (ia) where a like '测%%试' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Projection_13 250.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexReader_16 250.00 root index:Selection_15 + └─Selection_15 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, "测%%试", 92) + └─IndexRangeScan_14 250.00 cop[tikv] table:t, index:ia(a) range:["mK","mL"), keep order:true, stats:pseudo +select *, length(a) from t use index (ia) where a like '测%%试' order by a,_tidb_rowid; +a length(a) +测试 6 +explain select *, length(a) from t use index (ia) where a like '测试%%' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Projection_13 250.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexReader_16 250.00 root index:Selection_15 + └─Selection_15 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, "测试%%", 92) + └─IndexRangeScan_14 250.00 cop[tikv] table:t, index:ia(a) range:["mK\x8b\xd5","mK\x8b\xd6"), keep order:true, stats:pseudo +select *, length(a) from t use index (ia) where a like '测试%%' order by a,_tidb_rowid; +a length(a) +测试 6 +测试 11 +测试Abc 9 +explain select *, length(a) from t use index (ia) where a like '测试_' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Projection_13 250.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexReader_16 250.00 root index:Selection_15 + └─Selection_15 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, "测试_", 92) + └─IndexRangeScan_14 250.00 cop[tikv] table:t, index:ia(a) range:["mK\x8b\xd5","mK\x8b\xd6"), keep order:true, stats:pseudo +select *, length(a) from t use index (ia) where a like '测试_' order by a,_tidb_rowid; +a length(a) +explain select *, length(a) from t use index (ia) where a like '你好%' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Projection_13 250.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexReader_16 250.00 root index:Selection_15 + └─Selection_15 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, "你好%", 92) + └─IndexRangeScan_14 250.00 cop[tikv] table:t, index:ia(a) range:["O`Y}","O`Y~"), keep order:true, stats:pseudo +select *, length(a) from t use index (ia) where a like '你好%' order by a,_tidb_rowid; +a length(a) +你好 6 +explain select *, length(a) from t use index (ia) where a like 'aa' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 10.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Projection_13 10.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexReader_16 10.00 root index:Selection_15 + └─Selection_15 10.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, "aa", 92) + └─IndexRangeScan_14 10.00 cop[tikv] table:t, index:ia(a) range:["\x00A\x00A","\x00A\x00A"], keep order:true, stats:pseudo +select *, length(a) from t use index (ia) where a like 'aa' order by a,_tidb_rowid; +a length(a) +Aa 2 +explain select *, length(a) from t use index (ia) where a like 'aa%' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Projection_13 250.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexReader_16 250.00 root index:Selection_15 + └─Selection_15 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, "aa%", 92) + └─IndexRangeScan_14 250.00 cop[tikv] table:t, index:ia(a) range:["\x00A\x00A","\x00A\x00B"), keep order:true, stats:pseudo +select *, length(a) from t use index (ia) where a like 'aa%' order by a,_tidb_rowid; +a length(a) +Aa 2 +aABBccdd 8 +explain select *, length(a) from t use index (ia) where a like 'aa%cc' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Projection_13 250.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexReader_16 250.00 root index:Selection_15 + └─Selection_15 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, "aa%cc", 92) + └─IndexRangeScan_14 250.00 cop[tikv] table:t, index:ia(a) range:["\x00A\x00A","\x00A\x00B"), keep order:true, stats:pseudo +select *, length(a) from t use index (ia) where a like 'aa%cc' order by a,_tidb_rowid; +a length(a) +explain select *, length(a) from t use index (ia) where a like '' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 10.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Projection_13 10.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexReader_16 10.00 root index:Selection_15 + └─Selection_15 10.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, "", 92) + └─IndexRangeScan_14 10.00 cop[tikv] table:t, index:ia(a) range:["",""], keep order:true, stats:pseudo +select *, length(a) from t use index (ia) where a like '' order by a,_tidb_rowid; +a length(a) + 0 +explain select *, length(a) from t use index (ia) where a like ' ' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 10.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Projection_13 10.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexReader_16 10.00 root index:Selection_15 + └─Selection_15 10.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, " ", 92) + └─IndexRangeScan_14 10.00 cop[tikv] table:t, index:ia(a) range:["",""], keep order:true, stats:pseudo +select *, length(a) from t use index (ia) where a like ' ' order by a,_tidb_rowid; +a length(a) + 1 +explain select *, length(a) from t use index (ia) where a like 'aa%dd' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Projection_13 250.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexReader_16 250.00 root index:Selection_15 + └─Selection_15 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, "aa%dd", 92) + └─IndexRangeScan_14 250.00 cop[tikv] table:t, index:ia(a) range:["\x00A\x00A","\x00A\x00B"), keep order:true, stats:pseudo +select *, length(a) from t use index (ia) where a like 'aa%dd' order by a,_tidb_rowid; +a length(a) +aABBccdd 8 +explain select *, length(a) from t use index (ia) where a like 'aa%%dd' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Projection_13 250.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexReader_16 250.00 root index:Selection_15 + └─Selection_15 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, "aa%%dd", 92) + └─IndexRangeScan_14 250.00 cop[tikv] table:t, index:ia(a) range:["\x00A\x00A","\x00A\x00B"), keep order:true, stats:pseudo +select *, length(a) from t use index (ia) where a like 'aa%%dd' order by a,_tidb_rowid; +a length(a) +aABBccdd 8 +explain select *, length(a) from t use index (ia) where a like 'aa_bccdd' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Projection_13 250.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexReader_16 250.00 root index:Selection_15 + └─Selection_15 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, "aa_bccdd", 92) + └─IndexRangeScan_14 250.00 cop[tikv] table:t, index:ia(a) range:["\x00A\x00A","\x00A\x00B"), keep order:true, stats:pseudo +select *, length(a) from t use index (ia) where a like 'aa_bccdd' order by a,_tidb_rowid; +a length(a) +aABBccdd 8 +explain select *, length(a) from t use index (ia) where a like '%%' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 8000.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Projection_13 8000.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexReader_16 8000.00 root index:Selection_15 + └─Selection_15 8000.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, "%%", 92) + └─IndexFullScan_14 10000.00 cop[tikv] table:t, index:ia(a) keep order:true, stats:pseudo +select *, length(a) from t use index (ia) where a like '%%' order by a,_tidb_rowid; +a length(a) + 0 + 1 + 2 + 语 言 10 + 语言 7 +Aa 2 +aABBccdd 8 +你好 6 +测测试 10 +测测试 13 +测试 6 +测试 11 +测试Abc 9 +explain select *, length(a) from t use index (ia) where a like ' %%' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Projection_13 250.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexReader_16 250.00 root index:Selection_15 + └─Selection_15 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, " %%", 92) + └─IndexRangeScan_14 250.00 cop[tikv] table:t, index:ia(a) range:["","\x00!"), keep order:true, stats:pseudo +select *, length(a) from t use index (ia) where a like ' %%' order by a,_tidb_rowid; +a length(a) + 1 + 2 + 语 言 10 + 语言 7 +explain select *, length(a) from t use index (ia) where a like ' %%语言' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Projection_13 250.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexReader_16 250.00 root index:Selection_15 + └─Selection_15 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, " %%语言", 92) + └─IndexRangeScan_14 250.00 cop[tikv] table:t, index:ia(a) range:["","\x00!"), keep order:true, stats:pseudo +select *, length(a) from t use index (ia) where a like ' %%语言' order by a,_tidb_rowid; +a length(a) + 语言 7 +explain select *, length(a) from t use index (ia) where a like ' 语 %' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Projection_13 250.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexReader_16 250.00 root index:Selection_15 + └─Selection_15 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, " 语 %", 92) + └─IndexRangeScan_14 250.00 cop[tikv] table:t, index:ia(a) range:["\x00 \x00 \x8b\xed","\x00 \x00 \x8b\xed\x00!"), keep order:true, stats:pseudo +select *, length(a) from t use index (ia) where a like ' 语 %' order by a,_tidb_rowid; +a length(a) + 语 言 10 +explain select *, length(a) from t use index (ia) where a like ' 语 _' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Projection_13 250.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexReader_16 250.00 root index:Selection_15 + └─Selection_15 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, " 语 _", 92) + └─IndexRangeScan_14 250.00 cop[tikv] table:t, index:ia(a) range:["\x00 \x00 \x8b\xed","\x00 \x00 \x8b\xed\x00 \x00!"), keep order:true, stats:pseudo +select *, length(a) from t use index (ia) where a like ' 语 _' order by a,_tidb_rowid; +a length(a) +drop table t; +create table t(a varchar(20) collate utf8mb4_unicode_ci, unique index ia(a)); +insert into t value(''),('测试'),('测试abc'),('你好'),('aabbccdd'),(' 语言'),(' 语 言 '),('测测试 '); +explain select *, length(a) from t use index (ia) where a like '测试%' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Sort_7 250.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─Projection_9 250.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexReader_12 250.00 root index:Selection_11 + └─Selection_11 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, "测试%", 92) + └─IndexRangeScan_10 250.00 cop[tikv] table:t, index:ia(a) range:["\xfb@\xedK\xfbA\x8b\xd5","\xfb@\xedK\xfbA\x8b\xd6"), keep order:false, stats:pseudo +select *, length(a) from t use index (ia) where a like '测试%' order by a,_tidb_rowid; +a length(a) +测试 6 +测试abc 9 +explain select *, length(a) from t use index (ia) where a like '测%%' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Sort_7 250.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─Projection_9 250.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexReader_12 250.00 root index:Selection_11 + └─Selection_11 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, "测%%", 92) + └─IndexRangeScan_10 250.00 cop[tikv] table:t, index:ia(a) range:["\xfb@\xedK","\xfb@\xedL"), keep order:false, stats:pseudo +select *, length(a) from t use index (ia) where a like '测%%' order by a,_tidb_rowid; +a length(a) +测测试 13 +测试 6 +测试abc 9 +explain select *, length(a) from t use index (ia) where a like '测%%试' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Sort_7 250.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─Projection_9 250.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexReader_12 250.00 root index:Selection_11 + └─Selection_11 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, "测%%试", 92) + └─IndexRangeScan_10 250.00 cop[tikv] table:t, index:ia(a) range:["\xfb@\xedK","\xfb@\xedL"), keep order:false, stats:pseudo +select *, length(a) from t use index (ia) where a like '测%%试' order by a,_tidb_rowid; +a length(a) +测试 6 +explain select *, length(a) from t use index (ia) where a like '测试%%' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Sort_7 250.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─Projection_9 250.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexReader_12 250.00 root index:Selection_11 + └─Selection_11 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, "测试%%", 92) + └─IndexRangeScan_10 250.00 cop[tikv] table:t, index:ia(a) range:["\xfb@\xedK\xfbA\x8b\xd5","\xfb@\xedK\xfbA\x8b\xd6"), keep order:false, stats:pseudo +select *, length(a) from t use index (ia) where a like '测试%%' order by a,_tidb_rowid; +a length(a) +测试 6 +测试abc 9 +explain select *, length(a) from t use index (ia) where a like '测试_' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Sort_7 250.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─Projection_9 250.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexReader_12 250.00 root index:Selection_11 + └─Selection_11 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, "测试_", 92) + └─IndexRangeScan_10 250.00 cop[tikv] table:t, index:ia(a) range:["\xfb@\xedK\xfbA\x8b\xd5","\xfb@\xedK\xfbA\x8b\xd6"), keep order:false, stats:pseudo +select *, length(a) from t use index (ia) where a like '测试_' order by a,_tidb_rowid; +a length(a) +explain select *, length(a) from t use index (ia) where a like '你好%' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Sort_7 250.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─Projection_9 250.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexReader_12 250.00 root index:Selection_11 + └─Selection_11 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, "你好%", 92) + └─IndexRangeScan_10 250.00 cop[tikv] table:t, index:ia(a) range:["\xfb@\xcf`\xfb@\xd9}","\xfb@\xcf`\xfb@\xd9~"), keep order:false, stats:pseudo +select *, length(a) from t use index (ia) where a like '你好%' order by a,_tidb_rowid; +a length(a) +你好 6 +explain select *, length(a) from t use index (ia) where a like 'aa' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 1.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Sort_7 1.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─Projection_9 1.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─Selection_11 1.00 root like(planner__core__range_scan_for_like.t.a, "aa", 92) + └─Point_Get_10 1.00 root table:t, index:ia(a) +select *, length(a) from t use index (ia) where a like 'aa' order by a,_tidb_rowid; +a length(a) +explain select *, length(a) from t use index (ia) where a like 'aa%' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Sort_7 250.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─Projection_9 250.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexReader_12 250.00 root index:Selection_11 + └─Selection_11 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, "aa%", 92) + └─IndexRangeScan_10 250.00 cop[tikv] table:t, index:ia(a) range:["\x0e3\x0e3","\x0e3\x0e4"), keep order:false, stats:pseudo +select *, length(a) from t use index (ia) where a like 'aa%' order by a,_tidb_rowid; +a length(a) +aabbccdd 8 +explain select *, length(a) from t use index (ia) where a like 'aa%cc' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Sort_7 250.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─Projection_9 250.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexReader_12 250.00 root index:Selection_11 + └─Selection_11 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, "aa%cc", 92) + └─IndexRangeScan_10 250.00 cop[tikv] table:t, index:ia(a) range:["\x0e3\x0e3","\x0e3\x0e4"), keep order:false, stats:pseudo +select *, length(a) from t use index (ia) where a like 'aa%cc' order by a,_tidb_rowid; +a length(a) +explain select *, length(a) from t use index (ia) where a like '' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 1.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Sort_7 1.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─Projection_9 1.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─Selection_11 1.00 root like(planner__core__range_scan_for_like.t.a, "", 92) + └─Point_Get_10 1.00 root table:t, index:ia(a) +select *, length(a) from t use index (ia) where a like '' order by a,_tidb_rowid; +a length(a) + 0 +explain select *, length(a) from t use index (ia) where a like 'aa%dd' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Sort_7 250.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─Projection_9 250.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexReader_12 250.00 root index:Selection_11 + └─Selection_11 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, "aa%dd", 92) + └─IndexRangeScan_10 250.00 cop[tikv] table:t, index:ia(a) range:["\x0e3\x0e3","\x0e3\x0e4"), keep order:false, stats:pseudo +select *, length(a) from t use index (ia) where a like 'aa%dd' order by a,_tidb_rowid; +a length(a) +aabbccdd 8 +explain select *, length(a) from t use index (ia) where a like 'aa%%dd' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Sort_7 250.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─Projection_9 250.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexReader_12 250.00 root index:Selection_11 + └─Selection_11 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, "aa%%dd", 92) + └─IndexRangeScan_10 250.00 cop[tikv] table:t, index:ia(a) range:["\x0e3\x0e3","\x0e3\x0e4"), keep order:false, stats:pseudo +select *, length(a) from t use index (ia) where a like 'aa%%dd' order by a,_tidb_rowid; +a length(a) +aabbccdd 8 +explain select *, length(a) from t use index (ia) where a like 'aa_bccdd' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Sort_7 250.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─Projection_9 250.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexReader_12 250.00 root index:Selection_11 + └─Selection_11 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, "aa_bccdd", 92) + └─IndexRangeScan_10 250.00 cop[tikv] table:t, index:ia(a) range:["\x0e3\x0e3","\x0e3\x0e4"), keep order:false, stats:pseudo +select *, length(a) from t use index (ia) where a like 'aa_bccdd' order by a,_tidb_rowid; +a length(a) +aabbccdd 8 +explain select *, length(a) from t use index (ia) where a like '%%' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 8000.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Sort_7 8000.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─Projection_9 8000.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexReader_12 8000.00 root index:Selection_11 + └─Selection_11 8000.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, "%%", 92) + └─IndexFullScan_10 10000.00 cop[tikv] table:t, index:ia(a) keep order:false, stats:pseudo +select *, length(a) from t use index (ia) where a like '%%' order by a,_tidb_rowid; +a length(a) + 0 + 语 言 10 + 语言 7 +aabbccdd 8 +你好 6 +测测试 13 +测试 6 +测试abc 9 +explain select *, length(a) from t use index (ia) where a like ' %%' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Sort_7 250.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─Projection_9 250.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexReader_12 250.00 root index:Selection_11 + └─Selection_11 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, " %%", 92) + └─IndexRangeScan_10 250.00 cop[tikv] table:t, index:ia(a) range:["","\x02\n"), keep order:false, stats:pseudo +select *, length(a) from t use index (ia) where a like ' %%' order by a,_tidb_rowid; +a length(a) + 语 言 10 + 语言 7 +explain select *, length(a) from t use index (ia) where a like ' %%语言' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Sort_7 250.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─Projection_9 250.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexReader_12 250.00 root index:Selection_11 + └─Selection_11 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, " %%语言", 92) + └─IndexRangeScan_10 250.00 cop[tikv] table:t, index:ia(a) range:["","\x02\n"), keep order:false, stats:pseudo +select *, length(a) from t use index (ia) where a like ' %%语言' order by a,_tidb_rowid; +a length(a) + 语言 7 +explain select *, length(a) from t use index (ia) where a like ' 语 %' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Sort_7 250.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─Projection_9 250.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexReader_12 250.00 root index:Selection_11 + └─Selection_11 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, " 语 %", 92) + └─IndexRangeScan_10 250.00 cop[tikv] table:t, index:ia(a) range:["\x02\t\x02\t\xfbA\x8b\xed","\x02\t\x02\t\xfbA\x8b\xed\x02\n"), keep order:false, stats:pseudo +select *, length(a) from t use index (ia) where a like ' 语 %' order by a,_tidb_rowid; +a length(a) + 语 言 10 +explain select *, length(a) from t use index (ia) where a like ' 语 _' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, Column#3->Column#5 +└─Sort_7 250.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─Projection_9 250.00 root planner__core__range_scan_for_like.t.a, length(planner__core__range_scan_for_like.t.a)->Column#3, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexReader_12 250.00 root index:Selection_11 + └─Selection_11 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, " 语 _", 92) + └─IndexRangeScan_10 250.00 cop[tikv] table:t, index:ia(a) range:["\x02\t\x02\t\xfbA\x8b\xed","\x02\t\x02\t\xfbA\x8b\xed\x02\t\x02\n"), keep order:false, stats:pseudo +select *, length(a) from t use index (ia) where a like ' 语 _' order by a,_tidb_rowid; +a length(a) +drop table t; +create table t(a varchar(20) collate utf8mb4_0900_ai_ci, b varchar(20) collate ascii_bin, c bigint, primary key(a(1), b) clustered); +insert into t (a, b, c) values +('测试1', 'asdfgh', 345346), +('你好2', 'qqwweerrrr', 987765), +('こんにちは3', 'zxcvbnn', 1111111), +('안녕하세요4', 'asdfgh ', 3333333333), +('Ciao5', ' asdfgh', 444400), +('Hola6', ' asdfgh ', 6666), +('Bonjour ', '', 888888888), +('Olá8', ' ', 9999999), +('Привет9', ' ', 321321), +('Hallo10', '12345', 35678); +explain select * from t use index (primary) where a like '测试%' and b like 'asd%' order by a,b; +id estRows task access object operator info +Sort_5 6.25 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t.b +└─TableReader_10 6.25 root data:Selection_9 + └─Selection_9 6.25 cop[tikv] like(planner__core__range_scan_for_like.t.a, "测试%", 92), like(planner__core__range_scan_for_like.t.b, "asd%", 92) + └─TableRangeScan_8 250.00 cop[tikv] table:t range:["\xfb@\xedK","\xfb@\xedL"), keep order:false, stats:pseudo +select * from t use index (primary) where a like '测试%' and b like 'asd%' order by a,b; +a b c +测试1 asdfgh 345346 +explain select * from t use index (primary) where a like '测试1' and b like 'asdfgh %' order by a,b; +id estRows task access object operator info +Sort_5 0.25 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t.b +└─TableReader_10 0.25 root data:Selection_9 + └─Selection_9 0.25 cop[tikv] like(planner__core__range_scan_for_like.t.a, "测试1", 92), like(planner__core__range_scan_for_like.t.b, "asdfgh %", 92) + └─TableRangeScan_8 10.00 cop[tikv] table:t range:["\xfb@\xedK","\xfb@\xedK"], keep order:false, stats:pseudo +select * from t use index (primary) where a like '测试1' and b like 'asdfgh %' order by a,b; +a b c +explain select * from t use index (primary) where a like 'こんにち_' and b like 'zxc%' order by a,b; +id estRows task access object operator info +Sort_5 6.25 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t.b +└─TableReader_10 6.25 root data:Selection_9 + └─Selection_9 6.25 cop[tikv] like(planner__core__range_scan_for_like.t.a, "こんにち_", 92), like(planner__core__range_scan_for_like.t.b, "zxc%", 92) + └─TableRangeScan_8 250.00 cop[tikv] table:t range:["=d","=e"), keep order:false, stats:pseudo +select * from t use index (primary) where a like 'こんにち_' and b like 'zxc%' order by a,b; +a b c +explain select * from t use index (primary) where a like '안녕하세요%' and b like 'asd%' order by a,b; +id estRows task access object operator info +Sort_5 6.25 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t.b +└─TableReader_10 6.25 root data:Selection_9 + └─Selection_9 6.25 cop[tikv] like(planner__core__range_scan_for_like.t.a, "안녕하세요%", 92), like(planner__core__range_scan_for_like.t.b, "asd%", 92) + └─TableRangeScan_8 250.00 cop[tikv] table:t range:["<\x00 'aabb' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 3333.33 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t.b +└─Sort_7 3333.33 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexLookUp_12 3333.33 root + ├─IndexRangeScan_9(Build) 3333.33 cop[tikv] table:t, index:ia(a, b) range:["\x00A\x00A\x00B",+inf], keep order:false, stats:pseudo + └─Selection_11(Probe) 3333.33 cop[tikv] gt(planner__core__range_scan_for_like.t.a, "aabb") + └─TableRowIDScan_10 3333.33 cop[tikv] table:t keep order:false, stats:pseudo +select * from t use index (ia) where a > 'aabb' order by a,_tidb_rowid; +a b +aABBccdd 890 +你好 111 +测测试 2468 +测测试 99999 +测试 222 +测试 543 +测试Abc 324 +explain select * from t use index (ia) where a > 'aab' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 3333.33 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t.b +└─Sort_7 3333.33 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexLookUp_12 3333.33 root + ├─IndexRangeScan_9(Build) 3333.33 cop[tikv] table:t, index:ia(a, b) range:["\x00A\x00A\x00B",+inf], keep order:false, stats:pseudo + └─Selection_11(Probe) 3333.33 cop[tikv] gt(planner__core__range_scan_for_like.t.a, "aab") + └─TableRowIDScan_10 3333.33 cop[tikv] table:t keep order:false, stats:pseudo +select * from t use index (ia) where a > 'aab' order by a,_tidb_rowid; +a b +aabB 456 +aABBccdd 890 +你好 111 +测测试 2468 +测测试 99999 +测试 222 +测试 543 +测试Abc 324 +explain select * from t use index (ia) where a > 'aa' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 3333.33 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t.b +└─Sort_7 3333.33 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexLookUp_12 3333.33 root + ├─IndexRangeScan_9(Build) 3333.33 cop[tikv] table:t, index:ia(a, b) range:("\x00A\x00A",+inf], keep order:false, stats:pseudo + └─Selection_11(Probe) 3333.33 cop[tikv] gt(planner__core__range_scan_for_like.t.a, "aa") + └─TableRowIDScan_10 3333.33 cop[tikv] table:t keep order:false, stats:pseudo +select * from t use index (ia) where a > 'aa' order by a,_tidb_rowid; +a b +aab 456 +aabB 456 +aABBccdd 890 +你好 111 +测测试 2468 +测测试 99999 +测试 222 +测试 543 +测试Abc 324 +explain select * from t use index (ia) where a < 'aabb' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 3323.33 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t.b +└─Sort_7 3323.33 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexLookUp_12 3323.33 root + ├─IndexRangeScan_9(Build) 3323.33 cop[tikv] table:t, index:ia(a, b) range:[-inf,"\x00A\x00A\x00B"], keep order:false, stats:pseudo + └─Selection_11(Probe) 3323.33 cop[tikv] lt(planner__core__range_scan_for_like.t.a, "aabb") + └─TableRowIDScan_10 3323.33 cop[tikv] table:t keep order:false, stats:pseudo +select * from t use index (ia) where a < 'aabb' order by a,_tidb_rowid; +a b + 234 + 11111 + 66666 + 语 言 3579 + 语言 55555 +A 456 +Aa 456 +aab 456 +explain select * from t use index (ia) where a < 'aab' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 3323.33 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t.b +└─Sort_7 3323.33 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexLookUp_12 3323.33 root + ├─IndexRangeScan_9(Build) 3323.33 cop[tikv] table:t, index:ia(a, b) range:[-inf,"\x00A\x00A\x00B"), keep order:false, stats:pseudo + └─Selection_11(Probe) 3323.33 cop[tikv] lt(planner__core__range_scan_for_like.t.a, "aab") + └─TableRowIDScan_10 3323.33 cop[tikv] table:t keep order:false, stats:pseudo +select * from t use index (ia) where a < 'aab' order by a,_tidb_rowid; +a b + 234 + 11111 + 66666 + 语 言 3579 + 语言 55555 +A 456 +Aa 456 +explain select * from t use index (ia) where a < 'aa' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 3323.33 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t.b +└─Sort_7 3323.33 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexLookUp_12 3323.33 root + ├─IndexRangeScan_9(Build) 3323.33 cop[tikv] table:t, index:ia(a, b) range:[-inf,"\x00A\x00A"), keep order:false, stats:pseudo + └─Selection_11(Probe) 3323.33 cop[tikv] lt(planner__core__range_scan_for_like.t.a, "aa") + └─TableRowIDScan_10 3323.33 cop[tikv] table:t keep order:false, stats:pseudo +select * from t use index (ia) where a < 'aa' order by a,_tidb_rowid; +a b + 234 + 11111 + 66666 + 语 言 3579 + 语言 55555 +A 456 +explain select * from t use index (ia) where a != 'aa' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 6656.67 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t.b +└─Sort_7 6656.67 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexLookUp_12 6656.67 root + ├─IndexFullScan_9(Build) 10000.00 cop[tikv] table:t, index:ia(a, b) keep order:false, stats:pseudo + └─Selection_11(Probe) 6656.67 cop[tikv] ne(planner__core__range_scan_for_like.t.a, "aa") + └─TableRowIDScan_10 10000.00 cop[tikv] table:t keep order:false, stats:pseudo +select * from t use index (ia) where a != 'aa' order by a,_tidb_rowid; +a b + 234 + 11111 + 66666 + 语 言 3579 + 语言 55555 +A 456 +aab 456 +aabB 456 +aABBccdd 890 +你好 111 +测测试 2468 +测测试 99999 +测试 222 +测试 543 +测试Abc 324 +explain select * from t use index (ia) where a != 'aaBbc' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 6656.67 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t.b +└─Sort_7 6656.67 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexLookUp_12 6656.67 root + ├─IndexFullScan_9(Build) 10000.00 cop[tikv] table:t, index:ia(a, b) keep order:false, stats:pseudo + └─Selection_11(Probe) 6656.67 cop[tikv] ne(planner__core__range_scan_for_like.t.a, "aaBbc") + └─TableRowIDScan_10 10000.00 cop[tikv] table:t keep order:false, stats:pseudo +select * from t use index (ia) where a != 'aaBbc' order by a,_tidb_rowid; +a b + 234 + 11111 + 66666 + 语 言 3579 + 语言 55555 +A 456 +Aa 456 +aab 456 +aabB 456 +aABBccdd 890 +你好 111 +测测试 2468 +测测试 99999 +测试 222 +测试 543 +测试Abc 324 +explain select * from t use index (ia) where a like '测试abc' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 10.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t.b +└─Sort_7 10.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexLookUp_12 10.00 root + ├─IndexRangeScan_9(Build) 10.00 cop[tikv] table:t, index:ia(a, b) range:["mK\x8b\xd5\x00A","mK\x8b\xd5\x00A"], keep order:false, stats:pseudo + └─Selection_11(Probe) 10.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, "测试abc", 92) + └─TableRowIDScan_10 10.00 cop[tikv] table:t keep order:false, stats:pseudo +select * from t use index (ia) where a like '测试abc' order by a,_tidb_rowid; +a b +测试Abc 324 +explain select * from t use index (ia) where a = '测试abc' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 10.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t.b +└─Sort_7 10.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexLookUp_12 10.00 root + ├─IndexRangeScan_9(Build) 10.00 cop[tikv] table:t, index:ia(a, b) range:["mK\x8b\xd5\x00A","mK\x8b\xd5\x00A"], keep order:false, stats:pseudo + └─Selection_11(Probe) 10.00 cop[tikv] eq(planner__core__range_scan_for_like.t.a, "测试abc") + └─TableRowIDScan_10 10.00 cop[tikv] table:t keep order:false, stats:pseudo +select * from t use index (ia) where a = '测试abc' order by a,_tidb_rowid; +a b +测试Abc 324 +explain select * from t use index (ia) where a like 'aa' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 10.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t.b +└─Sort_7 10.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexLookUp_12 10.00 root + ├─IndexRangeScan_9(Build) 10.00 cop[tikv] table:t, index:ia(a, b) range:["\x00A\x00A","\x00A\x00A"], keep order:false, stats:pseudo + └─Selection_11(Probe) 10.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, "aa", 92) + └─TableRowIDScan_10 10.00 cop[tikv] table:t keep order:false, stats:pseudo +select * from t use index (ia) where a like 'aa' order by a,_tidb_rowid; +a b +Aa 456 +explain select * from t use index (ia) where a = 'aa' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 10.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t.b +└─Sort_7 10.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexLookUp_12 10.00 root + ├─IndexRangeScan_9(Build) 10.00 cop[tikv] table:t, index:ia(a, b) range:["\x00A\x00A","\x00A\x00A"], keep order:false, stats:pseudo + └─Selection_11(Probe) 10.00 cop[tikv] eq(planner__core__range_scan_for_like.t.a, "aa") + └─TableRowIDScan_10 10.00 cop[tikv] table:t keep order:false, stats:pseudo +select * from t use index (ia) where a = 'aa' order by a,_tidb_rowid; +a b +Aa 456 +explain select * from t use index (ia) where a like '测测试 ' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 10.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t.b +└─Sort_7 10.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexLookUp_12 10.00 root + ├─IndexRangeScan_9(Build) 10.00 cop[tikv] table:t, index:ia(a, b) range:["mKmK\x8b\xd5","mKmK\x8b\xd5"], keep order:false, stats:pseudo + └─Selection_11(Probe) 10.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, "测测试 ", 92) + └─TableRowIDScan_10 10.00 cop[tikv] table:t keep order:false, stats:pseudo +select * from t use index (ia) where a like '测测试 ' order by a,_tidb_rowid; +a b +测测试 2468 +explain select * from t use index (ia) where a = '测测试 ' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 10.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t.b +└─Sort_7 10.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexLookUp_12 10.00 root + ├─IndexRangeScan_9(Build) 10.00 cop[tikv] table:t, index:ia(a, b) range:["mKmK\x8b\xd5","mKmK\x8b\xd5"], keep order:false, stats:pseudo + └─Selection_11(Probe) 10.00 cop[tikv] eq(planner__core__range_scan_for_like.t.a, "测测试 ") + └─TableRowIDScan_10 10.00 cop[tikv] table:t keep order:false, stats:pseudo +select * from t use index (ia) where a = '测测试 ' order by a,_tidb_rowid; +a b +测测试 2468 +测测试 99999 +explain select * from t use index (ia) where a like ' 语 言' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 10.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t.b +└─Sort_7 10.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexLookUp_12 10.00 root + ├─IndexRangeScan_9(Build) 10.00 cop[tikv] table:t, index:ia(a, b) range:["\x00 \x00 \x8b\xed","\x00 \x00 \x8b\xed"], keep order:false, stats:pseudo + └─Selection_11(Probe) 10.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, " 语 言", 92) + └─TableRowIDScan_10 10.00 cop[tikv] table:t keep order:false, stats:pseudo +select * from t use index (ia) where a like ' 语 言' order by a,_tidb_rowid; +a b + 语 言 3579 +explain select * from t use index (ia) where a = ' 语 言' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 10.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t.b +└─Sort_7 10.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexLookUp_12 10.00 root + ├─IndexRangeScan_9(Build) 10.00 cop[tikv] table:t, index:ia(a, b) range:["\x00 \x00 \x8b\xed","\x00 \x00 \x8b\xed"], keep order:false, stats:pseudo + └─Selection_11(Probe) 10.00 cop[tikv] eq(planner__core__range_scan_for_like.t.a, " 语 言") + └─TableRowIDScan_10 10.00 cop[tikv] table:t keep order:false, stats:pseudo +select * from t use index (ia) where a = ' 语 言' order by a,_tidb_rowid; +a b + 语 言 3579 +explain select * from t use index (ia) where a like '测试%' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t.b +└─Sort_7 250.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexLookUp_12 250.00 root + ├─IndexRangeScan_9(Build) 250.00 cop[tikv] table:t, index:ia(a, b) range:["mK\x8b\xd5","mK\x8b\xd6"), keep order:false, stats:pseudo + └─Selection_11(Probe) 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, "测试%", 92) + └─TableRowIDScan_10 250.00 cop[tikv] table:t keep order:false, stats:pseudo +select * from t use index (ia) where a like '测试%' order by a,_tidb_rowid; +a b +测试 222 +测试 543 +测试Abc 324 +explain select * from t use index (ia) where a like '测_' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t.b +└─Sort_7 250.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexLookUp_12 250.00 root + ├─IndexRangeScan_9(Build) 250.00 cop[tikv] table:t, index:ia(a, b) range:["mK","mL"), keep order:false, stats:pseudo + └─Selection_11(Probe) 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, "测_", 92) + └─TableRowIDScan_10 250.00 cop[tikv] table:t keep order:false, stats:pseudo +select * from t use index (ia) where a like '测_' order by a,_tidb_rowid; +a b +测试 222 +explain select * from t use index (ia) where a like '测测试 %' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t.b +└─Sort_7 250.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexLookUp_12 250.00 root + ├─IndexRangeScan_9(Build) 250.00 cop[tikv] table:t, index:ia(a, b) range:["mKmK\x8b\xd5","mKmK\x8b\xd6"), keep order:false, stats:pseudo + └─Selection_11(Probe) 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, "测测试 %", 92) + └─TableRowIDScan_10 250.00 cop[tikv] table:t keep order:false, stats:pseudo +select * from t use index (ia) where a like '测测试 %' order by a,_tidb_rowid; +a b +测测试 2468 +测测试 99999 +explain select * from t use index (ia) where a like '测试a__' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t.b +└─Sort_7 250.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexLookUp_12 250.00 root + ├─IndexRangeScan_9(Build) 250.00 cop[tikv] table:t, index:ia(a, b) range:["mK\x8b\xd5\x00A","mK\x8b\xd5\x00B"), keep order:false, stats:pseudo + └─Selection_11(Probe) 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, "测试a__", 92) + └─TableRowIDScan_10 250.00 cop[tikv] table:t keep order:false, stats:pseudo +select * from t use index (ia) where a like '测试a__' order by a,_tidb_rowid; +a b +测试Abc 324 +explain select * from t use index (ia) where a like '测试 __' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t.b +└─Sort_7 250.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexLookUp_12 250.00 root + ├─IndexRangeScan_9(Build) 250.00 cop[tikv] table:t, index:ia(a, b) range:["mK\x8b\xd5","mK\x8b\xd5\x00!"), keep order:false, stats:pseudo + └─Selection_11(Probe) 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, "测试 __", 92) + └─TableRowIDScan_10 250.00 cop[tikv] table:t keep order:false, stats:pseudo +select * from t use index (ia) where a like '测试 __' order by a,_tidb_rowid; +a b +测试 543 +explain select * from t use index (ia) where a like ' _' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t.b +└─Sort_7 250.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexLookUp_12 250.00 root + ├─IndexRangeScan_9(Build) 250.00 cop[tikv] table:t, index:ia(a, b) range:["","\x00!"), keep order:false, stats:pseudo + └─Selection_11(Probe) 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, " _", 92) + └─TableRowIDScan_10 250.00 cop[tikv] table:t keep order:false, stats:pseudo +select * from t use index (ia) where a like ' _' order by a,_tidb_rowid; +a b +explain select * from t use index (ia) where a like ' %' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t.b +└─Sort_7 250.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexLookUp_12 250.00 root + ├─IndexRangeScan_9(Build) 250.00 cop[tikv] table:t, index:ia(a, b) range:["","\x00 \x00 \x00!"), keep order:false, stats:pseudo + └─Selection_11(Probe) 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, " %", 92) + └─TableRowIDScan_10 250.00 cop[tikv] table:t keep order:false, stats:pseudo +select * from t use index (ia) where a like ' %' order by a,_tidb_rowid; +a b + 66666 +explain select * from t use index (ia) where a like ' 语言%%' order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 250.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t.b +└─Sort_7 250.00 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexLookUp_12 250.00 root + ├─IndexRangeScan_9(Build) 250.00 cop[tikv] table:t, index:ia(a, b) range:["\x00 \x8b\xed\x8a\x00","\x00 \x8b\xed\x8a\x01"), keep order:false, stats:pseudo + └─Selection_11(Probe) 250.00 cop[tikv] like(planner__core__range_scan_for_like.t.a, " 语言%%", 92) + └─TableRowIDScan_10 250.00 cop[tikv] table:t keep order:false, stats:pseudo +select * from t use index (ia) where a like ' 语言%%' order by a,_tidb_rowid; +a b + 语言 55555 +explain select * from t use index (ia) where a not in ('aabc','dd') order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 3583.33 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t.b +└─Sort_7 3583.33 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexLookUp_12 3583.33 root + ├─IndexRangeScan_9(Build) 3583.33 cop[tikv] table:t, index:ia(a, b) range:(NULL,"\x00D\x00D"), ("\x00D\x00D",+inf], keep order:false, stats:pseudo + └─Selection_11(Probe) 3583.33 cop[tikv] not(in(planner__core__range_scan_for_like.t.a, "aabc", "dd")) + └─TableRowIDScan_10 3583.33 cop[tikv] table:t keep order:false, stats:pseudo +select * from t use index (ia) where a not in ('aabc','dd') order by a,_tidb_rowid; +a b + 234 + 11111 + 66666 + 语 言 3579 + 语言 55555 +A 456 +Aa 456 +aab 456 +aabB 456 +aABBccdd 890 +你好 111 +测测试 2468 +测测试 99999 +测试 222 +测试 543 +测试Abc 324 +explain select * from t where a >= 'aabb' and a <= 'aabd' and b = 456 order by a,_tidb_rowid; +id estRows task access object operator info +Projection_6 0.01 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t.b +└─Sort_7 0.01 root planner__core__range_scan_for_like.t.a, planner__core__range_scan_for_like.t._tidb_rowid + └─IndexLookUp_16 0.01 root + ├─Selection_14(Build) 0.01 cop[tikv] eq(planner__core__range_scan_for_like.t.b, 456) + │ └─IndexRangeScan_12 10.00 cop[tikv] table:t, index:ia(a, b) range:["\x00A\x00A\x00B","\x00A\x00A\x00B"], keep order:false, stats:pseudo + └─Selection_15(Probe) 0.01 cop[tikv] ge(planner__core__range_scan_for_like.t.a, "aabb"), le(planner__core__range_scan_for_like.t.a, "aabd") + └─TableRowIDScan_13 0.01 cop[tikv] table:t keep order:false, stats:pseudo +select * from t where a >= 'aabb' and a <= 'aabd' and b = 456 order by a,_tidb_rowid; +a b +aabB 456 diff --git a/tests/integrationtest/t/planner/core/range_scan_for_like.test b/tests/integrationtest/t/planner/core/range_scan_for_like.test new file mode 100644 index 0000000000000..ff60df3f17584 --- /dev/null +++ b/tests/integrationtest/t/planner/core/range_scan_for_like.test @@ -0,0 +1,231 @@ +# Suite 1: utf8mb4_general_ci + normal index +create table t(a varchar(20) collate utf8mb4_general_ci, index ia(a)); +insert into t value('测试'),('测试Abc'),('测试 '),('你好'),('aABBccdd'),('Aa'),(''),(' '),(' '),(' 语言'),(' 语 言 '),('测测试 '),('测测试 '),(NULL); +# test cases for the pattern string cover: +# with/without wildcard +# start/end with wildcard +# [non-]ascii characters +# [only] contain empty string/space +explain select *, length(a) from t use index (ia) where a like '测试%' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like '测试%' order by a,_tidb_rowid; +explain select *, length(a) from t use index (ia) where a like '测%%' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like '测%%' order by a,_tidb_rowid; +explain select *, length(a) from t use index (ia) where a like '测%%试' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like '测%%试' order by a,_tidb_rowid; +explain select *, length(a) from t use index (ia) where a like '测试%%' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like '测试%%' order by a,_tidb_rowid; +explain select *, length(a) from t use index (ia) where a like '测试_' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like '测试_' order by a,_tidb_rowid; +explain select *, length(a) from t use index (ia) where a like '你好%' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like '你好%' order by a,_tidb_rowid; +explain select *, length(a) from t use index (ia) where a like 'aa' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like 'aa' order by a,_tidb_rowid; +explain select *, length(a) from t use index (ia) where a like 'aa%' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like 'aa%' order by a,_tidb_rowid; +explain select *, length(a) from t use index (ia) where a like 'aa%cc' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like 'aa%cc' order by a,_tidb_rowid; +explain select *, length(a) from t use index (ia) where a like '' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like '' order by a,_tidb_rowid; +explain select *, length(a) from t use index (ia) where a like ' ' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like ' ' order by a,_tidb_rowid; +explain select *, length(a) from t use index (ia) where a like 'aa%dd' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like 'aa%dd' order by a,_tidb_rowid; +explain select *, length(a) from t use index (ia) where a like 'aa%%dd' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like 'aa%%dd' order by a,_tidb_rowid; +explain select *, length(a) from t use index (ia) where a like 'aa_bccdd' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like 'aa_bccdd' order by a,_tidb_rowid; +explain select *, length(a) from t use index (ia) where a like '%%' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like '%%' order by a,_tidb_rowid; +explain select *, length(a) from t use index (ia) where a like ' %%' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like ' %%' order by a,_tidb_rowid; +explain select *, length(a) from t use index (ia) where a like ' %%语言' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like ' %%语言' order by a,_tidb_rowid; +explain select *, length(a) from t use index (ia) where a like ' 语 %' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like ' 语 %' order by a,_tidb_rowid; +explain select *, length(a) from t use index (ia) where a like ' 语 _' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like ' 语 _' order by a,_tidb_rowid; +drop table t; +# Suite 2: utf8mb4_unicode_ci + unique index +create table t(a varchar(20) collate utf8mb4_unicode_ci, unique index ia(a)); +insert into t value(''),('测试'),('测试abc'),('你好'),('aabbccdd'),(' 语言'),(' 语 言 '),('测测试 '); +# test cases for the pattern string are the same with Suite 1 +explain select *, length(a) from t use index (ia) where a like '测试%' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like '测试%' order by a,_tidb_rowid; +explain select *, length(a) from t use index (ia) where a like '测%%' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like '测%%' order by a,_tidb_rowid; +explain select *, length(a) from t use index (ia) where a like '测%%试' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like '测%%试' order by a,_tidb_rowid; +explain select *, length(a) from t use index (ia) where a like '测试%%' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like '测试%%' order by a,_tidb_rowid; +explain select *, length(a) from t use index (ia) where a like '测试_' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like '测试_' order by a,_tidb_rowid; +explain select *, length(a) from t use index (ia) where a like '你好%' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like '你好%' order by a,_tidb_rowid; +explain select *, length(a) from t use index (ia) where a like 'aa' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like 'aa' order by a,_tidb_rowid; +explain select *, length(a) from t use index (ia) where a like 'aa%' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like 'aa%' order by a,_tidb_rowid; +explain select *, length(a) from t use index (ia) where a like 'aa%cc' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like 'aa%cc' order by a,_tidb_rowid; +explain select *, length(a) from t use index (ia) where a like '' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like '' order by a,_tidb_rowid; +explain select *, length(a) from t use index (ia) where a like 'aa%dd' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like 'aa%dd' order by a,_tidb_rowid; +explain select *, length(a) from t use index (ia) where a like 'aa%%dd' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like 'aa%%dd' order by a,_tidb_rowid; +explain select *, length(a) from t use index (ia) where a like 'aa_bccdd' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like 'aa_bccdd' order by a,_tidb_rowid; +explain select *, length(a) from t use index (ia) where a like '%%' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like '%%' order by a,_tidb_rowid; +explain select *, length(a) from t use index (ia) where a like ' %%' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like ' %%' order by a,_tidb_rowid; +explain select *, length(a) from t use index (ia) where a like ' %%语言' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like ' %%语言' order by a,_tidb_rowid; +explain select *, length(a) from t use index (ia) where a like ' 语 %' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like ' 语 %' order by a,_tidb_rowid; +explain select *, length(a) from t use index (ia) where a like ' 语 _' order by a,_tidb_rowid; +select *, length(a) from t use index (ia) where a like ' 语 _' order by a,_tidb_rowid; +drop table t; +# Suite 3: utf8mb4_0900_ai_ci + ascii_bin + multi-column index + prefix index + primary key (clustered) +create table t(a varchar(20) collate utf8mb4_0900_ai_ci, b varchar(20) collate ascii_bin, c bigint, primary key(a(1), b) clustered); +insert into t (a, b, c) values +('测试1', 'asdfgh', 345346), +('你好2', 'qqwweerrrr', 987765), +('こんにちは3', 'zxcvbnn', 1111111), +('안녕하세요4', 'asdfgh ', 3333333333), +('Ciao5', ' asdfgh', 444400), +('Hola6', ' asdfgh ', 6666), +('Bonjour ', '', 888888888), +('Olá8', ' ', 9999999), +('Привет9', ' ', 321321), +('Hallo10', '12345', 35678); +explain select * from t use index (primary) where a like '测试%' and b like 'asd%' order by a,b; +select * from t use index (primary) where a like '测试%' and b like 'asd%' order by a,b; +explain select * from t use index (primary) where a like '测试1' and b like 'asdfgh %' order by a,b; +select * from t use index (primary) where a like '测试1' and b like 'asdfgh %' order by a,b; +explain select * from t use index (primary) where a like 'こんにち_' and b like 'zxc%' order by a,b; +select * from t use index (primary) where a like 'こんにち_' and b like 'zxc%' order by a,b; +explain select * from t use index (primary) where a like '안녕하세요%' and b like 'asd%' order by a,b; +select * from t use index (primary) where a like '안녕하세요%' and b like 'asd%' order by a,b; +explain select * from t use index (primary) where a like 'Ciáo%' and b like ' _%' order by a,b; +select * from t use index (primary) where a like 'Ciáo%' and b like ' _%' order by a,b; +explain select * from t use index (primary) where a like '%HoLa%' and b like ' asdfgh' order by a,b; +select * from t use index (primary) where a like '%HoLa%' and b like ' asdfgh' order by a,b; +explain select * from t use index (primary) where a like 'bonjour _%' and b like '' order by a,b; +select * from t use index (primary) where a like 'bonjour _%' and b like '' order by a,b; +explain select * from t use index (primary) where a like 'OLa%' and b like '_' order by a,b; +select * from t use index (primary) where a like 'OLa%' and b like '_' order by a,b; +explain select * from t use index (primary) where a like 'Приве__' and b like ' %' order by a,b; +select * from t use index (primary) where a like 'Приве__' and b like ' %' order by a,b; +explain select * from t use index (primary) where a like 'Hallo%' and b like '123%' order by a,b; +select * from t use index (primary) where a like 'Hallo%' and b like '123%' order by a,b; +drop table t; +# Suite 4: gbk_chinese_ci + latin1_bin + multi-column index + prefix index + primary key (nonclustered) +create table t(a varchar(20) collate gbk_chinese_ci, b varchar(20) collate latin1_bin, c bigint, primary key(a, b(5)) nonclustered); +insert into t (a, b, c) values +('测试1', 'asdfgh', 345346), +('你好2', 'qqwweerrrr', 987765), +('zxcvbnn',0xE38193E38293E381ABE381A1E381AF33, 1111111), +('asdfgh ', 0xEC9588EB8595ED9598EC84B8EC9A9434, 3333333333), +('Ciao5', ' asdfgh', 444400), +(' asdfgh ', 'Hola6', 6666), +('Bonjour ', '', 888888888), +('Olá8', ' ', 9999999), +('Привет9', ' ', 321321), +(' ', '12345', 35678); +set names utf8mb4; +explain select * from t use index (primary) where a like '测试%' and b like 'asd%' order by a,b; +select * from t use index (primary) where a like '测试%' and b like 'asd%' order by a,b; +explain select * from t use index (primary) where a like '测试1' and b like 'asdfgh %' order by a,b; +select * from t use index (primary) where a like '测试1' and b like 'asdfgh %' order by a,b; +set names latin1; +explain select * from t use index (primary) where b like 'こんにち_' and a like 'zxc%' order by a,b; +select * from t use index (primary) where b like 'こんにち_' and a like 'zxc%' order by a,b; +explain select * from t use index (primary) where b like '안녕하세요%' and a like 'asd%' order by a,b; +select * from t use index (primary) where b like '안녕하세요%' and a like 'asd%' order by a,b; +set names utf8mb4; +explain select * from t use index (primary) where a like 'Ciao%' and b like ' _%' order by a,b; +select * from t use index (primary) where a like 'Ciao%' and b like ' _%' order by a,b; +explain select * from t use index (primary) where b like 'HoLa%' and a like ' asdfgh' order by a,b; +select * from t use index (primary) where b like 'HoLa%' and a like ' asdfgh' order by a,b; +explain select * from t use index (primary) where a like 'bonjour _%' and b like '' order by a,b; +select * from t use index (primary) where a like 'bonjour _%' and b like '' order by a,b; +explain select * from t use index (primary) where a like 'OLá' and b like '_' order by a,b; +select * from t use index (primary) where a like 'OLá' and b like '_' order by a,b; +explain select * from t use index (primary) where a like 'Приве__' and b like ' %' order by a,b; +select * from t use index (primary) where a like 'Приве__' and b like ' %' order by a,b; +explain select * from t use index (primary) where a like ' %' and b like '123%' order by a,b; +select * from t use index (primary) where a like ' %' and b like '123%' order by a,b; +drop table t; +# Suite 5: utf8mb4_general_ci + prefix index +create table t(a varchar(20) collate utf8mb4_general_ci, b bigint, index ia(a(3),b)); +insert into t value +('测试',222), +('测试Abc',324), +('测试 ',543), +('你好',111), +('aABBccdd',890), +('A',456), +('Aa',456), +('aab',456), +('aabB',456), +('',234), +(' ',11111), +(' ',66666), +(' 语言',55555), +(' 语 言',3579), +('测测试 ',2468), +('测测试 ',99999), +(NULL,10); +explain select * from t use index (ia) where a > 'aabb' order by a,_tidb_rowid; +select * from t use index (ia) where a > 'aabb' order by a,_tidb_rowid; +explain select * from t use index (ia) where a > 'aab' order by a,_tidb_rowid; +select * from t use index (ia) where a > 'aab' order by a,_tidb_rowid; +explain select * from t use index (ia) where a > 'aa' order by a,_tidb_rowid; +select * from t use index (ia) where a > 'aa' order by a,_tidb_rowid; +explain select * from t use index (ia) where a < 'aabb' order by a,_tidb_rowid; +select * from t use index (ia) where a < 'aabb' order by a,_tidb_rowid; +explain select * from t use index (ia) where a < 'aab' order by a,_tidb_rowid; +select * from t use index (ia) where a < 'aab' order by a,_tidb_rowid; +explain select * from t use index (ia) where a < 'aa' order by a,_tidb_rowid; +select * from t use index (ia) where a < 'aa' order by a,_tidb_rowid; +explain select * from t use index (ia) where a != 'aa' order by a,_tidb_rowid; +select * from t use index (ia) where a != 'aa' order by a,_tidb_rowid; +explain select * from t use index (ia) where a != 'aaBbc' order by a,_tidb_rowid; +select * from t use index (ia) where a != 'aaBbc' order by a,_tidb_rowid; +explain select * from t use index (ia) where a like '测试abc' order by a,_tidb_rowid; +select * from t use index (ia) where a like '测试abc' order by a,_tidb_rowid; +explain select * from t use index (ia) where a = '测试abc' order by a,_tidb_rowid; +select * from t use index (ia) where a = '测试abc' order by a,_tidb_rowid; +explain select * from t use index (ia) where a like 'aa' order by a,_tidb_rowid; +select * from t use index (ia) where a like 'aa' order by a,_tidb_rowid; +explain select * from t use index (ia) where a = 'aa' order by a,_tidb_rowid; +select * from t use index (ia) where a = 'aa' order by a,_tidb_rowid; +explain select * from t use index (ia) where a like '测测试 ' order by a,_tidb_rowid; +select * from t use index (ia) where a like '测测试 ' order by a,_tidb_rowid; +explain select * from t use index (ia) where a = '测测试 ' order by a,_tidb_rowid; +select * from t use index (ia) where a = '测测试 ' order by a,_tidb_rowid; +explain select * from t use index (ia) where a like ' 语 言' order by a,_tidb_rowid; +select * from t use index (ia) where a like ' 语 言' order by a,_tidb_rowid; +explain select * from t use index (ia) where a = ' 语 言' order by a,_tidb_rowid; +select * from t use index (ia) where a = ' 语 言' order by a,_tidb_rowid; +explain select * from t use index (ia) where a like '测试%' order by a,_tidb_rowid; +select * from t use index (ia) where a like '测试%' order by a,_tidb_rowid; +explain select * from t use index (ia) where a like '测_' order by a,_tidb_rowid; +select * from t use index (ia) where a like '测_' order by a,_tidb_rowid; +explain select * from t use index (ia) where a like '测测试 %' order by a,_tidb_rowid; +select * from t use index (ia) where a like '测测试 %' order by a,_tidb_rowid; +explain select * from t use index (ia) where a like '测试a__' order by a,_tidb_rowid; +select * from t use index (ia) where a like '测试a__' order by a,_tidb_rowid; +explain select * from t use index (ia) where a like '测试 __' order by a,_tidb_rowid; +select * from t use index (ia) where a like '测试 __' order by a,_tidb_rowid; +explain select * from t use index (ia) where a like ' _' order by a,_tidb_rowid; +select * from t use index (ia) where a like ' _' order by a,_tidb_rowid; +explain select * from t use index (ia) where a like ' %' order by a,_tidb_rowid; +select * from t use index (ia) where a like ' %' order by a,_tidb_rowid; +explain select * from t use index (ia) where a like ' 语言%%' order by a,_tidb_rowid; +select * from t use index (ia) where a like ' 语言%%' order by a,_tidb_rowid; +explain select * from t use index (ia) where a not in ('aabc','dd') order by a,_tidb_rowid; +select * from t use index (ia) where a not in ('aabc','dd') order by a,_tidb_rowid; +explain select * from t where a >= 'aabb' and a <= 'aabd' and b = 456 order by a,_tidb_rowid; +select * from t where a >= 'aabb' and a <= 'aabd' and b = 456 order by a,_tidb_rowid;