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.
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