Fix upsert warning for MySQL

This commit is contained in:
fatkodima 2024-03-07 11:59:26 +02:00
parent 2fa3294618
commit 0e7826e10e
2 changed files with 47 additions and 11 deletions

@ -639,18 +639,37 @@ def default_index_type?(index) # :nodoc:
end
def build_insert_sql(insert) # :nodoc:
sql = +"INSERT #{insert.into} #{insert.values_list}"
no_op_column = quote_column_name(insert.keys.first)
if insert.skip_duplicates?
no_op_column = quote_column_name(insert.keys.first)
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
elsif insert.update_duplicates?
sql << " ON DUPLICATE KEY UPDATE "
if insert.raw_update_sql?
sql << insert.raw_update_sql
else
sql << insert.touch_model_timestamps_unless { |column| "#{column}<=>VALUES(#{column})" }
sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
# Avoid MySQL 8.0 deprecation warning, see https://dev.mysql.com/worklog/task/?id=13325.
if !mariadb? && database_version >= "8.0.0"
values_alias = quote_table_name("#{insert.model.table_name}_values")
sql = +"INSERT #{insert.into} #{insert.values_list} AS #{values_alias}"
if insert.skip_duplicates?
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{values_alias}.#{no_op_column}"
elsif insert.update_duplicates?
sql << " ON DUPLICATE KEY UPDATE "
if insert.raw_update_sql?
sql << insert.raw_update_sql
else
sql << insert.touch_model_timestamps_unless { |column| "#{insert.model.quoted_table_name}.#{column}<=>#{values_alias}.#{column}" }
sql << insert.updatable_columns.map { |column| "#{column}=#{values_alias}.#{column}" }.join(",")
end
end
else
sql = +"INSERT #{insert.into} #{insert.values_list}"
if insert.skip_duplicates?
sql << " ON DUPLICATE KEY UPDATE #{no_op_column}=#{no_op_column}"
elsif insert.update_duplicates?
sql << " ON DUPLICATE KEY UPDATE "
if insert.raw_update_sql?
sql << insert.raw_update_sql
else
sql << insert.touch_model_timestamps_unless { |column| "#{column}<=>VALUES(#{column})" }
sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
end
end
end

@ -20,6 +20,7 @@ class InsertAllTest < ActiveRecord::TestCase
def setup
Arel::Table.engine = nil # should not rely on the global Arel::Table.engine
@original_db_warnings_action = :ignore
end
def teardown
@ -336,6 +337,22 @@ def test_upsert_logs_message_including_model_name
end
end
def test_upsert_and_db_warnings
skip unless supports_insert_on_duplicate_update?
begin
with_db_warnings_action(:raise) do
assert_nothing_raised do
Book.upsert({ id: 1001, name: "Remote", author_id: 1 })
end
end
ensure
# We need to explicitly remove the record, because `with_db_warnings_action`
# prevents the wrapping transaction to be rolled back.
Book.delete(1001)
end
end
def test_upsert_all_logs_message_including_model_name
skip unless supports_insert_on_duplicate_update?