arel 2.0.10 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|