Rails 3 добавляет имя базы данных в таблицу, включает в себя по умолчанию загружаемую нагрузку

Источники:

Мой проект движется к созданию нескольких баз данных (в настоящее время на одном сервере), и я хотел бы иметь возможность соединяться между этими базами данных. Для этого мне нужно добавить имя базы данных в префикс таблицы следующим образом:

class FirstBase < ActiveRecord::Base
    def self.table_name_prefix
        "DBNAME.t_"
    end

    establish_connection :firstdb
end

class User < FirstBase
    has_many :user_roles
end

class UserRole < FirstBase
    belongs_to :user
end

Добавление префикса имен таблиц, похоже, влияет на поведение по умолчанию, включенное в один и тот же запрос, даже в пределах одной базы данных. Рассмотрим User.includes(:user_roles).first

Без префикса имени таблицы:

Загрузка пользователя (67.1ms) SELECT t_users. * FROM t_users LIMIT 1 UserRole Load (84.5ms) SELECT t_user_roles. * FROM t_user_roles ГДЕ t_user_roles. user_id IN (1)

С префиксом имени таблицы:

SQL (76.8ms) SELECT DISTINCT DBNAME. t_users.id FROM DBNAME. t_users LEFT OUTER JOIN DBNAME. t_user_roles ВКЛ DBNAME. t_user_roles. user_id= DBNAME. t_users. id LIMIT 1

SQL (66.4ms) SELECT DBNAME. t_users. id AS t0_r0, DBNAME. t_users. email AS t0_r1, DBNAME. t_user_roles. id AS t1_r0, DBNAME. t_user_roles. user_id AS t1_r1 FROM DBNAME. t_users LEFT OUTER JOIN DBNAME. t_user_roles ВКЛ DBNAME. t_user_roles. user_id= DBNAME. t_users. id WHERE DBNAME. t_users. id IN (1)

Другими словами, поведение по умолчанию для входящих вызовов изменилось с предварительной загрузки на загружаемую нагрузку.

Кто-нибудь знает, почему меняется поведение по умолчанию? Должно быть что-то о добавлении имени базы данных, которое заставляет Rails думать, что мы должны загружать загрузку, но я не понимаю, почему. Я также удивлен, увидев это, так как считаю, что добавить имя базы данных не так уж и необычно. Я могу скорректировать это в нашей базе кода, изменив все включенные в preload, но я хотел бы понять, что здесь происходит. Есть ли способ изменить поведение по умолчанию?

Ответ 1

Проблема заключается в том, что table_name_prefix вводит период. Это смущает логику, которая пытается определить, должна ли она загружаться или загружаться. Это ошибка Rails 3 и была разрешена в Rails 4. Если вам нужно определенное поведение в Rails 3, вам нужно явно указать preload или eager_load, как вы указали в своем вопросе.

В ActiveRecord:: Relation, exec_queries вызывает eager_loading?, чтобы решить, нужна ли ему загрузка. Это вызывает references_eager_loaded_tables?, который использует tables_in_string, чтобы попытаться найти имена таблиц в SQL-запросе, которые не являются частью объединенных таблиц:

# ActiveRecord::Relation#references_eager_loaded_tables?
(tables_in_string(to_sql) - joined_tables).any?

Метод tables_in_string был ошибочным, поскольку он не всегда правильно разбирал SQL. Этот код можно использовать для просмотра того, что он считает именами таблиц в SQL-запросе:

relation = User.includes(:user_roles)
relation.send(:tables_in_string, relation.to_sql)

С префиксом имени таблицы DBNAME.t_ это даст ["DBNAME", "t_users"] как имена таблиц, что неверно. Он должен дать ["DBNAME.t_users"].

Аналогичная проблема была зафиксирована в изменении запроса ActiveRecord, когда точка/период находится в значении условия. Это привело к изменениям в ActiveRecord:: Relation, которые отошли от использования tables_in_string при принятии решения о предварительной загрузке или загрузке.