未验证 提交 46782f0c 编写于 作者: S Shay Rojansky 提交者: GitHub

Sync EF Core to 7.0.0-preview.5.22260.3 (#2369)

* Fix nested set operations with leftmost nulls

Fixes #2366
上级 e946af86
<Project>
<PropertyGroup>
<EFCoreVersion>7.0.0-preview.4.22229.2</EFCoreVersion>
<MicrosoftExtensionsVersion>7.0.0-preview.4.22229.4</MicrosoftExtensionsVersion>
<NpgsqlVersion>7.0.0-preview.4</NpgsqlVersion>
<EFCoreVersion>7.0.0-preview.5.22266.3</EFCoreVersion>
<MicrosoftExtensionsVersion>7.0.0-preview.5.22258.4</MicrosoftExtensionsVersion>
<NpgsqlVersion>7.0.0-preview.5-ci.20220516T154412</NpgsqlVersion>
</PropertyGroup>
<ItemGroup>
......
......@@ -9,6 +9,7 @@
<packageSourceMapping>
<packageSource key="npgsql-vnext">
<package pattern="Npgsql" />
<package pattern="Npgsql.*" />
</packageSource>
<packageSource key="dotnet7">
......
......@@ -96,7 +96,6 @@ public static IServiceCollection AddEntityFrameworkNpgsql(this IServiceCollectio
.TryAdd<IRelationalAnnotationProvider, NpgsqlAnnotationProvider>()
.TryAdd<IModelValidator, NpgsqlModelValidator>()
.TryAdd<IProviderConventionSetBuilder, NpgsqlConventionSetBuilder>()
.TryAdd<IRelationalValueBufferFactoryFactory, TypedRelationalValueBufferFactoryFactory>()
.TryAdd<IUpdateSqlGenerator, NpgsqlUpdateSqlGenerator>()
.TryAdd<IModificationCommandBatchFactory, NpgsqlModificationCommandBatchFactory>()
.TryAdd<IValueGeneratorSelector, NpgsqlValueGeneratorSelector>()
......@@ -111,6 +110,7 @@ public static IServiceCollection AddEntityFrameworkNpgsql(this IServiceCollectio
.TryAdd<IEvaluatableExpressionFilter, NpgsqlEvaluatableExpressionFilter>()
.TryAdd<IQuerySqlGeneratorFactory, NpgsqlQuerySqlGeneratorFactory>()
.TryAdd<IRelationalSqlTranslatingExpressionVisitorFactory, NpgsqlSqlTranslatingExpressionVisitorFactory>()
.TryAdd<IQueryTranslationPostprocessorFactory, NpgsqlQueryTranslationPostprocessorFactory>()
.TryAdd<IRelationalParameterBasedSqlProcessorFactory, NpgsqlParameterBasedSqlProcessorFactory>()
.TryAdd<ISqlExpressionFactory, NpgsqlSqlExpressionFactory>()
.TryAdd<ISingletonOptions, INpgsqlSingletonOptions>(p => p.GetRequiredService<INpgsqlSingletonOptions>())
......
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query.Internal;
public class NpgsqlQueryTranslationPostprocessor : RelationalQueryTranslationPostprocessor
{
public NpgsqlQueryTranslationPostprocessor(
QueryTranslationPostprocessorDependencies dependencies,
RelationalQueryTranslationPostprocessorDependencies relationalDependencies,
QueryCompilationContext queryCompilationContext)
: base(dependencies, relationalDependencies, queryCompilationContext)
{
}
public override Expression Process(Expression query)
{
var result = base.Process(query);
result = new NpgsqlSetOperationTypeResolutionCompensatingExpressionVisitor().Visit(result);
return result;
}
}
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query.Internal;
public class NpgsqlQueryTranslationPostprocessorFactory : IQueryTranslationPostprocessorFactory
{
public NpgsqlQueryTranslationPostprocessorFactory(
QueryTranslationPostprocessorDependencies dependencies,
RelationalQueryTranslationPostprocessorDependencies relationalDependencies)
{
Dependencies = dependencies;
RelationalDependencies = relationalDependencies;
}
/// <summary>
/// Dependencies for this service.
/// </summary>
protected virtual QueryTranslationPostprocessorDependencies Dependencies { get; }
/// <summary>
/// Relational provider-specific dependencies for this service.
/// </summary>
protected virtual RelationalQueryTranslationPostprocessorDependencies RelationalDependencies { get; }
/// <inheritdoc />
public virtual QueryTranslationPostprocessor Create(QueryCompilationContext queryCompilationContext)
=> new NpgsqlQueryTranslationPostprocessor(Dependencies, RelationalDependencies, queryCompilationContext);
}
using Microsoft.EntityFrameworkCore.Query.Internal;
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query.Internal;
public class NpgsqlSetOperationTypeResolutionCompensatingExpressionVisitor : ExpressionVisitor
{
private State _state;
protected override Expression VisitExtension(Expression extensionExpression)
=> extensionExpression switch
{
ShapedQueryExpression shapedQueryExpression
=> shapedQueryExpression.Update(
Visit(shapedQueryExpression.QueryExpression),
Visit(shapedQueryExpression.ShaperExpression)),
SetOperationBase setOperationExpression => VisitSetOperation(setOperationExpression),
SelectExpression selectExpression => VisitSelect(selectExpression),
TpcTablesExpression tpcTablesExpression => VisitTpcTablesExpression(tpcTablesExpression),
_ => base.VisitExtension(extensionExpression)
};
private Expression VisitSetOperation(SetOperationBase setOperationExpression)
{
switch (_state)
{
case State.Nothing:
_state = State.InSingleSetOperation;
var visited = base.VisitExtension(setOperationExpression);
_state = State.Nothing;
return visited;
case State.InSingleSetOperation:
_state = State.InNestedSetOperation;
visited = base.VisitExtension(setOperationExpression);
_state = State.InSingleSetOperation;
return visited;
default:
return base.VisitExtension(setOperationExpression);
}
}
#pragma warning disable EF1001
private Expression VisitTpcTablesExpression(TpcTablesExpression tpcTablesExpression)
{
var parentState = _state;
if (tpcTablesExpression.SelectExpressions.Count < 3)
{
return base.VisitExtension(tpcTablesExpression);
}
var changed = false;
var visitedSelectExpressions = new SelectExpression[tpcTablesExpression.SelectExpressions.Count];
_state = State.InNestedSetOperation;
visitedSelectExpressions[0] = (SelectExpression)Visit(tpcTablesExpression.SelectExpressions[0]);
changed |= visitedSelectExpressions[0] != tpcTablesExpression.SelectExpressions[0];
_state = State.AlreadyCompensated;
for (var i = 1; i < tpcTablesExpression.SelectExpressions.Count; i++)
{
var selectExpression = tpcTablesExpression.SelectExpressions[i];
var visitedSelectExpression = (SelectExpression)Visit(tpcTablesExpression.SelectExpressions[i]);
visitedSelectExpressions[i] = visitedSelectExpression;
changed |= selectExpression != visitedSelectExpression;
}
_state = parentState;
return changed
? new TpcTablesExpression(tpcTablesExpression.Alias, tpcTablesExpression.EntityType, visitedSelectExpressions)
: tpcTablesExpression;
}
#pragma warning restore EF1001
private Expression VisitSelect(SelectExpression selectExpression)
{
var changed = false;
var tables = new List<TableExpressionBase>();
foreach (var table in selectExpression.Tables)
{
var newTable = (TableExpressionBase)Visit(table);
changed |= newTable != table;
tables.Add(newTable);
}
// Above we visited the tables, which may contain nested set operations - so we retained our state.
// When visiting the below elements, reset to state to properly handle nested unions inside e.g. the predicate.
var parentState = _state;
_state = State.Nothing;
var projections = new List<ProjectionExpression>();
foreach (var item in selectExpression.Projection)
{
// Inject an explicit cast node around null literals
var updatedProjection = parentState == State.InNestedSetOperation && item.Expression is SqlConstantExpression { Value : null }
? item.Update(
new SqlUnaryExpression(ExpressionType.Convert, item.Expression, item.Expression.Type, item.Expression.TypeMapping))
: (ProjectionExpression)Visit(item);
projections.Add(updatedProjection);
changed |= updatedProjection != item;
}
var predicate = (SqlExpression?)Visit(selectExpression.Predicate);
changed |= predicate != selectExpression.Predicate;
var groupBy = new List<SqlExpression>();
foreach (var groupingKey in selectExpression.GroupBy)
{
var newGroupingKey = (SqlExpression)Visit(groupingKey);
changed |= newGroupingKey != groupingKey;
groupBy.Add(newGroupingKey);
}
var havingExpression = (SqlExpression?)Visit(selectExpression.Having);
changed |= havingExpression != selectExpression.Having;
var orderings = new List<OrderingExpression>();
foreach (var ordering in selectExpression.Orderings)
{
var orderingExpression = (SqlExpression)Visit(ordering.Expression);
changed |= orderingExpression != ordering.Expression;
orderings.Add(ordering.Update(orderingExpression));
}
var offset = (SqlExpression?)Visit(selectExpression.Offset);
changed |= offset != selectExpression.Offset;
var limit = (SqlExpression?)Visit(selectExpression.Limit);
changed |= limit != selectExpression.Limit;
// If we were in the InNestedSetOperation state, we've applied all explicit type mappings when visiting the ProjectionExpressions
// above; change the state to prevent unnecessarily continuing to compensate
_state = parentState == State.InNestedSetOperation ? State.AlreadyCompensated : parentState;
return changed
? selectExpression.Update(
projections, tables, predicate, groupBy, havingExpression, orderings, limit, offset)
: selectExpression;
}
private enum State
{
Nothing,
InSingleSetOperation,
InNestedSetOperation,
AlreadyCompensated
}
}
......@@ -34,6 +34,7 @@ public NpgsqlRelationalConnection(RelationalConnectionDependencies dependencies)
protected override DbConnection CreateDbConnection()
{
var conn = new NpgsqlConnection(ConnectionString);
if (ProvideClientCertificatesCallback is not null)
{
conn.ProvideClientCertificatesCallback = ProvideClientCertificatesCallback;
......
......@@ -86,8 +86,9 @@ protected override void Consume(RelationalDataReader reader)
modificationCommand.Entries);
}
var valueBufferFactory = CreateValueBufferFactory(modificationCommand.ColumnModifications);
modificationCommand.PropagateResults(valueBufferFactory.Create(npgsqlReader));
Check.DebugAssert(modificationCommand.RequiresResultPropagation, "RequiresResultPropagation is false");
modificationCommand.PropagateResults(reader);
npgsqlReader.NextResult();
}
......@@ -163,8 +164,9 @@ protected override void Consume(RelationalDataReader reader)
);
}
var valueBufferFactory = CreateValueBufferFactory(modificationCommand.ColumnModifications);
modificationCommand.PropagateResults(valueBufferFactory.Create(npgsqlReader));
Check.DebugAssert(modificationCommand.RequiresResultPropagation, "RequiresResultPropagation is false");
modificationCommand.PropagateResults(reader);
await npgsqlReader.NextResultAsync(cancellationToken).ConfigureAwait(false);
}
......
......@@ -106,10 +106,4 @@ public override void AppendNextSequenceValueOperation(StringBuilder commandStrin
SqlGenerationHelper.DelimitIdentifier(commandStringBuilder, Check.NotNull(name, nameof(name)), schema);
commandStringBuilder.Append("')");
}
protected override void AppendIdentityWhereCondition(StringBuilder commandStringBuilder, IColumnModification columnModification)
=> throw new NotSupportedException();
protected override void AppendRowsAffectedWhereCondition(StringBuilder commandStringBuilder, int expectedRowsAffected)
=> throw new NotSupportedException();
}
......@@ -133,8 +133,7 @@ public void Inserts_when_database_type_is_different()
}
#if !Test20
[Theory]
[ConditionalTheory]
[InlineData(3)]
[InlineData(4)]
public void Inserts_are_batched_only_when_necessary(int minBatchSize)
......@@ -148,13 +147,9 @@ public void Inserts_are_batched_only_when_necessary(int minBatchSize)
var owner = new Owner();
context.Owners.Add(owner);
for (var i = 1; i < 4; i++)
for (var i = 1; i < 3; i++)
{
var blog = new Blog
{
Id = Guid.NewGuid(),
Owner = owner
};
var blog = new Blog { Id = Guid.NewGuid(), Owner = owner };
context.Set<Blog>().Add(blog);
expectedBlogs.Add(blog);
......@@ -166,14 +161,15 @@ public void Inserts_are_batched_only_when_necessary(int minBatchSize)
Assert.Contains(
minBatchSize == 3
? RelationalResources.LogBatchReadyForExecution(new TestLogger<NpgsqlLoggingDefinitions>()).GenerateMessage(3)
: RelationalResources.LogBatchSmallerThanMinBatchSize(new TestLogger<NpgsqlLoggingDefinitions>()).GenerateMessage(3, 4),
? RelationalResources.LogBatchReadyForExecution(new TestLogger<NpgsqlLoggingDefinitions>())
.GenerateMessage(3)
: RelationalResources.LogBatchSmallerThanMinBatchSize(new TestLogger<NpgsqlLoggingDefinitions>())
.GenerateMessage(3, 4),
Fixture.TestSqlLoggerFactory.Log.Select(l => l.Message));
Assert.Equal(minBatchSize <= 3 ? 2 : 4, Fixture.TestSqlLoggerFactory.SqlStatements.Count);
Assert.Equal(minBatchSize <= 3 ? 1 : 3, Fixture.TestSqlLoggerFactory.SqlStatements.Count);
}, context => AssertDatabaseState(context, false, expectedBlogs));
}
#endif
private void AssertDatabaseState(DbContext context, bool clientOrder, List<Blog> expectedBlogs)
{
......
......@@ -11,6 +11,22 @@ protected ConnectionInterceptionNpgsqlTestBase(InterceptionNpgsqlFixtureBase fix
{
}
[ConditionalTheory(Skip = "#2368")]
public override Task Intercept_connection_creation_passively(bool async)
=> base.Intercept_connection_creation_passively(async);
[ConditionalTheory(Skip = "#2368")]
public override Task Intercept_connection_creation_with_multiple_interceptors(bool async)
=> base.Intercept_connection_creation_with_multiple_interceptors(async);
[ConditionalTheory(Skip = "#2368")]
public override Task Intercept_connection_to_override_connection_after_creation(bool async)
=> base.Intercept_connection_to_override_connection_after_creation(async);
[ConditionalTheory(Skip = "#2368")]
public override Task Intercept_connection_to_override_creation(bool async)
=> base.Intercept_connection_to_override_creation(async);
public abstract class InterceptionNpgsqlFixtureBase : InterceptionFixtureBase
{
protected override string StoreName => "ConnectionInterception";
......@@ -22,6 +38,9 @@ public abstract class InterceptionNpgsqlFixtureBase : InterceptionFixtureBase
=> base.InjectInterceptors(serviceCollection.AddEntityFrameworkNpgsql(), injectedInterceptors);
}
protected override DbContextOptionsBuilder ConfigureProvider(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.UseNpgsql();
protected override BadUniverseContext CreateBadUniverse(DbContextOptionsBuilder optionsBuilder)
=> new(optionsBuilder.UseNpgsql(new FakeDbConnection()).Options);
......@@ -66,4 +85,4 @@ public class InterceptionNpgsqlFixture : InterceptionNpgsqlFixtureBase
protected override bool ShouldSubscribeToDiagnosticListener => true;
}
}
}
\ No newline at end of file
}
using Microsoft.EntityFrameworkCore.TestModels.Northwind;
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query;
public class NorthwindSetOperationsQueryNpgsqlTest
......@@ -9,7 +11,157 @@ public class NorthwindSetOperationsQueryNpgsqlTest
: base(fixture)
{
ClearLog();
//Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
// Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
}
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual async Task Leftmost_nulls_with_two_unions(bool async)
{
await AssertQuery(
async,
ss => ss.Set<Order>().Select(o => new { OrderID = (int?)null, o.CustomerID })
.Union(ss.Set<Order>().Select(o => new { OrderID = (int?)o.OrderID, o.CustomerID })));
AssertSql(
@"SELECT NULL AS ""OrderID"", o.""CustomerID""
FROM ""Orders"" AS o
UNION
SELECT o0.""OrderID"", o0.""CustomerID""
FROM ""Orders"" AS o0");
}
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual async Task Leftmost_nulls_with_three_unions(bool async)
{
await AssertQuery(
async,
ss => ss.Set<Order>().Select(o => new { OrderID = (int?)null, o.CustomerID })
.Union(ss.Set<Order>().Select(o => new { OrderID = (int?)null, o.CustomerID }))
.Union(ss.Set<Order>().Select(o => new { OrderID = (int?)o.OrderID, o.CustomerID })));
AssertSql(
@"SELECT NULL::INT AS ""OrderID"", o.""CustomerID""
FROM ""Orders"" AS o
UNION
SELECT NULL AS ""OrderID"", o0.""CustomerID""
FROM ""Orders"" AS o0
UNION
SELECT o1.""OrderID"", o1.""CustomerID""
FROM ""Orders"" AS o1");
}
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual async Task Leftmost_nulls_with_four_unions(bool async)
{
await AssertQuery(
async,
ss => ss.Set<Order>().Select(o => new { OrderID = (int?)null, o.CustomerID })
.Union(ss.Set<Order>().Select(o => new { OrderID = (int?)null, o.CustomerID }))
.Union(ss.Set<Order>().Select(o => new { OrderID = (int?)null, o.CustomerID }))
.Union(ss.Set<Order>().Select(o => new { OrderID = (int?)o.OrderID, o.CustomerID })));
AssertSql(
@"SELECT NULL::INT AS ""OrderID"", o.""CustomerID""
FROM ""Orders"" AS o
UNION
SELECT NULL AS ""OrderID"", o0.""CustomerID""
FROM ""Orders"" AS o0
UNION
SELECT NULL AS ""OrderID"", o1.""CustomerID""
FROM ""Orders"" AS o1
UNION
SELECT o2.""OrderID"", o2.""CustomerID""
FROM ""Orders"" AS o2");
}
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual async Task Leftmost_nulls_with_five_unions(bool async)
{
await AssertQuery(
async,
ss => ss.Set<Order>().Select(o => new { OrderID = (int?)null, o.CustomerID })
.Union(ss.Set<Order>().Select(o => new { OrderID = (int?)null, o.CustomerID }))
.Union(ss.Set<Order>().Select(o => new { OrderID = (int?)null, o.CustomerID }))
.Union(ss.Set<Order>().Select(o => new { OrderID = (int?)null, o.CustomerID }))
.Union(ss.Set<Order>().Select(o => new { OrderID = (int?)o.OrderID, o.CustomerID })));
AssertSql(
@"SELECT NULL::INT AS ""OrderID"", o.""CustomerID""
FROM ""Orders"" AS o
UNION
SELECT NULL AS ""OrderID"", o0.""CustomerID""
FROM ""Orders"" AS o0
UNION
SELECT NULL AS ""OrderID"", o1.""CustomerID""
FROM ""Orders"" AS o1
UNION
SELECT NULL AS ""OrderID"", o2.""CustomerID""
FROM ""Orders"" AS o2
UNION
SELECT o3.""OrderID"", o3.""CustomerID""
FROM ""Orders"" AS o3");
}
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual async Task Leftmost_nulls_in_tables_and_predicate(bool async)
{
await AssertQuery(
async,
ss => ss.Set<Order>().Select(o => new { OrderID = (int?)null, o.CustomerID })
.Union(ss.Set<Order>().Select(o => new { OrderID = (int?)null, o.CustomerID }))
.Union(ss.Set<Order>().Select(o => new { OrderID = (int?)o.OrderID, o.CustomerID }))
.Where(o => o.CustomerID ==
ss.Set<Order>().Select(o => new { OrderID = (int?)null, o.CustomerID })
.Union(ss.Set<Order>().Select(o => new { OrderID = (int?)null, o.CustomerID }))
.Union(ss.Set<Order>().Select(o => new { OrderID = (int?)o.OrderID, o.CustomerID }))
.OrderBy(o => o.CustomerID)
.First()
.CustomerID));
AssertSql(
@"SELECT t0.""OrderID"", t0.""CustomerID""
FROM (
SELECT NULL::INT AS ""OrderID"", o.""CustomerID""
FROM ""Orders"" AS o
UNION
SELECT NULL AS ""OrderID"", o0.""CustomerID""
FROM ""Orders"" AS o0
UNION
SELECT o1.""OrderID"", o1.""CustomerID""
FROM ""Orders"" AS o1
) AS t0
WHERE t0.""CustomerID"" = (
SELECT t1.""CustomerID""
FROM (
SELECT NULL::INT AS ""OrderID"", o2.""CustomerID""
FROM ""Orders"" AS o2
UNION
SELECT NULL AS ""OrderID"", o3.""CustomerID""
FROM ""Orders"" AS o3
UNION
SELECT o4.""OrderID"", o4.""CustomerID""
FROM ""Orders"" AS o4
) AS t1
ORDER BY t1.""CustomerID"" NULLS FIRST
LIMIT 1) OR ((t0.""CustomerID"" IS NULL) AND ((
SELECT t1.""CustomerID""
FROM (
SELECT NULL::INT AS ""OrderID"", o2.""CustomerID""
FROM ""Orders"" AS o2
UNION
SELECT NULL AS ""OrderID"", o3.""CustomerID""
FROM ""Orders"" AS o3
UNION
SELECT o4.""OrderID"", o4.""CustomerID""
FROM ""Orders"" AS o4
) AS t1
ORDER BY t1.""CustomerID"" NULLS FIRST
LIMIT 1) IS NULL))");
}
public override async Task Client_eval_Union_FirstOrDefault(bool async)
......
......@@ -13,4 +13,8 @@ public override Task SelectMany_where_Select(bool async)
// Writes DateTime with Kind=Unspecified to timestamptz
public override Task Subquery_first_member_compared_to_null(bool async)
=> Task.CompletedTask;
[ConditionalTheory(Skip = "https://github.com/dotnet/efcore/pull/27995/files#r874038747")]
public override Task StoreType_for_UDF_used(bool async)
=> base.StoreType_for_UDF_used(async);
}
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query;
public class TPCFiltersInheritanceQueryNpgsqlFixture : TPCInheritanceQueryNpgsqlFixture
{
protected override bool EnableFilters
=> true;
}
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query;
public class TPCFiltersInheritanceQueryNpgsqlTest : TPCFiltersInheritanceQueryTestBase<TPCFiltersInheritanceQueryNpgsqlFixture>
{
public TPCFiltersInheritanceQueryNpgsqlTest(TPCFiltersInheritanceQueryNpgsqlFixture fixture)
: base(fixture)
{
}
}
using Microsoft.EntityFrameworkCore.TestModels.GearsOfWarModel;
using Npgsql.EntityFrameworkCore.PostgreSQL.TestUtilities;
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query;
public class TPCGearsOfWarQueryNpgsqlFixture : TPCGearsOfWarQueryRelationalFixture
{
protected override ITestStoreFactory TestStoreFactory
=> NpgsqlTestStoreFactory.Instance;
protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context)
{
base.OnModelCreating(modelBuilder, context);
modelBuilder.HasPostgresExtension("uuid-ossp");
modelBuilder.Entity<CogTag>().Property(c => c.IssueDate).HasColumnType("timestamp without time zone");
modelBuilder.Entity<City>().Property(g => g.Location).HasColumnType("varchar(100)");
}
private GearsOfWarData _expectedData;
public override ISetSource GetExpectedData()
{
if (_expectedData is null)
{
_expectedData = new GearsOfWarData();
// GearsOfWarData contains DateTimeOffsets with various offsets, which we don't support. Change these to UTC.
// Also chop sub-microsecond precision which PostgreSQL does not support.
foreach (var mission in _expectedData.Missions)
{
mission.Timeline = new DateTimeOffset(
mission.Timeline.Ticks - (mission.Timeline.Ticks % (TimeSpan.TicksPerMillisecond / 1000)), TimeSpan.Zero);
}
}
return _expectedData;
}
protected override void Seed(GearsOfWarContext context)
{
// GearsOfWarData contains DateTimeOffsets with various offsets, which we don't support. Change these to UTC.
// Also chop sub-microsecond precision which PostgreSQL does not support.
SeedForNpgsql(context);
}
public static void SeedForNpgsql(GearsOfWarContext context)
{
var squads = GearsOfWarData.CreateSquads();
var missions = GearsOfWarData.CreateMissions();
var squadMissions = GearsOfWarData.CreateSquadMissions();
var cities = GearsOfWarData.CreateCities();
var weapons = GearsOfWarData.CreateWeapons();
var tags = GearsOfWarData.CreateTags();
var gears = GearsOfWarData.CreateGears();
var locustLeaders = GearsOfWarData.CreateLocustLeaders();
var factions = GearsOfWarData.CreateFactions();
var locustHighCommands = GearsOfWarData.CreateHighCommands();
foreach (var mission in missions)
{
// var newThing = new DateTimeOffset(orig.Ticks - (orig.Ticks % (TimeSpan.TicksPerMillisecond / 1000)), TimeSpan.Zero);
mission.Timeline = new DateTimeOffset(
mission.Timeline.Ticks - (mission.Timeline.Ticks % (TimeSpan.TicksPerMillisecond / 1000)), TimeSpan.Zero);
}
GearsOfWarData.WireUp(
squads, missions, squadMissions, cities, weapons, tags, gears, locustLeaders, factions, locustHighCommands);
context.Squads.AddRange(squads);
context.Missions.AddRange(missions);
context.SquadMissions.AddRange(squadMissions);
context.Cities.AddRange(cities);
context.Weapons.AddRange(weapons);
context.Tags.AddRange(tags);
context.Gears.AddRange(gears);
context.LocustLeaders.AddRange(locustLeaders);
context.Factions.AddRange(factions);
context.LocustHighCommands.AddRange(locustHighCommands);
context.SaveChanges();
GearsOfWarData.WireUp2(locustLeaders, factions);
context.SaveChanges();
}
}
using Microsoft.EntityFrameworkCore.TestModels.GearsOfWarModel;
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query;
public class TPCGearsOfWarQueryNpgsqlTest : TPCGearsOfWarQueryRelationalTestBase<TPCGearsOfWarQueryNpgsqlFixture>
{
public TPCGearsOfWarQueryNpgsqlTest(TPCGearsOfWarQueryNpgsqlFixture fixture, ITestOutputHelper testOutputHelper)
: base(fixture)
{
Fixture.TestSqlLoggerFactory.Clear();
//Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
}
// See GearsOfWarQueryNpgsqlTest.Select_null_propagation_negative4
public override Task Select_null_propagation_negative4(bool async)
=> Assert.ThrowsAsync<InvalidOperationException>(() => base.Select_null_propagation_negative4(async));
[ConditionalTheory(Skip = "https://github.com/npgsql/efcore.pg/issues/2039")]
public override Task Where_DateOnly_Year(bool async)
=> base.Where_DateOnly_Year(async);
// Base implementation uses DateTimeOffset.Now, which we don't translate by design. Use DateTimeOffset.UtcNow instead.
public override async Task Select_datetimeoffset_comparison_in_projection(bool async)
{
await AssertQueryScalar(
async,
ss => ss.Set<Mission>().Select(m => m.Timeline > DateTimeOffset.UtcNow));
AssertSql(
@"SELECT m.""Timeline"" > now()
FROM ""Missions"" AS m");
}
public override async Task DateTimeOffset_Contains_Less_than_Greater_than(bool async)
{
var dto = new DateTimeOffset(599898024001234567, new TimeSpan(0));
var start = dto.AddDays(-1);
var end = dto.AddDays(1);
var dates = new[] { dto };
await AssertQuery(
async,
ss => ss.Set<Mission>().Where(
m => start <= m.Timeline.Date && m.Timeline < end && dates.Contains(m.Timeline)));
AssertSql(
@"@__start_0='1902-01-01T10:00:00.1234567+00:00' (DbType = DateTime)
@__end_1='1902-01-03T10:00:00.1234567+00:00' (DbType = DateTime)
@__dates_2={ '1902-01-02T10:00:00.1234567+00:00' } (DbType = Object)
SELECT m.""Id"", m.""CodeName"", m.""Date"", m.""Duration"", m.""Rating"", m.""Time"", m.""Timeline""
FROM ""Missions"" AS m
WHERE @__start_0 <= date_trunc('day', m.""Timeline"" AT TIME ZONE 'UTC')::timestamptz AND m.""Timeline"" < @__end_1 AND m.""Timeline"" = ANY (@__dates_2)");
}
public override async Task DateTimeOffset_Date_returns_datetime(bool async)
{
var dateTimeOffset = new DateTimeOffset(2, 3, 1, 8, 0, 0, new TimeSpan(-5, 0, 0));
await AssertQuery(
async,
ss => ss.Set<Mission>().Where(m => m.Timeline.Date.ToLocalTime() >= dateTimeOffset.Date));
AssertSql(
@"@__dateTimeOffset_Date_0='0002-03-01T00:00:00.0000000'
SELECT m.""Id"", m.""CodeName"", m.""Date"", m.""Duration"", m.""Rating"", m.""Time"", m.""Timeline""
FROM ""Missions"" AS m
WHERE date_trunc('day', m.""Timeline"" AT TIME ZONE 'UTC')::timestamp >= @__dateTimeOffset_Date_0");
}
public override async Task Where_datetimeoffset_date_component(bool async)
{
await AssertQuery(
async,
ss => from m in ss.Set<Mission>()
where m.Timeline.Date > new DateTime(1, DateTimeKind.Utc)
select m);
AssertSql(
@"SELECT m.""Id"", m.""CodeName"", m.""Date"", m.""Duration"", m.""Rating"", m.""Time"", m.""Timeline""
FROM ""Missions"" AS m
WHERE date_trunc('day', m.""Timeline"" AT TIME ZONE 'UTC') > TIMESTAMPTZ '0001-01-01 00:00:00Z'");
}
// Not supported by design: we support getting a local DateTime via DateTime.Now (based on PG TimeZone), but there's no way to get a
// non-UTC DateTimeOffset.
public override Task Where_datetimeoffset_now(bool async)
=> Task.CompletedTask;
public override Task Where_datetimeoffset_millisecond_component(bool async)
=> Task.CompletedTask; // SQL translation not implemented, too annoying
// Test runs successfully, but some time difference and precision issues and fail the assertion
public override Task Where_TimeSpan_Hours(bool async)
=> Task.CompletedTask;
public override Task Where_TimeOnly_Millisecond(bool async)
=> Task.CompletedTask; // Translation not implemented
private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
}
using Npgsql.EntityFrameworkCore.PostgreSQL.TestUtilities;
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query;
public class TPCInheritanceQueryNpgsqlFixture : TPCInheritanceQueryFixture
{
protected override ITestStoreFactory TestStoreFactory
=> NpgsqlTestStoreFactory.Instance;
}
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query;
public class TPCInheritanceQueryNpgsqlTest : TPCInheritanceQueryTestBase<TPCInheritanceQueryNpgsqlFixture>
{
public TPCInheritanceQueryNpgsqlTest(TPCInheritanceQueryNpgsqlFixture fixture, ITestOutputHelper testOutputHelper)
: base(fixture)
{
Fixture.TestSqlLoggerFactory.Clear();
//Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
}
protected override void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction)
=> facade.UseTransaction(transaction.GetDbTransaction());
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query;
public class TPCManyToManyNoTrackingQueryNpgsqlTest : TPCManyToManyNoTrackingQueryRelationalTestBase<TPCManyToManyQueryNpgsqlFixture>
{
public TPCManyToManyNoTrackingQueryNpgsqlTest(TPCManyToManyQueryNpgsqlFixture fixture, ITestOutputHelper testOutputHelper)
: base(fixture)
{
Fixture.TestSqlLoggerFactory.Clear();
//Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
}
}
using Microsoft.EntityFrameworkCore.TestModels.ManyToManyModel;
using Npgsql.EntityFrameworkCore.PostgreSQL.TestUtilities;
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query;
public class TPCManyToManyQueryNpgsqlFixture : TPCManyToManyQueryRelationalFixture
{
protected override ITestStoreFactory TestStoreFactory
=> NpgsqlTestStoreFactory.Instance;
protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context)
{
base.OnModelCreating(modelBuilder, context);
// We default to mapping DateTime to 'timestamp with time zone', but the seeding data has Unspecified DateTimes which aren't
// supported.
modelBuilder.Entity<EntityCompositeKey>().Property(e => e.Key3).HasColumnType("timestamp without time zone");
modelBuilder.Entity<JoinCompositeKeyToLeaf>().Property(e => e.CompositeId3).HasColumnType("timestamp without time zone");
modelBuilder.Entity<JoinOneSelfPayload>().Property(e => e.Payload).HasColumnType("timestamp without time zone");
modelBuilder.Entity<JoinThreeToCompositeKeyFull>().Property(e => e.CompositeId3).HasColumnType("timestamp without time zone");
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query;
public class TPCManyToManyQueryNpgsqlTest : TPCManyToManyQueryRelationalTestBase<TPCManyToManyQueryNpgsqlFixture>
{
public TPCManyToManyQueryNpgsqlTest(TPCManyToManyQueryNpgsqlFixture fixture, ITestOutputHelper testOutputHelper)
: base(fixture)
{
Fixture.TestSqlLoggerFactory.Clear();
//Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
}
}
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using Npgsql.EntityFrameworkCore.PostgreSQL.TestUtilities;
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query;
public class TPCRelationshipsQueryNpgsqlTest
: TPCRelationshipsQueryTestBase<TPCRelationshipsQueryNpgsqlTest.TPCRelationshipsQueryNpgsqlFixture>
{
public TPCRelationshipsQueryNpgsqlTest(
TPCRelationshipsQueryNpgsqlFixture fixture,
ITestOutputHelper testOutputHelper)
: base(fixture)
{
fixture.TestSqlLoggerFactory.Clear();
}
public class TPCRelationshipsQueryNpgsqlFixture : TPCRelationshipsQueryRelationalFixture
{
protected override ITestStoreFactory TestStoreFactory
=> NpgsqlTestStoreFactory.Instance;
}
}
......@@ -29,6 +29,18 @@ public class FakeRelationalCommandDiagnosticsLogger
CommandSource commandSource)
=> command;
public DbCommand CommandInitialized(
IRelationalConnection connection,
DbCommand command,
DbCommandMethod commandMethod,
DbContext? context,
Guid commandId,
Guid connectionId,
DateTimeOffset startTime,
TimeSpan duration,
CommandSource commandSource)
=> command;
public InterceptionResult<DbDataReader> CommandReaderExecuting(
IRelationalConnection connection,
DbCommand command,
......
......@@ -37,9 +37,6 @@ public void Uses_MaxBatchSize_specified_in_NpgsqlOptionsExtension()
new NpgsqlSqlGenerationHelper(
new RelationalSqlGenerationHelperDependencies()),
typeMapper)),
new TypedRelationalValueBufferFactoryFactory(
new RelationalValueBufferFactoryDependencies(
typeMapper, new CoreSingletonOptions())),
new CurrentDbContext(new FakeDbContext()),
logger),
optionsBuilder.Options);
......@@ -77,9 +74,6 @@ public void MaxBatchSize_is_optional()
new NpgsqlSqlGenerationHelper(
new RelationalSqlGenerationHelperDependencies()),
typeMapper)),
new TypedRelationalValueBufferFactoryFactory(
new RelationalValueBufferFactoryDependencies(
typeMapper, new CoreSingletonOptions())),
new CurrentDbContext(new FakeDbContext()),
logger),
optionsBuilder.Options);
......@@ -94,17 +88,10 @@ private class FakeDbContext : DbContext
{
}
private static IModificationCommand CreateModificationCommand(
private static INonTrackedModificationCommand CreateModificationCommand(
string name,
string schema,
bool sensitiveLoggingEnabled)
{
var modificationCommandParameters = new ModificationCommandParameters(
name, schema, sensitiveLoggingEnabled);
var modificationCommand = new ModificationCommandFactory().CreateModificationCommand(
modificationCommandParameters);
return modificationCommand;
}
=> new ModificationCommandFactory().CreateNonTrackedModificationCommand(
new NonTrackedModificationCommandParameters(name, schema, sensitiveLoggingEnabled));
}
......@@ -35,9 +35,6 @@ public void AddCommand_returns_false_when_max_batch_size_is_reached()
new NpgsqlSqlGenerationHelper(
new RelationalSqlGenerationHelperDependencies()),
typeMapper)),
new TypedRelationalValueBufferFactoryFactory(
new RelationalValueBufferFactoryDependencies(
typeMapper, new CoreSingletonOptions())),
new CurrentDbContext(new FakeDbContext()),
logger),
maxBatchSize: 1);
......@@ -54,17 +51,10 @@ private class FakeDbContext : DbContext
{
}
private static IModificationCommand CreateModificationCommand(
private static INonTrackedModificationCommand CreateModificationCommand(
string name,
string schema,
bool sensitiveLoggingEnabled)
{
var modificationCommandParameters = new ModificationCommandParameters(
name, schema, sensitiveLoggingEnabled);
var modificationCommand = new ModificationCommandFactory().CreateModificationCommand(
modificationCommandParameters);
return modificationCommand;
}
=> new ModificationCommandFactory().CreateNonTrackedModificationCommand(
new NonTrackedModificationCommandParameters(name, schema, sensitiveLoggingEnabled));
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册