In looking at (and trying to use) a has_one association, I noticed
that the eager loading code for has one associations doesn't respect
the "limit one" per record, and could potentially pull back millions
of records (though there is code to make sure only one object per
association is actually turned converted to an ActiveRecord object).

Since I always use Postgresql with my Rails projects nowadays, I
naturally assume that a subselect is the way to go here, but I don't
know where sqllite is with subselects. I know MySQL now uses them, but
I don't know if Rails is keeping to some kind of lowest common
database denominator. If not, I think it might be worth it to try to
add some logic to
ActiveRecord::AssociationPreload#find_associated_records, but I don't
want to get into a whole testing, branching thing if this is too
database-specific.

The code I'm envisioning would be something like this:

<code>
      def min_or_max_from_order(reflection)
        if reflection.nil? || reflection.options[:order].blank?
          return 'MAX'
        end
        case (reflection.options[:order].split.compact.last.upcase
rescue 'ASC')
        when 'DESC'
          'MAX'
        else
          'MIN'
        end
      end

      def order_column_from_reflection(reflection)
        if reflection.nil? || reflection.options[:order].blank?
          return "id"
        end
        reflection.options[:order].split.compact.first.split('.').last
      end


      def find_associated_records(ids, reflection, preload_options)
        options = reflection.options
        table_name = reflection.klass.quoted_table_name

        if interface = reflection.options[:as]
          conditions = "#{reflection.klass.quoted_table_name}.#
{connection.quote_column_name "#{interface}_id"} #{in_or_equals_for_ids
(ids)} and #{reflection.klass.quoted_table_name}.#
{connection.quote_column_name "#{interface}_type"} = '#
{self.base_class.sti_name}'"
        else
          foreign_key = reflection.primary_key_name
          conditions = "#{reflection.klass.quoted_table_name}.#
{foreign_key} #{in_or_equals_for_ids(ids)}"
        end

        if reflection.macro == :has_one
          if reflection.options[:order]
            # we'll try subselects here
            select = " #{reflection.klass.quoted_table_name}.#
{foreign_key} as __original_key, #{table_name}.* "
            conditions <<
              " and ( #{reflection.klass.quoted_table_name}.#
{order_column_from_reflection(reflection)} =
                      (select #{min_or_max_from_order(reflection)}
(\"subselect_table1\".#{order_column_from_reflection(reflection)})
                         from #{reflection.klass.quoted_table_name} as
\"subselect_table1\"
                        where \"subselect_table1\".#{foreign_key} = #
{reflection.klass.quoted_table_name}.#{foreign_key} "
            conditions << append_conditions if reflection.options
[:conditions]
            conditions << " ) ) "
          end
        end

        conditions << append_conditions(reflection, preload_options)

        reflection.klass.with_exclusive_scope do
          reflection.klass.find(:all,
                              :select => (preload_options[:select] ||
options[:select] || "#{table_name}.*"),
                              :include => preload_options[:include] ||
options[:include],
                              :conditions => [conditions, ids],
                              :joins => options[:joins],
                              :group => preload_options[:group] ||
options[:group],
                              :order => preload_options[:order] ||
options[:order])
        end
      end
</code>


Clearly this wouldn't solve all cases, and some restrictions would
have to be laid out in the docs, especially about valid "order by"
phrasing, and resolving table name issues in adding the reflection
conditions to the subselect conditions...

But anyone have any thoughts about this?

--

You received this message because you are subscribed to the Google Groups "Ruby 
on Rails: Core" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/rubyonrails-core?hl=en.


Reply via email to