aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/arel/crud.rb4
-rw-r--r--lib/arel/delete_manager.rb8
-rw-r--r--lib/arel/insert_manager.rb16
-rw-r--r--lib/arel/nodes/exists.rb8
-rw-r--r--lib/arel/select_manager.rb26
-rw-r--r--lib/arel/tree_manager.rb7
-rw-r--r--lib/arel/update_manager.rb16
-rw-r--r--lib/arel/visitors/mysql.rb5
-rw-r--r--lib/arel/visitors/to_sql.rb4
-rw-r--r--test/support/fake_record.rb4
-rw-r--r--test/test_insert_manager.rb11
-rw-r--r--test/test_select_manager.rb37
-rw-r--r--test/test_table.rb4
-rw-r--r--test/visitors/test_mysql.rb8
-rw-r--r--test/visitors/test_to_sql.rb13
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