Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/EFCore.Relational/Query/QuerySqlGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ public class QuerySqlGenerator : SqlExpressionVisitor
private IRelationalCommandBuilder _relationalCommandBuilder;
private Dictionary<string, int>? _repeatedParameterCounts;

private static readonly bool UseOldBehavior32375 =
AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue32375", out var enabled32375) && enabled32375;

/// <summary>
/// Creates a new instance of the <see cref="QuerySqlGenerator" /> class.
/// </summary>
Expand Down Expand Up @@ -1441,6 +1444,11 @@ protected override Expression VisitValues(ValuesExpression valuesExpression)
/// </summary>
protected virtual void GenerateValues(ValuesExpression valuesExpression)
{
if (!UseOldBehavior32375 && valuesExpression.RowValues.Count == 0)
{
throw new InvalidOperationException(RelationalStrings.EmptyCollectionNotSupportedAsInlineQueryRoot);
}

var rowValues = valuesExpression.RowValues;

// Some databases support providing the names of columns projected out of VALUES, e.g.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ public class RelationalQueryableMethodTranslatingExpressionVisitor : QueryableMe
private static readonly bool UseOldBehavior32218 =
AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue32218", out var enabled32218) && enabled32218;

private static readonly bool UseOldBehavior32375 =
AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue32375", out var enabled32375) && enabled32375;

/// <summary>
/// Creates a new instance of the <see cref="QueryableMethodTranslatingExpressionVisitor" /> class.
/// </summary>
Expand Down Expand Up @@ -394,7 +397,7 @@ static Expression UnwrapAsQueryable(Expression expression)
}));
}

if (rowExpressions.Count == 0)
if (UseOldBehavior32375 && rowExpressions.Count == 0)
{
AddTranslationErrorDetails(RelationalStrings.EmptyCollectionNotSupportedAsInlineQueryRoot);
return null;
Expand Down Expand Up @@ -579,11 +582,7 @@ protected override ShapedQueryExpression TranslateConcat(ShapedQueryExpression s
{
Tables:
[
ValuesExpression
{
RowValues: [{ Values.Count: 2 }, ..],
ColumnNames: [ValuesOrderingColumnName, ValuesValueColumnName]
} valuesExpression
ValuesExpression { ColumnNames: [ValuesOrderingColumnName, ValuesValueColumnName] } valuesExpression
],
Predicate: null,
GroupBy: [],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@ public ValuesExpression(
IEnumerable<IAnnotation>? annotations = null)
: base(alias, annotations)
{
Check.NotEmpty(rowValues, nameof(rowValues));

Check.DebugAssert(
rowValues.All(rv => rv.Values.Count == columnNames.Count),
"All row values must have a value count matching the number of column names");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ public class SqlServerQuerySqlGenerator : QuerySqlGenerator
private readonly ISqlGenerationHelper _sqlGenerationHelper;
private readonly int _sqlServerCompatibilityLevel;

private static readonly bool UseOldBehavior32375 =
AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue32375", out var enabled32375) && enabled32375;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
Expand Down Expand Up @@ -194,6 +197,11 @@ protected override Expression VisitValues(ValuesExpression valuesExpression)
/// </summary>
protected override void GenerateValues(ValuesExpression valuesExpression)
{
if (!UseOldBehavior32375 && valuesExpression.RowValues.Count == 0)
{
throw new InvalidOperationException(RelationalStrings.EmptyCollectionNotSupportedAsInlineQueryRoot);
}

// SQL Server supports providing the names of columns projected out of VALUES: (VALUES (1, 3), (2, 4)) AS x(a, b)
// (this is implemented in VisitValues above).
// But since other databases sometimes don't, the default relational implementation is complex, involving a SELECT for the first row
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ public PrimitiveCollectionsQueryRelationalTestBase(TFixture fixture)
{
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public override async Task Inline_collection_Count_with_zero_values(bool async)
{
var exception = await Assert.ThrowsAsync<InvalidOperationException>(() => base.Inline_collection_Count_with_zero_values(async));

Assert.Equal(RelationalStrings.EmptyCollectionNotSupportedAsInlineQueryRoot, exception.Message);
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public override Task Column_collection_Concat_parameter_collection_equality_inline_collection(bool async)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ public virtual Task Inline_collection_Contains_with_zero_values(bool async)
=> AssertQuery(
async,
// ReSharper disable once UseArrayEmptyMethod
ss => ss.Set<PrimitiveCollectionsEntity>().Where(c => new int[0].Contains(c.Id)));
ss => ss.Set<PrimitiveCollectionsEntity>().Where(c => new int[0].Contains(c.Id)),
assertEmpty: true);

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,10 +59,12 @@ FROM [PrimitiveCollectionsEntity] AS [p]
""");
}

public override Task Inline_collection_Count_with_zero_values(bool async)
=> AssertTranslationFailedWithDetails(
() => base.Inline_collection_Count_with_zero_values(async),
RelationalStrings.EmptyCollectionNotSupportedAsInlineQueryRoot);
public override async Task Inline_collection_Count_with_zero_values(bool async)
{
await base.Inline_collection_Count_with_zero_values(async);

AssertSql();
}

public override async Task Inline_collection_Count_with_one_value(bool async)
{
Expand Down Expand Up @@ -109,10 +111,17 @@ SELECT COUNT(*)
""");
}

public override Task Inline_collection_Contains_with_zero_values(bool async)
=> AssertTranslationFailedWithDetails(
() => base.Inline_collection_Contains_with_zero_values(async),
RelationalStrings.EmptyCollectionNotSupportedAsInlineQueryRoot);
public override async Task Inline_collection_Contains_with_zero_values(bool async)
{
await base.Inline_collection_Contains_with_zero_values(async);

AssertSql(
"""
SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings]
FROM [PrimitiveCollectionsEntity] AS [p]
WHERE 0 = 1
""");
}

public override async Task Inline_collection_Contains_with_one_value(bool async)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,12 @@ FROM [PrimitiveCollectionsEntity] AS [p]
""");
}

public override Task Inline_collection_Count_with_zero_values(bool async)
=> AssertTranslationFailedWithDetails(
() => base.Inline_collection_Count_with_zero_values(async),
RelationalStrings.EmptyCollectionNotSupportedAsInlineQueryRoot);
public override async Task Inline_collection_Count_with_zero_values(bool async)
{
await base.Inline_collection_Count_with_zero_values(async);

AssertSql();
}

public override async Task Inline_collection_Count_with_one_value(bool async)
{
Expand Down Expand Up @@ -101,10 +103,17 @@ SELECT COUNT(*)
""");
}

public override Task Inline_collection_Contains_with_zero_values(bool async)
=> AssertTranslationFailedWithDetails(
() => base.Inline_collection_Contains_with_zero_values(async),
RelationalStrings.EmptyCollectionNotSupportedAsInlineQueryRoot);
public override async Task Inline_collection_Contains_with_zero_values(bool async)
{
await base.Inline_collection_Contains_with_zero_values(async);

AssertSql(
"""
SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[String], [p].[Strings]
FROM [PrimitiveCollectionsEntity] AS [p]
WHERE 0 = 1
""");
}

public override async Task Inline_collection_Contains_with_one_value(bool async)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,12 @@ public override async Task Inline_collection_of_nullable_ints_Contains_null(bool
""");
}

public override Task Inline_collection_Count_with_zero_values(bool async)
=> AssertTranslationFailedWithDetails(
() => base.Inline_collection_Count_with_zero_values(async),
RelationalStrings.EmptyCollectionNotSupportedAsInlineQueryRoot);
public override async Task Inline_collection_Count_with_zero_values(bool async)
{
await base.Inline_collection_Count_with_zero_values(async);

AssertSql();
}

public override async Task Inline_collection_Count_with_one_value(bool async)
{
Expand Down Expand Up @@ -102,10 +104,17 @@ SELECT COUNT(*)
""");
}

public override Task Inline_collection_Contains_with_zero_values(bool async)
=> AssertTranslationFailedWithDetails(
() => base.Inline_collection_Contains_with_zero_values(async),
RelationalStrings.EmptyCollectionNotSupportedAsInlineQueryRoot);
public override async Task Inline_collection_Contains_with_zero_values(bool async)
{
await base.Inline_collection_Contains_with_zero_values(async);

AssertSql(
"""
SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."String", "p"."Strings"
FROM "PrimitiveCollectionsEntity" AS "p"
WHERE 0
""");
}

public override async Task Inline_collection_Contains_with_one_value(bool async)
{
Expand Down