Skip to content

Commit

Permalink
ranger: choose more prefix columns when building ranges
Browse files Browse the repository at this point in the history
  • Loading branch information
eurekaka committed Apr 2, 2019
1 parent b864fac commit 156096c
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 12 deletions.
18 changes: 10 additions & 8 deletions planner/core/logical_plans.go
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,7 @@ func (ds *DataSource) deriveIndexPathStats(path *accessPath) (bool, error) {
path.ranges = ranger.FullRange()
path.countAfterAccess = float64(ds.statisticTable.Count)
path.idxCols, path.idxColLens = expression.IndexInfo2Cols(ds.schema.Columns, path.index)
eqOrInCount := 0
if len(path.idxCols) != 0 {
res, err := ranger.DetachCondAndBuildRangeForIndex(ds.ctx, ds.pushedDownConds, path.idxCols, path.idxColLens)
if err != nil {
Expand All @@ -466,23 +467,24 @@ func (ds *DataSource) deriveIndexPathStats(path *accessPath) (bool, error) {
path.accessConds = res.AccessConds
path.tableFilters = res.RemainedConds
path.eqCondCount = res.EqCondCount
eqOrInCount = res.EqOrInCount
path.countAfterAccess, err = ds.stats.HistColl.GetRowCountByIndexRanges(sc, path.index.ID, path.ranges)
if err != nil {
return false, err
}
} else {
path.tableFilters = ds.pushedDownConds
}
if path.eqCondCount == len(path.accessConds) {
accesses, remained := path.splitCorColAccessCondFromFilters()
if eqOrInCount == len(path.accessConds) {
accesses, remained := path.splitCorColAccessCondFromFilters(eqOrInCount)
path.accessConds = append(path.accessConds, accesses...)
path.tableFilters = remained
if len(accesses) > 0 && ds.statisticTable.Pseudo {
path.countAfterAccess = ds.statisticTable.PseudoAvgCountPerValue()
} else {
selectivity := path.countAfterAccess / float64(ds.statisticTable.Count)
for i := range accesses {
col := path.idxCols[path.eqCondCount+i]
col := path.idxCols[eqOrInCount+i]
ndv := ds.getColumnNDV(col.ID)
ndv *= selectivity
if ndv < 1 {
Expand Down Expand Up @@ -529,24 +531,24 @@ func (ds *DataSource) deriveIndexPathStats(path *accessPath) (bool, error) {
return noIntervalRanges && !haveNullVal, nil
}

func (path *accessPath) splitCorColAccessCondFromFilters() (access, remained []expression.Expression) {
access = make([]expression.Expression, len(path.idxCols)-path.eqCondCount)
func (path *accessPath) splitCorColAccessCondFromFilters(eqOrInCount int) (access, remained []expression.Expression) {
access = make([]expression.Expression, len(path.idxCols)-eqOrInCount)
used := make([]bool, len(path.tableFilters))
for i := path.eqCondCount; i < len(path.idxCols); i++ {
for i := eqOrInCount; i < len(path.idxCols); i++ {
matched := false
for j, filter := range path.tableFilters {
if used[j] || !isColEqCorColOrConstant(filter, path.idxCols[i]) {
continue
}
matched = true
access[i-path.eqCondCount] = filter
access[i-eqOrInCount] = filter
if path.idxColLens[i] == types.UnspecifiedLength {
used[j] = true
}
break
}
if !matched {
access = access[:i-path.eqCondCount]
access = access[:i-eqOrInCount]
break
}
}
Expand Down
2 changes: 1 addition & 1 deletion planner/core/logical_plans_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ func (s *testUnitTestSuit) TestIndexPathSplitCorColCond(c *C) {
idxColLens: tt.idxColLens,
}

access, remained := path.splitCorColAccessCondFromFilters()
access, remained := path.splitCorColAccessCondFromFilters(path.eqCondCount)
c.Assert(fmt.Sprintf("%s", access), Equals, tt.access, comment)
c.Assert(fmt.Sprintf("%s", remained), Equals, tt.remained, comment)
}
Expand Down
7 changes: 4 additions & 3 deletions util/ranger/detacher.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,9 @@ func detachCNFCondAndBuildRangeForIndex(sctx sessionctx.Context, conditions []ex
// We should remove all accessConds, so that they will not be added to filter conditions.
newConditions = removeAccessConditions(newConditions, accessConds)
eqOrInCount := len(accessConds)
res.EqCondCount = eqCount
res.EqOrInCount = eqOrInCount
if eqOrInCount == len(cols) {
// If curIndex equals to len of index columns, it means the rest conditions haven't been appended to filter conditions.
filterConds = append(filterConds, newConditions...)
ranges, err = buildCNFIndexRange(sctx.GetSessionVars().StmtCtx, cols, tpSlice, lengths, eqOrInCount, accessConds)
if err != nil {
Expand All @@ -169,7 +170,6 @@ func detachCNFCondAndBuildRangeForIndex(sctx sessionctx.Context, conditions []ex
res.Ranges = ranges
res.AccessConds = accessConds
res.RemainedConds = filterConds
res.EqCondCount = eqCount
return res, nil
}
checker := &conditionChecker{
Expand All @@ -194,7 +194,6 @@ func detachCNFCondAndBuildRangeForIndex(sctx sessionctx.Context, conditions []ex
res.Ranges = ranges
res.AccessConds = accessConds
res.RemainedConds = filterConds
res.EqCondCount = eqCount
return res, err
}

Expand Down Expand Up @@ -319,6 +318,8 @@ type DetachRangeResult struct {
RemainedConds []expression.Expression
// EqCondCount is the number of equal conditions extracted.
EqCondCount int
// EqOrInCount is the number of equal/in conditions extracted.
EqOrInCount int
// IsDNFCond indicates if the top layer of conditions are in DNF.
IsDNFCond bool
}
Expand Down
32 changes: 32 additions & 0 deletions util/ranger/ranger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -989,3 +989,35 @@ func (s *testRangerSuite) TestIndexRangeElimininatedProjection(c *C) {
"1 2",
))
}

func (s *testRangerSuite) TestCompIndexInExprCorrCol(c *C) {
defer testleak.AfterTest(c)()
dom, store, err := newDomainStoreWithBootstrap(c)
defer func() {
dom.Close()
store.Close()
}()
c.Assert(err, IsNil)
testKit := testkit.NewTestKit(c, store)
testKit.MustExec("use test")
testKit.MustExec("drop table if exists t")
testKit.MustExec("create table t(a int primary key, b int, c int, d int, e int, index idx(b,c,d))")
testKit.MustExec("insert into t values(1,1,1,1,2),(2,1,2,1,0)")
testKit.MustExec("analyze table t")
testKit.MustQuery("explain select t.e in (select count(*) from t s use index(idx), t t1 where s.b = 1 and s.c in (1, 2) and s.d = t.a and s.a = t1.a) from t").Check(testkit.Rows(
"Projection_11 2.00 root 9_aux_0",
"└─Apply_13 2.00 root left outer semi join, inner:StreamAgg_20, other cond:eq(test.t.e, 7_col_0)",
" ├─TableReader_15 2.00 root data:TableScan_14",
" │ └─TableScan_14 2.00 cop table:t, range:[-inf,+inf], keep order:false",
" └─StreamAgg_20 1.00 root funcs:count(1)",
" └─IndexJoin_32 2.00 root inner join, inner:TableReader_31, outer key:s.a, inner key:t1.a",
" ├─IndexReader_27 2.00 root index:IndexScan_26",
" │ └─IndexScan_26 2.00 cop table:s, index:b, c, d, range: decided by [eq(s.b, 1) in(s.c, 1, 2) eq(s.d, test.t.a)], keep order:false",
" └─TableReader_31 1.00 root data:TableScan_30",
" └─TableScan_30 1.00 cop table:t1, range: decided by [s.a], keep order:false",
))
testKit.MustQuery("select t.e in (select count(*) from t s use index(idx), t t1 where s.b = 1 and s.c in (1, 2) and s.d = t.a and s.a = t1.a) from t").Check(testkit.Rows(
"1",
"1",
))
}

0 comments on commit 156096c

Please sign in to comment.