Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[release/7.0] Fix to #29279 - EF7 - GroupBy on Json column property + First/FirstOrDefault generates incorrect SQL #29288

Merged
merged 1 commit into from
Oct 7, 2022
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: 6 additions & 2 deletions src/EFCore.Relational/Query/JsonQueryExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -195,9 +195,13 @@ public virtual void Print(ExpressionPrinter expressionPrinter)
protected override Expression VisitChildren(ExpressionVisitor visitor)
{
var jsonColumn = (ColumnExpression)visitor.Visit(JsonColumn);
var newKeyPropertyMap = new Dictionary<IProperty, ColumnExpression>();
foreach (var (property, column) in _keyPropertyMap)
{
newKeyPropertyMap[property] = (ColumnExpression)visitor.Visit(column);
}

// TODO: also visit columns in the _keyPropertyMap?
return Update(jsonColumn, _keyPropertyMap);
return Update(jsonColumn, newKeyPropertyMap);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,49 @@ public virtual Task Group_by_on_json_scalar(bool async)
ss => ss.Set<JsonEntityBasic>()
.GroupBy(x => x.OwnedReferenceRoot.Name).Select(x => new { x.Key, Count = x.Count() }));

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Group_by_First_on_json_scalar(bool async)
=> AssertQuery(
async,
ss => ss.Set<JsonEntityBasic>()
.GroupBy(x => x.OwnedReferenceRoot.Name).Select(g => g.OrderBy(x => x.Id).First()),
entryCount: 40);

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Group_by_FirstOrDefault_on_json_scalar(bool async)
=> AssertQuery(
async,
ss => ss.Set<JsonEntityBasic>()
.GroupBy(x => x.OwnedReferenceRoot.Name).Select(g => g.OrderBy(x => x.Id).FirstOrDefault()),
entryCount: 40);

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Group_by_Skip_Take_on_json_scalar(bool async)
=> AssertQuery(
async,
ss => ss.Set<JsonEntityBasic>()
.GroupBy(x => x.OwnedReferenceRoot.Name).Select(g => g.OrderBy(x => x.Id).Skip(1).Take(5)));

[ConditionalTheory(Skip = "issue #29287")]
[MemberData(nameof(IsAsyncData))]
public virtual Task Group_by_json_scalar_Orderby_json_scalar_FirstOrDefault(bool async)
=> AssertQuery(
async,
ss => ss.Set<JsonEntityBasic>()
.GroupBy(x => x.OwnedReferenceRoot.OwnedReferenceBranch.Enum).Select(g => g.OrderBy(x => x.OwnedReferenceRoot.Number).FirstOrDefault()),
entryCount: 40);

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Group_by_json_scalar_Skip_First_project_json_scalar(bool async)
=> AssertQueryScalar(
async,
ss => ss.Set<JsonEntityBasic>()
.GroupBy(x => x.OwnedReferenceRoot.Name).Select(g => g.First().OwnedReferenceRoot.OwnedReferenceBranch.Enum));

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Json_with_include_on_json_entity(bool async)
Expand Down
109 changes: 109 additions & 0 deletions test/EFCore.SqlServer.FunctionalTests/Query/JsonQuerySqlServerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,115 @@ FROM [JsonEntitiesBasic] AS [j]
GROUP BY [t].[Key]");
}

public override async Task Group_by_First_on_json_scalar(bool async)
{
await base.Group_by_First_on_json_scalar(async);

AssertSql(
@"SELECT [t1].[Id], [t1].[EntityBasicId], [t1].[Name], JSON_QUERY([t1].[c],'$'), JSON_QUERY([t1].[c0],'$')
FROM (
SELECT [t].[Key]
FROM (
SELECT CAST(JSON_VALUE([j].[OwnedReferenceRoot],'$.Name') AS nvarchar(max)) AS [Key]
FROM [JsonEntitiesBasic] AS [j]
) AS [t]
GROUP BY [t].[Key]
) AS [t0]
LEFT JOIN (
SELECT [t2].[Id], [t2].[EntityBasicId], [t2].[Name], JSON_QUERY([t2].[c],'$') AS [c], JSON_QUERY([t2].[c0],'$') AS [c0], [t2].[Key]
FROM (
SELECT [t3].[Id], [t3].[EntityBasicId], [t3].[Name], JSON_QUERY([t3].[c],'$') AS [c], JSON_QUERY([t3].[c0],'$') AS [c0], [t3].[Key], ROW_NUMBER() OVER(PARTITION BY [t3].[Key] ORDER BY [t3].[Id]) AS [row]
FROM (
SELECT [j0].[Id], [j0].[EntityBasicId], [j0].[Name], JSON_QUERY([j0].[OwnedCollectionRoot],'$') AS [c], JSON_QUERY([j0].[OwnedReferenceRoot],'$') AS [c0], CAST(JSON_VALUE([j0].[OwnedReferenceRoot],'$.Name') AS nvarchar(max)) AS [Key]
FROM [JsonEntitiesBasic] AS [j0]
) AS [t3]
) AS [t2]
WHERE [t2].[row] <= 1
) AS [t1] ON [t0].[Key] = [t1].[Key]");
}

public override async Task Group_by_FirstOrDefault_on_json_scalar(bool async)
{
await base.Group_by_FirstOrDefault_on_json_scalar(async);

AssertSql(
@"SELECT [t1].[Id], [t1].[EntityBasicId], [t1].[Name], JSON_QUERY([t1].[c],'$'), JSON_QUERY([t1].[c0],'$')
FROM (
SELECT [t].[Key]
FROM (
SELECT CAST(JSON_VALUE([j].[OwnedReferenceRoot],'$.Name') AS nvarchar(max)) AS [Key]
FROM [JsonEntitiesBasic] AS [j]
) AS [t]
GROUP BY [t].[Key]
) AS [t0]
LEFT JOIN (
SELECT [t2].[Id], [t2].[EntityBasicId], [t2].[Name], JSON_QUERY([t2].[c],'$') AS [c], JSON_QUERY([t2].[c0],'$') AS [c0], [t2].[Key]
FROM (
SELECT [t3].[Id], [t3].[EntityBasicId], [t3].[Name], JSON_QUERY([t3].[c],'$') AS [c], JSON_QUERY([t3].[c0],'$') AS [c0], [t3].[Key], ROW_NUMBER() OVER(PARTITION BY [t3].[Key] ORDER BY [t3].[Id]) AS [row]
FROM (
SELECT [j0].[Id], [j0].[EntityBasicId], [j0].[Name], JSON_QUERY([j0].[OwnedCollectionRoot],'$') AS [c], JSON_QUERY([j0].[OwnedReferenceRoot],'$') AS [c0], CAST(JSON_VALUE([j0].[OwnedReferenceRoot],'$.Name') AS nvarchar(max)) AS [Key]
FROM [JsonEntitiesBasic] AS [j0]
) AS [t3]
) AS [t2]
WHERE [t2].[row] <= 1
) AS [t1] ON [t0].[Key] = [t1].[Key]");
}

public override async Task Group_by_Skip_Take_on_json_scalar(bool async)
{
await base.Group_by_Skip_Take_on_json_scalar(async);

AssertSql(
@"SELECT [t0].[Key], [t1].[Id], [t1].[EntityBasicId], [t1].[Name], [t1].[c], [t1].[c0]
FROM (
SELECT [t].[Key]
FROM (
SELECT CAST(JSON_VALUE([j].[OwnedReferenceRoot],'$.Name') AS nvarchar(max)) AS [Key]
FROM [JsonEntitiesBasic] AS [j]
) AS [t]
GROUP BY [t].[Key]
) AS [t0]
LEFT JOIN (
SELECT [t2].[Id], [t2].[EntityBasicId], [t2].[Name], [t2].[c], [t2].[c0], [t2].[Key]
FROM (
SELECT [t3].[Id], [t3].[EntityBasicId], [t3].[Name], JSON_QUERY([t3].[c],'$') AS [c], JSON_QUERY([t3].[c0],'$') AS [c0], [t3].[Key], ROW_NUMBER() OVER(PARTITION BY [t3].[Key] ORDER BY [t3].[Id]) AS [row]
FROM (
SELECT [j0].[Id], [j0].[EntityBasicId], [j0].[Name], JSON_QUERY([j0].[OwnedCollectionRoot],'$') AS [c], JSON_QUERY([j0].[OwnedReferenceRoot],'$') AS [c0], CAST(JSON_VALUE([j0].[OwnedReferenceRoot],'$.Name') AS nvarchar(max)) AS [Key]
FROM [JsonEntitiesBasic] AS [j0]
) AS [t3]
) AS [t2]
WHERE 1 < [t2].[row] AND [t2].[row] <= 6
) AS [t1] ON [t0].[Key] = [t1].[Key]
ORDER BY [t0].[Key], [t1].[Key], [t1].[Id]");
}

public override async Task Group_by_json_scalar_Orderby_json_scalar_FirstOrDefault(bool async)
{
await base.Group_by_json_scalar_Orderby_json_scalar_FirstOrDefault(async);

AssertSql(
@"");
}

public override async Task Group_by_json_scalar_Skip_First_project_json_scalar(bool async)
{
await base.Group_by_json_scalar_Skip_First_project_json_scalar(async);

AssertSql(
@"SELECT (
SELECT TOP(1) CAST(JSON_VALUE([t0].[c0],'$.OwnedReferenceBranch.Enum') AS nvarchar(max))
FROM (
SELECT [j0].[Id], [j0].[EntityBasicId], [j0].[Name], JSON_QUERY([j0].[OwnedCollectionRoot],'$') AS [c], JSON_QUERY([j0].[OwnedReferenceRoot],'$') AS [c0], CAST(JSON_VALUE([j0].[OwnedReferenceRoot],'$.Name') AS nvarchar(max)) AS [Key]
FROM [JsonEntitiesBasic] AS [j0]
) AS [t0]
WHERE [t].[Key] = [t0].[Key] OR (([t].[Key] IS NULL) AND ([t0].[Key] IS NULL)))
FROM (
SELECT CAST(JSON_VALUE([j].[OwnedReferenceRoot],'$.Name') AS nvarchar(max)) AS [Key]
FROM [JsonEntitiesBasic] AS [j]
) AS [t]
GROUP BY [t].[Key]");
}

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