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