github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/opt/norm/rules/scalar.opt (about)

     1  # =============================================================================
     2  # scalar.opt contains scalar normalization rules that aren't handled elsewhere.
     3  # =============================================================================
     4  
     5  # CommuteVar ensures that variable references are on the left side of
     6  # commutative comparison and binary operators. Other patterns don't need to
     7  # handle both combinations.
     8  [CommuteVar, Normalize]
     9  (Eq | Ne | Is | IsNot | Plus | Mult | Bitand | Bitor | Bitxor
    10      $left:^(Variable)
    11      $right:(Variable)
    12  )
    13  =>
    14  ((OpName) $right $left)
    15  
    16  # CommuteConst ensures that "constant expression trees" are on the right side
    17  # of commutative comparison and binary operators. A constant expression tree
    18  # has no unbound variables that refer to outer columns. It therefore always
    19  # evaluates to the same result. Note that this is possible even if the tree
    20  # contains variable expressions, as long as they are bound, such as in
    21  # uncorrelated subqueries:
    22  #
    23  #   SELECT * FROM a WHERE a.x = (SELECT SUM(b.x) FROM b)
    24  #
    25  # The right side of the equality expression is a constant expression tree, even
    26  # though it contains an entire subquery, because it always evalutes to the same
    27  # result. The left side is not a constant expression tree, even though it
    28  # contains just a single variable, because its value can be different for each
    29  # row in the table "a".
    30  #
    31  # The goal of this and related patterns is to push constant expression trees to
    32  # the right side until only a Variable remains on the left (if possible). Other
    33  # patterns can rely on this normal form and only handle one combination.
    34  [CommuteConst, Normalize]
    35  (Eq | Ne | Is | IsNot | Plus | Mult | Bitand | Bitor | Bitxor
    36      $left:(ConstValue)
    37      $right:^(ConstValue)
    38  )
    39  =>
    40  ((OpName) $right $left)
    41  
    42  # EliminateCoalesce discards the Coalesce operator if it has a single operand.
    43  [EliminateCoalesce, Normalize]
    44  (Coalesce [ $item:* ])
    45  =>
    46  $item
    47  
    48  # SimplifyCoalesce discards any leading null operands, and then if the next
    49  # operand is a constant, replaces with that constant. Note that ConstValue
    50  # matches nulls as well as other constants.
    51  [SimplifyCoalesce, Normalize]
    52  (Coalesce $args:[ $arg:* & (IsConstValueOrTuple $arg) ... ])
    53  =>
    54  (SimplifyCoalesce $args)
    55  
    56  # EliminateCast discards the cast operator if its input already has a type
    57  # that's equivalent to the desired static type.
    58  [EliminateCast, Normalize]
    59  (Cast $input:* $targetTyp:* & (HasColType $input $targetTyp))
    60  =>
    61  $input
    62  
    63  # NormalizeInConst ensures that the In operator's tuple operand is sorted with
    64  # duplicates removed (since duplicates do not change the result).
    65  [NormalizeInConst, Normalize]
    66  (In | NotIn
    67      $left:*
    68      $right:(Tuple $elems:*) & (NeedSortedUniqueList $elems)
    69  )
    70  =>
    71  ((OpName) $left (Tuple (ConstructSortedUniqueList $elems)))
    72  
    73  # FoldInNull replaces the In/Not operator with Null when the tuple only
    74  # contains null. The NormalizeInConst pattern will reduce multiple nulls to a
    75  # single null when it removes duplicates, so this pattern will match that.
    76  [FoldInNull, Normalize]
    77  (In | NotIn $left:* (Tuple [ (Null) ]))
    78  =>
    79  (Null (BoolType))
    80  
    81  # UnifyComparisonTypes takes a mixed-type comparison between a non-constant and
    82  # a constant and, if appropriate, converts the constant to the type of the
    83  # non-constant to allow constraints to be generated.
    84  [UnifyComparisonTypes, Normalize]
    85  (Comparison
    86      $left:(Variable)
    87      $right:(Const) &
    88          (Succeeded $result:(UnifyComparison $left $right))
    89  )
    90  =>
    91  ((OpName) $left $result)
    92  
    93  # EliminateExistsZeroRows converts an Exists subquery to False when it's known
    94  # that the input produces zero rows.
    95  [EliminateExistsZeroRows, Normalize]
    96  (Exists $input:* & (HasZeroRows $input))
    97  =>
    98  (False)
    99  
   100  # EliminateExistsProject discards a Project input to the Exists operator. The
   101  # Project operator never changes the row cardinality of its input, and row
   102  # cardinality is the only thing that Exists cares about, so Project is a no-op.
   103  [EliminateExistsProject, Normalize]
   104  (Exists (Project $input:*) $subqueryPrivate:*)
   105  =>
   106  (Exists $input $subqueryPrivate)
   107  
   108  # EliminateExistsGroupBy discards a non-scalar GroupBy input to the Exists
   109  # operator. While non-scalar GroupBy (or DistinctOn) can change row cardinality,
   110  # it always returns a non-empty set if its input is non-empty. Similarly, if its
   111  # input is empty, then it returns the empty set. Therefore, it's a no-op for
   112  # Exists.
   113  #
   114  # NOTE: EnsureDistinctOn has the side effect of error'ing if the input has
   115  # duplicates, so do not eliminate it.
   116  [EliminateExistsGroupBy, Normalize]
   117  (Exists (GroupBy | DistinctOn $input:*) $subqueryPrivate:*)
   118  =>
   119  (Exists $input $subqueryPrivate)
   120  
   121  # IntroduceExistsLimit inserts a LIMIT 1 "under" Exists so as to save resources
   122  # to make the EXISTS determination.
   123  #
   124  # This rule uses and sets a boolean "WasLimited" on the Exists
   125  # node to ensure the rule is only applied once. This is because the
   126  # rule expands to an Exists pattern that's also a valid input pattern
   127  # and it would recurse otherwise.
   128  #
   129  # We avoid this rule if the query is correlated because the decorrelation rules
   130  # get confused by the presence of a limit. (It will be worth re-considering this
   131  # when a general-purpose apply operator is supported - in that case it can be
   132  # definitely worthwhile pushing down a LIMIT 1 to limit the amount of work done
   133  # on every row.)
   134  [IntroduceExistsLimit, Normalize]
   135  (Exists
   136      $input:* & ^(HasOuterCols $input) & ^(HasZeroOrOneRow $input)
   137      $subqueryPrivate:* & ^(IsLimited $subqueryPrivate)
   138  )
   139  =>
   140  (Exists
   141      (Limit $input (IntConst 1) (EmptyOrdering))
   142      (MakeLimited $subqueryPrivate)
   143  )
   144  
   145  # EliminateExistsLimit discards a Limit operator with a positive limit inside an
   146  # Exist operator.
   147  #
   148  # The Limit operator prevents decorrelation rules from being applied. By
   149  # discarding the Limit, which is a no-op inside of Exist operators, the query
   150  # can be decorrelated into a more efficient SemiJoin or AntiJoin.
   151  #
   152  # Note that this rule uses HasOuterCols to ensure that it only matches
   153  # correlated Exists subqueries. There is no need to discard limits from
   154  # non-correlated Exists subqueries. Limits are preferred for non-correlated
   155  # Exists subqueries. See IntroduceExistsLimit above for details.
   156  [EliminateExistsLimit, Normalize]
   157  (Exists
   158      (Limit
   159          $input:* & (HasOuterCols $input)
   160          (Const $limit:*) & (IsPositiveInt $limit)
   161      )
   162      $subqueryPrivate:*
   163  )
   164  =>
   165  (Exists $input $subqueryPrivate)
   166  
   167  # NormalizeJSONFieldAccess transforms field access into a containment with a
   168  # simpler LHS. This allows inverted index constraints to be generated in some
   169  # cases.
   170  # The FetchVal operator also has an overload with integers instead of strings,
   171  # but this transformation is not valid in that case.
   172  # This transforms
   173  #
   174  #   a->'b' = '"c"'
   175  #
   176  # to
   177  #
   178  #   a @> '{"b": "c"}'
   179  #
   180  # Note that we can't make this transformation in cases like
   181  #
   182  #   a->'b' = '["c"]',
   183  #
   184  # because containment is not equivalent to equality for non-scalar types.
   185  [NormalizeJSONFieldAccess, Normalize]
   186  (Eq
   187      (FetchVal $val:* $key:(Const) & (IsString $key))
   188      $right:(Const) & (IsJSONScalar $right)
   189  )
   190  =>
   191  (Contains $val (MakeSingleKeyJSONObject $key $right))
   192  
   193  # NormalizeJSONContains contains transforms a field access containment into one
   194  # with a simpler LHS.  This transformation is only valid if the RHS is not a
   195  # scalar, since a JSON array "contains" a scalar which is inside of it.
   196  # This transforms
   197  #
   198  #   a->'b' @> '{"x": "c"}'
   199  #
   200  # to
   201  #
   202  #   a @> '{"b": {"x": "c"}}'
   203  #
   204  [NormalizeJSONContains, Normalize]
   205  (Contains
   206      (FetchVal $val:* $key:(Const) & (IsString $key))
   207      $right:(Const) & ^(IsJSONScalar $right)
   208  )
   209  =>
   210  (Contains $val (MakeSingleKeyJSONObject $key $right))
   211  
   212  # SimplifyCaseWhenConstValue removes branches known to not match. Any
   213  # branch known to match is used as the ELSE and further WHEN conditions
   214  # are skipped. If all WHEN conditions have been removed, the ELSE
   215  # expression is used.
   216  # This transforms
   217  #
   218  #   CASE WHEN v THEN 1 WHEN false THEN a WHEN true THEN b ELSE c END
   219  #
   220  # to
   221  #
   222  #   CASE WHEN v THEN 1 ELSE b END
   223  #
   224  [SimplifyCaseWhenConstValue, Normalize]
   225  (Case
   226      $condition:(ConstValue)
   227      $whens:[ ... (When (ConstValue)) ... ]
   228      $orElse:*
   229  )
   230  =>
   231  (SimplifyWhens $condition $whens $orElse)
   232  
   233  # InlineAnyValuesSingleCol converts Any with Values input to AnyScalar.
   234  # This version handles the case where there is a single column.
   235  [InlineAnyValuesSingleCol, Normalize]
   236  (Any $values:(Values) $scalar:* $private:*)
   237  =>
   238  (AnyScalar $scalar (InlineValues $values) (SubqueryCmp $private))
   239  
   240  # InlineAnyValuesMultiCol converts Any with Values input to AnyScalar.
   241  # This version handles the case where there are multiple columns; in this case,
   242  # the Values is wrapped into a Project that converts each row to a tuple.
   243  [InlineAnyValuesMultiCol, Normalize]
   244  (Any
   245      (Project
   246          $values:(Values * $valuesPrivate:*)
   247          [ (ProjectionsItem $tuple:(Tuple)) ] &
   248              (IsTupleOfVars $tuple (ValuesCols $valuesPrivate))
   249          $passthrough:* & (ColsAreEmpty $passthrough)
   250      )
   251      $scalar:*
   252      $private:*
   253  )
   254  =>
   255  (AnyScalar $scalar (InlineValues $values) (SubqueryCmp $private))
   256  
   257  # SimplifyEqualsAnyTuple converts a scalar ANY operation to an IN comparison.
   258  # It transforms
   259  #
   260  #   x = ANY (...)
   261  #
   262  # to
   263  #
   264  #   x IN (...)
   265  #
   266  # Which allows scans to be constrained.
   267  [SimplifyEqualsAnyTuple, Normalize]
   268  (AnyScalar $input:* $tuple:(Tuple) $cmp:* & (OpsAreSame $cmp Eq))
   269  =>
   270  (In $input $tuple)
   271  
   272  # SimplifyAnyScalarArray converts a scalar ANY operation on a constant ARRAY to a scalar
   273  # ANY operation on a tuple. In particular, this allows SimplifyEqualsAnyTuple to be
   274  # triggered, which allows constraints to be generated.
   275  [SimplifyAnyScalarArray, Normalize]
   276  (AnyScalar $input:* $ary:(Const) & (IsConstArray $ary) $cmp:*)
   277  =>
   278  (AnyScalar $input (ConvertConstArrayToTuple $ary) $cmp)
   279  
   280  # FoldCollate converts a Collate expr over an uncollated string into a collated
   281  # string constant.
   282  [FoldCollate, Normalize]
   283  (Collate $input:(Const) $locale:*)
   284  =>
   285  (CastToCollatedString $input $locale)
   286  
   287  # ArrayFlattenToAgg converts a correlated ArrayFlatten to an aggregation.
   288  # This rule exists because:
   289  #
   290  #     1. We cannot do the aggregation method if we don't have a scalar type
   291  #        (for instance, if we have a tuple type).
   292  #     2. We cannot decorrelate an ArrayFlatten directly (but we can decorrelate
   293  #        an aggregation). So it's desirable to perform this conversion in the
   294  #        interest of decorrelation.
   295  #
   296  # So the outcome is that we can perform uncorrelated ARRAY(...)s over any datatype,
   297  # and correlated ones only over the types that array_agg supports.
   298  #
   299  # Note that optbuilder should have already verified that if the input is
   300  # correlated, then we can array_agg over the input type.  Also note that the
   301  # Max1Row operator we introduce is guaranteed to be eliminated as
   302  # MakeArrayAggForFlatten will return a ScalarGroupBy.
   303  [NormalizeArrayFlattenToAgg, Normalize]
   304  (ArrayFlatten $input:(HasOuterCols $input) $subquery:*)
   305  =>
   306  (Coalesce
   307      [
   308          (Subquery
   309              (ScalarGroupBy
   310                  $input
   311                  [
   312                      (AggregationsItem
   313                          (ArrayAgg
   314                              (Variable
   315                                  $requestedCol:(SubqueryRequestedCol
   316                                      $subquery
   317                                  )
   318                              )
   319                          )
   320                          (MakeArrayAggCol
   321                              (ArrayType $requestedCol)
   322                          )
   323                      )
   324                  ]
   325                  (MakeGrouping
   326                      (MakeEmptyColSet)
   327                      (SubqueryOrdering $subquery)
   328                  )
   329              )
   330              (MakeUnorderedSubquery)
   331          )
   332          (Array [] (ArrayType $requestedCol))
   333      ]
   334  )
   335  
   336  # SimplifySameVarEqualities converts `x = x` and other equality
   337  # comparisons into `x IS NOT NULL OR NULL`. The `OR NULL` is necessary
   338  # when x is NULL.
   339  [SimplifySameVarEqualities, Normalize]
   340  (Eq | Le | Ge
   341      $left:(Variable)
   342      $right:(Variable) & (VarsAreSame $left $right)
   343  )
   344  =>
   345  (Or (IsNot $left (Null (TypeOf $left))) (Null (BoolType)))
   346  
   347  # SimplifySameVarInequalities converts `x != x` and other inequality
   348  # comparisons into `x IS NULL AND NULL`. The `AND NULL` is necessary
   349  # when x is NULL.
   350  [SimplifySameVarInequalities, Normalize]
   351  (Ne | Lt | Gt
   352      $left:(Variable)
   353      $right:(Variable) & (VarsAreSame $left $right)
   354  )
   355  =>
   356  (And (Is $left (Null (TypeOf $left))) (Null (BoolType)))