From 45c8f3872e1e4fcf9ff14245d91d420af75334cb Mon Sep 17 00:00:00 2001 From: hikari Date: Wed, 1 May 2024 00:13:17 +0900 Subject: [PATCH] fix: MySQLUpdateSqlGenerator https://bugs.mysql.com/bug.php?id=113443 --- EFCore/src/Update/MySQLUpdateSqlGenerator.cs | 241 ++----------------- 1 file changed, 24 insertions(+), 217 deletions(-) diff --git a/EFCore/src/Update/MySQLUpdateSqlGenerator.cs b/EFCore/src/Update/MySQLUpdateSqlGenerator.cs index 7350729a8..1650d65c8 100644 --- a/EFCore/src/Update/MySQLUpdateSqlGenerator.cs +++ b/EFCore/src/Update/MySQLUpdateSqlGenerator.cs @@ -50,7 +50,13 @@ namespace MySql.EntityFrameworkCore /// This service cannot depend on services registered as . /// /// - internal class MySQLUpdateSqlGenerator : UpdateSqlGenerator, IMySQLUpdateSqlGenerator + internal class MySQLUpdateSqlGenerator : +#if NET6_0 + UpdateSqlGenerator, +#elif NET7_0 || NET8_0 + UpdateAndSelectSqlGenerator, +#endif + IMySQLUpdateSqlGenerator { public MySQLUpdateSqlGenerator([NotNull] UpdateSqlGeneratorDependencies dependencies) : base(dependencies) @@ -250,7 +256,7 @@ internal enum ResultsGrouping || !o.IsRead || o.Property?.GetValueGenerationStrategy(table) == MySQLValueGenerationStrategy.IdentityColumn)) { - return AppendInsertOperation(commandStringBuilder, firstCommand, commandPosition, false, out requiresTransaction); + return AppendInsertOperation(commandStringBuilder, firstCommand, commandPosition, out requiresTransaction); } var readOperations = firstCommand.ColumnModifications.Where(o => o.IsRead).ToList(); @@ -291,7 +297,7 @@ internal enum ResultsGrouping foreach (var modification in modificationCommands) { - AppendInsertOperation(commandStringBuilder, modification, commandPosition, false, out requiresTransaction); + AppendInsertOperation(commandStringBuilder, modification, commandPosition, out requiresTransaction); } return ResultSetMapping.LastInResultSet; @@ -325,102 +331,6 @@ internal enum ResultsGrouping return ResultSetMapping.NoResults; } - public override ResultSetMapping AppendInsertOperation( - StringBuilder commandStringBuilder, - IReadOnlyModificationCommand command, - int commandPosition, - out bool requiresTransaction) - => AppendInsertOperation(commandStringBuilder, command, commandPosition, overridingSystemValue: false, out requiresTransaction); - - public virtual ResultSetMapping AppendInsertOperation( - StringBuilder commandStringBuilder, - IReadOnlyModificationCommand command, - int commandPosition, - bool overridingSystemValue, - out bool requiresTransaction) - { - requiresTransaction = false; - 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(); - - AppendInsertCommand(commandStringBuilder, name, schema, writeOperations, readOperations, overridingSystemValue); - - if (readOperations.Count > 0) - { - var keyOperations = operations.Where(o => o.IsKey).ToList(); - return AppendSelectAffectedCommand(commandStringBuilder, name, schema, readOperations, keyOperations); - } - - return readOperations.Count > 0 ? ResultSetMapping.LastInResultSet : ResultSetMapping.NoResults; - } - - protected virtual void AppendInsertCommand( - StringBuilder commandStringBuilder, - string name, - string? schema, - IReadOnlyList writeOperations, - IReadOnlyList readOperations, - bool overridingSystemValue) - { - AppendInsertCommandHeader(commandStringBuilder, name, schema, writeOperations); - AppendValuesHeader(commandStringBuilder, writeOperations); - AppendValues(commandStringBuilder, name, schema, writeOperations); - commandStringBuilder.AppendLine(SqlGenerationHelper.StatementTerminator); - } - - public override ResultSetMapping AppendUpdateOperation( - StringBuilder commandStringBuilder, - IReadOnlyModificationCommand command, - int commandPosition, - out bool requiresTransaction) - { - Check.NotNull(commandStringBuilder, nameof(commandStringBuilder)); - Check.NotNull(command, nameof(command)); - - requiresTransaction = false; - 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(); - - AppendUpdateCommand(commandStringBuilder, name, schema, writeOperations, readOperations, conditionOperations); - - if (readOperations.Count > 0) - { - var keyOperations = operations.Where(o => o.IsKey).ToList(); - - return AppendSelectAffectedCommand(commandStringBuilder, name, schema, readOperations, keyOperations); - } - - return ResultSetMapping.NoResults; - } - - public override ResultSetMapping AppendDeleteOperation( - StringBuilder commandStringBuilder, - IReadOnlyModificationCommand command, - int commandPosition, - out bool requiresTransaction) - { - // The default implementation adds RETURNING 1 to do concurrency check (was the row actually deleted), but in PostgreSQL we check - // the per-statement row-affected value exposed by Npgsql in the batch; so no need for RETURNING 1. - var name = command.TableName; - var schema = command.Schema; - var conditionOperations = command.ColumnModifications.Where(o => o.IsCondition).ToList(); - - requiresTransaction = false; - - AppendDeleteCommand(commandStringBuilder, name, schema, Array.Empty(), conditionOperations); - - return ResultSetMapping.NoResults; - } - protected override void AppendValues( [NotNull] StringBuilder commandStringBuilder, [NotNull] string name, @@ -435,123 +345,7 @@ internal enum ResultsGrouping } } - /// - /// Appends a SQL command for selecting affected data. - /// - /// The builder to which the SQL should be appended. - /// The name of the table. - /// The table schema, or to use the default schema. - /// The operations representing the data to be read. - /// The operations used to generate the WHERE clause for the select. - /// The for this command. - private ResultSetMapping AppendSelectAffectedCommand( - StringBuilder commandStringBuilder, - string name, - string? schema, - IReadOnlyList readOperations, - IReadOnlyList conditionOperations) - { - Check.NotNull(commandStringBuilder, nameof(commandStringBuilder)); - Check.NotEmpty(name, nameof(name)); - Check.NotNull(readOperations, nameof(readOperations)); - Check.NotNull(conditionOperations, nameof(conditionOperations)); - - AppendSelectCommandHeader(commandStringBuilder, readOperations); - AppendFromClause(commandStringBuilder, name, schema); - AppendWhereAffectedClause(commandStringBuilder, conditionOperations); - commandStringBuilder.AppendLine(SqlGenerationHelper.StatementTerminator) - .AppendLine(); - - return ResultSetMapping.LastInResultSet; - } - - /// - /// Appends a SQL fragment for starting a SELECT. - /// - /// The builder to which the SQL should be appended. - /// The operations representing the data to be read. - private void AppendSelectCommandHeader( - StringBuilder commandStringBuilder, - IReadOnlyList operations) - { - Check.NotNull(commandStringBuilder, nameof(commandStringBuilder)); - Check.NotNull(operations, nameof(operations)); - - commandStringBuilder - .Append("SELECT ") - .AppendJoin( - operations, - SqlGenerationHelper, - (sb, o, helper) => helper.DelimitIdentifier(sb, o.ColumnName)); - } - - /// - /// Appends a SQL fragment for starting a FROM clause. - /// - /// The builder to which the SQL should be appended. - /// The name of the table. - /// The table schema, or to use the default schema. - private void AppendFromClause( - StringBuilder commandStringBuilder, - string name, - string? schema) - { - Check.NotNull(commandStringBuilder, nameof(commandStringBuilder)); - Check.NotEmpty(name, nameof(name)); - - commandStringBuilder - .AppendLine() - .Append("FROM "); - - SqlGenerationHelper.DelimitIdentifier(commandStringBuilder, name, schema); - } - - /// - /// Appends a WHERE clause involving rows affected. - /// - /// The builder to which the SQL should be appended. - /// The operations from which to build the conditions. - protected virtual void AppendWhereAffectedClause( - StringBuilder commandStringBuilder, - IReadOnlyList operations) - { - Check.NotNull(commandStringBuilder, nameof(commandStringBuilder)); - Check.NotNull(operations, nameof(operations)); - - commandStringBuilder - .AppendLine() - .Append("WHERE "); - - AppendRowsAffectedWhereCondition(commandStringBuilder, 1); - - if (operations.Count > 0) - { - commandStringBuilder - .Append(" AND ") - .AppendJoin( - operations, (sb, v) => - { - if (v.IsKey) - { - if (!v.IsRead) - { - AppendWhereCondition(sb, v, v.UseOriginalValueParameter); - return true; - } - } - - if (IsIdentityOperation(v)) - { - AppendIdentityWhereCondition(sb, v); - return true; - } - - return false; - }, " AND "); - } - } - - private void AppendRowsAffectedWhereCondition(StringBuilder commandStringBuilder, int expectedRowsAffected) + protected override void AppendRowsAffectedWhereCondition(StringBuilder commandStringBuilder, int expectedRowsAffected) { Check.NotNull(commandStringBuilder, "commandStringBuilder"); commandStringBuilder @@ -566,12 +360,25 @@ private void AppendRowsAffectedWhereCondition(StringBuilder commandStringBuilder protected virtual bool IsIdentityOperation(IColumnModification modification) => modification.IsKey && modification.IsRead; - private void AppendIdentityWhereCondition(StringBuilder commandStringBuilder, IColumnModification columnModification) + protected override void AppendIdentityWhereCondition(StringBuilder commandStringBuilder, IColumnModification columnModification) { Check.NotNull(columnModification, "columnModification"); Check.NotNull(commandStringBuilder, "commandStringBuilder"); commandStringBuilder.AppendFormat("{0} = LAST_INSERT_ID()", SqlGenerationHelper.DelimitIdentifier(columnModification.ColumnName)); } + + protected override ResultSetMapping AppendSelectAffectedCountCommand(StringBuilder commandStringBuilder, string name, string? schemaName, int commandPosition) + { + Check.NotNull(commandStringBuilder, "commandStringBuilder"); + Check.NotEmpty(name, "name"); + + commandStringBuilder + .Append("SELECT ROW_COUNT()") + .Append(SqlGenerationHelper.StatementTerminator).AppendLine() + .AppendLine(); + + return ResultSetMapping.LastInResultSet; + } #endif