Skip to content

:primary_key option isn't respected on associations with custom scopes #54326

Open
@joshuay03

Description

@joshuay03

Reproduction:

# frozen_string_literal: true

require "bundler/inline"

gemfile(true) do
  source "https://rubygems.org"

  gem "rails", github: "rails/rails", branch: "main"

  gem "pg"
end

require "active_record/railtie"
require "minitest/autorun"

# This connection will do for database-independent bug reports.
ENV["DATABASE_URL"] = "postgresql:test"

class TestApp < Rails::Application
  config.load_defaults Rails::VERSION::STRING.to_f
  config.eager_load = false
  config.logger = Logger.new($stdout)
  config.secret_key_base = "secret_key_base"

  config.active_record.encryption.primary_key = "primary_key"
  config.active_record.encryption.deterministic_key = "deterministic_key"
  config.active_record.encryption.key_derivation_salt = "key_derivation_salt"
end
Rails.application.initialize!

ActiveRecord::Schema.define do
  create_table :posts_with_uuids, force: true, id: false do |t|
    t.uuid :uuid, primary_key: true, default: -> { "gen_random_uuid()" }
  end

  create_table :comments_with_uuids, force: true, id: false do |t|
    t.uuid :uuid, primary_key: true, default: -> { "gen_random_uuid()" }
    t.uuid :post_uuid
    t.boolean :special
  end

  create_table :posts_with_ids_and_uuids, force: true do |t|
    t.uuid :uuid, default: -> { "gen_random_uuid()" }
  end

  create_table :comments_with_ids_and_uuids, force: true do |t|
    t.uuid :uuid, default: -> { "gen_random_uuid()" }
    t.uuid :post_uuid
    t.boolean :special
  end
end

class PostWithUUID < ActiveRecord::Base
  self.table_name = :posts_with_uuids

  has_many :comments_with_uuids, class_name: :CommentWithUUID,
                                 foreign_key: :post_uuid,
                                 primary_key: :uuid,
                                 inverse_of: :post_with_uuid

  has_one :special_comment_with_uuid, -> { where(special: true) }, class_name: :CommentWithUUID,
                                                                   foreign_key: :post_uuid,
                                                                   primary_key: :uuid,
                                                                   inverse_of: :post_with_uuid
end

class CommentWithUUID < ActiveRecord::Base
  self.table_name = :comments_with_uuids

  belongs_to :post_with_uuid, class_name: :PostWithUUID, foreign_key: :post_uuid, primary_key: :uuid
end

class PostWithIdAndUUID < ActiveRecord::Base
  self.table_name = :posts_with_ids_and_uuids

  has_many :comments_with_ids_and_uuids, class_name: :CommentWithIdAndUUID,
                                         foreign_key: :post_uuid,
                                         primary_key: :uuid,
                                         inverse_of: :post_with_id_and_uuid

  has_one :special_comment_with_id_and_uuid, -> { where(special: true) }, class_name: :CommentWithIdAndUUID,
                                                                          foreign_key: :post_uuid,
                                                                          primary_key: :uuid,
                                                                          inverse_of: :post_with_id_and_uuid
end

class CommentWithIdAndUUID < ActiveRecord::Base
  self.table_name = :comments_with_ids_and_uuids

  belongs_to :post_with_id_and_uuid, class_name: :PostWithIdAndUUID, foreign_key: :post_uuid, primary_key: :uuid
end

class BugTest < ActiveSupport::TestCase
  def test_has_many_association_inverse_with_similar_association_pk_and_owner_pk
    post = PostWithUUID.create!
    post.comments_with_uuids.create!
    post.reload

    assert_same post, post.comments_with_uuids.first.post_with_uuid # ✅
  end

  def test_has_one_association_inverse_with_similar_association_pk_and_owner_pk
    post = PostWithUUID.create!
    post.comments_with_uuids.create!(special: true)
    post.reload

    assert_same post, post.special_comment_with_uuid.post_with_uuid # ✅
  end

  def test_has_many_association_inverse_with_different_association_pk_and_owner_pk
    post = PostWithIdAndUUID.create!
    post.comments_with_ids_and_uuids.create!
    post.reload

    assert_same post, post.comments_with_ids_and_uuids.first.post_with_id_and_uuid # ❌
  end

  def test_has_one_association_inverse_with_different_association_pk_and_owner_pk
    post = PostWithIdAndUUID.create!
    post.comments_with_ids_and_uuids.create!(special: true)
    post.reload

    assert_same post, post.special_comment_with_id_and_uuid.post_with_id_and_uuid # ❌
  end
end

Tested this from 7.1 to main and it consistently fails.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions