github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/xform/custom_funcs.go (about) 1 // Copyright 2018 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package xform 12 13 import ( 14 "fmt" 15 "sort" 16 17 "github.com/cockroachdb/cockroach/pkg/geo/geoindex" 18 "github.com/cockroachdb/cockroach/pkg/sql/opt" 19 "github.com/cockroachdb/cockroach/pkg/sql/opt/cat" 20 "github.com/cockroachdb/cockroach/pkg/sql/opt/constraint" 21 "github.com/cockroachdb/cockroach/pkg/sql/opt/idxconstraint" 22 "github.com/cockroachdb/cockroach/pkg/sql/opt/memo" 23 "github.com/cockroachdb/cockroach/pkg/sql/opt/norm" 24 "github.com/cockroachdb/cockroach/pkg/sql/opt/ordering" 25 "github.com/cockroachdb/cockroach/pkg/sql/opt/props" 26 "github.com/cockroachdb/cockroach/pkg/sql/opt/props/physical" 27 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 28 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 29 "github.com/cockroachdb/cockroach/pkg/sql/types" 30 "github.com/cockroachdb/cockroach/pkg/util" 31 "github.com/cockroachdb/errors" 32 ) 33 34 // CustomFuncs contains all the custom match and replace functions used by 35 // the exploration rules. The unnamed xfunc.CustomFuncs allows 36 // CustomFuncs to provide a clean interface for calling functions from both the 37 // xform and xfunc packages using the same struct. 38 type CustomFuncs struct { 39 norm.CustomFuncs 40 e *explorer 41 } 42 43 // Init initializes a new CustomFuncs with the given explorer. 44 func (c *CustomFuncs) Init(e *explorer) { 45 c.CustomFuncs.Init(e.f) 46 c.e = e 47 } 48 49 // ---------------------------------------------------------------------- 50 // 51 // Scan Rules 52 // Custom match and replace functions used with scan.opt rules. 53 // 54 // ---------------------------------------------------------------------- 55 56 // IsCanonicalScan returns true if the given ScanPrivate is an original 57 // unaltered primary index Scan operator (i.e. unconstrained and not limited). 58 func (c *CustomFuncs) IsCanonicalScan(scan *memo.ScanPrivate) bool { 59 return scan.IsCanonical() 60 } 61 62 // IsLocking returns true if the ScanPrivate is configured to use a row-level 63 // locking mode. This can be the case either because the Scan is in the scope of 64 // a SELECT .. FOR [KEY] UPDATE/SHARE clause or because the Scan was configured 65 // as part of the row retrieval of a DELETE or UPDATE statement. 66 func (c *CustomFuncs) IsLocking(scan *memo.ScanPrivate) bool { 67 return scan.IsLocking() 68 } 69 70 // GenerateIndexScans enumerates all secondary indexes on the given Scan 71 // operator's table and generates an alternate Scan operator for each index that 72 // includes the set of needed columns specified in the ScanOpDef. 73 // 74 // NOTE: This does not generate index joins for non-covering indexes (except in 75 // case of ForceIndex). Index joins are usually only introduced "one level 76 // up", when the Scan operator is wrapped by an operator that constrains 77 // or limits scan output in some way (e.g. Select, Limit, InnerJoin). 78 // Index joins are only lower cost when their input does not include all 79 // rows from the table. See ConstrainScans and LimitScans for cases where 80 // index joins are introduced into the memo. 81 func (c *CustomFuncs) GenerateIndexScans(grp memo.RelExpr, scanPrivate *memo.ScanPrivate) { 82 // Iterate over all secondary indexes. 83 var iter scanIndexIter 84 iter.init(c.e.mem, scanPrivate, onlyStandardIndexes) 85 for iter.next() { 86 // Skip primary index. 87 if iter.indexOrdinal == cat.PrimaryIndex { 88 continue 89 } 90 91 // If the secondary index includes the set of needed columns, then construct 92 // a new Scan operator using that index. 93 if iter.isCovering() { 94 scan := memo.ScanExpr{ScanPrivate: *scanPrivate} 95 scan.Index = iter.indexOrdinal 96 c.e.mem.AddScanToGroup(&scan, grp) 97 continue 98 } 99 100 // Otherwise, if the index must be forced, then construct an IndexJoin 101 // operator that provides the columns missing from the index. Note that 102 // if ForceIndex=true, scanIndexIter only returns the one index that is 103 // being forced, so no need to check that here. 104 if !scanPrivate.Flags.ForceIndex { 105 continue 106 } 107 108 var sb indexScanBuilder 109 sb.init(c, scanPrivate.Table) 110 111 // Scan whatever columns we need which are available from the index, plus 112 // the PK columns. 113 newScanPrivate := *scanPrivate 114 newScanPrivate.Index = iter.indexOrdinal 115 newScanPrivate.Cols = iter.indexCols().Intersection(scanPrivate.Cols) 116 newScanPrivate.Cols.UnionWith(sb.primaryKeyCols()) 117 sb.setScan(&newScanPrivate) 118 119 sb.addIndexJoin(scanPrivate.Cols) 120 sb.build(grp) 121 } 122 } 123 124 // ---------------------------------------------------------------------- 125 // 126 // Select Rules 127 // Custom match and replace functions used with select.opt rules. 128 // 129 // ---------------------------------------------------------------------- 130 131 // GenerateConstrainedScans enumerates all secondary indexes on the Scan 132 // operator's table and tries to push the given Select filter into new 133 // constrained Scan operators using those indexes. Since this only needs to be 134 // done once per table, GenerateConstrainedScans should only be called on the 135 // original unaltered primary index Scan operator (i.e. not constrained or 136 // limited). 137 // 138 // For each secondary index that "covers" the columns needed by the scan, there 139 // are three cases: 140 // 141 // - a filter that can be completely converted to a constraint over that index 142 // generates a single constrained Scan operator (to be added to the same 143 // group as the original Select operator): 144 // 145 // (Scan $scanDef) 146 // 147 // - a filter that can be partially converted to a constraint over that index 148 // generates a constrained Scan operator in a new memo group, wrapped in a 149 // Select operator having the remaining filter (to be added to the same group 150 // as the original Select operator): 151 // 152 // (Select (Scan $scanDef) $filter) 153 // 154 // - a filter that cannot be converted to a constraint generates nothing 155 // 156 // And for a secondary index that does not cover the needed columns: 157 // 158 // - a filter that can be completely converted to a constraint over that index 159 // generates a single constrained Scan operator in a new memo group, wrapped 160 // in an IndexJoin operator that looks up the remaining needed columns (and 161 // is added to the same group as the original Select operator) 162 // 163 // (IndexJoin (Scan $scanDef) $indexJoinDef) 164 // 165 // - a filter that can be partially converted to a constraint over that index 166 // generates a constrained Scan operator in a new memo group, wrapped in an 167 // IndexJoin operator that looks up the remaining needed columns; the 168 // remaining filter is distributed above and/or below the IndexJoin, 169 // depending on which columns it references: 170 // 171 // (IndexJoin 172 // (Select (Scan $scanDef) $filter) 173 // $indexJoinDef 174 // ) 175 // 176 // (Select 177 // (IndexJoin (Scan $scanDef) $indexJoinDef) 178 // $filter 179 // ) 180 // 181 // (Select 182 // (IndexJoin 183 // (Select (Scan $scanDef) $innerFilter) 184 // $indexJoinDef 185 // ) 186 // $outerFilter 187 // ) 188 // 189 // GenerateConstrainedScans will further constrain the enumerated index scans 190 // by trying to use the check constraints and computed columns that apply to the 191 // table being scanned, as well as the partitioning defined for the index. See 192 // comments above checkColumnFilters, computedColFilters, and 193 // partitionValuesFilters for more detail. 194 func (c *CustomFuncs) GenerateConstrainedScans( 195 grp memo.RelExpr, scanPrivate *memo.ScanPrivate, explicitFilters memo.FiltersExpr, 196 ) { 197 var sb indexScanBuilder 198 sb.init(c, scanPrivate.Table) 199 200 // Generate implicit filters from constraints and computed columns and add 201 // them to the list of explicit filters provided in the query. 202 optionalFilters := c.checkConstraintFilters(scanPrivate.Table) 203 computedColFilters := c.computedColFilters(scanPrivate.Table, explicitFilters, optionalFilters) 204 optionalFilters = append(optionalFilters, computedColFilters...) 205 206 filterColumns := c.FilterOuterCols(explicitFilters) 207 filterColumns.UnionWith(c.FilterOuterCols(optionalFilters)) 208 209 // Iterate over all indexes. 210 var iter scanIndexIter 211 md := c.e.mem.Metadata() 212 tabMeta := md.TableMeta(scanPrivate.Table) 213 iter.init(c.e.mem, scanPrivate, onlyStandardIndexes) 214 for iter.next() { 215 // We only consider the partition values when a particular index can otherwise 216 // not be constrained. For indexes that are constrained, the partitioned values 217 // add no benefit as they don't really constrain anything. 218 // Furthermore, if the filters don't take advantage of the index (use any of the 219 // index columns), using the partition values add no benefit. 220 // 221 // If the index is partitioned (by list), we generate two constraints and 222 // union them: the "main" constraint and the "in-between" constraint.The 223 // "main" constraint restricts the index to the known partition ranges. The 224 // "in-between" constraint restricts the index to the rest of the ranges 225 // (i.e. everything that falls in-between the main ranges); the in-between 226 // constraint is necessary for correctness (there can be rows outside of the 227 // partitioned ranges). 228 // 229 // For both constraints, the partition-related filters are passed as 230 // "optional" which guarantees that they return no remaining filters. This 231 // allows us to merge the remaining filters from both constraints. 232 // 233 // Consider the following index and its partition: 234 // 235 // CREATE INDEX orders_by_seq_num 236 // ON orders (region, seq_num, id) 237 // STORING (total) 238 // PARTITION BY LIST (region) 239 // ( 240 // PARTITION us_east1 VALUES IN ('us-east1'), 241 // PARTITION us_west1 VALUES IN ('us-west1'), 242 // PARTITION europe_west2 VALUES IN ('europe-west2') 243 // ) 244 // 245 // The constraint generated for the query: 246 // SELECT sum(total) FROM orders WHERE seq_num >= 100 AND seq_num < 200 247 // is: 248 // [/'europe-west2'/100 - /'europe-west2'/199] 249 // [/'us-east1'/100 - /'us-east1'/199] 250 // [/'us-west1'/100 - /'us-west1'/199] 251 // 252 // The spans before europe-west2, after us-west1 and in between the defined 253 // partitions are missing. We must add these spans now, appropriately 254 // constrained using the filters. 255 // 256 // It is important that we add these spans after the partition spans are generated 257 // because otherwise these spans would merge with the partition spans and would 258 // disallow the partition spans (and the in between ones) to be constrained further. 259 // Using the partitioning example and the query above, if we added the in between 260 // spans at the same time as the partitioned ones, we would end up with a span that 261 // looked like: 262 // [ - /'europe-west2'/99] 263 // 264 // Allowing the partition spans to be constrained further and then adding 265 // the spans give us a more constrained index scan as shown below: 266 // [ - /'europe-west2') 267 // [/'europe-west2'/100 - /'europe-west2'/199] 268 // [/e'europe-west2\x00'/100 - /'us-east1') 269 // [/'us-east1'/100 - /'us-east1'/199] 270 // [/e'us-east1\x00'/100 - /'us-west1') 271 // [/'us-west1'/100 - /'us-west1'/199] 272 // [/e'us-west1\x00'/100 - ] 273 // 274 // Notice how we 'skip' all the europe-west2 rows with seq_num < 100. 275 // 276 var partitionFilters, inBetweenFilters memo.FiltersExpr 277 278 indexColumns := tabMeta.IndexKeyColumns(iter.indexOrdinal) 279 firstIndexCol := scanPrivate.Table.ColumnID(iter.index.Column(0).Ordinal) 280 if !filterColumns.Contains(firstIndexCol) && indexColumns.Intersects(filterColumns) { 281 // Calculate any partition filters if appropriate (see below). 282 partitionFilters, inBetweenFilters = c.partitionValuesFilters(scanPrivate.Table, iter.index) 283 } 284 285 // Check whether the filter (along with any partitioning filters) can constrain the index. 286 constraint, remainingFilters, ok := c.tryConstrainIndex( 287 explicitFilters, 288 append(optionalFilters, partitionFilters...), 289 scanPrivate.Table, 290 iter.indexOrdinal, 291 false, /* isInverted */ 292 ) 293 if !ok { 294 continue 295 } 296 297 if len(partitionFilters) > 0 { 298 inBetweenConstraint, inBetweenRemainingFilters, ok := c.tryConstrainIndex( 299 explicitFilters, 300 append(optionalFilters, inBetweenFilters...), 301 scanPrivate.Table, 302 iter.indexOrdinal, 303 false, /* isInverted */ 304 ) 305 if !ok { 306 panic(errors.AssertionFailedf("in-between filters didn't yield a constraint")) 307 } 308 309 constraint.UnionWith(c.e.evalCtx, inBetweenConstraint) 310 311 // Even though the partitioned constraints and the inBetween constraints 312 // were consolidated, we must make sure their Union is as well. 313 constraint.ConsolidateSpans(c.e.evalCtx) 314 315 // Add all remaining filters that need to be present in the 316 // inBetween spans. Some of the remaining filters are common 317 // between them, so we must deduplicate them. 318 remainingFilters = c.ConcatFilters(remainingFilters, inBetweenRemainingFilters) 319 remainingFilters.Sort() 320 remainingFilters.Deduplicate() 321 } 322 323 // Construct new constrained ScanPrivate. 324 newScanPrivate := *scanPrivate 325 newScanPrivate.Index = iter.indexOrdinal 326 newScanPrivate.Constraint = constraint 327 // Record whether we were able to use partitions to constrain the scan. 328 newScanPrivate.PartitionConstrainedScan = (len(partitionFilters) > 0) 329 330 // If the alternate index includes the set of needed columns, then construct 331 // a new Scan operator using that index. 332 if iter.isCovering() { 333 sb.setScan(&newScanPrivate) 334 335 // If there are remaining filters, then the constrained Scan operator 336 // will be created in a new group, and a Select operator will be added 337 // to the same group as the original operator. 338 sb.addSelect(remainingFilters) 339 340 sb.build(grp) 341 continue 342 } 343 344 // Otherwise, construct an IndexJoin operator that provides the columns 345 // missing from the index. 346 if scanPrivate.Flags.NoIndexJoin { 347 continue 348 } 349 350 // Scan whatever columns we need which are available from the index, plus 351 // the PK columns. 352 newScanPrivate.Cols = iter.indexCols().Intersection(scanPrivate.Cols) 353 newScanPrivate.Cols.UnionWith(sb.primaryKeyCols()) 354 sb.setScan(&newScanPrivate) 355 356 // If remaining filter exists, split it into one part that can be pushed 357 // below the IndexJoin, and one part that needs to stay above. 358 remainingFilters = sb.addSelectAfterSplit(remainingFilters, newScanPrivate.Cols) 359 sb.addIndexJoin(scanPrivate.Cols) 360 sb.addSelect(remainingFilters) 361 362 sb.build(grp) 363 } 364 } 365 366 // checkConstraintFilters generates all filters that we can derive from the 367 // check constraints. These are constraints that have been validated and are 368 // non-nullable. We only use non-nullable check constraints because they 369 // behave differently from filters on NULL. Check constraints are satisfied 370 // when their expression evaluates to NULL, while filters are not. 371 // 372 // For example, the check constraint a > 1 is satisfied if a is NULL but the 373 // equivalent filter a > 1 is not. 374 // 375 // These filters do not really filter any rows, they are rather facts or 376 // guarantees about the data but treating them as filters may allow some 377 // indexes to be constrained and used. Consider the following example: 378 // 379 // CREATE TABLE abc ( 380 // a INT PRIMARY KEY, 381 // b INT NOT NULL, 382 // c STRING NOT NULL, 383 // CHECK (a < 10 AND a > 1), 384 // CHECK (b < 10 AND b > 1), 385 // CHECK (c in ('first', 'second')), 386 // INDEX secondary (b, a), 387 // INDEX tertiary (c, b, a)) 388 // 389 // Now consider the query: SELECT a, b WHERE a > 5 390 // 391 // Notice that the filter provided previously wouldn't let the optimizer use 392 // the secondary or tertiary indexes. However, given that we can use the 393 // constraints on a, b and c, we can actually use the secondary and tertiary 394 // indexes. In fact, for the above query we can do the following: 395 // 396 // select 397 // ├── columns: a:1(int!null) b:2(int!null) 398 // ├── scan abc@tertiary 399 // │ ├── columns: a:1(int!null) b:2(int!null) 400 // │ └── constraint: /3/2/1: [/'first'/2/6 - /'first'/9/9] [/'second'/2/6 - /'second'/9/9] 401 // └── filters 402 // └── gt [type=bool] 403 // ├── variable: a [type=int] 404 // └── const: 5 [type=int] 405 // 406 // Similarly, the secondary index could also be used. All such index scans 407 // will be added to the memo group. 408 func (c *CustomFuncs) checkConstraintFilters(tabID opt.TableID) memo.FiltersExpr { 409 md := c.e.mem.Metadata() 410 tabMeta := md.TableMeta(tabID) 411 if tabMeta.Constraints == nil { 412 return memo.FiltersExpr{} 413 } 414 filters := *tabMeta.Constraints.(*memo.FiltersExpr) 415 // Limit slice capacity to allow the caller to append if necessary. 416 return filters[:len(filters):len(filters)] 417 } 418 419 // computedColFilters generates all filters that can be derived from the list of 420 // computed column expressions from the given table. A computed column can be 421 // used as a filter when it has a constant value. That is true when: 422 // 423 // 1. All other columns it references are constant, because other filters in 424 // the query constrain them to be so. 425 // 2. All functions in the computed column expression can be folded into 426 // constants (i.e. they do not have problematic side effects). 427 // 428 // Note that computed columns can depend on other computed columns; in general 429 // the dependencies form an acyclic directed graph. computedColFilters will 430 // return filters for all constant computed columns, regardless of the order of 431 // their dependencies. 432 // 433 // As with checkConstraintFilters, computedColFilters do not really filter any 434 // rows, they are rather facts or guarantees about the data. Treating them as 435 // filters may allow some indexes to be constrained and used. Consider the 436 // following example: 437 // 438 // CREATE TABLE t ( 439 // k INT NOT NULL, 440 // hash INT AS (k % 4) STORED, 441 // PRIMARY KEY (hash, k) 442 // ) 443 // 444 // SELECT * FROM t WHERE k = 5 445 // 446 // Notice that the filter provided explicitly wouldn't allow the optimizer to 447 // seek using the primary index (it would have to fall back to a table scan). 448 // However, column "hash" can be proven to have the constant value of 1, since 449 // it's dependent on column "k", which has the constant value of 5. This enables 450 // usage of the primary index: 451 // 452 // scan t 453 // ├── columns: k:1(int!null) hash:2(int!null) 454 // ├── constraint: /2/1: [/1/5 - /1/5] 455 // ├── key: (2) 456 // └── fd: ()-->(1) 457 // 458 // The values of both columns in that index are known, enabling a single value 459 // constraint to be generated. 460 func (c *CustomFuncs) computedColFilters( 461 tabID opt.TableID, requiredFilters, optionalFilters memo.FiltersExpr, 462 ) memo.FiltersExpr { 463 tabMeta := c.e.mem.Metadata().TableMeta(tabID) 464 if len(tabMeta.ComputedCols) == 0 { 465 return nil 466 } 467 468 // Start with set of constant columns, as derived from the list of filter 469 // conditions. 470 constCols := make(map[opt.ColumnID]opt.ScalarExpr) 471 c.findConstantFilterCols(constCols, tabID, requiredFilters) 472 c.findConstantFilterCols(constCols, tabID, optionalFilters) 473 if len(constCols) == 0 { 474 // No constant values could be derived from filters, so assume that there 475 // are also no constant computed columns. 476 return nil 477 } 478 479 // Construct a new filter condition for each computed column that is 480 // constant (i.e. all of its variables are in the constCols set). 481 var computedColFilters memo.FiltersExpr 482 for colID := range tabMeta.ComputedCols { 483 if c.tryFoldComputedCol(tabMeta, colID, constCols) { 484 constVal := constCols[colID] 485 // Note: Eq is not correct here because of NULLs. 486 eqOp := c.e.f.ConstructIs(c.e.f.ConstructVariable(colID), constVal) 487 computedColFilters = append(computedColFilters, c.e.f.ConstructFiltersItem(eqOp)) 488 } 489 } 490 return computedColFilters 491 } 492 493 // findConstantFilterCols adds to constFilterCols mappings from table column ID 494 // to the constant value of that column. It does this by iterating over the 495 // given lists of filters and finding expressions that constrain columns to a 496 // single constant value. For example: 497 // 498 // x = 5 AND y = 'foo' 499 // 500 // This would add a mapping from x => 5 and y => 'foo', which constants can 501 // then be used to prove that dependent computed columns are also constant. 502 func (c *CustomFuncs) findConstantFilterCols( 503 constFilterCols map[opt.ColumnID]opt.ScalarExpr, tabID opt.TableID, filters memo.FiltersExpr, 504 ) { 505 tab := c.e.mem.Metadata().Table(tabID) 506 for i := range filters { 507 // If filter constraints are not tight, then no way to derive constant 508 // values. 509 props := filters[i].ScalarProps() 510 if !props.TightConstraints { 511 continue 512 } 513 514 // Iterate over constraint conjuncts with a single column and single 515 // span having a single key. 516 for i, n := 0, props.Constraints.Length(); i < n; i++ { 517 cons := props.Constraints.Constraint(i) 518 if cons.Columns.Count() != 1 || cons.Spans.Count() != 1 { 519 continue 520 } 521 522 // Skip columns with a data type that uses a composite key encoding. 523 // Each of these data types can have multiple distinct values that 524 // compare equal. For example, 0 == -0 for the FLOAT data type. It's 525 // not safe to treat these as constant inputs to computed columns, 526 // since the computed expression may differentiate between the 527 // different forms of the same value. 528 colID := cons.Columns.Get(0).ID() 529 colTyp := tab.Column(tabID.ColumnOrdinal(colID)).DatumType() 530 if sqlbase.HasCompositeKeyEncoding(colTyp) { 531 continue 532 } 533 534 span := cons.Spans.Get(0) 535 if !span.HasSingleKey(c.e.evalCtx) { 536 continue 537 } 538 539 datum := span.StartKey().Value(0) 540 if datum != tree.DNull { 541 constFilterCols[colID] = c.e.f.ConstructConstVal(datum, colTyp) 542 } 543 } 544 } 545 } 546 547 // tryFoldComputedCol tries to reduce the computed column with the given column 548 // ID into a constant value, by evaluating it with respect to a set of other 549 // columns that are constant. If the computed column is constant, enter it into 550 // the constCols map and return false. Otherwise, return false. 551 func (c *CustomFuncs) tryFoldComputedCol( 552 tabMeta *opt.TableMeta, computedColID opt.ColumnID, constCols map[opt.ColumnID]opt.ScalarExpr, 553 ) bool { 554 // Check whether computed column has already been folded. 555 if _, ok := constCols[computedColID]; ok { 556 return true 557 } 558 559 var replace func(e opt.Expr) opt.Expr 560 replace = func(e opt.Expr) opt.Expr { 561 if variable, ok := e.(*memo.VariableExpr); ok { 562 // Can variable be folded? 563 if constVal, ok := constCols[variable.Col]; ok { 564 // Yes, so replace it with its constant value. 565 return constVal 566 } 567 568 // No, but that may be because the variable refers to a dependent 569 // computed column. In that case, try to recursively fold that 570 // computed column. There are no infinite loops possible because the 571 // dependency graph is guaranteed to be acyclic. 572 if _, ok := tabMeta.ComputedCols[variable.Col]; ok { 573 if c.tryFoldComputedCol(tabMeta, variable.Col, constCols) { 574 return constCols[variable.Col] 575 } 576 } 577 578 return e 579 } 580 return c.e.f.Replace(e, replace) 581 } 582 583 computedCol := tabMeta.ComputedCols[computedColID] 584 replaced := replace(computedCol).(opt.ScalarExpr) 585 586 // If the computed column is constant, enter it into the constCols map. 587 if opt.IsConstValueOp(replaced) { 588 constCols[computedColID] = replaced 589 return true 590 } 591 return false 592 } 593 594 // inBetweenFilters returns a set of filters that are required to cover all the 595 // in-between spans given a set of partition values. This is required for 596 // correctness reasons; although values are unlikely to exist between defined 597 // partitions, they may exist and so the constraints of the scan must incorporate 598 // these spans. 599 func (c *CustomFuncs) inBetweenFilters( 600 tabID opt.TableID, index cat.Index, partitionValues []tree.Datums, 601 ) memo.FiltersExpr { 602 var inBetween memo.ScalarListExpr 603 604 if len(partitionValues) == 0 { 605 return memo.EmptyFiltersExpr 606 } 607 608 // Sort the partitionValues lexicographically. 609 sort.Slice(partitionValues, func(i, j int) bool { 610 return partitionValues[i].Compare(c.e.evalCtx, partitionValues[j]) < 0 611 }) 612 613 // Add the beginning span. 614 beginExpr := c.columnComparison(tabID, index, partitionValues[0], -1) 615 inBetween = append(inBetween, beginExpr) 616 617 // Add the end span. 618 endExpr := c.columnComparison(tabID, index, partitionValues[len(partitionValues)-1], 1) 619 inBetween = append(inBetween, endExpr) 620 621 // Add the in-between spans. 622 for i := 1; i < len(partitionValues); i++ { 623 lowerPartition := partitionValues[i-1] 624 higherPartition := partitionValues[i] 625 626 // The between spans will be greater than the lower partition but smaller 627 // than the higher partition. 628 var largerThanLower opt.ScalarExpr 629 if c.isPrefixOf(lowerPartition, higherPartition) { 630 631 // Since the lower partition is a prefix of the higher partition, the span 632 // must begin with the values defined in the lower partition. Consider the 633 // partitions ('us') and ('us', 'cali'). In this case the in-between span 634 // should be [/'us - /'us'/'cali'). 635 largerThanLower = c.columnComparison(tabID, index, lowerPartition, 0) 636 } else { 637 largerThanLower = c.columnComparison(tabID, index, lowerPartition, 1) 638 } 639 640 smallerThanHigher := c.columnComparison(tabID, index, higherPartition, -1) 641 642 // Add the in-between span to the list of inBetween spans. 643 betweenExpr := c.e.f.ConstructAnd(largerThanLower, smallerThanHigher) 644 inBetween = append(inBetween, betweenExpr) 645 } 646 647 // Return an Or expression between all the expressions. 648 return memo.FiltersExpr{c.e.f.ConstructFiltersItem(c.constructOr(inBetween))} 649 } 650 651 // columnComparison returns a filter that compares the index columns to the 652 // given values. The comp parameter can be -1, 0 or 1 to indicate whether the 653 // comparison type of the filter should be a Lt, Eq or Gt. 654 func (c *CustomFuncs) columnComparison( 655 tabID opt.TableID, index cat.Index, values tree.Datums, comp int, 656 ) opt.ScalarExpr { 657 colTypes := make([]*types.T, len(values)) 658 for i := range values { 659 colTypes[i] = values[i].ResolvedType() 660 } 661 662 columnVariables := make(memo.ScalarListExpr, len(values)) 663 scalarValues := make(memo.ScalarListExpr, len(values)) 664 665 for i, val := range values { 666 colID := tabID.ColumnID(index.Column(i).Ordinal) 667 columnVariables[i] = c.e.f.ConstructVariable(colID) 668 scalarValues[i] = c.e.f.ConstructConstVal(val, val.ResolvedType()) 669 } 670 671 colsTuple := c.e.f.ConstructTuple(columnVariables, types.MakeTuple(colTypes)) 672 valsTuple := c.e.f.ConstructTuple(scalarValues, types.MakeTuple(colTypes)) 673 if comp == 0 { 674 return c.e.f.ConstructEq(colsTuple, valsTuple) 675 } else if comp > 0 { 676 return c.e.f.ConstructGt(colsTuple, valsTuple) 677 } 678 679 return c.e.f.ConstructLt(colsTuple, valsTuple) 680 } 681 682 // inPartitionFilters returns a FiltersExpr that is required to cover 683 // all the partition spans. For each partition defined, inPartitionFilters 684 // will contain a FilterItem that restricts the index columns by 685 // the partition values. Use inBetweenFilters to generate filters that 686 // cover all the spans that the partitions don't cover. 687 func (c *CustomFuncs) inPartitionFilters( 688 tabID opt.TableID, index cat.Index, partitionValues []tree.Datums, 689 ) memo.FiltersExpr { 690 var partitions memo.ScalarListExpr 691 692 // Sort the partition values so the most selective ones are first. 693 sort.Slice(partitionValues, func(i, j int) bool { 694 return len(partitionValues[i]) >= len(partitionValues[j]) 695 }) 696 697 // Construct all the partition filters. 698 for i, partition := range partitionValues { 699 700 // Only add this partition if a more selective partition hasn't 701 // been defined on the same partition. 702 partitionSeen := false 703 for j, moreSelectivePartition := range partitionValues { 704 if j >= i { 705 break 706 } 707 708 // At this point we know whether the current partition was seen before. 709 partitionSeen = c.isPrefixOf(partition, moreSelectivePartition) 710 if partitionSeen { 711 break 712 } 713 } 714 715 // This partition is a prefix of a more selective partition and so, 716 // will be taken care of by the in-between partitions. 717 if partitionSeen { 718 continue 719 } 720 721 // Get an expression that restricts the values of the index to the 722 // partition values. 723 inPartition := c.columnComparison(tabID, index, partition, 0) 724 partitions = append(partitions, inPartition) 725 } 726 727 // Return an Or expression between all the expressions. 728 return memo.FiltersExpr{c.e.f.ConstructFiltersItem(c.constructOr(partitions))} 729 } 730 731 // isPrefixOf returns whether pre is a prefix of other. 732 func (c *CustomFuncs) isPrefixOf(pre []tree.Datum, other []tree.Datum) bool { 733 if len(pre) > len(other) { 734 // Pre can't be a prefix of other as it is larger. 735 return false 736 } 737 for i := range pre { 738 if pre[i].Compare(c.e.evalCtx, other[i]) != 0 { 739 return false 740 } 741 } 742 743 return true 744 } 745 746 // constructOr constructs an expression that is an OR between all the 747 // provided conditions 748 func (c *CustomFuncs) constructOr(conditions memo.ScalarListExpr) opt.ScalarExpr { 749 if len(conditions) == 0 { 750 return c.e.f.ConstructFalse() 751 } 752 753 orExpr := conditions[0] 754 for i := 1; i < len(conditions); i++ { 755 orExpr = c.e.f.ConstructOr(conditions[i], orExpr) 756 } 757 758 return orExpr 759 } 760 761 // partitionValuesFilters constructs filters with the purpose of 762 // constraining an index scan using the partition values similar to 763 // the filters added from the check constraints (see 764 // checkConstraintFilters). It returns two sets of filters, one to 765 // create the partition spans, and one to create the spans for all 766 // the in between ranges that are not part of any partitions. 767 // 768 // For example consider the following table and partitioned index: 769 // 770 // CREATE TABLE orders ( 771 // region STRING NOT NULL, id INT8 NOT NULL, total DECIMAL NOT NULL, seq_num INT NOT NULL, 772 // PRIMARY KEY (region, id) 773 // ) 774 // 775 // CREATE INDEX orders_by_seq_num 776 // ON orders (region, seq_num, id) 777 // STORING (total) 778 // PARTITION BY LIST (region) 779 // ( 780 // PARTITION us_east1 VALUES IN ('us-east1'), 781 // PARTITION us_west1 VALUES IN ('us-west1'), 782 // PARTITION europe_west2 VALUES IN ('europe-west2') 783 // ) 784 // 785 // Now consider the following query: 786 // SELECT sum(total) FROM orders WHERE seq_num >= 100 AND seq_num < 200 787 // 788 // Normally, the index would not be utilized but because we know what the 789 // partition values are for the prefix of the index, we can generate 790 // filters that allow us to use the index (adding the appropriate in-between 791 // filters to catch all the values that are not part of the partitions). 792 // By doing so, we get the following plan: 793 // scalar-group-by 794 // ├── select 795 // │ ├── scan orders@orders_by_seq_num 796 // │ │ └── constraint: /1/4/2: [ - /'europe-west2') 797 // │ │ [/'europe-west2'/100 - /'europe-west2'/199] 798 // │ │ [/e'europe-west2\x00'/100 - /'us-east1') 799 // │ │ [/'us-east1'/100 - /'us-east1'/199] 800 // │ │ [/e'us-east1\x00'/100 - /'us-west1') 801 // │ │ [/'us-west1'/100 - /'us-west1'/199] 802 // │ │ [/e'us-west1\x00'/100 - ] 803 // │ └── filters 804 // │ └── (seq_num >= 100) AND (seq_num < 200) 805 // └── aggregations 806 // └── sum 807 // └── variable: total 808 // 809 func (c *CustomFuncs) partitionValuesFilters( 810 tabID opt.TableID, index cat.Index, 811 ) (partitionFilter, inBetweenFilter memo.FiltersExpr) { 812 813 // Find all the partition values 814 partitionValues := index.PartitionByListPrefixes() 815 if len(partitionValues) == 0 { 816 return partitionFilter, inBetweenFilter 817 } 818 819 // Get the in partition expressions. 820 inPartition := c.inPartitionFilters(tabID, index, partitionValues) 821 822 // Get the in between expressions. 823 inBetween := c.inBetweenFilters(tabID, index, partitionValues) 824 825 return inPartition, inBetween 826 } 827 828 // HasInvertedIndexes returns true if at least one inverted index is defined on 829 // the Scan operator's table. 830 func (c *CustomFuncs) HasInvertedIndexes(scanPrivate *memo.ScanPrivate) bool { 831 // Don't bother matching unless there's an inverted index. 832 var iter scanIndexIter 833 iter.init(c.e.mem, scanPrivate, onlyInvertedIndexes) 834 return iter.next() 835 } 836 837 // GenerateInvertedIndexScans enumerates all inverted indexes on the Scan 838 // operator's table and generates an alternate Scan operator for each inverted 839 // index that can service the query. 840 // 841 // The resulting Scan operator is pre-constrained and requires an IndexJoin to 842 // project columns other than the primary key columns. The reason it's pre- 843 // constrained is that we cannot treat an inverted index in the same way as a 844 // regular index, since it does not actually contain the indexed column. 845 func (c *CustomFuncs) GenerateInvertedIndexScans( 846 grp memo.RelExpr, scanPrivate *memo.ScanPrivate, filters memo.FiltersExpr, 847 ) { 848 var sb indexScanBuilder 849 sb.init(c, scanPrivate.Table) 850 851 // Iterate over all inverted indexes. 852 var iter scanIndexIter 853 iter.init(c.e.mem, scanPrivate, onlyInvertedIndexes) 854 for iter.next() { 855 // Check whether the filter can constrain the index. 856 constraint, remaining, ok := c.tryConstrainIndex( 857 filters, nil /* optionalFilters */, scanPrivate.Table, iter.indexOrdinal, true /* isInverted */) 858 if !ok { 859 continue 860 } 861 862 // Construct new ScanOpDef with the new index and constraint. 863 newScanPrivate := *scanPrivate 864 newScanPrivate.Index = iter.indexOrdinal 865 newScanPrivate.Constraint = constraint 866 867 // Though the index is marked as containing the JSONB column being 868 // indexed, it doesn't actually, and it's only valid to extract the 869 // primary key columns from it. 870 newScanPrivate.Cols = sb.primaryKeyCols() 871 872 // The Scan operator always goes in a new group, since it's always nested 873 // underneath the IndexJoin. The IndexJoin may also go into its own group, 874 // if there's a remaining filter above it. 875 // TODO(justin): We might not need to do an index join in order to get the 876 // correct columns, but it's difficult to tell at this point. 877 sb.setScan(&newScanPrivate) 878 879 // If remaining filter exists, split it into one part that can be pushed 880 // below the IndexJoin, and one part that needs to stay above. 881 remaining = sb.addSelectAfterSplit(remaining, newScanPrivate.Cols) 882 sb.addIndexJoin(scanPrivate.Cols) 883 sb.addSelect(remaining) 884 885 sb.build(grp) 886 } 887 } 888 889 func (c *CustomFuncs) initIdxConstraintForIndex( 890 requiredFilters, optionalFilters memo.FiltersExpr, 891 tabID opt.TableID, 892 indexOrd int, 893 isInverted bool, 894 ) (ic *idxconstraint.Instance) { 895 ic = &idxconstraint.Instance{} 896 897 // Fill out data structures needed to initialize the idxconstraint library. 898 // Use LaxKeyColumnCount, since all columns <= LaxKeyColumnCount are 899 // guaranteed to be part of each row's key (i.e. not stored in row's value, 900 // which does not take part in an index scan). Note that the OrderingColumn 901 // slice cannot be reused, as Instance.Init can use it in the constraint. 902 md := c.e.mem.Metadata() 903 index := md.Table(tabID).Index(indexOrd) 904 columns := make([]opt.OrderingColumn, index.LaxKeyColumnCount()) 905 var notNullCols opt.ColSet 906 for i := range columns { 907 col := index.Column(i) 908 colID := tabID.ColumnID(col.Ordinal) 909 columns[i] = opt.MakeOrderingColumn(colID, col.Descending) 910 if !col.IsNullable() { 911 notNullCols.Add(colID) 912 } 913 } 914 915 // Generate index constraints. 916 ic.Init(requiredFilters, optionalFilters, columns, notNullCols, isInverted, c.e.evalCtx, c.e.f) 917 return ic 918 } 919 920 // tryConstrainIndex tries to derive a constraint for the given index from the 921 // specified filter. If a constraint is derived, it is returned along with any 922 // filter remaining after extracting the constraint. If no constraint can be 923 // derived, then tryConstrainIndex returns ok = false. 924 func (c *CustomFuncs) tryConstrainIndex( 925 requiredFilters, optionalFilters memo.FiltersExpr, 926 tabID opt.TableID, 927 indexOrd int, 928 isInverted bool, 929 ) (constraint *constraint.Constraint, remainingFilters memo.FiltersExpr, ok bool) { 930 // Start with fast check to rule out indexes that cannot be constrained. 931 if !isInverted && 932 !c.canMaybeConstrainIndex(requiredFilters, tabID, indexOrd) && 933 !c.canMaybeConstrainIndex(optionalFilters, tabID, indexOrd) { 934 return nil, nil, false 935 } 936 937 ic := c.initIdxConstraintForIndex(requiredFilters, optionalFilters, tabID, indexOrd, isInverted) 938 constraint = ic.Constraint() 939 if constraint.IsUnconstrained() { 940 return nil, nil, false 941 } 942 943 // Return 0 if no remaining filter. 944 remaining := ic.RemainingFilters() 945 946 // Make copy of constraint so that idxconstraint instance is not referenced. 947 copy := *constraint 948 return ©, remaining, true 949 } 950 951 // allInvIndexConstraints tries to derive all constraints for the specified inverted 952 // index that can be derived. If no constraint is derived, then it returns ok = false, 953 // similar to tryConstrainIndex. 954 func (c *CustomFuncs) allInvIndexConstraints( 955 filters memo.FiltersExpr, tabID opt.TableID, indexOrd int, 956 ) (constraints []*constraint.Constraint, ok bool) { 957 ic := c.initIdxConstraintForIndex(filters, nil /* optionalFilters */, tabID, indexOrd, true /* isInverted */) 958 constraints, err := ic.AllInvertedIndexConstraints() 959 if err != nil { 960 return nil, false 961 } 962 // As long as there was no error, AllInvertedIndexConstraints is guaranteed 963 // to add at least one constraint to the slice. It will be set to 964 // unconstrained if no constraints could be derived for this index. 965 constraint := constraints[0] 966 if constraint.IsUnconstrained() { 967 return constraints, false 968 } 969 970 return constraints, true 971 } 972 973 // canMaybeConstrainIndex returns true if we should try to constrain a given 974 // index by the given filter. It returns false if it is impossible for the 975 // filter can constrain the scan. 976 // 977 // If any of the three following statements are true, then it is 978 // possible that the index can be constrained: 979 // 980 // 1. The filter references the first index column. 981 // 2. The constraints are not tight (see props.Scalar.TightConstraints). 982 // 3. Any of the filter's constraints start with the first index column. 983 // 984 func (c *CustomFuncs) canMaybeConstrainIndex( 985 filters memo.FiltersExpr, tabID opt.TableID, indexOrd int, 986 ) bool { 987 md := c.e.mem.Metadata() 988 index := md.Table(tabID).Index(indexOrd) 989 990 for i := range filters { 991 filterProps := filters[i].ScalarProps() 992 993 // If the filter involves the first index column, then the index can 994 // possibly be constrained. 995 firstIndexCol := tabID.ColumnID(index.Column(0).Ordinal) 996 if filterProps.OuterCols.Contains(firstIndexCol) { 997 return true 998 } 999 1000 // If the constraints are not tight, then the index can possibly be 1001 // constrained, because index constraint generation supports more 1002 // expressions than filter constraint generation. 1003 if !filterProps.TightConstraints { 1004 return true 1005 } 1006 1007 // If any constraint involves the first index column, then the index can 1008 // possibly be constrained. 1009 cset := filterProps.Constraints 1010 for i := 0; i < cset.Length(); i++ { 1011 firstCol := cset.Constraint(i).Columns.Get(0).ID() 1012 if firstCol == firstIndexCol { 1013 return true 1014 } 1015 } 1016 } 1017 1018 return false 1019 } 1020 1021 // ---------------------------------------------------------------------- 1022 // 1023 // Limit Rules 1024 // Custom match and replace functions used with limit.opt rules. 1025 // 1026 // ---------------------------------------------------------------------- 1027 1028 // LimitScanPrivate constructs a new ScanPrivate value that is based on the 1029 // given ScanPrivate. The new private's HardLimit is set to the given limit, 1030 // which must be a constant int datum value. The other fields are inherited from 1031 // the existing private. 1032 func (c *CustomFuncs) LimitScanPrivate( 1033 scanPrivate *memo.ScanPrivate, limit tree.Datum, required physical.OrderingChoice, 1034 ) *memo.ScanPrivate { 1035 // Determine the scan direction necessary to provide the required ordering. 1036 _, reverse := ordering.ScanPrivateCanProvide(c.e.mem.Metadata(), scanPrivate, &required) 1037 1038 newScanPrivate := *scanPrivate 1039 newScanPrivate.HardLimit = memo.MakeScanLimit(int64(*limit.(*tree.DInt)), reverse) 1040 return &newScanPrivate 1041 } 1042 1043 // CanLimitConstrainedScan returns true if the given scan has already been 1044 // constrained and can have a row count limit installed as well. This is only 1045 // possible when the required ordering of the rows to be limited can be 1046 // satisfied by the Scan operator. 1047 // 1048 // NOTE: Limiting unconstrained scans is done by the PushLimitIntoScan rule, 1049 // since that can require IndexJoin operators to be generated. 1050 func (c *CustomFuncs) CanLimitConstrainedScan( 1051 scanPrivate *memo.ScanPrivate, required physical.OrderingChoice, 1052 ) bool { 1053 if scanPrivate.HardLimit != 0 { 1054 // Don't push limit into scan if scan is already limited. This would 1055 // usually only happen when normalizations haven't run, as otherwise 1056 // redundant Limit operators would be discarded. 1057 return false 1058 } 1059 1060 if scanPrivate.Constraint == nil { 1061 // This is not a constrained scan, so skip it. The PushLimitIntoScan rule 1062 // is responsible for limited unconstrained scans. 1063 return false 1064 } 1065 1066 ok, _ := ordering.ScanPrivateCanProvide(c.e.mem.Metadata(), scanPrivate, &required) 1067 return ok 1068 } 1069 1070 // GenerateLimitedScans enumerates all secondary indexes on the Scan operator's 1071 // table and tries to create new limited Scan operators from them. Since this 1072 // only needs to be done once per table, GenerateLimitedScans should only be 1073 // called on the original unaltered primary index Scan operator (i.e. not 1074 // constrained or limited). 1075 // 1076 // For a secondary index that "covers" the columns needed by the scan, a single 1077 // limited Scan operator is created. For a non-covering index, an IndexJoin is 1078 // constructed to add missing columns to the limited Scan. 1079 func (c *CustomFuncs) GenerateLimitedScans( 1080 grp memo.RelExpr, 1081 scanPrivate *memo.ScanPrivate, 1082 limit tree.Datum, 1083 required physical.OrderingChoice, 1084 ) { 1085 limitVal := int64(*limit.(*tree.DInt)) 1086 1087 var sb indexScanBuilder 1088 sb.init(c, scanPrivate.Table) 1089 1090 // Iterate over all indexes, looking for those that can be limited. 1091 var iter scanIndexIter 1092 iter.init(c.e.mem, scanPrivate, onlyStandardIndexes) 1093 for iter.next() { 1094 newScanPrivate := *scanPrivate 1095 newScanPrivate.Index = iter.indexOrdinal 1096 1097 // If the alternate index does not conform to the ordering, then skip it. 1098 // If reverse=true, then the scan needs to be in reverse order to match 1099 // the required ordering. 1100 ok, reverse := ordering.ScanPrivateCanProvide( 1101 c.e.mem.Metadata(), &newScanPrivate, &required, 1102 ) 1103 if !ok { 1104 continue 1105 } 1106 newScanPrivate.HardLimit = memo.MakeScanLimit(limitVal, reverse) 1107 1108 // If the alternate index includes the set of needed columns, then construct 1109 // a new Scan operator using that index. 1110 if iter.isCovering() { 1111 sb.setScan(&newScanPrivate) 1112 sb.build(grp) 1113 continue 1114 } 1115 1116 // Otherwise, try to construct an IndexJoin operator that provides the 1117 // columns missing from the index. 1118 if scanPrivate.Flags.NoIndexJoin { 1119 continue 1120 } 1121 1122 // Scan whatever columns we need which are available from the index, plus 1123 // the PK columns. 1124 newScanPrivate.Cols = iter.indexCols().Intersection(scanPrivate.Cols) 1125 newScanPrivate.Cols.UnionWith(sb.primaryKeyCols()) 1126 sb.setScan(&newScanPrivate) 1127 1128 // The Scan operator will go into its own group (because it projects a 1129 // different set of columns), and the IndexJoin operator will be added to 1130 // the same group as the original Limit operator. 1131 sb.addIndexJoin(scanPrivate.Cols) 1132 1133 sb.build(grp) 1134 } 1135 } 1136 1137 // ---------------------------------------------------------------------- 1138 // 1139 // Join Rules 1140 // Custom match and replace functions used with join.opt rules. 1141 // 1142 // ---------------------------------------------------------------------- 1143 1144 // NoJoinHints returns true if no hints were specified for this join. 1145 func (c *CustomFuncs) NoJoinHints(p *memo.JoinPrivate) bool { 1146 return p.Flags.Empty() 1147 } 1148 1149 // CommuteJoinFlags returns a join private for the commuted join (where the left 1150 // and right sides are swapped). It adjusts any join flags that are specific to 1151 // one side. 1152 func (c *CustomFuncs) CommuteJoinFlags(p *memo.JoinPrivate) *memo.JoinPrivate { 1153 if p.Flags.Empty() { 1154 return p 1155 } 1156 1157 // swap is a helper function which swaps the values of two (single-bit) flags. 1158 swap := func(f, a, b memo.JoinFlags) memo.JoinFlags { 1159 // If the bits are different, flip them both. 1160 if f.Has(a) != f.Has(b) { 1161 f ^= (a | b) 1162 } 1163 return f 1164 } 1165 f := p.Flags 1166 f = swap(f, memo.AllowLookupJoinIntoLeft, memo.AllowLookupJoinIntoRight) 1167 f = swap(f, memo.AllowHashJoinStoreLeft, memo.AllowHashJoinStoreRight) 1168 if p.Flags == f { 1169 return p 1170 } 1171 res := *p 1172 res.Flags = f 1173 return &res 1174 } 1175 1176 // GenerateMergeJoins spawns MergeJoinOps, based on any interesting orderings. 1177 func (c *CustomFuncs) GenerateMergeJoins( 1178 grp memo.RelExpr, 1179 originalOp opt.Operator, 1180 left, right memo.RelExpr, 1181 on memo.FiltersExpr, 1182 joinPrivate *memo.JoinPrivate, 1183 ) { 1184 if !joinPrivate.Flags.Has(memo.AllowMergeJoin) { 1185 return 1186 } 1187 1188 leftProps := left.Relational() 1189 rightProps := right.Relational() 1190 1191 leftEq, rightEq := memo.ExtractJoinEqualityColumns( 1192 leftProps.OutputCols, rightProps.OutputCols, on, 1193 ) 1194 n := len(leftEq) 1195 if n == 0 { 1196 return 1197 } 1198 1199 // We generate MergeJoin expressions based on interesting orderings from the 1200 // left side. The CommuteJoin rule will ensure that we actually try both 1201 // sides. 1202 orders := DeriveInterestingOrderings(left).Copy() 1203 orders.RestrictToCols(leftEq.ToSet()) 1204 1205 if !joinPrivate.Flags.Has(memo.AllowHashJoinStoreLeft) && 1206 !joinPrivate.Flags.Has(memo.AllowHashJoinStoreRight) { 1207 // If we don't allow hash join, we must do our best to generate a merge 1208 // join, even if it means sorting both sides. We append an arbitrary 1209 // ordering, in case the interesting orderings don't result in any merge 1210 // joins. 1211 o := make(opt.Ordering, len(leftEq)) 1212 for i := range o { 1213 o[i] = opt.MakeOrderingColumn(leftEq[i], false /* descending */) 1214 } 1215 orders.Add(o) 1216 } 1217 1218 if len(orders) == 0 { 1219 return 1220 } 1221 1222 var colToEq util.FastIntMap 1223 for i := range leftEq { 1224 colToEq.Set(int(leftEq[i]), i) 1225 colToEq.Set(int(rightEq[i]), i) 1226 } 1227 1228 var remainingFilters memo.FiltersExpr 1229 1230 for _, o := range orders { 1231 if len(o) < n { 1232 // TODO(radu): we have a partial ordering on the equality columns. We 1233 // should augment it with the other columns (in arbitrary order) in the 1234 // hope that we can get the full ordering cheaply using a "streaming" 1235 // sort. This would not useful now since we don't support streaming sorts. 1236 continue 1237 } 1238 1239 if remainingFilters == nil { 1240 remainingFilters = memo.ExtractRemainingJoinFilters(on, leftEq, rightEq) 1241 } 1242 1243 merge := memo.MergeJoinExpr{Left: left, Right: right, On: remainingFilters} 1244 merge.JoinPrivate = *joinPrivate 1245 merge.JoinType = originalOp 1246 merge.LeftEq = make(opt.Ordering, n) 1247 merge.RightEq = make(opt.Ordering, n) 1248 merge.LeftOrdering.Columns = make([]physical.OrderingColumnChoice, 0, n) 1249 merge.RightOrdering.Columns = make([]physical.OrderingColumnChoice, 0, n) 1250 for i := 0; i < n; i++ { 1251 eqIdx, _ := colToEq.Get(int(o[i].ID())) 1252 l, r, descending := leftEq[eqIdx], rightEq[eqIdx], o[i].Descending() 1253 merge.LeftEq[i] = opt.MakeOrderingColumn(l, descending) 1254 merge.RightEq[i] = opt.MakeOrderingColumn(r, descending) 1255 merge.LeftOrdering.AppendCol(l, descending) 1256 merge.RightOrdering.AppendCol(r, descending) 1257 } 1258 1259 // Simplify the orderings with the corresponding FD sets. 1260 merge.LeftOrdering.Simplify(&leftProps.FuncDeps) 1261 merge.RightOrdering.Simplify(&rightProps.FuncDeps) 1262 1263 c.e.mem.AddMergeJoinToGroup(&merge, grp) 1264 } 1265 } 1266 1267 // GenerateLookupJoins looks at the possible indexes and creates lookup join 1268 // expressions in the current group. A lookup join can be created when the ON 1269 // condition has equality constraints on a prefix of the index columns. 1270 // 1271 // There are two cases: 1272 // 1273 // 1. The index has all the columns we need; this is the simple case, where we 1274 // generate a LookupJoin expression in the current group: 1275 // 1276 // Join LookupJoin(t@idx)) 1277 // / \ | 1278 // / \ -> | 1279 // Input Scan(t) Input 1280 // 1281 // 1282 // 2. The index is not covering. We have to generate an index join above the 1283 // lookup join. Note that this index join is also implemented as a 1284 // LookupJoin, because an IndexJoin can only output columns from one table, 1285 // whereas we also need to output columns from Input. 1286 // 1287 // Join LookupJoin(t@primary) 1288 // / \ | 1289 // / \ -> | 1290 // Input Scan(t) LookupJoin(t@idx) 1291 // | 1292 // | 1293 // Input 1294 // 1295 // For example: 1296 // CREATE TABLE abc (a PRIMARY KEY, b INT, c INT) 1297 // CREATE TABLE xyz (x PRIMARY KEY, y INT, z INT, INDEX (y)) 1298 // SELECT * FROM abc JOIN xyz ON a=y 1299 // 1300 // We want to first join abc with the index on y (which provides columns y, x) 1301 // and then use a lookup join to retrieve column z. The "index join" (top 1302 // LookupJoin) will produce columns a,b,c,x,y; the lookup columns are just z 1303 // (the original index join produced x,y,z). 1304 // 1305 // Note that the top LookupJoin "sees" column IDs from the table on both 1306 // "sides" (in this example x,y on the left and z on the right) but there is 1307 // no overlap. 1308 // 1309 func (c *CustomFuncs) GenerateLookupJoins( 1310 grp memo.RelExpr, 1311 joinType opt.Operator, 1312 input memo.RelExpr, 1313 scanPrivate *memo.ScanPrivate, 1314 on memo.FiltersExpr, 1315 joinPrivate *memo.JoinPrivate, 1316 ) { 1317 if !joinPrivate.Flags.Has(memo.AllowLookupJoinIntoRight) { 1318 return 1319 } 1320 md := c.e.mem.Metadata() 1321 inputProps := input.Relational() 1322 1323 leftEq, rightEq := memo.ExtractJoinEqualityColumns(inputProps.OutputCols, scanPrivate.Cols, on) 1324 n := len(leftEq) 1325 if n == 0 { 1326 return 1327 } 1328 1329 var pkCols opt.ColList 1330 1331 var iter scanIndexIter 1332 iter.init(c.e.mem, scanPrivate, onlyStandardIndexes) 1333 for iter.next() { 1334 // Find the longest prefix of index key columns that are constrained by 1335 // an equality with another column or a constant. 1336 numIndexKeyCols := iter.index.LaxKeyColumnCount() 1337 1338 var projections memo.ProjectionsExpr 1339 var constFilters memo.FiltersExpr 1340 1341 // Check if the first column in the index has an equality constraint, or if 1342 // it is constrained to a constant value. This check doesn't guarantee that 1343 // we will find lookup join key columns, but it avoids the unnecessary work 1344 // in most cases. 1345 firstIdxCol := scanPrivate.Table.ColumnID(iter.index.Column(0).Ordinal) 1346 if _, ok := rightEq.Find(firstIdxCol); !ok { 1347 if _, _, ok := c.findConstantFilter(on, firstIdxCol); !ok { 1348 continue 1349 } 1350 } 1351 1352 lookupJoin := memo.LookupJoinExpr{Input: input} 1353 lookupJoin.JoinPrivate = *joinPrivate 1354 lookupJoin.JoinType = joinType 1355 lookupJoin.Table = scanPrivate.Table 1356 lookupJoin.Index = iter.indexOrdinal 1357 1358 lookupJoin.KeyCols = make(opt.ColList, 0, numIndexKeyCols) 1359 rightSideCols := make(opt.ColList, 0, numIndexKeyCols) 1360 needProjection := false 1361 1362 // All the lookup conditions must apply to the prefix of the index and so 1363 // the projected columns created must be created in order. 1364 for j := 0; j < numIndexKeyCols; j++ { 1365 idxCol := scanPrivate.Table.ColumnID(iter.index.Column(j).Ordinal) 1366 if eqIdx, ok := rightEq.Find(idxCol); ok { 1367 lookupJoin.KeyCols = append(lookupJoin.KeyCols, leftEq[eqIdx]) 1368 rightSideCols = append(rightSideCols, idxCol) 1369 continue 1370 } 1371 1372 // Try to find a filter that constrains this column to a non-NULL constant 1373 // value. We cannot use a NULL value because the lookup join implements 1374 // logic equivalent to simple equality between columns (where NULL never 1375 // equals anything). 1376 foundVal, onIdx, ok := c.findConstantFilter(on, idxCol) 1377 if !ok || foundVal == tree.DNull { 1378 break 1379 } 1380 1381 // We will project this constant value in the input to make it an equality 1382 // column. 1383 if projections == nil { 1384 projections = make(memo.ProjectionsExpr, 0, numIndexKeyCols-j) 1385 constFilters = make(memo.FiltersExpr, 0, numIndexKeyCols-j) 1386 } 1387 1388 idxColType := c.e.f.Metadata().ColumnMeta(idxCol).Type 1389 constColID := c.e.f.Metadata().AddColumn( 1390 fmt.Sprintf("project_const_col_@%d", idxCol), 1391 idxColType, 1392 ) 1393 projections = append(projections, c.e.f.ConstructProjectionsItem( 1394 c.e.f.ConstructConst(foundVal, idxColType), 1395 constColID, 1396 )) 1397 1398 needProjection = true 1399 lookupJoin.KeyCols = append(lookupJoin.KeyCols, constColID) 1400 rightSideCols = append(rightSideCols, idxCol) 1401 constFilters = append(constFilters, on[onIdx]) 1402 } 1403 1404 if len(lookupJoin.KeyCols) == 0 { 1405 // We couldn't find equality columns which we can lookup. 1406 continue 1407 } 1408 1409 tableFDs := memo.MakeTableFuncDep(md, scanPrivate.Table) 1410 // A lookup join will drop any input row which contains NULLs, so a lax key 1411 // is sufficient. 1412 lookupJoin.LookupColsAreTableKey = tableFDs.ColsAreLaxKey(rightSideCols.ToSet()) 1413 1414 // Construct the projections for the constant columns. 1415 if needProjection { 1416 lookupJoin.Input = c.e.f.ConstructProject(input, projections, input.Relational().OutputCols) 1417 } 1418 1419 // Remove the redundant filters and update the lookup condition. 1420 lookupJoin.On = memo.ExtractRemainingJoinFilters(on, lookupJoin.KeyCols, rightSideCols) 1421 lookupJoin.On.RemoveCommonFilters(constFilters) 1422 lookupJoin.ConstFilters = constFilters 1423 1424 if iter.isCovering() { 1425 // Case 1 (see function comment). 1426 lookupJoin.Cols = scanPrivate.Cols.Union(inputProps.OutputCols) 1427 c.e.mem.AddLookupJoinToGroup(&lookupJoin, grp) 1428 continue 1429 } 1430 1431 // Case 2 (see function comment). 1432 if scanPrivate.Flags.NoIndexJoin { 1433 continue 1434 } 1435 1436 if pkCols == nil { 1437 pkIndex := iter.tab.Index(cat.PrimaryIndex) 1438 pkCols = make(opt.ColList, pkIndex.KeyColumnCount()) 1439 for i := range pkCols { 1440 pkCols[i] = scanPrivate.Table.ColumnID(pkIndex.Column(i).Ordinal) 1441 } 1442 } 1443 1444 // The lower LookupJoin must return all PK columns (they are needed as key 1445 // columns for the index join). 1446 indexCols := iter.indexCols() 1447 lookupJoin.Cols = scanPrivate.Cols.Intersection(indexCols) 1448 for i := range pkCols { 1449 lookupJoin.Cols.Add(pkCols[i]) 1450 } 1451 lookupJoin.Cols.UnionWith(inputProps.OutputCols) 1452 1453 var indexJoin memo.LookupJoinExpr 1454 1455 // onCols are the columns that the ON condition in the (lower) lookup join 1456 // can refer to: input columns, or columns available in the index. 1457 onCols := indexCols.Union(inputProps.OutputCols) 1458 if c.FiltersBoundBy(lookupJoin.On, onCols) { 1459 // The ON condition refers only to the columns available in the index. 1460 // 1461 // For LeftJoin, both LookupJoins perform a LeftJoin. A null-extended row 1462 // from the lower LookupJoin will not have any matches in the top 1463 // LookupJoin (it has NULLs on key columns) and will get null-extended 1464 // there as well. 1465 indexJoin.On = memo.TrueFilter 1466 } else { 1467 // ON has some conditions that are bound by the columns in the index (at 1468 // the very least, the equality conditions we used for KeyCols), and some 1469 // conditions that refer to other columns. We can put the former in the 1470 // lower LookupJoin and the latter in the index join. 1471 // 1472 // This works for InnerJoin but not for LeftJoin because of a 1473 // technicality: if an input (left) row has matches in the lower 1474 // LookupJoin but has no matches in the index join, only the columns 1475 // looked up by the top index join get NULL-extended. 1476 if joinType == opt.LeftJoinOp { 1477 // TODO(radu): support LeftJoin, perhaps by looking up all columns and 1478 // discarding columns that are already available from the lower 1479 // LookupJoin. This requires a projection to avoid having the same 1480 // ColumnIDs on both sides of the index join. 1481 continue 1482 } 1483 conditions := lookupJoin.On 1484 lookupJoin.On = c.ExtractBoundConditions(conditions, onCols) 1485 indexJoin.On = c.ExtractUnboundConditions(conditions, onCols) 1486 } 1487 1488 indexJoin.Input = c.e.f.ConstructLookupJoin( 1489 lookupJoin.Input, 1490 lookupJoin.On, 1491 &lookupJoin.LookupJoinPrivate, 1492 ) 1493 indexJoin.JoinType = joinType 1494 indexJoin.Table = scanPrivate.Table 1495 indexJoin.Index = cat.PrimaryIndex 1496 indexJoin.KeyCols = pkCols 1497 indexJoin.Cols = scanPrivate.Cols.Union(inputProps.OutputCols) 1498 indexJoin.LookupColsAreTableKey = true 1499 1500 // Create the LookupJoin for the index join in the same group. 1501 c.e.mem.AddLookupJoinToGroup(&indexJoin, grp) 1502 } 1503 } 1504 1505 // GenerateGeoLookupJoins is similar to GenerateLookupJoins, but instead 1506 // of generating lookup joins with regular indexes, it generates geospatial 1507 // lookup joins with inverted geospatial indexes. Since these indexes are not 1508 // covering, all geospatial lookup joins must be wrapped in an index join with 1509 // the primary index of the table. See the description of Case 2 in the comment 1510 // above GenerateLookupJoins for details about how this works. 1511 func (c *CustomFuncs) GenerateGeoLookupJoins( 1512 grp memo.RelExpr, 1513 joinType opt.Operator, 1514 input memo.RelExpr, 1515 scanPrivate *memo.ScanPrivate, 1516 on memo.FiltersExpr, 1517 joinPrivate *memo.JoinPrivate, 1518 fn opt.ScalarExpr, 1519 ) { 1520 if !joinPrivate.Flags.Has(memo.AllowLookupJoinIntoRight) { 1521 return 1522 } 1523 1524 // Geospatial lookup joins are not covering, so we must wrap them in an 1525 // index join. 1526 if scanPrivate.Flags.NoIndexJoin { 1527 return 1528 } 1529 1530 function := fn.(*memo.FunctionExpr) 1531 inputProps := input.Relational() 1532 1533 // Extract the geospatial relationship as well as the variable inputs to 1534 // the geospatial function. 1535 relationship, ok := geoRelationshipMap[function.Name] 1536 if !ok { 1537 panic(errors.AssertionFailedf( 1538 "GenerateGeoLookupJoins called on a function that cannot be index-accelerated", 1539 )) 1540 } 1541 1542 if function.Args.ChildCount() != 2 { 1543 panic(errors.AssertionFailedf( 1544 "all index-accelerated geospatial functions should have two arguments", 1545 )) 1546 } 1547 1548 variable, ok := function.Args.Child(0).(*memo.VariableExpr) 1549 if !ok { 1550 panic(errors.AssertionFailedf( 1551 "GenerateGeoLookupJoins called on function containing non-variable inputs", 1552 )) 1553 } 1554 indexGeoCol := variable.Col 1555 1556 variable, ok = function.Args.Child(1).(*memo.VariableExpr) 1557 if !ok { 1558 panic(errors.AssertionFailedf( 1559 "GenerateGeoLookupJoins called on function containing non-variable inputs", 1560 )) 1561 } 1562 if !inputProps.OutputCols.Contains(variable.Col) { 1563 // The second argument should be from the input. 1564 // TODO(rytaft): Commute the geospatial function in this case. 1565 // Contains <-> ContainedBy 1566 // Intersects <-> Intersects 1567 return 1568 } 1569 inputGeoCol := variable.Col 1570 1571 var pkCols opt.ColList 1572 1573 var iter scanIndexIter 1574 iter.init(c.e.mem, scanPrivate, onlyInvertedIndexes) 1575 for iter.next() { 1576 if scanPrivate.Table.ColumnID(iter.index.Column(0).Ordinal) != indexGeoCol { 1577 continue 1578 } 1579 1580 if pkCols == nil { 1581 pkIndex := iter.tab.Index(cat.PrimaryIndex) 1582 pkCols = make(opt.ColList, pkIndex.KeyColumnCount()) 1583 for i := range pkCols { 1584 pkCols[i] = scanPrivate.Table.ColumnID(pkIndex.Column(i).Ordinal) 1585 } 1586 } 1587 1588 // Though the index is marked as containing the geospatial column being 1589 // indexed, it doesn't actually, and it is only valid to extract the 1590 // primary key columns from it. 1591 indexCols := pkCols.ToSet() 1592 1593 lookupJoin := memo.GeoLookupJoinExpr{Input: input} 1594 lookupJoin.JoinPrivate = *joinPrivate 1595 lookupJoin.JoinType = joinType 1596 lookupJoin.Table = scanPrivate.Table 1597 lookupJoin.Index = iter.indexOrdinal 1598 lookupJoin.GeoRelationshipType = relationship 1599 lookupJoin.GeoCol = inputGeoCol 1600 lookupJoin.Cols = indexCols.Union(inputProps.OutputCols) 1601 1602 var indexJoin memo.LookupJoinExpr 1603 1604 // ON may have some conditions that are bound by the columns in the index 1605 // and some conditions that refer to other columns. We can put the former 1606 // in the GeospatialLookupJoin and the latter in the index join. 1607 lookupJoin.On = c.ExtractBoundConditions(on, lookupJoin.Cols) 1608 indexJoin.On = c.ExtractUnboundConditions(on, lookupJoin.Cols) 1609 1610 indexJoin.Input = c.e.f.ConstructGeoLookupJoin( 1611 lookupJoin.Input, 1612 lookupJoin.On, 1613 &lookupJoin.GeoLookupJoinPrivate, 1614 ) 1615 indexJoin.JoinType = joinType 1616 indexJoin.Table = scanPrivate.Table 1617 indexJoin.Index = cat.PrimaryIndex 1618 indexJoin.KeyCols = pkCols 1619 indexJoin.Cols = scanPrivate.Cols.Union(inputProps.OutputCols) 1620 indexJoin.LookupColsAreTableKey = true 1621 1622 // Create the LookupJoin for the index join in the same group. 1623 c.e.mem.AddLookupJoinToGroup(&indexJoin, grp) 1624 } 1625 } 1626 1627 // geoRelationshipMap contains all the geospatial functions that can be index- 1628 // accelerated. Each function implies a certain type of geospatial relationship, 1629 // which affects how the index is queried as part of a constrained scan or 1630 // geospatial lookup join. geoRelationshipMap maps the function name to its 1631 // corresponding relationship (Covers, CoveredBy, or Intersects). 1632 // 1633 // Note that for all of these functions, a geospatial lookup join or constrained 1634 // index scan may produce false positives. Therefore, the original function must 1635 // be called on the output of the index operation to filter the results. 1636 // TODO(rytaft): add ST_DFullyWithin (geoindex.Covers) and ST_DWithin 1637 // (geoindex.Intersects) once we add support for extending a geometry. 1638 var geoRelationshipMap = map[string]geoindex.RelationshipType{ 1639 "st_covers": geoindex.Covers, 1640 "st_coveredby": geoindex.CoveredBy, 1641 "st_contains": geoindex.Covers, 1642 "st_containsproperly": geoindex.Covers, 1643 "st_crosses": geoindex.Intersects, 1644 "st_equals": geoindex.Intersects, 1645 "st_intersects": geoindex.Intersects, 1646 "st_overlaps": geoindex.Intersects, 1647 "st_touches": geoindex.Intersects, 1648 "st_within": geoindex.CoveredBy, 1649 } 1650 1651 // IsGeoIndexFunction returns true if the given function is a geospatial 1652 // function that can be index-accelerated. 1653 func (c *CustomFuncs) IsGeoIndexFunction(fn opt.ScalarExpr) bool { 1654 return IsGeoIndexFunction(fn) 1655 } 1656 1657 // IsGeoIndexFunction returns true if the given function is a geospatial 1658 // function that can be index-accelerated. 1659 func IsGeoIndexFunction(fn opt.ScalarExpr) bool { 1660 function := fn.(*memo.FunctionExpr) 1661 _, ok := geoRelationshipMap[function.Name] 1662 return ok 1663 } 1664 1665 // HasAllVariableArgs returns true if all the arguments to the given function 1666 // are variables. 1667 func (c *CustomFuncs) HasAllVariableArgs(fn opt.ScalarExpr) bool { 1668 function := fn.(*memo.FunctionExpr) 1669 for i, n := 0, function.Args.ChildCount(); i < n; i++ { 1670 if _, ok := function.Args.Child(i).(*memo.VariableExpr); !ok { 1671 return false 1672 } 1673 } 1674 return true 1675 } 1676 1677 // findConstantFilter tries to find a filter that is exactly equivalent to 1678 // constraining the given column to a constant value. Note that the constant 1679 // value can be NULL (for an `x IS NULL` filter). 1680 func (c *CustomFuncs) findConstantFilter( 1681 filters memo.FiltersExpr, col opt.ColumnID, 1682 ) (value tree.Datum, filterIdx int, ok bool) { 1683 for filterIdx := range filters { 1684 props := filters[filterIdx].ScalarProps() 1685 if props.TightConstraints { 1686 constCol, constVal, ok := props.Constraints.IsSingleColumnConstValue(c.e.evalCtx) 1687 if ok && constCol == col { 1688 return constVal, filterIdx, true 1689 } 1690 } 1691 } 1692 return nil, -1, false 1693 } 1694 1695 // eqColsForZigzag is a helper function to generate eqCol lists for the zigzag 1696 // joiner. The zigzag joiner requires that the equality columns immediately 1697 // follow the fixed columns in the index. Fixed here refers to columns that 1698 // have been constrained to a constant value. 1699 // 1700 // There are two kinds of equality columns that this function takes care of: 1701 // columns that have the same ColumnID on both sides (i.e. the same column), 1702 // as well as columns that have been equated in some ON filter (i.e. they are 1703 // contained in leftEqCols and rightEqCols at the same index). 1704 // 1705 // This function iterates through all columns of the indexes in order, 1706 // skips past the fixed columns, and then generates however many eqCols 1707 // there are that meet the above criteria. 1708 // 1709 // Returns a list of column ordinals for each index. 1710 // 1711 // See the comment in pkg/sql/rowexec/zigzag_joiner.go for more details 1712 // on the role eqCols and fixed cols play in zigzag joins. 1713 func eqColsForZigzag( 1714 tab cat.Table, 1715 tabID opt.TableID, 1716 leftIndex cat.Index, 1717 rightIndex cat.Index, 1718 fixedCols opt.ColSet, 1719 leftEqCols opt.ColList, 1720 rightEqCols opt.ColList, 1721 ) (leftEqPrefix, rightEqPrefix opt.ColList) { 1722 leftEqPrefix = make(opt.ColList, 0, len(leftEqCols)) 1723 rightEqPrefix = make(opt.ColList, 0, len(rightEqCols)) 1724 // We can only zigzag on columns present in the key component of the index, 1725 // so use the LaxKeyColumnCount here because that's the longest prefix of the 1726 // columns in the index which is guaranteed to exist in the key component. 1727 // Using KeyColumnCount is invalid, because if we have a unique index with 1728 // nullable columns, the "key columns" include the primary key of the table, 1729 // which is only present in the key component if one of the other columns is 1730 // NULL. 1731 i, leftCnt := 0, leftIndex.LaxKeyColumnCount() 1732 j, rightCnt := 0, rightIndex.LaxKeyColumnCount() 1733 for ; i < leftCnt; i++ { 1734 colID := tabID.ColumnID(leftIndex.Column(i).Ordinal) 1735 if !fixedCols.Contains(colID) { 1736 break 1737 } 1738 } 1739 for ; j < rightCnt; j++ { 1740 colID := tabID.ColumnID(rightIndex.Column(j).Ordinal) 1741 if !fixedCols.Contains(colID) { 1742 break 1743 } 1744 } 1745 1746 for i < leftCnt && j < rightCnt { 1747 leftColID := tabID.ColumnID(leftIndex.Column(i).Ordinal) 1748 rightColID := tabID.ColumnID(rightIndex.Column(j).Ordinal) 1749 i++ 1750 j++ 1751 1752 if leftColID == rightColID { 1753 leftEqPrefix = append(leftEqPrefix, leftColID) 1754 rightEqPrefix = append(rightEqPrefix, rightColID) 1755 continue 1756 } 1757 leftIdx, leftOk := leftEqCols.Find(leftColID) 1758 rightIdx, rightOk := rightEqCols.Find(rightColID) 1759 // If both columns are at the same index in their respective 1760 // EqCols lists, they were equated in the filters. 1761 if leftOk && rightOk && leftIdx == rightIdx { 1762 leftEqPrefix = append(leftEqPrefix, leftColID) 1763 rightEqPrefix = append(rightEqPrefix, rightColID) 1764 continue 1765 } else { 1766 // We've reached the first non-equal column; the zigzag 1767 // joiner does not support non-contiguous/non-prefix equal 1768 // columns. 1769 break 1770 } 1771 1772 } 1773 1774 return leftEqPrefix, rightEqPrefix 1775 } 1776 1777 // fixedColsForZigzag is a helper function to generate FixedCols lists for the 1778 // zigzag join expression. This function iterates through the columns of the 1779 // specified index in order until it comes across the first column ID that is 1780 // not constrained to a constant. 1781 func (c *CustomFuncs) fixedColsForZigzag( 1782 index cat.Index, tabID opt.TableID, filters memo.FiltersExpr, 1783 ) (fixedCols opt.ColList, vals memo.ScalarListExpr, typs []*types.T) { 1784 for i, cnt := 0, index.ColumnCount(); i < cnt; i++ { 1785 colID := tabID.ColumnID(index.Column(i).Ordinal) 1786 val := memo.ExtractValueForConstColumn(filters, c.e.mem, c.e.evalCtx, colID) 1787 if val == nil { 1788 break 1789 } 1790 if vals == nil { 1791 vals = make(memo.ScalarListExpr, 0, cnt-i) 1792 typs = make([]*types.T, 0, cnt-i) 1793 fixedCols = make(opt.ColList, 0, cnt-i) 1794 } 1795 1796 dt := val.ResolvedType() 1797 vals = append(vals, c.e.f.ConstructConstVal(val, dt)) 1798 typs = append(typs, dt) 1799 fixedCols = append(fixedCols, colID) 1800 } 1801 return fixedCols, vals, typs 1802 } 1803 1804 // GenerateZigzagJoins generates zigzag joins for all pairs of indexes of the 1805 // Scan table which contain one of the constant columns in the FiltersExpr as 1806 // its prefix. 1807 // 1808 // Similar to the lookup join, if the selected index pair does not contain 1809 // all the columns in the output of the scan, we wrap the zigzag join 1810 // in another index join (implemented as a lookup join) on the primary index. 1811 // The index join is implemented with a lookup join since the index join does 1812 // not support arbitrary input sources that are not plain index scans. 1813 func (c *CustomFuncs) GenerateZigzagJoins( 1814 grp memo.RelExpr, scanPrivate *memo.ScanPrivate, filters memo.FiltersExpr, 1815 ) { 1816 1817 // Short circuit unless zigzag joins are explicitly enabled. 1818 if !c.e.evalCtx.SessionData.ZigzagJoinEnabled { 1819 return 1820 } 1821 1822 fixedCols := memo.ExtractConstColumns(filters, c.e.mem, c.e.evalCtx) 1823 1824 if fixedCols.Len() == 0 { 1825 // Zigzagging isn't helpful in the absence of fixed columns. 1826 return 1827 } 1828 1829 // Iterate through indexes, looking for those prefixed with fixedEq cols. 1830 // Efficiently finding a set of indexes that make the most efficient zigzag 1831 // join, with no limit on the number of indexes selected, is an instance of 1832 // this NP-hard problem: 1833 // https://en.wikipedia.org/wiki/Maximum_coverage_problem 1834 // 1835 // A formal definition would be: Suppose we have a set of fixed columns F 1836 // (defined as fixedCols in the code above), and a set of indexes I. The 1837 // "fixed prefix" of every index, in this context, refers to the longest 1838 // prefix of each index's columns that is in F. In other words, we stop 1839 // adding to the prefix when we come across the first non-fixed column 1840 // in an index. 1841 // 1842 // We want to find at most k = 2 indexes from I (in the future k could be 1843 // >= 2 when the zigzag joiner supports 2+ index zigzag joins) that cover 1844 // the maximum number of columns in F. An index is defined to have covered 1845 // a column if that column is in the index's fixed prefix. 1846 // 1847 // Since only 2-way zigzag joins are currently supported, the naive 1848 // approach is bounded at n^2. For now, just do that - a quadratic 1849 // iteration through all indexes. 1850 // 1851 // TODO(itsbilal): Implement the greedy or weighted version of the 1852 // algorithm laid out here: 1853 // https://en.wikipedia.org/wiki/Maximum_coverage_problem 1854 var iter, iter2 scanIndexIter 1855 iter.init(c.e.mem, scanPrivate, onlyStandardIndexes) 1856 for iter.next() { 1857 if iter.indexOrdinal == cat.PrimaryIndex { 1858 continue 1859 } 1860 1861 leftFixed := c.indexConstrainedCols(iter.index, scanPrivate.Table, fixedCols) 1862 // Short-circuit quickly if the first column in the index is not a fixed 1863 // column. 1864 if leftFixed.Len() == 0 { 1865 continue 1866 } 1867 iter2.init(c.e.mem, scanPrivate, onlyStandardIndexes) 1868 // Only look at indexes after this one. 1869 iter2.indexOrdinal = iter.indexOrdinal 1870 1871 for iter2.next() { 1872 rightFixed := c.indexConstrainedCols(iter2.index, scanPrivate.Table, fixedCols) 1873 // If neither side contributes a fixed column not contributed by the 1874 // other, then there's no reason to zigzag on this pair of indexes. 1875 if leftFixed.SubsetOf(rightFixed) || rightFixed.SubsetOf(leftFixed) { 1876 continue 1877 } 1878 // Columns that are in both indexes are, by definition, equal. 1879 leftCols := iter.indexCols() 1880 rightCols := iter2.indexCols() 1881 eqCols := leftCols.Intersection(rightCols) 1882 eqCols.DifferenceWith(fixedCols) 1883 if eqCols.Len() == 0 { 1884 // A simple index join is more efficient in such cases. 1885 continue 1886 } 1887 1888 // If there are any equalities across the columns of the two indexes, 1889 // push them into the zigzag join spec. 1890 leftEq, rightEq := memo.ExtractJoinEqualityColumns( 1891 leftCols, rightCols, filters, 1892 ) 1893 leftEqCols, rightEqCols := eqColsForZigzag( 1894 iter.tab, 1895 scanPrivate.Table, 1896 iter.index, 1897 iter2.index, 1898 fixedCols, 1899 leftEq, 1900 rightEq, 1901 ) 1902 1903 if len(leftEqCols) == 0 || len(rightEqCols) == 0 { 1904 // One of the indexes is not sorted by any of the equality 1905 // columns, because the equality columns do not immediately 1906 // succeed the fixed columns. A zigzag join cannot be planned. 1907 continue 1908 } 1909 1910 // Confirm the primary key columns are in both leftEqCols and 1911 // rightEqCols. The conversion of a select with filters to a 1912 // zigzag join requires the primary key columns to be in the output 1913 // for output correctness; otherwise, we could be outputting more 1914 // results than there should be (due to an equality on a non-unique 1915 // non-required value). 1916 pkIndex := iter.tab.Index(cat.PrimaryIndex) 1917 pkCols := make(opt.ColList, pkIndex.KeyColumnCount()) 1918 pkColsFound := true 1919 for i := range pkCols { 1920 pkCols[i] = scanPrivate.Table.ColumnID(pkIndex.Column(i).Ordinal) 1921 1922 if _, ok := leftEqCols.Find(pkCols[i]); !ok { 1923 pkColsFound = false 1924 break 1925 } 1926 if _, ok := rightEqCols.Find(pkCols[i]); !ok { 1927 pkColsFound = false 1928 break 1929 } 1930 } 1931 if !pkColsFound { 1932 continue 1933 } 1934 1935 zigzagJoin := memo.ZigzagJoinExpr{ 1936 On: filters, 1937 ZigzagJoinPrivate: memo.ZigzagJoinPrivate{ 1938 LeftTable: scanPrivate.Table, 1939 LeftIndex: iter.indexOrdinal, 1940 RightTable: scanPrivate.Table, 1941 RightIndex: iter2.indexOrdinal, 1942 LeftEqCols: leftEqCols, 1943 RightEqCols: rightEqCols, 1944 }, 1945 } 1946 1947 leftFixedCols, leftVals, leftTypes := c.fixedColsForZigzag( 1948 iter.index, scanPrivate.Table, filters, 1949 ) 1950 rightFixedCols, rightVals, rightTypes := c.fixedColsForZigzag( 1951 iter2.index, scanPrivate.Table, filters, 1952 ) 1953 1954 if len(leftFixedCols) != leftFixed.Len() || len(rightFixedCols) != rightFixed.Len() { 1955 panic(errors.AssertionFailedf("could not populate all fixed columns for zig zag join")) 1956 } 1957 1958 zigzagJoin.LeftFixedCols = leftFixedCols 1959 zigzagJoin.RightFixedCols = rightFixedCols 1960 1961 leftTupleTyp := types.MakeTuple(leftTypes) 1962 rightTupleTyp := types.MakeTuple(rightTypes) 1963 zigzagJoin.FixedVals = memo.ScalarListExpr{ 1964 c.e.f.ConstructTuple(leftVals, leftTupleTyp), 1965 c.e.f.ConstructTuple(rightVals, rightTupleTyp), 1966 } 1967 1968 zigzagJoin.On = memo.ExtractRemainingJoinFilters( 1969 filters, 1970 zigzagJoin.LeftEqCols, 1971 zigzagJoin.RightEqCols, 1972 ) 1973 zigzagCols := leftCols.Copy() 1974 zigzagCols.UnionWith(rightCols) 1975 1976 if scanPrivate.Cols.SubsetOf(zigzagCols) { 1977 // Case 1 (zigzagged indexes contain all requested columns). 1978 zigzagJoin.Cols = scanPrivate.Cols 1979 c.e.mem.AddZigzagJoinToGroup(&zigzagJoin, grp) 1980 continue 1981 } 1982 1983 if scanPrivate.Flags.NoIndexJoin { 1984 continue 1985 } 1986 1987 // Case 2 (wrap zigzag join in an index join). 1988 var indexJoin memo.LookupJoinExpr 1989 // Ensure the zigzag join returns pk columns. 1990 zigzagJoin.Cols = scanPrivate.Cols.Intersection(zigzagCols) 1991 for i := range pkCols { 1992 zigzagJoin.Cols.Add(pkCols[i]) 1993 } 1994 1995 if c.FiltersBoundBy(zigzagJoin.On, zigzagCols) { 1996 // The ON condition refers only to the columns available in the zigzag 1997 // indices. 1998 indexJoin.On = memo.TrueFilter 1999 } else { 2000 // ON has some conditions that are bound by the columns in the index (at 2001 // the very least, the equality conditions we used for EqCols and FixedCols), 2002 // and some conditions that refer to other table columns. We can put 2003 // the former in the lower ZigzagJoin and the latter in the index join. 2004 conditions := zigzagJoin.On 2005 zigzagJoin.On = c.ExtractBoundConditions(conditions, zigzagCols) 2006 indexJoin.On = c.ExtractUnboundConditions(conditions, zigzagCols) 2007 } 2008 2009 indexJoin.Input = c.e.f.ConstructZigzagJoin( 2010 zigzagJoin.On, 2011 &zigzagJoin.ZigzagJoinPrivate, 2012 ) 2013 indexJoin.JoinType = opt.InnerJoinOp 2014 indexJoin.Table = scanPrivate.Table 2015 indexJoin.Index = cat.PrimaryIndex 2016 indexJoin.KeyCols = pkCols 2017 indexJoin.Cols = scanPrivate.Cols 2018 indexJoin.LookupColsAreTableKey = true 2019 2020 // Create the LookupJoin for the index join in the same group as the 2021 // original select. 2022 c.e.mem.AddLookupJoinToGroup(&indexJoin, grp) 2023 } 2024 } 2025 } 2026 2027 // indexConstrainedCols computes the set of columns in allFixedCols which form 2028 // a prefix of the key columns in idx. 2029 func (c *CustomFuncs) indexConstrainedCols( 2030 idx cat.Index, tab opt.TableID, allFixedCols opt.ColSet, 2031 ) opt.ColSet { 2032 var constrained opt.ColSet 2033 for i, n := 0, idx.ColumnCount(); i < n; i++ { 2034 col := tab.ColumnID(idx.Column(i).Ordinal) 2035 if allFixedCols.Contains(col) { 2036 constrained.Add(col) 2037 } else { 2038 break 2039 } 2040 } 2041 return constrained 2042 } 2043 2044 // GenerateInvertedIndexZigzagJoins generates zigzag joins for constraints on 2045 // inverted index. It looks for cases where one inverted index can satisfy 2046 // two constraints, and it produces zigzag joins with the same index on both 2047 // sides of the zigzag join for those cases, fixed on different constant values. 2048 func (c *CustomFuncs) GenerateInvertedIndexZigzagJoins( 2049 grp memo.RelExpr, scanPrivate *memo.ScanPrivate, filters memo.FiltersExpr, 2050 ) { 2051 // Short circuit unless zigzag joins are explicitly enabled. 2052 if !c.e.evalCtx.SessionData.ZigzagJoinEnabled { 2053 return 2054 } 2055 2056 var sb indexScanBuilder 2057 sb.init(c, scanPrivate.Table) 2058 2059 // Iterate over all inverted indexes. 2060 var iter scanIndexIter 2061 iter.init(c.e.mem, scanPrivate, onlyInvertedIndexes) 2062 for iter.next() { 2063 // See if there are two or more constraints that can be satisfied 2064 // by this inverted index. This is possible with inverted indexes as 2065 // opposed to secondary indexes, because one row in the primary index 2066 // can often correspond to multiple rows in an inverted index. This 2067 // function generates all constraints it can derive for this index; 2068 // not all of which might get used in this function. 2069 constraints, ok := c.allInvIndexConstraints( 2070 filters, scanPrivate.Table, iter.indexOrdinal, 2071 ) 2072 if !ok || len(constraints) < 2 { 2073 continue 2074 } 2075 // In theory, we could explore zigzag joins on all constraint pairs. 2076 // However, in the absence of stats on inverted indexes, we will not 2077 // be able to distinguish more selective constraints from less 2078 // selective ones anyway, so just pick the first two constraints. 2079 // 2080 // TODO(itsbilal): Use the remaining constraints to build a remaining 2081 // filters expression, instead of just reusing filters from the scan. 2082 constraint := constraints[0] 2083 constraint2 := constraints[1] 2084 2085 minPrefix := constraint.ExactPrefix(c.e.evalCtx) 2086 if otherPrefix := constraint2.ExactPrefix(c.e.evalCtx); otherPrefix < minPrefix { 2087 minPrefix = otherPrefix 2088 } 2089 2090 if minPrefix == 0 { 2091 continue 2092 } 2093 2094 zigzagJoin := memo.ZigzagJoinExpr{ 2095 On: filters, 2096 ZigzagJoinPrivate: memo.ZigzagJoinPrivate{ 2097 LeftTable: scanPrivate.Table, 2098 LeftIndex: iter.indexOrdinal, 2099 RightTable: scanPrivate.Table, 2100 RightIndex: iter.indexOrdinal, 2101 }, 2102 } 2103 2104 // Get constant values from each constraint. Add them to FixedVals as 2105 // tuples, with associated Column IDs in both {Left,Right}FixedCols. 2106 leftVals := make(memo.ScalarListExpr, minPrefix) 2107 leftTypes := make([]*types.T, minPrefix) 2108 rightVals := make(memo.ScalarListExpr, minPrefix) 2109 rightTypes := make([]*types.T, minPrefix) 2110 2111 zigzagJoin.LeftFixedCols = make(opt.ColList, minPrefix) 2112 zigzagJoin.RightFixedCols = make(opt.ColList, minPrefix) 2113 for i := 0; i < minPrefix; i++ { 2114 leftVal := constraint.Spans.Get(0).StartKey().Value(i) 2115 rightVal := constraint2.Spans.Get(0).StartKey().Value(i) 2116 2117 leftVals[i] = c.e.f.ConstructConstVal(leftVal, leftVal.ResolvedType()) 2118 leftTypes[i] = leftVal.ResolvedType() 2119 rightVals[i] = c.e.f.ConstructConstVal(rightVal, rightVal.ResolvedType()) 2120 rightTypes[i] = rightVal.ResolvedType() 2121 zigzagJoin.LeftFixedCols[i] = constraint.Columns.Get(i).ID() 2122 zigzagJoin.RightFixedCols[i] = constraint.Columns.Get(i).ID() 2123 } 2124 2125 leftTupleTyp := types.MakeTuple(leftTypes) 2126 rightTupleTyp := types.MakeTuple(rightTypes) 2127 zigzagJoin.FixedVals = memo.ScalarListExpr{ 2128 c.e.f.ConstructTuple(leftVals, leftTupleTyp), 2129 c.e.f.ConstructTuple(rightVals, rightTupleTyp), 2130 } 2131 2132 // Set equality columns - all remaining columns after the fixed prefix 2133 // need to be equal. 2134 eqColLen := iter.index.ColumnCount() - minPrefix 2135 zigzagJoin.LeftEqCols = make(opt.ColList, eqColLen) 2136 zigzagJoin.RightEqCols = make(opt.ColList, eqColLen) 2137 for i := minPrefix; i < iter.index.ColumnCount(); i++ { 2138 colID := scanPrivate.Table.ColumnID(iter.index.Column(i).Ordinal) 2139 zigzagJoin.LeftEqCols[i-minPrefix] = colID 2140 zigzagJoin.RightEqCols[i-minPrefix] = colID 2141 } 2142 zigzagJoin.On = filters 2143 2144 // Don't output the first column (i.e. the inverted index's JSON key 2145 // col) from the zigzag join. It could contain partial values, so 2146 // presenting it in the output or checking ON conditions against 2147 // it makes little sense. 2148 zigzagCols := iter.indexCols() 2149 for i, cnt := 0, iter.index.KeyColumnCount(); i < cnt; i++ { 2150 colID := scanPrivate.Table.ColumnID(iter.index.Column(i).Ordinal) 2151 zigzagCols.Remove(colID) 2152 } 2153 2154 pkIndex := iter.tab.Index(cat.PrimaryIndex) 2155 pkCols := make(opt.ColList, pkIndex.KeyColumnCount()) 2156 for i := range pkCols { 2157 pkCols[i] = scanPrivate.Table.ColumnID(pkIndex.Column(i).Ordinal) 2158 // Ensure primary key columns are always retrieved from the zigzag 2159 // join. 2160 zigzagCols.Add(pkCols[i]) 2161 } 2162 2163 // Case 1 (zigzagged indexes contain all requested columns). 2164 if scanPrivate.Cols.SubsetOf(zigzagCols) { 2165 zigzagJoin.Cols = scanPrivate.Cols 2166 c.e.mem.AddZigzagJoinToGroup(&zigzagJoin, grp) 2167 continue 2168 } 2169 2170 if scanPrivate.Flags.NoIndexJoin { 2171 continue 2172 } 2173 2174 // Case 2 (wrap zigzag join in an index join). 2175 2176 var indexJoin memo.LookupJoinExpr 2177 // Ensure the zigzag join returns pk columns. 2178 zigzagJoin.Cols = scanPrivate.Cols.Intersection(zigzagCols) 2179 for i := range pkCols { 2180 zigzagJoin.Cols.Add(pkCols[i]) 2181 } 2182 2183 if c.FiltersBoundBy(zigzagJoin.On, zigzagCols) { 2184 // The ON condition refers only to the columns available in the zigzag 2185 // indices. 2186 indexJoin.On = memo.TrueFilter 2187 } else { 2188 // ON has some conditions that are bound by the columns in the index (at 2189 // the very least, the equality conditions we used for EqCols and FixedCols), 2190 // and some conditions that refer to other table columns. We can put 2191 // the former in the lower ZigzagJoin and the latter in the index join. 2192 conditions := zigzagJoin.On 2193 zigzagJoin.On = c.ExtractBoundConditions(conditions, zigzagCols) 2194 indexJoin.On = c.ExtractUnboundConditions(conditions, zigzagCols) 2195 } 2196 2197 indexJoin.Input = c.e.f.ConstructZigzagJoin( 2198 zigzagJoin.On, 2199 &zigzagJoin.ZigzagJoinPrivate, 2200 ) 2201 indexJoin.JoinType = opt.InnerJoinOp 2202 indexJoin.Table = scanPrivate.Table 2203 indexJoin.Index = cat.PrimaryIndex 2204 indexJoin.KeyCols = pkCols 2205 indexJoin.Cols = scanPrivate.Cols 2206 indexJoin.LookupColsAreTableKey = true 2207 2208 // Create the LookupJoin for the index join in the same group as the 2209 // original select. 2210 c.e.mem.AddLookupJoinToGroup(&indexJoin, grp) 2211 } 2212 } 2213 2214 // deriveJoinSize returns the number of base relations (i.e., not joins) 2215 // being joined underneath the given relational expression. 2216 func (c *CustomFuncs) deriveJoinSize(e memo.RelExpr) int { 2217 relProps := e.Relational() 2218 if relProps.IsAvailable(props.JoinSize) { 2219 return relProps.Rule.JoinSize 2220 } 2221 relProps.SetAvailable(props.JoinSize) 2222 2223 switch j := e.(type) { 2224 case *memo.InnerJoinExpr: 2225 relProps.Rule.JoinSize = c.deriveJoinSize(j.Left) + c.deriveJoinSize(j.Right) 2226 default: 2227 relProps.Rule.JoinSize = 1 2228 } 2229 2230 return relProps.Rule.JoinSize 2231 } 2232 2233 // ShouldReorderJoins returns whether the optimizer should attempt to find 2234 // a better ordering of inner joins. 2235 func (c *CustomFuncs) ShouldReorderJoins(left, right memo.RelExpr) bool { 2236 // TODO(justin): referencing left and right here is a hack: ideally 2237 // we'd want to be able to reference the logical properties of the 2238 // expression being explored in this CustomFunc. 2239 size := c.deriveJoinSize(left) + c.deriveJoinSize(right) 2240 return size <= c.e.evalCtx.SessionData.ReorderJoinsLimit 2241 } 2242 2243 // IsSimpleEquality returns true if all of the filter conditions are equalities 2244 // between simple data types (constants, variables, tuples and NULL). 2245 func (c *CustomFuncs) IsSimpleEquality(filters memo.FiltersExpr) bool { 2246 for i := range filters { 2247 eqFilter, ok := filters[i].Condition.(*memo.EqExpr) 2248 if !ok { 2249 return false 2250 } 2251 2252 left, right := eqFilter.Left, eqFilter.Right 2253 switch left.Op() { 2254 case opt.VariableOp, opt.ConstOp, opt.NullOp, opt.TupleOp: 2255 default: 2256 return false 2257 } 2258 2259 switch right.Op() { 2260 case opt.VariableOp, opt.ConstOp, opt.NullOp, opt.TupleOp: 2261 default: 2262 return false 2263 } 2264 } 2265 2266 return true 2267 } 2268 2269 // ---------------------------------------------------------------------- 2270 // 2271 // GroupBy Rules 2272 // Custom match and replace functions used with groupby.opt rules. 2273 // 2274 // ---------------------------------------------------------------------- 2275 2276 // IsCanonicalGroupBy returns true if the private is for the canonical version 2277 // of the grouping operator. This is the operator that is built initially (and 2278 // has all grouping columns as optional in the ordering), as opposed to variants 2279 // generated by the GenerateStreamingGroupBy exploration rule. 2280 func (c *CustomFuncs) IsCanonicalGroupBy(private *memo.GroupingPrivate) bool { 2281 return private.Ordering.Any() || private.GroupingCols.SubsetOf(private.Ordering.Optional) 2282 } 2283 2284 // MakeProjectFromPassthroughAggs constructs a top-level Project operator that 2285 // contains one output column per function in the given aggregrate list. The 2286 // input expression is expected to return zero or one rows, and the aggregate 2287 // functions are expected to always pass through their values in that case. 2288 func (c *CustomFuncs) MakeProjectFromPassthroughAggs( 2289 grp memo.RelExpr, input memo.RelExpr, aggs memo.AggregationsExpr, 2290 ) { 2291 if !input.Relational().Cardinality.IsZeroOrOne() { 2292 panic(errors.AssertionFailedf("input expression cannot have more than one row: %v", input)) 2293 } 2294 2295 var passthrough opt.ColSet 2296 projections := make(memo.ProjectionsExpr, 0, len(aggs)) 2297 for i := range aggs { 2298 // If aggregate remaps the column ID, need to synthesize projection item; 2299 // otherwise, can just pass through. 2300 variable := aggs[i].Agg.Child(0).(*memo.VariableExpr) 2301 if variable.Col == aggs[i].Col { 2302 passthrough.Add(variable.Col) 2303 } else { 2304 projections = append(projections, c.e.f.ConstructProjectionsItem(variable, aggs[i].Col)) 2305 } 2306 } 2307 c.e.mem.AddProjectToGroup(&memo.ProjectExpr{ 2308 Input: input, 2309 Projections: projections, 2310 Passthrough: passthrough, 2311 }, grp) 2312 } 2313 2314 // GenerateStreamingGroupBy generates variants of a GroupBy or DistinctOn 2315 // expression with more specific orderings on the grouping columns, using the 2316 // interesting orderings property. See the GenerateStreamingGroupBy rule. 2317 func (c *CustomFuncs) GenerateStreamingGroupBy( 2318 grp memo.RelExpr, 2319 op opt.Operator, 2320 input memo.RelExpr, 2321 aggs memo.AggregationsExpr, 2322 private *memo.GroupingPrivate, 2323 ) { 2324 orders := DeriveInterestingOrderings(input) 2325 intraOrd := private.Ordering 2326 for _, o := range orders { 2327 // We are looking for a prefix of o that satisfies the intra-group ordering 2328 // if we ignore grouping columns. 2329 oIdx, intraIdx := 0, 0 2330 for ; oIdx < len(o); oIdx++ { 2331 oCol := o[oIdx].ID() 2332 if private.GroupingCols.Contains(oCol) || intraOrd.Optional.Contains(oCol) { 2333 // Grouping or optional column. 2334 continue 2335 } 2336 2337 if intraIdx < len(intraOrd.Columns) && 2338 intraOrd.Columns[intraIdx].Group.Contains(oCol) && 2339 intraOrd.Columns[intraIdx].Descending == o[oIdx].Descending() { 2340 // Column matches the one in the ordering. 2341 intraIdx++ 2342 continue 2343 } 2344 break 2345 } 2346 if oIdx == 0 || intraIdx < len(intraOrd.Columns) { 2347 // No match. 2348 continue 2349 } 2350 o = o[:oIdx] 2351 2352 var newOrd physical.OrderingChoice 2353 newOrd.FromOrderingWithOptCols(o, opt.ColSet{}) 2354 2355 // Simplify the ordering according to the input's FDs. Note that this is not 2356 // necessary for correctness because buildChildPhysicalProps would do it 2357 // anyway, but doing it here once can make things more efficient (and we may 2358 // generate fewer expressions if some of these orderings turn out to be 2359 // equivalent). 2360 newOrd.Simplify(&input.Relational().FuncDeps) 2361 2362 newPrivate := *private 2363 newPrivate.Ordering = newOrd 2364 2365 switch op { 2366 case opt.GroupByOp: 2367 newExpr := memo.GroupByExpr{ 2368 Input: input, 2369 Aggregations: aggs, 2370 GroupingPrivate: newPrivate, 2371 } 2372 c.e.mem.AddGroupByToGroup(&newExpr, grp) 2373 2374 case opt.DistinctOnOp: 2375 newExpr := memo.DistinctOnExpr{ 2376 Input: input, 2377 Aggregations: aggs, 2378 GroupingPrivate: newPrivate, 2379 } 2380 c.e.mem.AddDistinctOnToGroup(&newExpr, grp) 2381 2382 case opt.EnsureDistinctOnOp: 2383 newExpr := memo.EnsureDistinctOnExpr{ 2384 Input: input, 2385 Aggregations: aggs, 2386 GroupingPrivate: newPrivate, 2387 } 2388 c.e.mem.AddEnsureDistinctOnToGroup(&newExpr, grp) 2389 2390 case opt.UpsertDistinctOnOp: 2391 newExpr := memo.UpsertDistinctOnExpr{ 2392 Input: input, 2393 Aggregations: aggs, 2394 GroupingPrivate: newPrivate, 2395 } 2396 c.e.mem.AddUpsertDistinctOnToGroup(&newExpr, grp) 2397 2398 case opt.EnsureUpsertDistinctOnOp: 2399 newExpr := memo.EnsureUpsertDistinctOnExpr{ 2400 Input: input, 2401 Aggregations: aggs, 2402 GroupingPrivate: newPrivate, 2403 } 2404 c.e.mem.AddEnsureUpsertDistinctOnToGroup(&newExpr, grp) 2405 } 2406 } 2407 } 2408 2409 // OtherAggsAreConst returns true if all items in the given aggregate list 2410 // contain ConstAgg functions, except for the "except" item. The ConstAgg 2411 // functions will always return the same value, as long as there is at least 2412 // one input row. 2413 func (c *CustomFuncs) OtherAggsAreConst( 2414 aggs memo.AggregationsExpr, except *memo.AggregationsItem, 2415 ) bool { 2416 for i := range aggs { 2417 agg := &aggs[i] 2418 if agg == except { 2419 continue 2420 } 2421 2422 switch agg.Agg.Op() { 2423 case opt.ConstAggOp: 2424 // Ensure that argument is a VariableOp. 2425 if agg.Agg.Child(0).Op() != opt.VariableOp { 2426 return false 2427 } 2428 2429 default: 2430 return false 2431 } 2432 } 2433 return true 2434 } 2435 2436 // MakeOrderingChoiceFromColumn constructs a new OrderingChoice with 2437 // one element in the sequence: the columnID in the order defined by 2438 // (MIN/MAX) operator. This function was originally created to be used 2439 // with the Replace(Min|Max)WithLimit exploration rules. 2440 // 2441 // WARNING: The MinOp case can return a NULL value if the column allows it. This 2442 // is because NULL values sort first in CRDB. 2443 func (c *CustomFuncs) MakeOrderingChoiceFromColumn( 2444 op opt.Operator, col opt.ColumnID, 2445 ) physical.OrderingChoice { 2446 oc := physical.OrderingChoice{} 2447 switch op { 2448 case opt.MinOp: 2449 oc.AppendCol(col, false /* descending */) 2450 case opt.MaxOp: 2451 oc.AppendCol(col, true /* descending */) 2452 } 2453 return oc 2454 } 2455 2456 // ExprPair stores a left and right ScalarExpr. ExprPairForSplitDisjunction 2457 // returns ExprPair, which can be deconstructed later, to avoid extra 2458 // computation in determining the left and right expression groups. 2459 type ExprPair struct { 2460 left opt.ScalarExpr 2461 right opt.ScalarExpr 2462 itemToReplace *memo.FiltersItem 2463 } 2464 2465 // ExprPairLeft returns the left ScalarExpr in an ExprPair. 2466 func (c *CustomFuncs) ExprPairLeft(ep ExprPair) opt.ScalarExpr { 2467 return ep.left 2468 } 2469 2470 // ExprPairRight returns the right ScalarExpr in an ExprPair. 2471 func (c *CustomFuncs) ExprPairRight(ep ExprPair) opt.ScalarExpr { 2472 return ep.right 2473 } 2474 2475 // ExprPairFiltersItemToReplace returns the original FiltersItem that the 2476 // ExprPair was generated from. This FiltersItem should be replaced by 2477 // ExprPairLeft and ExprPairRight in the newly generated filters in 2478 // SplitDisjunction(AddKey). 2479 func (c *CustomFuncs) ExprPairFiltersItemToReplace(ep ExprPair) *memo.FiltersItem { 2480 return ep.itemToReplace 2481 } 2482 2483 // ExprPairSucceeded returns true if the ExprPair is not nil. 2484 func (c *CustomFuncs) ExprPairSucceeded(ep ExprPair) bool { 2485 return ep != ExprPair{} 2486 } 2487 2488 // ExprPairForSplitDisjunction finds the first "interesting" ExprPair in the 2489 // filters and returns it. If an "interesting" ExprPair is not found, an empty 2490 // ExprPair is returned. 2491 // 2492 // For details on what makes an ExprPair "interesting", see 2493 // buildExprPairForSplitDisjunction. 2494 func (c *CustomFuncs) ExprPairForSplitDisjunction( 2495 sp *memo.ScanPrivate, filters memo.FiltersExpr, 2496 ) ExprPair { 2497 for i := range filters { 2498 if filters[i].Condition.Op() == opt.OrOp { 2499 ep := c.buildExprPairForSplitDisjunction(sp, &filters[i]) 2500 if (ep != ExprPair{}) { 2501 return ep 2502 } 2503 } 2504 } 2505 return ExprPair{} 2506 } 2507 2508 // buildExprPairForSplitDisjunction groups disjuction sub-expressions into an 2509 // "interesting" ExprPair. 2510 // 2511 // An "interesting" ExprPair is one where: 2512 // 2513 // 1. The column sets of both expressions in the pair are not 2514 // equal. 2515 // 2. Two index scans can potentially be constrained by both expressions in 2516 // the pair. 2517 // 2518 // Consider the expression: 2519 // 2520 // u = 1 OR v = 2 2521 // 2522 // If an index exists on u and another on v, an "interesting" ExprPair exists, 2523 // ("u = 1", "v = 1"). If both indexes do not exist, there is no "interesting" 2524 // ExprPair possible. 2525 // 2526 // Now consider the expression: 2527 // 2528 // u = 1 OR u = 2 2529 // 2530 // There is no possible "interesting" ExprPair here because the left and right 2531 // sides of the disjunction share the same columns. 2532 // 2533 // buildExprPairForSplitDisjunction groups all sub-expressions adjacent to the 2534 // input's top-level OrExpr into left and right expression groups. These two 2535 // groups form the new filter expressions on the left and right side of the 2536 // generated UnionAll in SplitDisjunction(AddKey). 2537 // 2538 // All sub-expressions with the same columns as the left-most sub-expression 2539 // are grouped in the left group. All other sub-expressions are grouped in the 2540 // right group. 2541 // 2542 // buildExprPairForSplitDisjunction returns an empty ExprPair if all 2543 // sub-expressions have the same columns. It also returns an empty ExprPair if 2544 // either expression in the pair found is not likely to constrain an index 2545 // scan. See canMaybeConstrainIndexWithCols for details on how this is 2546 // determined. 2547 func (c *CustomFuncs) buildExprPairForSplitDisjunction( 2548 sp *memo.ScanPrivate, filter *memo.FiltersItem, 2549 ) ExprPair { 2550 var leftExprs memo.ScalarListExpr 2551 var rightExprs memo.ScalarListExpr 2552 var leftColSet opt.ColSet 2553 var rightColSet opt.ColSet 2554 2555 // Traverse all adjacent OrExpr. 2556 var collect func(opt.ScalarExpr) 2557 collect = func(expr opt.ScalarExpr) { 2558 switch t := expr.(type) { 2559 case *memo.OrExpr: 2560 collect(t.Left) 2561 collect(t.Right) 2562 return 2563 } 2564 2565 cols := c.OuterCols(expr) 2566 2567 // Set the left-most non-Or expression as the left ColSet to match (or 2568 // not match) on. 2569 if leftColSet.Empty() { 2570 leftColSet = cols 2571 } 2572 2573 // If the current expression ColSet matches leftColSet, add the expr to 2574 // the left group. Otherwise, add it to the right group. 2575 if leftColSet.Equals(cols) { 2576 leftExprs = append(leftExprs, expr) 2577 } else { 2578 rightColSet.UnionWith(cols) 2579 rightExprs = append(rightExprs, expr) 2580 } 2581 } 2582 collect(filter.Condition) 2583 2584 // Return an empty pair if either of the groups is empty or if either the 2585 // left or right groups are unlikely to constrain an index scan. 2586 if len(leftExprs) == 0 || 2587 len(rightExprs) == 0 || 2588 !c.canMaybeConstrainIndexWithCols(sp, leftColSet) || 2589 !c.canMaybeConstrainIndexWithCols(sp, rightColSet) { 2590 return ExprPair{} 2591 } 2592 2593 return ExprPair{ 2594 left: c.constructOr(leftExprs), 2595 right: c.constructOr(rightExprs), 2596 itemToReplace: filter, 2597 } 2598 } 2599 2600 // canMaybeConstrainIndexWithCols returns true if any indexes on the 2601 // ScanPrivate's table could be constrained by cols. It is a fast check for 2602 // SplitDisjunction to avoid matching a large number of queries that won't 2603 // obviously be improved by the rule. 2604 // 2605 // canMaybeConstrainIndexWithCols checks for an intersection between the input 2606 // columns and an index's columns. An intersection between column sets implies 2607 // that cols could constrain a scan on that index. For example, the columns "a" 2608 // would constrain a scan on an index over columns "a, b", because the "a" is a 2609 // subset of the index columns. Likewise, the columns "a" and "b" would 2610 // constrain a scan on an index over column "a", because "a" and "b" are a 2611 // superset of the index columns. 2612 // 2613 // Notice that this function can return both false positives and false 2614 // negatives. As an example of a false negative, consider the following table 2615 // and query. 2616 // 2617 // CREATE TABLE t ( 2618 // k PRIMARY KEY, 2619 // a INT, 2620 // hash INT AS (a % 4) STORED, 2621 // INDEX hash (hash) 2622 // ) 2623 // 2624 // SELECT * FROM t WHERE a = 5 2625 // 2626 // The expression "a = 5" can constrain a scan over the hash index: The columns 2627 // "hash" must be a constant value of 1 because it is dependent on column "a" 2628 // with a constant value of 5. However, canMaybeConstrainIndexWithCols will 2629 // return false in this case because "a" does not intersect with the index 2630 // column, "hash". 2631 func (c *CustomFuncs) canMaybeConstrainIndexWithCols(sp *memo.ScanPrivate, cols opt.ColSet) bool { 2632 md := c.e.mem.Metadata() 2633 tabMeta := md.TableMeta(sp.Table) 2634 2635 var iter scanIndexIter 2636 iter.init(c.e.mem, sp, allIndexes) 2637 for iter.next() { 2638 // Iterate through all indexes of the table and return true if cols 2639 // intersect with the index's columns. 2640 indexColumns := tabMeta.IndexKeyColumns(iter.indexOrdinal) 2641 if cols.Intersects(indexColumns) { 2642 return true 2643 } 2644 } 2645 2646 return false 2647 } 2648 2649 // DuplicateScanPrivate constructs a new ScanPrivate that is identical to the 2650 // input, but has new table and column IDs. 2651 // 2652 // DuplicateScanPrivate can only be called on canonical ScanPrivates because not 2653 // all scan properties are copied to the new ScanPrivate, e.g. constraints. 2654 func (c *CustomFuncs) DuplicateScanPrivate(sp *memo.ScanPrivate) *memo.ScanPrivate { 2655 if !c.IsCanonicalScan(sp) { 2656 panic(errors.AssertionFailedf("input ScanPrivate must be canonical: %v", sp)) 2657 } 2658 2659 md := c.e.mem.Metadata() 2660 tabMeta := md.TableMeta(sp.Table) 2661 dupTabID := md.AddTable(tabMeta.Table, &tabMeta.Alias) 2662 2663 var dupTabColIDs opt.ColSet 2664 cols := sp.Cols 2665 for i, ok := cols.Next(0); ok; i, ok = cols.Next(i + 1) { 2666 ord := tabMeta.MetaID.ColumnOrdinal(i) 2667 dupColID := dupTabID.ColumnID(ord) 2668 dupTabColIDs.Add(dupColID) 2669 } 2670 2671 return &memo.ScanPrivate{ 2672 Table: dupTabID, 2673 Cols: dupTabColIDs, 2674 Flags: sp.Flags, 2675 Locking: sp.Locking, 2676 } 2677 } 2678 2679 // MapScanFilterCols returns a new FiltersExpr with all the src column IDs in 2680 // the input expression replaced with column IDs in dst. 2681 // 2682 // NOTE: Every ColumnID in src must map to the a ColumnID in dst with the same 2683 // relative position in the ColSets. For example, if src and dst are (1, 5, 6) 2684 // and (7, 12, 15), then the following mapping would be applied: 2685 // 2686 // 1 => 7 2687 // 5 => 12 2688 // 6 => 15 2689 func (c *CustomFuncs) MapScanFilterCols( 2690 filters memo.FiltersExpr, src *memo.ScanPrivate, dst *memo.ScanPrivate, 2691 ) memo.FiltersExpr { 2692 if src.Cols.Len() != dst.Cols.Len() { 2693 panic(errors.AssertionFailedf( 2694 "src and dst must have the same number of columns, src.Cols: %v, dst.Cols: %v", 2695 src.Cols, 2696 dst.Cols, 2697 )) 2698 } 2699 2700 // Map each column in src to a column in dst based on the relative position 2701 // of both the src and dst ColumnIDs in the ColSet. 2702 var colMap util.FastIntMap 2703 dstCol, _ := dst.Cols.Next(0) 2704 for srcCol, ok := src.Cols.Next(0); ok; srcCol, ok = src.Cols.Next(srcCol + 1) { 2705 colMap.Set(int(srcCol), int(dstCol)) 2706 dstCol, _ = dst.Cols.Next(dstCol + 1) 2707 } 2708 2709 // Map the columns of each filter in the FiltersExpr. 2710 newFilters := make([]memo.FiltersItem, len(filters)) 2711 for i := range filters { 2712 expr := c.MapFiltersItemCols(&filters[i], colMap) 2713 newFilters[i] = c.e.f.ConstructFiltersItem(expr) 2714 } 2715 2716 return newFilters 2717 } 2718 2719 // MakeSetPrivateForSplitDisjunction constructs a new SetPrivate with column sets 2720 // from the left and right ScanPrivate. We use the same ColList for the 2721 // LeftCols and OutCols of the SetPrivate because we've used the original 2722 // ScanPrivate column IDs for the left ScanPrivate and those are safe to use as 2723 // output column IDs of the Union expression. 2724 func (c *CustomFuncs) MakeSetPrivateForSplitDisjunction( 2725 left, right *memo.ScanPrivate, 2726 ) *memo.SetPrivate { 2727 leftAndOutCols := opt.ColSetToList(left.Cols) 2728 return &memo.SetPrivate{ 2729 LeftCols: leftAndOutCols, 2730 RightCols: opt.ColSetToList(right.Cols), 2731 OutCols: leftAndOutCols, 2732 } 2733 } 2734 2735 // AddPrimaryKeyColsToScanPrivate creates a new ScanPrivate that is the same as 2736 // the input ScanPrivate, but has primary keys added to the ColSet. 2737 func (c *CustomFuncs) AddPrimaryKeyColsToScanPrivate(sp *memo.ScanPrivate) *memo.ScanPrivate { 2738 keyCols := c.PrimaryKeyCols(sp.Table) 2739 return &memo.ScanPrivate{ 2740 Table: sp.Table, 2741 Cols: sp.Cols.Union(keyCols), 2742 Flags: sp.Flags, 2743 Locking: sp.Locking, 2744 } 2745 } 2746 2747 // indexIterType is an option passed to scanIndexIter.init() to specify index types 2748 // to include during iteration. 2749 type indexIterType int 2750 2751 const ( 2752 // allIndexes sepcifies that no indexes will be skipped during iteration. 2753 allIndexes indexIterType = iota 2754 2755 // onlyStandardIndexes specifies iteration over all standard indexes, 2756 // skipping inverted indexes. 2757 onlyStandardIndexes 2758 2759 // onlyInvertedIndexes specifies iteration over all inverted indexes, 2760 // skipping standard indexes. 2761 onlyInvertedIndexes 2762 ) 2763 2764 // scanIndexIter is a helper struct that supports iteration over the indexes 2765 // of a Scan operator table. For example: 2766 // 2767 // var iter scanIndexIter 2768 // iter.init(mem, scanOpDef, onlyStandardIndexes) 2769 // for iter.next() { 2770 // doSomething(iter.indexOrdinal) 2771 // } 2772 // 2773 type scanIndexIter struct { 2774 mem *memo.Memo 2775 scanPrivate *memo.ScanPrivate 2776 tab cat.Table 2777 indexOrdinal cat.IndexOrdinal 2778 index cat.Index 2779 indexType indexIterType 2780 cols opt.ColSet 2781 } 2782 2783 func (it *scanIndexIter) init(mem *memo.Memo, scanPrivate *memo.ScanPrivate, t indexIterType) { 2784 it.mem = mem 2785 it.scanPrivate = scanPrivate 2786 it.tab = mem.Metadata().Table(scanPrivate.Table) 2787 it.indexOrdinal = -1 2788 it.index = nil 2789 it.indexType = t 2790 } 2791 2792 // next advances iteration to the next index of the Scan operator's table. This 2793 // is the primary index if it's the first time next is called, or a secondary 2794 // index thereafter. When there are no more indexes to enumerate, next returns 2795 // false. The current index is accessible via the iterator's "index" field. 2796 // 2797 // The indexType determines which indexes to skip when iterating, if any. 2798 // 2799 // If the ForceIndex flag is set, then all indexes except the forced index are 2800 // skipped. 2801 func (it *scanIndexIter) next() bool { 2802 for { 2803 it.indexOrdinal++ 2804 2805 if it.indexOrdinal >= it.tab.IndexCount() { 2806 it.index = nil 2807 return false 2808 } 2809 2810 it.index = it.tab.Index(it.indexOrdinal) 2811 2812 // Skip over inverted indexes if indexType is onlyStandardIndexes. 2813 if it.indexType == onlyStandardIndexes && it.index.IsInverted() { 2814 continue 2815 } 2816 2817 // Skip over standard indexes if indexType is onlyInvertedIndexes. 2818 if it.indexType == onlyInvertedIndexes && !it.index.IsInverted() { 2819 continue 2820 } 2821 2822 if it.scanPrivate.Flags.ForceIndex && it.scanPrivate.Flags.Index != it.indexOrdinal { 2823 // If we are forcing a specific index, ignore the others. 2824 continue 2825 } 2826 2827 it.cols = opt.ColSet{} 2828 return true 2829 } 2830 } 2831 2832 // indexCols returns the set of columns contained in the current index. 2833 func (it *scanIndexIter) indexCols() opt.ColSet { 2834 if it.cols.Empty() { 2835 it.cols = it.mem.Metadata().TableMeta(it.scanPrivate.Table).IndexColumns(it.indexOrdinal) 2836 } 2837 return it.cols 2838 } 2839 2840 // isCovering returns true if the current index contains all columns projected 2841 // by the Scan operator. 2842 func (it *scanIndexIter) isCovering() bool { 2843 return it.scanPrivate.Cols.SubsetOf(it.indexCols()) 2844 }