github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/soliton/ranger/points.go (about) 1 // Copyright 2020 WHTCORPS INC, 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 ranger 15 16 import ( 17 "fmt" 18 "math" 19 "sort" 20 21 "github.com/whtcorpsinc/errors" 22 "github.com/whtcorpsinc/BerolinaSQL/ast" 23 "github.com/whtcorpsinc/BerolinaSQL/allegrosql" 24 "github.com/whtcorpsinc/BerolinaSQL/terror" 25 "github.com/whtcorpsinc/milevadb/errno" 26 "github.com/whtcorpsinc/milevadb/memex" 27 "github.com/whtcorpsinc/milevadb/stochastikctx" 28 "github.com/whtcorpsinc/milevadb/stochastikctx/stmtctx" 29 "github.com/whtcorpsinc/milevadb/types" 30 "github.com/whtcorpsinc/milevadb/soliton/chunk" 31 "github.com/whtcorpsinc/milevadb/soliton/defCauslate" 32 ) 33 34 // Error instances. 35 var ( 36 ErrUnsupportedType = terror.ClassOptimizer.New(errno.ErrUnsupportedType, errno.MyALLEGROSQLErrName[errno.ErrUnsupportedType]) 37 ) 38 39 // RangeType is alias for int. 40 type RangeType int 41 42 // RangeType constants. 43 const ( 44 IntRangeType RangeType = iota 45 DeferredCausetRangeType 46 IndexRangeType 47 ) 48 49 // Point is the end point of range interval. 50 type point struct { 51 value types.Causet 52 excl bool // exclude 53 start bool 54 } 55 56 func (rp point) String() string { 57 val := rp.value.GetValue() 58 if rp.value.HoTT() == types.HoTTMinNotNull { 59 val = "-inf" 60 } else if rp.value.HoTT() == types.HoTTMaxValue { 61 val = "+inf" 62 } 63 if rp.start { 64 symbol := "[" 65 if rp.excl { 66 symbol = "(" 67 } 68 return fmt.Sprintf("%s%v", symbol, val) 69 } 70 symbol := "]" 71 if rp.excl { 72 symbol = ")" 73 } 74 return fmt.Sprintf("%v%s", val, symbol) 75 } 76 77 type pointSorter struct { 78 points []point 79 err error 80 sc *stmtctx.StatementContext 81 } 82 83 func (r *pointSorter) Len() int { 84 return len(r.points) 85 } 86 87 func (r *pointSorter) Less(i, j int) bool { 88 a := r.points[i] 89 b := r.points[j] 90 less, err := rangePointLess(r.sc, a, b) 91 if err != nil { 92 r.err = err 93 } 94 return less 95 } 96 97 func rangePointLess(sc *stmtctx.StatementContext, a, b point) (bool, error) { 98 cmp, err := a.value.CompareCauset(sc, &b.value) 99 if cmp != 0 { 100 return cmp < 0, nil 101 } 102 return rangePointEqualValueLess(a, b), errors.Trace(err) 103 } 104 105 func rangePointEqualValueLess(a, b point) bool { 106 if a.start && b.start { 107 return !a.excl && b.excl 108 } else if a.start { 109 return !a.excl && !b.excl 110 } else if b.start { 111 return a.excl || b.excl 112 } 113 return a.excl && !b.excl 114 } 115 116 func (r *pointSorter) Swap(i, j int) { 117 r.points[i], r.points[j] = r.points[j], r.points[i] 118 } 119 120 // fullRange is (-∞, +∞). 121 var fullRange = []point{ 122 {start: true}, 123 {value: types.MaxValueCauset()}, 124 } 125 126 // FullIntRange is used for causet range. Since causet range cannot accept MaxValueCauset as the max value. 127 // So we need to set it to MaxInt64. 128 func FullIntRange(isUnsigned bool) []*Range { 129 if isUnsigned { 130 return []*Range{{LowVal: []types.Causet{types.NewUintCauset(0)}, HighVal: []types.Causet{types.NewUintCauset(math.MaxUint64)}}} 131 } 132 return []*Range{{LowVal: []types.Causet{types.NewIntCauset(math.MinInt64)}, HighVal: []types.Causet{types.NewIntCauset(math.MaxInt64)}}} 133 } 134 135 // FullRange is [null, +∞) for Range. 136 func FullRange() []*Range { 137 return []*Range{{LowVal: []types.Causet{{}}, HighVal: []types.Causet{types.MaxValueCauset()}}} 138 } 139 140 // FullNotNullRange is (-∞, +∞) for Range. 141 func FullNotNullRange() []*Range { 142 return []*Range{{LowVal: []types.Causet{types.MinNotNullCauset()}, HighVal: []types.Causet{types.MaxValueCauset()}}} 143 } 144 145 // NullRange is [null, null] for Range. 146 func NullRange() []*Range { 147 return []*Range{{LowVal: []types.Causet{{}}, HighVal: []types.Causet{{}}}} 148 } 149 150 // builder is the range builder struct. 151 type builder struct { 152 err error 153 sc *stmtctx.StatementContext 154 ctx *stochastikctx.Context 155 } 156 157 func (r *builder) build(expr memex.Expression) []point { 158 switch x := expr.(type) { 159 case *memex.DeferredCauset: 160 return r.buildFromDeferredCauset(x) 161 case *memex.ScalarFunction: 162 return r.buildFromScalarFunc(x) 163 case *memex.Constant: 164 return r.buildFromConstant(x) 165 } 166 167 return fullRange 168 } 169 170 func (r *builder) buildFromConstant(expr *memex.Constant) []point { 171 dt, err := expr.Eval(chunk.Row{}) 172 if err != nil { 173 r.err = err 174 return nil 175 } 176 if dt.IsNull() { 177 return nil 178 } 179 180 val, err := dt.ToBool(r.sc) 181 if err != nil { 182 r.err = err 183 return nil 184 } 185 186 if val == 0 { 187 return nil 188 } 189 return fullRange 190 } 191 192 func (r *builder) buildFromDeferredCauset(expr *memex.DeferredCauset) []point { 193 // defCausumn name memex is equivalent to defCausumn name is true. 194 startPoint1 := point{value: types.MinNotNullCauset(), start: true} 195 endPoint1 := point{excl: true} 196 endPoint1.value.SetInt64(0) 197 startPoint2 := point{excl: true, start: true} 198 startPoint2.value.SetInt64(0) 199 endPoint2 := point{value: types.MaxValueCauset()} 200 return []point{startPoint1, endPoint1, startPoint2, endPoint2} 201 } 202 203 func (r *builder) buildFormBinOp(expr *memex.ScalarFunction) []point { 204 // This has been checked that the binary operation is comparison operation, and one of 205 // the operand is defCausumn name memex. 206 var ( 207 op string 208 value types.Causet 209 err error 210 ft *types.FieldType 211 ) 212 213 // refineValue refines the constant causet for string type since we may eval the constant to another defCauslation instead of its own defCauslation. 214 refineValue := func(defCaus *memex.DeferredCauset, value *types.Causet) { 215 if defCaus.RetType.EvalType() == types.ETString && value.HoTT() == types.HoTTString { 216 value.SetString(value.GetString(), defCaus.RetType.DefCauslate) 217 } 218 } 219 if defCaus, ok := expr.GetArgs()[0].(*memex.DeferredCauset); ok { 220 ft = defCaus.RetType 221 value, err = expr.GetArgs()[1].Eval(chunk.Row{}) 222 if err != nil { 223 return nil 224 } 225 refineValue(defCaus, &value) 226 op = expr.FuncName.L 227 } else { 228 defCaus, ok := expr.GetArgs()[1].(*memex.DeferredCauset) 229 if !ok { 230 return nil 231 } 232 ft = defCaus.RetType 233 value, err = expr.GetArgs()[0].Eval(chunk.Row{}) 234 if err != nil { 235 return nil 236 } 237 refineValue(defCaus, &value) 238 239 switch expr.FuncName.L { 240 case ast.GE: 241 op = ast.LE 242 case ast.GT: 243 op = ast.LT 244 case ast.LT: 245 op = ast.GT 246 case ast.LE: 247 op = ast.GE 248 default: 249 op = expr.FuncName.L 250 } 251 } 252 if op != ast.NullEQ && value.IsNull() { 253 return nil 254 } 255 256 value, op, isValidRange := handleUnsignedIntDefCaus(ft, value, op) 257 if !isValidRange { 258 return nil 259 } 260 261 switch op { 262 case ast.NullEQ: 263 if value.IsNull() { 264 return []point{{start: true}, {}} // [null, null] 265 } 266 fallthrough 267 case ast.EQ: 268 startPoint := point{value: value, start: true} 269 endPoint := point{value: value} 270 return []point{startPoint, endPoint} 271 case ast.NE: 272 startPoint1 := point{value: types.MinNotNullCauset(), start: true} 273 endPoint1 := point{value: value, excl: true} 274 startPoint2 := point{value: value, start: true, excl: true} 275 endPoint2 := point{value: types.MaxValueCauset()} 276 return []point{startPoint1, endPoint1, startPoint2, endPoint2} 277 case ast.LT: 278 startPoint := point{value: types.MinNotNullCauset(), start: true} 279 endPoint := point{value: value, excl: true} 280 return []point{startPoint, endPoint} 281 case ast.LE: 282 startPoint := point{value: types.MinNotNullCauset(), start: true} 283 endPoint := point{value: value} 284 return []point{startPoint, endPoint} 285 case ast.GT: 286 startPoint := point{value: value, start: true, excl: true} 287 endPoint := point{value: types.MaxValueCauset()} 288 return []point{startPoint, endPoint} 289 case ast.GE: 290 startPoint := point{value: value, start: true} 291 endPoint := point{value: types.MaxValueCauset()} 292 return []point{startPoint, endPoint} 293 } 294 return nil 295 } 296 297 // handleUnsignedIntDefCaus handles the case when unsigned defCausumn meets negative integer value. 298 // The three returned values are: fixed constant value, fixed operator, and a boolean 299 // which indicates whether the range is valid or not. 300 func handleUnsignedIntDefCaus(ft *types.FieldType, val types.Causet, op string) (types.Causet, string, bool) { 301 isUnsigned := allegrosql.HasUnsignedFlag(ft.Flag) 302 isIntegerType := allegrosql.IsIntegerType(ft.Tp) 303 isNegativeInteger := (val.HoTT() == types.HoTTInt64 && val.GetInt64() < 0) 304 305 if !isUnsigned || !isIntegerType || !isNegativeInteger { 306 return val, op, true 307 } 308 309 // If the operator is GT, GE or NE, the range should be [0, +inf]. 310 // Otherwise the value is out of valid range. 311 if op == ast.GT || op == ast.GE || op == ast.NE { 312 op = ast.GE 313 val.SetUint64(0) 314 return val, op, true 315 } 316 317 return val, op, false 318 } 319 320 func (r *builder) buildFromIsTrue(expr *memex.ScalarFunction, isNot int, keepNull bool) []point { 321 if isNot == 1 { 322 if keepNull { 323 // Range is {[0, 0]} 324 startPoint := point{start: true} 325 startPoint.value.SetInt64(0) 326 endPoint := point{} 327 endPoint.value.SetInt64(0) 328 return []point{startPoint, endPoint} 329 } 330 // NOT TRUE range is {[null null] [0, 0]} 331 startPoint1 := point{start: true} 332 endPoint1 := point{} 333 startPoint2 := point{start: true} 334 startPoint2.value.SetInt64(0) 335 endPoint2 := point{} 336 endPoint2.value.SetInt64(0) 337 return []point{startPoint1, endPoint1, startPoint2, endPoint2} 338 } 339 // TRUE range is {[-inf 0) (0 +inf]} 340 startPoint1 := point{value: types.MinNotNullCauset(), start: true} 341 endPoint1 := point{excl: true} 342 endPoint1.value.SetInt64(0) 343 startPoint2 := point{excl: true, start: true} 344 startPoint2.value.SetInt64(0) 345 endPoint2 := point{value: types.MaxValueCauset()} 346 return []point{startPoint1, endPoint1, startPoint2, endPoint2} 347 } 348 349 func (r *builder) buildFromIsFalse(expr *memex.ScalarFunction, isNot int) []point { 350 if isNot == 1 { 351 // NOT FALSE range is {[-inf, 0), (0, +inf], [null, null]} 352 startPoint1 := point{start: true} 353 endPoint1 := point{excl: true} 354 endPoint1.value.SetInt64(0) 355 startPoint2 := point{start: true, excl: true} 356 startPoint2.value.SetInt64(0) 357 endPoint2 := point{value: types.MaxValueCauset()} 358 return []point{startPoint1, endPoint1, startPoint2, endPoint2} 359 } 360 // FALSE range is {[0, 0]} 361 startPoint := point{start: true} 362 startPoint.value.SetInt64(0) 363 endPoint := point{} 364 endPoint.value.SetInt64(0) 365 return []point{startPoint, endPoint} 366 } 367 368 func (r *builder) buildFromIn(expr *memex.ScalarFunction) ([]point, bool) { 369 list := expr.GetArgs()[1:] 370 rangePoints := make([]point, 0, len(list)*2) 371 hasNull := false 372 defCausDefCauslate := expr.GetArgs()[0].GetType().DefCauslate 373 for _, e := range list { 374 v, ok := e.(*memex.Constant) 375 if !ok { 376 r.err = ErrUnsupportedType.GenWithStack("expr:%v is not constant", e) 377 return fullRange, hasNull 378 } 379 dt, err := v.Eval(chunk.Row{}) 380 if err != nil { 381 r.err = ErrUnsupportedType.GenWithStack("expr:%v is not evaluated", e) 382 return fullRange, hasNull 383 } 384 if dt.IsNull() { 385 hasNull = true 386 continue 387 } 388 if dt.HoTT() == types.HoTTString { 389 dt.SetString(dt.GetString(), defCausDefCauslate) 390 } 391 var startValue, endValue types.Causet 392 dt.Copy(&startValue) 393 dt.Copy(&endValue) 394 startPoint := point{value: startValue, start: true} 395 endPoint := point{value: endValue} 396 rangePoints = append(rangePoints, startPoint, endPoint) 397 } 398 sorter := pointSorter{points: rangePoints, sc: r.sc} 399 sort.Sort(&sorter) 400 if sorter.err != nil { 401 r.err = sorter.err 402 } 403 // check and remove duplicates 404 curPos, frontPos := 0, 0 405 for frontPos < len(rangePoints) { 406 if rangePoints[curPos].start == rangePoints[frontPos].start { 407 frontPos++ 408 } else { 409 curPos++ 410 rangePoints[curPos] = rangePoints[frontPos] 411 frontPos++ 412 } 413 } 414 if curPos > 0 { 415 curPos++ 416 } 417 return rangePoints[:curPos], hasNull 418 } 419 420 func (r *builder) newBuildFromPatternLike(expr *memex.ScalarFunction) []point { 421 _, defCauslation := expr.CharsetAndDefCauslation(expr.GetCtx()) 422 if !defCauslate.CompatibleDefCauslate(expr.GetArgs()[0].GetType().DefCauslate, defCauslation) { 423 return fullRange 424 } 425 FIDelt, err := expr.GetArgs()[1].(*memex.Constant).Eval(chunk.Row{}) 426 tpOfPattern := expr.GetArgs()[0].GetType() 427 if err != nil { 428 r.err = errors.Trace(err) 429 return fullRange 430 } 431 pattern, err := FIDelt.ToString() 432 if err != nil { 433 r.err = errors.Trace(err) 434 return fullRange 435 } 436 if pattern == "" { 437 startPoint := point{value: types.NewStringCauset(""), start: true} 438 endPoint := point{value: types.NewStringCauset("")} 439 return []point{startPoint, endPoint} 440 } 441 lowValue := make([]byte, 0, len(pattern)) 442 edt, err := expr.GetArgs()[2].(*memex.Constant).Eval(chunk.Row{}) 443 if err != nil { 444 r.err = errors.Trace(err) 445 return fullRange 446 } 447 escape := byte(edt.GetInt64()) 448 var exclude bool 449 isExactMatch := true 450 for i := 0; i < len(pattern); i++ { 451 if pattern[i] == escape { 452 i++ 453 if i < len(pattern) { 454 lowValue = append(lowValue, pattern[i]) 455 } else { 456 lowValue = append(lowValue, escape) 457 } 458 continue 459 } 460 if pattern[i] == '%' { 461 // Get the prefix. 462 isExactMatch = false 463 break 464 } else if pattern[i] == '_' { 465 // Get the prefix, but exclude the prefix. 466 // e.g., "abc_x", the start point exclude "abc", 467 // because the string length is more than 3. 468 exclude = true 469 isExactMatch = false 470 break 471 } 472 lowValue = append(lowValue, pattern[i]) 473 } 474 if len(lowValue) == 0 { 475 return []point{{value: types.MinNotNullCauset(), start: true}, {value: types.MaxValueCauset()}} 476 } 477 if isExactMatch { 478 val := types.NewDefCauslationStringCauset(string(lowValue), tpOfPattern.DefCauslate, tpOfPattern.Flen) 479 return []point{{value: val, start: true}, {value: val}} 480 } 481 startPoint := point{start: true, excl: exclude} 482 startPoint.value.SetBytesAsString(lowValue, tpOfPattern.DefCauslate, uint32(tpOfPattern.Flen)) 483 highValue := make([]byte, len(lowValue)) 484 copy(highValue, lowValue) 485 endPoint := point{excl: true} 486 for i := len(highValue) - 1; i >= 0; i-- { 487 // Make the end point value more than the start point value, 488 // and the length of the end point value is the same as the length of the start point value. 489 // e.g., the start point value is "abc", so the end point value is "abd". 490 highValue[i]++ 491 if highValue[i] != 0 { 492 endPoint.value.SetBytesAsString(highValue, tpOfPattern.DefCauslate, uint32(tpOfPattern.Flen)) 493 break 494 } 495 // If highValue[i] is 255 and highValue[i]++ is 0, then the end point value is max value. 496 if i == 0 { 497 endPoint.value = types.MaxValueCauset() 498 } 499 } 500 return []point{startPoint, endPoint} 501 } 502 503 func (r *builder) buildFromNot(expr *memex.ScalarFunction) []point { 504 switch n := expr.FuncName.L; n { 505 case ast.IsTruthWithoutNull: 506 return r.buildFromIsTrue(expr, 1, false) 507 case ast.IsTruthWithNull: 508 return r.buildFromIsTrue(expr, 1, true) 509 case ast.IsFalsity: 510 return r.buildFromIsFalse(expr, 1) 511 case ast.In: 512 var ( 513 isUnsignedIntDefCaus bool 514 nonNegativePos int 515 ) 516 rangePoints, hasNull := r.buildFromIn(expr) 517 if hasNull { 518 return nil 519 } 520 if x, ok := expr.GetArgs()[0].(*memex.DeferredCauset); ok { 521 isUnsignedIntDefCaus = allegrosql.HasUnsignedFlag(x.RetType.Flag) && allegrosql.IsIntegerType(x.RetType.Tp) 522 } 523 // negative ranges can be directly ignored for unsigned int defCausumns. 524 if isUnsignedIntDefCaus { 525 for nonNegativePos = 0; nonNegativePos < len(rangePoints); nonNegativePos += 2 { 526 if rangePoints[nonNegativePos].value.HoTT() == types.HoTTUint64 || rangePoints[nonNegativePos].value.GetInt64() >= 0 { 527 break 528 } 529 } 530 rangePoints = rangePoints[nonNegativePos:] 531 } 532 retRangePoints := make([]point, 0, 2+len(rangePoints)) 533 previousValue := types.Causet{} 534 for i := 0; i < len(rangePoints); i += 2 { 535 retRangePoints = append(retRangePoints, point{value: previousValue, start: true, excl: true}) 536 retRangePoints = append(retRangePoints, point{value: rangePoints[i].value, excl: true}) 537 previousValue = rangePoints[i].value 538 } 539 // Append the interval (last element, max value]. 540 retRangePoints = append(retRangePoints, point{value: previousValue, start: true, excl: true}) 541 retRangePoints = append(retRangePoints, point{value: types.MaxValueCauset()}) 542 return retRangePoints 543 case ast.Like: 544 // Pattern not like is not supported. 545 r.err = ErrUnsupportedType.GenWithStack("NOT LIKE is not supported.") 546 return fullRange 547 case ast.IsNull: 548 startPoint := point{value: types.MinNotNullCauset(), start: true} 549 endPoint := point{value: types.MaxValueCauset()} 550 return []point{startPoint, endPoint} 551 } 552 return nil 553 } 554 555 func (r *builder) buildFromScalarFunc(expr *memex.ScalarFunction) []point { 556 switch op := expr.FuncName.L; op { 557 case ast.GE, ast.GT, ast.LT, ast.LE, ast.EQ, ast.NE, ast.NullEQ: 558 return r.buildFormBinOp(expr) 559 case ast.LogicAnd: 560 return r.intersection(r.build(expr.GetArgs()[0]), r.build(expr.GetArgs()[1])) 561 case ast.LogicOr: 562 return r.union(r.build(expr.GetArgs()[0]), r.build(expr.GetArgs()[1])) 563 case ast.IsTruthWithoutNull: 564 return r.buildFromIsTrue(expr, 0, false) 565 case ast.IsTruthWithNull: 566 return r.buildFromIsTrue(expr, 0, true) 567 case ast.IsFalsity: 568 return r.buildFromIsFalse(expr, 0) 569 case ast.In: 570 retPoints, _ := r.buildFromIn(expr) 571 return retPoints 572 case ast.Like: 573 return r.newBuildFromPatternLike(expr) 574 case ast.IsNull: 575 startPoint := point{start: true} 576 endPoint := point{} 577 return []point{startPoint, endPoint} 578 case ast.UnaryNot: 579 return r.buildFromNot(expr.GetArgs()[0].(*memex.ScalarFunction)) 580 } 581 582 return nil 583 } 584 585 func (r *builder) intersection(a, b []point) []point { 586 return r.merge(a, b, false) 587 } 588 589 func (r *builder) union(a, b []point) []point { 590 return r.merge(a, b, true) 591 } 592 593 func (r *builder) mergeSorted(a, b []point) []point { 594 ret := make([]point, 0, len(a)+len(b)) 595 i, j := 0, 0 596 for i < len(a) && j < len(b) { 597 less, err := rangePointLess(r.sc, a[i], b[j]) 598 if err != nil { 599 r.err = err 600 return nil 601 } 602 if less { 603 ret = append(ret, a[i]) 604 i++ 605 } else { 606 ret = append(ret, b[j]) 607 j++ 608 } 609 } 610 if i < len(a) { 611 ret = append(ret, a[i:]...) 612 } else if j < len(b) { 613 ret = append(ret, b[j:]...) 614 } 615 return ret 616 } 617 618 func (r *builder) merge(a, b []point, union bool) []point { 619 mergedPoints := r.mergeSorted(a, b) 620 if r.err != nil { 621 return nil 622 } 623 624 var ( 625 inRangeCount int 626 requiredInRangeCount int 627 ) 628 if union { 629 requiredInRangeCount = 1 630 } else { 631 requiredInRangeCount = 2 632 } 633 curTail := 0 634 for _, val := range mergedPoints { 635 if val.start { 636 inRangeCount++ 637 if inRangeCount == requiredInRangeCount { 638 // Just reached the required in range count, a new range started. 639 mergedPoints[curTail] = val 640 curTail++ 641 } 642 } else { 643 if inRangeCount == requiredInRangeCount { 644 // Just about to leave the required in range count, the range is ended. 645 mergedPoints[curTail] = val 646 curTail++ 647 } 648 inRangeCount-- 649 } 650 } 651 return mergedPoints[:curTail] 652 }