Skip to content

Commit

Permalink
planner: forbid using IndexFullScan on multi-valued indexes (#40907)
Browse files Browse the repository at this point in the history
ref #40191
  • Loading branch information
qw4990 authored Jan 31, 2023
1 parent 00617c9 commit 6a18630
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 33 deletions.
3 changes: 1 addition & 2 deletions planner/core/indexmerge_path.go
Original file line number Diff line number Diff line change
Expand Up @@ -587,8 +587,7 @@ func (ds *DataSource) generateIndexMerge4MVIndex(normalPathCnt int, filters []ex
}

accessFilters, remainingFilters := ds.collectFilters4MVIndex(filters, idxCols)
if len(accessFilters) == 0 && // cannot use any filter on this MVIndex
!ds.possibleAccessPaths[idx].Forced { // whether this index is forced by use-index hint
if len(accessFilters) == 0 { // cannot use any filter on this MVIndex
continue
}

Expand Down
48 changes: 40 additions & 8 deletions planner/core/indexmerge_path_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package core_test

import (
"context"
"fmt"
"math/rand"
"strings"
Expand Down Expand Up @@ -224,6 +225,7 @@ func TestEnforceMVIndex(t *testing.T) {
var output []struct {
SQL string
Plan []string
Err string
}
planSuiteData := core.GetIndexMergeSuiteData()
planSuiteData.LoadTestCases(t, &input, &output)
Expand All @@ -232,11 +234,21 @@ func TestEnforceMVIndex(t *testing.T) {
testdata.OnRecord(func() {
output[i].SQL = query
})
result := tk.MustQuery("explain format = 'brief' " + query)
testdata.OnRecord(func() {
output[i].Plan = testdata.ConvertRowsToStrings(result.Rows())
})
result.Check(testkit.Rows(output[i].Plan...))
rs, err := tk.Exec("explain format = 'brief' " + query)
if err != nil {
testdata.OnRecord(func() {
output[i].Err = err.Error()
output[i].Plan = nil
})
require.Equal(t, output[i].Err, err.Error())
} else {
result := tk.ResultSetToResultWithCtx(context.Background(), rs, "")
testdata.OnRecord(func() {
output[i].Err = ""
output[i].Plan = testdata.ConvertRowsToStrings(result.Rows())
})
result.Check(testkit.Rows(output[i].Plan...))
}
}
}

Expand Down Expand Up @@ -270,6 +282,26 @@ func TestMVIndexInvisible(t *testing.T) {
` └─TableRowIDScan(Probe) 10.00 cop[tikv] table:t keep order:false, stats:pseudo`))
}

func TestMVIndexFullScan(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)`)

tk.MustQuery(`select /*+ use_index_merge(t, kj) */ count(*) from t`).Check(testkit.Rows("4"))
tk.MustQuery(`select /*+ use_index_merge(t, kj) */ count(*) from t where (1 member of (j))`).Check(testkit.Rows("2"))
tk.MustQuery(`select /*+ use_index_merge(t, kj) */ count(*) from t where json_contains((j), '[1]')`).Check(testkit.Rows("2"))
tk.MustQuery(`select /*+ use_index_merge(t, kj) */ count(*) from t where json_overlaps((j), '[1]')`).Check(testkit.Rows("2"))

// Forbid IndexMerge+IndexFullScan since IndexFullScan on MVIndex cannot read all rows some cases.
tk.MustGetErrMsg(`select /*+ use_index(t, kj) */ count(*) from t`, "[planner:1815]Internal : Can't find a proper physical plan for this query")
}

func TestMVIndexRandom(t *testing.T) {
store := testkit.CreateMockStore(t)
tk := testkit.NewTestKit(t, store)
Expand All @@ -284,8 +316,8 @@ func TestMVIndexRandom(t *testing.T) {
{"unsigned", randMVIndexValOpts{"unsigned", 0, 3}, randMVIndexValOpts{"unsigned", 0, 3}}, // unsigned-index + unsigned-values
{"char(3)", randMVIndexValOpts{"string", 3, 3}, randMVIndexValOpts{"string", 3, 3}},
{"char(3)", randMVIndexValOpts{"string", 3, 3}, randMVIndexValOpts{"string", 1, 3}},
//{"char(3)", randMVIndexValOpts{"string", 3, 3}, randMVIndexValOpts{"string", 5, 3}},
//{"date", randMVIndexValOpts{"date", 0, 3}, randMVIndexValOpts{"date", 0, 3}},
{"char(3)", randMVIndexValOpts{"string", 3, 3}, randMVIndexValOpts{"string", 5, 3}},
{"date", randMVIndexValOpts{"date", 0, 3}, randMVIndexValOpts{"date", 0, 3}},
} {
tk.MustExec("drop table if exists t")
tk.MustExec(fmt.Sprintf(`create table t(a int, j json, index kj((cast(j as %v array))))`, testCase.indexType))
Expand All @@ -306,7 +338,7 @@ func TestMVIndexRandom(t *testing.T) {
for i := 0; i < nQueries; i++ {
conds := randMVIndexConds(rand.Intn(3)+1, testCase.queryValsOpts)
r1 := tk.MustQuery("select /*+ ignore_index(t, kj) */ * from t where " + conds).Sort()
tk.MustQuery("select /*+ use_index(t, kj) */ * from t where " + conds).Sort().Check(r1.Rows())
tk.MustQuery("select /*+ use_index_merge(t, kj) */ * from t where " + conds).Sort().Check(r1.Rows())
}
}
}
Expand Down
6 changes: 5 additions & 1 deletion planner/core/testdata/index_merge_suite_in.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
"select /*+ use_index(t, kj) */ * from t where a<10",
"select /*+ use_index(t, kj) */ * from t where (1 member of (j))",
"select /*+ use_index(t, kj) */ * from t where (1 member of (j)) and a=10",
"select /*+ use_index(t, kj) */ * from t where (1 member of (j)) or a=10"
"select /*+ use_index(t, kj) */ * from t where (1 member of (j)) or a=10",
"select /*+ use_index_merge(t, kj) */ * from t",
"select /*+ use_index_merge(t, kj) */ a from t",
"select /*+ use_index_merge(t, kj) */ * from t where a<10",
"select /*+ use_index_merge(t, kj) */ * from t where (1 member of (j)) or a=10"
]
},
{
Expand Down
66 changes: 44 additions & 22 deletions planner/core/testdata/index_merge_suite_out.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,28 +4,18 @@
"Cases": [
{
"SQL": "select /*+ use_index(t, kj) */ * from t",
"Plan": [
"IndexMerge 10000.00 root type: union",
"├─IndexFullScan(Build) 10000.00 cop[tikv] table:t, index:kj(cast(`j` as signed array)) keep order:false, stats:pseudo",
"└─TableRowIDScan(Probe) 10000.00 cop[tikv] table:t keep order:false, stats:pseudo"
]
"Plan": null,
"Err": "[planner:1815]Internal : Can't find a proper physical plan for this query"
},
{
"SQL": "select /*+ use_index(t, kj) */ a from t",
"Plan": [
"IndexMerge 10000.00 root type: union",
"├─IndexFullScan(Build) 10000.00 cop[tikv] table:t, index:kj(cast(`j` as signed array)) keep order:false, stats:pseudo",
"└─TableRowIDScan(Probe) 10000.00 cop[tikv] table:t keep order:false, stats:pseudo"
]
"Plan": null,
"Err": "[planner:1815]Internal : Can't find a proper physical plan for this query"
},
{
"SQL": "select /*+ use_index(t, kj) */ * from t where a<10",
"Plan": [
"IndexMerge 3323.33 root type: union",
"├─IndexFullScan(Build) 10000.00 cop[tikv] table:t, index:kj(cast(`j` as signed array)) keep order:false, stats:pseudo",
"└─Selection(Probe) 3323.33 cop[tikv] lt(test.t.a, 10)",
" └─TableRowIDScan 10000.00 cop[tikv] table:t keep order:false, stats:pseudo"
]
"Plan": null,
"Err": "[planner:1815]Internal : Can't find a proper physical plan for this query"
},
{
"SQL": "select /*+ use_index(t, kj) */ * from t where (1 member of (j))",
Expand All @@ -34,7 +24,8 @@
"└─IndexMerge 10.00 root type: union",
" ├─IndexRangeScan(Build) 10.00 cop[tikv] table:t, index:kj(cast(`j` as signed array)) range:[1,1], keep order:false, stats:pseudo",
" └─TableRowIDScan(Probe) 10.00 cop[tikv] table:t keep order:false, stats:pseudo"
]
],
"Err": ""
},
{
"SQL": "select /*+ use_index(t, kj) */ * from t where (1 member of (j)) and a=10",
Expand All @@ -44,16 +35,47 @@
" ├─IndexRangeScan(Build) 10.00 cop[tikv] table:t, index:kj(cast(`j` as signed array)) range:[1,1], keep order:false, stats:pseudo",
" └─Selection(Probe) 0.01 cop[tikv] eq(test.t.a, 10)",
" └─TableRowIDScan 10.00 cop[tikv] table:t keep order:false, stats:pseudo"
]
],
"Err": ""
},
{
"SQL": "select /*+ use_index(t, kj) */ * from t where (1 member of (j)) or a=10",
"Plan": null,
"Err": "[planner:1815]Internal : Can't find a proper physical plan for this query"
},
{
"SQL": "select /*+ use_index_merge(t, kj) */ * from t",
"Plan": [
"TableReader 10000.00 root data:TableFullScan",
"└─TableFullScan 10000.00 cop[tikv] table:t keep order:false, stats:pseudo"
],
"Err": ""
},
{
"SQL": "select /*+ use_index_merge(t, kj) */ a from t",
"Plan": [
"TableReader 10000.00 root data:TableFullScan",
"└─TableFullScan 10000.00 cop[tikv] table:t keep order:false, stats:pseudo"
],
"Err": ""
},
{
"SQL": "select /*+ use_index_merge(t, kj) */ * from t where a<10",
"Plan": [
"TableReader 3323.33 root data:Selection",
"└─Selection 3323.33 cop[tikv] lt(test.t.a, 10)",
" └─TableFullScan 10000.00 cop[tikv] table:t keep order:false, stats:pseudo"
],
"Err": ""
},
{
"SQL": "select /*+ use_index_merge(t, kj) */ * from t where (1 member of (j)) or a=10",
"Plan": [
"Selection 8000.00 root or(json_memberof(cast(1, json BINARY), test.t.j), eq(test.t.a, 10))",
"└─IndexMerge 10000.00 root type: union",
" ├─IndexFullScan(Build) 10000.00 cop[tikv] table:t, index:kj(cast(`j` as signed array)) keep order:false, stats:pseudo",
" └─TableRowIDScan(Probe) 10000.00 cop[tikv] table:t keep order:false, stats:pseudo"
]
"└─TableReader 10000.00 root data:TableFullScan",
" └─TableFullScan 10000.00 cop[tikv] table:t keep order:false, stats:pseudo"
],
"Err": ""
}
]
},
Expand Down

0 comments on commit 6a18630

Please sign in to comment.