aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
authorJon Leighton <j@jonathanleighton.com>2010-10-02 21:45:46 +0100
committerJon Leighton <j@jonathanleighton.com>2010-10-02 21:45:46 +0100
commita34391c3b495bad268204bdf4f6b3483a61abcd5 (patch)
tree7e6cb70fbe87bfde123b4aef097d85d5ea81015f /activerecord
parent34ee586e993ad9e466b81f376fa92feb5d312b4c (diff)
downloadrails-a34391c3b495bad268204bdf4f6b3483a61abcd5.tar.gz
rails-a34391c3b495bad268204bdf4f6b3483a61abcd5.tar.bz2
rails-a34391c3b495bad268204bdf4f6b3483a61abcd5.zip
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).
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/lib/active_record/associations/through_association_scope.rb50
-rw-r--r--activerecord/test/cases/associations/nested_has_many_through_associations_test.rb5
-rw-r--r--activerecord/test/fixtures/authors.yml4
-rw-r--r--activerecord/test/fixtures/posts.yml14
-rw-r--r--activerecord/test/fixtures/taggings.yml12
-rw-r--r--activerecord/test/fixtures/tags.yml2
-rw-r--r--activerecord/test/models/author.rb3
7 files changed, 75 insertions, 15 deletions
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