Skip to content

HasManyThroughAssociation skips custom attribute serialization when target does not define the has_many inverse association #54418

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
such opened this issue Feb 1, 2025 · 1 comment
Labels

Comments

@such
Copy link

such commented Feb 1, 2025

Steps to reproduce

require 'bundler/inline'

gemfile(true) do
  source 'https://rubygems.org'
  gem 'activerecord', '~> 8.0'
  gem 'sqlite3'
end

require 'active_record'
require 'logger'
require 'minitest/autorun'

# Log SQL to stdout for debugging
ActiveRecord::Base.logger = Logger.new($stdout)

# Establish an in-memory SQLite DB
ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')

# -- Minimal schema --
ActiveRecord::Schema.define do
  create_table :authors, force: true do |t|
    t.binary :author_id
  end

  create_table :posts, force: true

  create_table :comments, force: true do |t|
    t.binary :author_id
    t.references :post, null: false
  end
end

class CustomBinaryType < ActiveRecord::Type::Binary
  def deserialize(value)
    value
  end

  def serialize(_value)
    'test'
  end
end

# Register the custom type
ActiveRecord::Type.register(:custom_binary, CustomBinaryType)

# Models
class Author < ActiveRecord::Base
  attribute :author_id, :custom_binary

  has_many :comments, primary_key: :author_id
  has_many :posts, through: :comments
end

class Post < ActiveRecord::Base
  # Uncomment to fix issue:
  # has_many :comments
end

class Comment < ActiveRecord::Base
  attribute :author_id, :custom_binary
  belongs_to :post
end

class BugTest < ActiveSupport::TestCase
  def test_association_stuff
    author_id = '1234'

    author = Author.create!(author_id: author_id)
    post = Post.create!
    Comment.create!(author_id: author_id, post: post)

    # puts "\n=== Generate the query for the has_many association ==="
    puts author.comments.to_sql
    assert_equal 1, author.comments.count

    # puts "\n=== Generate the query for the has_many :through association ==="
    puts author.posts.to_sql
    assert_equal 1, author.posts.count
  end
end

Expected behavior

We should be able to list the posts linked to the comment author.

Actual behavior

ActiveRecord generates the following SQL:

SELECT "posts".* FROM "posts" INNER JOIN "comments" ON "posts"."id" = "comments"."post_id" WHERE "comments"."author_id" = x'74657374'

we would expect:

SELECT "posts".* FROM "posts" INNER JOIN "comments" ON "posts"."id" = "comments"."post_id" WHERE "comments"."author_id" = 'test'

(74657374 is the ASCII encoded binary representation of "test")

It works correctly for the has_many association (author -> comments).

Declaring the comments association in Post fixes the issue.

I'm far from being familiar with ActiveRecord but while debugging, this line of code in table_metadata.rb felt sketchy:

reflection = klass._reflect_on_association(table_name) || klass._reflect_on_association(table_name.singularize    )

It looks this is where we assume there is an association named like the table. This leads to initializing a TypeCaster::Connection that is not aware of the custom attribute.

System configuration

Rails version: 8.0.1

Ruby version: 3.4.1

@rails-bot
Copy link

rails-bot bot commented May 2, 2025

This issue has been automatically marked as stale because it has not been commented on for at least three months.
The resources of the Rails team are limited, and so we are asking for your help.
If you can still reproduce this error on the 8-0-stable branch or on main, please reply with all of the information you have about it in order to keep the issue open.
Thank you for all your contributions.

@rails-bot rails-bot bot added the stale label May 2, 2025
@rails-bot rails-bot bot closed this as completed May 9, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant