arel 5.0.1.20140414130214 → 6.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +4 -2
  3. data/History.txt +9 -4
  4. data/Manifest.txt +9 -7
  5. data/README.markdown +85 -8
  6. data/Rakefile +1 -1
  7. data/arel.gemspec +15 -16
  8. data/lib/arel.rb +1 -12
  9. data/lib/arel/collectors/bind.rb +36 -0
  10. data/lib/arel/collectors/plain_string.rb +18 -0
  11. data/lib/arel/collectors/sql_string.rb +18 -0
  12. data/lib/arel/factory_methods.rb +1 -1
  13. data/lib/arel/insert_manager.rb +5 -1
  14. data/lib/arel/nodes.rb +41 -0
  15. data/lib/arel/nodes/and.rb +1 -5
  16. data/lib/arel/nodes/binary.rb +2 -0
  17. data/lib/arel/nodes/extract.rb +0 -2
  18. data/lib/arel/nodes/full_outer_join.rb +6 -0
  19. data/lib/arel/nodes/function.rb +0 -1
  20. data/lib/arel/nodes/insert_statement.rb +5 -2
  21. data/lib/arel/nodes/node.rb +5 -1
  22. data/lib/arel/nodes/right_outer_join.rb +6 -0
  23. data/lib/arel/nodes/window.rb +23 -5
  24. data/lib/arel/predications.rb +41 -33
  25. data/lib/arel/select_manager.rb +13 -37
  26. data/lib/arel/table.rb +13 -9
  27. data/lib/arel/tree_manager.rb +8 -2
  28. data/lib/arel/update_manager.rb +2 -2
  29. data/lib/arel/visitors.rb +0 -2
  30. data/lib/arel/visitors/bind_substitute.rb +9 -0
  31. data/lib/arel/visitors/bind_visitor.rb +10 -5
  32. data/lib/arel/visitors/depth_first.rb +60 -57
  33. data/lib/arel/visitors/dot.rb +84 -80
  34. data/lib/arel/visitors/ibm_db.rb +4 -2
  35. data/lib/arel/visitors/informix.rb +39 -21
  36. data/lib/arel/visitors/mssql.rb +41 -23
  37. data/lib/arel/visitors/mysql.rb +48 -22
  38. data/lib/arel/visitors/oracle.rb +33 -24
  39. data/lib/arel/visitors/postgresql.rb +15 -8
  40. data/lib/arel/visitors/reduce.rb +25 -0
  41. data/lib/arel/visitors/sqlite.rb +3 -2
  42. data/lib/arel/visitors/to_sql.rb +455 -248
  43. data/lib/arel/visitors/visitor.rb +2 -2
  44. data/lib/arel/visitors/where_sql.rb +3 -2
  45. data/test/attributes/test_attribute.rb +12 -3
  46. data/test/collectors/test_bind_collector.rb +70 -0
  47. data/test/collectors/test_sql_string.rb +38 -0
  48. data/test/helper.rb +10 -1
  49. data/test/nodes/test_bin.rb +2 -2
  50. data/test/nodes/test_count.rb +0 -6
  51. data/test/nodes/test_equality.rb +1 -1
  52. data/test/nodes/test_grouping.rb +1 -1
  53. data/test/nodes/test_infix_operation.rb +1 -1
  54. data/test/nodes/test_select_core.rb +7 -7
  55. data/test/nodes/test_sql_literal.rb +10 -6
  56. data/test/nodes/test_window.rb +9 -3
  57. data/test/support/fake_record.rb +16 -4
  58. data/test/test_factory_methods.rb +1 -1
  59. data/test/test_insert_manager.rb +33 -4
  60. data/test/test_select_manager.rb +164 -92
  61. data/test/test_table.rb +49 -4
  62. data/test/visitors/test_bind_visitor.rb +18 -10
  63. data/test/visitors/test_depth_first.rb +12 -0
  64. data/test/visitors/test_dot.rb +4 -4
  65. data/test/visitors/test_ibm_db.rb +11 -5
  66. data/test/visitors/test_informix.rb +14 -8
  67. data/test/visitors/test_mssql.rb +12 -8
  68. data/test/visitors/test_mysql.rb +17 -12
  69. data/test/visitors/test_oracle.rb +25 -21
  70. data/test/visitors/test_postgres.rb +50 -12
  71. data/test/visitors/test_sqlite.rb +2 -2
  72. data/test/visitors/test_to_sql.rb +177 -81
  73. metadata +24 -19
  74. data/lib/arel/deprecated.rb +0 -4
  75. data/lib/arel/expression.rb +0 -5
  76. data/lib/arel/sql/engine.rb +0 -10
  77. data/lib/arel/sql_literal.rb +0 -4
  78. data/lib/arel/visitors/join_sql.rb +0 -19
  79. data/lib/arel/visitors/order_clauses.rb +0 -11
  80. 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
- @visitor.accept(Nodes::Lock.new(Arel.sql('FOR UPDATE'))).must_be_like %{
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
- @visitor.accept(node).must_be_like %{
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 = @visitor.accept(sc)
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 )', @visitor.accept(core)
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', @visitor.accept(core)
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
- @visitor.accept(node).must_be_like %{
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
- @visitor.accept(node).must_be_like %{
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
- @visitor.accept(node).must_be_like %{
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
- @visitor.accept(node).must_be_like %{
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).must_be_nil
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
- @visitor = ToSql.new Table.engine.connection
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 = @visitor.accept node
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::Visitor) {
21
- define_method(:hello) do |node, attribute|
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 = @visitor.accept node
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(*)', @visitor.accept(function)
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 = @visitor.accept(function.eq(2))
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(*)', @visitor.accept(function)
58
+ assert_equal 'COUNT(*)', compile(function)
54
59
 
55
60
  function = Nodes::Sum.new([Arel.star])
56
- assert_equal 'SUM(*)', @visitor.accept(function)
61
+ assert_equal 'SUM(*)', compile(function)
57
62
 
58
63
  function = Nodes::Max.new([Arel.star])
59
- assert_equal 'MAX(*)', @visitor.accept(function)
64
+ assert_equal 'MAX(*)', compile(function)
60
65
 
61
66
  function = Nodes::Min.new([Arel.star])
62
- assert_equal 'MIN(*)', @visitor.accept(function)
67
+ assert_equal 'MIN(*)', compile(function)
63
68
 
64
69
  function = Nodes::Avg.new([Arel.star])
65
- assert_equal 'AVG(*)', @visitor.accept(function)
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 *)', @visitor.accept(function)
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 *)', @visitor.accept(function)
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 *)', @visitor.accept(function)
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 *)', @visitor.accept(function)
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 *)', @visitor.accept(function)
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(*, *)', @visitor.accept(function)
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
- @visitor.accept(test).must_be_like %{
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
- sql = @visitor.accept Nodes::Equality.new(false, false)
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
- sql = @visitor.accept Nodes::Equality.new(table[:id], '1-fooo')
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 = @visitor.accept Nodes::Equality.new(@table[:name], nil)
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
- sql = @visitor.accept Nodes::NotEqual.new(@table[:active], false)
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
- sql = @visitor.accept Nodes::NotEqual.new(@table[:name], nil)
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
- @visitor.accept(Class.new(String).new(":'("))
134
- @visitor.accept(Class.new(Class.new(String)).new(":'("))
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
- @visitor.accept(DateTime).must_equal "'DateTime'"
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'/, @visitor.accept(sc))
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
- @visitor.accept DateTime.now
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
- @visitor.accept 2.14
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 = @visitor.accept Nodes::Not.new(Arel.sql("foo"))
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 = @visitor.accept Nodes::Not.new(node)
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 = @visitor.accept as
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
- @visitor.accept 8787878092
217
+ compile 8787878092
174
218
  end
175
219
 
176
220
  it "should visit_Hash" do
177
- @visitor.accept({:a => 1})
221
+ compile(Nodes.build_quoted({:a => 1}))
178
222
  end
179
223
 
180
224
  it "should visit_BigDecimal" do
181
- @visitor.accept BigDecimal.new('2.14')
225
+ compile Nodes.build_quoted(BigDecimal.new('2.14'))
182
226
  end
183
227
 
184
228
  it "should visit_Date" do
185
- @visitor.accept Date.today
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
- @visitor.accept(nil).must_be_like "NULL"
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
- @visitor.accept(mgr).must_be_like '(SELECT bar FROM "foo")'
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
- @visitor.accept(node).must_be_like %{
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
- @visitor.accept(node).must_be_like %{
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
- @visitor.accept attr
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
- @visitor.accept(test).must_be_like %{ "users"."bool" = 't' }
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
- @visitor.accept(node).must_be_like %{
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
- @visitor.accept(node).must_be_like %{
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
- @visitor.accept(node).must_be_like %{
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
- @visitor.accept(node).must_be_like %{
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
- @visitor.accept(node).must_be_like %{
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
- @visitor.accept(node).must_be_like %{
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
- @visitor.accept(node).must_equal '1=0'
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
- @visitor.accept(node).must_be_like %{
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
- @visitor.accept(node).must_be_like %{
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
- @visitor.accept(node).must_be_like %{
367
+ compile(node).must_be_like %{
294
368
  "users"."id" >= 1
295
369
  }
296
370
  node = @attr.in(-Float::INFINITY..3)
297
- @visitor.accept(node).must_be_like %{
371
+ compile(node).must_be_like %{
298
372
  "users"."id" <= 3
299
373
  }
300
374
  node = @attr.in(-Float::INFINITY...3)
301
- @visitor.accept(node).must_be_like %{
375
+ compile(node).must_be_like %{
302
376
  "users"."id" < 3
303
377
  }
304
378
  node = @attr.in(-Float::INFINITY..Float::INFINITY)
305
- @visitor.accept(node).must_be_like %{1=1}
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
- @visitor.accept(node).must_be_like %{
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
- in_node = Nodes::In.new @attr, %w{ a b c }
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
- @visitor.accept(node).must_equal %("products"."price" * "currency_rates"."rate")
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
- @visitor.accept(node).must_equal %("products"."price" / 5)
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
- @visitor.accept(node).must_equal %(("products"."price" + 6))
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
- @visitor.accept(node).must_equal %(("products"."price" - 7))
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
- @visitor.accept(node).must_equal %("products"."name" || "products"."name")
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
- @visitor.accept(node).must_be_like %{
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
- @visitor.accept(node).must_equal '1=1'
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
- @visitor.accept(node).must_be_like %{
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
- @visitor.accept(node).must_be_like %{
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
- @visitor.accept(node).must_be_like %{
471
+ compile(node).must_be_like %{
397
472
  "users"."id" < 1
398
473
  }
399
474
  node = @attr.not_in(-Float::INFINITY..3)
400
- @visitor.accept(node).must_be_like %{
475
+ compile(node).must_be_like %{
401
476
  "users"."id" > 3
402
477
  }
403
478
  node = @attr.not_in(-Float::INFINITY...3)
404
- @visitor.accept(node).must_be_like %{
479
+ compile(node).must_be_like %{
405
480
  "users"."id" >= 3
406
481
  }
407
482
  node = @attr.not_in(-Float::INFINITY..Float::INFINITY)
408
- @visitor.accept(node).must_be_like %{1=0}
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
- @visitor.accept(node).must_be_like %{
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
- in_node = Nodes::NotIn.new @attr, %w{ a b c }
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
- visitor.accept(in_node).must_equal %("users"."name" NOT IN ('a', 'b', 'c'))
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
- @visitor.accept(test).must_be_like %{
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
- @visitor.accept(test).must_be_like %{
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
- @visitor.accept(test).must_be_like %{
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
- @visitor.accept(core)
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