Skip to content

Commit 897d104

Browse files
committed
backup
1 parent bfb5a6d commit 897d104

7 files changed

Lines changed: 107 additions & 23 deletions

File tree

src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs

Lines changed: 41 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -366,10 +366,12 @@ JsonScalarExpression jsonScalar
366366
{
367367
var elementType = inlineQueryRootExpression.ElementType;
368368

369-
var rowExpressions = new List<RowValueExpression>();
370369
var encounteredNull = false;
371370
var intTypeMapping = _typeMappingSource.FindMapping(typeof(int), RelationalDependencies.Model);
371+
RelationalTypeMapping? inferredTypeMaping = null;
372+
var sqlExpressions = new SqlExpression[inlineQueryRootExpression.Values.Count];
372373

374+
// Do a first pass, translating the elements and inferring type mappings/nullability.
373375
for (var i = 0; i < inlineQueryRootExpression.Values.Count; i++)
374376
{
375377
// Note that we specifically don't apply the default type mapping to the translation, to allow it to get inferred later based
@@ -380,48 +382,66 @@ JsonScalarExpression jsonScalar
380382
return null;
381383
}
382384

385+
// Infer the type mapping from the different inline elements, applying the type mapping of a column to constants/parameters, and
386+
// also to the projection of the VALUES expression as a whole.
387+
// TODO: This currently picks up the first type mapping; we can do better once we have a type compatibility chart (#15586)
388+
// TODO: See similarity with SqlExpressionFactory.ApplyTypeMappingOnIn()
389+
inferredTypeMaping ??= translatedValue.TypeMapping;
390+
383391
// TODO: Poor man's null semantics: in SqlNullabilityProcessor we don't fully handle the nullability of SelectExpression
384392
// projections. Whether the SelectExpression's projection is nullable or not is determined here in translation, but at this
385393
// point we don't know how to properly calculate nullability (and can't look at parameters).
386394
// So for now, we assume the projected column is nullable if we see anything but non-null constants and non-nullable columns.
387395
encounteredNull |=
388396
translatedValue is not SqlConstantExpression { Value: not null } and not ColumnExpression { IsNullable: false };
389397

390-
rowExpressions.Add(
398+
sqlExpressions[i] = translatedValue;
399+
}
400+
401+
// Second pass: create the VALUES expression's row value expressions.
402+
var rowExpressions = new RowValueExpression[sqlExpressions.Length];
403+
for (var i = 0; i < sqlExpressions.Length; i++)
404+
{
405+
var sqlExpression = sqlExpressions[i];
406+
407+
rowExpressions[i] =
391408
new RowValueExpression(
392409
new[]
393410
{
394411
// Since VALUES may not guarantee row ordering, we add an _ord value by which we'll order.
395412
_sqlExpressionFactory.Constant(i, intTypeMapping),
396-
// Note that for the actual value, we must leave the type mapping null to allow it to get inferred later based on usage
397-
translatedValue
398-
}));
413+
// If no type mapping was inferred (i.e. no column in the inline collection), it's left null, to allow it to get
414+
// inferred later based on usage.
415+
sqlExpression.TypeMapping is null && inferredTypeMaping is not null
416+
? _sqlExpressionFactory.ApplyTypeMapping(sqlExpression, inferredTypeMaping)
417+
: sqlExpression
418+
});
399419
}
400420

401421
var alias = _sqlAliasManager.GenerateTableAlias("values");
402422
var valuesExpression = new ValuesExpression(alias, rowExpressions, new[] { ValuesOrderingColumnName, ValuesValueColumnName });
403423

404424
// Note: we leave the element type mapping null, to allow it to get inferred based on queryable operators composed on top.
425+
var valueColumn = new ColumnExpression(
426+
ValuesValueColumnName,
427+
alias,
428+
elementType.UnwrapNullableType(),
429+
typeMapping: inferredTypeMaping,
430+
nullable: encounteredNull);
431+
var orderingColumn = new ColumnExpression(
432+
ValuesOrderingColumnName,
433+
alias,
434+
typeof(int),
435+
typeMapping: intTypeMapping,
436+
nullable: false);
437+
405438
var selectExpression = new SelectExpression(
406439
[valuesExpression],
407-
new ColumnExpression(
408-
ValuesValueColumnName,
409-
alias,
410-
elementType.UnwrapNullableType(),
411-
typeMapping: null,
412-
nullable: encounteredNull),
413-
identifier: [],
440+
valueColumn,
441+
identifier: [(orderingColumn, orderingColumn.TypeMapping!.Comparer)],
414442
_sqlAliasManager);
415443

416-
selectExpression.AppendOrdering(
417-
new OrderingExpression(
418-
selectExpression.CreateColumnExpression(
419-
valuesExpression,
420-
ValuesOrderingColumnName,
421-
typeof(int),
422-
intTypeMapping,
423-
columnNullable: false),
424-
ascending: true));
444+
selectExpression.AppendOrdering(new OrderingExpression(orderingColumn, ascending: true));
425445

426446
Expression shaperExpression = new ProjectionBindingExpression(
427447
selectExpression, new ProjectionMember(), encounteredNull ? elementType.MakeNullable() : elementType);

src/EFCore.SqlServer/Query/Internal/SqlServerQueryableMethodTranslatingExpressionVisitor.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,10 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Diagnostics.CodeAnalysis;
5-
using Microsoft.EntityFrameworkCore.Internal;
65
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
76
using Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal;
87
using Microsoft.EntityFrameworkCore.SqlServer.Internal;
98
using Microsoft.EntityFrameworkCore.SqlServer.Metadata.Internal;
10-
using Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal;
119

1210
namespace Microsoft.EntityFrameworkCore.SqlServer.Query.Internal;
1311

test/EFCore.Specification.Tests/Query/PrimitiveCollectionsQueryTestBase.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -928,6 +928,15 @@ public virtual Task Project_primitive_collections_element(bool async)
928928
},
929929
assertOrder: true);
930930

931+
[ConditionalTheory]
932+
[MemberData(nameof(IsAsyncData))]
933+
public virtual Task Project_inline_collection(bool async)
934+
=> AssertQuery(
935+
async,
936+
ss => ss.Set<PrimitiveCollectionsEntity>().Select(x => new[] { x.String, "foo" }),
937+
elementAsserter: (e, a) => AssertCollection(e, a, ordered: true),
938+
assertOrder: true);
939+
931940
[ConditionalTheory] // #32208, #32215
932941
[MemberData(nameof(IsAsyncData))]
933942
public virtual Task Nested_contains_with_Lists_and_no_inferred_type_mapping(bool async)

test/EFCore.SqlServer.FunctionalTests/Query/NonSharedPrimitiveCollectionsQuerySqlServerTest.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -871,6 +871,30 @@ public virtual async Task Same_collection_with_conflicting_type_mappings_not_sup
871871
Assert.Equal(RelationalStrings.ConflictingTypeMappingsInferredForColumn("value"), exception.Message);
872872
}
873873

874+
[ConditionalFact]
875+
public virtual async Task Infer_inline_collection_type_mapping()
876+
{
877+
var contextFactory = await InitializeAsync<TestContext>(
878+
onModelCreating: mb => mb.Entity<TestEntity>(b => b.Property<DateTime>("DateTime").HasColumnType("datetime")));
879+
880+
await using var context = contextFactory.CreateContext();
881+
882+
_ = await context.Set<TestEntity>()
883+
.Where(b => new[] { new DateTime(2020, 1, 1), EF.Property<DateTime>(b, "DateTime") }[0] == new DateTime(2020, 1, 1))
884+
.ToArrayAsync();
885+
886+
AssertSql(
887+
"""
888+
SELECT [t].[Id], [t].[DateTime], [t].[Ints]
889+
FROM [TestEntity] AS [t]
890+
WHERE (
891+
SELECT [v].[Value]
892+
FROM (VALUES (0, '2020-01-01T00:00:00.000'), (1, [t].[DateTime])) AS [v]([_ord], [Value])
893+
ORDER BY [v].[_ord]
894+
OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY) = '2020-01-01T00:00:00.000'
895+
""");
896+
}
897+
874898
#endregion Type mapping inference
875899

876900
[ConditionalFact]

test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQueryOldSqlServerTest.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,17 @@ ORDER BY [p].[Id]
792792
""");
793793
}
794794

795+
public override async Task Project_inline_collection(bool async)
796+
{
797+
await base.Project_inline_collection(async);
798+
799+
AssertSql(
800+
"""
801+
SELECT [p].[String]
802+
FROM [PrimitiveCollectionsEntity] AS [p]
803+
""");
804+
}
805+
795806
public override async Task Nested_contains_with_Lists_and_no_inferred_type_mapping(bool async)
796807
{
797808
await base.Nested_contains_with_Lists_and_no_inferred_type_mapping(async);

test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerTest.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1470,6 +1470,17 @@ ORDER BY [p].[Id]
14701470
""");
14711471
}
14721472

1473+
public override async Task Project_inline_collection(bool async)
1474+
{
1475+
await base.Project_inline_collection(async);
1476+
1477+
AssertSql(
1478+
"""
1479+
SELECT [p].[String]
1480+
FROM [PrimitiveCollectionsEntity] AS [p]
1481+
""");
1482+
}
1483+
14731484
public override async Task Nested_contains_with_Lists_and_no_inferred_type_mapping(bool async)
14741485
{
14751486
await base.Nested_contains_with_Lists_and_no_inferred_type_mapping(async);

test/EFCore.Sqlite.FunctionalTests/Query/PrimitiveCollectionsQuerySqliteTest.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1333,6 +1333,17 @@ ORDER BY "p"."Id"
13331333
""");
13341334
}
13351335

1336+
public override async Task Project_inline_collection(bool async)
1337+
{
1338+
await base.Project_inline_collection(async);
1339+
1340+
AssertSql(
1341+
"""
1342+
SELECT "p"."String"
1343+
FROM "PrimitiveCollectionsEntity" AS "p"
1344+
""");
1345+
}
1346+
13361347
public override async Task Project_empty_collection_of_nullables_and_collection_only_containing_nulls(bool async)
13371348
=> Assert.Equal(
13381349
SqliteStrings.ApplyNotSupported,

0 commit comments

Comments
 (0)