diff options
Diffstat (limited to 'activerecord/lib/active_record/associations.rb')
-rwxr-xr-x | activerecord/lib/active_record/associations.rb | 52 |
1 files changed, 32 insertions, 20 deletions
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 157716a477..4ed0cb70e9 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -516,14 +516,14 @@ module ActiveRecord # # Since only one table is loaded at a time, conditions or orders cannot reference tables other than the main one. If this is the case # Active Record falls back to the previously used LEFT OUTER JOIN based strategy. For example - # + # # Post.find(:all, :include => [ :author, :comments ], :conditions => ['comments.approved = ?', true]) # # will result in a single SQL query with joins along the lines of: <tt>LEFT OUTER JOIN comments ON comments.post_id = posts.id</tt> and # <tt>LEFT OUTER JOIN authors ON authors.id = posts.author_id</tt>. Note that using conditions like this can have unintended consequences. # In the above example posts with no approved comments are not returned at all, because the conditions apply to the SQL statement as a whole # and not just to the association. You must disambiguate column references for this fallback to happen, for example - # <tt>:order => "author.name DESC"</tt> will work but <tt>:order => "name DESC"</tt> will not. + # <tt>:order => "author.name DESC"</tt> will work but <tt>:order => "name DESC"</tt> will not. # # If you do want eagerload only some members of an association it is usually more natural to <tt>:include</tt> an association # which has conditions defined on it: @@ -557,10 +557,10 @@ module ActiveRecord # # Address.find(:all, :include => :addressable) # - # will execute one query to load the addresses and load the addressables with one query per addressable type. + # will execute one query to load the addresses and load the addressables with one query per addressable type. # For example if all the addressables are either of class Person or Company then a total of 3 queries will be executed. The list of # addressable types to load is determined on the back of the addresses loaded. This is not supported if Active Record has to fallback - # to the previous implementation of eager loading and will raise ActiveRecord::EagerLoadPolymorphicError. The reason is that the parent + # to the previous implementation of eager loading and will raise ActiveRecord::EagerLoadPolymorphicError. The reason is that the parent # model's type is a column value so its corresponding table name cannot be put in the +FROM+/+JOIN+ clauses of that query. # # == Table Aliasing @@ -875,7 +875,7 @@ module ActiveRecord # but not include the joined columns. Do not forget to include the primary and foreign keys, otherwise it will raise an error. # [:through] # Specifies a Join Model through which to perform the query. Options for <tt>:class_name</tt> and <tt>:foreign_key</tt> - # are ignored, as the association uses the source reflection. You can only use a <tt>:through</tt> query through a + # are ignored, as the association uses the source reflection. You can only use a <tt>:through</tt> query through a # <tt>has_one</tt> or <tt>belongs_to</tt> association on the join model. # [:source] # Specifies the source association name used by <tt>has_one :through</tt> queries. Only use it if the name cannot be @@ -1129,8 +1129,8 @@ module ActiveRecord # the association will use "project_id" as the default <tt>:association_foreign_key</tt>. # [:conditions] # Specify the conditions that the associated object must meet in order to be included as a +WHERE+ - # SQL fragment, such as <tt>authorized = 1</tt>. Record creations from the association are scoped if a hash is used. - # <tt>has_many :posts, :conditions => {:published => true}</tt> will create published posts with <tt>@blog.posts.create</tt> + # SQL fragment, such as <tt>authorized = 1</tt>. Record creations from the association are scoped if a hash is used. + # <tt>has_many :posts, :conditions => {:published => true}</tt> will create published posts with <tt>@blog.posts.create</tt> # or <tt>@blog.posts.build</tt>. # [:order] # Specify the order in which the associated objects are returned as an <tt>ORDER BY</tt> SQL fragment, @@ -1341,12 +1341,12 @@ module ActiveRecord "#{reflection.class_name}.send(:attr_readonly,\"#{cache_column}\".intern) if defined?(#{reflection.class_name}) && #{reflection.class_name}.respond_to?(:attr_readonly)" ) end - + def add_touch_callbacks(reflection, touch_attribute) method_name = "belongs_to_touch_after_save_or_destroy_for_#{reflection.name}".to_sym define_method(method_name) do association = send(reflection.name) - + if touch_attribute == true association.touch unless association.nil? else @@ -1558,7 +1558,7 @@ module ActiveRecord options[:extend] = create_extension_modules(association_id, extension, options[:extend]) reflection = create_reflection(:has_and_belongs_to_many, association_id, options, self) - + if reflection.association_foreign_key == reflection.primary_key_name raise HasAndBelongsToManyAssociationForeignKeyNeeded.new(reflection) end @@ -1590,19 +1590,23 @@ module ActiveRecord def construct_finder_sql_with_included_associations(options, join_dependency) scope = scope(:find) - sql = "SELECT #{column_aliases(join_dependency)} FROM #{(scope && scope[:from]) || options[:from] || quoted_table_name} " - sql << join_dependency.join_associations.collect{|join| join.association_join }.join - add_joins!(sql, options[:joins], scope) - add_conditions!(sql, options[:conditions], scope) - add_limited_ids_condition!(sql, options, join_dependency) if !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit]) + joins = join_dependency.join_associations.collect{|join| join.association_join }.join + joins << construct_join(options[:joins], scope) - add_group!(sql, options[:group], options[:having], scope) - add_order!(sql, options[:order], scope) - add_limit!(sql, options, scope) if using_limitable_reflections?(join_dependency.reflections) - add_lock!(sql, options, scope) + conditions = construct_conditions(options[:conditions], scope) || '' + conditions << construct_limited_ids_condition(conditions, options, join_dependency) if !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit]) - return sanitize_sql(sql) + arel = arel_table((scope && scope[:from]) || options[:from] || table_name). + join(joins). + where(conditions). + project(column_aliases(join_dependency)). + group(construct_group(options[:group], options[:having], scope)). + order(construct_order(options[:order], scope)) + + arel = arel.take(construct_limit(options, scope)) if using_limitable_reflections?(join_dependency.reflections) + + return sanitize_sql(arel.to_sql) end def add_limited_ids_condition!(sql, options, join_dependency) @@ -1613,6 +1617,14 @@ module ActiveRecord end end + def construct_limited_ids_condition(where, options, join_dependency) + unless (id_list = select_limited_ids_list(options, join_dependency)).empty? + "#{where.blank? ? '' : ' AND '} #{connection.quote_table_name table_name}.#{primary_key} IN (#{id_list}) " + else + throw :invalid_query + end + end + def select_limited_ids_list(options, join_dependency) pk = columns_hash[primary_key] |