diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/arel/crud.rb | 1 | ||||
-rw-r--r-- | lib/arel/delete_manager.rb | 5 | ||||
-rw-r--r-- | lib/arel/nodes.rb | 42 | ||||
-rw-r--r-- | lib/arel/nodes/binary.rb | 2 | ||||
-rw-r--r-- | lib/arel/nodes/casted.rb | 40 | ||||
-rw-r--r-- | lib/arel/nodes/delete_statement.rb | 2 | ||||
-rw-r--r-- | lib/arel/nodes/function.rb | 1 | ||||
-rw-r--r-- | lib/arel/nodes/matches.rb | 4 | ||||
-rw-r--r-- | lib/arel/nodes/regexp.rb | 14 | ||||
-rw-r--r-- | lib/arel/predications.rb | 24 | ||||
-rw-r--r-- | lib/arel/select_manager.rb | 6 | ||||
-rw-r--r-- | lib/arel/visitors.rb | 1 | ||||
-rw-r--r-- | lib/arel/visitors/mssql.rb | 17 | ||||
-rw-r--r-- | lib/arel/visitors/mysql.rb | 2 | ||||
-rw-r--r-- | lib/arel/visitors/oracle.rb | 2 | ||||
-rw-r--r-- | lib/arel/visitors/oracle12.rb | 53 | ||||
-rw-r--r-- | lib/arel/visitors/postgresql.rb | 12 | ||||
-rw-r--r-- | lib/arel/visitors/to_sql.rb | 32 |
18 files changed, 189 insertions, 71 deletions
diff --git a/lib/arel/crud.rb b/lib/arel/crud.rb index 2dfe27445c..d310c7381f 100644 --- a/lib/arel/crud.rb +++ b/lib/arel/crud.rb @@ -31,6 +31,7 @@ module Arel def compile_delete dm = DeleteManager.new + dm.take @ast.limit.expr if @ast.limit dm.wheres = @ctx.wheres dm.from @ctx.froms dm diff --git a/lib/arel/delete_manager.rb b/lib/arel/delete_manager.rb index af33c60740..20e988e01f 100644 --- a/lib/arel/delete_manager.rb +++ b/lib/arel/delete_manager.rb @@ -11,6 +11,11 @@ module Arel self end + def take limit + @ast.limit = Nodes::Limit.new(Nodes.build_quoted(limit)) if limit + self + end + def wheres= list @ast.wheres = list end diff --git a/lib/arel/nodes.rb b/lib/arel/nodes.rb index 7d900fe710..0e66d2dd0c 100644 --- a/lib/arel/nodes.rb +++ b/lib/arel/nodes.rb @@ -30,6 +30,7 @@ require 'arel/nodes/table_alias' require 'arel/nodes/infix_operation' require 'arel/nodes/over' require 'arel/nodes/matches' +require 'arel/nodes/regexp' # nary require 'arel/nodes/and' @@ -55,43 +56,4 @@ require 'arel/nodes/string_join' require 'arel/nodes/sql_literal' -module Arel - module Nodes - class Casted < Arel::Nodes::Node # :nodoc: - attr_reader :val, :attribute - def initialize val, attribute - @val = val - @attribute = attribute - super() - end - - def nil?; @val.nil?; end - - def eql? other - self.class == other.class && - self.val == other.val && - self.attribute == other.attribute - end - alias :== :eql? - end - - class Quoted < Arel::Nodes::Unary # :nodoc: - alias :val :value - def nil?; val.nil?; end - end - - def self.build_quoted other, attribute = nil - case other - when Arel::Nodes::Node, Arel::Attributes::Attribute, Arel::Table, Arel::Nodes::BindParam, Arel::SelectManager, Arel::Nodes::Quoted - other - else - case attribute - when Arel::Attributes::Attribute - Casted.new other, attribute - else - Quoted.new other - end - end - end - end -end +require 'arel/nodes/casted' diff --git a/lib/arel/nodes/binary.rb b/lib/arel/nodes/binary.rb index dddbde1431..763091c267 100644 --- a/lib/arel/nodes/binary.rb +++ b/lib/arel/nodes/binary.rb @@ -38,9 +38,7 @@ module Arel LessThanOrEqual NotEqual NotIn - NotRegexp Or - Regexp Union UnionAll Intersect diff --git a/lib/arel/nodes/casted.rb b/lib/arel/nodes/casted.rb new file mode 100644 index 0000000000..9fa02955ef --- /dev/null +++ b/lib/arel/nodes/casted.rb @@ -0,0 +1,40 @@ +module Arel + module Nodes + class Casted < Arel::Nodes::Node # :nodoc: + attr_reader :val, :attribute + def initialize val, attribute + @val = val + @attribute = attribute + super() + end + + def nil?; @val.nil?; end + + def eql? other + self.class == other.class && + self.val == other.val && + self.attribute == other.attribute + end + alias :== :eql? + end + + class Quoted < Arel::Nodes::Unary # :nodoc: + alias :val :value + def nil?; val.nil?; end + end + + def self.build_quoted other, attribute = nil + case other + when Arel::Nodes::Node, Arel::Attributes::Attribute, Arel::Table, Arel::Nodes::BindParam, Arel::SelectManager, Arel::Nodes::Quoted + other + else + case attribute + when Arel::Attributes::Attribute + Casted.new other, attribute + else + Quoted.new other + end + end + end + end +end diff --git a/lib/arel/nodes/delete_statement.rb b/lib/arel/nodes/delete_statement.rb index 3bac8225ec..8aaf8ca0b6 100644 --- a/lib/arel/nodes/delete_statement.rb +++ b/lib/arel/nodes/delete_statement.rb @@ -1,6 +1,8 @@ module Arel module Nodes class DeleteStatement < Arel::Nodes::Binary + attr_accessor :limit + alias :relation :left alias :relation= :left= alias :wheres :right diff --git a/lib/arel/nodes/function.rb b/lib/arel/nodes/function.rb index 733a00df46..182dfa7329 100644 --- a/lib/arel/nodes/function.rb +++ b/lib/arel/nodes/function.rb @@ -3,6 +3,7 @@ module Arel class Function < Arel::Nodes::Node include Arel::Predications include Arel::WindowPredications + include Arel::OrderPredications attr_accessor :expressions, :alias, :distinct def initialize expr, aliaz = nil diff --git a/lib/arel/nodes/matches.rb b/lib/arel/nodes/matches.rb index 583fb97c9b..0d9c1925dc 100644 --- a/lib/arel/nodes/matches.rb +++ b/lib/arel/nodes/matches.rb @@ -2,10 +2,12 @@ module Arel module Nodes class Matches < Binary attr_reader :escape + attr_accessor :case_sensitive - def initialize(left, right, escape = nil) + def initialize(left, right, escape = nil, case_sensitive = false) super(left, right) @escape = escape && Nodes.build_quoted(escape) + @case_sensitive = case_sensitive end end diff --git a/lib/arel/nodes/regexp.rb b/lib/arel/nodes/regexp.rb new file mode 100644 index 0000000000..784368f5bf --- /dev/null +++ b/lib/arel/nodes/regexp.rb @@ -0,0 +1,14 @@ +module Arel + module Nodes + class Regexp < Binary + attr_accessor :case_sensitive + + def initialize(left, right, case_sensitive = true) + super(left, right) + @case_sensitive = case_sensitive + end + end + + class NotRegexp < Regexp; end + end +end diff --git a/lib/arel/predications.rb b/lib/arel/predications.rb index b05fc6f99a..1d2b0de235 100644 --- a/lib/arel/predications.rb +++ b/lib/arel/predications.rb @@ -118,20 +118,28 @@ Passing a range to `#not_in` is deprecated. Call `#not_between`, instead. grouping_all :not_in, others end - def matches other, escape = nil - Nodes::Matches.new self, quoted_node(other), escape + def matches other, escape = nil, case_sensitive = false + Nodes::Matches.new self, quoted_node(other), escape, case_sensitive end - def matches_any others, escape = nil - grouping_any :matches, others, escape + def matches_regexp other, case_sensitive = true + Nodes::Regexp.new self, quoted_node(other), case_sensitive end - def matches_all others, escape = nil - grouping_all :matches, others, escape + def matches_any others, escape = nil, case_sensitive = false + grouping_any :matches, others, escape, case_sensitive end - def does_not_match other, escape = nil - Nodes::DoesNotMatch.new self, quoted_node(other), escape + def matches_all others, escape = nil, case_sensitive = false + grouping_all :matches, others, escape, case_sensitive + end + + def does_not_match other, escape = nil, case_sensitive = false + Nodes::DoesNotMatch.new self, quoted_node(other), escape, case_sensitive + end + + def does_not_match_regexp other, case_sensitive = true + Nodes::NotRegexp.new self, quoted_node(other), case_sensitive end def does_not_match_any others, escape = nil diff --git a/lib/arel/select_manager.rb b/lib/arel/select_manager.rb index e5fdbc887c..f7dec87ca3 100644 --- a/lib/arel/select_manager.rb +++ b/lib/arel/select_manager.rb @@ -19,7 +19,7 @@ module Arel end def limit - @ast.limit && @ast.limit.expr.expr + @ast.limit && @ast.limit.expr end alias :taken :limit @@ -216,8 +216,8 @@ module Arel def take limit if limit - @ast.limit = Nodes::Limit.new(Nodes.build_quoted(limit)) - @ctx.top = Nodes::Top.new(Nodes.build_quoted(limit)) + @ast.limit = Nodes::Limit.new(limit) + @ctx.top = Nodes::Top.new(limit) else @ast.limit = nil @ctx.top = nil diff --git a/lib/arel/visitors.rb b/lib/arel/visitors.rb index 4a8d254ba7..f492ca2d9d 100644 --- a/lib/arel/visitors.rb +++ b/lib/arel/visitors.rb @@ -6,6 +6,7 @@ require 'arel/visitors/postgresql' require 'arel/visitors/mysql' require 'arel/visitors/mssql' require 'arel/visitors/oracle' +require 'arel/visitors/oracle12' require 'arel/visitors/where_sql' require 'arel/visitors/dot' require 'arel/visitors/ibm_db' diff --git a/lib/arel/visitors/mssql.rb b/lib/arel/visitors/mssql.rb index 7c65ad33f2..92362a0c5f 100644 --- a/lib/arel/visitors/mssql.rb +++ b/lib/arel/visitors/mssql.rb @@ -66,6 +66,23 @@ module Arel end end + def visit_Arel_Nodes_DeleteStatement o, collector + collector << 'DELETE ' + if o.limit + collector << 'TOP (' + visit o.limit.expr, collector + collector << ') ' + end + collector << 'FROM ' + collector = visit o.relation, collector + if o.wheres.any? + collector << ' WHERE ' + inject_join o.wheres, collector, AND + else + collector + end + end + def determine_order_by orders, x if orders.any? orders diff --git a/lib/arel/visitors/mysql.rb b/lib/arel/visitors/mysql.rb index f989b8ddef..724e0fc43e 100644 --- a/lib/arel/visitors/mysql.rb +++ b/lib/arel/visitors/mysql.rb @@ -40,7 +40,7 @@ module Arel # http://dev.mysql.com/doc/refman/5.0/en/select.html#id3482214 def visit_Arel_Nodes_SelectStatement o, collector if o.offset && !o.limit - o.limit = Arel::Nodes::Limit.new(Nodes.build_quoted(18446744073709551615)) + o.limit = Arel::Nodes::Limit.new(18446744073709551615) end super end diff --git a/lib/arel/visitors/oracle.rb b/lib/arel/visitors/oracle.rb index ff9e38d050..875b0e5b6a 100644 --- a/lib/arel/visitors/oracle.rb +++ b/lib/arel/visitors/oracle.rb @@ -17,7 +17,7 @@ module Arel if o.limit && o.offset o = o.dup - limit = o.limit.expr.expr + limit = o.limit.expr offset = o.offset o.offset = nil collector << " diff --git a/lib/arel/visitors/oracle12.rb b/lib/arel/visitors/oracle12.rb new file mode 100644 index 0000000000..4a42343c9b --- /dev/null +++ b/lib/arel/visitors/oracle12.rb @@ -0,0 +1,53 @@ +module Arel + module Visitors + class Oracle12 < Arel::Visitors::ToSql + private + + def visit_Arel_Nodes_SelectStatement o, collector + # Oracle does not allow LIMIT clause with select for update + if o.limit && o.lock + o = o.dup + o.limit = [] + end + + super + end + + def visit_Arel_Nodes_SelectOptions o, collector + collector = maybe_visit o.offset, collector + collector = maybe_visit o.limit, collector + collector = maybe_visit o.lock, collector + end + + def visit_Arel_Nodes_Limit o, collector + collector << "FETCH FIRST " + collector = visit o.expr, collector + collector << " ROWS ONLY" + end + + def visit_Arel_Nodes_Offset o, collector + collector << "OFFSET " + visit o.expr, collector + collector << " ROWS" + end + + def visit_Arel_Nodes_Except o, collector + collector << "( " + collector = infix_value o, collector, " MINUS " + collector << " )" + end + + def visit_Arel_Nodes_UpdateStatement o, collector + # Oracle does not allow ORDER BY/LIMIT in UPDATEs. + if o.orders.any? && o.limit.nil? + # However, there is no harm in silently eating the ORDER BY clause if no LIMIT has been provided, + # otherwise let the user deal with the error + o = o.dup + o.orders = [] + end + + super + end + end + end +end diff --git a/lib/arel/visitors/postgresql.rb b/lib/arel/visitors/postgresql.rb index bd23fc0a47..1ef0261bdd 100644 --- a/lib/arel/visitors/postgresql.rb +++ b/lib/arel/visitors/postgresql.rb @@ -4,7 +4,8 @@ module Arel private def visit_Arel_Nodes_Matches o, collector - collector = infix_value o, collector, ' ILIKE ' + op = o.case_sensitive ? ' LIKE ' : ' ILIKE ' + collector = infix_value o, collector, op if o.escape collector << ' ESCAPE ' visit o.escape, collector @@ -14,7 +15,8 @@ module Arel end def visit_Arel_Nodes_DoesNotMatch o, collector - collector = infix_value o, collector, ' NOT ILIKE ' + op = o.case_sensitive ? ' NOT LIKE ' : ' NOT ILIKE ' + collector = infix_value o, collector, op if o.escape collector << ' ESCAPE ' visit o.escape, collector @@ -24,11 +26,13 @@ module Arel end def visit_Arel_Nodes_Regexp o, collector - infix_value o, collector, ' ~ ' + op = o.case_sensitive ? ' ~ ' : ' ~* ' + infix_value o, collector, op end def visit_Arel_Nodes_NotRegexp o, collector - infix_value o, collector, ' !~ ' + op = o.case_sensitive ? ' !~ ' : ' !~* ' + infix_value o, collector, op end def visit_Arel_Nodes_DistinctOn o, collector diff --git a/lib/arel/visitors/to_sql.rb b/lib/arel/visitors/to_sql.rb index 7dfa86a575..f2f9d20f21 100644 --- a/lib/arel/visitors/to_sql.rb +++ b/lib/arel/visitors/to_sql.rb @@ -74,14 +74,14 @@ module Arel end def visit_Arel_Nodes_DeleteStatement o, collector - collector << "DELETE FROM " + collector << 'DELETE FROM ' collector = visit o.relation, collector if o.wheres.any? - collector << " WHERE " - inject_join o.wheres, collector, AND - else - collector + collector << ' WHERE ' + collector = inject_join o.wheres, collector, AND end + + maybe_visit o.limit, collector end # FIXME: we should probably have a 2-pass visitor for this @@ -219,11 +219,15 @@ module Arel } end + visit_Arel_Nodes_SelectOptions(o, collector) + + collector + end + + def visit_Arel_Nodes_SelectOptions o, collector collector = maybe_visit o.limit, collector collector = maybe_visit o.offset, collector collector = maybe_visit o.lock, collector - - collector end def visit_Arel_Nodes_SelectCore o, collector @@ -572,8 +576,11 @@ module Arel visit o.left, collector end - def visit_Arel_Nodes_FullOuterJoin o - "FULL OUTER JOIN #{visit o.left} #{visit o.right}" + def visit_Arel_Nodes_FullOuterJoin o, collector + collector << "FULL OUTER JOIN " + collector = visit o.left, collector + collector << SPACE + visit o.right, collector end def visit_Arel_Nodes_OuterJoin o, collector @@ -583,8 +590,11 @@ module Arel visit o.right, collector end - def visit_Arel_Nodes_RightOuterJoin o - "RIGHT OUTER JOIN #{visit o.left} #{visit o.right}" + def visit_Arel_Nodes_RightOuterJoin o, collector + collector << "RIGHT OUTER JOIN " + collector = visit o.left, collector + collector << SPACE + visit o.right, collector end def visit_Arel_Nodes_InnerJoin o, collector |