diff --git a/src/EFCore.MySql/Update/Internal/MySqlUpdateSqlGenerator.cs b/src/EFCore.MySql/Update/Internal/MySqlUpdateSqlGenerator.cs
index dfde995e5..7f6f4cf3b 100644
--- a/src/EFCore.MySql/Update/Internal/MySqlUpdateSqlGenerator.cs
+++ b/src/EFCore.MySql/Update/Internal/MySqlUpdateSqlGenerator.cs
@@ -38,6 +38,45 @@ public override ResultSetMapping AppendInsertOperation(
? AppendInsertReturningOperation(commandStringBuilder, command, commandPosition, out requiresTransaction)
: base.AppendInsertOperation(commandStringBuilder, command, commandPosition, out requiresTransaction);
+ ///
+ /// Appends SQL for inserting a row to the commands being built, via an INSERT containing a RETURNING clause
+ /// to retrieve any database-generated values.
+ ///
+ /// The builder to which the SQL should be appended.
+ /// The command that represents the insert operation.
+ /// The ordinal of this command in the batch.
+ /// Returns whether the SQL appended must be executed in a transaction to work correctly.
+ /// The for the command.
+ public override ResultSetMapping AppendInsertReturningOperation(
+ StringBuilder commandStringBuilder,
+ IReadOnlyModificationCommand command,
+ int commandPosition,
+ out bool requiresTransaction)
+ {
+ var name = command.TableName;
+ var schema = command.Schema;
+ var operations = command.ColumnModifications;
+
+ var writeOperations = operations.Where(o => o.IsWrite).ToList();
+ var readOperations = operations.Where(o => o.IsRead).ToList();
+
+ AppendInsertCommandHeader(commandStringBuilder, name, schema, writeOperations);
+ AppendValuesHeader(commandStringBuilder, writeOperations);
+ AppendValues(commandStringBuilder, name, schema, writeOperations);
+
+ // MySQL supports RETURNING clause starting from version 8.0.21
+ if (_options.ServerVersion.Supports.Returning && readOperations.Count > 0)
+ {
+ AppendReturningClause(commandStringBuilder, readOperations);
+ }
+
+ commandStringBuilder.AppendLine(SqlGenerationHelper.StatementTerminator);
+
+ requiresTransaction = false;
+
+ return readOperations.Count > 0 ? ResultSetMapping.LastInResultSet : ResultSetMapping.NoResults;
+ }
+
public virtual ResultSetMapping AppendBulkInsertOperation(
StringBuilder commandStringBuilder,
IReadOnlyList modificationCommands,
@@ -137,6 +176,58 @@ protected override void AppendValues(
}
}
+ public override ResultSetMapping AppendUpdateOperation(
+ StringBuilder commandStringBuilder,
+ IReadOnlyModificationCommand command,
+ int commandPosition,
+ out bool requiresTransaction)
+ => _options.ServerVersion.Supports.Returning
+ ? AppendUpdateReturningOperation(commandStringBuilder, command, commandPosition, out requiresTransaction)
+ : base.AppendUpdateOperation(commandStringBuilder, command, commandPosition, out requiresTransaction);
+
+ ///
+ /// Appends SQL for updating a row to the commands being built, via an UPDATE containing a RETURNING clause
+ /// to retrieve any database-generated values or for concurrency checking.
+ ///
+ /// The builder to which the SQL should be appended.
+ /// The command that represents the update operation.
+ /// The ordinal of this command in the batch.
+ /// Returns whether the SQL appended must be executed in a transaction to work correctly.
+ /// The for the command.
+ protected override ResultSetMapping AppendUpdateReturningOperation(
+ StringBuilder commandStringBuilder,
+ IReadOnlyModificationCommand command,
+ int commandPosition,
+ out bool requiresTransaction)
+ {
+ var name = command.TableName;
+ var schema = command.Schema;
+ var operations = command.ColumnModifications;
+
+ var writeOperations = operations.Where(o => o.IsWrite).ToList();
+ var conditionOperations = operations.Where(o => o.IsCondition).ToList();
+ var readOperations = operations.Where(o => o.IsRead).ToList();
+
+ requiresTransaction = false;
+
+ var anyReadOperations = readOperations.Count > 0;
+
+ AppendUpdateCommandHeader(commandStringBuilder, name, schema, writeOperations);
+ AppendWhereClause(commandStringBuilder, conditionOperations);
+
+ // MySQL supports RETURNING clause starting from version 8.0.21
+ if (_options.ServerVersion.Supports.Returning)
+ {
+ AppendReturningClause(commandStringBuilder, readOperations, anyReadOperations ? null : "1");
+ }
+
+ commandStringBuilder.AppendLine(SqlGenerationHelper.StatementTerminator);
+
+ return anyReadOperations
+ ? ResultSetMapping.LastInResultSet
+ : ResultSetMapping.LastInResultSet | ResultSetMapping.ResultSetWithRowsAffectedOnly;
+ }
+
public override ResultSetMapping AppendDeleteOperation(StringBuilder commandStringBuilder,
IReadOnlyModificationCommand command,
int commandPosition,
@@ -145,6 +236,41 @@ public override ResultSetMapping AppendDeleteOperation(StringBuilder commandStri
? AppendDeleteReturningOperation(commandStringBuilder, command, commandPosition, out requiresTransaction)
: base.AppendDeleteOperation(commandStringBuilder, command, commandPosition, out requiresTransaction);
+ ///
+ /// Appends SQL for deleting a row to the commands being built, via a DELETE containing a RETURNING clause
+ /// for concurrency checking.
+ ///
+ /// The builder to which the SQL should be appended.
+ /// The command that represents the delete operation.
+ /// The ordinal of this command in the batch.
+ /// Returns whether the SQL appended must be executed in a transaction to work correctly.
+ /// The for the command.
+ protected override ResultSetMapping AppendDeleteReturningOperation(
+ StringBuilder commandStringBuilder,
+ IReadOnlyModificationCommand command,
+ int commandPosition,
+ out bool requiresTransaction)
+ {
+ var name = command.TableName;
+ var schema = command.Schema;
+ var conditionOperations = command.ColumnModifications.Where(o => o.IsCondition).ToList();
+
+ requiresTransaction = false;
+
+ AppendDeleteCommandHeader(commandStringBuilder, name, schema);
+ AppendWhereClause(commandStringBuilder, conditionOperations);
+
+ // MySQL supports RETURNING clause starting from version 8.0.21
+ if (_options.ServerVersion.Supports.Returning)
+ {
+ AppendReturningClause(commandStringBuilder, [], "1");
+ }
+
+ commandStringBuilder.AppendLine(SqlGenerationHelper.StatementTerminator);
+
+ return ResultSetMapping.LastInResultSet | ResultSetMapping.ResultSetWithRowsAffectedOnly;
+ }
+
protected override ResultSetMapping AppendSelectAffectedCountCommand(StringBuilder commandStringBuilder, string name, string schema, int commandPosition)
{
commandStringBuilder