sequel_core 2.2.0 → 3.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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