@@ -218,21 +218,39 @@ when binaryExpression.Left.Type.UnwrapNullableType().FullName == "NodaTime.Local
218
218
219
219
var translation = base . VisitBinary ( binaryExpression ) ;
220
220
221
- // A somewhat hacky workaround for #2942.
222
- // When an optional owned JSON entity is compared to null, we get WHERE (x -> y) IS NULL.
223
- // The -> operator (returning jsonb) is used rather than ->> (returning text), since an entity type is being extracted, and further
224
- // JSON operations may need to be composed. However, when the value extracted is a JSON null, a non-NULL jsonb value is returned,
225
- // and comparing that to relational NULL returns false.
226
- // Pattern-match this and force the use of ->> by changing the mapping to be a scalar rather than an entity type.
227
- if ( translation is SqlUnaryExpression
221
+ switch ( translation )
222
+ {
223
+ // Optimize (x - c) - (y - c) to x - y.
224
+ // This is particularly useful for DateOnly.DayNumber - DateOnly.DayNumber, which is the way to express DateOnly subtraction
225
+ // (the subtraction operator isn't defined over DateOnly in .NET). The translation of x.DayNumber is x - DATE '0001-01-01',
226
+ // so the below is a useful simplification.
227
+ // TODO: As this is a generic mathematical simplification, we should move it to a generic optimization phase in EF Core.
228
+ case SqlBinaryExpression
229
+ {
230
+ OperatorType : ExpressionType . Subtract ,
231
+ Left : SqlBinaryExpression { OperatorType : ExpressionType . Subtract , Left : var left1 , Right : var right1 } ,
232
+ Right : SqlBinaryExpression { OperatorType : ExpressionType . Subtract , Left : var left2 , Right : var right2 }
233
+ } originalBinary when right1 . Equals ( right2 ) :
234
+ {
235
+ return new SqlBinaryExpression ( ExpressionType . Subtract , left1 , left2 , originalBinary . Type , originalBinary . TypeMapping ) ;
236
+ }
237
+
238
+ // A somewhat hacky workaround for #2942.
239
+ // When an optional owned JSON entity is compared to null, we get WHERE (x -> y) IS NULL.
240
+ // The -> operator (returning jsonb) is used rather than ->> (returning text), since an entity type is being extracted, and
241
+ // further JSON operations may need to be composed. However, when the value extracted is a JSON null, a non-NULL jsonb value is
242
+ // returned, and comparing that to relational NULL returns false.
243
+ // Pattern-match this and force the use of ->> by changing the mapping to be a scalar rather than an entity type.
244
+ case SqlUnaryExpression
228
245
{
229
246
OperatorType : ExpressionType . Equal or ExpressionType . NotEqual ,
230
247
Operand : JsonScalarExpression { TypeMapping : NpgsqlOwnedJsonTypeMapping } operand
231
- } unary )
232
- {
233
- return unary . Update (
234
- new JsonScalarExpression (
235
- operand . Json , operand . Path , operand . Type , _typeMappingSource . FindMapping ( "text" ) , operand . IsNullable ) ) ;
248
+ } unary :
249
+ {
250
+ return unary . Update (
251
+ new JsonScalarExpression (
252
+ operand . Json , operand . Path , operand . Type , _typeMappingSource . FindMapping ( "text" ) , operand . IsNullable ) ) ;
253
+ }
236
254
}
237
255
238
256
return translation ;
0 commit comments