diff --git a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs index 5aec06691c9..7d13924e0e5 100644 --- a/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs +++ b/src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs @@ -2077,20 +2077,21 @@ void HandleStructuralTypeProjection( projection1.StructuralType.DisplayName(), projection2.StructuralType.DisplayName())); } - var propertyExpressions = new Dictionary(); - - ProcessStructuralType(projection1, projection2); + var resultProjection = ProcessStructuralType(projection1, projection2); + _projectionMapping[projectionMember] = resultProjection; - void ProcessStructuralType( - StructuralTypeProjectionExpression nestedProjection1, - StructuralTypeProjectionExpression nestedProjection2) + StructuralTypeProjectionExpression ProcessStructuralType( + StructuralTypeProjectionExpression structuralProjection1, + StructuralTypeProjectionExpression structuralProjection2) { - var type = nestedProjection1.StructuralType; + var propertyExpressions = new Dictionary(); + var complexPropertyCache = new Dictionary(); + var type = structuralProjection1.StructuralType; foreach (var property in type.GetAllPropertiesInHierarchy()) { - var column1 = nestedProjection1.BindProperty(property); - var column2 = nestedProjection2.BindProperty(property); + var column1 = structuralProjection1.BindProperty(property); + var column2 = structuralProjection2.BindProperty(property); var alias = GenerateUniqueColumnAlias(column1.Name); var innerProjection = new ProjectionExpression(column1, alias); select1._projection.Add(innerProjection); @@ -2127,7 +2128,7 @@ void ProcessStructuralType( // If the top-level projection - not the current nested one - is a complex type and not an entity type, then add // all its columns to the "otherExpressions" list (i.e. columns not part of a an entity primary key). This is // the same as with a non-structural type projection. - else if (projection1.StructuralType is IComplexType) + else if (type is IComplexType) { var outerTypeMapping = column1.TypeMapping ?? column1.TypeMapping; if (outerTypeMapping == null) @@ -2141,52 +2142,62 @@ void ProcessStructuralType( } } - foreach (var complexProperty in GetAllComplexPropertiesInHierarchy(nestedProjection1.StructuralType)) + foreach (var complexProperty in GetAllComplexPropertiesInHierarchy(type)) { - ProcessStructuralType( - (StructuralTypeProjectionExpression)nestedProjection1.BindComplexProperty(complexProperty).ValueBufferExpression, - (StructuralTypeProjectionExpression)nestedProjection2.BindComplexProperty(complexProperty).ValueBufferExpression); - } - } + var complexPropertyShaper1 = structuralProjection1.BindComplexProperty(complexProperty); + var complexPropertyShaper2 = structuralProjection2.BindComplexProperty(complexProperty); - Check.DebugAssert( - projection1.TableMap.Count == projection2.TableMap.Count, - "Set operation over entity projections with different table map counts"); - Check.DebugAssert( - projection1.TableMap.Keys.All(t => projection2.TableMap.ContainsKey(t)), - "Set operation over entity projections with table map discrepancy"); + var resultComplexProjection = ProcessStructuralType( + (StructuralTypeProjectionExpression)complexPropertyShaper1.ValueBufferExpression, + (StructuralTypeProjectionExpression)complexPropertyShaper2.ValueBufferExpression); - var tableMap = projection1.TableMap.ToDictionary(kvp => kvp.Key, _ => setOperationAlias); + var resultComplexShaper = new RelationalStructuralTypeShaperExpression( + complexProperty.ComplexType, + resultComplexProjection, + resultComplexProjection.IsNullable); - var discriminatorExpression = projection1.DiscriminatorExpression; - if (projection1.DiscriminatorExpression != null - && projection2.DiscriminatorExpression != null) - { - var alias = GenerateUniqueColumnAlias(DiscriminatorColumnAlias); - var innerProjection = new ProjectionExpression(projection1.DiscriminatorExpression, alias); - select1._projection.Add(innerProjection); - select2._projection.Add(new ProjectionExpression(projection2.DiscriminatorExpression, alias)); - discriminatorExpression = CreateColumnExpression(innerProjection, setOperationAlias); - } + complexPropertyCache[complexProperty] = resultComplexShaper; + } - var outerProjection = new StructuralTypeProjectionExpression( - projection1.StructuralType, propertyExpressions, tableMap, nullable: false, discriminatorExpression); + Check.DebugAssert( + structuralProjection1.TableMap.Count == structuralProjection2.TableMap.Count, + "Set operation over entity projections with different table map counts"); + Check.DebugAssert( + structuralProjection1.TableMap.Keys.All(t => structuralProjection2.TableMap.ContainsKey(t)), + "Set operation over entity projections with table map discrepancy"); - if (outerIdentifiers.Length > 0 && outerProjection is { StructuralType: IEntityType entityType }) - { - var primaryKey = entityType.FindPrimaryKey(); + var tableMap = projection1.TableMap.ToDictionary(kvp => kvp.Key, _ => setOperationAlias); - // We know that there are existing identifiers (see condition above); we know we must have a key since a keyless - // entity type would have wiped the identifiers when generating the join. - Check.DebugAssert(primaryKey != null, "primary key is null."); - foreach (var property in primaryKey.Properties) + var discriminatorExpression = structuralProjection1.DiscriminatorExpression; + if (structuralProjection1.DiscriminatorExpression != null + && structuralProjection2.DiscriminatorExpression != null) { - entityProjectionIdentifiers.Add(outerProjection.BindProperty(property)); - entityProjectionValueComparers.Add(property.GetKeyValueComparer()); + var alias = GenerateUniqueColumnAlias(DiscriminatorColumnAlias); + var innerProjection = new ProjectionExpression(structuralProjection1.DiscriminatorExpression, alias); + select1._projection.Add(innerProjection); + select2._projection.Add(new ProjectionExpression(structuralProjection2.DiscriminatorExpression, alias)); + discriminatorExpression = CreateColumnExpression(innerProjection, setOperationAlias); } - } - _projectionMapping[projectionMember] = outerProjection; + var outerProjection = new StructuralTypeProjectionExpression( + type, propertyExpressions, complexPropertyCache, tableMap, nullable: false, discriminatorExpression); + + if (outerIdentifiers.Length > 0 && outerProjection is { StructuralType: IEntityType entityType }) + { + var primaryKey = entityType.FindPrimaryKey(); + + // We know that there are existing identifiers (see condition above); we know we must have a key since a keyless + // entity type would have wiped the identifiers when generating the join. + Check.DebugAssert(primaryKey != null, "primary key is null."); + foreach (var property in primaryKey.Properties) + { + entityProjectionIdentifiers.Add(outerProjection.BindProperty(property)); + entityProjectionValueComparers.Add(property.GetKeyValueComparer()); + } + } + + return outerProjection; + } } string GenerateUniqueColumnAlias(string baseAlias) @@ -2560,10 +2571,10 @@ public static StructuralTypeShaperExpression GenerateComplexPropertyShaperExpres // We do not support complex type splitting, so we will only ever have a single table/view mapping to it. // See Issue #32853 and Issue #31248 var complexTypeTable = complexProperty.ComplexType.GetViewOrTableMappings().Single().Table; - if (!containerProjection.TableMap.TryGetValue(complexTypeTable, out var tableReferenceExpression)) + if (!containerProjection.TableMap.TryGetValue(complexTypeTable, out var tableAlias)) { complexTypeTable = complexProperty.ComplexType.GetDefaultMappings().Single().Table; - tableReferenceExpression = containerProjection.TableMap[complexTypeTable]; + tableAlias = containerProjection.TableMap[complexTypeTable]; } var isComplexTypeNullable = containerProjection.IsNullable || complexProperty.IsNullable; @@ -2581,14 +2592,14 @@ public static StructuralTypeShaperExpression GenerateComplexPropertyShaperExpres // TODO: Reimplement EntityProjectionExpression via TableMap, and then use that here var column = complexTypeTable.FindColumn(property)!; propertyExpressionMap[property] = CreateColumnExpression( - property, column, tableReferenceExpression, isComplexTypeNullable || column.IsNullable); + property, column, tableAlias, isComplexTypeNullable || column.IsNullable); } // The table map of the target complex type should only ever contains a single table (no table splitting). // If the source is itself a complex type (nested complex type), its table map is already suitable and we can just pass it on. var newTableMap = containerProjection.TableMap.Count == 1 ? containerProjection.TableMap - : new Dictionary { [complexTypeTable] = tableReferenceExpression }; + : new Dictionary { [complexTypeTable] = tableAlias }; Check.DebugAssert(newTableMap.Single().Key == complexTypeTable, "Bad new table map"); @@ -3530,33 +3541,35 @@ StructuralTypeProjectionExpression LiftEntityProjectionFromSubquery( string subqueryAlias) { var propertyExpressions = new Dictionary(); + var complexPropertyCache = new Dictionary(); - HandleTypeProjection(projection); - - void HandleTypeProjection(StructuralTypeProjectionExpression typeProjection) + foreach (var property in projection.StructuralType.GetAllPropertiesInHierarchy()) { - foreach (var property in typeProjection.StructuralType.GetAllPropertiesInHierarchy()) + // json entity projection (i.e. JSON entity that was transformed into query root) may have synthesized keys + // but they don't correspond to any columns - we need to skip those + if (projection is { StructuralType: IEntityType entityType } + && entityType.IsMappedToJson() + && property.IsOrdinalKeyProperty()) { - // json entity projection (i.e. JSON entity that was transformed into query root) may have synthesized keys - // but they don't correspond to any columns - we need to skip those - if (typeProjection is { StructuralType: IEntityType entityType } - && entityType.IsMappedToJson() - && property.IsOrdinalKeyProperty()) - { - continue; - } - - var innerColumn = typeProjection.BindProperty(property); - var outerColumn = subquery.GenerateOuterColumn(subqueryAlias, innerColumn); - projectionMap[innerColumn] = outerColumn; - propertyExpressions[property] = outerColumn; + continue; } - foreach (var complexProperty in GetAllComplexPropertiesInHierarchy(typeProjection.StructuralType)) - { - HandleTypeProjection( - (StructuralTypeProjectionExpression)typeProjection.BindComplexProperty(complexProperty).ValueBufferExpression); - } + var innerColumn = projection.BindProperty(property); + var outerColumn = subquery.GenerateOuterColumn(subqueryAlias, innerColumn); + + projectionMap[innerColumn] = outerColumn; + propertyExpressions[property] = outerColumn; + } + + foreach (var complexProperty in GetAllComplexPropertiesInHierarchy(projection.StructuralType)) + { + var complexPropertyShaper = projection.BindComplexProperty(complexProperty); + + var complexTypeProjectionExpression = LiftEntityProjectionFromSubquery( + (StructuralTypeProjectionExpression)complexPropertyShaper.ValueBufferExpression, + subqueryAlias); + + complexPropertyCache[complexProperty] = complexPropertyShaper.Update(complexTypeProjectionExpression); } ColumnExpression? discriminatorExpression = null; @@ -3570,7 +3583,7 @@ void HandleTypeProjection(StructuralTypeProjectionExpression typeProjection) var tableMap = projection.TableMap.ToDictionary(kvp => kvp.Key, _ => subqueryAlias); var newEntityProjection = new StructuralTypeProjectionExpression( - projection.StructuralType, propertyExpressions, tableMap, nullable: false, discriminatorExpression); + projection.StructuralType, propertyExpressions, complexPropertyCache, tableMap, nullable: false, discriminatorExpression); if (projection.StructuralType is IEntityType entityType2) { diff --git a/src/EFCore.Relational/Query/StructuralTypeProjectionExpression.cs b/src/EFCore.Relational/Query/StructuralTypeProjectionExpression.cs index de6b484dfa5..ff33d491091 100644 --- a/src/EFCore.Relational/Query/StructuralTypeProjectionExpression.cs +++ b/src/EFCore.Relational/Query/StructuralTypeProjectionExpression.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using Microsoft.EntityFrameworkCore.Query.Internal; using Microsoft.EntityFrameworkCore.Query.SqlExpressions; namespace Microsoft.EntityFrameworkCore.Query; @@ -37,7 +36,33 @@ public StructuralTypeProjectionExpression( : this( type, propertyExpressionMap, - new Dictionary(), + [], + null, + tableMap, + nullable, + discriminatorExpression) + { + } + + /// + /// 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 + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + [EntityFrameworkInternal] + public StructuralTypeProjectionExpression( + ITypeBase type, + IReadOnlyDictionary propertyExpressionMap, + Dictionary complexPropertyCache, + IReadOnlyDictionary tableMap, + bool nullable = false, + SqlExpression? discriminatorExpression = null) + : this( + type, + propertyExpressionMap, + [], + complexPropertyCache, tableMap, nullable, discriminatorExpression) @@ -48,6 +73,7 @@ private StructuralTypeProjectionExpression( ITypeBase type, IReadOnlyDictionary propertyExpressionMap, Dictionary ownedNavigationMap, + Dictionary? complexPropertyCache, IReadOnlyDictionary tableMap, bool nullable, SqlExpression? discriminatorExpression = null) @@ -55,6 +81,7 @@ private StructuralTypeProjectionExpression( StructuralType = type; _propertyExpressionMap = propertyExpressionMap; _ownedNavigationMap = ownedNavigationMap; + _complexPropertyCache = complexPropertyCache; TableMap = tableMap; IsNullable = nullable; DiscriminatorExpression = discriminatorExpression; @@ -124,6 +151,18 @@ protected override Expression VisitChildren(ExpressionVisitor visitor) propertyExpressionMap[property] = newExpression; } + var complexPropertyCache = default(Dictionary); + if (_complexPropertyCache != null) + { + complexPropertyCache = new(); + foreach (var (complexProperty, complexShaper) in _complexPropertyCache) + { + var newComplexShaper = (StructuralTypeShaperExpression)visitor.Visit(complexShaper); + changed |= complexShaper != newComplexShaper; + complexPropertyCache[complexProperty] = newComplexShaper; + } + } + var discriminatorExpression = (SqlExpression?)visitor.Visit(DiscriminatorExpression); changed |= discriminatorExpression != DiscriminatorExpression; @@ -137,7 +176,7 @@ protected override Expression VisitChildren(ExpressionVisitor visitor) return changed ? new StructuralTypeProjectionExpression( - StructuralType, propertyExpressionMap, ownedNavigationMap, TableMap, IsNullable, discriminatorExpression) + StructuralType, propertyExpressionMap, ownedNavigationMap, complexPropertyCache, TableMap, IsNullable, discriminatorExpression) : this; } @@ -153,6 +192,16 @@ public virtual StructuralTypeProjectionExpression MakeNullable() propertyExpressionMap[property] = columnExpression.MakeNullable(); } + var complexPropertyCache = default(Dictionary); + if (_complexPropertyCache != null) + { + complexPropertyCache = new(); + foreach (var (complexProperty, complexShaper) in _complexPropertyCache) + { + complexPropertyCache[complexProperty] = complexShaper.MakeNullable(); + } + } + var discriminatorExpression = DiscriminatorExpression; if (discriminatorExpression is ColumnExpression ce) { @@ -179,6 +228,7 @@ public virtual StructuralTypeProjectionExpression MakeNullable() StructuralType, propertyExpressionMap, ownedNavigationMap, + complexPropertyCache, TableMap, nullable: true, discriminatorExpression); @@ -213,6 +263,20 @@ public virtual StructuralTypeProjectionExpression UpdateEntityType(IEntityType d } } + var complexPropertyCache = default(Dictionary); + if (_complexPropertyCache != null) + { + complexPropertyCache = new(); + foreach (var (complexProperty, complexShaper) in _complexPropertyCache) + { + if (derivedType.IsAssignableFrom(complexProperty.DeclaringType) + || complexProperty.DeclaringType.IsAssignableFrom(derivedType)) + { + complexPropertyCache[complexProperty] = complexShaper; + } + } + } + var ownedNavigationMap = new Dictionary(); foreach (var (navigation, entityShaperExpression) in _ownedNavigationMap) { @@ -264,7 +328,7 @@ public virtual StructuralTypeProjectionExpression UpdateEntityType(IEntityType d } return new StructuralTypeProjectionExpression( - derivedType, propertyExpressionMap, ownedNavigationMap, newTableMap ?? TableMap, IsNullable, discriminatorExpression); + derivedType, propertyExpressionMap, ownedNavigationMap, complexPropertyCache, newTableMap ?? TableMap, IsNullable, discriminatorExpression); } /// diff --git a/test/EFCore.Relational.Specification.Tests/Query/AdHocAdvancedMappingsQueryRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/AdHocAdvancedMappingsQueryRelationalTestBase.cs index 7c1e5190a25..0643e846e66 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/AdHocAdvancedMappingsQueryRelationalTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/AdHocAdvancedMappingsQueryRelationalTestBase.cs @@ -14,6 +14,137 @@ protected void ClearLog() protected void AssertSql(params string[] expected) => TestSqlLoggerFactory.AssertBaseline(expected); + #region 32911 + + [ConditionalFact] + public virtual async Task Two_similar_complex_properties_projected_with_split_query1() + { + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using var context = contextFactory.CreateContext(); + var query = context.Offers + .Include(e => e.Variations) + .ThenInclude(v => v.Nested) + .AsSplitQuery() + .ToList(); + + var resultElement = query.Single(); + foreach (var variation in resultElement.Variations) + { + Assert.NotEqual(variation.Payment.Brutto, variation.Nested.Payment.Brutto); + Assert.NotEqual(variation.Payment.Netto, variation.Nested.Payment.Netto); + } + } + + [ConditionalFact] + public virtual async Task Two_similar_complex_properties_projected_with_split_query2() + { + var contextFactory = await InitializeAsync(seed: c => c.Seed()); + + using var context = contextFactory.CreateContext(); + var query = context.Offers + .Include(e => e.Variations) + .ThenInclude(v => v.Nested) + .AsSplitQuery() + .Single(x => x.Id == 1); + + foreach (var variation in query.Variations) + { + Assert.NotEqual(variation.Payment.Brutto, variation.Nested.Payment.Brutto); + Assert.NotEqual(variation.Payment.Netto, variation.Nested.Payment.Netto); + } + } + + protected class Context32911(DbContextOptions options) : DbContext(options) + { + public DbSet Offers { get; set; } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever(); + modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever(); + modelBuilder.Entity().ComplexProperty(x => x.Payment, cpb => + { + cpb.IsRequired(); + cpb.Property(p => p.Netto).HasColumnName("payment_netto"); + cpb.Property(p => p.Brutto).HasColumnName("payment_brutto"); + }); + modelBuilder.Entity().Property(x => x.Id).ValueGeneratedNever(); + modelBuilder.Entity().ComplexProperty(x => x.Payment, cpb => + { + cpb.IsRequired(); + cpb.Property(p => p.Netto).HasColumnName("payment_netto"); + cpb.Property(p => p.Brutto).HasColumnName("payment_brutto"); + }); + } + + public void Seed() + { + var v1 = new Variation + { + Id = 1, + Payment = new Payment(1, 10), + Nested = new NestedEntity + { + Id = 1, + Payment = new Payment(10, 100) + } + }; + + var v2 = new Variation + { + Id = 2, + Payment = new Payment(2, 20), + Nested = new NestedEntity + { + Id = 2, + Payment = new Payment(20, 200) + } + }; + + var v3 = new Variation + { + Id = 3, + Payment = new Payment(3, 30), + Nested = new NestedEntity + { + Id = 3, + Payment = new Payment(30, 300) + } + }; + + Offers.Add(new Offer { Id = 1, Variations = new List { v1, v2, v3 } }); + + SaveChanges(); + } + + public abstract class EntityBase + { + public int Id { get; set; } + } + + public class Offer : EntityBase + { + public ICollection Variations { get; set; } + } + + public class Variation : EntityBase + { + public Payment Payment { get; set; } = new Payment(0, 0); + + public NestedEntity Nested { get; set; } + } + + public class NestedEntity : EntityBase + { + public Payment Payment { get; set; } = new Payment(0, 0); + } + + public record Payment(decimal Netto, decimal Brutto); + } + + #endregion + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Hierarchy_query_with_abstract_type_sibling_TPC(bool async) diff --git a/test/EFCore.Specification.Tests/Query/ComplexTypeQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/ComplexTypeQueryTestBase.cs index c91a7ae046b..ae38af60bf3 100644 --- a/test/EFCore.Specification.Tests/Query/ComplexTypeQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/ComplexTypeQueryTestBase.cs @@ -528,6 +528,292 @@ public virtual Task Union_two_different_struct_complex_type(bool async) async, ss => ss.Set().Select(c => c.ShippingAddress).Union(ss.Set().Select(c => c.BillingAddress))); + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Project_same_nested_complex_type_twice_with_pushdown(bool async) + => AssertQuery( + async, + ss => (from c1 in ss.Set() + from c2 in ss.Set() + select new { BA1 = c1.BillingAddress, BA2 = c2.BillingAddress }) + .Distinct() + .Select(x => new { x.BA1, x.BA2 }), + elementSorter: e => (e.BA1.ZipCode, e.BA2.ZipCode), + elementAsserter: (e, a) => + { + AssertEqual(e.BA1, a.BA1); + AssertEqual(e.BA2, a.BA2); + }); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Project_same_entity_with_nested_complex_type_twice_with_pushdown(bool async) + => AssertQuery( + async, + ss => (from c1 in ss.Set() + from c2 in ss.Set() + select new { c1, c2 }) + .Distinct() + .Select(x => new { x.c1, x.c2 }), + elementSorter: e => (e.c1.Id, e.c2.Id), + elementAsserter: (e, a) => + { + AssertEqual(e.c1, a.c1); + AssertEqual(e.c2, a.c2); + }); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Project_same_nested_complex_type_twice_with_double_pushdown(bool async) + => AssertQuery( + async, + ss => (from c1 in ss.Set() + from c2 in ss.Set() + orderby c1.Id, c2.Id + select new { BA1 = c1.BillingAddress, BA2 = c2.BillingAddress }) + .Take(50) + .Distinct() + .Select(x => new { x.BA1, x.BA2 }), + elementSorter: e => (e.BA1.ZipCode, e.BA2.ZipCode), + elementAsserter: (e, a) => + { + AssertEqual(e.BA1, a.BA1); + AssertEqual(e.BA2, a.BA2); + }); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Project_same_entity_with_nested_complex_type_twice_with_double_pushdown(bool async) + => AssertQuery( + async, + ss => (from c1 in ss.Set() + from c2 in ss.Set() + orderby c1.Id, c2.Id + select new { c1, c2 }) + .Take(50) + .Distinct() + .Select(x => new { x.c1, x.c2 }), + elementSorter: e => (e.c1.Id, e.c2.Id), + elementAsserter: (e, a) => + { + AssertEqual(e.c1, a.c1); + AssertEqual(e.c2, a.c2); + }); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Project_same_struct_nested_complex_type_twice_with_pushdown(bool async) + => AssertQuery( + async, + ss => (from c1 in ss.Set() + from c2 in ss.Set() + select new { BA1 = c1.BillingAddress, BA2 = c2.BillingAddress }) + .Distinct() + .Select(x => new { x.BA1, x.BA2 }), + elementSorter: e => (e.BA1.ZipCode, e.BA2.ZipCode), + elementAsserter: (e, a) => + { + AssertEqual(e.BA1, a.BA1); + AssertEqual(e.BA2, a.BA2); + }); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Project_same_entity_with_struct_nested_complex_type_twice_with_pushdown(bool async) + => AssertQuery( + async, + ss => (from c1 in ss.Set() + from c2 in ss.Set() + select new { c1, c2 }) + .Distinct() + .Select(x => new { x.c1, x.c2 }), + elementSorter: e => (e.c1.Id, e.c2.Id), + elementAsserter: (e, a) => + { + AssertEqual(e.c1, a.c1); + AssertEqual(e.c2, a.c2); + }); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Project_same_struct_nested_complex_type_twice_with_double_pushdown(bool async) + => AssertQuery( + async, + ss => (from c1 in ss.Set() + from c2 in ss.Set() + orderby c1.Id, c2.Id + select new { BA1 = c1.BillingAddress, BA2 = c2.BillingAddress }) + .Take(50) + .Distinct() + .Select(x => new { x.BA1, x.BA2 }), + elementSorter: e => (e.BA1.ZipCode, e.BA2.ZipCode), + elementAsserter: (e, a) => + { + AssertEqual(e.BA1, a.BA1); + AssertEqual(e.BA2, a.BA2); + }); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Project_same_entity_with_struct_nested_complex_type_twice_with_double_pushdown(bool async) + => AssertQuery( + async, + ss => (from c1 in ss.Set() + from c2 in ss.Set() + orderby c1.Id, c2.Id + select new { c1, c2 }) + .Take(50) + .Distinct() + .Select(x => new { x.c1, x.c2 }), + elementSorter: e => (e.c1.Id, e.c2.Id), + elementAsserter: (e, a) => + { + AssertEqual(e.c1, a.c1); + AssertEqual(e.c2, a.c2); + }); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Union_of_same_entity_with_nested_complex_type_projected_twice_with_pushdown(bool async) + => AssertQuery( + async, + ss => (from c1 in ss.Set() + from c2 in ss.Set() + orderby c1.Id, c2.Id + select new { c1, c2 }) + .Union(from c1 in ss.Set() + from c2 in ss.Set() + orderby c1.Id, c2.Id + select new { c1, c2 }) + .OrderBy(x => x.c1.Id).ThenBy(x => x.c2.Id) + .Take(50) + .Select(x => new { x.c1, x.c2 }), + assertOrder: true, + elementAsserter: (e, a) => + { + AssertEqual(e.c1, a.c1); + AssertEqual(e.c2, a.c2); + }); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Union_of_same_entity_with_nested_complex_type_projected_twice_with_double_pushdown(bool async) + => AssertQuery( + async, + ss => (from c1 in ss.Set() + from c2 in ss.Set() + orderby c1.Id, c2.Id + select new { c1, c2 }) + .Union(from c1 in ss.Set() + from c2 in ss.Set() + orderby c1.Id, c2.Id + select new { c1, c2 }) + .OrderBy(x => x.c1.Id).ThenBy(x => x.c2.Id) + .Take(50) + .Select(x => new { x.c1, x.c2 }) + .Distinct() + .OrderBy(x => x.c1.Id).ThenBy(x => x.c2.Id) + .Take(50) + .Select(x => new { x.c1, x.c2 }), + assertOrder: true, + elementAsserter: (e, a) => + { + AssertEqual(e.c1, a.c1); + AssertEqual(e.c2, a.c2); + }); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Union_of_same_nested_complex_type_projected_twice_with_pushdown(bool async) + => AssertQuery( + async, + ss => (from c1 in ss.Set() + from c2 in ss.Set() + orderby c1.Id, c2.Id + select new { BA1 = c1.BillingAddress, BA2 = c2.BillingAddress }) + .Union(from c1 in ss.Set() + from c2 in ss.Set() + orderby c1.Id, c2.Id + select new { BA1 = c1.BillingAddress, BA2 = c2.BillingAddress }) + .OrderBy(x => x.BA1.ZipCode).ThenBy(x => x.BA2.ZipCode) + .Take(50) + .Select(x => new { x.BA1, x.BA2 }), + assertOrder: true, + elementAsserter: (e, a) => + { + AssertEqual(e.BA1, a.BA1); + AssertEqual(e.BA2, a.BA2); + }); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Union_of_same_nested_complex_type_projected_twice_with_double_pushdown(bool async) + => AssertQuery( + async, + ss => (from c1 in ss.Set() + from c2 in ss.Set() + orderby c1.Id, c2.Id + select new { BA1 = c1.BillingAddress, BA2 = c2.BillingAddress }) + .Union(from c1 in ss.Set() + from c2 in ss.Set() + orderby c1.Id, c2.Id + select new { BA1 = c1.BillingAddress, BA2 = c2.BillingAddress }) + .OrderBy(x => x.BA1.ZipCode).ThenBy(x => x.BA2.ZipCode) + .Take(50) + .Select(x => new { x.BA1, x.BA2 }) + .Distinct() + .OrderBy(x => x.BA1.ZipCode).ThenBy(x => x.BA2.ZipCode) + .Take(50) + .Select(x => new { x.BA1, x.BA2 }), + assertOrder: true, + elementAsserter: (e, a) => + { + AssertEqual(e.BA1, a.BA1); + AssertEqual(e.BA2, a.BA2); + }); + + [ConditionalTheory] + [MemberData(nameof(IsAsyncData))] + public virtual Task Same_entity_with_complex_type_projected_twice_with_pushdown_as_part_of_another_projection(bool async) + => AssertQuery( + async, + ss => ss.Set().Select(x => new + { + x.Id, + Complex = (from c1 in ss.Set() + from c2 in ss.Set() + orderby c1.Id, c2.Id descending + select new { One = c1, Two = c2 }).FirstOrDefault() + }), + elementSorter: e => e.Id, + elementAsserter: (e, a) => + { + AssertEqual(e.Id, a.Id); + AssertEqual(e.Complex?.One, a.Complex?.One); + AssertEqual(e.Complex?.Two, a.Complex?.Two); + }); + + [ConditionalTheory(Skip = "issue #31376")] + [MemberData(nameof(IsAsyncData))] + public virtual Task Same_complex_type_projected_twice_with_pushdown_as_part_of_another_projection(bool async) + => AssertQuery( + async, + ss => ss.Set().Select(x => new + { + x.Id, + Complex = (from c1 in ss.Set() + from c2 in ss.Set() + orderby c1.Id, c2.Id descending + select new { One = c1.BillingAddress, Two = c2.BillingAddress }).FirstOrDefault() + }), + elementSorter: e => e.Id, + elementAsserter: (e, a) => + { + AssertEqual(e.Id, a.Id); + AssertEqual(e.Complex?.One, a.Complex?.One); + AssertEqual(e.Complex?.Two, a.Complex?.Two); + }); + protected DbContext CreateContext() => Fixture.CreateContext(); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/AdHocAdvancedMappingsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/AdHocAdvancedMappingsQuerySqlServerTest.cs index aaeaf5fc010..109f8a9bf66 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/AdHocAdvancedMappingsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/AdHocAdvancedMappingsQuerySqlServerTest.cs @@ -231,6 +231,57 @@ UNION ALL FROM [Dogs] AS [d] ) AS [u] WHERE [u].[Species] LIKE N'F%' +"""); + } + + public override async Task Two_similar_complex_properties_projected_with_split_query1() + { + await base.Two_similar_complex_properties_projected_with_split_query1(); + + AssertSql( +""" +SELECT [o].[Id] +FROM [Offers] AS [o] +ORDER BY [o].[Id] +""", + // + """ +SELECT [s].[Id], [s].[NestedId], [s].[OfferId], [s].[payment_brutto], [s].[payment_netto], [s].[Id0], [s].[payment_brutto0], [s].[payment_netto0], [o].[Id] +FROM [Offers] AS [o] +INNER JOIN ( + SELECT [v].[Id], [v].[NestedId], [v].[OfferId], [v].[payment_brutto], [v].[payment_netto], [n].[Id] AS [Id0], [n].[payment_brutto] AS [payment_brutto0], [n].[payment_netto] AS [payment_netto0] + FROM [Variation] AS [v] + LEFT JOIN [NestedEntity] AS [n] ON [v].[NestedId] = [n].[Id] +) AS [s] ON [o].[Id] = [s].[OfferId] +ORDER BY [o].[Id] +"""); + } + + public override async Task Two_similar_complex_properties_projected_with_split_query2() + { + await base.Two_similar_complex_properties_projected_with_split_query2(); + + AssertSql( +""" +SELECT TOP(2) [o].[Id] +FROM [Offers] AS [o] +WHERE [o].[Id] = 1 +ORDER BY [o].[Id] +""", + // + """ +SELECT [s].[Id], [s].[NestedId], [s].[OfferId], [s].[payment_brutto], [s].[payment_netto], [s].[Id0], [s].[payment_brutto0], [s].[payment_netto0], [o0].[Id] +FROM ( + SELECT TOP(1) [o].[Id] + FROM [Offers] AS [o] + WHERE [o].[Id] = 1 +) AS [o0] +INNER JOIN ( + SELECT [v].[Id], [v].[NestedId], [v].[OfferId], [v].[payment_brutto], [v].[payment_netto], [n].[Id] AS [Id0], [n].[payment_brutto] AS [payment_brutto0], [n].[payment_netto] AS [payment_netto0] + FROM [Variation] AS [v] + LEFT JOIN [NestedEntity] AS [n] ON [v].[NestedId] = [n].[Id] +) AS [s] ON [o0].[Id] = [s].[OfferId] +ORDER BY [o0].[Id] """); } } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexTypeQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexTypeQuerySqlServerTest.cs index 9060eebbad9..56719f73de4 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexTypeQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexTypeQuerySqlServerTest.cs @@ -859,6 +859,276 @@ FROM [Customer] AS [c] """).Select(c => c.ShippingAddress).Distinct(), ss => ss.Set().Select(c => c.ShippingAddress).Distinct()); + public override async Task Project_same_entity_with_nested_complex_type_twice_with_pushdown(bool async) + { + await base.Project_same_entity_with_nested_complex_type_twice_with_pushdown(async); + + AssertSql( +""" +SELECT [s].[Id], [s].[Name], [s].[BillingAddress_AddressLine1], [s].[BillingAddress_AddressLine2], [s].[BillingAddress_Tags], [s].[BillingAddress_ZipCode], [s].[BillingAddress_Country_Code], [s].[BillingAddress_Country_FullName], [s].[ShippingAddress_AddressLine1], [s].[ShippingAddress_AddressLine2], [s].[ShippingAddress_Tags], [s].[ShippingAddress_ZipCode], [s].[ShippingAddress_Country_Code], [s].[ShippingAddress_Country_FullName], [s].[Id0], [s].[Name0], [s].[BillingAddress_AddressLine10], [s].[BillingAddress_AddressLine20], [s].[BillingAddress_Tags0], [s].[BillingAddress_ZipCode0], [s].[BillingAddress_Country_Code0], [s].[BillingAddress_Country_FullName0], [s].[ShippingAddress_AddressLine10], [s].[ShippingAddress_AddressLine20], [s].[ShippingAddress_Tags0], [s].[ShippingAddress_ZipCode0], [s].[ShippingAddress_Country_Code0], [s].[ShippingAddress_Country_FullName0] +FROM ( + SELECT DISTINCT [c].[Id], [c].[Name], [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_Tags], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_Tags], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName], [c0].[Id] AS [Id0], [c0].[Name] AS [Name0], [c0].[BillingAddress_AddressLine1] AS [BillingAddress_AddressLine10], [c0].[BillingAddress_AddressLine2] AS [BillingAddress_AddressLine20], [c0].[BillingAddress_Tags] AS [BillingAddress_Tags0], [c0].[BillingAddress_ZipCode] AS [BillingAddress_ZipCode0], [c0].[BillingAddress_Country_Code] AS [BillingAddress_Country_Code0], [c0].[BillingAddress_Country_FullName] AS [BillingAddress_Country_FullName0], [c0].[ShippingAddress_AddressLine1] AS [ShippingAddress_AddressLine10], [c0].[ShippingAddress_AddressLine2] AS [ShippingAddress_AddressLine20], [c0].[ShippingAddress_Tags] AS [ShippingAddress_Tags0], [c0].[ShippingAddress_ZipCode] AS [ShippingAddress_ZipCode0], [c0].[ShippingAddress_Country_Code] AS [ShippingAddress_Country_Code0], [c0].[ShippingAddress_Country_FullName] AS [ShippingAddress_Country_FullName0] + FROM [Customer] AS [c] + CROSS JOIN [Customer] AS [c0] +) AS [s] +"""); + } + + public override async Task Project_same_nested_complex_type_twice_with_pushdown(bool async) + { + await base.Project_same_nested_complex_type_twice_with_pushdown(async); + + AssertSql( +""" +SELECT [s].[BillingAddress_AddressLine1], [s].[BillingAddress_AddressLine2], [s].[BillingAddress_Tags], [s].[BillingAddress_ZipCode], [s].[BillingAddress_Country_Code], [s].[BillingAddress_Country_FullName], [s].[BillingAddress_AddressLine10], [s].[BillingAddress_AddressLine20], [s].[BillingAddress_Tags0], [s].[BillingAddress_ZipCode0], [s].[BillingAddress_Country_Code0], [s].[BillingAddress_Country_FullName0] +FROM ( + SELECT DISTINCT [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_Tags], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c0].[BillingAddress_AddressLine1] AS [BillingAddress_AddressLine10], [c0].[BillingAddress_AddressLine2] AS [BillingAddress_AddressLine20], [c0].[BillingAddress_Tags] AS [BillingAddress_Tags0], [c0].[BillingAddress_ZipCode] AS [BillingAddress_ZipCode0], [c0].[BillingAddress_Country_Code] AS [BillingAddress_Country_Code0], [c0].[BillingAddress_Country_FullName] AS [BillingAddress_Country_FullName0] + FROM [Customer] AS [c] + CROSS JOIN [Customer] AS [c0] +) AS [s] +"""); + } + + public override async Task Project_same_entity_with_nested_complex_type_twice_with_double_pushdown(bool async) + { + await base.Project_same_entity_with_nested_complex_type_twice_with_double_pushdown(async); + + AssertSql( +""" +@__p_0='50' + +SELECT [s0].[Id], [s0].[Name], [s0].[BillingAddress_AddressLine1], [s0].[BillingAddress_AddressLine2], [s0].[BillingAddress_Tags], [s0].[BillingAddress_ZipCode], [s0].[BillingAddress_Country_Code], [s0].[BillingAddress_Country_FullName], [s0].[ShippingAddress_AddressLine1], [s0].[ShippingAddress_AddressLine2], [s0].[ShippingAddress_Tags], [s0].[ShippingAddress_ZipCode], [s0].[ShippingAddress_Country_Code], [s0].[ShippingAddress_Country_FullName], [s0].[Id0], [s0].[Name0], [s0].[BillingAddress_AddressLine10], [s0].[BillingAddress_AddressLine20], [s0].[BillingAddress_Tags0], [s0].[BillingAddress_ZipCode0], [s0].[BillingAddress_Country_Code0], [s0].[BillingAddress_Country_FullName0], [s0].[ShippingAddress_AddressLine10], [s0].[ShippingAddress_AddressLine20], [s0].[ShippingAddress_Tags0], [s0].[ShippingAddress_ZipCode0], [s0].[ShippingAddress_Country_Code0], [s0].[ShippingAddress_Country_FullName0] +FROM ( + SELECT DISTINCT [s].[Id], [s].[Name], [s].[BillingAddress_AddressLine1], [s].[BillingAddress_AddressLine2], [s].[BillingAddress_Tags], [s].[BillingAddress_ZipCode], [s].[BillingAddress_Country_Code], [s].[BillingAddress_Country_FullName], [s].[ShippingAddress_AddressLine1], [s].[ShippingAddress_AddressLine2], [s].[ShippingAddress_Tags], [s].[ShippingAddress_ZipCode], [s].[ShippingAddress_Country_Code], [s].[ShippingAddress_Country_FullName], [s].[Id0], [s].[Name0], [s].[BillingAddress_AddressLine10], [s].[BillingAddress_AddressLine20], [s].[BillingAddress_Tags0], [s].[BillingAddress_ZipCode0], [s].[BillingAddress_Country_Code0], [s].[BillingAddress_Country_FullName0], [s].[ShippingAddress_AddressLine10], [s].[ShippingAddress_AddressLine20], [s].[ShippingAddress_Tags0], [s].[ShippingAddress_ZipCode0], [s].[ShippingAddress_Country_Code0], [s].[ShippingAddress_Country_FullName0] + FROM ( + SELECT TOP(@__p_0) [c].[Id], [c].[Name], [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_Tags], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_Tags], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName], [c0].[Id] AS [Id0], [c0].[Name] AS [Name0], [c0].[BillingAddress_AddressLine1] AS [BillingAddress_AddressLine10], [c0].[BillingAddress_AddressLine2] AS [BillingAddress_AddressLine20], [c0].[BillingAddress_Tags] AS [BillingAddress_Tags0], [c0].[BillingAddress_ZipCode] AS [BillingAddress_ZipCode0], [c0].[BillingAddress_Country_Code] AS [BillingAddress_Country_Code0], [c0].[BillingAddress_Country_FullName] AS [BillingAddress_Country_FullName0], [c0].[ShippingAddress_AddressLine1] AS [ShippingAddress_AddressLine10], [c0].[ShippingAddress_AddressLine2] AS [ShippingAddress_AddressLine20], [c0].[ShippingAddress_Tags] AS [ShippingAddress_Tags0], [c0].[ShippingAddress_ZipCode] AS [ShippingAddress_ZipCode0], [c0].[ShippingAddress_Country_Code] AS [ShippingAddress_Country_Code0], [c0].[ShippingAddress_Country_FullName] AS [ShippingAddress_Country_FullName0] + FROM [Customer] AS [c] + CROSS JOIN [Customer] AS [c0] + ORDER BY [c].[Id], [c0].[Id] + ) AS [s] +) AS [s0] +"""); + } + + public override async Task Project_same_nested_complex_type_twice_with_double_pushdown(bool async) + { + await base.Project_same_nested_complex_type_twice_with_double_pushdown(async); + + AssertSql( +""" +@__p_0='50' + +SELECT [s0].[BillingAddress_AddressLine1], [s0].[BillingAddress_AddressLine2], [s0].[BillingAddress_Tags], [s0].[BillingAddress_ZipCode], [s0].[BillingAddress_Country_Code], [s0].[BillingAddress_Country_FullName], [s0].[BillingAddress_AddressLine10], [s0].[BillingAddress_AddressLine20], [s0].[BillingAddress_Tags0], [s0].[BillingAddress_ZipCode0], [s0].[BillingAddress_Country_Code0], [s0].[BillingAddress_Country_FullName0] +FROM ( + SELECT DISTINCT [s].[BillingAddress_AddressLine1], [s].[BillingAddress_AddressLine2], [s].[BillingAddress_Tags], [s].[BillingAddress_ZipCode], [s].[BillingAddress_Country_Code], [s].[BillingAddress_Country_FullName], [s].[BillingAddress_AddressLine10], [s].[BillingAddress_AddressLine20], [s].[BillingAddress_Tags0], [s].[BillingAddress_ZipCode0], [s].[BillingAddress_Country_Code0], [s].[BillingAddress_Country_FullName0] + FROM ( + SELECT TOP(@__p_0) [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_Tags], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c0].[BillingAddress_AddressLine1] AS [BillingAddress_AddressLine10], [c0].[BillingAddress_AddressLine2] AS [BillingAddress_AddressLine20], [c0].[BillingAddress_Tags] AS [BillingAddress_Tags0], [c0].[BillingAddress_ZipCode] AS [BillingAddress_ZipCode0], [c0].[BillingAddress_Country_Code] AS [BillingAddress_Country_Code0], [c0].[BillingAddress_Country_FullName] AS [BillingAddress_Country_FullName0] + FROM [Customer] AS [c] + CROSS JOIN [Customer] AS [c0] + ORDER BY [c].[Id], [c0].[Id] + ) AS [s] +) AS [s0] +"""); + } + + public override async Task Project_same_entity_with_struct_nested_complex_type_twice_with_pushdown(bool async) + { + await base.Project_same_entity_with_struct_nested_complex_type_twice_with_pushdown(async); + + AssertSql( +""" +SELECT [s].[Id], [s].[Name], [s].[BillingAddress_AddressLine1], [s].[BillingAddress_AddressLine2], [s].[BillingAddress_ZipCode], [s].[BillingAddress_Country_Code], [s].[BillingAddress_Country_FullName], [s].[ShippingAddress_AddressLine1], [s].[ShippingAddress_AddressLine2], [s].[ShippingAddress_ZipCode], [s].[ShippingAddress_Country_Code], [s].[ShippingAddress_Country_FullName], [s].[Id0], [s].[Name0], [s].[BillingAddress_AddressLine10], [s].[BillingAddress_AddressLine20], [s].[BillingAddress_ZipCode0], [s].[BillingAddress_Country_Code0], [s].[BillingAddress_Country_FullName0], [s].[ShippingAddress_AddressLine10], [s].[ShippingAddress_AddressLine20], [s].[ShippingAddress_ZipCode0], [s].[ShippingAddress_Country_Code0], [s].[ShippingAddress_Country_FullName0] +FROM ( + SELECT DISTINCT [v].[Id], [v].[Name], [v].[BillingAddress_AddressLine1], [v].[BillingAddress_AddressLine2], [v].[BillingAddress_ZipCode], [v].[BillingAddress_Country_Code], [v].[BillingAddress_Country_FullName], [v].[ShippingAddress_AddressLine1], [v].[ShippingAddress_AddressLine2], [v].[ShippingAddress_ZipCode], [v].[ShippingAddress_Country_Code], [v].[ShippingAddress_Country_FullName], [v0].[Id] AS [Id0], [v0].[Name] AS [Name0], [v0].[BillingAddress_AddressLine1] AS [BillingAddress_AddressLine10], [v0].[BillingAddress_AddressLine2] AS [BillingAddress_AddressLine20], [v0].[BillingAddress_ZipCode] AS [BillingAddress_ZipCode0], [v0].[BillingAddress_Country_Code] AS [BillingAddress_Country_Code0], [v0].[BillingAddress_Country_FullName] AS [BillingAddress_Country_FullName0], [v0].[ShippingAddress_AddressLine1] AS [ShippingAddress_AddressLine10], [v0].[ShippingAddress_AddressLine2] AS [ShippingAddress_AddressLine20], [v0].[ShippingAddress_ZipCode] AS [ShippingAddress_ZipCode0], [v0].[ShippingAddress_Country_Code] AS [ShippingAddress_Country_Code0], [v0].[ShippingAddress_Country_FullName] AS [ShippingAddress_Country_FullName0] + FROM [ValuedCustomer] AS [v] + CROSS JOIN [ValuedCustomer] AS [v0] +) AS [s] +"""); + } + + public override async Task Project_same_struct_nested_complex_type_twice_with_pushdown(bool async) + { + await base.Project_same_struct_nested_complex_type_twice_with_pushdown(async); + + AssertSql( +""" +SELECT [s].[BillingAddress_AddressLine1], [s].[BillingAddress_AddressLine2], [s].[BillingAddress_ZipCode], [s].[BillingAddress_Country_Code], [s].[BillingAddress_Country_FullName], [s].[BillingAddress_AddressLine10], [s].[BillingAddress_AddressLine20], [s].[BillingAddress_ZipCode0], [s].[BillingAddress_Country_Code0], [s].[BillingAddress_Country_FullName0] +FROM ( + SELECT DISTINCT [v].[BillingAddress_AddressLine1], [v].[BillingAddress_AddressLine2], [v].[BillingAddress_ZipCode], [v].[BillingAddress_Country_Code], [v].[BillingAddress_Country_FullName], [v0].[BillingAddress_AddressLine1] AS [BillingAddress_AddressLine10], [v0].[BillingAddress_AddressLine2] AS [BillingAddress_AddressLine20], [v0].[BillingAddress_ZipCode] AS [BillingAddress_ZipCode0], [v0].[BillingAddress_Country_Code] AS [BillingAddress_Country_Code0], [v0].[BillingAddress_Country_FullName] AS [BillingAddress_Country_FullName0] + FROM [ValuedCustomer] AS [v] + CROSS JOIN [ValuedCustomer] AS [v0] +) AS [s] +"""); + } + + public override async Task Project_same_entity_with_struct_nested_complex_type_twice_with_double_pushdown(bool async) + { + await base.Project_same_entity_with_struct_nested_complex_type_twice_with_double_pushdown(async); + + AssertSql( +""" +@__p_0='50' + +SELECT [s0].[Id], [s0].[Name], [s0].[BillingAddress_AddressLine1], [s0].[BillingAddress_AddressLine2], [s0].[BillingAddress_ZipCode], [s0].[BillingAddress_Country_Code], [s0].[BillingAddress_Country_FullName], [s0].[ShippingAddress_AddressLine1], [s0].[ShippingAddress_AddressLine2], [s0].[ShippingAddress_ZipCode], [s0].[ShippingAddress_Country_Code], [s0].[ShippingAddress_Country_FullName], [s0].[Id0], [s0].[Name0], [s0].[BillingAddress_AddressLine10], [s0].[BillingAddress_AddressLine20], [s0].[BillingAddress_ZipCode0], [s0].[BillingAddress_Country_Code0], [s0].[BillingAddress_Country_FullName0], [s0].[ShippingAddress_AddressLine10], [s0].[ShippingAddress_AddressLine20], [s0].[ShippingAddress_ZipCode0], [s0].[ShippingAddress_Country_Code0], [s0].[ShippingAddress_Country_FullName0] +FROM ( + SELECT DISTINCT [s].[Id], [s].[Name], [s].[BillingAddress_AddressLine1], [s].[BillingAddress_AddressLine2], [s].[BillingAddress_ZipCode], [s].[BillingAddress_Country_Code], [s].[BillingAddress_Country_FullName], [s].[ShippingAddress_AddressLine1], [s].[ShippingAddress_AddressLine2], [s].[ShippingAddress_ZipCode], [s].[ShippingAddress_Country_Code], [s].[ShippingAddress_Country_FullName], [s].[Id0], [s].[Name0], [s].[BillingAddress_AddressLine10], [s].[BillingAddress_AddressLine20], [s].[BillingAddress_ZipCode0], [s].[BillingAddress_Country_Code0], [s].[BillingAddress_Country_FullName0], [s].[ShippingAddress_AddressLine10], [s].[ShippingAddress_AddressLine20], [s].[ShippingAddress_ZipCode0], [s].[ShippingAddress_Country_Code0], [s].[ShippingAddress_Country_FullName0] + FROM ( + SELECT TOP(@__p_0) [v].[Id], [v].[Name], [v].[BillingAddress_AddressLine1], [v].[BillingAddress_AddressLine2], [v].[BillingAddress_ZipCode], [v].[BillingAddress_Country_Code], [v].[BillingAddress_Country_FullName], [v].[ShippingAddress_AddressLine1], [v].[ShippingAddress_AddressLine2], [v].[ShippingAddress_ZipCode], [v].[ShippingAddress_Country_Code], [v].[ShippingAddress_Country_FullName], [v0].[Id] AS [Id0], [v0].[Name] AS [Name0], [v0].[BillingAddress_AddressLine1] AS [BillingAddress_AddressLine10], [v0].[BillingAddress_AddressLine2] AS [BillingAddress_AddressLine20], [v0].[BillingAddress_ZipCode] AS [BillingAddress_ZipCode0], [v0].[BillingAddress_Country_Code] AS [BillingAddress_Country_Code0], [v0].[BillingAddress_Country_FullName] AS [BillingAddress_Country_FullName0], [v0].[ShippingAddress_AddressLine1] AS [ShippingAddress_AddressLine10], [v0].[ShippingAddress_AddressLine2] AS [ShippingAddress_AddressLine20], [v0].[ShippingAddress_ZipCode] AS [ShippingAddress_ZipCode0], [v0].[ShippingAddress_Country_Code] AS [ShippingAddress_Country_Code0], [v0].[ShippingAddress_Country_FullName] AS [ShippingAddress_Country_FullName0] + FROM [ValuedCustomer] AS [v] + CROSS JOIN [ValuedCustomer] AS [v0] + ORDER BY [v].[Id], [v0].[Id] + ) AS [s] +) AS [s0] +"""); + } + + public override async Task Project_same_struct_nested_complex_type_twice_with_double_pushdown(bool async) + { + await base.Project_same_struct_nested_complex_type_twice_with_double_pushdown(async); + + AssertSql( +""" +@__p_0='50' + +SELECT [s0].[BillingAddress_AddressLine1], [s0].[BillingAddress_AddressLine2], [s0].[BillingAddress_ZipCode], [s0].[BillingAddress_Country_Code], [s0].[BillingAddress_Country_FullName], [s0].[BillingAddress_AddressLine10], [s0].[BillingAddress_AddressLine20], [s0].[BillingAddress_ZipCode0], [s0].[BillingAddress_Country_Code0], [s0].[BillingAddress_Country_FullName0] +FROM ( + SELECT DISTINCT [s].[BillingAddress_AddressLine1], [s].[BillingAddress_AddressLine2], [s].[BillingAddress_ZipCode], [s].[BillingAddress_Country_Code], [s].[BillingAddress_Country_FullName], [s].[BillingAddress_AddressLine10], [s].[BillingAddress_AddressLine20], [s].[BillingAddress_ZipCode0], [s].[BillingAddress_Country_Code0], [s].[BillingAddress_Country_FullName0] + FROM ( + SELECT TOP(@__p_0) [v].[BillingAddress_AddressLine1], [v].[BillingAddress_AddressLine2], [v].[BillingAddress_ZipCode], [v].[BillingAddress_Country_Code], [v].[BillingAddress_Country_FullName], [v0].[BillingAddress_AddressLine1] AS [BillingAddress_AddressLine10], [v0].[BillingAddress_AddressLine2] AS [BillingAddress_AddressLine20], [v0].[BillingAddress_ZipCode] AS [BillingAddress_ZipCode0], [v0].[BillingAddress_Country_Code] AS [BillingAddress_Country_Code0], [v0].[BillingAddress_Country_FullName] AS [BillingAddress_Country_FullName0] + FROM [ValuedCustomer] AS [v] + CROSS JOIN [ValuedCustomer] AS [v0] + ORDER BY [v].[Id], [v0].[Id] + ) AS [s] +) AS [s0] +"""); + } + + public override async Task Union_of_same_entity_with_nested_complex_type_projected_twice_with_pushdown(bool async) + { + await base.Union_of_same_entity_with_nested_complex_type_projected_twice_with_pushdown(async); + + AssertSql( +""" +@__p_0='50' + +SELECT TOP(@__p_0) [u].[Id], [u].[Name], [u].[BillingAddress_AddressLine1], [u].[BillingAddress_AddressLine2], [u].[BillingAddress_Tags], [u].[BillingAddress_ZipCode], [u].[BillingAddress_Country_Code], [u].[BillingAddress_Country_FullName], [u].[ShippingAddress_AddressLine1], [u].[ShippingAddress_AddressLine2], [u].[ShippingAddress_Tags], [u].[ShippingAddress_ZipCode], [u].[ShippingAddress_Country_Code], [u].[ShippingAddress_Country_FullName], [u].[Id0], [u].[Name0], [u].[BillingAddress_AddressLine10], [u].[BillingAddress_AddressLine20], [u].[BillingAddress_Tags0], [u].[BillingAddress_ZipCode0], [u].[BillingAddress_Country_Code0], [u].[BillingAddress_Country_FullName0], [u].[ShippingAddress_AddressLine10], [u].[ShippingAddress_AddressLine20], [u].[ShippingAddress_Tags0], [u].[ShippingAddress_ZipCode0], [u].[ShippingAddress_Country_Code0], [u].[ShippingAddress_Country_FullName0] +FROM ( + SELECT [c].[Id], [c].[Name], [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_Tags], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_Tags], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName], [c0].[Id] AS [Id0], [c0].[Name] AS [Name0], [c0].[BillingAddress_AddressLine1] AS [BillingAddress_AddressLine10], [c0].[BillingAddress_AddressLine2] AS [BillingAddress_AddressLine20], [c0].[BillingAddress_Tags] AS [BillingAddress_Tags0], [c0].[BillingAddress_ZipCode] AS [BillingAddress_ZipCode0], [c0].[BillingAddress_Country_Code] AS [BillingAddress_Country_Code0], [c0].[BillingAddress_Country_FullName] AS [BillingAddress_Country_FullName0], [c0].[ShippingAddress_AddressLine1] AS [ShippingAddress_AddressLine10], [c0].[ShippingAddress_AddressLine2] AS [ShippingAddress_AddressLine20], [c0].[ShippingAddress_Tags] AS [ShippingAddress_Tags0], [c0].[ShippingAddress_ZipCode] AS [ShippingAddress_ZipCode0], [c0].[ShippingAddress_Country_Code] AS [ShippingAddress_Country_Code0], [c0].[ShippingAddress_Country_FullName] AS [ShippingAddress_Country_FullName0] + FROM [Customer] AS [c] + CROSS JOIN [Customer] AS [c0] + UNION + SELECT [c1].[Id], [c1].[Name], [c1].[BillingAddress_AddressLine1], [c1].[BillingAddress_AddressLine2], [c1].[BillingAddress_Tags], [c1].[BillingAddress_ZipCode], [c1].[BillingAddress_Country_Code], [c1].[BillingAddress_Country_FullName], [c1].[ShippingAddress_AddressLine1], [c1].[ShippingAddress_AddressLine2], [c1].[ShippingAddress_Tags], [c1].[ShippingAddress_ZipCode], [c1].[ShippingAddress_Country_Code], [c1].[ShippingAddress_Country_FullName], [c2].[Id] AS [Id0], [c2].[Name] AS [Name0], [c2].[BillingAddress_AddressLine1] AS [BillingAddress_AddressLine10], [c2].[BillingAddress_AddressLine2] AS [BillingAddress_AddressLine20], [c2].[BillingAddress_Tags] AS [BillingAddress_Tags0], [c2].[BillingAddress_ZipCode] AS [BillingAddress_ZipCode0], [c2].[BillingAddress_Country_Code] AS [BillingAddress_Country_Code0], [c2].[BillingAddress_Country_FullName] AS [BillingAddress_Country_FullName0], [c2].[ShippingAddress_AddressLine1] AS [ShippingAddress_AddressLine10], [c2].[ShippingAddress_AddressLine2] AS [ShippingAddress_AddressLine20], [c2].[ShippingAddress_Tags] AS [ShippingAddress_Tags0], [c2].[ShippingAddress_ZipCode] AS [ShippingAddress_ZipCode0], [c2].[ShippingAddress_Country_Code] AS [ShippingAddress_Country_Code0], [c2].[ShippingAddress_Country_FullName] AS [ShippingAddress_Country_FullName0] + FROM [Customer] AS [c1] + CROSS JOIN [Customer] AS [c2] +) AS [u] +ORDER BY [u].[Id], [u].[Id0] +"""); + } + + public override async Task Union_of_same_entity_with_nested_complex_type_projected_twice_with_double_pushdown(bool async) + { + await base.Union_of_same_entity_with_nested_complex_type_projected_twice_with_double_pushdown(async); + + AssertSql( +""" +@__p_0='50' + +SELECT TOP(@__p_0) [u1].[Id], [u1].[Name], [u1].[BillingAddress_AddressLine1], [u1].[BillingAddress_AddressLine2], [u1].[BillingAddress_Tags], [u1].[BillingAddress_ZipCode], [u1].[BillingAddress_Country_Code], [u1].[BillingAddress_Country_FullName], [u1].[ShippingAddress_AddressLine1], [u1].[ShippingAddress_AddressLine2], [u1].[ShippingAddress_Tags], [u1].[ShippingAddress_ZipCode], [u1].[ShippingAddress_Country_Code], [u1].[ShippingAddress_Country_FullName], [u1].[Id0], [u1].[Name0], [u1].[BillingAddress_AddressLine10], [u1].[BillingAddress_AddressLine20], [u1].[BillingAddress_Tags0], [u1].[BillingAddress_ZipCode0], [u1].[BillingAddress_Country_Code0], [u1].[BillingAddress_Country_FullName0], [u1].[ShippingAddress_AddressLine10], [u1].[ShippingAddress_AddressLine20], [u1].[ShippingAddress_Tags0], [u1].[ShippingAddress_ZipCode0], [u1].[ShippingAddress_Country_Code0], [u1].[ShippingAddress_Country_FullName0] +FROM ( + SELECT DISTINCT [u0].[Id], [u0].[Name], [u0].[BillingAddress_AddressLine1], [u0].[BillingAddress_AddressLine2], [u0].[BillingAddress_Tags], [u0].[BillingAddress_ZipCode], [u0].[BillingAddress_Country_Code], [u0].[BillingAddress_Country_FullName], [u0].[ShippingAddress_AddressLine1], [u0].[ShippingAddress_AddressLine2], [u0].[ShippingAddress_Tags], [u0].[ShippingAddress_ZipCode], [u0].[ShippingAddress_Country_Code], [u0].[ShippingAddress_Country_FullName], [u0].[Id0], [u0].[Name0], [u0].[BillingAddress_AddressLine10], [u0].[BillingAddress_AddressLine20], [u0].[BillingAddress_Tags0], [u0].[BillingAddress_ZipCode0], [u0].[BillingAddress_Country_Code0], [u0].[BillingAddress_Country_FullName0], [u0].[ShippingAddress_AddressLine10], [u0].[ShippingAddress_AddressLine20], [u0].[ShippingAddress_Tags0], [u0].[ShippingAddress_ZipCode0], [u0].[ShippingAddress_Country_Code0], [u0].[ShippingAddress_Country_FullName0] + FROM ( + SELECT TOP(@__p_0) [u].[Id], [u].[Name], [u].[BillingAddress_AddressLine1], [u].[BillingAddress_AddressLine2], [u].[BillingAddress_Tags], [u].[BillingAddress_ZipCode], [u].[BillingAddress_Country_Code], [u].[BillingAddress_Country_FullName], [u].[ShippingAddress_AddressLine1], [u].[ShippingAddress_AddressLine2], [u].[ShippingAddress_Tags], [u].[ShippingAddress_ZipCode], [u].[ShippingAddress_Country_Code], [u].[ShippingAddress_Country_FullName], [u].[Id0], [u].[Name0], [u].[BillingAddress_AddressLine10], [u].[BillingAddress_AddressLine20], [u].[BillingAddress_Tags0], [u].[BillingAddress_ZipCode0], [u].[BillingAddress_Country_Code0], [u].[BillingAddress_Country_FullName0], [u].[ShippingAddress_AddressLine10], [u].[ShippingAddress_AddressLine20], [u].[ShippingAddress_Tags0], [u].[ShippingAddress_ZipCode0], [u].[ShippingAddress_Country_Code0], [u].[ShippingAddress_Country_FullName0] + FROM ( + SELECT [c].[Id], [c].[Name], [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_Tags], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_Tags], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName], [c0].[Id] AS [Id0], [c0].[Name] AS [Name0], [c0].[BillingAddress_AddressLine1] AS [BillingAddress_AddressLine10], [c0].[BillingAddress_AddressLine2] AS [BillingAddress_AddressLine20], [c0].[BillingAddress_Tags] AS [BillingAddress_Tags0], [c0].[BillingAddress_ZipCode] AS [BillingAddress_ZipCode0], [c0].[BillingAddress_Country_Code] AS [BillingAddress_Country_Code0], [c0].[BillingAddress_Country_FullName] AS [BillingAddress_Country_FullName0], [c0].[ShippingAddress_AddressLine1] AS [ShippingAddress_AddressLine10], [c0].[ShippingAddress_AddressLine2] AS [ShippingAddress_AddressLine20], [c0].[ShippingAddress_Tags] AS [ShippingAddress_Tags0], [c0].[ShippingAddress_ZipCode] AS [ShippingAddress_ZipCode0], [c0].[ShippingAddress_Country_Code] AS [ShippingAddress_Country_Code0], [c0].[ShippingAddress_Country_FullName] AS [ShippingAddress_Country_FullName0] + FROM [Customer] AS [c] + CROSS JOIN [Customer] AS [c0] + UNION + SELECT [c1].[Id], [c1].[Name], [c1].[BillingAddress_AddressLine1], [c1].[BillingAddress_AddressLine2], [c1].[BillingAddress_Tags], [c1].[BillingAddress_ZipCode], [c1].[BillingAddress_Country_Code], [c1].[BillingAddress_Country_FullName], [c1].[ShippingAddress_AddressLine1], [c1].[ShippingAddress_AddressLine2], [c1].[ShippingAddress_Tags], [c1].[ShippingAddress_ZipCode], [c1].[ShippingAddress_Country_Code], [c1].[ShippingAddress_Country_FullName], [c2].[Id] AS [Id0], [c2].[Name] AS [Name0], [c2].[BillingAddress_AddressLine1] AS [BillingAddress_AddressLine10], [c2].[BillingAddress_AddressLine2] AS [BillingAddress_AddressLine20], [c2].[BillingAddress_Tags] AS [BillingAddress_Tags0], [c2].[BillingAddress_ZipCode] AS [BillingAddress_ZipCode0], [c2].[BillingAddress_Country_Code] AS [BillingAddress_Country_Code0], [c2].[BillingAddress_Country_FullName] AS [BillingAddress_Country_FullName0], [c2].[ShippingAddress_AddressLine1] AS [ShippingAddress_AddressLine10], [c2].[ShippingAddress_AddressLine2] AS [ShippingAddress_AddressLine20], [c2].[ShippingAddress_Tags] AS [ShippingAddress_Tags0], [c2].[ShippingAddress_ZipCode] AS [ShippingAddress_ZipCode0], [c2].[ShippingAddress_Country_Code] AS [ShippingAddress_Country_Code0], [c2].[ShippingAddress_Country_FullName] AS [ShippingAddress_Country_FullName0] + FROM [Customer] AS [c1] + CROSS JOIN [Customer] AS [c2] + ) AS [u] + ORDER BY [u].[Id], [u].[Id0] + ) AS [u0] +) AS [u1] +ORDER BY [u1].[Id], [u1].[Id0] +"""); + } + + public override async Task Union_of_same_nested_complex_type_projected_twice_with_pushdown(bool async) + { + await base.Union_of_same_nested_complex_type_projected_twice_with_pushdown(async); + + AssertSql( +""" +@__p_0='50' + +SELECT TOP(@__p_0) [u].[BillingAddress_AddressLine1], [u].[BillingAddress_AddressLine2], [u].[BillingAddress_Tags], [u].[BillingAddress_ZipCode], [u].[BillingAddress_Country_Code], [u].[BillingAddress_Country_FullName], [u].[BillingAddress_AddressLine10], [u].[BillingAddress_AddressLine20], [u].[BillingAddress_Tags0], [u].[BillingAddress_ZipCode0], [u].[BillingAddress_Country_Code0], [u].[BillingAddress_Country_FullName0] +FROM ( + SELECT [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_Tags], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c0].[BillingAddress_AddressLine1] AS [BillingAddress_AddressLine10], [c0].[BillingAddress_AddressLine2] AS [BillingAddress_AddressLine20], [c0].[BillingAddress_Tags] AS [BillingAddress_Tags0], [c0].[BillingAddress_ZipCode] AS [BillingAddress_ZipCode0], [c0].[BillingAddress_Country_Code] AS [BillingAddress_Country_Code0], [c0].[BillingAddress_Country_FullName] AS [BillingAddress_Country_FullName0] + FROM [Customer] AS [c] + CROSS JOIN [Customer] AS [c0] + UNION + SELECT [c1].[BillingAddress_AddressLine1], [c1].[BillingAddress_AddressLine2], [c1].[BillingAddress_Tags], [c1].[BillingAddress_ZipCode], [c1].[BillingAddress_Country_Code], [c1].[BillingAddress_Country_FullName], [c2].[BillingAddress_AddressLine1] AS [BillingAddress_AddressLine10], [c2].[BillingAddress_AddressLine2] AS [BillingAddress_AddressLine20], [c2].[BillingAddress_Tags] AS [BillingAddress_Tags0], [c2].[BillingAddress_ZipCode] AS [BillingAddress_ZipCode0], [c2].[BillingAddress_Country_Code] AS [BillingAddress_Country_Code0], [c2].[BillingAddress_Country_FullName] AS [BillingAddress_Country_FullName0] + FROM [Customer] AS [c1] + CROSS JOIN [Customer] AS [c2] +) AS [u] +ORDER BY [u].[BillingAddress_ZipCode], [u].[BillingAddress_ZipCode0] +"""); + } + + public override async Task Union_of_same_nested_complex_type_projected_twice_with_double_pushdown(bool async) + { + await base.Union_of_same_nested_complex_type_projected_twice_with_double_pushdown(async); + + AssertSql( +""" +@__p_0='50' + +SELECT TOP(@__p_0) [u1].[BillingAddress_AddressLine1], [u1].[BillingAddress_AddressLine2], [u1].[BillingAddress_Tags], [u1].[BillingAddress_ZipCode], [u1].[BillingAddress_Country_Code], [u1].[BillingAddress_Country_FullName], [u1].[BillingAddress_AddressLine10], [u1].[BillingAddress_AddressLine20], [u1].[BillingAddress_Tags0], [u1].[BillingAddress_ZipCode0], [u1].[BillingAddress_Country_Code0], [u1].[BillingAddress_Country_FullName0] +FROM ( + SELECT DISTINCT [u0].[BillingAddress_AddressLine1], [u0].[BillingAddress_AddressLine2], [u0].[BillingAddress_Tags], [u0].[BillingAddress_ZipCode], [u0].[BillingAddress_Country_Code], [u0].[BillingAddress_Country_FullName], [u0].[BillingAddress_AddressLine10], [u0].[BillingAddress_AddressLine20], [u0].[BillingAddress_Tags0], [u0].[BillingAddress_ZipCode0], [u0].[BillingAddress_Country_Code0], [u0].[BillingAddress_Country_FullName0] + FROM ( + SELECT TOP(@__p_0) [u].[BillingAddress_AddressLine1], [u].[BillingAddress_AddressLine2], [u].[BillingAddress_Tags], [u].[BillingAddress_ZipCode], [u].[BillingAddress_Country_Code], [u].[BillingAddress_Country_FullName], [u].[BillingAddress_AddressLine10], [u].[BillingAddress_AddressLine20], [u].[BillingAddress_Tags0], [u].[BillingAddress_ZipCode0], [u].[BillingAddress_Country_Code0], [u].[BillingAddress_Country_FullName0] + FROM ( + SELECT [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_Tags], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c0].[BillingAddress_AddressLine1] AS [BillingAddress_AddressLine10], [c0].[BillingAddress_AddressLine2] AS [BillingAddress_AddressLine20], [c0].[BillingAddress_Tags] AS [BillingAddress_Tags0], [c0].[BillingAddress_ZipCode] AS [BillingAddress_ZipCode0], [c0].[BillingAddress_Country_Code] AS [BillingAddress_Country_Code0], [c0].[BillingAddress_Country_FullName] AS [BillingAddress_Country_FullName0] + FROM [Customer] AS [c] + CROSS JOIN [Customer] AS [c0] + UNION + SELECT [c1].[BillingAddress_AddressLine1], [c1].[BillingAddress_AddressLine2], [c1].[BillingAddress_Tags], [c1].[BillingAddress_ZipCode], [c1].[BillingAddress_Country_Code], [c1].[BillingAddress_Country_FullName], [c2].[BillingAddress_AddressLine1] AS [BillingAddress_AddressLine10], [c2].[BillingAddress_AddressLine2] AS [BillingAddress_AddressLine20], [c2].[BillingAddress_Tags] AS [BillingAddress_Tags0], [c2].[BillingAddress_ZipCode] AS [BillingAddress_ZipCode0], [c2].[BillingAddress_Country_Code] AS [BillingAddress_Country_Code0], [c2].[BillingAddress_Country_FullName] AS [BillingAddress_Country_FullName0] + FROM [Customer] AS [c1] + CROSS JOIN [Customer] AS [c2] + ) AS [u] + ORDER BY [u].[BillingAddress_ZipCode], [u].[BillingAddress_ZipCode0] + ) AS [u0] +) AS [u1] +ORDER BY [u1].[BillingAddress_ZipCode], [u1].[BillingAddress_ZipCode0] +"""); + } + + public override async Task Same_entity_with_complex_type_projected_twice_with_pushdown_as_part_of_another_projection(bool async) + { + await base.Same_entity_with_complex_type_projected_twice_with_pushdown_as_part_of_another_projection(async); + + AssertSql( +""" +SELECT [c].[Id], [s].[Id], [s].[Name], [s].[BillingAddress_AddressLine1], [s].[BillingAddress_AddressLine2], [s].[BillingAddress_Tags], [s].[BillingAddress_ZipCode], [s].[BillingAddress_Country_Code], [s].[BillingAddress_Country_FullName], [s].[ShippingAddress_AddressLine1], [s].[ShippingAddress_AddressLine2], [s].[ShippingAddress_Tags], [s].[ShippingAddress_ZipCode], [s].[ShippingAddress_Country_Code], [s].[ShippingAddress_Country_FullName], [s].[Id0], [s].[Name0], [s].[BillingAddress_AddressLine10], [s].[BillingAddress_AddressLine20], [s].[BillingAddress_Tags0], [s].[BillingAddress_ZipCode0], [s].[BillingAddress_Country_Code0], [s].[BillingAddress_Country_FullName0], [s].[ShippingAddress_AddressLine10], [s].[ShippingAddress_AddressLine20], [s].[ShippingAddress_Tags0], [s].[ShippingAddress_ZipCode0], [s].[ShippingAddress_Country_Code0], [s].[ShippingAddress_Country_FullName0], [s].[c] +FROM [Customer] AS [c] +OUTER APPLY ( + SELECT TOP(1) [c0].[Id], [c0].[Name], [c0].[BillingAddress_AddressLine1], [c0].[BillingAddress_AddressLine2], [c0].[BillingAddress_Tags], [c0].[BillingAddress_ZipCode], [c0].[BillingAddress_Country_Code], [c0].[BillingAddress_Country_FullName], [c0].[ShippingAddress_AddressLine1], [c0].[ShippingAddress_AddressLine2], [c0].[ShippingAddress_Tags], [c0].[ShippingAddress_ZipCode], [c0].[ShippingAddress_Country_Code], [c0].[ShippingAddress_Country_FullName], [c1].[Id] AS [Id0], [c1].[Name] AS [Name0], [c1].[BillingAddress_AddressLine1] AS [BillingAddress_AddressLine10], [c1].[BillingAddress_AddressLine2] AS [BillingAddress_AddressLine20], [c1].[BillingAddress_Tags] AS [BillingAddress_Tags0], [c1].[BillingAddress_ZipCode] AS [BillingAddress_ZipCode0], [c1].[BillingAddress_Country_Code] AS [BillingAddress_Country_Code0], [c1].[BillingAddress_Country_FullName] AS [BillingAddress_Country_FullName0], [c1].[ShippingAddress_AddressLine1] AS [ShippingAddress_AddressLine10], [c1].[ShippingAddress_AddressLine2] AS [ShippingAddress_AddressLine20], [c1].[ShippingAddress_Tags] AS [ShippingAddress_Tags0], [c1].[ShippingAddress_ZipCode] AS [ShippingAddress_ZipCode0], [c1].[ShippingAddress_Country_Code] AS [ShippingAddress_Country_Code0], [c1].[ShippingAddress_Country_FullName] AS [ShippingAddress_Country_FullName0], 1 AS [c] + FROM [Customer] AS [c0] + CROSS JOIN [Customer] AS [c1] + ORDER BY [c0].[Id], [c1].[Id] DESC +) AS [s] +"""); + } + + public override async Task Same_complex_type_projected_twice_with_pushdown_as_part_of_another_projection(bool async) + { + await base.Same_complex_type_projected_twice_with_pushdown_as_part_of_another_projection(async); + + AssertSql(""); + } + [ConditionalFact] public virtual void Check_all_tests_overridden() => TestHelpers.AssertAllMethodsOverridden(GetType()); diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/AdHocAdvancedMappingsQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/AdHocAdvancedMappingsQuerySqliteTest.cs index 2e0852f0dbd..1807f23b4a2 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/AdHocAdvancedMappingsQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/AdHocAdvancedMappingsQuerySqliteTest.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.ComponentModel.DataAnnotations; - namespace Microsoft.EntityFrameworkCore.Query; public class AdHocAdvancedMappingsQuerySqliteTest : AdHocAdvancedMappingsQueryRelationalTestBase diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/ComplexTypeQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/ComplexTypeQuerySqliteTest.cs index 0e2239a3b41..a09ea2e985b 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/ComplexTypeQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/ComplexTypeQuerySqliteTest.cs @@ -742,6 +742,205 @@ public override async Task Union_two_different_struct_complex_type(bool async) AssertSql(); } + public override async Task Project_same_entity_with_nested_complex_type_twice_with_pushdown(bool async) + { + await base.Project_same_entity_with_nested_complex_type_twice_with_pushdown(async); + + AssertSql( +""" +SELECT "s"."Id", "s"."Name", "s"."BillingAddress_AddressLine1", "s"."BillingAddress_AddressLine2", "s"."BillingAddress_Tags", "s"."BillingAddress_ZipCode", "s"."BillingAddress_Country_Code", "s"."BillingAddress_Country_FullName", "s"."ShippingAddress_AddressLine1", "s"."ShippingAddress_AddressLine2", "s"."ShippingAddress_Tags", "s"."ShippingAddress_ZipCode", "s"."ShippingAddress_Country_Code", "s"."ShippingAddress_Country_FullName", "s"."Id0", "s"."Name0", "s"."BillingAddress_AddressLine10", "s"."BillingAddress_AddressLine20", "s"."BillingAddress_Tags0", "s"."BillingAddress_ZipCode0", "s"."BillingAddress_Country_Code0", "s"."BillingAddress_Country_FullName0", "s"."ShippingAddress_AddressLine10", "s"."ShippingAddress_AddressLine20", "s"."ShippingAddress_Tags0", "s"."ShippingAddress_ZipCode0", "s"."ShippingAddress_Country_Code0", "s"."ShippingAddress_Country_FullName0" +FROM ( + SELECT DISTINCT "c"."Id", "c"."Name", "c"."BillingAddress_AddressLine1", "c"."BillingAddress_AddressLine2", "c"."BillingAddress_Tags", "c"."BillingAddress_ZipCode", "c"."BillingAddress_Country_Code", "c"."BillingAddress_Country_FullName", "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_Tags", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName", "c0"."Id" AS "Id0", "c0"."Name" AS "Name0", "c0"."BillingAddress_AddressLine1" AS "BillingAddress_AddressLine10", "c0"."BillingAddress_AddressLine2" AS "BillingAddress_AddressLine20", "c0"."BillingAddress_Tags" AS "BillingAddress_Tags0", "c0"."BillingAddress_ZipCode" AS "BillingAddress_ZipCode0", "c0"."BillingAddress_Country_Code" AS "BillingAddress_Country_Code0", "c0"."BillingAddress_Country_FullName" AS "BillingAddress_Country_FullName0", "c0"."ShippingAddress_AddressLine1" AS "ShippingAddress_AddressLine10", "c0"."ShippingAddress_AddressLine2" AS "ShippingAddress_AddressLine20", "c0"."ShippingAddress_Tags" AS "ShippingAddress_Tags0", "c0"."ShippingAddress_ZipCode" AS "ShippingAddress_ZipCode0", "c0"."ShippingAddress_Country_Code" AS "ShippingAddress_Country_Code0", "c0"."ShippingAddress_Country_FullName" AS "ShippingAddress_Country_FullName0" + FROM "Customer" AS "c" + CROSS JOIN "Customer" AS "c0" +) AS "s" +"""); + } + + public override async Task Project_same_nested_complex_type_twice_with_pushdown(bool async) + { + await base.Project_same_nested_complex_type_twice_with_pushdown(async); + + AssertSql( +""" +SELECT "s"."BillingAddress_AddressLine1", "s"."BillingAddress_AddressLine2", "s"."BillingAddress_Tags", "s"."BillingAddress_ZipCode", "s"."BillingAddress_Country_Code", "s"."BillingAddress_Country_FullName", "s"."BillingAddress_AddressLine10", "s"."BillingAddress_AddressLine20", "s"."BillingAddress_Tags0", "s"."BillingAddress_ZipCode0", "s"."BillingAddress_Country_Code0", "s"."BillingAddress_Country_FullName0" +FROM ( + SELECT DISTINCT "c"."BillingAddress_AddressLine1", "c"."BillingAddress_AddressLine2", "c"."BillingAddress_Tags", "c"."BillingAddress_ZipCode", "c"."BillingAddress_Country_Code", "c"."BillingAddress_Country_FullName", "c0"."BillingAddress_AddressLine1" AS "BillingAddress_AddressLine10", "c0"."BillingAddress_AddressLine2" AS "BillingAddress_AddressLine20", "c0"."BillingAddress_Tags" AS "BillingAddress_Tags0", "c0"."BillingAddress_ZipCode" AS "BillingAddress_ZipCode0", "c0"."BillingAddress_Country_Code" AS "BillingAddress_Country_Code0", "c0"."BillingAddress_Country_FullName" AS "BillingAddress_Country_FullName0" + FROM "Customer" AS "c" + CROSS JOIN "Customer" AS "c0" +) AS "s" +"""); + } + + public override async Task Project_same_entity_with_nested_complex_type_twice_with_double_pushdown(bool async) + { + await base.Project_same_entity_with_nested_complex_type_twice_with_double_pushdown(async); + + AssertSql( +""" +@__p_0='50' + +SELECT "s0"."Id", "s0"."Name", "s0"."BillingAddress_AddressLine1", "s0"."BillingAddress_AddressLine2", "s0"."BillingAddress_Tags", "s0"."BillingAddress_ZipCode", "s0"."BillingAddress_Country_Code", "s0"."BillingAddress_Country_FullName", "s0"."ShippingAddress_AddressLine1", "s0"."ShippingAddress_AddressLine2", "s0"."ShippingAddress_Tags", "s0"."ShippingAddress_ZipCode", "s0"."ShippingAddress_Country_Code", "s0"."ShippingAddress_Country_FullName", "s0"."Id0", "s0"."Name0", "s0"."BillingAddress_AddressLine10", "s0"."BillingAddress_AddressLine20", "s0"."BillingAddress_Tags0", "s0"."BillingAddress_ZipCode0", "s0"."BillingAddress_Country_Code0", "s0"."BillingAddress_Country_FullName0", "s0"."ShippingAddress_AddressLine10", "s0"."ShippingAddress_AddressLine20", "s0"."ShippingAddress_Tags0", "s0"."ShippingAddress_ZipCode0", "s0"."ShippingAddress_Country_Code0", "s0"."ShippingAddress_Country_FullName0" +FROM ( + SELECT DISTINCT "s"."Id", "s"."Name", "s"."BillingAddress_AddressLine1", "s"."BillingAddress_AddressLine2", "s"."BillingAddress_Tags", "s"."BillingAddress_ZipCode", "s"."BillingAddress_Country_Code", "s"."BillingAddress_Country_FullName", "s"."ShippingAddress_AddressLine1", "s"."ShippingAddress_AddressLine2", "s"."ShippingAddress_Tags", "s"."ShippingAddress_ZipCode", "s"."ShippingAddress_Country_Code", "s"."ShippingAddress_Country_FullName", "s"."Id0", "s"."Name0", "s"."BillingAddress_AddressLine10", "s"."BillingAddress_AddressLine20", "s"."BillingAddress_Tags0", "s"."BillingAddress_ZipCode0", "s"."BillingAddress_Country_Code0", "s"."BillingAddress_Country_FullName0", "s"."ShippingAddress_AddressLine10", "s"."ShippingAddress_AddressLine20", "s"."ShippingAddress_Tags0", "s"."ShippingAddress_ZipCode0", "s"."ShippingAddress_Country_Code0", "s"."ShippingAddress_Country_FullName0" + FROM ( + SELECT "c"."Id", "c"."Name", "c"."BillingAddress_AddressLine1", "c"."BillingAddress_AddressLine2", "c"."BillingAddress_Tags", "c"."BillingAddress_ZipCode", "c"."BillingAddress_Country_Code", "c"."BillingAddress_Country_FullName", "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_Tags", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName", "c0"."Id" AS "Id0", "c0"."Name" AS "Name0", "c0"."BillingAddress_AddressLine1" AS "BillingAddress_AddressLine10", "c0"."BillingAddress_AddressLine2" AS "BillingAddress_AddressLine20", "c0"."BillingAddress_Tags" AS "BillingAddress_Tags0", "c0"."BillingAddress_ZipCode" AS "BillingAddress_ZipCode0", "c0"."BillingAddress_Country_Code" AS "BillingAddress_Country_Code0", "c0"."BillingAddress_Country_FullName" AS "BillingAddress_Country_FullName0", "c0"."ShippingAddress_AddressLine1" AS "ShippingAddress_AddressLine10", "c0"."ShippingAddress_AddressLine2" AS "ShippingAddress_AddressLine20", "c0"."ShippingAddress_Tags" AS "ShippingAddress_Tags0", "c0"."ShippingAddress_ZipCode" AS "ShippingAddress_ZipCode0", "c0"."ShippingAddress_Country_Code" AS "ShippingAddress_Country_Code0", "c0"."ShippingAddress_Country_FullName" AS "ShippingAddress_Country_FullName0" + FROM "Customer" AS "c" + CROSS JOIN "Customer" AS "c0" + ORDER BY "c"."Id", "c0"."Id" + LIMIT @__p_0 + ) AS "s" +) AS "s0" +"""); + } + + public override async Task Project_same_nested_complex_type_twice_with_double_pushdown(bool async) + { + await base.Project_same_nested_complex_type_twice_with_double_pushdown(async); + + AssertSql( +""" +@__p_0='50' + +SELECT "s0"."BillingAddress_AddressLine1", "s0"."BillingAddress_AddressLine2", "s0"."BillingAddress_Tags", "s0"."BillingAddress_ZipCode", "s0"."BillingAddress_Country_Code", "s0"."BillingAddress_Country_FullName", "s0"."BillingAddress_AddressLine10", "s0"."BillingAddress_AddressLine20", "s0"."BillingAddress_Tags0", "s0"."BillingAddress_ZipCode0", "s0"."BillingAddress_Country_Code0", "s0"."BillingAddress_Country_FullName0" +FROM ( + SELECT DISTINCT "s"."BillingAddress_AddressLine1", "s"."BillingAddress_AddressLine2", "s"."BillingAddress_Tags", "s"."BillingAddress_ZipCode", "s"."BillingAddress_Country_Code", "s"."BillingAddress_Country_FullName", "s"."BillingAddress_AddressLine10", "s"."BillingAddress_AddressLine20", "s"."BillingAddress_Tags0", "s"."BillingAddress_ZipCode0", "s"."BillingAddress_Country_Code0", "s"."BillingAddress_Country_FullName0" + FROM ( + SELECT "c"."BillingAddress_AddressLine1", "c"."BillingAddress_AddressLine2", "c"."BillingAddress_Tags", "c"."BillingAddress_ZipCode", "c"."BillingAddress_Country_Code", "c"."BillingAddress_Country_FullName", "c0"."BillingAddress_AddressLine1" AS "BillingAddress_AddressLine10", "c0"."BillingAddress_AddressLine2" AS "BillingAddress_AddressLine20", "c0"."BillingAddress_Tags" AS "BillingAddress_Tags0", "c0"."BillingAddress_ZipCode" AS "BillingAddress_ZipCode0", "c0"."BillingAddress_Country_Code" AS "BillingAddress_Country_Code0", "c0"."BillingAddress_Country_FullName" AS "BillingAddress_Country_FullName0" + FROM "Customer" AS "c" + CROSS JOIN "Customer" AS "c0" + ORDER BY "c"."Id", "c0"."Id" + LIMIT @__p_0 + ) AS "s" +) AS "s0" +"""); + } + + public override async Task Project_same_entity_with_struct_nested_complex_type_twice_with_pushdown(bool async) + { + await base.Project_same_entity_with_struct_nested_complex_type_twice_with_pushdown(async); + + AssertSql( +""" +SELECT "s"."Id", "s"."Name", "s"."BillingAddress_AddressLine1", "s"."BillingAddress_AddressLine2", "s"."BillingAddress_ZipCode", "s"."BillingAddress_Country_Code", "s"."BillingAddress_Country_FullName", "s"."ShippingAddress_AddressLine1", "s"."ShippingAddress_AddressLine2", "s"."ShippingAddress_ZipCode", "s"."ShippingAddress_Country_Code", "s"."ShippingAddress_Country_FullName", "s"."Id0", "s"."Name0", "s"."BillingAddress_AddressLine10", "s"."BillingAddress_AddressLine20", "s"."BillingAddress_ZipCode0", "s"."BillingAddress_Country_Code0", "s"."BillingAddress_Country_FullName0", "s"."ShippingAddress_AddressLine10", "s"."ShippingAddress_AddressLine20", "s"."ShippingAddress_ZipCode0", "s"."ShippingAddress_Country_Code0", "s"."ShippingAddress_Country_FullName0" +FROM ( + SELECT DISTINCT "v"."Id", "v"."Name", "v"."BillingAddress_AddressLine1", "v"."BillingAddress_AddressLine2", "v"."BillingAddress_ZipCode", "v"."BillingAddress_Country_Code", "v"."BillingAddress_Country_FullName", "v"."ShippingAddress_AddressLine1", "v"."ShippingAddress_AddressLine2", "v"."ShippingAddress_ZipCode", "v"."ShippingAddress_Country_Code", "v"."ShippingAddress_Country_FullName", "v0"."Id" AS "Id0", "v0"."Name" AS "Name0", "v0"."BillingAddress_AddressLine1" AS "BillingAddress_AddressLine10", "v0"."BillingAddress_AddressLine2" AS "BillingAddress_AddressLine20", "v0"."BillingAddress_ZipCode" AS "BillingAddress_ZipCode0", "v0"."BillingAddress_Country_Code" AS "BillingAddress_Country_Code0", "v0"."BillingAddress_Country_FullName" AS "BillingAddress_Country_FullName0", "v0"."ShippingAddress_AddressLine1" AS "ShippingAddress_AddressLine10", "v0"."ShippingAddress_AddressLine2" AS "ShippingAddress_AddressLine20", "v0"."ShippingAddress_ZipCode" AS "ShippingAddress_ZipCode0", "v0"."ShippingAddress_Country_Code" AS "ShippingAddress_Country_Code0", "v0"."ShippingAddress_Country_FullName" AS "ShippingAddress_Country_FullName0" + FROM "ValuedCustomer" AS "v" + CROSS JOIN "ValuedCustomer" AS "v0" +) AS "s" +"""); + } + + public override async Task Project_same_struct_nested_complex_type_twice_with_pushdown(bool async) + { + await base.Project_same_struct_nested_complex_type_twice_with_pushdown(async); + + AssertSql( +""" +SELECT "s"."BillingAddress_AddressLine1", "s"."BillingAddress_AddressLine2", "s"."BillingAddress_ZipCode", "s"."BillingAddress_Country_Code", "s"."BillingAddress_Country_FullName", "s"."BillingAddress_AddressLine10", "s"."BillingAddress_AddressLine20", "s"."BillingAddress_ZipCode0", "s"."BillingAddress_Country_Code0", "s"."BillingAddress_Country_FullName0" +FROM ( + SELECT DISTINCT "v"."BillingAddress_AddressLine1", "v"."BillingAddress_AddressLine2", "v"."BillingAddress_ZipCode", "v"."BillingAddress_Country_Code", "v"."BillingAddress_Country_FullName", "v0"."BillingAddress_AddressLine1" AS "BillingAddress_AddressLine10", "v0"."BillingAddress_AddressLine2" AS "BillingAddress_AddressLine20", "v0"."BillingAddress_ZipCode" AS "BillingAddress_ZipCode0", "v0"."BillingAddress_Country_Code" AS "BillingAddress_Country_Code0", "v0"."BillingAddress_Country_FullName" AS "BillingAddress_Country_FullName0" + FROM "ValuedCustomer" AS "v" + CROSS JOIN "ValuedCustomer" AS "v0" +) AS "s" +"""); + } + + public override async Task Project_same_entity_with_struct_nested_complex_type_twice_with_double_pushdown(bool async) + { + await base.Project_same_entity_with_struct_nested_complex_type_twice_with_double_pushdown(async); + + AssertSql( +""" +@__p_0='50' + +SELECT "s0"."Id", "s0"."Name", "s0"."BillingAddress_AddressLine1", "s0"."BillingAddress_AddressLine2", "s0"."BillingAddress_ZipCode", "s0"."BillingAddress_Country_Code", "s0"."BillingAddress_Country_FullName", "s0"."ShippingAddress_AddressLine1", "s0"."ShippingAddress_AddressLine2", "s0"."ShippingAddress_ZipCode", "s0"."ShippingAddress_Country_Code", "s0"."ShippingAddress_Country_FullName", "s0"."Id0", "s0"."Name0", "s0"."BillingAddress_AddressLine10", "s0"."BillingAddress_AddressLine20", "s0"."BillingAddress_ZipCode0", "s0"."BillingAddress_Country_Code0", "s0"."BillingAddress_Country_FullName0", "s0"."ShippingAddress_AddressLine10", "s0"."ShippingAddress_AddressLine20", "s0"."ShippingAddress_ZipCode0", "s0"."ShippingAddress_Country_Code0", "s0"."ShippingAddress_Country_FullName0" +FROM ( + SELECT DISTINCT "s"."Id", "s"."Name", "s"."BillingAddress_AddressLine1", "s"."BillingAddress_AddressLine2", "s"."BillingAddress_ZipCode", "s"."BillingAddress_Country_Code", "s"."BillingAddress_Country_FullName", "s"."ShippingAddress_AddressLine1", "s"."ShippingAddress_AddressLine2", "s"."ShippingAddress_ZipCode", "s"."ShippingAddress_Country_Code", "s"."ShippingAddress_Country_FullName", "s"."Id0", "s"."Name0", "s"."BillingAddress_AddressLine10", "s"."BillingAddress_AddressLine20", "s"."BillingAddress_ZipCode0", "s"."BillingAddress_Country_Code0", "s"."BillingAddress_Country_FullName0", "s"."ShippingAddress_AddressLine10", "s"."ShippingAddress_AddressLine20", "s"."ShippingAddress_ZipCode0", "s"."ShippingAddress_Country_Code0", "s"."ShippingAddress_Country_FullName0" + FROM ( + SELECT "v"."Id", "v"."Name", "v"."BillingAddress_AddressLine1", "v"."BillingAddress_AddressLine2", "v"."BillingAddress_ZipCode", "v"."BillingAddress_Country_Code", "v"."BillingAddress_Country_FullName", "v"."ShippingAddress_AddressLine1", "v"."ShippingAddress_AddressLine2", "v"."ShippingAddress_ZipCode", "v"."ShippingAddress_Country_Code", "v"."ShippingAddress_Country_FullName", "v0"."Id" AS "Id0", "v0"."Name" AS "Name0", "v0"."BillingAddress_AddressLine1" AS "BillingAddress_AddressLine10", "v0"."BillingAddress_AddressLine2" AS "BillingAddress_AddressLine20", "v0"."BillingAddress_ZipCode" AS "BillingAddress_ZipCode0", "v0"."BillingAddress_Country_Code" AS "BillingAddress_Country_Code0", "v0"."BillingAddress_Country_FullName" AS "BillingAddress_Country_FullName0", "v0"."ShippingAddress_AddressLine1" AS "ShippingAddress_AddressLine10", "v0"."ShippingAddress_AddressLine2" AS "ShippingAddress_AddressLine20", "v0"."ShippingAddress_ZipCode" AS "ShippingAddress_ZipCode0", "v0"."ShippingAddress_Country_Code" AS "ShippingAddress_Country_Code0", "v0"."ShippingAddress_Country_FullName" AS "ShippingAddress_Country_FullName0" + FROM "ValuedCustomer" AS "v" + CROSS JOIN "ValuedCustomer" AS "v0" + ORDER BY "v"."Id", "v0"."Id" + LIMIT @__p_0 + ) AS "s" +) AS "s0" +"""); + } + + public override async Task Project_same_struct_nested_complex_type_twice_with_double_pushdown(bool async) + { + await base.Project_same_struct_nested_complex_type_twice_with_double_pushdown(async); + + AssertSql( +""" +@__p_0='50' + +SELECT "s0"."BillingAddress_AddressLine1", "s0"."BillingAddress_AddressLine2", "s0"."BillingAddress_ZipCode", "s0"."BillingAddress_Country_Code", "s0"."BillingAddress_Country_FullName", "s0"."BillingAddress_AddressLine10", "s0"."BillingAddress_AddressLine20", "s0"."BillingAddress_ZipCode0", "s0"."BillingAddress_Country_Code0", "s0"."BillingAddress_Country_FullName0" +FROM ( + SELECT DISTINCT "s"."BillingAddress_AddressLine1", "s"."BillingAddress_AddressLine2", "s"."BillingAddress_ZipCode", "s"."BillingAddress_Country_Code", "s"."BillingAddress_Country_FullName", "s"."BillingAddress_AddressLine10", "s"."BillingAddress_AddressLine20", "s"."BillingAddress_ZipCode0", "s"."BillingAddress_Country_Code0", "s"."BillingAddress_Country_FullName0" + FROM ( + SELECT "v"."BillingAddress_AddressLine1", "v"."BillingAddress_AddressLine2", "v"."BillingAddress_ZipCode", "v"."BillingAddress_Country_Code", "v"."BillingAddress_Country_FullName", "v0"."BillingAddress_AddressLine1" AS "BillingAddress_AddressLine10", "v0"."BillingAddress_AddressLine2" AS "BillingAddress_AddressLine20", "v0"."BillingAddress_ZipCode" AS "BillingAddress_ZipCode0", "v0"."BillingAddress_Country_Code" AS "BillingAddress_Country_Code0", "v0"."BillingAddress_Country_FullName" AS "BillingAddress_Country_FullName0" + FROM "ValuedCustomer" AS "v" + CROSS JOIN "ValuedCustomer" AS "v0" + ORDER BY "v"."Id", "v0"."Id" + LIMIT @__p_0 + ) AS "s" +) AS "s0" +"""); + } + + public override async Task Union_of_same_entity_with_nested_complex_type_projected_twice_with_pushdown(bool async) + { + await base.Union_of_same_entity_with_nested_complex_type_projected_twice_with_pushdown(async); + + AssertSql( +""" +@__p_0='50' + +SELECT "u"."Id", "u"."Name", "u"."BillingAddress_AddressLine1", "u"."BillingAddress_AddressLine2", "u"."BillingAddress_Tags", "u"."BillingAddress_ZipCode", "u"."BillingAddress_Country_Code", "u"."BillingAddress_Country_FullName", "u"."ShippingAddress_AddressLine1", "u"."ShippingAddress_AddressLine2", "u"."ShippingAddress_Tags", "u"."ShippingAddress_ZipCode", "u"."ShippingAddress_Country_Code", "u"."ShippingAddress_Country_FullName", "u"."Id0", "u"."Name0", "u"."BillingAddress_AddressLine10", "u"."BillingAddress_AddressLine20", "u"."BillingAddress_Tags0", "u"."BillingAddress_ZipCode0", "u"."BillingAddress_Country_Code0", "u"."BillingAddress_Country_FullName0", "u"."ShippingAddress_AddressLine10", "u"."ShippingAddress_AddressLine20", "u"."ShippingAddress_Tags0", "u"."ShippingAddress_ZipCode0", "u"."ShippingAddress_Country_Code0", "u"."ShippingAddress_Country_FullName0" +FROM ( + SELECT "c"."Id", "c"."Name", "c"."BillingAddress_AddressLine1", "c"."BillingAddress_AddressLine2", "c"."BillingAddress_Tags", "c"."BillingAddress_ZipCode", "c"."BillingAddress_Country_Code", "c"."BillingAddress_Country_FullName", "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_Tags", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName", "c0"."Id" AS "Id0", "c0"."Name" AS "Name0", "c0"."BillingAddress_AddressLine1" AS "BillingAddress_AddressLine10", "c0"."BillingAddress_AddressLine2" AS "BillingAddress_AddressLine20", "c0"."BillingAddress_Tags" AS "BillingAddress_Tags0", "c0"."BillingAddress_ZipCode" AS "BillingAddress_ZipCode0", "c0"."BillingAddress_Country_Code" AS "BillingAddress_Country_Code0", "c0"."BillingAddress_Country_FullName" AS "BillingAddress_Country_FullName0", "c0"."ShippingAddress_AddressLine1" AS "ShippingAddress_AddressLine10", "c0"."ShippingAddress_AddressLine2" AS "ShippingAddress_AddressLine20", "c0"."ShippingAddress_Tags" AS "ShippingAddress_Tags0", "c0"."ShippingAddress_ZipCode" AS "ShippingAddress_ZipCode0", "c0"."ShippingAddress_Country_Code" AS "ShippingAddress_Country_Code0", "c0"."ShippingAddress_Country_FullName" AS "ShippingAddress_Country_FullName0" + FROM "Customer" AS "c" + CROSS JOIN "Customer" AS "c0" + UNION + SELECT "c1"."Id", "c1"."Name", "c1"."BillingAddress_AddressLine1", "c1"."BillingAddress_AddressLine2", "c1"."BillingAddress_Tags", "c1"."BillingAddress_ZipCode", "c1"."BillingAddress_Country_Code", "c1"."BillingAddress_Country_FullName", "c1"."ShippingAddress_AddressLine1", "c1"."ShippingAddress_AddressLine2", "c1"."ShippingAddress_Tags", "c1"."ShippingAddress_ZipCode", "c1"."ShippingAddress_Country_Code", "c1"."ShippingAddress_Country_FullName", "c2"."Id" AS "Id0", "c2"."Name" AS "Name0", "c2"."BillingAddress_AddressLine1" AS "BillingAddress_AddressLine10", "c2"."BillingAddress_AddressLine2" AS "BillingAddress_AddressLine20", "c2"."BillingAddress_Tags" AS "BillingAddress_Tags0", "c2"."BillingAddress_ZipCode" AS "BillingAddress_ZipCode0", "c2"."BillingAddress_Country_Code" AS "BillingAddress_Country_Code0", "c2"."BillingAddress_Country_FullName" AS "BillingAddress_Country_FullName0", "c2"."ShippingAddress_AddressLine1" AS "ShippingAddress_AddressLine10", "c2"."ShippingAddress_AddressLine2" AS "ShippingAddress_AddressLine20", "c2"."ShippingAddress_Tags" AS "ShippingAddress_Tags0", "c2"."ShippingAddress_ZipCode" AS "ShippingAddress_ZipCode0", "c2"."ShippingAddress_Country_Code" AS "ShippingAddress_Country_Code0", "c2"."ShippingAddress_Country_FullName" AS "ShippingAddress_Country_FullName0" + FROM "Customer" AS "c1" + CROSS JOIN "Customer" AS "c2" +) AS "u" +ORDER BY "u"."Id", "u"."Id0" +LIMIT @__p_0 +"""); + } + + public override async Task Union_of_same_entity_with_nested_complex_type_projected_twice_with_double_pushdown(bool async) + { + await base.Union_of_same_entity_with_nested_complex_type_projected_twice_with_double_pushdown(async); + + AssertSql( +""" +@__p_0='50' + +SELECT "u0"."Id", "u0"."Name", "u0"."BillingAddress_AddressLine1", "u0"."BillingAddress_AddressLine2", "u0"."BillingAddress_Tags", "u0"."BillingAddress_ZipCode", "u0"."BillingAddress_Country_Code", "u0"."BillingAddress_Country_FullName", "u0"."ShippingAddress_AddressLine1", "u0"."ShippingAddress_AddressLine2", "u0"."ShippingAddress_Tags", "u0"."ShippingAddress_ZipCode", "u0"."ShippingAddress_Country_Code", "u0"."ShippingAddress_Country_FullName", "u0"."Id0", "u0"."Name0", "u0"."BillingAddress_AddressLine10", "u0"."BillingAddress_AddressLine20", "u0"."BillingAddress_Tags0", "u0"."BillingAddress_ZipCode0", "u0"."BillingAddress_Country_Code0", "u0"."BillingAddress_Country_FullName0", "u0"."ShippingAddress_AddressLine10", "u0"."ShippingAddress_AddressLine20", "u0"."ShippingAddress_Tags0", "u0"."ShippingAddress_ZipCode0", "u0"."ShippingAddress_Country_Code0", "u0"."ShippingAddress_Country_FullName0" +FROM ( + SELECT "u"."Id", "u"."Name", "u"."BillingAddress_AddressLine1", "u"."BillingAddress_AddressLine2", "u"."BillingAddress_Tags", "u"."BillingAddress_ZipCode", "u"."BillingAddress_Country_Code", "u"."BillingAddress_Country_FullName", "u"."ShippingAddress_AddressLine1", "u"."ShippingAddress_AddressLine2", "u"."ShippingAddress_Tags", "u"."ShippingAddress_ZipCode", "u"."ShippingAddress_Country_Code", "u"."ShippingAddress_Country_FullName", "u"."Id0", "u"."Name0", "u"."BillingAddress_AddressLine10", "u"."BillingAddress_AddressLine20", "u"."BillingAddress_Tags0", "u"."BillingAddress_ZipCode0", "u"."BillingAddress_Country_Code0", "u"."BillingAddress_Country_FullName0", "u"."ShippingAddress_AddressLine10", "u"."ShippingAddress_AddressLine20", "u"."ShippingAddress_Tags0", "u"."ShippingAddress_ZipCode0", "u"."ShippingAddress_Country_Code0", "u"."ShippingAddress_Country_FullName0" + FROM ( + SELECT "c"."Id", "c"."Name", "c"."BillingAddress_AddressLine1", "c"."BillingAddress_AddressLine2", "c"."BillingAddress_Tags", "c"."BillingAddress_ZipCode", "c"."BillingAddress_Country_Code", "c"."BillingAddress_Country_FullName", "c"."ShippingAddress_AddressLine1", "c"."ShippingAddress_AddressLine2", "c"."ShippingAddress_Tags", "c"."ShippingAddress_ZipCode", "c"."ShippingAddress_Country_Code", "c"."ShippingAddress_Country_FullName", "c0"."Id" AS "Id0", "c0"."Name" AS "Name0", "c0"."BillingAddress_AddressLine1" AS "BillingAddress_AddressLine10", "c0"."BillingAddress_AddressLine2" AS "BillingAddress_AddressLine20", "c0"."BillingAddress_Tags" AS "BillingAddress_Tags0", "c0"."BillingAddress_ZipCode" AS "BillingAddress_ZipCode0", "c0"."BillingAddress_Country_Code" AS "BillingAddress_Country_Code0", "c0"."BillingAddress_Country_FullName" AS "BillingAddress_Country_FullName0", "c0"."ShippingAddress_AddressLine1" AS "ShippingAddress_AddressLine10", "c0"."ShippingAddress_AddressLine2" AS "ShippingAddress_AddressLine20", "c0"."ShippingAddress_Tags" AS "ShippingAddress_Tags0", "c0"."ShippingAddress_ZipCode" AS "ShippingAddress_ZipCode0", "c0"."ShippingAddress_Country_Code" AS "ShippingAddress_Country_Code0", "c0"."ShippingAddress_Country_FullName" AS "ShippingAddress_Country_FullName0" + FROM "Customer" AS "c" + CROSS JOIN "Customer" AS "c0" + UNION + SELECT "c1"."Id", "c1"."Name", "c1"."BillingAddress_AddressLine1", "c1"."BillingAddress_AddressLine2", "c1"."BillingAddress_Tags", "c1"."BillingAddress_ZipCode", "c1"."BillingAddress_Country_Code", "c1"."BillingAddress_Country_FullName", "c1"."ShippingAddress_AddressLine1", "c1"."ShippingAddress_AddressLine2", "c1"."ShippingAddress_Tags", "c1"."ShippingAddress_ZipCode", "c1"."ShippingAddress_Country_Code", "c1"."ShippingAddress_Country_FullName", "c2"."Id" AS "Id0", "c2"."Name" AS "Name0", "c2"."BillingAddress_AddressLine1" AS "BillingAddress_AddressLine10", "c2"."BillingAddress_AddressLine2" AS "BillingAddress_AddressLine20", "c2"."BillingAddress_Tags" AS "BillingAddress_Tags0", "c2"."BillingAddress_ZipCode" AS "BillingAddress_ZipCode0", "c2"."BillingAddress_Country_Code" AS "BillingAddress_Country_Code0", "c2"."BillingAddress_Country_FullName" AS "BillingAddress_Country_FullName0", "c2"."ShippingAddress_AddressLine1" AS "ShippingAddress_AddressLine10", "c2"."ShippingAddress_AddressLine2" AS "ShippingAddress_AddressLine20", "c2"."ShippingAddress_Tags" AS "ShippingAddress_Tags0", "c2"."ShippingAddress_ZipCode" AS "ShippingAddress_ZipCode0", "c2"."ShippingAddress_Country_Code" AS "ShippingAddress_Country_Code0", "c2"."ShippingAddress_Country_FullName" AS "ShippingAddress_Country_FullName0" + FROM "Customer" AS "c1" + CROSS JOIN "Customer" AS "c2" + ) AS "u" + ORDER BY "u"."Id", "u"."Id0" + LIMIT @__p_0 +) AS "u0" +ORDER BY "u0"."Id", "u0"."Id0" +LIMIT @__p_0 +"""); + } + [ConditionalFact] public virtual void Check_all_tests_overridden() => TestHelpers.AssertAllMethodsOverridden(GetType());