diff options
author | Aaron Patterson <aaron.patterson@gmail.com> | 2010-09-14 13:39:33 -0700 |
---|---|---|
committer | Aaron Patterson <aaron.patterson@gmail.com> | 2010-09-14 13:39:33 -0700 |
commit | b93a23827a2244ec730be1b46ec44fb368d00396 (patch) | |
tree | 6fe68c4c927512b5f3914c5c8e795ce998208491 | |
parent | de5f2916aa47fb1274d56a2c5c5ba636f5fe2cc4 (diff) | |
download | rails-b93a23827a2244ec730be1b46ec44fb368d00396.tar.gz rails-b93a23827a2244ec730be1b46ec44fb368d00396.tar.bz2 rails-b93a23827a2244ec730be1b46ec44fb368d00396.zip |
adding an EXISTS node, update method will generate an IN clause
-rw-r--r-- | lib/arel/crud.rb | 17 | ||||
-rw-r--r-- | lib/arel/nodes.rb | 1 | ||||
-rw-r--r-- | lib/arel/nodes/exists.rb | 11 | ||||
-rw-r--r-- | lib/arel/nodes/select_statement.rb | 2 | ||||
-rw-r--r-- | lib/arel/table.rb | 5 | ||||
-rw-r--r-- | lib/arel/visitors/to_sql.rb | 4 | ||||
-rw-r--r-- | spec/arel/nodes/select_statement_spec.rb | 4 | ||||
-rw-r--r-- | spec/arel/select_manager_spec.rb | 39 | ||||
-rw-r--r-- | spec/arel/table_spec.rb | 6 |
9 files changed, 83 insertions, 6 deletions
diff --git a/lib/arel/crud.rb b/lib/arel/crud.rb index b1269bd1da..196dc56554 100644 --- a/lib/arel/crud.rb +++ b/lib/arel/crud.rb @@ -7,12 +7,23 @@ module Arel um = UpdateManager.new @engine if Nodes::SqlLiteral === values - um.table @ctx.froms.last + relation = @ctx.froms.last else - um.table values.first.first.relation + relation = values.first.first.relation end + um.table relation um.set values - um.wheres = @ctx.wheres + + if @head.orders.empty? && @head.limit.nil? + um.wheres = @ctx.wheres + else + head = @head.clone + core = head.cores.first + core.projections = [relation.primary_key] + + um.wheres = [Nodes::In.new(relation.primary_key, [head])] + end + @engine.connection.update um.to_sql, 'AREL' end diff --git a/lib/arel/nodes.rb b/lib/arel/nodes.rb index 3ffc81d7df..99a34c3b2b 100644 --- a/lib/arel/nodes.rb +++ b/lib/arel/nodes.rb @@ -14,6 +14,7 @@ require 'arel/nodes/count' require 'arel/nodes/values' require 'arel/nodes/offset' require 'arel/nodes/sum' +require 'arel/nodes/exists' require 'arel/nodes/max' require 'arel/nodes/min' require 'arel/nodes/avg' diff --git a/lib/arel/nodes/exists.rb b/lib/arel/nodes/exists.rb new file mode 100644 index 0000000000..167a345006 --- /dev/null +++ b/lib/arel/nodes/exists.rb @@ -0,0 +1,11 @@ +module Arel + module Nodes + class Exists + attr_reader :select_stmt + + def initialize select_stmt + @select_stmt = select_stmt + end + end + end +end diff --git a/lib/arel/nodes/select_statement.rb b/lib/arel/nodes/select_statement.rb index 6272fd126d..637ba5d1d0 100644 --- a/lib/arel/nodes/select_statement.rb +++ b/lib/arel/nodes/select_statement.rb @@ -14,7 +14,7 @@ module Arel def initialize_copy other super - @cores = @cores.clone + @cores = @cores.map { |x| x.clone } end end end diff --git a/lib/arel/table.rb b/lib/arel/table.rb index 1519efa63b..06bbe7b99e 100644 --- a/lib/arel/table.rb +++ b/lib/arel/table.rb @@ -14,6 +14,7 @@ module Arel @columns = nil @aliases = [] @table_alias = nil + @primary_key = nil # Sometime AR sends an :as parameter to table, to let the table know that # it is an Alias. We may want to override new, and return a TableAlias @@ -21,6 +22,10 @@ module Arel @table_alias = engine[:as] if Hash === engine end + def primary_key + @primary_key ||= self[@engine.connection.primary_key(name)] + end + def alias Nodes::TableAlias.new("#{name}_2", self).tap do |node| @aliases << node diff --git a/lib/arel/visitors/to_sql.rb b/lib/arel/visitors/to_sql.rb index 2660c7d284..dc65e86219 100644 --- a/lib/arel/visitors/to_sql.rb +++ b/lib/arel/visitors/to_sql.rb @@ -41,6 +41,10 @@ module Arel ].compact.join ' ' end + def visit_Arel_Nodes_Exists o + "EXISTS (#{visit o.select_stmt})" + end + def visit_Arel_Nodes_Values o "VALUES (#{o.expressions.map { |value| value.nil? ? 'NULL' : visit(value) diff --git a/spec/arel/nodes/select_statement_spec.rb b/spec/arel/nodes/select_statement_spec.rb index 75463f1d95..68bde3c4f7 100644 --- a/spec/arel/nodes/select_statement_spec.rb +++ b/spec/arel/nodes/select_statement_spec.rb @@ -5,10 +5,10 @@ describe Arel::Nodes::SelectStatement do it "clones cores" do statement = Arel::Nodes::SelectStatement.new %w[a b c] - statement.cores.should_receive(:clone).and_return([:cores]) + statement.cores.map { |x| x.should_receive(:clone).and_return(:f) } dolly = statement.clone - dolly.cores.should == [:cores] + dolly.cores.should == [:f, :f, :f] end end end diff --git a/spec/arel/select_manager_spec.rb b/spec/arel/select_manager_spec.rb index 4262a7465b..3fe68a8db0 100644 --- a/spec/arel/select_manager_spec.rb +++ b/spec/arel/select_manager_spec.rb @@ -74,6 +74,17 @@ module Arel end end + describe 'clone' do + it 'creates new cores' do + table = Table.new :users, :engine => Table.engine, :as => 'foo' + mgr = table.from table + m2 = mgr.clone + m2.project "foo" + + check mgr.to_sql.should_not == m2.to_sql + end + end + describe 'initialize' do it 'uses alias in sql' do table = Table.new :users, :engine => Table.engine, :as => 'foo' @@ -354,6 +365,34 @@ module Arel end describe 'update' do + it 'copies limits' do + engine = EngineProxy.new Table.engine + table = Table.new :users + manager = Arel::SelectManager.new engine + manager.from table + manager.take 1 + manager.update(SqlLiteral.new('foo = bar')) + + engine.executed.last.should be_like %{ + UPDATE "users" SET foo = bar + WHERE "users"."id" IN (SELECT "users"."id" FROM "users" LIMIT 1) + } + end + + it 'copies order' do + engine = EngineProxy.new Table.engine + table = Table.new :users + manager = Arel::SelectManager.new engine + manager.from table + manager.order :foo + manager.update(SqlLiteral.new('foo = bar')) + + engine.executed.last.should be_like %{ + UPDATE "users" SET foo = bar + WHERE "users"."id" IN (SELECT "users"."id" FROM "users" ORDER BY foo) + } + end + it 'takes a string' do engine = EngineProxy.new Table.engine table = Table.new :users diff --git a/spec/arel/table_spec.rb b/spec/arel/table_spec.rb index 7cc0b20a25..5b68040aac 100644 --- a/spec/arel/table_spec.rb +++ b/spec/arel/table_spec.rb @@ -6,6 +6,12 @@ module Arel @relation = Table.new(:users) end + describe 'primary_key' do + it 'should return an attribute' do + check @relation.primary_key.name.should == :id + end + end + describe 'having' do it 'adds a having clause' do mgr = @relation.having @relation[:id].eq(10) |