Use null propagation to optimize away IS NOT NULL checks#34127
Use null propagation to optimize away IS NOT NULL checks#34127ranma42 wants to merge 3 commits intodotnet:mainfrom
IS NOT NULL checks#34127Conversation
IS NOT NULL checks
3769918 to
afcfba2
Compare
afcfba2 to
d9c0736
Compare
089d329 to
fb551f8
Compare
fb551f8 to
bbe125a
Compare
| } | ||
|
|
||
| // optimize expressions such as expr != null ? expr : null and expr == null ? null : expr | ||
| if (testIsCondition && whenClauses is [var clause] && (elseResult == null || IsNull(clause.Result))) |
There was a problem hiding this comment.
I'm personally moving to is null pretty systematically (here and below):
| if (testIsCondition && whenClauses is [var clause] && (elseResult == null || IsNull(clause.Result))) | |
| if (testIsCondition && whenClauses is [var clause] && (elseResult is null || IsNull(clause.Result))) |
(I actually use is for any constant/literal check at this point).
| SqlExpression test, expr; | ||
|
|
||
| if (elseResult == null) | ||
| { | ||
| expr = clause.Result; | ||
| test = clause.Test; | ||
| } | ||
| else | ||
| { | ||
| expr = elseResult; | ||
| test = _sqlExpressionFactory.Not(clause.Test); | ||
| } |
There was a problem hiding this comment.
nit:
| SqlExpression test, expr; | |
| if (elseResult == null) | |
| { | |
| expr = clause.Result; | |
| test = clause.Test; | |
| } | |
| else | |
| { | |
| expr = elseResult; | |
| test = _sqlExpressionFactory.Not(clause.Test); | |
| } | |
| var (test, expr) = elseResult is null | |
| ? (clause.Result, clause.Test) | |
| : (_sqlExpressionFactory.Not(clause.Test), elseResult); |
... and move nullPropagatedOperands below.
There was a problem hiding this comment.
oops, this was swapped (clause.Result, clause.Test) vs (clause.Test, clause.Result)
| { | ||
| operands.Add(expression); | ||
|
|
||
| if (expression is SqlUnaryExpression unary |
There was a problem hiding this comment.
nit: this can all be one nice switch statement with pattern matching ;)
There was a problem hiding this comment.
will it work even if the result is void? I am unable to get its syntax right :\
There was a problem hiding this comment.
uh, statement, not expression... right!
| }; | ||
|
|
||
| // FIXME: unify nullability computations | ||
| static void NullPropagatedOperands(SqlExpression expression, HashSet<SqlExpression> operands) |
There was a problem hiding this comment.
Naming-wise, maybe DetectNullPropagatingNodes?
| _ => expression, | ||
| }; | ||
|
|
||
| // FIXME: unify nullability computations |
There was a problem hiding this comment.
Yeah, this definitely feels like something more general than as a very local utility to case, plus there should be extensibility for provider-specific expression types. We could even compute this information and bubble it up as part of the normal visitation of SqlNullabilityProcessor - assuming it's useful enough for other parts of the visitor.
But of course this can all be done later.
There was a problem hiding this comment.
This is what I was considering doing in #33889 (comment) (instead of simple boolean nullability, propagate the "nullability expression") 😉
I will most likely do that in a later experiment, at the very least to evaluate whether the nullability expressions are simple to create/consume.
| NullPropagatedOperands(binary.Left, operands); | ||
| NullPropagatedOperands(binary.Right, operands); | ||
| } | ||
| else if (expression is SqlFunctionExpression { IsNullable: true } func) |
There was a problem hiding this comment.
AtTimeZone, Collate (which are types of binary expressions)
There was a problem hiding this comment.
this function is mostly based on ProcessNullNotNull (which, just like this function, ignores AtTimeZone, Collate, ...)
There was a problem hiding this comment.
I see... We should probably fix that in both places - if you prefer not to do this in this PR we can open a separate issue.
There was a problem hiding this comment.
I will add the part specific to this PR here; I added the other part in #34263
| operands.Add(expression); | ||
|
|
||
| if (expression is SqlUnaryExpression unary | ||
| && unary.OperatorType is ExpressionType.Not or ExpressionType.Negate or ExpressionType.Convert) |
There was a problem hiding this comment.
Are there other unary operators that propagate? Maybe better to exclude those that you don't want, rather than specifying those that you do?
There was a problem hiding this comment.
the advantage of specifying them in a positive way is that changing the supported OperatorTypes is safe
OTOH it is unlikely to happen and would likely require other changes across the repo, so I can replace this by excluding Equal and NotEqual if that is easier to read
There was a problem hiding this comment.
Just noting that binary just below is negative rather than positive. Up to you - the possible values here in ExpressionType is a closed set, so it should be safe either way.
69ae99a to
65d9a68
Compare
When a CASE expression simply replicates SQL null propagation, simplify it.
12a532b to
0514a23
Compare
|
rebased to fix conflicts |
249ae47 to
6b86657
Compare
When a CASE expression simply replicates SQL null propagation, simplify it.
Contributes to #34126.