-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix to #16078 - Query/Null semantics: when checking if expression is …
…null, just check it's constituents rather than entire expression Problem was that during null semantics rewrite we create IS NULL calls on the operands of the comparison. If the operands themselves are complicated, we were simply comparing the entire complex expression to null. In some cases, we only need to look at constituents, e.g. a + b == null <=> a == null || b == null. Also added other minor optimizations around null semantics: - non_nullable_column IS NULL resolves to false, - try to simplify expression after applying de Morgan transformations Also fixed a bug exposed by these changes, where column nullability would be incorrect for scenarios with owned types.
- Loading branch information
Showing
15 changed files
with
442 additions
and
97 deletions.
There are no files selected for viewing
74 changes: 74 additions & 0 deletions
74
src/EFCore.Relational/Query/Internal/IsNullOptimizingExpressionVisitor.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System.Linq.Expressions; | ||
using Microsoft.EntityFrameworkCore.Query.SqlExpressions; | ||
|
||
namespace Microsoft.EntityFrameworkCore.Query.Internal | ||
{ | ||
public class IsNullOptimizingExpressionVisitor : ExpressionVisitor | ||
{ | ||
private readonly ISqlExpressionFactory _sqlExpressionFactory; | ||
|
||
public IsNullOptimizingExpressionVisitor(ISqlExpressionFactory sqlExpressionFactory) | ||
{ | ||
_sqlExpressionFactory = sqlExpressionFactory; | ||
} | ||
|
||
protected override Expression VisitExtension(Expression extensionExpression) | ||
{ | ||
if (extensionExpression is SqlUnaryExpression sqlUnaryExpression) | ||
{ | ||
if (sqlUnaryExpression.OperatorType == ExpressionType.Equal) | ||
{ | ||
switch (sqlUnaryExpression.Operand) | ||
{ | ||
case SqlUnaryExpression sqlUnaryOperand | ||
when sqlUnaryOperand.OperatorType == ExpressionType.Convert | ||
|| sqlUnaryOperand.OperatorType == ExpressionType.Not | ||
|| sqlUnaryOperand.OperatorType == ExpressionType.Negate: | ||
return (SqlExpression)Visit(_sqlExpressionFactory.IsNull(sqlUnaryOperand.Operand)); | ||
|
||
case SqlBinaryExpression sqlBinaryOperand: | ||
var newLeft = (SqlExpression)Visit(_sqlExpressionFactory.IsNull(sqlBinaryOperand.Left)); | ||
var newRight = (SqlExpression)Visit(_sqlExpressionFactory.IsNull(sqlBinaryOperand.Right)); | ||
|
||
return sqlBinaryOperand.OperatorType == ExpressionType.Coalesce | ||
? _sqlExpressionFactory.AndAlso(newLeft, newRight) | ||
: _sqlExpressionFactory.OrElse(newLeft, newRight); | ||
|
||
case ColumnExpression columnOperand | ||
when !columnOperand.IsNullable: | ||
return _sqlExpressionFactory.Constant(false, sqlUnaryExpression.TypeMapping); | ||
} | ||
} | ||
|
||
if (sqlUnaryExpression.OperatorType == ExpressionType.Equal) | ||
{ | ||
switch (sqlUnaryExpression.Operand) | ||
{ | ||
case SqlUnaryExpression sqlUnaryOperand | ||
when sqlUnaryOperand.OperatorType == ExpressionType.Convert | ||
|| sqlUnaryOperand.OperatorType == ExpressionType.Not | ||
|| sqlUnaryOperand.OperatorType == ExpressionType.Negate: | ||
return (SqlExpression)Visit(_sqlExpressionFactory.IsNotNull(sqlUnaryOperand.Operand)); | ||
|
||
case SqlBinaryExpression sqlBinaryOperand: | ||
var newLeft = (SqlExpression)Visit(_sqlExpressionFactory.IsNotNull(sqlBinaryOperand.Left)); | ||
var newRight = (SqlExpression)Visit(_sqlExpressionFactory.IsNotNull(sqlBinaryOperand.Right)); | ||
|
||
return sqlBinaryOperand.OperatorType == ExpressionType.Coalesce | ||
? _sqlExpressionFactory.OrElse(newLeft, newRight) | ||
: _sqlExpressionFactory.AndAlso(newLeft, newRight); | ||
|
||
case ColumnExpression columnOperand | ||
when !columnOperand.IsNullable: | ||
return _sqlExpressionFactory.Constant(true, sqlUnaryExpression.TypeMapping); | ||
} | ||
} | ||
} | ||
|
||
return base.VisitExtension(extensionExpression); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.