diff --git a/planner/core/indexmerge_path.go b/planner/core/indexmerge_path.go index cf2b83c3ebe20..1b384aef1fd02 100644 --- a/planner/core/indexmerge_path.go +++ b/planner/core/indexmerge_path.go @@ -46,7 +46,9 @@ func (ds *DataSource) generateIndexMergePath() error { } stmtCtx := ds.ctx.GetSessionVars().StmtCtx - isPossibleIdxMerge := len(indexMergeConds) > 0 && len(ds.possibleAccessPaths) > 1 + isPossibleIdxMerge := len(indexMergeConds) > 0 && // have corresponding access conditions, and + (len(ds.possibleAccessPaths) > 1 || // (have multiple index paths, or + (len(ds.possibleAccessPaths) == 1 && isMVIndexPath(ds.possibleAccessPaths[0]))) // have a MVIndex) sessionAndStmtPermission := (ds.ctx.GetSessionVars().GetEnableIndexMerge() || len(ds.indexMergeHints) > 0) && !stmtCtx.NoIndexMergeHint // We current do not consider `IndexMergePath`: // 1. If there is an index path. @@ -505,7 +507,7 @@ func (ds *DataSource) generateAndPruneIndexMergePath(indexMergeConds []expressio */ func (ds *DataSource) generateIndexMergeOnDNF4MVIndex(normalPathCnt int, filters []expression.Expression) (mvIndexPaths []*util.AccessPath, err error) { for idx := 0; idx < normalPathCnt; idx++ { - if ds.possibleAccessPaths[idx].IsTablePath() || ds.possibleAccessPaths[idx].Index == nil || !ds.possibleAccessPaths[idx].Index.MVIndex { + if !isMVIndexPath(ds.possibleAccessPaths[idx]) { continue // not a MVIndex path } @@ -587,7 +589,7 @@ func (ds *DataSource) generateIndexMerge4MVIndex(normalPathCnt int, filters []ex mvIndexPaths = append(mvIndexPaths, dnfMVIndexPaths...) for idx := 0; idx < normalPathCnt; idx++ { - if ds.possibleAccessPaths[idx].IsTablePath() || ds.possibleAccessPaths[idx].Index == nil || !ds.possibleAccessPaths[idx].Index.MVIndex { + if !isMVIndexPath(ds.possibleAccessPaths[idx]) { continue // not a MVIndex path } @@ -894,3 +896,7 @@ func unwrapJSONCast(expr expression.Expression) (expression.Expression, bool) { } return sf.GetArgs()[0], true } + +func isMVIndexPath(path *util.AccessPath) bool { + return !path.IsTablePath() && path.Index != nil && path.Index.MVIndex +} diff --git a/planner/core/testdata/index_merge_suite_in.json b/planner/core/testdata/index_merge_suite_in.json index 2883e51c29736..8f664b6d312b5 100644 --- a/planner/core/testdata/index_merge_suite_in.json +++ b/planner/core/testdata/index_merge_suite_in.json @@ -6,6 +6,12 @@ "select /*+ use_index_merge(t, j0_1) */ * from t where (1 member of (j0->'$.path1')) and a<10", "select /*+ use_index_merge(t, j0_1) */ * from t where (1 member of (j0->'$.XXX')) and a<10", "select /*+ use_index_merge(t, j0_1) */ * from t where (1 member of (j0->'$.path1')) and (2 member of (j1)) and a<10", + "select /*+ use_index(t, j0_0) */ * from t where (1 member of (j0->'$.path0'))", + "select /*+ use_index(t, j0_1) */ * from t where (1 member of (j0->'$.path1')) and a<10", + "select * from t use index(j0_0) where (1 member of (j0->'$.path0'))", + "select * from t use index(j0_1) where (1 member of (j0->'$.path1')) and a<10", + "select * from t force index(j0_0) where (1 member of (j0->'$.path0'))", + "select * from t force index(j0_1) where (1 member of (j0->'$.path1')) and a<10", "select /*+ use_index_merge(t, j1) */ * from t where (1 member of (j0->'$.path1')) and (2 member of (j1)) and a<10", "select /*+ use_index_merge(t, j0_0) */ * from t where json_contains((j0->'$.path0'), '[1, 2, 3]')", "select /*+ use_index_merge(t, j0_0) */ * from t where json_overlaps((j0->'$.path0'), '[1, 2, 3]')", @@ -55,7 +61,13 @@ "select /*+ use_index_merge(t, idx2) */ * from t where a=1 and b=2 and ('3' member of (j->'$.str')) and c=4", "select /*+ use_index_merge(t, idx2) */ * from t where a=1 and b=2 and ('3' member of (j->'$.str'))", "select /*+ use_index_merge(t, idx2) */ * from t where a=1 and b=2", - "select /*+ use_index_merge(t, idx2) */ * from t where a=1" + "select /*+ use_index_merge(t, idx2) */ * from t where a=1", + "select /*+ use_index(t, idx) */ * from t where a=1 and b=2 and (3 member of (j)) and c=4", + "select * from t use index(idx) where a=1 and b=2 and (3 member of (j))", + "select /*+ use_index(t, idx) */ * from t where a=1 and b=2", + "select * from t use index(idx) where a=1", + "select * from t force index(idx) where a=1 and b=2 and (3 member of (j))", + "select * from t force index(idx) where a=1" ] }, { diff --git a/planner/core/testdata/index_merge_suite_out.json b/planner/core/testdata/index_merge_suite_out.json index ffabf6f66b7d3..3f83d636afac4 100644 --- a/planner/core/testdata/index_merge_suite_out.json +++ b/planner/core/testdata/index_merge_suite_out.json @@ -40,6 +40,63 @@ " └─TableRowIDScan 10.00 cop[tikv] table:t keep order:false, stats:pseudo" ] }, + { + "SQL": "select /*+ use_index(t, j0_0) */ * from t where (1 member of (j0->'$.path0'))", + "Plan": [ + "Selection 8000.00 root json_memberof(cast(1, json BINARY), json_extract(test.t.j0, \"$.path0\"))", + "└─IndexMerge 10.00 root type: union", + " ├─IndexRangeScan(Build) 10.00 cop[tikv] table:t, index:j0_0(cast(json_extract(`j0`, _utf8mb4'$.path0') as signed array)) range:[1,1], keep order:false, stats:pseudo", + " └─TableRowIDScan(Probe) 10.00 cop[tikv] table:t keep order:false, stats:pseudo" + ] + }, + { + "SQL": "select /*+ use_index(t, j0_1) */ * from t where (1 member of (j0->'$.path1')) and a<10", + "Plan": [ + "Selection 2658.67 root json_memberof(cast(1, json BINARY), json_extract(test.t.j0, \"$.path1\"))", + "└─IndexMerge 3.32 root type: union", + " ├─IndexRangeScan(Build) 10.00 cop[tikv] table:t, index:j0_1(cast(json_extract(`j0`, _utf8mb4'$.path1') as signed array)) range:[1,1], keep order:false, stats:pseudo", + " └─Selection(Probe) 3.32 cop[tikv] lt(test.t.a, 10)", + " └─TableRowIDScan 10.00 cop[tikv] table:t keep order:false, stats:pseudo" + ] + }, + { + "SQL": "select * from t use index(j0_0) where (1 member of (j0->'$.path0'))", + "Plan": [ + "Selection 8000.00 root json_memberof(cast(1, json BINARY), json_extract(test.t.j0, \"$.path0\"))", + "└─IndexMerge 10.00 root type: union", + " ├─IndexRangeScan(Build) 10.00 cop[tikv] table:t, index:j0_0(cast(json_extract(`j0`, _utf8mb4'$.path0') as signed array)) range:[1,1], keep order:false, stats:pseudo", + " └─TableRowIDScan(Probe) 10.00 cop[tikv] table:t keep order:false, stats:pseudo" + ] + }, + { + "SQL": "select * from t use index(j0_1) where (1 member of (j0->'$.path1')) and a<10", + "Plan": [ + "Selection 2658.67 root json_memberof(cast(1, json BINARY), json_extract(test.t.j0, \"$.path1\"))", + "└─IndexMerge 3.32 root type: union", + " ├─IndexRangeScan(Build) 10.00 cop[tikv] table:t, index:j0_1(cast(json_extract(`j0`, _utf8mb4'$.path1') as signed array)) range:[1,1], keep order:false, stats:pseudo", + " └─Selection(Probe) 3.32 cop[tikv] lt(test.t.a, 10)", + " └─TableRowIDScan 10.00 cop[tikv] table:t keep order:false, stats:pseudo" + ] + }, + { + "SQL": "select * from t force index(j0_0) where (1 member of (j0->'$.path0'))", + "Plan": [ + "Selection 8000.00 root json_memberof(cast(1, json BINARY), json_extract(test.t.j0, \"$.path0\"))", + "└─IndexMerge 10.00 root type: union", + " ├─IndexRangeScan(Build) 10.00 cop[tikv] table:t, index:j0_0(cast(json_extract(`j0`, _utf8mb4'$.path0') as signed array)) range:[1,1], keep order:false, stats:pseudo", + " └─TableRowIDScan(Probe) 10.00 cop[tikv] table:t keep order:false, stats:pseudo" + ] + }, + { + "SQL": "select * from t force index(j0_1) where (1 member of (j0->'$.path1')) and a<10", + "Plan": [ + "Selection 2658.67 root json_memberof(cast(1, json BINARY), json_extract(test.t.j0, \"$.path1\"))", + "└─IndexMerge 3.32 root type: union", + " ├─IndexRangeScan(Build) 10.00 cop[tikv] table:t, index:j0_1(cast(json_extract(`j0`, _utf8mb4'$.path1') as signed array)) range:[1,1], keep order:false, stats:pseudo", + " └─Selection(Probe) 3.32 cop[tikv] lt(test.t.a, 10)", + " └─TableRowIDScan 10.00 cop[tikv] table:t keep order:false, stats:pseudo" + ] + }, { "SQL": "select /*+ use_index_merge(t, j1) */ * from t where (1 member of (j0->'$.path1')) and (2 member of (j1)) and a<10", "Plan": [ @@ -496,6 +553,57 @@ "├─IndexRangeScan(Build) 10.00 cop[tikv] table:t, index:idx(a, b, cast(`j` as signed array), c) range:[1,1], keep order:false, stats:pseudo", "└─TableRowIDScan(Probe) 10.00 cop[tikv] table:t keep order:false, stats:pseudo" ] + }, + { + "SQL": "select /*+ use_index(t, idx) */ * from t where a=1 and b=2 and (3 member of (j)) and c=4", + "Plan": [ + "Selection 0.00 root json_memberof(cast(3, json BINARY), test.t.j)", + "└─IndexMerge 0.00 root type: union", + " ├─IndexRangeScan(Build) 0.00 cop[tikv] table:t, index:idx(a, b, cast(`j` as signed array), c) range:[1 2 3 4,1 2 3 4], keep order:false, stats:pseudo", + " └─TableRowIDScan(Probe) 0.00 cop[tikv] table:t keep order:false, stats:pseudo" + ] + }, + { + "SQL": "select * from t use index(idx) where a=1 and b=2 and (3 member of (j))", + "Plan": [ + "Selection 0.08 root json_memberof(cast(3, json BINARY), test.t.j)", + "└─IndexMerge 0.00 root type: union", + " ├─IndexRangeScan(Build) 0.00 cop[tikv] table:t, index:idx(a, b, cast(`j` as signed array), c) range:[1 2 3,1 2 3], keep order:false, stats:pseudo", + " └─TableRowIDScan(Probe) 0.00 cop[tikv] table:t keep order:false, stats:pseudo" + ] + }, + { + "SQL": "select /*+ use_index(t, idx) */ * from t where a=1 and b=2", + "Plan": [ + "IndexMerge 0.10 root type: union", + "├─IndexRangeScan(Build) 0.10 cop[tikv] table:t, index:idx(a, b, cast(`j` as signed array), c) range:[1 2,1 2], keep order:false, stats:pseudo", + "└─TableRowIDScan(Probe) 0.10 cop[tikv] table:t keep order:false, stats:pseudo" + ] + }, + { + "SQL": "select * from t use index(idx) where a=1", + "Plan": [ + "IndexMerge 10.00 root type: union", + "├─IndexRangeScan(Build) 10.00 cop[tikv] table:t, index:idx(a, b, cast(`j` as signed array), c) range:[1,1], keep order:false, stats:pseudo", + "└─TableRowIDScan(Probe) 10.00 cop[tikv] table:t keep order:false, stats:pseudo" + ] + }, + { + "SQL": "select * from t force index(idx) where a=1 and b=2 and (3 member of (j))", + "Plan": [ + "Selection 0.08 root json_memberof(cast(3, json BINARY), test.t.j)", + "└─IndexMerge 0.00 root type: union", + " ├─IndexRangeScan(Build) 0.00 cop[tikv] table:t, index:idx(a, b, cast(`j` as signed array), c) range:[1 2 3,1 2 3], keep order:false, stats:pseudo", + " └─TableRowIDScan(Probe) 0.00 cop[tikv] table:t keep order:false, stats:pseudo" + ] + }, + { + "SQL": "select * from t force index(idx) where a=1", + "Plan": [ + "IndexMerge 10.00 root type: union", + "├─IndexRangeScan(Build) 10.00 cop[tikv] table:t, index:idx(a, b, cast(`j` as signed array), c) range:[1,1], keep order:false, stats:pseudo", + "└─TableRowIDScan(Probe) 10.00 cop[tikv] table:t keep order:false, stats:pseudo" + ] } ] },