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/named_function.rb2
-rw-r--r--lib/arel/nodes/node.rb2
-rw-r--r--lib/arel/nodes/select_statement.rb13
-rw-r--r--lib/arel/nodes/with.rb10
-rw-r--r--lib/arel/predications.rb2
-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.rb87
11 files changed, 115 insertions, 50 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/named_function.rb b/lib/arel/nodes/named_function.rb
index 56669bf858..5fca33e323 100644
--- a/lib/arel/nodes/named_function.rb
+++ b/lib/arel/nodes/named_function.rb
@@ -3,6 +3,8 @@ module Arel
class NamedFunction < Arel::Nodes::Function
attr_accessor :name
+ include Arel::Predications
+
def initialize name, expr, aliaz = nil
super(expr, aliaz)
@name = name
diff --git a/lib/arel/nodes/node.rb b/lib/arel/nodes/node.rb
index 711fa34b6d..1a5bc27856 100644
--- a/lib/arel/nodes/node.rb
+++ b/lib/arel/nodes/node.rb
@@ -10,7 +10,7 @@ module Arel
# Factory method to create a Nodes::Not node that has the recipient of
# the caller as a child.
def not
- Nodes::Not.new Nodes::Grouping.new self
+ Nodes::Not.new self
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/predications.rb b/lib/arel/predications.rb
index 58f02a2b53..920a9ee374 100644
--- a/lib/arel/predications.rb
+++ b/lib/arel/predications.rb
@@ -163,6 +163,7 @@ module Arel
private
def grouping_any method_id, others
+ others = others.dup
first = send method_id, others.shift
Nodes::Grouping.new others.inject(first) { |memo,expr|
@@ -171,6 +172,7 @@ module Arel
end
def grouping_all method_id, others
+ others = others.dup
first = send method_id, others.shift
Nodes::Grouping.new others.inject(first) { |memo,expr|
diff --git a/lib/arel/select_manager.rb b/lib/arel/select_manager.rb
index fce6ca17d8..249ad680a7 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..70e10a5e0f 100644
--- a/lib/arel/visitors/to_sql.rb
+++ b/lib/arel/visitors/to_sql.rb
@@ -11,14 +11,6 @@ module Arel
@last_column = nil
@quoted_tables = {}
@quoted_columns = {}
- @column_cache = Hash.new { |h,pool|
- h[pool] = Hash.new { |conn_h,column|
- conn_h[column] = {}
- }
- }
- @table_exists = Hash.new { |h,pool|
- h[pool] = {}
- }
end
def accept object
@@ -38,6 +30,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 +51,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 +70,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 ' '
@@ -88,37 +83,20 @@ eowarn
end
def table_exists? name
- return true if table_exists.key? name
-
- @connection.tables.each do |table|
- table_exists[table] = true
- end
-
- table_exists.key? name
- end
-
- def table_exists
- @table_exists[@pool]
+ @pool.table_exists? name
end
def column_for attr
- name = attr.name.to_sym
+ name = attr.name.to_s
table = attr.relation.name
return nil unless table_exists? table
- # If we don't have this column cached, get a list of columns and
- # cache them for this table
- unless column_cache.key? table
- columns = @connection.columns(table, "#{table}(#{name}) Columns")
- column_cache[table] = Hash[columns.map { |c| [c.name.to_sym, c] }]
- end
-
column_cache[table][name]
end
def column_cache
- @column_cache[@pool]
+ @pool.columns_hash
end
def visit_Arel_Nodes_Values o
@@ -129,6 +107,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 +128,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 +289,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