Skip to content

Commit 2be205d

Browse files
committed
Simplify AND and OR
When constructing `AndAlso` and `OrElse` expressions, perform some basic local simplifications.
1 parent 051c33a commit 2be205d

File tree

2 files changed

+90
-4
lines changed

2 files changed

+90
-4
lines changed

src/EFCore.Relational/Query/ISqlExpressionFactory.cs

+3-1
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,14 @@ public interface ISqlExpressionFactory
5757
/// <param name="left">The left operand of binary operation.</param>
5858
/// <param name="right">The right operand of binary operation.</param>
5959
/// <param name="typeMapping">A type mapping to be assigned to the created expression.</param>
60+
/// <param name="existingExpr">An optional expression that can be re-used if it matches the new expression.</param>
6061
/// <returns>A <see cref="SqlExpression" /> with the given arguments.</returns>
6162
SqlExpression? MakeBinary(
6263
ExpressionType operatorType,
6364
SqlExpression left,
6465
SqlExpression right,
65-
RelationalTypeMapping? typeMapping);
66+
RelationalTypeMapping? typeMapping,
67+
SqlExpression? existingExpr = null);
6668

6769
// Comparison
6870
/// <summary>

src/EFCore.Relational/Query/SqlExpressionFactory.cs

+87-3
Original file line numberDiff line numberDiff line change
@@ -395,8 +395,25 @@ private SqlExpression ApplyTypeMappingOnJsonScalar(
395395
ExpressionType operatorType,
396396
SqlExpression left,
397397
SqlExpression right,
398-
RelationalTypeMapping? typeMapping)
398+
RelationalTypeMapping? typeMapping,
399+
SqlExpression? existingExpr = null)
399400
{
401+
if (existingExpr is SqlBinaryExpression binaryExpr
402+
&& binaryExpr.OperatorType == operatorType
403+
&& left == binaryExpr.Left
404+
&& right == binaryExpr.Right)
405+
{
406+
return existingExpr;
407+
}
408+
409+
switch (operatorType)
410+
{
411+
case ExpressionType.AndAlso:
412+
return AndAlso(left, right);
413+
case ExpressionType.OrElse:
414+
return OrElse(left, right);
415+
}
416+
400417
if (!SqlBinaryExpression.IsValidOperator(operatorType))
401418
{
402419
return null;
@@ -447,11 +464,78 @@ public virtual SqlExpression LessThanOrEqual(SqlExpression left, SqlExpression r
447464

448465
/// <inheritdoc />
449466
public virtual SqlExpression AndAlso(SqlExpression left, SqlExpression right)
450-
=> MakeBinary(ExpressionType.AndAlso, left, right, null)!;
467+
{
468+
SqlExpression result;
469+
470+
// false && x -> false
471+
// x && true -> x
472+
// x && x -> x
473+
if (left is SqlConstantExpression { Value: false }
474+
|| right is SqlConstantExpression { Value: true }
475+
|| left.Equals(right))
476+
{
477+
result = left;
478+
}
479+
// true && x -> x
480+
// x && false -> false
481+
else if (left is SqlConstantExpression { Value: true } || right is SqlConstantExpression { Value: false })
482+
{
483+
result = right;
484+
}
485+
// x is null && x is not null -> false
486+
// x is not null && x is null -> false
487+
else if (left is SqlUnaryExpression { OperatorType: ExpressionType.Equal or ExpressionType.NotEqual } leftUnary
488+
&& right is SqlUnaryExpression { OperatorType: ExpressionType.Equal or ExpressionType.NotEqual } rightUnary
489+
&& leftUnary.Operand.Equals(rightUnary.Operand))
490+
{
491+
// the case in which left and right are the same expression is handled above
492+
result = Constant(false);
493+
}
494+
else
495+
{
496+
result = new SqlBinaryExpression(ExpressionType.AndAlso, left, right, typeof(bool), null);
497+
}
498+
499+
return ApplyTypeMapping(result, _boolTypeMapping);
500+
}
451501

452502
/// <inheritdoc />
453503
public virtual SqlExpression OrElse(SqlExpression left, SqlExpression right)
454-
=> MakeBinary(ExpressionType.OrElse, left, right, null)!;
504+
{
505+
SqlExpression result;
506+
507+
// true || x -> true
508+
// x || false -> x
509+
// x || x -> x
510+
if (left is SqlConstantExpression { Value: true }
511+
|| right is SqlConstantExpression { Value: false }
512+
|| left.Equals(right))
513+
{
514+
result = left;
515+
}
516+
// false || x -> x
517+
// x || true -> true
518+
else if (left is SqlConstantExpression { Value: false }
519+
|| right is SqlConstantExpression { Value: true })
520+
{
521+
result = right;
522+
}
523+
// x is null || x is not null -> true
524+
// x is not null || x is null -> true
525+
else if (left is SqlUnaryExpression { OperatorType: ExpressionType.Equal or ExpressionType.NotEqual } leftUnary
526+
&& right is SqlUnaryExpression { OperatorType: ExpressionType.Equal or ExpressionType.NotEqual } rightUnary
527+
&& leftUnary.Operand.Equals(rightUnary.Operand))
528+
{
529+
// the case in which left and right are the same expression is handled above
530+
result = Constant(true);
531+
}
532+
else
533+
{
534+
result = new SqlBinaryExpression(ExpressionType.OrElse, left, right, typeof(bool), null);
535+
}
536+
537+
return ApplyTypeMapping(result, _boolTypeMapping);
538+
}
455539

456540
/// <inheritdoc />
457541
public virtual SqlExpression Add(SqlExpression left, SqlExpression right, RelationalTypeMapping? typeMapping = null)

0 commit comments

Comments
 (0)