diff options
Diffstat (limited to 'lib/arel')
52 files changed, 429 insertions, 362 deletions
diff --git a/lib/arel/attributes/attribute.rb b/lib/arel/attributes/attribute.rb index 5cbe194b41..9a42e5a4da 100644 --- a/lib/arel/attributes/attribute.rb +++ b/lib/arel/attributes/attribute.rb @@ -1,6 +1,6 @@ module Arel module Attributes - class Attribute < Struct.new :relation, :name, :column + class Attribute < Struct.new :relation, :name include Arel::Expressions include Arel::Predications end diff --git a/lib/arel/crud.rb b/lib/arel/crud.rb index b3199e6d38..e6b1cd9675 100644 --- a/lib/arel/crud.rb +++ b/lib/arel/crud.rb @@ -2,12 +2,11 @@ module Arel ### # FIXME hopefully we can remove this module Crud - # FIXME: this method should go away - def update values + def compile_update values um = UpdateManager.new @engine if Nodes::SqlLiteral === values - relation = @ctx.froms + relation = @ctx.from else relation = values.first.first.relation end @@ -16,22 +15,54 @@ module Arel um.take @ast.limit um.order(*@ast.orders) um.wheres = @ctx.wheres + um + end + + # FIXME: this method should go away + def update values + if $VERBOSE + warn <<-eowarn +update (#{caller.first}) is deprecated and will be removed in ARel 3.0.0. Please +switch to `compile_update` + eowarn + end + um = compile_update values @engine.connection.update um.to_sql, 'AREL' end - # FIXME: this method should go away - def insert values + def compile_insert values im = InsertManager.new @engine im.insert values - @engine.connection.insert im.to_sql + im end - def delete + # FIXME: this method should go away + def insert values + if $VERBOSE + warn <<-eowarn +insert (#{caller.first}) is deprecated and will be removed in ARel 3.0.0. Please +switch to `compile_insert` + eowarn + end + @engine.connection.insert compile_insert(values).to_sql + end + + def compile_delete dm = DeleteManager.new @engine dm.wheres = @ctx.wheres dm.from @ctx.froms - @engine.connection.delete dm.to_sql, 'AREL' + dm + end + + def delete + if $VERBOSE + warn <<-eowarn +delete (#{caller.first}) is deprecated and will be removed in ARel 3.0.0. Please +switch to `compile_delete` + eowarn + end + @engine.connection.delete compile_delete.to_sql, 'AREL' end end end diff --git a/lib/arel/factory_methods.rb b/lib/arel/factory_methods.rb new file mode 100644 index 0000000000..09b82c0555 --- /dev/null +++ b/lib/arel/factory_methods.rb @@ -0,0 +1,25 @@ +module Arel + ### + # Methods for creating various nodes + module FactoryMethods + def create_join to, constraint = nil, klass = Nodes::InnerJoin + klass.new(to, constraint) + end + + def create_string_join to + create_join to, nil, Nodes::StringJoin + end + + def create_and clauses + Nodes::And.new clauses + end + + def create_on expr + Nodes::On.new expr + end + + def grouping expr + Nodes::Grouping.new expr + end + end +end diff --git a/lib/arel/nodes.rb b/lib/arel/nodes.rb index db2f22750a..c43134bb50 100644 --- a/lib/arel/nodes.rb +++ b/lib/arel/nodes.rb @@ -1,49 +1,37 @@ +# node require 'arel/nodes/node' +require 'arel/nodes/lock' +require 'arel/nodes/select_statement' +require 'arel/nodes/select_core' +require 'arel/nodes/insert_statement' +require 'arel/nodes/update_statement' + +# unary require 'arel/nodes/unary' +require 'arel/nodes/unqualified_column' + +# binary require 'arel/nodes/binary' require 'arel/nodes/equality' -require 'arel/nodes/between' -require 'arel/nodes/not_equal' -require 'arel/nodes/assignment' -require 'arel/nodes/or' +require 'arel/nodes/in' # Why is this subclassed from equality? +require 'arel/nodes/join_source' +require 'arel/nodes/ordering' +require 'arel/nodes/delete_statement' +require 'arel/nodes/table_alias' + +# nary require 'arel/nodes/and' -require 'arel/nodes/as' -require 'arel/nodes/not' -require 'arel/nodes/greater_than' -require 'arel/nodes/greater_than_or_equal' -require 'arel/nodes/less_than' -require 'arel/nodes/less_than_or_equal' -require 'arel/nodes/matches' -require 'arel/nodes/does_not_match' -require 'arel/nodes/in' -require 'arel/nodes/not_in' -require 'arel/nodes/ordering' -require 'arel/nodes/lock' +# function +# FIXME: Function + Alias can be rewritten as a Function and Alias node. +# We should make Function a Unary node and deprecate the use of "aliaz" require 'arel/nodes/function' require 'arel/nodes/count' require 'arel/nodes/values' -require 'arel/nodes/offset' -require 'arel/nodes/limit' -require 'arel/nodes/top' -require 'arel/nodes/sum' -require 'arel/nodes/exists' -require 'arel/nodes/max' -require 'arel/nodes/min' -require 'arel/nodes/avg' -require 'arel/nodes/having' -require 'arel/nodes/sql_literal' -require 'arel/nodes/select_core' -require 'arel/nodes/select_statement' -require 'arel/nodes/insert_statement' -require 'arel/nodes/update_statement' -require 'arel/nodes/delete_statement' -require 'arel/nodes/unqualified_column' -require 'arel/nodes/table_alias' -require 'arel/nodes/join' -require 'arel/nodes/group' -require 'arel/nodes/grouping' + +# joins require 'arel/nodes/inner_join' require 'arel/nodes/outer_join' require 'arel/nodes/string_join' -require 'arel/nodes/on' + +require 'arel/nodes/sql_literal' diff --git a/lib/arel/nodes/and.rb b/lib/arel/nodes/and.rb index 80f420b4f1..b4443c3d27 100644 --- a/lib/arel/nodes/and.rb +++ b/lib/arel/nodes/and.rb @@ -1,6 +1,23 @@ module Arel module Nodes - class And < Arel::Nodes::Binary + class And < Arel::Nodes::Node + attr_reader :children + + def initialize children, right = nil + unless Array === children + warn "(#{caller.first}) AND nodes should be created with a list" + children = [children, right] + end + @children = children + end + + def left + children.first + end + + def right + children[1] + end end end end diff --git a/lib/arel/nodes/as.rb b/lib/arel/nodes/as.rb deleted file mode 100644 index 9009fe12f4..0000000000 --- a/lib/arel/nodes/as.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Arel - module Nodes - class As < Arel::Nodes::Binary - end - end -end diff --git a/lib/arel/nodes/assignment.rb b/lib/arel/nodes/assignment.rb deleted file mode 100644 index 693bd5afe6..0000000000 --- a/lib/arel/nodes/assignment.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Arel - module Nodes - class Assignment < Arel::Nodes::Binary - end - end -end diff --git a/lib/arel/nodes/avg.rb b/lib/arel/nodes/avg.rb deleted file mode 100644 index 8fc86fc21e..0000000000 --- a/lib/arel/nodes/avg.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Arel - module Nodes - class Avg < Arel::Nodes::Function - end - end -end diff --git a/lib/arel/nodes/between.rb b/lib/arel/nodes/between.rb deleted file mode 100644 index 2e7596cdae..0000000000 --- a/lib/arel/nodes/between.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Arel - module Nodes - class Between < Arel::Nodes::Binary - end - end -end diff --git a/lib/arel/nodes/binary.rb b/lib/arel/nodes/binary.rb index cfa75909c5..0d02554199 100644 --- a/lib/arel/nodes/binary.rb +++ b/lib/arel/nodes/binary.rb @@ -7,6 +7,30 @@ module Arel @left = left @right = right end + + def initialize_copy other + super + @left = @left.clone if @left + @right = @right.clone if @right + end + end + + %w{ + As + Assignment + Between + DoesNotMatch + GreaterThan + GreaterThanOrEqual + Join + LessThan + LessThanOrEqual + Matches + NotEqual + NotIn + Or + }.each do |name| + const_set name, Class.new(Binary) end end end diff --git a/lib/arel/nodes/does_not_match.rb b/lib/arel/nodes/does_not_match.rb deleted file mode 100644 index 33bdeab005..0000000000 --- a/lib/arel/nodes/does_not_match.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Arel - module Nodes - class DoesNotMatch < Arel::Nodes::Binary - end - end -end diff --git a/lib/arel/nodes/exists.rb b/lib/arel/nodes/exists.rb deleted file mode 100644 index 18ba8403b4..0000000000 --- a/lib/arel/nodes/exists.rb +++ /dev/null @@ -1,7 +0,0 @@ -module Arel - module Nodes - class Exists < Arel::Nodes::Function - alias :select_stmt :expressions - end - end -end diff --git a/lib/arel/nodes/function.rb b/lib/arel/nodes/function.rb index 133dd66019..e4e45bff31 100644 --- a/lib/arel/nodes/function.rb +++ b/lib/arel/nodes/function.rb @@ -14,5 +14,15 @@ module Arel self end end + + %w{ + Sum + Exists + Max + Min + Avg + }.each do |name| + const_set(name, Class.new(Function)) + end end end diff --git a/lib/arel/nodes/greater_than.rb b/lib/arel/nodes/greater_than.rb deleted file mode 100644 index 2e03cc2e18..0000000000 --- a/lib/arel/nodes/greater_than.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Arel - module Nodes - class GreaterThan < Arel::Nodes::Binary - end - end -end diff --git a/lib/arel/nodes/greater_than_or_equal.rb b/lib/arel/nodes/greater_than_or_equal.rb deleted file mode 100644 index a8cfaab04e..0000000000 --- a/lib/arel/nodes/greater_than_or_equal.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Arel - module Nodes - class GreaterThanOrEqual < Arel::Nodes::Binary - end - end -end diff --git a/lib/arel/nodes/group.rb b/lib/arel/nodes/group.rb deleted file mode 100644 index a7fa6f170d..0000000000 --- a/lib/arel/nodes/group.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Arel - module Nodes - class Group < Arel::Nodes::Unary - end - end -end diff --git a/lib/arel/nodes/grouping.rb b/lib/arel/nodes/grouping.rb deleted file mode 100644 index 18adeae97f..0000000000 --- a/lib/arel/nodes/grouping.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Arel - module Nodes - class Grouping < Arel::Nodes::Unary - end - end -end diff --git a/lib/arel/nodes/having.rb b/lib/arel/nodes/having.rb deleted file mode 100644 index 6972c58dda..0000000000 --- a/lib/arel/nodes/having.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Arel - module Nodes - class Having < Arel::Nodes::Unary - end - end -end diff --git a/lib/arel/nodes/join.rb b/lib/arel/nodes/join.rb deleted file mode 100644 index 07f8c98e85..0000000000 --- a/lib/arel/nodes/join.rb +++ /dev/null @@ -1,13 +0,0 @@ -module Arel - module Nodes - class Join < Arel::Nodes::Node - attr_accessor :left, :right, :constraint - - def initialize left, right, constraint - @left = left - @right = right - @constraint = constraint - end - end - end -end diff --git a/lib/arel/nodes/join_source.rb b/lib/arel/nodes/join_source.rb new file mode 100644 index 0000000000..c57ad0e930 --- /dev/null +++ b/lib/arel/nodes/join_source.rb @@ -0,0 +1,14 @@ +module Arel + module Nodes + ### + # Class that represents a join source + # + # http://www.sqlite.org/syntaxdiagrams.html#join-source + + class JoinSource < Arel::Nodes::Binary + def initialize single_source, joinop = [] + super + end + end + end +end diff --git a/lib/arel/nodes/less_than.rb b/lib/arel/nodes/less_than.rb deleted file mode 100644 index cfaf716c42..0000000000 --- a/lib/arel/nodes/less_than.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Arel - module Nodes - class LessThan < Arel::Nodes::Binary - end - end -end diff --git a/lib/arel/nodes/less_than_or_equal.rb b/lib/arel/nodes/less_than_or_equal.rb deleted file mode 100644 index 55449d12f1..0000000000 --- a/lib/arel/nodes/less_than_or_equal.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Arel - module Nodes - class LessThanOrEqual < Arel::Nodes::Binary - end - end -end diff --git a/lib/arel/nodes/limit.rb b/lib/arel/nodes/limit.rb deleted file mode 100644 index 68ea95daf5..0000000000 --- a/lib/arel/nodes/limit.rb +++ /dev/null @@ -1,7 +0,0 @@ -module Arel - module Nodes - class Limit < Arel::Nodes::Unary - end - end -end - diff --git a/lib/arel/nodes/matches.rb b/lib/arel/nodes/matches.rb deleted file mode 100644 index 5ef8ac8302..0000000000 --- a/lib/arel/nodes/matches.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Arel - module Nodes - class Matches < Arel::Nodes::Binary - end - end -end diff --git a/lib/arel/nodes/max.rb b/lib/arel/nodes/max.rb deleted file mode 100644 index 5af611b0d6..0000000000 --- a/lib/arel/nodes/max.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Arel - module Nodes - class Max < Arel::Nodes::Function - end - end -end diff --git a/lib/arel/nodes/min.rb b/lib/arel/nodes/min.rb deleted file mode 100644 index bdc1371858..0000000000 --- a/lib/arel/nodes/min.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Arel - module Nodes - class Min < Arel::Nodes::Function - end - end -end diff --git a/lib/arel/nodes/node.rb b/lib/arel/nodes/node.rb index 404ad22ece..711fa34b6d 100644 --- a/lib/arel/nodes/node.rb +++ b/lib/arel/nodes/node.rb @@ -3,6 +3,9 @@ module Arel ### # Abstract base class for all AST nodes class Node + include Arel::FactoryMethods + include Enumerable + ### # Factory method to create a Nodes::Not node that has the recipient of # the caller as a child. @@ -20,7 +23,7 @@ module Arel ### # Factory method to create an Nodes::And node. def and right - Nodes::And.new self, right + Nodes::And.new [self, right] end # FIXME: this method should go away. I don't like people calling diff --git a/lib/arel/nodes/not.rb b/lib/arel/nodes/not.rb deleted file mode 100644 index de138435bb..0000000000 --- a/lib/arel/nodes/not.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Arel - module Nodes - class Not < Arel::Nodes::Unary - end - end -end diff --git a/lib/arel/nodes/not_equal.rb b/lib/arel/nodes/not_equal.rb deleted file mode 100644 index 7f892940cb..0000000000 --- a/lib/arel/nodes/not_equal.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Arel - module Nodes - class NotEqual < Arel::Nodes::Binary - end - end -end diff --git a/lib/arel/nodes/not_in.rb b/lib/arel/nodes/not_in.rb deleted file mode 100644 index 6c01921a46..0000000000 --- a/lib/arel/nodes/not_in.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Arel - module Nodes - class NotIn < Arel::Nodes::Binary - end - end -end diff --git a/lib/arel/nodes/offset.rb b/lib/arel/nodes/offset.rb deleted file mode 100644 index d93e46aa1f..0000000000 --- a/lib/arel/nodes/offset.rb +++ /dev/null @@ -1,7 +0,0 @@ -module Arel - module Nodes - class Offset < Arel::Nodes::Unary - alias :value :expr - end - end -end diff --git a/lib/arel/nodes/on.rb b/lib/arel/nodes/on.rb deleted file mode 100644 index 953d64f9f1..0000000000 --- a/lib/arel/nodes/on.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Arel - module Nodes - class On < Arel::Nodes::Unary - end - end -end diff --git a/lib/arel/nodes/or.rb b/lib/arel/nodes/or.rb deleted file mode 100644 index bdf7f6d9b3..0000000000 --- a/lib/arel/nodes/or.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Arel - module Nodes - class Or < Arel::Nodes::Binary - end - end -end diff --git a/lib/arel/nodes/select_core.rb b/lib/arel/nodes/select_core.rb index 501a2aaf7c..7f577e0a05 100644 --- a/lib/arel/nodes/select_core.rb +++ b/lib/arel/nodes/select_core.rb @@ -1,24 +1,35 @@ module Arel module Nodes class SelectCore < Arel::Nodes::Node - attr_accessor :top, :froms, :projections, :wheres, :groups - attr_accessor :having + attr_accessor :top, :projections, :wheres, :groups + attr_accessor :having, :source def initialize + @source = JoinSource.new nil @top = nil - @froms = nil @projections = [] @wheres = [] @groups = [] @having = nil end + def from + @source.left + end + + def from= value + @source.left = value + end + + alias :froms= :from= + alias :froms :from + def initialize_copy other super - @froms = @froms.clone if @froms + @source = @source.clone if @source @projections = @projections.clone @wheres = @wheres.clone - @group = @groups.clone + @groups = @groups.clone @having = @having.clone if @having end end diff --git a/lib/arel/nodes/select_statement.rb b/lib/arel/nodes/select_statement.rb index 6881a66747..c9a0cde4e0 100644 --- a/lib/arel/nodes/select_statement.rb +++ b/lib/arel/nodes/select_statement.rb @@ -5,6 +5,7 @@ module Arel attr_accessor :limit, :orders, :lock, :offset def initialize cores = [SelectCore.new] + #puts caller @cores = cores @orders = [] @limit = nil diff --git a/lib/arel/nodes/string_join.rb b/lib/arel/nodes/string_join.rb index ea7912f92b..7fb0033c0f 100644 --- a/lib/arel/nodes/string_join.rb +++ b/lib/arel/nodes/string_join.rb @@ -1,10 +1,8 @@ module Arel module Nodes class StringJoin < Arel::Nodes::Join - undef :constraint - - def initialize left, right - super(left, right, nil) + def initialize left, right = nil + super end end end diff --git a/lib/arel/nodes/sum.rb b/lib/arel/nodes/sum.rb deleted file mode 100644 index 3e043b7330..0000000000 --- a/lib/arel/nodes/sum.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Arel - module Nodes - class Sum < Arel::Nodes::Function - end - end -end diff --git a/lib/arel/nodes/table_alias.rb b/lib/arel/nodes/table_alias.rb index 723b025883..4f4d5e29e9 100644 --- a/lib/arel/nodes/table_alias.rb +++ b/lib/arel/nodes/table_alias.rb @@ -6,7 +6,7 @@ module Arel alias :table_alias :name def [] name - Attribute.new self, name + Attribute.new(self, name) end end end diff --git a/lib/arel/nodes/top.rb b/lib/arel/nodes/top.rb deleted file mode 100644 index 56e2e97e8d..0000000000 --- a/lib/arel/nodes/top.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Arel - module Nodes - class Top < Arel::Nodes::Unary - end - end -end diff --git a/lib/arel/nodes/unary.rb b/lib/arel/nodes/unary.rb index edda89e1f0..e1576b9c99 100644 --- a/lib/arel/nodes/unary.rb +++ b/lib/arel/nodes/unary.rb @@ -2,10 +2,24 @@ module Arel module Nodes class Unary < Arel::Nodes::Node attr_accessor :expr + alias :value :expr def initialize expr @expr = expr end end + + %w{ + Group + Grouping + Having + Limit + Not + Offset + On + Top + }.each do |name| + const_set(name, Class.new(Unary)) + end end end diff --git a/lib/arel/nodes/unqualified_column.rb b/lib/arel/nodes/unqualified_column.rb index f7ba653c11..2820dba9d2 100644 --- a/lib/arel/nodes/unqualified_column.rb +++ b/lib/arel/nodes/unqualified_column.rb @@ -4,6 +4,10 @@ module Arel alias :attribute :expr alias :attribute= :expr= + def relation + @expr.relation + end + def column @expr.column end diff --git a/lib/arel/nodes/update_statement.rb b/lib/arel/nodes/update_statement.rb index 288e9f4676..c08f1b2c5e 100644 --- a/lib/arel/nodes/update_statement.rb +++ b/lib/arel/nodes/update_statement.rb @@ -2,13 +2,15 @@ module Arel module Nodes class UpdateStatement < Arel::Nodes::Node attr_accessor :relation, :wheres, :values, :orders, :limit + attr_accessor :key def initialize @relation = nil @wheres = [] @values = [] - @orders = [] - @limit = nil + @orders = [] + @limit = nil + @key = nil end def initialize_copy other diff --git a/lib/arel/predications.rb b/lib/arel/predications.rb index 23e68e99f1..58f02a2b53 100644 --- a/lib/arel/predications.rb +++ b/lib/arel/predications.rb @@ -36,9 +36,9 @@ module Arel if other.exclude_end? left = Nodes::GreaterThanOrEqual.new(self, other.begin) right = Nodes::LessThan.new(self, other.end) - Nodes::And.new left, right + Nodes::And.new [left, right] else - Nodes::Between.new(self, Nodes::And.new(other.begin, other.end)) + Nodes::Between.new(self, Nodes::And.new([other.begin, other.end])) end else Nodes::In.new self, other @@ -174,7 +174,7 @@ module Arel first = send method_id, others.shift Nodes::Grouping.new others.inject(first) { |memo,expr| - Nodes::And.new(memo, send(method_id, expr)) + Nodes::And.new([memo, send(method_id, expr)]) } end end diff --git a/lib/arel/select_manager.rb b/lib/arel/select_manager.rb index cb656d5340..3e0d4038d7 100644 --- a/lib/arel/select_manager.rb +++ b/lib/arel/select_manager.rb @@ -9,9 +9,10 @@ module Arel from table end - def taken + def limit @ast.limit && @ast.limit.expr end + alias :taken :limit def constraints @ctx.wheres @@ -29,7 +30,9 @@ module Arel end def where_clauses - #warn "where_clauses is deprecated" if $VERBOSE + if $VERBOSE + 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 } end @@ -46,7 +49,7 @@ module Arel end def on *exprs - @ctx.froms.constraint = Nodes::On.new(collapse(exprs)) + @ctx.source.right.last.right = Nodes::On.new(collapse(exprs)) self end @@ -66,29 +69,32 @@ module Arel # FIXME: this is a hack to support # test_with_two_tables_in_from_without_getting_double_quoted # from the AR tests. - if @ctx.froms - source = @ctx.froms - if Nodes::SqlLiteral === table && Nodes::Join === source - source.left = table - table = source - end + case table + when Nodes::SqlLiteral, Arel::Table + @ctx.source.left = table + when Nodes::Join + @ctx.source.right << table end - @ctx.froms = table self end + def froms + @ast.cores.map { |x| x.from }.compact + end + def join relation, klass = Nodes::InnerJoin return self unless relation case relation when String, Nodes::SqlLiteral raise if relation.blank? - from Nodes::StringJoin.new(@ctx.froms, relation) - else - from klass.new(@ctx.froms, relation, nil) + klass = Nodes::StringJoin end + + @ctx.source.right << create_join(relation, nil, klass) + self end def having expr @@ -137,10 +143,10 @@ module Arel end def join_sql - return nil unless @ctx.froms + return nil if @ctx.source.right.empty? - viz = Visitors::JoinSql.new @engine - Nodes::SqlLiteral.new viz.accept @ctx + sql = @visitor.dup.extend(Visitors::JoinSql).accept @ctx + Nodes::SqlLiteral.new sql end def order_clauses @@ -149,9 +155,13 @@ module Arel } end + def join_sources + @ctx.source.right + end + def joins manager if $VERBOSE - warn "joins is deprecated and will be removed in 2.2" + warn "joins is deprecated and will be removed in 3.0.0" warn "please remove your call to joins from #{caller.first}" end manager.join_sql @@ -177,13 +187,22 @@ module Arel # FIXME: this method should go away def insert values - im = InsertManager.new @engine + if $VERBOSE + warn <<-eowarn +insert (#{caller.first}) is deprecated and will be removed in ARel 3.0.0. Please +switch to `compile_insert` + eowarn + end + + im = compile_insert(values) table = @ctx.froms - primary_key_name = (primary_key = table.primary_key) && primary_key.name + + primary_key = table.primary_key + primary_key_name = primary_key.name if primary_key + # FIXME: in AR tests values sometimes were Array and not Hash therefore is_a?(Hash) check is added primary_key_value = primary_key && values.is_a?(Hash) && values[primary_key] im.into table - im.insert values # Oracle adapter needs primary key name to generate RETURNING ... INTO ... clause # for tables which assign primary key value using trigger. # RETURNING ... INTO ... clause will be added only if primary_key_value is nil @@ -195,14 +214,12 @@ module Arel def collapse exprs return exprs.first if exprs.length == 1 - right = exprs.pop - left = exprs.pop - - right = Nodes::SqlLiteral.new(right) if String === right - - right = Nodes::And.new left, right - exprs.reverse.inject(right) { |memo,expr| - Nodes::And.new(expr, memo) + create_and exprs.compact.map { |expr| + if String === expr + Arel.sql(expr) + else + expr + end } end end diff --git a/lib/arel/table.rb b/lib/arel/table.rb index aa23a7d601..d1d1e40e11 100644 --- a/lib/arel/table.rb +++ b/lib/arel/table.rb @@ -1,6 +1,7 @@ module Arel class Table include Arel::Crud + include Arel::FactoryMethods @engine = nil class << self; attr_accessor :engine; end @@ -17,7 +18,6 @@ module Arel if Hash === engine @engine = engine[:engine] || Table.engine - @columns = attributes_for engine[:columns] # Sometime AR sends an :as parameter to table, to let the table know # that it is an Alias. We may want to override new, and return a @@ -27,6 +27,11 @@ module Arel end def primary_key + if $VERBOSE + warn <<-eowarn +primary_key (#{caller.first}) is deprecated and will be removed in ARel 3.0.0 + eowarn + end @primary_key ||= begin primary_key_name = @engine.connection.primary_key(name) # some tables might be without primary key @@ -46,7 +51,7 @@ module Arel def joins manager if $VERBOSE - warn "joins is deprecated and will be removed in 2.2" + warn "joins is deprecated and will be removed in 3.0.0" warn "please remove your call to joins from #{caller.first}" end nil @@ -58,10 +63,10 @@ module Arel case relation when String, Nodes::SqlLiteral raise if relation.blank? - from Nodes::StringJoin.new(self, relation) - else - from klass.new(self, relation, nil) + klass = Nodes::StringJoin end + + from(self).join(relation, klass) end def group *columns @@ -93,41 +98,46 @@ module Arel end def columns + if $VERBOSE + warn <<-eowarn +(#{caller.first}) Arel::Table#columns is deprecated and will be removed in +Arel 3.0.0 with no replacement. PEW PEW PEW!!! + eowarn + end @columns ||= attributes_for @engine.connection.columns(@name, "#{@name} Columns") end def [] name - return nil unless table_exists? - - name = name.to_sym - columns.find { |column| column.name == name } + ::Arel::Attribute.new self, name end def select_manager SelectManager.new(@engine) end + def insert_manager + InsertManager.new(@engine) + end + private def attributes_for columns return nil unless columns columns.map do |column| - Attributes.for(column).new self, column.name.to_sym, column + Attributes.for(column).new self, column.name.to_sym end end - def table_exists? - @table_exists ||= tables.key?(@name) || engine.connection.table_exists?(name) - end - - def tables - self.class.table_cache(@engine) - end - @@table_cache = nil def self.table_cache engine # :nodoc: + if $VERBOSE + warn <<-eowarn +(#{caller.first}) Arel::Table.table_cache is deprecated and will be removed in +Arel 3.0.0 with no replacement. PEW PEW PEW!!! + eowarn + end @@table_cache ||= Hash[engine.connection.tables.map { |x| [x,true] }] end end diff --git a/lib/arel/tree_manager.rb b/lib/arel/tree_manager.rb index 2fa770a0e3..5722baca77 100644 --- a/lib/arel/tree_manager.rb +++ b/lib/arel/tree_manager.rb @@ -2,6 +2,7 @@ module Arel class TreeManager # FIXME: Remove this. include Arel::Relation + include Arel::FactoryMethods attr_accessor :visitor attr_reader :ast, :engine diff --git a/lib/arel/update_manager.rb b/lib/arel/update_manager.rb index 821dce7d81..cf24dbac92 100644 --- a/lib/arel/update_manager.rb +++ b/lib/arel/update_manager.rb @@ -11,6 +11,10 @@ module Arel self end + def key= key + @ast.key = key + end + def order *expr @ast.orders = expr self diff --git a/lib/arel/visitors/depth_first.rb b/lib/arel/visitors/depth_first.rb index 00f18727f0..ed95cad472 100644 --- a/lib/arel/visitors/depth_first.rb +++ b/lib/arel/visitors/depth_first.rb @@ -18,9 +18,11 @@ module Arel alias :visit_Arel_Nodes_Group :unary alias :visit_Arel_Nodes_Grouping :unary alias :visit_Arel_Nodes_Having :unary + alias :visit_Arel_Nodes_Limit :unary alias :visit_Arel_Nodes_Not :unary alias :visit_Arel_Nodes_Offset :unary alias :visit_Arel_Nodes_On :unary + alias :visit_Arel_Nodes_Top :unary alias :visit_Arel_Nodes_UnqualifiedColumn :unary def function o @@ -39,19 +41,15 @@ module Arel visit o.distinct end - def join o - visit o.left - visit o.right - visit o.constraint + def nary o + o.children.each { |child| visit child } end - alias :visit_Arel_Nodes_InnerJoin :join - alias :visit_Arel_Nodes_OuterJoin :join + alias :visit_Arel_Nodes_And :nary def binary o visit o.left visit o.right end - alias :visit_Arel_Nodes_And :binary alias :visit_Arel_Nodes_As :binary alias :visit_Arel_Nodes_Assignment :binary alias :visit_Arel_Nodes_Between :binary @@ -61,6 +59,8 @@ module Arel alias :visit_Arel_Nodes_GreaterThan :binary alias :visit_Arel_Nodes_GreaterThanOrEqual :binary alias :visit_Arel_Nodes_In :binary + alias :visit_Arel_Nodes_JoinSource :binary + alias :visit_Arel_Nodes_InnerJoin :binary alias :visit_Arel_Nodes_LessThan :binary alias :visit_Arel_Nodes_LessThanOrEqual :binary alias :visit_Arel_Nodes_Matches :binary @@ -68,10 +68,14 @@ module Arel alias :visit_Arel_Nodes_NotIn :binary alias :visit_Arel_Nodes_Or :binary alias :visit_Arel_Nodes_Ordering :binary - alias :visit_Arel_Nodes_StringJoin :binary + alias :visit_Arel_Nodes_OuterJoin :binary alias :visit_Arel_Nodes_TableAlias :binary alias :visit_Arel_Nodes_Values :binary + def visit_Arel_Nodes_StringJoin o + visit o.left + end + def visit_Arel_Attribute o visit o.relation visit o.name @@ -118,7 +122,7 @@ module Arel def visit_Arel_Nodes_SelectCore o visit o.projections - visit o.froms + visit o.source visit o.wheres visit o.groups visit o.having diff --git a/lib/arel/visitors/dot.rb b/lib/arel/visitors/dot.rb index eab5e4afdc..3c4fe12d1f 100644 --- a/lib/arel/visitors/dot.rb +++ b/lib/arel/visitors/dot.rb @@ -36,7 +36,6 @@ module Arel def visit_Arel_Nodes_TableAlias o visit_edge o, "name" visit_edge o, "relation" - visit_edge o, "columns" end def visit_Arel_Nodes_Sum o @@ -57,13 +56,11 @@ module Arel def visit_Arel_Nodes_StringJoin o visit_edge o, "left" - visit_edge o, "right" end def visit_Arel_Nodes_InnerJoin o visit_edge o, "left" visit_edge o, "right" - visit_edge o, "constraint" end alias :visit_Arel_Nodes_OuterJoin :visit_Arel_Nodes_InnerJoin @@ -78,9 +75,11 @@ module Arel alias :visit_Arel_Nodes_Group :unary alias :visit_Arel_Nodes_Grouping :unary alias :visit_Arel_Nodes_Having :unary + alias :visit_Arel_Nodes_Limit :unary alias :visit_Arel_Nodes_Not :unary alias :visit_Arel_Nodes_Offset :unary alias :visit_Arel_Nodes_On :unary + alias :visit_Arel_Nodes_Top :unary alias :visit_Arel_Nodes_UnqualifiedColumn :unary def visit_Arel_Nodes_InsertStatement o @@ -90,7 +89,7 @@ module Arel end def visit_Arel_Nodes_SelectCore o - visit_edge o, "froms" + visit_edge o, "source" visit_edge o, "projections" visit_edge o, "wheres" end @@ -123,23 +122,32 @@ module Arel alias :visit_Arel_Attributes_Boolean :visit_Arel_Attribute alias :visit_Arel_Attributes_Attribute :visit_Arel_Attribute - def visit_Arel_Nodes_Equality o + def nary o + o.children.each_with_index do |x,i| + edge(i) { visit x } + end + end + alias :visit_Arel_Nodes_And :nary + + def binary o visit_edge o, "left" visit_edge o, "right" end - alias :visit_Arel_Nodes_And :visit_Arel_Nodes_Equality - alias :visit_Arel_Nodes_Or :visit_Arel_Nodes_Equality - alias :visit_Arel_Nodes_NotEqual :visit_Arel_Nodes_Equality - alias :visit_Arel_Nodes_GreaterThan :visit_Arel_Nodes_Equality - alias :visit_Arel_Nodes_GreaterThanOrEqual :visit_Arel_Nodes_Equality - alias :visit_Arel_Nodes_Assignment :visit_Arel_Nodes_Equality - alias :visit_Arel_Nodes_In :visit_Arel_Nodes_Equality - alias :visit_Arel_Nodes_LessThan :visit_Arel_Nodes_Equality - alias :visit_Arel_Nodes_LessThanOrEqual :visit_Arel_Nodes_Equality - alias :visit_Arel_Nodes_Between :visit_Arel_Nodes_Equality - alias :visit_Arel_Nodes_NotIn :visit_Arel_Nodes_Equality - alias :visit_Arel_Nodes_DoesNotMatch :visit_Arel_Nodes_Equality - alias :visit_Arel_Nodes_Matches :visit_Arel_Nodes_Equality + alias :visit_Arel_Nodes_As :binary + alias :visit_Arel_Nodes_Assignment :binary + alias :visit_Arel_Nodes_Between :binary + alias :visit_Arel_Nodes_DoesNotMatch :binary + alias :visit_Arel_Nodes_Equality :binary + alias :visit_Arel_Nodes_GreaterThan :binary + alias :visit_Arel_Nodes_GreaterThanOrEqual :binary + alias :visit_Arel_Nodes_In :binary + alias :visit_Arel_Nodes_JoinSource :binary + alias :visit_Arel_Nodes_LessThan :binary + alias :visit_Arel_Nodes_LessThanOrEqual :binary + alias :visit_Arel_Nodes_Matches :binary + alias :visit_Arel_Nodes_NotEqual :binary + alias :visit_Arel_Nodes_NotIn :binary + alias :visit_Arel_Nodes_Or :binary def visit_String o @node_stack.last.fields << o diff --git a/lib/arel/visitors/join_sql.rb b/lib/arel/visitors/join_sql.rb index d3fb18d3c6..1cdd7eb5ca 100644 --- a/lib/arel/visitors/join_sql.rb +++ b/lib/arel/visitors/join_sql.rb @@ -8,32 +8,11 @@ module Arel # # This visitor is used in SelectManager#join_sql and is for backwards # compatibility with Arel V1.0 - class JoinSql < Arel::Visitors::ToSql + module JoinSql private def visit_Arel_Nodes_SelectCore o - [o.froms].grep(Nodes::Join).map { |x| visit x }.join ', ' - end - - def visit_Arel_Nodes_StringJoin o - [ - (visit o.left if Nodes::Join === o.left), - visit(o.right) - ].compact.join ' ' - end - - def visit_Arel_Nodes_OuterJoin o - [ - (visit o.left if Nodes::Join === o.left), - "LEFT OUTER JOIN #{visit o.right} #{visit o.constraint if o.constraint}" - ].compact.join ' ' - end - - def visit_Arel_Nodes_InnerJoin o - [ - (visit o.left if Nodes::Join === o.left), - "INNER JOIN #{visit o.right} #{visit o.constraint if o.constraint}" - ].compact.join ' ' + o.source.right.map { |j| visit j }.join ' ' end end end diff --git a/lib/arel/visitors/oracle.rb b/lib/arel/visitors/oracle.rb index 3c8c816feb..aaf0324fd7 100644 --- a/lib/arel/visitors/oracle.rb +++ b/lib/arel/visitors/oracle.rb @@ -25,7 +25,7 @@ module Arel SELECT * FROM ( SELECT raw_sql_.*, rownum raw_rnum_ FROM (#{sql}) raw_sql_ - WHERE rownum <= #{offset.value.to_i + limit} + WHERE rownum <= #{offset.expr.to_i + limit} ) WHERE #{visit offset} eosql @@ -58,7 +58,7 @@ module Arel end def visit_Arel_Nodes_Offset o - "raw_rnum_ > #{visit o.value}" + "raw_rnum_ > #{visit o.expr}" end ### diff --git a/lib/arel/visitors/to_sql.rb b/lib/arel/visitors/to_sql.rb index 7435e41561..4eae5a9ae5 100644 --- a/lib/arel/visitors/to_sql.rb +++ b/lib/arel/visitors/to_sql.rb @@ -7,14 +7,24 @@ module Arel def initialize engine @engine = engine @connection = nil + @pool = nil @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 @last_column = nil - @engine.connection_pool.with_connection do |conn| + @pool = @engine.connection_pool + @pool.with_connection do |conn| @connection = conn super end @@ -32,20 +42,31 @@ module Arel if o.orders.empty? && o.limit.nil? wheres = o.wheres else + key = o.key + unless key + warn(<<-eowarn) if $VERBOSE +(#{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 + key = o.relation.primary_key + end + + wheres = o.wheres stmt = Nodes::SelectStatement.new core = stmt.cores.first core.froms = o.relation - core.projections = [o.relation.primary_key] + core.projections = [key] stmt.limit = o.limit stmt.orders = o.orders - wheres = [Nodes::In.new(o.relation.primary_key, [stmt])] + wheres = [Nodes::In.new(key, [stmt])] end [ "UPDATE #{visit o.relation}", ("SET #{o.values.map { |value| visit value }.join ', '}" unless o.values.empty?), - ("WHERE #{wheres.map { |x| visit x }.join ' AND '}" unless wheres.empty?) + ("WHERE #{wheres.map { |x| visit x }.join ' AND '}" unless wheres.empty?), ].compact.join ' ' end @@ -62,13 +83,47 @@ module Arel end def visit_Arel_Nodes_Exists o - "EXISTS (#{visit o.select_stmt})#{ + "EXISTS (#{visit o.expressions})#{ o.alias ? " AS #{visit o.alias}" : ''}" 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] + end + + def column_for attr + name = attr.name.to_sym + 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] + end + def visit_Arel_Nodes_Values o - "VALUES (#{o.expressions.zip(o.columns).map { |value, column| - quote(value, column && column.column) + "VALUES (#{o.expressions.zip(o.columns).map { |value, attr| + quote(value, attr && column_for(attr)) }.join ', '})" end @@ -87,7 +142,7 @@ module Arel "SELECT", (visit(o.top) if o.top), "#{o.projections.map { |x| visit x }.join ', '}", - ("FROM #{visit o.froms}" if o.froms), + visit(o.source), ("WHERE #{o.wheres.map { |x| visit x }.join ' AND ' }" unless o.wheres.empty?), ("GROUP BY #{o.groups.map { |x| visit x }.join ', ' }" unless o.groups.empty?), (visit(o.having) if o.having), @@ -186,16 +241,26 @@ module Arel "#{visit o.left} NOT LIKE #{visit o.right}" end + def visit_Arel_Nodes_JoinSource o + return unless o.left || !o.right.empty? + + [ + "FROM", + (visit(o.left) if o.left), + o.right.map { |j| visit j }.join(' ') + ].compact.join ' ' + end + def visit_Arel_Nodes_StringJoin o - "#{visit o.left} #{visit o.right}" + visit o.left end def visit_Arel_Nodes_OuterJoin o - "#{visit o.left} LEFT OUTER JOIN #{visit o.right} #{visit o.constraint}" + "LEFT OUTER JOIN #{visit o.left} #{visit o.right}" end def visit_Arel_Nodes_InnerJoin o - "#{visit o.left} INNER JOIN #{visit o.right} #{visit o.constraint if o.constraint}" + "INNER JOIN #{visit o.left} #{visit o.right if o.right}" end def visit_Arel_Nodes_On o @@ -223,7 +288,7 @@ module Arel end def visit_Arel_Nodes_And o - "#{visit o.left} AND #{visit o.right}" + o.children.map { |x| visit x }.join ' AND ' end def visit_Arel_Nodes_Or o @@ -231,7 +296,7 @@ module Arel end def visit_Arel_Nodes_Assignment o - right = quote(o.right, o.left.column) + right = quote(o.right, column_for(o.left)) "#{visit o.left} = #{right}" end @@ -264,7 +329,7 @@ module Arel end def visit_Arel_Attributes_Attribute o - @last_column = o.column + @last_column = column_for o join_name = o.relation.table_alias || o.relation.name "#{quote_table_name join_name}.#{quote_column_name o.name}" end @@ -275,26 +340,29 @@ module Arel alias :visit_Arel_Attributes_Time :visit_Arel_Attributes_Attribute alias :visit_Arel_Attributes_Boolean :visit_Arel_Attributes_Attribute - def visit_Fixnum o; o end - alias :visit_Arel_Nodes_SqlLiteral :visit_Fixnum - alias :visit_Arel_SqlLiteral :visit_Fixnum # This is deprecated - alias :visit_Bignum :visit_Fixnum - - def visit_String o; quote(o, @last_column) end - - alias :visit_ActiveSupport_Multibyte_Chars :visit_String - alias :visit_BigDecimal :visit_String - alias :visit_Date :visit_String - alias :visit_DateTime :visit_String - alias :visit_FalseClass :visit_String - alias :visit_Float :visit_String - alias :visit_Hash :visit_String - alias :visit_Symbol :visit_String - alias :visit_Time :visit_String - alias :visit_TrueClass :visit_String - alias :visit_NilClass :visit_String - alias :visit_ActiveSupport_StringInquirer :visit_String - alias :visit_Class :visit_String + def literal o; o end + + alias :visit_Arel_Nodes_SqlLiteral :literal + alias :visit_Arel_SqlLiteral :literal # This is deprecated + alias :visit_Bignum :literal + alias :visit_Fixnum :literal + + def quoted o; quote(o, @last_column) end + + alias :visit_ActiveSupport_Multibyte_Chars :quoted + alias :visit_ActiveSupport_StringInquirer :quoted + alias :visit_BigDecimal :quoted + alias :visit_Class :quoted + alias :visit_Date :quoted + alias :visit_DateTime :quoted + alias :visit_FalseClass :quoted + alias :visit_Float :quoted + alias :visit_Hash :quoted + alias :visit_NilClass :quoted + alias :visit_String :quoted + alias :visit_Symbol :quoted + alias :visit_Time :quoted + alias :visit_TrueClass :quoted def visit_Array o o.empty? ? 'NULL' : o.map { |x| visit x }.join(', ') @@ -309,7 +377,7 @@ module Arel end def quote_column_name name - @quoted_columns[name] ||= @connection.quote_column_name(name) + @quoted_columns[name] ||= Arel::Nodes::SqlLiteral === name ? name : @connection.quote_column_name(name) end end end |