Skip to content

Commit

Permalink
Translate logical Boolean operators as conditional Boolean operators (#…
Browse files Browse the repository at this point in the history
…33873)

* Translate logical Boolean operators as conditional Boolean operators

In C# the expected results of logical Boolean operators are the same as those of
the conditional Boolean operators, as per the
[documentation](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/boolean-logical-operators#nullable-boolean-logical-operators).

Instead, in SQL logical operators propagate NULLs. It is possible to avoid
complex translations by simply mapping the logical operators to the
corresponding conditional ones for Boolean expressions.

Fixes #30245.

* Update tests
  • Loading branch information
ranma42 authored Jun 3, 2024
1 parent 77097ee commit 2c84a6e
Show file tree
Hide file tree
Showing 7 changed files with 19 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -433,11 +433,14 @@ static Expression RemoveConvert(Expression e)
return result;
}

var isBooleanExpression = binaryExpression.Type == typeof(bool) || binaryExpression.Type == typeof(bool?);
var uncheckedNodeTypeVariant = binaryExpression.NodeType switch
{
ExpressionType.AddChecked => ExpressionType.Add,
ExpressionType.SubtractChecked => ExpressionType.Subtract,
ExpressionType.MultiplyChecked => ExpressionType.Multiply,
ExpressionType.And when isBooleanExpression => ExpressionType.AndAlso,
ExpressionType.Or when isBooleanExpression => ExpressionType.OrElse,
_ => binaryExpression.NodeType
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3121,10 +3121,7 @@ public override async Task Select_bitwise_or(bool async)
AssertSql(
"""
SELECT [c].[CustomerID], CASE
WHEN [c].[CustomerID] = N'ALFKI' THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END | CASE
WHEN [c].[CustomerID] = N'ANATR' THEN CAST(1 AS bit)
WHEN [c].[CustomerID] IN (N'ALFKI', N'ANATR') THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END AS [Value]
FROM [Customers] AS [c]
Expand All @@ -3139,13 +3136,7 @@ public override async Task Select_bitwise_or_multiple(bool async)
AssertSql(
"""
SELECT [c].[CustomerID], CASE
WHEN [c].[CustomerID] = N'ALFKI' THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END | CASE
WHEN [c].[CustomerID] = N'ANATR' THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END | CASE
WHEN [c].[CustomerID] = N'ANTON' THEN CAST(1 AS bit)
WHEN [c].[CustomerID] IN (N'ALFKI', N'ANATR', N'ANTON') THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END AS [Value]
FROM [Customers] AS [c]
Expand All @@ -3159,13 +3150,7 @@ public override async Task Select_bitwise_and(bool async)

AssertSql(
"""
SELECT [c].[CustomerID], CASE
WHEN [c].[CustomerID] = N'ALFKI' THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END & CASE
WHEN [c].[CustomerID] = N'ANATR' THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END AS [Value]
SELECT [c].[CustomerID], CAST(0 AS bit) AS [Value]
FROM [Customers] AS [c]
ORDER BY [c].[CustomerID]
""");
Expand All @@ -3177,13 +3162,7 @@ public override async Task Select_bitwise_and_or(bool async)

AssertSql(
"""
SELECT [c].[CustomerID], (CASE
WHEN [c].[CustomerID] = N'ALFKI' THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END & CASE
WHEN [c].[CustomerID] = N'ANATR' THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END) | CASE
SELECT [c].[CustomerID], CASE
WHEN [c].[CustomerID] = N'ANTON' THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END AS [Value]
Expand All @@ -3200,13 +3179,7 @@ public override async Task Where_bitwise_or_with_logical_or(bool async)
"""
SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region]
FROM [Customers] AS [c]
WHERE CASE
WHEN [c].[CustomerID] = N'ALFKI' THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END | CASE
WHEN [c].[CustomerID] = N'ANATR' THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END = CAST(1 AS bit) OR [c].[CustomerID] = N'ANTON'
WHERE [c].[CustomerID] IN (N'ALFKI', N'ANATR', N'ANTON')
""");
}

Expand All @@ -3218,13 +3191,7 @@ public override async Task Where_bitwise_and_with_logical_and(bool async)
"""
SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region]
FROM [Customers] AS [c]
WHERE CASE
WHEN [c].[CustomerID] = N'ALFKI' THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END & CASE
WHEN [c].[CustomerID] = N'ANATR' THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END = CAST(1 AS bit) AND [c].[CustomerID] = N'ANTON'
WHERE 0 = 1
""");
}

Expand All @@ -3236,13 +3203,7 @@ public override async Task Where_bitwise_or_with_logical_and(bool async)
"""
SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region]
FROM [Customers] AS [c]
WHERE CASE
WHEN [c].[CustomerID] = N'ALFKI' THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END | CASE
WHEN [c].[CustomerID] = N'ANATR' THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END = CAST(1 AS bit) AND [c].[Country] = N'Germany'
WHERE [c].[CustomerID] IN (N'ALFKI', N'ANATR') AND [c].[Country] = N'Germany'
""");
}

Expand All @@ -3254,13 +3215,7 @@ public override async Task Where_bitwise_and_with_logical_or(bool async)
"""
SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region]
FROM [Customers] AS [c]
WHERE CASE
WHEN [c].[CustomerID] = N'ALFKI' THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END & CASE
WHEN [c].[CustomerID] = N'ANATR' THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END = CAST(1 AS bit) OR [c].[CustomerID] = N'ANTON'
WHERE [c].[CustomerID] = N'ANTON'
""");
}

Expand Down Expand Up @@ -3309,13 +3264,7 @@ public override async Task Select_bitwise_or_with_logical_or(bool async)
AssertSql(
"""
SELECT [c].[CustomerID], CASE
WHEN CASE
WHEN [c].[CustomerID] = N'ALFKI' THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END | CASE
WHEN [c].[CustomerID] = N'ANATR' THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END = CAST(1 AS bit) OR [c].[CustomerID] = N'ANTON' THEN CAST(1 AS bit)
WHEN [c].[CustomerID] IN (N'ALFKI', N'ANATR', N'ANTON') THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END AS [Value]
FROM [Customers] AS [c]
Expand All @@ -3329,16 +3278,7 @@ public override async Task Select_bitwise_and_with_logical_and(bool async)

AssertSql(
"""
SELECT [c].[CustomerID], CASE
WHEN CASE
WHEN [c].[CustomerID] = N'ALFKI' THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END & CASE
WHEN [c].[CustomerID] = N'ANATR' THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END = CAST(1 AS bit) AND [c].[CustomerID] = N'ANTON' THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END AS [Value]
SELECT [c].[CustomerID], CAST(0 AS bit) AS [Value]
FROM [Customers] AS [c]
ORDER BY [c].[CustomerID]
""");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -497,13 +497,7 @@ public override async Task Where_bitwise_or(bool async)
"""
SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region]
FROM [Customers] AS [c]
WHERE CASE
WHEN [c].[CustomerID] = N'ALFKI' THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END | CASE
WHEN [c].[CustomerID] = N'ANATR' THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END = CAST(1 AS bit)
WHERE [c].[CustomerID] IN (N'ALFKI', N'ANATR')
""");
}

Expand All @@ -515,13 +509,7 @@ public override async Task Where_bitwise_and(bool async)
"""
SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region]
FROM [Customers] AS [c]
WHERE CASE
WHEN [c].[CustomerID] = N'ALFKI' THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END & CASE
WHEN [c].[CustomerID] = N'ANATR' THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END = CAST(1 AS bit)
WHERE 0 = 1
""");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3130,10 +3130,7 @@ 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 [e].[BoolB] | CASE
WHEN [e].[NullableBoolA] IS NOT NULL THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END = CAST(1 AS bit)
WHERE [e].[BoolB] = CAST(1 AS bit) OR [e].[NullableBoolA] IS NOT NULL
""");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,7 @@ CROSS JOIN [OperatorEntityString] AS [o0]
CROSS JOIN [OperatorEntityString] AS [o1]
CROSS JOIN [OperatorEntityString] AS [o2]
CROSS JOIN [OperatorEntityInt] AS [o3]
WHERE CASE
WHEN [o].[Value] = N'A' AND [o].[Value] IS NOT NULL AND [o0].[Value] = N'A' AND [o0].[Value] IS NOT NULL THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END | CASE
WHEN [o1].[Value] = N'B' AND [o1].[Value] IS NOT NULL AND [o2].[Value] = N'B' AND [o2].[Value] IS NOT NULL THEN CAST(1 AS bit)
ELSE CAST(0 AS bit)
END = CAST(1 AS bit) AND [o3].[Value] = 2
WHERE (([o].[Value] = N'A' AND [o0].[Value] = N'A') OR ([o1].[Value] = N'B' AND [o2].[Value] = N'B')) AND [o3].[Value] = 2
ORDER BY [o].[Id], [o0].[Id], [o1].[Id], [o2].[Id], [o3].[Id]
""");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ 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)
WHERE "e"."BoolB" OR "e"."NullableBoolA" IS NOT NULL
""");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ CROSS JOIN "OperatorEntityString" AS "o0"
CROSS JOIN "OperatorEntityString" AS "o1"
CROSS JOIN "OperatorEntityString" AS "o2"
CROSS JOIN "OperatorEntityInt" AS "o3"
WHERE ("o"."Value" = 'A' AND "o"."Value" IS NOT NULL AND "o0"."Value" = 'A' AND "o0"."Value" IS NOT NULL) | ("o1"."Value" = 'B' AND "o1"."Value" IS NOT NULL AND "o2"."Value" = 'B' AND "o2"."Value" IS NOT NULL) AND "o3"."Value" = 2
WHERE (("o"."Value" = 'A' AND "o0"."Value" = 'A') OR ("o1"."Value" = 'B' AND "o2"."Value" = 'B')) AND "o3"."Value" = 2
ORDER BY "o"."Id", "o0"."Id", "o1"."Id", "o2"."Id", "o3"."Id"
""");
}
Expand Down

0 comments on commit 2c84a6e

Please sign in to comment.