Permalink
Browse files

Add support for table aliasing, with a test that needs aliasing in or…

…der to work correctly. This test incidentally provides a more complicated test case (4 inner joins, 2 using polymorphism).
  • Loading branch information...
1 parent 34ee586 commit a34391c3b495bad268204bdf4f6b3483a61abcd5 @jonleighton jonleighton committed Oct 2, 2010
@@ -22,9 +22,8 @@ def construct_scope
# 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 @@ def construct_through_joins
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 @@ def construct_through_joins
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.
@@ -40,4 +40,9 @@ def test_distinct_has_many_through_a_has_many_through_association_on_through_ref
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
@@ -7,3 +7,7 @@ david:
mary:
id: 2
name: Mary
+
+bob:
+ id: 3
+ name: Bob
@@ -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
@@ -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
@@ -4,4 +4,4 @@ general:
misc:
id: 2
- name: Misc
+ name: Misc
@@ -84,8 +84,9 @@ def testing_proxy_target
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

0 comments on commit a34391c

Please sign in to comment.