square-arel 2.0.9.20110222133018
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/.autotest +26 -0
- data/History.txt +105 -0
- data/MIT-LICENSE.txt +20 -0
- data/Manifest.txt +124 -0
- data/README.markdown +94 -0
- data/Rakefile +20 -0
- data/lib/arel.rb +39 -0
- data/lib/arel/attributes.rb +20 -0
- data/lib/arel/attributes/attribute.rb +18 -0
- data/lib/arel/compatibility/wheres.rb +33 -0
- data/lib/arel/crud.rb +37 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/deprecated.rb +4 -0
- data/lib/arel/expression.rb +4 -0
- data/lib/arel/expressions.rb +23 -0
- data/lib/arel/insert_manager.rb +34 -0
- data/lib/arel/nodes.rb +53 -0
- data/lib/arel/nodes/and.rb +6 -0
- data/lib/arel/nodes/as.rb +6 -0
- data/lib/arel/nodes/assignment.rb +6 -0
- data/lib/arel/nodes/avg.rb +6 -0
- data/lib/arel/nodes/between.rb +6 -0
- data/lib/arel/nodes/binary.rb +12 -0
- data/lib/arel/nodes/count.rb +13 -0
- data/lib/arel/nodes/delete_statement.rb +19 -0
- data/lib/arel/nodes/does_not_match.rb +6 -0
- data/lib/arel/nodes/equality.rb +9 -0
- data/lib/arel/nodes/except.rb +7 -0
- data/lib/arel/nodes/exists.rb +7 -0
- data/lib/arel/nodes/function.rb +18 -0
- data/lib/arel/nodes/greater_than.rb +6 -0
- data/lib/arel/nodes/greater_than_or_equal.rb +6 -0
- data/lib/arel/nodes/group.rb +6 -0
- data/lib/arel/nodes/grouping.rb +6 -0
- data/lib/arel/nodes/having.rb +6 -0
- data/lib/arel/nodes/in.rb +6 -0
- data/lib/arel/nodes/inner_join.rb +6 -0
- data/lib/arel/nodes/insert_statement.rb +19 -0
- data/lib/arel/nodes/intersect.rb +7 -0
- data/lib/arel/nodes/join.rb +13 -0
- data/lib/arel/nodes/less_than.rb +6 -0
- data/lib/arel/nodes/less_than_or_equal.rb +6 -0
- data/lib/arel/nodes/limit.rb +7 -0
- data/lib/arel/nodes/lock.rb +6 -0
- data/lib/arel/nodes/matches.rb +6 -0
- data/lib/arel/nodes/max.rb +6 -0
- data/lib/arel/nodes/min.rb +6 -0
- data/lib/arel/nodes/node.rb +44 -0
- data/lib/arel/nodes/not.rb +6 -0
- data/lib/arel/nodes/not_equal.rb +6 -0
- data/lib/arel/nodes/not_in.rb +6 -0
- data/lib/arel/nodes/offset.rb +7 -0
- data/lib/arel/nodes/on.rb +6 -0
- data/lib/arel/nodes/or.rb +6 -0
- data/lib/arel/nodes/ordering.rb +20 -0
- data/lib/arel/nodes/outer_join.rb +6 -0
- data/lib/arel/nodes/select_core.rb +26 -0
- data/lib/arel/nodes/select_statement.rb +22 -0
- data/lib/arel/nodes/sql_literal.rb +8 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/sum.rb +6 -0
- data/lib/arel/nodes/table_alias.rb +13 -0
- data/lib/arel/nodes/top.rb +6 -0
- data/lib/arel/nodes/unary.rb +11 -0
- data/lib/arel/nodes/union.rb +7 -0
- data/lib/arel/nodes/union_all.rb +7 -0
- data/lib/arel/nodes/unqualified_column.rb +16 -0
- data/lib/arel/nodes/update_statement.rb +21 -0
- data/lib/arel/nodes/values.rb +14 -0
- data/lib/arel/predications.rb +183 -0
- data/lib/arel/relation.rb +6 -0
- data/lib/arel/select_manager.rb +237 -0
- data/lib/arel/sql/engine.rb +10 -0
- data/lib/arel/sql_literal.rb +4 -0
- data/lib/arel/table.rb +134 -0
- data/lib/arel/tree_manager.rb +36 -0
- data/lib/arel/update_manager.rb +49 -0
- data/lib/arel/visitors.rb +38 -0
- data/lib/arel/visitors/depth_first.rb +154 -0
- data/lib/arel/visitors/dot.rb +230 -0
- data/lib/arel/visitors/join_sql.rb +40 -0
- data/lib/arel/visitors/mssql.rb +16 -0
- data/lib/arel/visitors/mysql.rb +34 -0
- data/lib/arel/visitors/oracle.rb +116 -0
- data/lib/arel/visitors/order_clauses.rb +11 -0
- data/lib/arel/visitors/postgresql.rb +58 -0
- data/lib/arel/visitors/sqlite.rb +11 -0
- data/lib/arel/visitors/to_sql.rb +331 -0
- data/lib/arel/visitors/visitor.rb +27 -0
- data/lib/arel/visitors/where_sql.rb +9 -0
- data/square-arel.gemspec +36 -0
- data/test/attributes/test_attribute.rb +664 -0
- data/test/helper.rb +13 -0
- data/test/nodes/test_as.rb +16 -0
- data/test/nodes/test_count.rb +18 -0
- data/test/nodes/test_delete_statement.rb +14 -0
- data/test/nodes/test_equality.rb +74 -0
- data/test/nodes/test_insert_statement.rb +18 -0
- data/test/nodes/test_node.rb +33 -0
- data/test/nodes/test_not.rb +20 -0
- data/test/nodes/test_or.rb +22 -0
- data/test/nodes/test_select_core.rb +22 -0
- data/test/nodes/test_select_statement.rb +13 -0
- data/test/nodes/test_sql_literal.rb +52 -0
- data/test/nodes/test_sum.rb +12 -0
- data/test/nodes/test_update_statement.rb +18 -0
- data/test/support/fake_record.rb +91 -0
- data/test/test_activerecord_compat.rb +18 -0
- data/test/test_attributes.rb +46 -0
- data/test/test_crud.rb +69 -0
- data/test/test_delete_manager.rb +42 -0
- data/test/test_insert_manager.rb +125 -0
- data/test/test_select_manager.rb +659 -0
- data/test/test_table.rb +193 -0
- data/test/test_update_manager.rb +86 -0
- data/test/visitors/test_depth_first.rb +212 -0
- data/test/visitors/test_dot.rb +29 -0
- data/test/visitors/test_join_sql.rb +35 -0
- data/test/visitors/test_mssql.rb +18 -0
- data/test/visitors/test_mysql.rb +45 -0
- data/test/visitors/test_oracle.rb +147 -0
- data/test/visitors/test_postgres.rb +36 -0
- data/test/visitors/test_sqlite.rb +18 -0
- data/test/visitors/test_to_sql.rb +255 -0
- metadata +261 -0
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
module Arel
|
4
|
+
module Visitors
|
5
|
+
class TestDot < MiniTest::Unit::TestCase
|
6
|
+
def setup
|
7
|
+
@visitor = Visitors::Dot.new
|
8
|
+
end
|
9
|
+
|
10
|
+
# unary ops
|
11
|
+
[
|
12
|
+
Arel::Nodes::Not,
|
13
|
+
Arel::Nodes::Group,
|
14
|
+
Arel::Nodes::On,
|
15
|
+
Arel::Nodes::Grouping,
|
16
|
+
Arel::Nodes::Offset,
|
17
|
+
Arel::Nodes::Having,
|
18
|
+
Arel::Nodes::UnqualifiedColumn,
|
19
|
+
Arel::Nodes::Top,
|
20
|
+
Arel::Nodes::Limit,
|
21
|
+
].each do |klass|
|
22
|
+
define_method("test_#{klass.name.gsub('::', '_')}") do
|
23
|
+
op = klass.new(:a)
|
24
|
+
@visitor.accept op
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
module Arel
|
4
|
+
module Visitors
|
5
|
+
describe 'the join_sql visitor' do
|
6
|
+
before do
|
7
|
+
@visitor = JoinSql.new Table.engine
|
8
|
+
end
|
9
|
+
|
10
|
+
describe 'inner join' do
|
11
|
+
it 'should visit left if left is a join' do
|
12
|
+
t = Table.new :users
|
13
|
+
join = Nodes::InnerJoin.new t, t, Nodes::On.new(t[:id])
|
14
|
+
j2 = Nodes::InnerJoin.new join, t, Nodes::On.new(t[:id])
|
15
|
+
@visitor.accept(j2).must_be_like %{
|
16
|
+
INNER JOIN "users" ON "users"."id"
|
17
|
+
INNER JOIN "users" ON "users"."id"
|
18
|
+
}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe 'outer join' do
|
23
|
+
it 'should visit left if left is a join' do
|
24
|
+
t = Table.new :users
|
25
|
+
join = Nodes::OuterJoin.new t, t, Nodes::On.new(t[:id])
|
26
|
+
j2 = Nodes::OuterJoin.new join, t, Nodes::On.new(t[:id])
|
27
|
+
@visitor.accept(j2).must_be_like %{
|
28
|
+
LEFT OUTER JOIN "users" ON "users"."id"
|
29
|
+
LEFT OUTER JOIN "users" ON "users"."id"
|
30
|
+
}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
module Arel
|
4
|
+
module Visitors
|
5
|
+
describe 'the mssql visitor' do
|
6
|
+
before do
|
7
|
+
@visitor = MSSQL.new Table.engine
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'uses TOP to limit results' do
|
11
|
+
stmt = Nodes::SelectStatement.new
|
12
|
+
stmt.cores.last.top = Nodes::Top.new(1)
|
13
|
+
sql = @visitor.accept(stmt)
|
14
|
+
sql.must_be_like "SELECT TOP 1"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
module Arel
|
4
|
+
module Visitors
|
5
|
+
describe 'the mysql visitor' do
|
6
|
+
before do
|
7
|
+
@visitor = MySQL.new Table.engine
|
8
|
+
end
|
9
|
+
|
10
|
+
###
|
11
|
+
# :'(
|
12
|
+
# http://dev.mysql.com/doc/refman/5.0/en/select.html#id3482214
|
13
|
+
it 'defaults limit to 18446744073709551615' do
|
14
|
+
stmt = Nodes::SelectStatement.new
|
15
|
+
stmt.offset = Nodes::Offset.new(1)
|
16
|
+
sql = @visitor.accept(stmt)
|
17
|
+
sql.must_be_like "SELECT FROM DUAL LIMIT 18446744073709551615 OFFSET 1"
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should escape LIMIT" do
|
21
|
+
sc = Arel::Nodes::UpdateStatement.new
|
22
|
+
sc.limit = Nodes::Limit.new("omg")
|
23
|
+
assert_equal("UPDATE NULL LIMIT 'omg'", @visitor.accept(sc))
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'uses DUAL for empty from' do
|
27
|
+
stmt = Nodes::SelectStatement.new
|
28
|
+
sql = @visitor.accept(stmt)
|
29
|
+
sql.must_be_like "SELECT FROM DUAL"
|
30
|
+
end
|
31
|
+
|
32
|
+
describe 'locking' do
|
33
|
+
it 'defaults to FOR UPDATE when locking' do
|
34
|
+
node = Nodes::Lock.new(Arel.sql('FOR UPDATE'))
|
35
|
+
@visitor.accept(node).must_be_like "FOR UPDATE"
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'allows a custom string to be used as a lock' do
|
39
|
+
node = Nodes::Lock.new(Arel.sql('LOCK IN SHARE MODE'))
|
40
|
+
@visitor.accept(node).must_be_like "LOCK IN SHARE MODE"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
module Arel
|
4
|
+
module Visitors
|
5
|
+
describe 'the oracle visitor' do
|
6
|
+
before do
|
7
|
+
@visitor = Oracle.new Table.engine
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'modifies order when there is distinct and first value' do
|
11
|
+
# *sigh*
|
12
|
+
select = "DISTINCT foo.id, FIRST_VALUE(projects.name) OVER (foo) AS alias_0__"
|
13
|
+
stmt = Nodes::SelectStatement.new
|
14
|
+
stmt.cores.first.projections << Nodes::SqlLiteral.new(select)
|
15
|
+
stmt.orders << Nodes::SqlLiteral.new('foo')
|
16
|
+
sql = @visitor.accept(stmt)
|
17
|
+
sql.must_be_like %{
|
18
|
+
SELECT #{select} ORDER BY alias_0__
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'is idempotent with crazy query' do
|
23
|
+
# *sigh*
|
24
|
+
select = "DISTINCT foo.id, FIRST_VALUE(projects.name) OVER (foo) AS alias_0__"
|
25
|
+
stmt = Nodes::SelectStatement.new
|
26
|
+
stmt.cores.first.projections << Nodes::SqlLiteral.new(select)
|
27
|
+
stmt.orders << Nodes::SqlLiteral.new('foo')
|
28
|
+
|
29
|
+
sql = @visitor.accept(stmt)
|
30
|
+
sql2 = @visitor.accept(stmt)
|
31
|
+
sql.must_equal sql2
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'splits orders with commas' do
|
35
|
+
# *sigh*
|
36
|
+
select = "DISTINCT foo.id, FIRST_VALUE(projects.name) OVER (foo) AS alias_0__"
|
37
|
+
stmt = Nodes::SelectStatement.new
|
38
|
+
stmt.cores.first.projections << Nodes::SqlLiteral.new(select)
|
39
|
+
stmt.orders << Nodes::SqlLiteral.new('foo, bar')
|
40
|
+
sql = @visitor.accept(stmt)
|
41
|
+
sql.must_be_like %{
|
42
|
+
SELECT #{select} ORDER BY alias_0__, alias_1__
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'splits orders with commas and function calls' do
|
47
|
+
# *sigh*
|
48
|
+
select = "DISTINCT foo.id, FIRST_VALUE(projects.name) OVER (foo) AS alias_0__"
|
49
|
+
stmt = Nodes::SelectStatement.new
|
50
|
+
stmt.cores.first.projections << Nodes::SqlLiteral.new(select)
|
51
|
+
stmt.orders << Nodes::SqlLiteral.new('NVL(LOWER(bar, foo), foo) DESC, UPPER(baz)')
|
52
|
+
sql = @visitor.accept(stmt)
|
53
|
+
sql.must_be_like %{
|
54
|
+
SELECT #{select} ORDER BY alias_0__ DESC, alias_1__
|
55
|
+
}
|
56
|
+
end
|
57
|
+
|
58
|
+
describe 'Nodes::SelectStatement' do
|
59
|
+
describe 'limit' do
|
60
|
+
it 'adds a rownum clause' do
|
61
|
+
stmt = Nodes::SelectStatement.new
|
62
|
+
stmt.limit = Nodes::Limit.new(10)
|
63
|
+
sql = @visitor.accept stmt
|
64
|
+
sql.must_be_like %{ SELECT WHERE ROWNUM <= 10 }
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'is idempotent' do
|
68
|
+
stmt = Nodes::SelectStatement.new
|
69
|
+
stmt.orders << Nodes::SqlLiteral.new('foo')
|
70
|
+
stmt.limit = Nodes::Limit.new(10)
|
71
|
+
sql = @visitor.accept stmt
|
72
|
+
sql2 = @visitor.accept stmt
|
73
|
+
sql.must_equal sql2
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'creates a subquery when there is order_by' do
|
77
|
+
stmt = Nodes::SelectStatement.new
|
78
|
+
stmt.orders << Nodes::SqlLiteral.new('foo')
|
79
|
+
stmt.limit = Nodes::Limit.new(10)
|
80
|
+
sql = @visitor.accept stmt
|
81
|
+
sql.must_be_like %{
|
82
|
+
SELECT * FROM (SELECT ORDER BY foo) WHERE ROWNUM <= 10
|
83
|
+
}
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'creates a subquery when there is DISTINCT' do
|
87
|
+
stmt = Nodes::SelectStatement.new
|
88
|
+
stmt.cores.first.projections << Nodes::SqlLiteral.new('DISTINCT id')
|
89
|
+
stmt.limit = Arel::Nodes::Limit.new(10)
|
90
|
+
sql = @visitor.accept stmt
|
91
|
+
sql.must_be_like %{
|
92
|
+
SELECT * FROM (SELECT DISTINCT id) WHERE ROWNUM <= 10
|
93
|
+
}
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'creates a different subquery when there is an offset' do
|
97
|
+
stmt = Nodes::SelectStatement.new
|
98
|
+
stmt.limit = Nodes::Limit.new(10)
|
99
|
+
stmt.offset = Nodes::Offset.new(10)
|
100
|
+
sql = @visitor.accept stmt
|
101
|
+
sql.must_be_like %{
|
102
|
+
SELECT * FROM (
|
103
|
+
SELECT raw_sql_.*, rownum raw_rnum_
|
104
|
+
FROM (SELECT ) raw_sql_
|
105
|
+
WHERE rownum <= 20
|
106
|
+
)
|
107
|
+
WHERE raw_rnum_ > 10
|
108
|
+
}
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'is idempotent with different subquery' do
|
112
|
+
stmt = Nodes::SelectStatement.new
|
113
|
+
stmt.limit = Nodes::Limit.new(10)
|
114
|
+
stmt.offset = Nodes::Offset.new(10)
|
115
|
+
sql = @visitor.accept stmt
|
116
|
+
sql2 = @visitor.accept stmt
|
117
|
+
sql.must_equal sql2
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe 'only offset' do
|
122
|
+
it 'creates a select from subquery with rownum condition' do
|
123
|
+
stmt = Nodes::SelectStatement.new
|
124
|
+
stmt.offset = Nodes::Offset.new(10)
|
125
|
+
sql = @visitor.accept stmt
|
126
|
+
sql.must_be_like %{
|
127
|
+
SELECT * FROM (
|
128
|
+
SELECT raw_sql_.*, rownum raw_rnum_
|
129
|
+
FROM (SELECT ) raw_sql_
|
130
|
+
)
|
131
|
+
WHERE raw_rnum_ > 10
|
132
|
+
}
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'modified except to be minus' do
|
137
|
+
left = Nodes::SqlLiteral.new("SELECT * FROM users WHERE age > 10")
|
138
|
+
right = Nodes::SqlLiteral.new("SELECT * FROM users WHERE age > 20")
|
139
|
+
sql = @visitor.accept Nodes::Except.new(left, right)
|
140
|
+
sql.must_be_like %{
|
141
|
+
( SELECT * FROM users WHERE age > 10 MINUS SELECT * FROM users WHERE age > 20 )
|
142
|
+
}
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
module Arel
|
4
|
+
module Visitors
|
5
|
+
describe 'the postgres visitor' do
|
6
|
+
before do
|
7
|
+
@visitor = PostgreSQL.new Table.engine
|
8
|
+
end
|
9
|
+
|
10
|
+
describe 'locking' do
|
11
|
+
it 'defaults to FOR UPDATE' do
|
12
|
+
@visitor.accept(Nodes::Lock.new(Arel.sql('FOR UPDATE'))).must_be_like %{
|
13
|
+
FOR UPDATE
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'allows a custom string to be used as a lock' do
|
18
|
+
node = Nodes::Lock.new(Arel.sql('FOR SHARE'))
|
19
|
+
@visitor.accept(node).must_be_like %{
|
20
|
+
FOR SHARE
|
21
|
+
}
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should escape LIMIT" do
|
26
|
+
sc = Arel::Nodes::SelectStatement.new
|
27
|
+
sc.limit = Nodes::Limit.new("omg")
|
28
|
+
sc.cores.first.projections << 'DISTINCT ON'
|
29
|
+
sc.orders << "xyz"
|
30
|
+
sql = @visitor.accept(sc)
|
31
|
+
assert_match(/LIMIT 'omg'/, sql)
|
32
|
+
assert_equal 1, sql.scan(/LIMIT/).length, 'should have one limit'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
module Arel
|
4
|
+
module Visitors
|
5
|
+
describe 'the sqlite visitor' do
|
6
|
+
before do
|
7
|
+
@visitor = SQLite.new Table.engine
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'defaults limit to -1' do
|
11
|
+
stmt = Nodes::SelectStatement.new
|
12
|
+
stmt.offset = Nodes::Offset.new(1)
|
13
|
+
sql = @visitor.accept(stmt)
|
14
|
+
sql.must_be_like "SELECT LIMIT -1 OFFSET 1"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,255 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
class Arel::Visitors::ToSql
|
4
|
+
def last_column; Thread.current[:arel_visitors_to_sql_last_column] || @last_column; end
|
5
|
+
end
|
6
|
+
|
7
|
+
module Arel
|
8
|
+
module Visitors
|
9
|
+
describe 'the to_sql visitor' do
|
10
|
+
before do
|
11
|
+
@visitor = ToSql.new Table.engine
|
12
|
+
@table = Table.new(:users)
|
13
|
+
@attr = @table[:id]
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should be thread safe around usage of last_column" do
|
17
|
+
visit_integer_column = Thread.new do
|
18
|
+
Thread.stop
|
19
|
+
@visitor.send(:visit_Arel_Attributes_Attribute, @attr)
|
20
|
+
end
|
21
|
+
|
22
|
+
@visitor.accept(@table[:name])
|
23
|
+
assert_equal(:string, @visitor.last_column.type)
|
24
|
+
visit_integer_column.run
|
25
|
+
visit_integer_column.join
|
26
|
+
assert_equal(:string, @visitor.last_column.type)
|
27
|
+
end
|
28
|
+
|
29
|
+
describe 'equality' do
|
30
|
+
it 'should handle false' do
|
31
|
+
sql = @visitor.accept Nodes::Equality.new(false, false)
|
32
|
+
sql.must_be_like %{ 'f' = 'f' }
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should use the column to quote' do
|
36
|
+
table = Table.new(:users)
|
37
|
+
sql = @visitor.accept Nodes::Equality.new(table[:id], '1-fooo')
|
38
|
+
sql.must_be_like %{ "users"."id" = 1 }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should visit string subclass" do
|
43
|
+
@visitor.accept(Class.new(String).new(":'("))
|
44
|
+
@visitor.accept(Class.new(Class.new(String)).new(":'("))
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should visit_Class" do
|
48
|
+
@visitor.accept(DateTime).must_equal "'DateTime'"
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should escape LIMIT" do
|
52
|
+
sc = Arel::Nodes::SelectStatement.new
|
53
|
+
sc.limit = Arel::Nodes::Limit.new("omg")
|
54
|
+
assert_match(/LIMIT 'omg'/, @visitor.accept(sc))
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should visit_DateTime" do
|
58
|
+
@visitor.accept DateTime.now
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should visit_Float" do
|
62
|
+
@visitor.accept 2.14
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should visit_Not" do
|
66
|
+
sql = @visitor.accept Nodes::Not.new(Arel.sql("foo"))
|
67
|
+
sql.must_be_like "NOT (foo)"
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should apply Not to the whole expression" do
|
71
|
+
node = Nodes::And.new @attr.eq(10), @attr.eq(11)
|
72
|
+
sql = @visitor.accept Nodes::Not.new(node)
|
73
|
+
sql.must_be_like %{NOT ("users"."id" = 10 AND "users"."id" = 11)}
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should visit_As" do
|
77
|
+
as = Nodes::As.new(Arel.sql("foo"), Arel.sql("bar"))
|
78
|
+
sql = @visitor.accept as
|
79
|
+
sql.must_be_like "foo AS bar"
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should visit_Bignum" do
|
83
|
+
@visitor.accept 8787878092
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should visit_Hash" do
|
87
|
+
@visitor.accept({:a => 1})
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should visit_BigDecimal" do
|
91
|
+
@visitor.accept BigDecimal.new('2.14')
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should visit_Date" do
|
95
|
+
@visitor.accept Date.today
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should visit_NilClass" do
|
99
|
+
@visitor.accept(nil).must_be_like "NULL"
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should visit_Arel_Nodes_And" do
|
103
|
+
node = Nodes::And.new @attr.eq(10), @attr.eq(11)
|
104
|
+
@visitor.accept(node).must_be_like %{
|
105
|
+
"users"."id" = 10 AND "users"."id" = 11
|
106
|
+
}
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should visit_Arel_Nodes_Or" do
|
110
|
+
node = Nodes::Or.new @attr.eq(10), @attr.eq(11)
|
111
|
+
@visitor.accept(node).must_be_like %{
|
112
|
+
"users"."id" = 10 OR "users"."id" = 11
|
113
|
+
}
|
114
|
+
end
|
115
|
+
|
116
|
+
it "should visit visit_Arel_Attributes_Time" do
|
117
|
+
attr = Attributes::Time.new(@attr.relation, @attr.name, @attr.column)
|
118
|
+
@visitor.accept attr
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should visit_TrueClass" do
|
122
|
+
test = Table.new(:users)[:bool].eq(true)
|
123
|
+
@visitor.accept(test).must_be_like %{ "users"."bool" = 't' }
|
124
|
+
end
|
125
|
+
|
126
|
+
describe "Nodes::Ordering" do
|
127
|
+
it "should know how to visit" do
|
128
|
+
node = @attr.desc
|
129
|
+
@visitor.accept(node).must_be_like %{
|
130
|
+
"users"."id" DESC
|
131
|
+
}
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe "Nodes::In" do
|
136
|
+
it "should know how to visit" do
|
137
|
+
node = @attr.in [1, 2, 3]
|
138
|
+
@visitor.accept(node).must_be_like %{
|
139
|
+
"users"."id" IN (1, 2, 3)
|
140
|
+
}
|
141
|
+
end
|
142
|
+
|
143
|
+
it "should turn empty right to NULL" do
|
144
|
+
node = @attr.in []
|
145
|
+
@visitor.accept(node).must_be_like %{
|
146
|
+
"users"."id" IN (NULL)
|
147
|
+
}
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'can handle two dot ranges' do
|
151
|
+
node = @attr.in 1..3
|
152
|
+
@visitor.accept(node).must_be_like %{
|
153
|
+
"users"."id" BETWEEN 1 AND 3
|
154
|
+
}
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'can handle three dot ranges' do
|
158
|
+
node = @attr.in 1...3
|
159
|
+
@visitor.accept(node).must_be_like %{
|
160
|
+
"users"."id" >= 1 AND "users"."id" < 3
|
161
|
+
}
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'can handle subqueries' do
|
165
|
+
table = Table.new(:users)
|
166
|
+
subquery = table.project(:id).where(table[:name].eq('Aaron'))
|
167
|
+
node = @attr.in subquery
|
168
|
+
@visitor.accept(node).must_be_like %{
|
169
|
+
"users"."id" IN (SELECT id FROM "users" WHERE "users"."name" = 'Aaron')
|
170
|
+
}
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'uses the same column for escaping values' do
|
174
|
+
@attr = Table.new(:users)[:name]
|
175
|
+
visitor = Class.new(ToSql) do
|
176
|
+
attr_accessor :expected
|
177
|
+
|
178
|
+
def quote value, column = nil
|
179
|
+
raise unless column == expected
|
180
|
+
super
|
181
|
+
end
|
182
|
+
end
|
183
|
+
in_node = Nodes::In.new @attr, %w{ a b c }
|
184
|
+
visitor = visitor.new(Table.engine)
|
185
|
+
visitor.expected = @attr.column
|
186
|
+
visitor.accept(in_node).must_equal %("users"."name" IN ('a', 'b', 'c'))
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
describe "Nodes::NotIn" do
|
191
|
+
it "should know how to visit" do
|
192
|
+
node = @attr.not_in [1, 2, 3]
|
193
|
+
@visitor.accept(node).must_be_like %{
|
194
|
+
"users"."id" NOT IN (1, 2, 3)
|
195
|
+
}
|
196
|
+
end
|
197
|
+
|
198
|
+
it "should turn empty right to NULL" do
|
199
|
+
node = @attr.not_in []
|
200
|
+
@visitor.accept(node).must_be_like %{
|
201
|
+
"users"."id" NOT IN (NULL)
|
202
|
+
}
|
203
|
+
end
|
204
|
+
|
205
|
+
it 'can handle two dot ranges' do
|
206
|
+
node = @attr.not_in 1..3
|
207
|
+
@visitor.accept(node).must_be_like %{
|
208
|
+
"users"."id" < 1 OR "users"."id" > 3
|
209
|
+
}
|
210
|
+
end
|
211
|
+
|
212
|
+
it 'can handle three dot ranges' do
|
213
|
+
node = @attr.not_in 1...3
|
214
|
+
@visitor.accept(node).must_be_like %{
|
215
|
+
"users"."id" < 1 OR "users"."id" >= 3
|
216
|
+
}
|
217
|
+
end
|
218
|
+
|
219
|
+
it 'can handle subqueries' do
|
220
|
+
table = Table.new(:users)
|
221
|
+
subquery = table.project(:id).where(table[:name].eq('Aaron'))
|
222
|
+
node = @attr.not_in subquery
|
223
|
+
@visitor.accept(node).must_be_like %{
|
224
|
+
"users"."id" NOT IN (SELECT id FROM "users" WHERE "users"."name" = 'Aaron')
|
225
|
+
}
|
226
|
+
end
|
227
|
+
|
228
|
+
it 'uses the same column for escaping values' do
|
229
|
+
@attr = Table.new(:users)[:name]
|
230
|
+
visitor = Class.new(ToSql) do
|
231
|
+
attr_accessor :expected
|
232
|
+
|
233
|
+
def quote value, column = nil
|
234
|
+
raise unless column == expected
|
235
|
+
super
|
236
|
+
end
|
237
|
+
end
|
238
|
+
in_node = Nodes::NotIn.new @attr, %w{ a b c }
|
239
|
+
visitor = visitor.new(Table.engine)
|
240
|
+
visitor.expected = @attr.column
|
241
|
+
visitor.accept(in_node).must_equal %("users"."name" NOT IN ('a', 'b', 'c'))
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
describe 'Equality' do
|
246
|
+
it "should escape strings" do
|
247
|
+
test = Table.new(:users)[:name].eq 'Aaron Patterson'
|
248
|
+
@visitor.accept(test).must_be_like %{
|
249
|
+
"users"."name" = 'Aaron Patterson'
|
250
|
+
}
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|