Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Changeset 8672

Show
Ignore:
Timestamp:
01/19/08 04:19:53 (4 months ago)
Author:
bitsweat
Message:

Introduce preload query strategy for eager :includes. Closes #9640.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/activerecord/CHANGELOG

    r8671 r8672  
    11*SVN* 
     2 
     3* Introduce preload query strategy for eager :includes.  #9640 [Frederick Cheung, Aleksey Kondratenko] 
    24 
    35* Support aggregations in finder conditions.  #10572 [Ryan Kinderman] 
  • trunk/activerecord/lib/active_record.rb

    r7768 r8672  
    4444require 'active_record/reflection' 
    4545require 'active_record/associations' 
     46require 'active_record/association_preload' 
    4647require 'active_record/aggregations' 
    4748require 'active_record/transactions' 
     
    6465  include ActiveRecord::Timestamp 
    6566  include ActiveRecord::Associations 
     67  include ActiveRecord::AssociationPreload 
    6668  include ActiveRecord::Aggregations 
    6769  include ActiveRecord::Transactions 
  • trunk/activerecord/lib/active_record/associations.rb

    r8562 r8672  
    13661366          order = options[:order] 
    13671367          return false unless order 
    1368           order.scan(/([\.\w]+)\.\w+/).flatten.any? do |order_table_name| 
     1368          order.to_s.scan(/([\.\w]+)\.\w+/).flatten.any? do |order_table_name| 
    13691369            order_table_name != table_name 
    13701370          end 
     1371        end 
     1372 
     1373        def references_eager_loaded_tables?(options) 
     1374          include_eager_order?(options) || include_eager_conditions?(options) 
    13711375        end 
    13721376 
  • trunk/activerecord/lib/active_record/base.rb

    r8671 r8672  
    12381238 
    12391239        def find_every(options) 
    1240           records = scoped?(:find, :include) || options[:include] ? 
    1241             find_with_associations(options) : 
    1242             find_by_sql(construct_finder_sql(options)) 
     1240          include_associations = merge_includes(scope(:find, :include), options[:include]) 
     1241 
     1242          if include_associations.any? && references_eager_loaded_tables?(options) 
     1243            records = find_with_associations(options) 
     1244          else 
     1245            records = find_by_sql(construct_finder_sql(options)) 
     1246            if include_associations.any? 
     1247              preload_associations(records, include_associations) 
     1248            end 
     1249          end 
    12431250 
    12441251          records.each { |record| record.readonly! } if options[:readonly] 
  • trunk/activerecord/test/cases/associations_test.rb

    r8661 r8672  
    2222           :computers 
    2323 
     24  def test_include_with_order_works 
     25    assert_nothing_raised {Account.find(:first, :order => 'id', :include => :firm)} 
     26    assert_nothing_raised {Account.find(:first, :order => :id, :include => :firm)} 
     27  end 
     28 
    2429  def test_bad_collection_keys 
    2530    assert_raise(ArgumentError, 'ActiveRecord should have barked on bad collection keys') do 
  • trunk/activerecord/test/cases/associations/eager_test.rb

    r8660 r8672  
    11require 'abstract_unit' 
    22require 'models/post' 
     3require 'models/tagging' 
    34require 'models/comment' 
    45require 'models/author' 
     
    1011class EagerAssociationTest < ActiveSupport::TestCase 
    1112  fixtures :posts, :comments, :authors, :categories, :categories_posts, 
    12             :companies, :accounts, :tags, :people, :readers 
     13            :companies, :accounts, :tags, :taggings, :people, :readers 
    1314 
    1415  def test_loading_with_one_association 
     
    5556    assert_equal 2, posts.first.categories.size 
    5657    assert posts.first.comments.include?(comments(:greetings)) 
     58  end 
     59 
     60  def test_duplicate_middle_objects 
     61    comments = Comment.find :all, :conditions => 'post_id = 1', :include => [:post => :author] 
     62    assert_no_queries do 
     63      comments.each {|comment| comment.post.author.name} 
     64    end 
    5765  end 
    5866 
     
    352360    assert_equal posts(:thinking, :sti_comments), Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title), posts.id', :limit => 2, :offset => 1) 
    353361    assert_equal posts(:sti_post_and_comments, :sti_comments), Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title) DESC, posts.id', :limit => 2, :offset => 1) 
     362  end 
     363 
     364  def test_preload_with_interpolation 
     365    assert_equal [comments(:greetings)], Post.find(posts(:welcome).id, :include => :comments_with_interpolated_conditions).comments_with_interpolated_conditions 
     366  end 
     367 
     368  def test_polymorphic_type_condition 
     369    post = Post.find(posts(:thinking).id, :include => :taggings) 
     370    assert post.taggings.include?(taggings(:thinking_general)) 
     371    post = SpecialPost.find(posts(:thinking).id, :include => :taggings) 
     372    assert post.taggings.include?(taggings(:thinking_general)) 
    354373  end 
    355374 
     
    406425  def test_preconfigured_includes_with_belongs_to 
    407426    author = posts(:welcome).author_with_posts 
    408     assert_equal 5, author.posts.size 
     427    assert_no_queries {assert_equal 5, author.posts.size} 
    409428  end 
    410429 
    411430  def test_preconfigured_includes_with_has_one 
    412431    comment = posts(:sti_comments).very_special_comment_with_post 
    413     assert_equal posts(:sti_comments), comment.post 
     432    assert_no_queries {assert_equal posts(:sti_comments), comment.post} 
    414433  end 
    415434 
     
    417436    posts = authors(:david).posts_with_comments 
    418437    one = posts.detect { |p| p.id == 1 } 
    419     assert_equal 5, posts.size 
    420     assert_equal 2, one.comments.size 
     438    assert_no_queries do 
     439      assert_equal 5, posts.size 
     440      assert_equal 2, one.comments.size 
     441    end 
    421442  end 
    422443 
     
    424445    posts = authors(:david).posts_with_categories 
    425446    one = posts.detect { |p| p.id == 1 } 
    426     assert_equal 5, posts.size 
    427     assert_equal 2, one.categories.size 
     447    assert_no_queries do 
     448      assert_equal 5, posts.size 
     449      assert_equal 2, one.categories.size 
     450    end 
    428451  end 
    429452 
     
    431454    posts = authors(:david).posts_with_comments_and_categories 
    432455    one = posts.detect { |p| p.id == 1 } 
    433     assert_equal 5, posts.size 
    434     assert_equal 2, one.comments.size 
    435     assert_equal 2, one.categories.size 
     456    assert_no_queries do 
     457      assert_equal 5, posts.size 
     458      assert_equal 2, one.comments.size 
     459      assert_equal 2, one.categories.size 
     460    end 
    436461  end 
    437462 
  • trunk/activerecord/test/cases/associations/join_model_test.rb

    r8661 r8672  
    318318    end 
    319319    assert_raise ActiveRecord::EagerLoadPolymorphicError do 
    320       assert_equal posts(:welcome, :thinking), tags(:general).taggings.find(:all, :include => :taggable
     320      assert_equal posts(:welcome, :thinking), tags(:general).taggings.find(:all, :include => :taggable, :conditions => 'bogus_table.column = 1'
    321321    end 
    322322  end 
     
    332332      assert_equal desired, tag_with_include.tagged_posts 
    333333    end 
     334    assert_equal 4, tag_with_include.taggings.length 
    334335  end 
    335336 
     
    547548  end 
    548549 
     550  def test_polymorphic_has_many 
     551    expected = taggings(:welcome_general) 
     552    p = Post.find(posts(:welcome).id, :include => :taggings) 
     553    assert_no_queries {assert p.taggings.include?(expected)} 
     554    assert posts(:welcome).taggings.include?(taggings(:welcome_general)) 
     555  end 
     556 
     557  def test_polymorphic_has_one 
     558    expected = posts(:welcome) 
     559 
     560    tagging  = Tagging.find(taggings(:welcome_general).id, :include => :taggable) 
     561    assert_no_queries { assert_equal expected, tagging.taggable} 
     562  end 
     563 
     564  def test_polymorphic_belongs_to 
     565    p = Post.find(posts(:welcome).id, :include => {:taggings => :taggable}) 
     566    assert_no_queries {assert_equal posts(:welcome), p.taggings.first.taggable} 
     567  end 
     568 
     569  def test_preload_polymorphic_has_many_through 
     570    posts           = Post.find(:all, :order => 'posts.id') 
     571    posts_with_tags = Post.find(:all, :include => :tags, :order => 'posts.id') 
     572    assert_equal posts.length, posts_with_tags.length 
     573    posts.length.times do |i| 
     574      assert_equal posts[i].tags.length, assert_no_queries { posts_with_tags[i].tags.length } 
     575    end 
     576  end 
     577 
     578  def test_preload_polymorph_many_types 
     579    taggings = Tagging.find :all, :include => :taggable, :conditions => ['taggable_type != ?', 'FakeModel'] 
     580    assert_no_queries do 
     581      taggings.first.taggable.id 
     582      taggings[1].taggable.id 
     583    end 
     584 
     585    taggables = taggings.map(&:taggable) 
     586    assert taggables.include?(items(:dvd)) 
     587    assert taggables.include?(posts(:welcome)) 
     588  end 
     589 
     590  def test_preload_polymorphic_has_many 
     591    posts               = Post.find(:all, :order => 'posts.id') 
     592    posts_with_taggings = Post.find(:all, :include => :taggings, :order => 'posts.id') 
     593    assert_equal posts.length, posts_with_taggings.length 
     594    posts.length.times do |i| 
     595      assert_equal posts[i].taggings.length, assert_no_queries { posts_with_taggings[i].taggings.length } 
     596    end 
     597  end 
     598 
     599  def test_belongs_to_shared_parent 
     600    comments = Comment.find(:all, :include => :post, :conditions => 'post_id = 1') 
     601    assert_no_queries do 
     602      assert_equal comments.first.post, comments[1].post 
     603    end 
     604  end 
     605 
    549606  private 
    550607    # create dynamic Post models to allow different dependency options 
  • trunk/activerecord/test/models/post.rb

    r8657 r8672  
    1313    end 
    1414  end 
     15 
     16  has_many :comments_with_interpolated_conditions, :class_name => 'Comment', 
     17      :conditions => ['#{"#{aliased_table_name}." rescue ""}body = ?', 'Thank you for the welcome'] 
    1518 
    1619  has_one  :very_special_comment