github.com/dolthub/go-mysql-server@v0.18.0/sql/index_builder.go (about) 1 // Copyright 2021 Dolthub, 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 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package sql 16 17 import ( 18 "math" 19 "strings" 20 21 "github.com/shopspring/decimal" 22 "gopkg.in/src-d/go-errors.v1" 23 ) 24 25 var ( 26 ErrInvalidColExpr = errors.NewKind("the expression `%s` could not be found from the index `%s`") 27 ErrRangeSimplification = errors.NewKind("attempting to simplify ranges has removed all ranges") 28 ErrInvalidRangeType = errors.NewKind("encountered the RangeType_Invalid") 29 ) 30 31 // IndexBuilder builds ranges based on the combination of calls made for the given index, and then relies on the Index 32 // to return an IndexLookup from the created ranges. 33 type IndexBuilder struct { 34 idx Index 35 isInvalid bool 36 err error 37 colExprTypes map[string]Type 38 ranges map[string][]RangeColumnExpr 39 } 40 41 // NewIndexBuilder returns a new IndexBuilder. Used internally to construct a range that will later be passed to 42 // integrators through the Index function NewLookup. 43 func NewIndexBuilder(idx Index) *IndexBuilder { 44 colExprTypes := make(map[string]Type) 45 ranges := make(map[string][]RangeColumnExpr) 46 for _, cet := range idx.ColumnExpressionTypes() { 47 colExprTypes[strings.ToLower(cet.Expression)] = cet.Type 48 ranges[strings.ToLower(cet.Expression)] = []RangeColumnExpr{AllRangeColumnExpr(cet.Type)} 49 } 50 return &IndexBuilder{ 51 idx: idx, 52 isInvalid: false, 53 err: nil, 54 colExprTypes: colExprTypes, 55 ranges: ranges, 56 } 57 } 58 59 func ceil(val interface{}) interface{} { 60 switch v := val.(type) { 61 case float32: 62 return float32(math.Ceil(float64(v))) 63 case float64: 64 return math.Ceil(v) 65 case decimal.Decimal: 66 return v.Ceil() 67 case string: 68 dec, err := decimal.NewFromString(v) 69 if err != nil { 70 return v 71 } 72 return ceil(dec) 73 case []byte: 74 return ceil(string(v)) 75 default: 76 return v 77 } 78 } 79 80 func floor(val interface{}) interface{} { 81 switch v := val.(type) { 82 case float32: 83 return float32(math.Floor(float64(v))) 84 case float64: 85 return math.Floor(v) 86 case decimal.Decimal: 87 return v.Floor() 88 case string: 89 dec, err := decimal.NewFromString(v) 90 if err != nil { 91 return v 92 } 93 return floor(dec) 94 case []byte: 95 return floor(string(v)) 96 default: 97 return v 98 } 99 } 100 101 // Equals represents colExpr = key. For IN expressions, pass all of them in the same Equals call. 102 func (b *IndexBuilder) Equals(ctx *Context, colExpr string, keys ...interface{}) *IndexBuilder { 103 if b.isInvalid { 104 return b 105 } 106 typ, ok := b.colExprTypes[colExpr] 107 if !ok { 108 b.isInvalid = true 109 b.err = ErrInvalidColExpr.New(colExpr, b.idx.ID()) 110 return b 111 } 112 potentialRanges := make([]RangeColumnExpr, len(keys)) 113 for i, k := range keys { 114 // if converting from float to int results in rounding, then it's empty range 115 if t, ok := typ.(NumberType); ok && !t.IsFloat() { 116 f, c := floor(k), ceil(k) 117 switch k.(type) { 118 case float32, float64: 119 if f != c { 120 potentialRanges[i] = EmptyRangeColumnExpr(typ) 121 continue 122 } 123 case decimal.Decimal: 124 if !f.(decimal.Decimal).Equals(c.(decimal.Decimal)) { 125 potentialRanges[i] = EmptyRangeColumnExpr(typ) 126 continue 127 } 128 } 129 } 130 131 res, _, err := typ.Convert(k) 132 if err != nil { 133 b.isInvalid = true 134 b.err = err 135 return b 136 } 137 potentialRanges[i] = ClosedRangeColumnExpr(res, res, typ) 138 } 139 b.updateCol(ctx, colExpr, potentialRanges...) 140 return b 141 } 142 143 // NotEquals represents colExpr <> key. 144 func (b *IndexBuilder) NotEquals(ctx *Context, colExpr string, key interface{}) *IndexBuilder { 145 if b.isInvalid { 146 return b 147 } 148 typ, ok := b.colExprTypes[colExpr] 149 if !ok { 150 b.isInvalid = true 151 b.err = ErrInvalidColExpr.New(colExpr, b.idx.ID()) 152 return b 153 } 154 155 // if converting from float to int results in rounding, then it's entire range (excluding nulls) 156 f, c := floor(key), ceil(key) 157 switch key.(type) { 158 case float32, float64: 159 if f != c { 160 b.updateCol(ctx, colExpr, NotNullRangeColumnExpr(typ)) 161 return b 162 } 163 case decimal.Decimal: 164 if !f.(decimal.Decimal).Equals(c.(decimal.Decimal)) { 165 b.updateCol(ctx, colExpr, NotNullRangeColumnExpr(typ)) 166 return b 167 } 168 } 169 170 key, _, err := typ.Convert(key) 171 if err != nil { 172 b.isInvalid = true 173 b.err = err 174 return b 175 } 176 177 b.updateCol(ctx, colExpr, GreaterThanRangeColumnExpr(key, typ), LessThanRangeColumnExpr(key, typ)) 178 if !b.isInvalid { 179 ranges, err := SimplifyRangeColumn(b.ranges[colExpr]...) 180 if err != nil { 181 b.isInvalid = true 182 b.err = err 183 return b 184 } 185 if len(ranges) == 0 { 186 b.isInvalid = true 187 return b 188 } 189 b.ranges[colExpr] = ranges 190 } 191 return b 192 } 193 194 // GreaterThan represents colExpr > key. 195 func (b *IndexBuilder) GreaterThan(ctx *Context, colExpr string, key interface{}) *IndexBuilder { 196 if b.isInvalid { 197 return b 198 } 199 typ, ok := b.colExprTypes[colExpr] 200 if !ok { 201 b.isInvalid = true 202 b.err = ErrInvalidColExpr.New(colExpr, b.idx.ID()) 203 return b 204 } 205 206 if t, ok := typ.(NumberType); ok && !t.IsFloat() { 207 key = floor(key) 208 } 209 210 key, _, err := typ.Convert(key) 211 if err != nil { 212 b.isInvalid = true 213 b.err = err 214 return b 215 } 216 217 b.updateCol(ctx, colExpr, GreaterThanRangeColumnExpr(key, typ)) 218 return b 219 } 220 221 // GreaterOrEqual represents colExpr >= key. 222 func (b *IndexBuilder) GreaterOrEqual(ctx *Context, colExpr string, key interface{}) *IndexBuilder { 223 if b.isInvalid { 224 return b 225 } 226 typ, ok := b.colExprTypes[colExpr] 227 if !ok { 228 b.isInvalid = true 229 b.err = ErrInvalidColExpr.New(colExpr, b.idx.ID()) 230 return b 231 } 232 233 var exclude bool 234 if t, ok := typ.(NumberType); ok && !t.IsFloat() { 235 newKey := floor(key) 236 switch key.(type) { 237 case float32, float64: 238 exclude = key != newKey 239 case decimal.Decimal: 240 exclude = !key.(decimal.Decimal).Equals(newKey.(decimal.Decimal)) 241 } 242 key = newKey 243 } 244 245 key, _, err := typ.Convert(key) 246 if err != nil { 247 b.isInvalid = true 248 b.err = err 249 return b 250 } 251 252 var rangeColExpr RangeColumnExpr 253 if exclude { 254 rangeColExpr = GreaterThanRangeColumnExpr(key, typ) 255 } else { 256 rangeColExpr = GreaterOrEqualRangeColumnExpr(key, typ) 257 } 258 b.updateCol(ctx, colExpr, rangeColExpr) 259 260 return b 261 } 262 263 // LessThan represents colExpr < key. 264 func (b *IndexBuilder) LessThan(ctx *Context, colExpr string, key interface{}) *IndexBuilder { 265 if b.isInvalid { 266 return b 267 } 268 typ, ok := b.colExprTypes[colExpr] 269 if !ok { 270 b.isInvalid = true 271 b.err = ErrInvalidColExpr.New(colExpr, b.idx.ID()) 272 return b 273 } 274 275 if t, ok := typ.(NumberType); ok && !t.IsFloat() { 276 key = ceil(key) 277 } 278 279 key, _, err := typ.Convert(key) 280 if err != nil { 281 b.isInvalid = true 282 b.err = err 283 return b 284 } 285 286 b.updateCol(ctx, colExpr, LessThanRangeColumnExpr(key, typ)) 287 return b 288 } 289 290 // LessOrEqual represents colExpr <= key. 291 func (b *IndexBuilder) LessOrEqual(ctx *Context, colExpr string, key interface{}) *IndexBuilder { 292 if b.isInvalid { 293 return b 294 } 295 typ, ok := b.colExprTypes[colExpr] 296 if !ok { 297 b.isInvalid = true 298 b.err = ErrInvalidColExpr.New(colExpr, b.idx.ID()) 299 return b 300 } 301 302 var exclude bool 303 if t, ok := typ.(NumberType); ok && !t.IsFloat() { 304 newKey := ceil(key) 305 switch key.(type) { 306 case float32, float64: 307 exclude = key != newKey 308 case decimal.Decimal: 309 exclude = !key.(decimal.Decimal).Equals(newKey.(decimal.Decimal)) 310 } 311 key = newKey 312 } 313 314 key, _, err := typ.Convert(key) 315 if err != nil { 316 b.isInvalid = true 317 b.err = err 318 return b 319 } 320 321 var rangeColExpr RangeColumnExpr 322 if exclude { 323 rangeColExpr = LessThanRangeColumnExpr(key, typ) 324 } else { 325 rangeColExpr = LessOrEqualRangeColumnExpr(key, typ) 326 } 327 b.updateCol(ctx, colExpr, rangeColExpr) 328 329 return b 330 } 331 332 // IsNull represents colExpr = nil 333 func (b *IndexBuilder) IsNull(ctx *Context, colExpr string) *IndexBuilder { 334 if b.isInvalid { 335 return b 336 } 337 typ, ok := b.colExprTypes[colExpr] 338 if !ok { 339 b.isInvalid = true 340 b.err = ErrInvalidColExpr.New(colExpr, b.idx.ID()) 341 return b 342 } 343 b.updateCol(ctx, colExpr, NullRangeColumnExpr(typ)) 344 345 return b 346 } 347 348 // IsNotNull represents colExpr != nil 349 func (b *IndexBuilder) IsNotNull(ctx *Context, colExpr string) *IndexBuilder { 350 if b.isInvalid { 351 return b 352 } 353 typ, ok := b.colExprTypes[colExpr] 354 if !ok { 355 b.isInvalid = true 356 b.err = ErrInvalidColExpr.New(colExpr, b.idx.ID()) 357 return b 358 } 359 b.updateCol(ctx, colExpr, NotNullRangeColumnExpr(typ)) 360 361 return b 362 } 363 364 // Ranges returns all ranges for this index builder. If the builder is in an error state then this returns nil. 365 func (b *IndexBuilder) Ranges(ctx *Context) RangeCollection { 366 if b.err != nil { 367 return nil 368 } 369 // An invalid builder that did not error got into a state where no columns will ever match, so we return an empty range 370 if b.isInvalid { 371 cets := b.idx.ColumnExpressionTypes() 372 emptyRange := make(Range, len(cets)) 373 for i, cet := range cets { 374 emptyRange[i] = EmptyRangeColumnExpr(cet.Type) 375 } 376 return RangeCollection{emptyRange} 377 } 378 var allColumns [][]RangeColumnExpr 379 for _, colExpr := range b.idx.Expressions() { 380 ranges, ok := b.ranges[strings.ToLower(colExpr)] 381 if !ok { 382 // An index builder is guaranteed to cover the first n expressions, so if we hit an expression that we do 383 // not have an entry for then we've hit all the ranges. 384 break 385 } 386 allColumns = append(allColumns, ranges) 387 } 388 389 // In the builder ranges map we store multiple column expressions per column, however we want all permutations to 390 // be their own range, so here we're creating a new range for every permutation. 391 colCounts := make([]int, len(allColumns)) 392 permutation := make([]int, len(allColumns)) 393 for i, rangeColumn := range allColumns { 394 colCounts[i] = len(rangeColumn) 395 } 396 var ranges []Range 397 exit := false 398 for !exit { 399 exit = true 400 currentRange := make(Range, len(allColumns)) 401 for colIdx, exprCount := range colCounts { 402 permutation[colIdx] = (permutation[colIdx] + 1) % exprCount 403 if permutation[colIdx] != 0 { 404 exit = false 405 break 406 } 407 } 408 for colIdx, exprIdx := range permutation { 409 currentRange[colIdx] = allColumns[colIdx][exprIdx] 410 } 411 isempty, err := currentRange.IsEmpty() 412 if err != nil { 413 b.err = err 414 return nil 415 } 416 if !isempty { 417 ranges = append(ranges, currentRange) 418 } 419 } 420 if len(ranges) == 0 { 421 cets := b.idx.ColumnExpressionTypes() 422 emptyRange := make(Range, len(cets)) 423 for i, cet := range cets { 424 emptyRange[i] = EmptyRangeColumnExpr(cet.Type) 425 } 426 return RangeCollection{emptyRange} 427 } 428 return ranges 429 } 430 431 // Build constructs a new IndexLookup based on the ranges that have been built internally by this builder. 432 func (b *IndexBuilder) Build(ctx *Context) (IndexLookup, error) { 433 if b.err != nil { 434 return emptyLookup, b.err 435 } else { 436 ranges := b.Ranges(ctx) 437 if len(ranges) == 0 { 438 return emptyLookup, nil 439 } 440 return IndexLookup{Index: b.idx, Ranges: ranges}, nil 441 } 442 } 443 444 // updateCol updates the internal columns with the given ranges by intersecting each given range with each existing 445 // range. That means that each given range is treated as an OR with respect to the other given ranges. If multiple 446 // ranges are to be intersected with respect to one another, multiple calls to updateCol should be made. 447 func (b *IndexBuilder) updateCol(ctx *Context, colExpr string, potentialRanges ...RangeColumnExpr) { 448 if len(potentialRanges) == 0 { 449 return 450 } 451 452 currentRanges, ok := b.ranges[colExpr] 453 if !ok { 454 b.ranges[colExpr] = potentialRanges 455 return 456 } 457 458 var newRanges []RangeColumnExpr 459 for _, currentRange := range currentRanges { 460 for _, potentialRange := range potentialRanges { 461 462 newRange, ok, err := currentRange.TryIntersect(potentialRange) 463 if err != nil { 464 b.isInvalid = true 465 if !ErrInvalidValue.Is(err) { 466 b.err = err 467 } 468 return 469 } 470 if ok { 471 isempty, err := newRange.IsEmpty() 472 if err != nil { 473 b.isInvalid = true 474 b.err = err 475 return 476 } 477 if !isempty { 478 newRanges = append(newRanges, newRange) 479 } 480 } 481 } 482 } 483 484 // If we end up with zero ranges then we had an impossible combination, such as (x < 1 AND x > 1) 485 if len(newRanges) == 0 { 486 b.isInvalid = true 487 return 488 } 489 b.ranges[colExpr] = newRanges 490 } 491 492 // SpatialIndexBuilder is like the IndexBuilder, but spatial 493 type SpatialIndexBuilder struct { 494 idx Index 495 typ Type 496 rng RangeColumnExpr 497 } 498 499 func NewSpatialIndexBuilder(idx Index) *SpatialIndexBuilder { 500 return &SpatialIndexBuilder{idx: idx, typ: idx.ColumnExpressionTypes()[0].Type} 501 } 502 503 func (b *SpatialIndexBuilder) AddRange(lower, upper interface{}) *SpatialIndexBuilder { 504 b.rng = RangeColumnExpr{ 505 LowerBound: Below{Key: lower}, 506 UpperBound: Above{Key: upper}, 507 Typ: b.typ, 508 } 509 return b 510 } 511 512 func (b *SpatialIndexBuilder) Build() (IndexLookup, error) { 513 return IndexLookup{ 514 Index: b.idx, 515 Ranges: RangeCollection{{b.rng}}, 516 IsSpatialLookup: true, 517 }, nil 518 }