diff options
author | Nick Kallen <nkallen@nick-kallens-computer-2.local> | 2008-01-21 20:02:48 -0800 |
---|---|---|
committer | Nick Kallen <nkallen@nick-kallens-computer-2.local> | 2008-01-21 20:02:48 -0800 |
commit | 7f35d492372fd91cd35e7f9a2408efd64d28ccf5 (patch) | |
tree | 569b2217f861f99fa32104c7c9cc00ec09e50830 | |
parent | 559d9bbfe09034e807e5eae169bea26df780aa36 (diff) | |
download | rails-7f35d492372fd91cd35e7f9a2408efd64d28ccf5.tar.gz rails-7f35d492372fd91cd35e7f9a2408efd64d28ccf5.tar.bz2 rails-7f35d492372fd91cd35e7f9a2408efd64d28ccf5.zip |
joining on aggregations; this time where the aggregation is on the right.
-rw-r--r-- | lib/active_relation/relations/alias.rb | 4 | ||||
-rw-r--r-- | lib/active_relation/relations/compound.rb | 3 | ||||
-rw-r--r-- | lib/active_relation/relations/join.rb | 28 | ||||
-rw-r--r-- | lib/active_relation/relations/relation.rb | 4 | ||||
-rw-r--r-- | lib/active_relation/relations/rename.rb | 6 | ||||
-rw-r--r-- | spec/active_relation/relations/join_spec.rb | 50 |
6 files changed, 66 insertions, 29 deletions
diff --git a/lib/active_relation/relations/alias.rb b/lib/active_relation/relations/alias.rb index 701ab189d0..26b582e98a 100644 --- a/lib/active_relation/relations/alias.rb +++ b/lib/active_relation/relations/alias.rb @@ -8,11 +8,11 @@ module ActiveRelation end def attributes - relation.attributes.collect { |attribute| attribute.substitute(self) } + relation.attributes.collect { |a| a.substitute(self) } end def ==(other) - relation == other.relation and self.alias == other.alias + relation == other.relation and @alias == other.alias end protected diff --git a/lib/active_relation/relations/compound.rb b/lib/active_relation/relations/compound.rb index a7595c9e3d..d71d0ffeb5 100644 --- a/lib/active_relation/relations/compound.rb +++ b/lib/active_relation/relations/compound.rb @@ -2,7 +2,8 @@ module ActiveRelation class Compound < Relation attr_reader :relation - delegate :projections, :attribute, :joins, :selects, :orders, :groupings, :table_sql, :inserts, :limit, :offset, :name, :alias, :aggregation?, + delegate :projections, :attributes, :attribute, :joins, :selects, :orders, :groupings, :table_sql, :inserts, :limit, + :offset, :name, :alias, :aggregation?, :to => :relation end end
\ No newline at end of file diff --git a/lib/active_relation/relations/join.rb b/lib/active_relation/relations/join.rb index d4cfe19239..37ed558e9f 100644 --- a/lib/active_relation/relations/join.rb +++ b/lib/active_relation/relations/join.rb @@ -1,7 +1,6 @@ module ActiveRelation class Join < Relation attr_reader :join_sql, :relation1, :relation2, :predicates - delegate :table_sql, :to => :relation1 def initialize(join_sql, relation1, relation2, *predicates) @join_sql, @relation1, @relation2, @predicates = join_sql, relation1, relation2, predicates @@ -16,6 +15,10 @@ module ActiveRelation def qualify Join.new(join_sql, relation1.qualify, relation2.qualify, *predicates.collect(&:qualify)) end + + def attributes + projections.map(&:to_attribute) + end protected def joins @@ -23,28 +26,33 @@ module ActiveRelation end def selects - relation1.send(:selects) + relation2.send(:selects) + [ + (relation1.send(:selects) unless relation1.aggregation?), + (relation2.send(:selects) unless relation2.aggregation?) + ].compact.flatten end - # this is magick!!! def projections - relation1.send(:projections) + relation2.attributes + [ + relation1.aggregation?? relation1.attributes : relation1.send(:projections), + relation2.aggregation?? relation2.attributes : relation2.send(:projections), + ].flatten end def attribute(name) relation1[name] || relation2[name] end - - private - def join - "#{join_sql} #{relation2.send(:table_sql)} ON #{predicates.collect { |p| p.to_sql(Sql::Predicate.new) }.join(' AND ')}" + + def table_sql + relation1.aggregation?? relation1.to_sql(Sql::Aggregation.new) : relation1.send(:table_sql) end + private def join - [join_sql, right_table, "ON", predicates.collect { |p| p.to_sql(Sql::Predicate.new) }.join(' AND ')].join(" ") + [join_sql, right_table_sql, "ON", predicates.collect { |p| p.to_sql(Sql::Predicate.new) }.join(' AND ')].join(" ") end - def right_table + def right_table_sql relation2.aggregation?? relation2.to_sql(Sql::Aggregation.new) : relation2.send(:table_sql) end end diff --git a/lib/active_relation/relations/relation.rb b/lib/active_relation/relations/relation.rb index cb981eb3de..d09ee058ef 100644 --- a/lib/active_relation/relations/relation.rb +++ b/lib/active_relation/relations/relation.rb @@ -77,10 +77,6 @@ module ActiveRelation end include Operations - def attributes - projections.collect(&:to_attribute) - end - def aggregation? false end diff --git a/lib/active_relation/relations/rename.rb b/lib/active_relation/relations/rename.rb index c9c47f95b8..94e5edcd47 100644 --- a/lib/active_relation/relations/rename.rb +++ b/lib/active_relation/relations/rename.rb @@ -14,7 +14,11 @@ module ActiveRelation def qualify Rename.new(relation.qualify, autonym.qualify => self.pseudonym) end - + + def attributes + projections.collect(&:to_attribute) + end + protected def projections relation.send(:projections).collect(&method(:substitute)) diff --git a/spec/active_relation/relations/join_spec.rb b/spec/active_relation/relations/join_spec.rb index 00a6218c03..422a93772d 100644 --- a/spec/active_relation/relations/join_spec.rb +++ b/spec/active_relation/relations/join_spec.rb @@ -42,22 +42,27 @@ module ActiveRelation describe '#to_sql' do describe 'with simple relations' do - before do - @relation1 = @relation1.select(@relation1[:id].equals(1)) - end - - it 'manufactures sql joining the two tables on the predicate, merging the selects' do + it 'manufactures sql joining the two tables on the predicate' do Join.new("INNER JOIN", @relation1, @relation2, @predicate).to_sql.should be_like(""" SELECT `foo`.`name`, `foo`.`id`, `bar`.`name`, `bar`.`foo_id`, `bar`.`id` FROM `foo` INNER JOIN `bar` ON `foo`.`id` = `bar`.`id` - WHERE - `foo`.`id` = 1 """) end + + it 'manufactures sql joining the two tables, merging any selects' do + Join.new("INNER JOIN", @relation1.select(@relation1[:id].equals(1)), + @relation2.select(@relation2[:id].equals(2)), @predicate).to_sql.should be_like(""" + SELECT `foo`.`name`, `foo`.`id`, `bar`.`name`, `bar`.`foo_id`, `bar`.`id` + FROM `foo` + INNER JOIN `bar` ON `foo`.`id` = `bar`.`id` + WHERE `foo`.`id` = 1 + AND `bar`.`id` = 2 + """) + end end - describe 'with aggregated relations' do + describe 'aggregated relations' do before do @relation = Table.new(:users) photos = Table.new(:photos) @@ -66,11 +71,34 @@ module ActiveRelation @predicate = Equality.new(@aggregate_relation[:user_id], @relation[:id]) end - it 'manufactures sql joining the left table to a derived table' do - Join.new("INNER JOIN", @relation, @aggregate_relation, @predicate).to_sql.should be_like(""" + describe 'with the aggregation on the right' do + it 'manufactures sql joining the left table to a derived table' do + Join.new("INNER JOIN", @relation, @aggregate_relation, @predicate).to_sql.should be_like(""" + SELECT `users`.`name`, `users`.`id`, `photo_count`.`user_id`, `photo_count`.`cnt` + FROM `users` + INNER JOIN (SELECT `photos`.`user_id`, COUNT(`photos`.`id`) AS `cnt` FROM `photos` GROUP BY `photos`.`user_id`) AS `photo_count` + ON `photo_count`.`user_id` = `users`.`id` + """) + end + end + + describe 'with the aggregation on the left' do + it 'manufactures sql joining the right table to a derived table' do + Join.new("INNER JOIN", @aggregate_relation, @relation, @predicate).to_sql.should be_like(""" + SELECT `photo_count`.`user_id`, `photo_count`.`cnt`, `users`.`name`, `users`.`id` + FROM (SELECT `photos`.`user_id`, COUNT(`photos`.`id`) AS `cnt` FROM `photos` GROUP BY `photos`.`user_id`) AS `photo_count` + INNER JOIN `users` + ON `photo_count`.`user_id` = `users`.`id` + """) + end + end + + it "keeps selects on the aggregation within the derived table" do + pending + Join.new("INNER JOIN", @relation, @aggregate_relation.select(@aggregate_relation[:user_id].equals(1)), @predicate).to_sql.should be_like(""" SELECT `users`.`name`, `users`.`id`, `photo_count`.`user_id`, `photo_count`.`cnt` FROM `users` - INNER JOIN (SELECT `photos`.`user_id`, COUNT(`photos`.`id`) AS `cnt` FROM `photos` GROUP BY `photos`.`user_id`) AS `photo_count` + INNER JOIN (SELECT `photos`.`user_id`, COUNT(`photos`.`id`) AS `cnt` FROM `photos` WHERE `photos`.`user_id` = 1 GROUP BY `photos`.`user_id`) AS `photo_count` ON `photo_count`.`user_id` = `users`.`id` """) end |