diff --git a/pkg/planner/core/indexmerge_path.go b/pkg/planner/core/indexmerge_path.go index cc6edcca63256..8faad0306514b 100644 --- a/pkg/planner/core/indexmerge_path.go +++ b/pkg/planner/core/indexmerge_path.go @@ -1052,7 +1052,7 @@ func collectFilters4MVIndex( usedAsAccess[i] = true found = true // access filter type on mv col overrides normal col for the return value of this function - if accessTp == unspecifiedFilterTp || accessTp == eqOnNonMVColTp { + if accessTp == unspecifiedFilterTp || accessTp == eqOrInOnNonMVColTp { accessTp = tp } break @@ -1207,7 +1207,7 @@ func indexMergeContainSpecificIndex(path *util.AccessPath, indexSet map[int64]st const ( unspecifiedFilterTp int = iota - eqOnNonMVColTp + eqOrInOnNonMVColTp multiValuesOROnMVColTp multiValuesANDOnMVColTp singleValueOnMVColTp @@ -1301,7 +1301,23 @@ func checkAccessFilter4IdxCol( } // else: non virtual column - if sf.FuncName.L != ast.EQ { // only support EQ now + if sf.FuncName.L == ast.In { + args := sf.GetArgs() + if len(args) < 2 { + return false, unspecifiedFilterTp + } + c, isCol := args[0].(*expression.Column) + if !isCol || !c.Equal(sctx.GetExprCtx().GetEvalCtx(), idxCol) { + return false, unspecifiedFilterTp + } + for _, arg := range args[1:] { + if _, isCon := arg.(*expression.Constant); !isCon { + return false, unspecifiedFilterTp + } + } + return true, eqOrInOnNonMVColTp + } + if sf.FuncName.L != ast.EQ { return false, unspecifiedFilterTp } args := sf.GetArgs() @@ -1320,7 +1336,7 @@ func checkAccessFilter4IdxCol( return false, unspecifiedFilterTp } if argCol.Equal(sctx.GetExprCtx().GetEvalCtx(), idxCol) { - return true, eqOnNonMVColTp + return true, eqOrInOnNonMVColTp } return false, unspecifiedFilterTp } diff --git a/pkg/planner/core/indexmerge_unfinished_path.go b/pkg/planner/core/indexmerge_unfinished_path.go index fb0d737b65297..85e529a4c83e3 100644 --- a/pkg/planner/core/indexmerge_unfinished_path.go +++ b/pkg/planner/core/indexmerge_unfinished_path.go @@ -218,7 +218,7 @@ func initUnfinishedPathsFromExpr( if ok, tp := checkAccessFilter4IdxCol(ds.SCtx(), cnfItem, col); ok && // Since we only handle the OR list nested in the AND list, and only generate IndexMerge OR path, // we disable the multiValuesANDOnMVColTp case here. - (tp == eqOnNonMVColTp || tp == multiValuesOROnMVColTp || tp == singleValueOnMVColTp) { + (tp == eqOrInOnNonMVColTp || tp == multiValuesOROnMVColTp || tp == singleValueOnMVColTp) { ret[i].usableFilters = append(ret[i].usableFilters, cnfItem) ret[i].idxColHasUsableFilter[j] = true // Once we find one valid access filter for this column, we directly go to the next column without diff --git a/tests/integrationtest/r/planner/core/casetest/physicalplantest/physical_plan.result b/tests/integrationtest/r/planner/core/casetest/physicalplantest/physical_plan.result index 8791b24bfeaf4..40a496e354c97 100644 --- a/tests/integrationtest/r/planner/core/casetest/physicalplantest/physical_plan.result +++ b/tests/integrationtest/r/planner/core/casetest/physicalplantest/physical_plan.result @@ -3783,8 +3783,8 @@ id task access object operator info Limit root offset:0, count:2 └─Projection root planner__core__casetest__physicalplantest__physical_plan.t.a, planner__core__casetest__physicalplantest__physical_plan.t.b, planner__core__casetest__physicalplantest__physical_plan.t.c └─IndexMerge root type: union - ├─IndexRangeScan(Build) cop[tikv] table:t, index:idx(a, c) range:[1,1], keep order:true, stats:pseudo - ├─IndexRangeScan(Build) cop[tikv] table:t, index:idx2(b, c) range:[2,2], keep order:true, stats:pseudo + ├─IndexRangeScan(Build) cop[tikv] table:t, index:idx(a, c) range:[1 1,1 1], [1 2,1 2], [1 3,1 3], keep order:true, stats:pseudo + ├─IndexRangeScan(Build) cop[tikv] table:t, index:idx2(b, c) range:[2 1,2 1], [2 2,2 2], [2 3,2 3], keep order:true, stats:pseudo └─Selection(Probe) cop[tikv] in(planner__core__casetest__physicalplantest__physical_plan.t.c, 1, 2, 3) └─TableRowIDScan cop[tikv] table:t keep order:false, stats:pseudo show warnings; @@ -3793,8 +3793,8 @@ explain format = 'plan_tree' select * from t where (a = 1 or b = 2) and c in (1, id task access object operator info TopN root planner__core__casetest__physicalplantest__physical_plan.t.b, offset:0, count:2 └─IndexMerge root type: union - ├─IndexRangeScan(Build) cop[tikv] table:t, index:idx(a, c) range:[1,1], keep order:false, stats:pseudo - ├─IndexRangeScan(Build) cop[tikv] table:t, index:idx2(b, c) range:[2,2], keep order:false, stats:pseudo + ├─IndexRangeScan(Build) cop[tikv] table:t, index:idx(a, c) range:[1 1,1 1], [1 2,1 2], [1 3,1 3], keep order:false, stats:pseudo + ├─IndexRangeScan(Build) cop[tikv] table:t, index:idx2(b, c) range:[2 1,2 1], [2 2,2 2], [2 3,2 3], keep order:false, stats:pseudo └─TopN(Probe) cop[tikv] planner__core__casetest__physicalplantest__physical_plan.t.b, offset:0, count:2 └─Selection cop[tikv] in(planner__core__casetest__physicalplantest__physical_plan.t.c, 1, 2, 3) └─TableRowIDScan cop[tikv] table:t keep order:false, stats:pseudo diff --git a/tests/integrationtest/r/planner/core/indexmerge_path.result b/tests/integrationtest/r/planner/core/indexmerge_path.result index 8c596d77f3ea6..b95f44738a4fa 100644 --- a/tests/integrationtest/r/planner/core/indexmerge_path.result +++ b/tests/integrationtest/r/planner/core/indexmerge_path.result @@ -1373,3 +1373,23 @@ a b c 6 6 10 7 7 20 8 8 30 +drop table if exists t1; +create table t1(a int, b int, c int, d int, e int, index iea(e,a), index ieb(e,b), index iec(e,c), index ied(e,d)); +explain format = 'plan_tree' select /*+ use_index_merge(t1) */ * from t1 where e = 1 and (a in (1,2,3) or b in (2,3,4) or c in (3,4,5)); +id task access object operator info +IndexMerge root type: union +├─IndexRangeScan(Build) cop[tikv] table:t1, index:iea(e, a) range:[1 1,1 1], [1 2,1 2], [1 3,1 3], keep order:false, stats:pseudo +├─IndexRangeScan(Build) cop[tikv] table:t1, index:ieb(e, b) range:[1 2,1 2], [1 3,1 3], [1 4,1 4], keep order:false, stats:pseudo +├─IndexRangeScan(Build) cop[tikv] table:t1, index:iec(e, c) range:[1 3,1 3], [1 4,1 4], [1 5,1 5], keep order:false, stats:pseudo +└─Selection(Probe) cop[tikv] eq(planner__core__indexmerge_path.t1.e, 1), or(in(planner__core__indexmerge_path.t1.a, 1, 2, 3), or(in(planner__core__indexmerge_path.t1.b, 2, 3, 4), in(planner__core__indexmerge_path.t1.c, 3, 4, 5))) + └─TableRowIDScan cop[tikv] table:t1 keep order:false, stats:pseudo +explain format = 'plan_tree' select /*+ use_index_merge(t1) */ * from t1 where e = 1 and (a in (1,2,3) or b in (2,3,4) or c in (3,4,5)) limit 3; +id task access object operator info +Limit root offset:0, count:3 +└─IndexMerge root type: union + ├─IndexRangeScan(Build) cop[tikv] table:t1, index:iea(e, a) range:[1 1,1 1], [1 2,1 2], [1 3,1 3], keep order:false, stats:pseudo + ├─IndexRangeScan(Build) cop[tikv] table:t1, index:ieb(e, b) range:[1 2,1 2], [1 3,1 3], [1 4,1 4], keep order:false, stats:pseudo + ├─IndexRangeScan(Build) cop[tikv] table:t1, index:iec(e, c) range:[1 3,1 3], [1 4,1 4], [1 5,1 5], keep order:false, stats:pseudo + └─Limit(Probe) cop[tikv] offset:0, count:3 + └─Selection cop[tikv] eq(planner__core__indexmerge_path.t1.e, 1), or(in(planner__core__indexmerge_path.t1.a, 1, 2, 3), or(in(planner__core__indexmerge_path.t1.b, 2, 3, 4), in(planner__core__indexmerge_path.t1.c, 3, 4, 5))) + └─TableRowIDScan cop[tikv] table:t1 keep order:false, stats:pseudo diff --git a/tests/integrationtest/t/planner/core/indexmerge_path.test b/tests/integrationtest/t/planner/core/indexmerge_path.test index a38713d63c694..543634731c17b 100644 --- a/tests/integrationtest/t/planner/core/indexmerge_path.test +++ b/tests/integrationtest/t/planner/core/indexmerge_path.test @@ -589,3 +589,9 @@ explain format='plan_tree' select /*+ use_index_merge(t, idx_ac, idx_bc) */ * fr select /*+ use_index_merge(t, idx_ac, idx_bc) */ * from t where a = 1 or b > 5 order by c limit 3; explain format='plan_tree' select /*+ use_index_merge(t, idx_ac, idx_bc) */ * from t where a = 1 or b > 5 order by c limit 3 offset 1; select /*+ use_index_merge(t, idx_ac, idx_bc) */ * from t where a = 1 or b > 5 order by c limit 3 offset 1; + +# TestIndexMergeINInORList +drop table if exists t1; +create table t1(a int, b int, c int, d int, e int, index iea(e,a), index ieb(e,b), index iec(e,c), index ied(e,d)); +explain format = 'plan_tree' select /*+ use_index_merge(t1) */ * from t1 where e = 1 and (a in (1,2,3) or b in (2,3,4) or c in (3,4,5)); +explain format = 'plan_tree' select /*+ use_index_merge(t1) */ * from t1 where e = 1 and (a in (1,2,3) or b in (2,3,4) or c in (3,4,5)) limit 3;