github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/memo/expr.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 "context" 15 "fmt" 16 "math/bits" 17 "sort" 18 "strings" 19 20 "github.com/cockroachdb/cockroach/pkg/sql/opt" 21 "github.com/cockroachdb/cockroach/pkg/sql/opt/cat" 22 "github.com/cockroachdb/cockroach/pkg/sql/opt/props" 23 "github.com/cockroachdb/cockroach/pkg/sql/opt/props/physical" 24 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 25 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 26 "github.com/cockroachdb/cockroach/pkg/sql/types" 27 "github.com/cockroachdb/cockroach/pkg/util/log" 28 "github.com/cockroachdb/errors" 29 ) 30 31 // RelExpr is implemented by all operators tagged as Relational. Relational 32 // expressions have a set of logical properties that describe the content and 33 // characteristics of their behavior and results. They are stored as part of a 34 // memo group that contains other logically equivalent expressions. Expressions 35 // in the same memo group are linked together in a list that can be traversed 36 // via calls to FirstExpr and NextExpr: 37 // 38 // +--------------------------------------+ 39 // | +---------------+ | 40 // | | |FirstExpr |FirstExpr 41 // v v | | 42 // member #1 -------> member #2 --------> member #3 -------> nil 43 // NextExpr NextExpr NextExpr 44 // 45 // A relational expression's physical properties and cost are defined once it 46 // has been optimized. 47 type RelExpr interface { 48 opt.Expr 49 50 // Memo is the memo which contains this relational expression. 51 Memo() *Memo 52 53 // Relational is the set of logical properties that describe the content and 54 // characteristics of this expression's behavior and results. 55 Relational() *props.Relational 56 57 // RequiredPhysical is the set of required physical properties with respect to 58 // which this expression was optimized. Enforcers may be added to the 59 // expression tree to ensure the physical properties are provided. 60 // 61 // Set when optimization is complete, only for the expressions in the final 62 // tree. 63 RequiredPhysical() *physical.Required 64 65 // ProvidedPhysical is the set of provided physical properties (which must be 66 // compatible with the set of required physical properties). 67 // 68 // Set when optimization is complete, only for the expressions in the final 69 // tree. 70 ProvidedPhysical() *physical.Provided 71 72 // Cost is an estimate of the cost of executing this expression tree. Set 73 // when optimization is complete, only for the expressions in the final tree. 74 Cost() Cost 75 76 // FirstExpr returns the first member expression in the memo group (could be 77 // this expression if it happens to be first in the group). Subsequent members 78 // can be enumerated by then calling NextExpr. Note that enforcer operators 79 // are not part of this list (but do maintain a link to it). 80 FirstExpr() RelExpr 81 82 // NextExpr returns the next member expression in the memo group, or nil if 83 // there are no further members in the group. 84 NextExpr() RelExpr 85 86 // group returns the memo group that contains this expression and any other 87 // logically equivalent expressions. There is one group struct for each memo 88 // group that stores the properties for the group, as well as the pointer to 89 // the first member of the group. 90 group() exprGroup 91 92 // bestProps returns the instance of bestProps associated with this 93 // expression. 94 bestProps() *bestProps 95 96 // setNext sets this expression's next pointer to point to the given 97 // expression. setNext will panic if the next pointer has already been set. 98 setNext(e RelExpr) 99 } 100 101 // ScalarPropsExpr is implemented by scalar expressions which cache scalar 102 // properties, like FiltersExpr and ProjectionsExpr. These expressions are also 103 // tagged with the ScalarProps tag. 104 type ScalarPropsExpr interface { 105 opt.ScalarExpr 106 107 // ScalarProps returns the scalar properties associated with the expression. 108 ScalarProps() *props.Scalar 109 } 110 111 // TrueSingleton is a global instance of TrueExpr, to avoid allocations. 112 var TrueSingleton = &TrueExpr{} 113 114 // FalseSingleton is a global instance of FalseExpr, to avoid allocations. 115 var FalseSingleton = &FalseExpr{} 116 117 // NullSingleton is a global instance of NullExpr having the Unknown type (most 118 // common case), to avoid allocations. 119 var NullSingleton = &NullExpr{Typ: types.Unknown} 120 121 // TODO(justin): perhaps these should be auto-generated. 122 123 // RankSingleton is the global instance of RankExpr. 124 var RankSingleton = &RankExpr{} 125 126 // RowNumberSingleton is the global instance of RowNumber. 127 var RowNumberSingleton = &RowNumberExpr{} 128 129 // DenseRankSingleton is the global instance of DenseRankExpr. 130 var DenseRankSingleton = &DenseRankExpr{} 131 132 // PercentRankSingleton is the global instance of PercentRankExpr. 133 var PercentRankSingleton = &PercentRankExpr{} 134 135 // CumeDistSingleton is the global instance of CumeDistExpr. 136 var CumeDistSingleton = &CumeDistExpr{} 137 138 // CountRowsSingleton maintains a global instance of CountRowsExpr, to avoid 139 // allocations. 140 var CountRowsSingleton = &CountRowsExpr{} 141 142 // TrueFilter is a global instance of the empty FiltersExpr, used in situations 143 // where the filter should always evaluate to true: 144 // 145 // SELECT * FROM a INNER JOIN b ON True 146 // 147 var TrueFilter = FiltersExpr{} 148 149 // EmptyTuple is a global instance of a TupleExpr that contains no elements. 150 // While this cannot be created in SQL, it can be the created by normalizations. 151 var EmptyTuple = &TupleExpr{Typ: types.EmptyTuple} 152 153 // ScalarListWithEmptyTuple is a global instance of a ScalarListExpr containing 154 // a TupleExpr that contains no elements. It's used when constructing an empty 155 // ValuesExpr: 156 // 157 // SELECT 1 158 // 159 var ScalarListWithEmptyTuple = ScalarListExpr{EmptyTuple} 160 161 // EmptyGroupingPrivate is a global instance of a GroupingPrivate that has no 162 // grouping columns and no ordering. 163 var EmptyGroupingPrivate = &GroupingPrivate{} 164 165 // EmptyJoinPrivate is a global instance of a JoinPrivate that has no fields 166 // set. 167 var EmptyJoinPrivate = &JoinPrivate{} 168 169 // LastGroupMember returns the last member in the same memo group of the given 170 // relational expression. 171 func LastGroupMember(e RelExpr) RelExpr { 172 for { 173 next := e.NextExpr() 174 if next == nil { 175 return e 176 } 177 e = next 178 } 179 } 180 181 // IsTrue is true if the FiltersExpr always evaluates to true. This is the case 182 // when it has zero conditions. 183 func (n FiltersExpr) IsTrue() bool { 184 return len(n) == 0 185 } 186 187 // IsFalse is true if the FiltersExpr always evaluates to false. The only case 188 // that's checked is the fully normalized case, when the list contains a single 189 // False condition. 190 func (n FiltersExpr) IsFalse() bool { 191 return len(n) == 1 && n[0].Condition.Op() == opt.FalseOp 192 } 193 194 // OuterCols returns the set of outer columns needed by any of the filter 195 // condition expressions. 196 func (n FiltersExpr) OuterCols(mem *Memo) opt.ColSet { 197 var colSet opt.ColSet 198 for i := range n { 199 colSet.UnionWith(n[i].ScalarProps().OuterCols) 200 } 201 return colSet 202 } 203 204 // Sort sorts the FilterItems in n by the IDs of the expression. 205 func (n *FiltersExpr) Sort() { 206 sort.Slice(*n, func(i, j int) bool { 207 return (*n)[i].Condition.(opt.ScalarExpr).ID() < (*n)[j].Condition.(opt.ScalarExpr).ID() 208 }) 209 } 210 211 // Deduplicate removes all the duplicate filters from n. 212 func (n *FiltersExpr) Deduplicate() { 213 dedup := (*n)[:0] 214 215 // Only add it if it hasn't already been added. 216 for i, filter := range *n { 217 found := false 218 for j := i - 1; j >= 0; j-- { 219 previouslySeenFilter := (*n)[j] 220 if previouslySeenFilter.Condition == filter.Condition { 221 found = true 222 break 223 } 224 } 225 if !found { 226 dedup = append(dedup, filter) 227 } 228 } 229 230 *n = dedup 231 } 232 233 // RemoveCommonFilters removes the filters found in other from n. 234 func (n *FiltersExpr) RemoveCommonFilters(other FiltersExpr) { 235 // TODO(ridwanmsharif): Faster intersection using a map 236 common := (*n)[:0] 237 for _, filter := range *n { 238 found := false 239 for _, otherFilter := range other { 240 if filter.Condition == otherFilter.Condition { 241 found = true 242 break 243 } 244 } 245 if !found { 246 common = append(common, filter) 247 } 248 } 249 *n = common 250 } 251 252 // OutputCols returns the set of columns constructed by the Aggregations 253 // expression. 254 func (n AggregationsExpr) OutputCols() opt.ColSet { 255 var colSet opt.ColSet 256 for i := range n { 257 colSet.Add(n[i].Col) 258 } 259 return colSet 260 } 261 262 // OuterCols returns the set of outer columns needed by any of the zip 263 // expressions. 264 func (n ZipExpr) OuterCols() opt.ColSet { 265 var colSet opt.ColSet 266 for i := range n { 267 colSet.UnionWith(n[i].ScalarProps().OuterCols) 268 } 269 return colSet 270 } 271 272 // OutputCols returns the set of columns constructed by the Zip expression. 273 func (n ZipExpr) OutputCols() opt.ColSet { 274 var colSet opt.ColSet 275 for i := range n { 276 for _, col := range n[i].Cols { 277 colSet.Add(col) 278 } 279 } 280 return colSet 281 } 282 283 // TupleOrdinal is an ordinal index into an expression of type Tuple. It is 284 // used by the ColumnAccess scalar expression. 285 type TupleOrdinal uint32 286 287 // ScanLimit is used for a limited table or index scan and stores the limit as 288 // well as the desired scan direction. A value of 0 means that there is no 289 // limit. 290 type ScanLimit int64 291 292 // MakeScanLimit initializes a ScanLimit with a number of rows and a direction. 293 func MakeScanLimit(rowCount int64, reverse bool) ScanLimit { 294 if reverse { 295 return ScanLimit(-rowCount) 296 } 297 return ScanLimit(rowCount) 298 } 299 300 // IsSet returns true if there is a limit. 301 func (sl ScanLimit) IsSet() bool { 302 return sl != 0 303 } 304 305 // RowCount returns the number of rows in the limit. 306 func (sl ScanLimit) RowCount() int64 { 307 if sl.Reverse() { 308 return int64(-sl) 309 } 310 return int64(sl) 311 } 312 313 // Reverse returns true if the limit requires a reverse scan. 314 func (sl ScanLimit) Reverse() bool { 315 return sl < 0 316 } 317 318 func (sl ScanLimit) String() string { 319 if sl.Reverse() { 320 return fmt.Sprintf("%d(rev)", -sl) 321 } 322 return fmt.Sprintf("%d", sl) 323 } 324 325 // ScanFlags stores any flags for the scan specified in the query (see 326 // tree.IndexFlags). These flags may be consulted by transformation rules or the 327 // coster. 328 type ScanFlags struct { 329 // NoIndexJoin disallows use of non-covering indexes (index-join) for scanning 330 // this table. 331 NoIndexJoin bool 332 333 // ForceIndex forces the use of a specific index (specified in Index). 334 // ForceIndex and NoIndexJoin cannot both be set at the same time. 335 ForceIndex bool 336 Direction tree.Direction 337 Index int 338 } 339 340 // Empty returns true if there are no flags set. 341 func (sf *ScanFlags) Empty() bool { 342 return !sf.NoIndexJoin && !sf.ForceIndex 343 } 344 345 // JoinFlags stores restrictions on the join execution method, derived from 346 // hints for a join specified in the query (see tree.JoinTableExpr). 347 // It is a bitfield where a bit is 1 if a certain type of join is allowed. The 348 // value 0 is special and indicates that any join is allowed. 349 type JoinFlags uint8 350 351 // Each flag indicates if a certain type of join is allowed. The JoinFlags are 352 // an OR of these flags, with the special case that the value 0 means anything 353 // is allowed. 354 const ( 355 // AllowHashJoinStoreLeft corresponds to a hash join where the left side is 356 // stored into the hashtable. Note that execution can override the stored side 357 // if it finds that the other side is smaller (up to a certain size). 358 AllowHashJoinStoreLeft JoinFlags = (1 << iota) 359 360 // AllowHashJoinStoreRight corresponds to a hash join where the right side is 361 // stored into the hashtable. Note that execution can override the stored side 362 // if it finds that the other side is smaller (up to a certain size). 363 AllowHashJoinStoreRight 364 365 // AllowMergeJoin corresponds to a merge join. 366 AllowMergeJoin 367 368 // AllowLookupJoinIntoLeft corresponds to a lookup join where the lookup 369 // table is on the left side. 370 AllowLookupJoinIntoLeft 371 372 // AllowLookupJoinIntoRight corresponds to a lookup join where the lookup 373 // table is on the right side. 374 AllowLookupJoinIntoRight 375 ) 376 377 var joinFlagStr = map[JoinFlags]string{ 378 AllowHashJoinStoreLeft: "hash join (store left side)", 379 AllowHashJoinStoreRight: "hash join (store right side)", 380 AllowMergeJoin: "merge join", 381 AllowLookupJoinIntoLeft: "lookup join (into left side)", 382 AllowLookupJoinIntoRight: "lookup join (into right side)", 383 } 384 385 // Empty returns true if this is the default value (where all join types are 386 // allowed). 387 func (jf JoinFlags) Empty() bool { 388 return jf == 0 389 } 390 391 // Has returns true if the given flag is set. 392 func (jf JoinFlags) Has(flag JoinFlags) bool { 393 return jf.Empty() || jf&flag != 0 394 } 395 396 func (jf JoinFlags) String() string { 397 if jf.Empty() { 398 return "no flags" 399 } 400 401 // Special cases for prettier results. 402 switch jf { 403 case AllowHashJoinStoreLeft | AllowHashJoinStoreRight: 404 return "force hash join" 405 case AllowLookupJoinIntoLeft | AllowLookupJoinIntoRight: 406 return "force lookup join" 407 } 408 409 var b strings.Builder 410 b.WriteString("force ") 411 first := true 412 for jf != 0 { 413 flag := JoinFlags(1 << uint8(bits.TrailingZeros8(uint8(jf)))) 414 if !first { 415 b.WriteString(" or ") 416 } 417 first = false 418 b.WriteString(joinFlagStr[flag]) 419 jf ^= flag 420 } 421 return b.String() 422 } 423 424 func (lj *LookupJoinExpr) initUnexportedFields(mem *Memo) { 425 // lookupProps are initialized as necessary by the logical props builder. 426 } 427 428 func (gj *GeoLookupJoinExpr) initUnexportedFields(mem *Memo) { 429 // lookupProps are initialized as necessary by the logical props builder. 430 } 431 432 func (zj *ZigzagJoinExpr) initUnexportedFields(mem *Memo) { 433 // leftProps and rightProps are initialized as necessary by the logical props 434 // builder. 435 } 436 437 // WindowFrame denotes the definition of a window frame for an individual 438 // window function, excluding the OFFSET expressions, if present. 439 type WindowFrame struct { 440 Mode tree.WindowFrameMode 441 StartBoundType tree.WindowFrameBoundType 442 EndBoundType tree.WindowFrameBoundType 443 FrameExclusion tree.WindowFrameExclusion 444 } 445 446 func (f *WindowFrame) String() string { 447 var bld strings.Builder 448 switch f.Mode { 449 case tree.GROUPS: 450 fmt.Fprintf(&bld, "groups") 451 case tree.ROWS: 452 fmt.Fprintf(&bld, "rows") 453 case tree.RANGE: 454 fmt.Fprintf(&bld, "range") 455 } 456 457 frameBoundName := func(b tree.WindowFrameBoundType) string { 458 switch b { 459 case tree.UnboundedFollowing, tree.UnboundedPreceding: 460 return "unbounded" 461 case tree.CurrentRow: 462 return "current-row" 463 case tree.OffsetFollowing, tree.OffsetPreceding: 464 return "offset" 465 } 466 panic(errors.AssertionFailedf("unexpected bound")) 467 } 468 fmt.Fprintf(&bld, " from %s to %s", 469 frameBoundName(f.StartBoundType), 470 frameBoundName(f.EndBoundType), 471 ) 472 switch f.FrameExclusion { 473 case tree.ExcludeCurrentRow: 474 bld.WriteString(" exclude current row") 475 case tree.ExcludeGroup: 476 bld.WriteString(" exclude group") 477 case tree.ExcludeTies: 478 bld.WriteString(" exclude ties") 479 } 480 return bld.String() 481 } 482 483 // IsCanonical returns true if the ScanPrivate indicates an original unaltered 484 // primary index Scan operator (i.e. unconstrained and not limited). 485 func (s *ScanPrivate) IsCanonical() bool { 486 return s.Index == cat.PrimaryIndex && 487 s.Constraint == nil && 488 s.HardLimit == 0 489 } 490 491 // IsLocking returns true if the ScanPrivate is configured to use a row-level 492 // locking mode. This can be the case either because the Scan is in the scope of 493 // a SELECT .. FOR [KEY] UPDATE/SHARE clause or because the Scan was configured 494 // as part of the row retrieval of a DELETE or UPDATE statement. 495 func (s *ScanPrivate) IsLocking() bool { 496 return s.Locking != nil 497 } 498 499 // NeedResults returns true if the mutation operator can return the rows that 500 // were mutated. 501 func (m *MutationPrivate) NeedResults() bool { 502 return m.ReturnCols != nil 503 } 504 505 // IsColumnOutput returns true if the i-th ordinal column should be part of the 506 // mutation's output columns. 507 func (m *MutationPrivate) IsColumnOutput(i int) bool { 508 return i < len(m.ReturnCols) && m.ReturnCols[i] != 0 509 } 510 511 // MapToInputID maps from the ID of a returned column to the ID of the 512 // corresponding input column that provides the value for it. If there is no 513 // matching input column ID, MapToInputID returns 0. 514 // 515 // NOTE: This can only be called if the mutation operator returns rows. 516 func (m *MutationPrivate) MapToInputID(tabColID opt.ColumnID) opt.ColumnID { 517 if m.ReturnCols == nil { 518 panic(errors.AssertionFailedf("MapToInputID cannot be called if ReturnCols is not defined")) 519 } 520 ord := m.Table.ColumnOrdinal(tabColID) 521 return m.ReturnCols[ord] 522 } 523 524 // MapToInputCols maps the given set of table columns to a corresponding set of 525 // input columns using the MapToInputID function. 526 func (m *MutationPrivate) MapToInputCols(tabCols opt.ColSet) opt.ColSet { 527 var inCols opt.ColSet 528 tabCols.ForEach(func(t opt.ColumnID) { 529 id := m.MapToInputID(t) 530 if id == 0 { 531 panic(errors.AssertionFailedf("could not find input column for %d", log.Safe(t))) 532 } 533 inCols.Add(id) 534 }) 535 return inCols 536 } 537 538 // AddEquivTableCols adds an FD to the given set that declares an equivalence 539 // between each table column and its corresponding input column. 540 func (m *MutationPrivate) AddEquivTableCols(md *opt.Metadata, fdset *props.FuncDepSet) { 541 for i, n := 0, md.Table(m.Table).DeletableColumnCount(); i < n; i++ { 542 t := m.Table.ColumnID(i) 543 id := m.MapToInputID(t) 544 if id != 0 { 545 fdset.AddEquivalency(t, id) 546 } 547 } 548 } 549 550 // initUnexportedFields is called when a project expression is created. 551 func (prj *ProjectExpr) initUnexportedFields(mem *Memo) { 552 inputProps := prj.Input.Relational() 553 // Determine the not-null columns. 554 prj.notNullCols = inputProps.NotNullCols.Copy() 555 for i := range prj.Projections { 556 item := &prj.Projections[i] 557 if ExprIsNeverNull(item.Element, inputProps.NotNullCols) { 558 prj.notNullCols.Add(item.Col) 559 } 560 } 561 562 // Determine the "internal" functional dependencies (for the union of input 563 // columns and synthesized columns). 564 prj.internalFuncDeps.CopyFrom(&inputProps.FuncDeps) 565 for i := range prj.Projections { 566 item := &prj.Projections[i] 567 if v, ok := item.Element.(*VariableExpr); ok && inputProps.OutputCols.Contains(v.Col) { 568 // Handle any column that is a direct reference to an input column. The 569 // optimizer sometimes constructs these in order to generate different 570 // column IDs; they can also show up after constant-folding e.g. an ORDER 571 // BY expression. 572 prj.internalFuncDeps.AddEquivalency(v.Col, item.Col) 573 continue 574 } 575 576 if !item.scalar.CanHaveSideEffects { 577 from := item.scalar.OuterCols.Intersection(inputProps.OutputCols) 578 579 // We want to set up the FD: from --> colID. 580 // This does not necessarily hold for "composite" types like decimals or 581 // collated strings. For example if d is a decimal, d::TEXT can have 582 // different values for equal values of d, like 1 and 1.0. 583 // 584 // We only add the FD if composite types are not involved. 585 // 586 // TODO(radu): add a whitelist of expressions/operators that are ok, like 587 // arithmetic. 588 composite := false 589 for i, ok := from.Next(0); ok; i, ok = from.Next(i + 1) { 590 typ := mem.Metadata().ColumnMeta(i).Type 591 if sqlbase.HasCompositeKeyEncoding(typ) { 592 composite = true 593 break 594 } 595 } 596 if !composite { 597 prj.internalFuncDeps.AddSynthesizedCol(from, item.Col) 598 } 599 } 600 } 601 prj.internalFuncDeps.MakeNotNull(prj.notNullCols) 602 } 603 604 // InternalFDs returns the functional dependencies for the set of all input 605 // columns plus the synthesized columns. 606 func (prj *ProjectExpr) InternalFDs() *props.FuncDepSet { 607 return &prj.internalFuncDeps 608 } 609 610 // ExprIsNeverNull makes a best-effort attempt to prove that the provided 611 // scalar is always non-NULL, given the set of outer columns that are known 612 // to be not null. This is particularly useful with check constraints. 613 // Check constraints are satisfied when the condition evaluates to NULL, 614 // whereas filters are not. For example consider the following check constraint: 615 // 616 // CHECK (col IN (1, 2, NULL)) 617 // 618 // Any row evaluating this check constraint with any value for the column will 619 // satisfy this check constraint, as they would evaluate to true (in the case 620 // of 1 or 2) or NULL (in the case of everything else). 621 func ExprIsNeverNull(e opt.ScalarExpr, notNullCols opt.ColSet) bool { 622 switch t := e.(type) { 623 case *VariableExpr: 624 return notNullCols.Contains(t.Col) 625 626 case *TrueExpr, *FalseExpr, *ConstExpr, *IsExpr, *IsNotExpr, *IsTupleNullExpr, *IsTupleNotNullExpr: 627 return true 628 629 case *NullExpr: 630 return false 631 632 case *TupleExpr: 633 // TODO(ridwanmsharif): Make this less conservative and instead update how 634 // IN and NOT IN behave w.r.t tuples and how IndirectionExpr works with arrays. 635 // Currently, the semantics of this function on Tuples are different 636 // as it returns whether a NULL evaluation is possible given the composition of 637 // the tuple. Changing this will require some additional logic in the IN cases. 638 for i := range t.Elems { 639 if !ExprIsNeverNull(t.Elems[i], notNullCols) { 640 return false 641 } 642 } 643 return true 644 645 case *InExpr, *NotInExpr: 646 // TODO(ridwanmsharif): If a tuple is found in either side, determine if the 647 // expression is nullable based on the composition of the tuples. 648 return ExprIsNeverNull(t.Child(0).(opt.ScalarExpr), notNullCols) && 649 ExprIsNeverNull(t.Child(1).(opt.ScalarExpr), notNullCols) 650 651 case *ArrayExpr: 652 for i := range t.Elems { 653 if !ExprIsNeverNull(t.Elems[i], notNullCols) { 654 return false 655 } 656 } 657 return true 658 659 case *CaseExpr: 660 for i := range t.Whens { 661 if !ExprIsNeverNull(t.Whens[i], notNullCols) { 662 return false 663 } 664 } 665 return ExprIsNeverNull(t.Input, notNullCols) && ExprIsNeverNull(t.OrElse, notNullCols) 666 667 case *CastExpr, *NotExpr, *RangeExpr: 668 return ExprIsNeverNull(t.Child(0).(opt.ScalarExpr), notNullCols) 669 670 case *AndExpr, *OrExpr, *GeExpr, *GtExpr, *NeExpr, *EqExpr, *LeExpr, *LtExpr, *LikeExpr, 671 *NotLikeExpr, *ILikeExpr, *NotILikeExpr, *SimilarToExpr, *NotSimilarToExpr, *RegMatchExpr, 672 *NotRegMatchExpr, *RegIMatchExpr, *NotRegIMatchExpr, *ContainsExpr, *JsonExistsExpr, 673 *JsonAllExistsExpr, *JsonSomeExistsExpr, *AnyScalarExpr, *BitandExpr, *BitorExpr, *BitxorExpr, 674 *PlusExpr, *MinusExpr, *MultExpr, *DivExpr, *FloorDivExpr, *ModExpr, *PowExpr, *ConcatExpr, 675 *LShiftExpr, *RShiftExpr, *WhenExpr: 676 return ExprIsNeverNull(t.Child(0).(opt.ScalarExpr), notNullCols) && 677 ExprIsNeverNull(t.Child(1).(opt.ScalarExpr), notNullCols) 678 679 default: 680 return false 681 } 682 } 683 684 // OutputColumnIsAlwaysNull returns true if the expression produces only NULL 685 // values for the given column. Used to elide foreign key checks. 686 // 687 // This could be a logical property but we only care about simple cases (NULLs 688 // in Projections and Values). 689 func OutputColumnIsAlwaysNull(e RelExpr, col opt.ColumnID) bool { 690 isNullScalar := func(scalar opt.ScalarExpr) bool { 691 switch scalar.Op() { 692 case opt.NullOp: 693 return true 694 case opt.CastOp: 695 // Normally this cast should have been folded, but we want this to work 696 // in "build" opttester mode (disabled normalization rules). 697 return scalar.Child(0).Op() == opt.NullOp 698 default: 699 return false 700 } 701 } 702 703 switch e.Op() { 704 case opt.ProjectOp: 705 p := e.(*ProjectExpr) 706 if p.Passthrough.Contains(col) { 707 return OutputColumnIsAlwaysNull(p.Input, col) 708 } 709 for i := range p.Projections { 710 if p.Projections[i].Col == col { 711 return isNullScalar(p.Projections[i].Element) 712 } 713 } 714 715 case opt.ValuesOp: 716 v := e.(*ValuesExpr) 717 colOrdinal, ok := v.Cols.Find(col) 718 if !ok { 719 return false 720 } 721 for i := range v.Rows { 722 if !isNullScalar(v.Rows[i].(*TupleExpr).Elems[colOrdinal]) { 723 return false 724 } 725 } 726 return true 727 } 728 729 return false 730 } 731 732 // FKCascades stores metadata necessary for building cascading queries. 733 type FKCascades []FKCascade 734 735 // FKCascade stores metadata necessary for building a cascading query. 736 // Cascading queries are built as needed, after the original query is executed. 737 type FKCascade struct { 738 // FKName is the name of the FK constraint. 739 FKName string 740 741 // Builder is an object that can be used as the "optbuilder" for the cascading 742 // query. 743 Builder CascadeBuilder 744 745 // WithID identifies the buffer for the mutation input in the original 746 // expression tree. 747 WithID opt.WithID 748 749 // OldValues are column IDs from the mutation input that correspond to the 750 // old values of the modified rows. The list maps 1-to-1 to foreign key 751 // columns. 752 OldValues opt.ColList 753 754 // NewValues are column IDs from the mutation input that correspond to the 755 // new values of the modified rows. The list maps 1-to-1 to foreign key columns. 756 // It is empty if the mutation is a deletion. 757 NewValues opt.ColList 758 } 759 760 // CascadeBuilder is an interface used to construct a cascading query for a 761 // specific FK relation. For example: if we are deleting rows from a parent 762 // table, after deleting the rows from the parent table this interface will be 763 // used to build the corresponding deletion in the child table. 764 type CascadeBuilder interface { 765 // Build constructs a cascading query that mutates the child table. The input 766 // is scanned using WithScan with the given WithID; oldValues and newValues 767 // columns correspond 1-to-1 to foreign key columns. For deletes, newValues is 768 // empty. 769 // 770 // The query does not need to be built in the same memo as the original query; 771 // the only requirement is that the mutation input columns 772 // (oldValues/newValues) are valid in the metadata. 773 // 774 // The method does not mutate any captured state; it is ok to call Build 775 // concurrently (e.g. if the plan it originates from is cached and reused). 776 // 777 // Note: factory is always *norm.Factory; it is an interface{} only to avoid 778 // circular package dependencies. 779 Build( 780 ctx context.Context, 781 semaCtx *tree.SemaContext, 782 evalCtx *tree.EvalContext, 783 catalog cat.Catalog, 784 factory interface{}, 785 binding opt.WithID, 786 bindingProps *props.Relational, 787 oldValues, newValues opt.ColList, 788 ) (RelExpr, error) 789 }