`ActiveRecord#joins` can't join delegated table if the model is defined with delegated_type method.

Title ActiveRecord#joins can’t join delegated table if the model is defined with delegated_type method.

summary ActiveRecord#joins says error messages if I execute INNER JOIN delegated table with the symbol which is association method’s name of the table.

environment

  • Ruby: ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [aarch64-linux]
  • Rails: 7.0.4
  • activerecord 7.0.4

Steps to reproduce

  1. Create DelegatedType-ed table and models in your Rails App.
class Chat::Message < ApplicationRecord
  delegated_type :messageable, types: %w[
                                 Chat::NormalMessage
                                 Chat::NotifyMessage
                               ],
                               dependent: :destroy
  ...
end

class Chat::NormalMessage < ApplicationRecord
  included do
    has_one :message, as: :messageable, touch: true, dependent: :destroy
  end
end

  1. Call joins method with a symbol of delegated table
irb(main):006:0> Chat::Message.last(2).first.chat_normal_message
  Chat::Message Load (9.5ms)  SELECT `chat_messages`.* FROM `chat_messages` ORDER BY `chat_messages`.`id` DESC LIMIT 2
  Chat::NormalMessage Load (6.8ms)  SELECT `chat_normal_messages`.* FROM `chat_normal_messages` WHERE `chat_normal_messages`.`id` = 449 LIMIT 1
=> #<Chat::NormalMessage:0x0000ffffac1dfa40 id: 449, body: "test", created_at: Thu, 07 Sep 2023 13:34:48.000000000 JST +09:00, updated_at: Thu, 07 Sep 2023 13:34:48.000000000 JST +09:00>
irb(main):011:0> Chat::Message.joins(:chat_normal_message)

actual result

Rails console says ActiveRecord::ConfigurationError.

irb(main):011:0> Chat::Message.joins(:chat_normal_message)
An error occurred when inspecting the object: #<ActiveRecord::ConfigurationError: Can't join 'Chat::Message' to association named 'chat_normal_message'; perhaps you misspelled it?>
Result of Kernel#inspect: #<Chat::Message::ActiveRecord_Relation:0x0000ffffac06ce10 @klass=Chat::Message(id: integer, room_id: integer, property_bulk_suggestion_id: integer, messageable_id: integer, messageable_type: string, sender_type_id: integer, staff_id: integer, status_id: integer, created_at: datetime, updated_at: datetime), @table=#<Arel::Table:0x0000ffffae925be0 @name="chat_messages", @klass=Chat::Message(id: integer, room_id: integer, test_id: integer, messageable_id: integer, messageable_type: string, status_id: integer, created_at: datetime, updated_at: datetime), @type_caster=#<ActiveRecord::TypeCaster::Map:0x0000ffffae924f60 ...

expected result

Got an array of ActiveRecord instances with INNER JOIN delegated table.

irb(main):024:0> Chat::Message.joins(:chat_normal_message)
  Chat::Message Load (23.7ms)  SELECT `chat_messages`.* FROM `chat_messages` INNER JOIN chat_normal_messages ON chat_messages.messageable_type = "Chat::NormalMessage" AND chat_messages.messageable_id = chat_normal_messages.id
=>
[#<Chat::Message:0x0000ffffac12b1a8
  id: 1,
...]

additional information

I’ve confirmed that INNER JOIN is effective if I pass a raw SQL statements as a parameter of the joins method.

irb:1:0*  join_chat_normal_messages = 'INNER JOIN chat_normal_messages ' \
irb:2:0*  'ON ' \
irb:3:0*  'chat_messages.messageable_type = "Chat::NormalMessage" ' \
irb:4:0>  'AND chat_messages.messageable_id = chat_normal_messages.id'
=> "INNER JOIN chat_normal_messages ON chat_messages.messageable_type = \"Chat::NormalMessage\" AND chat_messages.messageable_id = chat_normal_messages.id"
irb:5:0> Chat::Message.joins(join_chat_normal_messages)
  Chat::Message Load (21.0ms)  SELECT `chat_messages`.* FROM `chat_messages` INNER JOIN chat_normal_messages ON chat_messages.messageable_type = "Chat::NormalMessage" AND chat_messages.messageable_id = chat_normal_messages.id
=>
[#<Chat::Message:0x0000ffffac9ef4b0
  id: 1,
...]