From 6ca44086115d36d2d7ec490d51e1512d890b95f7 Mon Sep 17 00:00:00 2001 From: fatkodima Date: Thu, 21 Dec 2023 02:14:59 +0200 Subject: [PATCH] Fix `find_by` for finding by associations with composite primary keys --- activerecord/lib/active_record/core.rb | 29 ++++++++++++++++++++++---- activerecord/test/cases/finder_test.rb | 18 +++++++++++++++- 2 files changed, 42 insertions(+), 5 deletions(-) diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index 87e56b00fb3cf..625dd365513fb 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -275,10 +275,25 @@ def find_by(*args) # :nodoc: elsif reflection.belongs_to? && !reflection.polymorphic? key = reflection.join_foreign_key pkey = reflection.join_primary_key - value = value.public_send(pkey) if value.respond_to?(pkey) + + if pkey.is_a?(Array) + if pkey.all? { |attribute| value.respond_to?(attribute) } + value = pkey.map do |attribute| + if attribute == "id" + value.id_value + else + value.public_send(attribute) + end + end + composite_primary_key = true + end + else + value = value.public_send(pkey) if value.respond_to?(pkey) + end end - if !columns_hash.key?(key) || StatementCache.unsupported_value?(value) + if !composite_primary_key && + (!columns_hash.key?(key) || StatementCache.unsupported_value?(value)) return super end @@ -405,12 +420,18 @@ def table_metadata def cached_find_by(keys, values) statement = cached_find_by_statement(keys) { |params| - wheres = keys.index_with { params.bind } + wheres = keys.index_with do |key| + if key.is_a?(Array) + [key.map { params.bind }] + else + params.bind + end + end where(wheres).limit(1) } begin - statement.execute(values, connection).first + statement.execute(values.flatten, connection).first rescue TypeError raise ActiveRecord::StatementInvalid end diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index 66be08220e452..5b55bb77d4346 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -34,7 +34,7 @@ class FinderTest < ActiveRecord::TestCase fixtures :companies, :topics, :entrants, :developers, :developers_projects, :posts, :comments, :accounts, :authors, :author_addresses, :customers, - :categories, :categorizations, :cars, :clothing_items, :cpk_books + :categories, :categorizations, :cars, :clothing_items, :cpk_books, :cpk_reviews def test_find_by_id_with_hash assert_nothing_raised do @@ -1875,6 +1875,22 @@ def test_finder_with_offset_string assert_equal books.map(&:id), Cpk::Book.order(author_id: :asc).find(books.map(&:id)).map(&:id) end + test "#find_by with composite primary key" do + book = cpk_books(:cpk_book_with_generated_pk) + assert_equal cpk_reviews(:first_book_review), Cpk::Review.find_by(book: book) + end + + test "#find_by with composite primary key and query caching" do + book = cpk_books(:cpk_book_with_generated_pk) + + Cpk::Review.cache do + assert_queries_count(1) do + Cpk::Review.find_by(book: book) + Cpk::Review.find_by(book: book) + end + end + end + private def table_with_custom_primary_key yield(Class.new(Toy) do