提交 a1aa03ed 编写于 作者: S Shay Rojansky

Implement bulk delete

Following https://github.com/dotnet/efcore/pull/28492

Part of #2450
上级 2524b30e
...@@ -105,6 +105,7 @@ public static IServiceCollection AddEntityFrameworkNpgsql(this IServiceCollectio ...@@ -105,6 +105,7 @@ public static IServiceCollection AddEntityFrameworkNpgsql(this IServiceCollectio
.TryAdd<IHistoryRepository, NpgsqlHistoryRepository>() .TryAdd<IHistoryRepository, NpgsqlHistoryRepository>()
.TryAdd<ICompiledQueryCacheKeyGenerator, NpgsqlCompiledQueryCacheKeyGenerator>() .TryAdd<ICompiledQueryCacheKeyGenerator, NpgsqlCompiledQueryCacheKeyGenerator>()
.TryAdd<IExecutionStrategyFactory, NpgsqlExecutionStrategyFactory>() .TryAdd<IExecutionStrategyFactory, NpgsqlExecutionStrategyFactory>()
.TryAdd<IQueryableMethodTranslatingExpressionVisitorFactory, NpgsqlQueryableMethodTranslatingExpressionVisitorFactory>()
.TryAdd<IMethodCallTranslatorProvider, NpgsqlMethodCallTranslatorProvider>() .TryAdd<IMethodCallTranslatorProvider, NpgsqlMethodCallTranslatorProvider>()
.TryAdd<IAggregateMethodCallTranslatorProvider, NpgsqlAggregateMethodCallTranslatorProvider>() .TryAdd<IAggregateMethodCallTranslatorProvider, NpgsqlAggregateMethodCallTranslatorProvider>()
.TryAdd<IMemberTranslatorProvider, NpgsqlMemberTranslatorProvider>() .TryAdd<IMemberTranslatorProvider, NpgsqlMemberTranslatorProvider>()
......
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query.Expressions.Internal;
public sealed class PostgresDeleteExpression : Expression, IPrintableExpression
{
/// <summary>
/// The tables that rows are to be deleted from.
/// </summary>
public TableExpression Table { get; }
/// <summary>
/// Additional tables which can be referenced in the predicate.
/// </summary>
public IReadOnlyList<TableExpressionBase> FromItems { get; }
/// <summary>
/// The WHERE predicate for the DELETE.
/// </summary>
public SqlExpression? Predicate { get; }
public PostgresDeleteExpression(TableExpression table, IReadOnlyList<TableExpressionBase> fromItems, SqlExpression? predicate)
=> (Table, FromItems, Predicate) = (table, fromItems, predicate);
/// <inheritdoc />
public override Type Type
=> typeof(object);
/// <inheritdoc />
public override ExpressionType NodeType
=> ExpressionType.Extension;
protected override Expression VisitChildren(ExpressionVisitor visitor)
=> Predicate is null
? this
: Update((SqlExpression?)visitor.Visit(Predicate));
public PostgresDeleteExpression Update(SqlExpression? predicate)
=> predicate == Predicate
? this
: new PostgresDeleteExpression(Table, FromItems, predicate);
public void Print(ExpressionPrinter expressionPrinter)
{
expressionPrinter.AppendLine($"DELETE FROM {Table.Name} AS {Table.Alias}");
if (FromItems.Count > 0)
{
var first = true;
foreach (var fromItem in FromItems)
{
if (first)
{
expressionPrinter.Append("USING ");
first = false;
}
else
{
expressionPrinter.Append(", ");
}
expressionPrinter.Visit(fromItem);
}
}
if (Predicate is not null)
{
expressionPrinter.Append("WHERE ");
expressionPrinter.Visit(Predicate);
}
}
/// <inheritdoc />
public override bool Equals(object? obj)
=> obj != null
&& (ReferenceEquals(this, obj)
|| obj is PostgresDeleteExpression pgDeleteExpression
&& Equals(pgDeleteExpression));
private bool Equals(PostgresDeleteExpression pgDeleteExpression)
=> Table == pgDeleteExpression.Table
&& FromItems.SequenceEqual(pgDeleteExpression.FromItems)
&& (Predicate is null ? pgDeleteExpression.Predicate is null : Predicate.Equals(pgDeleteExpression.Predicate));
/// <inheritdoc />
public override int GetHashCode() => Table.GetHashCode();
}
using Npgsql.EntityFrameworkCore.PostgreSQL.Query.Expressions.Internal;
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query.Internal;
/// <summary>
/// Converts the relational <see cref="NonQueryExpression" /> into a PG-specific <see cref="PostgresDeleteExpression" />, which
/// precisely models a DELETE statement in PostgreSQL. This is done to handle the PG-specific USING syntax for table joining.
/// </summary>
public class NonQueryConvertingExpressionVisitor : ExpressionVisitor
{
public virtual Expression Process(Expression node)
=> node switch
{
DeleteExpression deleteExpression => VisitDelete(deleteExpression),
_ => node
};
protected virtual Expression VisitDelete(DeleteExpression deleteExpression)
{
var selectExpression = deleteExpression.SelectExpression;
if (selectExpression.Offset != null
|| selectExpression.Limit != null
|| selectExpression.Having != null
|| selectExpression.Orderings.Count > 0
|| selectExpression.GroupBy.Count > 0
|| selectExpression.Projection.Count > 0)
{
throw new InvalidOperationException(
RelationalStrings.ExecuteOperationWithUnsupportedOperatorInSqlGeneration(
nameof(RelationalQueryableExtensions.ExecuteDelete)));
}
var fromItems = new List<TableExpressionBase>();
SqlExpression? joinPredicates = null;
// The SelectExpression also contains the target table being modified (same as deleteExpression.Table).
// If it has additional inner joins, use the PostgreSQL-specific USING syntax to express the join.
// Note that the non-join TableExpression isn't necessary the target table - through projection the last table being
// joined may be the one being modified.
foreach (var tableBase in selectExpression.Tables)
{
switch (tableBase)
{
case TableExpression tableExpression:
if (tableExpression != deleteExpression.Table)
{
fromItems.Add(tableExpression);
}
break;
case InnerJoinExpression { Table: { } tableExpression } innerJoinExpression:
if (tableExpression != deleteExpression.Table)
{
fromItems.Add(tableExpression);
}
joinPredicates = joinPredicates is null
? innerJoinExpression.JoinPredicate
: new SqlBinaryExpression(
ExpressionType.AndAlso, joinPredicates, innerJoinExpression.JoinPredicate, typeof(bool),
innerJoinExpression.JoinPredicate.TypeMapping);
break;
default:
throw new InvalidOperationException(
RelationalStrings.ExecuteOperationWithUnsupportedOperatorInSqlGeneration(
nameof(RelationalQueryableExtensions.ExecuteDelete)));
}
}
// Combine the join predicates (if any) before the user-provided predicate
var predicate = (joinPredicates, selectExpression.Predicate) switch
{
(null, not null) => selectExpression.Predicate,
(not null, null) => joinPredicates,
(null, null) => null,
(not null, not null) => new SqlBinaryExpression(
ExpressionType.AndAlso, joinPredicates, selectExpression.Predicate, typeof(bool), joinPredicates.TypeMapping)
};
return new PostgresDeleteExpression(deleteExpression.Table, fromItems, predicate);
}
}
...@@ -9,6 +9,18 @@ public class NpgsqlParameterBasedSqlProcessor : RelationalParameterBasedSqlProce ...@@ -9,6 +9,18 @@ public class NpgsqlParameterBasedSqlProcessor : RelationalParameterBasedSqlProce
{ {
} }
public override Expression Optimize(
Expression queryExpression,
IReadOnlyDictionary<string, object?> parametersValues,
out bool canCache)
{
queryExpression = base.Optimize(queryExpression, parametersValues, out canCache);
queryExpression = new NonQueryConvertingExpressionVisitor().Process(queryExpression);
return queryExpression;
}
/// <inheritdoc /> /// <inheritdoc />
protected override Expression ProcessSqlNullability( protected override Expression ProcessSqlNullability(
Expression selectExpression, IReadOnlyDictionary<string, object?> parametersValues, out bool canCache) Expression selectExpression, IReadOnlyDictionary<string, object?> parametersValues, out bool canCache)
......
...@@ -44,6 +44,7 @@ protected override Expression VisitExtension(Expression extensionExpression) ...@@ -44,6 +44,7 @@ protected override Expression VisitExtension(Expression extensionExpression)
PostgresAnyExpression anyExpression => VisitArrayAny(anyExpression), PostgresAnyExpression anyExpression => VisitArrayAny(anyExpression),
PostgresArrayIndexExpression arrayIndexExpression => VisitArrayIndex(arrayIndexExpression), PostgresArrayIndexExpression arrayIndexExpression => VisitArrayIndex(arrayIndexExpression),
PostgresBinaryExpression binaryExpression => VisitPostgresBinary(binaryExpression), PostgresBinaryExpression binaryExpression => VisitPostgresBinary(binaryExpression),
PostgresDeleteExpression deleteExpression => VisitPostgresDelete(deleteExpression),
PostgresFunctionExpression functionExpression => VisitPostgresFunction(functionExpression), PostgresFunctionExpression functionExpression => VisitPostgresFunction(functionExpression),
PostgresILikeExpression iLikeExpression => VisitILike(iLikeExpression), PostgresILikeExpression iLikeExpression => VisitILike(iLikeExpression),
PostgresJsonTraversalExpression jsonTraversalExpression => VisitJsonPathTraversal(jsonTraversalExpression), PostgresJsonTraversalExpression jsonTraversalExpression => VisitJsonPathTraversal(jsonTraversalExpression),
...@@ -206,6 +207,45 @@ protected override Expression VisitSqlBinary(SqlBinaryExpression binary) ...@@ -206,6 +207,45 @@ protected override Expression VisitSqlBinary(SqlBinaryExpression binary)
} }
} }
protected override void GenerateRootCommand(Expression rootExpression)
{
switch (rootExpression)
{
case PostgresDeleteExpression pgDeleteExpression:
VisitPostgresDelete(pgDeleteExpression);
return;
default:
base.GenerateRootCommand(rootExpression);
return;
}
}
// NonQueryConvertingExpressionVisitor converts the relational DeleteExpression to PostgresDeleteExpression, so we should never
// get here
protected override Expression VisitDelete(DeleteExpression deleteExpression)
=> throw new InvalidOperationException("Inconceivable!");
protected virtual Expression VisitPostgresDelete(PostgresDeleteExpression pgDeleteExpression)
{
Sql.Append("DELETE FROM ");
Visit(pgDeleteExpression.Table);
if (pgDeleteExpression.FromItems.Count > 0)
{
Sql.AppendLine().Append("USING ");
GenerateList(pgDeleteExpression.FromItems, t => Visit(t), sql => sql.Append(", "));
}
if (pgDeleteExpression.Predicate != null)
{
Sql.AppendLine().Append("WHERE ");
Visit(pgDeleteExpression.Predicate);
}
return pgDeleteExpression;
}
protected virtual Expression VisitPostgresNewArray(PostgresNewArrayExpression postgresNewArrayExpression) protected virtual Expression VisitPostgresNewArray(PostgresNewArrayExpression postgresNewArrayExpression)
{ {
Debug.Assert(postgresNewArrayExpression.TypeMapping is not null); Debug.Assert(postgresNewArrayExpression.TypeMapping is not null);
...@@ -769,4 +809,22 @@ public virtual Expression VisitPostgresFunction(PostgresFunctionExpression e) ...@@ -769,4 +809,22 @@ public virtual Expression VisitPostgresFunction(PostgresFunctionExpression e)
private static bool RequiresBrackets(SqlExpression expression) private static bool RequiresBrackets(SqlExpression expression)
=> expression is SqlBinaryExpression || expression is LikeExpression || expression is PostgresBinaryExpression; => expression is SqlBinaryExpression || expression is LikeExpression || expression is PostgresBinaryExpression;
private void GenerateList<T>(
IReadOnlyList<T> items,
Action<T> generationAction,
Action<IRelationalCommandBuilder>? joinAction = null)
{
joinAction ??= (isb => isb.Append(", "));
for (var i = 0; i < items.Count; i++)
{
if (i > 0)
{
joinAction(Sql);
}
generationAction(items[i]);
}
}
} }
\ No newline at end of file
using System.Diagnostics.CodeAnalysis;
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query.Internal;
public class NpgsqlQueryableMethodTranslatingExpressionVisitor : RelationalQueryableMethodTranslatingExpressionVisitor
{
public NpgsqlQueryableMethodTranslatingExpressionVisitor(
QueryableMethodTranslatingExpressionVisitorDependencies dependencies,
RelationalQueryableMethodTranslatingExpressionVisitorDependencies relationalDependencies,
QueryCompilationContext queryCompilationContext)
: base(dependencies, relationalDependencies, queryCompilationContext)
{
}
protected override bool IsValidSelectExpressionForExecuteDelete(
SelectExpression selectExpression,
EntityShaperExpression entityShaperExpression,
[NotNullWhen(true)] out TableExpression? tableExpression)
{
// The default relational behavior is to allow only single-table expressions, and the only permitted feature is a predicate.
// Here we extend this to also inner joins to tables, which we generate via the PostgreSQL-specific USING construct.
if (selectExpression.Offset == null
&& selectExpression.Limit == null
// If entity type has primary key then Distinct is no-op
&& (!selectExpression.IsDistinct || entityShaperExpression.EntityType.FindPrimaryKey() != null)
&& selectExpression.GroupBy.Count == 0
&& selectExpression.Having == null
&& selectExpression.Orderings.Count == 0)
{
TableExpressionBase? table = null;
if (selectExpression.Tables.Count == 1)
{
table = selectExpression.Tables[0];
}
else if (selectExpression.Tables.All(t => t is TableExpression or InnerJoinExpression))
{
var projectionBindingExpression = (ProjectionBindingExpression)entityShaperExpression.ValueBufferExpression;
var entityProjectionExpression = (EntityProjectionExpression)selectExpression.GetProjection(projectionBindingExpression);
var column = entityProjectionExpression.BindProperty(entityShaperExpression.EntityType.GetProperties().First());
table = column.Table;
if (table is JoinExpressionBase joinExpressionBase)
{
table = joinExpressionBase.Table;
}
}
if (table is TableExpression te)
{
tableExpression = te;
return true;
}
}
tableExpression = null;
return false;
}
}
namespace Npgsql.EntityFrameworkCore.PostgreSQL.Query.Internal;
public class NpgsqlQueryableMethodTranslatingExpressionVisitorFactory : IQueryableMethodTranslatingExpressionVisitorFactory
{
public NpgsqlQueryableMethodTranslatingExpressionVisitorFactory(
QueryableMethodTranslatingExpressionVisitorDependencies dependencies,
RelationalQueryableMethodTranslatingExpressionVisitorDependencies relationalDependencies)
{
Dependencies = dependencies;
RelationalDependencies = relationalDependencies;
}
protected virtual QueryableMethodTranslatingExpressionVisitorDependencies Dependencies { get; }
protected virtual RelationalQueryableMethodTranslatingExpressionVisitorDependencies RelationalDependencies { get; }
public virtual QueryableMethodTranslatingExpressionVisitor Create(QueryCompilationContext queryCompilationContext)
=> new NpgsqlQueryableMethodTranslatingExpressionVisitor(Dependencies, RelationalDependencies, queryCompilationContext);
}
// 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.BulkUpdates;
public class FiltersInheritanceBulkUpdatesNpgsqlFixture : InheritanceBulkUpdatesNpgsqlFixture
{
protected override bool EnableFilters
=> true;
}
using Microsoft.EntityFrameworkCore.BulkUpdates;
using Npgsql.EntityFrameworkCore.PostgreSQL.Query;
namespace Npgsql.EntityFrameworkCore.PostgreSQL.BulkUpdates;
public class FiltersInheritanceBulkUpdatesNpgsqlTest : FiltersInheritanceBulkUpdatesTestBase<FiltersInheritanceBulkUpdatesNpgsqlFixture>
{
public FiltersInheritanceBulkUpdatesNpgsqlTest(FiltersInheritanceBulkUpdatesNpgsqlFixture fixture)
: base(fixture)
{
ClearLog();
}
public override async Task Delete_where_hierarchy(bool async)
{
await base.Delete_where_hierarchy(async);
AssertSql(
@"DELETE FROM ""Animals"" AS a
WHERE a.""CountryId"" = 1 AND a.""Name"" = 'Great spotted kiwi'");
}
public override async Task Delete_where_hierarchy_derived(bool async)
{
await base.Delete_where_hierarchy_derived(async);
AssertSql(
@"DELETE FROM ""Animals"" AS a
WHERE a.""Discriminator"" = 'Kiwi' AND a.""CountryId"" = 1 AND a.""Name"" = 'Great spotted kiwi'");
}
public override async Task Delete_where_using_hierarchy(bool async)
{
await base.Delete_where_using_hierarchy(async);
AssertSql(
@"DELETE FROM ""Countries"" AS c
WHERE (
SELECT count(*)::int
FROM ""Animals"" AS a
WHERE a.""CountryId"" = 1 AND c.""Id"" = a.""CountryId"" AND a.""CountryId"" > 0) > 0");
}
public override async Task Delete_where_using_hierarchy_derived(bool async)
{
await base.Delete_where_using_hierarchy_derived(async);
AssertSql(
@"DELETE FROM ""Countries"" AS c
WHERE (
SELECT count(*)::int
FROM ""Animals"" AS a
WHERE a.""CountryId"" = 1 AND c.""Id"" = a.""CountryId"" AND a.""Discriminator"" = 'Kiwi' AND a.""CountryId"" > 0) > 0");
}
public override async Task Delete_where_keyless_entity_mapped_to_sql_query(bool async)
{
await base.Delete_where_keyless_entity_mapped_to_sql_query(async);
AssertSql();
}
public override async Task Delete_where_hierarchy_subquery(bool async)
{
await base.Delete_where_hierarchy_subquery(async);
AssertSql();
}
[ConditionalFact]
public virtual void Check_all_tests_overridden()
=> TestHelpers.AssertAllMethodsOverridden(GetType());
protected override void ClearLog() => Fixture.TestSqlLoggerFactory.Clear();
private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
}
using Microsoft.EntityFrameworkCore.BulkUpdates;
using Npgsql.EntityFrameworkCore.PostgreSQL.TestUtilities;
namespace Npgsql.EntityFrameworkCore.PostgreSQL.BulkUpdates;
public class InheritanceBulkUpdatesNpgsqlFixture : InheritanceBulkUpdatesRelationalFixture
{
protected override ITestStoreFactory TestStoreFactory
=> NpgsqlTestStoreFactory.Instance;
}
using Microsoft.EntityFrameworkCore.BulkUpdates;
namespace Npgsql.EntityFrameworkCore.PostgreSQL.BulkUpdates;
public class InheritanceBulkUpdatesNpgsqlTest : InheritanceBulkUpdatesTestBase<InheritanceBulkUpdatesNpgsqlFixture>
{
public InheritanceBulkUpdatesNpgsqlTest(InheritanceBulkUpdatesNpgsqlFixture fixture)
: base(fixture)
{
ClearLog();
}
public override async Task Delete_where_hierarchy(bool async)
{
await base.Delete_where_hierarchy(async);
AssertSql(
@"DELETE FROM ""Animals"" AS a
WHERE a.""Name"" = 'Great spotted kiwi'");
}
public override async Task Delete_where_hierarchy_derived(bool async)
{
await base.Delete_where_hierarchy_derived(async);
AssertSql(
@"DELETE FROM ""Animals"" AS a
WHERE a.""Discriminator"" = 'Kiwi' AND a.""Name"" = 'Great spotted kiwi'");
}
public override async Task Delete_where_using_hierarchy(bool async)
{
await base.Delete_where_using_hierarchy(async);
AssertSql(
@"DELETE FROM ""Countries"" AS c
WHERE (
SELECT count(*)::int
FROM ""Animals"" AS a
WHERE c.""Id"" = a.""CountryId"" AND a.""CountryId"" > 0) > 0");
}
public override async Task Delete_where_using_hierarchy_derived(bool async)
{
await base.Delete_where_using_hierarchy_derived(async);
AssertSql(
@"DELETE FROM ""Countries"" AS c
WHERE (
SELECT count(*)::int
FROM ""Animals"" AS a
WHERE c.""Id"" = a.""CountryId"" AND a.""Discriminator"" = 'Kiwi' AND a.""CountryId"" > 0) > 0");
}
public override async Task Delete_where_keyless_entity_mapped_to_sql_query(bool async)
{
await base.Delete_where_keyless_entity_mapped_to_sql_query(async);
AssertSql();
}
public override async Task Delete_where_hierarchy_subquery(bool async)
{
await base.Delete_where_hierarchy_subquery(async);
AssertSql();
}
[ConditionalFact]
public virtual void Check_all_tests_overridden()
=> TestHelpers.AssertAllMethodsOverridden(GetType());
protected override void ClearLog() => Fixture.TestSqlLoggerFactory.Clear();
private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
}
using Microsoft.EntityFrameworkCore.BulkUpdates;
using Microsoft.EntityFrameworkCore.TestModels.Northwind;
using Npgsql.EntityFrameworkCore.PostgreSQL.TestUtilities;
namespace Npgsql.EntityFrameworkCore.PostgreSQL.BulkUpdates;
public class NorthwindBulkUpdatesNpgsqlFixture<TModelCustomizer> : NorthwindBulkUpdatesFixture<TModelCustomizer>
where TModelCustomizer: IModelCustomizer, new()
{
protected override ITestStoreFactory TestStoreFactory
=> NpgsqlNorthwindTestStoreFactory.Instance;
protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context)
{
base.OnModelCreating(modelBuilder, context);
modelBuilder.Entity<Customer>()
.Property(c => c.CustomerID)
.HasColumnType("char(5)");
modelBuilder.Entity<Employee>(
b =>
{
b.Property(c => c.EmployeeID).HasColumnType("int");
b.Property(c => c.ReportsTo).HasColumnType("int");
});
modelBuilder.Entity<Order>(
b =>
{
b.Property(o => o.EmployeeID).HasColumnType("int");
b.Property(o => o.OrderDate).HasColumnType("timestamp without time zone");
});
modelBuilder.Entity<OrderDetail>()
.Property(od => od.UnitPrice)
.HasColumnType("money");
modelBuilder.Entity<Product>(
b =>
{
b.Property(p => p.UnitPrice).HasColumnType("money");
b.Property(p => p.UnitsInStock).HasColumnType("smallint");
});
modelBuilder.Entity<MostExpensiveProduct>()
.Property(p => p.UnitPrice)
.HasColumnType("money");
}
}
using Microsoft.EntityFrameworkCore.BulkUpdates;
namespace Npgsql.EntityFrameworkCore.PostgreSQL.BulkUpdates;
public class NorthwindBulkUpdatesNpgsqlTest : NorthwindBulkUpdatesTestBase<NorthwindBulkUpdatesNpgsqlFixture<NoopModelCustomizer>>
{
public NorthwindBulkUpdatesNpgsqlTest(
NorthwindBulkUpdatesNpgsqlFixture<NoopModelCustomizer> fixture,
ITestOutputHelper testOutputHelper)
: base(fixture)
{
ClearLog();
// Fixture.TestSqlLoggerFactory.SetTestOutputHelper(testOutputHelper);
}
[ConditionalFact]
public virtual void Check_all_tests_overridden()
=> TestHelpers.AssertAllMethodsOverridden(GetType());
public override async Task Delete_Where(bool async)
{
await base.Delete_Where(async);
AssertSql(
@"DELETE FROM ""Order Details"" AS o
WHERE o.""OrderID"" < 10300");
}
public override async Task Delete_Where_parameter(bool async)
{
await base.Delete_Where_parameter(async);
AssertSql(
@"@__quantity_0='1' (Nullable = true) (DbType = Int16)
DELETE FROM ""Order Details"" AS o
WHERE o.""Quantity"" = @__quantity_0",
//
@"DELETE FROM ""Order Details"" AS o
WHERE FALSE");
}
public override async Task Delete_Where_OrderBy(bool async)
{
await base.Delete_Where_OrderBy(async);
AssertSql(
@"DELETE FROM ""Order Details"" AS o
WHERE EXISTS (
SELECT 1
FROM ""Order Details"" AS o0
WHERE o0.""OrderID"" < 10300 AND o0.""OrderID"" = o.""OrderID"" AND o0.""ProductID"" = o.""ProductID"")");
}
public override async Task Delete_Where_OrderBy_Skip(bool async)
{
await base.Delete_Where_OrderBy_Skip(async);
AssertSql(
@"@__p_0='100'
DELETE FROM ""Order Details"" AS o
WHERE EXISTS (
SELECT 1
FROM (
SELECT o0.""OrderID"", o0.""ProductID"", o0.""Discount"", o0.""Quantity"", o0.""UnitPrice""
FROM ""Order Details"" AS o0
WHERE o0.""OrderID"" < 10300
ORDER BY o0.""OrderID"" NULLS FIRST
OFFSET @__p_0
) AS t
WHERE t.""OrderID"" = o.""OrderID"" AND t.""ProductID"" = o.""ProductID"")");
}
public override async Task Delete_Where_OrderBy_Take(bool async)
{
await base.Delete_Where_OrderBy_Take(async);
AssertSql(
@"@__p_0='100'
DELETE FROM ""Order Details"" AS o
WHERE EXISTS (
SELECT 1
FROM (
SELECT o0.""OrderID"", o0.""ProductID"", o0.""Discount"", o0.""Quantity"", o0.""UnitPrice""
FROM ""Order Details"" AS o0
WHERE o0.""OrderID"" < 10300
ORDER BY o0.""OrderID"" NULLS FIRST
LIMIT @__p_0
) AS t
WHERE t.""OrderID"" = o.""OrderID"" AND t.""ProductID"" = o.""ProductID"")");
}
public override async Task Delete_Where_OrderBy_Skip_Take(bool async)
{
await base.Delete_Where_OrderBy_Skip_Take(async);
AssertSql(
@"@__p_0='100'
DELETE FROM ""Order Details"" AS o
WHERE EXISTS (
SELECT 1
FROM (
SELECT o0.""OrderID"", o0.""ProductID"", o0.""Discount"", o0.""Quantity"", o0.""UnitPrice""
FROM ""Order Details"" AS o0
WHERE o0.""OrderID"" < 10300
ORDER BY o0.""OrderID"" NULLS FIRST
LIMIT @__p_0 OFFSET @__p_0
) AS t
WHERE t.""OrderID"" = o.""OrderID"" AND t.""ProductID"" = o.""ProductID"")");
}
public override async Task Delete_Where_Skip(bool async)
{
await base.Delete_Where_Skip(async);
AssertSql(
@"@__p_0='100'
DELETE FROM ""Order Details"" AS o
WHERE EXISTS (
SELECT 1
FROM (
SELECT o0.""OrderID"", o0.""ProductID"", o0.""Discount"", o0.""Quantity"", o0.""UnitPrice""
FROM ""Order Details"" AS o0
WHERE o0.""OrderID"" < 10300
OFFSET @__p_0
) AS t
WHERE t.""OrderID"" = o.""OrderID"" AND t.""ProductID"" = o.""ProductID"")");
}
public override async Task Delete_Where_Take(bool async)
{
await base.Delete_Where_Take(async);
AssertSql(
@"@__p_0='100'
DELETE FROM ""Order Details"" AS o
WHERE EXISTS (
SELECT 1
FROM (
SELECT o0.""OrderID"", o0.""ProductID"", o0.""Discount"", o0.""Quantity"", o0.""UnitPrice""
FROM ""Order Details"" AS o0
WHERE o0.""OrderID"" < 10300
LIMIT @__p_0
) AS t
WHERE t.""OrderID"" = o.""OrderID"" AND t.""ProductID"" = o.""ProductID"")");
}
public override async Task Delete_Where_Skip_Take(bool async)
{
await base.Delete_Where_Skip_Take(async);
AssertSql(
@"@__p_0='100'
DELETE FROM ""Order Details"" AS o
WHERE EXISTS (
SELECT 1
FROM (
SELECT o0.""OrderID"", o0.""ProductID"", o0.""Discount"", o0.""Quantity"", o0.""UnitPrice""
FROM ""Order Details"" AS o0
WHERE o0.""OrderID"" < 10300
LIMIT @__p_0 OFFSET @__p_0
) AS t
WHERE t.""OrderID"" = o.""OrderID"" AND t.""ProductID"" = o.""ProductID"")");
}
public override async Task Delete_Where_predicate_with_group_by_aggregate(bool async)
{
await base.Delete_Where_predicate_with_group_by_aggregate(async);
AssertSql(
@"DELETE FROM ""Order Details"" AS o
WHERE o.""OrderID"" < (
SELECT (
SELECT o1.""OrderID""
FROM ""Orders"" AS o1
WHERE o0.""CustomerID"" = o1.""CustomerID"" OR ((o0.""CustomerID"" IS NULL) AND (o1.""CustomerID"" IS NULL))
LIMIT 1)
FROM ""Orders"" AS o0
GROUP BY o0.""CustomerID""
HAVING count(*)::int > 11
LIMIT 1)");
}
public override async Task Delete_Where_predicate_with_group_by_aggregate_2(bool async)
{
await base.Delete_Where_predicate_with_group_by_aggregate_2(async);
AssertSql();
}
public override async Task Delete_GroupBy_Where_Select(bool async)
{
await base.Delete_GroupBy_Where_Select(async);
AssertSql();
}
public override async Task Delete_Where_Skip_Take_Skip_Take_causing_subquery(bool async)
{
await base.Delete_Where_Skip_Take_Skip_Take_causing_subquery(async);
AssertSql(
@"@__p_0='100'
@__p_2='5'
@__p_1='20'
DELETE FROM ""Order Details"" AS o
WHERE EXISTS (
SELECT 1
FROM (
SELECT t.""OrderID"", t.""ProductID"", t.""Discount"", t.""Quantity"", t.""UnitPrice""
FROM (
SELECT o0.""OrderID"", o0.""ProductID"", o0.""Discount"", o0.""Quantity"", o0.""UnitPrice""
FROM ""Order Details"" AS o0
WHERE o0.""OrderID"" < 10300
LIMIT @__p_0 OFFSET @__p_0
) AS t
LIMIT @__p_2 OFFSET @__p_1
) AS t0
WHERE t0.""OrderID"" = o.""OrderID"" AND t0.""ProductID"" = o.""ProductID"")");
}
public override async Task Delete_Where_Distinct(bool async)
{
await base.Delete_Where_Distinct(async);
AssertSql(
@"DELETE FROM ""Order Details"" AS o
WHERE o.""OrderID"" < 10300");
}
public override async Task Delete_SelectMany(bool async)
{
await base.Delete_SelectMany(async);
AssertSql(
@"DELETE FROM ""Order Details"" AS o0
USING ""Orders"" AS o
WHERE o.""OrderID"" = o0.""OrderID"" AND o.""OrderID"" < 10250");
}
public override async Task Delete_SelectMany_subquery(bool async)
{
await base.Delete_SelectMany_subquery(async);
AssertSql(
@"DELETE FROM ""Order Details"" AS o
WHERE EXISTS (
SELECT 1
FROM ""Orders"" AS o0
INNER JOIN (
SELECT o1.""OrderID"", o1.""ProductID"", o1.""Discount"", o1.""Quantity"", o1.""UnitPrice""
FROM ""Order Details"" AS o1
WHERE o1.""ProductID"" > 0
) AS t ON o0.""OrderID"" = t.""OrderID""
WHERE o0.""OrderID"" < 10250 AND t.""OrderID"" = o.""OrderID"" AND t.""ProductID"" = o.""ProductID"")");
}
public override async Task Delete_Where_using_navigation(bool async)
{
await base.Delete_Where_using_navigation(async);
AssertSql(
@"DELETE FROM ""Order Details"" AS o
USING ""Orders"" AS o0
WHERE o.""OrderID"" = o0.""OrderID"" AND date_part('year', o0.""OrderDate"")::int = 2000");
}
public override async Task Delete_Where_using_navigation_2(bool async)
{
await base.Delete_Where_using_navigation_2(async);
AssertSql(
@"DELETE FROM ""Order Details"" AS o
WHERE EXISTS (
SELECT 1
FROM ""Order Details"" AS o0
INNER JOIN ""Orders"" AS o1 ON o0.""OrderID"" = o1.""OrderID""
LEFT JOIN ""Customers"" AS c ON o1.""CustomerID"" = c.""CustomerID""
WHERE (c.""CustomerID"" IS NOT NULL) AND (c.""CustomerID"" LIKE 'F%') AND o0.""OrderID"" = o.""OrderID"" AND o0.""ProductID"" = o.""ProductID"")");
}
public override async Task Delete_Union(bool async)
{
await base.Delete_Union(async);
AssertSql(
@"DELETE FROM ""Order Details"" AS o
WHERE EXISTS (
SELECT 1
FROM (
SELECT o0.""OrderID"", o0.""ProductID"", o0.""Discount"", o0.""Quantity"", o0.""UnitPrice""
FROM ""Order Details"" AS o0
WHERE o0.""OrderID"" < 10250
UNION
SELECT o1.""OrderID"", o1.""ProductID"", o1.""Discount"", o1.""Quantity"", o1.""UnitPrice""
FROM ""Order Details"" AS o1
WHERE o1.""OrderID"" > 11250
) AS t
WHERE t.""OrderID"" = o.""OrderID"" AND t.""ProductID"" = o.""ProductID"")");
}
public override async Task Delete_Concat(bool async)
{
await base.Delete_Concat(async);
AssertSql(
@"DELETE FROM ""Order Details"" AS o
WHERE EXISTS (
SELECT 1
FROM (
SELECT o0.""OrderID"", o0.""ProductID"", o0.""Discount"", o0.""Quantity"", o0.""UnitPrice""
FROM ""Order Details"" AS o0
WHERE o0.""OrderID"" < 10250
UNION ALL
SELECT o1.""OrderID"", o1.""ProductID"", o1.""Discount"", o1.""Quantity"", o1.""UnitPrice""
FROM ""Order Details"" AS o1
WHERE o1.""OrderID"" > 11250
) AS t
WHERE t.""OrderID"" = o.""OrderID"" AND t.""ProductID"" = o.""ProductID"")");
}
public override async Task Delete_Intersect(bool async)
{
await base.Delete_Intersect(async);
AssertSql(
@"DELETE FROM ""Order Details"" AS o
WHERE EXISTS (
SELECT 1
FROM (
SELECT o0.""OrderID"", o0.""ProductID"", o0.""Discount"", o0.""Quantity"", o0.""UnitPrice""
FROM ""Order Details"" AS o0
WHERE o0.""OrderID"" < 10250
INTERSECT
SELECT o1.""OrderID"", o1.""ProductID"", o1.""Discount"", o1.""Quantity"", o1.""UnitPrice""
FROM ""Order Details"" AS o1
WHERE o1.""OrderID"" > 11250
) AS t
WHERE t.""OrderID"" = o.""OrderID"" AND t.""ProductID"" = o.""ProductID"")");
}
public override async Task Delete_Except(bool async)
{
await base.Delete_Except(async);
AssertSql(
@"DELETE FROM ""Order Details"" AS o
WHERE EXISTS (
SELECT 1
FROM (
SELECT o0.""OrderID"", o0.""ProductID"", o0.""Discount"", o0.""Quantity"", o0.""UnitPrice""
FROM ""Order Details"" AS o0
WHERE o0.""OrderID"" < 10250
EXCEPT
SELECT o1.""OrderID"", o1.""ProductID"", o1.""Discount"", o1.""Quantity"", o1.""UnitPrice""
FROM ""Order Details"" AS o1
WHERE o1.""OrderID"" > 11250
) AS t
WHERE t.""OrderID"" = o.""OrderID"" AND t.""ProductID"" = o.""ProductID"")");
}
public override async Task Delete_non_entity_projection(bool async)
{
await base.Delete_non_entity_projection(async);
AssertSql();
}
public override async Task Delete_non_entity_projection_2(bool async)
{
await base.Delete_non_entity_projection_2(async);
AssertSql();
}
public override async Task Delete_non_entity_projection_3(bool async)
{
await base.Delete_non_entity_projection_3(async);
AssertSql();
}
public override async Task Delete_FromSql_converted_to_subquery(bool async)
{
await base.Delete_FromSql_converted_to_subquery(async);
AssertSql(
@"DELETE FROM ""Order Details"" AS o
WHERE EXISTS (
SELECT 1
FROM (
SELECT ""OrderID"", ""ProductID"", ""UnitPrice"", ""Quantity"", ""Discount""
FROM ""Order Details""
WHERE ""OrderID"" < 10300
) AS m
WHERE m.""OrderID"" = o.""OrderID"" AND m.""ProductID"" = o.""ProductID"")");
}
public override async Task Delete_with_join(bool async)
{
await base.Delete_with_join(async);
AssertSql(
@"@__p_1='100'
@__p_0='0'
DELETE FROM ""Order Details"" AS o
USING (
SELECT o0.""OrderID"", o0.""CustomerID"", o0.""EmployeeID"", o0.""OrderDate""
FROM ""Orders"" AS o0
WHERE o0.""OrderID"" < 10300
ORDER BY o0.""OrderID"" NULLS FIRST
LIMIT @__p_1 OFFSET @__p_0
) AS t
WHERE o.""OrderID"" = t.""OrderID""");
}
public override async Task Delete_with_left_join(bool async)
{
await base.Delete_with_left_join(async);
AssertSql(
@"@__p_1='100'
@__p_0='0'
DELETE FROM ""Order Details"" AS o
WHERE EXISTS (
SELECT 1
FROM ""Order Details"" AS o0
LEFT JOIN (
SELECT o1.""OrderID"", o1.""CustomerID"", o1.""EmployeeID"", o1.""OrderDate""
FROM ""Orders"" AS o1
WHERE o1.""OrderID"" < 10300
ORDER BY o1.""OrderID"" NULLS FIRST
LIMIT @__p_1 OFFSET @__p_0
) AS t ON o0.""OrderID"" = t.""OrderID""
WHERE o0.""OrderID"" < 10276 AND o0.""OrderID"" = o.""OrderID"" AND o0.""ProductID"" = o.""ProductID"")");
}
public override async Task Delete_with_cross_join(bool async)
{
await base.Delete_with_cross_join(async);
AssertSql(
@"DELETE FROM ""Order Details"" AS o
WHERE EXISTS (
SELECT 1
FROM ""Order Details"" AS o0
CROSS JOIN (
SELECT o1.""OrderID"", o1.""CustomerID"", o1.""EmployeeID"", o1.""OrderDate""
FROM ""Orders"" AS o1
WHERE o1.""OrderID"" < 10300
ORDER BY o1.""OrderID"" NULLS FIRST
LIMIT 100 OFFSET 0
) AS t
WHERE o0.""OrderID"" < 10276 AND o0.""OrderID"" = o.""OrderID"" AND o0.""ProductID"" = o.""ProductID"")");
}
public override async Task Delete_with_cross_apply(bool async)
{
await base.Delete_with_cross_apply(async);
AssertSql(
@"DELETE FROM ""Order Details"" AS o
WHERE EXISTS (
SELECT 1
FROM ""Order Details"" AS o0
JOIN LATERAL (
SELECT o1.""OrderID"", o1.""CustomerID"", o1.""EmployeeID"", o1.""OrderDate""
FROM ""Orders"" AS o1
WHERE o1.""OrderID"" < o0.""OrderID""
ORDER BY o1.""OrderID"" NULLS FIRST
LIMIT 100 OFFSET 0
) AS t ON TRUE
WHERE o0.""OrderID"" < 10276 AND o0.""OrderID"" = o.""OrderID"" AND o0.""ProductID"" = o.""ProductID"")");
}
public override async Task Delete_with_outer_apply(bool async)
{
await base.Delete_with_outer_apply(async);
AssertSql(
@"DELETE FROM ""Order Details"" AS o
WHERE EXISTS (
SELECT 1
FROM ""Order Details"" AS o0
LEFT JOIN LATERAL (
SELECT o1.""OrderID"", o1.""CustomerID"", o1.""EmployeeID"", o1.""OrderDate""
FROM ""Orders"" AS o1
WHERE o1.""OrderID"" < o0.""OrderID""
ORDER BY o1.""OrderID"" NULLS FIRST
LIMIT 100 OFFSET 0
) AS t ON TRUE
WHERE o0.""OrderID"" < 10276 AND o0.""OrderID"" = o.""OrderID"" AND o0.""ProductID"" = o.""ProductID"")");
}
private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
}
namespace Npgsql.EntityFrameworkCore.PostgreSQL.BulkUpdates;
public class TPCFiltersInheritanceBulkUpdatesNpgsqlFixture : TPCInheritanceBulkUpdatesNpgsqlFixture
{
protected override bool EnableFilters => true;
}
using Microsoft.EntityFrameworkCore.BulkUpdates;
using Npgsql.EntityFrameworkCore.PostgreSQL.Query;
using Npgsql.EntityFrameworkCore.PostgreSQL.TestUtilities;
namespace Npgsql.EntityFrameworkCore.PostgreSQL.BulkUpdates;
public class TPCFiltersInheritanceBulkUpdatesNpgsqlTest
: TPCFiltersInheritanceBulkUpdatesTestBase<TPCFiltersInheritanceBulkUpdatesNpgsqlFixture>
{
public TPCFiltersInheritanceBulkUpdatesNpgsqlTest(TPCFiltersInheritanceBulkUpdatesNpgsqlFixture fixture)
: base(fixture)
{
ClearLog();
}
[ConditionalFact]
public virtual void Check_all_tests_overridden()
=> TestHelpers.AssertAllMethodsOverridden(GetType());
public override async Task Delete_where_hierarchy(bool async)
{
await base.Delete_where_hierarchy(async);
AssertSql();
}
public override async Task Delete_where_hierarchy_derived(bool async)
{
await base.Delete_where_hierarchy_derived(async);
AssertSql(
@"DELETE FROM ""Kiwi"" AS k
WHERE k.""CountryId"" = 1 AND k.""Name"" = 'Great spotted kiwi'");
}
public override async Task Delete_where_using_hierarchy(bool async)
{
await base.Delete_where_using_hierarchy(async);
AssertSql(
@"DELETE FROM ""Countries"" AS c
WHERE (
SELECT count(*)::int
FROM (
SELECT e.""Id"", e.""CountryId"", e.""Name"", e.""Species"", e.""EagleId"", e.""IsFlightless"", e.""Group"", NULL AS ""FoundOn"", 'Eagle' AS ""Discriminator""
FROM ""Eagle"" AS e
UNION ALL
SELECT k.""Id"", k.""CountryId"", k.""Name"", k.""Species"", k.""EagleId"", k.""IsFlightless"", NULL AS ""Group"", k.""FoundOn"", 'Kiwi' AS ""Discriminator""
FROM ""Kiwi"" AS k
) AS t
WHERE t.""CountryId"" = 1 AND c.""Id"" = t.""CountryId"" AND t.""CountryId"" > 0) > 0");
}
public override async Task Delete_where_using_hierarchy_derived(bool async)
{
await base.Delete_where_using_hierarchy_derived(async);
AssertSql(
@"DELETE FROM ""Countries"" AS c
WHERE (
SELECT count(*)::int
FROM (
SELECT k.""Id"", k.""CountryId"", k.""Name"", k.""Species"", k.""EagleId"", k.""IsFlightless"", NULL AS ""Group"", k.""FoundOn"", 'Kiwi' AS ""Discriminator""
FROM ""Kiwi"" AS k
) AS t
WHERE t.""CountryId"" = 1 AND c.""Id"" = t.""CountryId"" AND t.""CountryId"" > 0) > 0");
}
public override async Task Delete_where_keyless_entity_mapped_to_sql_query(bool async)
{
await base.Delete_where_keyless_entity_mapped_to_sql_query(async);
AssertSql();
}
public override async Task Delete_where_hierarchy_subquery(bool async)
{
await base.Delete_where_hierarchy_subquery(async);
AssertSql();
}
protected override void ClearLog() => Fixture.TestSqlLoggerFactory.Clear();
private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
}
using Microsoft.EntityFrameworkCore.BulkUpdates;
using Npgsql.EntityFrameworkCore.PostgreSQL.TestUtilities;
namespace Npgsql.EntityFrameworkCore.PostgreSQL.BulkUpdates;
public class TPCInheritanceBulkUpdatesNpgsqlFixture : TPCInheritanceBulkUpdatesFixture
{
protected override ITestStoreFactory TestStoreFactory
=> NpgsqlTestStoreFactory.Instance;
protected override bool UseGeneratedKeys
=> false;
}
using Microsoft.EntityFrameworkCore.BulkUpdates;
using Npgsql.EntityFrameworkCore.PostgreSQL.Query;
using Npgsql.EntityFrameworkCore.PostgreSQL.TestUtilities;
namespace Npgsql.EntityFrameworkCore.PostgreSQL.BulkUpdates;
public class TPCInheritanceBulkUpdatesNpgsqlTest
: TPCInheritanceBulkUpdatesTestBase<TPCInheritanceBulkUpdatesNpgsqlFixture>
{
public TPCInheritanceBulkUpdatesNpgsqlTest(TPCInheritanceBulkUpdatesNpgsqlFixture fixture)
: base(fixture)
{
ClearLog();
}
[ConditionalFact]
public virtual void Check_all_tests_overridden()
=> TestHelpers.AssertAllMethodsOverridden(GetType());
public override async Task Delete_where_hierarchy(bool async)
{
await base.Delete_where_hierarchy(async);
AssertSql();
}
public override async Task Delete_where_hierarchy_derived(bool async)
{
await base.Delete_where_hierarchy_derived(async);
AssertSql(
@"DELETE FROM ""Kiwi"" AS k
WHERE k.""Name"" = 'Great spotted kiwi'");
}
public override async Task Delete_where_using_hierarchy(bool async)
{
await base.Delete_where_using_hierarchy(async);
AssertSql(
@"DELETE FROM ""Countries"" AS c
WHERE (
SELECT count(*)::int
FROM (
SELECT e.""Id"", e.""CountryId"", e.""Name"", e.""Species"", e.""EagleId"", e.""IsFlightless"", e.""Group"", NULL AS ""FoundOn"", 'Eagle' AS ""Discriminator""
FROM ""Eagle"" AS e
UNION ALL
SELECT k.""Id"", k.""CountryId"", k.""Name"", k.""Species"", k.""EagleId"", k.""IsFlightless"", NULL AS ""Group"", k.""FoundOn"", 'Kiwi' AS ""Discriminator""
FROM ""Kiwi"" AS k
) AS t
WHERE c.""Id"" = t.""CountryId"" AND t.""CountryId"" > 0) > 0");
}
public override async Task Delete_where_using_hierarchy_derived(bool async)
{
await base.Delete_where_using_hierarchy_derived(async);
AssertSql(
@"DELETE FROM ""Countries"" AS c
WHERE (
SELECT count(*)::int
FROM (
SELECT k.""Id"", k.""CountryId"", k.""Name"", k.""Species"", k.""EagleId"", k.""IsFlightless"", NULL AS ""Group"", k.""FoundOn"", 'Kiwi' AS ""Discriminator""
FROM ""Kiwi"" AS k
) AS t
WHERE c.""Id"" = t.""CountryId"" AND t.""CountryId"" > 0) > 0");
}
public override async Task Delete_where_keyless_entity_mapped_to_sql_query(bool async)
{
await base.Delete_where_keyless_entity_mapped_to_sql_query(async);
AssertSql();
}
public override async Task Delete_where_hierarchy_subquery(bool async)
{
await base.Delete_where_hierarchy_subquery(async);
AssertSql();
}
protected override void ClearLog() => Fixture.TestSqlLoggerFactory.Clear();
private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
}
namespace Npgsql.EntityFrameworkCore.PostgreSQL.BulkUpdates;
public class TPTFiltersInheritanceBulkUpdatesNpgsqlFixture : TPTInheritanceBulkUpdatesNpgsqlFixture
{
protected override bool EnableFilters => true;
}
using Microsoft.EntityFrameworkCore.BulkUpdates;
using Npgsql.EntityFrameworkCore.PostgreSQL.Query;
namespace Npgsql.EntityFrameworkCore.PostgreSQL.BulkUpdates;
public class TPTFiltersInheritanceBulkUpdatesSqlServerTest
: TPTFiltersInheritanceBulkUpdatesTestBase<TPTFiltersInheritanceBulkUpdatesNpgsqlFixture>
{
public TPTFiltersInheritanceBulkUpdatesSqlServerTest(TPTFiltersInheritanceBulkUpdatesNpgsqlFixture fixture)
: base(fixture)
{
ClearLog();
}
[ConditionalFact]
public virtual void Check_all_tests_overridden()
=> TestHelpers.AssertAllMethodsOverridden(GetType());
public override async Task Delete_where_hierarchy(bool async)
{
await base.Delete_where_hierarchy(async);
AssertSql();
}
public override async Task Delete_where_hierarchy_derived(bool async)
{
await base.Delete_where_hierarchy_derived(async);
AssertSql();
}
public override async Task Delete_where_using_hierarchy(bool async)
{
await base.Delete_where_using_hierarchy(async);
AssertSql(
@"DELETE FROM [c]
FROM [Countries] AS [c]
WHERE (
SELECT COUNT(*)
FROM [Animals] AS [a]
WHERE [a].[CountryId] = 1 AND [c].[Id] = [a].[CountryId] AND [a].[CountryId] > 0) > 0");
}
public override async Task Delete_where_using_hierarchy_derived(bool async)
{
await base.Delete_where_using_hierarchy_derived(async);
AssertSql(
@"DELETE FROM [c]
FROM [Countries] AS [c]
WHERE (
SELECT COUNT(*)
FROM [Animals] AS [a]
WHERE [a].[CountryId] = 1 AND [c].[Id] = [a].[CountryId] AND [a].[Discriminator] = N'Kiwi' AND [a].[CountryId] > 0) > 0");
}
public override async Task Delete_where_keyless_entity_mapped_to_sql_query(bool async)
{
await base.Delete_where_keyless_entity_mapped_to_sql_query(async);
AssertSql();
}
public override async Task Delete_where_hierarchy_subquery(bool async)
{
await base.Delete_where_hierarchy_subquery(async);
AssertSql();
}
protected override void ClearLog() => Fixture.TestSqlLoggerFactory.Clear();
private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
}
using Microsoft.EntityFrameworkCore.BulkUpdates;
using Npgsql.EntityFrameworkCore.PostgreSQL.TestUtilities;
namespace Npgsql.EntityFrameworkCore.PostgreSQL.BulkUpdates;
public class TPTInheritanceBulkUpdatesNpgsqlFixture : TPTInheritanceBulkUpdatesFixture
{
protected override ITestStoreFactory TestStoreFactory
=> NpgsqlTestStoreFactory.Instance;
}
using Microsoft.EntityFrameworkCore.BulkUpdates;
using Npgsql.EntityFrameworkCore.PostgreSQL.Query;
namespace Npgsql.EntityFrameworkCore.PostgreSQL.BulkUpdates;
public class TPTInheritanceBulkUpdatesNpgsqlTest : TPTInheritanceBulkUpdatesTestBase<TPTInheritanceBulkUpdatesNpgsqlFixture>
{
public TPTInheritanceBulkUpdatesNpgsqlTest(TPTInheritanceBulkUpdatesNpgsqlFixture fixture)
: base(fixture)
{
ClearLog();
}
[ConditionalFact]
public virtual void Check_all_tests_overridden()
=> TestHelpers.AssertAllMethodsOverridden(GetType());
public override async Task Delete_where_hierarchy(bool async)
{
await base.Delete_where_hierarchy(async);
AssertSql();
}
public override async Task Delete_where_hierarchy_derived(bool async)
{
await base.Delete_where_hierarchy_derived(async);
AssertSql();
}
public override async Task Delete_where_using_hierarchy(bool async)
{
await base.Delete_where_using_hierarchy(async);
AssertSql();
}
public override async Task Delete_where_using_hierarchy_derived(bool async)
{
await base.Delete_where_using_hierarchy_derived(async);
AssertSql();
}
public override async Task Delete_where_keyless_entity_mapped_to_sql_query(bool async)
{
await base.Delete_where_keyless_entity_mapped_to_sql_query(async);
AssertSql();
}
public override async Task Delete_where_hierarchy_subquery(bool async)
{
await base.Delete_where_hierarchy_subquery(async);
AssertSql();
}
protected override void ClearLog() => Fixture.TestSqlLoggerFactory.Clear();
private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册