When to-many associations are eagerly loaded, a "base" object and its associated objects can be represented by an unpredictable number of rows in the result set of the query. As a consequence, retrieving a certain range of objects including associations cannot be achieved by using OFFSET and LIMIT as they refer to rows in the result set, not objects.
In order to enable :offset and :limit in cases like this, the current code splits the task of retrieving objects. In a first query it retrieves a list of ids of objects, properly offset and limited. Then, in a second query, these ids are used in a condition to restrict the returned rows from which objects are instantiated. The code looks like this (slightly reformatted).
def add_limited_ids_condition!(sql, options, join_dependency) unless (id_list = select_limited_ids_list(options, join_dependency)).empty? sql << "#{condition_word(sql)} #{table_name}.#{primary_key} IN (#{id_list}) " else throw :invalid_query end end
def select_limited_ids_list(options, join_dependency) connection.select_all( construct_finder_sql_for_association_limiting(options, join_dependency), "#{name} Load IDs For Limited Eager Loading" ).collect { |row| connection.quote(row[primary_key]) }.join(", ") end
The advantage of this approach is that it works even on less capable DBMS, specifically those that don't support subselects. The disadvantage is that on DBMS that do support subselects it incurs an unnecessary roundtrip to the database and robs the optimizer of a chance to do its job.
A solution is rather simple, or so it seems to me. Apart from naming issues, it would look like this, for databases that support subselects
def select_limited_ids_list(options, join_dependency) construct_finder_sql_for_association_limiting(options, join_dependency) end
Instead of eagerly executing a query for the ids, the requisite SQL is simply inserted in the main query to be executed as a subselect. I've applied this change and except for a test that explicitly checks select_limited_ids_list all test still pass on PostgreSQL.
Obviously, this change should not be applied unconditionally. Rather, I think it should be conditional on the capabilities of the used DBMS. Information, which in turn could be supplied by the respective adapters.
Michael