diff options
-rw-r--r-- | lib/arel/crud.rb | 4 | ||||
-rw-r--r-- | lib/arel/delete_manager.rb | 8 | ||||
-rw-r--r-- | lib/arel/insert_manager.rb | 16 | ||||
-rw-r--r-- | lib/arel/nodes/exists.rb | 8 | ||||
-rw-r--r-- | lib/arel/select_manager.rb | 26 | ||||
-rw-r--r-- | lib/arel/tree_manager.rb | 7 | ||||
-rw-r--r-- | lib/arel/update_manager.rb | 16 | ||||
-rw-r--r-- | lib/arel/visitors/mysql.rb | 5 | ||||
-rw-r--r-- | lib/arel/visitors/to_sql.rb | 4 | ||||
-rw-r--r-- | test/support/fake_record.rb | 4 | ||||
-rw-r--r-- | test/test_insert_manager.rb | 11 | ||||
-rw-r--r-- | test/test_select_manager.rb | 37 | ||||
-rw-r--r-- | test/test_table.rb | 4 | ||||
-rw-r--r-- | test/visitors/test_mysql.rb | 8 | ||||
-rw-r--r-- | test/visitors/test_to_sql.rb | 13 |
15 files changed, 105 insertions, 66 deletions
diff --git a/lib/arel/crud.rb b/lib/arel/crud.rb index 640189fbe9..b3199e6d38 100644 --- a/lib/arel/crud.rb +++ b/lib/arel/crud.rb @@ -13,8 +13,8 @@ module Arel end um.table relation um.set values - um.take @head.limit - um.order(*@head.orders) + um.take @ast.limit + um.order(*@ast.orders) um.wheres = @ctx.wheres @engine.connection.update um.to_sql, 'AREL' diff --git a/lib/arel/delete_manager.rb b/lib/arel/delete_manager.rb index 4759f116f1..3aab248c23 100644 --- a/lib/arel/delete_manager.rb +++ b/lib/arel/delete_manager.rb @@ -2,21 +2,21 @@ module Arel class DeleteManager < Arel::TreeManager def initialize engine super - @head = Nodes::DeleteStatement.new + @ast = Nodes::DeleteStatement.new end def from relation - @head.relation = relation + @ast.relation = relation self end def where expression - @head.wheres << expression + @ast.wheres << expression self end def wheres= list - @head.wheres = list + @ast.wheres = list end end end diff --git a/lib/arel/insert_manager.rb b/lib/arel/insert_manager.rb index 1759475542..f62f36369b 100644 --- a/lib/arel/insert_manager.rb +++ b/lib/arel/insert_manager.rb @@ -2,32 +2,32 @@ module Arel class InsertManager < Arel::TreeManager def initialize engine super - @head = Nodes::InsertStatement.new + @ast = Nodes::InsertStatement.new end def into table - @head.relation = table + @ast.relation = table self end - def columns; @head.columns end - def values= val; @head.values = val; end + def columns; @ast.columns end + def values= val; @ast.values = val; end def insert fields return if fields.empty? if String === fields - @head.values = SqlLiteral.new(fields) + @ast.values = SqlLiteral.new(fields) else - @head.relation ||= fields.first.first.relation + @ast.relation ||= fields.first.first.relation values = [] fields.each do |column, value| - @head.columns << column + @ast.columns << column values << value end - @head.values = Nodes::Values.new values, @head.columns + @ast.values = Nodes::Values.new values, @ast.columns end end end diff --git a/lib/arel/nodes/exists.rb b/lib/arel/nodes/exists.rb index 167a345006..18ba8403b4 100644 --- a/lib/arel/nodes/exists.rb +++ b/lib/arel/nodes/exists.rb @@ -1,11 +1,7 @@ module Arel module Nodes - class Exists - attr_reader :select_stmt - - def initialize select_stmt - @select_stmt = select_stmt - end + class Exists < Arel::Nodes::Function + alias :select_stmt :expressions end end end diff --git a/lib/arel/select_manager.rb b/lib/arel/select_manager.rb index 7c3c67b477..f6738bf26f 100644 --- a/lib/arel/select_manager.rb +++ b/lib/arel/select_manager.rb @@ -4,13 +4,13 @@ module Arel def initialize engine, table = nil super(engine) - @head = Nodes::SelectStatement.new - @ctx = @head.cores.last + @ast = Nodes::SelectStatement.new + @ctx = @ast.cores.last from table end def taken - @head.limit + @ast.limit end def constraints @@ -18,10 +18,16 @@ module Arel end def skip amount - @head.offset = Nodes::Offset.new(amount) + @ast.offset = Nodes::Offset.new(amount) self end + ### + # Produces an Arel::Nodes::Exists node + def exists + Arel::Nodes::Exists.new @ast + end + def where_clauses #warn "where_clauses is deprecated" if $VERBOSE to_sql = Visitors::ToSql.new @engine @@ -31,12 +37,12 @@ module Arel def lock locking = true # FIXME: do we even need to store this? If locking is +false+ shouldn't # we just remove the node from the AST? - @head.lock = Nodes::Lock.new + @ast.lock = Nodes::Lock.new self end def locked - @head.lock + @ast.lock end def on *exprs @@ -108,14 +114,14 @@ module Arel def order *expr # FIXME: We SHOULD NOT be converting these to SqlLiteral automatically - @head.orders.concat expr.map { |x| + @ast.orders.concat expr.map { |x| String === x || Symbol === x ? Nodes::SqlLiteral.new(x.to_s) : x } self end def orders - @head.orders + @ast.orders end def wheres @@ -130,7 +136,7 @@ module Arel end def take limit - @head.limit = limit + @ast.limit = limit self end @@ -142,7 +148,7 @@ module Arel end def order_clauses - Visitors::OrderClauses.new(@engine).accept(@head).map { |x| + Visitors::OrderClauses.new(@engine).accept(@ast).map { |x| Nodes::SqlLiteral.new x } end diff --git a/lib/arel/tree_manager.rb b/lib/arel/tree_manager.rb index 077458fe18..6176f8a250 100644 --- a/lib/arel/tree_manager.rb +++ b/lib/arel/tree_manager.rb @@ -4,6 +4,7 @@ module Arel include Arel::Relation attr_accessor :visitor + attr_reader :ast, :engine def initialize engine @engine = engine @@ -11,16 +12,16 @@ module Arel end def to_dot - Visitors::Dot.new.accept @head + Visitors::Dot.new.accept @ast end def to_sql - @visitor.accept @head + @visitor.accept @ast end def initialize_copy other super - @head = @head.clone + @ast = @ast.clone end end end diff --git a/lib/arel/update_manager.rb b/lib/arel/update_manager.rb index 3f6a483751..c0fb4fbd5f 100644 --- a/lib/arel/update_manager.rb +++ b/lib/arel/update_manager.rb @@ -2,40 +2,40 @@ module Arel class UpdateManager < Arel::TreeManager def initialize engine super - @head = Nodes::UpdateStatement.new + @ast = Nodes::UpdateStatement.new end def take limit - @head.limit = limit + @ast.limit = limit self end def order *expr - @head.orders = expr + @ast.orders = expr self end ### # UPDATE +table+ def table table - @head.relation = table + @ast.relation = table self end def wheres= exprs - @head.wheres = exprs + @ast.wheres = exprs end def where expr - @head.wheres << expr + @ast.wheres << expr self end def set values if String === values - @head.values = [values] + @ast.values = [values] else - @head.values = values.map { |column,value| + @ast.values = values.map { |column,value| Nodes::Assignment.new( Nodes::UnqualifiedColumn.new(column), value diff --git a/lib/arel/visitors/mysql.rb b/lib/arel/visitors/mysql.rb index 594fd5504a..afffd37f9b 100644 --- a/lib/arel/visitors/mysql.rb +++ b/lib/arel/visitors/mysql.rb @@ -10,6 +10,11 @@ module Arel super end + def visit_Arel_Nodes_SelectCore o + o.froms ||= Arel.sql('DUAL') + super + end + def visit_Arel_Nodes_UpdateStatement o [ "UPDATE #{visit o.relation}", diff --git a/lib/arel/visitors/to_sql.rb b/lib/arel/visitors/to_sql.rb index 1412797fb5..b6af47cc57 100644 --- a/lib/arel/visitors/to_sql.rb +++ b/lib/arel/visitors/to_sql.rb @@ -48,6 +48,7 @@ module Arel ("WHERE #{wheres.map { |x| visit x }.join ' AND '}" unless wheres.empty?) ].compact.join ' ' end + def visit_Arel_Nodes_InsertStatement o [ "INSERT INTO #{visit o.relation}", @@ -61,7 +62,8 @@ module Arel end def visit_Arel_Nodes_Exists o - "EXISTS (#{visit o.select_stmt})" + "EXISTS (#{visit o.select_stmt})#{ + o.alias ? " AS #{visit o.alias}" : ''}" end def visit_Arel_Nodes_Values o diff --git a/test/support/fake_record.rb b/test/support/fake_record.rb index e0f9685c05..376ed40f2d 100644 --- a/test/support/fake_record.rb +++ b/test/support/fake_record.rb @@ -10,7 +10,9 @@ module FakeRecord @columns = { 'users' => [ Column.new('id', :integer), - Column.new('name', :string) + Column.new('name', :string), + Column.new('bool', :boolean), + Column.new('created_at', :date), ] } @primary_keys = { diff --git a/test/test_insert_manager.rb b/test/test_insert_manager.rb index 3094408a41..0a1b1fcf5a 100644 --- a/test/test_insert_manager.rb +++ b/test/test_insert_manager.rb @@ -13,11 +13,9 @@ module Arel table = Table.new(:users) manager = Arel::InsertManager.new Table.engine - table[:id].column.extend(Module.new { def type; :boolean; end }) - - manager.insert [[table[:id], false]] + manager.insert [[table[:bool], false]] manager.to_sql.must_be_like %{ - INSERT INTO "users" ("id") VALUES ('f') + INSERT INTO "users" ("bool") VALUES ('f') } end @@ -35,12 +33,11 @@ module Arel manager = Arel::InsertManager.new Table.engine time = Time.now - attribute = table[:id] - attribute.column.type = :date + attribute = table[:created_at] manager.insert [[attribute, time]] manager.to_sql.must_be_like %{ - INSERT INTO "users" ("id") VALUES (#{Table.engine.connection.quote time}) + INSERT INTO "users" ("created_at") VALUES (#{Table.engine.connection.quote time}) } end diff --git a/test/test_select_manager.rb b/test/test_select_manager.rb index 50f04a1a58..d63bec0093 100644 --- a/test/test_select_manager.rb +++ b/test/test_select_manager.rb @@ -56,9 +56,7 @@ module Arel manager.project SqlLiteral.new '*' manager.from table manager.order :foo - manager.to_sql.must_be_like %{ - SELECT * FROM "users" ORDER BY foo - } + manager.to_sql.must_be_like %{ SELECT * FROM "users" ORDER BY foo } end end @@ -68,9 +66,7 @@ module Arel manager = Arel::SelectManager.new Table.engine manager.from table manager.group :foo - manager.to_sql.must_be_like %{ - SELECT FROM "users" GROUP BY foo - } + manager.to_sql.must_be_like %{ SELECT FROM "users" GROUP BY foo } end end @@ -130,6 +126,35 @@ module Arel end end + describe 'exists' do + it 'should create an exists clause' do + table = Table.new(:users) + manager = Arel::SelectManager.new Table.engine, table + manager.project SqlLiteral.new '*' + m2 = Arel::SelectManager.new(manager.engine) + m2.project manager.exists + m2.to_sql.must_be_like %{ SELECT EXISTS (#{manager.to_sql}) } + end + + it 'can be aliased' do + table = Table.new(:users) + manager = Arel::SelectManager.new Table.engine, table + manager.project SqlLiteral.new '*' + m2 = Arel::SelectManager.new(manager.engine) + m2.project manager.exists.as('foo') + m2.to_sql.must_be_like %{ SELECT EXISTS (#{manager.to_sql}) AS foo } + end + end + + describe 'ast' do + it 'should return the ast' do + table = Table.new :users + mgr = table.from table + ast = mgr.ast + mgr.visitor.accept(ast).must_equal mgr.to_sql + end + end + describe 'taken' do it 'should return limit' do manager = Arel::SelectManager.new Table.engine diff --git a/test/test_table.rb b/test/test_table.rb index d34383922c..0dbb3f8dd1 100644 --- a/test/test_table.rb +++ b/test/test_table.rb @@ -150,8 +150,8 @@ module Arel describe 'columns' do it 'returns a list of columns' do columns = @relation.columns - columns.length.must_equal 2 - columns.map { |x| x.name.to_s }.sort.must_equal %w{ name id }.sort + columns.length.must_equal 4 + columns.map { |x| x.name.to_s }.sort.must_equal %w{ created_at bool name id }.sort end end diff --git a/test/visitors/test_mysql.rb b/test/visitors/test_mysql.rb index 1d38161caf..871d662d4b 100644 --- a/test/visitors/test_mysql.rb +++ b/test/visitors/test_mysql.rb @@ -14,7 +14,13 @@ module Arel stmt = Nodes::SelectStatement.new stmt.offset = Nodes::Offset.new(1) sql = @visitor.accept(stmt) - sql.must_be_like "SELECT LIMIT 18446744073709551615 OFFSET 1" + sql.must_be_like "SELECT FROM DUAL LIMIT 18446744073709551615 OFFSET 1" + end + + it 'uses DUAL for empty from' do + stmt = Nodes::SelectStatement.new + sql = @visitor.accept(stmt) + sql.must_be_like "SELECT FROM DUAL" end end end diff --git a/test/visitors/test_to_sql.rb b/test/visitors/test_to_sql.rb index a6042ef9c3..8723332005 100644 --- a/test/visitors/test_to_sql.rb +++ b/test/visitors/test_to_sql.rb @@ -69,9 +69,8 @@ module Arel end it "should visit_TrueClass" do - test = @attr.eq(true) - test.left.column.type = :boolean - @visitor.accept(test).must_be_like %{ "users"."id" = 't' } + test = Table.new(:users)[:bool].eq(true) + @visitor.accept(test).must_be_like %{ "users"."bool" = 't' } end describe "Nodes::Ordering" do @@ -113,6 +112,7 @@ module Arel end it 'uses the same column for escaping values' do + @attr = Table.new(:users)[:name] visitor = Class.new(ToSql) do attr_accessor :expected @@ -124,16 +124,15 @@ module Arel in_node = Nodes::In.new @attr, %w{ a b c } visitor = visitor.new(Table.engine) visitor.expected = @attr.column - visitor.accept(in_node).must_equal %("users"."id" IN ('a', 'b', 'c')) + visitor.accept(in_node).must_equal %("users"."name" IN ('a', 'b', 'c')) end end describe 'Equality' do it "should escape strings" do - test = @attr.eq 'Aaron Patterson' - test.left.column.type = :string + test = Table.new(:users)[:name].eq 'Aaron Patterson' @visitor.accept(test).must_be_like %{ - "users"."id" = 'Aaron Patterson' + "users"."name" = 'Aaron Patterson' } end end |