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)))