diff options
-rw-r--r-- | History.txt | 9 | ||||
-rw-r--r-- | Manifest.txt | 1 | ||||
-rw-r--r-- | arel.gemspec | 15 | ||||
-rw-r--r-- | lib/arel/nodes.rb | 1 | ||||
-rw-r--r-- | lib/arel/nodes/lock.rb | 6 | ||||
-rw-r--r-- | lib/arel/nodes/named_function.rb | 2 | ||||
-rw-r--r-- | lib/arel/nodes/node.rb | 2 | ||||
-rw-r--r-- | lib/arel/nodes/unary.rb | 1 | ||||
-rw-r--r-- | lib/arel/predications.rb | 2 | ||||
-rw-r--r-- | lib/arel/select_manager.rb | 37 | ||||
-rw-r--r-- | lib/arel/visitors/mssql.rb | 7 | ||||
-rw-r--r-- | lib/arel/visitors/mysql.rb | 2 | ||||
-rw-r--r-- | lib/arel/visitors/postgresql.rb | 2 | ||||
-rw-r--r-- | lib/arel/visitors/to_sql.rb | 52 | ||||
-rw-r--r-- | test/attributes/test_attribute.rb | 16 | ||||
-rw-r--r-- | test/nodes/test_not.rb | 11 | ||||
-rw-r--r-- | test/support/fake_record.rb | 13 | ||||
-rw-r--r-- | test/test_select_manager.rb | 62 | ||||
-rw-r--r-- | test/visitors/test_depth_first.rb | 2 | ||||
-rw-r--r-- | test/visitors/test_mssql.rb | 9 | ||||
-rw-r--r-- | test/visitors/test_mysql.rb | 15 | ||||
-rw-r--r-- | test/visitors/test_postgres.rb | 17 | ||||
-rw-r--r-- | test/visitors/test_to_sql.rb | 6 |
23 files changed, 205 insertions, 85 deletions
diff --git a/History.txt b/History.txt index 51e950512f..fac98381a6 100644 --- a/History.txt +++ b/History.txt @@ -6,6 +6,15 @@ * AND nodes are now n-ary nodes * SQL Literals may be used as Attribute names * Added Arel::Nodes::NamedFunction for representing generic SQL functions + * Add Arel::SelectManager#limit= + * Add Arel::SelectManager#offset + * Add Arel::SelectManager#offset= + +* Bug fixes + + * MSSQL adds TOP to sub selects + * Assigning nil to take() removes LIMIT from statement. + * Assigning nil to offset() removes OFFSET from statement. * Deprecations diff --git a/Manifest.txt b/Manifest.txt index 5545ddf0cf..f5fd7d7a80 100644 --- a/Manifest.txt +++ b/Manifest.txt @@ -27,7 +27,6 @@ lib/arel/nodes/in.rb lib/arel/nodes/inner_join.rb lib/arel/nodes/insert_statement.rb lib/arel/nodes/join_source.rb -lib/arel/nodes/lock.rb lib/arel/nodes/named_function.rb lib/arel/nodes/node.rb lib/arel/nodes/ordering.rb diff --git a/arel.gemspec b/arel.gemspec index 5df1dd918a..16ceb11c05 100644 --- a/arel.gemspec +++ b/arel.gemspec @@ -2,42 +2,41 @@ Gem::Specification.new do |s| s.name = %q{arel} - s.version = "2.0.7.beta.20110121165657" + s.version = "2.0.7.beta.20110224095102" 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{2011-01-21} + s.date = %q{2011-02-24} 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"] - s.files = [".autotest", "History.txt", "MIT-LICENSE.txt", "Manifest.txt", "README.markdown", "Rakefile", "arel.gemspec", "lib/arel.rb", "lib/arel/attributes.rb", "lib/arel/attributes/attribute.rb", "lib/arel/compatibility/wheres.rb", "lib/arel/crud.rb", "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/binary.rb", "lib/arel/nodes/count.rb", "lib/arel/nodes/delete_statement.rb", "lib/arel/nodes/equality.rb", "lib/arel/nodes/function.rb", "lib/arel/nodes/in.rb", "lib/arel/nodes/inner_join.rb", "lib/arel/nodes/insert_statement.rb", "lib/arel/nodes/join_source.rb", "lib/arel/nodes/lock.rb", "lib/arel/nodes/named_function.rb", "lib/arel/nodes/node.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/table_alias.rb", "lib/arel/nodes/unary.rb", "lib/arel/nodes/unqualified_column.rb", "lib/arel/nodes/update_statement.rb", "lib/arel/nodes/values.rb", "lib/arel/nodes/with.rb", "lib/arel/predications.rb", "lib/arel/relation.rb", "lib/arel/select_manager.rb", "lib/arel/sql/engine.rb", "lib/arel/sql_literal.rb", "lib/arel/table.rb", "lib/arel/tree_manager.rb", "lib/arel/update_manager.rb", "lib/arel/visitors.rb", "lib/arel/visitors/depth_first.rb", "lib/arel/visitors/dot.rb", "lib/arel/visitors/join_sql.rb", "lib/arel/visitors/mssql.rb", "lib/arel/visitors/mysql.rb", "lib/arel/visitors/oracle.rb", "lib/arel/visitors/order_clauses.rb", "lib/arel/visitors/postgresql.rb", "lib/arel/visitors/sqlite.rb", "lib/arel/visitors/to_sql.rb", "lib/arel/visitors/visitor.rb", "lib/arel/visitors/where_sql.rb", "test/attributes/test_attribute.rb", "test/helper.rb", "test/nodes/test_as.rb", "test/nodes/test_count.rb", "test/nodes/test_delete_statement.rb", "test/nodes/test_equality.rb", "test/nodes/test_insert_statement.rb", "test/nodes/test_named_function.rb", "test/nodes/test_node.rb", "test/nodes/test_not.rb", "test/nodes/test_or.rb", "test/nodes/test_select_core.rb", "test/nodes/test_select_statement.rb", "test/nodes/test_sql_literal.rb", "test/nodes/test_sum.rb", "test/nodes/test_update_statement.rb", "test/support/fake_record.rb", "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", "test/test_update_manager.rb", "test/visitors/test_depth_first.rb", "test/visitors/test_dot.rb", "test/visitors/test_join_sql.rb", "test/visitors/test_mssql.rb", "test/visitors/test_mysql.rb", "test/visitors/test_oracle.rb", "test/visitors/test_postgres.rb", "test/visitors/test_sqlite.rb", "test/visitors/test_to_sql.rb"] + s.files = [".autotest", "History.txt", "MIT-LICENSE.txt", "Manifest.txt", "README.markdown", "Rakefile", "arel.gemspec", "lib/arel.rb", "lib/arel/attributes.rb", "lib/arel/attributes/attribute.rb", "lib/arel/compatibility/wheres.rb", "lib/arel/crud.rb", "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/binary.rb", "lib/arel/nodes/count.rb", "lib/arel/nodes/delete_statement.rb", "lib/arel/nodes/equality.rb", "lib/arel/nodes/function.rb", "lib/arel/nodes/in.rb", "lib/arel/nodes/inner_join.rb", "lib/arel/nodes/insert_statement.rb", "lib/arel/nodes/join_source.rb", "lib/arel/nodes/named_function.rb", "lib/arel/nodes/node.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/table_alias.rb", "lib/arel/nodes/unary.rb", "lib/arel/nodes/unqualified_column.rb", "lib/arel/nodes/update_statement.rb", "lib/arel/nodes/values.rb", "lib/arel/nodes/with.rb", "lib/arel/predications.rb", "lib/arel/relation.rb", "lib/arel/select_manager.rb", "lib/arel/sql/engine.rb", "lib/arel/sql_literal.rb", "lib/arel/table.rb", "lib/arel/tree_manager.rb", "lib/arel/update_manager.rb", "lib/arel/visitors.rb", "lib/arel/visitors/depth_first.rb", "lib/arel/visitors/dot.rb", "lib/arel/visitors/join_sql.rb", "lib/arel/visitors/mssql.rb", "lib/arel/visitors/mysql.rb", "lib/arel/visitors/oracle.rb", "lib/arel/visitors/order_clauses.rb", "lib/arel/visitors/postgresql.rb", "lib/arel/visitors/sqlite.rb", "lib/arel/visitors/to_sql.rb", "lib/arel/visitors/visitor.rb", "lib/arel/visitors/where_sql.rb", "test/attributes/test_attribute.rb", "test/helper.rb", "test/nodes/test_as.rb", "test/nodes/test_count.rb", "test/nodes/test_delete_statement.rb", "test/nodes/test_equality.rb", "test/nodes/test_insert_statement.rb", "test/nodes/test_named_function.rb", "test/nodes/test_node.rb", "test/nodes/test_not.rb", "test/nodes/test_or.rb", "test/nodes/test_select_core.rb", "test/nodes/test_select_statement.rb", "test/nodes/test_sql_literal.rb", "test/nodes/test_sum.rb", "test/nodes/test_update_statement.rb", "test/support/fake_record.rb", "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", "test/test_update_manager.rb", "test/visitors/test_depth_first.rb", "test/visitors/test_dot.rb", "test/visitors/test_join_sql.rb", "test/visitors/test_mssql.rb", "test/visitors/test_mysql.rb", "test/visitors/test_oracle.rb", "test/visitors/test_postgres.rb", "test/visitors/test_sqlite.rb", "test/visitors/test_to_sql.rb", ".gemtest"] s.homepage = %q{http://github.com/rails/arel} s.rdoc_options = ["--main", "README.markdown"] s.require_paths = ["lib"] s.rubyforge_project = %q{arel} - s.rubygems_version = %q{1.3.7} + s.rubygems_version = %q{1.5.2} s.summary = %q{Arel is a Relational Algebra for Ruby} s.test_files = ["test/attributes/test_attribute.rb", "test/nodes/test_as.rb", "test/nodes/test_count.rb", "test/nodes/test_delete_statement.rb", "test/nodes/test_equality.rb", "test/nodes/test_insert_statement.rb", "test/nodes/test_named_function.rb", "test/nodes/test_node.rb", "test/nodes/test_not.rb", "test/nodes/test_or.rb", "test/nodes/test_select_core.rb", "test/nodes/test_select_statement.rb", "test/nodes/test_sql_literal.rb", "test/nodes/test_sum.rb", "test/nodes/test_update_statement.rb", "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", "test/test_update_manager.rb", "test/visitors/test_depth_first.rb", "test/visitors/test_dot.rb", "test/visitors/test_join_sql.rb", "test/visitors/test_mssql.rb", "test/visitors/test_mysql.rb", "test/visitors/test_oracle.rb", "test/visitors/test_postgres.rb", "test/visitors/test_sqlite.rb", "test/visitors/test_to_sql.rb"] if s.respond_to? :specification_version then - current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION s.specification_version = 3 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then s.add_development_dependency(%q<minitest>, [">= 2.0.0"]) s.add_development_dependency(%q<hoe>, [">= 2.1.0"]) s.add_development_dependency(%q<minitest>, [">= 1.6.0"]) - s.add_development_dependency(%q<hoe>, [">= 2.8.0"]) + s.add_development_dependency(%q<hoe>, [">= 2.9.1"]) else s.add_dependency(%q<minitest>, [">= 2.0.0"]) s.add_dependency(%q<hoe>, [">= 2.1.0"]) s.add_dependency(%q<minitest>, [">= 1.6.0"]) - s.add_dependency(%q<hoe>, [">= 2.8.0"]) + s.add_dependency(%q<hoe>, [">= 2.9.1"]) end else s.add_dependency(%q<minitest>, [">= 2.0.0"]) s.add_dependency(%q<hoe>, [">= 2.1.0"]) s.add_dependency(%q<minitest>, [">= 1.6.0"]) - s.add_dependency(%q<hoe>, [">= 2.8.0"]) + s.add_dependency(%q<hoe>, [">= 2.9.1"]) end end diff --git a/lib/arel/nodes.rb b/lib/arel/nodes.rb index 129c43b713..4b97e28668 100644 --- a/lib/arel/nodes.rb +++ b/lib/arel/nodes.rb @@ -1,6 +1,5 @@ # node require 'arel/nodes/node' -require 'arel/nodes/lock' require 'arel/nodes/select_statement' require 'arel/nodes/select_core' require 'arel/nodes/insert_statement' diff --git a/lib/arel/nodes/lock.rb b/lib/arel/nodes/lock.rb deleted file mode 100644 index e5fb258e26..0000000000 --- a/lib/arel/nodes/lock.rb +++ /dev/null @@ -1,6 +0,0 @@ -module Arel - module Nodes - class Lock < Arel::Nodes::Node - end - end -end diff --git a/lib/arel/nodes/named_function.rb b/lib/arel/nodes/named_function.rb index 56669bf858..5fca33e323 100644 --- a/lib/arel/nodes/named_function.rb +++ b/lib/arel/nodes/named_function.rb @@ -3,6 +3,8 @@ module Arel class NamedFunction < Arel::Nodes::Function attr_accessor :name + include Arel::Predications + def initialize name, expr, aliaz = nil super(expr, aliaz) @name = name diff --git a/lib/arel/nodes/node.rb b/lib/arel/nodes/node.rb index 711fa34b6d..1a5bc27856 100644 --- a/lib/arel/nodes/node.rb +++ b/lib/arel/nodes/node.rb @@ -10,7 +10,7 @@ module Arel # Factory method to create a Nodes::Not node that has the recipient of # the caller as a child. def not - Nodes::Not.new Nodes::Grouping.new self + Nodes::Not.new self end ### diff --git a/lib/arel/nodes/unary.rb b/lib/arel/nodes/unary.rb index e1576b9c99..1b1dcb1053 100644 --- a/lib/arel/nodes/unary.rb +++ b/lib/arel/nodes/unary.rb @@ -18,6 +18,7 @@ module Arel Offset On Top + Lock }.each do |name| const_set(name, Class.new(Unary)) end diff --git a/lib/arel/predications.rb b/lib/arel/predications.rb index 58f02a2b53..920a9ee374 100644 --- a/lib/arel/predications.rb +++ b/lib/arel/predications.rb @@ -163,6 +163,7 @@ module Arel private def grouping_any method_id, others + others = others.dup first = send method_id, others.shift Nodes::Grouping.new others.inject(first) { |memo,expr| @@ -171,6 +172,7 @@ module Arel end def grouping_all method_id, others + others = others.dup first = send method_id, others.shift Nodes::Grouping.new others.inject(first) { |memo,expr| diff --git a/lib/arel/select_manager.rb b/lib/arel/select_manager.rb index 0fa344945e..7f533fa91b 100644 --- a/lib/arel/select_manager.rb +++ b/lib/arel/select_manager.rb @@ -18,10 +18,19 @@ module Arel @ctx.wheres end + def offset + @ast.offset && @ast.offset.expr + end + def skip amount - @ast.offset = Nodes::Offset.new(amount) + if amount + @ast.offset = Nodes::Offset.new(amount) + else + @ast.offset = nil + end self end + alias :offset= :skip ### # Produces an Arel::Nodes::Exists node @@ -37,10 +46,16 @@ module Arel @ctx.wheres.map { |c| to_sql.accept c } end - def lock locking = true - # FIXME: do we even need to store this? If locking is +false+ shouldn't - # we just remove the node from the AST? - @ast.lock = Nodes::Lock.new + def lock locking = Arel.sql('FOR UPDATE') + case locking + when true + locking = Arel.sql('FOR UPDATE') + when Arel::Nodes::SqlLiteral + when String + locking = Arel.sql locking + end + + @ast.lock = Nodes::Lock.new(locking) self end @@ -161,13 +176,21 @@ module Arel node_class = Nodes::With end @ast.with = node_class.new(subqueries.flatten) + + self end def take limit - @ast.limit = Nodes::Limit.new(limit) - @ctx.top = Nodes::Top.new(limit) + if limit + @ast.limit = Nodes::Limit.new(limit) + @ctx.top = Nodes::Top.new(limit) + else + @ast.limit = nil + @ctx.top = nil + end self end + alias limit= take def join_sql return nil if @ctx.source.right.empty? diff --git a/lib/arel/visitors/mssql.rb b/lib/arel/visitors/mssql.rb index 9b0e77c19b..ea7ab6394c 100644 --- a/lib/arel/visitors/mssql.rb +++ b/lib/arel/visitors/mssql.rb @@ -3,6 +3,13 @@ module Arel class MSSQL < Arel::Visitors::ToSql private + def build_subselect key, o + stmt = super + core = stmt.cores.first + core.top = Nodes::Top.new(o.limit.expr) if o.limit + stmt + end + def visit_Arel_Nodes_Limit o "" end diff --git a/lib/arel/visitors/mysql.rb b/lib/arel/visitors/mysql.rb index e90161eee4..4f029245ac 100644 --- a/lib/arel/visitors/mysql.rb +++ b/lib/arel/visitors/mysql.rb @@ -3,7 +3,7 @@ module Arel class MySQL < Arel::Visitors::ToSql private def visit_Arel_Nodes_Lock o - "FOR UPDATE" + visit o.expr end ### diff --git a/lib/arel/visitors/postgresql.rb b/lib/arel/visitors/postgresql.rb index 0e82a703ca..c423dc6fc6 100644 --- a/lib/arel/visitors/postgresql.rb +++ b/lib/arel/visitors/postgresql.rb @@ -3,7 +3,7 @@ module Arel class PostgreSQL < Arel::Visitors::ToSql private def visit_Arel_Nodes_Lock o - "FOR UPDATE" + visit o.expr end def visit_Arel_Nodes_SelectStatement o diff --git a/lib/arel/visitors/to_sql.rb b/lib/arel/visitors/to_sql.rb index 376d1460db..7eaaff8e13 100644 --- a/lib/arel/visitors/to_sql.rb +++ b/lib/arel/visitors/to_sql.rb @@ -11,14 +11,6 @@ module Arel @last_column = nil @quoted_tables = {} @quoted_columns = {} - @column_cache = Hash.new { |h,pool| - h[pool] = Hash.new { |conn_h,column| - conn_h[column] = {} - } - } - @table_exists = Hash.new { |h,pool| - h[pool] = {} - } end def accept object @@ -38,6 +30,17 @@ module Arel ].compact.join ' ' end + # FIXME: we should probably have a 2-pass visitor for this + def build_subselect key, o + stmt = Nodes::SelectStatement.new + core = stmt.cores.first + core.froms = o.relation + core.projections = [key] + stmt.limit = o.limit + stmt.orders = o.orders + stmt + end + def visit_Arel_Nodes_UpdateStatement o if o.orders.empty? && o.limit.nil? wheres = o.wheres @@ -52,15 +55,7 @@ key on UpdateManager using UpdateManager#key= key = o.relation.primary_key end - wheres = o.wheres - stmt = Nodes::SelectStatement.new - core = stmt.cores.first - core.froms = o.relation - core.projections = [key] - stmt.limit = o.limit - stmt.orders = o.orders - - wheres = [Nodes::In.new(key, [stmt])] + wheres = [Nodes::In.new(key, [build_subselect(key, o)])] end [ @@ -88,37 +83,20 @@ key on UpdateManager using UpdateManager#key= end def table_exists? name - return true if table_exists.key? name - - @connection.tables.each do |table| - table_exists[table] = true - end - - table_exists.key? name - end - - def table_exists - @table_exists[@pool] + @pool.table_exists? name end def column_for attr - name = attr.name.to_sym + name = attr.name.to_s table = attr.relation.name return nil unless table_exists? table - # If we don't have this column cached, get a list of columns and - # cache them for this table - unless column_cache.key? table - columns = @connection.columns(table, "#{table}(#{name}) Columns") - column_cache[table] = Hash[columns.map { |c| [c.name.to_sym, c] }] - end - column_cache[table][name] end def column_cache - @column_cache[@pool] + @pool.columns_hash end def visit_Arel_Nodes_Values o diff --git a/test/attributes/test_attribute.rb b/test/attributes/test_attribute.rb index df7dc69621..352774071a 100644 --- a/test/attributes/test_attribute.rb +++ b/test/attributes/test_attribute.rb @@ -366,6 +366,14 @@ module Arel SELECT "users"."id" FROM "users" WHERE ("users"."id" = 1 OR "users"."id" = 2) } end + + it 'should not eat input' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + values = [1,2] + mgr.where relation[:id].eq_any(values) + values.must_equal [1,2] + end end describe '#eq_all' do @@ -382,6 +390,14 @@ module Arel SELECT "users"."id" FROM "users" WHERE ("users"."id" = 1 AND "users"."id" = 2) } end + + it 'should not eat input' do + relation = Table.new(:users) + mgr = relation.project relation[:id] + values = [1,2] + mgr.where relation[:id].eq_all(values) + values.must_equal [1,2] + end end describe '#matches' do diff --git a/test/nodes/test_not.rb b/test/nodes/test_not.rb index d02a9bad74..c5bb0088c8 100644 --- a/test/nodes/test_not.rb +++ b/test/nodes/test_not.rb @@ -6,13 +6,10 @@ module Arel describe '#not' do it 'makes a NOT node' do attr = Table.new(:users)[:id] - left = attr.eq(10) - right = attr.eq(11) - node = left.or right - node.expr.left.must_equal left - node.expr.right.must_equal right - - node.or(right).not + expr = attr.eq(10) + node = expr.not + node.must_be_kind_of Not + node.expr.must_equal expr end end end diff --git a/test/support/fake_record.rb b/test/support/fake_record.rb index cdea690a7b..a0fa79d519 100644 --- a/test/support/fake_record.rb +++ b/test/support/fake_record.rb @@ -3,7 +3,7 @@ module FakeRecord end class Connection - attr_reader :tables + attr_reader :tables, :columns_hash def initialize @tables = %w{ users photos developers products} @@ -19,6 +19,9 @@ module FakeRecord Column.new('price', :decimal) ] } + @columns_hash = { + 'users' => Hash[@columns['users'].map { |x| [x.name, x] }] + } @primary_keys = { 'users' => 'id' } @@ -79,6 +82,14 @@ module FakeRecord def with_connection yield connection end + + def table_exists? name + connection.tables.include? name.to_s + end + + def columns_hash + connection.columns_hash + end end class Base diff --git a/test/test_select_manager.rb b/test/test_select_manager.rb index f457c55f40..2fe43aa982 100644 --- a/test/test_select_manager.rb +++ b/test/test_select_manager.rb @@ -30,6 +30,10 @@ module Arel @engine.connection.columns table, message end + def columns_hash + @engine.connection.columns_hash + end + def table_exists? name @engine.connection.table_exists? name end @@ -122,6 +126,24 @@ module Arel mgr.to_sql.must_be_like %{ SELECT FROM "users" HAVING foo AND bar } end end + + describe 'on' do + it 'converts to sqlliterals' do + table = Table.new :users + right = table.alias + mgr = table.from table + mgr.join(right).on("omg") + mgr.to_sql.must_be_like %{ SELECT FROM "users" INNER JOIN "users" "users_2" ON omg } + end + + it 'converts to sqlliterals' do + table = Table.new :users + right = table.alias + mgr = table.from table + mgr.join(right).on("omg", "123") + mgr.to_sql.must_be_like %{ SELECT FROM "users" INNER JOIN "users" "users_2" ON omg AND 123 } + end + end end describe 'clone' do @@ -158,6 +180,32 @@ module Arel end end + describe 'offset' do + it 'should add an offset' do + table = Table.new :users + mgr = table.from table + mgr.offset = 10 + mgr.to_sql.must_be_like %{ SELECT FROM "users" OFFSET 10 } + end + + it 'should remove an offset' do + table = Table.new :users + mgr = table.from table + mgr.offset = 10 + mgr.to_sql.must_be_like %{ SELECT FROM "users" OFFSET 10 } + + mgr.offset = nil + mgr.to_sql.must_be_like %{ SELECT FROM "users" } + end + + it 'should return the offset' do + table = Table.new :users + mgr = table.from table + mgr.offset = 10 + assert_equal 10, mgr.offset + end + end + describe 'exists' do it 'should create an exists clause' do table = Table.new(:users) @@ -288,9 +336,7 @@ module Arel as_statement = Arel::Nodes::As.new replies, union manager = Arel::SelectManager.new Table.engine - manager.from replies - manager.with :recursive, as_statement - manager.project Arel.star + manager.with(:recursive, as_statement).from(replies).project(Arel.star) sql = manager.to_sql sql.must_be_like %{ @@ -302,7 +348,6 @@ module Arel SELECT * FROM "replies" } end - end describe 'ast' do @@ -724,6 +769,15 @@ module Arel manager = Arel::SelectManager.new Table.engine manager.take(1).must_equal manager end + + it 'removes LIMIT when nil is passed' do + manager = Arel::SelectManager.new Table.engine + manager.limit = 10 + assert_match('LIMIT', manager.to_sql) + + manager.limit = nil + refute_match('LIMIT', manager.to_sql) + end end describe 'where' do diff --git a/test/visitors/test_depth_first.rb b/test/visitors/test_depth_first.rb index 6d74926743..06e7bba9fb 100644 --- a/test/visitors/test_depth_first.rb +++ b/test/visitors/test_depth_first.rb @@ -63,7 +63,7 @@ module Arel end def test_lock - lock = Nodes::Lock.new + lock = Nodes::Lock.new true @visitor.accept lock assert_equal [lock], @collector.calls end diff --git a/test/visitors/test_mssql.rb b/test/visitors/test_mssql.rb index b658e46828..ccaea395fe 100644 --- a/test/visitors/test_mssql.rb +++ b/test/visitors/test_mssql.rb @@ -13,6 +13,15 @@ module Arel sql = @visitor.accept(stmt) sql.must_be_like "SELECT TOP 1" end + + it 'uses TOP in updates with a limit' do + stmt = Nodes::UpdateStatement.new + stmt.limit = Nodes::Limit.new(1) + stmt.key = 'id' + sql = @visitor.accept(stmt) + sql.must_be_like "UPDATE NULL WHERE 'id' IN (SELECT TOP 1 'id' )" + end + end end end diff --git a/test/visitors/test_mysql.rb b/test/visitors/test_mysql.rb index c3b79ca667..3c15c218b2 100644 --- a/test/visitors/test_mysql.rb +++ b/test/visitors/test_mysql.rb @@ -29,11 +29,16 @@ module Arel sql.must_be_like "SELECT FROM DUAL" end - it 'uses FOR UPDATE when locking' do - stmt = Nodes::SelectStatement.new - stmt.lock = Nodes::Lock.new - sql = @visitor.accept(stmt) - sql.must_be_like "SELECT FROM DUAL FOR UPDATE" + describe 'locking' do + it 'defaults to FOR UPDATE when locking' do + node = Nodes::Lock.new(Arel.sql('FOR UPDATE')) + @visitor.accept(node).must_be_like "FOR UPDATE" + end + + it 'allows a custom string to be used as a lock' do + node = Nodes::Lock.new(Arel.sql('LOCK IN SHARE MODE')) + @visitor.accept(node).must_be_like "LOCK IN SHARE MODE" + end end end end diff --git a/test/visitors/test_postgres.rb b/test/visitors/test_postgres.rb index 8d3f19aa6e..74446c23ba 100644 --- a/test/visitors/test_postgres.rb +++ b/test/visitors/test_postgres.rb @@ -7,10 +7,19 @@ module Arel @visitor = PostgreSQL.new Table.engine end - it 'should produce a lock value' do - @visitor.accept(Nodes::Lock.new).must_be_like %{ - FOR UPDATE - } + describe 'locking' do + it 'defaults to FOR UPDATE' do + @visitor.accept(Nodes::Lock.new(Arel.sql('FOR UPDATE'))).must_be_like %{ + FOR UPDATE + } + end + + it 'allows a custom string to be used as a lock' do + node = Nodes::Lock.new(Arel.sql('FOR SHARE')) + @visitor.accept(node).must_be_like %{ + FOR SHARE + } + end end it "should escape LIMIT" do diff --git a/test/visitors/test_to_sql.rb b/test/visitors/test_to_sql.rb index 82ad17a82c..3af316037a 100644 --- a/test/visitors/test_to_sql.rb +++ b/test/visitors/test_to_sql.rb @@ -20,6 +20,12 @@ module Arel assert_equal 'omg(*)', @visitor.accept(function) end + it 'should chain predications on named functions' do + function = Nodes::NamedFunction.new('omg', [Arel.star]) + sql = @visitor.accept(function.eq(2)) + sql.must_be_like %{ omg(*) = 2 } + end + it 'works with lists' do function = Nodes::NamedFunction.new('omg', [Arel.star, Arel.star]) assert_equal 'omg(*, *)', @visitor.accept(function) |