From 2ea5c24085e39efb6c1bc6e7062bcc5f496d1fa6 Mon Sep 17 00:00:00 2001 From: Kasper Timm Hansen Date: Sat, 9 Mar 2019 01:15:17 +0100 Subject: Bulk Insert: Reuse indexes for unique_by I found `:unique_by` with `:columns` and `:where` inside it tough to grasp. The documentation only mentioned indexes and partial indexes. So why duplicate a model's indexes in an insert_all/upsert_all call when we can just look it up? This has the added benefit of raising if no index is found, such that people can't insert thousands of records without relying on an index of some form. --- activerecord/test/cases/insert_all_test.rb | 36 +++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) (limited to 'activerecord/test') diff --git a/activerecord/test/cases/insert_all_test.rb b/activerecord/test/cases/insert_all_test.rb index a3e920969d..0818d7c1ab 100644 --- a/activerecord/test/cases/insert_all_test.rb +++ b/activerecord/test/cases/insert_all_test.rb @@ -95,7 +95,37 @@ class InsertAllTest < ActiveRecord::TestCase assert_raise ActiveRecord::RecordNotUnique do Book.insert_all [{ id: 1, name: "Agile Web Development with Rails" }], - unique_by: { columns: %i{author_id name} } + unique_by: :index_books_on_author_id_and_name + end + end + + def test_insert_all_and_upsert_all_with_index_finding_options + skip unless supports_insert_conflict_target? + + assert_difference "Book.count", +3 do + Book.insert_all [{ name: "Rework", author_id: 1 }], unique_by: :isbn + Book.insert_all [{ name: "Remote", author_id: 1 }], unique_by: %i( author_id name ) + Book.insert_all [{ name: "Renote", author_id: 1 }], unique_by: :index_books_on_isbn + end + + assert_raise ActiveRecord::RecordNotUnique do + Book.upsert_all [{ name: "Rework", author_id: 1 }], unique_by: :isbn + end + end + + def test_insert_all_and_upsert_all_raises_when_index_is_missing + skip unless supports_insert_conflict_target? + + [ :cats, %i( author_id isbn ), :author_id ].each do |missing_or_non_unique_by| + error = assert_raises ArgumentError do + Book.insert_all [{ name: "Rework", author_id: 1 }], unique_by: missing_or_non_unique_by + end + assert_match "No unique index", error.message + + error = assert_raises ArgumentError do + Book.upsert_all [{ name: "Rework", author_id: 1 }], unique_by: missing_or_non_unique_by + end + assert_match "No unique index", error.message end end @@ -120,7 +150,7 @@ class InsertAllTest < ActiveRecord::TestCase Book.upsert_all [{ id: 101, name: "Perelandra", author_id: 7 }] Book.upsert_all [{ id: 103, name: "Perelandra", author_id: 7, isbn: "1974522598" }], - unique_by: { columns: %i{author_id name} } + unique_by: :index_books_on_author_id_and_name book = Book.find_by(name: "Perelandra") assert_equal 101, book.id, "Should not have updated the ID" @@ -132,7 +162,7 @@ class InsertAllTest < ActiveRecord::TestCase Book.upsert_all [{ name: "Out of the Silent Planet", author_id: 7, isbn: "1974522598", published_on: Date.new(1938, 4, 1) }] Book.upsert_all [{ name: "Perelandra", author_id: 7, isbn: "1974522598" }], - unique_by: { columns: %w[ isbn ], where: "published_on IS NOT NULL" } + unique_by: :index_books_on_isbn assert_equal ["Out of the Silent Planet", "Perelandra"], Book.where(isbn: "1974522598").order(:name).pluck(:name) end -- cgit v1.2.3