diff --git a/src/EFCore.Relational/Query/SqlNullabilityProcessor.cs b/src/EFCore.Relational/Query/SqlNullabilityProcessor.cs index 3e7d3d72158..5947c50d4b7 100644 --- a/src/EFCore.Relational/Query/SqlNullabilityProcessor.cs +++ b/src/EFCore.Relational/Query/SqlNullabilityProcessor.cs @@ -400,7 +400,100 @@ protected virtual SqlExpression VisitCase(CaseExpression caseExpression, bool al return elseResult ?? _sqlExpressionFactory.Constant(null, caseExpression.Type, caseExpression.TypeMapping); } + if (IsNull(elseResult)) + { + elseResult = null; + } + + // optimize expressions such as expr != null ? expr : null and expr == null ? null : expr + if (testIsCondition && whenClauses is [var clause] && (elseResult is null || IsNull(clause.Result))) + { + HashSet nullPropagatedOperands = []; + + var (test, expr) = elseResult is null + ? (clause.Test, clause.Result) + : (_sqlExpressionFactory.Not(clause.Test), elseResult); + + DetectNullPropagatingNodes(expr, nullPropagatedOperands); + test = DropNotNullChecks(test, nullPropagatedOperands); + + if (IsTrue(test)) + { + return expr; + } + + if (elseResult != null) + { + test = _sqlExpressionFactory.Not(test); + } + + whenClauses = [new(test, clause.Result)]; + } + return _sqlExpressionFactory.Case(operand, whenClauses, elseResult, caseExpression); + + SqlExpression DropNotNullChecks(SqlExpression expression, HashSet nullPropagatedOperands) + => expression switch + { + SqlUnaryExpression { OperatorType: ExpressionType.NotEqual } isNotNull + when nullPropagatedOperands.Contains(isNotNull.Operand) + => _sqlExpressionFactory.Constant(true, expression.Type, expression.TypeMapping), + + SqlBinaryExpression { OperatorType: ExpressionType.AndAlso } binary + => _sqlExpressionFactory.MakeBinary( + ExpressionType.AndAlso, + DropNotNullChecks(binary.Left, nullPropagatedOperands), + DropNotNullChecks(binary.Right, nullPropagatedOperands), + expression.TypeMapping, + expression)!, + + _ => expression, + }; + + // FIXME: unify nullability computations + static void DetectNullPropagatingNodes(SqlExpression expression, HashSet operands) + { + operands.Add(expression); + + switch (expression) + { + case AtTimeZoneExpression atTimeZone: + DetectNullPropagatingNodes(atTimeZone.Operand, operands); + DetectNullPropagatingNodes(atTimeZone.TimeZone, operands); + break; + + case CollateExpression collate: + DetectNullPropagatingNodes(collate.Operand, operands); + break; + + case SqlUnaryExpression { OperatorType: not (ExpressionType.Equal or ExpressionType.NotEqual) } unary: + DetectNullPropagatingNodes(unary.Operand, operands); + break; + + case SqlBinaryExpression { OperatorType: not (ExpressionType.AndAlso or ExpressionType.OrElse) } binary: + DetectNullPropagatingNodes(binary.Left, operands); + DetectNullPropagatingNodes(binary.Right, operands); + break; + + case SqlFunctionExpression { IsNullable: true } func: + if (func.InstancePropagatesNullability is true) + { + DetectNullPropagatingNodes(func.Instance!, operands); + } + + if (!func.IsNiladic) + { + for (var i = 0; i < func.ArgumentsPropagateNullability.Count; i++) + { + if (func.ArgumentsPropagateNullability[i]) + { + DetectNullPropagatingNodes(func.Arguments[i], operands); + } + } + } + break; + } + } } /// diff --git a/test/EFCore.Relational.Specification.Tests/Query/NullSemanticsQueryTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/NullSemanticsQueryTestBase.cs index f03d130e7f1..753fbc74f7d 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/NullSemanticsQueryTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/NullSemanticsQueryTestBase.cs @@ -2344,6 +2344,52 @@ public virtual Task Is_null_on_column_followed_by_OrElse_optimizes_nullability_c ? x.NullableBoolA != x.NullableBoolC : x.NullableBoolC != x.NullableBoolA))); + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Is_not_null_optimizes_unary_op(bool async) + => AssertQuery( + async, + ss => ss.Set().Select( + x => x.NullableIntA != null ? ~x.NullableIntA : null)); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Is_not_null_optimizes_binary_op(bool async) + => AssertQuery( + async, + ss => ss.Set().Select( + x => x.NullableIntA != null && x.NullableIntB != null + ? x.NullableIntA + x.NullableIntB + : null)); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Is_not_null_optimizes_binary_op_with_partial_checks(bool async) + => AssertQuery( + async, + ss => ss.Set().Select( + x => x.NullableStringA != null && x.NullableStringB != null + ? x.NullableStringA + x.NullableStringB + x.NullableStringC + : null)); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Is_not_null_optimizes_binary_op_with_nested_checks(bool async) + => AssertQuery( + async, + ss => ss.Set().Select( + x => x.NullableStringA != null + ? x.NullableStringB != null ? x.NullableStringA + x.NullableStringB : null + : null)); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Is_not_null_optimizes_binary_op_with_mixed_checks(bool async) + => AssertQuery( + async, + ss => ss.Set().Select( + x => x.NullableStringA != null && x.BoolA ? x.NullableStringA + x.NullableStringB : null)); + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Sum_function_is_always_considered_non_nullable(bool async) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/EntitySplittingQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/EntitySplittingQuerySqlServerTest.cs index d0331af77b8..0d487558750 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/EntitySplittingQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/EntitySplittingQuerySqlServerTest.cs @@ -208,7 +208,7 @@ public override async Task Normal_entity_owning_a_split_reference_with_main_frag AssertSql( """ SELECT [e].[Id], CASE - WHEN [e].[OwnedReference_Id] IS NOT NULL AND [e].[OwnedReference_OwnedIntValue1] IS NOT NULL AND [e].[OwnedReference_OwnedIntValue2] IS NOT NULL AND [o0].[OwnedIntValue3] IS NOT NULL AND [o].[OwnedIntValue4] IS NOT NULL THEN [o].[OwnedIntValue4] + WHEN [e].[OwnedReference_Id] IS NOT NULL AND [e].[OwnedReference_OwnedIntValue1] IS NOT NULL AND [e].[OwnedReference_OwnedIntValue2] IS NOT NULL AND [o0].[OwnedIntValue3] IS NOT NULL THEN [o].[OwnedIntValue4] END AS [OwnedIntValue4], CASE WHEN [e].[OwnedReference_Id] IS NOT NULL AND [e].[OwnedReference_OwnedIntValue1] IS NOT NULL AND [e].[OwnedReference_OwnedIntValue2] IS NOT NULL AND [o0].[OwnedIntValue3] IS NOT NULL AND [o].[OwnedIntValue4] IS NOT NULL THEN [o].[OwnedStringValue4] END AS [OwnedStringValue4] diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs index 53b862ff500..836d17f3254 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs @@ -632,10 +632,7 @@ public override async Task Null_propagation_optimization4(bool async) """ SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank] FROM [Gears] AS [g] -WHERE CASE - WHEN [g].[LeaderNickname] IS NULL THEN NULL - ELSE CAST(LEN([g].[LeaderNickname]) AS int) -END = 5 +WHERE CAST(LEN([g].[LeaderNickname]) AS int) = 5 """); } @@ -648,9 +645,7 @@ public override async Task Null_propagation_optimization5(bool async) """ SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank] FROM [Gears] AS [g] -WHERE CASE - WHEN [g].[LeaderNickname] IS NOT NULL THEN CAST(LEN([g].[LeaderNickname]) AS int) -END = 5 +WHERE CAST(LEN([g].[LeaderNickname]) AS int) = 5 """); } @@ -663,9 +658,7 @@ public override async Task Null_propagation_optimization6(bool async) """ SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank] FROM [Gears] AS [g] -WHERE CASE - WHEN [g].[LeaderNickname] IS NOT NULL THEN CAST(LEN([g].[LeaderNickname]) AS int) -END = 5 +WHERE CAST(LEN([g].[LeaderNickname]) AS int) = 5 """); } @@ -676,9 +669,7 @@ public override async Task Select_null_propagation_optimization7(bool async) // issue #16050 AssertSql( """ -SELECT CASE - WHEN [g].[LeaderNickname] IS NOT NULL THEN [g].[LeaderNickname] + [g].[LeaderNickname] -END +SELECT [g].[LeaderNickname] + [g].[LeaderNickname] FROM [Gears] AS [g] """); } @@ -855,9 +846,7 @@ public override async Task Select_null_propagation_works_for_multiple_navigation AssertSql( """ -SELECT CASE - WHEN [c].[Name] IS NOT NULL THEN [c].[Name] -END +SELECT [c].[Name] FROM [Tags] AS [t] LEFT JOIN [Gears] AS [g] ON [t].[GearNickName] = [g].[Nickname] AND [t].[GearSquadId] = [g].[SquadId] LEFT JOIN [Tags] AS [t0] ON ([g].[Nickname] = [t0].[GearNickName] OR ([g].[Nickname] IS NULL AND [t0].[GearNickName] IS NULL)) AND ([g].[SquadId] = [t0].[GearSquadId] OR ([g].[SquadId] IS NULL AND [t0].[GearSquadId] IS NULL)) @@ -3057,9 +3046,7 @@ public override async Task Select_null_conditional_with_inheritance(bool async) AssertSql( """ -SELECT CASE - WHEN [f].[CommanderName] IS NOT NULL THEN [f].[CommanderName] -END +SELECT [f].[CommanderName] FROM [Factions] AS [f] """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindGroupByQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindGroupByQuerySqlServerTest.cs index d34131a8b58..c97aad27944 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindGroupByQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindGroupByQuerySqlServerTest.cs @@ -2499,9 +2499,7 @@ public override async Task GroupBy_group_Where_Select_Distinct_aggregate(bool as AssertSql( """ -SELECT [o].[CustomerID] AS [Key], MAX(CASE - WHEN [o].[OrderDate] IS NOT NULL THEN [o].[OrderDate] -END) AS [Max] +SELECT [o].[CustomerID] AS [Key], MAX([o].[OrderDate]) AS [Max] FROM [Orders] AS [o] GROUP BY [o].[CustomerID] """); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NullSemanticsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NullSemanticsQuerySqlServerTest.cs index a52b3ddd15f..afbd910af59 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NullSemanticsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NullSemanticsQuerySqlServerTest.cs @@ -4417,6 +4417,63 @@ ELSE CAST(0 AS bit) """); } + public override async Task Is_not_null_optimizes_unary_op(bool async) + { + await base.Is_not_null_optimizes_unary_op(async); + + AssertSql( + """ +SELECT ~[e].[NullableIntA] +FROM [Entities1] AS [e] +"""); + } + + public override async Task Is_not_null_optimizes_binary_op(bool async) + { + await base.Is_not_null_optimizes_binary_op(async); + + AssertSql( + """ +SELECT [e].[NullableIntA] + [e].[NullableIntB] +FROM [Entities1] AS [e] +"""); + } + + public override async Task Is_not_null_optimizes_binary_op_with_partial_checks(bool async) + { + await base.Is_not_null_optimizes_binary_op_with_partial_checks(async); + + AssertSql( + """ +SELECT [e].[NullableStringA] + [e].[NullableStringB] + COALESCE([e].[NullableStringC], N'') +FROM [Entities1] AS [e] +"""); + } + + public override async Task Is_not_null_optimizes_binary_op_with_nested_checks(bool async) + { + await base.Is_not_null_optimizes_binary_op_with_nested_checks(async); + + AssertSql( + """ +SELECT [e].[NullableStringA] + [e].[NullableStringB] +FROM [Entities1] AS [e] +"""); + } + + public override async Task Is_not_null_optimizes_binary_op_with_mixed_checks(bool async) + { + await base.Is_not_null_optimizes_binary_op_with_mixed_checks(async); + + AssertSql( + """ +SELECT CASE + WHEN [e].[BoolA] = CAST(1 AS bit) THEN [e].[NullableStringA] + COALESCE([e].[NullableStringB], N'') +END +FROM [Entities1] AS [e] +"""); + } + public override async Task Sum_function_is_always_considered_non_nullable(bool async) { await base.Sum_function_is_always_considered_non_nullable(async); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/SpatialQuerySqlServerGeographyTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/SpatialQuerySqlServerGeographyTest.cs index 58fd84fc1be..cb9f5904c3e 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/SpatialQuerySqlServerGeographyTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/SpatialQuerySqlServerGeographyTest.cs @@ -88,10 +88,7 @@ public override async Task AsBinary_with_null_check(bool async) AssertSql( """ -SELECT [p].[Id], CASE - WHEN [p].[Point] IS NULL THEN NULL - ELSE [p].[Point].STAsBinary() -END AS [Binary] +SELECT [p].[Id], [p].[Point].STAsBinary() AS [Binary] FROM [PointEntity] AS [p] """); } @@ -285,10 +282,7 @@ public override async Task Disjoint_with_null_check(bool async) """ @point='0xE6100000010C000000000000F03F000000000000F03F' (Size = 22) (DbType = Object) -SELECT [p].[Id], CASE - WHEN [p].[Polygon] IS NULL THEN NULL - ELSE [p].[Polygon].STDisjoint(@point) -END AS [Disjoint] +SELECT [p].[Id], [p].[Polygon].STDisjoint(@point) AS [Disjoint] FROM [PolygonEntity] AS [p] """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/SpatialQuerySqlServerGeometryTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/SpatialQuerySqlServerGeometryTest.cs index 81d1045363c..0a8d53ef665 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/SpatialQuerySqlServerGeometryTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/SpatialQuerySqlServerGeometryTest.cs @@ -83,10 +83,7 @@ public override async Task AsBinary_with_null_check(bool async) AssertSql( """ -SELECT [p].[Id], CASE - WHEN [p].[Point] IS NULL THEN NULL - ELSE [p].[Point].STAsBinary() -END AS [Binary] +SELECT [p].[Id], [p].[Point].STAsBinary() AS [Binary] FROM [PointEntity] AS [p] """); } @@ -404,10 +401,7 @@ public override async Task Disjoint_with_null_check(bool async) """ @point='0x00000000010C000000000000F03F000000000000F03F' (Size = 22) (DbType = Object) -SELECT [p].[Id], CASE - WHEN [p].[Polygon] IS NULL THEN NULL - ELSE [p].[Polygon].STDisjoint(@point) -END AS [Disjoint] +SELECT [p].[Id], [p].[Polygon].STDisjoint(@point) AS [Disjoint] FROM [PolygonEntity] AS [p] """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs index 84c3380ba7d..89d9718890d 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs @@ -820,10 +820,7 @@ UNION ALL SELECT [o].[Nickname], [o].[SquadId], [o].[AssignedCityName], [o].[CityOfBirthName], [o].[FullName], [o].[HasSoulPatch], [o].[LeaderNickname], [o].[LeaderSquadId], [o].[Rank], N'Officer' AS [Discriminator] FROM [Officers] AS [o] ) AS [u] -WHERE CASE - WHEN [u].[LeaderNickname] IS NULL THEN NULL - ELSE CAST(LEN([u].[LeaderNickname]) AS int) -END = 5 +WHERE CAST(LEN([u].[LeaderNickname]) AS int) = 5 """); } @@ -842,9 +839,7 @@ UNION ALL SELECT [o].[Nickname], [o].[SquadId], [o].[AssignedCityName], [o].[CityOfBirthName], [o].[FullName], [o].[HasSoulPatch], [o].[LeaderNickname], [o].[LeaderSquadId], [o].[Rank], N'Officer' AS [Discriminator] FROM [Officers] AS [o] ) AS [u] -WHERE CASE - WHEN [u].[LeaderNickname] IS NOT NULL THEN CAST(LEN([u].[LeaderNickname]) AS int) -END = 5 +WHERE CAST(LEN([u].[LeaderNickname]) AS int) = 5 """); } @@ -863,9 +858,7 @@ UNION ALL SELECT [o].[Nickname], [o].[SquadId], [o].[AssignedCityName], [o].[CityOfBirthName], [o].[FullName], [o].[HasSoulPatch], [o].[LeaderNickname], [o].[LeaderSquadId], [o].[Rank], N'Officer' AS [Discriminator] FROM [Officers] AS [o] ) AS [u] -WHERE CASE - WHEN [u].[LeaderNickname] IS NOT NULL THEN CAST(LEN([u].[LeaderNickname]) AS int) -END = 5 +WHERE CAST(LEN([u].[LeaderNickname]) AS int) = 5 """); } @@ -876,9 +869,7 @@ public override async Task Select_null_propagation_optimization7(bool async) // issue #16050 AssertSql( """ -SELECT CASE - WHEN [u].[LeaderNickname] IS NOT NULL THEN [u].[LeaderNickname] + [u].[LeaderNickname] -END +SELECT [u].[LeaderNickname] + [u].[LeaderNickname] FROM ( SELECT [g].[LeaderNickname] FROM [Gears] AS [g] @@ -1157,9 +1148,7 @@ public override async Task Select_null_propagation_works_for_multiple_navigation AssertSql( """ -SELECT CASE - WHEN [c].[Name] IS NOT NULL THEN [c].[Name] -END +SELECT [c].[Name] FROM [Tags] AS [t] LEFT JOIN ( SELECT [g].[Nickname], [g].[SquadId] @@ -4066,9 +4055,7 @@ public override async Task Select_null_conditional_with_inheritance(bool async) AssertSql( """ -SELECT CASE - WHEN [l].[CommanderName] IS NOT NULL THEN [l].[CommanderName] -END +SELECT [l].[CommanderName] FROM [LocustHordes] AS [l] """); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs index 8155ea58a3a..dc75e08e868 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs @@ -788,10 +788,7 @@ WHEN [o].[Nickname] IS NOT NULL THEN N'Officer' END AS [Discriminator] FROM [Gears] AS [g] LEFT JOIN [Officers] AS [o] ON [g].[Nickname] = [o].[Nickname] AND [g].[SquadId] = [o].[SquadId] -WHERE CASE - WHEN [g].[LeaderNickname] IS NULL THEN NULL - ELSE CAST(LEN([g].[LeaderNickname]) AS int) -END = 5 +WHERE CAST(LEN([g].[LeaderNickname]) AS int) = 5 """); } @@ -807,9 +804,7 @@ WHEN [o].[Nickname] IS NOT NULL THEN N'Officer' END AS [Discriminator] FROM [Gears] AS [g] LEFT JOIN [Officers] AS [o] ON [g].[Nickname] = [o].[Nickname] AND [g].[SquadId] = [o].[SquadId] -WHERE CASE - WHEN [g].[LeaderNickname] IS NOT NULL THEN CAST(LEN([g].[LeaderNickname]) AS int) -END = 5 +WHERE CAST(LEN([g].[LeaderNickname]) AS int) = 5 """); } @@ -825,9 +820,7 @@ WHEN [o].[Nickname] IS NOT NULL THEN N'Officer' END AS [Discriminator] FROM [Gears] AS [g] LEFT JOIN [Officers] AS [o] ON [g].[Nickname] = [o].[Nickname] AND [g].[SquadId] = [o].[SquadId] -WHERE CASE - WHEN [g].[LeaderNickname] IS NOT NULL THEN CAST(LEN([g].[LeaderNickname]) AS int) -END = 5 +WHERE CAST(LEN([g].[LeaderNickname]) AS int) = 5 """); } @@ -838,9 +831,7 @@ public override async Task Select_null_propagation_optimization7(bool async) // issue #16050 AssertSql( """ -SELECT CASE - WHEN [g].[LeaderNickname] IS NOT NULL THEN [g].[LeaderNickname] + [g].[LeaderNickname] -END +SELECT [g].[LeaderNickname] + [g].[LeaderNickname] FROM [Gears] AS [g] """); } @@ -1035,9 +1026,7 @@ public override async Task Select_null_propagation_works_for_multiple_navigation AssertSql( """ -SELECT CASE - WHEN [c].[Name] IS NOT NULL THEN [c].[Name] -END +SELECT [c].[Name] FROM [Tags] AS [t] LEFT JOIN ( SELECT [g].[Nickname], [g].[SquadId] @@ -3505,9 +3494,7 @@ public override async Task Select_null_conditional_with_inheritance(bool async) AssertSql( """ -SELECT CASE - WHEN [l].[CommanderName] IS NOT NULL THEN [l].[CommanderName] -END +SELECT [l].[CommanderName] FROM [Factions] AS [f] LEFT JOIN [LocustHordes] AS [l] ON [f].[Id] = [l].[Id] WHERE [l].[Id] IS NOT NULL diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalComplexNavigationsCollectionsSharedTypeQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalComplexNavigationsCollectionsSharedTypeQuerySqlServerTest.cs index a7a4200801c..4c221bbe793 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalComplexNavigationsCollectionsSharedTypeQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalComplexNavigationsCollectionsSharedTypeQuerySqlServerTest.cs @@ -1848,9 +1848,9 @@ public override async Task LeftJoin_with_Any_on_outer_source_and_projecting_coll SELECT CASE WHEN [s].[OneToOne_Required_PK_Date] IS NULL OR [s].[Level1_Required_Id] IS NULL OR [s].[OneToMany_Required_Inverse2Id] IS NULL OR CASE - WHEN [s].[PeriodEnd0] IS NOT NULL AND [s].[PeriodStart0] IS NOT NULL THEN [s].[PeriodEnd0] + WHEN [s].[PeriodStart0] IS NOT NULL THEN [s].[PeriodEnd0] END IS NULL OR CASE - WHEN [s].[PeriodEnd0] IS NOT NULL AND [s].[PeriodStart0] IS NOT NULL THEN [s].[PeriodStart0] + WHEN [s].[PeriodEnd0] IS NOT NULL THEN [s].[PeriodStart0] END IS NULL THEN 0 WHEN [s].[OneToOne_Required_PK_Date] IS NOT NULL AND [s].[Level1_Required_Id] IS NOT NULL AND [s].[OneToMany_Required_Inverse2Id] IS NOT NULL AND [s].[PeriodEnd0] IS NOT NULL AND [s].[PeriodStart0] IS NOT NULL THEN [s].[Id0] END, [l].[Id], [s].[Id], [s].[Id0], [l4].[Id], [l4].[Level2_Optional_Id], [l4].[Level2_Required_Id], [l4].[Level3_Name], [l4].[OneToMany_Optional_Inverse3Id], [l4].[OneToMany_Required_Inverse3Id], [l4].[OneToOne_Optional_PK_Inverse3Id], [l4].[PeriodEnd], [l4].[PeriodStart] @@ -1866,9 +1866,9 @@ WHERE [l1].[OneToOne_Required_PK_Date] IS NOT NULL AND [l1].[Level1_Required_Id] WHEN [l2].[OneToOne_Required_PK_Date] IS NOT NULL AND [l2].[Level1_Required_Id] IS NOT NULL AND [l2].[OneToMany_Required_Inverse2Id] IS NOT NULL THEN [l2].[Id] END WHERE [l2].[OneToOne_Required_PK_Date] IS NOT NULL AND [l2].[Level1_Required_Id] IS NOT NULL AND [l2].[OneToMany_Required_Inverse2Id] IS NOT NULL AND CASE - WHEN [l2].[PeriodEnd] IS NOT NULL AND [l2].[PeriodStart] IS NOT NULL THEN [l2].[PeriodEnd] + WHEN [l2].[PeriodStart] IS NOT NULL THEN [l2].[PeriodEnd] END IS NOT NULL AND CASE - WHEN [l2].[PeriodEnd] IS NOT NULL AND [l2].[PeriodStart] IS NOT NULL THEN [l2].[PeriodStart] + WHEN [l2].[PeriodEnd] IS NOT NULL THEN [l2].[PeriodStart] END IS NOT NULL ) AS [s] ON [l].[Id] = [s].[Level1_Required_Id] LEFT JOIN ( @@ -2251,9 +2251,9 @@ public override async Task Null_check_in_anonymous_type_projection_should_not_be LEFT JOIN ( SELECT CASE WHEN [l2].[Level2_Required_Id] IS NULL OR [l2].[OneToMany_Required_Inverse3Id] IS NULL OR CASE - WHEN [l2].[PeriodEnd] IS NOT NULL AND [l2].[PeriodStart] IS NOT NULL THEN [l2].[PeriodEnd] + WHEN [l2].[PeriodStart] IS NOT NULL THEN [l2].[PeriodEnd] END IS NULL OR CASE - WHEN [l2].[PeriodEnd] IS NOT NULL AND [l2].[PeriodStart] IS NOT NULL THEN [l2].[PeriodStart] + WHEN [l2].[PeriodEnd] IS NOT NULL THEN [l2].[PeriodStart] END IS NULL THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END AS [c], [l2].[Level3_Name], [l0].[Id], [l2].[Id] AS [Id0], [l0].[OneToMany_Optional_Inverse2Id] @@ -2282,9 +2282,9 @@ public override async Task Null_check_in_Dto_projection_should_not_be_removed(bo LEFT JOIN ( SELECT CASE WHEN [l2].[Level2_Required_Id] IS NULL OR [l2].[OneToMany_Required_Inverse3Id] IS NULL OR CASE - WHEN [l2].[PeriodEnd] IS NOT NULL AND [l2].[PeriodStart] IS NOT NULL THEN [l2].[PeriodEnd] + WHEN [l2].[PeriodStart] IS NOT NULL THEN [l2].[PeriodEnd] END IS NULL OR CASE - WHEN [l2].[PeriodEnd] IS NOT NULL AND [l2].[PeriodStart] IS NOT NULL THEN [l2].[PeriodStart] + WHEN [l2].[PeriodEnd] IS NOT NULL THEN [l2].[PeriodStart] END IS NULL THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END AS [c], [l2].[Level3_Name], [l0].[Id], [l2].[Id] AS [Id0], [l0].[OneToMany_Optional_Inverse2Id] diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs index f6e550bed33..77ea48e2fcb 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs @@ -1179,9 +1179,7 @@ public override async Task Null_propagation_optimization6(bool async) """ SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[PeriodEnd], [g].[PeriodStart], [g].[Rank] FROM [Gears] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [g] -WHERE CASE - WHEN [g].[LeaderNickname] IS NOT NULL THEN CAST(LEN([g].[LeaderNickname]) AS int) -END = 5 +WHERE CAST(LEN([g].[LeaderNickname]) AS int) = 5 """); } @@ -3490,9 +3488,7 @@ public override async Task Null_propagation_optimization5(bool async) """ SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[PeriodEnd], [g].[PeriodStart], [g].[Rank] FROM [Gears] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [g] -WHERE CASE - WHEN [g].[LeaderNickname] IS NOT NULL THEN CAST(LEN([g].[LeaderNickname]) AS int) -END = 5 +WHERE CAST(LEN([g].[LeaderNickname]) AS int) = 5 """); } @@ -4304,10 +4300,7 @@ public override async Task Null_propagation_optimization4(bool async) """ SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[PeriodEnd], [g].[PeriodStart], [g].[Rank] FROM [Gears] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [g] -WHERE CASE - WHEN [g].[LeaderNickname] IS NULL THEN NULL - ELSE CAST(LEN([g].[LeaderNickname]) AS int) -END = 5 +WHERE CAST(LEN([g].[LeaderNickname]) AS int) = 5 """); } @@ -4413,9 +4406,7 @@ public override async Task Select_null_propagation_optimization7(bool async) AssertSql( """ -SELECT CASE - WHEN [g].[LeaderNickname] IS NOT NULL THEN [g].[LeaderNickname] + [g].[LeaderNickname] -END +SELECT [g].[LeaderNickname] + [g].[LeaderNickname] FROM [Gears] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [g] """); } @@ -5007,9 +4998,7 @@ public override async Task Select_null_propagation_works_for_multiple_navigation AssertSql( """ -SELECT CASE - WHEN [c].[Name] IS NOT NULL THEN [c].[Name] -END +SELECT [c].[Name] FROM [Tags] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [t] LEFT JOIN [Gears] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [g] ON [t].[GearNickName] = [g].[Nickname] AND [t].[GearSquadId] = [g].[SquadId] LEFT JOIN [Tags] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [t0] ON ([g].[Nickname] = [t0].[GearNickName] OR ([g].[Nickname] IS NULL AND [t0].[GearNickName] IS NULL)) AND ([g].[SquadId] = [t0].[GearSquadId] OR ([g].[SquadId] IS NULL AND [t0].[GearSquadId] IS NULL)) @@ -6922,9 +6911,7 @@ public override async Task Select_null_conditional_with_inheritance(bool async) AssertSql( """ -SELECT CASE - WHEN [f].[CommanderName] IS NOT NULL THEN [f].[CommanderName] -END +SELECT [f].[CommanderName] FROM [Factions] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [f] """); } diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs index a64e1b0fa5b..c413e453599 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs @@ -561,9 +561,7 @@ public override async Task Null_propagation_optimization6(bool async) """ SELECT "g"."Nickname", "g"."SquadId", "g"."AssignedCityName", "g"."CityOfBirthName", "g"."Discriminator", "g"."FullName", "g"."HasSoulPatch", "g"."LeaderNickname", "g"."LeaderSquadId", "g"."Rank" FROM "Gears" AS "g" -WHERE CASE - WHEN "g"."LeaderNickname" IS NOT NULL THEN length("g"."LeaderNickname") -END = 5 +WHERE length("g"."LeaderNickname") = 5 """); } @@ -2556,9 +2554,7 @@ public override async Task Select_null_propagation_works_for_multiple_navigation AssertSql( """ -SELECT CASE - WHEN "c"."Name" IS NOT NULL THEN "c"."Name" -END +SELECT "c"."Name" FROM "Tags" AS "t" LEFT JOIN "Gears" AS "g" ON "t"."GearNickName" = "g"."Nickname" AND "t"."GearSquadId" = "g"."SquadId" LEFT JOIN "Tags" AS "t0" ON ("g"."Nickname" = "t0"."GearNickName" OR ("g"."Nickname" IS NULL AND "t0"."GearNickName" IS NULL)) AND ("g"."SquadId" = "t0"."GearSquadId" OR ("g"."SquadId" IS NULL AND "t0"."GearSquadId" IS NULL)) @@ -4937,9 +4933,7 @@ public override async Task Select_null_propagation_optimization7(bool async) AssertSql( """ -SELECT CASE - WHEN "g"."LeaderNickname" IS NOT NULL THEN "g"."LeaderNickname" || "g"."LeaderNickname" -END +SELECT "g"."LeaderNickname" || "g"."LeaderNickname" FROM "Gears" AS "g" """); } @@ -5525,9 +5519,7 @@ public override async Task Select_null_conditional_with_inheritance(bool async) AssertSql( """ -SELECT CASE - WHEN "f"."CommanderName" IS NOT NULL THEN "f"."CommanderName" -END +SELECT "f"."CommanderName" FROM "Factions" AS "f" """); } @@ -6584,9 +6576,7 @@ public override async Task Null_propagation_optimization5(bool async) """ SELECT "g"."Nickname", "g"."SquadId", "g"."AssignedCityName", "g"."CityOfBirthName", "g"."Discriminator", "g"."FullName", "g"."HasSoulPatch", "g"."LeaderNickname", "g"."LeaderSquadId", "g"."Rank" FROM "Gears" AS "g" -WHERE CASE - WHEN "g"."LeaderNickname" IS NOT NULL THEN length("g"."LeaderNickname") -END = 5 +WHERE length("g"."LeaderNickname") = 5 """); } @@ -7329,10 +7319,7 @@ public override async Task Null_propagation_optimization4(bool async) """ SELECT "g"."Nickname", "g"."SquadId", "g"."AssignedCityName", "g"."CityOfBirthName", "g"."Discriminator", "g"."FullName", "g"."HasSoulPatch", "g"."LeaderNickname", "g"."LeaderSquadId", "g"."Rank" FROM "Gears" AS "g" -WHERE CASE - WHEN "g"."LeaderNickname" IS NULL THEN NULL - ELSE length("g"."LeaderNickname") -END = 5 +WHERE length("g"."LeaderNickname") = 5 """); } diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/SpatialQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/SpatialQuerySqliteTest.cs index ab7aaf62568..a186cbb397e 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/SpatialQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/SpatialQuerySqliteTest.cs @@ -128,10 +128,7 @@ public override async Task AsBinary_with_null_check(bool async) AssertSql( """ -SELECT "p"."Id", CASE - WHEN "p"."Point" IS NULL THEN NULL - ELSE AsBinary("p"."Point") -END AS "Binary" +SELECT "p"."Id", AsBinary("p"."Point") AS "Binary" FROM "PointEntity" AS "p" """); }