From a34391c3b495bad268204bdf4f6b3483a61abcd5 Mon Sep 17 00:00:00 2001 From: Jon Leighton Date: Sat, 2 Oct 2010 21:45:46 +0100 Subject: Add support for table aliasing, with a test that needs aliasing in order to work correctly. This test incidentally provides a more complicated test case (4 inner joins, 2 using polymorphism). --- .../associations/through_association_scope.rb | 50 ++++++++++++++++------ .../nested_has_many_through_associations_test.rb | 5 +++ activerecord/test/fixtures/authors.yml | 4 ++ activerecord/test/fixtures/posts.yml | 14 ++++++ activerecord/test/fixtures/taggings.yml | 12 ++++++ activerecord/test/fixtures/tags.yml | 2 +- activerecord/test/models/author.rb | 3 +- 7 files changed, 75 insertions(+), 15 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/through_association_scope.rb b/activerecord/lib/active_record/associations/through_association_scope.rb index c5453fa79f..90ebadda89 100644 --- a/activerecord/lib/active_record/associations/through_association_scope.rb +++ b/activerecord/lib/active_record/associations/through_association_scope.rb @@ -22,9 +22,8 @@ module ActiveRecord # Build SQL conditions from attributes, qualified by table name. def construct_conditions reflection = @reflection.through_reflection_chain.last - table_name = reflection.quoted_table_name conditions = construct_quoted_owner_attributes(reflection).map do |attr, value| - "#{table_name}.#{attr} = #{value}" + "#{table_aliases[reflection]}.#{attr} = #{value}" end conditions << sql_conditions if sql_conditions "(" + conditions.join(') AND (') + ")" @@ -67,21 +66,23 @@ module ActiveRecord polymorphic_join = nil case - when left.options[:as] + when left.source_reflection.nil? left_primary_key = left.primary_key_name right_primary_key = right.klass.primary_key - polymorphic_join = "AND %s.%s = %s" % [ - left.quoted_table_name, "#{left.options[:as]}_type", - @owner.class.quote_value(right.klass.name) - ] + if left.options[:as] + polymorphic_join = "AND %s.%s = %s" % [ + table_aliases[left], "#{left.options[:as]}_type", + @owner.class.quote_value(right.klass.name) + ] + end when left.source_reflection.macro == :belongs_to left_primary_key = left.klass.primary_key right_primary_key = left.source_reflection.primary_key_name if left.options[:source_type] polymorphic_join = "AND %s.%s = %s" % [ - right.quoted_table_name, + table_aliases[right], left.source_reflection.options[:foreign_type].to_s, @owner.class.quote_value(left.options[:source_type]) ] @@ -92,22 +93,45 @@ module ActiveRecord if left.source_reflection.options[:as] polymorphic_join = "AND %s.%s = %s" % [ - left.quoted_table_name, + table_aliases[left], "#{left.source_reflection.options[:as]}_type", @owner.class.quote_value(right.klass.name) ] end end + if right.quoted_table_name == table_aliases[right] + table = right.quoted_table_name + else + table = "#{right.quoted_table_name} #{table_aliases[right]}" + end + joins << "INNER JOIN %s ON %s.%s = %s.%s %s" % [ - right.quoted_table_name, - left.quoted_table_name, left_primary_key, - right.quoted_table_name, right_primary_key, + table, + table_aliases[left], left_primary_key, + table_aliases[right], right_primary_key, polymorphic_join ] end - joins + joins.join(" ") + end + + def table_aliases + @table_aliases ||= begin + tally = {} + @reflection.through_reflection_chain.inject({}) do |aliases, reflection| + if tally[reflection.table_name].nil? + tally[reflection.table_name] = 1 + aliases[reflection] = reflection.quoted_table_name + else + tally[reflection.table_name] += 1 + aliased_table_name = reflection.table_name + "_#{tally[reflection.table_name]}" + aliases[reflection] = reflection.klass.connection.quote_table_name(aliased_table_name) + end + aliases + end + end end # Construct attributes for associate pointing to owner. diff --git a/activerecord/test/cases/associations/nested_has_many_through_associations_test.rb b/activerecord/test/cases/associations/nested_has_many_through_associations_test.rb index 938643b1b3..925a9598fb 100644 --- a/activerecord/test/cases/associations/nested_has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/nested_has_many_through_associations_test.rb @@ -40,4 +40,9 @@ class NestedHasManyThroughAssociationsTest < ActiveRecord::TestCase author = authors(:david) assert_equal [subscribers(:first), subscribers(:second)], author.distinct_subscribers end + + def test_nested_has_many_through_with_a_table_referenced_multiple_times + author = authors(:bob) + assert_equal [posts(:misc_by_bob), posts(:misc_by_mary)], author.similar_posts.sort_by(&:id) + end end diff --git a/activerecord/test/fixtures/authors.yml b/activerecord/test/fixtures/authors.yml index de2ec7d38b..6f13ec4dac 100644 --- a/activerecord/test/fixtures/authors.yml +++ b/activerecord/test/fixtures/authors.yml @@ -7,3 +7,7 @@ david: mary: id: 2 name: Mary + +bob: + id: 3 + name: Bob diff --git a/activerecord/test/fixtures/posts.yml b/activerecord/test/fixtures/posts.yml index f817493190..ca6d4c2fe1 100644 --- a/activerecord/test/fixtures/posts.yml +++ b/activerecord/test/fixtures/posts.yml @@ -50,3 +50,17 @@ eager_other: title: eager loading with OR'd conditions body: hello type: Post + +misc_by_bob: + id: 8 + author_id: 3 + title: misc post by bob + body: hello + type: Post + +misc_by_mary: + id: 9 + author_id: 2 + title: misc post by mary + body: hello + type: Post diff --git a/activerecord/test/fixtures/taggings.yml b/activerecord/test/fixtures/taggings.yml index 3db6a4c079..7cc7198ded 100644 --- a/activerecord/test/fixtures/taggings.yml +++ b/activerecord/test/fixtures/taggings.yml @@ -26,3 +26,15 @@ godfather: orphaned: id: 5 tag_id: 1 + +misc_post_by_bob: + id: 6 + tag_id: 2 + taggable_id: 8 + taggable_type: Post + +misc_post_by_mary: + id: 7 + tag_id: 2 + taggable_id: 9 + taggable_type: Post diff --git a/activerecord/test/fixtures/tags.yml b/activerecord/test/fixtures/tags.yml index 7610fd38b9..6cb886dc46 100644 --- a/activerecord/test/fixtures/tags.yml +++ b/activerecord/test/fixtures/tags.yml @@ -4,4 +4,4 @@ general: misc: id: 2 - name: Misc \ No newline at end of file + name: Misc diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb index 1efb4fc095..1fbd729b60 100644 --- a/activerecord/test/models/author.rb +++ b/activerecord/test/models/author.rb @@ -84,8 +84,9 @@ class Author < ActiveRecord::Base has_many :favorite_authors, :through => :author_favorites, :order => 'name' has_many :tagging, :through => :posts # through polymorphic has_one - has_many :taggings, :through => :posts, :source => :taggings # through polymorphic has_many TODO: Why is the :source needed? + has_many :taggings, :through => :posts # through polymorphic has_many has_many :tags, :through => :posts # through has_many :through (on source reflection + polymorphic) + has_many :similar_posts, :through => :tags, :source => :tagged_posts has_many :distinct_tags, :through => :posts, :source => :tags, :select => "DISTINCT tags.*", :order => "tags.name" has_many :post_categories, :through => :posts, :source => :categories -- cgit v1.2.3