From 0a1d0c55c365bdbaff2ab3c7e893eb3ab83929bd Mon Sep 17 00:00:00 2001 From: Yuanjia Zhang Date: Fri, 3 Feb 2023 12:25:55 +0800 Subject: [PATCH] planner: not allow the optimizer to use `json_contains(j, '[]')` as access conditions on MVIndex (#41013) ref pingcap/tidb#40191 --- planner/core/indexmerge_path.go | 4 +-- planner/core/indexmerge_path_test.go | 38 ++++++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/planner/core/indexmerge_path.go b/planner/core/indexmerge_path.go index 27500e1d8d816..ab67a1634f1f9 100644 --- a/planner/core/indexmerge_path.go +++ b/planner/core/indexmerge_path.go @@ -668,7 +668,7 @@ func (ds *DataSource) buildPartialPaths4MVIndex(accessFilters []expression.Expre case ast.JSONContains: // (json_contains(a->'$.zip', '[1, 2, 3]') isIntersection = true virColVals, ok = jsonArrayExpr2Exprs(ds.ctx, sf.GetArgs()[1], jsonType) - if !ok { + if !ok || len(virColVals) == 0 { // json_contains(JSON, '[]') is TRUE return nil, false, false, nil } case ast.JSONOverlaps: // (json_overlaps(a->'$.zip', '[1, 2, 3]') @@ -682,7 +682,7 @@ func (ds *DataSource) buildPartialPaths4MVIndex(accessFilters []expression.Expre } var ok bool virColVals, ok = jsonArrayExpr2Exprs(ds.ctx, sf.GetArgs()[1-jsonPathIdx], jsonType) - if !ok { + if !ok || len(virColVals) == 0 { // forbid empty array for safety return nil, false, false, nil } default: diff --git a/planner/core/indexmerge_path_test.go b/planner/core/indexmerge_path_test.go index b1487249b0902..f5a02cd7ca513 100644 --- a/planner/core/indexmerge_path_test.go +++ b/planner/core/indexmerge_path_test.go @@ -302,6 +302,31 @@ func TestMVIndexFullScan(t *testing.T) { tk.MustGetErrMsg(`select /*+ use_index(t, kj) */ count(*) from t`, "[planner:1815]Internal : Can't find a proper physical plan for this query") } +func TestMVIndexEmptyArray(t *testing.T) { + store := testkit.CreateMockStore(t) + tk := testkit.NewTestKit(t, store) + tk.MustExec("use test") + tk.MustExec(`create table t(j json, index kj((cast(j as signed array))))`) + tk.MustExec(`insert into t values ('[1]')`) + tk.MustExec(`insert into t values ('[1, 2]')`) + tk.MustExec(`insert into t values ('[]')`) + tk.MustExec(`insert into t values (NULL)`) + + for _, cond := range []string{ + "json_contains(j, '[]')", + "json_contains(j, '[1]')", + "json_contains(j, '[1, 2]')", + "json_contains(j, '[1, 10]')", + "json_overlaps(j, '[]')", + "json_overlaps(j, '[1]')", + "json_overlaps(j, '[1, 2]')", + "json_overlaps(j, '[1, 10]')", + } { + tk.MustQuery(fmt.Sprintf("select /*+ use_index_merge(t) */ * from t where %v", cond)).Sort().Check( + tk.MustQuery(fmt.Sprintf("select /*+ ignore_index(t, kj) */ * from t where %v", cond)).Sort().Rows()) + } +} + func TestMVIndexRandom(t *testing.T) { store := testkit.CreateMockStore(t) tk := testkit.NewTestKit(t, store) @@ -364,14 +389,23 @@ func randMVIndexCond(condType int, valOpts randMVIndexValOpts) string { case 0: // member_of return fmt.Sprintf(`(%v member of (j))`, randMVIndexValue(valOpts)) case 1: // json_contains - return fmt.Sprintf(`json_contains(j, '[%v, %v]')`, randMVIndexValue(valOpts), randMVIndexValue(valOpts)) + return fmt.Sprintf(`json_contains(j, '%v')`, randArray(valOpts)) case 2: // json_overlaps - return fmt.Sprintf(`json_overlaps(j, '[%v, %v]')`, randMVIndexValue(valOpts), randMVIndexValue(valOpts)) + return fmt.Sprintf(`json_overlaps(j, '%v')`, randArray(valOpts)) default: // others return fmt.Sprintf(`a < %v`, rand.Intn(valOpts.distinct)) } } +func randArray(opts randMVIndexValOpts) string { + n := rand.Intn(5) // n can be 0 + var vals []string + for i := 0; i < n; i++ { + vals = append(vals, randMVIndexValue(opts)) + } + return "[" + strings.Join(vals, ", ") + "]" +} + type randMVIndexValOpts struct { valType string // INT, UNSIGNED, STR, DATE maxStrLen int