Skip to content

Commit

Permalink
Fix to #21164 - Error translating Contains expression when UseRelatio…
Browse files Browse the repository at this point in the history
…nalNulls(true)

Correctly processing values expression for the relational nulls code path

Fixes #21164
  • Loading branch information
maumar committed Jun 9, 2020
1 parent d3ad139 commit 64ce204
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 5 deletions.
12 changes: 7 additions & 5 deletions src/EFCore.Relational/Query/SqlNullabilityProcessor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -557,14 +557,16 @@ protected virtual SqlExpression VisitIn([NotNull] InExpression inExpression, boo
if (UseRelationalNulls
|| !(inExpression.Values is SqlConstantExpression || inExpression.Values is SqlParameterExpression))
{
var values = Visit(inExpression.Values, out _);
var (valuesExpression, valuesList, _) = ProcessInExpressionValues(inExpression.Values, extractNullValues: false);
nullable = false;

return inExpression.Update(item, values, subquery: null);
return valuesList.Count == 0
? (SqlExpression)_sqlExpressionFactory.Constant(false, inExpression.TypeMapping)
: inExpression.Update(item, valuesExpression, subquery: null);
}

// for c# null semantics we need to remove nulls from Values and add IsNull/IsNotNull when necessary
var (inValuesExpression, inValuesList, hasNullValue) = ProcessInExpressionValues(inExpression.Values);
var (inValuesExpression, inValuesList, hasNullValue) = ProcessInExpressionValues(inExpression.Values, extractNullValues: true);

// either values array is empty or only contains null
if (inValuesList.Count == 0)
Expand Down Expand Up @@ -613,7 +615,7 @@ protected virtual SqlExpression VisitIn([NotNull] InExpression inExpression, boo
inExpression.Update(item, inValuesExpression, subquery: null),
_sqlExpressionFactory.IsNull(item));

(SqlConstantExpression ProcessedValuesExpression, List<object> ProcessedValuesList, bool HasNullValue) ProcessInExpressionValues(SqlExpression valuesExpression)
(SqlConstantExpression ProcessedValuesExpression, List<object> ProcessedValuesList, bool HasNullValue) ProcessInExpressionValues(SqlExpression valuesExpression, bool extractNullValues)
{
var inValues = new List<object>();
var hasNullValue = false;
Expand All @@ -634,7 +636,7 @@ protected virtual SqlExpression VisitIn([NotNull] InExpression inExpression, boo

foreach (var value in values)
{
if (value == null)
if (value == null && extractNullValues)
{
hasNullValue = true;
continue;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,42 @@ public virtual void Where_equal_using_relational_null_semantics()
.Select(e => e.Id).ToList();
}

[ConditionalFact]
public virtual void Where_contains_on_parameter_array_with_relational_null_semantics()
{
using var context = CreateContext(useRelationalNulls: true);
var names = new[] { "Foo", "Bar" };
var result = context.Entities1
.Where(e => names.Contains(e.NullableStringA))
.Select(e => e.NullableStringA).ToList();

Assert.True(result.All(r => r == "Foo" || r == "Bar"));
}

[ConditionalFact]
public virtual void Where_contains_on_parameter_empty_array_with_relational_null_semantics()
{
using var context = CreateContext(useRelationalNulls: true);
var names = new string[0];
var result = context.Entities1
.Where(e => names.Contains(e.NullableStringA))
.Select(e => e.NullableStringA).ToList();

Assert.Equal(0, result.Count);
}

[ConditionalFact]
public virtual void Where_contains_on_parameter_array_with_just_null_with_relational_null_semantics()
{
using var context = CreateContext(useRelationalNulls: true);
var names = new string[] { null };
var result = context.Entities1
.Where(e => names.Contains(e.NullableStringA))
.Select(e => e.NullableStringA).ToList();

Assert.Equal(0, result.Count);
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Where_nullable_bool(bool async)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1005,6 +1005,36 @@ FROM [Entities1] AS [e]
WHERE [e].[NullableBoolA] = [e].[NullableBoolB]");
}

public override void Where_contains_on_parameter_array_with_relational_null_semantics()
{
base.Where_contains_on_parameter_array_with_relational_null_semantics();

AssertSql(
@"SELECT [e].[NullableStringA]
FROM [Entities1] AS [e]
WHERE [e].[NullableStringA] IN (N'Foo', N'Bar')");
}

public override void Where_contains_on_parameter_empty_array_with_relational_null_semantics()
{
base.Where_contains_on_parameter_empty_array_with_relational_null_semantics();

AssertSql(
@"SELECT [e].[NullableStringA]
FROM [Entities1] AS [e]
WHERE 0 = 1");
}

public override void Where_contains_on_parameter_array_with_just_null_with_relational_null_semantics()
{
base.Where_contains_on_parameter_array_with_just_null_with_relational_null_semantics();

AssertSql(
@"SELECT [e].[NullableStringA]
FROM [Entities1] AS [e]
WHERE [e].[NullableStringA] IN (NULL)");
}

public override async Task Where_nullable_bool(bool async)
{
await base.Where_nullable_bool(async);
Expand Down

0 comments on commit 64ce204

Please sign in to comment.