github.com/dolthub/go-mysql-server@v0.18.0/sql/plan/indexed_table_access.go (about) 1 // Copyright 2020-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 plan 16 17 import ( 18 "fmt" 19 "strings" 20 21 "gopkg.in/src-d/go-errors.v1" 22 23 "github.com/dolthub/go-mysql-server/sql" 24 ) 25 26 type itaType uint8 27 28 const ( 29 ItaTypeStatic itaType = iota 30 ItaTypeLookup 31 ) 32 33 var ErrInvalidLookupForIndexedTable = errors.NewKind("indexable table does not support given lookup: %s") 34 35 // IndexedTableAccess represents an indexed lookup of a particular plan.TableNode. The values for the key used to access 36 // the indexed table is provided in RowIter(), or during static analysis. 37 type IndexedTableAccess struct { 38 TableNode sql.TableNode 39 lb *LookupBuilder 40 lookup sql.IndexLookup 41 Table sql.IndexedTable 42 Typ itaType 43 id sql.TableId 44 cols sql.ColSet 45 } 46 47 var _ sql.Table = (*IndexedTableAccess)(nil) 48 var _ sql.Node = (*IndexedTableAccess)(nil) 49 var _ sql.Nameable = (*IndexedTableAccess)(nil) 50 var _ sql.Expressioner = (*IndexedTableAccess)(nil) 51 var _ sql.CollationCoercible = (*IndexedTableAccess)(nil) 52 var _ sql.TableNode = (*IndexedTableAccess)(nil) 53 54 // NewIndexedAccessForTableNode creates an IndexedTableAccess node if the resolved table embeds 55 // an IndexAddressableTable, otherwise returns an error. 56 func NewIndexedAccessForTableNode(node sql.TableNode, lb *LookupBuilder) (*IndexedTableAccess, error) { 57 var table = node.UnderlyingTable() 58 iaTable, ok := table.(sql.IndexAddressableTable) 59 if !ok { 60 return nil, fmt.Errorf("table is not index addressable: %s", table.Name()) 61 } 62 63 lookup, err := lb.GetLookup(lb.GetZeroKey()) 64 if err != nil { 65 return nil, err 66 } 67 if !lookup.Index.CanSupport(lookup.Ranges...) { 68 return nil, ErrInvalidLookupForIndexedTable.New(lookup.Ranges.DebugString()) 69 } 70 var indexedTable sql.IndexedTable 71 indexedTable = iaTable.IndexedAccess(lookup) 72 if err != nil { 73 return nil, err 74 } 75 76 if mtn, ok := node.(sql.MutableTableNode); ok { 77 mtn, err = mtn.WithTable(indexedTable) 78 if err != nil { 79 return nil, err 80 } 81 82 indexedTable, ok = mtn.WrappedTable().(sql.IndexedTable) 83 if !ok { 84 return nil, fmt.Errorf("table is not index addressable: %s", table.Name()) 85 } 86 87 node = mtn 88 } 89 90 var id sql.TableId 91 var cols sql.ColSet 92 if tin, ok := node.(TableIdNode); ok { 93 id = tin.Id() 94 cols = tin.Columns() 95 } 96 97 return &IndexedTableAccess{ 98 TableNode: node, 99 lb: lb, 100 Table: indexedTable, 101 Typ: ItaTypeLookup, 102 id: id, 103 cols: cols, 104 }, nil 105 } 106 107 // NewStaticIndexedAccessForTableNode creates an IndexedTableAccess node if the resolved table embeds 108 // an IndexAddressableTable, otherwise returns an error. 109 func NewStaticIndexedAccessForTableNode(node sql.TableNode, lookup sql.IndexLookup) (*IndexedTableAccess, error) { 110 var table sql.Table 111 table = node.UnderlyingTable() 112 iaTable, ok := table.(sql.IndexAddressableTable) 113 if !ok { 114 return nil, fmt.Errorf("table is not index addressable: %s", table.Name()) 115 } 116 117 if !lookup.Index.CanSupport(lookup.Ranges...) { 118 return nil, ErrInvalidLookupForIndexedTable.New(lookup.Ranges.DebugString()) 119 } 120 indexedTable := iaTable.IndexedAccess(lookup) 121 122 if mtn, ok := node.(sql.MutableTableNode); ok { 123 var err error 124 mtn, err = mtn.WithTable(indexedTable) 125 if err != nil { 126 return nil, err 127 } 128 129 indexedTable, ok = mtn.WrappedTable().(sql.IndexedTable) 130 if !ok { 131 return nil, fmt.Errorf("table is not index addressable: %s", table.Name()) 132 } 133 134 node = mtn 135 } 136 137 var id sql.TableId 138 var cols sql.ColSet 139 if tin, ok := node.(TableIdNode); ok { 140 id = tin.Id() 141 cols = tin.Columns() 142 } 143 144 return &IndexedTableAccess{ 145 TableNode: node, 146 lookup: lookup, 147 Table: indexedTable, 148 Typ: ItaTypeStatic, 149 id: id, 150 cols: cols, 151 }, nil 152 } 153 154 // NewStaticIndexedAccessForFullTextTable creates an IndexedTableAccess node for Full-Text tables, which have a 155 // different behavior compared to other indexed tables. 156 func NewStaticIndexedAccessForFullTextTable(node sql.TableNode, lookup sql.IndexLookup, ftTable sql.IndexedTable) *IndexedTableAccess { 157 return &IndexedTableAccess{ 158 TableNode: node, 159 lookup: lookup, 160 Table: ftTable, 161 Typ: ItaTypeStatic, 162 } 163 } 164 165 func (i *IndexedTableAccess) WithDatabase(database sql.Database) (sql.Node, error) { 166 return i, nil 167 } 168 169 func (i *IndexedTableAccess) UnderlyingTable() sql.Table { 170 return i.TableNode.UnderlyingTable() 171 } 172 173 // WithId implements sql.TableIdNode 174 func (i *IndexedTableAccess) WithId(id sql.TableId) TableIdNode { 175 ret := *i 176 ret.id = id 177 return &ret 178 } 179 180 // Id implements sql.TableIdNode 181 func (i *IndexedTableAccess) Id() sql.TableId { 182 return i.id 183 } 184 185 // WithColumns implements sql.TableIdNode 186 func (i *IndexedTableAccess) WithColumns(set sql.ColSet) TableIdNode { 187 ret := *i 188 ret.cols = set 189 return &ret 190 } 191 192 // Columns implements sql.TableIdNode 193 func (i *IndexedTableAccess) Columns() sql.ColSet { 194 return i.cols 195 } 196 197 func (i *IndexedTableAccess) IsStatic() bool { 198 return !i.lookup.IsEmpty() 199 } 200 201 func (i *IndexedTableAccess) Resolved() bool { 202 return i.TableNode.Resolved() 203 } 204 205 func (i *IndexedTableAccess) IsReadOnly() bool { 206 return true 207 } 208 209 func (i *IndexedTableAccess) Schema() sql.Schema { 210 return i.TableNode.Schema() 211 } 212 213 func (i *IndexedTableAccess) Collation() sql.CollationID { 214 return i.TableNode.Collation() 215 } 216 217 func (i *IndexedTableAccess) Comment() string { 218 if ct, ok := i.Table.(sql.CommentedTable); ok { 219 return ct.Comment() 220 } 221 return "" 222 } 223 224 func (i *IndexedTableAccess) Children() []sql.Node { 225 return nil 226 } 227 228 func (i *IndexedTableAccess) WithChildren(children ...sql.Node) (sql.Node, error) { 229 if len(children) != 0 { 230 return nil, sql.ErrInvalidChildrenNumber.New(i, len(children), 0) 231 } 232 233 return i, nil 234 } 235 236 func (i *IndexedTableAccess) Name() string { 237 return i.TableNode.Name() 238 } 239 240 func (i *IndexedTableAccess) WithName(s string) sql.Node { 241 ret := *i 242 ret.TableNode = i.TableNode.(sql.RenameableNode).WithName(s).(sql.TableNode) 243 return &ret 244 } 245 246 func (i *IndexedTableAccess) Database() sql.Database { 247 return i.TableNode.Database() 248 } 249 250 func (i *IndexedTableAccess) CheckPrivileges(ctx *sql.Context, opChecker sql.PrivilegedOperationChecker) bool { 251 return i.TableNode.CheckPrivileges(ctx, opChecker) 252 } 253 254 // CollationCoercibility implements the interface sql.CollationCoercible. 255 func (i *IndexedTableAccess) CollationCoercibility(ctx *sql.Context) (collation sql.CollationID, coercibility byte) { 256 return i.TableNode.CollationCoercibility(ctx) 257 } 258 259 func (i *IndexedTableAccess) Index() sql.Index { 260 if !i.lookup.IsEmpty() { 261 return i.lookup.Index 262 } 263 return i.lb.index 264 } 265 266 // CanBuildIndex returns whether an index lookup on this table can be successfully built for a zero-valued key. For a 267 // static lookup, no lookup needs to be built, so returns true. 268 func (i *IndexedTableAccess) CanBuildIndex(ctx *sql.Context) (bool, error) { 269 // If the lookup was provided at analysis time (static evaluation), then an index was already built 270 if !i.lookup.IsEmpty() { 271 return true, nil 272 } 273 274 key := i.lb.GetZeroKey() 275 lookup, err := i.lb.GetLookup(key) 276 return err == nil && !lookup.IsEmpty(), nil 277 } 278 279 func (i *IndexedTableAccess) GetLookup(ctx *sql.Context, row sql.Row) (sql.IndexLookup, error) { 280 // if the lookup was provided at analysis time (static evaluation), use it. 281 if !i.lookup.IsEmpty() { 282 return i.lookup, nil 283 } 284 285 key, err := i.lb.GetKey(ctx, row) 286 if err != nil { 287 return sql.IndexLookup{}, err 288 } 289 return i.lb.GetLookup(key) 290 } 291 292 func (i *IndexedTableAccess) getLookup2(ctx *sql.Context, row sql.Row2) (sql.IndexLookup, error) { 293 // if the lookup was provided at analysis time (static evaluation), use it. 294 if !i.lookup.IsEmpty() { 295 return i.lookup, nil 296 } 297 298 key, err := i.lb.GetKey2(ctx, row) 299 if err != nil { 300 return sql.IndexLookup{}, err 301 } 302 return i.lb.GetLookup(key) 303 } 304 305 func (i *IndexedTableAccess) String() string { 306 pr := sql.NewTreePrinter() 307 pr.WriteNode("IndexedTableAccess(%s)", i.TableNode.Name()) 308 var children []string 309 children = append(children, fmt.Sprintf("index: %s", formatIndexDecoratorString(i.Index()))) 310 if !i.lookup.IsEmpty() { 311 children = append(children, fmt.Sprintf("filters: %s", i.lookup.Ranges.DebugString())) 312 } 313 314 if pt, ok := i.Table.(sql.ProjectedTable); ok { 315 projections := pt.Projections() 316 if projections != nil { 317 columns := make([]string, len(projections)) 318 for i, c := range projections { 319 columns[i] = strings.ToLower(c) 320 } 321 children = append(children, fmt.Sprintf("columns: %v", columns)) 322 } 323 } 324 325 if i.lb != nil && len(i.lb.keyExprs) > 0 { 326 keys := make([]string, len(i.lb.keyExprs)) 327 for i, e := range i.lb.keyExprs { 328 keys[i] = e.String() 329 } 330 children = append(children, fmt.Sprintf("keys: %s", strings.Join(keys, ", "))) 331 } 332 333 if ft, ok := i.Table.(sql.FilteredTable); ok { 334 var filters []string 335 for _, f := range ft.Filters() { 336 filters = append(filters, f.String()) 337 } 338 if len(filters) > 0 { 339 pr.WriteChildren(fmt.Sprintf("filters: %v", filters)) 340 } 341 } 342 343 if i.lookup.IsReverse { 344 children = append(children, fmt.Sprintf("reverse: %v", i.lookup.IsReverse)) 345 } 346 347 pr.WriteChildren(children...) 348 return pr.String() 349 } 350 351 func formatIndexDecoratorString(idx sql.Index) string { 352 var expStrs []string 353 expStrs = append(expStrs, idx.Expressions()...) 354 return fmt.Sprintf("[%s]", strings.Join(expStrs, ",")) 355 } 356 357 func (i *IndexedTableAccess) DebugString() string { 358 pr := sql.NewTreePrinter() 359 pr.WriteNode("IndexedTableAccess(%s)", i.TableNode.Name()) 360 var children []string 361 children = append(children, fmt.Sprintf("index: %s", formatIndexDecoratorString(i.Index()))) 362 if !i.lookup.IsEmpty() { 363 children = append(children, fmt.Sprintf("static: %s", i.lookup.Ranges.DebugString())) 364 if i.lookup.IsReverse { 365 children = append(children, fmt.Sprintf("reverse: %v", i.lookup.IsReverse)) 366 } 367 } else { 368 var filters []string 369 for _, e := range i.lb.keyExprs { 370 filters = append(filters, sql.DebugString(e)) 371 } 372 if len(filters) > 0 { 373 children = append(children, fmt.Sprintf(fmt.Sprintf("keys: %v", filters))) 374 } 375 } 376 377 children = append(children, fmt.Sprintf("colSet: %s", i.Columns()), fmt.Sprintf("tableId: %d", i.Id())) 378 379 // TableWrappers may want to print their own debug info 380 if wrapper, ok := i.Table.(sql.TableWrapper); ok { 381 if ds, ok := wrapper.(sql.DebugStringer); ok { 382 children = append(children, sql.DebugString(ds)) 383 } 384 } else { 385 children = append(children, TableDebugString(i.Table)) 386 } 387 388 pr.WriteChildren(children...) 389 return pr.String() 390 } 391 392 // Expressions implements sql.Expressioner 393 func (i *IndexedTableAccess) Expressions() []sql.Expression { 394 if !i.lookup.IsEmpty() { 395 return nil 396 } 397 return i.lb.Expressions() 398 } 399 400 func (i *IndexedTableAccess) NullMask() []bool { 401 if !i.lookup.IsEmpty() { 402 return nil 403 } 404 return i.lb.matchesNullMask 405 } 406 407 // WithExpressions implements sql.Expressioner 408 func (i *IndexedTableAccess) WithExpressions(exprs ...sql.Expression) (sql.Node, error) { 409 if !i.lookup.IsEmpty() { 410 if len(exprs) != 0 { 411 return nil, sql.ErrInvalidChildrenNumber.New(i, len(exprs), 0) 412 } 413 n := *i 414 return &n, nil 415 } 416 lb, err := i.lb.WithExpressions(i, exprs...) 417 if err != nil { 418 return nil, err 419 } 420 ret := *i 421 ret.lb = lb 422 return &ret, nil 423 } 424 425 func (i IndexedTableAccess) WithTable(table sql.IndexedTable) (sql.Node, error) { 426 i.Table = table 427 return &i, nil 428 } 429 430 // Partitions implements sql.Table 431 func (i *IndexedTableAccess) Partitions(ctx *sql.Context) (sql.PartitionIter, error) { 432 return i.Table.LookupPartitions(ctx, i.lookup) 433 } 434 435 // PartitionRows implements sql.Table 436 func (i *IndexedTableAccess) PartitionRows(ctx *sql.Context, partition sql.Partition) (sql.RowIter, error) { 437 return i.Table.PartitionRows(ctx, partition) 438 } 439 440 // GetIndexLookup returns the sql.IndexLookup from an IndexedTableAccess. 441 // This method is exported for use in integration tests. 442 func GetIndexLookup(ita *IndexedTableAccess) sql.IndexLookup { 443 return ita.lookup 444 } 445 446 type lookupBuilderKey []interface{} 447 448 // LookupBuilder abstracts secondary table access for an LookupJoin. 449 // A row from the primary table is first evaluated on the secondary index's 450 // expressions (columns) to produce a lookupBuilderKey. Consider the 451 // query below, assuming B has an index `xy (x,y)`: 452 // 453 // select * from A join B on a.x = b.x AND a.y = b.y 454 // 455 // Assume we choose A as the primary row source and B as a secondary lookup 456 // on `xy`. For every row in A, we will produce a lookupBuilderKey on B 457 // using the join condition. For the A row (x=1,y=2), the lookup key into B 458 // will be (1,2) to reflect the B-xy index access. 459 // 460 // Then we construct a sql.RangeCollection to represent the (1,2) point 461 // lookup into B-xy. The collection will always be a single range, because 462 // a point lookup cannot be a disjoint set of ranges. The range will also 463 // have the same dimension as the index itself. If the join condition is 464 // a partial prefix on the index (ex: INDEX x (x)), the unfiltered columns 465 // are padded. 466 // 467 // The <=> filter is a special case for two reasons. 1) It is not a point 468 // lookup, the corresponding range will either be IsNull or IsNotNull 469 // depending on whether the primary row key column is nil or not, 470 // respectfully. 2) The format of the output range is variable, while 471 // equality ranges are identical except for bound values. 472 // 473 // Currently the analyzer constructs one of these and uses it for the 474 // IndexedTableAccess nodes below an indexed join, for example. This struct is 475 // also used to implement Expressioner on the IndexedTableAccess node. 476 type LookupBuilder struct { 477 keyExprs []sql.Expression 478 keyExprs2 []sql.Expression2 479 480 // When building the lookup, we will use an IndexBuilder. If the 481 // extracted lookup value is NULL, but we have a non-NULL safe 482 // comparison, then the lookup should return no values. But if the 483 // comparison is NULL-safe, then the lookup should returns indexed 484 // values having that value <=> NULL. For each |keyExpr|, this field 485 // contains |true| if the lookup should also match NULLs, and |false| 486 // otherwise. 487 matchesNullMask []bool 488 489 index sql.Index 490 491 key lookupBuilderKey 492 rang sql.Range 493 nullSafe bool 494 isPointLookup bool 495 emptyRange bool 496 cets []sql.ColumnExpressionType 497 } 498 499 func NewLookupBuilder(index sql.Index, keyExprs []sql.Expression, matchesNullMask []bool) *LookupBuilder { 500 cets := index.ColumnExpressionTypes() 501 var nullSafe = true 502 for i := range matchesNullMask { 503 if matchesNullMask[i] { 504 nullSafe = false 505 } 506 } 507 return &LookupBuilder{ 508 index: index, 509 keyExprs: keyExprs, 510 matchesNullMask: matchesNullMask, 511 cets: cets, 512 nullSafe: nullSafe, 513 isPointLookup: true, 514 } 515 } 516 517 func (lb *LookupBuilder) initializeRange(key lookupBuilderKey) { 518 lb.rang = make(sql.Range, len(lb.cets)) 519 lb.emptyRange = false 520 lb.isPointLookup = len(key) == len(lb.cets) 521 var i int 522 for i < len(key) { 523 if key[i] == nil { 524 lb.emptyRange = true 525 lb.isPointLookup = false 526 } 527 if lb.matchesNullMask[i] { 528 if key[i] == nil { 529 lb.rang[i] = sql.NullRangeColumnExpr(lb.cets[i].Type) 530 531 } else { 532 lb.rang[i] = sql.NotNullRangeColumnExpr(lb.cets[i].Type) 533 } 534 } else { 535 lb.rang[i] = sql.ClosedRangeColumnExpr(key[i], key[i], lb.cets[i].Type) 536 } 537 i++ 538 } 539 for i < len(lb.cets) { 540 lb.rang[i] = sql.AllRangeColumnExpr(lb.cets[i].Type) 541 lb.isPointLookup = false 542 i++ 543 } 544 return 545 } 546 547 func (lb *LookupBuilder) GetLookup(key lookupBuilderKey) (sql.IndexLookup, error) { 548 if lb.rang == nil { 549 lb.initializeRange(key) 550 return sql.IndexLookup{ 551 Index: lb.index, 552 Ranges: []sql.Range{lb.rang}, 553 IsPointLookup: lb.nullSafe && lb.isPointLookup && lb.index.IsUnique(), 554 IsEmptyRange: lb.emptyRange, 555 IsSpatialLookup: false, 556 }, nil 557 } 558 559 lb.emptyRange = false 560 lb.isPointLookup = len(key) == len(lb.cets) 561 for i := range key { 562 if key[i] == nil { 563 lb.emptyRange = true 564 lb.isPointLookup = false 565 } 566 if lb.matchesNullMask[i] { 567 if key[i] == nil { 568 lb.rang[i] = sql.NullRangeColumnExpr(lb.cets[i].Type) 569 } else { 570 k, _, err := lb.rang[i].Typ.Convert(key[i]) 571 if err != nil { 572 // TODO: throw warning, and this should truncate for strings 573 err = nil 574 k = lb.rang[i].Typ.Zero() 575 } 576 lb.rang[i].LowerBound = sql.Below{Key: k} 577 lb.rang[i].UpperBound = sql.Above{Key: k} 578 } 579 } else { 580 k, _, err := lb.rang[i].Typ.Convert(key[i]) 581 if err != nil { 582 // TODO: throw warning, and this should truncate for strings 583 err = nil 584 k = lb.rang[i].Typ.Zero() 585 } 586 lb.rang[i].LowerBound = sql.Below{Key: k} 587 lb.rang[i].UpperBound = sql.Above{Key: k} 588 } 589 } 590 591 return sql.IndexLookup{ 592 Index: lb.index, 593 Ranges: []sql.Range{lb.rang}, 594 IsPointLookup: lb.nullSafe && lb.isPointLookup && lb.index.IsUnique(), 595 IsEmptyRange: lb.emptyRange, 596 IsSpatialLookup: false, 597 }, nil 598 } 599 600 func (lb *LookupBuilder) GetKey(ctx *sql.Context, row sql.Row) (lookupBuilderKey, error) { 601 if lb.key == nil { 602 lb.key = make([]interface{}, len(lb.keyExprs)) 603 } 604 for i := range lb.keyExprs { 605 var err error 606 lb.key[i], err = lb.keyExprs[i].Eval(ctx, row) 607 if err != nil { 608 return nil, err 609 } 610 } 611 return lb.key, nil 612 } 613 614 func (lb *LookupBuilder) GetKey2(ctx *sql.Context, row sql.Row2) (lookupBuilderKey, error) { 615 if lb.key == nil { 616 lb.key = make([]interface{}, len(lb.keyExprs)) 617 } 618 for i := range lb.keyExprs { 619 var err error 620 lb.key[i], err = lb.keyExprs2[i].Eval2(ctx, row) 621 if err != nil { 622 return nil, err 623 } 624 } 625 return lb.key, nil 626 } 627 628 func (lb *LookupBuilder) GetZeroKey() lookupBuilderKey { 629 key := make(lookupBuilderKey, len(lb.keyExprs)) 630 for i, keyExpr := range lb.keyExprs { 631 key[i] = keyExpr.Type().Zero() 632 } 633 return key 634 } 635 636 func (lb *LookupBuilder) Index() sql.Index { 637 return lb.index 638 } 639 640 func (lb *LookupBuilder) Expressions() []sql.Expression { 641 return lb.keyExprs 642 } 643 644 func (lb *LookupBuilder) DebugString() string { 645 keyExprs := make([]string, len(lb.keyExprs)) 646 for i := range lb.keyExprs { 647 keyExprs[i] = sql.DebugString(lb.keyExprs[i]) 648 } 649 return fmt.Sprintf("on %s, using fields %s", formatIndexDecoratorString(lb.Index()), strings.Join(keyExprs, ", ")) 650 } 651 652 func (lb *LookupBuilder) WithExpressions(node sql.Node, exprs ...sql.Expression) (*LookupBuilder, error) { 653 if len(exprs) != len(lb.keyExprs) { 654 return &LookupBuilder{}, sql.ErrInvalidChildrenNumber.New(node, len(exprs), len(lb.keyExprs)) 655 } 656 ret := *lb 657 ret.keyExprs = exprs 658 return &ret, nil 659 }