Skip to content

Commit 7f086a8

Browse files
committed
2 parents 6d4e0fd + 2e8ef35 commit 7f086a8

11 files changed

+87
-94
lines changed

src/EFCore.Cosmos/Query/Internal/ISqlExpressionFactory.cs

-9
Original file line numberDiff line numberDiff line change
@@ -181,15 +181,6 @@ SqlBinaryExpression And(
181181
SqlBinaryExpression Or(
182182
[NotNull] SqlExpression left, [NotNull] SqlExpression right, [CanBeNull] CoreTypeMapping typeMapping = null);
183183

184-
/// <summary>
185-
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
186-
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
187-
/// any release. You should only use it directly in your code with extreme caution and knowing that
188-
/// doing so can result in application failures when updating to a new Entity Framework Core release.
189-
/// </summary>
190-
SqlBinaryExpression Coalesce(
191-
[NotNull] SqlExpression left, [NotNull] SqlExpression right, [CanBeNull] CoreTypeMapping typeMapping = null);
192-
193184
/// <summary>
194185
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
195186
/// the same compatibility standards as public APIs. It may be changed or removed without notice in

src/EFCore.Cosmos/Query/Internal/QuerySqlGenerator.cs

+1-4
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,7 @@ public class QuerySqlGenerator : SqlExpressionVisitor
6060
// Unary
6161
{ ExpressionType.UnaryPlus, "+" },
6262
{ ExpressionType.Negate, "-" },
63-
{ ExpressionType.Not, "~" },
64-
65-
// Others
66-
{ ExpressionType.Coalesce, " ?? " }
63+
{ ExpressionType.Not, "~" }
6764
};
6865

6966
/// <summary>

src/EFCore.Cosmos/Query/Internal/SqlBinaryExpression.cs

+1-6
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ public class SqlBinaryExpression : SqlExpression
3737
ExpressionType.Equal,
3838
ExpressionType.NotEqual,
3939
ExpressionType.ExclusiveOr,
40-
ExpressionType.Coalesce,
4140
ExpressionType.RightShift,
4241
ExpressionType.LeftShift
4342
};
@@ -160,12 +159,8 @@ public override void Print(ExpressionPrinter expressionPrinter)
160159
{
161160
expressionPrinter.Append(")");
162161
}
163-
}
164162

165-
private bool RequiresBrackets(SqlExpression expression)
166-
{
167-
return expression is SqlBinaryExpression sqlBinary
168-
&& sqlBinary.OperatorType != ExpressionType.Coalesce;
163+
static bool RequiresBrackets(SqlExpression expression) => expression is SqlBinaryExpression;
169164
}
170165

171166
/// <summary>

src/EFCore.Cosmos/Query/Internal/SqlExpressionFactory.cs

-10
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,6 @@ private SqlExpression ApplyTypeMappingOnSqlBinary(
179179
case ExpressionType.Modulo:
180180
case ExpressionType.LeftShift:
181181
case ExpressionType.RightShift:
182-
case ExpressionType.Coalesce:
183182
case ExpressionType.And:
184183
case ExpressionType.Or:
185184
{
@@ -373,15 +372,6 @@ public virtual SqlBinaryExpression And(SqlExpression left, SqlExpression right,
373372
public virtual SqlBinaryExpression Or(SqlExpression left, SqlExpression right, CoreTypeMapping typeMapping = null)
374373
=> MakeBinary(ExpressionType.Or, left, right, typeMapping);
375374

376-
/// <summary>
377-
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
378-
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
379-
/// any release. You should only use it directly in your code with extreme caution and knowing that
380-
/// doing so can result in application failures when updating to a new Entity Framework Core release.
381-
/// </summary>
382-
public virtual SqlBinaryExpression Coalesce(SqlExpression left, SqlExpression right, CoreTypeMapping typeMapping = null)
383-
=> MakeBinary(ExpressionType.Coalesce, left, right, typeMapping);
384-
385375
private SqlUnaryExpression MakeUnary(
386376
ExpressionType operatorType, SqlExpression operand, Type type, CoreTypeMapping typeMapping = null)
387377
{

src/EFCore.Relational/Query/ISqlExpressionFactory.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ SqlBinaryExpression Or(
7979
[NotNull] SqlExpression left, [NotNull] SqlExpression right, [CanBeNull] RelationalTypeMapping typeMapping = null);
8080

8181
// Other
82-
SqlBinaryExpression Coalesce(
82+
SqlFunctionExpression Coalesce(
8383
[NotNull] SqlExpression left, [NotNull] SqlExpression right, [CanBeNull] RelationalTypeMapping typeMapping = null);
8484

8585
SqlUnaryExpression IsNull([NotNull] SqlExpression operand);

src/EFCore.Relational/Query/Internal/NullSemanticsRewritingExpressionVisitor.cs

+17
Original file line numberDiff line numberDiff line change
@@ -624,6 +624,23 @@ protected override Expression VisitSqlFunction(SqlFunctionExpression sqlFunction
624624
var canOptimize = _canOptimize;
625625
_canOptimize = false;
626626

627+
if (sqlFunctionExpression.IsBuiltIn
628+
&& string.Equals(sqlFunctionExpression.Name, "COALESCE", StringComparison.OrdinalIgnoreCase))
629+
{
630+
_isNullable = false;
631+
var newLeft = (SqlExpression)Visit(sqlFunctionExpression.Arguments[0]);
632+
var leftNullable = _isNullable;
633+
634+
_isNullable = false;
635+
var newRight = (SqlExpression)Visit(sqlFunctionExpression.Arguments[1]);
636+
var rightNullable = _isNullable;
637+
638+
_isNullable = leftNullable && rightNullable;
639+
_canOptimize = canOptimize;
640+
641+
return sqlFunctionExpression.Update(sqlFunctionExpression.Instance, new[] { newLeft, newRight });
642+
}
643+
627644
var newInstance = (SqlExpression)Visit(sqlFunctionExpression.Instance);
628645
var newArguments = new SqlExpression[sqlFunctionExpression.Arguments.Count];
629646
for (var i = 0; i < newArguments.Length; i++)

src/EFCore.Relational/Query/Internal/SqlExpressionOptimizingExpressionVisitor.cs

+20-12
Original file line numberDiff line numberDiff line change
@@ -250,9 +250,6 @@ private SqlExpression SimplifyNullNotNullExpression(
250250
// in general:
251251
// binaryOp(a, b) == null -> a == null || b == null
252252
// binaryOp(a, b) != null -> a != null && b != null
253-
// for coalesce:
254-
// (a ?? b) == null -> a == null && b == null
255-
// (a ?? b) != null -> a != null || b != null
256253
// for AndAlso, OrElse we can't do this optimization
257254
// we could do something like this, but it seems too complicated:
258255
// (a && b) == null -> a == null && b != 0 || a != 0 && b == null
@@ -262,15 +259,7 @@ private SqlExpression SimplifyNullNotNullExpression(
262259
var newLeft = SimplifyNullNotNullExpression(operatorType, sqlBinaryOperand.Left, typeof(bool), typeMapping);
263260
var newRight = SimplifyNullNotNullExpression(operatorType, sqlBinaryOperand.Right, typeof(bool), typeMapping);
264261

265-
return sqlBinaryOperand.OperatorType == ExpressionType.Coalesce
266-
? SimplifyLogicalSqlBinaryExpression(
267-
operatorType == ExpressionType.Equal
268-
? ExpressionType.AndAlso
269-
: ExpressionType.OrElse,
270-
newLeft,
271-
newRight,
272-
typeMapping)
273-
: SimplifyLogicalSqlBinaryExpression(
262+
return SimplifyLogicalSqlBinaryExpression(
274263
operatorType == ExpressionType.Equal
275264
? ExpressionType.OrElse
276265
: ExpressionType.AndAlso,
@@ -279,6 +268,25 @@ private SqlExpression SimplifyNullNotNullExpression(
279268
typeMapping);
280269
}
281270
break;
271+
272+
case SqlFunctionExpression sqlFunctionExpression
273+
when sqlFunctionExpression.IsBuiltIn
274+
&& string.Equals("COALESCE", sqlFunctionExpression.Name, StringComparison.OrdinalIgnoreCase):
275+
// for coalesce:
276+
// (a ?? b) == null -> a == null && b == null
277+
// (a ?? b) != null -> a != null || b != null
278+
var leftArgument = SimplifyNullNotNullExpression(
279+
operatorType, sqlFunctionExpression.Arguments[0], typeof(bool), typeMapping);
280+
var rightArgument = SimplifyNullNotNullExpression(
281+
operatorType, sqlFunctionExpression.Arguments[1], typeof(bool), typeMapping);
282+
283+
return SimplifyLogicalSqlBinaryExpression(
284+
operatorType == ExpressionType.Equal
285+
? ExpressionType.AndAlso
286+
: ExpressionType.OrElse,
287+
leftArgument,
288+
rightArgument,
289+
typeMapping);
282290
}
283291
break;
284292
}

src/EFCore.Relational/Query/QuerySqlGenerator.cs

+21-36
Original file line numberDiff line numberDiff line change
@@ -375,56 +375,41 @@ protected override Expression VisitSqlBinary(SqlBinaryExpression sqlBinaryExpres
375375
{
376376
Check.NotNull(sqlBinaryExpression, nameof(sqlBinaryExpression));
377377

378-
if (sqlBinaryExpression.OperatorType == ExpressionType.Coalesce)
378+
var requiresBrackets = RequiresBrackets(sqlBinaryExpression.Left);
379+
380+
if (requiresBrackets)
379381
{
380-
_relationalCommandBuilder.Append("COALESCE(");
381-
Visit(sqlBinaryExpression.Left);
382-
_relationalCommandBuilder.Append(", ");
383-
Visit(sqlBinaryExpression.Right);
384-
_relationalCommandBuilder.Append(")");
382+
_relationalCommandBuilder.Append("(");
385383
}
386-
else
387-
{
388-
var requiresBrackets = RequiresBrackets(sqlBinaryExpression.Left);
389384

390-
if (requiresBrackets)
391-
{
392-
_relationalCommandBuilder.Append("(");
393-
}
385+
Visit(sqlBinaryExpression.Left);
394386

395-
Visit(sqlBinaryExpression.Left);
396-
397-
if (requiresBrackets)
398-
{
399-
_relationalCommandBuilder.Append(")");
400-
}
387+
if (requiresBrackets)
388+
{
389+
_relationalCommandBuilder.Append(")");
390+
}
401391

402-
_relationalCommandBuilder.Append(GenerateOperator(sqlBinaryExpression));
392+
_relationalCommandBuilder.Append(GenerateOperator(sqlBinaryExpression));
403393

404-
requiresBrackets = RequiresBrackets(sqlBinaryExpression.Right);
394+
requiresBrackets = RequiresBrackets(sqlBinaryExpression.Right);
405395

406-
if (requiresBrackets)
407-
{
408-
_relationalCommandBuilder.Append("(");
409-
}
396+
if (requiresBrackets)
397+
{
398+
_relationalCommandBuilder.Append("(");
399+
}
410400

411-
Visit(sqlBinaryExpression.Right);
401+
Visit(sqlBinaryExpression.Right);
412402

413-
if (requiresBrackets)
414-
{
415-
_relationalCommandBuilder.Append(")");
416-
}
403+
if (requiresBrackets)
404+
{
405+
_relationalCommandBuilder.Append(")");
417406
}
418407

419408
return sqlBinaryExpression;
420409
}
421410

422-
private bool RequiresBrackets(SqlExpression expression)
423-
{
424-
return expression is SqlBinaryExpression sqlBinary
425-
&& sqlBinary.OperatorType != ExpressionType.Coalesce
426-
|| expression is LikeExpression;
427-
}
411+
private static bool RequiresBrackets(SqlExpression expression)
412+
=> expression is SqlBinaryExpression || expression is LikeExpression;
428413

429414
protected override Expression VisitSqlConstant(SqlConstantExpression sqlConstantExpression)
430415
{

src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs

+7-5
Original file line numberDiff line numberDiff line change
@@ -512,11 +512,13 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression)
512512
return TranslationFailed(binaryExpression.Left, Visit(left), out var sqlLeft)
513513
|| TranslationFailed(binaryExpression.Right, Visit(right), out var sqlRight)
514514
? null
515-
: SqlExpressionFactory.MakeBinary(
516-
binaryExpression.NodeType,
517-
sqlLeft,
518-
sqlRight,
519-
null);
515+
: binaryExpression.NodeType == ExpressionType.Coalesce
516+
? SqlExpressionFactory.Coalesce(sqlLeft, sqlRight)
517+
: (Expression)SqlExpressionFactory.MakeBinary(
518+
binaryExpression.NodeType,
519+
sqlLeft,
520+
sqlRight,
521+
null);
520522
}
521523

522524
private SqlConstantExpression GetConstantOrNull(Expression expression)

src/EFCore.Relational/Query/SqlExpressionFactory.cs

+18-4
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,6 @@ private SqlExpression ApplyTypeMappingOnSqlBinary(
180180
case ExpressionType.Multiply:
181181
case ExpressionType.Divide:
182182
case ExpressionType.Modulo:
183-
case ExpressionType.Coalesce:
184183
case ExpressionType.And:
185184
case ExpressionType.Or:
186185
{
@@ -358,12 +357,27 @@ public virtual SqlBinaryExpression Or(SqlExpression left, SqlExpression right, R
358357
return MakeBinary(ExpressionType.Or, left, right, typeMapping);
359358
}
360359

361-
public virtual SqlBinaryExpression Coalesce(SqlExpression left, SqlExpression right, RelationalTypeMapping typeMapping = null)
360+
public virtual SqlFunctionExpression Coalesce(SqlExpression left, SqlExpression right, RelationalTypeMapping typeMapping = null)
362361
{
363362
Check.NotNull(left, nameof(left));
364363
Check.NotNull(right, nameof(right));
365364

366-
return MakeBinary(ExpressionType.Coalesce, left, right, typeMapping);
365+
var resultType = right.Type;
366+
var inferredTypeMapping = typeMapping
367+
?? ExpressionExtensions.InferTypeMapping(left, right)
368+
?? _typeMappingSource.FindMapping(resultType);
369+
370+
var typeMappedArguments = new List<SqlExpression>()
371+
{
372+
ApplyTypeMapping(left, inferredTypeMapping),
373+
ApplyTypeMapping(right, inferredTypeMapping)
374+
};
375+
376+
return SqlFunctionExpression.Create(
377+
"COALESCE",
378+
typeMappedArguments,
379+
resultType,
380+
inferredTypeMapping);
367381
}
368382

369383
public virtual SqlUnaryExpression MakeUnary(
@@ -677,7 +691,7 @@ private void AddConditions(
677691

678692
if (sharingTypes.Count > 0)
679693
{
680-
bool discriminatorAdded = AddDiscriminatorCondition(selectExpression, entityType);
694+
var discriminatorAdded = AddDiscriminatorCondition(selectExpression, entityType);
681695

682696
var linkingFks = entityType.GetRootType().FindForeignKeys(entityType.FindPrimaryKey().Properties)
683697
.Where(

src/EFCore.Relational/Query/SqlExpressions/SqlBinaryExpression.cs

+1-7
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ public class SqlBinaryExpression : SqlExpression
3131
ExpressionType.Equal,
3232
ExpressionType.NotEqual,
3333
//ExpressionType.ExclusiveOr,
34-
ExpressionType.Coalesce
3534
//ExpressionType.ArrayIndex,
3635
//ExpressionType.RightShift,
3736
//ExpressionType.LeftShift,
@@ -116,13 +115,8 @@ public override void Print(ExpressionPrinter expressionPrinter)
116115
{
117116
expressionPrinter.Append(")");
118117
}
119-
}
120118

121-
private bool RequiresBrackets(SqlExpression expression)
122-
{
123-
return expression is SqlBinaryExpression sqlBinary
124-
&& sqlBinary.OperatorType != ExpressionType.Coalesce
125-
|| expression is LikeExpression;
119+
static bool RequiresBrackets(SqlExpression expression) => expression is SqlBinaryExpression || expression is LikeExpression;
126120
}
127121

128122
public override bool Equals(object obj)

0 commit comments

Comments
 (0)