arel 5.0.1.20140414130214 → 6.0.0.beta1
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.
- checksums.yaml +4 -4
- data/.travis.yml +4 -2
- data/History.txt +9 -4
- data/Manifest.txt +9 -7
- data/README.markdown +85 -8
- data/Rakefile +1 -1
- data/arel.gemspec +15 -16
- data/lib/arel.rb +1 -12
- data/lib/arel/collectors/bind.rb +36 -0
- data/lib/arel/collectors/plain_string.rb +18 -0
- data/lib/arel/collectors/sql_string.rb +18 -0
- data/lib/arel/factory_methods.rb +1 -1
- data/lib/arel/insert_manager.rb +5 -1
- data/lib/arel/nodes.rb +41 -0
- data/lib/arel/nodes/and.rb +1 -5
- data/lib/arel/nodes/binary.rb +2 -0
- data/lib/arel/nodes/extract.rb +0 -2
- data/lib/arel/nodes/full_outer_join.rb +6 -0
- data/lib/arel/nodes/function.rb +0 -1
- data/lib/arel/nodes/insert_statement.rb +5 -2
- data/lib/arel/nodes/node.rb +5 -1
- data/lib/arel/nodes/right_outer_join.rb +6 -0
- data/lib/arel/nodes/window.rb +23 -5
- data/lib/arel/predications.rb +41 -33
- data/lib/arel/select_manager.rb +13 -37
- data/lib/arel/table.rb +13 -9
- data/lib/arel/tree_manager.rb +8 -2
- data/lib/arel/update_manager.rb +2 -2
- data/lib/arel/visitors.rb +0 -2
- data/lib/arel/visitors/bind_substitute.rb +9 -0
- data/lib/arel/visitors/bind_visitor.rb +10 -5
- data/lib/arel/visitors/depth_first.rb +60 -57
- data/lib/arel/visitors/dot.rb +84 -80
- data/lib/arel/visitors/ibm_db.rb +4 -2
- data/lib/arel/visitors/informix.rb +39 -21
- data/lib/arel/visitors/mssql.rb +41 -23
- data/lib/arel/visitors/mysql.rb +48 -22
- data/lib/arel/visitors/oracle.rb +33 -24
- data/lib/arel/visitors/postgresql.rb +15 -8
- data/lib/arel/visitors/reduce.rb +25 -0
- data/lib/arel/visitors/sqlite.rb +3 -2
- data/lib/arel/visitors/to_sql.rb +455 -248
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel/visitors/where_sql.rb +3 -2
- data/test/attributes/test_attribute.rb +12 -3
- data/test/collectors/test_bind_collector.rb +70 -0
- data/test/collectors/test_sql_string.rb +38 -0
- data/test/helper.rb +10 -1
- data/test/nodes/test_bin.rb +2 -2
- data/test/nodes/test_count.rb +0 -6
- data/test/nodes/test_equality.rb +1 -1
- data/test/nodes/test_grouping.rb +1 -1
- data/test/nodes/test_infix_operation.rb +1 -1
- data/test/nodes/test_select_core.rb +7 -7
- data/test/nodes/test_sql_literal.rb +10 -6
- data/test/nodes/test_window.rb +9 -3
- data/test/support/fake_record.rb +16 -4
- data/test/test_factory_methods.rb +1 -1
- data/test/test_insert_manager.rb +33 -4
- data/test/test_select_manager.rb +164 -92
- data/test/test_table.rb +49 -4
- data/test/visitors/test_bind_visitor.rb +18 -10
- data/test/visitors/test_depth_first.rb +12 -0
- data/test/visitors/test_dot.rb +4 -4
- data/test/visitors/test_ibm_db.rb +11 -5
- data/test/visitors/test_informix.rb +14 -8
- data/test/visitors/test_mssql.rb +12 -8
- data/test/visitors/test_mysql.rb +17 -12
- data/test/visitors/test_oracle.rb +25 -21
- data/test/visitors/test_postgres.rb +50 -12
- data/test/visitors/test_sqlite.rb +2 -2
- data/test/visitors/test_to_sql.rb +177 -81
- metadata +24 -19
- data/lib/arel/deprecated.rb +0 -4
- data/lib/arel/expression.rb +0 -5
- data/lib/arel/sql/engine.rb +0 -10
- data/lib/arel/sql_literal.rb +0 -4
- data/lib/arel/visitors/join_sql.rb +0 -19
- data/lib/arel/visitors/order_clauses.rb +0 -11
- data/test/visitors/test_join_sql.rb +0 -42
@@ -9,16 +9,20 @@ module Arel
|
|
9
9
|
@attr = @table[:id]
|
10
10
|
end
|
11
11
|
|
12
|
+
def compile node
|
13
|
+
@visitor.accept(node, Collectors::SQLString.new).value
|
14
|
+
end
|
15
|
+
|
12
16
|
describe 'locking' do
|
13
17
|
it 'defaults to FOR UPDATE' do
|
14
|
-
|
18
|
+
compile(Nodes::Lock.new(Arel.sql('FOR UPDATE'))).must_be_like %{
|
15
19
|
FOR UPDATE
|
16
20
|
}
|
17
21
|
end
|
18
22
|
|
19
23
|
it 'allows a custom string to be used as a lock' do
|
20
24
|
node = Nodes::Lock.new(Arel.sql('FOR SHARE'))
|
21
|
-
|
25
|
+
compile(node).must_be_like %{
|
22
26
|
FOR SHARE
|
23
27
|
}
|
24
28
|
end
|
@@ -26,10 +30,10 @@ module Arel
|
|
26
30
|
|
27
31
|
it "should escape LIMIT" do
|
28
32
|
sc = Arel::Nodes::SelectStatement.new
|
29
|
-
sc.limit = Nodes::Limit.new("omg")
|
30
|
-
sc.cores.first.projections << 'DISTINCT ON'
|
31
|
-
sc.orders << "xyz"
|
32
|
-
sql =
|
33
|
+
sc.limit = Nodes::Limit.new(Nodes.build_quoted("omg"))
|
34
|
+
sc.cores.first.projections << Arel.sql('DISTINCT ON')
|
35
|
+
sc.orders << Arel.sql("xyz")
|
36
|
+
sql = compile(sc)
|
33
37
|
assert_match(/LIMIT 'omg'/, sql)
|
34
38
|
assert_equal 1, sql.scan(/LIMIT/).length, 'should have one limit'
|
35
39
|
end
|
@@ -37,19 +41,19 @@ module Arel
|
|
37
41
|
it 'should support DISTINCT ON' do
|
38
42
|
core = Arel::Nodes::SelectCore.new
|
39
43
|
core.set_quantifier = Arel::Nodes::DistinctOn.new(Arel.sql('aaron'))
|
40
|
-
assert_match 'DISTINCT ON ( aaron )',
|
44
|
+
assert_match 'DISTINCT ON ( aaron )', compile(core)
|
41
45
|
end
|
42
46
|
|
43
47
|
it 'should support DISTINCT' do
|
44
48
|
core = Arel::Nodes::SelectCore.new
|
45
49
|
core.set_quantifier = Arel::Nodes::Distinct.new
|
46
|
-
assert_equal 'SELECT DISTINCT',
|
50
|
+
assert_equal 'SELECT DISTINCT', compile(core)
|
47
51
|
end
|
48
52
|
|
49
53
|
describe "Nodes::Matches" do
|
50
54
|
it "should know how to visit" do
|
51
55
|
node = @table[:name].matches('foo%')
|
52
|
-
|
56
|
+
compile(node).must_be_like %{
|
53
57
|
"users"."name" ILIKE 'foo%'
|
54
58
|
}
|
55
59
|
end
|
@@ -57,7 +61,7 @@ module Arel
|
|
57
61
|
it 'can handle subqueries' do
|
58
62
|
subquery = @table.project(:id).where(@table[:name].matches('foo%'))
|
59
63
|
node = @attr.in subquery
|
60
|
-
|
64
|
+
compile(node).must_be_like %{
|
61
65
|
"users"."id" IN (SELECT id FROM "users" WHERE "users"."name" ILIKE 'foo%')
|
62
66
|
}
|
63
67
|
end
|
@@ -66,7 +70,7 @@ module Arel
|
|
66
70
|
describe "Nodes::DoesNotMatch" do
|
67
71
|
it "should know how to visit" do
|
68
72
|
node = @table[:name].does_not_match('foo%')
|
69
|
-
|
73
|
+
compile(node).must_be_like %{
|
70
74
|
"users"."name" NOT ILIKE 'foo%'
|
71
75
|
}
|
72
76
|
end
|
@@ -74,11 +78,45 @@ module Arel
|
|
74
78
|
it 'can handle subqueries' do
|
75
79
|
subquery = @table.project(:id).where(@table[:name].does_not_match('foo%'))
|
76
80
|
node = @attr.in subquery
|
77
|
-
|
81
|
+
compile(node).must_be_like %{
|
78
82
|
"users"."id" IN (SELECT id FROM "users" WHERE "users"."name" NOT ILIKE 'foo%')
|
79
83
|
}
|
80
84
|
end
|
81
85
|
end
|
86
|
+
|
87
|
+
describe "Nodes::Regexp" do
|
88
|
+
it "should know how to visit" do
|
89
|
+
node = Arel::Nodes::Regexp.new(@table[:name], Nodes.build_quoted('foo%'))
|
90
|
+
compile(node).must_be_like %{
|
91
|
+
"users"."name" ~ 'foo%'
|
92
|
+
}
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'can handle subqueries' do
|
96
|
+
subquery = @table.project(:id).where(Arel::Nodes::Regexp.new(@table[:name], Nodes.build_quoted('foo%')))
|
97
|
+
node = @attr.in subquery
|
98
|
+
compile(node).must_be_like %{
|
99
|
+
"users"."id" IN (SELECT id FROM "users" WHERE "users"."name" ~ 'foo%')
|
100
|
+
}
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe "Nodes::NotRegexp" do
|
105
|
+
it "should know how to visit" do
|
106
|
+
node = Arel::Nodes::NotRegexp.new(@table[:name], Nodes.build_quoted('foo%'))
|
107
|
+
compile(node).must_be_like %{
|
108
|
+
"users"."name" !~ 'foo%'
|
109
|
+
}
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'can handle subqueries' do
|
113
|
+
subquery = @table.project(:id).where(Arel::Nodes::NotRegexp.new(@table[:name], Nodes.build_quoted('foo%')))
|
114
|
+
node = @attr.in subquery
|
115
|
+
compile(node).must_be_like %{
|
116
|
+
"users"."id" IN (SELECT id FROM "users" WHERE "users"."name" !~ 'foo%')
|
117
|
+
}
|
118
|
+
end
|
119
|
+
end
|
82
120
|
end
|
83
121
|
end
|
84
122
|
end
|
@@ -10,13 +10,13 @@ module Arel
|
|
10
10
|
it 'defaults limit to -1' do
|
11
11
|
stmt = Nodes::SelectStatement.new
|
12
12
|
stmt.offset = Nodes::Offset.new(1)
|
13
|
-
sql = @visitor.accept(stmt)
|
13
|
+
sql = @visitor.accept(stmt, Collectors::SQLString.new).value
|
14
14
|
sql.must_be_like "SELECT LIMIT -1 OFFSET 1"
|
15
15
|
end
|
16
16
|
|
17
17
|
it 'does not support locking' do
|
18
18
|
node = Nodes::Lock.new(Arel.sql('FOR UPDATE'))
|
19
|
-
@visitor.accept(node).
|
19
|
+
assert_equal '', @visitor.accept(node, Collectors::SQLString.new).value
|
20
20
|
end
|
21
21
|
end
|
22
22
|
end
|
@@ -4,21 +4,26 @@ module Arel
|
|
4
4
|
module Visitors
|
5
5
|
describe 'the to_sql visitor' do
|
6
6
|
before do
|
7
|
-
@
|
7
|
+
@conn = FakeRecord::Base.new
|
8
|
+
@visitor = ToSql.new @conn.connection
|
8
9
|
@table = Table.new(:users)
|
9
10
|
@attr = @table[:id]
|
10
11
|
end
|
11
12
|
|
13
|
+
def compile node
|
14
|
+
@visitor.accept(node, Collectors::SQLString.new).value
|
15
|
+
end
|
16
|
+
|
12
17
|
it 'works with BindParams' do
|
13
18
|
node = Nodes::BindParam.new 'omg'
|
14
|
-
sql =
|
19
|
+
sql = compile node
|
15
20
|
sql.must_be_like 'omg'
|
16
21
|
end
|
17
22
|
|
18
23
|
it 'can define a dispatch method' do
|
19
24
|
visited = false
|
20
|
-
viz = Class.new(Arel::Visitors::
|
21
|
-
define_method(:hello) do |node,
|
25
|
+
viz = Class.new(Arel::Visitors::Reduce) {
|
26
|
+
define_method(:hello) do |node, c|
|
22
27
|
visited = true
|
23
28
|
end
|
24
29
|
|
@@ -27,201 +32,270 @@ module Arel
|
|
27
32
|
end
|
28
33
|
}.new
|
29
34
|
|
30
|
-
viz.accept(@table)
|
35
|
+
viz.accept(@table, Collectors::SQLString.new)
|
31
36
|
assert visited, 'hello method was called'
|
32
37
|
end
|
33
38
|
|
34
39
|
it 'should not quote sql literals' do
|
35
40
|
node = @table[Arel.star]
|
36
|
-
sql =
|
41
|
+
sql = compile node
|
37
42
|
sql.must_be_like '"users".*'
|
38
43
|
end
|
39
44
|
|
40
45
|
it 'should visit named functions' do
|
41
46
|
function = Nodes::NamedFunction.new('omg', [Arel.star])
|
42
|
-
assert_equal 'omg(*)',
|
47
|
+
assert_equal 'omg(*)', compile(function)
|
43
48
|
end
|
44
49
|
|
45
50
|
it 'should chain predications on named functions' do
|
46
51
|
function = Nodes::NamedFunction.new('omg', [Arel.star])
|
47
|
-
sql =
|
52
|
+
sql = compile(function.eq(2))
|
48
53
|
sql.must_be_like %{ omg(*) = 2 }
|
49
54
|
end
|
50
55
|
|
51
56
|
it 'should visit built-in functions' do
|
52
57
|
function = Nodes::Count.new([Arel.star])
|
53
|
-
assert_equal 'COUNT(*)',
|
58
|
+
assert_equal 'COUNT(*)', compile(function)
|
54
59
|
|
55
60
|
function = Nodes::Sum.new([Arel.star])
|
56
|
-
assert_equal 'SUM(*)',
|
61
|
+
assert_equal 'SUM(*)', compile(function)
|
57
62
|
|
58
63
|
function = Nodes::Max.new([Arel.star])
|
59
|
-
assert_equal 'MAX(*)',
|
64
|
+
assert_equal 'MAX(*)', compile(function)
|
60
65
|
|
61
66
|
function = Nodes::Min.new([Arel.star])
|
62
|
-
assert_equal 'MIN(*)',
|
67
|
+
assert_equal 'MIN(*)', compile(function)
|
63
68
|
|
64
69
|
function = Nodes::Avg.new([Arel.star])
|
65
|
-
assert_equal 'AVG(*)',
|
70
|
+
assert_equal 'AVG(*)', compile(function)
|
66
71
|
end
|
67
72
|
|
68
73
|
it 'should visit built-in functions operating on distinct values' do
|
69
74
|
function = Nodes::Count.new([Arel.star])
|
70
75
|
function.distinct = true
|
71
|
-
assert_equal 'COUNT(DISTINCT *)',
|
76
|
+
assert_equal 'COUNT(DISTINCT *)', compile(function)
|
72
77
|
|
73
78
|
function = Nodes::Sum.new([Arel.star])
|
74
79
|
function.distinct = true
|
75
|
-
assert_equal 'SUM(DISTINCT *)',
|
80
|
+
assert_equal 'SUM(DISTINCT *)', compile(function)
|
76
81
|
|
77
82
|
function = Nodes::Max.new([Arel.star])
|
78
83
|
function.distinct = true
|
79
|
-
assert_equal 'MAX(DISTINCT *)',
|
84
|
+
assert_equal 'MAX(DISTINCT *)', compile(function)
|
80
85
|
|
81
86
|
function = Nodes::Min.new([Arel.star])
|
82
87
|
function.distinct = true
|
83
|
-
assert_equal 'MIN(DISTINCT *)',
|
88
|
+
assert_equal 'MIN(DISTINCT *)', compile(function)
|
84
89
|
|
85
90
|
function = Nodes::Avg.new([Arel.star])
|
86
91
|
function.distinct = true
|
87
|
-
assert_equal 'AVG(DISTINCT *)',
|
92
|
+
assert_equal 'AVG(DISTINCT *)', compile(function)
|
88
93
|
end
|
89
94
|
|
90
95
|
it 'works with lists' do
|
91
96
|
function = Nodes::NamedFunction.new('omg', [Arel.star, Arel.star])
|
92
|
-
assert_equal 'omg(*, *)',
|
97
|
+
assert_equal 'omg(*, *)', compile(function)
|
93
98
|
end
|
94
99
|
|
95
100
|
describe 'Nodes::Equality' do
|
96
101
|
it "should escape strings" do
|
97
102
|
test = Table.new(:users)[:name].eq 'Aaron Patterson'
|
98
|
-
|
103
|
+
compile(test).must_be_like %{
|
99
104
|
"users"."name" = 'Aaron Patterson'
|
100
105
|
}
|
101
106
|
end
|
102
107
|
|
103
108
|
it 'should handle false' do
|
104
|
-
|
109
|
+
table = Table.new(:users)
|
110
|
+
val = Nodes.build_quoted(false, table[:active])
|
111
|
+
sql = compile Nodes::Equality.new(val, val)
|
105
112
|
sql.must_be_like %{ 'f' = 'f' }
|
106
113
|
end
|
107
114
|
|
108
115
|
it 'should use the column to quote' do
|
109
116
|
table = Table.new(:users)
|
110
|
-
|
117
|
+
val = Nodes.build_quoted('1-fooo', table[:id])
|
118
|
+
sql = compile Nodes::Equality.new(table[:id], val)
|
111
119
|
sql.must_be_like %{ "users"."id" = 1 }
|
112
120
|
end
|
113
121
|
|
122
|
+
it 'should use the column to quote integers' do
|
123
|
+
table = Table.new(:users)
|
124
|
+
sql = compile table[:name].eq(0)
|
125
|
+
sql.must_be_like %{ "users"."name" = '0' }
|
126
|
+
end
|
127
|
+
|
114
128
|
it 'should handle nil' do
|
115
|
-
sql =
|
129
|
+
sql = compile Nodes::Equality.new(@table[:name], nil)
|
116
130
|
sql.must_be_like %{ "users"."name" IS NULL }
|
117
131
|
end
|
118
132
|
end
|
119
133
|
|
120
134
|
describe 'Nodes::NotEqual' do
|
121
135
|
it 'should handle false' do
|
122
|
-
|
136
|
+
val = Nodes.build_quoted(false, @table[:active])
|
137
|
+
sql = compile Nodes::NotEqual.new(@table[:active], val)
|
123
138
|
sql.must_be_like %{ "users"."active" != 'f' }
|
124
139
|
end
|
125
140
|
|
126
141
|
it 'should handle nil' do
|
127
|
-
|
142
|
+
val = Nodes.build_quoted(nil, @table[:active])
|
143
|
+
sql = compile Nodes::NotEqual.new(@table[:name], val)
|
128
144
|
sql.must_be_like %{ "users"."name" IS NOT NULL }
|
129
145
|
end
|
130
146
|
end
|
131
147
|
|
132
148
|
it "should visit string subclass" do
|
133
|
-
|
134
|
-
|
149
|
+
[
|
150
|
+
Class.new(String).new(":'("),
|
151
|
+
Class.new(Class.new(String)).new(":'("),
|
152
|
+
].each do |obj|
|
153
|
+
val = Nodes.build_quoted(obj, @table[:active])
|
154
|
+
sql = compile Nodes::NotEqual.new(@table[:name], val)
|
155
|
+
sql.must_be_like %{ "users"."name" != ':\\'(' }
|
156
|
+
end
|
135
157
|
end
|
136
158
|
|
137
159
|
it "should visit_Class" do
|
138
|
-
|
160
|
+
compile(Nodes.build_quoted(DateTime)).must_equal "'DateTime'"
|
139
161
|
end
|
140
162
|
|
141
163
|
it "should escape LIMIT" do
|
142
164
|
sc = Arel::Nodes::SelectStatement.new
|
143
|
-
sc.limit = Arel::Nodes::Limit.new("omg")
|
144
|
-
assert_match(/LIMIT 'omg'/,
|
165
|
+
sc.limit = Arel::Nodes::Limit.new(Nodes.build_quoted("omg"))
|
166
|
+
assert_match(/LIMIT 'omg'/, compile(sc))
|
167
|
+
end
|
168
|
+
|
169
|
+
it "should quote LIMIT without column type coercion" do
|
170
|
+
table = Table.new(:users)
|
171
|
+
sc = table.where(table[:name].eq(0)).take(1).ast
|
172
|
+
assert_match(/WHERE "users"."name" = '0' LIMIT 1/, compile(sc))
|
145
173
|
end
|
146
174
|
|
147
175
|
it "should visit_DateTime" do
|
148
|
-
|
176
|
+
called_with = nil
|
177
|
+
@conn.connection.extend(Module.new {
|
178
|
+
define_method(:quote) do |thing, column|
|
179
|
+
called_with = column
|
180
|
+
super(thing, column)
|
181
|
+
end
|
182
|
+
})
|
183
|
+
|
184
|
+
dt = DateTime.now
|
185
|
+
table = Table.new(:users)
|
186
|
+
test = table[:created_at].eq dt
|
187
|
+
sql = compile test
|
188
|
+
|
189
|
+
assert_equal "created_at", called_with.name
|
190
|
+
sql.must_be_like %{"users"."created_at" = '#{dt.strftime("%Y-%m-%d %H:%M:%S")}'}
|
149
191
|
end
|
150
192
|
|
151
193
|
it "should visit_Float" do
|
152
|
-
|
194
|
+
test = Table.new(:products)[:price].eq 2.14
|
195
|
+
sql = compile test
|
196
|
+
sql.must_be_like %{"products"."price" = 2.14}
|
153
197
|
end
|
154
198
|
|
155
199
|
it "should visit_Not" do
|
156
|
-
sql =
|
200
|
+
sql = compile Nodes::Not.new(Arel.sql("foo"))
|
157
201
|
sql.must_be_like "NOT (foo)"
|
158
202
|
end
|
159
203
|
|
160
204
|
it "should apply Not to the whole expression" do
|
161
205
|
node = Nodes::And.new [@attr.eq(10), @attr.eq(11)]
|
162
|
-
sql =
|
206
|
+
sql = compile Nodes::Not.new(node)
|
163
207
|
sql.must_be_like %{NOT ("users"."id" = 10 AND "users"."id" = 11)}
|
164
208
|
end
|
165
209
|
|
166
210
|
it "should visit_As" do
|
167
211
|
as = Nodes::As.new(Arel.sql("foo"), Arel.sql("bar"))
|
168
|
-
sql =
|
212
|
+
sql = compile as
|
169
213
|
sql.must_be_like "foo AS bar"
|
170
214
|
end
|
171
215
|
|
172
216
|
it "should visit_Bignum" do
|
173
|
-
|
217
|
+
compile 8787878092
|
174
218
|
end
|
175
219
|
|
176
220
|
it "should visit_Hash" do
|
177
|
-
|
221
|
+
compile(Nodes.build_quoted({:a => 1}))
|
178
222
|
end
|
179
223
|
|
180
224
|
it "should visit_BigDecimal" do
|
181
|
-
|
225
|
+
compile Nodes.build_quoted(BigDecimal.new('2.14'))
|
182
226
|
end
|
183
227
|
|
184
228
|
it "should visit_Date" do
|
185
|
-
|
229
|
+
called_with = nil
|
230
|
+
@conn.connection.extend(Module.new {
|
231
|
+
define_method(:quote) do |thing, column|
|
232
|
+
called_with = column
|
233
|
+
super(thing, column)
|
234
|
+
end
|
235
|
+
})
|
236
|
+
|
237
|
+
dt = Date.today
|
238
|
+
table = Table.new(:users)
|
239
|
+
test = table[:created_at].eq dt
|
240
|
+
sql = compile test
|
241
|
+
|
242
|
+
assert_equal "created_at", called_with.name
|
243
|
+
sql.must_be_like %{"users"."created_at" = '#{dt.strftime("%Y-%m-%d")}'}
|
186
244
|
end
|
187
245
|
|
188
246
|
it "should visit_NilClass" do
|
189
|
-
|
247
|
+
compile(Nodes.build_quoted(nil)).must_be_like "NULL"
|
248
|
+
end
|
249
|
+
|
250
|
+
it "unsupported input should not raise ArgumentError" do
|
251
|
+
error = assert_raises(RuntimeError) { compile(nil) }
|
252
|
+
assert_match(/\Aunsupported/, error.message)
|
190
253
|
end
|
191
254
|
|
192
255
|
it "should visit_Arel_SelectManager, which is a subquery" do
|
193
256
|
mgr = Table.new(:foo).project(:bar)
|
194
|
-
|
257
|
+
compile(mgr).must_be_like '(SELECT bar FROM "foo")'
|
195
258
|
end
|
196
259
|
|
197
260
|
it "should visit_Arel_Nodes_And" do
|
198
261
|
node = Nodes::And.new [@attr.eq(10), @attr.eq(11)]
|
199
|
-
|
262
|
+
compile(node).must_be_like %{
|
200
263
|
"users"."id" = 10 AND "users"."id" = 11
|
201
264
|
}
|
202
265
|
end
|
203
266
|
|
204
267
|
it "should visit_Arel_Nodes_Or" do
|
205
268
|
node = Nodes::Or.new @attr.eq(10), @attr.eq(11)
|
206
|
-
|
269
|
+
compile(node).must_be_like %{
|
207
270
|
"users"."id" = 10 OR "users"."id" = 11
|
208
271
|
}
|
209
272
|
end
|
210
273
|
|
274
|
+
it "should visit_Arel_Nodes_Assignment" do
|
275
|
+
column = @table["id"]
|
276
|
+
node = Nodes::Assignment.new(
|
277
|
+
Nodes::UnqualifiedColumn.new(column),
|
278
|
+
Nodes::UnqualifiedColumn.new(column)
|
279
|
+
)
|
280
|
+
compile(node).must_be_like %{
|
281
|
+
"id" = "id"
|
282
|
+
}
|
283
|
+
end
|
284
|
+
|
211
285
|
it "should visit visit_Arel_Attributes_Time" do
|
212
286
|
attr = Attributes::Time.new(@attr.relation, @attr.name)
|
213
|
-
|
287
|
+
compile attr
|
214
288
|
end
|
215
289
|
|
216
290
|
it "should visit_TrueClass" do
|
217
291
|
test = Table.new(:users)[:bool].eq(true)
|
218
|
-
|
292
|
+
compile(test).must_be_like %{ "users"."bool" = 't' }
|
219
293
|
end
|
220
294
|
|
221
295
|
describe "Nodes::Matches" do
|
222
296
|
it "should know how to visit" do
|
223
297
|
node = @table[:name].matches('foo%')
|
224
|
-
|
298
|
+
compile(node).must_be_like %{
|
225
299
|
"users"."name" LIKE 'foo%'
|
226
300
|
}
|
227
301
|
end
|
@@ -229,7 +303,7 @@ module Arel
|
|
229
303
|
it 'can handle subqueries' do
|
230
304
|
subquery = @table.project(:id).where(@table[:name].matches('foo%'))
|
231
305
|
node = @attr.in subquery
|
232
|
-
|
306
|
+
compile(node).must_be_like %{
|
233
307
|
"users"."id" IN (SELECT id FROM "users" WHERE "users"."name" LIKE 'foo%')
|
234
308
|
}
|
235
309
|
end
|
@@ -238,7 +312,7 @@ module Arel
|
|
238
312
|
describe "Nodes::DoesNotMatch" do
|
239
313
|
it "should know how to visit" do
|
240
314
|
node = @table[:name].does_not_match('foo%')
|
241
|
-
|
315
|
+
compile(node).must_be_like %{
|
242
316
|
"users"."name" NOT LIKE 'foo%'
|
243
317
|
}
|
244
318
|
end
|
@@ -246,7 +320,7 @@ module Arel
|
|
246
320
|
it 'can handle subqueries' do
|
247
321
|
subquery = @table.project(:id).where(@table[:name].does_not_match('foo%'))
|
248
322
|
node = @attr.in subquery
|
249
|
-
|
323
|
+
compile(node).must_be_like %{
|
250
324
|
"users"."id" IN (SELECT id FROM "users" WHERE "users"."name" NOT LIKE 'foo%')
|
251
325
|
}
|
252
326
|
end
|
@@ -255,7 +329,7 @@ module Arel
|
|
255
329
|
describe "Nodes::Ordering" do
|
256
330
|
it "should know how to visit" do
|
257
331
|
node = @attr.desc
|
258
|
-
|
332
|
+
compile(node).must_be_like %{
|
259
333
|
"users"."id" DESC
|
260
334
|
}
|
261
335
|
end
|
@@ -264,52 +338,52 @@ module Arel
|
|
264
338
|
describe "Nodes::In" do
|
265
339
|
it "should know how to visit" do
|
266
340
|
node = @attr.in [1, 2, 3]
|
267
|
-
|
341
|
+
compile(node).must_be_like %{
|
268
342
|
"users"."id" IN (1, 2, 3)
|
269
343
|
}
|
270
344
|
end
|
271
345
|
|
272
346
|
it "should return 1=0 when empty right which is always false" do
|
273
347
|
node = @attr.in []
|
274
|
-
|
348
|
+
compile(node).must_equal '1=0'
|
275
349
|
end
|
276
350
|
|
277
351
|
it 'can handle two dot ranges' do
|
278
352
|
node = @attr.in 1..3
|
279
|
-
|
353
|
+
compile(node).must_be_like %{
|
280
354
|
"users"."id" BETWEEN 1 AND 3
|
281
355
|
}
|
282
356
|
end
|
283
357
|
|
284
358
|
it 'can handle three dot ranges' do
|
285
359
|
node = @attr.in 1...3
|
286
|
-
|
360
|
+
compile(node).must_be_like %{
|
287
361
|
"users"."id" >= 1 AND "users"."id" < 3
|
288
362
|
}
|
289
363
|
end
|
290
364
|
|
291
365
|
it 'can handle ranges bounded by infinity' do
|
292
366
|
node = @attr.in 1..Float::INFINITY
|
293
|
-
|
367
|
+
compile(node).must_be_like %{
|
294
368
|
"users"."id" >= 1
|
295
369
|
}
|
296
370
|
node = @attr.in(-Float::INFINITY..3)
|
297
|
-
|
371
|
+
compile(node).must_be_like %{
|
298
372
|
"users"."id" <= 3
|
299
373
|
}
|
300
374
|
node = @attr.in(-Float::INFINITY...3)
|
301
|
-
|
375
|
+
compile(node).must_be_like %{
|
302
376
|
"users"."id" < 3
|
303
377
|
}
|
304
378
|
node = @attr.in(-Float::INFINITY..Float::INFINITY)
|
305
|
-
|
379
|
+
compile(node).must_be_like %{1=1}
|
306
380
|
end
|
307
381
|
|
308
382
|
it 'can handle subqueries' do
|
309
383
|
table = Table.new(:users)
|
310
384
|
subquery = table.project(:id).where(table[:name].eq('Aaron'))
|
311
385
|
node = @attr.in subquery
|
312
|
-
|
386
|
+
compile(node).must_be_like %{
|
313
387
|
"users"."id" IN (SELECT id FROM "users" WHERE "users"."name" = 'Aaron')
|
314
388
|
}
|
315
389
|
end
|
@@ -324,34 +398,35 @@ module Arel
|
|
324
398
|
super
|
325
399
|
end
|
326
400
|
end
|
327
|
-
|
401
|
+
vals = %w{ a b c }.map { |x| Nodes.build_quoted(x, @attr) }
|
402
|
+
in_node = Nodes::In.new @attr, vals
|
328
403
|
visitor = visitor.new(Table.engine.connection)
|
329
404
|
visitor.expected = Table.engine.connection.columns(:users).find { |x|
|
330
405
|
x.name == 'name'
|
331
406
|
}
|
332
|
-
visitor.accept(in_node).must_equal %("users"."name" IN ('a', 'b', 'c'))
|
407
|
+
visitor.accept(in_node, Collectors::SQLString.new).value.must_equal %("users"."name" IN ('a', 'b', 'c'))
|
333
408
|
end
|
334
409
|
end
|
335
410
|
|
336
411
|
describe "Nodes::InfixOperation" do
|
337
412
|
it "should handle Multiplication" do
|
338
413
|
node = Arel::Attributes::Decimal.new(Table.new(:products), :price) * Arel::Attributes::Decimal.new(Table.new(:currency_rates), :rate)
|
339
|
-
|
414
|
+
compile(node).must_equal %("products"."price" * "currency_rates"."rate")
|
340
415
|
end
|
341
416
|
|
342
417
|
it "should handle Division" do
|
343
418
|
node = Arel::Attributes::Decimal.new(Table.new(:products), :price) / 5
|
344
|
-
|
419
|
+
compile(node).must_equal %("products"."price" / 5)
|
345
420
|
end
|
346
421
|
|
347
422
|
it "should handle Addition" do
|
348
423
|
node = Arel::Attributes::Decimal.new(Table.new(:products), :price) + 6
|
349
|
-
|
424
|
+
compile(node).must_equal %(("products"."price" + 6))
|
350
425
|
end
|
351
426
|
|
352
427
|
it "should handle Subtraction" do
|
353
428
|
node = Arel::Attributes::Decimal.new(Table.new(:products), :price) - 7
|
354
|
-
|
429
|
+
compile(node).must_equal %(("products"."price" - 7))
|
355
430
|
end
|
356
431
|
|
357
432
|
it "should handle arbitrary operators" do
|
@@ -360,59 +435,59 @@ module Arel
|
|
360
435
|
Arel::Attributes::String.new(Table.new(:products), :name),
|
361
436
|
Arel::Attributes::String.new(Table.new(:products), :name)
|
362
437
|
)
|
363
|
-
|
438
|
+
compile(node).must_equal %("products"."name" || "products"."name")
|
364
439
|
end
|
365
440
|
end
|
366
441
|
|
367
442
|
describe "Nodes::NotIn" do
|
368
443
|
it "should know how to visit" do
|
369
444
|
node = @attr.not_in [1, 2, 3]
|
370
|
-
|
445
|
+
compile(node).must_be_like %{
|
371
446
|
"users"."id" NOT IN (1, 2, 3)
|
372
447
|
}
|
373
448
|
end
|
374
449
|
|
375
450
|
it "should return 1=1 when empty right which is always true" do
|
376
451
|
node = @attr.not_in []
|
377
|
-
|
452
|
+
compile(node).must_equal '1=1'
|
378
453
|
end
|
379
454
|
|
380
455
|
it 'can handle two dot ranges' do
|
381
456
|
node = @attr.not_in 1..3
|
382
|
-
|
457
|
+
compile(node).must_be_like %{
|
383
458
|
"users"."id" < 1 OR "users"."id" > 3
|
384
459
|
}
|
385
460
|
end
|
386
461
|
|
387
462
|
it 'can handle three dot ranges' do
|
388
463
|
node = @attr.not_in 1...3
|
389
|
-
|
464
|
+
compile(node).must_be_like %{
|
390
465
|
"users"."id" < 1 OR "users"."id" >= 3
|
391
466
|
}
|
392
467
|
end
|
393
468
|
|
394
469
|
it 'can handle ranges bounded by infinity' do
|
395
470
|
node = @attr.not_in 1..Float::INFINITY
|
396
|
-
|
471
|
+
compile(node).must_be_like %{
|
397
472
|
"users"."id" < 1
|
398
473
|
}
|
399
474
|
node = @attr.not_in(-Float::INFINITY..3)
|
400
|
-
|
475
|
+
compile(node).must_be_like %{
|
401
476
|
"users"."id" > 3
|
402
477
|
}
|
403
478
|
node = @attr.not_in(-Float::INFINITY...3)
|
404
|
-
|
479
|
+
compile(node).must_be_like %{
|
405
480
|
"users"."id" >= 3
|
406
481
|
}
|
407
482
|
node = @attr.not_in(-Float::INFINITY..Float::INFINITY)
|
408
|
-
|
483
|
+
compile(node).must_be_like %{1=0}
|
409
484
|
end
|
410
485
|
|
411
486
|
it 'can handle subqueries' do
|
412
487
|
table = Table.new(:users)
|
413
488
|
subquery = table.project(:id).where(table[:name].eq('Aaron'))
|
414
489
|
node = @attr.not_in subquery
|
415
|
-
|
490
|
+
compile(node).must_be_like %{
|
416
491
|
"users"."id" NOT IN (SELECT id FROM "users" WHERE "users"."name" = 'Aaron')
|
417
492
|
}
|
418
493
|
end
|
@@ -427,26 +502,27 @@ module Arel
|
|
427
502
|
super
|
428
503
|
end
|
429
504
|
end
|
430
|
-
|
505
|
+
vals = %w{ a b c }.map { |x| Nodes.build_quoted(x, @attr) }
|
506
|
+
in_node = Nodes::NotIn.new @attr, vals
|
431
507
|
visitor = visitor.new(Table.engine.connection)
|
432
508
|
visitor.expected = Table.engine.connection.columns(:users).find { |x|
|
433
509
|
x.name == 'name'
|
434
510
|
}
|
435
|
-
|
511
|
+
compile(in_node).must_equal %("users"."name" NOT IN ('a', 'b', 'c'))
|
436
512
|
end
|
437
513
|
end
|
438
514
|
|
439
515
|
describe 'Constants' do
|
440
516
|
it "should handle true" do
|
441
517
|
test = Table.new(:users).create_true
|
442
|
-
|
518
|
+
compile(test).must_be_like %{
|
443
519
|
TRUE
|
444
520
|
}
|
445
521
|
end
|
446
522
|
|
447
523
|
it "should handle false" do
|
448
524
|
test = Table.new(:users).create_false
|
449
|
-
|
525
|
+
compile(test).must_be_like %{
|
450
526
|
FALSE
|
451
527
|
}
|
452
528
|
end
|
@@ -455,7 +531,7 @@ module Arel
|
|
455
531
|
describe 'TableAlias' do
|
456
532
|
it "should use the underlying table for checking columns" do
|
457
533
|
test = Table.new(:users).alias('zomgusers')[:id].eq '3'
|
458
|
-
|
534
|
+
compile(test).must_be_like %{
|
459
535
|
"zomgusers"."id" = 3
|
460
536
|
}
|
461
537
|
end
|
@@ -467,7 +543,27 @@ module Arel
|
|
467
543
|
core.set_quantifier = Arel::Nodes::DistinctOn.new(Arel.sql('aaron'))
|
468
544
|
|
469
545
|
assert_raises(NotImplementedError) do
|
470
|
-
|
546
|
+
compile(core)
|
547
|
+
end
|
548
|
+
end
|
549
|
+
end
|
550
|
+
|
551
|
+
describe 'Nodes::Regexp' do
|
552
|
+
it 'raises not implemented error' do
|
553
|
+
node = Arel::Nodes::Regexp.new(@table[:name], Nodes.build_quoted('foo%'))
|
554
|
+
|
555
|
+
assert_raises(NotImplementedError) do
|
556
|
+
compile(node)
|
557
|
+
end
|
558
|
+
end
|
559
|
+
end
|
560
|
+
|
561
|
+
describe 'Nodes::NotRegexp' do
|
562
|
+
it 'raises not implemented error' do
|
563
|
+
node = Arel::Nodes::NotRegexp.new(@table[:name], Nodes.build_quoted('foo%'))
|
564
|
+
|
565
|
+
assert_raises(NotImplementedError) do
|
566
|
+
compile(node)
|
471
567
|
end
|
472
568
|
end
|
473
569
|
end
|