diff --git a/src/EFCore.Relational/Query/QuerySqlGenerator.cs b/src/EFCore.Relational/Query/QuerySqlGenerator.cs index 44c54be4cee..dbbf169fe3a 100644 --- a/src/EFCore.Relational/Query/QuerySqlGenerator.cs +++ b/src/EFCore.Relational/Query/QuerySqlGenerator.cs @@ -556,7 +556,12 @@ protected override Expression VisitSqlBinary(SqlBinaryExpression sqlBinaryExpres } private static bool RequiresBrackets(SqlExpression expression) - => expression is SqlBinaryExpression || expression is LikeExpression; + => expression is SqlBinaryExpression + || expression is LikeExpression + || (expression is SqlUnaryExpression unary + && unary.Operand.Type == typeof(bool) + && (unary.OperatorType == ExpressionType.Equal + || unary.OperatorType == ExpressionType.NotEqual)); /// protected override Expression VisitSqlConstant(SqlConstantExpression sqlConstantExpression) diff --git a/test/EFCore.Relational.Specification.Tests/Query/NullSemanticsQueryTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/NullSemanticsQueryTestBase.cs index 36ec11bb0c4..0d6e703259c 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/NullSemanticsQueryTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/NullSemanticsQueryTestBase.cs @@ -1546,6 +1546,101 @@ public virtual Task Multiple_non_equality_comparisons_with_null_in_the_middle(bo ss => ss.Set().Where(e => e.NullableIntA != 1 && e.NullableIntA != null && e.NullableIntA != 2)); } + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual async Task Bool_equal_nullable_bool_HasValue(bool async) + { + await AssertQuery( + async, + ss => ss.Set().Where(e => true == e.NullableBoolA.HasValue)); + + var prm = false; + await AssertQuery( + async, + ss => ss.Set().Where(e => prm == e.NullableBoolA.HasValue)); + + await AssertQuery( + async, + ss => ss.Set().Where(e => e.BoolB == e.NullableBoolA.HasValue)); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual async Task Bool_equal_nullable_bool_compared_to_null(bool async) + { + await AssertQuery( + async, + ss => ss.Set().Where(e => true == (e.NullableBoolA == null))); + + var prm = false; + await AssertQuery( + async, + ss => ss.Set().Where(e => prm == (e.NullableBoolA != null))); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual async Task Bool_not_equal_nullable_bool_HasValue(bool async) + { + await AssertQuery( + async, + ss => ss.Set().Where(e => true != e.NullableBoolA.HasValue)); + + var prm = false; + await AssertQuery( + async, + ss => ss.Set().Where(e => prm != e.NullableBoolA.HasValue)); + + await AssertQuery( + async, + ss => ss.Set().Where(e => e.BoolB != e.NullableBoolA.HasValue)); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual async Task Bool_not_equal_nullable_bool_compared_to_null(bool async) + { + await AssertQuery( + async, + ss => ss.Set().Where(e => true != (e.NullableBoolA == null))); + + var prm = false; + await AssertQuery( + async, + ss => ss.Set().Where(e => prm != (e.NullableBoolA != null))); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual async Task Bool_logical_operation_with_nullable_bool_HasValue(bool async) + { + await AssertQuery( + async, + ss => ss.Set().Where(e => true || e.NullableBoolA.HasValue)); + + var prm = false; + await AssertQuery( + async, + ss => ss.Set().Where(e => prm && e.NullableBoolA.HasValue)); + + await AssertQuery( + async, + ss => ss.Set().Where(e => e.BoolB | e.NullableBoolA.HasValue)); + } + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual async Task Comparison_compared_to_null_check_on_bool(bool async) + { + await AssertQuery( + async, + ss => ss.Set().Where(e => (e.IntA == e.IntB) != e.NullableBoolA.HasValue)); + + await AssertQuery( + async, + ss => ss.Set().Where(e => (e.IntA != e.IntB) == (e.NullableBoolA != null))); + } + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual async Task Multiple_non_equality_comparisons_including_null_comparison_work_for_relational_null_semantics(bool async) diff --git a/test/EFCore.Specification.Tests/TestUtilities/ExpectedQueryRewritingVisitor.cs b/test/EFCore.Specification.Tests/TestUtilities/ExpectedQueryRewritingVisitor.cs index 6ce5b7c63ed..91d02b5e1f7 100644 --- a/test/EFCore.Specification.Tests/TestUtilities/ExpectedQueryRewritingVisitor.cs +++ b/test/EFCore.Specification.Tests/TestUtilities/ExpectedQueryRewritingVisitor.cs @@ -302,7 +302,12 @@ private Expression AddNullProtectionForNonNullableMemberAccess(Expression expres instance.Type, memberExpression.Type.UnwrapNullableType()); - return Expression.Call(methodInfo, instance, maybeLambda); + var maybeMethodCall = Expression.Call(methodInfo, instance, maybeLambda); + + return memberExpression.Member.DeclaringType.IsNullableType() + && memberExpression.Member.Name == "HasValue" + ? Expression.Coalesce(maybeMethodCall, Expression.Constant(false)) + : maybeMethodCall; } return Visit(expression); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/FunkyDataQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/FunkyDataQuerySqlServerTest.cs index 21d1ec868ea..f3d8ed331f6 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/FunkyDataQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/FunkyDataQuerySqlServerTest.cs @@ -443,7 +443,7 @@ CROSS JOIN [FunkyCustomers] AS [f0] WHERE (CASE WHEN (([f0].[LastName] = N'') AND [f0].[LastName] IS NOT NULL) OR ([f].[FirstName] IS NOT NULL AND ([f0].[LastName] IS NOT NULL AND (RIGHT([f].[FirstName], LEN([f0].[LastName])) = [f0].[LastName]))) THEN CAST(1 AS bit) ELSE CAST(0 AS bit) -END <> [f].[NullableBool]) OR [f].[NullableBool] IS NULL"); +END <> [f].[NullableBool]) OR ([f].[NullableBool] IS NULL)"); } public override async Task String_FirstOrDefault_and_LastOrDefault(bool async) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs index d3ad78345d1..fe025a6a8ac 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs @@ -4518,7 +4518,7 @@ INNER JOIN ( FROM [Factions] AS [f] WHERE [f].[Name] = N'Swarm' ) AS [t] ON [l].[Name] = [t].[CommanderName] -WHERE ([t].[Eradicated] <> CAST(1 AS bit)) OR [t].[Eradicated] IS NULL"); +WHERE ([t].[Eradicated] <> CAST(1 AS bit)) OR ([t].[Eradicated] IS NULL)"); } public override async Task Null_semantics_on_nullable_bool_from_left_join_subquery_is_fully_applied(bool async) @@ -4533,7 +4533,7 @@ LEFT JOIN ( FROM [Factions] AS [f] WHERE [f].[Name] = N'Swarm' ) AS [t] ON [l].[Name] = [t].[CommanderName] -WHERE ([t].[Eradicated] <> CAST(1 AS bit)) OR [t].[Eradicated] IS NULL"); +WHERE ([t].[Eradicated] <> CAST(1 AS bit)) OR ([t].[Eradicated] IS NULL)"); } public override async Task Include_on_derived_type_with_order_by_and_paging(bool async) @@ -6237,7 +6237,7 @@ public override async Task Nullable_bool_comparison_is_translated_to_server(bool AssertSql( @"SELECT CASE - WHEN ([f].[Eradicated] = CAST(1 AS bit)) AND [f].[Eradicated] IS NOT NULL THEN CAST(1 AS bit) + WHEN ([f].[Eradicated] = CAST(1 AS bit)) AND ([f].[Eradicated] IS NOT NULL) THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END AS [IsEradicated] FROM [Factions] AS [f]"); @@ -6697,10 +6697,10 @@ FROM [LocustLeaders] AS [l] WHERE (CASE WHEN [f].[Name] = N'Locust' THEN CAST(1 AS bit) ELSE NULL -END <> CAST(1 AS bit)) OR CASE +END <> CAST(1 AS bit)) OR (CASE WHEN [f].[Name] = N'Locust' THEN CAST(1 AS bit) ELSE NULL -END IS NULL"); +END IS NULL)"); } public override async Task Byte_array_contains_literal(bool async) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NullSemanticsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NullSemanticsQuerySqlServerTest.cs index fa842e4aae2..6ae05a87ce1 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NullSemanticsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NullSemanticsQuerySqlServerTest.cs @@ -37,7 +37,7 @@ FROM [Entities1] AS [e] // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[NullableBoolA] = [e].[NullableBoolB]) OR ([e].[NullableBoolA] IS NULL AND [e].[NullableBoolB] IS NULL)"); +WHERE ([e].[NullableBoolA] = [e].[NullableBoolB]) OR (([e].[NullableBoolA] IS NULL) AND ([e].[NullableBoolB] IS NULL))"); } public override async Task Compare_negated_bool_with_bool_equal(bool async) @@ -51,15 +51,15 @@ FROM [Entities1] AS [e] // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[BoolA] <> [e].[NullableBoolB]) AND [e].[NullableBoolB] IS NOT NULL", +WHERE ([e].[BoolA] <> [e].[NullableBoolB]) AND ([e].[NullableBoolB] IS NOT NULL)", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[NullableBoolA] <> [e].[BoolB]) AND [e].[NullableBoolA] IS NOT NULL", +WHERE ([e].[NullableBoolA] <> [e].[BoolB]) AND ([e].[NullableBoolA] IS NOT NULL)", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE (([e].[NullableBoolA] <> [e].[NullableBoolB]) AND ([e].[NullableBoolA] IS NOT NULL AND [e].[NullableBoolB] IS NOT NULL)) OR ([e].[NullableBoolA] IS NULL AND [e].[NullableBoolB] IS NULL)"); +WHERE (([e].[NullableBoolA] <> [e].[NullableBoolB]) AND (([e].[NullableBoolA] IS NOT NULL) AND ([e].[NullableBoolB] IS NOT NULL))) OR (([e].[NullableBoolA] IS NULL) AND ([e].[NullableBoolB] IS NULL))"); } public override async Task Compare_bool_with_negated_bool_equal(bool async) @@ -73,15 +73,15 @@ FROM [Entities1] AS [e] // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[BoolA] <> [e].[NullableBoolB]) AND [e].[NullableBoolB] IS NOT NULL", +WHERE ([e].[BoolA] <> [e].[NullableBoolB]) AND ([e].[NullableBoolB] IS NOT NULL)", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[NullableBoolA] <> [e].[BoolB]) AND [e].[NullableBoolA] IS NOT NULL", +WHERE ([e].[NullableBoolA] <> [e].[BoolB]) AND ([e].[NullableBoolA] IS NOT NULL)", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE (([e].[NullableBoolA] <> [e].[NullableBoolB]) AND ([e].[NullableBoolA] IS NOT NULL AND [e].[NullableBoolB] IS NOT NULL)) OR ([e].[NullableBoolA] IS NULL AND [e].[NullableBoolB] IS NULL)"); +WHERE (([e].[NullableBoolA] <> [e].[NullableBoolB]) AND (([e].[NullableBoolA] IS NOT NULL) AND ([e].[NullableBoolB] IS NOT NULL))) OR (([e].[NullableBoolA] IS NULL) AND ([e].[NullableBoolB] IS NULL))"); } public override async Task Compare_negated_bool_with_negated_bool_equal(bool async) @@ -95,15 +95,15 @@ FROM [Entities1] AS [e] // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[BoolA] = [e].[NullableBoolB]) AND [e].[NullableBoolB] IS NOT NULL", +WHERE ([e].[BoolA] = [e].[NullableBoolB]) AND ([e].[NullableBoolB] IS NOT NULL)", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[NullableBoolA] = [e].[BoolB]) AND [e].[NullableBoolA] IS NOT NULL", +WHERE ([e].[NullableBoolA] = [e].[BoolB]) AND ([e].[NullableBoolA] IS NOT NULL)", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE (([e].[NullableBoolA] = [e].[NullableBoolB]) AND ([e].[NullableBoolA] IS NOT NULL AND [e].[NullableBoolB] IS NOT NULL)) OR ([e].[NullableBoolA] IS NULL AND [e].[NullableBoolB] IS NULL)"); +WHERE (([e].[NullableBoolA] = [e].[NullableBoolB]) AND (([e].[NullableBoolA] IS NOT NULL) AND ([e].[NullableBoolB] IS NOT NULL))) OR (([e].[NullableBoolA] IS NULL) AND ([e].[NullableBoolB] IS NULL))"); } public override async Task Compare_bool_with_bool_equal_negated(bool async) @@ -117,15 +117,15 @@ FROM [Entities1] AS [e] // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[BoolA] <> [e].[NullableBoolB]) OR [e].[NullableBoolB] IS NULL", +WHERE ([e].[BoolA] <> [e].[NullableBoolB]) OR ([e].[NullableBoolB] IS NULL)", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[NullableBoolA] <> [e].[BoolB]) OR [e].[NullableBoolA] IS NULL", +WHERE ([e].[NullableBoolA] <> [e].[BoolB]) OR ([e].[NullableBoolA] IS NULL)", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE (([e].[NullableBoolA] <> [e].[NullableBoolB]) OR ([e].[NullableBoolA] IS NULL OR [e].[NullableBoolB] IS NULL)) AND ([e].[NullableBoolA] IS NOT NULL OR [e].[NullableBoolB] IS NOT NULL)"); +WHERE (([e].[NullableBoolA] <> [e].[NullableBoolB]) OR (([e].[NullableBoolA] IS NULL) OR ([e].[NullableBoolB] IS NULL))) AND (([e].[NullableBoolA] IS NOT NULL) OR ([e].[NullableBoolB] IS NOT NULL))"); } public override async Task Compare_negated_bool_with_bool_equal_negated(bool async) @@ -139,15 +139,15 @@ FROM [Entities1] AS [e] // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[BoolA] = [e].[NullableBoolB]) OR [e].[NullableBoolB] IS NULL", +WHERE ([e].[BoolA] = [e].[NullableBoolB]) OR ([e].[NullableBoolB] IS NULL)", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[NullableBoolA] = [e].[BoolB]) OR [e].[NullableBoolA] IS NULL", +WHERE ([e].[NullableBoolA] = [e].[BoolB]) OR ([e].[NullableBoolA] IS NULL)", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE (([e].[NullableBoolA] = [e].[NullableBoolB]) OR ([e].[NullableBoolA] IS NULL OR [e].[NullableBoolB] IS NULL)) AND ([e].[NullableBoolA] IS NOT NULL OR [e].[NullableBoolB] IS NOT NULL)"); +WHERE (([e].[NullableBoolA] = [e].[NullableBoolB]) OR (([e].[NullableBoolA] IS NULL) OR ([e].[NullableBoolB] IS NULL))) AND (([e].[NullableBoolA] IS NOT NULL) OR ([e].[NullableBoolB] IS NOT NULL))"); } public override async Task Compare_bool_with_negated_bool_equal_negated(bool async) @@ -161,15 +161,15 @@ FROM [Entities1] AS [e] // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[BoolA] = [e].[NullableBoolB]) OR [e].[NullableBoolB] IS NULL", +WHERE ([e].[BoolA] = [e].[NullableBoolB]) OR ([e].[NullableBoolB] IS NULL)", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[NullableBoolA] = [e].[BoolB]) OR [e].[NullableBoolA] IS NULL", +WHERE ([e].[NullableBoolA] = [e].[BoolB]) OR ([e].[NullableBoolA] IS NULL)", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE (([e].[NullableBoolA] = [e].[NullableBoolB]) OR ([e].[NullableBoolA] IS NULL OR [e].[NullableBoolB] IS NULL)) AND ([e].[NullableBoolA] IS NOT NULL OR [e].[NullableBoolB] IS NOT NULL)"); +WHERE (([e].[NullableBoolA] = [e].[NullableBoolB]) OR (([e].[NullableBoolA] IS NULL) OR ([e].[NullableBoolB] IS NULL))) AND (([e].[NullableBoolA] IS NOT NULL) OR ([e].[NullableBoolB] IS NOT NULL))"); } public override async Task Compare_negated_bool_with_negated_bool_equal_negated(bool async) @@ -183,15 +183,15 @@ FROM [Entities1] AS [e] // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[BoolA] <> [e].[NullableBoolB]) OR [e].[NullableBoolB] IS NULL", +WHERE ([e].[BoolA] <> [e].[NullableBoolB]) OR ([e].[NullableBoolB] IS NULL)", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[NullableBoolA] <> [e].[BoolB]) OR [e].[NullableBoolA] IS NULL", +WHERE ([e].[NullableBoolA] <> [e].[BoolB]) OR ([e].[NullableBoolA] IS NULL)", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE (([e].[NullableBoolA] <> [e].[NullableBoolB]) OR ([e].[NullableBoolA] IS NULL OR [e].[NullableBoolB] IS NULL)) AND ([e].[NullableBoolA] IS NOT NULL OR [e].[NullableBoolB] IS NOT NULL)"); +WHERE (([e].[NullableBoolA] <> [e].[NullableBoolB]) OR (([e].[NullableBoolA] IS NULL) OR ([e].[NullableBoolB] IS NULL))) AND (([e].[NullableBoolA] IS NOT NULL) OR ([e].[NullableBoolB] IS NOT NULL))"); } public override async Task Compare_bool_with_bool_not_equal(bool async) @@ -205,15 +205,15 @@ FROM [Entities1] AS [e] // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[BoolA] <> [e].[NullableBoolB]) OR [e].[NullableBoolB] IS NULL", +WHERE ([e].[BoolA] <> [e].[NullableBoolB]) OR ([e].[NullableBoolB] IS NULL)", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[NullableBoolA] <> [e].[BoolB]) OR [e].[NullableBoolA] IS NULL", +WHERE ([e].[NullableBoolA] <> [e].[BoolB]) OR ([e].[NullableBoolA] IS NULL)", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE (([e].[NullableBoolA] <> [e].[NullableBoolB]) OR ([e].[NullableBoolA] IS NULL OR [e].[NullableBoolB] IS NULL)) AND ([e].[NullableBoolA] IS NOT NULL OR [e].[NullableBoolB] IS NOT NULL)"); +WHERE (([e].[NullableBoolA] <> [e].[NullableBoolB]) OR (([e].[NullableBoolA] IS NULL) OR ([e].[NullableBoolB] IS NULL))) AND (([e].[NullableBoolA] IS NOT NULL) OR ([e].[NullableBoolB] IS NOT NULL))"); } public override async Task Compare_negated_bool_with_bool_not_equal(bool async) @@ -227,15 +227,15 @@ FROM [Entities1] AS [e] // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[BoolA] = [e].[NullableBoolB]) OR [e].[NullableBoolB] IS NULL", +WHERE ([e].[BoolA] = [e].[NullableBoolB]) OR ([e].[NullableBoolB] IS NULL)", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[NullableBoolA] = [e].[BoolB]) OR [e].[NullableBoolA] IS NULL", +WHERE ([e].[NullableBoolA] = [e].[BoolB]) OR ([e].[NullableBoolA] IS NULL)", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE (([e].[NullableBoolA] = [e].[NullableBoolB]) OR ([e].[NullableBoolA] IS NULL OR [e].[NullableBoolB] IS NULL)) AND ([e].[NullableBoolA] IS NOT NULL OR [e].[NullableBoolB] IS NOT NULL)"); +WHERE (([e].[NullableBoolA] = [e].[NullableBoolB]) OR (([e].[NullableBoolA] IS NULL) OR ([e].[NullableBoolB] IS NULL))) AND (([e].[NullableBoolA] IS NOT NULL) OR ([e].[NullableBoolB] IS NOT NULL))"); } public override async Task Compare_bool_with_negated_bool_not_equal(bool async) @@ -249,15 +249,15 @@ FROM [Entities1] AS [e] // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[BoolA] = [e].[NullableBoolB]) OR [e].[NullableBoolB] IS NULL", +WHERE ([e].[BoolA] = [e].[NullableBoolB]) OR ([e].[NullableBoolB] IS NULL)", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[NullableBoolA] = [e].[BoolB]) OR [e].[NullableBoolA] IS NULL", +WHERE ([e].[NullableBoolA] = [e].[BoolB]) OR ([e].[NullableBoolA] IS NULL)", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE (([e].[NullableBoolA] = [e].[NullableBoolB]) OR ([e].[NullableBoolA] IS NULL OR [e].[NullableBoolB] IS NULL)) AND ([e].[NullableBoolA] IS NOT NULL OR [e].[NullableBoolB] IS NOT NULL)"); +WHERE (([e].[NullableBoolA] = [e].[NullableBoolB]) OR (([e].[NullableBoolA] IS NULL) OR ([e].[NullableBoolB] IS NULL))) AND (([e].[NullableBoolA] IS NOT NULL) OR ([e].[NullableBoolB] IS NOT NULL))"); } public override async Task Compare_negated_bool_with_negated_bool_not_equal(bool async) @@ -271,15 +271,15 @@ FROM [Entities1] AS [e] // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[BoolA] <> [e].[NullableBoolB]) OR [e].[NullableBoolB] IS NULL", +WHERE ([e].[BoolA] <> [e].[NullableBoolB]) OR ([e].[NullableBoolB] IS NULL)", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[NullableBoolA] <> [e].[BoolB]) OR [e].[NullableBoolA] IS NULL", +WHERE ([e].[NullableBoolA] <> [e].[BoolB]) OR ([e].[NullableBoolA] IS NULL)", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE (([e].[NullableBoolA] <> [e].[NullableBoolB]) OR ([e].[NullableBoolA] IS NULL OR [e].[NullableBoolB] IS NULL)) AND ([e].[NullableBoolA] IS NOT NULL OR [e].[NullableBoolB] IS NOT NULL)"); +WHERE (([e].[NullableBoolA] <> [e].[NullableBoolB]) OR (([e].[NullableBoolA] IS NULL) OR ([e].[NullableBoolB] IS NULL))) AND (([e].[NullableBoolA] IS NOT NULL) OR ([e].[NullableBoolB] IS NOT NULL))"); } public override async Task Compare_bool_with_bool_not_equal_negated(bool async) @@ -293,15 +293,15 @@ FROM [Entities1] AS [e] // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[BoolA] = [e].[NullableBoolB]) AND [e].[NullableBoolB] IS NOT NULL", +WHERE ([e].[BoolA] = [e].[NullableBoolB]) AND ([e].[NullableBoolB] IS NOT NULL)", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[NullableBoolA] = [e].[BoolB]) AND [e].[NullableBoolA] IS NOT NULL", +WHERE ([e].[NullableBoolA] = [e].[BoolB]) AND ([e].[NullableBoolA] IS NOT NULL)", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE (([e].[NullableBoolA] = [e].[NullableBoolB]) AND ([e].[NullableBoolA] IS NOT NULL AND [e].[NullableBoolB] IS NOT NULL)) OR ([e].[NullableBoolA] IS NULL AND [e].[NullableBoolB] IS NULL)"); +WHERE (([e].[NullableBoolA] = [e].[NullableBoolB]) AND (([e].[NullableBoolA] IS NOT NULL) AND ([e].[NullableBoolB] IS NOT NULL))) OR (([e].[NullableBoolA] IS NULL) AND ([e].[NullableBoolB] IS NULL))"); } public override async Task Compare_negated_bool_with_bool_not_equal_negated(bool async) @@ -315,15 +315,15 @@ FROM [Entities1] AS [e] // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[BoolA] <> [e].[NullableBoolB]) AND [e].[NullableBoolB] IS NOT NULL", +WHERE ([e].[BoolA] <> [e].[NullableBoolB]) AND ([e].[NullableBoolB] IS NOT NULL)", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[NullableBoolA] <> [e].[BoolB]) AND [e].[NullableBoolA] IS NOT NULL", +WHERE ([e].[NullableBoolA] <> [e].[BoolB]) AND ([e].[NullableBoolA] IS NOT NULL)", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE (([e].[NullableBoolA] <> [e].[NullableBoolB]) AND ([e].[NullableBoolA] IS NOT NULL AND [e].[NullableBoolB] IS NOT NULL)) OR ([e].[NullableBoolA] IS NULL AND [e].[NullableBoolB] IS NULL)"); +WHERE (([e].[NullableBoolA] <> [e].[NullableBoolB]) AND (([e].[NullableBoolA] IS NOT NULL) AND ([e].[NullableBoolB] IS NOT NULL))) OR (([e].[NullableBoolA] IS NULL) AND ([e].[NullableBoolB] IS NULL))"); } public override async Task Compare_bool_with_negated_bool_not_equal_negated(bool async) @@ -337,15 +337,15 @@ FROM [Entities1] AS [e] // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[BoolA] <> [e].[NullableBoolB]) AND [e].[NullableBoolB] IS NOT NULL", +WHERE ([e].[BoolA] <> [e].[NullableBoolB]) AND ([e].[NullableBoolB] IS NOT NULL)", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[NullableBoolA] <> [e].[BoolB]) AND [e].[NullableBoolA] IS NOT NULL", +WHERE ([e].[NullableBoolA] <> [e].[BoolB]) AND ([e].[NullableBoolA] IS NOT NULL)", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE (([e].[NullableBoolA] <> [e].[NullableBoolB]) AND ([e].[NullableBoolA] IS NOT NULL AND [e].[NullableBoolB] IS NOT NULL)) OR ([e].[NullableBoolA] IS NULL AND [e].[NullableBoolB] IS NULL)"); +WHERE (([e].[NullableBoolA] <> [e].[NullableBoolB]) AND (([e].[NullableBoolA] IS NOT NULL) AND ([e].[NullableBoolB] IS NOT NULL))) OR (([e].[NullableBoolA] IS NULL) AND ([e].[NullableBoolB] IS NULL))"); } public override async Task Compare_negated_bool_with_negated_bool_not_equal_negated(bool async) @@ -359,15 +359,15 @@ FROM [Entities1] AS [e] // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[BoolA] = [e].[NullableBoolB]) AND [e].[NullableBoolB] IS NOT NULL", +WHERE ([e].[BoolA] = [e].[NullableBoolB]) AND ([e].[NullableBoolB] IS NOT NULL)", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[NullableBoolA] = [e].[BoolB]) AND [e].[NullableBoolA] IS NOT NULL", +WHERE ([e].[NullableBoolA] = [e].[BoolB]) AND ([e].[NullableBoolA] IS NOT NULL)", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE (([e].[NullableBoolA] = [e].[NullableBoolB]) AND ([e].[NullableBoolA] IS NOT NULL AND [e].[NullableBoolB] IS NOT NULL)) OR ([e].[NullableBoolA] IS NULL AND [e].[NullableBoolB] IS NULL)"); +WHERE (([e].[NullableBoolA] = [e].[NullableBoolB]) AND (([e].[NullableBoolA] IS NOT NULL) AND ([e].[NullableBoolB] IS NOT NULL))) OR (([e].[NullableBoolA] IS NULL) AND ([e].[NullableBoolB] IS NULL))"); } public override async Task Compare_equals_method(bool async) @@ -389,7 +389,7 @@ FROM [Entities1] AS [e] // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[NullableBoolA] = [e].[NullableBoolB]) OR ([e].[NullableBoolA] IS NULL AND [e].[NullableBoolB] IS NULL)"); +WHERE ([e].[NullableBoolA] = [e].[NullableBoolB]) OR (([e].[NullableBoolA] IS NULL) AND ([e].[NullableBoolB] IS NULL))"); } public override async Task Compare_equals_method_static(bool async) @@ -411,7 +411,7 @@ FROM [Entities1] AS [e] // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[NullableBoolA] = [e].[NullableBoolB]) OR ([e].[NullableBoolA] IS NULL AND [e].[NullableBoolB] IS NULL)"); +WHERE ([e].[NullableBoolA] = [e].[NullableBoolB]) OR (([e].[NullableBoolA] IS NULL) AND ([e].[NullableBoolB] IS NULL))"); } public override async Task Compare_equals_method_negated(bool async) @@ -425,15 +425,15 @@ FROM [Entities1] AS [e] // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[BoolA] <> [e].[NullableBoolB]) OR [e].[NullableBoolB] IS NULL", +WHERE ([e].[BoolA] <> [e].[NullableBoolB]) OR ([e].[NullableBoolB] IS NULL)", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[NullableBoolA] <> [e].[BoolB]) OR [e].[NullableBoolA] IS NULL", +WHERE ([e].[NullableBoolA] <> [e].[BoolB]) OR ([e].[NullableBoolA] IS NULL)", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE (([e].[NullableBoolA] <> [e].[NullableBoolB]) OR ([e].[NullableBoolA] IS NULL OR [e].[NullableBoolB] IS NULL)) AND ([e].[NullableBoolA] IS NOT NULL OR [e].[NullableBoolB] IS NOT NULL)"); +WHERE (([e].[NullableBoolA] <> [e].[NullableBoolB]) OR (([e].[NullableBoolA] IS NULL) OR ([e].[NullableBoolB] IS NULL))) AND (([e].[NullableBoolA] IS NOT NULL) OR ([e].[NullableBoolB] IS NOT NULL))"); } public override async Task Compare_equals_method_negated_static(bool async) @@ -447,15 +447,15 @@ FROM [Entities1] AS [e] // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[BoolA] <> [e].[NullableBoolB]) OR [e].[NullableBoolB] IS NULL", +WHERE ([e].[BoolA] <> [e].[NullableBoolB]) OR ([e].[NullableBoolB] IS NULL)", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[NullableBoolA] <> [e].[BoolB]) OR [e].[NullableBoolA] IS NULL", +WHERE ([e].[NullableBoolA] <> [e].[BoolB]) OR ([e].[NullableBoolA] IS NULL)", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE (([e].[NullableBoolA] <> [e].[NullableBoolB]) OR ([e].[NullableBoolA] IS NULL OR [e].[NullableBoolB] IS NULL)) AND ([e].[NullableBoolA] IS NOT NULL OR [e].[NullableBoolB] IS NOT NULL)"); +WHERE (([e].[NullableBoolA] <> [e].[NullableBoolB]) OR (([e].[NullableBoolA] IS NULL) OR ([e].[NullableBoolB] IS NULL))) AND (([e].[NullableBoolA] IS NOT NULL) OR ([e].[NullableBoolB] IS NOT NULL))"); } public override async Task Compare_complex_equal_equal_equal(bool async) @@ -476,7 +476,7 @@ ELSE CAST(0 AS bit) @"SELECT [e].[Id] FROM [Entities1] AS [e] WHERE CASE - WHEN ([e].[NullableBoolA] = [e].[BoolB]) AND [e].[NullableBoolA] IS NOT NULL THEN CAST(1 AS bit) + WHEN ([e].[NullableBoolA] = [e].[BoolB]) AND ([e].[NullableBoolA] IS NOT NULL) THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END = CASE WHEN ([e].[IntA] = [e].[NullableIntB]) AND [e].[NullableIntB] IS NOT NULL THEN CAST(1 AS bit) @@ -486,7 +486,7 @@ ELSE CAST(0 AS bit) @"SELECT [e].[Id] FROM [Entities1] AS [e] WHERE CASE - WHEN (([e].[NullableBoolA] = [e].[NullableBoolB]) AND ([e].[NullableBoolA] IS NOT NULL AND [e].[NullableBoolB] IS NOT NULL)) OR ([e].[NullableBoolA] IS NULL AND [e].[NullableBoolB] IS NULL) THEN CAST(1 AS bit) + WHEN (([e].[NullableBoolA] = [e].[NullableBoolB]) AND (([e].[NullableBoolA] IS NOT NULL) AND ([e].[NullableBoolB] IS NOT NULL))) OR (([e].[NullableBoolA] IS NULL) AND ([e].[NullableBoolB] IS NULL)) THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END = CASE WHEN (([e].[NullableIntA] = [e].[NullableIntB]) AND ([e].[NullableIntA] IS NOT NULL AND [e].[NullableIntB] IS NOT NULL)) OR ([e].[NullableIntA] IS NULL AND [e].[NullableIntB] IS NULL) THEN CAST(1 AS bit) @@ -512,7 +512,7 @@ ELSE CAST(0 AS bit) @"SELECT [e].[Id] FROM [Entities1] AS [e] WHERE CASE - WHEN ([e].[NullableBoolA] = [e].[BoolB]) AND [e].[NullableBoolA] IS NOT NULL THEN CAST(1 AS bit) + WHEN ([e].[NullableBoolA] = [e].[BoolB]) AND ([e].[NullableBoolA] IS NOT NULL) THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END <> CASE WHEN ([e].[IntA] = [e].[NullableIntB]) AND [e].[NullableIntB] IS NOT NULL THEN CAST(1 AS bit) @@ -522,7 +522,7 @@ ELSE CAST(0 AS bit) @"SELECT [e].[Id] FROM [Entities1] AS [e] WHERE CASE - WHEN (([e].[NullableBoolA] = [e].[NullableBoolB]) AND ([e].[NullableBoolA] IS NOT NULL AND [e].[NullableBoolB] IS NOT NULL)) OR ([e].[NullableBoolA] IS NULL AND [e].[NullableBoolB] IS NULL) THEN CAST(1 AS bit) + WHEN (([e].[NullableBoolA] = [e].[NullableBoolB]) AND (([e].[NullableBoolA] IS NOT NULL) AND ([e].[NullableBoolB] IS NOT NULL))) OR (([e].[NullableBoolA] IS NULL) AND ([e].[NullableBoolB] IS NULL)) THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END <> CASE WHEN (([e].[NullableIntA] = [e].[NullableIntB]) AND ([e].[NullableIntA] IS NOT NULL AND [e].[NullableIntB] IS NOT NULL)) OR ([e].[NullableIntA] IS NULL AND [e].[NullableIntB] IS NULL) THEN CAST(1 AS bit) @@ -548,7 +548,7 @@ ELSE CAST(0 AS bit) @"SELECT [e].[Id] FROM [Entities1] AS [e] WHERE CASE - WHEN ([e].[NullableBoolA] <> [e].[BoolB]) OR [e].[NullableBoolA] IS NULL THEN CAST(1 AS bit) + WHEN ([e].[NullableBoolA] <> [e].[BoolB]) OR ([e].[NullableBoolA] IS NULL) THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END = CASE WHEN ([e].[IntA] = [e].[NullableIntB]) AND [e].[NullableIntB] IS NOT NULL THEN CAST(1 AS bit) @@ -558,7 +558,7 @@ ELSE CAST(0 AS bit) @"SELECT [e].[Id] FROM [Entities1] AS [e] WHERE CASE - WHEN (([e].[NullableBoolA] <> [e].[NullableBoolB]) OR ([e].[NullableBoolA] IS NULL OR [e].[NullableBoolB] IS NULL)) AND ([e].[NullableBoolA] IS NOT NULL OR [e].[NullableBoolB] IS NOT NULL) THEN CAST(1 AS bit) + WHEN (([e].[NullableBoolA] <> [e].[NullableBoolB]) OR (([e].[NullableBoolA] IS NULL) OR ([e].[NullableBoolB] IS NULL))) AND (([e].[NullableBoolA] IS NOT NULL) OR ([e].[NullableBoolB] IS NOT NULL)) THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END = CASE WHEN (([e].[NullableIntA] = [e].[NullableIntB]) AND ([e].[NullableIntA] IS NOT NULL AND [e].[NullableIntB] IS NOT NULL)) OR ([e].[NullableIntA] IS NULL AND [e].[NullableIntB] IS NULL) THEN CAST(1 AS bit) @@ -584,7 +584,7 @@ ELSE CAST(0 AS bit) @"SELECT [e].[Id] FROM [Entities1] AS [e] WHERE CASE - WHEN ([e].[NullableBoolA] <> [e].[BoolB]) OR [e].[NullableBoolA] IS NULL THEN CAST(1 AS bit) + WHEN ([e].[NullableBoolA] <> [e].[BoolB]) OR ([e].[NullableBoolA] IS NULL) THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END <> CASE WHEN ([e].[IntA] = [e].[NullableIntB]) AND [e].[NullableIntB] IS NOT NULL THEN CAST(1 AS bit) @@ -594,7 +594,7 @@ ELSE CAST(0 AS bit) @"SELECT [e].[Id] FROM [Entities1] AS [e] WHERE CASE - WHEN (([e].[NullableBoolA] <> [e].[NullableBoolB]) OR ([e].[NullableBoolA] IS NULL OR [e].[NullableBoolB] IS NULL)) AND ([e].[NullableBoolA] IS NOT NULL OR [e].[NullableBoolB] IS NOT NULL) THEN CAST(1 AS bit) + WHEN (([e].[NullableBoolA] <> [e].[NullableBoolB]) OR (([e].[NullableBoolA] IS NULL) OR ([e].[NullableBoolB] IS NULL))) AND (([e].[NullableBoolA] IS NOT NULL) OR ([e].[NullableBoolB] IS NOT NULL)) THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END <> CASE WHEN (([e].[NullableIntA] = [e].[NullableIntB]) AND ([e].[NullableIntA] IS NOT NULL AND [e].[NullableIntB] IS NOT NULL)) OR ([e].[NullableIntA] IS NULL AND [e].[NullableIntB] IS NULL) THEN CAST(1 AS bit) @@ -620,7 +620,7 @@ ELSE CAST(0 AS bit) @"SELECT [e].[Id] FROM [Entities1] AS [e] WHERE CASE - WHEN ([e].[NullableBoolA] <> [e].[BoolB]) OR [e].[NullableBoolA] IS NULL THEN CAST(1 AS bit) + WHEN ([e].[NullableBoolA] <> [e].[BoolB]) OR ([e].[NullableBoolA] IS NULL) THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END = CASE WHEN ([e].[IntA] <> [e].[NullableIntB]) OR [e].[NullableIntB] IS NULL THEN CAST(1 AS bit) @@ -630,7 +630,7 @@ ELSE CAST(0 AS bit) @"SELECT [e].[Id] FROM [Entities1] AS [e] WHERE CASE - WHEN (([e].[NullableBoolA] <> [e].[NullableBoolB]) OR ([e].[NullableBoolA] IS NULL OR [e].[NullableBoolB] IS NULL)) AND ([e].[NullableBoolA] IS NOT NULL OR [e].[NullableBoolB] IS NOT NULL) THEN CAST(1 AS bit) + WHEN (([e].[NullableBoolA] <> [e].[NullableBoolB]) OR (([e].[NullableBoolA] IS NULL) OR ([e].[NullableBoolB] IS NULL))) AND (([e].[NullableBoolA] IS NOT NULL) OR ([e].[NullableBoolB] IS NOT NULL)) THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END = CASE WHEN (([e].[NullableIntA] <> [e].[NullableIntB]) OR ([e].[NullableIntA] IS NULL OR [e].[NullableIntB] IS NULL)) AND ([e].[NullableIntA] IS NOT NULL OR [e].[NullableIntB] IS NOT NULL) THEN CAST(1 AS bit) @@ -656,7 +656,7 @@ ELSE CAST(0 AS bit) @"SELECT [e].[Id] FROM [Entities1] AS [e] WHERE CASE - WHEN ([e].[NullableBoolA] <> [e].[BoolB]) OR [e].[NullableBoolA] IS NULL THEN CAST(1 AS bit) + WHEN ([e].[NullableBoolA] <> [e].[BoolB]) OR ([e].[NullableBoolA] IS NULL) THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END <> CASE WHEN ([e].[IntA] <> [e].[NullableIntB]) OR [e].[NullableIntB] IS NULL THEN CAST(1 AS bit) @@ -666,7 +666,7 @@ ELSE CAST(0 AS bit) @"SELECT [e].[Id] FROM [Entities1] AS [e] WHERE CASE - WHEN (([e].[NullableBoolA] <> [e].[NullableBoolB]) OR ([e].[NullableBoolA] IS NULL OR [e].[NullableBoolB] IS NULL)) AND ([e].[NullableBoolA] IS NOT NULL OR [e].[NullableBoolB] IS NOT NULL) THEN CAST(1 AS bit) + WHEN (([e].[NullableBoolA] <> [e].[NullableBoolB]) OR (([e].[NullableBoolA] IS NULL) OR ([e].[NullableBoolB] IS NULL))) AND (([e].[NullableBoolA] IS NOT NULL) OR ([e].[NullableBoolB] IS NOT NULL)) THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END <> CASE WHEN (([e].[NullableIntA] <> [e].[NullableIntB]) OR ([e].[NullableIntA] IS NULL OR [e].[NullableIntB] IS NULL)) AND ([e].[NullableIntA] IS NOT NULL OR [e].[NullableIntB] IS NOT NULL) THEN CAST(1 AS bit) @@ -1062,7 +1062,7 @@ public override async Task Where_nullable_bool_with_null_check(bool async) AssertSql( @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE [e].[NullableBoolA] IS NOT NULL AND ([e].[NullableBoolA] = CAST(1 AS bit))"); +WHERE ([e].[NullableBoolA] IS NOT NULL) AND ([e].[NullableBoolA] = CAST(1 AS bit))"); } public override void Where_equal_using_relational_null_semantics_with_parameter() @@ -1186,7 +1186,7 @@ public override void Switching_null_semantics_produces_different_cache_entry() AssertSql( @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[NullableBoolA] = [e].[NullableBoolB]) OR ([e].[NullableBoolA] IS NULL AND [e].[NullableBoolB] IS NULL)", +WHERE ([e].[NullableBoolA] = [e].[NullableBoolB]) OR (([e].[NullableBoolA] IS NULL) AND ([e].[NullableBoolB] IS NULL))", // @"SELECT [e].[Id] FROM [Entities1] AS [e] @@ -1418,15 +1418,15 @@ FROM [Entities1] AS [e] // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ([e].[NullableBoolA] = COALESCE([e].[NullableBoolB], [e].[NullableBoolC])) OR ([e].[NullableBoolA] IS NULL AND ([e].[NullableBoolB] IS NULL AND [e].[NullableBoolC] IS NULL))", +WHERE ([e].[NullableBoolA] = COALESCE([e].[NullableBoolB], [e].[NullableBoolC])) OR (([e].[NullableBoolA] IS NULL) AND (([e].[NullableBoolB] IS NULL) AND ([e].[NullableBoolC] IS NULL)))", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE (COALESCE([e].[NullableBoolB], [e].[BoolC]) <> [e].[NullableBoolA]) OR [e].[NullableBoolA] IS NULL", +WHERE (COALESCE([e].[NullableBoolB], [e].[BoolC]) <> [e].[NullableBoolA]) OR ([e].[NullableBoolA] IS NULL)", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ((COALESCE([e].[NullableBoolB], [e].[NullableBoolC]) <> [e].[NullableBoolA]) OR (([e].[NullableBoolB] IS NULL AND [e].[NullableBoolC] IS NULL) OR [e].[NullableBoolA] IS NULL)) AND (([e].[NullableBoolB] IS NOT NULL OR [e].[NullableBoolC] IS NOT NULL) OR [e].[NullableBoolA] IS NOT NULL)"); +WHERE ((COALESCE([e].[NullableBoolB], [e].[NullableBoolC]) <> [e].[NullableBoolA]) OR ((([e].[NullableBoolB] IS NULL) AND ([e].[NullableBoolC] IS NULL)) OR ([e].[NullableBoolA] IS NULL))) AND ((([e].[NullableBoolB] IS NOT NULL) OR ([e].[NullableBoolC] IS NOT NULL)) OR ([e].[NullableBoolA] IS NOT NULL))"); } public override async Task Null_semantics_conditional(bool async) @@ -1444,7 +1444,7 @@ ELSE [e].[NullableBoolC] @"SELECT [e].[Id] FROM [Entities1] AS [e] WHERE CASE - WHEN (([e].[NullableBoolA] <> [e].[NullableBoolB]) OR ([e].[NullableBoolA] IS NULL OR [e].[NullableBoolB] IS NULL)) AND ([e].[NullableBoolA] IS NOT NULL OR [e].[NullableBoolB] IS NOT NULL) THEN [e].[BoolB] + WHEN (([e].[NullableBoolA] <> [e].[NullableBoolB]) OR (([e].[NullableBoolA] IS NULL) OR ([e].[NullableBoolB] IS NULL))) AND (([e].[NullableBoolA] IS NOT NULL) OR ([e].[NullableBoolB] IS NOT NULL)) THEN [e].[BoolB] ELSE [e].[BoolC] END = [e].[BoolA]", // @@ -1453,13 +1453,13 @@ FROM [Entities1] AS [e] WHERE CASE WHEN CASE WHEN [e].[BoolA] = CAST(1 AS bit) THEN CASE - WHEN (([e].[NullableBoolA] <> [e].[NullableBoolB]) OR ([e].[NullableBoolA] IS NULL OR [e].[NullableBoolB] IS NULL)) AND ([e].[NullableBoolA] IS NOT NULL OR [e].[NullableBoolB] IS NOT NULL) THEN CAST(1 AS bit) + WHEN (([e].[NullableBoolA] <> [e].[NullableBoolB]) OR (([e].[NullableBoolA] IS NULL) OR ([e].[NullableBoolB] IS NULL))) AND (([e].[NullableBoolA] IS NOT NULL) OR ([e].[NullableBoolB] IS NOT NULL)) THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END ELSE [e].[BoolC] END <> [e].[BoolB] THEN [e].[BoolA] ELSE CASE - WHEN (([e].[NullableBoolB] = [e].[NullableBoolC]) AND ([e].[NullableBoolB] IS NOT NULL AND [e].[NullableBoolC] IS NOT NULL)) OR ([e].[NullableBoolB] IS NULL AND [e].[NullableBoolC] IS NULL) THEN CAST(1 AS bit) + WHEN (([e].[NullableBoolB] = [e].[NullableBoolC]) AND (([e].[NullableBoolB] IS NOT NULL) AND ([e].[NullableBoolC] IS NOT NULL))) OR (([e].[NullableBoolB] IS NULL) AND ([e].[NullableBoolC] IS NULL)) THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END END = CAST(1 AS bit)"); @@ -1649,11 +1649,11 @@ public override async Task Null_semantics_with_null_check_complex2(bool async) AssertSql( @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ((([e].[NullableBoolA] IS NOT NULL AND [e].[NullableBoolB] IS NOT NULL) AND (([e].[NullableBoolB] <> [e].[NullableBoolA]) OR [e].[NullableBoolC] IS NOT NULL)) AND (([e].[NullableBoolC] <> [e].[NullableBoolB]) OR [e].[NullableBoolC] IS NULL)) OR (([e].[NullableBoolC] <> [e].[BoolB]) OR [e].[NullableBoolC] IS NULL)", +WHERE (((([e].[NullableBoolA] IS NOT NULL) AND ([e].[NullableBoolB] IS NOT NULL)) AND (([e].[NullableBoolB] <> [e].[NullableBoolA]) OR ([e].[NullableBoolC] IS NOT NULL))) AND (([e].[NullableBoolC] <> [e].[NullableBoolB]) OR ([e].[NullableBoolC] IS NULL))) OR (([e].[NullableBoolC] <> [e].[BoolB]) OR ([e].[NullableBoolC] IS NULL))", // @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE ((([e].[NullableBoolA] IS NOT NULL AND [e].[NullableBoolB] IS NOT NULL) AND (([e].[NullableBoolB] <> [e].[NullableBoolA]) OR [e].[NullableBoolC] IS NOT NULL)) AND (([e].[NullableBoolC] <> [e].[NullableBoolB]) OR [e].[NullableBoolC] IS NULL)) OR (([e].[NullableBoolB] <> [e].[BoolB]) OR [e].[NullableBoolB] IS NULL)"); +WHERE (((([e].[NullableBoolA] IS NOT NULL) AND ([e].[NullableBoolB] IS NOT NULL)) AND (([e].[NullableBoolB] <> [e].[NullableBoolA]) OR ([e].[NullableBoolC] IS NOT NULL))) AND (([e].[NullableBoolC] <> [e].[NullableBoolB]) OR ([e].[NullableBoolC] IS NULL))) OR (([e].[NullableBoolB] <> [e].[BoolB]) OR ([e].[NullableBoolB] IS NULL))"); } public override async Task IsNull_on_complex_expression(bool async) @@ -1798,7 +1798,7 @@ public override async Task Nullable_column_info_doesnt_propagate_between_differe @"SELECT [e].[Id] FROM [Entities1] AS [e] INNER JOIN [Entities1] AS [e0] ON [e].[NullableBoolA] IS NULL -WHERE (([e].[NullableBoolA] <> [e0].[NullableBoolB]) OR ([e].[NullableBoolA] IS NULL OR [e0].[NullableBoolB] IS NULL)) AND ([e].[NullableBoolA] IS NOT NULL OR [e0].[NullableBoolB] IS NOT NULL)"); +WHERE (([e].[NullableBoolA] <> [e0].[NullableBoolB]) OR (([e].[NullableBoolA] IS NULL) OR ([e0].[NullableBoolB] IS NULL))) AND (([e].[NullableBoolA] IS NOT NULL) OR ([e0].[NullableBoolB] IS NOT NULL))"); } public override async Task Nullable_column_info_propagation_complex(bool async) @@ -1808,7 +1808,7 @@ public override async Task Nullable_column_info_propagation_complex(bool async) AssertSql( @"SELECT [e].[Id] FROM [Entities1] AS [e] -WHERE (([e].[NullableStringA] IS NOT NULL AND [e].[NullableBoolB] IS NOT NULL) AND [e].[NullableStringC] IS NOT NULL) AND (([e].[NullableBoolB] <> [e].[NullableBoolC]) OR [e].[NullableBoolC] IS NULL)"); +WHERE (([e].[NullableStringA] IS NOT NULL AND ([e].[NullableBoolB] IS NOT NULL)) AND [e].[NullableStringC] IS NOT NULL) AND (([e].[NullableBoolB] <> [e].[NullableBoolC]) OR ([e].[NullableBoolC] IS NULL))"); } public override async Task Empty_subquery_with_contains_returns_false(bool async) @@ -1984,6 +1984,142 @@ FROM [Entities1] AS [e] WHERE [e].[NullableIntA] NOT IN (1, 2, 3)"); } + public override async Task Bool_equal_nullable_bool_HasValue(bool async) + { + await base.Bool_equal_nullable_bool_HasValue(async); + + AssertSql( + @"SELECT [e].[Id], [e].[BoolA], [e].[BoolB], [e].[BoolC], [e].[IntA], [e].[IntB], [e].[IntC], [e].[NullableBoolA], [e].[NullableBoolB], [e].[NullableBoolC], [e].[NullableIntA], [e].[NullableIntB], [e].[NullableIntC], [e].[NullableStringA], [e].[NullableStringB], [e].[NullableStringC], [e].[StringA], [e].[StringB], [e].[StringC] +FROM [Entities1] AS [e] +WHERE [e].[NullableBoolA] IS NOT NULL", + // + @"@__prm_0='False' + +SELECT [e].[Id], [e].[BoolA], [e].[BoolB], [e].[BoolC], [e].[IntA], [e].[IntB], [e].[IntC], [e].[NullableBoolA], [e].[NullableBoolB], [e].[NullableBoolC], [e].[NullableIntA], [e].[NullableIntB], [e].[NullableIntC], [e].[NullableStringA], [e].[NullableStringB], [e].[NullableStringC], [e].[StringA], [e].[StringB], [e].[StringC] +FROM [Entities1] AS [e] +WHERE @__prm_0 = CASE + WHEN [e].[NullableBoolA] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END", + // + @"SELECT [e].[Id], [e].[BoolA], [e].[BoolB], [e].[BoolC], [e].[IntA], [e].[IntB], [e].[IntC], [e].[NullableBoolA], [e].[NullableBoolB], [e].[NullableBoolC], [e].[NullableIntA], [e].[NullableIntB], [e].[NullableIntC], [e].[NullableStringA], [e].[NullableStringB], [e].[NullableStringC], [e].[StringA], [e].[StringB], [e].[StringC] +FROM [Entities1] AS [e] +WHERE [e].[BoolB] = CASE + WHEN [e].[NullableBoolA] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END"); + } + + public override async Task Bool_equal_nullable_bool_compared_to_null(bool async) + { + await base.Bool_equal_nullable_bool_compared_to_null(async); + + AssertSql( + @"SELECT [e].[Id], [e].[BoolA], [e].[BoolB], [e].[BoolC], [e].[IntA], [e].[IntB], [e].[IntC], [e].[NullableBoolA], [e].[NullableBoolB], [e].[NullableBoolC], [e].[NullableIntA], [e].[NullableIntB], [e].[NullableIntC], [e].[NullableStringA], [e].[NullableStringB], [e].[NullableStringC], [e].[StringA], [e].[StringB], [e].[StringC] +FROM [Entities1] AS [e] +WHERE [e].[NullableBoolA] IS NULL", + // + @"@__prm_0='False' + +SELECT [e].[Id], [e].[BoolA], [e].[BoolB], [e].[BoolC], [e].[IntA], [e].[IntB], [e].[IntC], [e].[NullableBoolA], [e].[NullableBoolB], [e].[NullableBoolC], [e].[NullableIntA], [e].[NullableIntB], [e].[NullableIntC], [e].[NullableStringA], [e].[NullableStringB], [e].[NullableStringC], [e].[StringA], [e].[StringB], [e].[StringC] +FROM [Entities1] AS [e] +WHERE @__prm_0 = CASE + WHEN [e].[NullableBoolA] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END"); + } + + public override async Task Bool_not_equal_nullable_bool_HasValue(bool async) + { + await base.Bool_not_equal_nullable_bool_HasValue(async); + + AssertSql( + @"SELECT [e].[Id], [e].[BoolA], [e].[BoolB], [e].[BoolC], [e].[IntA], [e].[IntB], [e].[IntC], [e].[NullableBoolA], [e].[NullableBoolB], [e].[NullableBoolC], [e].[NullableIntA], [e].[NullableIntB], [e].[NullableIntC], [e].[NullableStringA], [e].[NullableStringB], [e].[NullableStringC], [e].[StringA], [e].[StringB], [e].[StringC] +FROM [Entities1] AS [e] +WHERE [e].[NullableBoolA] IS NULL", + // + @"@__prm_0='False' + +SELECT [e].[Id], [e].[BoolA], [e].[BoolB], [e].[BoolC], [e].[IntA], [e].[IntB], [e].[IntC], [e].[NullableBoolA], [e].[NullableBoolB], [e].[NullableBoolC], [e].[NullableIntA], [e].[NullableIntB], [e].[NullableIntC], [e].[NullableStringA], [e].[NullableStringB], [e].[NullableStringC], [e].[StringA], [e].[StringB], [e].[StringC] +FROM [Entities1] AS [e] +WHERE @__prm_0 <> CASE + WHEN [e].[NullableBoolA] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END", + // + @"SELECT [e].[Id], [e].[BoolA], [e].[BoolB], [e].[BoolC], [e].[IntA], [e].[IntB], [e].[IntC], [e].[NullableBoolA], [e].[NullableBoolB], [e].[NullableBoolC], [e].[NullableIntA], [e].[NullableIntB], [e].[NullableIntC], [e].[NullableStringA], [e].[NullableStringB], [e].[NullableStringC], [e].[StringA], [e].[StringB], [e].[StringC] +FROM [Entities1] AS [e] +WHERE [e].[BoolB] <> CASE + WHEN [e].[NullableBoolA] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END"); + } + + public override async Task Bool_not_equal_nullable_bool_compared_to_null(bool async) + { + await base.Bool_not_equal_nullable_bool_compared_to_null(async); + + AssertSql( + @"SELECT [e].[Id], [e].[BoolA], [e].[BoolB], [e].[BoolC], [e].[IntA], [e].[IntB], [e].[IntC], [e].[NullableBoolA], [e].[NullableBoolB], [e].[NullableBoolC], [e].[NullableIntA], [e].[NullableIntB], [e].[NullableIntC], [e].[NullableStringA], [e].[NullableStringB], [e].[NullableStringC], [e].[StringA], [e].[StringB], [e].[StringC] +FROM [Entities1] AS [e] +WHERE [e].[NullableBoolA] IS NOT NULL", + // + @"@__prm_0='False' + +SELECT [e].[Id], [e].[BoolA], [e].[BoolB], [e].[BoolC], [e].[IntA], [e].[IntB], [e].[IntC], [e].[NullableBoolA], [e].[NullableBoolB], [e].[NullableBoolC], [e].[NullableIntA], [e].[NullableIntB], [e].[NullableIntC], [e].[NullableStringA], [e].[NullableStringB], [e].[NullableStringC], [e].[StringA], [e].[StringB], [e].[StringC] +FROM [Entities1] AS [e] +WHERE @__prm_0 <> CASE + WHEN [e].[NullableBoolA] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END"); + } + + public override async Task Bool_logical_operation_with_nullable_bool_HasValue(bool async) + { + await base.Bool_logical_operation_with_nullable_bool_HasValue(async); + + AssertSql( + @"SELECT [e].[Id], [e].[BoolA], [e].[BoolB], [e].[BoolC], [e].[IntA], [e].[IntB], [e].[IntC], [e].[NullableBoolA], [e].[NullableBoolB], [e].[NullableBoolC], [e].[NullableIntA], [e].[NullableIntB], [e].[NullableIntC], [e].[NullableStringA], [e].[NullableStringB], [e].[NullableStringC], [e].[StringA], [e].[StringB], [e].[StringC] +FROM [Entities1] AS [e]", + // + @"SELECT [e].[Id], [e].[BoolA], [e].[BoolB], [e].[BoolC], [e].[IntA], [e].[IntB], [e].[IntC], [e].[NullableBoolA], [e].[NullableBoolB], [e].[NullableBoolC], [e].[NullableIntA], [e].[NullableIntB], [e].[NullableIntC], [e].[NullableStringA], [e].[NullableStringB], [e].[NullableStringC], [e].[StringA], [e].[StringB], [e].[StringC] +FROM [Entities1] AS [e] +WHERE 0 = 1", + // + @"SELECT [e].[Id], [e].[BoolA], [e].[BoolB], [e].[BoolC], [e].[IntA], [e].[IntB], [e].[IntC], [e].[NullableBoolA], [e].[NullableBoolB], [e].[NullableBoolC], [e].[NullableIntA], [e].[NullableIntB], [e].[NullableIntC], [e].[NullableStringA], [e].[NullableStringB], [e].[NullableStringC], [e].[StringA], [e].[StringB], [e].[StringC] +FROM [Entities1] AS [e] +WHERE ([e].[BoolB] | CASE + WHEN [e].[NullableBoolA] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END) = CAST(1 AS bit)"); + } + + public override async Task Comparison_compared_to_null_check_on_bool(bool async) + { + await base.Comparison_compared_to_null_check_on_bool(async); + + AssertSql( + @"SELECT [e].[Id], [e].[BoolA], [e].[BoolB], [e].[BoolC], [e].[IntA], [e].[IntB], [e].[IntC], [e].[NullableBoolA], [e].[NullableBoolB], [e].[NullableBoolC], [e].[NullableIntA], [e].[NullableIntB], [e].[NullableIntC], [e].[NullableStringA], [e].[NullableStringB], [e].[NullableStringC], [e].[StringA], [e].[StringB], [e].[StringC] +FROM [Entities1] AS [e] +WHERE CASE + WHEN [e].[IntA] = [e].[IntB] THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END <> CASE + WHEN [e].[NullableBoolA] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END", + // + @"SELECT [e].[Id], [e].[BoolA], [e].[BoolB], [e].[BoolC], [e].[IntA], [e].[IntB], [e].[IntC], [e].[NullableBoolA], [e].[NullableBoolB], [e].[NullableBoolC], [e].[NullableIntA], [e].[NullableIntB], [e].[NullableIntC], [e].[NullableStringA], [e].[NullableStringB], [e].[NullableStringC], [e].[StringA], [e].[StringB], [e].[StringC] +FROM [Entities1] AS [e] +WHERE CASE + WHEN [e].[IntA] <> [e].[IntB] THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END = CASE + WHEN [e].[NullableBoolA] IS NOT NULL THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) +END"); + } + private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs index 223829e62b3..1a3dfa76fbb 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs @@ -5359,7 +5359,7 @@ FROM [Factions] AS [f] LEFT JOIN [LocustHordes] AS [l1] ON [f].[Id] = [l1].[Id] WHERE [l1].[Id] IS NOT NULL AND ([f].[Name] = N'Swarm') ) AS [t] ON [l].[Name] = [t].[CommanderName] -WHERE ([t].[Eradicated] <> CAST(1 AS bit)) OR [t].[Eradicated] IS NULL"); +WHERE ([t].[Eradicated] <> CAST(1 AS bit)) OR ([t].[Eradicated] IS NULL)"); } public override async Task Null_semantics_on_nullable_bool_from_left_join_subquery_is_fully_applied(bool async) @@ -5377,7 +5377,7 @@ FROM [Factions] AS [f] LEFT JOIN [LocustHordes] AS [l1] ON [f].[Id] = [l1].[Id] WHERE [l1].[Id] IS NOT NULL AND ([f].[Name] = N'Swarm') ) AS [t] ON [l].[Name] = [t].[CommanderName] -WHERE ([t].[Eradicated] <> CAST(1 AS bit)) OR [t].[Eradicated] IS NULL"); +WHERE ([t].[Eradicated] <> CAST(1 AS bit)) OR ([t].[Eradicated] IS NULL)"); } public override async Task Include_on_derived_type_with_order_by_and_paging(bool async) @@ -7254,7 +7254,7 @@ public override async Task Nullable_bool_comparison_is_translated_to_server(bool AssertSql( @"SELECT CASE - WHEN ([l].[Eradicated] = CAST(1 AS bit)) AND [l].[Eradicated] IS NOT NULL THEN CAST(1 AS bit) + WHEN ([l].[Eradicated] = CAST(1 AS bit)) AND ([l].[Eradicated] IS NOT NULL) THEN CAST(1 AS bit) ELSE CAST(0 AS bit) END AS [IsEradicated] FROM [Factions] AS [f] @@ -7789,10 +7789,10 @@ WHERE [l1].[Id] IS NOT NULL WHERE (CASE WHEN [t].[Name] = N'Locust' THEN CAST(1 AS bit) ELSE NULL -END <> CAST(1 AS bit)) OR CASE +END <> CAST(1 AS bit)) OR (CASE WHEN [t].[Name] = N'Locust' THEN CAST(1 AS bit) ELSE NULL -END IS NULL"); +END IS NULL)"); } public override async Task Byte_array_contains_literal(bool async) diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/NullSemanticsQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/NullSemanticsQuerySqliteTest.cs index 888468988a4..e9eb29c0683 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/NullSemanticsQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/NullSemanticsQuerySqliteTest.cs @@ -11,8 +11,115 @@ public class NullSemanticsQuerySqliteTest : NullSemanticsQueryTestBase (""e"".""NullableBoolA"" IS NOT NULL)", + // + @"SELECT ""e"".""Id"", ""e"".""BoolA"", ""e"".""BoolB"", ""e"".""BoolC"", ""e"".""IntA"", ""e"".""IntB"", ""e"".""IntC"", ""e"".""NullableBoolA"", ""e"".""NullableBoolB"", ""e"".""NullableBoolC"", ""e"".""NullableIntA"", ""e"".""NullableIntB"", ""e"".""NullableIntC"", ""e"".""NullableStringA"", ""e"".""NullableStringB"", ""e"".""NullableStringC"", ""e"".""StringA"", ""e"".""StringB"", ""e"".""StringC"" +FROM ""Entities1"" AS ""e"" +WHERE ""e"".""BoolB"" <> (""e"".""NullableBoolA"" IS NOT NULL)"); + } + + public override async Task Bool_not_equal_nullable_bool_compared_to_null(bool async) + { + await base.Bool_not_equal_nullable_bool_compared_to_null(async); + + AssertSql( + @"SELECT ""e"".""Id"", ""e"".""BoolA"", ""e"".""BoolB"", ""e"".""BoolC"", ""e"".""IntA"", ""e"".""IntB"", ""e"".""IntC"", ""e"".""NullableBoolA"", ""e"".""NullableBoolB"", ""e"".""NullableBoolC"", ""e"".""NullableIntA"", ""e"".""NullableIntB"", ""e"".""NullableIntC"", ""e"".""NullableStringA"", ""e"".""NullableStringB"", ""e"".""NullableStringC"", ""e"".""StringA"", ""e"".""StringB"", ""e"".""StringC"" +FROM ""Entities1"" AS ""e"" +WHERE ""e"".""NullableBoolA"" IS NOT NULL", + // + @"@__prm_0='False' (DbType = String) + +SELECT ""e"".""Id"", ""e"".""BoolA"", ""e"".""BoolB"", ""e"".""BoolC"", ""e"".""IntA"", ""e"".""IntB"", ""e"".""IntC"", ""e"".""NullableBoolA"", ""e"".""NullableBoolB"", ""e"".""NullableBoolC"", ""e"".""NullableIntA"", ""e"".""NullableIntB"", ""e"".""NullableIntC"", ""e"".""NullableStringA"", ""e"".""NullableStringB"", ""e"".""NullableStringC"", ""e"".""StringA"", ""e"".""StringB"", ""e"".""StringC"" +FROM ""Entities1"" AS ""e"" +WHERE @__prm_0 <> (""e"".""NullableBoolA"" IS NOT NULL)"); + } + + public override async Task Bool_logical_operation_with_nullable_bool_HasValue(bool async) + { + await base.Bool_logical_operation_with_nullable_bool_HasValue(async); + + AssertSql( + @"SELECT ""e"".""Id"", ""e"".""BoolA"", ""e"".""BoolB"", ""e"".""BoolC"", ""e"".""IntA"", ""e"".""IntB"", ""e"".""IntC"", ""e"".""NullableBoolA"", ""e"".""NullableBoolB"", ""e"".""NullableBoolC"", ""e"".""NullableIntA"", ""e"".""NullableIntB"", ""e"".""NullableIntC"", ""e"".""NullableStringA"", ""e"".""NullableStringB"", ""e"".""NullableStringC"", ""e"".""StringA"", ""e"".""StringB"", ""e"".""StringC"" +FROM ""Entities1"" AS ""e""", + // + @"SELECT ""e"".""Id"", ""e"".""BoolA"", ""e"".""BoolB"", ""e"".""BoolC"", ""e"".""IntA"", ""e"".""IntB"", ""e"".""IntC"", ""e"".""NullableBoolA"", ""e"".""NullableBoolB"", ""e"".""NullableBoolC"", ""e"".""NullableIntA"", ""e"".""NullableIntB"", ""e"".""NullableIntC"", ""e"".""NullableStringA"", ""e"".""NullableStringB"", ""e"".""NullableStringC"", ""e"".""StringA"", ""e"".""StringB"", ""e"".""StringC"" +FROM ""Entities1"" AS ""e"" +WHERE 0", + // + @"SELECT ""e"".""Id"", ""e"".""BoolA"", ""e"".""BoolB"", ""e"".""BoolC"", ""e"".""IntA"", ""e"".""IntB"", ""e"".""IntC"", ""e"".""NullableBoolA"", ""e"".""NullableBoolB"", ""e"".""NullableBoolC"", ""e"".""NullableIntA"", ""e"".""NullableIntB"", ""e"".""NullableIntC"", ""e"".""NullableStringA"", ""e"".""NullableStringB"", ""e"".""NullableStringC"", ""e"".""StringA"", ""e"".""StringB"", ""e"".""StringC"" +FROM ""Entities1"" AS ""e"" +WHERE ""e"".""BoolB"" | (""e"".""NullableBoolA"" IS NOT NULL)"); + } + + public override async Task Comparison_compared_to_null_check_on_bool(bool async) + { + await base.Comparison_compared_to_null_check_on_bool(async); + + AssertSql( + @"SELECT ""e"".""Id"", ""e"".""BoolA"", ""e"".""BoolB"", ""e"".""BoolC"", ""e"".""IntA"", ""e"".""IntB"", ""e"".""IntC"", ""e"".""NullableBoolA"", ""e"".""NullableBoolB"", ""e"".""NullableBoolC"", ""e"".""NullableIntA"", ""e"".""NullableIntB"", ""e"".""NullableIntC"", ""e"".""NullableStringA"", ""e"".""NullableStringB"", ""e"".""NullableStringC"", ""e"".""StringA"", ""e"".""StringB"", ""e"".""StringC"" +FROM ""Entities1"" AS ""e"" +WHERE (""e"".""IntA"" = ""e"".""IntB"") <> (""e"".""NullableBoolA"" IS NOT NULL)", + // + @"SELECT ""e"".""Id"", ""e"".""BoolA"", ""e"".""BoolB"", ""e"".""BoolC"", ""e"".""IntA"", ""e"".""IntB"", ""e"".""IntC"", ""e"".""NullableBoolA"", ""e"".""NullableBoolB"", ""e"".""NullableBoolC"", ""e"".""NullableIntA"", ""e"".""NullableIntB"", ""e"".""NullableIntC"", ""e"".""NullableStringA"", ""e"".""NullableStringB"", ""e"".""NullableStringC"", ""e"".""StringA"", ""e"".""StringB"", ""e"".""StringC"" +FROM ""Entities1"" AS ""e"" +WHERE (""e"".""IntA"" <> ""e"".""IntB"") = (""e"".""NullableBoolA"" IS NOT NULL)"); + } + + private void AssertSql(params string[] expected) + => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); + protected override NullSemanticsContext CreateContext(bool useRelationalNulls = false) { var options = new DbContextOptionsBuilder(Fixture.CreateOptions());