Permalink
Browse files

Backported Set operators from master

Union, UnionAll, Intersect and Except
  • Loading branch information...
1 parent 738bf13 commit 9e816c406399139d9ca76d2089df7f2d94d4fb8b @bcardarella bcardarella committed Jan 25, 2011
View
@@ -47,3 +47,7 @@
require 'arel/nodes/outer_join'
require 'arel/nodes/string_join'
require 'arel/nodes/on'
+require 'arel/nodes/except'
+require 'arel/nodes/intersect'
+require 'arel/nodes/union'
+require 'arel/nodes/union_all'
View
@@ -0,0 +1,7 @@
+module Arel
+ module Nodes
+ class Except < Arel::Nodes::Binary
+ end
+ end
+end
+
@@ -0,0 +1,7 @@
+module Arel
+ module Nodes
+ class Intersect < Arel::Nodes::Binary
+ end
+ end
+end
+
View
@@ -0,0 +1,7 @@
+module Arel
+ module Nodes
+ class Union < Arel::Nodes::Binary
+ end
+ end
+end
+
@@ -0,0 +1,7 @@
+module Arel
+ module Nodes
+ class UnionAll < Arel::Nodes::Binary
+ end
+ end
+end
+
View
@@ -130,6 +130,26 @@ def where_sql
Nodes::SqlLiteral.new viz.accept @ctx
end
+ def union operation, other = nil
+ if other
+ node_class = Nodes.const_get("Union#{operation.to_s.capitalize}")
+ else
+ other = operation
+ node_class = Nodes::Union
+ end
+
+ node_class.new self.ast, other.ast
+ end
+
+ def intersect other
+ Nodes::Intersect.new ast, other.ast
+ end
+
+ def except other
+ Nodes::Except.new ast, other.ast
+ end
+ alias :minus :except
+
def take limit
@ast.limit = Nodes::Limit.new(limit)
@ctx.top = Nodes::Top.new(limit)
@@ -61,6 +61,10 @@ def visit_Arel_Nodes_Offset o
"raw_rnum_ > #{visit o.value}"
end
+ def visit_Arel_Nodes_Except o
+ "( #{visit o.left} MINUS #{visit o.right} )"
+ end
+
###
# Hacks for the order clauses specific to Oracle
def order_hacks o
@@ -206,6 +206,22 @@ def visit_Arel_Nodes_Not o
"NOT (#{visit o.expr})"
end
+ def visit_Arel_Nodes_Union o
+ "( #{visit o.left} UNION #{visit o.right} )"
+ end
+
+ def visit_Arel_Nodes_UnionAll o
+ "( #{visit o.left} UNION ALL #{visit o.right} )"
+ end
+
+ def visit_Arel_Nodes_Intersect o
+ "( #{visit o.left} INTERSECT #{visit o.right} )"
+ end
+
+ def visit_Arel_Nodes_Except o
+ "( #{visit o.left} EXCEPT #{visit o.right} )"
+ end
+
def visit_Arel_Table o
if o.table_alias
"#{quote_table_name o.name} #{quote_table_name o.table_alias}"
@@ -596,4 +596,46 @@ def execute sql, name = nil, *args
end
end
end
+
+ describe 'set operations' do
+ before do
+ @table = Table.new :users
+ @m1 = Arel::SelectManager.new Table.engine, @table
+ @m1.project Arel.sql('*')
+ @m2 = Arel::SelectManager.new Table.engine, @table
+ @m2.project Arel.sql('*')
+ end
+
+ it 'supports union' do
+ @m1.where(@table[:id].lt(18))
+ @m2.where(@table[:id].gt(99))
+ (@m1.union @m2).to_sql.must_be_like %{
+ ( SELECT * FROM "users" WHERE "users"."id" < 18 UNION SELECT * FROM "users" WHERE "users"."id" > 99 )
+ }
+ end
+
+ it 'supports union all' do
+ @m1.where(@table[:id].lt(18))
+ @m2.where(@table[:id].gt(99))
+ (@m1.union :all, @m2).to_sql.must_be_like %{
+ ( SELECT * FROM "users" WHERE "users"."id" < 18 UNION ALL SELECT * FROM "users" WHERE "users"."id" > 99 )
+ }
+ end
+
+ it 'supports intersect' do
+ @m1.where(@table[:id].gt(18))
+ @m2.where(@table[:id].lt(99))
+ (@m1.intersect @m2).to_sql.must_be_like %{
+ ( SELECT * FROM "users" WHERE "users"."id" > 18 INTERSECT SELECT * FROM "users" WHERE "users"."id" < 99 )
+ }
+ end
+
+ it 'supports except' do
+ @m1.where(@table[:id].in(18..60))
+ @m2.where(@table[:id].in(40..99))
+ (@m1.except @m2).to_sql.must_be_like %{
+ ( SELECT * FROM "users" WHERE "users"."id" BETWEEN 18 AND 60 EXCEPT SELECT * FROM "users" WHERE "users"."id" BETWEEN 40 AND 99 )
+ }
+ end
+ end
end
@@ -133,6 +133,14 @@ module Visitors
end
end
+ it 'modified except to be minus' do
+ left = Nodes::SqlLiteral.new("SELECT * FROM users WHERE age > 10")
+ right = Nodes::SqlLiteral.new("SELECT * FROM users WHERE age > 20")
+ sql = @visitor.accept Nodes::Except.new(left, right)
+ sql.must_be_like %{
+ ( SELECT * FROM users WHERE age > 10 MINUS SELECT * FROM users WHERE age > 20 )
+ }
+ end
end
end
end

0 comments on commit 9e816c4

Please sign in to comment.