diff options
67 files changed, 697 insertions, 491 deletions
diff --git a/History.txt b/History.txt index b604fcfc00..d301854236 100644 --- a/History.txt +++ b/History.txt @@ -1,3 +1,39 @@ +== 2.1.0 (unreleased) + +* Enhancements + + * AST is now Enumerable + * AND nodes are now n-ary nodes + * SQL Literals may be used as Attribute names + +* Deprecations + + * Calls to `insert` are deprecated. Please use `compile_insert` then call + `to_sql` on the resulting object and execute that SQL. + + * Calls to `update` are deprecated. Please use `compile_update` then call + `to_sql` on the resulting object and execute that SQL. + + * Calls to `delete` are deprecated. Please use `compile_delete` then call + `to_sql` on the resulting object and execute that SQL. + + * Arel::Table#joins is deprecated and will be removed in 3.0.0 with no + replacement. + + * Arel::Table#columns is deprecated and will be removed in 3.0.0 with no + replacement. + + * Arel::Table.table_cache is deprecated and will be removed in 3.0.0 with no + replacement. + + * Arel::Nodes::And.new takes a single list instead of left and right. + + * Arel::Table#primary_key is deprecated and will be removed in 3.0.0 with no + replacement. + + * Arel::SelectManager#where_clauses is deprecated and will be removed in + 3.0.0 with no replacement. + == 2.0.7 (unreleased) * Bug Fixes diff --git a/Manifest.txt b/Manifest.txt index 095d3c276e..4b6e08319f 100644 --- a/Manifest.txt +++ b/Manifest.txt @@ -14,49 +14,27 @@ lib/arel/delete_manager.rb lib/arel/deprecated.rb lib/arel/expression.rb lib/arel/expressions.rb +lib/arel/factory_methods.rb lib/arel/insert_manager.rb lib/arel/nodes.rb lib/arel/nodes/and.rb -lib/arel/nodes/as.rb -lib/arel/nodes/assignment.rb -lib/arel/nodes/avg.rb -lib/arel/nodes/between.rb lib/arel/nodes/binary.rb lib/arel/nodes/count.rb lib/arel/nodes/delete_statement.rb -lib/arel/nodes/does_not_match.rb lib/arel/nodes/equality.rb -lib/arel/nodes/exists.rb lib/arel/nodes/function.rb -lib/arel/nodes/greater_than.rb -lib/arel/nodes/greater_than_or_equal.rb -lib/arel/nodes/group.rb -lib/arel/nodes/grouping.rb -lib/arel/nodes/having.rb lib/arel/nodes/in.rb lib/arel/nodes/inner_join.rb lib/arel/nodes/insert_statement.rb -lib/arel/nodes/join.rb -lib/arel/nodes/less_than.rb -lib/arel/nodes/less_than_or_equal.rb +lib/arel/nodes/join_source.rb lib/arel/nodes/lock.rb -lib/arel/nodes/matches.rb -lib/arel/nodes/max.rb -lib/arel/nodes/min.rb lib/arel/nodes/node.rb -lib/arel/nodes/not.rb -lib/arel/nodes/not_equal.rb -lib/arel/nodes/not_in.rb -lib/arel/nodes/offset.rb -lib/arel/nodes/on.rb -lib/arel/nodes/or.rb lib/arel/nodes/ordering.rb lib/arel/nodes/outer_join.rb lib/arel/nodes/select_core.rb lib/arel/nodes/select_statement.rb lib/arel/nodes/sql_literal.rb lib/arel/nodes/string_join.rb -lib/arel/nodes/sum.rb lib/arel/nodes/table_alias.rb lib/arel/nodes/unary.rb lib/arel/nodes/unqualified_column.rb @@ -102,6 +80,7 @@ test/test_activerecord_compat.rb test/test_attributes.rb test/test_crud.rb test/test_delete_manager.rb +test/test_factory_methods.rb test/test_insert_manager.rb test/test_select_manager.rb test/test_table.rb diff --git a/arel.gemspec b/arel.gemspec index 2e52ae4a19..c7ec979042 100644 --- a/arel.gemspec +++ b/arel.gemspec @@ -2,11 +2,11 @@ Gem::Specification.new do |s| s.name = %q{arel} - s.version = "2.0.5.20101130111154" + s.version = "2.0.7.beta.20101201093009" - s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= + s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version= s.authors = ["Aaron Patterson", "Bryan Halmkamp", "Emilio Tagua", "Nick Kallen"] - s.date = %q{2010-11-30} + s.date = %q{2010-12-01} s.description = %q{Arel is a Relational Algebra for Ruby. It 1) simplifies the generation complex of SQL queries and it 2) adapts to various RDBMS systems. It is intended to be a framework framework; that is, you can build your own ORM with it, focusing on innovative object and collection modeling as opposed to database compatibility and query generation.} s.email = ["aaron@tenderlovemaking.com", "bryan@brynary.com", "miloops@gmail.com", "nick@example.org"] s.extra_rdoc_files = ["History.txt", "MIT-LICENSE.txt", "Manifest.txt", "README.markdown"] diff --git a/lib/arel.rb b/lib/arel.rb index 6c9f51d03a..32fc8d9bc0 100644 --- a/lib/arel.rb +++ b/lib/arel.rb @@ -1,4 +1,5 @@ require 'arel/crud' +require 'arel/factory_methods' require 'arel/expressions' require 'arel/predications' @@ -29,11 +30,15 @@ require 'arel/sql_literal' #### module Arel - VERSION = '2.0.6' + VERSION = '2.0.7.beta' def self.sql raw_sql Arel::Nodes::SqlLiteral.new raw_sql end + + def self.star + sql '*' + end ## Convenience Alias Node = Arel::Nodes::Node end 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 diff --git a/test/attributes/test_attribute.rb b/test/attributes/test_attribute.rb index 06954c242b..df7dc69621 100644 --- a/test/attributes/test_attribute.rb +++ b/test/attributes/test_attribute.rb @@ -326,7 +326,7 @@ module Arel describe '#eq' do it 'should return an equality node' do - attribute = Attribute.new nil, nil, nil + attribute = Attribute.new nil, nil equality = attribute.eq 1 equality.left.must_equal attribute equality.right.must_equal 1 @@ -485,7 +485,7 @@ module Arel end it 'should return an in node' do - attribute = Attribute.new nil, nil, nil + attribute = Attribute.new nil, nil node = Nodes::In.new attribute, [1,2,3] node.left.must_equal attribute node.right.must_equal [1, 2, 3] @@ -538,7 +538,7 @@ module Arel end it 'should return a NotIn node' do - attribute = Attribute.new nil, nil, nil + attribute = Attribute.new nil, nil node = Nodes::NotIn.new attribute, [1,2,3] node.left.must_equal attribute node.right.must_equal [1, 2, 3] diff --git a/test/helper.rb b/test/helper.rb index 3f9ac22447..f13596f9ec 100644 --- a/test/helper.rb +++ b/test/helper.rb @@ -8,6 +8,6 @@ Arel::Table.engine = Arel::Sql::Engine.new(FakeRecord::Base.new) class Object def must_be_like other - self.gsub(/\s+/, ' ').strip.must_equal other.gsub(/\s+/, ' ').strip + gsub(/\s+/, ' ').strip.must_equal other.gsub(/\s+/, ' ').strip end end diff --git a/test/nodes/test_node.rb b/test/nodes/test_node.rb index 71d2098a60..ffa3f273ea 100644 --- a/test/nodes/test_node.rb +++ b/test/nodes/test_node.rb @@ -2,6 +2,10 @@ require 'helper' module Arel class TestNode < MiniTest::Unit::TestCase + def test_includes_factory_methods + assert Node.new.respond_to?(:create_join) + end + def test_all_nodes_are_nodes Nodes.constants.map { |k| Nodes.const_get(k) diff --git a/test/test_crud.rb b/test/test_crud.rb index 582f0ec072..fe3e4f2e02 100644 --- a/test/test_crud.rb +++ b/test/test_crud.rb @@ -35,10 +35,8 @@ module Arel table = Table.new :users fc = FakeCrudder.new fc.from table - fc.insert [[table[:id], 'foo']] - fc.engine.calls.find { |method, _| - method == :insert - }.wont_be_nil + im = fc.compile_insert [[table[:id], 'foo']] + assert_instance_of Arel::InsertManager, im end end @@ -47,10 +45,8 @@ module Arel table = Table.new :users fc = FakeCrudder.new fc.from table - fc.update [[table[:id], 'foo']] - fc.engine.calls.find { |method, _| - method == :update - }.wont_be_nil + stmt = fc.compile_update [[table[:id], 'foo']] + assert_instance_of Arel::UpdateManager, stmt end end @@ -59,10 +55,8 @@ module Arel table = Table.new :users fc = FakeCrudder.new fc.from table - fc.delete - fc.engine.calls.find { |method, _| - method == :delete - }.wont_be_nil + stmt = fc.compile_delete + assert_instance_of Arel::DeleteManager, stmt end end end diff --git a/test/test_factory_methods.rb b/test/test_factory_methods.rb new file mode 100644 index 0000000000..6506d3c472 --- /dev/null +++ b/test/test_factory_methods.rb @@ -0,0 +1,27 @@ +require 'helper' + +module Arel + module FactoryMethods + class TestFactoryMethods < MiniTest::Unit::TestCase + class Factory + include Arel::FactoryMethods + end + + def setup + @factory = Factory.new + end + + def test_create_join + join = @factory.create_join :one, :two + assert_kind_of Nodes::Join, join + assert_equal :two, join.right + end + + def test_create_on + on = @factory.create_on :one + assert_instance_of Nodes::On, on + assert_equal :one, on.expr + end + end + end +end diff --git a/test/test_select_manager.rb b/test/test_select_manager.rb index d63bec0093..7c8da972e1 100644 --- a/test/test_select_manager.rb +++ b/test/test_select_manager.rb @@ -26,6 +26,17 @@ module Arel def quote_table_name thing; @engine.connection.quote_table_name thing end def quote_column_name thing; @engine.connection.quote_column_name thing end def quote thing, column; @engine.connection.quote thing, column end + def columns table, message = nil + @engine.connection.columns table, message + end + + def table_exists? name + @engine.connection.table_exists? name + end + + def tables + @engine.connection.tables + end def execute sql, name = nil, *args @executed << sql @@ -36,6 +47,12 @@ module Arel end describe 'select manager' do + def test_join_sources + manager = Arel::SelectManager.new Table.engine + manager.join_sources << Arel::Nodes::StringJoin.new('foo') + assert_equal "SELECT FROM 'foo'", manager.to_sql + end + describe 'backwards compatibility' do describe 'project' do it 'accepts symbols as sql literals' do @@ -266,6 +283,35 @@ module Arel end end + it 'should hand back froms' do + relation = Arel::SelectManager.new Table.engine + assert_equal [], relation.froms + end + + it 'should create and nodes' do + relation = Arel::SelectManager.new Table.engine + children = ['foo', 'bar', 'baz'] + clause = relation.create_and children + assert_kind_of Arel::Nodes::And, clause + assert_equal children, clause.children + end + + it 'should create join nodes' do + relation = Arel::SelectManager.new Table.engine + join = relation.create_join 'foo', 'bar' + assert_kind_of Arel::Nodes::InnerJoin, join + assert_equal 'foo', join.left + assert_equal 'bar', join.right + end + + it 'should create join nodes with a klass' do + relation = Arel::SelectManager.new Table.engine + join = relation.create_join 'foo', 'bar', Arel::Nodes::OuterJoin + assert_kind_of Arel::Nodes::OuterJoin, join + assert_equal 'foo', join.left + assert_equal 'bar', join.right + end + describe 'join' do it 'responds to join' do left = Table.new :users @@ -308,30 +354,27 @@ module Arel table = Table.new :users aliaz = table.alias manager = Arel::SelectManager.new Table.engine - manager.from Nodes::InnerJoin.new(table, aliaz, table[:id].eq(aliaz[:id])) + manager.from Nodes::InnerJoin.new(aliaz, table[:id].eq(aliaz[:id])) manager.join_sql.must_be_like %{ INNER JOIN "users" "users_2" "users"."id" = "users_2"."id" } - manager.joins(manager).must_equal manager.join_sql end it 'returns outer join sql' do table = Table.new :users aliaz = table.alias manager = Arel::SelectManager.new Table.engine - manager.from Nodes::OuterJoin.new(table, aliaz, table[:id].eq(aliaz[:id])) + manager.from Nodes::OuterJoin.new(aliaz, table[:id].eq(aliaz[:id])) manager.join_sql.must_be_like %{ LEFT OUTER JOIN "users" "users_2" "users"."id" = "users_2"."id" } - manager.joins(manager).must_equal manager.join_sql end it 'returns string join sql' do table = Table.new :users manager = Arel::SelectManager.new Table.engine - manager.from Nodes::StringJoin.new(table, 'hello') + manager.from Nodes::StringJoin.new('hello') manager.join_sql.must_be_like %{ 'hello' } - manager.joins(manager).must_equal manager.join_sql end it 'returns nil join sql' do @@ -393,9 +436,9 @@ module Arel table = Table.new :users manager = Arel::SelectManager.new engine manager.from table - manager.delete + stmt = manager.compile_delete - engine.executed.last.must_be_like %{ DELETE FROM "users" } + stmt.to_sql.must_be_like %{ DELETE FROM "users" } end it "copies where" do @@ -404,9 +447,9 @@ module Arel manager = Arel::SelectManager.new engine manager.from table manager.where table[:id].eq 10 - manager.delete + stmt = manager.compile_delete - engine.executed.last.must_be_like %{ + stmt.to_sql.must_be_like %{ DELETE FROM "users" WHERE "users"."id" = 10 } end @@ -436,9 +479,10 @@ module Arel manager = Arel::SelectManager.new engine manager.from table manager.take 1 - manager.update(SqlLiteral.new('foo = bar')) + stmt = manager.compile_update(SqlLiteral.new('foo = bar')) + stmt.key = table['id'] - engine.executed.last.must_be_like %{ + stmt.to_sql.must_be_like %{ UPDATE "users" SET foo = bar WHERE "users"."id" IN (SELECT "users"."id" FROM "users" LIMIT 1) } @@ -450,9 +494,10 @@ module Arel manager = Arel::SelectManager.new engine manager.from table manager.order :foo - manager.update(SqlLiteral.new('foo = bar')) + stmt = manager.compile_update(SqlLiteral.new('foo = bar')) + stmt.key = table['id'] - engine.executed.last.must_be_like %{ + stmt.to_sql.must_be_like %{ UPDATE "users" SET foo = bar WHERE "users"."id" IN (SELECT "users"."id" FROM "users" ORDER BY foo) } @@ -463,9 +508,9 @@ module Arel table = Table.new :users manager = Arel::SelectManager.new engine manager.from table - manager.update(SqlLiteral.new('foo = bar')) + stmt = manager.compile_update(SqlLiteral.new('foo = bar')) - engine.executed.last.must_be_like %{ UPDATE "users" SET foo = bar } + stmt.to_sql.must_be_like %{ UPDATE "users" SET foo = bar } end it 'copies where clauses' do @@ -474,9 +519,9 @@ module Arel manager = Arel::SelectManager.new engine manager.where table[:id].eq 10 manager.from table - manager.update(table[:id] => 1) + stmt = manager.compile_update(table[:id] => 1) - engine.executed.last.must_be_like %{ + stmt.to_sql.must_be_like %{ UPDATE "users" SET "id" = 1 WHERE "users"."id" = 10 } end @@ -486,9 +531,9 @@ module Arel table = Table.new :users manager = Arel::SelectManager.new engine manager.from table - manager.update(table[:id] => 1) + stmt = manager.compile_update(table[:id] => 1) - engine.executed.last.must_be_like %{ + stmt.to_sql.must_be_like %{ UPDATE "users" SET "id" = 1 } end diff --git a/test/test_table.rb b/test/test_table.rb index 8d37a8eaff..129d7ba736 100644 --- a/test/test_table.rb +++ b/test/test_table.rb @@ -6,6 +6,38 @@ module Arel @relation = Table.new(:users) end + it 'should create join nodes' do + join = @relation.create_string_join 'foo' + assert_kind_of Arel::Nodes::StringJoin, join + assert_equal 'foo', join.left + end + + it 'should create join nodes' do + join = @relation.create_join 'foo', 'bar' + assert_kind_of Arel::Nodes::InnerJoin, join + assert_equal 'foo', join.left + assert_equal 'bar', join.right + end + + it 'should create join nodes with a klass' do + join = @relation.create_join 'foo', 'bar', Arel::Nodes::OuterJoin + assert_kind_of Arel::Nodes::OuterJoin, join + assert_equal 'foo', join.left + assert_equal 'bar', join.right + end + + it 'should return an insert manager' do + im = @relation.compile_insert 'VALUES(NULL)' + assert_kind_of Arel::InsertManager, im + assert_equal 'INSERT INTO NULL VALUES(NULL)', im.to_sql + end + + it 'should return IM from insert_manager' do + im = @relation.insert_manager + assert_kind_of Arel::InsertManager, im + assert_equal im.engine, @relation.engine + end + describe 'skip' do it 'should add an offset' do sm = @relation.skip 2 @@ -13,12 +45,6 @@ module Arel end end - describe 'primary_key' do - it 'should return an attribute' do - @relation.primary_key.name.must_equal :id - end - end - describe 'select_manager' do it 'should return an empty select manager' do sm = @relation.select_manager @@ -36,12 +62,6 @@ module Arel end describe 'backwards compat' do - describe 'joins' do - it 'returns nil' do - @relation.joins(nil).must_equal nil - end - end - describe 'join' do it 'noops on nil' do mgr = @relation.join nil @@ -84,13 +104,6 @@ module Arel end describe 'new' do - it 'takes :columns' do - columns = Table.engine.connection.columns("users") - @relation = Table.new(:users, :columns => columns) - @relation.columns.first.name.must_equal :id - @relation.engine.must_equal Table.engine - end - it 'should accept an engine' do rel = Table.new :users, 'foo' rel.engine.must_equal 'foo' @@ -147,14 +160,6 @@ module Arel end end - describe 'columns' do - it 'returns a list of columns' do - columns = @relation.columns - columns.length.must_equal 4 - columns.map { |x| x.name.to_s }.sort.must_equal %w{ created_at bool name id }.sort - end - end - it "should have a name" do @relation.name.must_equal 'users' end @@ -168,24 +173,6 @@ module Arel it "manufactures an attribute if the symbol names an attribute within the relation" do column = @relation[:id] column.name.must_equal :id - column.must_be_kind_of Attributes::Integer - end - end - - describe 'when table does not exist' do - it 'returns nil' do - @relation[:foooo].must_be_nil - end - end - end - end - - describe Table do - describe 'when checking the existence of a table' do - it 'should be present in the table cache despite the class of its name' do - [ 'users', :users ].each do |name| - relation = Table.new name - Table.table_cache(relation.engine).key?(relation.name).must_equal true end end end diff --git a/test/visitors/test_depth_first.rb b/test/visitors/test_depth_first.rb index 1bee0328cf..306605b7c6 100644 --- a/test/visitors/test_depth_first.rb +++ b/test/visitors/test_depth_first.rb @@ -29,7 +29,10 @@ module Arel Arel::Nodes::Grouping, Arel::Nodes::Offset, Arel::Nodes::Having, + Arel::Nodes::StringJoin, Arel::Nodes::UnqualifiedColumn, + Arel::Nodes::Top, + Arel::Nodes::Limit, ].each do |klass| define_method("test_#{klass.name.gsub('::', '_')}") do op = klass.new(:a) @@ -66,19 +69,18 @@ module Arel end def test_inner_join - join = Nodes::InnerJoin.new :a, :b, :c + join = Nodes::InnerJoin.new :a, :b @visitor.accept join - assert_equal [:a, :b, :c, join], @collector.calls + assert_equal [:a, :b, join], @collector.calls end def test_outer_join - join = Nodes::OuterJoin.new :a, :b, :c + join = Nodes::OuterJoin.new :a, :b @visitor.accept join - assert_equal [:a, :b, :c, join], @collector.calls + assert_equal [:a, :b, join], @collector.calls end [ - Arel::Nodes::And, Arel::Nodes::Assignment, Arel::Nodes::Between, Arel::Nodes::DoesNotMatch, @@ -92,12 +94,12 @@ module Arel Arel::Nodes::NotEqual, Arel::Nodes::NotIn, Arel::Nodes::Or, - Arel::Nodes::StringJoin, Arel::Nodes::TableAlias, Arel::Nodes::Values, Arel::Nodes::As, Arel::Nodes::DeleteStatement, Arel::Nodes::Ordering, + Arel::Nodes::JoinSource, ].each do |klass| define_method("test_#{klass.name.gsub('::', '_')}") do binary = klass.new(:a, :b) @@ -106,6 +108,17 @@ module Arel end end + # N-ary + [ + Arel::Nodes::And, + ].each do |klass| + define_method("test_#{klass.name.gsub('::', '_')}") do + binary = klass.new([:a, :b, :c]) + @visitor.accept binary + assert_equal [:a, :b, :c, binary], @collector.calls + end + end + [ Arel::Attributes::Integer, Arel::Attributes::Float, @@ -165,7 +178,8 @@ module Arel @visitor.accept core assert_equal [ :a, core.projections, - :b, + :b, [], + core.source, :c, core.wheres, :d, core.groups, :e, diff --git a/test/visitors/test_dot.rb b/test/visitors/test_dot.rb index 19a554ce42..3c7da8958a 100644 --- a/test/visitors/test_dot.rb +++ b/test/visitors/test_dot.rb @@ -16,12 +16,42 @@ module Arel Arel::Nodes::Offset, Arel::Nodes::Having, Arel::Nodes::UnqualifiedColumn, + Arel::Nodes::Top, + Arel::Nodes::Limit, ].each do |klass| define_method("test_#{klass.name.gsub('::', '_')}") do op = klass.new(:a) @visitor.accept op end end + + # binary ops + [ + Arel::Nodes::Assignment, + Arel::Nodes::Between, + Arel::Nodes::DoesNotMatch, + Arel::Nodes::Equality, + Arel::Nodes::GreaterThan, + Arel::Nodes::GreaterThanOrEqual, + Arel::Nodes::In, + Arel::Nodes::LessThan, + Arel::Nodes::LessThanOrEqual, + Arel::Nodes::Matches, + Arel::Nodes::NotEqual, + Arel::Nodes::NotIn, + Arel::Nodes::Or, + Arel::Nodes::TableAlias, + Arel::Nodes::Values, + Arel::Nodes::As, + Arel::Nodes::DeleteStatement, + Arel::Nodes::Ordering, + Arel::Nodes::JoinSource, + ].each do |klass| + define_method("test_#{klass.name.gsub('::', '_')}") do + binary = klass.new(:a, :b) + @visitor.accept binary + end + end end end end diff --git a/test/visitors/test_join_sql.rb b/test/visitors/test_join_sql.rb index 8253fe5ab4..b672f88ecf 100644 --- a/test/visitors/test_join_sql.rb +++ b/test/visitors/test_join_sql.rb @@ -4,15 +4,21 @@ module Arel module Visitors describe 'the join_sql visitor' do before do - @visitor = JoinSql.new Table.engine + @visitor = ToSql.new Table.engine + @visitor.extend(JoinSql) + end + + it 'should visit string join' do + sql = @visitor.accept Nodes::StringJoin.new('omg') + sql.must_be_like "'omg'" end describe 'inner join' do it 'should visit left if left is a join' do t = Table.new :users - join = Nodes::InnerJoin.new t, t, Nodes::On.new(t[:id]) - j2 = Nodes::InnerJoin.new join, t, Nodes::On.new(t[:id]) - @visitor.accept(j2).must_be_like %{ + sm = t.select_manager + sm.join(t).on(t[:id]).join(t).on(t[:id]) + sm.join_sql.must_be_like %{ INNER JOIN "users" ON "users"."id" INNER JOIN "users" ON "users"."id" } @@ -22,9 +28,10 @@ module Arel describe 'outer join' do it 'should visit left if left is a join' do t = Table.new :users - join = Nodes::OuterJoin.new t, t, Nodes::On.new(t[:id]) - j2 = Nodes::OuterJoin.new join, t, Nodes::On.new(t[:id]) - @visitor.accept(j2).must_be_like %{ + sm = t.select_manager + sm.join(t, Nodes::OuterJoin).on(t[:id]).join( + t, Nodes::OuterJoin).on(t[:id]) + sm.join_sql.must_be_like %{ LEFT OUTER JOIN "users" ON "users"."id" LEFT OUTER JOIN "users" ON "users"."id" } diff --git a/test/visitors/test_to_sql.rb b/test/visitors/test_to_sql.rb index 33783f7d23..34ebb2b278 100644 --- a/test/visitors/test_to_sql.rb +++ b/test/visitors/test_to_sql.rb @@ -5,7 +5,14 @@ module Arel describe 'the to_sql visitor' do before do @visitor = ToSql.new Table.engine - @attr = Table.new(:users)[:id] + @table = Table.new(:users) + @attr = @table[:id] + end + + it 'should not quote sql literals' do + node = @table[Arel.star] + sql = @visitor.accept node + sql.must_be_like '"users".*' end describe 'equality' do @@ -50,7 +57,7 @@ module Arel end it "should apply Not to the whole expression" do - node = Nodes::And.new @attr.eq(10), @attr.eq(11) + node = Nodes::And.new [@attr.eq(10), @attr.eq(11)] sql = @visitor.accept Nodes::Not.new(node) sql.must_be_like %{NOT ("users"."id" = 10 AND "users"."id" = 11)} end @@ -82,7 +89,7 @@ module Arel end it "should visit_Arel_Nodes_And" do - node = Nodes::And.new @attr.eq(10), @attr.eq(11) + node = Nodes::And.new [@attr.eq(10), @attr.eq(11)] @visitor.accept(node).must_be_like %{ "users"."id" = 10 AND "users"."id" = 11 } @@ -96,7 +103,7 @@ module Arel end it "should visit visit_Arel_Attributes_Time" do - attr = Attributes::Time.new(@attr.relation, @attr.name, @attr.column) + attr = Attributes::Time.new(@attr.relation, @attr.name) @visitor.accept attr end @@ -164,7 +171,9 @@ module Arel end in_node = Nodes::In.new @attr, %w{ a b c } visitor = visitor.new(Table.engine) - visitor.expected = @attr.column + visitor.expected = Table.engine.connection.columns(:users).find { |x| + x.name == 'name' + } visitor.accept(in_node).must_equal %("users"."name" IN ('a', 'b', 'c')) end end @@ -219,7 +228,9 @@ module Arel end in_node = Nodes::NotIn.new @attr, %w{ a b c } visitor = visitor.new(Table.engine) - visitor.expected = @attr.column + visitor.expected = Table.engine.connection.columns(:users).find { |x| + x.name == 'name' + } visitor.accept(in_node).must_equal %("users"."name" NOT IN ('a', 'b', 'c')) end end |