func (r *rangeBuilder) buildFormBinOp(expr *expression.ScalarFunction) []rangePoint { // This has been checked that the binary operation is comparison operation, and one of // the operand is column name expression. var value types.Datum var op string if v, ok := expr.Args[0].(*expression.Constant); ok { value = v.Value switch expr.FuncName.L { case ast.GE: op = ast.LE case ast.GT: op = ast.LT case ast.LT: op = ast.GT case ast.LE: op = ast.GE default: op = expr.FuncName.L } } else { value = expr.Args[1].(*expression.Constant).Value op = expr.FuncName.L } if value.IsNull() { return nil } switch op { case ast.EQ: startPoint := rangePoint{value: value, start: true} endPoint := rangePoint{value: value} return []rangePoint{startPoint, endPoint} case ast.NE: startPoint1 := rangePoint{value: types.MinNotNullDatum(), start: true} endPoint1 := rangePoint{value: value, excl: true} startPoint2 := rangePoint{value: value, start: true, excl: true} endPoint2 := rangePoint{value: types.MaxValueDatum()} return []rangePoint{startPoint1, endPoint1, startPoint2, endPoint2} case ast.LT: startPoint := rangePoint{value: types.MinNotNullDatum(), start: true} endPoint := rangePoint{value: value, excl: true} return []rangePoint{startPoint, endPoint} case ast.LE: startPoint := rangePoint{value: types.MinNotNullDatum(), start: true} endPoint := rangePoint{value: value} return []rangePoint{startPoint, endPoint} case ast.GT: startPoint := rangePoint{value: value, start: true, excl: true} endPoint := rangePoint{value: types.MaxValueDatum()} return []rangePoint{startPoint, endPoint} case ast.GE: startPoint := rangePoint{value: value, start: true} endPoint := rangePoint{value: types.MaxValueDatum()} return []rangePoint{startPoint, endPoint} } return nil }
func (r *rangeBuilder) buildFromPatternLike(x *ast.PatternLikeExpr) []rangePoint { if x.Not { // Pattern not like is not supported. r.err = ErrUnsupportedType.Gen("NOT LIKE is not supported.") return fullRange } pattern, err := types.ToString(x.Pattern.GetValue()) if err != nil { r.err = errors.Trace(err) return fullRange } lowValue := make([]byte, 0, len(pattern)) // unscape the pattern var exclude bool for i := 0; i < len(pattern); i++ { if pattern[i] == x.Escape { i++ if i < len(pattern) { lowValue = append(lowValue, pattern[i]) } else { lowValue = append(lowValue, x.Escape) } continue } if pattern[i] == '%' { break } else if pattern[i] == '_' { exclude = true break } lowValue = append(lowValue, pattern[i]) } if len(lowValue) == 0 { return []rangePoint{{value: types.MinNotNullDatum(), start: true}, {value: types.MaxValueDatum()}} } startPoint := rangePoint{start: true, excl: exclude} startPoint.value.SetBytesAsString(lowValue) highValue := make([]byte, len(lowValue)) copy(highValue, lowValue) endPoint := rangePoint{excl: true} for i := len(highValue) - 1; i >= 0; i-- { highValue[i]++ if highValue[i] != 0 { endPoint.value.SetBytesAsString(highValue) break } if i == 0 { endPoint.value = types.MaxValueDatum() break } } ranges := make([]rangePoint, 2) ranges[0] = startPoint ranges[1] = endPoint return ranges }
func (r *rangeBuilder) buildFromColumn(expr *expression.Column) []rangePoint { // column name expression is equivalent to column name is true. startPoint1 := rangePoint{value: types.MinNotNullDatum(), start: true} endPoint1 := rangePoint{excl: true} endPoint1.value.SetInt64(0) startPoint2 := rangePoint{excl: true, start: true} startPoint2.value.SetInt64(0) endPoint2 := rangePoint{value: types.MaxValueDatum()} return []rangePoint{startPoint1, endPoint1, startPoint2, endPoint2} }
func (r *rangeBuilder) buildFromIsNull(x *ast.IsNullExpr) []rangePoint { if x.Not { startPoint := rangePoint{value: types.MinNotNullDatum(), start: true} endPoint := rangePoint{value: types.MaxValueDatum()} return []rangePoint{startPoint, endPoint} } startPoint := rangePoint{start: true} endPoint := rangePoint{} return []rangePoint{startPoint, endPoint} }
func (r *rangeBuilder) buildFromIsTruth(x *ast.IsTruthExpr) []rangePoint { if x.True != 0 { if x.Not { // NOT TRUE range is {[null null] [0, 0]} startPoint1 := rangePoint{start: true} endPoint1 := rangePoint{} startPoint2 := rangePoint{start: true} startPoint2.value.SetInt64(0) endPoint2 := rangePoint{} endPoint2.value.SetInt64(0) return []rangePoint{startPoint1, endPoint1, startPoint2, endPoint2} } // TRUE range is {[-inf 0) (0 +inf]} startPoint1 := rangePoint{value: types.MinNotNullDatum(), start: true} endPoint1 := rangePoint{excl: true} endPoint1.value.SetInt64(0) startPoint2 := rangePoint{excl: true, start: true} startPoint2.value.SetInt64(0) endPoint2 := rangePoint{value: types.MaxValueDatum()} return []rangePoint{startPoint1, endPoint1, startPoint2, endPoint2} } if x.Not { startPoint1 := rangePoint{start: true} endPoint1 := rangePoint{excl: true} endPoint1.value.SetInt64(0) startPoint2 := rangePoint{start: true, excl: true} startPoint2.value.SetInt64(0) endPoint2 := rangePoint{value: types.MaxValueDatum()} return []rangePoint{startPoint1, endPoint1, startPoint2, endPoint2} } startPoint := rangePoint{start: true} startPoint.value.SetInt64(0) endPoint := rangePoint{} endPoint.value.SetInt64(0) return []rangePoint{startPoint, endPoint} }
func (r *rangeBuilder) buildFromIsFalse(expr *expression.ScalarFunction, isNot int) []rangePoint { if isNot == 1 { // NOT FALSE range is {[-inf, 0), (0, +inf], [null, null]} startPoint1 := rangePoint{start: true} endPoint1 := rangePoint{excl: true} endPoint1.value.SetInt64(0) startPoint2 := rangePoint{start: true, excl: true} startPoint2.value.SetInt64(0) endPoint2 := rangePoint{value: types.MaxValueDatum()} return []rangePoint{startPoint1, endPoint1, startPoint2, endPoint2} } // FALSE range is {[0, 0]} startPoint := rangePoint{start: true} startPoint.value.SetInt64(0) endPoint := rangePoint{} endPoint.value.SetInt64(0) return []rangePoint{startPoint, endPoint} }
func (r *rangeBuilder) buildFromIsTrue(expr *expression.ScalarFunction, isNot int) []rangePoint { if isNot == 1 { // NOT TRUE range is {[null null] [0, 0]} startPoint1 := rangePoint{start: true} endPoint1 := rangePoint{} startPoint2 := rangePoint{start: true} startPoint2.value.SetInt64(0) endPoint2 := rangePoint{} endPoint2.value.SetInt64(0) return []rangePoint{startPoint1, endPoint1, startPoint2, endPoint2} } // TRUE range is {[-inf 0) (0 +inf]} startPoint1 := rangePoint{value: types.MinNotNullDatum(), start: true} endPoint1 := rangePoint{excl: true} endPoint1.value.SetInt64(0) startPoint2 := rangePoint{excl: true, start: true} startPoint2.value.SetInt64(0) endPoint2 := rangePoint{value: types.MaxValueDatum()} return []rangePoint{startPoint1, endPoint1, startPoint2, endPoint2} }
func (r *rangeBuilder) buildFromNot(expr *expression.ScalarFunction) []rangePoint { switch n := expr.FuncName.L; n { case ast.IsTruth: return r.buildFromIsTrue(expr, 1) case ast.IsFalsity: return r.buildFromIsFalse(expr, 1) case ast.In: // Pattern not in is not supported. r.err = ErrUnsupportedType.Gen("NOT IN is not supported") return fullRange case ast.Like: // Pattern not like is not supported. r.err = ErrUnsupportedType.Gen("NOT LIKE is not supported.") return fullRange case ast.IsNull: startPoint := rangePoint{value: types.MinNotNullDatum(), start: true} endPoint := rangePoint{value: types.MaxValueDatum()} return []rangePoint{startPoint, endPoint} } return nil }
package plan import ( "math" "github.com/juju/errors" "github.com/pingcap/tidb/ast" "github.com/pingcap/tidb/expression" "github.com/pingcap/tidb/model" "github.com/pingcap/tidb/mysql" "github.com/pingcap/tidb/util/types" ) var fullRange = []rangePoint{ {start: true}, {value: types.MaxValueDatum()}, } func buildNewIndexRange(p *PhysicalIndexScan) error { rb := rangeBuilder{} if p.accessEqualCount > 0 { // Build ranges for equal access conditions. point := rb.newBuild(p.AccessCondition[0]) p.Ranges = rb.buildIndexRanges(point) for i := 1; i < p.accessEqualCount; i++ { point = rb.newBuild(p.AccessCondition[i]) p.Ranges = rb.appendIndexRanges(p.Ranges, point) } } rangePoints := fullRange // Build rangePoints for non-equal access condtions.
func (r *rangeBuilder) newBuildFromPatternLike(expr *expression.ScalarFunction) []rangePoint { pattern, err := expr.Args[1].(*expression.Constant).Value.ToString() if err != nil { r.err = errors.Trace(err) return fullRange } if pattern == "" { startPoint := rangePoint{value: types.NewStringDatum(""), start: true} endPoint := rangePoint{value: types.NewStringDatum("")} return []rangePoint{startPoint, endPoint} } lowValue := make([]byte, 0, len(pattern)) escape := byte(expr.Args[2].(*expression.Constant).Value.GetInt64()) var exclude bool isExactMatch := true for i := 0; i < len(pattern); i++ { if pattern[i] == escape { i++ if i < len(pattern) { lowValue = append(lowValue, pattern[i]) } else { lowValue = append(lowValue, escape) } continue } if pattern[i] == '%' { // Get the prefix. isExactMatch = false break } else if pattern[i] == '_' { // Get the prefix, but exclude the prefix. // e.g., "abc_x", the start point exclude "abc", // because the string length is more than 3. exclude = true isExactMatch = false break } lowValue = append(lowValue, pattern[i]) } if len(lowValue) == 0 { return []rangePoint{{value: types.MinNotNullDatum(), start: true}, {value: types.MaxValueDatum()}} } if isExactMatch { val := types.NewStringDatum(string(lowValue)) return []rangePoint{{value: val, start: true}, {value: val}} } startPoint := rangePoint{start: true, excl: exclude} startPoint.value.SetBytesAsString(lowValue) highValue := make([]byte, len(lowValue)) copy(highValue, lowValue) endPoint := rangePoint{excl: true} for i := len(highValue) - 1; i >= 0; i-- { // Make the end point value more than the start point value, // and the length of the end point value is the same as the length of the start point value. // e.g., the start point value is "abc", so the end point value is "abd". highValue[i]++ if highValue[i] != 0 { endPoint.value.SetBytesAsString(highValue) break } // If highValue[i] is 255 and highValue[i]++ is 0, then the end point value is max value. if i == 0 { endPoint.value = types.MaxValueDatum() } } return []rangePoint{startPoint, endPoint} }
func (r *rangeBuilder) buildFromBinop(x *ast.BinaryOperationExpr) []rangePoint { if x.Op == opcode.OrOr { return r.union(r.build(x.L), r.build(x.R)) } else if x.Op == opcode.AndAnd { return r.intersection(r.build(x.L), r.build(x.R)) } // This has been checked that the binary operation is comparison operation, and one of // the operand is column name expression. var value types.Datum var op opcode.Op if _, ok := x.L.(*ast.ValueExpr); ok { value = types.NewDatum(x.L.GetValue()) switch x.Op { case opcode.GE: op = opcode.LE case opcode.GT: op = opcode.LT case opcode.LT: op = opcode.GT case opcode.LE: op = opcode.GE default: op = x.Op } } else { value = types.NewDatum(x.R.GetValue()) op = x.Op } if value.IsNull() { return nil } switch op { case opcode.EQ: startPoint := rangePoint{value: value, start: true} endPoint := rangePoint{value: value} return []rangePoint{startPoint, endPoint} case opcode.NE: startPoint1 := rangePoint{value: types.MinNotNullDatum(), start: true} endPoint1 := rangePoint{value: value, excl: true} startPoint2 := rangePoint{value: value, start: true, excl: true} endPoint2 := rangePoint{value: types.MaxValueDatum()} return []rangePoint{startPoint1, endPoint1, startPoint2, endPoint2} case opcode.LT: startPoint := rangePoint{value: types.MinNotNullDatum(), start: true} endPoint := rangePoint{value: value, excl: true} return []rangePoint{startPoint, endPoint} case opcode.LE: startPoint := rangePoint{value: types.MinNotNullDatum(), start: true} endPoint := rangePoint{value: value} return []rangePoint{startPoint, endPoint} case opcode.GT: startPoint := rangePoint{value: value, start: true, excl: true} endPoint := rangePoint{value: types.MaxValueDatum()} return []rangePoint{startPoint, endPoint} case opcode.GE: startPoint := rangePoint{value: value, start: true} endPoint := rangePoint{value: types.MaxValueDatum()} return []rangePoint{startPoint, endPoint} } return nil }
func (r *rangeBuilder) buildFromScalarFunc(expr *expression.ScalarFunction) []rangePoint { // TODO: It only implements the binary operation range building. And it needs to implement other scalar functions. if len(expr.Args) != 2 { return nil } if expr.FuncName.L == ast.OrOr { return r.union(r.newBuild(expr.Args[0]), r.newBuild(expr.Args[1])) } if expr.FuncName.L == ast.AndAnd { return r.intersection(r.newBuild(expr.Args[0]), r.newBuild(expr.Args[1])) } // This has been checked that the binary operation is comparison operation, and one of // the operand is column name expression. var value types.Datum var op string if v, ok := expr.Args[0].(*expression.Constant); ok { value = v.Value switch expr.FuncName.L { case ast.GE: op = ast.LE case ast.GT: op = ast.LT case ast.LT: op = ast.GT case ast.LE: op = ast.GE default: op = expr.FuncName.L } } else { value = expr.Args[1].(*expression.Constant).Value op = expr.FuncName.L } if value.IsNull() { return nil } switch op { case ast.EQ: startPoint := rangePoint{value: value, start: true} endPoint := rangePoint{value: value} return []rangePoint{startPoint, endPoint} case ast.NE: startPoint1 := rangePoint{value: types.MinNotNullDatum(), start: true} endPoint1 := rangePoint{value: value, excl: true} startPoint2 := rangePoint{value: value, start: true, excl: true} endPoint2 := rangePoint{value: types.MaxValueDatum()} return []rangePoint{startPoint1, endPoint1, startPoint2, endPoint2} case ast.LT: startPoint := rangePoint{value: types.MinNotNullDatum(), start: true} endPoint := rangePoint{value: value, excl: true} return []rangePoint{startPoint, endPoint} case ast.LE: startPoint := rangePoint{value: types.MinNotNullDatum(), start: true} endPoint := rangePoint{value: value} return []rangePoint{startPoint, endPoint} case ast.GT: startPoint := rangePoint{value: value, start: true, excl: true} endPoint := rangePoint{value: types.MaxValueDatum()} return []rangePoint{startPoint, endPoint} case ast.GE: startPoint := rangePoint{value: value, start: true} endPoint := rangePoint{value: types.MaxValueDatum()} return []rangePoint{startPoint, endPoint} } return nil }