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  }