github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/optimizer/plan/refiner.go (about) 1 // Copyright 2015 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package plan 15 16 import ( 17 "math" 18 19 "github.com/insionng/yougam/libraries/juju/errors" 20 "github.com/insionng/yougam/libraries/pingcap/tidb/ast" 21 "github.com/insionng/yougam/libraries/pingcap/tidb/model" 22 "github.com/insionng/yougam/libraries/pingcap/tidb/parser/opcode" 23 "github.com/insionng/yougam/libraries/pingcap/tidb/util/types" 24 ) 25 26 // Refine tries to build index or table range. 27 func Refine(p Plan) error { 28 return refine(p) 29 } 30 31 func refine(in Plan) error { 32 var err error 33 for _, c := range in.GetChildren() { 34 e := refine(c) 35 if e != nil { 36 err = errors.Trace(e) 37 } 38 } 39 switch x := in.(type) { 40 case *IndexScan: 41 err = buildIndexRange(x) 42 case *Limit: 43 x.SetLimit(0) 44 case *TableScan: 45 err = buildTableRange(x) 46 } 47 return err 48 } 49 50 var fullRange = []rangePoint{ 51 {start: true}, 52 {value: types.MaxValueDatum()}, 53 } 54 55 func buildIndexRange(p *IndexScan) error { 56 rb := rangeBuilder{} 57 if p.AccessEqualCount > 0 { 58 // Build ranges for equal access conditions. 59 point := rb.build(p.AccessConditions[0]) 60 p.Ranges = rb.buildIndexRanges(point) 61 for i := 1; i < p.AccessEqualCount; i++ { 62 point = rb.build(p.AccessConditions[i]) 63 p.Ranges = rb.appendIndexRanges(p.Ranges, point) 64 } 65 } 66 rangePoints := fullRange 67 // Build rangePoints for non-equal access condtions. 68 for i := p.AccessEqualCount; i < len(p.AccessConditions); i++ { 69 rangePoints = rb.intersection(rangePoints, rb.build(p.AccessConditions[i])) 70 } 71 if p.AccessEqualCount == 0 { 72 p.Ranges = rb.buildIndexRanges(rangePoints) 73 } else if p.AccessEqualCount < len(p.AccessConditions) { 74 p.Ranges = rb.appendIndexRanges(p.Ranges, rangePoints) 75 } 76 return errors.Trace(rb.err) 77 } 78 79 func buildTableRange(p *TableScan) error { 80 if len(p.AccessConditions) == 0 { 81 p.Ranges = []TableRange{{math.MinInt64, math.MaxInt64}} 82 return nil 83 } 84 rb := rangeBuilder{} 85 rangePoints := fullRange 86 for _, cond := range p.AccessConditions { 87 rangePoints = rb.intersection(rangePoints, rb.build(cond)) 88 } 89 p.Ranges = rb.buildTableRanges(rangePoints) 90 return rb.err 91 } 92 93 // conditionChecker checks if this condition can be pushed to index plan. 94 type conditionChecker struct { 95 tableName model.CIStr 96 idx *model.IndexInfo 97 // the offset of the indexed column to be checked. 98 columnOffset int 99 pkName model.CIStr 100 } 101 102 func (c *conditionChecker) check(condition ast.ExprNode) bool { 103 switch x := condition.(type) { 104 case *ast.BinaryOperationExpr: 105 return c.checkBinaryOperation(x) 106 case *ast.BetweenExpr: 107 if ast.IsPreEvaluable(x.Left) && ast.IsPreEvaluable(x.Right) && c.checkColumnExpr(x.Expr) { 108 return true 109 } 110 case *ast.ColumnNameExpr: 111 return c.checkColumnExpr(x) 112 case *ast.IsNullExpr: 113 if c.checkColumnExpr(x.Expr) { 114 return true 115 } 116 case *ast.IsTruthExpr: 117 if c.checkColumnExpr(x.Expr) { 118 return true 119 } 120 case *ast.ParenthesesExpr: 121 return c.check(x.Expr) 122 case *ast.PatternInExpr: 123 if x.Sel != nil || x.Not { 124 return false 125 } 126 if !c.checkColumnExpr(x.Expr) { 127 return false 128 } 129 for _, val := range x.List { 130 if !ast.IsPreEvaluable(val) { 131 return false 132 } 133 } 134 return true 135 case *ast.PatternLikeExpr: 136 if x.Not { 137 return false 138 } 139 if !c.checkColumnExpr(x.Expr) { 140 return false 141 } 142 if !ast.IsPreEvaluable(x.Pattern) { 143 return false 144 } 145 patternVal := x.Pattern.GetValue() 146 if patternVal == nil { 147 return false 148 } 149 patternStr, err := types.ToString(patternVal) 150 if err != nil { 151 return false 152 } 153 firstChar := patternStr[0] 154 return firstChar != '%' && firstChar != '.' 155 } 156 return false 157 } 158 159 func (c *conditionChecker) checkBinaryOperation(b *ast.BinaryOperationExpr) bool { 160 switch b.Op { 161 case opcode.OrOr: 162 return c.check(b.L) && c.check(b.R) 163 case opcode.AndAnd: 164 return c.check(b.L) && c.check(b.R) 165 case opcode.EQ, opcode.NE, opcode.GE, opcode.GT, opcode.LE, opcode.LT: 166 if ast.IsPreEvaluable(b.L) { 167 return c.checkColumnExpr(b.R) 168 } else if ast.IsPreEvaluable(b.R) { 169 return c.checkColumnExpr(b.L) 170 } 171 } 172 return false 173 } 174 175 func (c *conditionChecker) checkColumnExpr(expr ast.ExprNode) bool { 176 cn, ok := expr.(*ast.ColumnNameExpr) 177 if !ok { 178 return false 179 } 180 if cn.Refer.Table.Name.L != c.tableName.L { 181 return false 182 } 183 if c.pkName.L != "" { 184 return c.pkName.L == cn.Refer.Column.Name.L 185 } 186 if c.idx != nil { 187 return cn.Refer.Column.Name.L == c.idx.Columns[c.columnOffset].Name.L 188 } 189 return true 190 }