arel 2.0.10 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +51 -4
- data/Manifest.txt +15 -31
- data/README.markdown +27 -3
- data/Rakefile +0 -2
- data/arel.gemspec +20 -19
- data/lib/arel.rb +9 -1
- data/lib/arel/alias_predication.rb +7 -0
- data/lib/arel/attributes/attribute.rb +10 -1
- data/lib/arel/crud.rb +43 -8
- data/lib/arel/expression.rb +1 -0
- data/lib/arel/factory_methods.rb +35 -0
- data/lib/arel/insert_manager.rb +5 -1
- data/lib/arel/math.rb +19 -0
- data/lib/arel/nodes.rb +32 -42
- data/lib/arel/nodes/and.rb +18 -1
- data/lib/arel/nodes/binary.rb +28 -0
- data/lib/arel/nodes/count.rb +0 -3
- data/lib/arel/nodes/function.rb +13 -2
- data/lib/arel/nodes/infix_operation.rb +43 -0
- data/lib/arel/nodes/join_source.rb +14 -0
- data/lib/arel/nodes/named_function.rb +14 -0
- data/lib/arel/nodes/node.rb +5 -2
- data/lib/arel/nodes/select_core.rb +24 -10
- data/lib/arel/nodes/select_statement.rb +8 -6
- data/lib/arel/nodes/sql_literal.rb +2 -0
- data/lib/arel/nodes/string_join.rb +2 -4
- data/lib/arel/nodes/table_alias.rb +7 -3
- data/lib/arel/nodes/{as.rb → terminal.rb} +1 -1
- data/lib/arel/nodes/unary.rb +17 -0
- data/lib/arel/nodes/unqualified_column.rb +4 -0
- data/lib/arel/nodes/update_statement.rb +4 -2
- data/lib/arel/nodes/with.rb +10 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +6 -24
- data/lib/arel/select_manager.rb +95 -40
- data/lib/arel/table.rb +32 -19
- data/lib/arel/tree_manager.rb +1 -0
- data/lib/arel/update_manager.rb +4 -0
- data/lib/arel/visitors.rb +2 -0
- data/lib/arel/visitors/depth_first.rb +19 -9
- data/lib/arel/visitors/dot.rb +42 -25
- data/lib/arel/visitors/ibm_db.rb +12 -0
- data/lib/arel/visitors/join_sql.rb +2 -23
- data/lib/arel/visitors/mssql.rb +7 -0
- data/lib/arel/visitors/mysql.rb +4 -0
- data/lib/arel/visitors/oracle.rb +2 -2
- data/lib/arel/visitors/postgresql.rb +2 -38
- data/lib/arel/visitors/to_sql.rb +148 -67
- data/test/attributes/test_attribute.rb +3 -3
- data/test/helper.rb +1 -1
- data/test/nodes/test_as.rb +6 -0
- data/test/nodes/test_bin.rb +23 -0
- data/test/nodes/test_named_function.rb +30 -0
- data/test/nodes/test_node.rb +10 -0
- data/test/nodes/test_not.rb +4 -7
- data/test/nodes/test_select_core.rb +23 -14
- data/test/support/fake_record.rb +21 -4
- data/test/test_attributes.rb +8 -0
- data/test/test_crud.rb +6 -12
- data/test/test_factory_methods.rb +34 -0
- data/test/test_insert_manager.rb +19 -0
- data/test/test_select_manager.rb +343 -64
- data/test/test_table.rb +36 -45
- data/test/visitors/test_depth_first.rb +29 -11
- data/test/visitors/test_dot.rb +47 -0
- data/test/visitors/test_ibm_db.rb +27 -0
- data/test/visitors/test_join_sql.rb +14 -7
- data/test/visitors/test_mssql.rb +9 -0
- data/test/visitors/test_oracle.rb +11 -10
- data/test/visitors/test_postgres.rb +12 -0
- data/test/visitors/test_to_sql.rb +80 -17
- metadata +80 -101
- data/lib/arel/nodes/assignment.rb +0 -6
- data/lib/arel/nodes/avg.rb +0 -6
- data/lib/arel/nodes/between.rb +0 -6
- data/lib/arel/nodes/does_not_match.rb +0 -6
- data/lib/arel/nodes/except.rb +0 -7
- data/lib/arel/nodes/exists.rb +0 -7
- data/lib/arel/nodes/greater_than.rb +0 -6
- data/lib/arel/nodes/greater_than_or_equal.rb +0 -6
- data/lib/arel/nodes/group.rb +0 -6
- data/lib/arel/nodes/grouping.rb +0 -6
- data/lib/arel/nodes/having.rb +0 -6
- data/lib/arel/nodes/intersect.rb +0 -7
- data/lib/arel/nodes/join.rb +0 -13
- data/lib/arel/nodes/less_than.rb +0 -6
- data/lib/arel/nodes/less_than_or_equal.rb +0 -6
- data/lib/arel/nodes/limit.rb +0 -7
- data/lib/arel/nodes/lock.rb +0 -6
- data/lib/arel/nodes/matches.rb +0 -6
- data/lib/arel/nodes/max.rb +0 -6
- data/lib/arel/nodes/min.rb +0 -6
- data/lib/arel/nodes/not.rb +0 -6
- data/lib/arel/nodes/not_equal.rb +0 -6
- data/lib/arel/nodes/not_in.rb +0 -6
- data/lib/arel/nodes/offset.rb +0 -7
- data/lib/arel/nodes/on.rb +0 -6
- data/lib/arel/nodes/or.rb +0 -6
- data/lib/arel/nodes/sum.rb +0 -6
- data/lib/arel/nodes/top.rb +0 -6
- data/lib/arel/nodes/union.rb +0 -7
- data/lib/arel/nodes/union_all.rb +0 -7
@@ -326,7 +326,7 @@ module Arel
|
|
326
326
|
|
327
327
|
describe '#eq' do
|
328
328
|
it 'should return an equality node' do
|
329
|
-
attribute = Attribute.new nil, nil
|
329
|
+
attribute = Attribute.new nil, nil
|
330
330
|
equality = attribute.eq 1
|
331
331
|
equality.left.must_equal attribute
|
332
332
|
equality.right.must_equal 1
|
@@ -501,7 +501,7 @@ module Arel
|
|
501
501
|
end
|
502
502
|
|
503
503
|
it 'should return an in node' do
|
504
|
-
attribute = Attribute.new nil, nil
|
504
|
+
attribute = Attribute.new nil, nil
|
505
505
|
node = Nodes::In.new attribute, [1,2,3]
|
506
506
|
node.left.must_equal attribute
|
507
507
|
node.right.must_equal [1, 2, 3]
|
@@ -554,7 +554,7 @@ module Arel
|
|
554
554
|
end
|
555
555
|
|
556
556
|
it 'should return a NotIn node' do
|
557
|
-
attribute = Attribute.new nil, nil
|
557
|
+
attribute = Attribute.new nil, nil
|
558
558
|
node = Nodes::NotIn.new attribute, [1,2,3]
|
559
559
|
node.left.must_equal attribute
|
560
560
|
node.right.must_equal [1, 2, 3]
|
data/test/helper.rb
CHANGED
data/test/nodes/test_as.rb
CHANGED
@@ -10,6 +10,12 @@ module Arel
|
|
10
10
|
assert_equal attr, as.left
|
11
11
|
assert_equal 'foo', as.right
|
12
12
|
end
|
13
|
+
|
14
|
+
it 'converts right to SqlLiteral if a string' do
|
15
|
+
attr = Table.new(:users)[:id]
|
16
|
+
as = attr.as('foo')
|
17
|
+
assert_kind_of Arel::Nodes::SqlLiteral, as.right
|
18
|
+
end
|
13
19
|
end
|
14
20
|
end
|
15
21
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
module Arel
|
4
|
+
module Nodes
|
5
|
+
class TestBin < MiniTest::Unit::TestCase
|
6
|
+
def test_new
|
7
|
+
assert Arel::Nodes::Bin.new('zomg')
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_default_to_sql
|
11
|
+
viz = Arel::Visitors::ToSql.new Table.engine
|
12
|
+
node = Arel::Nodes::Bin.new(Arel.sql('zomg'))
|
13
|
+
assert_equal 'zomg', viz.accept(node)
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_mysql_to_sql
|
17
|
+
viz = Arel::Visitors::MySQL.new Table.engine
|
18
|
+
node = Arel::Nodes::Bin.new(Arel.sql('zomg'))
|
19
|
+
assert_equal 'BINARY zomg', viz.accept(node)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
module Arel
|
4
|
+
module Nodes
|
5
|
+
class TestNamedFunction < MiniTest::Unit::TestCase
|
6
|
+
def test_construct
|
7
|
+
function = NamedFunction.new 'omg', 'zomg'
|
8
|
+
assert_equal 'omg', function.name
|
9
|
+
assert_equal 'zomg', function.expressions
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_function_alias
|
13
|
+
function = NamedFunction.new 'omg', 'zomg'
|
14
|
+
function = function.as('wth')
|
15
|
+
assert_equal 'omg', function.name
|
16
|
+
assert_equal 'zomg', function.expressions
|
17
|
+
assert_kind_of SqlLiteral, function.alias
|
18
|
+
assert_equal 'wth', function.alias
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_construct_with_alias
|
22
|
+
function = NamedFunction.new 'omg', 'zomg', 'wth'
|
23
|
+
assert_equal 'omg', function.name
|
24
|
+
assert_equal 'zomg', function.expressions
|
25
|
+
assert_kind_of SqlLiteral, function.alias
|
26
|
+
assert_equal 'wth', function.alias
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/test/nodes/test_node.rb
CHANGED
@@ -2,11 +2,16 @@ require 'helper'
|
|
2
2
|
|
3
3
|
module Arel
|
4
4
|
class TestNode < MiniTest::Unit::TestCase
|
5
|
+
def test_includes_factory_methods
|
6
|
+
assert Node.new.respond_to?(:create_join)
|
7
|
+
end
|
8
|
+
|
5
9
|
def test_all_nodes_are_nodes
|
6
10
|
Nodes.constants.map { |k|
|
7
11
|
Nodes.const_get(k)
|
8
12
|
}.grep(Class).each do |klass|
|
9
13
|
next if Nodes::SqlLiteral == klass
|
14
|
+
next if klass.name =~ /^Arel::Nodes::Test/
|
10
15
|
assert klass.ancestors.include?(Nodes::Node), klass.name
|
11
16
|
end
|
12
17
|
end
|
@@ -24,5 +29,10 @@ module Arel
|
|
24
29
|
node.each.each { |n| list << n }
|
25
30
|
assert_equal [node], list
|
26
31
|
end
|
32
|
+
|
33
|
+
def test_enumerable
|
34
|
+
node = Nodes::Node.new
|
35
|
+
assert_kind_of Enumerable, node
|
36
|
+
end
|
27
37
|
end
|
28
38
|
end
|
data/test/nodes/test_not.rb
CHANGED
@@ -6,13 +6,10 @@ module Arel
|
|
6
6
|
describe '#not' do
|
7
7
|
it 'makes a NOT node' do
|
8
8
|
attr = Table.new(:users)[:id]
|
9
|
-
|
10
|
-
|
11
|
-
node
|
12
|
-
node.expr.
|
13
|
-
node.expr.right.must_equal right
|
14
|
-
|
15
|
-
node.or(right).not
|
9
|
+
expr = attr.eq(10)
|
10
|
+
node = expr.not
|
11
|
+
node.must_be_kind_of Not
|
12
|
+
node.expr.must_equal expr
|
16
13
|
end
|
17
14
|
end
|
18
15
|
end
|
@@ -1,22 +1,31 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
3
|
+
module Arel
|
4
|
+
module Nodes
|
5
|
+
class TestSelectCore < MiniTest::Unit::TestCase
|
6
|
+
def test_clone
|
7
|
+
core = Arel::Nodes::SelectCore.new
|
8
|
+
core.froms = %w[a b c]
|
9
|
+
core.projections = %w[d e f]
|
10
|
+
core.wheres = %w[g h i]
|
10
11
|
|
11
|
-
|
12
|
+
dolly = core.clone
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
14
|
+
dolly.froms.must_equal core.froms
|
15
|
+
dolly.projections.must_equal core.projections
|
16
|
+
dolly.wheres.must_equal core.wheres
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
18
|
+
dolly.froms.wont_be_same_as core.froms
|
19
|
+
dolly.projections.wont_be_same_as core.projections
|
20
|
+
dolly.wheres.wont_be_same_as core.wheres
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_set_quantifier
|
24
|
+
core = Arel::Nodes::SelectCore.new
|
25
|
+
core.set_quantifier = Arel::Nodes::Distinct.new
|
26
|
+
viz = Arel::Visitors::ToSql.new Table.engine
|
27
|
+
assert_match 'DISTINCT', viz.accept(core)
|
28
|
+
end
|
20
29
|
end
|
21
30
|
end
|
22
31
|
end
|
data/test/support/fake_record.rb
CHANGED
@@ -3,20 +3,29 @@ module FakeRecord
|
|
3
3
|
end
|
4
4
|
|
5
5
|
class Connection
|
6
|
-
attr_reader :tables
|
6
|
+
attr_reader :tables, :columns_hash
|
7
7
|
|
8
8
|
def initialize
|
9
|
-
@tables = %w{ users photos developers }
|
9
|
+
@tables = %w{ users photos developers products}
|
10
10
|
@columns = {
|
11
11
|
'users' => [
|
12
12
|
Column.new('id', :integer),
|
13
13
|
Column.new('name', :string),
|
14
14
|
Column.new('bool', :boolean),
|
15
|
-
Column.new('created_at', :date)
|
15
|
+
Column.new('created_at', :date)
|
16
|
+
],
|
17
|
+
'products' => [
|
18
|
+
Column.new('id', :integer),
|
19
|
+
Column.new('price', :decimal)
|
16
20
|
]
|
17
21
|
}
|
22
|
+
@columns_hash = {
|
23
|
+
'users' => Hash[@columns['users'].map { |x| [x.name, x] }],
|
24
|
+
'products' => Hash[@columns['products'].map { |x| [x.name, x] }]
|
25
|
+
}
|
18
26
|
@primary_keys = {
|
19
|
-
'users' => 'id'
|
27
|
+
'users' => 'id',
|
28
|
+
'products' => 'id'
|
20
29
|
}
|
21
30
|
end
|
22
31
|
|
@@ -75,6 +84,14 @@ module FakeRecord
|
|
75
84
|
def with_connection
|
76
85
|
yield connection
|
77
86
|
end
|
87
|
+
|
88
|
+
def table_exists? name
|
89
|
+
connection.tables.include? name.to_s
|
90
|
+
end
|
91
|
+
|
92
|
+
def columns_hash
|
93
|
+
connection.columns_hash
|
94
|
+
end
|
78
95
|
end
|
79
96
|
|
80
97
|
class Base
|
data/test/test_attributes.rb
CHANGED
@@ -2,6 +2,14 @@ require 'helper'
|
|
2
2
|
|
3
3
|
module Arel
|
4
4
|
describe 'Attributes' do
|
5
|
+
it 'responds to lower' do
|
6
|
+
relation = Table.new(:users)
|
7
|
+
attribute = relation[:foo]
|
8
|
+
node = attribute.lower
|
9
|
+
assert_equal 'LOWER', node.name
|
10
|
+
assert_equal [attribute], node.expressions
|
11
|
+
end
|
12
|
+
|
5
13
|
describe 'for' do
|
6
14
|
it 'deals with unknown column types' do
|
7
15
|
column = Struct.new(:type).new :crazy
|
data/test/test_crud.rb
CHANGED
@@ -35,10 +35,8 @@ module Arel
|
|
35
35
|
table = Table.new :users
|
36
36
|
fc = FakeCrudder.new
|
37
37
|
fc.from table
|
38
|
-
fc.
|
39
|
-
|
40
|
-
method == :insert
|
41
|
-
}.wont_be_nil
|
38
|
+
im = fc.compile_insert [[table[:id], 'foo']]
|
39
|
+
assert_instance_of Arel::InsertManager, im
|
42
40
|
end
|
43
41
|
end
|
44
42
|
|
@@ -47,10 +45,8 @@ module Arel
|
|
47
45
|
table = Table.new :users
|
48
46
|
fc = FakeCrudder.new
|
49
47
|
fc.from table
|
50
|
-
fc.
|
51
|
-
|
52
|
-
method == :update
|
53
|
-
}.wont_be_nil
|
48
|
+
stmt = fc.compile_update [[table[:id], 'foo']]
|
49
|
+
assert_instance_of Arel::UpdateManager, stmt
|
54
50
|
end
|
55
51
|
end
|
56
52
|
|
@@ -59,10 +55,8 @@ module Arel
|
|
59
55
|
table = Table.new :users
|
60
56
|
fc = FakeCrudder.new
|
61
57
|
fc.from table
|
62
|
-
fc.
|
63
|
-
|
64
|
-
method == :delete
|
65
|
-
}.wont_be_nil
|
58
|
+
stmt = fc.compile_delete
|
59
|
+
assert_instance_of Arel::DeleteManager, stmt
|
66
60
|
end
|
67
61
|
end
|
68
62
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
module Arel
|
4
|
+
module FactoryMethods
|
5
|
+
class TestFactoryMethods < MiniTest::Unit::TestCase
|
6
|
+
class Factory
|
7
|
+
include Arel::FactoryMethods
|
8
|
+
end
|
9
|
+
|
10
|
+
def setup
|
11
|
+
@factory = Factory.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_create_join
|
15
|
+
join = @factory.create_join :one, :two
|
16
|
+
assert_kind_of Nodes::Join, join
|
17
|
+
assert_equal :two, join.right
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_create_on
|
21
|
+
on = @factory.create_on :one
|
22
|
+
assert_instance_of Nodes::On, on
|
23
|
+
assert_equal :one, on.expr
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_lower
|
27
|
+
lower = @factory.lower :one
|
28
|
+
assert_instance_of Nodes::NamedFunction, lower
|
29
|
+
assert_equal 'LOWER', lower.name
|
30
|
+
assert_equal [:one], lower.expressions
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/test/test_insert_manager.rb
CHANGED
@@ -9,6 +9,25 @@ module Arel
|
|
9
9
|
end
|
10
10
|
|
11
11
|
describe 'insert' do
|
12
|
+
it 'can create a Values node' do
|
13
|
+
table = Table.new(:users)
|
14
|
+
manager = Arel::InsertManager.new Table.engine
|
15
|
+
values = manager.create_values %w{ a b }, %w{ c d }
|
16
|
+
|
17
|
+
assert_kind_of Arel::Nodes::Values, values
|
18
|
+
assert_equal %w{ a b }, values.left
|
19
|
+
assert_equal %w{ c d }, values.right
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'allows sql literals' do
|
23
|
+
table = Table.new(:users)
|
24
|
+
manager = Arel::InsertManager.new Table.engine
|
25
|
+
manager.values = manager.create_values [Arel.sql('*')], %w{ a }
|
26
|
+
manager.to_sql.must_be_like %{
|
27
|
+
INSERT INTO NULL VALUES (*)
|
28
|
+
}
|
29
|
+
end
|
30
|
+
|
12
31
|
it "inserts false" do
|
13
32
|
table = Table.new(:users)
|
14
33
|
manager = Arel::InsertManager.new Table.engine
|
data/test/test_select_manager.rb
CHANGED
@@ -26,6 +26,21 @@ module Arel
|
|
26
26
|
def quote_table_name thing; @engine.connection.quote_table_name thing end
|
27
27
|
def quote_column_name thing; @engine.connection.quote_column_name thing end
|
28
28
|
def quote thing, column; @engine.connection.quote thing, column end
|
29
|
+
def columns table, message = nil
|
30
|
+
@engine.connection.columns table, message
|
31
|
+
end
|
32
|
+
|
33
|
+
def columns_hash
|
34
|
+
@engine.connection.columns_hash
|
35
|
+
end
|
36
|
+
|
37
|
+
def table_exists? name
|
38
|
+
@engine.connection.table_exists? name
|
39
|
+
end
|
40
|
+
|
41
|
+
def tables
|
42
|
+
@engine.connection.tables
|
43
|
+
end
|
29
44
|
|
30
45
|
def execute sql, name = nil, *args
|
31
46
|
@executed << sql
|
@@ -36,6 +51,12 @@ module Arel
|
|
36
51
|
end
|
37
52
|
|
38
53
|
describe 'select manager' do
|
54
|
+
def test_join_sources
|
55
|
+
manager = Arel::SelectManager.new Table.engine
|
56
|
+
manager.join_sources << Arel::Nodes::StringJoin.new('foo')
|
57
|
+
assert_equal "SELECT FROM 'foo'", manager.to_sql
|
58
|
+
end
|
59
|
+
|
39
60
|
describe 'backwards compatibility' do
|
40
61
|
describe 'project' do
|
41
62
|
it 'accepts symbols as sql literals' do
|
@@ -70,6 +91,34 @@ module Arel
|
|
70
91
|
end
|
71
92
|
end
|
72
93
|
|
94
|
+
describe 'as' do
|
95
|
+
it 'makes an AS node by grouping the AST' do
|
96
|
+
manager = Arel::SelectManager.new Table.engine
|
97
|
+
as = manager.as(Arel.sql('foo'))
|
98
|
+
assert_kind_of Arel::Nodes::Grouping, as.left
|
99
|
+
assert_equal manager.ast, as.left.expr
|
100
|
+
assert_equal 'foo', as.right
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'converts right to SqlLiteral if a string' do
|
104
|
+
manager = Arel::SelectManager.new Table.engine
|
105
|
+
as = manager.as('foo')
|
106
|
+
assert_kind_of Arel::Nodes::SqlLiteral, as.right
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'can make a subselect' do
|
110
|
+
manager = Arel::SelectManager.new Table.engine
|
111
|
+
manager.project Arel.star
|
112
|
+
manager.from Arel.sql('zomg')
|
113
|
+
as = manager.as(Arel.sql('foo'))
|
114
|
+
|
115
|
+
manager = Arel::SelectManager.new Table.engine
|
116
|
+
manager.project Arel.sql('name')
|
117
|
+
manager.from as
|
118
|
+
manager.to_sql.must_be_like "SELECT name FROM (SELECT * FROM zomg ) foo"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
73
122
|
describe 'from' do
|
74
123
|
it 'ignores strings when table of same name exists' do
|
75
124
|
table = Table.new :users
|
@@ -80,15 +129,47 @@ module Arel
|
|
80
129
|
manager.project table['id']
|
81
130
|
manager.to_sql.must_be_like 'SELECT "users"."id" FROM users'
|
82
131
|
end
|
132
|
+
|
133
|
+
it 'should support any ast' do
|
134
|
+
table = Table.new :users
|
135
|
+
manager1 = Arel::SelectManager.new Table.engine
|
136
|
+
|
137
|
+
manager2 = Arel::SelectManager.new Table.engine
|
138
|
+
manager2.project(Arel.sql('*'))
|
139
|
+
manager2.from table
|
140
|
+
|
141
|
+
manager1.project Arel.sql('lol')
|
142
|
+
as = manager2.as Arel.sql('omg')
|
143
|
+
manager1.from(as)
|
144
|
+
|
145
|
+
manager1.to_sql.must_be_like %{
|
146
|
+
SELECT lol FROM (SELECT * FROM "users" ) omg
|
147
|
+
}
|
148
|
+
end
|
83
149
|
end
|
84
150
|
|
85
|
-
describe '
|
151
|
+
describe 'having' do
|
86
152
|
it 'converts strings to SQLLiterals' do
|
87
153
|
table = Table.new :users
|
88
154
|
mgr = table.from table
|
89
155
|
mgr.having 'foo'
|
90
156
|
mgr.to_sql.must_be_like %{ SELECT FROM "users" HAVING foo }
|
91
157
|
end
|
158
|
+
|
159
|
+
it 'can have multiple items specified separately' do
|
160
|
+
table = Table.new :users
|
161
|
+
mgr = table.from table
|
162
|
+
mgr.having 'foo'
|
163
|
+
mgr.having 'bar'
|
164
|
+
mgr.to_sql.must_be_like %{ SELECT FROM "users" HAVING foo AND bar }
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'can have multiple items specified together' do
|
168
|
+
table = Table.new :users
|
169
|
+
mgr = table.from table
|
170
|
+
mgr.having 'foo', 'bar'
|
171
|
+
mgr.to_sql.must_be_like %{ SELECT FROM "users" HAVING foo AND bar }
|
172
|
+
end
|
92
173
|
end
|
93
174
|
|
94
175
|
describe 'on' do
|
@@ -118,6 +199,16 @@ module Arel
|
|
118
199
|
m2.project "foo"
|
119
200
|
mgr.to_sql.wont_equal m2.to_sql
|
120
201
|
end
|
202
|
+
|
203
|
+
it 'makes updates to the correct copy' do
|
204
|
+
table = Table.new :users, :engine => Table.engine, :as => 'foo'
|
205
|
+
mgr = table.from table
|
206
|
+
m2 = mgr.clone
|
207
|
+
m3 = m2.clone
|
208
|
+
m2.project "foo"
|
209
|
+
mgr.to_sql.wont_equal m2.to_sql
|
210
|
+
m3.to_sql.must_equal mgr.to_sql
|
211
|
+
end
|
121
212
|
end
|
122
213
|
|
123
214
|
describe 'initialize' do
|
@@ -125,7 +216,7 @@ module Arel
|
|
125
216
|
table = Table.new :users, :engine => Table.engine, :as => 'foo'
|
126
217
|
mgr = table.from table
|
127
218
|
mgr.skip 10
|
128
|
-
mgr.to_sql.must_be_like %{ SELECT FROM "users"
|
219
|
+
mgr.to_sql.must_be_like %{ SELECT FROM "users" foo OFFSET 10 }
|
129
220
|
end
|
130
221
|
end
|
131
222
|
|
@@ -144,6 +235,32 @@ module Arel
|
|
144
235
|
end
|
145
236
|
end
|
146
237
|
|
238
|
+
describe 'offset' do
|
239
|
+
it 'should add an offset' do
|
240
|
+
table = Table.new :users
|
241
|
+
mgr = table.from table
|
242
|
+
mgr.offset = 10
|
243
|
+
mgr.to_sql.must_be_like %{ SELECT FROM "users" OFFSET 10 }
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'should remove an offset' do
|
247
|
+
table = Table.new :users
|
248
|
+
mgr = table.from table
|
249
|
+
mgr.offset = 10
|
250
|
+
mgr.to_sql.must_be_like %{ SELECT FROM "users" OFFSET 10 }
|
251
|
+
|
252
|
+
mgr.offset = nil
|
253
|
+
mgr.to_sql.must_be_like %{ SELECT FROM "users" }
|
254
|
+
end
|
255
|
+
|
256
|
+
it 'should return the offset' do
|
257
|
+
table = Table.new :users
|
258
|
+
mgr = table.from table
|
259
|
+
mgr.offset = 10
|
260
|
+
assert_equal 10, mgr.offset
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
147
264
|
describe 'exists' do
|
148
265
|
it 'should create an exists clause' do
|
149
266
|
table = Table.new(:users)
|
@@ -164,6 +281,130 @@ module Arel
|
|
164
281
|
end
|
165
282
|
end
|
166
283
|
|
284
|
+
describe 'union' do
|
285
|
+
before do
|
286
|
+
table = Table.new :users
|
287
|
+
@m1 = Arel::SelectManager.new Table.engine, table
|
288
|
+
@m1.project Arel.star
|
289
|
+
@m1.where(table[:age].lt(18))
|
290
|
+
|
291
|
+
@m2 = Arel::SelectManager.new Table.engine, table
|
292
|
+
@m2.project Arel.star
|
293
|
+
@m2.where(table[:age].gt(99))
|
294
|
+
|
295
|
+
|
296
|
+
end
|
297
|
+
|
298
|
+
it 'should union two managers' do
|
299
|
+
# FIXME should this union "managers" or "statements" ?
|
300
|
+
# FIXME this probably shouldn't return a node
|
301
|
+
node = @m1.union @m2
|
302
|
+
|
303
|
+
# maybe FIXME: decide when wrapper parens are needed
|
304
|
+
node.to_sql.must_be_like %{
|
305
|
+
( SELECT * FROM "users" WHERE "users"."age" < 18 UNION SELECT * FROM "users" WHERE "users"."age" > 99 )
|
306
|
+
}
|
307
|
+
end
|
308
|
+
|
309
|
+
it 'should union all' do
|
310
|
+
node = @m1.union :all, @m2
|
311
|
+
|
312
|
+
node.to_sql.must_be_like %{
|
313
|
+
( SELECT * FROM "users" WHERE "users"."age" < 18 UNION ALL SELECT * FROM "users" WHERE "users"."age" > 99 )
|
314
|
+
}
|
315
|
+
end
|
316
|
+
|
317
|
+
end
|
318
|
+
|
319
|
+
describe 'intersect' do
|
320
|
+
before do
|
321
|
+
table = Table.new :users
|
322
|
+
@m1 = Arel::SelectManager.new Table.engine, table
|
323
|
+
@m1.project Arel.star
|
324
|
+
@m1.where(table[:age].gt(18))
|
325
|
+
|
326
|
+
@m2 = Arel::SelectManager.new Table.engine, table
|
327
|
+
@m2.project Arel.star
|
328
|
+
@m2.where(table[:age].lt(99))
|
329
|
+
|
330
|
+
|
331
|
+
end
|
332
|
+
|
333
|
+
it 'should interect two managers' do
|
334
|
+
# FIXME should this intersect "managers" or "statements" ?
|
335
|
+
# FIXME this probably shouldn't return a node
|
336
|
+
node = @m1.intersect @m2
|
337
|
+
|
338
|
+
# maybe FIXME: decide when wrapper parens are needed
|
339
|
+
node.to_sql.must_be_like %{
|
340
|
+
( SELECT * FROM "users" WHERE "users"."age" > 18 INTERSECT SELECT * FROM "users" WHERE "users"."age" < 99 )
|
341
|
+
}
|
342
|
+
end
|
343
|
+
|
344
|
+
end
|
345
|
+
|
346
|
+
describe 'except' do
|
347
|
+
before do
|
348
|
+
table = Table.new :users
|
349
|
+
@m1 = Arel::SelectManager.new Table.engine, table
|
350
|
+
@m1.project Arel.star
|
351
|
+
@m1.where(table[:age].in(18..60))
|
352
|
+
|
353
|
+
@m2 = Arel::SelectManager.new Table.engine, table
|
354
|
+
@m2.project Arel.star
|
355
|
+
@m2.where(table[:age].in(40..99))
|
356
|
+
|
357
|
+
|
358
|
+
end
|
359
|
+
|
360
|
+
it 'should except two managers' do
|
361
|
+
# FIXME should this except "managers" or "statements" ?
|
362
|
+
# FIXME this probably shouldn't return a node
|
363
|
+
node = @m1.except @m2
|
364
|
+
|
365
|
+
# maybe FIXME: decide when wrapper parens are needed
|
366
|
+
node.to_sql.must_be_like %{
|
367
|
+
( SELECT * FROM "users" WHERE "users"."age" BETWEEN 18 AND 60 EXCEPT SELECT * FROM "users" WHERE "users"."age" BETWEEN 40 AND 99 )
|
368
|
+
}
|
369
|
+
end
|
370
|
+
|
371
|
+
end
|
372
|
+
|
373
|
+
describe 'with' do
|
374
|
+
|
375
|
+
it "should support WITH RECURSIVE" do
|
376
|
+
comments = Table.new(:comments)
|
377
|
+
comments_id = comments[:id]
|
378
|
+
comments_parent_id = comments[:parent_id]
|
379
|
+
|
380
|
+
replies = Table.new(:replies)
|
381
|
+
replies_id = replies[:id]
|
382
|
+
|
383
|
+
recursive_term = Arel::SelectManager.new Table.engine
|
384
|
+
recursive_term.from(comments).project(comments_id, comments_parent_id).where(comments_id.eq 42)
|
385
|
+
|
386
|
+
non_recursive_term = Arel::SelectManager.new Table.engine
|
387
|
+
non_recursive_term.from(comments).project(comments_id, comments_parent_id).join(replies).on(comments_parent_id.eq replies_id)
|
388
|
+
|
389
|
+
union = recursive_term.union(non_recursive_term)
|
390
|
+
|
391
|
+
as_statement = Arel::Nodes::As.new replies, union
|
392
|
+
|
393
|
+
manager = Arel::SelectManager.new Table.engine
|
394
|
+
manager.with(:recursive, as_statement).from(replies).project(Arel.star)
|
395
|
+
|
396
|
+
sql = manager.to_sql
|
397
|
+
sql.must_be_like %{
|
398
|
+
WITH RECURSIVE "replies" AS (
|
399
|
+
SELECT "comments"."id", "comments"."parent_id" FROM "comments" WHERE "comments"."id" = 42
|
400
|
+
UNION
|
401
|
+
SELECT "comments"."id", "comments"."parent_id" FROM "comments" INNER JOIN "replies" ON "comments"."parent_id" = "replies"."id"
|
402
|
+
)
|
403
|
+
SELECT * FROM "replies"
|
404
|
+
}
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
167
408
|
describe 'ast' do
|
168
409
|
it 'should return the ast' do
|
169
410
|
table = Table.new :users
|
@@ -243,6 +484,29 @@ module Arel
|
|
243
484
|
manager = Arel::SelectManager.new Table.engine
|
244
485
|
manager.order(table[:id]).must_equal manager
|
245
486
|
end
|
487
|
+
|
488
|
+
it 'has order attributes' do
|
489
|
+
table = Table.new :users
|
490
|
+
manager = Arel::SelectManager.new Table.engine
|
491
|
+
manager.project SqlLiteral.new '*'
|
492
|
+
manager.from table
|
493
|
+
manager.order table[:id].desc
|
494
|
+
manager.to_sql.must_be_like %{
|
495
|
+
SELECT * FROM "users" ORDER BY "users"."id" DESC
|
496
|
+
}
|
497
|
+
end
|
498
|
+
|
499
|
+
it 'has order attributes for expressions' do
|
500
|
+
table = Table.new :users
|
501
|
+
manager = Arel::SelectManager.new Table.engine
|
502
|
+
manager.project SqlLiteral.new '*'
|
503
|
+
manager.from table
|
504
|
+
manager.order table[:id].count.desc
|
505
|
+
manager.to_sql.must_be_like %{
|
506
|
+
SELECT * FROM "users" ORDER BY COUNT("users"."id") DESC
|
507
|
+
}
|
508
|
+
end
|
509
|
+
|
246
510
|
end
|
247
511
|
|
248
512
|
describe 'on' do
|
@@ -284,6 +548,41 @@ module Arel
|
|
284
548
|
end
|
285
549
|
end
|
286
550
|
|
551
|
+
it 'should hand back froms' do
|
552
|
+
relation = Arel::SelectManager.new Table.engine
|
553
|
+
assert_equal [], relation.froms
|
554
|
+
end
|
555
|
+
|
556
|
+
it 'should create and nodes' do
|
557
|
+
relation = Arel::SelectManager.new Table.engine
|
558
|
+
children = ['foo', 'bar', 'baz']
|
559
|
+
clause = relation.create_and children
|
560
|
+
assert_kind_of Arel::Nodes::And, clause
|
561
|
+
assert_equal children, clause.children
|
562
|
+
end
|
563
|
+
|
564
|
+
it 'should create insert managers' do
|
565
|
+
relation = Arel::SelectManager.new Table.engine
|
566
|
+
insert = relation.create_insert
|
567
|
+
assert_kind_of Arel::InsertManager, insert
|
568
|
+
end
|
569
|
+
|
570
|
+
it 'should create join nodes' do
|
571
|
+
relation = Arel::SelectManager.new Table.engine
|
572
|
+
join = relation.create_join 'foo', 'bar'
|
573
|
+
assert_kind_of Arel::Nodes::InnerJoin, join
|
574
|
+
assert_equal 'foo', join.left
|
575
|
+
assert_equal 'bar', join.right
|
576
|
+
end
|
577
|
+
|
578
|
+
it 'should create join nodes with a klass' do
|
579
|
+
relation = Arel::SelectManager.new Table.engine
|
580
|
+
join = relation.create_join 'foo', 'bar', Arel::Nodes::OuterJoin
|
581
|
+
assert_kind_of Arel::Nodes::OuterJoin, join
|
582
|
+
assert_equal 'foo', join.left
|
583
|
+
assert_equal 'bar', join.right
|
584
|
+
end
|
585
|
+
|
287
586
|
describe 'join' do
|
288
587
|
it 'responds to join' do
|
289
588
|
left = Table.new :users
|
@@ -326,30 +625,27 @@ module Arel
|
|
326
625
|
table = Table.new :users
|
327
626
|
aliaz = table.alias
|
328
627
|
manager = Arel::SelectManager.new Table.engine
|
329
|
-
manager.from Nodes::InnerJoin.new(
|
628
|
+
manager.from Nodes::InnerJoin.new(aliaz, table[:id].eq(aliaz[:id]))
|
330
629
|
manager.join_sql.must_be_like %{
|
331
630
|
INNER JOIN "users" "users_2" "users"."id" = "users_2"."id"
|
332
631
|
}
|
333
|
-
manager.joins(manager).must_equal manager.join_sql
|
334
632
|
end
|
335
633
|
|
336
634
|
it 'returns outer join sql' do
|
337
635
|
table = Table.new :users
|
338
636
|
aliaz = table.alias
|
339
637
|
manager = Arel::SelectManager.new Table.engine
|
340
|
-
manager.from Nodes::OuterJoin.new(
|
638
|
+
manager.from Nodes::OuterJoin.new(aliaz, table[:id].eq(aliaz[:id]))
|
341
639
|
manager.join_sql.must_be_like %{
|
342
640
|
LEFT OUTER JOIN "users" "users_2" "users"."id" = "users_2"."id"
|
343
641
|
}
|
344
|
-
manager.joins(manager).must_equal manager.join_sql
|
345
642
|
end
|
346
643
|
|
347
644
|
it 'returns string join sql' do
|
348
645
|
table = Table.new :users
|
349
646
|
manager = Arel::SelectManager.new Table.engine
|
350
|
-
manager.from Nodes::StringJoin.new(
|
647
|
+
manager.from Nodes::StringJoin.new('hello')
|
351
648
|
manager.join_sql.must_be_like %{ 'hello' }
|
352
|
-
manager.joins(manager).must_equal manager.join_sql
|
353
649
|
end
|
354
650
|
|
355
651
|
it 'returns nil join sql' do
|
@@ -411,9 +707,9 @@ module Arel
|
|
411
707
|
table = Table.new :users
|
412
708
|
manager = Arel::SelectManager.new engine
|
413
709
|
manager.from table
|
414
|
-
manager.
|
710
|
+
stmt = manager.compile_delete
|
415
711
|
|
416
|
-
|
712
|
+
stmt.to_sql.must_be_like %{ DELETE FROM "users" }
|
417
713
|
end
|
418
714
|
|
419
715
|
it "copies where" do
|
@@ -422,9 +718,9 @@ module Arel
|
|
422
718
|
manager = Arel::SelectManager.new engine
|
423
719
|
manager.from table
|
424
720
|
manager.where table[:id].eq 10
|
425
|
-
manager.
|
721
|
+
stmt = manager.compile_delete
|
426
722
|
|
427
|
-
|
723
|
+
stmt.to_sql.must_be_like %{
|
428
724
|
DELETE FROM "users" WHERE "users"."id" = 10
|
429
725
|
}
|
430
726
|
end
|
@@ -454,9 +750,10 @@ module Arel
|
|
454
750
|
manager = Arel::SelectManager.new engine
|
455
751
|
manager.from table
|
456
752
|
manager.take 1
|
457
|
-
manager.
|
753
|
+
stmt = manager.compile_update(SqlLiteral.new('foo = bar'))
|
754
|
+
stmt.key = table['id']
|
458
755
|
|
459
|
-
|
756
|
+
stmt.to_sql.must_be_like %{
|
460
757
|
UPDATE "users" SET foo = bar
|
461
758
|
WHERE "users"."id" IN (SELECT "users"."id" FROM "users" LIMIT 1)
|
462
759
|
}
|
@@ -468,9 +765,10 @@ module Arel
|
|
468
765
|
manager = Arel::SelectManager.new engine
|
469
766
|
manager.from table
|
470
767
|
manager.order :foo
|
471
|
-
manager.
|
768
|
+
stmt = manager.compile_update(SqlLiteral.new('foo = bar'))
|
769
|
+
stmt.key = table['id']
|
472
770
|
|
473
|
-
|
771
|
+
stmt.to_sql.must_be_like %{
|
474
772
|
UPDATE "users" SET foo = bar
|
475
773
|
WHERE "users"."id" IN (SELECT "users"."id" FROM "users" ORDER BY foo)
|
476
774
|
}
|
@@ -481,9 +779,9 @@ module Arel
|
|
481
779
|
table = Table.new :users
|
482
780
|
manager = Arel::SelectManager.new engine
|
483
781
|
manager.from table
|
484
|
-
manager.
|
782
|
+
stmt = manager.compile_update(SqlLiteral.new('foo = bar'))
|
485
783
|
|
486
|
-
|
784
|
+
stmt.to_sql.must_be_like %{ UPDATE "users" SET foo = bar }
|
487
785
|
end
|
488
786
|
|
489
787
|
it 'copies where clauses' do
|
@@ -492,21 +790,35 @@ module Arel
|
|
492
790
|
manager = Arel::SelectManager.new engine
|
493
791
|
manager.where table[:id].eq 10
|
494
792
|
manager.from table
|
495
|
-
manager.
|
793
|
+
stmt = manager.compile_update(table[:id] => 1)
|
496
794
|
|
497
|
-
|
795
|
+
stmt.to_sql.must_be_like %{
|
498
796
|
UPDATE "users" SET "id" = 1 WHERE "users"."id" = 10
|
499
797
|
}
|
500
798
|
end
|
501
799
|
|
800
|
+
it 'copies where clauses when nesting is triggered' do
|
801
|
+
engine = EngineProxy.new Table.engine
|
802
|
+
table = Table.new :users
|
803
|
+
manager = Arel::SelectManager.new engine
|
804
|
+
manager.where table[:foo].eq 10
|
805
|
+
manager.take 42
|
806
|
+
manager.from table
|
807
|
+
stmt = manager.compile_update(table[:id] => 1)
|
808
|
+
|
809
|
+
stmt.to_sql.must_be_like %{
|
810
|
+
UPDATE "users" SET "id" = 1 WHERE "users"."id" IN (SELECT "users"."id" FROM "users" WHERE "users"."foo" = 10 LIMIT 42)
|
811
|
+
}
|
812
|
+
end
|
813
|
+
|
502
814
|
it 'executes an update statement' do
|
503
815
|
engine = EngineProxy.new Table.engine
|
504
816
|
table = Table.new :users
|
505
817
|
manager = Arel::SelectManager.new engine
|
506
818
|
manager.from table
|
507
|
-
manager.
|
819
|
+
stmt = manager.compile_update(table[:id] => 1)
|
508
820
|
|
509
|
-
|
821
|
+
stmt.to_sql.must_be_like %{
|
510
822
|
UPDATE "users" SET "id" = 1
|
511
823
|
}
|
512
824
|
end
|
@@ -555,6 +867,15 @@ module Arel
|
|
555
867
|
manager = Arel::SelectManager.new Table.engine
|
556
868
|
manager.take(1).must_equal manager
|
557
869
|
end
|
870
|
+
|
871
|
+
it 'removes LIMIT when nil is passed' do
|
872
|
+
manager = Arel::SelectManager.new Table.engine
|
873
|
+
manager.limit = 10
|
874
|
+
assert_match('LIMIT', manager.to_sql)
|
875
|
+
|
876
|
+
manager.limit = nil
|
877
|
+
refute_match('LIMIT', manager.to_sql)
|
878
|
+
end
|
558
879
|
end
|
559
880
|
|
560
881
|
describe 'where' do
|
@@ -614,46 +935,4 @@ module Arel
|
|
614
935
|
end
|
615
936
|
end
|
616
937
|
end
|
617
|
-
|
618
|
-
describe 'set operations' do
|
619
|
-
before do
|
620
|
-
@table = Table.new :users
|
621
|
-
@m1 = Arel::SelectManager.new Table.engine, @table
|
622
|
-
@m1.project Arel.sql('*')
|
623
|
-
@m2 = Arel::SelectManager.new Table.engine, @table
|
624
|
-
@m2.project Arel.sql('*')
|
625
|
-
end
|
626
|
-
|
627
|
-
it 'supports union' do
|
628
|
-
@m1.where(@table[:id].lt(18))
|
629
|
-
@m2.where(@table[:id].gt(99))
|
630
|
-
(@m1.union @m2).to_sql.must_be_like %{
|
631
|
-
( SELECT * FROM "users" WHERE "users"."id" < 18 UNION SELECT * FROM "users" WHERE "users"."id" > 99 )
|
632
|
-
}
|
633
|
-
end
|
634
|
-
|
635
|
-
it 'supports union all' do
|
636
|
-
@m1.where(@table[:id].lt(18))
|
637
|
-
@m2.where(@table[:id].gt(99))
|
638
|
-
(@m1.union :all, @m2).to_sql.must_be_like %{
|
639
|
-
( SELECT * FROM "users" WHERE "users"."id" < 18 UNION ALL SELECT * FROM "users" WHERE "users"."id" > 99 )
|
640
|
-
}
|
641
|
-
end
|
642
|
-
|
643
|
-
it 'supports intersect' do
|
644
|
-
@m1.where(@table[:id].gt(18))
|
645
|
-
@m2.where(@table[:id].lt(99))
|
646
|
-
(@m1.intersect @m2).to_sql.must_be_like %{
|
647
|
-
( SELECT * FROM "users" WHERE "users"."id" > 18 INTERSECT SELECT * FROM "users" WHERE "users"."id" < 99 )
|
648
|
-
}
|
649
|
-
end
|
650
|
-
|
651
|
-
it 'supports except' do
|
652
|
-
@m1.where(@table[:id].in(18..60))
|
653
|
-
@m2.where(@table[:id].in(40..99))
|
654
|
-
(@m1.except @m2).to_sql.must_be_like %{
|
655
|
-
( SELECT * FROM "users" WHERE "users"."id" BETWEEN 18 AND 60 EXCEPT SELECT * FROM "users" WHERE "users"."id" BETWEEN 40 AND 99 )
|
656
|
-
}
|
657
|
-
end
|
658
|
-
end
|
659
938
|
end
|