diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/arel/nodes.rb | 1 | ||||
-rw-r--r-- | lib/arel/nodes/binary.rb | 4 | ||||
-rw-r--r-- | lib/arel/nodes/named_function.rb | 2 | ||||
-rw-r--r-- | lib/arel/nodes/node.rb | 2 | ||||
-rw-r--r-- | lib/arel/nodes/select_statement.rb | 13 | ||||
-rw-r--r-- | lib/arel/nodes/with.rb | 10 | ||||
-rw-r--r-- | lib/arel/predications.rb | 2 | ||||
-rw-r--r-- | lib/arel/select_manager.rb | 33 | ||||
-rw-r--r-- | lib/arel/visitors/mssql.rb | 7 | ||||
-rw-r--r-- | lib/arel/visitors/oracle.rb | 4 | ||||
-rw-r--r-- | lib/arel/visitors/to_sql.rb | 87 |
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 |