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