mirror of https://github.com/rails/rails
Merge pull request #35854 from boblail/fix-bug-with-insert_all-on-mysql
When skipping duplicates in bulk insert on MySQL, avoid assigning an AUTONUMBER column when not specified
This commit is contained in:
commit
652ce83a4d
|
@ -516,8 +516,8 @@ module ActiveRecord
|
|||
sql = +"INSERT #{insert.into} #{insert.values_list}"
|
||||
|
||||
if insert.skip_duplicates?
|
||||
any_column = quote_column_name(insert.model.columns.first.name)
|
||||
sql << " ON DUPLICATE KEY UPDATE #{any_column}=#{any_column}"
|
||||
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 "
|
||||
sql << insert.updatable_columns.map { |column| "#{column}=VALUES(#{column})" }.join(",")
|
||||
|
|
|
@ -111,7 +111,7 @@ module ActiveRecord
|
|||
class Builder
|
||||
attr_reader :model
|
||||
|
||||
delegate :skip_duplicates?, :update_duplicates?, to: :insert_all
|
||||
delegate :skip_duplicates?, :update_duplicates?, :keys, to: :insert_all
|
||||
|
||||
def initialize(insert_all)
|
||||
@insert_all, @model, @connection = insert_all, insert_all.model, insert_all.connection
|
||||
|
@ -122,7 +122,7 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def values_list
|
||||
types = extract_types_from_columns_on(model.table_name, keys: insert_all.keys)
|
||||
types = extract_types_from_columns_on(model.table_name, keys: keys)
|
||||
|
||||
values_list = insert_all.map_key_with_value do |key, value|
|
||||
bind = Relation::QueryAttribute.new(key, value, types[key])
|
||||
|
|
|
@ -104,6 +104,44 @@ class InsertAllTest < ActiveRecord::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_insert_all_with_skip_duplicates_and_autonumber_id_not_given
|
||||
skip unless supports_insert_on_duplicate_skip?
|
||||
|
||||
assert_difference "Book.count", 1 do
|
||||
# These two books are duplicates according to an index on %i[author_id name]
|
||||
# but their IDs are not specified so they will be assigned different IDs
|
||||
# by autonumber. We will get an exception from MySQL if we attempt to skip
|
||||
# one of these records by assigning its ID.
|
||||
Book.insert_all [
|
||||
{ author_id: 8, name: "Refactoring" },
|
||||
{ author_id: 8, name: "Refactoring" }
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
def test_insert_all_with_skip_duplicates_and_autonumber_id_given
|
||||
skip unless supports_insert_on_duplicate_skip?
|
||||
|
||||
assert_difference "Book.count", 1 do
|
||||
Book.insert_all [
|
||||
{ id: 200, author_id: 8, name: "Refactoring" },
|
||||
{ id: 201, author_id: 8, name: "Refactoring" }
|
||||
]
|
||||
end
|
||||
end
|
||||
|
||||
def test_skip_duplicates_strategy_does_not_secretly_upsert
|
||||
skip unless supports_insert_on_duplicate_skip?
|
||||
|
||||
book = Book.create!(author_id: 8, name: "Refactoring", format: "EXPECTED")
|
||||
|
||||
assert_no_difference "Book.count" do
|
||||
Book.insert(author_id: 8, name: "Refactoring", format: "UNEXPECTED")
|
||||
end
|
||||
|
||||
assert_equal "EXPECTED", book.reload.format
|
||||
end
|
||||
|
||||
def test_insert_all_will_raise_if_duplicates_are_skipped_only_for_a_certain_conflict_target
|
||||
skip unless supports_insert_on_duplicate_skip? && supports_insert_conflict_target?
|
||||
|
||||
|
|
Loading…
Reference in New Issue