github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/memo/logical_props_builder.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 memo 12 13 import ( 14 "math" 15 16 "github.com/cockroachdb/cockroach/pkg/sql/opt" 17 "github.com/cockroachdb/cockroach/pkg/sql/opt/constraint" 18 "github.com/cockroachdb/cockroach/pkg/sql/opt/props" 19 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 20 "github.com/cockroachdb/cockroach/pkg/util/log" 21 "github.com/cockroachdb/errors" 22 ) 23 24 var fdAnnID = opt.NewTableAnnID() 25 26 // logicalPropsBuilder is a helper class that consolidates the code that derives 27 // a parent expression's logical properties from those of its children. 28 // 29 // buildProps is called by the memo group construction code in order to 30 // initialize the new group's logical properties. 31 // NOTE: When deriving properties from children, be sure to keep the child 32 // properties immutable by copying them if necessary. 33 // NOTE: The parent expression is passed as an expression for convenient access 34 // to children, but certain properties on it are not yet defined (like 35 // its logical properties!). 36 type logicalPropsBuilder struct { 37 evalCtx *tree.EvalContext 38 mem *Memo 39 sb statisticsBuilder 40 41 // When set to true, disableStats disables stat generation during 42 // logical prop building. Useful in checkExpr when we don't want 43 // to create stats for non-normalized expressions and potentially 44 // mutate opt_tester output compared to cases where checkExpr is 45 // not run. 46 disableStats bool 47 } 48 49 func (b *logicalPropsBuilder) init(evalCtx *tree.EvalContext, mem *Memo) { 50 b.evalCtx = evalCtx 51 b.mem = mem 52 b.sb.init(evalCtx, mem.Metadata()) 53 } 54 55 func (b *logicalPropsBuilder) clear() { 56 b.evalCtx = nil 57 b.mem = nil 58 b.sb.clear() 59 } 60 61 func (b *logicalPropsBuilder) buildScanProps(scan *ScanExpr, rel *props.Relational) { 62 md := scan.Memo().Metadata() 63 hardLimit := scan.HardLimit.RowCount() 64 65 // Side Effects 66 // ------------ 67 // A Locking option is a side-effect (we don't want to elide this scan). 68 if scan.Locking != nil { 69 rel.CanHaveSideEffects = true 70 rel.VolatilitySet.AddVolatile() 71 } 72 73 // Output Columns 74 // -------------- 75 // Scan output columns are stored in the definition. 76 rel.OutputCols = scan.Cols 77 78 // Not Null Columns 79 // ---------------- 80 // Initialize not-NULL columns from the table schema. 81 rel.NotNullCols = tableNotNullCols(md, scan.Table) 82 if scan.Constraint != nil { 83 rel.NotNullCols.UnionWith(scan.Constraint.ExtractNotNullCols(b.evalCtx)) 84 } 85 rel.NotNullCols.IntersectionWith(rel.OutputCols) 86 87 // Outer Columns 88 // ------------- 89 // Scan operator never has outer columns. 90 91 // Functional Dependencies 92 // ----------------------- 93 // Check the hard limit to determine whether there is at most one row. Note 94 // that def.HardLimit = 0 indicates there is no known limit. 95 if hardLimit == 1 { 96 rel.FuncDeps.MakeMax1Row(rel.OutputCols) 97 } else { 98 // Initialize key FD's from the table schema, including constant columns from 99 // the constraint, minus any columns that are not projected by the Scan 100 // operator. 101 rel.FuncDeps.CopyFrom(MakeTableFuncDep(md, scan.Table)) 102 if scan.Constraint != nil { 103 rel.FuncDeps.AddConstants(scan.Constraint.ExtractConstCols(b.evalCtx)) 104 } 105 if tabMeta := md.TableMeta(scan.Table); tabMeta.Constraints != nil { 106 b.addFiltersToFuncDep(*tabMeta.Constraints.(*FiltersExpr), &rel.FuncDeps) 107 } 108 rel.FuncDeps.MakeNotNull(rel.NotNullCols) 109 rel.FuncDeps.ProjectCols(rel.OutputCols) 110 } 111 112 // Cardinality 113 // ----------- 114 // Restrict cardinality based on constraint, FDs, and hard limit. 115 rel.Cardinality = props.AnyCardinality 116 if scan.Constraint != nil && scan.Constraint.IsContradiction() { 117 rel.Cardinality = props.ZeroCardinality 118 } else if rel.FuncDeps.HasMax1Row() { 119 rel.Cardinality = rel.Cardinality.Limit(1) 120 } else { 121 if hardLimit > 0 && hardLimit < math.MaxUint32 { 122 rel.Cardinality = rel.Cardinality.Limit(uint32(hardLimit)) 123 } 124 if scan.Constraint != nil { 125 b.updateCardinalityFromConstraint(scan.Constraint, rel) 126 } 127 } 128 129 // Statistics 130 // ---------- 131 if !b.disableStats { 132 b.sb.buildScan(scan, rel) 133 } 134 } 135 136 func (b *logicalPropsBuilder) buildSequenceSelectProps( 137 seq *SequenceSelectExpr, rel *props.Relational, 138 ) { 139 // Output Columns 140 // -------------- 141 // Output columns are stored in the definition. 142 rel.OutputCols = seq.Cols.ToSet() 143 144 // Not Null Columns 145 // ---------------- 146 // Every column is not null. 147 rel.NotNullCols = rel.OutputCols 148 149 // Outer Columns 150 // ------------- 151 // The operator never has outer columns. 152 153 // Functional Dependencies 154 // ----------------------- 155 rel.FuncDeps.MakeMax1Row(rel.OutputCols) 156 157 // Cardinality 158 // ----------- 159 rel.Cardinality = props.OneCardinality 160 161 // Statistics 162 // ---------- 163 if !b.disableStats { 164 b.sb.buildSequenceSelect(rel) 165 } 166 } 167 168 func (b *logicalPropsBuilder) buildSelectProps(sel *SelectExpr, rel *props.Relational) { 169 BuildSharedProps(sel, &rel.Shared) 170 171 inputProps := sel.Input.Relational() 172 173 // Output Columns 174 // -------------- 175 // Inherit output columns from input. 176 rel.OutputCols = inputProps.OutputCols 177 178 // Not Null Columns 179 // ---------------- 180 // A column can become not null due to a null rejecting filter expression: 181 // 182 // SELECT y FROM xy WHERE y=5 183 // 184 // "y" cannot be null because the SQL equality operator rejects nulls. 185 rel.NotNullCols = b.rejectNullCols(sel.Filters) 186 rel.NotNullCols.UnionWith(inputProps.NotNullCols) 187 rel.NotNullCols.IntersectionWith(rel.OutputCols) 188 189 // Outer Columns 190 // ------------- 191 // Outer columns were derived by BuildSharedProps; remove any that are bound 192 // by input columns. 193 rel.OuterCols.DifferenceWith(inputProps.OutputCols) 194 195 // Functional Dependencies 196 // ----------------------- 197 // Start with copy of FuncDepSet from input, add FDs from the WHERE clause 198 // and outer columns, modify with any additional not-null columns, then 199 // possibly simplify by calling ProjectCols. 200 rel.FuncDeps.CopyFrom(&inputProps.FuncDeps) 201 b.addFiltersToFuncDep(sel.Filters, &rel.FuncDeps) 202 addOuterColsToFuncDep(rel.OuterCols, &rel.FuncDeps) 203 rel.FuncDeps.MakeNotNull(rel.NotNullCols) 204 rel.FuncDeps.ProjectCols(rel.OutputCols) 205 206 // Cardinality 207 // ----------- 208 // Select filter can filter any or all rows. 209 rel.Cardinality = inputProps.Cardinality.AsLowAs(0) 210 if sel.Filters.IsFalse() { 211 rel.Cardinality = props.ZeroCardinality 212 } else if rel.FuncDeps.HasMax1Row() { 213 rel.Cardinality = rel.Cardinality.Limit(1) 214 } else { 215 b.updateCardinalityFromFilters(sel.Filters, rel) 216 } 217 218 // Statistics 219 // ---------- 220 if !b.disableStats { 221 b.sb.buildSelect(sel, rel) 222 } 223 } 224 225 func (b *logicalPropsBuilder) buildProjectProps(prj *ProjectExpr, rel *props.Relational) { 226 BuildSharedProps(prj, &rel.Shared) 227 228 inputProps := prj.Input.Relational() 229 230 // Output Columns 231 // -------------- 232 // Output columns are the union of synthesized columns and passthrough columns. 233 for i := range prj.Projections { 234 rel.OutputCols.Add(prj.Projections[i].Col) 235 } 236 rel.OutputCols.UnionWith(prj.Passthrough) 237 238 // Not Null Columns 239 // ---------------- 240 // Not null columns were derived by initUnexportedFields; just intersect them 241 // with the output columns. 242 rel.NotNullCols = prj.notNullCols.Intersection(rel.OutputCols) 243 244 // Outer Columns 245 // ------------- 246 // Outer columns were derived by BuildSharedProps; remove any that are bound 247 // by input columns. 248 rel.OuterCols.DifferenceWith(inputProps.OutputCols) 249 250 // Functional Dependencies 251 // ----------------------- 252 // The functional dependencies were derived by initUnexportedFields; just 253 // remove columns that are not projected. 254 rel.FuncDeps.CopyFrom(&prj.internalFuncDeps) 255 rel.FuncDeps.ProjectCols(rel.OutputCols) 256 257 // Cardinality 258 // ----------- 259 // Inherit cardinality from input. 260 rel.Cardinality = inputProps.Cardinality 261 262 // Statistics 263 // ---------- 264 if !b.disableStats { 265 b.sb.buildProject(prj, rel) 266 } 267 } 268 269 func (b *logicalPropsBuilder) buildInnerJoinProps(join *InnerJoinExpr, rel *props.Relational) { 270 b.buildJoinProps(join, rel) 271 } 272 273 func (b *logicalPropsBuilder) buildLeftJoinProps(join *LeftJoinExpr, rel *props.Relational) { 274 b.buildJoinProps(join, rel) 275 } 276 277 func (b *logicalPropsBuilder) buildRightJoinProps(join *RightJoinExpr, rel *props.Relational) { 278 b.buildJoinProps(join, rel) 279 } 280 281 func (b *logicalPropsBuilder) buildFullJoinProps(join *FullJoinExpr, rel *props.Relational) { 282 b.buildJoinProps(join, rel) 283 } 284 285 func (b *logicalPropsBuilder) buildSemiJoinProps(join *SemiJoinExpr, rel *props.Relational) { 286 b.buildJoinProps(join, rel) 287 } 288 289 func (b *logicalPropsBuilder) buildAntiJoinProps(join *AntiJoinExpr, rel *props.Relational) { 290 b.buildJoinProps(join, rel) 291 } 292 293 func (b *logicalPropsBuilder) buildInnerJoinApplyProps( 294 join *InnerJoinApplyExpr, rel *props.Relational, 295 ) { 296 b.buildJoinProps(join, rel) 297 } 298 299 func (b *logicalPropsBuilder) buildLeftJoinApplyProps( 300 join *LeftJoinApplyExpr, rel *props.Relational, 301 ) { 302 b.buildJoinProps(join, rel) 303 } 304 305 func (b *logicalPropsBuilder) buildSemiJoinApplyProps( 306 join *SemiJoinApplyExpr, rel *props.Relational, 307 ) { 308 b.buildJoinProps(join, rel) 309 } 310 311 func (b *logicalPropsBuilder) buildAntiJoinApplyProps( 312 join *AntiJoinApplyExpr, rel *props.Relational, 313 ) { 314 b.buildJoinProps(join, rel) 315 } 316 317 func (b *logicalPropsBuilder) buildJoinProps(join RelExpr, rel *props.Relational) { 318 BuildSharedProps(join, &rel.Shared) 319 320 var h joinPropsHelper 321 h.init(b, join) 322 323 // Output Columns 324 // -------------- 325 rel.OutputCols = h.outputCols() 326 327 // Not Null Columns 328 // ---------------- 329 rel.NotNullCols = h.notNullCols() 330 rel.NotNullCols.IntersectionWith(rel.OutputCols) 331 332 // Outer Columns 333 // ------------- 334 // Outer columns were initially set by BuildSharedProps. Remove any that are 335 // bound by the input columns. 336 inputCols := h.leftProps.OutputCols.Union(h.rightProps.OutputCols) 337 rel.OuterCols.DifferenceWith(inputCols) 338 339 // Functional Dependencies 340 // ----------------------- 341 h.setFuncDeps(rel) 342 343 // Cardinality 344 // ----------- 345 // Calculate cardinality, depending on join type. 346 rel.Cardinality = h.cardinality() 347 if rel.FuncDeps.HasMax1Row() { 348 rel.Cardinality = rel.Cardinality.Limit(1) 349 } 350 351 // Statistics 352 // ---------- 353 if !b.disableStats { 354 b.sb.buildJoin(join, rel, &h) 355 } 356 } 357 358 func (b *logicalPropsBuilder) buildIndexJoinProps(indexJoin *IndexJoinExpr, rel *props.Relational) { 359 BuildSharedProps(indexJoin, &rel.Shared) 360 361 inputProps := indexJoin.Input.Relational() 362 md := b.mem.Metadata() 363 364 // Output Columns 365 // -------------- 366 rel.OutputCols = indexJoin.Cols 367 368 // Not Null Columns 369 // ---------------- 370 // Add not-NULL columns from the table schema, and filter out any not-NULL 371 // columns from the input that are not projected by the index join. 372 rel.NotNullCols = tableNotNullCols(md, indexJoin.Table) 373 rel.NotNullCols.IntersectionWith(rel.OutputCols) 374 375 // Outer Columns 376 // ------------- 377 // Outer columns were already derived by BuildSharedProps. 378 379 // Functional Dependencies 380 // ----------------------- 381 // Start with the input FD set, and join that with the table's FD. 382 rel.FuncDeps.CopyFrom(&inputProps.FuncDeps) 383 rel.FuncDeps.AddFrom(MakeTableFuncDep(md, indexJoin.Table)) 384 rel.FuncDeps.MakeNotNull(rel.NotNullCols) 385 rel.FuncDeps.ProjectCols(rel.OutputCols) 386 387 // Cardinality 388 // ----------- 389 // Inherit cardinality from input. 390 rel.Cardinality = inputProps.Cardinality 391 392 // Statistics 393 // ---------- 394 if !b.disableStats { 395 b.sb.buildIndexJoin(indexJoin, rel) 396 } 397 } 398 399 func (b *logicalPropsBuilder) buildLookupJoinProps(join *LookupJoinExpr, rel *props.Relational) { 400 b.buildJoinProps(join, rel) 401 } 402 403 func (b *logicalPropsBuilder) buildGeoLookupJoinProps( 404 join *GeoLookupJoinExpr, rel *props.Relational, 405 ) { 406 b.buildJoinProps(join, rel) 407 } 408 409 func (b *logicalPropsBuilder) buildZigzagJoinProps(join *ZigzagJoinExpr, rel *props.Relational) { 410 b.buildJoinProps(join, rel) 411 } 412 413 func (b *logicalPropsBuilder) buildMergeJoinProps(join *MergeJoinExpr, rel *props.Relational) { 414 b.buildJoinProps(join, rel) 415 } 416 417 func (b *logicalPropsBuilder) buildGroupByProps(groupBy *GroupByExpr, rel *props.Relational) { 418 b.buildGroupingExprProps(groupBy, rel) 419 } 420 421 func (b *logicalPropsBuilder) buildScalarGroupByProps( 422 scalarGroupBy *ScalarGroupByExpr, rel *props.Relational, 423 ) { 424 b.buildGroupingExprProps(scalarGroupBy, rel) 425 } 426 427 func (b *logicalPropsBuilder) buildDistinctOnProps( 428 distinctOn *DistinctOnExpr, rel *props.Relational, 429 ) { 430 b.buildGroupingExprProps(distinctOn, rel) 431 } 432 433 func (b *logicalPropsBuilder) buildEnsureDistinctOnProps( 434 distinctOn *EnsureDistinctOnExpr, rel *props.Relational, 435 ) { 436 b.buildGroupingExprProps(distinctOn, rel) 437 } 438 439 func (b *logicalPropsBuilder) buildUpsertDistinctOnProps( 440 distinctOn *UpsertDistinctOnExpr, rel *props.Relational, 441 ) { 442 b.buildGroupingExprProps(distinctOn, rel) 443 } 444 445 func (b *logicalPropsBuilder) buildEnsureUpsertDistinctOnProps( 446 distinctOn *EnsureUpsertDistinctOnExpr, rel *props.Relational, 447 ) { 448 b.buildGroupingExprProps(distinctOn, rel) 449 } 450 451 func (b *logicalPropsBuilder) buildGroupingExprProps(groupExpr RelExpr, rel *props.Relational) { 452 BuildSharedProps(groupExpr, &rel.Shared) 453 454 inputProps := groupExpr.Child(0).(RelExpr).Relational() 455 aggs := *groupExpr.Child(1).(*AggregationsExpr) 456 groupPrivate := groupExpr.Private().(*GroupingPrivate) 457 groupingCols := groupPrivate.GroupingCols 458 459 // Output Columns 460 // -------------- 461 // Output columns are the union of grouping columns with columns from the 462 // aggregate projection list. 463 rel.OutputCols = groupingCols.Copy() 464 for i := range aggs { 465 rel.OutputCols.Add(aggs[i].Col) 466 } 467 468 // Not Null Columns 469 // ---------------- 470 // Propagate not null setting from input columns that are being grouped. 471 rel.NotNullCols = inputProps.NotNullCols.Intersection(groupingCols) 472 473 for i := range aggs { 474 item := &aggs[i] 475 agg := ExtractAggFunc(item.Agg) 476 477 // Some aggregates never return NULL, regardless of input. 478 if opt.AggregateIsNeverNull(agg.Op()) { 479 rel.NotNullCols.Add(item.Col) 480 continue 481 } 482 483 // If there is a possibility that the aggregate function has zero input 484 // rows, then it may return NULL. This is possible with ScalarGroupBy and 485 // with AggFilter. 486 if groupExpr.Op() == opt.ScalarGroupByOp || item.Agg.Op() == opt.AggFilterOp { 487 continue 488 } 489 490 // Most aggregate functions return a non-NULL result if they have at least 491 // one input row with non-NULL argument value, and if all argument values are non-NULL. 492 if opt.AggregateIsNeverNullOnNonNullInput(agg.Op()) { 493 inputCols := ExtractAggInputColumns(agg) 494 if inputCols.SubsetOf(inputProps.NotNullCols) { 495 rel.NotNullCols.Add(item.Col) 496 } 497 } 498 } 499 500 // Outer Columns 501 // ------------- 502 // Outer columns were derived by BuildSharedProps; remove any that are bound 503 // by input columns. 504 rel.OuterCols.DifferenceWith(inputProps.OutputCols) 505 506 // Functional Dependencies 507 // ----------------------- 508 rel.FuncDeps.CopyFrom(&inputProps.FuncDeps) 509 if groupingCols.Empty() { 510 // When there are no grouping columns, then there is a single group, and 511 // therefore at most one output row. 512 rel.FuncDeps.MakeMax1Row(rel.OutputCols) 513 } else { 514 // Start by eliminating input columns that aren't projected. 515 rel.FuncDeps.ProjectCols(rel.OutputCols) 516 517 // The output of most of the grouping operators forms a strict key because 518 // they eliminate all duplicates in the grouping columns. However, the 519 // UpsertDistinctOn and EnsureUpsertDistinctOn operators do not group 520 // NULL values together, so they only form a lax key when NULL values 521 // are possible. 522 if groupPrivate.NullsAreDistinct && !groupingCols.SubsetOf(rel.NotNullCols) { 523 rel.FuncDeps.AddLaxKey(groupingCols, rel.OutputCols) 524 } else { 525 rel.FuncDeps.AddStrictKey(groupingCols, rel.OutputCols) 526 } 527 } 528 529 // Cardinality 530 // ----------- 531 if groupExpr.Op() == opt.ScalarGroupByOp { 532 // Scalar GroupBy returns exactly one row. 533 rel.Cardinality = props.OneCardinality 534 } else { 535 // GroupBy and DistinctOn act like a filter, never returning more rows 536 // than the input has. However, if the input has at least one row, then 537 // at least one row will also be returned by GroupBy and DistinctOn. 538 rel.Cardinality = inputProps.Cardinality.AsLowAs(1) 539 if rel.FuncDeps.HasMax1Row() { 540 rel.Cardinality = rel.Cardinality.Limit(1) 541 } 542 } 543 544 // Statistics 545 // ---------- 546 if !b.disableStats { 547 b.sb.buildGroupBy(groupExpr, rel) 548 } 549 } 550 551 func (b *logicalPropsBuilder) buildUnionProps(union *UnionExpr, rel *props.Relational) { 552 b.buildSetProps(union, rel) 553 } 554 555 func (b *logicalPropsBuilder) buildIntersectProps(isect *IntersectExpr, rel *props.Relational) { 556 b.buildSetProps(isect, rel) 557 } 558 559 func (b *logicalPropsBuilder) buildExceptProps(except *ExceptExpr, rel *props.Relational) { 560 b.buildSetProps(except, rel) 561 } 562 563 func (b *logicalPropsBuilder) buildUnionAllProps(union *UnionAllExpr, rel *props.Relational) { 564 b.buildSetProps(union, rel) 565 } 566 567 func (b *logicalPropsBuilder) buildIntersectAllProps( 568 isect *IntersectAllExpr, rel *props.Relational, 569 ) { 570 b.buildSetProps(isect, rel) 571 } 572 573 func (b *logicalPropsBuilder) buildExceptAllProps(except *ExceptAllExpr, rel *props.Relational) { 574 b.buildSetProps(except, rel) 575 } 576 577 func (b *logicalPropsBuilder) buildSetProps(setNode RelExpr, rel *props.Relational) { 578 BuildSharedProps(setNode, &rel.Shared) 579 580 leftProps := setNode.Child(0).(RelExpr).Relational() 581 rightProps := setNode.Child(1).(RelExpr).Relational() 582 setPrivate := setNode.Private().(*SetPrivate) 583 if len(setPrivate.OutCols) != len(setPrivate.LeftCols) || 584 len(setPrivate.OutCols) != len(setPrivate.RightCols) { 585 panic(errors.AssertionFailedf( 586 "lists in SetPrivate are not all the same length. new:%d, left:%d, right:%d", 587 log.Safe(len(setPrivate.OutCols)), log.Safe(len(setPrivate.LeftCols)), log.Safe(len(setPrivate.RightCols)), 588 )) 589 } 590 591 // Output Columns 592 // -------------- 593 // Output columns are stored in the definition. 594 rel.OutputCols = setPrivate.OutCols.ToSet() 595 596 // Not Null Columns 597 // ---------------- 598 // Columns have to be not-null on both sides to be not-null in result. 599 // setPrivate matches columns on the left and right sides of the operator 600 // with the output columns, since OutputCols are not ordered and may 601 // not correspond to each other. 602 for i := range setPrivate.OutCols { 603 if leftProps.NotNullCols.Contains((setPrivate.LeftCols)[i]) && 604 rightProps.NotNullCols.Contains((setPrivate.RightCols)[i]) { 605 rel.NotNullCols.Add((setPrivate.OutCols)[i]) 606 } 607 } 608 609 // Outer Columns 610 // ------------- 611 // Outer columns were already derived by BuildSharedProps. 612 613 // Functional Dependencies 614 // ----------------------- 615 switch setNode.Op() { 616 case opt.UnionOp, opt.IntersectOp, opt.ExceptOp: 617 // These operators eliminate duplicates, so a strict key exists. 618 rel.FuncDeps.AddStrictKey(rel.OutputCols, rel.OutputCols) 619 } 620 621 // Cardinality 622 // ----------- 623 // Calculate cardinality of the set operator. 624 rel.Cardinality = b.makeSetCardinality( 625 setNode.Op(), leftProps.Cardinality, rightProps.Cardinality) 626 627 // Statistics 628 // ---------- 629 if !b.disableStats { 630 b.sb.buildSetNode(setNode, rel) 631 } 632 } 633 634 func (b *logicalPropsBuilder) buildValuesProps(values *ValuesExpr, rel *props.Relational) { 635 BuildSharedProps(values, &rel.Shared) 636 637 card := uint32(len(values.Rows)) 638 639 // Output Columns 640 // -------------- 641 // Use output columns that are attached to the values op. 642 rel.OutputCols = values.Cols.ToSet() 643 644 // Not Null Columns 645 // ---------------- 646 // All columns are assumed to be nullable, unless they contain only constant 647 // non-null values. 648 649 for colIdx, col := range values.Cols { 650 notNull := true 651 for rowIdx := range values.Rows { 652 val := values.Rows[rowIdx].(*TupleExpr).Elems[colIdx] 653 if !opt.IsConstValueOp(val) || val.Op() == opt.NullOp { 654 // Null or not a constant. 655 notNull = false 656 break 657 } 658 } 659 if notNull { 660 rel.NotNullCols.Add(col) 661 } 662 } 663 664 // Outer Columns 665 // ------------- 666 // Outer columns were already derived by BuildSharedProps. 667 668 // Functional Dependencies 669 // ----------------------- 670 if card <= 1 { 671 rel.FuncDeps.MakeMax1Row(rel.OutputCols) 672 } 673 674 // Cardinality 675 // ----------- 676 // Cardinality is number of tuples in the Values operator. 677 rel.Cardinality = props.Cardinality{Min: card, Max: card} 678 679 // Statistics 680 // ---------- 681 if !b.disableStats { 682 b.sb.buildValues(values, rel) 683 } 684 } 685 686 func (b *logicalPropsBuilder) buildBasicProps(e opt.Expr, cols opt.ColList, rel *props.Relational) { 687 BuildSharedProps(e, &rel.Shared) 688 689 // Output Columns 690 // -------------- 691 rel.OutputCols = cols.ToSet() 692 693 // Not Null Columns 694 // ---------------- 695 // All columns are assumed to be nullable. 696 697 // Outer Columns 698 // ------------- 699 // No outer columns. 700 701 // Functional Dependencies 702 // ----------------------- 703 // Empty FD set. 704 705 // Cardinality 706 // ----------- 707 // Don't make any assumptions about cardinality of output. 708 rel.Cardinality = props.AnyCardinality 709 710 // Statistics 711 // ---------- 712 if !b.disableStats { 713 b.sb.buildUnknown(rel) 714 } 715 } 716 717 func (b *logicalPropsBuilder) buildWithProps(with *WithExpr, rel *props.Relational) { 718 // Copy over the props from the input. 719 inputProps := with.Main.Relational() 720 721 BuildSharedProps(with, &rel.Shared) 722 723 // Side Effects 724 // ------------ 725 // This expression has side effects if either Binding or Input has side 726 // effects, which is what is computed by the call to BuildSharedProps. 727 728 // Output Columns 729 // -------------- 730 // Inherited from the input expression. 731 rel.OutputCols = inputProps.OutputCols 732 733 // Not Null Columns 734 // ---------------- 735 // Inherited from the input expression. 736 rel.NotNullCols = inputProps.NotNullCols 737 738 // Outer Columns 739 // ------------- 740 // The outer columns are the union of the outer columns from Binding or Input, 741 // which is what is computed by the call to BuildSharedProps. 742 743 // Functional Dependencies 744 // ----------------------- 745 rel.FuncDeps = inputProps.FuncDeps 746 747 // Cardinality 748 // ----------- 749 rel.Cardinality = inputProps.Cardinality 750 751 // Statistics 752 // ---------- 753 // Inherited from the input expression. 754 rel.Stats = inputProps.Stats 755 } 756 757 func (b *logicalPropsBuilder) buildWithScanProps(withScan *WithScanExpr, rel *props.Relational) { 758 BuildSharedProps(withScan, &rel.Shared) 759 bindingProps := withScan.BindingProps 760 761 // Side Effects 762 // ------------ 763 // WithScan has no side effects (even if the original expression had them). 764 765 // Output Columns 766 // -------------- 767 rel.OutputCols = withScan.OutCols.ToSet() 768 769 // Not Null Columns 770 // ---------------- 771 rel.NotNullCols = opt.TranslateColSet(bindingProps.NotNullCols, withScan.InCols, withScan.OutCols) 772 773 // Outer Columns 774 // ------------- 775 // No outer columns. 776 777 // Functional Dependencies 778 // ----------------------- 779 // Inherit dependencies from the referenced expression (remapping the 780 // columns). 781 rel.FuncDeps.CopyFrom(&bindingProps.FuncDeps) 782 for i := range withScan.InCols { 783 rel.FuncDeps.AddEquivalency(withScan.InCols[i], withScan.OutCols[i]) 784 } 785 rel.FuncDeps.ProjectCols(withScan.OutCols.ToSet()) 786 787 // Cardinality 788 // ----------- 789 // Inherit from the referenced expression. 790 rel.Cardinality = bindingProps.Cardinality 791 792 // Statistics 793 // ---------- 794 if !b.disableStats { 795 b.sb.buildWithScan(withScan, rel) 796 } 797 } 798 799 func (b *logicalPropsBuilder) buildRecursiveCTEProps(rec *RecursiveCTEExpr, rel *props.Relational) { 800 BuildSharedProps(rec, &rel.Shared) 801 802 // Output Columns 803 // -------------- 804 rel.OutputCols = rec.OutCols.ToSet() 805 806 // Not Null Columns 807 // ---------------- 808 // All columns are assumed to be nullable. 809 810 // Outer Columns 811 // ------------- 812 // No outer columns. 813 814 // Functional Dependencies 815 // ----------------------- 816 // No known FDs. 817 818 // Cardinality 819 // ----------- 820 // At least the cardinality of the initial buffer. 821 rel.Cardinality = props.AnyCardinality.AtLeast(rec.Initial.Relational().Cardinality) 822 823 // Statistics 824 // ---------- 825 if !b.disableStats { 826 b.sb.buildUnknown(rel) 827 } 828 } 829 830 func (b *logicalPropsBuilder) buildExplainProps(explain *ExplainExpr, rel *props.Relational) { 831 b.buildBasicProps(explain, explain.ColList, rel) 832 } 833 834 func (b *logicalPropsBuilder) buildShowTraceForSessionProps( 835 showTrace *ShowTraceForSessionExpr, rel *props.Relational, 836 ) { 837 b.buildBasicProps(showTrace, showTrace.ColList, rel) 838 } 839 840 func (b *logicalPropsBuilder) buildOpaqueRelProps(op *OpaqueRelExpr, rel *props.Relational) { 841 b.buildBasicProps(op, op.Columns, rel) 842 } 843 844 func (b *logicalPropsBuilder) buildOpaqueMutationProps( 845 op *OpaqueMutationExpr, rel *props.Relational, 846 ) { 847 b.buildBasicProps(op, op.Columns, rel) 848 } 849 850 func (b *logicalPropsBuilder) buildOpaqueDDLProps(op *OpaqueDDLExpr, rel *props.Relational) { 851 b.buildBasicProps(op, op.Columns, rel) 852 } 853 854 func (b *logicalPropsBuilder) buildAlterTableSplitProps( 855 split *AlterTableSplitExpr, rel *props.Relational, 856 ) { 857 b.buildBasicProps(split, split.Columns, rel) 858 } 859 860 func (b *logicalPropsBuilder) buildAlterTableUnsplitProps( 861 unsplit *AlterTableUnsplitExpr, rel *props.Relational, 862 ) { 863 b.buildBasicProps(unsplit, unsplit.Columns, rel) 864 } 865 866 func (b *logicalPropsBuilder) buildAlterTableUnsplitAllProps( 867 unsplitAll *AlterTableUnsplitAllExpr, rel *props.Relational, 868 ) { 869 b.buildBasicProps(unsplitAll, unsplitAll.Columns, rel) 870 } 871 872 func (b *logicalPropsBuilder) buildAlterTableRelocateProps( 873 relocate *AlterTableRelocateExpr, rel *props.Relational, 874 ) { 875 b.buildBasicProps(relocate, relocate.Columns, rel) 876 } 877 878 func (b *logicalPropsBuilder) buildControlJobsProps(ctl *ControlJobsExpr, rel *props.Relational) { 879 b.buildBasicProps(ctl, opt.ColList{}, rel) 880 } 881 882 func (b *logicalPropsBuilder) buildCancelQueriesProps( 883 cancel *CancelQueriesExpr, rel *props.Relational, 884 ) { 885 b.buildBasicProps(cancel, opt.ColList{}, rel) 886 } 887 888 func (b *logicalPropsBuilder) buildCancelSessionsProps( 889 cancel *CancelSessionsExpr, rel *props.Relational, 890 ) { 891 b.buildBasicProps(cancel, opt.ColList{}, rel) 892 } 893 894 func (b *logicalPropsBuilder) buildExportProps(export *ExportExpr, rel *props.Relational) { 895 b.buildBasicProps(export, export.Columns, rel) 896 } 897 898 func (b *logicalPropsBuilder) buildLimitProps(limit *LimitExpr, rel *props.Relational) { 899 BuildSharedProps(limit, &rel.Shared) 900 901 inputProps := limit.Input.Relational() 902 903 haveConstLimit := false 904 constLimit := int64(math.MaxUint32) 905 if cnst, ok := limit.Limit.(*ConstExpr); ok { 906 haveConstLimit = true 907 constLimit = int64(*cnst.Value.(*tree.DInt)) 908 } 909 910 // Side Effects 911 // ------------ 912 // Negative limits can trigger a runtime error. 913 if constLimit < 0 || !haveConstLimit { 914 rel.CanHaveSideEffects = true 915 rel.VolatilitySet.AddImmutable() 916 } 917 918 // Output Columns 919 // -------------- 920 // Output columns are inherited from input. 921 rel.OutputCols = inputProps.OutputCols 922 923 // Not Null Columns 924 // ---------------- 925 // Not null columns are inherited from input. 926 rel.NotNullCols = inputProps.NotNullCols 927 928 // Outer Columns 929 // ------------- 930 // Outer columns were already derived by BuildSharedProps. 931 932 // Functional Dependencies 933 // ----------------------- 934 // Inherit functional dependencies from input if limit is > 1, else just use 935 // single row dependencies. 936 if constLimit > 1 { 937 rel.FuncDeps.CopyFrom(&inputProps.FuncDeps) 938 } else { 939 rel.FuncDeps.MakeMax1Row(rel.OutputCols) 940 } 941 942 // Cardinality 943 // ----------- 944 // Limit puts a cap on the number of rows returned by input. 945 rel.Cardinality = inputProps.Cardinality 946 if constLimit <= 0 { 947 rel.Cardinality = props.ZeroCardinality 948 } else if constLimit < math.MaxUint32 { 949 rel.Cardinality = rel.Cardinality.Limit(uint32(constLimit)) 950 } 951 952 // Statistics 953 // ---------- 954 if !b.disableStats { 955 b.sb.buildLimit(limit, rel) 956 } 957 } 958 959 func (b *logicalPropsBuilder) buildOffsetProps(offset *OffsetExpr, rel *props.Relational) { 960 BuildSharedProps(offset, &rel.Shared) 961 962 inputProps := offset.Input.Relational() 963 964 // Output Columns 965 // -------------- 966 // Output columns are inherited from input. 967 rel.OutputCols = inputProps.OutputCols 968 969 // Not Null Columns 970 // ---------------- 971 // Not null columns are inherited from input. 972 rel.NotNullCols = inputProps.NotNullCols 973 974 // Outer Columns 975 // ------------- 976 // Outer columns were already derived by BuildSharedProps. 977 978 // Functional Dependencies 979 // ----------------------- 980 // Inherit functional dependencies from input. 981 rel.FuncDeps.CopyFrom(&inputProps.FuncDeps) 982 983 // Cardinality 984 // ----------- 985 // Offset decreases the number of rows that are passed through from input. 986 rel.Cardinality = inputProps.Cardinality 987 if cnst, ok := offset.Offset.(*ConstExpr); ok { 988 constOffset := int64(*cnst.Value.(*tree.DInt)) 989 if constOffset > 0 { 990 if constOffset > math.MaxUint32 { 991 constOffset = math.MaxUint32 992 } 993 rel.Cardinality = inputProps.Cardinality.Skip(uint32(constOffset)) 994 } 995 } 996 997 // Statistics 998 // ---------- 999 if !b.disableStats { 1000 b.sb.buildOffset(offset, rel) 1001 } 1002 } 1003 1004 func (b *logicalPropsBuilder) buildMax1RowProps(max1Row *Max1RowExpr, rel *props.Relational) { 1005 BuildSharedProps(max1Row, &rel.Shared) 1006 1007 inputProps := max1Row.Input.Relational() 1008 1009 // Output Columns 1010 // -------------- 1011 // Output columns are inherited from input. 1012 rel.OutputCols = inputProps.OutputCols 1013 1014 // Not Null Columns 1015 // ---------------- 1016 // Not null columns are inherited from input. 1017 rel.NotNullCols = inputProps.NotNullCols 1018 1019 // Outer Columns 1020 // ------------- 1021 // Outer columns were already derived by BuildSharedProps. 1022 1023 // Functional Dependencies 1024 // ----------------------- 1025 // Max1Row always returns zero or one rows. 1026 rel.FuncDeps.MakeMax1Row(rel.OutputCols) 1027 1028 // Cardinality 1029 // ----------- 1030 // Max1Row ensures that zero or one row is returned from input. 1031 rel.Cardinality = inputProps.Cardinality.Limit(1) 1032 1033 // Statistics 1034 // ---------- 1035 if !b.disableStats { 1036 b.sb.buildMax1Row(max1Row, rel) 1037 } 1038 } 1039 1040 func (b *logicalPropsBuilder) buildOrdinalityProps(ord *OrdinalityExpr, rel *props.Relational) { 1041 BuildSharedProps(ord, &rel.Shared) 1042 1043 inputProps := ord.Input.Relational() 1044 1045 // Output Columns 1046 // -------------- 1047 // An extra output column is added to those projected by input operator. 1048 rel.OutputCols = inputProps.OutputCols.Copy() 1049 rel.OutputCols.Add(ord.ColID) 1050 1051 // Not Null Columns 1052 // ---------------- 1053 // The new output column is not null, and other columns inherit not null 1054 // property from input. 1055 rel.NotNullCols = inputProps.NotNullCols.Copy() 1056 rel.NotNullCols.Add(ord.ColID) 1057 1058 // Outer Columns 1059 // ------------- 1060 // Outer columns were already derived by BuildSharedProps. 1061 1062 // Functional Dependencies 1063 // ----------------------- 1064 // Inherit functional dependencies from input, and add strict key FD for the 1065 // additional key column. 1066 rel.FuncDeps.CopyFrom(&inputProps.FuncDeps) 1067 if key, ok := rel.FuncDeps.StrictKey(); ok { 1068 // Any existing keys are still keys. 1069 rel.FuncDeps.AddStrictKey(key, rel.OutputCols) 1070 } 1071 rel.FuncDeps.AddStrictKey(opt.MakeColSet(ord.ColID), rel.OutputCols) 1072 1073 // Cardinality 1074 // ----------- 1075 // Inherit cardinality from input. 1076 rel.Cardinality = inputProps.Cardinality 1077 1078 // Statistics 1079 // ---------- 1080 if !b.disableStats { 1081 b.sb.buildOrdinality(ord, rel) 1082 } 1083 } 1084 1085 func (b *logicalPropsBuilder) buildWindowProps(window *WindowExpr, rel *props.Relational) { 1086 BuildSharedProps(window, &rel.Shared) 1087 1088 inputProps := window.Input.Relational() 1089 1090 // Output Columns 1091 // -------------- 1092 // Output columns are all the passthrough columns with the addition of the 1093 // window function column. 1094 rel.OutputCols = inputProps.OutputCols.Copy() 1095 for _, w := range window.Windows { 1096 rel.OutputCols.Add(w.Col) 1097 } 1098 1099 // Not Null Columns 1100 // ---------------- 1101 // Inherit not null columns from input. 1102 // TODO(justin): in many cases the added column may not be nullable. 1103 rel.NotNullCols = inputProps.NotNullCols.Intersection(rel.OutputCols) 1104 1105 // Outer Columns 1106 // ------------- 1107 // Outer columns were derived by BuildSharedProps; remove any that are bound 1108 // by input columns. 1109 rel.OuterCols.DifferenceWith(inputProps.OutputCols) 1110 1111 // Functional Dependencies 1112 // ----------------------- 1113 // Functional dependencies are the same as the input. 1114 // TODO(justin): in many cases there are more FDs to be derived, some 1115 // examples include: 1116 // * row_number+the partition is a key. 1117 // * rank is determined by the partition and the value being ordered by. 1118 // * aggregations/first_value/last_value are determined by the partition. 1119 rel.FuncDeps.CopyFrom(&inputProps.FuncDeps) 1120 1121 // Cardinality 1122 // ----------- 1123 // Window functions never change the cardinality of their input. 1124 rel.Cardinality = inputProps.Cardinality 1125 1126 // Statistics 1127 // ---------- 1128 if !b.disableStats { 1129 b.sb.buildWindow(window, rel) 1130 } 1131 } 1132 1133 func (b *logicalPropsBuilder) buildProjectSetProps( 1134 projectSet *ProjectSetExpr, rel *props.Relational, 1135 ) { 1136 BuildSharedProps(projectSet, &rel.Shared) 1137 1138 inputProps := projectSet.Input.Relational() 1139 1140 // Output Columns 1141 // -------------- 1142 // Output columns are the union between the output columns from the Zip and 1143 // the input. 1144 rel.OutputCols = projectSet.Zip.OutputCols() 1145 rel.OutputCols.UnionWith(inputProps.OutputCols) 1146 1147 // Not Null Columns 1148 // ---------------- 1149 // Inherit not null columns from input. All other columns are assumed to be 1150 // nullable. 1151 rel.NotNullCols = inputProps.NotNullCols.Copy() 1152 1153 // Outer Columns 1154 // ------------- 1155 // Outer columns were derived by BuildSharedProps; remove any that are bound 1156 // by input columns. 1157 rel.OuterCols.DifferenceWith(inputProps.OutputCols) 1158 1159 // Functional Dependencies 1160 // ----------------------- 1161 // Start with copy of FuncDepSet. Since ProjectSet is a lateral cross join 1162 // between the input and the functional zip (which has an empty FD set), call 1163 // MakeApply with an empty FD set. Then add outer columns, modify with 1164 // any additional not-null columns, and possibly simplify by calling 1165 // ProjectCols. 1166 rel.FuncDeps.CopyFrom(&inputProps.FuncDeps) 1167 rel.FuncDeps.MakeApply(&props.FuncDepSet{}) 1168 addOuterColsToFuncDep(rel.OuterCols, &rel.FuncDeps) 1169 rel.FuncDeps.MakeNotNull(rel.NotNullCols) 1170 rel.FuncDeps.ProjectCols(rel.OutputCols) 1171 1172 // Cardinality 1173 // ----------- 1174 // Don't make any assumptions about cardinality of ProjectSet unless the 1175 // input cardinality is zero. 1176 if inputProps.Cardinality == props.ZeroCardinality { 1177 rel.Cardinality = props.ZeroCardinality 1178 } else { 1179 rel.Cardinality = props.AnyCardinality 1180 } 1181 1182 // Statistics 1183 // ---------- 1184 if !b.disableStats { 1185 b.sb.buildProjectSet(projectSet, rel) 1186 } 1187 } 1188 1189 func (b *logicalPropsBuilder) buildInsertProps(ins *InsertExpr, rel *props.Relational) { 1190 b.buildMutationProps(ins, rel) 1191 } 1192 1193 func (b *logicalPropsBuilder) buildUpdateProps(upd *UpdateExpr, rel *props.Relational) { 1194 b.buildMutationProps(upd, rel) 1195 } 1196 1197 func (b *logicalPropsBuilder) buildUpsertProps(ups *UpsertExpr, rel *props.Relational) { 1198 b.buildMutationProps(ups, rel) 1199 } 1200 1201 func (b *logicalPropsBuilder) buildDeleteProps(del *DeleteExpr, rel *props.Relational) { 1202 b.buildMutationProps(del, rel) 1203 } 1204 1205 func (b *logicalPropsBuilder) buildMutationProps(mutation RelExpr, rel *props.Relational) { 1206 BuildSharedProps(mutation, &rel.Shared) 1207 1208 private := mutation.Private().(*MutationPrivate) 1209 1210 // If no rows are output by the operator, then all other properties retain 1211 // default values. 1212 if !private.NeedResults() { 1213 return 1214 } 1215 1216 inputProps := mutation.Child(0).(RelExpr).Relational() 1217 md := b.mem.Metadata() 1218 tab := md.Table(private.Table) 1219 1220 // Output Columns 1221 // -------------- 1222 // Only non-mutation columns are output columns. 1223 for i, n := 0, tab.ColumnCount(); i < n; i++ { 1224 if private.IsColumnOutput(i) { 1225 colID := private.Table.ColumnID(i) 1226 rel.OutputCols.Add(colID) 1227 } 1228 } 1229 1230 // The output columns of the mutation will also include all 1231 // columns it allowed to pass through. 1232 for _, col := range private.PassthroughCols { 1233 if col != 0 { 1234 rel.OutputCols.Add(col) 1235 } 1236 } 1237 1238 // Not Null Columns 1239 // ---------------- 1240 // A column should be marked as not-null if the target table column is not 1241 // null or the corresponding insert and fetch/update columns are not null. In 1242 // other words, if either the source or destination column is not null, then 1243 // the column must be not null. 1244 for i, n := 0, tab.ColumnCount(); i < n; i++ { 1245 tabColID := private.Table.ColumnID(i) 1246 if !rel.OutputCols.Contains(tabColID) { 1247 continue 1248 } 1249 1250 // If the target table column is not null, then mark the column as not null. 1251 if !tab.Column(i).IsNullable() { 1252 rel.NotNullCols.Add(tabColID) 1253 continue 1254 } 1255 1256 // If the input column is not null, then the result will be not null. 1257 if inputProps.NotNullCols.Contains(private.ReturnCols[i]) { 1258 rel.NotNullCols.Add(private.Table.ColumnID(i)) 1259 } 1260 } 1261 1262 // Outer Columns 1263 // ------------- 1264 // Outer columns were already derived by BuildSharedProps. 1265 1266 // Functional Dependencies 1267 // ----------------------- 1268 // Start with copy of FuncDepSet from input. Map the FDs of each source column 1269 // to the corresponding destination column by making the columns equivalent 1270 // and then filtering out the source columns via a call to ProjectCols. 1271 rel.FuncDeps.CopyFrom(&inputProps.FuncDeps) 1272 private.AddEquivTableCols(md, &rel.FuncDeps) 1273 rel.FuncDeps.ProjectCols(rel.OutputCols) 1274 1275 // Cardinality 1276 // ----------- 1277 // Inherit cardinality from input. 1278 rel.Cardinality = inputProps.Cardinality 1279 1280 // Statistics 1281 // ---------- 1282 if !b.disableStats { 1283 b.sb.buildMutation(mutation, rel) 1284 } 1285 } 1286 1287 func (b *logicalPropsBuilder) buildCreateTableProps(ct *CreateTableExpr, rel *props.Relational) { 1288 BuildSharedProps(ct, &rel.Shared) 1289 } 1290 1291 func (b *logicalPropsBuilder) buildCreateViewProps(cv *CreateViewExpr, rel *props.Relational) { 1292 BuildSharedProps(cv, &rel.Shared) 1293 } 1294 1295 func (b *logicalPropsBuilder) buildFiltersItemProps(item *FiltersItem, scalar *props.Scalar) { 1296 BuildSharedProps(item.Condition, &scalar.Shared) 1297 1298 // Constraints 1299 // ----------- 1300 cb := constraintsBuilder{md: b.mem.Metadata(), evalCtx: b.evalCtx} 1301 // TODO(rytaft): Using local variables here to avoid a data race. It would be 1302 // better to avoid lazy building of props altogether. 1303 constraints, tightConstraints := cb.buildConstraints(item.Condition) 1304 if constraints.IsUnconstrained() { 1305 scalar.Constraints, scalar.TightConstraints = nil, false 1306 } else { 1307 scalar.Constraints, scalar.TightConstraints = constraints, tightConstraints 1308 } 1309 1310 // Functional Dependencies 1311 // ----------------------- 1312 // Add constant columns. No need to add not null columns, because they 1313 // are only relevant if there are lax FDs that can be made strict. 1314 if scalar.Constraints != nil { 1315 constCols := scalar.Constraints.ExtractConstCols(b.evalCtx) 1316 scalar.FuncDeps.AddConstants(constCols) 1317 } 1318 1319 // Check for filter conjunct of the form: x = y. 1320 if eq, ok := item.Condition.(*EqExpr); ok { 1321 if leftVar, ok := eq.Left.(*VariableExpr); ok { 1322 if rightVar, ok := eq.Right.(*VariableExpr); ok { 1323 scalar.FuncDeps.AddEquivalency(leftVar.Col, rightVar.Col) 1324 } 1325 } 1326 } 1327 } 1328 1329 func (b *logicalPropsBuilder) buildProjectionsItemProps( 1330 item *ProjectionsItem, scalar *props.Scalar, 1331 ) { 1332 item.Typ = item.Element.DataType() 1333 BuildSharedProps(item.Element, &scalar.Shared) 1334 } 1335 1336 func (b *logicalPropsBuilder) buildAggregationsItemProps( 1337 item *AggregationsItem, scalar *props.Scalar, 1338 ) { 1339 item.Typ = item.Agg.DataType() 1340 BuildSharedProps(item.Agg, &scalar.Shared) 1341 } 1342 1343 func (b *logicalPropsBuilder) buildWindowsItemProps(item *WindowsItem, scalar *props.Scalar) { 1344 item.Typ = item.Function.DataType() 1345 BuildSharedProps(item.Function, &scalar.Shared) 1346 } 1347 1348 func (b *logicalPropsBuilder) buildZipItemProps(item *ZipItem, scalar *props.Scalar) { 1349 item.Typ = item.Fn.DataType() 1350 BuildSharedProps(item.Fn, &scalar.Shared) 1351 } 1352 1353 // BuildSharedProps fills in the shared properties derived from the given 1354 // expression's subtree. It will only recurse into a child when it is not 1355 // already caching properties. 1356 // 1357 // Note that shared is an "input-output" argument, and should be assumed 1358 // to be partially filled in already. Boolean fields such as HasPlaceholder, 1359 // CanHaveSideEffects and HasCorrelatedSubquery should never be reset to false 1360 // once set to true. 1361 func BuildSharedProps(e opt.Expr, shared *props.Shared) { 1362 switch t := e.(type) { 1363 case *VariableExpr: 1364 // Variable introduces outer column. 1365 shared.OuterCols.Add(t.Col) 1366 return 1367 1368 case *PlaceholderExpr: 1369 shared.HasPlaceholder = true 1370 return 1371 1372 case *DivExpr: 1373 // Division by zero error is possible, unless the right-hand side is a 1374 // non-zero constant. 1375 var nonZero bool 1376 if c, ok := t.Right.(*ConstExpr); ok { 1377 switch v := c.Value.(type) { 1378 case *tree.DInt: 1379 nonZero = (*v != 0) 1380 case *tree.DFloat: 1381 nonZero = (*v != 0.0) 1382 case *tree.DDecimal: 1383 nonZero = !v.IsZero() 1384 } 1385 } 1386 if !nonZero { 1387 shared.CanHaveSideEffects = true 1388 shared.VolatilitySet.AddImmutable() 1389 } 1390 1391 case *SubqueryExpr, *ExistsExpr, *AnyExpr, *ArrayFlattenExpr: 1392 shared.HasSubquery = true 1393 if hasOuterCols(e.Child(0)) { 1394 shared.HasCorrelatedSubquery = true 1395 } 1396 if t.Op() == opt.AnyOp && hasOuterCols(e.Child(1)) { 1397 shared.HasCorrelatedSubquery = true 1398 } 1399 1400 case *FunctionExpr: 1401 if t.Properties.Impure { 1402 // Impure functions can return different value on each call. 1403 shared.CanHaveSideEffects = true 1404 } 1405 shared.VolatilitySet.Add(t.Overload.Volatility) 1406 1407 default: 1408 if opt.IsMutationOp(e) { 1409 shared.CanHaveSideEffects = true 1410 shared.CanMutate = true 1411 shared.VolatilitySet.AddVolatile() 1412 } 1413 } 1414 1415 // Recursively build the shared properties. 1416 for i, n := 0, e.ChildCount(); i < n; i++ { 1417 child := e.Child(i) 1418 1419 // Some expressions cache shared properties. 1420 var cached *props.Shared 1421 switch t := child.(type) { 1422 case RelExpr: 1423 cached = &t.Relational().Shared 1424 case ScalarPropsExpr: 1425 cached = &t.ScalarProps().Shared 1426 } 1427 1428 // Don't need to recurse if properties are cached. 1429 if cached != nil { 1430 shared.OuterCols.UnionWith(cached.OuterCols) 1431 if cached.HasPlaceholder { 1432 shared.HasPlaceholder = true 1433 } 1434 shared.VolatilitySet.UnionWith(cached.VolatilitySet) 1435 if cached.CanHaveSideEffects { 1436 shared.CanHaveSideEffects = true 1437 } 1438 if cached.CanMutate { 1439 shared.CanMutate = true 1440 } 1441 if cached.HasSubquery { 1442 shared.HasSubquery = true 1443 } 1444 if cached.HasCorrelatedSubquery { 1445 shared.HasCorrelatedSubquery = true 1446 } 1447 } else { 1448 BuildSharedProps(e.Child(i), shared) 1449 } 1450 } 1451 } 1452 1453 // hasOuterCols returns true if the given expression has outer columns (i.e. 1454 // columns that are referenced by the expression but not bound by it). 1455 func hasOuterCols(e opt.Expr) bool { 1456 switch t := e.(type) { 1457 case *VariableExpr: 1458 return true 1459 case RelExpr: 1460 return !t.Relational().OuterCols.Empty() 1461 case ScalarPropsExpr: 1462 return !t.ScalarProps().Shared.OuterCols.Empty() 1463 } 1464 1465 for i, n := 0, e.ChildCount(); i < n; i++ { 1466 if hasOuterCols(e.Child(i)) { 1467 return true 1468 } 1469 } 1470 1471 return false 1472 } 1473 1474 // MakeTableFuncDep returns the set of functional dependencies derived from the 1475 // given base table. The set is derived lazily and is cached in the metadata, 1476 // since it may be accessed multiple times during query optimization. For more 1477 // details, see Relational.FuncDepSet. 1478 func MakeTableFuncDep(md *opt.Metadata, tabID opt.TableID) *props.FuncDepSet { 1479 fd, ok := md.TableAnnotation(tabID, fdAnnID).(*props.FuncDepSet) 1480 if ok { 1481 // Already made. 1482 return fd 1483 } 1484 1485 // Make now and annotate the metadata table with it for next time. 1486 var allCols opt.ColSet 1487 tab := md.Table(tabID) 1488 for i := 0; i < tab.DeletableColumnCount(); i++ { 1489 allCols.Add(tabID.ColumnID(i)) 1490 } 1491 1492 fd = &props.FuncDepSet{} 1493 for i := 0; i < tab.IndexCount(); i++ { 1494 var keyCols opt.ColSet 1495 index := tab.Index(i) 1496 1497 if index.IsInverted() { 1498 // Skip inverted indexes for now. 1499 continue 1500 } 1501 1502 // If index has a separate lax key, add a lax key FD. Otherwise, add a 1503 // strict key. See the comment for cat.Index.LaxKeyColumnCount. 1504 for col := 0; col < index.LaxKeyColumnCount(); col++ { 1505 ord := index.Column(col).Ordinal 1506 keyCols.Add(tabID.ColumnID(ord)) 1507 } 1508 if index.LaxKeyColumnCount() < index.KeyColumnCount() { 1509 // This case only occurs for a UNIQUE index having a NULL-able column. 1510 fd.AddLaxKey(keyCols, allCols) 1511 } else { 1512 fd.AddStrictKey(keyCols, allCols) 1513 } 1514 } 1515 md.SetTableAnnotation(tabID, fdAnnID, fd) 1516 return fd 1517 } 1518 1519 func (b *logicalPropsBuilder) makeSetCardinality( 1520 nt opt.Operator, left, right props.Cardinality, 1521 ) props.Cardinality { 1522 var card props.Cardinality 1523 switch nt { 1524 case opt.UnionOp, opt.UnionAllOp: 1525 // Add cardinality of left and right inputs. 1526 card = left.Add(right) 1527 1528 case opt.IntersectOp, opt.IntersectAllOp: 1529 // Use minimum of left and right Max cardinality. 1530 card = props.Cardinality{Min: 0, Max: left.Max} 1531 card = card.Limit(right.Max) 1532 1533 case opt.ExceptOp, opt.ExceptAllOp: 1534 // Use left Max cardinality. 1535 card = props.Cardinality{Min: 0, Max: left.Max} 1536 if left.Min > right.Max { 1537 card.Min = left.Min - right.Max 1538 } 1539 } 1540 switch nt { 1541 case opt.UnionOp, opt.IntersectOp, opt.ExceptOp: 1542 // Removing distinct values results in at least one row if input has at 1543 // least one row. 1544 card = card.AsLowAs(1) 1545 } 1546 return card 1547 } 1548 1549 // rejectNullCols returns the set of all columns that are inferred to be not- 1550 // null, based on the filter conditions. 1551 func (b *logicalPropsBuilder) rejectNullCols(filters FiltersExpr) opt.ColSet { 1552 var notNullCols opt.ColSet 1553 for i := range filters { 1554 filterProps := filters[i].ScalarProps() 1555 if filterProps.Constraints != nil { 1556 notNullCols.UnionWith(filterProps.Constraints.ExtractNotNullCols(b.evalCtx)) 1557 } 1558 } 1559 return notNullCols 1560 } 1561 1562 // addFiltersToFuncDep returns the union of all functional dependencies from 1563 // each condition in the filters. 1564 func (b *logicalPropsBuilder) addFiltersToFuncDep(filters FiltersExpr, fdset *props.FuncDepSet) { 1565 for i := range filters { 1566 filterProps := filters[i].ScalarProps() 1567 fdset.AddFrom(&filterProps.FuncDeps) 1568 } 1569 1570 if len(filters) <= 1 { 1571 return 1572 } 1573 1574 // Some columns can only be determined to be constant from multiple 1575 // constraints (e.g. x <= 1 AND x >= 1); we intersect the constraints and 1576 // extract const columns from the intersection. But intersection is expensive 1577 // so we first do a quick check to rule out cases where each constraint refers 1578 // to a different set of columns. 1579 var cols opt.ColSet 1580 possibleIntersection := false 1581 for i := range filters { 1582 if c := filters[i].ScalarProps().Constraints; c != nil { 1583 s := c.ExtractCols() 1584 if cols.Intersects(s) { 1585 possibleIntersection = true 1586 break 1587 } 1588 cols.UnionWith(s) 1589 } 1590 } 1591 1592 if possibleIntersection { 1593 intersection := constraint.Unconstrained 1594 for i := range filters { 1595 if c := filters[i].ScalarProps().Constraints; c != nil { 1596 intersection = intersection.Intersect(b.evalCtx, c) 1597 } 1598 } 1599 constCols := intersection.ExtractConstCols(b.evalCtx) 1600 fdset.AddConstants(constCols) 1601 } 1602 } 1603 1604 // updateCardinalityFromFilters determines whether a tight cardinality bound 1605 // can be determined from the filters, and updates the cardinality accordingly. 1606 // Specifically, it may be possible to determine a tight bound if the key 1607 // column(s) are constrained to a finite number of values. 1608 func (b *logicalPropsBuilder) updateCardinalityFromFilters( 1609 filters FiltersExpr, rel *props.Relational, 1610 ) { 1611 for i := range filters { 1612 filterProps := filters[i].ScalarProps() 1613 if filterProps.Constraints == nil { 1614 continue 1615 } 1616 1617 for j, n := 0, filterProps.Constraints.Length(); j < n; j++ { 1618 c := filterProps.Constraints.Constraint(j) 1619 b.updateCardinalityFromConstraint(c, rel) 1620 } 1621 } 1622 } 1623 1624 // updateCardinalityFromConstraint determines whether a tight cardinality 1625 // bound can be determined from the constraint, and updates the cardinality 1626 // accordingly. Specifically, it may be possible to determine a tight bound 1627 // if the key column(s) are constrained to a finite number of values. 1628 func (b *logicalPropsBuilder) updateCardinalityFromConstraint( 1629 c *constraint.Constraint, rel *props.Relational, 1630 ) { 1631 cols := c.Columns.ColSet() 1632 if !rel.FuncDeps.ColsAreLaxKey(cols) { 1633 return 1634 } 1635 1636 count := c.CalculateMaxResults(b.evalCtx, cols, rel.NotNullCols) 1637 if count != 0 && count < math.MaxUint32 { 1638 rel.Cardinality = rel.Cardinality.Limit(uint32(count)) 1639 } 1640 } 1641 1642 // ensureLookupJoinInputProps lazily populates the relational properties that 1643 // apply to the lookup side of the join, as if it were a Scan operator. 1644 func ensureLookupJoinInputProps(join *LookupJoinExpr, sb *statisticsBuilder) *props.Relational { 1645 relational := &join.lookupProps 1646 if relational.OutputCols.Empty() { 1647 md := join.Memo().Metadata() 1648 relational.OutputCols = join.Cols.Difference(join.Input.Relational().OutputCols) 1649 1650 // Include the key columns in the output columns. 1651 index := md.Table(join.Table).Index(join.Index) 1652 for i := range join.KeyCols { 1653 indexColID := join.Table.ColumnID(index.Column(i).Ordinal) 1654 relational.OutputCols.Add(indexColID) 1655 } 1656 1657 relational.NotNullCols = tableNotNullCols(md, join.Table) 1658 relational.NotNullCols.IntersectionWith(relational.OutputCols) 1659 relational.Cardinality = props.AnyCardinality 1660 relational.FuncDeps.CopyFrom(MakeTableFuncDep(md, join.Table)) 1661 relational.FuncDeps.ProjectCols(relational.OutputCols) 1662 relational.Stats = *sb.makeTableStatistics(join.Table) 1663 } 1664 return relational 1665 } 1666 1667 // ensureGeoLookupJoinInputProps lazily populates the relational properties 1668 // that apply to the lookup side of the join, as if it were a Scan operator. 1669 func ensureGeoLookupJoinInputProps( 1670 join *GeoLookupJoinExpr, sb *statisticsBuilder, 1671 ) *props.Relational { 1672 relational := &join.lookupProps 1673 if relational.OutputCols.Empty() { 1674 md := join.Memo().Metadata() 1675 relational.OutputCols = join.Cols.Difference(join.Input.Relational().OutputCols) 1676 relational.NotNullCols = tableNotNullCols(md, join.Table) 1677 relational.NotNullCols.IntersectionWith(relational.OutputCols) 1678 relational.Cardinality = props.AnyCardinality 1679 1680 // TODO(rytaft): See if we need to use different functional dependencies 1681 // for the inverted index. 1682 relational.FuncDeps.CopyFrom(MakeTableFuncDep(md, join.Table)) 1683 relational.FuncDeps.ProjectCols(relational.OutputCols) 1684 1685 // TODO(rytaft): Change this to use inverted index stats once available. 1686 relational.Stats = *sb.makeTableStatistics(join.Table) 1687 } 1688 return relational 1689 } 1690 1691 // ensureZigzagJoinInputProps lazily populates the relational properties that 1692 // apply to the two sides of the join, as if it were a Scan operator. 1693 func ensureZigzagJoinInputProps(join *ZigzagJoinExpr, sb *statisticsBuilder) { 1694 ensureInputPropsForIndex( 1695 join.Memo().Metadata(), 1696 join.LeftTable, 1697 join.LeftIndex, 1698 join.Cols, 1699 &join.leftProps, 1700 sb, 1701 ) 1702 // For stats purposes, ensure left and right column sets are disjoint. 1703 ensureInputPropsForIndex( 1704 join.Memo().Metadata(), 1705 join.RightTable, 1706 join.RightIndex, 1707 join.Cols.Difference(join.leftProps.OutputCols), 1708 &join.rightProps, 1709 sb, 1710 ) 1711 } 1712 1713 // ensureInputPropsForIndex populates relational properties for the specified 1714 // table and index at the specified logical properties pointer. 1715 func ensureInputPropsForIndex( 1716 md *opt.Metadata, 1717 tabID opt.TableID, 1718 indexOrd int, 1719 outputCols opt.ColSet, 1720 relProps *props.Relational, 1721 sb *statisticsBuilder, 1722 ) { 1723 if relProps.OutputCols.Empty() { 1724 relProps.OutputCols = md.TableMeta(tabID).IndexColumns(indexOrd) 1725 relProps.OutputCols.IntersectionWith(outputCols) 1726 relProps.NotNullCols = tableNotNullCols(md, tabID) 1727 relProps.NotNullCols.IntersectionWith(relProps.OutputCols) 1728 relProps.Cardinality = props.AnyCardinality 1729 relProps.FuncDeps.CopyFrom(MakeTableFuncDep(md, tabID)) 1730 relProps.FuncDeps.ProjectCols(relProps.OutputCols) 1731 relProps.Stats = *sb.makeTableStatistics(tabID) 1732 } 1733 } 1734 1735 // tableNotNullCols returns the set of not-NULL columns from the given table. 1736 func tableNotNullCols(md *opt.Metadata, tabID opt.TableID) opt.ColSet { 1737 cs := opt.ColSet{} 1738 tab := md.Table(tabID) 1739 1740 // Only iterate over non-mutation columns, since even non-null mutation 1741 // columns can be null during backfill. 1742 for i := 0; i < tab.ColumnCount(); i++ { 1743 // Non-null mutation columns can be null during backfill. 1744 if !tab.Column(i).IsNullable() { 1745 cs.Add(tabID.ColumnID(i)) 1746 } 1747 } 1748 return cs 1749 } 1750 1751 // addOuterColsToFuncDep adds the given outer columns and columns equivalent to 1752 // them to the FD set. References to outer columns act like constants, since 1753 // they are the same for all rows in the inner relation. 1754 func addOuterColsToFuncDep(outerCols opt.ColSet, fdset *props.FuncDepSet) { 1755 equivCols := fdset.ComputeEquivClosure(outerCols) 1756 fdset.AddConstants(equivCols) 1757 } 1758 1759 // joinPropsHelper is a helper that calculates and stores properties related to 1760 // joins that are used internally when deriving logical properties and 1761 // statistics. 1762 type joinPropsHelper struct { 1763 join RelExpr 1764 joinType opt.Operator 1765 1766 leftProps *props.Relational 1767 rightProps *props.Relational 1768 1769 filters FiltersExpr 1770 filtersFD props.FuncDepSet 1771 filterNotNullCols opt.ColSet 1772 filterIsTrue bool 1773 filterIsFalse bool 1774 1775 selfJoinCols opt.ColSet 1776 } 1777 1778 func (h *joinPropsHelper) init(b *logicalPropsBuilder, joinExpr RelExpr) { 1779 h.join = joinExpr 1780 1781 switch join := joinExpr.(type) { 1782 case *LookupJoinExpr: 1783 h.leftProps = joinExpr.Child(0).(RelExpr).Relational() 1784 ensureLookupJoinInputProps(join, &b.sb) 1785 h.joinType = join.JoinType 1786 h.rightProps = &join.lookupProps 1787 h.filters = join.On 1788 b.addFiltersToFuncDep(h.filters, &h.filtersFD) 1789 h.filterNotNullCols = b.rejectNullCols(h.filters) 1790 1791 // Apply the lookup join equalities. 1792 md := join.Memo().Metadata() 1793 index := md.Table(join.Table).Index(join.Index) 1794 for i, colID := range join.KeyCols { 1795 indexColID := join.Table.ColumnID(index.Column(i).Ordinal) 1796 h.filterNotNullCols.Add(colID) 1797 h.filterNotNullCols.Add(indexColID) 1798 h.filtersFD.AddEquivalency(colID, indexColID) 1799 if colID == indexColID { 1800 // This can happen if an index join was converted into a lookup join. 1801 h.selfJoinCols.Add(colID) 1802 } 1803 } 1804 1805 // Lookup join has implicit equality conditions on KeyCols. 1806 h.filterIsTrue = false 1807 h.filterIsFalse = h.filters.IsFalse() 1808 1809 case *GeoLookupJoinExpr: 1810 h.leftProps = joinExpr.Child(0).(RelExpr).Relational() 1811 ensureGeoLookupJoinInputProps(join, &b.sb) 1812 h.joinType = join.JoinType 1813 h.rightProps = &join.lookupProps 1814 h.filters = join.On 1815 b.addFiltersToFuncDep(h.filters, &h.filtersFD) 1816 h.filterNotNullCols = b.rejectNullCols(h.filters) 1817 1818 // Geospatial lookup join always has a filter condition on the index keys. 1819 h.filterIsTrue = false 1820 h.filterIsFalse = h.filters.IsFalse() 1821 1822 case *MergeJoinExpr: 1823 h.joinType = join.JoinType 1824 h.leftProps = join.Left.Relational() 1825 h.rightProps = join.Right.Relational() 1826 h.filters = join.On 1827 b.addFiltersToFuncDep(h.filters, &h.filtersFD) 1828 h.filterNotNullCols = b.rejectNullCols(h.filters) 1829 1830 // Apply the merge join equalities. 1831 for i := range join.LeftEq { 1832 l := join.LeftEq[i].ID() 1833 r := join.RightEq[i].ID() 1834 h.filterNotNullCols.Add(l) 1835 h.filterNotNullCols.Add(r) 1836 h.filtersFD.AddEquivalency(l, r) 1837 } 1838 1839 // Merge join has implicit equality conditions on the merge columns. 1840 h.filterIsTrue = false 1841 h.filterIsFalse = h.filters.IsFalse() 1842 1843 case *ZigzagJoinExpr: 1844 ensureZigzagJoinInputProps(join, &b.sb) 1845 h.joinType = opt.InnerJoinOp 1846 h.leftProps = &join.leftProps 1847 h.rightProps = &join.rightProps 1848 h.filters = join.On 1849 b.addFiltersToFuncDep(h.filters, &h.filtersFD) 1850 h.filterNotNullCols = b.rejectNullCols(h.filters) 1851 1852 // Apply the zigzag join equalities. 1853 for i := range join.LeftEqCols { 1854 leftColID := join.LeftEqCols[i] 1855 rightColID := join.RightEqCols[i] 1856 1857 h.filterNotNullCols.Add(leftColID) 1858 h.filterNotNullCols.Add(rightColID) 1859 h.filtersFD.AddEquivalency(leftColID, rightColID) 1860 } 1861 1862 default: 1863 h.joinType = join.Op() 1864 h.leftProps = join.Child(0).(RelExpr).Relational() 1865 h.rightProps = join.Child(1).(RelExpr).Relational() 1866 1867 h.filters = *join.Child(2).(*FiltersExpr) 1868 b.addFiltersToFuncDep(h.filters, &h.filtersFD) 1869 h.filterNotNullCols = b.rejectNullCols(h.filters) 1870 h.filterIsTrue = h.filters.IsTrue() 1871 h.filterIsFalse = h.filters.IsFalse() 1872 } 1873 } 1874 1875 func (h *joinPropsHelper) outputCols() opt.ColSet { 1876 // Output columns are union of columns from left and right inputs, except 1877 // in case of: 1878 // 1879 // 1. semi and anti joins, which only project the left columns 1880 // 2. lookup joins, which can project a subset of input columns 1881 // 1882 var cols opt.ColSet 1883 switch h.joinType { 1884 case opt.SemiJoinOp, opt.AntiJoinOp, opt.SemiJoinApplyOp, opt.AntiJoinApplyOp: 1885 cols = h.leftProps.OutputCols.Copy() 1886 1887 default: 1888 cols = h.leftProps.OutputCols.Union(h.rightProps.OutputCols) 1889 } 1890 1891 if lookup, ok := h.join.(*LookupJoinExpr); ok { 1892 // Remove any columns that are not projected by the lookup join. 1893 cols.IntersectionWith(lookup.Cols) 1894 } 1895 1896 return cols 1897 } 1898 1899 func (h *joinPropsHelper) notNullCols() opt.ColSet { 1900 var notNullCols opt.ColSet 1901 1902 // Left/full outer joins can result in right columns becoming null. 1903 // Otherwise, propagate not null setting from right child. 1904 switch h.joinType { 1905 case opt.LeftJoinOp, opt.FullJoinOp, opt.LeftJoinApplyOp, 1906 opt.SemiJoinOp, opt.SemiJoinApplyOp, opt.AntiJoinOp, opt.AntiJoinApplyOp: 1907 1908 default: 1909 notNullCols = h.rightProps.NotNullCols.Copy() 1910 } 1911 1912 // Right/full outer joins can result in left columns becoming null. 1913 // Otherwise, propagate not null setting from left child. 1914 switch h.joinType { 1915 case opt.RightJoinOp, opt.FullJoinOp: 1916 1917 default: 1918 notNullCols.UnionWith(h.leftProps.NotNullCols) 1919 } 1920 1921 // Add not-null constraints from ON predicate for inner and semi-joins. 1922 switch h.joinType { 1923 case opt.InnerJoinOp, opt.SemiJoinApplyOp: 1924 notNullCols.UnionWith(h.filterNotNullCols) 1925 } 1926 1927 return notNullCols 1928 } 1929 1930 func (h *joinPropsHelper) setFuncDeps(rel *props.Relational) { 1931 // Start with FDs from left side, and modify based on join type. 1932 rel.FuncDeps.CopyFrom(&h.leftProps.FuncDeps) 1933 1934 // Anti and semi joins only inherit FDs from left side, since right side 1935 // simply acts like a filter. 1936 switch h.joinType { 1937 case opt.SemiJoinOp, opt.SemiJoinApplyOp: 1938 // Add FDs from the ON predicate, which include equivalent columns and 1939 // constant columns. Any outer columns become constants as well. 1940 rel.FuncDeps.AddFrom(&h.filtersFD) 1941 addOuterColsToFuncDep(rel.OuterCols, &rel.FuncDeps) 1942 rel.FuncDeps.MakeNotNull(rel.NotNullCols) 1943 1944 // Call ProjectCols to remove any FDs involving columns from the right side. 1945 rel.FuncDeps.ProjectCols(rel.OutputCols) 1946 1947 case opt.AntiJoinOp, opt.AntiJoinApplyOp: 1948 // Anti-joins inherit FDs from left input, and nothing more, since the 1949 // right input is not projected, and the ON predicate doesn't filter rows 1950 // in the usual way. 1951 1952 default: 1953 // Joins are modeled as consisting of several steps: 1954 // 1. Compute cartesian product of left and right inputs. 1955 // 2. For inner joins, apply ON predicate filter on resulting rows. 1956 // 3. For outer joins, add non-matching rows, extended with NULL values 1957 // for the null-supplying side of the join. 1958 if opt.IsJoinApplyOp(h.join) { 1959 rel.FuncDeps.MakeApply(&h.rightProps.FuncDeps) 1960 } else { 1961 rel.FuncDeps.MakeProduct(&h.rightProps.FuncDeps) 1962 } 1963 1964 notNullInputCols := h.leftProps.NotNullCols.Union(h.rightProps.NotNullCols) 1965 1966 switch h.joinType { 1967 case opt.InnerJoinOp, opt.InnerJoinApplyOp: 1968 // Add FDs from the ON predicate, which include equivalent columns and 1969 // constant columns. 1970 rel.FuncDeps.AddFrom(&h.filtersFD) 1971 addOuterColsToFuncDep(rel.OuterCols, &rel.FuncDeps) 1972 1973 case opt.LeftJoinOp, opt.LeftJoinApplyOp: 1974 rel.FuncDeps.MakeLeftOuter( 1975 &h.leftProps.FuncDeps, &h.filtersFD, 1976 h.leftProps.OutputCols, h.rightProps.OutputCols, notNullInputCols, 1977 ) 1978 1979 case opt.RightJoinOp: 1980 rel.FuncDeps.MakeLeftOuter( 1981 &h.rightProps.FuncDeps, &h.filtersFD, 1982 h.rightProps.OutputCols, h.leftProps.OutputCols, notNullInputCols, 1983 ) 1984 1985 case opt.FullJoinOp: 1986 rel.FuncDeps.MakeFullOuter(h.leftProps.OutputCols, h.rightProps.OutputCols, notNullInputCols) 1987 1988 default: 1989 panic(errors.AssertionFailedf("unhandled join type %s", h.joinType)) 1990 } 1991 1992 rel.FuncDeps.MakeNotNull(rel.NotNullCols) 1993 1994 // Call ProjectCols to trigger simplification, since outer joins may have 1995 // created new possibilities for simplifying removed columns. 1996 rel.FuncDeps.ProjectCols(rel.OutputCols) 1997 } 1998 } 1999 2000 func (h *joinPropsHelper) cardinality() props.Cardinality { 2001 left := h.leftProps.Cardinality 2002 2003 switch h.joinType { 2004 case opt.SemiJoinOp, opt.SemiJoinApplyOp, opt.AntiJoinOp, opt.AntiJoinApplyOp: 2005 // Semi/Anti join cardinality never exceeds left input cardinality, and 2006 // allows zero rows. 2007 return left.AsLowAs(0) 2008 } 2009 2010 // Other join types can return up to cross product of rows. 2011 right := h.rightProps.Cardinality 2012 innerJoinCard := left.Product(right) 2013 2014 // Apply filter to cardinality. 2015 if !h.filterIsTrue { 2016 if h.filterIsFalse { 2017 innerJoinCard = props.ZeroCardinality 2018 } else { 2019 innerJoinCard = innerJoinCard.AsLowAs(0) 2020 } 2021 } 2022 2023 // Outer joins return minimum number of rows, depending on type. 2024 switch h.joinType { 2025 case opt.LeftJoinOp, opt.LeftJoinApplyOp: 2026 return innerJoinCard.AtLeast(left) 2027 2028 case opt.RightJoinOp: 2029 return innerJoinCard.AtLeast(right) 2030 2031 case opt.FullJoinOp: 2032 if innerJoinCard.IsZero() { 2033 // In this case, we know that each left or right row will generate an 2034 // output row. 2035 return left.Add(right) 2036 } 2037 var c props.Cardinality 2038 // We get at least MAX(left.Min, right.Min) rows. 2039 c.Min = left.Min 2040 if c.Min < right.Min { 2041 c.Min = right.Min 2042 } 2043 // We could get left.Max + right.Max rows (if the filter doesn't match 2044 // anything). We use Add here because it handles overflow. 2045 c.Max = left.Add(right).Max 2046 return innerJoinCard.AtLeast(c) 2047 2048 default: 2049 return innerJoinCard 2050 } 2051 } 2052 2053 func (b *logicalPropsBuilder) buildFakeRelProps(fake *FakeRelExpr, rel *props.Relational) { 2054 *rel = *fake.Props 2055 }