diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosAliasManager.cs b/src/EFCore.Cosmos/Query/Internal/CosmosAliasManager.cs
new file mode 100644
index 00000000000..3e686495b86
--- /dev/null
+++ b/src/EFCore.Cosmos/Query/Internal/CosmosAliasManager.cs
@@ -0,0 +1,238 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections;
+
+namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
+
+///
+/// A stateful manager for SQL aliases, capable of generating uniquified source aliases and rewriting them in post-processing.
+/// An instance of is valid for a single query compilation, and is owned by
+/// .
+///
+///
+/// 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.
+///
+public class CosmosAliasManager
+{
+ ///
+ /// Maps alias prefixes to the highest number postfix currently in use.
+ ///
+ ///
+ /// 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.
+ ///
+ private readonly Dictionary _aliases = new();
+
+ ///
+ /// Generates an alias based on the given .
+ /// All aliases produced by a given instance of are unique.
+ ///
+ ///
+ /// An expression to use as the starting point for the alias; this method knows a number of well-known expression types and can
+ /// generate appropriate aliases for them. A number postfix will be appended to it as necessary.
+ ///
+ ///
+ /// If isn't a well-known expression type, this fallback string will be used.
+ ///
+ /// A fully unique alias within the context of this translation process.
+ ///
+ /// 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.
+ ///
+ public virtual string GenerateSourceAlias(Expression expression, string? fallback = null)
+ => GenerateSourceAlias(expression switch
+ {
+ IAccessExpression { PropertyName: string propertyName } => propertyName,
+ FromSqlExpression => "sql",
+ SqlFunctionExpression { Name: "ARRAY_SLICE", Arguments: [var array, ..] } => GenerateSourceAlias(array),
+ ObjectFunctionExpression { Name: "ARRAY_SLICE", Arguments: [var array, ..] } => GenerateSourceAlias(array),
+ SqlFunctionExpression { Name: var name } => name,
+ ObjectFunctionExpression { Name: var name } => name,
+ ArrayConstantExpression => "array",
+
+ _ => fallback ?? "value"
+ });
+
+ ///
+ /// Generates an alias based on the given .
+ /// All aliases produced by a given instance of are unique.
+ ///
+ ///
+ /// A name (e.g. of a container) to use as the starting point for the alias; a number postfix will be appended to it as necessary.
+ ///
+ /// A fully unique alias within the context of this translation process.
+ ///
+ /// 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.
+ ///
+ public virtual string GenerateSourceAlias(string name)
+ {
+ var firstChar = char.ToLowerInvariant(name[0]);
+
+ if (_aliases.TryGetValue(firstChar, out var counter))
+ {
+ return firstChar.ToString() + counter.Value++;
+ }
+
+ _aliases[firstChar] = new MutableInt { Value = 0 };
+ return firstChar.ToString();
+ }
+
+ ///
+ /// Performs a post-processing pass over aliases in the provided SQL tree, closing any gaps.
+ ///
+ /// The SQL tree to post-process.
+ ///
+ /// 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.
+ ///
+ public virtual Expression PostprocessAliases(Expression expression)
+ {
+ // To post-process (finalize) source aliases in the tree, we visit it to see which aliases are actually in use.
+ // We then remap those alias, e.g. closing any gaps caused by tables getting pruned, etc.
+ // Finally, we revisit the tree in order to apply the remapped aliases.
+
+ var sourceAliases = SourceAliasCollector.Collect(expression);
+
+ var aliasRewritingMap = RemapSourceAliases(sourceAliases);
+
+ return aliasRewritingMap is null
+ ? expression
+ : SourceAliasRewriter.Rewrite(expression, aliasRewritingMap);
+ }
+
+ ///
+ /// Given the list of source aliases currently in use in the SQL tree, produces a remapping for aliases within that list.
+ /// Can be used to e.g. close gaps for sources which have been pruned, etc.
+ ///
+ ///
+ /// 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.
+ ///
+ protected virtual Dictionary? RemapSourceAliases(IReadOnlySet usedAliases)
+ {
+ // Aliases consist of a single character, followed by a counter for uniquification.
+ // We process the collected aliases above into a bitmap that represents, for each alias char, which numbers have been seen.
+ // Note that since a0 is the 2nd uniquified alias (a is the first), the bits are off-by-one, with position 0 representing
+ // a, position 1 representing a0, and so on.
+ Dictionary aliasBitmaps = new();
+
+ foreach (var alias in usedAliases)
+ {
+ var aliasBase = alias[0];
+ var aliasNum = alias.Length == 1 ? 0 : int.Parse(alias[1..]) + 1;
+
+ if (aliasBitmaps.TryGetValue(aliasBase, out var bitmap))
+ {
+ if (bitmap.Length < aliasNum + 1)
+ {
+ bitmap.Length = aliasNum + 1;
+ }
+ }
+ else
+ {
+ bitmap = aliasBitmaps[aliasBase] = new(aliasNum + 1);
+ }
+
+ bitmap[aliasNum] = true;
+ }
+
+ Dictionary? aliasRewritingMap = null;
+ foreach (var (aliasBase, bitmap) in aliasBitmaps)
+ {
+ if (bitmap.HasAllSet())
+ {
+ // There are no gaps, no need to do any rewriting of the aliases for this alias base
+ continue;
+ }
+
+ var numHoles = 0;
+ for (var i = 0; i < bitmap.Length; i++)
+ {
+ if (!bitmap[i])
+ {
+ numHoles++;
+ }
+ else if (numHoles > 0)
+ {
+ var oldAlias = aliasBase + (i == 0 ? "" : (i - 1).ToString());
+ var j = i - numHoles;
+ var newAlias = aliasBase + (j == 0 ? "" : (j - 1).ToString());
+
+ aliasRewritingMap ??= new();
+ aliasRewritingMap[oldAlias] = newAlias;
+ }
+ }
+ }
+
+ return aliasRewritingMap;
+ }
+
+ private sealed class SourceAliasCollector : ExpressionVisitor
+ {
+ private readonly HashSet _sourceAliases = new();
+
+ internal static HashSet Collect(Expression expression)
+ {
+ var collector = new SourceAliasCollector();
+ collector.Visit(expression);
+ return collector._sourceAliases;
+ }
+
+ protected override Expression VisitExtension(Expression node)
+ {
+ switch (node)
+ {
+ case ShapedQueryExpression shapedQuery:
+ return shapedQuery.UpdateQueryExpression(Visit(shapedQuery.QueryExpression));
+
+ case SourceExpression { Alias: string alias }:
+ _sourceAliases.Add(alias);
+ return base.VisitExtension(node);
+
+ default:
+ return base.VisitExtension(node);
+ }
+ }
+ }
+
+ private sealed class SourceAliasRewriter(IReadOnlyDictionary aliasRewritingMap) : ExpressionVisitor
+ {
+ internal static Expression Rewrite(Expression expression, IReadOnlyDictionary aliasRewritingMap)
+ => new SourceAliasRewriter(aliasRewritingMap).Visit(expression);
+
+ protected override Expression VisitExtension(Expression node)
+ => node switch
+ {
+ ShapedQueryExpression shapedQuery => shapedQuery.UpdateQueryExpression(Visit(shapedQuery.QueryExpression)),
+
+ SourceExpression { Alias: string alias } source when aliasRewritingMap.TryGetValue(alias, out var newAlias)
+ => base.VisitExtension(new SourceExpression(source.Expression, newAlias, source.WithIn)),
+ ScalarReferenceExpression reference when aliasRewritingMap.TryGetValue(reference.Name, out var newAlias)
+ => new ScalarReferenceExpression(newAlias, reference.Type, reference.TypeMapping),
+ ObjectReferenceExpression reference when aliasRewritingMap.TryGetValue(reference.Name, out var newAlias)
+ => new ObjectReferenceExpression(reference.EntityType, newAlias),
+
+ _ => base.VisitExtension(node)
+ };
+ }
+
+ private sealed class MutableInt
+ {
+ internal int Value;
+ }
+}
diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosQueryCompilationContext.cs b/src/EFCore.Cosmos/Query/Internal/CosmosQueryCompilationContext.cs
index 1b0d35baf5d..bee3bdb6b5b 100644
--- a/src/EFCore.Cosmos/Query/Internal/CosmosQueryCompilationContext.cs
+++ b/src/EFCore.Cosmos/Query/Internal/CosmosQueryCompilationContext.cs
@@ -30,4 +30,15 @@ public class CosmosQueryCompilationContext(QueryCompilationContextDependencies d
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
public virtual PartitionKey? PartitionKeyValueFromExtension { get; internal set; }
+
+ ///
+ /// A manager for aliases, capable of generate uniquified source aliases.
+ ///
+ ///
+ /// 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.
+ ///
+ public virtual CosmosAliasManager AliasManager { get; } = new();
}
diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosQueryTranslationPostprocessor.cs b/src/EFCore.Cosmos/Query/Internal/CosmosQueryTranslationPostprocessor.cs
index 7742bc50617..afd87a9d63b 100644
--- a/src/EFCore.Cosmos/Query/Internal/CosmosQueryTranslationPostprocessor.cs
+++ b/src/EFCore.Cosmos/Query/Internal/CosmosQueryTranslationPostprocessor.cs
@@ -12,7 +12,7 @@ namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
public class CosmosQueryTranslationPostprocessor(
QueryTranslationPostprocessorDependencies dependencies,
ISqlExpressionFactory sqlExpressionFactory,
- QueryCompilationContext queryCompilationContext)
+ CosmosQueryCompilationContext queryCompilationContext)
: QueryTranslationPostprocessor(dependencies, queryCompilationContext)
{
///
@@ -31,8 +31,9 @@ public override Expression Process(Expression query)
selectExpression.ApplyProjection();
}
- query = new CosmosValueConverterCompensatingExpressionVisitor(sqlExpressionFactory).Visit(query);
+ var afterValueConverterCompensation = new CosmosValueConverterCompensatingExpressionVisitor(sqlExpressionFactory).Visit(query);
+ var afterAliases = queryCompilationContext.AliasManager.PostprocessAliases(afterValueConverterCompensation);
- return query;
+ return afterAliases;
}
}
diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosQueryTranslationPostprocessorFactory.cs b/src/EFCore.Cosmos/Query/Internal/CosmosQueryTranslationPostprocessorFactory.cs
index fb4260b9b53..313976e3e92 100644
--- a/src/EFCore.Cosmos/Query/Internal/CosmosQueryTranslationPostprocessorFactory.cs
+++ b/src/EFCore.Cosmos/Query/Internal/CosmosQueryTranslationPostprocessorFactory.cs
@@ -29,5 +29,5 @@ public virtual QueryTranslationPostprocessor Create(QueryCompilationContext quer
=> new CosmosQueryTranslationPostprocessor(
Dependencies,
sqlExpressionFactory,
- queryCompilationContext);
+ ((CosmosQueryCompilationContext)queryCompilationContext));
}
diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Cosmos/Query/Internal/CosmosQueryableMethodTranslatingExpressionVisitor.cs
index 1bc4e87cd2a..e4aa5b15cba 100644
--- a/src/EFCore.Cosmos/Query/Internal/CosmosQueryableMethodTranslatingExpressionVisitor.cs
+++ b/src/EFCore.Cosmos/Query/Internal/CosmosQueryableMethodTranslatingExpressionVisitor.cs
@@ -25,8 +25,9 @@ public class CosmosQueryableMethodTranslatingExpressionVisitor : QueryableMethod
private readonly IMethodCallTranslatorProvider _methodCallTranslatorProvider;
private readonly CosmosSqlTranslatingExpressionVisitor _sqlTranslator;
private readonly CosmosProjectionBindingExpressionVisitor _projectionBindingExpressionVisitor;
+ private readonly CosmosAliasManager _aliasManager;
private bool _subquery;
- private ReadItemInfo? _readItemExpression;
+ private ReadItemInfo? _readItemInfo;
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -57,6 +58,7 @@ public CosmosQueryableMethodTranslatingExpressionVisitor(
this);
_projectionBindingExpressionVisitor =
new CosmosProjectionBindingExpressionVisitor(_queryCompilationContext.Model, this, _sqlTranslator, _typeMappingSource);
+ _aliasManager = queryCompilationContext.AliasManager;
_subquery = false;
}
@@ -84,6 +86,7 @@ protected CosmosQueryableMethodTranslatingExpressionVisitor(
parentVisitor);
_projectionBindingExpressionVisitor =
new CosmosProjectionBindingExpressionVisitor(_queryCompilationContext.Model, this, _sqlTranslator, _typeMappingSource);
+ _aliasManager = parentVisitor._aliasManager;
_subquery = true;
}
@@ -225,7 +228,7 @@ public override Expression Translate(Expression expression)
(property, parameter) => (property, parameter))
.ToDictionary(tuple => tuple.property, tuple => tuple.parameter);
- _readItemExpression = new ReadItemInfo(entityType, propertyParameterList, clrType);
+ _readItemInfo = new ReadItemInfo(entityType, propertyParameterList, clrType);
}
}
}
@@ -359,11 +362,14 @@ protected override Expression VisitExtension(Expression extensionExpression)
AddTranslationErrorDetails(CosmosStrings.NonCorrelatedSubqueriesNotSupported);
return QueryCompilationContext.NotTranslatedExpression;
- case FromSqlQueryRootExpression fromSqlQueryRootExpression:
- return CreateShapedQueryExpression(
- fromSqlQueryRootExpression.EntityType,
- _sqlExpressionFactory.Select(
- fromSqlQueryRootExpression.EntityType, fromSqlQueryRootExpression.Sql, fromSqlQueryRootExpression.Argument));
+ case FromSqlQueryRootExpression fromSqlQueryRoot:
+ var entityType = fromSqlQueryRoot.EntityType;
+ var fromSql = new FromSqlExpression(entityType.ClrType, fromSqlQueryRoot.Sql, fromSqlQueryRoot.Argument);
+ var alias = _aliasManager.GenerateSourceAlias(fromSql);
+ var selectExpression = new SelectExpression(
+ new SourceExpression(fromSql, alias),
+ new EntityProjectionExpression(new ObjectReferenceExpression(entityType, alias), entityType));
+ return CreateShapedQueryExpression(entityType, selectExpression);
default:
return base.VisitExtension(extensionExpression);
@@ -404,13 +410,49 @@ protected override QueryableMethodTranslatingExpressionVisitor CreateSubqueryVis
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
protected override ShapedQueryExpression CreateShapedQueryExpression(IEntityType entityType)
- => CreateShapedQueryExpression(
- entityType,
- _readItemExpression == null
- ? _sqlExpressionFactory.Select(entityType)
- : _sqlExpressionFactory.ReadItem(entityType, _readItemExpression));
+ {
+ Check.DebugAssert(!entityType.IsOwned(), "Can't create ShapedQueryExpression for owned entity type");
+
+ var alias = _aliasManager.GenerateSourceAlias("c");
+ var selectExpression = new SelectExpression(
+ new SourceExpression(new ObjectReferenceExpression(entityType, "root"), alias),
+ new EntityProjectionExpression(new ObjectReferenceExpression(entityType, alias), entityType),
+ _readItemInfo);
+
+ // Add discriminator predicate
+ var concreteEntityTypes = entityType.GetConcreteDerivedTypesInclusive().ToList();
+ if (concreteEntityTypes.Count == 1)
+ {
+ var concreteEntityType = concreteEntityTypes[0];
+ var discriminatorProperty = concreteEntityType.FindDiscriminatorProperty();
+ if (discriminatorProperty != null)
+ {
+ var discriminatorColumn = ((EntityProjectionExpression)selectExpression.GetMappedProjection(new ProjectionMember()))
+ .BindProperty(discriminatorProperty, clientEval: false);
+
+ selectExpression.ApplyPredicate(
+ _sqlExpressionFactory.Equal(
+ (SqlExpression)discriminatorColumn,
+ _sqlExpressionFactory.Constant(concreteEntityType.GetDiscriminatorValue())));
+ }
+ }
+ else
+ {
+ var discriminatorProperty = concreteEntityTypes[0].FindDiscriminatorProperty();
+ Check.DebugAssert(discriminatorProperty is not null, "Missing discriminator property in hierarchy");
+ var discriminatorColumn = ((EntityProjectionExpression)selectExpression.GetMappedProjection(new ProjectionMember()))
+ .BindProperty(discriminatorProperty, clientEval: false);
- private ShapedQueryExpression CreateShapedQueryExpression(IEntityType entityType, Expression queryExpression)
+ selectExpression.ApplyPredicate(
+ _sqlExpressionFactory.In(
+ (SqlExpression)discriminatorColumn,
+ concreteEntityTypes.Select(et => _sqlExpressionFactory.Constant(et.GetDiscriminatorValue())).ToArray()));
+ }
+
+ return CreateShapedQueryExpression(entityType, selectExpression);
+ }
+
+ private ShapedQueryExpression CreateShapedQueryExpression(IEntityType entityType, SelectExpression queryExpression)
{
if (!entityType.IsOwned())
{
@@ -1088,7 +1130,7 @@ protected override ShapedQueryExpression TranslateSelect(ShapedQueryExpression s
if (Visit(collectionSelectorBody) is ShapedQueryExpression inner)
{
var select = (SelectExpression)source.QueryExpression;
- var shaper = select.AddJoin(inner, source.ShaperExpression);
+ var shaper = select.AddJoin(inner, source.ShaperExpression, _aliasManager);
return TranslateTwoParameterSelector(source.UpdateShaperExpression(shaper), resultSelector);
}
@@ -1196,11 +1238,11 @@ protected override ShapedQueryExpression TranslateSelect(ShapedQueryExpression s
var slice = _sqlExpressionFactory.Function("ARRAY_SLICE", [scalarArray, translatedCount], arrayType, arrayTypeMapping);
- // TODO: Proper alias management (#33894). Ideally reach into the source of the original SelectExpression and use that alias.
+ var alias = _aliasManager.GenerateSourceAlias(slice);
var translatedSelect = SelectExpression.CreateForCollection(
slice,
- "i",
- new ScalarReferenceExpression("i", element.Type, element.TypeMapping));
+ alias,
+ new ScalarReferenceExpression(alias, element.Type, element.TypeMapping));
return source.UpdateQueryExpression(translatedSelect);
}
@@ -1208,14 +1250,13 @@ protected override ShapedQueryExpression TranslateSelect(ShapedQueryExpression s
case not null when projectedStructuralTypeShaper is not null:
{
var arrayType = typeof(IEnumerable<>).MakeGenericType(projectedStructuralTypeShaper.Type);
-
- // TODO: Proper alias management (#33894).
var slice = new ObjectFunctionExpression("ARRAY_SLICE", [array, translatedCount], arrayType);
+ var alias = _aliasManager.GenerateSourceAlias(slice);
var translatedSelect = SelectExpression.CreateForCollection(
slice,
- "i",
+ alias,
new EntityProjectionExpression(
- new ObjectReferenceExpression((IEntityType)projectedStructuralTypeShaper.StructuralType, "i"),
+ new ObjectReferenceExpression((IEntityType)projectedStructuralTypeShaper.StructuralType, alias),
(IEntityType)projectedStructuralTypeShaper.StructuralType));
return source.Update(
translatedSelect,
@@ -1322,29 +1363,29 @@ protected override ShapedQueryExpression TranslateSelect(ShapedQueryExpression s
"ARRAY_SLICE", [scalarArray, TranslateExpression(Expression.Constant(0))!, translatedCount], scalarArray.Type,
scalarArray.TypeMapping);
- // TODO: Proper alias management (#33894). Ideally reach into the source of the original SelectExpression and use that alias.
+ var alias = _aliasManager.GenerateSourceAlias(slice);
select = SelectExpression.CreateForCollection(
slice,
- "i",
- new ScalarReferenceExpression("i", element.Type, element.TypeMapping));
+ alias,
+ new ScalarReferenceExpression(alias, element.Type, element.TypeMapping));
return source.UpdateQueryExpression(select);
}
// ElementAtOrDefault over an array os structural types
case not null when projectedStructuralTypeShaper is not null:
{
- // TODO: Proper alias management (#33894).
// Take() is composed over Skip(), combine the two together to a single ARRAY_SLICE()
var slice = array is ObjectFunctionExpression { Name: "ARRAY_SLICE", Arguments: [var nestedArray, var skipCount] } previousSlice
? previousSlice.Update([nestedArray, skipCount, translatedCount])
: new ObjectFunctionExpression(
"ARRAY_SLICE", [array, TranslateExpression(Expression.Constant(0))!, translatedCount], projectedStructuralTypeShaper.Type);
+ var alias = _aliasManager.GenerateSourceAlias(slice);
var translatedSelect = SelectExpression.CreateForCollection(
slice,
- "i",
+ alias,
new EntityProjectionExpression(
- new ObjectReferenceExpression((IEntityType)projectedStructuralTypeShaper.StructuralType, "i"),
+ new ObjectReferenceExpression((IEntityType)projectedStructuralTypeShaper.StructuralType, alias),
(IEntityType)projectedStructuralTypeShaper.StructuralType));
return source.Update(
translatedSelect,
@@ -1590,13 +1631,14 @@ when methodCallExpression.TryGetIndexerArguments(_queryCompilationContext.Model,
// Maybe have it return the StructuralTypeShaperExpression instead, and only when binding from within SqlTranslatingEV,
// wrap with ERE?
// Check: how is this currently working in relational?
+
+ var sourceAlias = _aliasManager.GenerateSourceAlias(property.Name);
+
switch (translatedExpression)
{
case StructuralTypeShaperExpression shaper when property is INavigation { IsCollection: true }:
{
- // TODO: Alias management #33894
var targetEntityType = (IEntityType)shaper.StructuralType;
- var sourceAlias = "t";
var projection = new EntityProjectionExpression(
new ObjectReferenceExpression(targetEntityType, sourceAlias), targetEntityType);
var select = SelectExpression.CreateForCollection(
@@ -1614,11 +1656,10 @@ when methodCallExpression.TryGetIndexerArguments(_queryCompilationContext.Model,
case SqlExpression sqlExpression when property is IProperty { IsPrimitiveCollection: true }:
{
var elementClrType = sqlExpression.Type.GetSequenceType();
- // TODO: Do proper alias management: #33894
var select = SelectExpression.CreateForCollection(
sqlExpression,
- "i",
- new ScalarReferenceExpression("i", elementClrType, sqlExpression.TypeMapping!.ElementTypeMapping!));
+ sourceAlias,
+ new ScalarReferenceExpression(sourceAlias, elementClrType, sqlExpression.TypeMapping!.ElementTypeMapping!));
return CreateShapedQueryExpression(select, elementClrType);
}
}
@@ -1635,7 +1676,7 @@ when methodCallExpression.TryGetIndexerArguments(_queryCompilationContext.Model,
///
protected override ShapedQueryExpression? TranslateInlineQueryRoot(InlineQueryRootExpression inlineQueryRootExpression)
{
- // The below produces an InlineArrayExpression ([1,2,3]), wrapped by a SelectExpression (SELECT VALUE [1,2,3]).
+ // The below produces an ArrayConstantExpression ([1,2,3]), wrapped by a SelectExpression (SELECT VALUE [1,2,3]).
// This is because a bare inline array can only appear in the projection. For example, the following is wrong:
// SELECT i FROM i IN [1,2,3] (syntax error)
var values = inlineQueryRootExpression.Values;
@@ -1658,11 +1699,11 @@ when methodCallExpression.TryGetIndexerArguments(_queryCompilationContext.Model,
var arrayTypeMapping = _typeMappingSource.FindMapping(typeof(IEnumerable<>).MakeGenericType(elementClrType));
var inlineArray = new ArrayConstantExpression(elementClrType, translatedItems, arrayTypeMapping);
- // TODO: Do proper alias management: #33894
+ var sourceAlias = _aliasManager.GenerateSourceAlias(inlineArray);
var select = SelectExpression.CreateForCollection(
inlineArray,
- "i",
- new ScalarReferenceExpression("i", elementClrType, elementTypeMapping));
+ sourceAlias,
+ new ScalarReferenceExpression(sourceAlias, elementClrType, elementTypeMapping));
return CreateShapedQueryExpression(select, elementClrType);
}
@@ -1688,11 +1729,11 @@ when methodCallExpression.TryGetIndexerArguments(_queryCompilationContext.Model,
var elementTypeMapping = _typeMappingSource.FindMapping(elementClrType)!;
var sqlParameterExpression = new SqlParameterExpression(parameterQueryRootExpression.ParameterExpression, arrayTypeMapping);
- // TODO: Do proper alias management: #33894
+ var sourceAlias = _aliasManager.GenerateSourceAlias(sqlParameterExpression.Name.TrimStart('_'));
var select = SelectExpression.CreateForCollection(
sqlParameterExpression,
- "i",
- new ScalarReferenceExpression("i", elementClrType, elementTypeMapping));
+ sourceAlias,
+ new ScalarReferenceExpression(sourceAlias, elementClrType, elementTypeMapping));
return CreateShapedQueryExpression(select, elementClrType);
}
@@ -1761,11 +1802,10 @@ when methodCallExpression.TryGetIndexerArguments(_queryCompilationContext.Model,
&& (sqlProjection1.TypeMapping ?? sqlProjection2.TypeMapping) is CosmosTypeMapping typeMapping)
{
var arrayTypeMapping = _typeMappingSource.FindMapping(arrayType, _queryCompilationContext.Model, typeMapping);
-
- // TODO: Proper alias management (#33894).
var translation = _sqlExpressionFactory.Function(functionName, [array1, array2], arrayType, arrayTypeMapping);
+ var alias = _aliasManager.GenerateSourceAlias(translation);
var select = SelectExpression.CreateForCollection(
- translation, "i", new ScalarReferenceExpression("i", projection1.Type, typeMapping));
+ translation, alias, new ScalarReferenceExpression(alias, projection1.Type, typeMapping));
return source1.UpdateQueryExpression(select);
}
@@ -1774,10 +1814,10 @@ when methodCallExpression.TryGetIndexerArguments(_queryCompilationContext.Model,
&& source2.ShaperExpression is StructuralTypeShaperExpression { StructuralType: var structuralType2 }
&& structuralType1 == structuralType2)
{
- // TODO: Proper alias management (#33894).
var translation = new ObjectFunctionExpression(functionName, [array1, array2], arrayType);
+ var alias = _aliasManager.GenerateSourceAlias(translation);
var select = SelectExpression.CreateForCollection(
- translation, "i", new ObjectReferenceExpression((IEntityType)structuralType1, "i"));
+ translation, alias, new ObjectReferenceExpression((IEntityType)structuralType1, alias));
return CreateShapedQueryExpression(select, structuralType1.ClrType);
}
}
diff --git a/src/EFCore.Cosmos/Query/Internal/Expressions/SelectExpression.cs b/src/EFCore.Cosmos/Query/Internal/Expressions/SelectExpression.cs
index 56a96abf42e..af87947caf3 100644
--- a/src/EFCore.Cosmos/Query/Internal/Expressions/SelectExpression.cs
+++ b/src/EFCore.Cosmos/Query/Internal/Expressions/SelectExpression.cs
@@ -16,8 +16,6 @@ namespace Microsoft.EntityFrameworkCore.Cosmos.Query.Internal;
[DebuggerDisplay("{PrintShortSql(), nq}")]
public class SelectExpression : Expression, IPrintableExpression
{
- private const string RootAlias = "c";
-
private IDictionary _projectionMapping = new Dictionary();
private readonly List _sources = [];
private readonly List _projection = [];
@@ -31,38 +29,7 @@ public class SelectExpression : Expression, IPrintableExpression
/// 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.
///
- public SelectExpression(IEntityType entityType, ReadItemInfo readItemInfo)
- : this(entityType)
- {
- ReadItemInfo = readItemInfo;
- }
-
- ///
- /// 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.
- ///
- public SelectExpression(IEntityType entityType)
- {
- // TODO: Redo aliasing
- _sources = [new SourceExpression(new ObjectReferenceExpression(entityType, "root"), RootAlias)];
- _projectionMapping[new ProjectionMember()]
- = new EntityProjectionExpression(new ObjectReferenceExpression(entityType, RootAlias), entityType);
- }
-
- ///
- /// 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.
- ///
- public SelectExpression(IEntityType entityType, string sql, Expression argument)
- {
- var fromSql = new FromSqlExpression(entityType.ClrType, sql, argument);
- _sources = [new SourceExpression(fromSql, RootAlias)];
- _projectionMapping[new ProjectionMember()] = new EntityProjectionExpression(new ObjectReferenceExpression(entityType, RootAlias), entityType);
- }
+ public virtual ReadItemInfo? ReadItemInfo { get; init; }
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -80,14 +47,6 @@ public SelectExpression(
_orderings = orderings;
}
- ///
- /// 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.
- ///
- public virtual ReadItemInfo? ReadItemInfo { get; init; }
-
///
/// 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
@@ -103,14 +62,11 @@ public SelectExpression(Expression projection)
/// 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.
///
- public SelectExpression(SourceExpression source, Expression projection)
+ public SelectExpression(SourceExpression source, Expression projection, ReadItemInfo? readItemInfo = null)
{
_sources.Add(source);
_projectionMapping[new ProjectionMember()] = projection;
- }
-
- private SelectExpression()
- {
+ ReadItemInfo = readItemInfo;
}
///
@@ -140,10 +96,8 @@ [new ProjectionExpression(sourceExpression, null!)],
var source = new SourceExpression(sourceExpression, sourceAlias, withIn: true);
- return new SelectExpression
+ return new SelectExpression(source, projection)
{
- _sources = { source },
- _projectionMapping = { [new ProjectionMember()] = projection },
UsesSingleValueProjection = true
};
}
@@ -498,16 +452,29 @@ public virtual void ReverseOrderings()
/// 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.
///
- public virtual Expression AddJoin(ShapedQueryExpression inner, Expression outerShaper)
+ public virtual Expression AddJoin(ShapedQueryExpression inner, Expression outerShaper, CosmosAliasManager aliasManager)
{
var (innerSelect, innerShaper) = ((SelectExpression)inner.QueryExpression, inner.ShaperExpression);
- // TODO: Proper alias management (#33894).
// Create a new source (JOIN) for the server side of the query; if the inner query represents a bare array, unwrap it and
// add the JOIN directly
- var joinSource = inner.TryExtractArray(out var bareArray) && SourceExpression.IsCompatible(bareArray)
- ? new SourceExpression(bareArray, "a", withIn: true)
- : new SourceExpression(innerSelect, "a");
+ SourceExpression? joinSource = null;
+ string sourceAlias;
+ if (inner.TryExtractArray(out var bareArray) && SourceExpression.IsCompatible(bareArray))
+ {
+ sourceAlias = aliasManager.GenerateSourceAlias(bareArray);
+
+ if (SourceExpression.IsCompatible(bareArray))
+ {
+ joinSource = new SourceExpression(bareArray, sourceAlias, withIn: true);
+ }
+ }
+ else
+ {
+ sourceAlias = aliasManager.GenerateSourceAlias("join");
+ }
+
+ joinSource ??= new SourceExpression(innerSelect, sourceAlias);
// Make the necessary modifications to the shaper side, projecting out a TransparentIdentifier (outer/inner)
var transparentIdentifierType = TransparentIdentifierFactory.Create(outerShaper.Type, innerShaper.Type);
@@ -658,7 +625,8 @@ protected override Expression VisitChildren(ExpressionVisitor visitor)
Predicate = predicate,
Offset = offset,
Limit = limit,
- IsDistinct = IsDistinct
+ IsDistinct = IsDistinct,
+ UsesSingleValueProjection = UsesSingleValueProjection
};
return newSelectExpression;
diff --git a/src/EFCore.Cosmos/Query/Internal/ISqlExpressionFactory.cs b/src/EFCore.Cosmos/Query/Internal/ISqlExpressionFactory.cs
index abd4c69d034..6529ac44bea 100644
--- a/src/EFCore.Cosmos/Query/Internal/ISqlExpressionFactory.cs
+++ b/src/EFCore.Cosmos/Query/Internal/ISqlExpressionFactory.cs
@@ -304,28 +304,4 @@ SqlConditionalExpression Condition(
/// doing so can result in application failures when updating to a new Entity Framework Core release.
///
SqlConstantExpression Constant(object? value, CoreTypeMapping? typeMapping = null);
-
- ///
- /// 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.
- ///
- SelectExpression Select(IEntityType entityType);
-
- ///
- /// 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.
- ///
- SelectExpression Select(IEntityType entityType, string sql, Expression argument);
-
- ///
- /// 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.
- ///
- SelectExpression ReadItem(IEntityType entityType, ReadItemInfo argument);
}
diff --git a/src/EFCore.Cosmos/Query/Internal/SqlExpressionFactory.cs b/src/EFCore.Cosmos/Query/Internal/SqlExpressionFactory.cs
index a47640efdd1..953ca08c48d 100644
--- a/src/EFCore.Cosmos/Query/Internal/SqlExpressionFactory.cs
+++ b/src/EFCore.Cosmos/Query/Internal/SqlExpressionFactory.cs
@@ -629,70 +629,4 @@ public virtual InExpression In(SqlExpression item, SqlParameterExpression values
///
public virtual SqlConstantExpression Constant(object? value, CoreTypeMapping? typeMapping = null)
=> new(Expression.Constant(value), typeMapping);
-
- ///
- /// 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.
- ///
- public virtual SelectExpression Select(IEntityType entityType)
- {
- var selectExpression = new SelectExpression(entityType);
- AddDiscriminator(selectExpression, entityType);
-
- return selectExpression;
- }
-
- ///
- /// 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.
- ///
- public virtual SelectExpression ReadItem(IEntityType entityType, ReadItemInfo readItemInfo)
- {
- var selectExpression = new SelectExpression(entityType, readItemInfo);
- AddDiscriminator(selectExpression, entityType);
-
- return selectExpression;
- }
-
- ///
- /// 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.
- ///
- public virtual SelectExpression Select(IEntityType entityType, string sql, Expression argument)
- => new(entityType, sql, argument);
-
- private void AddDiscriminator(SelectExpression selectExpression, IEntityType entityType)
- {
- var concreteEntityTypes = entityType.GetConcreteDerivedTypesInclusive().ToList();
-
- if (concreteEntityTypes.Count == 1)
- {
- var concreteEntityType = concreteEntityTypes[0];
- var discriminatorProperty = concreteEntityType.FindDiscriminatorProperty();
- if (discriminatorProperty != null)
- {
- var discriminatorColumn = ((EntityProjectionExpression)selectExpression.GetMappedProjection(new ProjectionMember()))
- .BindProperty(discriminatorProperty, clientEval: false);
-
- selectExpression.ApplyPredicate(
- Equal((SqlExpression)discriminatorColumn, Constant(concreteEntityType.GetDiscriminatorValue())));
- }
- }
- else
- {
- var discriminatorProperty = concreteEntityTypes[0].FindDiscriminatorProperty();
- Check.DebugAssert(discriminatorProperty is not null, "Missing discriminator property in hierarchy");
- var discriminatorColumn = ((EntityProjectionExpression)selectExpression.GetMappedProjection(new ProjectionMember()))
- .BindProperty(discriminatorProperty, clientEval: false);
-
- selectExpression.ApplyPredicate(
- In((SqlExpression)discriminatorColumn, concreteEntityTypes.Select(et => Constant(et.GetDiscriminatorValue())).ToArray()));
- }
- }
}
diff --git a/src/EFCore.Relational/Query/RelationalQueryCompilationContextDependencies.cs b/src/EFCore.Relational/Query/RelationalQueryCompilationContextDependencies.cs
index 51c49e52697..afecb979d90 100644
--- a/src/EFCore.Relational/Query/RelationalQueryCompilationContextDependencies.cs
+++ b/src/EFCore.Relational/Query/RelationalQueryCompilationContextDependencies.cs
@@ -51,7 +51,7 @@ public RelationalQueryCompilationContextDependencies(ISqlAliasManagerFactory sql
=> SqlAliasManagerFactory = sqlAliasManagerFactory;
///
- /// The current context.
+ /// A manager for SQL aliases, capable of generate uniquified table aliases.
///
public ISqlAliasManagerFactory SqlAliasManagerFactory { get; init; }
}
diff --git a/src/EFCore.Relational/Query/SqlAliasManager.cs b/src/EFCore.Relational/Query/SqlAliasManager.cs
index 977253c3974..4d1f247eea7 100644
--- a/src/EFCore.Relational/Query/SqlAliasManager.cs
+++ b/src/EFCore.Relational/Query/SqlAliasManager.cs
@@ -23,8 +23,7 @@ public class SqlAliasManager
/// All aliases produced by a given instance of are unique.
///
///
- /// A name (e.g. of a table) to use as the starting point for the aliasA base for the alias; a number postfix will be appended to it
- /// as necessary.
+ /// A name (e.g. of a table) to use as the starting point for the alias; a number postfix will be appended to it as necessary.
///
/// A fully unique alias within the context of this translation process.
public virtual string GenerateTableAlias(string name)
@@ -95,8 +94,8 @@ public virtual Expression PostprocessAliases(Expression expression)
protected virtual Dictionary? RemapTableAliases(IReadOnlySet usedAliases)
{
// Aliases consist of a single character, followed by a counter for uniquification.
- // We construct process the collected aliases above into a bitmap that represents, for each alias char, which numbers have been
- // seen. Note that since a0 is the 2nd uniquified alias (a is the first), the bits are off-by-one, with position 0 representing
+ // We process the collected aliases above into a bitmap that represents, for each alias char, which numbers have been seen.
+ // Note that since a0 is the 2nd uniquified alias (a is the first), the bits are off-by-one, with position 0 representing
// a, position 1 representing a0, and so on.
Dictionary aliasBitmaps = new();
@@ -169,10 +168,6 @@ protected override Expression VisitExtension(Expression node)
case ShapedQueryExpression shapedQuery:
return shapedQuery.UpdateQueryExpression(Visit(shapedQuery.QueryExpression));
- case ColumnExpression { TableAlias: var alias }:
- _tableAliases.Add(alias);
- return base.VisitExtension(node);
-
case TableExpressionBase { Alias: string alias }:
_tableAliases.Add(alias);
return base.VisitExtension(node);
@@ -189,28 +184,19 @@ internal static Expression Rewrite(Expression expression, IReadOnlyDictionary new TableAliasRewriter(aliasRewritingMap).Visit(expression);
protected override Expression VisitExtension(Expression node)
- {
- switch (node)
+ => node switch
{
- case ShapedQueryExpression shapedQuery:
- return shapedQuery.UpdateQueryExpression(Visit(shapedQuery.QueryExpression));
+ ShapedQueryExpression shapedQuery => shapedQuery.UpdateQueryExpression(Visit(shapedQuery.QueryExpression)),
// Note that this skips joins (which wrap the table that has the actual alias), as well as the top-level select
- case TableExpressionBase { Alias: string alias } table:
- if (aliasRewritingMap.TryGetValue(alias, out var newAlias))
- {
- table = table.WithAlias(newAlias);
- }
-
- return base.VisitExtension(table);
+ TableExpressionBase { Alias: string alias } table when aliasRewritingMap.TryGetValue(alias, out var newAlias)
+ => base.VisitExtension(table.WithAlias(newAlias)),
- case ColumnExpression column when aliasRewritingMap.TryGetValue(column.TableAlias, out var newTableAlias):
- return new ColumnExpression(column.Name, newTableAlias, column.Type, column.TypeMapping, column.IsNullable);
+ ColumnExpression column when aliasRewritingMap.TryGetValue(column.TableAlias, out var newTableAlias)
+ => new ColumnExpression(column.Name, newTableAlias, column.Type, column.TypeMapping, column.IsNullable),
- default:
- return base.VisitExtension(node);
- }
- }
+ _ => base.VisitExtension(node)
+ };
}
private sealed class MutableInt
diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/FromSqlQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/FromSqlQueryCosmosTest.cs
index 7aba1c99fbe..8f5be39c0bc 100644
--- a/test/EFCore.Cosmos.FunctionalTests/Query/FromSqlQueryCosmosTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/Query/FromSqlQueryCosmosTest.cs
@@ -40,10 +40,10 @@ public Task FromSqlRaw_queryable_simple(bool async)
AssertSql(
"""
-SELECT c
+SELECT s
FROM (
SELECT * FROM root c WHERE c["Discriminator"] = "Customer" AND c["ContactName"] LIKE '%z%'
-) c
+) s
""");
});
@@ -83,10 +83,10 @@ public Task FromSqlRaw_queryable_simple_columns_out_of_order(bool async)
AssertSql(
"""
-SELECT c
+SELECT s
FROM (
SELECT c["id"], c["Discriminator"], c["Region"], c["PostalCode"], c["Phone"], c["Fax"], c["CustomerID"], c["Country"], c["ContactTitle"], c["ContactName"], c["CompanyName"], c["City"], c["Address"] FROM root c WHERE c["Discriminator"] = "Customer"
-) c
+) s
""");
});
@@ -111,10 +111,10 @@ public Task FromSqlRaw_queryable_simple_columns_out_of_order_and_extra_columns(b
AssertSql(
"""
-SELECT c
+SELECT s
FROM (
SELECT c["id"], c["Discriminator"], c["Region"], c["PostalCode"], c["PostalCode"] AS Foo, c["Phone"], c["Fax"], c["CustomerID"], c["Country"], c["ContactTitle"], c["ContactName"], c["CompanyName"], c["City"], c["Address"] FROM root c WHERE c["Discriminator"] = "Customer"
-) c
+) s
""");
});
@@ -141,11 +141,11 @@ public Task FromSqlRaw_queryable_composed(bool async)
AssertSql(
"""
-SELECT c
+SELECT s
FROM (
SELECT * FROM root c WHERE c["Discriminator"] = "Customer"
-) c
-WHERE CONTAINS(c["ContactName"], "z")
+) s
+WHERE CONTAINS(s["ContactName"], "z")
""");
});
@@ -167,17 +167,20 @@ public virtual Task FromSqlRaw_queryable_composed_after_removing_whitespaces(boo
Assert.Equal(14, actual.Length);
AssertSql(
- @"SELECT c
+ """
+SELECT s
FROM (
- "
- + @"
+
+""" + " " + """
+
SELECT
- * FROM root c WHERE c[""Discriminator""] = ""Customer""
-) c
-WHERE CONTAINS(c[""ContactName""], ""z"")");
+ * FROM root c WHERE c["Discriminator"] = "Customer"
+) s
+WHERE CONTAINS(s["ContactName"], "z")
+""");
});
[ConditionalTheory]
@@ -219,11 +222,11 @@ public Task FromSqlRaw_queryable_composed_compiled(bool async)
AssertSql(
"""
-SELECT c
+SELECT s
FROM (
SELECT * FROM root c WHERE c["Discriminator"] = "Customer"
-) c
-WHERE CONTAINS(c["ContactName"], "z")
+) s
+WHERE CONTAINS(s["ContactName"], "z")
""");
});
@@ -264,11 +267,11 @@ public virtual Task FromSqlRaw_queryable_composed_compiled_with_parameter(bool a
AssertSql(
"""
-SELECT c
+SELECT s
FROM (
SELECT * FROM root c WHERE c["Discriminator"] = "Customer" AND c["CustomerID"] = "CONSH"
-) c
-WHERE CONTAINS(c["ContactName"], "z")
+) s
+WHERE CONTAINS(s["ContactName"], "z")
""");
});
@@ -295,12 +298,12 @@ FROM root c
AssertSql(
"""
-SELECT c
+SELECT s
FROM (
SELECT *
FROM root c
WHERE c["Discriminator"] = "Customer" AND c["City"] = 'London'
-) c
+) s
""");
});
@@ -328,13 +331,13 @@ FROM root c
AssertSql(
"""
-SELECT c
+SELECT s
FROM (
SELECT *
FROM root c
WHERE c["Discriminator"] = "Customer"
-) c
-WHERE (c["City"] = "London")
+) s
+WHERE (s["City"] = "London")
""");
});
@@ -366,10 +369,10 @@ public Task FromSqlRaw_queryable_with_parameters(bool async)
@p0='London'
@p1='Sales Representative'
-SELECT c
+SELECT s
FROM (
SELECT * FROM root c WHERE c["Discriminator"] = "Customer" AND c["City"] = @p0 AND c["ContactTitle"] = @p1
-) c
+) s
""");
});
@@ -398,10 +401,10 @@ public Task FromSqlRaw_queryable_with_parameters_inline(bool async)
@p0='London'
@p1='Sales Representative'
-SELECT c
+SELECT s
FROM (
SELECT * FROM root c WHERE c["Discriminator"] = "Customer" AND c["City"] = @p0 AND c["ContactTitle"] = @p1
-) c
+) s
""");
});
@@ -428,10 +431,10 @@ public Task FromSqlRaw_queryable_with_null_parameter(bool async)
"""
@p0=null
-SELECT c
+SELECT s
FROM (
SELECT * FROM root c WHERE c["Discriminator"] = "Employee" AND c["ReportsTo"] = @p0 OR (IS_NULL(c["ReportsTo"]) AND IS_NULL(@p0))
-) c
+) s
""");
});
@@ -463,11 +466,11 @@ public Task FromSqlRaw_queryable_with_parameters_and_closure(bool async)
@p0='London'
@__contactTitle_1='Sales Representative'
-SELECT c
+SELECT s
FROM (
SELECT * FROM root c WHERE c["Discriminator"] = "Customer" AND c["City"] = @p0
-) c
-WHERE (c["ContactTitle"] = @__contactTitle_1)
+) s
+WHERE (s["ContactTitle"] = @__contactTitle_1)
""");
});
@@ -500,17 +503,17 @@ public virtual Task FromSqlRaw_queryable_simple_cache_key_includes_query_string(
AssertSql(
"""
-SELECT c
+SELECT s
FROM (
SELECT * FROM root c WHERE c["Discriminator"] = "Customer" AND c["City"] = 'London'
-) c
+) s
""",
//
"""
-SELECT c
+SELECT s
FROM (
SELECT * FROM root c WHERE c["Discriminator"] = "Customer" AND c["City"] = 'Seattle'
-) c
+) s
""");
});
@@ -554,20 +557,20 @@ public virtual Task FromSqlRaw_queryable_with_parameters_cache_key_includes_para
@p0='London'
@p1='Sales Representative'
-SELECT c
+SELECT s
FROM (
SELECT * FROM root c WHERE c["Discriminator"] = "Customer" AND c["City"] = @p0 AND c["ContactTitle"] = @p1
-) c
+) s
""",
//
"""
@p0='Madrid'
@p1='Accounting Manager'
-SELECT c
+SELECT s
FROM (
SELECT * FROM root c WHERE c["Discriminator"] = "Customer" AND c["City"] = @p0 AND c["ContactTitle"] = @p1
-) c
+) s
""");
});
@@ -592,10 +595,10 @@ public virtual Task FromSqlRaw_queryable_simple_as_no_tracking_not_composed(bool
AssertSql(
"""
-SELECT c
+SELECT s
FROM (
SELECT * FROM root c WHERE c["Discriminator"] = "Customer"
-) c
+) s
""");
});
@@ -622,12 +625,12 @@ FROM root c
AssertSql(
"""
-SELECT c["ProductName"]
+SELECT s["ProductName"]
FROM (
SELECT *
FROM root c
WHERE c["Discriminator"] = "Product" AND NOT c["Discontinued"] AND ((c["UnitsInStock"] + c["UnitsOnOrder"]) < c["ReorderLevel"])
-) c
+) s
""");
});
@@ -651,11 +654,11 @@ public virtual Task FromSqlRaw_composed_with_nullable_predicate(bool async)
AssertSql(
"""
-SELECT c
+SELECT s
FROM (
SELECT * FROM root c WHERE c["Discriminator"] = "Customer"
-) c
-WHERE (c["ContactName"] = c["CompanyName"])
+) s
+WHERE (s["ContactName"] = s["CompanyName"])
""");
});
@@ -701,10 +704,10 @@ public virtual Task FromSqlRaw_queryable_simple_projection_not_composed(bool asy
AssertSql(
"""
-SELECT c["CustomerID"], c["City"]
+SELECT s["CustomerID"], s["City"]
FROM (
SELECT * FROM root c WHERE c["Discriminator"] = "Customer"
-) c
+) s
""");
});
@@ -745,10 +748,10 @@ await AssertQuery(
@p0='London'
@p1='Sales Representative'
-SELECT c
+SELECT s
FROM (
SELECT * FROM root c WHERE c["City"] = @p0 AND c["ContactTitle"] = @p1
-) c
+) s
""");
});
@@ -769,10 +772,10 @@ await AssertQuery(
@p0='London'
@p1='Sales Representative'
-SELECT c
+SELECT s
FROM (
SELECT * FROM root c WHERE c["City"] = @p0 AND c["ContactTitle"] = @p1
-) c
+) s
""");
});
diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/NonSharedPrimitiveCollectionsQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/NonSharedPrimitiveCollectionsQueryCosmosTest.cs
index c2396f72b87..4b6929c2649 100644
--- a/test/EFCore.Cosmos.FunctionalTests/Query/NonSharedPrimitiveCollectionsQueryCosmosTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/Query/NonSharedPrimitiveCollectionsQueryCosmosTest.cs
@@ -17,8 +17,8 @@ SELECT c
FROM root c
WHERE ((c["Discriminator"] = "TestEntity") AND ((
SELECT VALUE COUNT(1)
- FROM i IN c["SomeArray"]
- WHERE (i = "a")) = 2))
+ FROM s IN c["SomeArray"]
+ WHERE (s = "a")) = 2))
OFFSET 0 LIMIT 2
""");
}
@@ -33,8 +33,8 @@ SELECT c
FROM root c
WHERE ((c["Discriminator"] = "TestEntity") AND ((
SELECT VALUE COUNT(1)
- FROM i IN c["SomeArray"]
- WHERE (i = 1)) = 2))
+ FROM s IN c["SomeArray"]
+ WHERE (s = 1)) = 2))
OFFSET 0 LIMIT 2
""");
}
@@ -49,8 +49,8 @@ SELECT c
FROM root c
WHERE ((c["Discriminator"] = "TestEntity") AND ((
SELECT VALUE COUNT(1)
- FROM i IN c["SomeArray"]
- WHERE (i = 1)) = 2))
+ FROM s IN c["SomeArray"]
+ WHERE (s = 1)) = 2))
OFFSET 0 LIMIT 2
""");
}
@@ -65,8 +65,8 @@ SELECT c
FROM root c
WHERE ((c["Discriminator"] = "TestEntity") AND ((
SELECT VALUE COUNT(1)
- FROM i IN c["SomeArray"]
- WHERE (i = 1)) = 2))
+ FROM s IN c["SomeArray"]
+ WHERE (s = 1)) = 2))
OFFSET 0 LIMIT 2
""");
}
@@ -89,8 +89,8 @@ SELECT c
FROM root c
WHERE ((c["Discriminator"] = "TestEntity") AND ((
SELECT VALUE COUNT(1)
- FROM i IN c["SomeArray"]
- WHERE (i = 1.0)) = 2))
+ FROM s IN c["SomeArray"]
+ WHERE (s = 1.0)) = 2))
OFFSET 0 LIMIT 2
""");
}
@@ -105,8 +105,8 @@ SELECT c
FROM root c
WHERE ((c["Discriminator"] = "TestEntity") AND ((
SELECT VALUE COUNT(1)
- FROM i IN c["SomeArray"]
- WHERE (i = 1.0)) = 2))
+ FROM s IN c["SomeArray"]
+ WHERE (s = 1.0)) = 2))
OFFSET 0 LIMIT 2
""");
}
@@ -121,8 +121,8 @@ SELECT c
FROM root c
WHERE ((c["Discriminator"] = "TestEntity") AND ((
SELECT VALUE COUNT(1)
- FROM i IN c["SomeArray"]
- WHERE (i = 1.0)) = 2))
+ FROM s IN c["SomeArray"]
+ WHERE (s = 1.0)) = 2))
OFFSET 0 LIMIT 2
""");
}
@@ -137,8 +137,8 @@ SELECT c
FROM root c
WHERE ((c["Discriminator"] = "TestEntity") AND ((
SELECT VALUE COUNT(1)
- FROM i IN c["SomeArray"]
- WHERE (i = "2023-01-01T12:30:00")) = 2))
+ FROM s IN c["SomeArray"]
+ WHERE (s = "2023-01-01T12:30:00")) = 2))
OFFSET 0 LIMIT 2
""");
}
@@ -153,8 +153,8 @@ SELECT c
FROM root c
WHERE ((c["Discriminator"] = "TestEntity") AND ((
SELECT VALUE COUNT(1)
- FROM i IN c["SomeArray"]
- WHERE (i = "2023-01-01T12:30:00.123")) = 2))
+ FROM s IN c["SomeArray"]
+ WHERE (s = "2023-01-01T12:30:00.123")) = 2))
OFFSET 0 LIMIT 2
""");
}
@@ -169,8 +169,8 @@ SELECT c
FROM root c
WHERE ((c["Discriminator"] = "TestEntity") AND ((
SELECT VALUE COUNT(1)
- FROM i IN c["SomeArray"]
- WHERE (i = "2023-01-01T12:30:00.123456")) = 2))
+ FROM s IN c["SomeArray"]
+ WHERE (s = "2023-01-01T12:30:00.123456")) = 2))
OFFSET 0 LIMIT 2
""");
}
@@ -185,8 +185,8 @@ SELECT c
FROM root c
WHERE ((c["Discriminator"] = "TestEntity") AND ((
SELECT VALUE COUNT(1)
- FROM i IN c["SomeArray"]
- WHERE (i = "2023-01-01")) = 2))
+ FROM s IN c["SomeArray"]
+ WHERE (s = "2023-01-01")) = 2))
OFFSET 0 LIMIT 2
""");
}
@@ -201,8 +201,8 @@ SELECT c
FROM root c
WHERE ((c["Discriminator"] = "TestEntity") AND ((
SELECT VALUE COUNT(1)
- FROM i IN c["SomeArray"]
- WHERE (i = "12:30:00")) = 2))
+ FROM s IN c["SomeArray"]
+ WHERE (s = "12:30:00")) = 2))
OFFSET 0 LIMIT 2
""");
}
@@ -217,8 +217,8 @@ SELECT c
FROM root c
WHERE ((c["Discriminator"] = "TestEntity") AND ((
SELECT VALUE COUNT(1)
- FROM i IN c["SomeArray"]
- WHERE (i = "12:30:00.123")) = 2))
+ FROM s IN c["SomeArray"]
+ WHERE (s = "12:30:00.123")) = 2))
OFFSET 0 LIMIT 2
""");
}
@@ -233,8 +233,8 @@ SELECT c
FROM root c
WHERE ((c["Discriminator"] = "TestEntity") AND ((
SELECT VALUE COUNT(1)
- FROM i IN c["SomeArray"]
- WHERE (i = "12:30:00.123456")) = 2))
+ FROM s IN c["SomeArray"]
+ WHERE (s = "12:30:00.123456")) = 2))
OFFSET 0 LIMIT 2
""");
}
@@ -249,8 +249,8 @@ SELECT c
FROM root c
WHERE ((c["Discriminator"] = "TestEntity") AND ((
SELECT VALUE COUNT(1)
- FROM i IN c["SomeArray"]
- WHERE (i = "2023-01-01T12:30:00+02:00")) = 2))
+ FROM s IN c["SomeArray"]
+ WHERE (s = "2023-01-01T12:30:00+02:00")) = 2))
OFFSET 0 LIMIT 2
""");
}
@@ -265,8 +265,8 @@ SELECT c
FROM root c
WHERE ((c["Discriminator"] = "TestEntity") AND ((
SELECT VALUE COUNT(1)
- FROM i IN c["SomeArray"]
- WHERE (i = true)) = 2))
+ FROM s IN c["SomeArray"]
+ WHERE (s = true)) = 2))
OFFSET 0 LIMIT 2
""");
}
@@ -281,8 +281,8 @@ SELECT c
FROM root c
WHERE ((c["Discriminator"] = "TestEntity") AND ((
SELECT VALUE COUNT(1)
- FROM i IN c["SomeArray"]
- WHERE (i = "dc8c903d-d655-4144-a0fd-358099d40ae1")) = 2))
+ FROM s IN c["SomeArray"]
+ WHERE (s = "dc8c903d-d655-4144-a0fd-358099d40ae1")) = 2))
OFFSET 0 LIMIT 2
""");
}
@@ -292,13 +292,14 @@ public override async Task Array_of_byte_array()
// TODO
await Assert.ThrowsAsync(() => base.Array_of_byte_array());
- AssertSql("""
+ AssertSql(
+ """
SELECT c
FROM root c
WHERE ((c["Discriminator"] = "TestEntity") AND ((
SELECT VALUE COUNT(1)
- FROM i IN c["SomeArray"]
- WHERE (i = "AQI=")) = 2))
+ FROM s IN c["SomeArray"]
+ WHERE (s = "AQI=")) = 2))
OFFSET 0 LIMIT 2
""");
}
@@ -313,8 +314,8 @@ SELECT c
FROM root c
WHERE ((c["Discriminator"] = "TestEntity") AND ((
SELECT VALUE COUNT(1)
- FROM i IN c["SomeArray"]
- WHERE (i = 0)) = 2))
+ FROM s IN c["SomeArray"]
+ WHERE (s = 0)) = 2))
OFFSET 0 LIMIT 2
""");
}
@@ -365,8 +366,8 @@ SELECT c
FROM root c
WHERE ((c["Discriminator"] = "TestEntity") AND ((
SELECT VALUE COUNT(1)
- FROM i IN (SELECT VALUE [1, 2, 3])
- WHERE (i > c["Id"])) = 1))
+ FROM a IN (SELECT VALUE [1, 2, 3])
+ WHERE (a > c["Id"])) = 1))
OFFSET 0 LIMIT 2
""");
}
diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindAggregateOperatorsQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindAggregateOperatorsQueryCosmosTest.cs
index 044e501f7ff..e29f1608e15 100644
--- a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindAggregateOperatorsQueryCosmosTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindAggregateOperatorsQueryCosmosTest.cs
@@ -1659,8 +1659,8 @@ SELECT c
FROM root c
WHERE ((c["Discriminator"] = "Customer") AND EXISTS (
SELECT 1
- FROM i IN (SELECT VALUE ["ABCDE", "ALFKI"])
- WHERE ((i != null) AND (i = c["CustomerID"]))))
+ FROM a IN (SELECT VALUE ["ABCDE", "ALFKI"])
+ WHERE ((a != null) AND (a = c["CustomerID"]))))
""");
});
@@ -1678,8 +1678,8 @@ SELECT c
FROM root c
WHERE ((c["Discriminator"] = "Customer") AND EXISTS (
SELECT 1
- FROM i IN (SELECT VALUE @__p_0)
- WHERE ((i != null) AND (i = c["CustomerID"]))))
+ FROM p IN (SELECT VALUE @__p_0)
+ WHERE ((p != null) AND (p = c["CustomerID"]))))
""",
//
"""
@@ -1689,8 +1689,8 @@ SELECT c
FROM root c
WHERE ((c["Discriminator"] = "Customer") AND EXISTS (
SELECT 1
- FROM i IN (SELECT VALUE @__p_0)
- WHERE ((i != null) AND (i = c["CustomerID"]))))
+ FROM p IN (SELECT VALUE @__p_0)
+ WHERE ((p != null) AND (p = c["CustomerID"]))))
""");
});
@@ -1761,8 +1761,7 @@ public override Task Contains_with_local_ordered_enumerable_inline(bool async)
SELECT c
FROM root c
WHERE ((c["Discriminator"] = "Customer") AND c["CustomerID"] IN ("ABCDE", "ALFKI"))
-"""
- );
+""");
});
public override Task Contains_with_local_ordered_enumerable_inline_closure_mix(bool async)
@@ -1810,8 +1809,7 @@ FROM root c
SELECT c
FROM root c
WHERE ((c["Discriminator"] = "Customer") AND ARRAY_CONTAINS(@__ids_0, c["CustomerID"]))
-"""
- );
+""");
});
public override Task Contains_with_local_object_read_only_collection_closure(bool async)
@@ -1827,8 +1825,7 @@ public override Task Contains_with_local_object_read_only_collection_closure(boo
SELECT c
FROM root c
WHERE ((c["Discriminator"] = "Customer") AND ARRAY_CONTAINS(@__ids_0, c["CustomerID"]))
-"""
- );
+""");
});
public override Task Contains_with_local_ordered_read_only_collection_all_null(bool async)
@@ -1858,8 +1855,7 @@ public override Task Contains_with_local_read_only_collection_inline(bool async)
SELECT c
FROM root c
WHERE ((c["Discriminator"] = "Customer") AND c["CustomerID"] IN ("ABCDE", "ALFKI"))
-"""
- );
+""");
});
public override Task Contains_with_local_read_only_collection_inline_closure_mix(bool async)
@@ -1876,15 +1872,14 @@ SELECT c
FROM root c
WHERE ((c["Discriminator"] = "Customer") AND ARRAY_CONTAINS(@__AsReadOnly_0, c["CustomerID"]))
""",
- //
- """
+ //
+ """
@__AsReadOnly_0='["ABCDE","ANATR"]'
SELECT c
FROM root c
WHERE ((c["Discriminator"] = "Customer") AND ARRAY_CONTAINS(@__AsReadOnly_0, c["CustomerID"]))
-"""
- );
+""");
});
public override Task Contains_with_local_collection_false(bool async)
diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindFunctionsQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindFunctionsQueryCosmosTest.cs
index ff9ad39f9ea..8b94e8ddd97 100644
--- a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindFunctionsQueryCosmosTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindFunctionsQueryCosmosTest.cs
@@ -2020,7 +2020,7 @@ public override Task String_Contains_negated_in_predicate(bool async)
await base.String_Contains_negated_in_predicate(a);
AssertSql(
-"""
+ """
SELECT c
FROM root c
WHERE ((c["Discriminator"] = "Customer") AND NOT(CONTAINS(c["CompanyName"], c["ContactName"])))
diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindWhereQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindWhereQueryCosmosTest.cs
index f9db421c40e..d48b83e5b54 100644
--- a/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindWhereQueryCosmosTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/Query/NorthwindWhereQueryCosmosTest.cs
@@ -495,15 +495,6 @@ SELECT c
FROM root c
WHERE ((c["Discriminator"] = "Customer") AND (c["City"] = @__city_0))
""");
-
- Assert.Equal(
- """
--- @__city_0='London'
-SELECT c
-FROM root c
-WHERE ((c["Discriminator"] = "Customer") AND (c["City"] = @__city_0))
-""", queryString, ignoreLineEndingDifferences: true,
- ignoreWhiteSpaceDifferences: true);
});
return null;
diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/OwnedQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/OwnedQueryCosmosTest.cs
index befaeddd1ba..f5537bf5faa 100644
--- a/test/EFCore.Cosmos.FunctionalTests/Query/OwnedQueryCosmosTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/Query/OwnedQueryCosmosTest.cs
@@ -57,9 +57,9 @@ public override async Task Navigation_rewrite_on_owned_collection_with_compositi
AssertSql(
"""
SELECT (ARRAY(
- SELECT VALUE (t["Id"] != 42)
- FROM t IN c["Orders"]
- ORDER BY t["Id"])[0] ?? false) AS c
+ SELECT VALUE (o["Id"] != 42)
+ FROM o IN c["Orders"]
+ ORDER BY o["Id"])[0] ?? false) AS c
FROM root c
WHERE c["Discriminator"] IN ("OwnedPerson", "Branch", "LeafB", "LeafA")
ORDER BY c["Id"]
@@ -247,9 +247,9 @@ public override Task SelectMany_on_owned_collection(bool async)
AssertSql(
"""
-SELECT a
+SELECT o AS o0
FROM root c
-JOIN a IN c["Orders"]
+JOIN o IN c["Orders"]
WHERE c["Discriminator"] IN ("OwnedPerson", "Branch", "LeafB", "LeafA")
""");
});
@@ -267,10 +267,10 @@ public override Task SelectMany_with_result_selector(bool async)
SELECT VALUE
{
"PersonId" : c["Id"],
- "OrderId" : a["Id"]
+ "OrderId" : o["Id"]
}
FROM root c
-JOIN a IN c["Orders"]
+JOIN o IN c["Orders"]
WHERE c["Discriminator"] IN ("OwnedPerson", "Branch", "LeafB", "LeafA")
""");
});
@@ -388,10 +388,10 @@ await AssertQuery(
// TODO: The following should project out a["Details"], not a: #34067
AssertSql(
"""
-SELECT a["Details"]
+SELECT o["Details"]
FROM root c
-JOIN a IN c["Orders"]
-WHERE (c["Discriminator"] IN ("OwnedPerson", "Branch", "LeafB", "LeafA") AND (ARRAY_LENGTH(a["Details"]) = 1))
+JOIN o IN c["Orders"]
+WHERE (c["Discriminator"] IN ("OwnedPerson", "Branch", "LeafB", "LeafA") AND (ARRAY_LENGTH(o["Details"]) = 1))
ORDER BY c["Id"]
""");
});
@@ -415,10 +415,10 @@ await AssertQuery(
AssertSql(
"""
-SELECT a
+SELECT o AS o0
FROM root c
-JOIN a IN c["Orders"]
-WHERE (c["Discriminator"] IN ("OwnedPerson", "Branch", "LeafB", "LeafA") AND (ARRAY_LENGTH(a["Details"]) = 1))
+JOIN o IN c["Orders"]
+WHERE (c["Discriminator"] IN ("OwnedPerson", "Branch", "LeafB", "LeafA") AND (ARRAY_LENGTH(o["Details"]) = 1))
ORDER BY c["Id"]
""");
});
@@ -442,10 +442,10 @@ await AssertQuery(
AssertSql(
"""
-SELECT a
+SELECT o AS o0
FROM root c
-JOIN a IN c["Orders"]
-WHERE (c["Discriminator"] IN ("OwnedPerson", "Branch", "LeafB", "LeafA") AND (ARRAY_LENGTH(a["Details"]) = 1))
+JOIN o IN c["Orders"]
+WHERE (c["Discriminator"] IN ("OwnedPerson", "Branch", "LeafB", "LeafA") AND (ARRAY_LENGTH(o["Details"]) = 1))
ORDER BY c["Id"]
""");
});
@@ -469,10 +469,10 @@ await AssertQuery(
AssertSql(
"""
-SELECT a["Details"]
+SELECT o["Details"]
FROM root c
-JOIN a IN c["Orders"]
-WHERE (c["Discriminator"] IN ("OwnedPerson", "Branch", "LeafB", "LeafA") AND (ARRAY_LENGTH(a["Details"]) = 1))
+JOIN o IN c["Orders"]
+WHERE (c["Discriminator"] IN ("OwnedPerson", "Branch", "LeafB", "LeafA") AND (ARRAY_LENGTH(o["Details"]) = 1))
ORDER BY c["Id"]
""");
});
@@ -496,10 +496,10 @@ await AssertQuery(
AssertSql(
"""
-SELECT a
+SELECT o AS o0
FROM root c
-JOIN a IN c["Orders"]
-WHERE (c["Discriminator"] IN ("OwnedPerson", "Branch", "LeafB", "LeafA") AND (ARRAY_LENGTH(a["Details"]) = 1))
+JOIN o IN c["Orders"]
+WHERE (c["Discriminator"] IN ("OwnedPerson", "Branch", "LeafB", "LeafA") AND (ARRAY_LENGTH(o["Details"]) = 1))
ORDER BY c["Id"]
""");
});
@@ -741,8 +741,8 @@ SELECT c["Name"]
FROM root c
WHERE (c["Discriminator"] IN ("OwnedPerson", "Branch", "LeafB", "LeafA") AND ((
SELECT VALUE COUNT(1)
- FROM t IN c["Orders"]
- WHERE (DateTimePart("yyyy", t["OrderDate"]) = 2018)) = 1))
+ FROM o IN c["Orders"]
+ WHERE (DateTimePart("yyyy", o["OrderDate"]) = 2018)) = 1))
""");
});
@@ -806,10 +806,10 @@ OFFSET 0 LIMIT 2
"""
@__p_0='1'
-SELECT a
+SELECT o AS o0
FROM root c
-JOIN a IN c["Orders"]
-WHERE (c["Discriminator"] IN ("OwnedPerson", "Branch", "LeafB", "LeafA") AND (a["ClientId"] = @__p_0))
+JOIN o IN c["Orders"]
+WHERE (c["Discriminator"] IN ("OwnedPerson", "Branch", "LeafB", "LeafA") AND (o["ClientId"] = @__p_0))
""");
});
@@ -1091,8 +1091,8 @@ SELECT c
FROM root c
WHERE (c["Discriminator"] IN ("OwnedPerson", "Branch", "LeafB", "LeafA") AND EXISTS (
SELECT 1
- FROM t IN c["Orders"]
- WHERE (t["Id"] = -30)))
+ FROM o IN c["Orders"]
+ WHERE (o["Id"] = -30)))
""");
});
@@ -1110,8 +1110,8 @@ SELECT c
FROM root c
WHERE (c["Discriminator"] IN ("OwnedPerson", "Branch", "LeafB", "LeafA") AND EXISTS (
SELECT 1
- FROM t IN c["Orders"]
- WHERE (t["Id"] = -30)))
+ FROM o IN c["Orders"]
+ WHERE (o["Id"] = -30)))
""");
});
@@ -1163,9 +1163,9 @@ public override async Task OrderBy_ElementAt_over_owned_collection(bool async)
SELECT c
FROM root c
WHERE (c["Discriminator"] IN ("OwnedPerson", "Branch", "LeafB", "LeafA") AND (ARRAY(
- SELECT VALUE t["Id"]
- FROM t IN c["Orders"]
- ORDER BY t["Id"])[1] = -10))
+ SELECT VALUE o["Id"]
+ FROM o IN c["Orders"]
+ ORDER BY o["Id"])[1] = -10))
""");
}
}
@@ -1199,9 +1199,9 @@ public override Task FirstOrDefault_over_owned_collection(bool async)
SELECT c
FROM root c
WHERE (c["Discriminator"] IN ("OwnedPerson", "Branch", "LeafB", "LeafA") AND (DateTimePart("yyyy", (ARRAY(
- SELECT VALUE t["OrderDate"]
- FROM t IN c["Orders"]
- WHERE (t["Id"] > -20))[0] ?? "0001-01-01T00:00:00")) = 2018))
+ SELECT VALUE o["OrderDate"]
+ FROM o IN c["Orders"]
+ WHERE (o["Id"] > -20))[0] ?? "0001-01-01T00:00:00")) = 2018))
""");
});
@@ -1232,12 +1232,12 @@ public override Task Union_over_owned_collection(bool async)
SELECT c
FROM root c
WHERE (c["Discriminator"] IN ("OwnedPerson", "Branch", "LeafB", "LeafA") AND (ARRAY_LENGTH(SetUnion(ARRAY(
- SELECT VALUE t
- FROM t IN c["Orders"]
- WHERE (t["Id"] = -10)), ARRAY(
- SELECT VALUE t
- FROM t IN c["Orders"]
- WHERE (t["Id"] = -11)))) = 2))
+ SELECT VALUE o
+ FROM o IN c["Orders"]
+ WHERE (o["Id"] = -10)), ARRAY(
+ SELECT VALUE o0
+ FROM o0 IN c["Orders"]
+ WHERE (o0["Id"] = -11)))) = 2))
""");
});
@@ -1265,6 +1265,8 @@ public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder build
protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context)
{
+ modelBuilder.HasDefaultContainer("OwnedQueryTest");
+
modelBuilder.Entity(
eb =>
{
diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/PrimitiveCollectionsQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/PrimitiveCollectionsQueryCosmosTest.cs
index 7f65fc88cd7..aa642d076b3 100644
--- a/test/EFCore.Cosmos.FunctionalTests/Query/PrimitiveCollectionsQueryCosmosTest.cs
+++ b/test/EFCore.Cosmos.FunctionalTests/Query/PrimitiveCollectionsQueryCosmosTest.cs
@@ -73,8 +73,8 @@ SELECT c
FROM root c
WHERE ((c["Discriminator"] = "PrimitiveCollectionsEntity") AND ((
SELECT VALUE COUNT(1)
- FROM i IN (SELECT VALUE [])
- WHERE (i > c["Id"])) = 1))
+ FROM a IN (SELECT VALUE [])
+ WHERE (a > c["Id"])) = 1))
""");
});
@@ -90,8 +90,8 @@ SELECT c
FROM root c
WHERE ((c["Discriminator"] = "PrimitiveCollectionsEntity") AND ((
SELECT VALUE COUNT(1)
- FROM i IN (SELECT VALUE [2])
- WHERE (i > c["Id"])) = 1))
+ FROM a IN (SELECT VALUE [2])
+ WHERE (a > c["Id"])) = 1))
""");
});
@@ -107,8 +107,8 @@ SELECT c
FROM root c
WHERE ((c["Discriminator"] = "PrimitiveCollectionsEntity") AND ((
SELECT VALUE COUNT(1)
- FROM i IN (SELECT VALUE [2, 999])
- WHERE (i > c["Id"])) = 1))
+ FROM a IN (SELECT VALUE [2, 999])
+ WHERE (a > c["Id"])) = 1))
""");
});
@@ -124,8 +124,8 @@ SELECT c
FROM root c
WHERE ((c["Discriminator"] = "PrimitiveCollectionsEntity") AND ((
SELECT VALUE COUNT(1)
- FROM i IN (SELECT VALUE [2, 999, 1000])
- WHERE (i > c["Id"])) = 2))
+ FROM a IN (SELECT VALUE [2, 999, 1000])
+ WHERE (a > c["Id"])) = 2))
""");
});
@@ -303,8 +303,8 @@ public override Task Inline_collection_Min_with_two_values(bool async)
SELECT c
FROM root c
WHERE ((c["Discriminator"] = "PrimitiveCollectionsEntity") AND ((
- SELECT VALUE MIN(i)
- FROM i IN (SELECT VALUE [30, c["Int"]])) = 30))
+ SELECT VALUE MIN(a)
+ FROM a IN (SELECT VALUE [30, c["Int"]])) = 30))
""");
});
@@ -319,8 +319,8 @@ public override Task Inline_collection_List_Min_with_two_values(bool async)
SELECT c
FROM root c
WHERE ((c["Discriminator"] = "PrimitiveCollectionsEntity") AND ((
- SELECT VALUE MIN(i)
- FROM i IN (SELECT VALUE [30, c["Int"]])) = 30))
+ SELECT VALUE MIN(a)
+ FROM a IN (SELECT VALUE [30, c["Int"]])) = 30))
""");
});
@@ -335,8 +335,8 @@ public override Task Inline_collection_Max_with_two_values(bool async)
SELECT c
FROM root c
WHERE ((c["Discriminator"] = "PrimitiveCollectionsEntity") AND ((
- SELECT VALUE MAX(i)
- FROM i IN (SELECT VALUE [30, c["Int"]])) = 30))
+ SELECT VALUE MAX(a)
+ FROM a IN (SELECT VALUE [30, c["Int"]])) = 30))
""");
});
@@ -351,8 +351,8 @@ public override Task Inline_collection_List_Max_with_two_values(bool async)
SELECT c
FROM root c
WHERE ((c["Discriminator"] = "PrimitiveCollectionsEntity") AND ((
- SELECT VALUE MAX(i)
- FROM i IN (SELECT VALUE [30, c["Int"]])) = 30))
+ SELECT VALUE MAX(a)
+ FROM a IN (SELECT VALUE [30, c["Int"]])) = 30))
""");
});
@@ -369,8 +369,8 @@ public override Task Inline_collection_Min_with_three_values(bool async)
SELECT c
FROM root c
WHERE ((c["Discriminator"] = "PrimitiveCollectionsEntity") AND ((
- SELECT VALUE MIN(i)
- FROM i IN (SELECT VALUE [30, c["Int"], @__i_0])) = 25))
+ SELECT VALUE MIN(a)
+ FROM a IN (SELECT VALUE [30, c["Int"], @__i_0])) = 25))
""");
});
@@ -387,8 +387,8 @@ public override Task Inline_collection_List_Min_with_three_values(bool async)
SELECT c
FROM root c
WHERE ((c["Discriminator"] = "PrimitiveCollectionsEntity") AND ((
- SELECT VALUE MIN(i)
- FROM i IN (SELECT VALUE [30, c["Int"], @__i_0])) = 25))
+ SELECT VALUE MIN(a)
+ FROM a IN (SELECT VALUE [30, c["Int"], @__i_0])) = 25))
""");
});
@@ -405,8 +405,8 @@ public override Task Inline_collection_Max_with_three_values(bool async)
SELECT c
FROM root c
WHERE ((c["Discriminator"] = "PrimitiveCollectionsEntity") AND ((
- SELECT VALUE MAX(i)
- FROM i IN (SELECT VALUE [30, c["Int"], @__i_0])) = 35))
+ SELECT VALUE MAX(a)
+ FROM a IN (SELECT VALUE [30, c["Int"], @__i_0])) = 35))
""");
});
@@ -423,8 +423,8 @@ public override Task Inline_collection_List_Max_with_three_values(bool async)
SELECT c
FROM root c
WHERE ((c["Discriminator"] = "PrimitiveCollectionsEntity") AND ((
- SELECT VALUE MAX(i)
- FROM i IN (SELECT VALUE [30, c["Int"], @__i_0])) = 35))
+ SELECT VALUE MAX(a)
+ FROM a IN (SELECT VALUE [30, c["Int"], @__i_0])) = 35))
""");
});
@@ -1307,9 +1307,9 @@ public override Task Column_collection_SelectMany(bool async)
AssertSql(
"""
-SELECT a
+SELECT i AS i0
FROM root c
-JOIN a IN c["Ints"]
+JOIN i IN c["Ints"]
WHERE (c["Discriminator"] = "PrimitiveCollectionsEntity")
""");
});
@@ -1322,12 +1322,12 @@ public override Task Column_collection_SelectMany_with_filter(bool async)
AssertSql(
"""
-SELECT a
+SELECT j
FROM root c
JOIN (
SELECT VALUE i
FROM i IN c["Ints"]
- WHERE (i > 1)) a
+ WHERE (i > 1)) j
WHERE (c["Discriminator"] = "PrimitiveCollectionsEntity")
""");
});
@@ -1656,9 +1656,9 @@ public override Task Project_collection_of_datetimes_filtered(bool async)
AssertSql(
"""
SELECT ARRAY(
- SELECT VALUE i
- FROM i IN c["DateTimes"]
- WHERE (DateTimePart("dd", i) != 1)) AS c
+ SELECT VALUE d
+ FROM d IN c["DateTimes"]
+ WHERE (DateTimePart("dd", d) != 1)) AS c
FROM root c
WHERE (c["Discriminator"] = "PrimitiveCollectionsEntity")
ORDER BY c["Id"]
@@ -1762,13 +1762,13 @@ public override Task Project_empty_collection_of_nullables_and_collection_only_c
SELECT VALUE
{
"c" : ARRAY(
- SELECT VALUE i
- FROM i IN c["NullableInts"]
+ SELECT VALUE n
+ FROM n IN c["NullableInts"]
WHERE false),
"c0" : ARRAY(
- SELECT VALUE i
- FROM i IN c["NullableInts"]
- WHERE (i = null))
+ SELECT VALUE n0
+ FROM n0 IN c["NullableInts"]
+ WHERE (n0 = null))
}
FROM root c
WHERE (c["Discriminator"] = "PrimitiveCollectionsEntity")
@@ -1794,17 +1794,17 @@ SELECT VALUE
SELECT VALUE i
FROM i IN c["Ints"]),
"c0" : ARRAY(
- SELECT VALUE i
- FROM i IN c["Ints"]
- ORDER BY i DESC),
+ SELECT VALUE i0
+ FROM i0 IN c["Ints"]
+ ORDER BY i0 DESC),
"c1" : ARRAY(
- SELECT VALUE i
- FROM i IN c["DateTimes"]
- WHERE (DateTimePart("dd", i) != 1)),
+ SELECT VALUE d
+ FROM d IN c["DateTimes"]
+ WHERE (DateTimePart("dd", d) != 1)),
"c2" : ARRAY(
- SELECT VALUE i
- FROM i IN c["DateTimes"]
- WHERE (i > "2000-01-01T00:00:00"))
+ SELECT VALUE d0
+ FROM d0 IN c["DateTimes"]
+ WHERE (d0 > "2000-01-01T00:00:00"))
}
FROM root c
WHERE (c["Discriminator"] = "PrimitiveCollectionsEntity")