From 58570670dce1858f347f46fe21d2f8333f834352 Mon Sep 17 00:00:00 2001 From: Smit Patel Date: Thu, 18 Aug 2022 14:55:42 -0700 Subject: [PATCH] ExecuteDelete: Always generate Any form Visit retrived ProjectionBinding from select expression as projection binding can bind to non-SqlExpression too. Resolves #28524 Resolves #28745 Resolves #28752 --- ...yableMethodTranslatingExpressionVisitor.cs | 8 - ...lationalSqlTranslatingExpressionVisitor.cs | 179 +++++++++--------- ...yableMethodTranslatingExpressionVisitor.cs | 10 +- .../FiltersInheritanceBulkUpdatesTestBase.cs | 31 ++- .../InheritanceBulkUpdatesFixtureBase.cs | 3 + .../InheritanceBulkUpdatesTestBase.cs | 31 ++- .../NorthwindBulkUpdatesTestBase.cs | 35 +++- ...PCFiltersInheritanceBulkUpdatesTestBase.cs | 10 + .../TPCInheritanceBulkUpdatesTestBase.cs | 10 + ...PTFiltersInheritanceBulkUpdatesTestBase.cs | 10 + .../TPTInheritanceBulkUpdatesTestBase.cs | 10 + ...tersInheritanceBulkUpdatesSqlServerTest.cs | 49 ++++- .../InheritanceBulkUpdatesSqlServerTest.cs | 48 ++++- .../NorthwindBulkUpdatesSqlServerTest.cs | 44 ++++- ...tersInheritanceBulkUpdatesSqlServerTest.cs | 21 ++ .../TPCInheritanceBulkUpdatesSqlServerTest.cs | 21 ++ ...tersInheritanceBulkUpdatesSqlServerTest.cs | 21 ++ .../TPTInheritanceBulkUpdatesSqlServerTest.cs | 21 ++ ...FiltersInheritanceBulkUpdatesSqliteTest.cs | 48 ++++- .../InheritanceBulkUpdatesSqliteTest.cs | 47 ++++- .../NorthwindBulkUpdatesSqliteTest.cs | 47 ++++- ...FiltersInheritanceBulkUpdatesSqliteTest.cs | 21 ++ .../TPCInheritanceBulkUpdatesSqliteTest.cs | 21 ++ ...FiltersInheritanceBulkUpdatesSqliteTest.cs | 21 ++ .../TPTInheritanceBulkUpdatesSqliteTest.cs | 21 ++ 25 files changed, 660 insertions(+), 128 deletions(-) diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs index 99afa6f307e..6ba42322788 100644 --- a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs @@ -1079,13 +1079,6 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp var clrType = entityType.ClrType; var entityParameter = Expression.Parameter(clrType); Expression predicateBody; - //if (pk.Properties.Count == 1) - //{ - // predicateBody = Expression.Call( - // EnumerableMethods.Contains.MakeGenericMethod(clrType), source, entityParameter); - //} - //else - //{ var innerParameter = Expression.Parameter(clrType); predicateBody = Expression.Call( QueryableMethods.AnyWithPredicate.MakeGenericMethod(clrType), @@ -1093,7 +1086,6 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp Expression.Quote(Expression.Lambda( Infrastructure.ExpressionExtensions.CreateEqualsExpression(innerParameter, entityParameter), innerParameter))); - //} var newSource = Expression.Call( QueryableMethods.Where.MakeGenericMethod(clrType), diff --git a/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs index 83f40952443..9e79413f4f0 100644 --- a/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs @@ -3,7 +3,6 @@ using System.Collections; using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; using Microsoft.EntityFrameworkCore.Metadata.Internal; using Microsoft.EntityFrameworkCore.Query.SqlExpressions; @@ -606,8 +605,82 @@ protected override Expression VisitExtension(Expression extensionExpression) return new EntityReferenceExpression(entityShaperExpression); case ProjectionBindingExpression projectionBindingExpression: - return ((SelectExpression)projectionBindingExpression.QueryExpression) - .GetProjection(projectionBindingExpression); + return Visit(((SelectExpression)projectionBindingExpression.QueryExpression) + .GetProjection(projectionBindingExpression)); + + case ShapedQueryExpression shapedQueryExpression: + if (shapedQueryExpression.ResultCardinality == ResultCardinality.Enumerable) + { + return QueryCompilationContext.NotTranslatedExpression; + } + + var shaperExpression = shapedQueryExpression.ShaperExpression; + ProjectionBindingExpression? mappedProjectionBindingExpression = null; + + var innerExpression = shaperExpression; + Type? convertedType = null; + if (shaperExpression is UnaryExpression unaryExpression + && unaryExpression.NodeType == ExpressionType.Convert) + { + convertedType = unaryExpression.Type; + innerExpression = unaryExpression.Operand; + } + + if (innerExpression is EntityShaperExpression ese + && (convertedType == null + || convertedType.IsAssignableFrom(ese.Type))) + { + return new EntityReferenceExpression(shapedQueryExpression.UpdateShaperExpression(innerExpression)); + } + + if (innerExpression is ProjectionBindingExpression pbe + && (convertedType == null + || convertedType.MakeNullable() == innerExpression.Type)) + { + mappedProjectionBindingExpression = pbe; + } + + if (mappedProjectionBindingExpression == null + && shaperExpression is BlockExpression blockExpression + && blockExpression.Expressions.Count == 2 + && blockExpression.Expressions[0] is BinaryExpression binaryExpression + && binaryExpression.NodeType == ExpressionType.Assign + && binaryExpression.Right is ProjectionBindingExpression pbe2) + { + mappedProjectionBindingExpression = pbe2; + } + + if (mappedProjectionBindingExpression == null) + { + return QueryCompilationContext.NotTranslatedExpression; + } + + var subquery = (SelectExpression)shapedQueryExpression.QueryExpression; + var projection = subquery.GetProjection(mappedProjectionBindingExpression); + if (projection is not SqlExpression sqlExpression) + { + return QueryCompilationContext.NotTranslatedExpression; + } + + if (subquery.Tables.Count == 0) + { + return sqlExpression; + } + + subquery.ReplaceProjection(new List { sqlExpression }); + subquery.ApplyProjection(); + + SqlExpression scalarSubqueryExpression = new ScalarSubqueryExpression(subquery); + + if (shapedQueryExpression.ResultCardinality == ResultCardinality.SingleOrDefault + && !shaperExpression.Type.IsNullableType()) + { + scalarSubqueryExpression = _sqlExpressionFactory.Coalesce( + scalarSubqueryExpression, + (SqlExpression)Visit(shaperExpression.Type.GetDefaultValueConstant())); + } + + return scalarSubqueryExpression; default: return QueryCompilationContext.NotTranslatedExpression; @@ -632,7 +705,7 @@ protected override Expression VisitMember(MemberExpression memberExpression) var innerExpression = Visit(memberExpression.Expression); return TryBindMember(innerExpression, MemberIdentity.Create(memberExpression.Member)) - ?? (TranslationFailed(memberExpression.Expression, Visit(memberExpression.Expression), out var sqlInnerExpression) + ?? (TranslationFailed(memberExpression.Expression, innerExpression, out var sqlInnerExpression) ? QueryCompilationContext.NotTranslatedExpression : Dependencies.MemberTranslatorProvider.Translate( sqlInnerExpression, memberExpression.Member, memberExpression.Type, _queryCompilationContext.Logger)) @@ -792,9 +865,9 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp : method; var enumerableSource = Visit(arguments[0]); - if (enumerableSource is EnumerableExpression) + if (enumerableSource is EnumerableExpression ee) { - enumerableExpression = (EnumerableExpression)enumerableSource; + enumerableExpression = ee; switch (method.Name) { case nameof(Queryable.AsQueryable) @@ -928,10 +1001,10 @@ when QueryableMethods.IsSumWithSelector(genericMethod): && !skipVisitChildren) { var @object = Visit(methodCallExpression.Object); - if (@object is EnumerableExpression) + if (@object is EnumerableExpression eeo) { // This is safe since if enumerableExpression is non-null then it was static method - enumerableExpression = (EnumerableExpression)@object; + enumerableExpression = eeo; } else if (TranslationFailed(methodCallExpression.Object, @object, out sqlObject)) { @@ -944,7 +1017,7 @@ when QueryableMethods.IsSumWithSelector(genericMethod): { var argument = arguments[i]; var visitedArgument = Visit(argument); - if (visitedArgument is EnumerableExpression) + if (visitedArgument is EnumerableExpression eea) { if (enumerableExpression != null) { @@ -952,7 +1025,7 @@ when QueryableMethods.IsSumWithSelector(genericMethod): break; } - enumerableExpression = (EnumerableExpression)visitedArgument; + enumerableExpression = eea; continue; } @@ -1009,83 +1082,10 @@ when QueryableMethods.IsSumWithSelector(genericMethod): // Subquery case var subqueryTranslation = _queryableMethodTranslatingExpressionVisitor.TranslateSubquery(methodCallExpression); - if (subqueryTranslation != null) - { - if (subqueryTranslation.ResultCardinality == ResultCardinality.Enumerable) - { - return QueryCompilationContext.NotTranslatedExpression; - } - - var shaperExpression = subqueryTranslation.ShaperExpression; - ProjectionBindingExpression? mappedProjectionBindingExpression = null; - var innerExpression = shaperExpression; - Type? convertedType = null; - if (shaperExpression is UnaryExpression unaryExpression - && unaryExpression.NodeType == ExpressionType.Convert) - { - convertedType = unaryExpression.Type; - innerExpression = unaryExpression.Operand; - } - - if (innerExpression is EntityShaperExpression ese - && (convertedType == null - || convertedType.IsAssignableFrom(ese.Type))) - { - return new EntityReferenceExpression(subqueryTranslation.UpdateShaperExpression(innerExpression)); - } - - if (innerExpression is ProjectionBindingExpression pbe - && (convertedType == null - || convertedType.MakeNullable() == innerExpression.Type)) - { - mappedProjectionBindingExpression = pbe; - } - - if (mappedProjectionBindingExpression == null - && shaperExpression is BlockExpression blockExpression - && blockExpression.Expressions.Count == 2 - && blockExpression.Expressions[0] is BinaryExpression binaryExpression - && binaryExpression.NodeType == ExpressionType.Assign - && binaryExpression.Right is ProjectionBindingExpression pbe2) - { - mappedProjectionBindingExpression = pbe2; - } - - if (mappedProjectionBindingExpression == null) - { - return QueryCompilationContext.NotTranslatedExpression; - } - - var subquery = (SelectExpression)subqueryTranslation.QueryExpression; - var projection = subquery.GetProjection(mappedProjectionBindingExpression); - if (projection is not SqlExpression sqlExpression) - { - return QueryCompilationContext.NotTranslatedExpression; - } - - if (subquery.Tables.Count == 0) - { - return sqlExpression; - } - - subquery.ReplaceProjection(new List { sqlExpression }); - subquery.ApplyProjection(); - - SqlExpression scalarSubqueryExpression = new ScalarSubqueryExpression(subquery); - - if (subqueryTranslation.ResultCardinality == ResultCardinality.SingleOrDefault - && !shaperExpression.Type.IsNullableType()) - { - scalarSubqueryExpression = _sqlExpressionFactory.Coalesce( - scalarSubqueryExpression, - (SqlExpression)Visit(shaperExpression.Type.GetDefaultValueConstant())); - } - - return scalarSubqueryExpression; - } - - return QueryCompilationContext.NotTranslatedExpression; + return subqueryTranslation == null + ? QueryCompilationContext.NotTranslatedExpression + : Visit(subqueryTranslation); } /// @@ -1394,12 +1394,7 @@ private static EnumerableExpression ProcessSelector(EnumerableExpression enumera { var lambdaBody = RemapLambda(enumerableExpression, lambdaExpression); var predicate = TranslateInternal(lambdaBody); - if (predicate == null) - { - return null; - } - - return enumerableExpression.ApplyPredicate(predicate); + return predicate == null ? null : enumerableExpression.ApplyPredicate(predicate); } private static Expression TryRemoveImplicitConvert(Expression expression) diff --git a/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs index 121f3574f1b..4b90c04448f 100644 --- a/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs @@ -153,10 +153,7 @@ when QueryableMethods.IsAverageWithSelector(method): var source2 = Visit(methodCallExpression.Arguments[1]); if (source2 is ShapedQueryExpression innerShapedQueryExpression) { - return CheckTranslated( - TranslateConcat( - shapedQueryExpression, - innerShapedQueryExpression)); + return CheckTranslated(TranslateConcat(shapedQueryExpression, innerShapedQueryExpression)); } break; @@ -207,10 +204,7 @@ when QueryableMethods.IsAverageWithSelector(method): var source2 = Visit(methodCallExpression.Arguments[1]); if (source2 is ShapedQueryExpression innerShapedQueryExpression) { - return CheckTranslated( - TranslateExcept( - shapedQueryExpression, - innerShapedQueryExpression)); + return CheckTranslated(TranslateExcept(shapedQueryExpression, innerShapedQueryExpression)); } break; diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/FiltersInheritanceBulkUpdatesTestBase.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/FiltersInheritanceBulkUpdatesTestBase.cs index 5e85a43743c..7011a2f92d2 100644 --- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/FiltersInheritanceBulkUpdatesTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/FiltersInheritanceBulkUpdatesTestBase.cs @@ -21,7 +21,7 @@ public virtual Task Delete_where_hierarchy(bool async) ss => ss.Set().Where(e => e.Name == "Great spotted kiwi"), rowsAffectedCount: 1); - [ConditionalTheory(Skip = "Issue#28524")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Delete_where_hierarchy_subquery(bool async) => AssertDelete( @@ -53,6 +53,35 @@ public virtual Task Delete_where_using_hierarchy_derived(bool async) ss => ss.Set().Where(e => e.Animals.OfType().Where(a => a.CountryId > 0).Count() > 0), rowsAffectedCount: 1); + [ConditionalTheory(Skip = "Issue#28525")] + [MemberData(nameof(IsAsyncData))] + public virtual Task Delete_GroupBy_Where_Select_First(bool async) + => AssertDelete( + async, + ss => ss.Set() + .GroupBy(e => e.CountryId) + .Where(g => g.Count() < 3) + .Select(g => g.First()), + rowsAffectedCount: 1); + + [ConditionalTheory(Skip = "Issue#26753")] + [MemberData(nameof(IsAsyncData))] + public virtual Task Delete_GroupBy_Where_Select_First_2(bool async) + => AssertDelete( + async, + ss => ss.Set().Where(e => e == ss.Set().GroupBy(e => e.CountryId) + .Where(g => g.Count() < 3).Select(g => g.First()).FirstOrDefault()), + rowsAffectedCount: 1); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Delete_GroupBy_Where_Select_First_3(bool async) + => AssertDelete( + async, + ss => ss.Set().Where(e => ss.Set().GroupBy(e => e.CountryId) + .Where(g => g.Count() < 3).Select(g => g.First()).Any(i => i == e)), + rowsAffectedCount: 1); + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Delete_where_keyless_entity_mapped_to_sql_query(bool async) diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/InheritanceBulkUpdatesFixtureBase.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/InheritanceBulkUpdatesFixtureBase.cs index 13a41526378..40227581efa 100644 --- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/InheritanceBulkUpdatesFixtureBase.cs +++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/InheritanceBulkUpdatesFixtureBase.cs @@ -7,6 +7,9 @@ public abstract class InheritanceBulkUpdatesFixtureBase : InheritanceQueryFixtur { protected override string StoreName => "InheritanceBulkUpdatesTest"; + public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder) + => base.AddOptions(builder).ConfigureWarnings(w => w.Log(CoreEventId.FirstWithoutOrderByAndFilterWarning)); + public void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction) => facade.UseTransaction(transaction.GetDbTransaction()); } diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/InheritanceBulkUpdatesTestBase.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/InheritanceBulkUpdatesTestBase.cs index 4b491e425d7..7b90eb47c40 100644 --- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/InheritanceBulkUpdatesTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/InheritanceBulkUpdatesTestBase.cs @@ -21,7 +21,7 @@ public virtual Task Delete_where_hierarchy(bool async) ss => ss.Set().Where(e => e.Name == "Great spotted kiwi"), rowsAffectedCount: 1); - [ConditionalTheory(Skip = "Issue#28524")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Delete_where_hierarchy_subquery(bool async) => AssertDelete( @@ -53,6 +53,35 @@ public virtual Task Delete_where_using_hierarchy_derived(bool async) ss => ss.Set().Where(e => e.Animals.OfType().Where(a => a.CountryId > 0).Count() > 0), rowsAffectedCount: 1); + [ConditionalTheory(Skip = "Issue#28525")] + [MemberData(nameof(IsAsyncData))] + public virtual Task Delete_GroupBy_Where_Select_First(bool async) + => AssertDelete( + async, + ss => ss.Set() + .GroupBy(e => e.CountryId) + .Where( g=> g.Count() < 3) + .Select(g => g.First()), + rowsAffectedCount: 2); + + [ConditionalTheory(Skip = "Issue#26753")] + [MemberData(nameof(IsAsyncData))] + public virtual Task Delete_GroupBy_Where_Select_First_2(bool async) + => AssertDelete( + async, + ss => ss.Set().Where(e => e == ss.Set().GroupBy(e => e.CountryId) + .Where(g => g.Count() < 3).Select(g => g.First()).FirstOrDefault()), + rowsAffectedCount: 2); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Delete_GroupBy_Where_Select_First_3(bool async) + => AssertDelete( + async, + ss => ss.Set().Where(e => ss.Set().GroupBy(e => e.CountryId) + .Where(g => g.Count() < 3).Select(g => g.First()).Any(i => i == e)), + rowsAffectedCount: 2); + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Delete_where_keyless_entity_mapped_to_sql_query(bool async) diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesTestBase.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesTestBase.cs index dc8d5cca556..e82e1aadbc2 100644 --- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesTestBase.cs @@ -109,7 +109,7 @@ public virtual Task Delete_Where_predicate_with_GroupBy_aggregate(bool async) .Select(g => g.First()).First().OrderID), rowsAffectedCount: 284); - [ConditionalTheory(Skip = "Issue#28524")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Delete_Where_predicate_with_GroupBy_aggregate_2(bool async) => AssertDelete( @@ -119,7 +119,7 @@ public virtual Task Delete_Where_predicate_with_GroupBy_aggregate_2(bool async) .GroupBy(o => o.CustomerID) .Where(g => g.Count() > 9) .Select(g => g.First()).Contains(e.Order)), - rowsAffectedCount: 40); + rowsAffectedCount: 109); [ConditionalTheory(Skip = "Issue#28525")] [MemberData(nameof(IsAsyncData))] @@ -132,6 +132,15 @@ public virtual Task Delete_GroupBy_Where_Select(bool async) .Select(g => g.First()), rowsAffectedCount: 284); + [ConditionalTheory(Skip = "Issue#26753")] + [MemberData(nameof(IsAsyncData))] + public virtual Task Delete_GroupBy_Where_Select_2(bool async) + => AssertDelete( + async, + ss => ss.Set() + .Where(od => od == ss.Set().GroupBy(od => od.OrderID).Where(g => g.Count() > 5).Select(g => g.First()).FirstOrDefault()), + rowsAffectedCount: 284); + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Delete_Where_Skip_Take_Skip_Take_causing_subquery(bool async) @@ -516,7 +525,7 @@ public virtual Task Update_Where_GroupBy_First_set_constant_2(bool async) rowsAffectedCount: 1, (b, a) => Assert.All(a, c => Assert.Equal("Updated", c.ContactName))); - [ConditionalTheory(Skip = "Issue#28524")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Update_Where_GroupBy_First_set_constant_3(bool async) => AssertUpdate( @@ -526,7 +535,7 @@ public virtual Task Update_Where_GroupBy_First_set_constant_3(bool async) .GroupBy(e => e.CustomerID).Where(g => g.Count() > 11).Select(e => e.First().Customer).Contains(c)), e => e, s => s.SetProperty(c => c.ContactName, c => "Updated"), - rowsAffectedCount: 1, + rowsAffectedCount: 24, (b, a) => Assert.All(a, c => Assert.Equal("Updated", c.ContactName))); [ConditionalTheory] @@ -869,7 +878,7 @@ public virtual Task Update_Where_SelectMany_subquery_set_null(bool async) rowsAffectedCount: 35, (b, a) => Assert.All(a, c => Assert.Null(c.OrderDate))); - [ConditionalTheory(Skip = "Issue#28752")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Update_Where_Join_set_property_from_joined_single_result_table(bool async) => AssertUpdate( @@ -877,9 +886,19 @@ public virtual Task Update_Where_Join_set_property_from_joined_single_result_tab ss => from c in ss.Set().Where(c => c.CustomerID.StartsWith("F")) select new { c, LastOrder = c.Orders.OrderByDescending(o => o.OrderDate).FirstOrDefault() }, e => e.c, - s => s.SetProperty(c => c.c.City, c => c.LastOrder.OrderDate.ToString()), - rowsAffectedCount: 35, - (b, a) => Assert.All(a, c => Assert.NotNull(c.City))); + s => s.SetProperty(c => c.c.City, c => c.LastOrder.OrderDate.Value.Year.ToString()), + rowsAffectedCount: 8, + (b, a) => Assert.All(a, c => + { + if (c.CustomerID == "FISSA") + { + Assert.Null(c.City); + } + else + { + Assert.NotNull(c.City); + } + })); [ConditionalTheory] [MemberData(nameof(IsAsyncData))] diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesTestBase.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesTestBase.cs index cbab0c39ede..bf4c657b7ec 100644 --- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesTestBase.cs @@ -19,6 +19,16 @@ public override Task Delete_where_hierarchy(bool async) RelationalStrings.ExecuteOperationOnTPC("ExecuteDelete", "Animal"), () => base.Delete_where_hierarchy(async)); + public override Task Delete_where_hierarchy_subquery(bool async) + => AssertTranslationFailed( + RelationalStrings.ExecuteOperationOnTPC("ExecuteDelete", "Animal"), + () => base.Delete_where_hierarchy_subquery(async)); + + public override Task Delete_GroupBy_Where_Select_First_3(bool async) + => AssertTranslationFailed( + RelationalStrings.ExecuteOperationOnTPC("ExecuteDelete", "Animal"), + () => base.Delete_GroupBy_Where_Select_First_3(async)); + // Keyless entities are mapped as TPH only public override Task Update_where_keyless_entity_mapped_to_sql_query(bool async) => Task.CompletedTask; diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPCInheritanceBulkUpdatesTestBase.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPCInheritanceBulkUpdatesTestBase.cs index 1091866fa79..f1d2a57c877 100644 --- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPCInheritanceBulkUpdatesTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPCInheritanceBulkUpdatesTestBase.cs @@ -19,6 +19,16 @@ public override Task Delete_where_hierarchy(bool async) RelationalStrings.ExecuteOperationOnTPC("ExecuteDelete", "Animal"), () => base.Delete_where_hierarchy(async)); + public override Task Delete_where_hierarchy_subquery(bool async) + => AssertTranslationFailed( + RelationalStrings.ExecuteOperationOnTPC("ExecuteDelete", "Animal"), + () => base.Delete_where_hierarchy_subquery(async)); + + public override Task Delete_GroupBy_Where_Select_First_3(bool async) + => AssertTranslationFailed( + RelationalStrings.ExecuteOperationOnTPC("ExecuteDelete", "Animal"), + () => base.Delete_GroupBy_Where_Select_First_3(async)); + // Keyless entities are mapped as TPH only public override Task Update_where_keyless_entity_mapped_to_sql_query(bool async) => Task.CompletedTask; diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesTestBase.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesTestBase.cs index 9e144a3b6a1..8eb6cc01c08 100644 --- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesTestBase.cs @@ -19,11 +19,21 @@ public override Task Delete_where_hierarchy(bool async) RelationalStrings.ExecuteOperationOnTPT("ExecuteDelete", "Animal"), () => base.Delete_where_hierarchy(async)); + public override Task Delete_where_hierarchy_subquery(bool async) + => AssertTranslationFailed( + RelationalStrings.ExecuteOperationOnTPT("ExecuteDelete", "Animal"), + () => base.Delete_where_hierarchy_subquery(async)); + public override Task Delete_where_hierarchy_derived(bool async) => AssertTranslationFailed( RelationalStrings.ExecuteOperationOnTPT("ExecuteDelete", "Kiwi"), () => base.Delete_where_hierarchy_derived(async)); + public override Task Delete_GroupBy_Where_Select_First_3(bool async) + => AssertTranslationFailed( + RelationalStrings.ExecuteOperationOnTPT("ExecuteDelete", "Animal"), + () => base.Delete_GroupBy_Where_Select_First_3(async)); + [ConditionalTheory(Skip = "Issue#28532")] public override Task Delete_where_using_hierarchy(bool async) { diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPTInheritanceBulkUpdatesTestBase.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPTInheritanceBulkUpdatesTestBase.cs index 3d97ec4d2e4..7c6b53d8ca1 100644 --- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPTInheritanceBulkUpdatesTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/TPTInheritanceBulkUpdatesTestBase.cs @@ -19,11 +19,21 @@ public override Task Delete_where_hierarchy(bool async) RelationalStrings.ExecuteOperationOnTPT("ExecuteDelete", "Animal"), () => base.Delete_where_hierarchy(async)); + public override Task Delete_where_hierarchy_subquery(bool async) + => AssertTranslationFailed( + RelationalStrings.ExecuteOperationOnTPT("ExecuteDelete", "Animal"), + () => base.Delete_where_hierarchy_subquery(async)); + public override Task Delete_where_hierarchy_derived(bool async) => AssertTranslationFailed( RelationalStrings.ExecuteOperationOnTPT("ExecuteDelete", "Kiwi"), () => base.Delete_where_hierarchy_derived(async)); + public override Task Delete_GroupBy_Where_Select_First_3(bool async) + => AssertTranslationFailed( + RelationalStrings.ExecuteOperationOnTPT("ExecuteDelete", "Animal"), + () => base.Delete_GroupBy_Where_Select_First_3(async)); + [ConditionalTheory(Skip = "FK constraint issue")] public override Task Delete_where_using_hierarchy(bool async) { diff --git a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/FiltersInheritanceBulkUpdatesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/FiltersInheritanceBulkUpdatesSqlServerTest.cs index b5dc9ff2ce6..3f03db1c022 100644 --- a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/FiltersInheritanceBulkUpdatesSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/FiltersInheritanceBulkUpdatesSqlServerTest.cs @@ -61,6 +61,38 @@ FROM [Animals] AS [a] WHERE [a].[CountryId] = 1 AND [c].[Id] = [a].[CountryId] AND [a].[Discriminator] = N'Kiwi' AND [a].[CountryId] > 0) > 0"); } + public override async Task Delete_GroupBy_Where_Select_First(bool async) + { + await base.Delete_GroupBy_Where_Select_First(async); + + AssertSql(); + } + + public override async Task Delete_GroupBy_Where_Select_First_2(bool async) + { + await base.Delete_GroupBy_Where_Select_First_2(async); + + AssertSql(); + } + + public override async Task Delete_GroupBy_Where_Select_First_3(bool async) + { + await base.Delete_GroupBy_Where_Select_First_3(async); + + AssertSql( + @"DELETE FROM [a] +FROM [Animals] AS [a] +WHERE [a].[CountryId] = 1 AND EXISTS ( + SELECT 1 + FROM [Animals] AS [a0] + WHERE [a0].[CountryId] = 1 + GROUP BY [a0].[CountryId] + HAVING COUNT(*) < 3 AND ( + SELECT TOP(1) [a1].[Id] + FROM [Animals] AS [a1] + WHERE [a1].[CountryId] = 1 AND [a0].[CountryId] = [a1].[CountryId]) = [a].[Id])"); + } + public override async Task Delete_where_keyless_entity_mapped_to_sql_query(bool async) { await base.Delete_where_keyless_entity_mapped_to_sql_query(async); @@ -72,7 +104,22 @@ public override async Task Delete_where_hierarchy_subquery(bool async) { await base.Delete_where_hierarchy_subquery(async); - AssertSql(); + AssertSql( + @"@__p_0='0' +@__p_1='3' + +DELETE FROM [a] +FROM [Animals] AS [a] +WHERE EXISTS ( + SELECT 1 + FROM ( + SELECT [a0].[Id], [a0].[CountryId], [a0].[Discriminator], [a0].[Name], [a0].[Species], [a0].[EagleId], [a0].[IsFlightless], [a0].[Group], [a0].[FoundOn] + FROM [Animals] AS [a0] + WHERE [a0].[CountryId] = 1 AND [a0].[Name] = N'Great spotted kiwi' + ORDER BY [a0].[Name] + OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY + ) AS [t] + WHERE [t].[Id] = [a].[Id])"); } public override async Task Update_where_hierarchy(bool async) diff --git a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/InheritanceBulkUpdatesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/InheritanceBulkUpdatesSqlServerTest.cs index 19086fa182a..b46afb9201d 100644 --- a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/InheritanceBulkUpdatesSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/InheritanceBulkUpdatesSqlServerTest.cs @@ -61,6 +61,37 @@ FROM [Animals] AS [a] WHERE [c].[Id] = [a].[CountryId] AND [a].[Discriminator] = N'Kiwi' AND [a].[CountryId] > 0) > 0"); } + public override async Task Delete_GroupBy_Where_Select_First(bool async) + { + await base.Delete_GroupBy_Where_Select_First(async); + + AssertSql(); + } + + public override async Task Delete_GroupBy_Where_Select_First_2(bool async) + { + await base.Delete_GroupBy_Where_Select_First_2(async); + + AssertSql(); + } + + public override async Task Delete_GroupBy_Where_Select_First_3(bool async) + { + await base.Delete_GroupBy_Where_Select_First_3(async); + + AssertSql( + @"DELETE FROM [a] +FROM [Animals] AS [a] +WHERE EXISTS ( + SELECT 1 + FROM [Animals] AS [a0] + GROUP BY [a0].[CountryId] + HAVING COUNT(*) < 3 AND ( + SELECT TOP(1) [a1].[Id] + FROM [Animals] AS [a1] + WHERE [a0].[CountryId] = [a1].[CountryId]) = [a].[Id])"); + } + public override async Task Delete_where_keyless_entity_mapped_to_sql_query(bool async) { await base.Delete_where_keyless_entity_mapped_to_sql_query(async); @@ -72,7 +103,22 @@ public override async Task Delete_where_hierarchy_subquery(bool async) { await base.Delete_where_hierarchy_subquery(async); - AssertSql(); + AssertSql( + @"@__p_0='0' +@__p_1='3' + +DELETE FROM [a] +FROM [Animals] AS [a] +WHERE EXISTS ( + SELECT 1 + FROM ( + SELECT [a0].[Id], [a0].[CountryId], [a0].[Discriminator], [a0].[Name], [a0].[Species], [a0].[EagleId], [a0].[IsFlightless], [a0].[Group], [a0].[FoundOn] + FROM [Animals] AS [a0] + WHERE [a0].[Name] = N'Great spotted kiwi' + ORDER BY [a0].[Name] + OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY + ) AS [t] + WHERE [t].[Id] = [a].[Id])"); } public override async Task Update_where_hierarchy(bool async) diff --git a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqlServerTest.cs index 3a95f571010..62b53752d38 100644 --- a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqlServerTest.cs @@ -190,7 +190,18 @@ public override async Task Delete_Where_predicate_with_GroupBy_aggregate_2(bool { await base.Delete_Where_predicate_with_GroupBy_aggregate_2(async); - AssertSql(); + AssertSql( + @"DELETE FROM [o] +FROM [Order Details] AS [o] +INNER JOIN [Orders] AS [o0] ON [o].[OrderID] = [o0].[OrderID] +WHERE EXISTS ( + SELECT 1 + FROM [Orders] AS [o1] + GROUP BY [o1].[CustomerID] + HAVING COUNT(*) > 9 AND ( + SELECT TOP(1) [o2].[OrderID] + FROM [Orders] AS [o2] + WHERE [o1].[CustomerID] = [o2].[CustomerID] OR ([o1].[CustomerID] IS NULL AND [o2].[CustomerID] IS NULL)) = [o0].[OrderID])"); } public override async Task Delete_GroupBy_Where_Select(bool async) @@ -200,6 +211,13 @@ public override async Task Delete_GroupBy_Where_Select(bool async) AssertSql(); } + public override async Task Delete_GroupBy_Where_Select_2(bool async) + { + await base.Delete_GroupBy_Where_Select_2(async); + + AssertSql(); + } + public override async Task Delete_Where_Skip_Take_Skip_Take_causing_subquery(bool async) { await base.Delete_Where_Skip_Take_Skip_Take_causing_subquery(async); @@ -759,7 +777,19 @@ public override async Task Update_Where_GroupBy_First_set_constant_3(bool async) { await base.Update_Where_GroupBy_First_set_constant_3(async); - AssertExecuteUpdateSql(); + AssertExecuteUpdateSql( + @"UPDATE [c] + SET [c].[ContactName] = N'Updated' +FROM [Customers] AS [c] +WHERE EXISTS ( + SELECT 1 + FROM [Orders] AS [o] + GROUP BY [o].[CustomerID] + HAVING COUNT(*) > 11 AND ( + SELECT TOP(1) [c0].[CustomerID] + FROM [Orders] AS [o0] + LEFT JOIN [Customers] AS [c0] ON [o0].[CustomerID] = [c0].[CustomerID] + WHERE [o].[CustomerID] = [o0].[CustomerID] OR ([o].[CustomerID] IS NULL AND [o0].[CustomerID] IS NULL)) = [c].[CustomerID])"); } public override async Task Update_Where_Distinct_set_constant(bool async) @@ -1103,7 +1133,15 @@ public override async Task Update_Where_Join_set_property_from_joined_single_res { await base.Update_Where_Join_set_property_from_joined_single_result_table(async); - AssertExecuteUpdateSql(); + AssertExecuteUpdateSql( + @"UPDATE [c] + SET [c].[City] = CONVERT(varchar(11), DATEPART(year, ( + SELECT TOP(1) [o].[OrderDate] + FROM [Orders] AS [o] + WHERE [c].[CustomerID] = [o].[CustomerID] + ORDER BY [o].[OrderDate] DESC))) +FROM [Customers] AS [c] +WHERE [c].[CustomerID] LIKE N'F%'"); } public override async Task Update_Where_Join_set_property_from_joined_table(bool async) diff --git a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesSqlServerTest.cs index 1aff954c2aa..2599e995da5 100644 --- a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesSqlServerTest.cs @@ -81,6 +81,27 @@ public override async Task Delete_where_hierarchy_subquery(bool async) AssertSql(); } + public override async Task Delete_GroupBy_Where_Select_First(bool async) + { + await base.Delete_GroupBy_Where_Select_First(async); + + AssertSql(); + } + + public override async Task Delete_GroupBy_Where_Select_First_2(bool async) + { + await base.Delete_GroupBy_Where_Select_First_2(async); + + AssertSql(); + } + + public override async Task Delete_GroupBy_Where_Select_First_3(bool async) + { + await base.Delete_GroupBy_Where_Select_First_3(async); + + AssertSql(); + } + public override async Task Update_where_hierarchy(bool async) { await base.Update_where_hierarchy(async); diff --git a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPCInheritanceBulkUpdatesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPCInheritanceBulkUpdatesSqlServerTest.cs index 75e7110f4c4..071d1b78c3e 100644 --- a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPCInheritanceBulkUpdatesSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPCInheritanceBulkUpdatesSqlServerTest.cs @@ -81,6 +81,27 @@ public override async Task Delete_where_hierarchy_subquery(bool async) AssertSql(); } + public override async Task Delete_GroupBy_Where_Select_First(bool async) + { + await base.Delete_GroupBy_Where_Select_First(async); + + AssertSql(); + } + + public override async Task Delete_GroupBy_Where_Select_First_2(bool async) + { + await base.Delete_GroupBy_Where_Select_First_2(async); + + AssertSql(); + } + + public override async Task Delete_GroupBy_Where_Select_First_3(bool async) + { + await base.Delete_GroupBy_Where_Select_First_3(async); + + AssertSql(); + } + public override async Task Update_where_hierarchy(bool async) { await base.Update_where_hierarchy(async); diff --git a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesSqlServerTest.cs index b07f2686746..d11df1e626e 100644 --- a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesSqlServerTest.cs @@ -69,6 +69,27 @@ public override async Task Delete_where_hierarchy_subquery(bool async) AssertSql(); } + public override async Task Delete_GroupBy_Where_Select_First(bool async) + { + await base.Delete_GroupBy_Where_Select_First(async); + + AssertSql(); + } + + public override async Task Delete_GroupBy_Where_Select_First_2(bool async) + { + await base.Delete_GroupBy_Where_Select_First_2(async); + + AssertSql(); + } + + public override async Task Delete_GroupBy_Where_Select_First_3(bool async) + { + await base.Delete_GroupBy_Where_Select_First_3(async); + + AssertSql(); + } + public override async Task Update_where_hierarchy(bool async) { await base.Update_where_hierarchy(async); diff --git a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPTInheritanceBulkUpdatesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPTInheritanceBulkUpdatesSqlServerTest.cs index 91ce894d821..b9b523680fd 100644 --- a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPTInheritanceBulkUpdatesSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/TPTInheritanceBulkUpdatesSqlServerTest.cs @@ -57,6 +57,27 @@ public override async Task Delete_where_hierarchy_subquery(bool async) AssertSql(); } + public override async Task Delete_GroupBy_Where_Select_First(bool async) + { + await base.Delete_GroupBy_Where_Select_First(async); + + AssertSql(); + } + + public override async Task Delete_GroupBy_Where_Select_First_2(bool async) + { + await base.Delete_GroupBy_Where_Select_First_2(async); + + AssertSql(); + } + + public override async Task Delete_GroupBy_Where_Select_First_3(bool async) + { + await base.Delete_GroupBy_Where_Select_First_3(async); + + AssertSql(); + } + public override async Task Update_where_hierarchy(bool async) { await base.Update_where_hierarchy(async); diff --git a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/FiltersInheritanceBulkUpdatesSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/FiltersInheritanceBulkUpdatesSqliteTest.cs index 362f5e7b3e1..dcdddef09ce 100644 --- a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/FiltersInheritanceBulkUpdatesSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/FiltersInheritanceBulkUpdatesSqliteTest.cs @@ -28,7 +28,21 @@ public override async Task Delete_where_hierarchy_subquery(bool async) { await base.Delete_where_hierarchy_subquery(async); - AssertSql(); + AssertSql( + @"@__p_1='3' +@__p_0='0' + +DELETE FROM ""Animals"" AS ""a"" +WHERE EXISTS ( + SELECT 1 + FROM ( + SELECT ""a0"".""Id"", ""a0"".""CountryId"", ""a0"".""Discriminator"", ""a0"".""Name"", ""a0"".""Species"", ""a0"".""EagleId"", ""a0"".""IsFlightless"", ""a0"".""Group"", ""a0"".""FoundOn"" + FROM ""Animals"" AS ""a0"" + WHERE ""a0"".""CountryId"" = 1 AND ""a0"".""Name"" = 'Great spotted kiwi' + ORDER BY ""a0"".""Name"" + LIMIT @__p_1 OFFSET @__p_0 + ) AS ""t"" + WHERE ""t"".""Id"" = ""a"".""Id"")"); } public override async Task Delete_where_hierarchy_derived(bool async) @@ -71,6 +85,38 @@ public override async Task Delete_where_keyless_entity_mapped_to_sql_query(bool AssertSql(); } + public override async Task Delete_GroupBy_Where_Select_First(bool async) + { + await base.Delete_GroupBy_Where_Select_First(async); + + AssertSql(); + } + + public override async Task Delete_GroupBy_Where_Select_First_2(bool async) + { + await base.Delete_GroupBy_Where_Select_First_2(async); + + AssertSql(); + } + + public override async Task Delete_GroupBy_Where_Select_First_3(bool async) + { + await base.Delete_GroupBy_Where_Select_First_3(async); + + AssertSql( + @"DELETE FROM ""Animals"" AS ""a"" +WHERE ""a"".""CountryId"" = 1 AND EXISTS ( + SELECT 1 + FROM ""Animals"" AS ""a0"" + WHERE ""a0"".""CountryId"" = 1 + GROUP BY ""a0"".""CountryId"" + HAVING COUNT(*) < 3 AND ( + SELECT ""a1"".""Id"" + FROM ""Animals"" AS ""a1"" + WHERE ""a1"".""CountryId"" = 1 AND ""a0"".""CountryId"" = ""a1"".""CountryId"" + LIMIT 1) = ""a"".""Id"")"); + } + public override async Task Update_where_hierarchy(bool async) { await base.Update_where_hierarchy(async); diff --git a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/InheritanceBulkUpdatesSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/InheritanceBulkUpdatesSqliteTest.cs index 15da6f75101..b8c70262b20 100644 --- a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/InheritanceBulkUpdatesSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/InheritanceBulkUpdatesSqliteTest.cs @@ -28,7 +28,21 @@ public override async Task Delete_where_hierarchy_subquery(bool async) { await base.Delete_where_hierarchy_subquery(async); - AssertSql(); + AssertSql( + @"@__p_1='3' +@__p_0='0' + +DELETE FROM ""Animals"" AS ""a"" +WHERE EXISTS ( + SELECT 1 + FROM ( + SELECT ""a0"".""Id"", ""a0"".""CountryId"", ""a0"".""Discriminator"", ""a0"".""Name"", ""a0"".""Species"", ""a0"".""EagleId"", ""a0"".""IsFlightless"", ""a0"".""Group"", ""a0"".""FoundOn"" + FROM ""Animals"" AS ""a0"" + WHERE ""a0"".""Name"" = 'Great spotted kiwi' + ORDER BY ""a0"".""Name"" + LIMIT @__p_1 OFFSET @__p_0 + ) AS ""t"" + WHERE ""t"".""Id"" = ""a"".""Id"")"); } public override async Task Delete_where_hierarchy_derived(bool async) @@ -71,6 +85,37 @@ public override async Task Delete_where_keyless_entity_mapped_to_sql_query(bool AssertSql(); } + public override async Task Delete_GroupBy_Where_Select_First(bool async) + { + await base.Delete_GroupBy_Where_Select_First(async); + + AssertSql(); + } + + public override async Task Delete_GroupBy_Where_Select_First_2(bool async) + { + await base.Delete_GroupBy_Where_Select_First_2(async); + + AssertSql(); + } + + public override async Task Delete_GroupBy_Where_Select_First_3(bool async) + { + await base.Delete_GroupBy_Where_Select_First_3(async); + + AssertSql( + @"DELETE FROM ""Animals"" AS ""a"" +WHERE EXISTS ( + SELECT 1 + FROM ""Animals"" AS ""a0"" + GROUP BY ""a0"".""CountryId"" + HAVING COUNT(*) < 3 AND ( + SELECT ""a1"".""Id"" + FROM ""Animals"" AS ""a1"" + WHERE ""a0"".""CountryId"" = ""a1"".""CountryId"" + LIMIT 1) = ""a"".""Id"")"); + } + public override async Task Update_where_hierarchy(bool async) { await base.Update_where_hierarchy(async); diff --git a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqliteTest.cs index dd85bb0a8b2..7b7813af545 100644 --- a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqliteTest.cs @@ -190,7 +190,21 @@ public override async Task Delete_Where_predicate_with_GroupBy_aggregate_2(bool { await base.Delete_Where_predicate_with_GroupBy_aggregate_2(async); - AssertSql(); + AssertSql( + @"DELETE FROM ""Order Details"" AS ""o"" +WHERE EXISTS ( + SELECT 1 + FROM ""Order Details"" AS ""o0"" + INNER JOIN ""Orders"" AS ""o1"" ON ""o0"".""OrderID"" = ""o1"".""OrderID"" + WHERE EXISTS ( + SELECT 1 + FROM ""Orders"" AS ""o2"" + GROUP BY ""o2"".""CustomerID"" + HAVING COUNT(*) > 9 AND ( + SELECT ""o3"".""OrderID"" + FROM ""Orders"" AS ""o3"" + WHERE ""o2"".""CustomerID"" = ""o3"".""CustomerID"" OR (""o2"".""CustomerID"" IS NULL AND ""o3"".""CustomerID"" IS NULL) + LIMIT 1) = ""o1"".""OrderID"") AND ""o0"".""OrderID"" = ""o"".""OrderID"" AND ""o0"".""ProductID"" = ""o"".""ProductID"")"); } public override async Task Delete_GroupBy_Where_Select(bool async) @@ -200,6 +214,13 @@ public override async Task Delete_GroupBy_Where_Select(bool async) AssertSql(); } + public override async Task Delete_GroupBy_Where_Select_2(bool async) + { + await base.Delete_GroupBy_Where_Select_2(async); + + AssertSql(); + } + public override async Task Delete_Where_Skip_Take_Skip_Take_causing_subquery(bool async) { await base.Delete_Where_Skip_Take_Skip_Take_causing_subquery(async); @@ -741,7 +762,19 @@ public override async Task Update_Where_GroupBy_First_set_constant_3(bool async) { await base.Update_Where_GroupBy_First_set_constant_3(async); - AssertExecuteUpdateSql(); + AssertExecuteUpdateSql( + @"UPDATE ""Customers"" AS ""c"" + SET ""ContactName"" = 'Updated' +WHERE EXISTS ( + SELECT 1 + FROM ""Orders"" AS ""o"" + GROUP BY ""o"".""CustomerID"" + HAVING COUNT(*) > 11 AND ( + SELECT ""c0"".""CustomerID"" + FROM ""Orders"" AS ""o0"" + LEFT JOIN ""Customers"" AS ""c0"" ON ""o0"".""CustomerID"" = ""c0"".""CustomerID"" + WHERE ""o"".""CustomerID"" = ""o0"".""CustomerID"" OR (""o"".""CustomerID"" IS NULL AND ""o0"".""CustomerID"" IS NULL) + LIMIT 1) = ""c"".""CustomerID"")"); } public override async Task Update_Where_Distinct_set_constant(bool async) @@ -1065,7 +1098,15 @@ public override async Task Update_Where_Join_set_property_from_joined_single_res { await base.Update_Where_Join_set_property_from_joined_single_result_table(async); - AssertExecuteUpdateSql(); + AssertExecuteUpdateSql( + @"UPDATE ""Customers"" AS ""c"" + SET ""City"" = CAST(CAST(strftime('%Y', ( + SELECT ""o"".""OrderDate"" + FROM ""Orders"" AS ""o"" + WHERE ""c"".""CustomerID"" = ""o"".""CustomerID"" + ORDER BY ""o"".""OrderDate"" DESC + LIMIT 1)) AS INTEGER) AS TEXT) +WHERE ""c"".""CustomerID"" LIKE 'F%'"); } public override async Task Update_Where_Join_set_property_from_joined_table(bool async) diff --git a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesSqliteTest.cs index 5ff49896ae8..8124cee18f7 100644 --- a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPCFiltersInheritanceBulkUpdatesSqliteTest.cs @@ -78,6 +78,27 @@ public override async Task Delete_where_hierarchy_subquery(bool async) AssertSql(); } + public override async Task Delete_GroupBy_Where_Select_First(bool async) + { + await base.Delete_GroupBy_Where_Select_First(async); + + AssertSql(); + } + + public override async Task Delete_GroupBy_Where_Select_First_2(bool async) + { + await base.Delete_GroupBy_Where_Select_First_2(async); + + AssertSql(); + } + + public override async Task Delete_GroupBy_Where_Select_First_3(bool async) + { + await base.Delete_GroupBy_Where_Select_First_3(async); + + AssertSql(); + } + public override async Task Update_where_hierarchy(bool async) { await base.Update_where_hierarchy(async); diff --git a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPCInheritanceBulkUpdatesSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPCInheritanceBulkUpdatesSqliteTest.cs index 701c9f8435c..f723adff3d3 100644 --- a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPCInheritanceBulkUpdatesSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPCInheritanceBulkUpdatesSqliteTest.cs @@ -78,6 +78,27 @@ public override async Task Delete_where_hierarchy_subquery(bool async) AssertSql(); } + public override async Task Delete_GroupBy_Where_Select_First(bool async) + { + await base.Delete_GroupBy_Where_Select_First(async); + + AssertSql(); + } + + public override async Task Delete_GroupBy_Where_Select_First_2(bool async) + { + await base.Delete_GroupBy_Where_Select_First_2(async); + + AssertSql(); + } + + public override async Task Delete_GroupBy_Where_Select_First_3(bool async) + { + await base.Delete_GroupBy_Where_Select_First_3(async); + + AssertSql(); + } + public override async Task Update_where_hierarchy(bool async) { await base.Update_where_hierarchy(async); diff --git a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesSqliteTest.cs index 162ac651920..c19e0492324 100644 --- a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPTFiltersInheritanceBulkUpdatesSqliteTest.cs @@ -69,6 +69,27 @@ public override async Task Delete_where_hierarchy_subquery(bool async) AssertSql(); } + public override async Task Delete_GroupBy_Where_Select_First(bool async) + { + await base.Delete_GroupBy_Where_Select_First(async); + + AssertSql(); + } + + public override async Task Delete_GroupBy_Where_Select_First_2(bool async) + { + await base.Delete_GroupBy_Where_Select_First_2(async); + + AssertSql(); + } + + public override async Task Delete_GroupBy_Where_Select_First_3(bool async) + { + await base.Delete_GroupBy_Where_Select_First_3(async); + + AssertSql(); + } + public override async Task Update_where_hierarchy(bool async) { await base.Update_where_hierarchy(async); diff --git a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPTInheritanceBulkUpdatesSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPTInheritanceBulkUpdatesSqliteTest.cs index 6c276c2076f..7182b55da68 100644 --- a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPTInheritanceBulkUpdatesSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/TPTInheritanceBulkUpdatesSqliteTest.cs @@ -43,6 +43,27 @@ public override async Task Delete_where_using_hierarchy_derived(bool async) AssertSql(); } + public override async Task Delete_GroupBy_Where_Select_First(bool async) + { + await base.Delete_GroupBy_Where_Select_First(async); + + AssertSql(); + } + + public override async Task Delete_GroupBy_Where_Select_First_2(bool async) + { + await base.Delete_GroupBy_Where_Select_First_2(async); + + AssertSql(); + } + + public override async Task Delete_GroupBy_Where_Select_First_3(bool async) + { + await base.Delete_GroupBy_Where_Select_First_3(async); + + AssertSql(); + } + public override async Task Delete_where_keyless_entity_mapped_to_sql_query(bool async) { await base.Delete_where_keyless_entity_mapped_to_sql_query(async);