diff --git a/src/EFCore.Relational/Query/Internal/RelationalCommandCache.cs b/src/EFCore.Relational/Query/Internal/RelationalCommandCache.cs
index 0606cae494f..d0c18bb5afd 100644
--- a/src/EFCore.Relational/Query/Internal/RelationalCommandCache.cs
+++ b/src/EFCore.Relational/Query/Internal/RelationalCommandCache.cs
@@ -3,7 +3,6 @@
using System.Collections;
using System.Collections.Concurrent;
-using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
using Microsoft.Extensions.Caching.Memory;
namespace Microsoft.EntityFrameworkCore.Query.Internal;
diff --git a/src/EFCore.Relational/Query/QuerySqlGenerator.cs b/src/EFCore.Relational/Query/QuerySqlGenerator.cs
index 5dd0a830c74..6453c48fe7d 100644
--- a/src/EFCore.Relational/Query/QuerySqlGenerator.cs
+++ b/src/EFCore.Relational/Query/QuerySqlGenerator.cs
@@ -1237,8 +1237,6 @@ protected override Expression VisitUpdate(UpdateExpression updateExpression)
&& selectExpression.Having == null
&& selectExpression.Orderings.Count == 0
&& selectExpression.GroupBy.Count == 0
- && selectExpression.Tables.Count == 1
- && selectExpression.Tables[0] == updateExpression.Table
&& selectExpression.Projection.Count == 0)
{
_relationalCommandBuilder.Append("UPDATE ");
@@ -1255,13 +1253,58 @@ protected override Expression VisitUpdate(UpdateExpression updateExpression)
},
joinAction: e => e.AppendLine(","));
- _relationalCommandBuilder.AppendLine();
}
- if (selectExpression.Predicate != null)
+ var predicate = selectExpression.Predicate;
+ var firstTablePrinted = false;
+ if (selectExpression.Tables.Count > 1)
+ {
+ _relationalCommandBuilder.AppendLine().Append("FROM ");
+ for (var i = 0; i < selectExpression.Tables.Count; i++)
+ {
+ var table = selectExpression.Tables[i];
+ var joinExpression = table as JoinExpressionBase;
+
+ if (ReferenceEquals(updateExpression.Table, joinExpression?.Table ?? table))
+ {
+ LiftPredicate(table);
+ continue;
+ }
+
+ if (firstTablePrinted)
+ {
+ _relationalCommandBuilder.AppendLine();
+ }
+ else
+ {
+ firstTablePrinted = true;
+ LiftPredicate(table);
+ table = joinExpression?.Table ?? table;
+ }
+
+ Visit(table);
+
+ void LiftPredicate(TableExpressionBase joinTable)
+ {
+ if (joinTable is PredicateJoinExpressionBase predicateJoinExpression)
+ {
+ predicate = predicate == null
+ ? predicateJoinExpression.JoinPredicate
+ : new SqlBinaryExpression(
+ ExpressionType.AndAlso,
+ predicateJoinExpression.JoinPredicate,
+ predicate,
+ typeof(bool),
+ predicate.TypeMapping);
+ }
+ }
+ }
+ }
+
+ if (predicate != null)
{
_relationalCommandBuilder.AppendLine().Append("WHERE ");
- Visit(selectExpression.Predicate);
+ Visit(predicate);
}
return updateExpression;
diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs
index e41ba43fc6e..743c7479f32 100644
--- a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs
+++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs
@@ -1105,6 +1105,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
foreach (var (propertyExpression, valueExpression) in propertyValueLambdaExpressions)
{
var left = RemapLambdaBody(source, propertyExpression);
+ left = left.UnwrapTypeConversion(out _);
if (!IsValidPropertyAccess(left, out var ese))
{
AddTranslationErrorDetails(RelationalStrings.InvalidPropertyInSetProperty(propertyExpression.Print()));
@@ -1123,6 +1124,10 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
}
var right = RemapLambdaBody(source, valueExpression);
+ if (right.Type != left.Type)
+ {
+ right = Expression.Convert(right, left.Type);
+ }
// We generate equality between property = value while translating sothat value infer tye type mapping from property correctly.
// Later we decompose it back into left/right components so that the equality is not in the tree which can get affected by
// null semantics or other visitor.
@@ -1280,7 +1285,7 @@ static bool IsValidPropertyAccess(Expression expression, [NotNullWhen(true)] out
/// The select expression to validate.
/// The entity shaper expression on which the delete operation is being applied.
/// The table expression from which rows are being deleted.
- /// das
+ /// Returns if the current select expression can be used for delete as-is, otherwise.
protected virtual bool IsValidSelectExpressionForExecuteDelete(
SelectExpression selectExpression,
EntityShaperExpression entityShaperExpression,
@@ -1305,13 +1310,12 @@ protected virtual bool IsValidSelectExpressionForExecuteDelete(
return false;
}
- // TODO: Update this documentation.
///
- /// Validates if the current select expression can be used for execute update operation or it requires to be pushed into a subquery.
+ /// Validates if the current select expression can be used for execute update operation or it requires to be joined as a subquery.
///
///
///
- /// By default, only single-table select expressions are supported, and optionally with a predicate.
+ /// By default, only muli-table select expressions are supported, and optionally with a predicate.
///
///
/// Providers can override this to allow more select expression features to be supported without pushing down into a subquery.
@@ -1322,7 +1326,7 @@ protected virtual bool IsValidSelectExpressionForExecuteDelete(
/// The select expression to validate.
/// The entity shaper expression on which the update operation is being applied.
/// The table expression from which rows are being deleted.
- /// das
+ /// Returns if the current select expression can be used for update as-is, otherwise.
protected virtual bool IsValidSelectExpressionForExecuteUpdate(
SelectExpression selectExpression,
EntityShaperExpression entityShaperExpression,
@@ -1334,13 +1338,30 @@ protected virtual bool IsValidSelectExpressionForExecuteUpdate(
&& (!selectExpression.IsDistinct || entityShaperExpression.EntityType.FindPrimaryKey() != null)
&& selectExpression.GroupBy.Count == 0
&& selectExpression.Having == null
- && selectExpression.Orderings.Count == 0
- && selectExpression.Tables.Count == 1
- && selectExpression.Tables[0] is TableExpression expression)
+ && selectExpression.Orderings.Count == 0)
{
- tableExpression = expression;
+ TableExpressionBase table;
+ if (selectExpression.Tables.Count == 1)
+ {
+ table = selectExpression.Tables[0];
+ }
+ else
+ {
+ 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;
+ }
+ }
- return true;
+ if (table is TableExpression te)
+ {
+ tableExpression = te;
+ return true;
+ }
}
tableExpression = null;
diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGenerator.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGenerator.cs
index 85d8489f975..95b67577259 100644
--- a/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGenerator.cs
+++ b/src/EFCore.SqlServer/Query/Internal/SqlServerQuerySqlGenerator.cs
@@ -80,15 +80,14 @@ protected override Expression VisitUpdate(UpdateExpression updateExpression)
var selectExpression = updateExpression.SelectExpression;
if (selectExpression.Offset == null
- && selectExpression.Limit == null
&& selectExpression.Having == null
&& selectExpression.Orderings.Count == 0
&& selectExpression.GroupBy.Count == 0
- && selectExpression.Tables.Count == 1
- && selectExpression.Tables[0] == updateExpression.Table
&& selectExpression.Projection.Count == 0)
{
Sql.Append("UPDATE ");
+ GenerateTop(selectExpression);
+
Sql.AppendLine($"{Dependencies.SqlGenerationHelper.DelimitIdentifier(updateExpression.Table.Alias)}");
using (Sql.Indent())
{
diff --git a/src/EFCore.SqlServer/Query/Internal/SqlServerQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.SqlServer/Query/Internal/SqlServerQueryableMethodTranslatingExpressionVisitor.cs
index ab05a121594..53fbc0f3ddc 100644
--- a/src/EFCore.SqlServer/Query/Internal/SqlServerQueryableMethodTranslatingExpressionVisitor.cs
+++ b/src/EFCore.SqlServer/Query/Internal/SqlServerQueryableMethodTranslatingExpressionVisitor.cs
@@ -146,6 +146,52 @@ protected override bool IsValidSelectExpressionForExecuteDelete(
return false;
}
+ ///
+ /// 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 override bool IsValidSelectExpressionForExecuteUpdate(
+ SelectExpression selectExpression,
+ EntityShaperExpression entityShaperExpression,
+ [NotNullWhen(true)] out TableExpression? tableExpression)
+ {
+ if (selectExpression.Offset == 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;
+ if (selectExpression.Tables.Count == 1)
+ {
+ table = selectExpression.Tables[0];
+ }
+ else
+ {
+ 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;
+ }
+
private sealed class TemporalAnnotationApplyingExpressionVisitor : ExpressionVisitor
{
private readonly Func _annotationApplyingFunc;
diff --git a/test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesTestBase.cs b/test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesTestBase.cs
index a68e4cf351b..0533f0bcedc 100644
--- a/test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/BulkUpdates/NorthwindBulkUpdatesTestBase.cs
@@ -343,6 +343,29 @@ public virtual Task Update_where_constant(bool async)
rowsAffectedCount: 8,
(b, a) => a.ForEach(c => Assert.Equal("Updated", c.ContactName)));
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual async Task Update_where_parameter_in_predicate(bool async)
+ {
+ var customer = "ALFKI";
+ await AssertUpdate(
+ async,
+ ss => ss.Set().Where(c => c.CustomerID == customer),
+ e => e,
+ s => s.SetProperty(c => c.ContactName, c => "Updated"),
+ rowsAffectedCount: 1,
+ (b, a) => a.ForEach(c => Assert.Equal("Updated", c.ContactName)));
+
+ customer = null;
+ await AssertUpdate(
+ async,
+ ss => ss.Set().Where(c => c.CustomerID == customer),
+ e => e,
+ s => s.SetProperty(c => c.ContactName, c => "Updated"),
+ rowsAffectedCount: 0,
+ (b, a) => a.ForEach(c => Assert.Equal("Updated", c.ContactName)));
+ }
+
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Update_where_parameter(bool async)
@@ -357,6 +380,113 @@ public virtual Task Update_where_parameter(bool async)
(b, a) => a.ForEach(c => Assert.Equal("Abc", c.ContactName)));
}
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Update_where_take_constant(bool async)
+ => AssertUpdate(
+ async,
+ ss => ss.Set().Where(c => c.CustomerID.StartsWith("F")).Take(4),
+ e => e,
+ s => s.SetProperty(c => c.ContactName, c => "Updated"),
+ rowsAffectedCount: 4,
+ (b, a) => a.ForEach(c => Assert.Equal("Updated", c.ContactName)));
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Update_where_group_by_aggregate_constant(bool async)
+ => AssertUpdate(
+ async,
+ ss => ss.Set()
+ .Where(c => c.CustomerID == ss.Set()
+ .GroupBy(e => e.CustomerID).Where(g => g.Count() > 11).Select(e => e.Key).FirstOrDefault()),
+ e => e,
+ s => s.SetProperty(c => c.ContactName, c => "Updated"),
+ rowsAffectedCount: 1,
+ (b, a) => a.ForEach(c => Assert.Equal("Updated", c.ContactName)));
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Update_where_group_by_first_constant(bool async)
+ => AssertUpdate(
+ async,
+ ss => ss.Set()
+ .Where(c => c.CustomerID == ss.Set()
+ .GroupBy(e => e.CustomerID).Where(g => g.Count() > 11).Select(e => e.First().CustomerID).FirstOrDefault()),
+ e => e,
+ s => s.SetProperty(c => c.ContactName, c => "Updated"),
+ rowsAffectedCount: 1,
+ (b, a) => a.ForEach(c => Assert.Equal("Updated", c.ContactName)));
+
+ [ConditionalTheory(Skip = "Issue#26753")]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Update_where_group_by_first_constant_2(bool async)
+ => AssertUpdate(
+ async,
+ ss => ss.Set()
+ .Where(c => c == ss.Set()
+ .GroupBy(e => e.CustomerID).Where(g => g.Count() > 11).Select(e => e.First().Customer).FirstOrDefault()),
+ e => e,
+ s => s.SetProperty(c => c.ContactName, c => "Updated"),
+ rowsAffectedCount: 1,
+ (b, a) => a.ForEach(c => Assert.Equal("Updated", c.ContactName)));
+
+ [ConditionalTheory(Skip = "Issue#28524")]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Update_where_group_by_first_constant_3(bool async)
+ => AssertUpdate(
+ async,
+ ss => ss.Set()
+ .Where(c => ss.Set()
+ .GroupBy(e => e.CustomerID).Where(g => g.Count() > 11).Select(e => e.First().Customer).Contains(c)),
+ e => e,
+ s => s.SetProperty(c => c.ContactName, c => "Updated"),
+ rowsAffectedCount: 1,
+ (b, a) => a.ForEach(c => Assert.Equal("Updated", c.ContactName)));
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Update_where_distinct_constant(bool async)
+ => AssertUpdate(
+ async,
+ ss => ss.Set().Where(c => c.CustomerID.StartsWith("F")).Distinct(),
+ e => e,
+ s => s.SetProperty(c => c.ContactName, c => "Updated"),
+ rowsAffectedCount: 8,
+ (b, a) => a.ForEach(c => Assert.Equal("Updated", c.ContactName)));
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Update_where_using_navigation(bool async)
+ => AssertUpdate(
+ async,
+ ss => ss.Set().Where(o => o.Customer.City == "Seattle"),
+ e => e,
+ s => s.SetProperty(c => c.OrderDate, c => null),
+ rowsAffectedCount: 14,
+ (b, a) => a.ForEach(c => Assert.Null(c.OrderDate)));
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Update_where_using_navigation_2(bool async)
+ => AssertUpdate(
+ async,
+ ss => ss.Set().Where(od => od.Order.Customer.City == "Seattle"),
+ e => e,
+ s => s.SetProperty(c => c.Quantity, c => 1),
+ rowsAffectedCount: 40,
+ (b, a) => a.ForEach(c => Assert.Equal(1, c.Quantity)));
+
+ [ConditionalTheory]
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Update_where_select_many(bool async)
+ => AssertUpdate(
+ async,
+ ss => ss.Set().Where(c => c.CustomerID.StartsWith("F")).SelectMany(c => c.Orders),
+ e => e,
+ s => s.SetProperty(c => c.OrderDate, c => null),
+ rowsAffectedCount: 63,
+ (b, a) => a.ForEach(c => Assert.Null(c.OrderDate)));
+
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual Task Update_where_using_property_plus_constant(bool async)
diff --git a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqlServerTest.cs
index 3893c12c948..b08ada7c255 100644
--- a/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqlServerTest.cs
@@ -513,6 +513,34 @@ FROM [Customers] AS [c]
WHERE [c].[CustomerID] LIKE N'F%'");
}
+ public override async Task Update_where_parameter_in_predicate(bool async)
+ {
+ await base.Update_where_parameter_in_predicate(async);
+
+ AssertExecuteUpdateSql(
+ @"@__customer_0='ALFKI' (Size = 5) (DbType = StringFixedLength)
+
+UPDATE [c]
+ SET [c].[ContactName] = N'Updated'
+FROM [Customers] AS [c]
+WHERE [c].[CustomerID] = @__customer_0",
+ //
+ @"@__customer_0='ALFKI' (Size = 5) (DbType = StringFixedLength)
+
+SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region]
+FROM [Customers] AS [c]
+WHERE [c].[CustomerID] = @__customer_0",
+ //
+ @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region]
+FROM [Customers] AS [c]
+WHERE 0 = 1",
+ //
+ @"UPDATE [c]
+ SET [c].[ContactName] = N'Updated'
+FROM [Customers] AS [c]
+WHERE 0 = 1");
+ }
+
public override async Task Update_where_parameter(bool async)
{
await base.Update_where_parameter(async);
@@ -526,6 +554,114 @@ FROM [Customers] AS [c]
WHERE [c].[CustomerID] LIKE N'F%'");
}
+ public override async Task Update_where_take_constant(bool async)
+ {
+ await base.Update_where_take_constant(async);
+
+ AssertExecuteUpdateSql(
+ @"@__p_0='4'
+
+UPDATE TOP(@__p_0) [c]
+ SET [c].[ContactName] = N'Updated'
+FROM [Customers] AS [c]
+WHERE [c].[CustomerID] LIKE N'F%'");
+ }
+
+ public override async Task Update_where_group_by_aggregate_constant(bool async)
+ {
+ await base.Update_where_group_by_aggregate_constant(async);
+
+ AssertExecuteUpdateSql(
+ @"UPDATE [c]
+ SET [c].[ContactName] = N'Updated'
+FROM [Customers] AS [c]
+WHERE [c].[CustomerID] = (
+ SELECT TOP(1) [o].[CustomerID]
+ FROM [Orders] AS [o]
+ GROUP BY [o].[CustomerID]
+ HAVING COUNT(*) > 11)");
+ }
+
+ public override async Task Update_where_group_by_first_constant(bool async)
+ {
+ await base.Update_where_group_by_first_constant(async);
+
+ AssertExecuteUpdateSql(
+ @"UPDATE [c]
+ SET [c].[ContactName] = N'Updated'
+FROM [Customers] AS [c]
+WHERE [c].[CustomerID] = (
+ SELECT TOP(1) (
+ SELECT TOP(1) [o0].[CustomerID]
+ FROM [Orders] AS [o0]
+ WHERE [o].[CustomerID] = [o0].[CustomerID] OR ([o].[CustomerID] IS NULL AND [o0].[CustomerID] IS NULL))
+ FROM [Orders] AS [o]
+ GROUP BY [o].[CustomerID]
+ HAVING COUNT(*) > 11)");
+ }
+
+ public override async Task Update_where_group_by_first_constant_2(bool async)
+ {
+ await base.Update_where_group_by_first_constant_2(async);
+
+ AssertExecuteUpdateSql();
+ }
+
+ public override async Task Update_where_group_by_first_constant_3(bool async)
+ {
+ await base.Update_where_group_by_first_constant_3(async);
+
+ AssertExecuteUpdateSql();
+ }
+
+ public override async Task Update_where_distinct_constant(bool async)
+ {
+ await base.Update_where_distinct_constant(async);
+
+ AssertExecuteUpdateSql(
+ @"UPDATE [c]
+ SET [c].[ContactName] = N'Updated'
+FROM [Customers] AS [c]
+WHERE [c].[CustomerID] LIKE N'F%'");
+ }
+
+ public override async Task Update_where_using_navigation(bool async)
+ {
+ await base.Update_where_using_navigation(async);
+
+ AssertExecuteUpdateSql(
+ @"UPDATE [o]
+ SET [o].[OrderDate] = NULL
+FROM [Orders] AS [o]
+LEFT JOIN [Customers] AS [c] ON [o].[CustomerID] = [c].[CustomerID]
+WHERE [c].[City] = N'Seattle'");
+ }
+
+ public override async Task Update_where_using_navigation_2(bool async)
+ {
+ await base.Update_where_using_navigation_2(async);
+
+ AssertExecuteUpdateSql(
+ @"UPDATE [o]
+ SET [o].[Quantity] = CAST(1 AS smallint)
+FROM [Order Details] AS [o]
+INNER JOIN [Orders] AS [o0] ON [o].[OrderID] = [o0].[OrderID]
+LEFT JOIN [Customers] AS [c] ON [o0].[CustomerID] = [c].[CustomerID]
+WHERE [c].[City] = N'Seattle'");
+ }
+
+ public override async Task Update_where_select_many(bool async)
+ {
+ await base.Update_where_select_many(async);
+
+ AssertExecuteUpdateSql(
+ @"UPDATE [o]
+ SET [o].[OrderDate] = NULL
+FROM [Customers] AS [c]
+INNER JOIN [Orders] AS [o] ON [c].[CustomerID] = [o].[CustomerID]
+WHERE [c].[CustomerID] LIKE N'F%'");
+ }
+
public override async Task Update_where_using_property_plus_constant(bool async)
{
await base.Update_where_using_property_plus_constant(async);
diff --git a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqliteTest.cs
index dfff622f6b8..ae8c31f367e 100644
--- a/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqliteTest.cs
+++ b/test/EFCore.Sqlite.FunctionalTests/BulkUpdates/NorthwindBulkUpdatesSqliteTest.cs
@@ -488,10 +488,35 @@ public override async Task Update_where_constant(bool async)
AssertExecuteUpdateSql(
@"UPDATE ""Customers"" AS ""c""
SET ""ContactName"" = 'Updated'
-
WHERE ""c"".""CustomerID"" LIKE 'F%'");
}
+ public override async Task Update_where_parameter_in_predicate(bool async)
+ {
+ await base.Update_where_parameter_in_predicate(async);
+
+ AssertExecuteUpdateSql(
+ @"@__customer_0='ALFKI' (Size = 5)
+
+UPDATE ""Customers"" AS ""c""
+ SET ""ContactName"" = 'Updated'
+WHERE ""c"".""CustomerID"" = @__customer_0",
+ //
+ @"@__customer_0='ALFKI' (Size = 5)
+
+SELECT ""c"".""CustomerID"", ""c"".""Address"", ""c"".""City"", ""c"".""CompanyName"", ""c"".""ContactName"", ""c"".""ContactTitle"", ""c"".""Country"", ""c"".""Fax"", ""c"".""Phone"", ""c"".""PostalCode"", ""c"".""Region""
+FROM ""Customers"" AS ""c""
+WHERE ""c"".""CustomerID"" = @__customer_0",
+ //
+ @"SELECT ""c"".""CustomerID"", ""c"".""Address"", ""c"".""City"", ""c"".""CompanyName"", ""c"".""ContactName"", ""c"".""ContactTitle"", ""c"".""Country"", ""c"".""Fax"", ""c"".""Phone"", ""c"".""PostalCode"", ""c"".""Region""
+FROM ""Customers"" AS ""c""
+WHERE 0",
+ //
+ @"UPDATE ""Customers"" AS ""c""
+ SET ""ContactName"" = 'Updated'
+WHERE 0");
+ }
+
public override async Task Update_where_parameter(bool async)
{
await base.Update_where_parameter(async);
@@ -501,10 +526,115 @@ public override async Task Update_where_parameter(bool async)
UPDATE ""Customers"" AS ""c""
SET ""ContactName"" = @__value_0
+WHERE ""c"".""CustomerID"" LIKE 'F%'");
+ }
+
+ [ConditionalTheory(Skip = "Issue#28661")]
+ public override async Task Update_where_take_constant(bool async)
+ {
+ await base.Update_where_take_constant(async);
+
+ AssertExecuteUpdateSql(
+ @"@__p_0='4'
+
+UPDATE TOP(@__p_0) [c]
+ SET [c].[ContactName] = N'Updated'
+FROM [Customers] AS [c]
+WHERE [c].[CustomerID] LIKE N'F%'");
+ }
+
+ public override async Task Update_where_group_by_aggregate_constant(bool async)
+ {
+ await base.Update_where_group_by_aggregate_constant(async);
+
+ AssertExecuteUpdateSql(
+ @"UPDATE ""Customers"" AS ""c""
+ SET ""ContactName"" = 'Updated'
+WHERE ""c"".""CustomerID"" = (
+ SELECT ""o"".""CustomerID""
+ FROM ""Orders"" AS ""o""
+ GROUP BY ""o"".""CustomerID""
+ HAVING COUNT(*) > 11
+ LIMIT 1)");
+ }
+ public override async Task Update_where_group_by_first_constant(bool async)
+ {
+ await base.Update_where_group_by_first_constant(async);
+
+ AssertExecuteUpdateSql(
+ @"UPDATE ""Customers"" AS ""c""
+ SET ""ContactName"" = 'Updated'
+WHERE ""c"".""CustomerID"" = (
+ SELECT (
+ SELECT ""o0"".""CustomerID""
+ FROM ""Orders"" AS ""o0""
+ WHERE ""o"".""CustomerID"" = ""o0"".""CustomerID"" OR (""o"".""CustomerID"" IS NULL AND ""o0"".""CustomerID"" IS NULL)
+ LIMIT 1)
+ FROM ""Orders"" AS ""o""
+ GROUP BY ""o"".""CustomerID""
+ HAVING COUNT(*) > 11
+ LIMIT 1)");
+ }
+
+ public override async Task Update_where_group_by_first_constant_2(bool async)
+ {
+ await base.Update_where_group_by_first_constant_2(async);
+
+ AssertExecuteUpdateSql();
+ }
+
+ public override async Task Update_where_group_by_first_constant_3(bool async)
+ {
+ await base.Update_where_group_by_first_constant_3(async);
+
+ AssertExecuteUpdateSql();
+ }
+
+ public override async Task Update_where_distinct_constant(bool async)
+ {
+ await base.Update_where_distinct_constant(async);
+
+ AssertExecuteUpdateSql(
+ @"UPDATE ""Customers"" AS ""c""
+ SET ""ContactName"" = 'Updated'
WHERE ""c"".""CustomerID"" LIKE 'F%'");
}
+ public override async Task Update_where_using_navigation(bool async)
+ {
+ await base.Update_where_using_navigation(async);
+
+ AssertExecuteUpdateSql(
+ @"UPDATE ""Orders"" AS ""o""
+ SET ""OrderDate"" = NULL
+FROM ""Customers"" AS ""c""
+WHERE ""o"".""CustomerID"" = ""c"".""CustomerID"" AND ""c"".""City"" = 'Seattle'");
+ }
+
+ public override async Task Update_where_using_navigation_2(bool async)
+ {
+ await base.Update_where_using_navigation_2(async);
+
+ AssertExecuteUpdateSql(
+ @"UPDATE ""Order Details"" AS ""o""
+ SET ""Quantity"" = CAST(1 AS INTEGER)
+FROM ""Orders"" AS ""o0""
+LEFT JOIN ""Customers"" AS ""c"" ON ""o0"".""CustomerID"" = ""c"".""CustomerID""
+WHERE ""o"".""OrderID"" = ""o0"".""OrderID"" AND ""c"".""City"" = 'Seattle'");
+ }
+
+ public override async Task Update_where_select_many(bool async)
+ {
+ await base.Update_where_select_many(async);
+
+ AssertExecuteUpdateSql(
+ @"UPDATE ""Orders"" AS ""o""
+ SET ""OrderDate"" = NULL
+FROM ""Customers"" AS ""c""
+WHERE ""c"".""CustomerID"" = ""o"".""CustomerID"" AND (""c"".""CustomerID"" LIKE 'F%')");
+ }
+
public override async Task Update_where_using_property_plus_constant(bool async)
{
await base.Update_where_using_property_plus_constant(async);
@@ -512,7 +642,6 @@ public override async Task Update_where_using_property_plus_constant(bool async)
AssertExecuteUpdateSql(
@"UPDATE ""Customers"" AS ""c""
SET ""ContactName"" = COALESCE(""c"".""ContactName"", '') || 'Abc'
-
WHERE ""c"".""CustomerID"" LIKE 'F%'");
}
@@ -525,7 +654,6 @@ public override async Task Update_where_using_property_plus_parameter(bool async
UPDATE ""Customers"" AS ""c""
SET ""ContactName"" = COALESCE(""c"".""ContactName"", '') || @__value_0
-
WHERE ""c"".""CustomerID"" LIKE 'F%'");
}
@@ -536,7 +664,6 @@ public override async Task Update_where_using_property_plus_property(bool async)
AssertExecuteUpdateSql(
@"UPDATE ""Customers"" AS ""c""
SET ""ContactName"" = COALESCE(""c"".""ContactName"", '') || ""c"".""CustomerID""
-
WHERE ""c"".""CustomerID"" LIKE 'F%'");
}
@@ -547,7 +674,6 @@ public override async Task Update_where_constant_using_ef_property(bool async)
AssertExecuteUpdateSql(
@"UPDATE ""Customers"" AS ""c""
SET ""ContactName"" = 'Updated'
-
WHERE ""c"".""CustomerID"" LIKE 'F%'");
}
@@ -558,7 +684,6 @@ public override async Task Update_where_null(bool async)
AssertExecuteUpdateSql(
@"UPDATE ""Customers"" AS ""c""
SET ""ContactName"" = NULL
-
WHERE ""c"".""CustomerID"" LIKE 'F%'");
}
@@ -586,7 +711,6 @@ public override async Task Update_where_multi_property_update(bool async)
UPDATE ""Customers"" AS ""c""
SET ""City"" = 'Seattle',
""ContactName"" = @__value_0
-
WHERE ""c"".""CustomerID"" LIKE 'F%'");
}