aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/arel/nodes.rb1
-rw-r--r--lib/arel/nodes/binary.rb4
-rw-r--r--lib/arel/nodes/select_statement.rb13
-rw-r--r--lib/arel/nodes/with.rb10
-rw-r--r--lib/arel/select_manager.rb33
-rw-r--r--lib/arel/visitors/mssql.rb7
-rw-r--r--lib/arel/visitors/oracle.rb4
-rw-r--r--lib/arel/visitors/to_sql.rb56
8 files changed, 107 insertions, 21 deletions
diff --git a/lib/arel/nodes.rb b/lib/arel/nodes.rb
index 64f2cc13fd..cbd10c31e0 100644
--- a/lib/arel/nodes.rb
+++ b/lib/arel/nodes.rb
@@ -9,6 +9,7 @@ require 'arel/nodes/update_statement'
# unary
require 'arel/nodes/unary'
require 'arel/nodes/unqualified_column'
+require 'arel/nodes/with'
# binary
require 'arel/nodes/binary'
diff --git a/lib/arel/nodes/binary.rb b/lib/arel/nodes/binary.rb
index 0d02554199..bcd46db398 100644
--- a/lib/arel/nodes/binary.rb
+++ b/lib/arel/nodes/binary.rb
@@ -29,6 +29,10 @@ module Arel
NotEqual
NotIn
Or
+ Union
+ UnionAll
+ Intersect
+ Except
}.each do |name|
const_set name, Class.new(Binary)
end
diff --git a/lib/arel/nodes/select_statement.rb b/lib/arel/nodes/select_statement.rb
index c9a0cde4e0..c99842f22f 100644
--- a/lib/arel/nodes/select_statement.rb
+++ b/lib/arel/nodes/select_statement.rb
@@ -2,15 +2,16 @@ module Arel
module Nodes
class SelectStatement < Arel::Nodes::Node
attr_reader :cores
- attr_accessor :limit, :orders, :lock, :offset
+ attr_accessor :limit, :orders, :lock, :offset, :with
def initialize cores = [SelectCore.new]
#puts caller
- @cores = cores
- @orders = []
- @limit = nil
- @lock = nil
- @offset = nil
+ @cores = cores
+ @orders = []
+ @limit = nil
+ @lock = nil
+ @offset = nil
+ @with = nil
end
def initialize_copy other
diff --git a/lib/arel/nodes/with.rb b/lib/arel/nodes/with.rb
new file mode 100644
index 0000000000..7f08abe47d
--- /dev/null
+++ b/lib/arel/nodes/with.rb
@@ -0,0 +1,10 @@
+module Arel
+ module Nodes
+ class With < Arel::Nodes::Unary
+ alias children expr
+ end
+
+ class WithRecursive < With; end
+ end
+end
+
diff --git a/lib/arel/select_manager.rb b/lib/arel/select_manager.rb
index b4103144fe..2747bdfda8 100644
--- a/lib/arel/select_manager.rb
+++ b/lib/arel/select_manager.rb
@@ -31,7 +31,7 @@ module Arel
def where_clauses
if $VERBOSE
- warn "(#{caller.first}) where_clauses is deprecated and will be removed in arel 3.0.0 with no replacement"
+ warn "(#{caller.first}) where_clauses is deprecated and will be removed in arel 3.0.0 with no replacement"
end
to_sql = Visitors::ToSql.new @engine
@ctx.wheres.map { |c| to_sql.accept c }
@@ -134,6 +134,37 @@ module Arel
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 with *subqueries
+ if subqueries.first.is_a? Symbol
+ node_class = Nodes.const_get("With#{subqueries.shift.to_s.capitalize}")
+ else
+ node_class = Nodes::With
+ end
+ @ast.with = node_class.new(subqueries.flatten)
+
+ self
+ end
+
def take limit
@ast.limit = Nodes::Limit.new(limit)
@ctx.top = Nodes::Top.new(limit)
diff --git a/lib/arel/visitors/mssql.rb b/lib/arel/visitors/mssql.rb
index 9b0e77c19b..ea7ab6394c 100644
--- a/lib/arel/visitors/mssql.rb
+++ b/lib/arel/visitors/mssql.rb
@@ -3,6 +3,13 @@ module Arel
class MSSQL < Arel::Visitors::ToSql
private
+ def build_subselect key, o
+ stmt = super
+ core = stmt.cores.first
+ core.top = Nodes::Top.new(o.limit.expr) if o.limit
+ stmt
+ end
+
def visit_Arel_Nodes_Limit o
""
end
diff --git a/lib/arel/visitors/oracle.rb b/lib/arel/visitors/oracle.rb
index aaf0324fd7..3beea287a6 100644
--- a/lib/arel/visitors/oracle.rb
+++ b/lib/arel/visitors/oracle.rb
@@ -61,6 +61,10 @@ module Arel
"raw_rnum_ > #{visit o.expr}"
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
diff --git a/lib/arel/visitors/to_sql.rb b/lib/arel/visitors/to_sql.rb
index d5534384f8..61f68a8c09 100644
--- a/lib/arel/visitors/to_sql.rb
+++ b/lib/arel/visitors/to_sql.rb
@@ -38,6 +38,17 @@ module Arel
].compact.join ' '
end
+ # FIXME: we should probably have a 2-pass visitor for this
+ def build_subselect key, o
+ stmt = Nodes::SelectStatement.new
+ core = stmt.cores.first
+ core.froms = o.relation
+ core.projections = [key]
+ stmt.limit = o.limit
+ stmt.orders = o.orders
+ stmt
+ end
+
def visit_Arel_Nodes_UpdateStatement o
if o.orders.empty? && o.limit.nil?
wheres = o.wheres
@@ -48,19 +59,11 @@ module Arel
(#{caller.first}) Using UpdateManager without setting UpdateManager#key is
deprecated and support will be removed in ARel 3.0.0. Please set the primary
key on UpdateManager using UpdateManager#key=
-eowarn
+ eowarn
key = o.relation.primary_key
end
- wheres = o.wheres
- stmt = Nodes::SelectStatement.new
- core = stmt.cores.first
- core.froms = o.relation
- core.projections = [key]
- stmt.limit = o.limit
- stmt.orders = o.orders
-
- wheres = [Nodes::In.new(key, [stmt])]
+ wheres = [Nodes::In.new(key, [build_subselect(key, o)])]
end
[
@@ -75,8 +78,8 @@ eowarn
"INSERT INTO #{visit o.relation}",
("(#{o.columns.map { |x|
- quote_column_name x.name
- }.join ', '})" unless o.columns.empty?),
+ quote_column_name x.name
+ }.join ', '})" unless o.columns.empty?),
(visit o.values if o.values),
].compact.join ' '
@@ -129,6 +132,7 @@ eowarn
def visit_Arel_Nodes_SelectStatement o
[
+ (visit(o.with) if o.with),
o.cores.map { |x| visit_Arel_Nodes_SelectCore x }.join,
("ORDER BY #{o.orders.map { |x| visit x }.join(', ')}" unless o.orders.empty?),
(visit(o.limit) if o.limit),
@@ -149,6 +153,30 @@ eowarn
].compact.join ' '
end
+ def visit_Arel_Nodes_With o
+ "WITH #{o.children.map { |x| visit x }.join(', ')}"
+ end
+
+ def visit_Arel_Nodes_WithRecursive o
+ "WITH RECURSIVE #{o.children.map { |x| visit x }.join(', ')}"
+ 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_Nodes_Having o
"HAVING #{visit o.expr}"
end
@@ -286,11 +314,11 @@ eowarn
end
def visit_Arel_Nodes_In o
- "#{visit o.left} IN (#{visit o.right})"
+ "#{visit o.left} IN (#{visit o.right})"
end
def visit_Arel_Nodes_NotIn o
- "#{visit o.left} NOT IN (#{visit o.right})"
+ "#{visit o.left} NOT IN (#{visit o.right})"
end
def visit_Arel_Nodes_And o