github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/norm/bool_funcs.go (about) 1 // Copyright 2020 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 norm 12 13 import ( 14 "github.com/cockroachdb/cockroach/pkg/sql/opt" 15 "github.com/cockroachdb/cockroach/pkg/sql/opt/memo" 16 ) 17 18 // ConcatLeftDeepAnds concatenates any left-deep And expressions in the right 19 // expression with any left-deep And expressions in the left expression. The 20 // result is a combined left-deep And expression. Note that NormalizeNestedAnds 21 // has already guaranteed that both inputs will already be left-deep. 22 func (c *CustomFuncs) ConcatLeftDeepAnds(left, right opt.ScalarExpr) opt.ScalarExpr { 23 if and, ok := right.(*memo.AndExpr); ok { 24 return c.f.ConstructAnd(c.ConcatLeftDeepAnds(left, and.Left), and.Right) 25 } 26 return c.f.ConstructAnd(left, right) 27 } 28 29 // NegateComparison negates a comparison op like: 30 // a.x = 5 31 // to: 32 // a.x <> 5 33 func (c *CustomFuncs) NegateComparison( 34 cmp opt.Operator, left, right opt.ScalarExpr, 35 ) opt.ScalarExpr { 36 negate := opt.NegateOpMap[cmp] 37 return c.f.DynamicConstruct(negate, left, right).(opt.ScalarExpr) 38 } 39 40 // FindRedundantConjunct takes the left and right operands of an Or operator as 41 // input. It examines each conjunct from the left expression and determines 42 // whether it appears as a conjunct in the right expression. If so, it returns 43 // the matching conjunct. Otherwise, it returns nil. For example: 44 // 45 // A OR A => A 46 // B OR A => nil 47 // A OR (A AND B) => A 48 // (A AND B) OR (A AND C) => A 49 // (A AND B AND C) OR (A AND (D OR E)) => A 50 // 51 // Once a redundant conjunct has been found, it is extracted via a call to the 52 // ExtractRedundantConjunct function. Redundant conjuncts are extracted from 53 // multiple nested Or operators by repeated application of these functions. 54 func (c *CustomFuncs) FindRedundantConjunct(left, right opt.ScalarExpr) opt.ScalarExpr { 55 // Recurse over each conjunct from the left expression and determine whether 56 // it's redundant. 57 for { 58 // Assume a left-deep And expression tree normalized by NormalizeNestedAnds. 59 if and, ok := left.(*memo.AndExpr); ok { 60 if c.isConjunct(and.Right, right) { 61 return and.Right 62 } 63 left = and.Left 64 } else { 65 if c.isConjunct(left, right) { 66 return left 67 } 68 return nil 69 } 70 } 71 } 72 73 // isConjunct returns true if the candidate expression is a conjunct within the 74 // given conjunction. The conjunction is assumed to be left-deep (normalized by 75 // the NormalizeNestedAnds rule). 76 func (c *CustomFuncs) isConjunct(candidate, conjunction opt.ScalarExpr) bool { 77 for { 78 if and, ok := conjunction.(*memo.AndExpr); ok { 79 if and.Right == candidate { 80 return true 81 } 82 conjunction = and.Left 83 } else { 84 return conjunction == candidate 85 } 86 } 87 } 88 89 // ExtractRedundantConjunct extracts a redundant conjunct from an Or expression, 90 // and returns an And of the conjunct with the remaining Or expression (a 91 // logically equivalent expression). For example: 92 // 93 // (A AND B) OR (A AND C) => A AND (B OR C) 94 // 95 // If extracting the conjunct from one of the OR conditions would result in an 96 // empty condition, the conjunct itself is returned (a logically equivalent 97 // expression). For example: 98 // 99 // A OR (A AND B) => A 100 // 101 // These transformations are useful for finding a conjunct that can be pushed 102 // down in the query tree. For example, if the redundant conjunct A is fully 103 // bound by one side of a join, it can be pushed through the join, even if B and 104 // C cannot. 105 func (c *CustomFuncs) ExtractRedundantConjunct( 106 conjunct, left, right opt.ScalarExpr, 107 ) opt.ScalarExpr { 108 if conjunct == left || conjunct == right { 109 return conjunct 110 } 111 112 return c.f.ConstructAnd( 113 conjunct, 114 c.f.ConstructOr( 115 c.extractConjunct(conjunct, left.(*memo.AndExpr)), 116 c.extractConjunct(conjunct, right.(*memo.AndExpr)), 117 ), 118 ) 119 } 120 121 // extractConjunct traverses the And subtree looking for the given conjunct, 122 // which must be present. Once it's located, it's removed from the tree, and 123 // the remaining expression is returned. 124 func (c *CustomFuncs) extractConjunct(conjunct opt.ScalarExpr, and *memo.AndExpr) opt.ScalarExpr { 125 if and.Right == conjunct { 126 return and.Left 127 } 128 if and.Left == conjunct { 129 return and.Right 130 } 131 return c.f.ConstructAnd(c.extractConjunct(conjunct, and.Left.(*memo.AndExpr)), and.Right) 132 }