sequel_core 2.2.0 → 3.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. metadata +30 -101
  2. data/CHANGELOG +0 -1519
  3. data/COPYING +0 -19
  4. data/README +0 -313
  5. data/Rakefile +0 -158
  6. data/bin/sequel +0 -117
  7. data/doc/cheat_sheet.rdoc +0 -225
  8. data/doc/dataset_filtering.rdoc +0 -182
  9. data/lib/sequel_core.rb +0 -136
  10. data/lib/sequel_core/adapters/adapter_skeleton.rb +0 -68
  11. data/lib/sequel_core/adapters/ado.rb +0 -90
  12. data/lib/sequel_core/adapters/db2.rb +0 -160
  13. data/lib/sequel_core/adapters/dbi.rb +0 -127
  14. data/lib/sequel_core/adapters/informix.rb +0 -89
  15. data/lib/sequel_core/adapters/jdbc.rb +0 -110
  16. data/lib/sequel_core/adapters/mysql.rb +0 -486
  17. data/lib/sequel_core/adapters/odbc.rb +0 -167
  18. data/lib/sequel_core/adapters/odbc_mssql.rb +0 -106
  19. data/lib/sequel_core/adapters/openbase.rb +0 -76
  20. data/lib/sequel_core/adapters/oracle.rb +0 -182
  21. data/lib/sequel_core/adapters/postgres.rb +0 -560
  22. data/lib/sequel_core/adapters/sqlite.rb +0 -270
  23. data/lib/sequel_core/connection_pool.rb +0 -194
  24. data/lib/sequel_core/core_ext.rb +0 -197
  25. data/lib/sequel_core/core_sql.rb +0 -184
  26. data/lib/sequel_core/database.rb +0 -462
  27. data/lib/sequel_core/database/schema.rb +0 -156
  28. data/lib/sequel_core/dataset.rb +0 -457
  29. data/lib/sequel_core/dataset/callback.rb +0 -13
  30. data/lib/sequel_core/dataset/convenience.rb +0 -245
  31. data/lib/sequel_core/dataset/pagination.rb +0 -96
  32. data/lib/sequel_core/dataset/query.rb +0 -41
  33. data/lib/sequel_core/dataset/schema.rb +0 -15
  34. data/lib/sequel_core/dataset/sql.rb +0 -889
  35. data/lib/sequel_core/deprecated.rb +0 -26
  36. data/lib/sequel_core/exceptions.rb +0 -42
  37. data/lib/sequel_core/migration.rb +0 -187
  38. data/lib/sequel_core/object_graph.rb +0 -216
  39. data/lib/sequel_core/pretty_table.rb +0 -71
  40. data/lib/sequel_core/schema.rb +0 -2
  41. data/lib/sequel_core/schema/generator.rb +0 -239
  42. data/lib/sequel_core/schema/sql.rb +0 -326
  43. data/lib/sequel_core/sql.rb +0 -812
  44. data/lib/sequel_core/worker.rb +0 -68
  45. data/spec/adapters/informix_spec.rb +0 -96
  46. data/spec/adapters/mysql_spec.rb +0 -765
  47. data/spec/adapters/oracle_spec.rb +0 -222
  48. data/spec/adapters/postgres_spec.rb +0 -441
  49. data/spec/adapters/sqlite_spec.rb +0 -413
  50. data/spec/connection_pool_spec.rb +0 -363
  51. data/spec/core_ext_spec.rb +0 -156
  52. data/spec/core_sql_spec.rb +0 -427
  53. data/spec/database_spec.rb +0 -963
  54. data/spec/dataset_spec.rb +0 -2933
  55. data/spec/expression_filters_spec.rb +0 -316
  56. data/spec/migration_spec.rb +0 -261
  57. data/spec/object_graph_spec.rb +0 -230
  58. data/spec/pretty_table_spec.rb +0 -58
  59. data/spec/rcov.opts +0 -6
  60. data/spec/schema_generator_spec.rb +0 -122
  61. data/spec/schema_spec.rb +0 -422
  62. data/spec/spec.opts +0 -0
  63. data/spec/spec_config.rb +0 -7
  64. data/spec/spec_config.rb.example +0 -8
  65. data/spec/spec_helper.rb +0 -55
  66. data/spec/worker_spec.rb +0 -96
@@ -1,2933 +0,0 @@
1
- require File.join(File.dirname(__FILE__), "spec_helper")
2
-
3
- context "Dataset" do
4
- setup do
5
- @dataset = Sequel::Dataset.new("db")
6
- end
7
-
8
- specify "should accept database and opts in initialize" do
9
- db = "db"
10
- opts = {:from => :test}
11
- d = Sequel::Dataset.new(db, opts)
12
- d.db.should be(db)
13
- d.opts.should be(opts)
14
-
15
- d = Sequel::Dataset.new(db)
16
- d.db.should be(db)
17
- d.opts.should be_a_kind_of(Hash)
18
- d.opts.should == {}
19
- end
20
-
21
- specify "should provide clone for chainability" do
22
- d1 = @dataset.clone(:from => [:test])
23
- d1.class.should == @dataset.class
24
- d1.should_not == @dataset
25
- d1.db.should be(@dataset.db)
26
- d1.opts[:from].should == [:test]
27
- @dataset.opts[:from].should be_nil
28
-
29
- d2 = d1.clone(:order => [:name])
30
- d2.class.should == @dataset.class
31
- d2.should_not == d1
32
- d2.should_not == @dataset
33
- d2.db.should be(@dataset.db)
34
- d2.opts[:from].should == [:test]
35
- d2.opts[:order].should == [:name]
36
- d1.opts[:order].should be_nil
37
- end
38
-
39
- specify "should include Enumerable" do
40
- Sequel::Dataset.included_modules.should include(Enumerable)
41
- end
42
-
43
- specify "should raise ImplementedError for the dataset interface methods" do
44
- proc {@dataset.fetch_rows('abc')}.should raise_error(NotImplementedError)
45
- proc {@dataset.insert(1, 2, 3)}.should raise_error(NotImplementedError)
46
- proc {@dataset.update(:name => 'abc')}.should raise_error(NotImplementedError)
47
- proc {@dataset.delete}.should raise_error(NotImplementedError)
48
- end
49
- end
50
-
51
- context "Dataset#clone" do
52
- setup do
53
- @dataset = Sequel::Dataset.new(nil).from(:items)
54
- end
55
-
56
- specify "should create an exact copy of the dataset" do
57
- @c = Class.new
58
- @dataset.set_model(@c)
59
- @clone = @dataset.clone
60
-
61
- @clone.should_not === @dataset
62
- @clone.class.should == @dataset.class
63
- @clone.model_classes.should == @dataset.model_classes
64
- end
65
-
66
- specify "should deep-copy the dataset opts" do
67
- @clone = @dataset.clone
68
-
69
- @clone.opts.should_not equal(@dataset.opts)
70
- @dataset.filter!(:a => 'b')
71
- @clone.opts[:filter].should be_nil
72
- end
73
-
74
- specify "should return a clone self" do
75
- clone = @dataset.clone({})
76
- clone.class.should == @dataset.class
77
- clone.db.should == @dataset.db
78
- clone.opts.should == @dataset.opts
79
- end
80
-
81
- specify "should merge the specified options" do
82
- clone = @dataset.clone(1 => 2)
83
- clone.opts.should == {1 => 2, :from => [:items]}
84
- end
85
-
86
- specify "should overwrite existing options" do
87
- clone = @dataset.clone(:from => [:other])
88
- clone.opts.should == {:from => [:other]}
89
- end
90
-
91
- specify "should create a clone with a deep copy of options" do
92
- clone = @dataset.clone(:from => [:other])
93
- @dataset.opts[:from].should == [:items]
94
- clone.opts[:from].should == [:other]
95
- end
96
-
97
- specify "should return an object with the same modules included" do
98
- m = Module.new do
99
- def __xyz__; "xyz"; end
100
- end
101
- @dataset.extend(m)
102
- @dataset.clone({}).should respond_to(:__xyz__)
103
- end
104
- end
105
-
106
- context "A simple dataset" do
107
- setup do
108
- @dataset = Sequel::Dataset.new(nil).from(:test)
109
- end
110
-
111
- specify "should format a select statement" do
112
- @dataset.select_sql.should == 'SELECT * FROM test'
113
- end
114
-
115
- specify "should format a delete statement" do
116
- @dataset.delete_sql.should == 'DELETE FROM test'
117
- end
118
-
119
- specify "should format an insert statement with default values" do
120
- @dataset.insert_sql.should == 'INSERT INTO test DEFAULT VALUES'
121
- end
122
-
123
- specify "should format an insert statement with hash" do
124
- @dataset.insert_sql(:name => 'wxyz', :price => 342).
125
- should match(/INSERT INTO test \(name, price\) VALUES \('wxyz', 342\)|INSERT INTO test \(price, name\) VALUES \(342, 'wxyz'\)/)
126
-
127
- @dataset.insert_sql({}).should == "INSERT INTO test DEFAULT VALUES"
128
- end
129
-
130
- specify "should format an insert statement with string keys" do
131
- @dataset.insert_sql('name' => 'wxyz', 'price' => 342).
132
- should match(/INSERT INTO test \(name, price\) VALUES \('wxyz', 342\)|INSERT INTO test \(price, name\) VALUES \(342, 'wxyz'\)/)
133
- end
134
-
135
- specify "should format an insert statement with an object that respond_to? :values" do
136
- dbb = Sequel::Database.new
137
-
138
- v = Object.new
139
- def v.values; {:a => 1}; end
140
-
141
- @dataset.insert_sql(v).should == "INSERT INTO test (a) VALUES (1)"
142
-
143
- def v.values; {}; end
144
- @dataset.insert_sql(v).should == "INSERT INTO test DEFAULT VALUES"
145
- end
146
-
147
- specify "should format an insert statement with an arbitrary value" do
148
- @dataset.insert_sql(123).should == "INSERT INTO test VALUES (123)"
149
- end
150
-
151
- specify "should format an insert statement with sub-query" do
152
- @sub = Sequel::Dataset.new(nil).from(:something).filter(:x => 2)
153
- @dataset.insert_sql(@sub).should == \
154
- "INSERT INTO test (SELECT * FROM something WHERE (x = 2))"
155
- end
156
-
157
- specify "should format an insert statement with array" do
158
- @dataset.insert_sql('a', 2, 6.5).should ==
159
- "INSERT INTO test VALUES ('a', 2, 6.5)"
160
- end
161
-
162
- specify "should format an update statement" do
163
- @dataset.update_sql(:name => 'abc').should ==
164
- "UPDATE test SET name = 'abc'"
165
- end
166
-
167
- specify "should be able to return rows for arbitrary SQL" do
168
- @dataset.select_sql(:sql => 'xxx yyy zzz').should ==
169
- "xxx yyy zzz"
170
- end
171
- end
172
-
173
- context "A dataset with multiple tables in its FROM clause" do
174
- setup do
175
- @dataset = Sequel::Dataset.new(nil).from(:t1, :t2)
176
- end
177
-
178
- specify "should raise on #update_sql" do
179
- proc {@dataset.update_sql(:a=>1)}.should raise_error(Sequel::Error::InvalidOperation)
180
- end
181
-
182
- specify "should raise on #delete_sql" do
183
- proc {@dataset.delete_sql}.should raise_error(Sequel::Error::InvalidOperation)
184
- end
185
-
186
- specify "should generate a select query FROM all specified tables" do
187
- @dataset.select_sql.should == "SELECT * FROM t1, t2"
188
- end
189
- end
190
-
191
- context "Dataset#where" do
192
- setup do
193
- @dataset = Sequel::Dataset.new(nil).from(:test)
194
- @d1 = @dataset.where(:region => 'Asia')
195
- @d2 = @dataset.where('region = ?', 'Asia')
196
- @d3 = @dataset.where("a = 1")
197
- end
198
-
199
- specify "should work with hashes" do
200
- @dataset.where(:name => 'xyz', :price => 342).select_sql.
201
- should match(/WHERE \(\(name = 'xyz'\) AND \(price = 342\)\)|WHERE \(\(price = 342\) AND \(name = 'xyz'\)\)/)
202
- end
203
-
204
- specify "should work with arrays (ala ActiveRecord)" do
205
- @dataset.where('price < ? AND id in ?', 100, [1, 2, 3]).select_sql.should ==
206
- "SELECT * FROM test WHERE (price < 100 AND id in (1, 2, 3))"
207
- end
208
-
209
- specify "should work with strings (custom SQL expressions)" do
210
- @dataset.where('(a = 1 AND b = 2)').select_sql.should ==
211
- "SELECT * FROM test WHERE ((a = 1 AND b = 2))"
212
- end
213
-
214
- specify "should affect select, delete and update statements" do
215
- @d1.select_sql.should == "SELECT * FROM test WHERE (region = 'Asia')"
216
- @d1.delete_sql.should == "DELETE FROM test WHERE (region = 'Asia')"
217
- @d1.update_sql(:GDP => 0).should == "UPDATE test SET GDP = 0 WHERE (region = 'Asia')"
218
-
219
- @d2.select_sql.should == "SELECT * FROM test WHERE (region = 'Asia')"
220
- @d2.delete_sql.should == "DELETE FROM test WHERE (region = 'Asia')"
221
- @d2.update_sql(:GDP => 0).should == "UPDATE test SET GDP = 0 WHERE (region = 'Asia')"
222
-
223
- @d3.select_sql.should == "SELECT * FROM test WHERE (a = 1)"
224
- @d3.delete_sql.should == "DELETE FROM test WHERE (a = 1)"
225
- @d3.update_sql(:GDP => 0).should == "UPDATE test SET GDP = 0 WHERE (a = 1)"
226
-
227
- end
228
-
229
- specify "should be composable using AND operator (for scoping)" do
230
- # hashes are merged, no problem
231
- @d1.where(:size => 'big').select_sql.should ==
232
- "SELECT * FROM test WHERE ((region = 'Asia') AND (size = 'big'))"
233
-
234
- # hash and string
235
- @d1.where('population > 1000').select_sql.should ==
236
- "SELECT * FROM test WHERE ((region = 'Asia') AND (population > 1000))"
237
- @d1.where('(a > 1) OR (b < 2)').select_sql.should ==
238
- "SELECT * FROM test WHERE ((region = 'Asia') AND ((a > 1) OR (b < 2)))"
239
-
240
- # hash and array
241
- @d1.where('GDP > ?', 1000).select_sql.should ==
242
- "SELECT * FROM test WHERE ((region = 'Asia') AND (GDP > 1000))"
243
-
244
- # array and array
245
- @d2.where('GDP > ?', 1000).select_sql.should ==
246
- "SELECT * FROM test WHERE ((region = 'Asia') AND (GDP > 1000))"
247
-
248
- # array and hash
249
- @d2.where(:name => ['Japan', 'China']).select_sql.should ==
250
- "SELECT * FROM test WHERE ((region = 'Asia') AND (name IN ('Japan', 'China')))"
251
-
252
- # array and string
253
- @d2.where('GDP > ?').select_sql.should ==
254
- "SELECT * FROM test WHERE ((region = 'Asia') AND (GDP > ?))"
255
-
256
- # string and string
257
- @d3.where('b = 2').select_sql.should ==
258
- "SELECT * FROM test WHERE ((a = 1) AND (b = 2))"
259
-
260
- # string and hash
261
- @d3.where(:c => 3).select_sql.should ==
262
- "SELECT * FROM test WHERE ((a = 1) AND (c = 3))"
263
-
264
- # string and array
265
- @d3.where('d = ?', 4).select_sql.should ==
266
- "SELECT * FROM test WHERE ((a = 1) AND (d = 4))"
267
- end
268
-
269
- specify "should be composable using AND operator (for scoping) with block" do
270
- @d3.where{:e < 5}.select_sql.should ==
271
- "SELECT * FROM test WHERE ((a = 1) AND (e < 5))"
272
- end
273
-
274
- specify "should raise if the dataset is grouped" do
275
- proc {@dataset.group(:t).where(:a => 1)}.should_not raise_error
276
- @dataset.group(:t).where(:a => 1).sql.should ==
277
- "SELECT * FROM test WHERE (a = 1) GROUP BY t"
278
- end
279
-
280
- specify "should accept ranges" do
281
- @dataset.filter(:id => 4..7).sql.should ==
282
- 'SELECT * FROM test WHERE ((id >= 4) AND (id <= 7))'
283
- @dataset.filter(:id => 4...7).sql.should ==
284
- 'SELECT * FROM test WHERE ((id >= 4) AND (id < 7))'
285
-
286
- @dataset.filter(:table__id => 4..7).sql.should ==
287
- 'SELECT * FROM test WHERE ((table.id >= 4) AND (table.id <= 7))'
288
- @dataset.filter(:table__id => 4...7).sql.should ==
289
- 'SELECT * FROM test WHERE ((table.id >= 4) AND (table.id < 7))'
290
- end
291
-
292
- specify "should accept nil" do
293
- @dataset.filter(:owner_id => nil).sql.should ==
294
- 'SELECT * FROM test WHERE (owner_id IS NULL)'
295
- end
296
-
297
- specify "should accept a subquery" do
298
- @dataset.filter('gdp > ?', @d1.select(:avg[:gdp])).sql.should ==
299
- "SELECT * FROM test WHERE (gdp > (SELECT avg(gdp) FROM test WHERE (region = 'Asia')))"
300
-
301
- @dataset.filter(:id => @d1.select(:id)).sql.should ==
302
- "SELECT * FROM test WHERE (id IN (SELECT id FROM test WHERE (region = 'Asia')))"
303
- end
304
-
305
- specify "should accept a subquery for an EXISTS clause" do
306
- a = @dataset.filter(:price < 100)
307
- @dataset.filter(a.exists).sql.should ==
308
- 'SELECT * FROM test WHERE (EXISTS (SELECT * FROM test WHERE (price < 100)))'
309
- end
310
-
311
- specify "should accept proc expressions" do
312
- d = @d1.select(:avg[:gdp])
313
- @dataset.filter {:gdp > d}.sql.should ==
314
- "SELECT * FROM test WHERE (gdp > (SELECT avg(gdp) FROM test WHERE (region = 'Asia')))"
315
-
316
- @dataset.filter {:a < 1}.sql.should ==
317
- 'SELECT * FROM test WHERE (a < 1)'
318
-
319
- @dataset.filter {(:a >= 1) & (:b <= 2)}.sql.should ==
320
- 'SELECT * FROM test WHERE ((a >= 1) AND (b <= 2))'
321
-
322
- @dataset.filter {:c.like 'ABC%'}.sql.should ==
323
- "SELECT * FROM test WHERE (c LIKE 'ABC%')"
324
-
325
- @dataset.filter {:c.like 'ABC%'}.sql.should ==
326
- "SELECT * FROM test WHERE (c LIKE 'ABC%')"
327
-
328
- @dataset.filter {:c.like 'ABC%', '%XYZ'}.sql.should ==
329
- "SELECT * FROM test WHERE ((c LIKE 'ABC%') OR (c LIKE '%XYZ'))"
330
- end
331
-
332
- specify "should work for grouped datasets" do
333
- @dataset.group(:a).filter(:b => 1).sql.should ==
334
- 'SELECT * FROM test WHERE (b = 1) GROUP BY a'
335
- end
336
-
337
- specify "should accept true and false as arguments" do
338
- @dataset.filter(true).sql.should ==
339
- "SELECT * FROM test WHERE 't'"
340
- @dataset.filter(false).sql.should ==
341
- "SELECT * FROM test WHERE 'f'"
342
- end
343
-
344
- specify "should allow the use of blocks and arguments simultaneously" do
345
- @dataset.filter(:zz < 3){:yy > 3}.sql.should ==
346
- 'SELECT * FROM test WHERE ((zz < 3) AND (yy > 3))'
347
- end
348
-
349
- specify "should yield a VirtualRow to the block" do
350
- x = nil
351
- @dataset.filter{|r| x = r; false}
352
- x.should be_a_kind_of(Sequel::SQL::VirtualRow)
353
- @dataset.filter{|r| ((r.name < 'b') & {r.table__id => 1}) | r.is_active(r.blah, r.xx, r.x__y_z)}.sql.should ==
354
- "SELECT * FROM test WHERE (((name < 'b') AND (table.id = 1)) OR is_active(blah, xx, x.y_z))"
355
- end
356
- end
357
-
358
- context "Dataset#or" do
359
- setup do
360
- @dataset = Sequel::Dataset.new(nil).from(:test)
361
- @d1 = @dataset.where(:x => 1)
362
- end
363
-
364
- specify "should raise if no filter exists" do
365
- proc {@dataset.or(:a => 1)}.should raise_error(Sequel::Error)
366
- end
367
-
368
- specify "should add an alternative expression to the where clause" do
369
- @d1.or(:y => 2).sql.should ==
370
- 'SELECT * FROM test WHERE ((x = 1) OR (y = 2))'
371
- end
372
-
373
- specify "should accept all forms of filters" do
374
- @d1.or('y > ?', 2).sql.should ==
375
- 'SELECT * FROM test WHERE ((x = 1) OR (y > 2))'
376
- @d1.or(:yy > 3).sql.should ==
377
- 'SELECT * FROM test WHERE ((x = 1) OR (yy > 3))'
378
- end
379
-
380
- specify "should accept blocks passed to filter" do
381
- @d1.or{:yy > 3}.sql.should ==
382
- 'SELECT * FROM test WHERE ((x = 1) OR (yy > 3))'
383
- end
384
-
385
- specify "should correctly add parens to give predictable results" do
386
- @d1.filter(:y => 2).or(:z => 3).sql.should ==
387
- 'SELECT * FROM test WHERE (((x = 1) AND (y = 2)) OR (z = 3))'
388
-
389
- @d1.or(:y => 2).filter(:z => 3).sql.should ==
390
- 'SELECT * FROM test WHERE (((x = 1) OR (y = 2)) AND (z = 3))'
391
- end
392
-
393
- specify "should allow the use of blocks and arguments simultaneously" do
394
- @d1.or(:zz < 3){:yy > 3}.sql.should ==
395
- 'SELECT * FROM test WHERE ((x = 1) OR ((zz < 3) AND (yy > 3)))'
396
- end
397
- end
398
-
399
- context "Dataset#and" do
400
- setup do
401
- @dataset = Sequel::Dataset.new(nil).from(:test)
402
- @d1 = @dataset.where(:x => 1)
403
- end
404
-
405
- specify "should raise if no filter exists" do
406
- proc {@dataset.and(:a => 1)}.should raise_error(Sequel::Error)
407
- proc {@dataset.where(:a => 1).group(:t).and(:b => 2)}.should_not raise_error(Sequel::Error)
408
- @dataset.where(:a => 1).group(:t).and(:b => 2).sql ==
409
- "SELECT * FROM test WHERE (a = 1) AND (b = 2) GROUP BY t"
410
- end
411
-
412
- specify "should add an alternative expression to the where clause" do
413
- @d1.and(:y => 2).sql.should ==
414
- 'SELECT * FROM test WHERE ((x = 1) AND (y = 2))'
415
- end
416
-
417
- specify "should accept all forms of filters" do
418
- # probably not exhaustive, but good enough
419
- @d1.and('y > ?', 2).sql.should ==
420
- 'SELECT * FROM test WHERE ((x = 1) AND (y > 2))'
421
- @d1.and(:yy > 3).sql.should ==
422
- 'SELECT * FROM test WHERE ((x = 1) AND (yy > 3))'
423
- end
424
-
425
- specify "should accept blocks passed to filter" do
426
- @d1.and {:yy > 3}.sql.should ==
427
- 'SELECT * FROM test WHERE ((x = 1) AND (yy > 3))'
428
- end
429
-
430
- specify "should correctly add parens to give predictable results" do
431
- @d1.or(:y => 2).and(:z => 3).sql.should ==
432
- 'SELECT * FROM test WHERE (((x = 1) OR (y = 2)) AND (z = 3))'
433
-
434
- @d1.and(:y => 2).or(:z => 3).sql.should ==
435
- 'SELECT * FROM test WHERE (((x = 1) AND (y = 2)) OR (z = 3))'
436
- end
437
- end
438
-
439
- context "Dataset#exclude" do
440
- setup do
441
- @dataset = Sequel::Dataset.new(nil).from(:test)
442
- end
443
-
444
- specify "should correctly include the NOT operator when one condition is given" do
445
- @dataset.exclude(:region=>'Asia').select_sql.should ==
446
- "SELECT * FROM test WHERE (region != 'Asia')"
447
- end
448
-
449
- specify "should take multiple conditions as a hash and express the logic correctly in SQL" do
450
- @dataset.exclude(:region => 'Asia', :name => 'Japan').select_sql.
451
- should match(Regexp.union(/WHERE \(\(region != 'Asia'\) AND \(name != 'Japan'\)\)/,
452
- /WHERE \(\(name != 'Japan'\) AND \(region != 'Asia'\)\)/))
453
- end
454
-
455
- specify "should parenthesize a single string condition correctly" do
456
- @dataset.exclude("region = 'Asia' AND name = 'Japan'").select_sql.should ==
457
- "SELECT * FROM test WHERE NOT (region = 'Asia' AND name = 'Japan')"
458
- end
459
-
460
- specify "should parenthesize an array condition correctly" do
461
- @dataset.exclude('region = ? AND name = ?', 'Asia', 'Japan').select_sql.should ==
462
- "SELECT * FROM test WHERE NOT (region = 'Asia' AND name = 'Japan')"
463
- end
464
-
465
- specify "should correctly parenthesize when it is used twice" do
466
- @dataset.exclude(:region => 'Asia').exclude(:name => 'Japan').select_sql.should ==
467
- "SELECT * FROM test WHERE ((region != 'Asia') AND (name != 'Japan'))"
468
- end
469
-
470
- specify "should support proc expressions" do
471
- @dataset.exclude{:id < 6}.sql.should ==
472
- 'SELECT * FROM test WHERE (id >= 6)'
473
- end
474
-
475
- specify "should allow the use of blocks and arguments simultaneously" do
476
- @dataset.exclude(:id => (7..11)){:id < 6}.sql.should ==
477
- 'SELECT * FROM test WHERE (((id < 7) OR (id > 11)) OR (id >= 6))'
478
- end
479
- end
480
-
481
- context "Dataset#invert" do
482
- setup do
483
- @d = Sequel::Dataset.new(nil).from(:test)
484
- end
485
-
486
- specify "should raise error if the dataset is not filtered" do
487
- proc{@d.invert}.should raise_error(Sequel::Error)
488
- end
489
-
490
- specify "should invert current filter if dataset is filtered" do
491
- @d.filter(:x).invert.sql.should == 'SELECT * FROM test WHERE NOT x'
492
- end
493
-
494
- specify "should invert both having and where if both are preset" do
495
- @d.filter(:x).group(:x).having(:x).invert.sql.should == 'SELECT * FROM test WHERE NOT x GROUP BY x HAVING NOT x'
496
- end
497
- end
498
-
499
- context "Dataset#having" do
500
- setup do
501
- @dataset = Sequel::Dataset.new(nil).from(:test)
502
- @grouped = @dataset.group(:region).select(:region, :sum[:population], :avg[:gdp])
503
- @d1 = @grouped.having('sum(population) > 10')
504
- @d2 = @grouped.having(:region => 'Asia')
505
- @columns = "region, sum(population), avg(gdp)"
506
- end
507
-
508
- specify "should raise if the dataset is not grouped" do
509
- proc {@dataset.having('avg(gdp) > 10')}.should raise_error(Sequel::Error::InvalidOperation)
510
- end
511
-
512
- specify "should affect select statements" do
513
- @d1.select_sql.should ==
514
- "SELECT #{@columns} FROM test GROUP BY region HAVING (sum(population) > 10)"
515
- end
516
-
517
- specify "should support proc expressions" do
518
- @grouped.having {:sum[:population] > 10}.sql.should ==
519
- "SELECT #{@columns} FROM test GROUP BY region HAVING (sum(population) > 10)"
520
- end
521
-
522
- specify "should work with and on the having clause" do
523
- @grouped.having( :a > 1 ).and( :b < 2 ).sql.should ==
524
- "SELECT #{@columns} FROM test GROUP BY region HAVING ((a > 1) AND (b < 2))"
525
- end
526
- end
527
-
528
- context "a grouped dataset" do
529
- setup do
530
- @dataset = Sequel::Dataset.new(nil).from(:test).group(:type_id)
531
- end
532
-
533
- specify "should raise when trying to generate an update statement" do
534
- proc {@dataset.update_sql(:id => 0)}.should raise_error
535
- end
536
-
537
- specify "should raise when trying to generate a delete statement" do
538
- proc {@dataset.delete_sql}.should raise_error
539
- end
540
-
541
- specify "should specify the grouping in generated select statement" do
542
- @dataset.select_sql.should ==
543
- "SELECT * FROM test GROUP BY type_id"
544
- end
545
-
546
- specify "should format the right statement for counting (as a subquery)" do
547
- db = MockDatabase.new
548
- db[:test].select(:name).group(:name).count
549
- db.sqls.should == ["SELECT COUNT(*) FROM (SELECT name FROM test GROUP BY name) t1 LIMIT 1"]
550
- end
551
- end
552
-
553
- context "Dataset#group_by" do
554
- setup do
555
- @dataset = Sequel::Dataset.new(nil).from(:test).group_by(:type_id)
556
- end
557
-
558
- specify "should raise when trying to generate an update statement" do
559
- proc {@dataset.update_sql(:id => 0)}.should raise_error
560
- end
561
-
562
- specify "should raise when trying to generate a delete statement" do
563
- proc {@dataset.delete_sql}.should raise_error
564
- end
565
-
566
- specify "should specify the grouping in generated select statement" do
567
- @dataset.select_sql.should ==
568
- "SELECT * FROM test GROUP BY type_id"
569
- @dataset.group_by(:a, :b).select_sql.should ==
570
- "SELECT * FROM test GROUP BY a, b"
571
- end
572
-
573
- specify "should specify the grouping in generated select statement" do
574
- @dataset.group_by(:type_id=>nil).select_sql.should ==
575
- "SELECT * FROM test GROUP BY (type_id IS NULL)"
576
- end
577
-
578
- specify "should be aliased as #group" do
579
- @dataset.group(:type_id=>nil).select_sql.should ==
580
- "SELECT * FROM test GROUP BY (type_id IS NULL)"
581
- end
582
- end
583
-
584
- context "Dataset#as" do
585
- specify "should set up an alias" do
586
- dataset = Sequel::Dataset.new(nil).from(:test)
587
- dataset.select(dataset.limit(1).select(:name).as(:n)).sql.should == \
588
- 'SELECT (SELECT name FROM test LIMIT 1) AS n FROM test'
589
- end
590
- end
591
-
592
- context "Dataset#literal" do
593
- setup do
594
- @dataset = Sequel::Dataset.new(nil).from(:test)
595
- end
596
-
597
- specify "should escape strings properly" do
598
- @dataset.literal('abc').should == "'abc'"
599
- @dataset.literal('a"x"bc').should == "'a\"x\"bc'"
600
- @dataset.literal("a'bc").should == "'a''bc'"
601
- @dataset.literal("a''bc").should == "'a''''bc'"
602
- @dataset.literal("a\\bc").should == "'a\\\\bc'"
603
- @dataset.literal("a\\\\bc").should == "'a\\\\\\\\bc'"
604
- @dataset.literal("a\\'bc").should == "'a\\\\''bc'"
605
- end
606
-
607
- specify "should literalize numbers properly" do
608
- @dataset.literal(1).should == "1"
609
- @dataset.literal(1.5).should == "1.5"
610
- end
611
-
612
- specify "should literalize nil as NULL" do
613
- @dataset.literal(nil).should == "NULL"
614
- end
615
-
616
- specify "should literalize an array properly" do
617
- @dataset.literal([]).should == "(NULL)"
618
- @dataset.literal([1, 'abc', 3]).should == "(1, 'abc', 3)"
619
- @dataset.literal([1, "a'b''c", 3]).should == "(1, 'a''b''''c', 3)"
620
- end
621
-
622
- specify "should literalize symbols as column references" do
623
- @dataset.literal(:name).should == "name"
624
- @dataset.literal(:items__name).should == "items.name"
625
- end
626
-
627
- specify "should raise an error for unsupported types" do
628
- proc {@dataset.literal({})}.should raise_error
629
- end
630
-
631
- specify "should literalize datasets as subqueries" do
632
- d = @dataset.from(:test)
633
- d.literal(d).should == "(#{d.sql})"
634
- end
635
-
636
- specify "should literalize Time properly" do
637
- t = Time.now
638
- s = t.strftime("TIMESTAMP '%Y-%m-%d %H:%M:%S'")
639
- @dataset.literal(t).should == s
640
- end
641
-
642
- specify "should literalize Date properly" do
643
- d = Date.today
644
- s = d.strftime("DATE '%Y-%m-%d'")
645
- @dataset.literal(d).should == s
646
- end
647
-
648
- specify "should not literalize expression strings" do
649
- @dataset.literal('col1 + 2'.expr).should == 'col1 + 2'
650
-
651
- @dataset.update_sql(:a => 'a + 2'.expr).should ==
652
- 'UPDATE test SET a = a + 2'
653
- end
654
-
655
- specify "should literalize BigDecimal instances correctly" do
656
- @dataset.literal(BigDecimal.new("80")).should == "80.0"
657
- end
658
- end
659
-
660
- context "Dataset#from" do
661
- setup do
662
- @dataset = Sequel::Dataset.new(nil)
663
- end
664
-
665
- specify "should accept a Dataset" do
666
- proc {@dataset.from(@dataset)}.should_not raise_error
667
- end
668
-
669
- specify "should format a Dataset as a subquery if it has had options set" do
670
- @dataset.from(@dataset.from(:a).where(:a=>1)).select_sql.should ==
671
- "SELECT * FROM (SELECT * FROM a WHERE (a = 1)) t1"
672
- end
673
-
674
- specify "should automatically alias sub-queries" do
675
- @dataset.from(@dataset.from(:a).group(:b)).select_sql.should ==
676
- "SELECT * FROM (SELECT * FROM a GROUP BY b) t1"
677
-
678
- d1 = @dataset.from(:a).group(:b)
679
- d2 = @dataset.from(:c).group(:d)
680
-
681
- @dataset.from(d1, d2).sql.should ==
682
- "SELECT * FROM (SELECT * FROM a GROUP BY b) t1, (SELECT * FROM c GROUP BY d) t2"
683
- end
684
-
685
- specify "should accept a hash for aliasing" do
686
- @dataset.from(:a => :b).sql.should ==
687
- "SELECT * FROM a b"
688
-
689
- @dataset.from(:a => 'b').sql.should ==
690
- "SELECT * FROM a b"
691
-
692
- @dataset.from(:a => :c[:d]).sql.should ==
693
- "SELECT * FROM a c(d)"
694
-
695
- @dataset.from(@dataset.from(:a).group(:b) => :c).sql.should ==
696
- "SELECT * FROM (SELECT * FROM a GROUP BY b) c"
697
- end
698
-
699
- specify "should always use a subquery if given a dataset" do
700
- @dataset.from(@dataset.from(:a)).select_sql.should ==
701
- "SELECT * FROM (SELECT * FROM a) t1"
702
- end
703
-
704
- specify "should raise if no source is given" do
705
- proc {@dataset.from(@dataset.from).select_sql}.should raise_error(Sequel::Error)
706
- end
707
-
708
- specify "should accept sql functions" do
709
- @dataset.from(:abc[:def]).select_sql.should ==
710
- "SELECT * FROM abc(def)"
711
-
712
- @dataset.from(:a[:i]).select_sql.should ==
713
- "SELECT * FROM a(i)"
714
- end
715
- end
716
-
717
- context "Dataset#select" do
718
- setup do
719
- @d = Sequel::Dataset.new(nil).from(:test)
720
- end
721
-
722
- specify "should accept variable arity" do
723
- @d.select(:name).sql.should == 'SELECT name FROM test'
724
- @d.select(:a, :b, :test__c).sql.should == 'SELECT a, b, test.c FROM test'
725
- end
726
-
727
- specify "should accept symbols and literal strings" do
728
- @d.select('aaa'.lit).sql.should == 'SELECT aaa FROM test'
729
- @d.select(:a, 'b'.lit).sql.should == 'SELECT a, b FROM test'
730
- @d.select(:test__cc, 'test.d AS e'.lit).sql.should ==
731
- 'SELECT test.cc, test.d AS e FROM test'
732
- @d.select('test.d AS e'.lit, :test__cc).sql.should ==
733
- 'SELECT test.d AS e, test.cc FROM test'
734
-
735
- # symbol helpers
736
- @d.select(:test.*).sql.should ==
737
- 'SELECT test.* FROM test'
738
- @d.select(:test__name.as(:n)).sql.should ==
739
- 'SELECT test.name AS n FROM test'
740
- @d.select(:test__name___n).sql.should ==
741
- 'SELECT test.name AS n FROM test'
742
- end
743
-
744
- specify "should use the wildcard if no arguments are given" do
745
- @d.select.sql.should == 'SELECT * FROM test'
746
- end
747
-
748
- specify "should accept a hash for AS values" do
749
- @d.select(:name => 'n', :__ggh => 'age').sql.should =~
750
- /SELECT ((name AS n, __ggh AS age)|(__ggh AS age, name AS n)) FROM test/
751
- end
752
-
753
- specify "should overrun the previous select option" do
754
- @d.select!(:a, :b, :c).select.sql.should == 'SELECT * FROM test'
755
- @d.select!(:price).select(:name).sql.should == 'SELECT name FROM test'
756
- end
757
-
758
- specify "should accept arbitrary objects and literalize them correctly" do
759
- @d.select(1, :a, 't').sql.should == "SELECT 1, a, 't' FROM test"
760
-
761
- @d.select(nil, :sum[:t], :x___y).sql.should == "SELECT NULL, sum(t), x AS y FROM test"
762
-
763
- @d.select(nil, 1, :x => :y).sql.should == "SELECT NULL, 1, x AS y FROM test"
764
- end
765
- end
766
-
767
- context "Dataset#select_all" do
768
- setup do
769
- @d = Sequel::Dataset.new(nil).from(:test)
770
- end
771
-
772
- specify "should select the wildcard" do
773
- @d.select_all.sql.should == 'SELECT * FROM test'
774
- end
775
-
776
- specify "should overrun the previous select option" do
777
- @d.select!(:a, :b, :c).select_all.sql.should == 'SELECT * FROM test'
778
- end
779
- end
780
-
781
- context "Dataset#select_more" do
782
- setup do
783
- @d = Sequel::Dataset.new(nil).from(:test)
784
- end
785
-
786
- specify "should act like #select for datasets with no selection" do
787
- @d.select_more(:a, :b).sql.should == 'SELECT a, b FROM test'
788
- @d.select_all.select_more(:a, :b).sql.should == 'SELECT a, b FROM test'
789
- @d.select(:blah).select_all.select_more(:a, :b).sql.should == 'SELECT a, b FROM test'
790
- end
791
-
792
- specify "should add to the currently selected columns" do
793
- @d.select(:a).select_more(:b).sql.should == 'SELECT a, b FROM test'
794
- @d.select(:a.*).select_more(:b.*).sql.should == 'SELECT a.*, b.* FROM test'
795
- end
796
- end
797
-
798
- context "Dataset#order" do
799
- setup do
800
- @dataset = Sequel::Dataset.new(nil).from(:test)
801
- end
802
-
803
- specify "should include an ORDER BY clause in the select statement" do
804
- @dataset.order(:name).sql.should ==
805
- 'SELECT * FROM test ORDER BY name'
806
- end
807
-
808
- specify "should accept multiple arguments" do
809
- @dataset.order(:name, :price.desc).sql.should ==
810
- 'SELECT * FROM test ORDER BY name, price DESC'
811
- end
812
-
813
- specify "should overrun a previous ordering" do
814
- @dataset.order(:name).order(:stamp).sql.should ==
815
- 'SELECT * FROM test ORDER BY stamp'
816
- end
817
-
818
- specify "should accept a literal string" do
819
- @dataset.order('dada ASC'.lit).sql.should ==
820
- 'SELECT * FROM test ORDER BY dada ASC'
821
- end
822
-
823
- specify "should accept a hash as an expression" do
824
- @dataset.order(:name=>nil).sql.should ==
825
- 'SELECT * FROM test ORDER BY (name IS NULL)'
826
- end
827
-
828
- specify "should accept a nil to remove ordering" do
829
- @dataset.order(:bah).order(nil).sql.should ==
830
- 'SELECT * FROM test'
831
- end
832
- end
833
-
834
- context "Dataset#unfiltered" do
835
- setup do
836
- @dataset = Sequel::Dataset.new(nil).from(:test)
837
- end
838
-
839
- specify "should remove filtering from the dataset" do
840
- @dataset.filter(:score=>1).unfiltered.sql.should ==
841
- 'SELECT * FROM test'
842
- end
843
- end
844
-
845
- context "Dataset#unordered" do
846
- setup do
847
- @dataset = Sequel::Dataset.new(nil).from(:test)
848
- end
849
-
850
- specify "should remove ordering from the dataset" do
851
- @dataset.order(:name).unordered.sql.should ==
852
- 'SELECT * FROM test'
853
- end
854
- end
855
-
856
- context "Dataset#order_by" do
857
- setup do
858
- @dataset = Sequel::Dataset.new(nil).from(:test)
859
- end
860
-
861
- specify "should include an ORDER BY clause in the select statement" do
862
- @dataset.order_by(:name).sql.should ==
863
- 'SELECT * FROM test ORDER BY name'
864
- end
865
-
866
- specify "should accept multiple arguments" do
867
- @dataset.order_by(:name, :price.desc).sql.should ==
868
- 'SELECT * FROM test ORDER BY name, price DESC'
869
- end
870
-
871
- specify "should overrun a previous ordering" do
872
- @dataset.order_by(:name).order(:stamp).sql.should ==
873
- 'SELECT * FROM test ORDER BY stamp'
874
- end
875
-
876
- specify "should accept a string" do
877
- @dataset.order_by('dada ASC'.lit).sql.should ==
878
- 'SELECT * FROM test ORDER BY dada ASC'
879
- end
880
-
881
- specify "should accept a nil to remove ordering" do
882
- @dataset.order_by(:bah).order_by(nil).sql.should ==
883
- 'SELECT * FROM test'
884
- end
885
- end
886
-
887
- context "Dataset#order_more" do
888
- setup do
889
- @dataset = Sequel::Dataset.new(nil).from(:test)
890
- end
891
-
892
- specify "should include an ORDER BY clause in the select statement" do
893
- @dataset.order_more(:name).sql.should ==
894
- 'SELECT * FROM test ORDER BY name'
895
- end
896
-
897
- specify "should add to a previous ordering" do
898
- @dataset.order(:name).order_more(:stamp.desc).sql.should ==
899
- 'SELECT * FROM test ORDER BY name, stamp DESC'
900
- end
901
- end
902
-
903
- context "Dataset#reverse_order" do
904
- setup do
905
- @dataset = Sequel::Dataset.new(nil).from(:test)
906
- end
907
-
908
- specify "should use DESC as default order" do
909
- @dataset.reverse_order(:name).sql.should ==
910
- 'SELECT * FROM test ORDER BY name DESC'
911
- end
912
-
913
- specify "should invert the order given" do
914
- @dataset.reverse_order(:name.desc).sql.should ==
915
- 'SELECT * FROM test ORDER BY name ASC'
916
- end
917
-
918
- specify "should invert the order for ASC expressions" do
919
- @dataset.reverse_order(:name.asc).sql.should ==
920
- 'SELECT * FROM test ORDER BY name DESC'
921
- end
922
-
923
- specify "should accept multiple arguments" do
924
- @dataset.reverse_order(:name, :price.desc).sql.should ==
925
- 'SELECT * FROM test ORDER BY name DESC, price ASC'
926
- end
927
-
928
- specify "should reverse a previous ordering if no arguments are given" do
929
- @dataset.order(:name).reverse_order.sql.should ==
930
- 'SELECT * FROM test ORDER BY name DESC'
931
- @dataset.order(:clumsy.desc, :fool).reverse_order.sql.should ==
932
- 'SELECT * FROM test ORDER BY clumsy ASC, fool DESC'
933
- end
934
-
935
- specify "should return an unordered dataset for a dataset with no order" do
936
- @dataset.unordered.reverse_order.sql.should ==
937
- 'SELECT * FROM test'
938
- end
939
-
940
- specify "should have #reverse alias" do
941
- @dataset.order(:name).reverse.sql.should ==
942
- 'SELECT * FROM test ORDER BY name DESC'
943
- end
944
- end
945
-
946
- context "Dataset#limit" do
947
- setup do
948
- @dataset = Sequel::Dataset.new(nil).from(:test)
949
- end
950
-
951
- specify "should include a LIMIT clause in the select statement" do
952
- @dataset.limit(10).sql.should ==
953
- 'SELECT * FROM test LIMIT 10'
954
- end
955
-
956
- specify "should accept ranges" do
957
- @dataset.limit(3..7).sql.should ==
958
- 'SELECT * FROM test LIMIT 5 OFFSET 3'
959
-
960
- @dataset.limit(3...7).sql.should ==
961
- 'SELECT * FROM test LIMIT 4 OFFSET 3'
962
- end
963
-
964
- specify "should include an offset if a second argument is given" do
965
- @dataset.limit(6, 10).sql.should ==
966
- 'SELECT * FROM test LIMIT 6 OFFSET 10'
967
- end
968
-
969
- specify "should work with fixed sql datasets" do
970
- @dataset.opts[:sql] = 'select * from cccc'
971
- @dataset.limit(6, 10).sql.should ==
972
- 'SELECT * FROM (select * from cccc) t1 LIMIT 6 OFFSET 10'
973
- end
974
-
975
- specify "should raise an error if an invalid limit or offset is used" do
976
- proc{@dataset.limit(-1)}.should raise_error(Sequel::Error)
977
- proc{@dataset.limit(0)}.should raise_error(Sequel::Error)
978
- proc{@dataset.limit(1)}.should_not raise_error(Sequel::Error)
979
- proc{@dataset.limit(1, -1)}.should raise_error(Sequel::Error)
980
- proc{@dataset.limit(1, 0)}.should_not raise_error(Sequel::Error)
981
- proc{@dataset.limit(1, 1)}.should_not raise_error(Sequel::Error)
982
- end
983
- end
984
-
985
- context "Dataset#naked" do
986
- setup do
987
- @d1 = Sequel::Dataset.new(nil, {1 => 2, 3 => 4})
988
- @d2 = Sequel::Dataset.new(nil, {1 => 2, 3 => 4}).set_model(Object)
989
- end
990
-
991
- specify "should return a clone with :naked option set" do
992
- naked = @d1.naked
993
- naked.opts[:naked].should be_true
994
- end
995
-
996
- specify "should remove any existing reference to a model class" do
997
- naked = @d2.naked
998
- naked.opts[:models].should be_nil
999
- end
1000
- end
1001
-
1002
- context "Dataset#qualified_column_name" do
1003
- setup do
1004
- @dataset = Sequel::Dataset.new(nil).from(:test)
1005
- end
1006
-
1007
- specify "should return the literal value if not given a symbol" do
1008
- @dataset.literal(@dataset.send(:qualified_column_name, 'ccc__b', :items)).should == "'ccc__b'"
1009
- @dataset.literal(@dataset.send(:qualified_column_name, 3, :items)).should == '3'
1010
- @dataset.literal(@dataset.send(:qualified_column_name, 'a'.lit, :items)).should == 'a'
1011
- end
1012
-
1013
- specify "should qualify the column with the supplied table name if given an unqualified symbol" do
1014
- @dataset.literal(@dataset.send(:qualified_column_name, :b1, :items)).should == 'items.b1'
1015
- end
1016
-
1017
- specify "should not changed the qualifed column's table if given a qualified symbol" do
1018
- @dataset.literal(@dataset.send(:qualified_column_name, :ccc__b, :items)).should == 'ccc.b'
1019
- end
1020
- end
1021
-
1022
- class DummyDataset < Sequel::Dataset
1023
- VALUES = [
1024
- {:a => 1, :b => 2},
1025
- {:a => 3, :b => 4},
1026
- {:a => 5, :b => 6}
1027
- ]
1028
- def fetch_rows(sql, &block)
1029
- VALUES.each(&block)
1030
- end
1031
- end
1032
-
1033
- context "Dataset#map" do
1034
- setup do
1035
- @d = DummyDataset.new(nil).from(:items)
1036
- end
1037
-
1038
- specify "should provide the usual functionality if no argument is given" do
1039
- @d.map {|n| n[:a] + n[:b]}.should == [3, 7, 11]
1040
- end
1041
-
1042
- specify "should map using #[column name] if column name is given" do
1043
- @d.map(:a).should == [1, 3, 5]
1044
- end
1045
-
1046
- specify "should return the complete dataset values if nothing is given" do
1047
- @d.map.to_a.should == DummyDataset::VALUES
1048
- end
1049
- end
1050
-
1051
- context "Dataset#to_hash" do
1052
- setup do
1053
- @d = DummyDataset.new(nil).from(:items)
1054
- end
1055
-
1056
- specify "should provide a hash with the first column as key and the second as value" do
1057
- @d.to_hash(:a, :b).should == {1 => 2, 3 => 4, 5 => 6}
1058
- @d.to_hash(:b, :a).should == {2 => 1, 4 => 3, 6 => 5}
1059
- end
1060
-
1061
- specify "should provide a hash with the first column as key and the entire hash as value if the value column is blank or nil" do
1062
- @d.to_hash(:a).should == {1 => {:a => 1, :b => 2}, 3 => {:a => 3, :b => 4}, 5 => {:a => 5, :b => 6}}
1063
- @d.to_hash(:b).should == {2 => {:a => 1, :b => 2}, 4 => {:a => 3, :b => 4}, 6 => {:a => 5, :b => 6}}
1064
- end
1065
- end
1066
-
1067
- context "Dataset#uniq" do
1068
- setup do
1069
- @db = MockDatabase.new
1070
- @dataset = @db[:test].select(:name)
1071
- end
1072
-
1073
- specify "should include DISTINCT clause in statement" do
1074
- @dataset.uniq.sql.should == 'SELECT DISTINCT name FROM test'
1075
- end
1076
-
1077
- specify "should be aliased by Dataset#distinct" do
1078
- @dataset.distinct.sql.should == 'SELECT DISTINCT name FROM test'
1079
- end
1080
-
1081
- specify "should accept an expression list" do
1082
- @dataset.uniq(:a, :b).sql.should == 'SELECT DISTINCT ON (a, b) name FROM test'
1083
-
1084
- @dataset.uniq(:stamp.cast_as(:integer), :node_id=>nil).sql.should == 'SELECT DISTINCT ON (cast(stamp AS integer), (node_id IS NULL)) name FROM test'
1085
- end
1086
-
1087
- specify "should do a subselect for count" do
1088
- @dataset.uniq.count
1089
- @db.sqls.should == ['SELECT COUNT(*) FROM (SELECT DISTINCT name FROM test) t1 LIMIT 1']
1090
- end
1091
- end
1092
-
1093
- context "Dataset#count" do
1094
- setup do
1095
- @c = Class.new(Sequel::Dataset) do
1096
- def self.sql
1097
- @@sql
1098
- end
1099
-
1100
- def fetch_rows(sql)
1101
- @@sql = sql
1102
- yield({1 => 1})
1103
- end
1104
- end
1105
- @dataset = @c.new(nil).from(:test)
1106
- end
1107
-
1108
- specify "should format SQL properly" do
1109
- @dataset.count.should == 1
1110
- @c.sql.should == 'SELECT COUNT(*) FROM test LIMIT 1'
1111
- end
1112
-
1113
- specify "should be aliased by #size" do
1114
- @dataset.size.should == 1
1115
- end
1116
-
1117
- specify "should include the where clause if it's there" do
1118
- @dataset.filter(:abc < 30).count.should == 1
1119
- @c.sql.should == 'SELECT COUNT(*) FROM test WHERE (abc < 30) LIMIT 1'
1120
- end
1121
-
1122
- specify "should count properly for datasets with fixed sql" do
1123
- @dataset.opts[:sql] = "select abc from xyz"
1124
- @dataset.count.should == 1
1125
- @c.sql.should == "SELECT COUNT(*) FROM (select abc from xyz) t1 LIMIT 1"
1126
- end
1127
-
1128
- specify "should return limit if count is greater than it" do
1129
- @dataset.limit(5).count.should == 1
1130
- @c.sql.should == "SELECT COUNT(*) FROM (SELECT * FROM test LIMIT 5) t1 LIMIT 1"
1131
- end
1132
- end
1133
-
1134
-
1135
- context "Dataset#group_and_count" do
1136
- setup do
1137
- @c = Class.new(Sequel::Dataset) do
1138
- def self.sql
1139
- @@sql
1140
- end
1141
-
1142
- def fetch_rows(sql)
1143
- @@sql = sql
1144
- yield({1 => 1})
1145
- end
1146
- end
1147
- @ds = @c.new(nil).from(:test)
1148
- end
1149
-
1150
- specify "should format SQL properly" do
1151
- @ds.group_and_count(:name).sql.should ==
1152
- "SELECT name, count(*) AS count FROM test GROUP BY name ORDER BY count"
1153
- end
1154
-
1155
- specify "should accept multiple columns for grouping" do
1156
- @ds.group_and_count(:a, :b).sql.should ==
1157
- "SELECT a, b, count(*) AS count FROM test GROUP BY a, b ORDER BY count"
1158
- end
1159
-
1160
- specify "should work within query block" do
1161
- @ds.query{group_and_count(:a, :b)}.sql.should ==
1162
- "SELECT a, b, count(*) AS count FROM test GROUP BY a, b ORDER BY count"
1163
- end
1164
- end
1165
-
1166
- context "Dataset#empty?" do
1167
- specify "should return true if records exist in the dataset" do
1168
- @db = Sequel::Database.new
1169
- @db.meta_def(:execute) {|sql| @sqls ||=[]; @sqls << sql}
1170
- @db.meta_def(:sqls) {@sqls ||= []}
1171
-
1172
- $cccc = Class.new(Sequel::Dataset) do
1173
- def fetch_rows(sql)
1174
- @db.execute(sql)
1175
- yield(:x => 'blah')
1176
- end
1177
- end
1178
-
1179
- @db.meta_def(:dataset) do
1180
- $cccc.new(self)
1181
- end
1182
-
1183
- @dataset = Sequel::Dataset.new(@db).from(:test)
1184
-
1185
- @dataset.should_not be_empty
1186
- @db.sqls.last.should == 'SELECT 1 WHERE (EXISTS (SELECT * FROM test)) LIMIT 1'
1187
-
1188
- @db.meta_def(:dataset) do
1189
- ds = $cccc.new(self)
1190
- ds.meta_def(:get) {|c| nil}
1191
- ds
1192
- end
1193
-
1194
- @dataset.should be_empty
1195
- end
1196
- end
1197
-
1198
- context "Dataset#join_table" do
1199
- setup do
1200
- @d = MockDataset.new(nil).from(:items)
1201
- @d.quote_identifiers = true
1202
- end
1203
-
1204
- specify "should format the JOIN clause properly" do
1205
- @d.join_table(:left_outer, :categories, :category_id => :id).sql.should ==
1206
- 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1207
- end
1208
-
1209
- specify "should handle multiple conditions on the same join table column" do
1210
- @d.join_table(:left_outer, :categories, [[:category_id, :id], [:category_id, 0..100]]).sql.should ==
1211
- 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON (("categories"."category_id" = "items"."id") AND (("categories"."category_id" >= 0) AND ("categories"."category_id" <= 100)))'
1212
- end
1213
-
1214
- specify "should include WHERE clause if applicable" do
1215
- @d.filter(:price < 100).join_table(:right_outer, :categories, :category_id => :id).sql.should ==
1216
- 'SELECT * FROM "items" RIGHT OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id") WHERE ("price" < 100)'
1217
- end
1218
-
1219
- specify "should include ORDER BY clause if applicable" do
1220
- @d.order(:stamp).join_table(:full_outer, :categories, :category_id => :id).sql.should ==
1221
- 'SELECT * FROM "items" FULL OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id") ORDER BY "stamp"'
1222
- end
1223
-
1224
- specify "should support multiple joins" do
1225
- @d.join_table(:inner, :b, :items_id=>:id).join_table(:left_outer, :c, :b_id => :b__id).sql.should ==
1226
- 'SELECT * FROM "items" INNER JOIN "b" ON ("b"."items_id" = "items"."id") LEFT OUTER JOIN "c" ON ("c"."b_id" = "b"."id")'
1227
- end
1228
-
1229
- specify "should support left outer joins" do
1230
- @d.join_table(:left_outer, :categories, :category_id=>:id).sql.should ==
1231
- 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1232
-
1233
- @d.left_outer_join(:categories, :category_id=>:id).sql.should ==
1234
- 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1235
- end
1236
-
1237
- specify "should support right outer joins" do
1238
- @d.join_table(:right_outer, :categories, :category_id=>:id).sql.should ==
1239
- 'SELECT * FROM "items" RIGHT OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1240
-
1241
- @d.right_outer_join(:categories, :category_id=>:id).sql.should ==
1242
- 'SELECT * FROM "items" RIGHT OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1243
- end
1244
-
1245
- specify "should support full outer joins" do
1246
- @d.join_table(:full_outer, :categories, :category_id=>:id).sql.should ==
1247
- 'SELECT * FROM "items" FULL OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1248
-
1249
- @d.full_outer_join(:categories, :category_id=>:id).sql.should ==
1250
- 'SELECT * FROM "items" FULL OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1251
- end
1252
-
1253
- specify "should support inner joins" do
1254
- @d.join_table(:inner, :categories, :category_id=>:id).sql.should ==
1255
- 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1256
-
1257
- @d.inner_join(:categories, :category_id=>:id).sql.should ==
1258
- 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1259
- end
1260
-
1261
- specify "should default to a plain join if nil is used for the type" do
1262
- @d.join_table(nil, :categories, :category_id=>:id).sql.should ==
1263
- 'SELECT * FROM "items" JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1264
- end
1265
-
1266
- specify "should use an inner join for Dataset#join" do
1267
- @d.join(:categories, :category_id=>:id).sql.should ==
1268
- 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1269
- end
1270
-
1271
- specify "should support aliased tables" do
1272
- @d.from('stats').join('players', {:id => :player_id}, 'p').sql.should ==
1273
- 'SELECT * FROM "stats" INNER JOIN "players" AS "p" ON ("p"."id" = "stats"."player_id")'
1274
-
1275
- ds = MockDataset.new(nil).from(:foo => :f)
1276
- ds.quote_identifiers = true
1277
- ds.join_table(:inner, :bar, :id => :bar_id).sql.should ==
1278
- 'SELECT * FROM "foo" "f" INNER JOIN "bar" ON ("bar"."id" = "f"."bar_id")'
1279
- end
1280
-
1281
- specify "should allow for arbitrary conditions in the JOIN clause" do
1282
- @d.join_table(:left_outer, :categories, :status => 0).sql.should ==
1283
- 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."status" = 0)'
1284
- @d.join_table(:left_outer, :categories, :categorizable_type => "Post").sql.should ==
1285
- 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."categorizable_type" = \'Post\')'
1286
- @d.join_table(:left_outer, :categories, :timestamp => "CURRENT_TIMESTAMP".lit).sql.should ==
1287
- 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."timestamp" = CURRENT_TIMESTAMP)'
1288
- @d.join_table(:left_outer, :categories, :status => [1, 2, 3]).sql.should ==
1289
- 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."status" IN (1, 2, 3))'
1290
- end
1291
-
1292
- specify "should raise error for a table without a source" do
1293
- proc {Sequel::Dataset.new(nil).join('players', :id => :player_id)}. \
1294
- should raise_error(Sequel::Error)
1295
- end
1296
-
1297
- specify "should support joining datasets" do
1298
- ds = Sequel::Dataset.new(nil).from(:categories)
1299
-
1300
- @d.join_table(:left_outer, ds, :item_id => :id).sql.should ==
1301
- 'SELECT * FROM "items" LEFT OUTER JOIN (SELECT * FROM categories) AS "t1" ON ("t1"."item_id" = "items"."id")'
1302
-
1303
- ds.filter!(:active => true)
1304
-
1305
- @d.join_table(:left_outer, ds, :item_id => :id).sql.should ==
1306
- 'SELECT * FROM "items" LEFT OUTER JOIN (SELECT * FROM categories WHERE (active = \'t\')) AS "t1" ON ("t1"."item_id" = "items"."id")'
1307
- end
1308
-
1309
- specify "should support joining datasets and aliasing the join" do
1310
- ds = Sequel::Dataset.new(nil).from(:categories)
1311
-
1312
- @d.join_table(:left_outer, ds, {:ds__item_id => :id}, :ds).sql.should ==
1313
- 'SELECT * FROM "items" LEFT OUTER JOIN (SELECT * FROM categories) AS "ds" ON ("ds"."item_id" = "items"."id")'
1314
- end
1315
-
1316
- specify "should support joining multiple datasets" do
1317
- ds = Sequel::Dataset.new(nil).from(:categories)
1318
- ds2 = Sequel::Dataset.new(nil).from(:nodes).select(:name)
1319
- ds3 = Sequel::Dataset.new(nil).from(:attributes).filter("name = 'blah'")
1320
-
1321
- @d.join_table(:left_outer, ds, :item_id => :id).join_table(:inner, ds2, :node_id=>:id).join_table(:right_outer, ds3, :attribute_id=>:id).sql.should ==
1322
- 'SELECT * FROM "items" LEFT OUTER JOIN (SELECT * FROM categories) AS "t1" ON ("t1"."item_id" = "items"."id") ' \
1323
- 'INNER JOIN (SELECT name FROM nodes) AS "t2" ON ("t2"."node_id" = "t1"."id") ' \
1324
- 'RIGHT OUTER JOIN (SELECT * FROM attributes WHERE (name = \'blah\')) AS "t3" ON ("t3"."attribute_id" = "t2"."id")'
1325
- end
1326
-
1327
- specify "should support joining objects that respond to :table_name" do
1328
- ds = Object.new
1329
- def ds.table_name; :categories end
1330
-
1331
- @d.join(ds, :item_id => :id).sql.should ==
1332
- 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."item_id" = "items"."id")'
1333
- end
1334
-
1335
- specify "should support using a SQL String as the join condition" do
1336
- @d.join(:categories, %{c.item_id = items.id}, :c).sql.should ==
1337
- 'SELECT * FROM "items" INNER JOIN "categories" AS "c" ON (c.item_id = items.id)'
1338
- end
1339
-
1340
- specify "should support using a boolean column as the join condition" do
1341
- @d.join(:categories, :active).sql.should ==
1342
- 'SELECT * FROM "items" INNER JOIN "categories" ON "active"'
1343
- end
1344
-
1345
- specify "should support using an expression as the join condition" do
1346
- @d.join(:categories, :number > 10).sql.should ==
1347
- 'SELECT * FROM "items" INNER JOIN "categories" ON ("number" > 10)'
1348
- end
1349
-
1350
- specify "should support natural and cross joins using nil" do
1351
- @d.join_table(:natural, :categories).sql.should ==
1352
- 'SELECT * FROM "items" NATURAL JOIN "categories"'
1353
- @d.join_table(:cross, :categories, nil).sql.should ==
1354
- 'SELECT * FROM "items" CROSS JOIN "categories"'
1355
- @d.join_table(:natural, :categories, nil, :c).sql.should ==
1356
- 'SELECT * FROM "items" NATURAL JOIN "categories" AS "c"'
1357
- end
1358
-
1359
- specify "should support joins with a USING clause if an array of symbols is used" do
1360
- @d.join(:categories, [:id]).sql.should ==
1361
- 'SELECT * FROM "items" INNER JOIN "categories" USING ("id")'
1362
- @d.join(:categories, [:id1, :id2]).sql.should ==
1363
- 'SELECT * FROM "items" INNER JOIN "categories" USING ("id1", "id2")'
1364
- end
1365
-
1366
- specify "should raise an error if using an array of symbols with a block" do
1367
- proc{@d.join(:categories, [:id]){|j,lj,js|}}.should raise_error(Sequel::Error)
1368
- end
1369
-
1370
- specify "should support using a block that receieves the join table/alias, last join table/alias, and array of previous joins" do
1371
- @d.join(:categories) do |join_alias, last_join_alias, joins|
1372
- join_alias.should == :categories
1373
- last_join_alias.should == :items
1374
- joins.should == []
1375
- end
1376
-
1377
- @d.from(:items=>:i).join(:categories, nil, :c) do |join_alias, last_join_alias, joins|
1378
- join_alias.should == :c
1379
- last_join_alias.should == :i
1380
- joins.should == []
1381
- end
1382
-
1383
- @d.from(:items___i).join(:categories, nil, :c) do |join_alias, last_join_alias, joins|
1384
- join_alias.should == :c
1385
- last_join_alias.should == :i
1386
- joins.should == []
1387
- end
1388
-
1389
- @d.join(:blah).join(:categories, nil, :c) do |join_alias, last_join_alias, joins|
1390
- join_alias.should == :c
1391
- last_join_alias.should == :blah
1392
- joins.should be_a_kind_of(Array)
1393
- joins.length.should == 1
1394
- joins.first.should be_a_kind_of(Sequel::SQL::JoinClause)
1395
- joins.first.join_type.should == :inner
1396
- end
1397
-
1398
- @d.join_table(:natural, :blah, nil, :b).join(:categories, nil, :c) do |join_alias, last_join_alias, joins|
1399
- join_alias.should == :c
1400
- last_join_alias.should == :b
1401
- joins.should be_a_kind_of(Array)
1402
- joins.length.should == 1
1403
- joins.first.should be_a_kind_of(Sequel::SQL::JoinClause)
1404
- joins.first.join_type.should == :natural
1405
- end
1406
-
1407
- @d.join(:blah).join(:categories).join(:blah2) do |join_alias, last_join_alias, joins|
1408
- join_alias.should == :blah2
1409
- last_join_alias.should == :categories
1410
- joins.should be_a_kind_of(Array)
1411
- joins.length.should == 2
1412
- joins.first.should be_a_kind_of(Sequel::SQL::JoinClause)
1413
- joins.first.table.should == :blah
1414
- joins.last.should be_a_kind_of(Sequel::SQL::JoinClause)
1415
- joins.last.table.should == :categories
1416
- end
1417
- end
1418
-
1419
- specify "should use the block result as the only condition if no condition is given" do
1420
- @d.join(:categories){|j,lj,js| {:b.qualify(j)=>:c.qualify(lj)}}.sql.should ==
1421
- 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."b" = "items"."c")'
1422
- @d.join(:categories){|j,lj,js| :b.qualify(j) > :c.qualify(lj)}.sql.should ==
1423
- 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."b" > "items"."c")'
1424
- end
1425
-
1426
- specify "should combine the block conditions and argument conditions if both given" do
1427
- @d.join(:categories, :a=>:d){|j,lj,js| {:b.qualify(j)=>:c.qualify(lj)}}.sql.should ==
1428
- 'SELECT * FROM "items" INNER JOIN "categories" ON (("categories"."a" = "items"."d") AND ("categories"."b" = "items"."c"))'
1429
- @d.join(:categories, :a=>:d){|j,lj,js| :b.qualify(j) > :c.qualify(lj)}.sql.should ==
1430
- 'SELECT * FROM "items" INNER JOIN "categories" ON (("categories"."a" = "items"."d") AND ("categories"."b" > "items"."c"))'
1431
- end
1432
- end
1433
-
1434
- context "Dataset#[]=" do
1435
- setup do
1436
- c = Class.new(Sequel::Dataset) do
1437
- def last_sql
1438
- @@last_sql
1439
- end
1440
-
1441
- def update(*args)
1442
- @@last_sql = update_sql(*args)
1443
- end
1444
- end
1445
-
1446
- @d = c.new(nil).from(:items)
1447
- end
1448
-
1449
- specify "should perform an update on the specified filter" do
1450
- @d[:a => 1] = {:x => 3}
1451
- @d.last_sql.should == 'UPDATE items SET x = 3 WHERE (a = 1)'
1452
- end
1453
- end
1454
-
1455
- context "Dataset#set" do
1456
- setup do
1457
- c = Class.new(Sequel::Dataset) do
1458
- def last_sql
1459
- @@last_sql
1460
- end
1461
-
1462
- def update(*args, &block)
1463
- @@last_sql = update_sql(*args, &block)
1464
- end
1465
- end
1466
-
1467
- @d = c.new(nil).from(:items)
1468
- end
1469
-
1470
- specify "should act as alias to #update" do
1471
- @d.set({:x => 3})
1472
- @d.last_sql.should == 'UPDATE items SET x = 3'
1473
- end
1474
- end
1475
-
1476
-
1477
- context "Dataset#insert_multiple" do
1478
- setup do
1479
- c = Class.new(Sequel::Dataset) do
1480
- attr_reader :inserts
1481
- def insert(arg)
1482
- @inserts ||= []
1483
- @inserts << arg
1484
- end
1485
- end
1486
-
1487
- @d = c.new(nil)
1488
- end
1489
-
1490
- specify "should insert all items in the supplied array" do
1491
- @d.insert_multiple [:aa, 5, 3, {1 => 2}]
1492
- @d.inserts.should == [:aa, 5, 3, {1 => 2}]
1493
- end
1494
-
1495
- specify "should pass array items through the supplied block if given" do
1496
- a = ["inevitable", "hello", "the ticking clock"]
1497
- @d.insert_multiple(a) {|i| i.gsub('l', 'r')}
1498
- @d.inserts.should == ["inevitabre", "herro", "the ticking crock"]
1499
- end
1500
- end
1501
-
1502
- context "Dataset aggregate methods" do
1503
- setup do
1504
- c = Class.new(Sequel::Dataset) do
1505
- def fetch_rows(sql)
1506
- yield({1 => sql})
1507
- end
1508
- end
1509
- @d = c.new(nil).from(:test)
1510
- end
1511
-
1512
- specify "should include min" do
1513
- @d.min(:a).should == 'SELECT min(a) FROM test LIMIT 1'
1514
- end
1515
-
1516
- specify "should include max" do
1517
- @d.max(:b).should == 'SELECT max(b) FROM test LIMIT 1'
1518
- end
1519
-
1520
- specify "should include sum" do
1521
- @d.sum(:c).should == 'SELECT sum(c) FROM test LIMIT 1'
1522
- end
1523
-
1524
- specify "should include avg" do
1525
- @d.avg(:d).should == 'SELECT avg(d) FROM test LIMIT 1'
1526
- end
1527
-
1528
- specify "should accept qualified columns" do
1529
- @d.avg(:test__bc).should == 'SELECT avg(test.bc) FROM test LIMIT 1'
1530
- end
1531
- end
1532
-
1533
- context "Dataset#range" do
1534
- setup do
1535
- c = Class.new(Sequel::Dataset) do
1536
- @@sql = nil
1537
-
1538
- def last_sql; @@sql; end
1539
-
1540
- def fetch_rows(sql)
1541
- @@sql = sql
1542
- yield(:v1 => 1, :v2 => 10)
1543
- end
1544
- end
1545
- @d = c.new(nil).from(:test)
1546
- end
1547
-
1548
- specify "should generate a correct SQL statement" do
1549
- @d.range(:stamp)
1550
- @d.last_sql.should == "SELECT min(stamp) AS v1, max(stamp) AS v2 FROM test LIMIT 1"
1551
-
1552
- @d.filter(:price > 100).range(:stamp)
1553
- @d.last_sql.should == "SELECT min(stamp) AS v1, max(stamp) AS v2 FROM test WHERE (price > 100) LIMIT 1"
1554
- end
1555
-
1556
- specify "should return a range object" do
1557
- @d.range(:tryme).should == (1..10)
1558
- end
1559
- end
1560
-
1561
- context "Dataset#interval" do
1562
- setup do
1563
- c = Class.new(Sequel::Dataset) do
1564
- @@sql = nil
1565
-
1566
- def last_sql; @@sql; end
1567
-
1568
- def fetch_rows(sql)
1569
- @@sql = sql
1570
- yield(:v => 1234)
1571
- end
1572
- end
1573
- @d = c.new(nil).from(:test)
1574
- end
1575
-
1576
- specify "should generate a correct SQL statement" do
1577
- @d.interval(:stamp)
1578
- @d.last_sql.should == "SELECT (max(stamp) - min(stamp)) FROM test LIMIT 1"
1579
-
1580
- @d.filter(:price > 100).interval(:stamp)
1581
- @d.last_sql.should == "SELECT (max(stamp) - min(stamp)) FROM test WHERE (price > 100) LIMIT 1"
1582
- end
1583
-
1584
- specify "should return an integer" do
1585
- @d.interval(:tryme).should == 1234
1586
- end
1587
- end
1588
-
1589
- context "Dataset #first and #last" do
1590
- setup do
1591
- @c = Class.new(Sequel::Dataset) do
1592
- def each(opts = nil, &block)
1593
- s = select_sql(opts)
1594
- x = [:a,1,:b,2,s]
1595
- i = /LIMIT (\d+)/.match(s)[1].to_i.times{yield x}
1596
- end
1597
- end
1598
- @d = @c.new(nil).from(:test)
1599
- end
1600
-
1601
- specify "should return a single record if no argument is given" do
1602
- @d.order(:a).first.should == [:a,1,:b,2, 'SELECT * FROM test ORDER BY a LIMIT 1']
1603
- @d.order(:a).last.should == [:a,1,:b,2, 'SELECT * FROM test ORDER BY a DESC LIMIT 1']
1604
- end
1605
-
1606
- specify "should return the first/last matching record if argument is not an Integer" do
1607
- @d.order(:a).first(:z => 26).should == [:a,1,:b,2, 'SELECT * FROM test WHERE (z = 26) ORDER BY a LIMIT 1']
1608
- @d.order(:a).first('z = ?', 15).should == [:a,1,:b,2, 'SELECT * FROM test WHERE (z = 15) ORDER BY a LIMIT 1']
1609
- @d.order(:a).last(:z => 26).should == [:a,1,:b,2, 'SELECT * FROM test WHERE (z = 26) ORDER BY a DESC LIMIT 1']
1610
- @d.order(:a).last('z = ?', 15).should == [:a,1,:b,2, 'SELECT * FROM test WHERE (z = 15) ORDER BY a DESC LIMIT 1']
1611
- end
1612
-
1613
- specify "should set the limit and return an array of records if the given number is > 1" do
1614
- i = rand(10) + 10
1615
- r = @d.order(:a).first(i).should == [[:a,1,:b,2, "SELECT * FROM test ORDER BY a LIMIT #{i}"]] * i
1616
- i = rand(10) + 10
1617
- r = @d.order(:a).last(i).should == [[:a,1,:b,2, "SELECT * FROM test ORDER BY a DESC LIMIT #{i}"]] * i
1618
- end
1619
-
1620
- specify "should return the first matching record if a block is given without an argument" do
1621
- @d.first{:z > 26}.should == [:a,1,:b,2, 'SELECT * FROM test WHERE (z > 26) LIMIT 1']
1622
- @d.order(:name).last{:z > 26}.should == [:a,1,:b,2, 'SELECT * FROM test WHERE (z > 26) ORDER BY name DESC LIMIT 1']
1623
- end
1624
-
1625
- specify "should combine block and standard argument filters if argument is not an Integer" do
1626
- @d.first(:y=>25){:z > 26}.should == [:a,1,:b,2, 'SELECT * FROM test WHERE ((z > 26) AND (y = 25)) LIMIT 1']
1627
- @d.order(:name).last('y = ?', 16){:z > 26}.should == [:a,1,:b,2, 'SELECT * FROM test WHERE ((z > 26) AND (y = 16)) ORDER BY name DESC LIMIT 1']
1628
- end
1629
-
1630
- specify "should filter and return an array of records if an Integer argument is provided and a block is given" do
1631
- i = rand(10) + 10
1632
- r = @d.order(:a).first(i){:z > 26}.should == [[:a,1,:b,2, "SELECT * FROM test WHERE (z > 26) ORDER BY a LIMIT #{i}"]] * i
1633
- i = rand(10) + 10
1634
- r = @d.order(:a).last(i){:z > 26}.should == [[:a,1,:b,2, "SELECT * FROM test WHERE (z > 26) ORDER BY a DESC LIMIT #{i}"]] * i
1635
- end
1636
-
1637
- specify "#last should raise if no order is given" do
1638
- proc {@d.last}.should raise_error(Sequel::Error)
1639
- proc {@d.last(2)}.should raise_error(Sequel::Error)
1640
- proc {@d.order(:a).last}.should_not raise_error
1641
- proc {@d.order(:a).last(2)}.should_not raise_error
1642
- end
1643
-
1644
- specify "#last should invert the order" do
1645
- @d.order(:a).last.pop.should == 'SELECT * FROM test ORDER BY a DESC LIMIT 1'
1646
- @d.order(:b.desc).last.pop.should == 'SELECT * FROM test ORDER BY b ASC LIMIT 1'
1647
- @d.order(:c, :d).last.pop.should == 'SELECT * FROM test ORDER BY c DESC, d DESC LIMIT 1'
1648
- @d.order(:e.desc, :f).last.pop.should == 'SELECT * FROM test ORDER BY e ASC, f DESC LIMIT 1'
1649
- end
1650
- end
1651
-
1652
- context "Dataset set operations" do
1653
- setup do
1654
- @a = Sequel::Dataset.new(nil).from(:a).filter(:z => 1)
1655
- @b = Sequel::Dataset.new(nil).from(:b).filter(:z => 2)
1656
- end
1657
-
1658
- specify "should support UNION and UNION ALL" do
1659
- @a.union(@b).sql.should == \
1660
- "SELECT * FROM a WHERE (z = 1) UNION SELECT * FROM b WHERE (z = 2)"
1661
- @b.union(@a, true).sql.should == \
1662
- "SELECT * FROM b WHERE (z = 2) UNION ALL SELECT * FROM a WHERE (z = 1)"
1663
- end
1664
-
1665
- specify "should support INTERSECT and INTERSECT ALL" do
1666
- @a.intersect(@b).sql.should == \
1667
- "SELECT * FROM a WHERE (z = 1) INTERSECT SELECT * FROM b WHERE (z = 2)"
1668
- @b.intersect(@a, true).sql.should == \
1669
- "SELECT * FROM b WHERE (z = 2) INTERSECT ALL SELECT * FROM a WHERE (z = 1)"
1670
- end
1671
-
1672
- specify "should support EXCEPT and EXCEPT ALL" do
1673
- @a.except(@b).sql.should == \
1674
- "SELECT * FROM a WHERE (z = 1) EXCEPT SELECT * FROM b WHERE (z = 2)"
1675
- @b.except(@a, true).sql.should == \
1676
- "SELECT * FROM b WHERE (z = 2) EXCEPT ALL SELECT * FROM a WHERE (z = 1)"
1677
- end
1678
- end
1679
-
1680
- context "Dataset#[]" do
1681
- setup do
1682
- @c = Class.new(Sequel::Dataset) do
1683
- @@last_dataset = nil
1684
-
1685
- def self.last_dataset
1686
- @@last_dataset
1687
- end
1688
-
1689
- def single_record(opts = nil)
1690
- @@last_dataset = opts ? clone(opts) : self
1691
- {1 => 2, 3 => 4}
1692
- end
1693
- end
1694
- @d = @c.new(nil).from(:test)
1695
- end
1696
-
1697
- specify "should return a single record filtered according to the given conditions" do
1698
- @d[:name => 'didi'].should == {1 => 2, 3 => 4}
1699
- @c.last_dataset.literal(@c.last_dataset.opts[:where]).should == "(name = 'didi')"
1700
-
1701
- @d[:id => 5..45].should == {1 => 2, 3 => 4}
1702
- @c.last_dataset.literal(@c.last_dataset.opts[:where]).should == "((id >= 5) AND (id <= 45))"
1703
- end
1704
- end
1705
-
1706
- context "Dataset#single_record" do
1707
- setup do
1708
- @c = Class.new(Sequel::Dataset) do
1709
- def fetch_rows(sql)
1710
- yield sql
1711
- end
1712
- end
1713
- @cc = Class.new(@c) do
1714
- def fetch_rows(sql); end
1715
- end
1716
-
1717
- @d = @c.new(nil).from(:test)
1718
- @e = @cc.new(nil).from(:test)
1719
- end
1720
-
1721
- specify "should call each with a limit of 1 and return the record" do
1722
- @d.single_record.should == 'SELECT * FROM test LIMIT 1'
1723
- end
1724
-
1725
- specify "should pass opts to each" do
1726
- @d.single_record(:order => [:name]).should == 'SELECT * FROM test ORDER BY name LIMIT 1'
1727
- end
1728
-
1729
- specify "should override the limit if passed as an option" do
1730
- @d.single_record(:limit => 3).should == 'SELECT * FROM test LIMIT 1'
1731
- end
1732
-
1733
- specify "should return nil if no record is present" do
1734
- @e.single_record.should be_nil
1735
- end
1736
- end
1737
-
1738
- context "Dataset#single_value" do
1739
- setup do
1740
- @c = Class.new(Sequel::Dataset) do
1741
- def fetch_rows(sql)
1742
- yield({1 => sql})
1743
- end
1744
- end
1745
- @cc = Class.new(@c) do
1746
- def fetch_rows(sql); end
1747
- end
1748
-
1749
- @d = @c.new(nil).from(:test)
1750
- @e = @cc.new(nil).from(:test)
1751
- end
1752
-
1753
- specify "should call each and return the first value of the first record" do
1754
- @d.single_value.should == 'SELECT * FROM test LIMIT 1'
1755
- end
1756
-
1757
- specify "should pass opts to each" do
1758
- @d.single_value(:from => [:blah]).should == 'SELECT * FROM blah LIMIT 1'
1759
- end
1760
-
1761
- specify "should return nil if no records" do
1762
- @e.single_value.should be_nil
1763
- end
1764
- end
1765
-
1766
- context "Dataset#get" do
1767
- setup do
1768
- @c = Class.new(Sequel::Dataset) do
1769
- attr_reader :last_sql
1770
-
1771
- def fetch_rows(sql)
1772
- @last_sql = sql
1773
- yield(:name => sql)
1774
- end
1775
- end
1776
-
1777
- @d = @c.new(nil).from(:test)
1778
- end
1779
-
1780
- specify "should select the specified column and fetch its value" do
1781
- @d.get(:name).should == "SELECT name FROM test LIMIT 1"
1782
- @d.get(:abc).should == "SELECT abc FROM test LIMIT 1" # the first available value is returned always
1783
- end
1784
-
1785
- specify "should work with filters" do
1786
- @d.filter(:id => 1).get(:name).should == "SELECT name FROM test WHERE (id = 1) LIMIT 1"
1787
- end
1788
-
1789
- specify "should work with aliased fields" do
1790
- @d.get(:x__b.as(:name)).should == "SELECT x.b AS name FROM test LIMIT 1"
1791
- end
1792
- end
1793
-
1794
- context "Dataset#set_row_proc" do
1795
- setup do
1796
- @c = Class.new(Sequel::Dataset) do
1797
- def fetch_rows(sql, &block)
1798
- # yield a hash with kind as the 1 bit of a number
1799
- (1..10).each {|i| block.call({:kind => i[0]})}
1800
- end
1801
- end
1802
- @dataset = @c.new(nil).from(:items)
1803
- end
1804
-
1805
- specify "should cause dataset to pass all rows through the filter" do
1806
- @dataset.row_proc = proc{|h| h[:der] = h[:kind] + 2; h}
1807
-
1808
- rows = @dataset.all
1809
- rows.size.should == 10
1810
-
1811
- rows.each {|r| r[:der].should == (r[:kind] + 2)}
1812
- end
1813
-
1814
- specify "should be copied over when dataset is cloned" do
1815
- @dataset.row_proc = proc{|h| h[:der] = h[:kind] + 2; h}
1816
-
1817
- @dataset.filter(:a => 1).first.should == {:kind => 1, :der => 3}
1818
- end
1819
- end
1820
-
1821
- context "Dataset#set_model" do
1822
- setup do
1823
- @c = Class.new(Sequel::Dataset) do
1824
- def fetch_rows(sql, &block)
1825
- # yield a hash with kind as the 1 bit of a number
1826
- (1..10).each {|i| block.call({:kind => i[0]})}
1827
- end
1828
- end
1829
- @dataset = @c.new(nil).from(:items)
1830
- @m = Class.new do
1831
- attr_accessor :c, :args
1832
- def initialize(c, *args); @c = c; @args = args; end
1833
- def ==(o); (@c == o.c) && (@args = o.args); end
1834
- end
1835
- end
1836
-
1837
- specify "should clear the models hash and restore the stock #each if nil is specified" do
1838
- @dataset.set_model(@m)
1839
- @dataset.set_model(nil)
1840
- @dataset.first.should == {:kind => 1}
1841
- @dataset.model_classes.should be_nil
1842
- end
1843
-
1844
- specify "should clear the models hash and restore the stock #each if nothing is specified" do
1845
- @dataset.set_model(@m)
1846
- @dataset.set_model(nil)
1847
- @dataset.first.should == {:kind => 1}
1848
- @dataset.model_classes.should be_nil
1849
- end
1850
-
1851
- specify "should alter #each to provide model instances" do
1852
- @dataset.first.should == {:kind => 1}
1853
- @dataset.set_model(@m)
1854
- @dataset.first.should == @m.new({:kind => 1})
1855
- end
1856
-
1857
- specify "should set opts[:naked] to nil" do
1858
- @dataset.opts[:naked] = true
1859
- @dataset.set_model(@m)
1860
- @dataset.opts[:naked].should be_nil
1861
- end
1862
-
1863
- specify "should send additional arguments to the models' initialize method" do
1864
- @dataset.set_model(@m, 7, 6, 5)
1865
- @dataset.first.should == @m.new({:kind => 1}, 7, 6, 5)
1866
- end
1867
-
1868
- specify "should provide support for polymorphic model instantiation" do
1869
- @m1 = Class.new(@m)
1870
- @m2 = Class.new(@m)
1871
- @dataset.set_model(:kind, 0 => @m1, 1 => @m2)
1872
- @dataset.opts[:polymorphic_key].should == :kind
1873
- all = @dataset.all
1874
- all[0].class.should == @m2
1875
- all[1].class.should == @m1
1876
- all[2].class.should == @m2
1877
- all[3].class.should == @m1
1878
- #...
1879
-
1880
- # denude model
1881
- @dataset.set_model(nil)
1882
- @dataset.first.should == {:kind => 1}
1883
- end
1884
-
1885
- specify "should send additional arguments for polymorphic models as well" do
1886
- @m1 = Class.new(@m)
1887
- @m2 = Class.new(@m)
1888
- @dataset.set_model(:kind, {0 => @m1, 1 => @m2}, :hey => :wow)
1889
- all = @dataset.all
1890
- all[0].class.should == @m2; all[0].args.should == [{:hey => :wow}]
1891
- all[1].class.should == @m1; all[1].args.should == [{:hey => :wow}]
1892
- all[2].class.should == @m2; all[2].args.should == [{:hey => :wow}]
1893
- all[3].class.should == @m1; all[3].args.should == [{:hey => :wow}]
1894
- end
1895
-
1896
- specify "should raise for invalid parameters" do
1897
- proc {@dataset.set_model('kind')}.should raise_error(ArgumentError)
1898
- proc {@dataset.set_model(0)}.should raise_error(ArgumentError)
1899
- proc {@dataset.set_model(:kind)}.should raise_error(ArgumentError) # no hash given
1900
- end
1901
- end
1902
-
1903
- context "Dataset#model_classes" do
1904
- setup do
1905
- @c = Class.new(Sequel::Dataset) do
1906
- # # We don't need that for now
1907
- # def fetch_rows(sql, &block)
1908
- # (1..10).each(&block)
1909
- # end
1910
- end
1911
- @dataset = @c.new(nil).from(:items)
1912
- @m = Class.new do
1913
- attr_accessor :c
1914
- def initialize(c); @c = c; end
1915
- def ==(o); @c == o.c; end
1916
- end
1917
- end
1918
-
1919
- specify "should return nil for a naked dataset" do
1920
- @dataset.model_classes.should == nil
1921
- end
1922
-
1923
- specify "should return a {nil => model_class} hash for a model dataset" do
1924
- @dataset.set_model(@m)
1925
- @dataset.model_classes.should == {nil => @m}
1926
- end
1927
-
1928
- specify "should return the polymorphic hash for a polymorphic model dataset" do
1929
- @m1 = Class.new(@m)
1930
- @m2 = Class.new(@m)
1931
- @dataset.set_model(:key, 0 => @m1, 1 => @m2)
1932
- @dataset.model_classes.should == {0 => @m1, 1 => @m2}
1933
- end
1934
- end
1935
-
1936
- context "Dataset#polymorphic_key" do
1937
- setup do
1938
- @c = Class.new(Sequel::Dataset) do
1939
- # # We don't need this for now
1940
- # def fetch_rows(sql, &block)
1941
- # (1..10).each(&block)
1942
- # end
1943
- end
1944
- @dataset = @c.new(nil).from(:items)
1945
- @m = Class.new do
1946
- attr_accessor :c
1947
- def initialize(c); @c = c; end
1948
- def ==(o); @c == o.c; end
1949
- end
1950
- end
1951
-
1952
- specify "should return nil for a naked dataset" do
1953
- @dataset.polymorphic_key.should be_nil
1954
- end
1955
-
1956
- specify "should return the polymorphic key" do
1957
- @dataset.set_model(:id, nil => @m)
1958
- @dataset.polymorphic_key.should == :id
1959
- end
1960
- end
1961
-
1962
- context "A model dataset" do
1963
- setup do
1964
- @c = Class.new(Sequel::Dataset) do
1965
- def fetch_rows(sql, &block)
1966
- (1..10).each(&block)
1967
- end
1968
- end
1969
- @dataset = @c.new(nil).from(:items)
1970
- @m = Class.new do
1971
- attr_accessor :c
1972
- def initialize(c); @c = c; end
1973
- def ==(o); @c == o.c; end
1974
- end
1975
- @dataset.set_model(@m)
1976
- end
1977
-
1978
- specify "should supply naked records if the naked option is specified" do
1979
- @dataset.each {|r| r.class.should == @m}
1980
- @dataset.each(:naked => true) {|r| r.class.should == Fixnum}
1981
- end
1982
- end
1983
-
1984
- context "A polymorphic model dataset" do
1985
- setup do
1986
- @c = Class.new(Sequel::Dataset) do
1987
- def fetch_rows(sql, &block)
1988
- (1..10).each {|i| block.call(:bit => i[0])}
1989
- end
1990
- end
1991
- @dataset = @c.new(nil).from(:items)
1992
- @m = Class.new do
1993
- attr_accessor :c
1994
- def initialize(c); @c = c; end
1995
- def ==(o); @c == o.c; end
1996
- end
1997
- end
1998
-
1999
- specify "should use a nil key in the polymorphic hash to specify the default model class" do
2000
- @m2 = Class.new(@m)
2001
- @dataset.set_model(:bit, nil => @m, 1 => @m2)
2002
- all = @dataset.all
2003
- all[0].class.should == @m2
2004
- all[1].class.should == @m
2005
- all[2].class.should == @m2
2006
- all[3].class.should == @m
2007
- #...
2008
- end
2009
-
2010
- specify "should raise Sequel::Error if no suitable class is found in the polymorphic hash" do
2011
- @m2 = Class.new(@m)
2012
- @dataset.set_model(:bit, 1 => @m2)
2013
- proc {@dataset.all}.should raise_error(Sequel::Error)
2014
- end
2015
-
2016
- specify "should supply naked records if the naked option is specified" do
2017
- @dataset.set_model(:bit, nil => @m)
2018
- @dataset.each(:naked => true) {|r| r.class.should == Hash}
2019
- end
2020
- end
2021
-
2022
- context "A dataset with associated model class(es)" do
2023
- setup do
2024
- @c = Class.new(Sequel::Dataset) do
2025
- def fetch_rows(sql, &block)
2026
- block.call({:x => 1, :y => 2})
2027
- end
2028
- end
2029
- @dataset = @c.new(nil).from(:items)
2030
- @m1 = Class.new do
2031
- attr_accessor :v
2032
- def initialize(v); @v = v; end
2033
- end
2034
- @m2 = Class.new do
2035
- attr_accessor :v, :vv
2036
- def initialize(v = nil); @v = v; end
2037
- def self.load(v); o = new(nil); o.vv = v; o; end
2038
- end
2039
- @m3 = Class.new(@m2)
2040
- end
2041
-
2042
- specify "should instantiate an instance by passing the record hash as argument" do
2043
- @dataset.set_model(@m1)
2044
- o = @dataset.first
2045
- o.class.should == @m1
2046
- o.v.should == {:x => 1, :y => 2}
2047
- end
2048
-
2049
- specify "should use the .load constructor if available" do
2050
- @dataset.set_model(@m2)
2051
- o = @dataset.first
2052
- o.class.should == @m2
2053
- o.v.should == nil
2054
- o.vv.should == {:x => 1, :y => 2}
2055
- end
2056
-
2057
- specify "should use the .load constructor also for polymorphic datasets" do
2058
- @dataset.set_model(:y, 1 => @m2, 2 => @m3)
2059
- o = @dataset.first
2060
- o.class.should == @m3
2061
- o.v.should == nil
2062
- o.vv.should == {:x => 1, :y => 2}
2063
- end
2064
- end
2065
-
2066
- context "Dataset#<<" do
2067
- setup do
2068
- @d = Sequel::Dataset.new(nil)
2069
- @d.meta_def(:insert) do |*args|
2070
- 1234567890
2071
- end
2072
- end
2073
-
2074
- specify "should call #insert" do
2075
- (@d << {:name => 1}).should == 1234567890
2076
- end
2077
- end
2078
-
2079
- context "A paginated dataset" do
2080
- setup do
2081
- @d = Sequel::Dataset.new(nil)
2082
- @d.meta_def(:count) {153}
2083
-
2084
- @paginated = @d.paginate(1, 20)
2085
- end
2086
-
2087
- specify "should raise an error if the dataset already has a limit" do
2088
- proc{@d.limit(10).paginate(1,10)}.should raise_error(Sequel::Error)
2089
- proc{@paginated.paginate(2,20)}.should raise_error(Sequel::Error)
2090
- end
2091
-
2092
- specify "should set the limit and offset options correctly" do
2093
- @paginated.opts[:limit].should == 20
2094
- @paginated.opts[:offset].should == 0
2095
- end
2096
-
2097
- specify "should set the page count correctly" do
2098
- @paginated.page_count.should == 8
2099
- @d.paginate(1, 50).page_count.should == 4
2100
- end
2101
-
2102
- specify "should set the current page number correctly" do
2103
- @paginated.current_page.should == 1
2104
- @d.paginate(3, 50).current_page.should == 3
2105
- end
2106
-
2107
- specify "should return the next page number or nil if we're on the last" do
2108
- @paginated.next_page.should == 2
2109
- @d.paginate(4, 50).next_page.should be_nil
2110
- end
2111
-
2112
- specify "should return the previous page number or nil if we're on the last" do
2113
- @paginated.prev_page.should be_nil
2114
- @d.paginate(4, 50).prev_page.should == 3
2115
- end
2116
-
2117
- specify "should return the page range" do
2118
- @paginated.page_range.should == (1..8)
2119
- @d.paginate(4, 50).page_range.should == (1..4)
2120
- end
2121
-
2122
- specify "should return the record range for the current page" do
2123
- @paginated.current_page_record_range.should == (1..20)
2124
- @d.paginate(4, 50).current_page_record_range.should == (151..153)
2125
- @d.paginate(5, 50).current_page_record_range.should == (0..0)
2126
- end
2127
-
2128
- specify "should return the record count for the current page" do
2129
- @paginated.current_page_record_count.should == 20
2130
- @d.paginate(3, 50).current_page_record_count.should == 50
2131
- @d.paginate(4, 50).current_page_record_count.should == 3
2132
- @d.paginate(5, 50).current_page_record_count.should == 0
2133
- end
2134
-
2135
- specify "should know if current page is last page" do
2136
- @paginated.last_page?.should be_false
2137
- @d.paginate(2, 20).last_page?.should be_false
2138
- @d.paginate(5, 30).last_page?.should be_false
2139
- @d.paginate(6, 30).last_page?.should be_true
2140
- end
2141
-
2142
- specify "should know if current page is first page" do
2143
- @paginated.first_page?.should be_true
2144
- @d.paginate(1, 20).first_page?.should be_true
2145
- @d.paginate(2, 20).first_page?.should be_false
2146
- end
2147
-
2148
- specify "should work with fixed sql" do
2149
- ds = @d.clone(:sql => 'select * from blah')
2150
- ds.meta_def(:count) {150}
2151
- ds.paginate(2, 50).sql.should == 'SELECT * FROM (select * from blah) t1 LIMIT 50 OFFSET 50'
2152
- end
2153
- end
2154
-
2155
- context "Dataset#each_page" do
2156
- setup do
2157
- @d = Sequel::Dataset.new(nil).from(:items)
2158
- @d.meta_def(:count) {153}
2159
- end
2160
-
2161
- specify "should raise an error if the dataset already has a limit" do
2162
- proc{@d.limit(10).each_page(10){}}.should raise_error(Sequel::Error)
2163
- end
2164
-
2165
- specify "should iterate over each page in the resultset as a paginated dataset" do
2166
- a = []
2167
- @d.each_page(50) {|p| a << p}
2168
- a.map {|p| p.sql}.should == [
2169
- 'SELECT * FROM items LIMIT 50 OFFSET 0',
2170
- 'SELECT * FROM items LIMIT 50 OFFSET 50',
2171
- 'SELECT * FROM items LIMIT 50 OFFSET 100',
2172
- 'SELECT * FROM items LIMIT 50 OFFSET 150',
2173
- ]
2174
- end
2175
- end
2176
-
2177
- context "Dataset#columns" do
2178
- setup do
2179
- @dataset = DummyDataset.new(nil).from(:items)
2180
- @dataset.meta_def(:columns=) {|c| @columns = c}
2181
- i = 'a'
2182
- @dataset.meta_def(:each) {|o| @columns = select_sql(o||@opts) + i; i = i.next}
2183
- end
2184
-
2185
- specify "should return the value of @columns if @columns is not nil" do
2186
- @dataset.columns = [:a, :b, :c]
2187
- @dataset.columns.should == [:a, :b, :c]
2188
- end
2189
-
2190
- specify "should attempt to get a single record and return @columns if @columns is nil" do
2191
- @dataset.columns = nil
2192
- @dataset.columns.should == 'SELECT * FROM items LIMIT 1a'
2193
- @dataset.opts[:from] = [:nana]
2194
- @dataset.columns.should == 'SELECT * FROM items LIMIT 1a'
2195
- end
2196
-
2197
- specify "should ignore any filters, orders, or DISTINCT clauses" do
2198
- @dataset.filter!(:b=>100).order!(:b).distinct!(:b)
2199
- @dataset.columns = nil
2200
- @dataset.columns.should == 'SELECT * FROM items LIMIT 1a'
2201
- end
2202
- end
2203
-
2204
- context "Dataset#columns!" do
2205
- setup do
2206
- @dataset = DummyDataset.new(nil).from(:items)
2207
- i = 'a'
2208
- @dataset.meta_def(:each) {|o| @columns = select_sql(o||@opts) + i; i = i.next}
2209
- end
2210
-
2211
- specify "should always attempt to get a record and return @columns" do
2212
- @dataset.columns!.should == 'SELECT * FROM items LIMIT 1a'
2213
- @dataset.columns!.should == 'SELECT * FROM items LIMIT 1b'
2214
- @dataset.opts[:from] = [:nana]
2215
- @dataset.columns!.should == 'SELECT * FROM nana LIMIT 1c'
2216
- end
2217
- end
2218
-
2219
- require 'stringio'
2220
-
2221
- context "Dataset#print" do
2222
- setup do
2223
- @output = StringIO.new
2224
- @orig_stdout = $stdout
2225
- $stdout = @output
2226
- @dataset = DummyDataset.new(nil).from(:items)
2227
- end
2228
-
2229
- teardown do
2230
- $stdout = @orig_stdout
2231
- end
2232
-
2233
- specify "should print out a table with the values" do
2234
- @dataset.print(:a, :b)
2235
- @output.rewind
2236
- @output.read.should == \
2237
- "+-+-+\n|a|b|\n+-+-+\n|1|2|\n|3|4|\n|5|6|\n+-+-+\n"
2238
- end
2239
-
2240
- specify "should default to the dataset's columns" do
2241
- @dataset.meta_def(:columns) {[:a, :b]}
2242
- @dataset.print
2243
- @output.rewind
2244
- @output.read.should == \
2245
- "+-+-+\n|a|b|\n+-+-+\n|1|2|\n|3|4|\n|5|6|\n+-+-+\n"
2246
- end
2247
- end
2248
-
2249
- context "Dataset#multi_insert" do
2250
- setup do
2251
- @dbc = Class.new do
2252
- attr_reader :sqls
2253
-
2254
- def execute(sql)
2255
- @sqls ||= []
2256
- @sqls << sql
2257
- end
2258
-
2259
- def transaction
2260
- @sqls ||= []
2261
- @sqls << 'BEGIN'
2262
- yield
2263
- @sqls << 'COMMIT'
2264
- end
2265
- end
2266
- @db = @dbc.new
2267
-
2268
- @ds = Sequel::Dataset.new(@db).from(:items)
2269
-
2270
- @list = [{:name => 'abc'}, {:name => 'def'}, {:name => 'ghi'}]
2271
- end
2272
-
2273
- specify "should join all inserts into a single SQL string" do
2274
- @ds.multi_insert(@list)
2275
- @db.sqls.should == [
2276
- 'BEGIN',
2277
- "INSERT INTO items (name) VALUES ('abc')",
2278
- "INSERT INTO items (name) VALUES ('def')",
2279
- "INSERT INTO items (name) VALUES ('ghi')",
2280
- 'COMMIT'
2281
- ]
2282
- end
2283
-
2284
- specify "should accept the :commit_every option for committing every x records" do
2285
- @ds.multi_insert(@list, :commit_every => 2)
2286
- @db.sqls.should == [
2287
- 'BEGIN',
2288
- "INSERT INTO items (name) VALUES ('abc')",
2289
- "INSERT INTO items (name) VALUES ('def')",
2290
- 'COMMIT',
2291
- 'BEGIN',
2292
- "INSERT INTO items (name) VALUES ('ghi')",
2293
- 'COMMIT'
2294
- ]
2295
- end
2296
-
2297
- specify "should accept the :slice option for committing every x records" do
2298
- @ds.multi_insert(@list, :slice => 2)
2299
- @db.sqls.should == [
2300
- 'BEGIN',
2301
- "INSERT INTO items (name) VALUES ('abc')",
2302
- "INSERT INTO items (name) VALUES ('def')",
2303
- 'COMMIT',
2304
- 'BEGIN',
2305
- "INSERT INTO items (name) VALUES ('ghi')",
2306
- 'COMMIT'
2307
- ]
2308
- end
2309
-
2310
- specify "should accept a columns array and a values array" do
2311
- @ds.multi_insert([:x, :y], [[1, 2], [3, 4]])
2312
- @db.sqls.should == [
2313
- 'BEGIN',
2314
- "INSERT INTO items (x, y) VALUES (1, 2)",
2315
- "INSERT INTO items (x, y) VALUES (3, 4)",
2316
- 'COMMIT'
2317
- ]
2318
- end
2319
-
2320
- specify "should accept a columns array and a dataset" do
2321
- @ds2 = Sequel::Dataset.new(@db).from(:cats).filter(:purr => true).select(:a, :b)
2322
-
2323
- @ds.multi_insert([:x, :y], @ds2)
2324
- @db.sqls.should == [
2325
- 'BEGIN',
2326
- "INSERT INTO items (x, y) VALUES (SELECT a, b FROM cats WHERE (purr = 't'))",
2327
- 'COMMIT'
2328
- ]
2329
- end
2330
-
2331
- specify "should accept a columns array and a values array with slice option" do
2332
- @ds.multi_insert([:x, :y], [[1, 2], [3, 4], [5, 6]], :slice => 2)
2333
- @db.sqls.should == [
2334
- 'BEGIN',
2335
- "INSERT INTO items (x, y) VALUES (1, 2)",
2336
- "INSERT INTO items (x, y) VALUES (3, 4)",
2337
- 'COMMIT',
2338
- 'BEGIN',
2339
- "INSERT INTO items (x, y) VALUES (5, 6)",
2340
- 'COMMIT'
2341
- ]
2342
- end
2343
-
2344
- specify "should be aliased by #import" do
2345
- @ds.import([:x, :y], [[1, 2], [3, 4], [5, 6]], :slice => 2)
2346
- @db.sqls.should == [
2347
- 'BEGIN',
2348
- "INSERT INTO items (x, y) VALUES (1, 2)",
2349
- "INSERT INTO items (x, y) VALUES (3, 4)",
2350
- 'COMMIT',
2351
- 'BEGIN',
2352
- "INSERT INTO items (x, y) VALUES (5, 6)",
2353
- 'COMMIT'
2354
- ]
2355
- end
2356
-
2357
- specify "should not do anything if no columns or values are given" do
2358
- @ds.multi_insert
2359
- @db.sqls.should be_nil
2360
-
2361
- @ds.multi_insert([])
2362
- @db.sqls.should be_nil
2363
-
2364
- @ds.multi_insert([], [])
2365
- @db.sqls.should be_nil
2366
-
2367
- @ds.multi_insert([{}, {}])
2368
- @db.sqls.should be_nil
2369
-
2370
- @ds.multi_insert([:a, :b], [])
2371
- @db.sqls.should be_nil
2372
-
2373
- @ds.multi_insert([:x, :y], [[1, 2], [3, 4], [5, 6]], :slice => 2)
2374
- @db.sqls.should == [
2375
- 'BEGIN',
2376
- "INSERT INTO items (x, y) VALUES (1, 2)",
2377
- "INSERT INTO items (x, y) VALUES (3, 4)",
2378
- 'COMMIT',
2379
- 'BEGIN',
2380
- "INSERT INTO items (x, y) VALUES (5, 6)",
2381
- 'COMMIT'
2382
- ]
2383
- end
2384
-
2385
- end
2386
-
2387
- context "Dataset#query" do
2388
- setup do
2389
- @d = Sequel::Dataset.new(nil)
2390
- end
2391
-
2392
- specify "should support #from" do
2393
- q = @d.query {from :xxx}
2394
- q.class.should == @d.class
2395
- q.sql.should == "SELECT * FROM xxx"
2396
- end
2397
-
2398
- specify "should support #select" do
2399
- q = @d.query do
2400
- select :a, :b___mongo
2401
- from :yyy
2402
- end
2403
- q.class.should == @d.class
2404
- q.sql.should == "SELECT a, b AS mongo FROM yyy"
2405
- end
2406
-
2407
- specify "should support #where" do
2408
- q = @d.query do
2409
- from :zzz
2410
- where(:x + 2 > :y + 3)
2411
- end
2412
- q.class.should == @d.class
2413
- q.sql.should == "SELECT * FROM zzz WHERE ((x + 2) > (y + 3))"
2414
-
2415
- q = @d.from(:zzz).query do
2416
- where((:x > 1) & (:y > 2))
2417
- end
2418
- q.class.should == @d.class
2419
- q.sql.should == "SELECT * FROM zzz WHERE ((x > 1) AND (y > 2))"
2420
-
2421
- q = @d.from(:zzz).query do
2422
- where :x => 33
2423
- end
2424
- q.class.should == @d.class
2425
- q.sql.should == "SELECT * FROM zzz WHERE (x = 33)"
2426
- end
2427
-
2428
- specify "should support #group_by and #having" do
2429
- q = @d.query do
2430
- from :abc
2431
- group_by :id
2432
- having(:x >= 2)
2433
- end
2434
- q.class.should == @d.class
2435
- q.sql.should == "SELECT * FROM abc GROUP BY id HAVING (x >= 2)"
2436
- end
2437
-
2438
- specify "should support #order, #order_by" do
2439
- q = @d.query do
2440
- from :xyz
2441
- order_by :stamp
2442
- end
2443
- q.class.should == @d.class
2444
- q.sql.should == "SELECT * FROM xyz ORDER BY stamp"
2445
- end
2446
-
2447
- specify "should raise on non-chainable method calls" do
2448
- proc {@d.query {first_source}}.should raise_error(Sequel::Error)
2449
- end
2450
-
2451
- specify "should raise on each, insert, update, delete" do
2452
- proc {@d.query {each}}.should raise_error(Sequel::Error)
2453
- proc {@d.query {insert(:x => 1)}}.should raise_error(Sequel::Error)
2454
- proc {@d.query {update(:x => 1)}}.should raise_error(Sequel::Error)
2455
- proc {@d.query {delete}}.should raise_error(Sequel::Error)
2456
- end
2457
- end
2458
-
2459
- context "Dataset" do
2460
- setup do
2461
- @d = Sequel::Dataset.new(nil).from(:x)
2462
- end
2463
-
2464
- specify "should support self-changing select!" do
2465
- @d.select!(:y)
2466
- @d.sql.should == "SELECT y FROM x"
2467
- end
2468
-
2469
- specify "should support self-changing from!" do
2470
- @d.from!(:y)
2471
- @d.sql.should == "SELECT * FROM y"
2472
- end
2473
-
2474
- specify "should support self-changing order!" do
2475
- @d.order!(:y)
2476
- @d.sql.should == "SELECT * FROM x ORDER BY y"
2477
- end
2478
-
2479
- specify "should support self-changing filter!" do
2480
- @d.filter!(:y => 1)
2481
- @d.sql.should == "SELECT * FROM x WHERE (y = 1)"
2482
- end
2483
-
2484
- specify "should support self-changing filter! with block" do
2485
- @d.filter!{:y < 2}
2486
- @d.sql.should == "SELECT * FROM x WHERE (y < 2)"
2487
- end
2488
-
2489
- specify "should raise for ! methods that don't return a dataset" do
2490
- proc {@d.opts!}.should raise_error(NameError)
2491
- end
2492
-
2493
- specify "should raise for missing methods" do
2494
- proc {@d.xuyz}.should raise_error(NameError)
2495
- proc {@d.xyz!}.should raise_error(NameError)
2496
- proc {@d.xyz?}.should raise_error(NameError)
2497
- end
2498
-
2499
- specify "should support chaining of bang methods" do
2500
- @d.order!(:y)
2501
- @d.filter!(:y => 1)
2502
- @d.sql.should == "SELECT * FROM x WHERE (y = 1) ORDER BY y"
2503
- end
2504
- end
2505
-
2506
- context "Dataset#transform" do
2507
- setup do
2508
- @c = Class.new(Sequel::Dataset) do
2509
- attr_accessor :raw
2510
- attr_accessor :sql
2511
-
2512
- def fetch_rows(sql, &block)
2513
- block[@raw]
2514
- end
2515
-
2516
- def insert(v)
2517
- @sql = insert_sql(v)
2518
- end
2519
-
2520
- def update(v)
2521
- @sql = update_sql(v)
2522
- end
2523
- end
2524
-
2525
- @ds = @c.new(nil).from(:items)
2526
- @ds.transform(:x => [
2527
- proc {|v| Marshal.load(v)},
2528
- proc {|v| Marshal.dump(v)}
2529
- ])
2530
- end
2531
-
2532
- specify "should change the dataset to transform values loaded from the database" do
2533
- @ds.raw = {:x => Marshal.dump([1, 2, 3]), :y => 'hello'}
2534
- @ds.first.should == {:x => [1, 2, 3], :y => 'hello'}
2535
- @ds.raw = {:x => Marshal.dump([1, 2, 3]), :y => 'hello'}
2536
- @ds.all.should == [{:x => [1, 2, 3], :y => 'hello'}]
2537
- end
2538
-
2539
- specify "should change the dataset to transform values saved to the database" do
2540
- @ds.insert(:x => :toast)
2541
- @ds.sql.should == "INSERT INTO items (x) VALUES ('#{Marshal.dump(:toast)}')"
2542
-
2543
- @ds.insert(:y => 'butter')
2544
- @ds.sql.should == "INSERT INTO items (y) VALUES ('butter')"
2545
-
2546
- @ds.update(:x => ['dream'])
2547
- @ds.sql.should == "UPDATE items SET x = '#{Marshal.dump(['dream'])}'"
2548
- end
2549
-
2550
- specify "should be transferred to cloned datasets" do
2551
- @ds2 = @ds.filter(:a => 1)
2552
-
2553
- @ds2.raw = {:x => Marshal.dump([1, 2, 3]), :y => 'hello'}
2554
- @ds2.first.should == {:x => [1, 2, 3], :y => 'hello'}
2555
-
2556
- @ds2.insert(:x => :toast)
2557
- @ds2.sql.should == "INSERT INTO items (x) VALUES ('#{Marshal.dump(:toast)}')"
2558
- end
2559
-
2560
- specify "should work correctly together with set_row_proc" do
2561
- @ds.row_proc = proc{|r| r[:z] = r[:x] * 2; r}
2562
- @ds.raw = {:x => Marshal.dump("wow"), :y => 'hello'}
2563
- @ds.first.should == {:x => "wow", :y => 'hello', :z => "wowwow"}
2564
-
2565
- f = nil
2566
- @ds.raw = {:x => Marshal.dump("wow"), :y => 'hello'}
2567
- @ds.each(:naked => true) {|r| f = r}
2568
- f.should == {:x => "wow", :y => 'hello'}
2569
- end
2570
-
2571
- specify "should leave the supplied values intact" do
2572
- h = {:x => :toast}
2573
- @ds.insert(h)
2574
- h.should == {:x => :toast}
2575
- end
2576
- end
2577
-
2578
- context "Dataset#transform" do
2579
- setup do
2580
- @c = Class.new(Sequel::Dataset) do
2581
- attr_accessor :raw
2582
- attr_accessor :sql
2583
-
2584
- def fetch_rows(sql, &block)
2585
- block[@raw]
2586
- end
2587
-
2588
- def insert(v)
2589
- @sql = insert_sql(v)
2590
- end
2591
-
2592
- def update(v)
2593
- @sql = update_sql(v)
2594
- end
2595
- end
2596
-
2597
- @ds = @c.new(nil).from(:items)
2598
- end
2599
-
2600
- specify "should raise Sequel::Error for invalid transformations" do
2601
- proc {@ds.transform(:x => 'mau')}.should raise_error(Sequel::Error::InvalidTransform)
2602
- proc {@ds.transform(:x => :mau)}.should raise_error(Sequel::Error::InvalidTransform)
2603
- proc {@ds.transform(:x => [])}.should raise_error(Sequel::Error::InvalidTransform)
2604
- proc {@ds.transform(:x => ['mau'])}.should raise_error(Sequel::Error::InvalidTransform)
2605
- proc {@ds.transform(:x => [proc {|v|}, proc {|v|}])}.should_not raise_error(Sequel::Error::InvalidTransform)
2606
- end
2607
-
2608
- specify "should support stock YAML transformation" do
2609
- @ds.transform(:x => :yaml)
2610
-
2611
- @ds.raw = {:x => [1, 2, 3].to_yaml, :y => 'hello'}
2612
- @ds.first.should == {:x => [1, 2, 3], :y => 'hello'}
2613
-
2614
- @ds.insert(:x => :toast)
2615
- @ds.sql.should == "INSERT INTO items (x) VALUES ('#{:toast.to_yaml}')"
2616
- @ds.insert(:y => 'butter')
2617
- @ds.sql.should == "INSERT INTO items (y) VALUES ('butter')"
2618
- @ds.update(:x => ['dream'])
2619
- @ds.sql.should == "UPDATE items SET x = '#{['dream'].to_yaml}'"
2620
-
2621
- @ds2 = @ds.filter(:a => 1)
2622
- @ds2.raw = {:x => [1, 2, 3].to_yaml, :y => 'hello'}
2623
- @ds2.first.should == {:x => [1, 2, 3], :y => 'hello'}
2624
- @ds2.insert(:x => :toast)
2625
- @ds2.sql.should == "INSERT INTO items (x) VALUES ('#{:toast.to_yaml}')"
2626
-
2627
- @ds.row_proc = proc{|r| r[:z] = r[:x] * 2; r}
2628
- @ds.raw = {:x => "wow".to_yaml, :y => 'hello'}
2629
- @ds.first.should == {:x => "wow", :y => 'hello', :z => "wowwow"}
2630
- f = nil
2631
- @ds.raw = {:x => "wow".to_yaml, :y => 'hello'}
2632
- @ds.each(:naked => true) {|r| f = r}
2633
- f.should == {:x => "wow", :y => 'hello'}
2634
- end
2635
-
2636
- specify "should support stock Marshal transformation with Base64 encoding" do
2637
- @ds.transform(:x => :marshal)
2638
-
2639
- @ds.raw = {:x => [Marshal.dump([1, 2, 3])].pack('m'), :y => 'hello'}
2640
- @ds.first.should == {:x => [1, 2, 3], :y => 'hello'}
2641
-
2642
- @ds.insert(:x => :toast)
2643
- @ds.sql.should == "INSERT INTO items (x) VALUES ('#{[Marshal.dump(:toast)].pack('m')}')"
2644
- @ds.insert(:y => 'butter')
2645
- @ds.sql.should == "INSERT INTO items (y) VALUES ('butter')"
2646
- @ds.update(:x => ['dream'])
2647
- @ds.sql.should == "UPDATE items SET x = '#{[Marshal.dump(['dream'])].pack('m')}'"
2648
-
2649
- @ds2 = @ds.filter(:a => 1)
2650
- @ds2.raw = {:x => [Marshal.dump([1, 2, 3])].pack('m'), :y => 'hello'}
2651
- @ds2.first.should == {:x => [1, 2, 3], :y => 'hello'}
2652
- @ds2.insert(:x => :toast)
2653
- @ds2.sql.should == "INSERT INTO items (x) VALUES ('#{[Marshal.dump(:toast)].pack('m')}')"
2654
-
2655
- @ds.row_proc = proc{|r| r[:z] = r[:x] * 2; r}
2656
- @ds.raw = {:x => [Marshal.dump("wow")].pack('m'), :y => 'hello'}
2657
- @ds.first.should == {:x => "wow", :y => 'hello', :z => "wowwow"}
2658
- f = nil
2659
- @ds.raw = {:x => [Marshal.dump("wow")].pack('m'), :y => 'hello'}
2660
- @ds.each(:naked => true) {|r| f = r}
2661
- f.should == {:x => "wow", :y => 'hello'}
2662
- end
2663
-
2664
- specify "should support loading of Marshalled values without Base64 encoding" do
2665
- @ds.transform(:x => :marshal)
2666
-
2667
- @ds.raw = {:x => Marshal.dump([1,2,3]), :y => nil}
2668
- @ds.first.should == {:x => [1,2,3], :y => nil}
2669
- end
2670
-
2671
- specify "should return self" do
2672
- @ds.transform(:x => :marshal).should be(@ds)
2673
- end
2674
- end
2675
-
2676
- context "A dataset with a transform" do
2677
- setup do
2678
- @ds = Sequel::Dataset.new(nil).from(:items)
2679
- @ds.transform(:x => :marshal)
2680
- end
2681
-
2682
- specify "should automatically transform hash filters" do
2683
- @ds.filter(:y => 2).sql.should == 'SELECT * FROM items WHERE (y = 2)'
2684
-
2685
- @ds.filter(:x => 2).sql.should == "SELECT * FROM items WHERE (x = '#{[Marshal.dump(2)].pack('m')}')"
2686
- end
2687
- end
2688
-
2689
- context "Dataset#to_csv" do
2690
- setup do
2691
- @c = Class.new(Sequel::Dataset) do
2692
- attr_accessor :data
2693
- attr_accessor :columns
2694
-
2695
- def fetch_rows(sql, &block)
2696
- @data.each(&block)
2697
- end
2698
-
2699
- # naked should return self here because to_csv wants a naked result set.
2700
- def naked
2701
- self
2702
- end
2703
- end
2704
-
2705
- @ds = @c.new(nil).from(:items)
2706
- @ds.columns = [:a, :b, :c]
2707
- @ds.data = [ {:a=>1, :b=>2, :c=>3}, {:a=>4, :b=>5, :c=>6}, {:a=>7, :b=>8, :c=>9} ]
2708
- end
2709
-
2710
- specify "should format a CSV representation of the records" do
2711
- @ds.to_csv.should ==
2712
- "a, b, c\r\n1, 2, 3\r\n4, 5, 6\r\n7, 8, 9\r\n"
2713
- end
2714
-
2715
- specify "should exclude column titles if so specified" do
2716
- @ds.to_csv(false).should ==
2717
- "1, 2, 3\r\n4, 5, 6\r\n7, 8, 9\r\n"
2718
- end
2719
- end
2720
-
2721
- context "Dataset#create_view" do
2722
- setup do
2723
- @dbc = Class.new(Sequel::Database) do
2724
- attr_reader :sqls
2725
-
2726
- def execute(sql)
2727
- @sqls ||= []
2728
- @sqls << sql
2729
- end
2730
- end
2731
- @db = @dbc.new
2732
-
2733
- @ds = @db[:items].order(:abc).filter(:category => 'ruby')
2734
- end
2735
-
2736
- specify "should create a view with the dataset's sql" do
2737
- @ds.create_view(:xyz)
2738
- @db.sqls.should == ["CREATE VIEW xyz AS #{@ds.sql}"]
2739
- end
2740
- end
2741
-
2742
- context "Dataset#create_or_replace_view" do
2743
- setup do
2744
- @dbc = Class.new(Sequel::Database) do
2745
- attr_reader :sqls
2746
-
2747
- def execute(sql)
2748
- @sqls ||= []
2749
- @sqls << sql
2750
- end
2751
- end
2752
- @db = @dbc.new
2753
-
2754
- @ds = @db[:items].order(:abc).filter(:category => 'ruby')
2755
- end
2756
-
2757
- specify "should create a view with the dataset's sql" do
2758
- @ds.create_or_replace_view(:xyz)
2759
- @db.sqls.should == ["CREATE OR REPLACE VIEW xyz AS #{@ds.sql}"]
2760
- end
2761
- end
2762
-
2763
- context "Dataset#update_sql" do
2764
- setup do
2765
- @ds = Sequel::Dataset.new(nil).from(:items)
2766
- end
2767
-
2768
- specify "should accept strings" do
2769
- @ds.update_sql("a = b").should == "UPDATE items SET a = b"
2770
- end
2771
-
2772
- specify "should accept hash with string keys" do
2773
- @ds.update_sql('c' => 'd').should == "UPDATE items SET c = 'd'"
2774
- end
2775
-
2776
- specify "should accept array subscript references" do
2777
- @ds.update_sql((:day|1) => 'd').should == "UPDATE items SET day[1] = 'd'"
2778
- end
2779
- end
2780
-
2781
- context "Dataset#insert_sql" do
2782
- setup do
2783
- @ds = Sequel::Dataset.new(nil).from(:items)
2784
- end
2785
-
2786
- specify "should accept hash with symbol keys" do
2787
- @ds.insert_sql(:c => 'd').should == "INSERT INTO items (c) VALUES ('d')"
2788
- end
2789
-
2790
- specify "should accept hash with string keys" do
2791
- @ds.insert_sql('c' => 'd').should == "INSERT INTO items (c) VALUES ('d')"
2792
- end
2793
-
2794
- specify "should accept array subscript references" do
2795
- @ds.insert_sql((:day|1) => 'd').should == "INSERT INTO items (day[1]) VALUES ('d')"
2796
- end
2797
- end
2798
-
2799
- class DummyMummyDataset < Sequel::Dataset
2800
- def first
2801
- raise if @opts[:from] == [:a]
2802
- true
2803
- end
2804
- end
2805
-
2806
- class DummyMummyDatabase < Sequel::Database
2807
- attr_reader :sqls
2808
-
2809
- def execute(sql)
2810
- @sqls ||= []
2811
- @sqls << sql
2812
- end
2813
-
2814
- def transaction; yield; end
2815
-
2816
- def dataset
2817
- DummyMummyDataset.new(self)
2818
- end
2819
- end
2820
-
2821
- context "Dataset#table_exists?" do
2822
- setup do
2823
- @db = DummyMummyDatabase.new
2824
- @db.stub!(:tables).and_return([:a, :b])
2825
- @db2 = DummyMummyDatabase.new
2826
- end
2827
-
2828
- specify "should use Database#tables if available" do
2829
- @db[:a].table_exists?.should be_true
2830
- @db[:b].table_exists?.should be_true
2831
- @db[:c].table_exists?.should be_false
2832
- end
2833
-
2834
- specify "should otherwise try to select the first record from the table's dataset" do
2835
- @db2[:a].table_exists?.should be_false
2836
- @db2[:b].table_exists?.should be_true
2837
- end
2838
-
2839
- specify "should raise Sequel::Error if dataset references more than one table" do
2840
- proc {@db.from(:a, :b).table_exists?}.should raise_error(Sequel::Error)
2841
- end
2842
-
2843
- specify "should raise Sequel::Error if dataset is from a subquery" do
2844
- proc {@db.from(@db[:a]).table_exists?}.should raise_error(Sequel::Error)
2845
- end
2846
-
2847
- specify "should raise Sequel::Error if dataset has fixed sql" do
2848
- proc {@db['select * from blah'].table_exists?}.should raise_error(Sequel::Error)
2849
- end
2850
- end
2851
-
2852
- context "Dataset#inspect" do
2853
- setup do
2854
- @ds = Sequel::Dataset.new(nil).from(:blah)
2855
- end
2856
-
2857
- specify "should include the class name and the corresponding SQL statement" do
2858
- @ds.inspect.should == '#<%s: %s>' % [@ds.class.to_s, @ds.sql.inspect]
2859
- end
2860
- end
2861
-
2862
- context "Dataset#all" do
2863
- setup do
2864
- @c = Class.new(Sequel::Dataset) do
2865
- def fetch_rows(sql, &block)
2866
- block.call({:x => 1, :y => 2})
2867
- block.call({:x => 3, :y => 4})
2868
- block.call(sql)
2869
- end
2870
- end
2871
- @dataset = @c.new(nil).from(:items)
2872
- end
2873
-
2874
- specify "should return an array with all records" do
2875
- @dataset.all.should == [
2876
- {:x => 1, :y => 2},
2877
- {:x => 3, :y => 4},
2878
- "SELECT * FROM items"
2879
- ]
2880
- end
2881
-
2882
- specify "should accept options and pass them to #each" do
2883
- @dataset.all(:limit => 33).should == [
2884
- {:x => 1, :y => 2},
2885
- {:x => 3, :y => 4},
2886
- "SELECT * FROM items LIMIT 33"
2887
- ]
2888
- end
2889
-
2890
- specify "should iterate over the array if a block is given" do
2891
- a = []
2892
-
2893
- @dataset.all do |r|
2894
- a << (r.is_a?(Hash) ? r[:x] : r)
2895
- end
2896
-
2897
- a.should == [1, 3, "SELECT * FROM items"]
2898
- end
2899
- end
2900
-
2901
- context "Dataset#grep" do
2902
- setup do
2903
- @ds = Sequel::Dataset.new(nil).from(:posts)
2904
- end
2905
-
2906
- specify "should format a SQL filter correctly" do
2907
- @ds.grep(:title, 'ruby').sql.should ==
2908
- "SELECT * FROM posts WHERE ((title LIKE 'ruby'))"
2909
- end
2910
-
2911
- specify "should support multiple columns" do
2912
- @ds.grep([:title, :body], 'ruby').sql.should ==
2913
- "SELECT * FROM posts WHERE ((title LIKE 'ruby') OR (body LIKE 'ruby'))"
2914
- end
2915
-
2916
- specify "should support multiple search terms" do
2917
- @ds.grep(:title, ['abc', 'def']).sql.should ==
2918
- "SELECT * FROM posts WHERE (((title LIKE 'abc') OR (title LIKE 'def')))"
2919
- end
2920
-
2921
- specify "should support multiple columns and search terms" do
2922
- @ds.grep([:title, :body], ['abc', 'def']).sql.should ==
2923
- "SELECT * FROM posts WHERE (((title LIKE 'abc') OR (title LIKE 'def')) OR ((body LIKE 'abc') OR (body LIKE 'def')))"
2924
- end
2925
-
2926
- specify "should support regexps though the database may not support it" do
2927
- @ds.grep(:title, /ruby/).sql.should ==
2928
- "SELECT * FROM posts WHERE ((title ~ 'ruby'))"
2929
-
2930
- @ds.grep(:title, [/^ruby/, 'ruby']).sql.should ==
2931
- "SELECT * FROM posts WHERE (((title ~ '^ruby') OR (title LIKE 'ruby')))"
2932
- end
2933
- end