Skip to content

Commit

Permalink
Simplify and improve null semantics rewrites (dotnet#34072)
Browse files Browse the repository at this point in the history
- exhaustive testing of null rewrites
- a simpler implementation of the rewrite, which instead of hand-coding all of the cases, combines the generic case and the already available simplifications
- a more generic translation of the rewrite optimized for predicates, which simplifies some existing tests.
  • Loading branch information
ranma42 authored and roji committed Jun 27, 2024
1 parent 1732d15 commit f5fdb0b
Show file tree
Hide file tree
Showing 9 changed files with 2,170 additions and 346 deletions.
353 changes: 31 additions & 322 deletions src/EFCore.Relational/Query/SqlNullabilityProcessor.cs

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,97 @@ protected NullSemanticsQueryTestBase(TFixture fixture)
{
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual async Task Rewrite_compare_int_with_int(bool async)
{
var bools = new bool[] { false, true };

foreach (var neq in bools)
{
foreach (var negated in bools)
{
foreach (var nullableB in bools)
{
foreach (var nullableA in bools)
{
var queryBuilder = (ISetSource ss) =>
{
var data = nullableA
? ss.Set<NullSemanticsEntity1>().Select(e => new { e.Id, A = e.NullableIntA, e.IntB, e.NullableIntB })
: ss.Set<NullSemanticsEntity1>().Select(e => new { e.Id, A = (int?)e.IntA, e.IntB, e.NullableIntB });

var query = nullableB
? data.Select(e => new { e.Id, e.A, B = e.NullableIntB })
: data.Select(e => new { e.Id, e.A, B = (int?)e.IntB });

var result = neq
? query.Select(e => new { e.Id, X = e.A != e.B })
: query.Select(e => new { e.Id, X = e.A == e.B });

return negated
? result.Select(e => new { e.Id, X = !e.X })
: result;
};

await AssertQuery(async, queryBuilder);
await AssertQueryScalar(async, ss => queryBuilder(ss).Where(e => e.X).Select(e => e.Id));
}
}
}
}
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual async Task Rewrite_compare_bool_with_bool(bool async)
{
var bools = new bool[] { false, true };

foreach (var neq in bools)
{
foreach (var negated in bools)
{
foreach (var negateB in bools)
{
foreach (var nullableA in bools)
{
foreach (var negateA in bools)
{
foreach (var nullableB in bools)
{
var queryBuilder = (ISetSource ss) =>
{
var data = nullableA
? ss.Set<NullSemanticsEntity1>().Select(e => new { e.Id, A = e.NullableBoolA, e.BoolB, e.NullableBoolB })
: ss.Set<NullSemanticsEntity1>().Select(e => new { e.Id, A = (bool?)e.BoolA, e.BoolB, e.NullableBoolB });

var query = nullableB
? data.Select(e => new { e.Id, e.A, B = e.NullableBoolB })
: data.Select(e => new { e.Id, e.A, B = (bool?)e.BoolB });

query = negateA ? query.Select(e => new { e.Id, A = !e.A, e.B }) : query;
query = negateB ? query.Select(e => new { e.Id, e.A, B = !e.B }) : query;

var result = neq
? query.Select(e => new { e.Id, X = e.A != e.B })
: query.Select(e => new { e.Id, X = e.A == e.B });

return negated
? result.Select(e => new { e.Id, X = !e.X })
: result;
};

await AssertQuery(async, queryBuilder);
await AssertQueryScalar(async, ss => queryBuilder(ss).Where(e => e.X).Select(e => e.Id));
}
}
}
}
}
}
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual async Task Compare_bool_with_bool_equal(bool async)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5028,7 +5028,7 @@ INNER JOIN (
FROM [Factions] AS [f]
WHERE [f].[Name] = N'Swarm'
) AS [f0] ON [l].[Name] = [f0].[CommanderName]
WHERE [f0].[Eradicated] <> CAST(1 AS bit) OR [f0].[Eradicated] IS NULL
WHERE [f0].[Eradicated] = CAST(0 AS bit) OR [f0].[Eradicated] IS NULL
""");
}

Expand All @@ -5045,7 +5045,7 @@ LEFT JOIN (
FROM [Factions] AS [f]
WHERE [f].[Name] = N'Swarm'
) AS [f0] ON [l].[Name] = [f0].[CommanderName]
WHERE [f0].[Eradicated] <> CAST(1 AS bit) OR [f0].[Eradicated] IS NULL
WHERE [f0].[Eradicated] = CAST(0 AS bit) OR [f0].[Eradicated] IS NULL
""");
}

Expand Down Expand Up @@ -7618,7 +7618,7 @@ 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(0 AS bit) OR CASE
WHEN [f].[Name] = N'Locust' THEN CAST(1 AS bit)
ELSE NULL
END IS NULL
Expand Down
Loading

0 comments on commit f5fdb0b

Please sign in to comment.