aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAaron Patterson <aaron.patterson@gmail.com>2010-08-18 16:32:32 -0700
committerAaron Patterson <aaron.patterson@gmail.com>2010-08-18 16:32:32 -0700
commit6b851c686faefe54efce0857f8b7b7b0c04bb673 (patch)
treec95e779ce383412519a06b4b455d31df53ea3c4d
parent0403efa4c6ad926a61dd0d134d63d962238e2c4f (diff)
downloadrails-6b851c686faefe54efce0857f8b7b7b0c04bb673.tar.gz
rails-6b851c686faefe54efce0857f8b7b7b0c04bb673.tar.bz2
rails-6b851c686faefe54efce0857f8b7b7b0c04bb673.zip
joins can be created
-rw-r--r--lib/arel/nodes.rb3
-rw-r--r--lib/arel/nodes/inner_join.rb13
-rw-r--r--lib/arel/nodes/on.rb11
-rw-r--r--lib/arel/nodes/table_alias.rb20
-rw-r--r--lib/arel/select_manager.rb9
-rw-r--r--lib/arel/table.rb13
-rw-r--r--lib/arel/visitors/to_sql.rb25
-rw-r--r--spec/arel/select_manager_spec.rb22
-rw-r--r--spec/arel/table_spec.rb12
-rw-r--r--spec/arel/update_manager_spec.rb2
10 files changed, 120 insertions, 10 deletions
diff --git a/lib/arel/nodes.rb b/lib/arel/nodes.rb
index 4d6db39aec..7183a60591 100644
--- a/lib/arel/nodes.rb
+++ b/lib/arel/nodes.rb
@@ -10,3 +10,6 @@ require 'arel/nodes/insert_statement'
require 'arel/nodes/update_statement'
require 'arel/nodes/delete_statement'
require 'arel/nodes/unqualified_column'
+require 'arel/nodes/table_alias'
+require 'arel/nodes/inner_join'
+require 'arel/nodes/on'
diff --git a/lib/arel/nodes/inner_join.rb b/lib/arel/nodes/inner_join.rb
new file mode 100644
index 0000000000..a39ded9f1c
--- /dev/null
+++ b/lib/arel/nodes/inner_join.rb
@@ -0,0 +1,13 @@
+module Arel
+ module Nodes
+ class InnerJoin
+ attr_accessor :left, :right, :constraint
+
+ def initialize left, right, constraint
+ @left = left
+ @right = right
+ @constraint = constraint
+ end
+ end
+ end
+end
diff --git a/lib/arel/nodes/on.rb b/lib/arel/nodes/on.rb
new file mode 100644
index 0000000000..4cc76b70db
--- /dev/null
+++ b/lib/arel/nodes/on.rb
@@ -0,0 +1,11 @@
+module Arel
+ module Nodes
+ class On
+ attr_accessor :expr
+
+ def initialize expr
+ @expr = expr
+ end
+ end
+ end
+end
diff --git a/lib/arel/nodes/table_alias.rb b/lib/arel/nodes/table_alias.rb
new file mode 100644
index 0000000000..4f1d70ee54
--- /dev/null
+++ b/lib/arel/nodes/table_alias.rb
@@ -0,0 +1,20 @@
+module Arel
+ module Nodes
+ class TableAlias
+ attr_reader :name, :relation, :columns
+
+ def initialize name, relation
+ @name = name
+ @relation = relation
+ @columns = relation.columns.map { |column|
+ column.dup.tap { |col| col.relation = self }
+ }
+ end
+
+ def [] name
+ name = name.to_s
+ columns.find { |column| column.name == name }
+ end
+ end
+ end
+end
diff --git a/lib/arel/select_manager.rb b/lib/arel/select_manager.rb
index f278465c51..533911e635 100644
--- a/lib/arel/select_manager.rb
+++ b/lib/arel/select_manager.rb
@@ -8,12 +8,21 @@ module Arel
@ctx = @head.cores.last
end
+ def on expr
+ @ctx.froms.last.constraint = Nodes::On.new(expr)
+ self
+ end
+
def from table
@ctx.froms << table
self
end
def project projection
+ projection = ::String == projection.class ?
+ Nodes::SqlLiteral.new(projection) :
+ projection
+
@ctx.projections << projection
self
end
diff --git a/lib/arel/table.rb b/lib/arel/table.rb
index 2df20e50e8..5a66db0627 100644
--- a/lib/arel/table.rb
+++ b/lib/arel/table.rb
@@ -5,19 +5,30 @@ module Arel
@engine = nil
class << self; attr_accessor :engine; end
- attr_reader :name, :engine
+ attr_reader :name, :engine, :aliases
def initialize name, engine = Table.engine
@name = name
@engine = engine
@engine = engine[:engine] if Hash === engine
@columns = nil
+ @aliases = []
+ end
+
+ def alias
+ Nodes::TableAlias.new("#{name}_2", self).tap do |node|
+ @aliases << node
+ end
end
def tm
SelectManager.new(@engine).from(self)
end
+ def join relation
+ SelectManager.new(@engine).from(Nodes::InnerJoin.new(self, relation, nil))
+ end
+
def where condition
tm.where condition
end
diff --git a/lib/arel/visitors/to_sql.rb b/lib/arel/visitors/to_sql.rb
index 0e5998c573..3b0a0dc5df 100644
--- a/lib/arel/visitors/to_sql.rb
+++ b/lib/arel/visitors/to_sql.rb
@@ -36,7 +36,7 @@ module Arel
}.join ', '})" unless o.columns.empty?),
("VALUES (#{o.values.map { |value|
- value ? quote(visit(value)) : 'NULL'
+ value ? visit(value) : 'NULL'
}.join ', '})" unless o.values.empty?),
].compact.join ' '
@@ -57,12 +57,24 @@ module Arel
].compact.join ' '
end
+ def visit_Arel_Nodes_TableAlias o
+ "#{visit o.relation} #{quote_table_name o.name}"
+ end
+
+ def visit_Arel_Nodes_InnerJoin o
+ "#{visit o.left} INNER JOIN #{visit o.right} #{visit o.constraint}"
+ end
+
+ def visit_Arel_Nodes_On o
+ "ON #{visit o.expr}"
+ end
+
def visit_Arel_Table o
quote_table_name o.name
end
def visit_Arel_Nodes_In o
- "#{visit o.left} IN (#{o.right.map { |x| quote visit x }.join ', '})"
+ "#{visit o.left} IN (#{o.right.map { |x| visit x }.join ', '})"
end
def visit_Arel_Nodes_Or o
@@ -71,7 +83,7 @@ module Arel
def visit_Arel_Nodes_Equality o
right = o.right
- right = right ? quote(visit(right)) : 'NULL'
+ right = right ? visit(right) : 'NULL'
"#{visit o.left} = #{right}"
end
@@ -87,12 +99,13 @@ module Arel
alias :visit_Arel_Attributes_Time :visit_Arel_Attributes_Attribute
def visit_Fixnum o; o end
- alias :visit_Time :visit_Fixnum
- alias :visit_String :visit_Fixnum
- alias :visit_TrueClass :visit_Fixnum
alias :visit_Arel_Nodes_SqlLiteral :visit_Fixnum
alias :visit_Arel_SqlLiteral :visit_Fixnum # This is deprecated
+ def visit_TrueClass o; quote(o) end
+ def visit_String o; quote(o) end
+ def visit_Time o; quote(o) end
+
DISPATCH = {}
def visit object
send "visit_#{object.class.name.gsub('::', '_')}", object
diff --git a/spec/arel/select_manager_spec.rb b/spec/arel/select_manager_spec.rb
index 01d1b35004..74e2db408b 100644
--- a/spec/arel/select_manager_spec.rb
+++ b/spec/arel/select_manager_spec.rb
@@ -54,7 +54,7 @@ module Arel
table = Table.new :users
manager = Arel::SelectManager.new engine
manager.from table
- manager.update('foo = bar')
+ manager.update(SqlLiteral.new('foo = bar'))
engine.executed.last.should be_like %{ UPDATE "users" SET foo = bar }
end
@@ -89,7 +89,7 @@ module Arel
it 'takes strings' do
table = Table.new :users
manager = Arel::SelectManager.new Table.engine
- manager.project '*'
+ manager.project Nodes::SqlLiteral.new('*')
manager.to_sql.should be_like %{
SELECT *
}
@@ -149,6 +149,24 @@ module Arel
end
end
+ describe "join" do
+ it "joins itself" do
+ left = Table.new :users
+ right = left.alias
+ predicate = left[:id].eq(right[:id])
+
+ mgr = left.join(right)
+ mgr.project Nodes::SqlLiteral.new('*')
+ check mgr.on(predicate).should == mgr
+
+ mgr.to_sql.should be_like %{
+ SELECT * FROM "users"
+ INNER JOIN "users" "users_2"
+ ON "users"."id" = "users_2"."id"
+ }
+ end
+ end
+
describe 'from' do
it "makes sql" do
table = Table.new :users
diff --git a/spec/arel/table_spec.rb b/spec/arel/table_spec.rb
index bc3d6938a7..58ccc9e7be 100644
--- a/spec/arel/table_spec.rb
+++ b/spec/arel/table_spec.rb
@@ -6,6 +6,18 @@ module Arel
@relation = Table.new(:users)
end
+ describe 'alias' do
+ it 'should create a node that proxies to a table' do
+ check @relation.aliases.should == []
+
+ node = @relation.alias
+ check @relation.aliases.should == [node]
+ check node.name.should == 'users_2'
+ check node[:id].relation.should == node
+ check node[:id].relation.should != node
+ end
+ end
+
describe 'new' do
it 'should accept an engine' do
rel = Table.new :users, 'foo'
diff --git a/spec/arel/update_manager_spec.rb b/spec/arel/update_manager_spec.rb
index 8c4c4f93b8..e70ae2cef3 100644
--- a/spec/arel/update_manager_spec.rb
+++ b/spec/arel/update_manager_spec.rb
@@ -21,7 +21,7 @@ module Arel
table = Table.new(:users)
um = Arel::UpdateManager.new Table.engine
um.table table
- um.set "foo = bar"
+ um.set Nodes::SqlLiteral.new "foo = bar"
um.to_sql.should be_like %{ UPDATE "users" SET foo = bar }
end