sequel_core 1.5.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. data/CHANGELOG +116 -0
  2. data/COPYING +19 -19
  3. data/README +83 -32
  4. data/Rakefile +9 -20
  5. data/bin/sequel +43 -112
  6. data/doc/cheat_sheet.rdoc +225 -0
  7. data/doc/dataset_filtering.rdoc +257 -0
  8. data/lib/sequel_core/adapters/adapter_skeleton.rb +4 -2
  9. data/lib/sequel_core/adapters/ado.rb +3 -1
  10. data/lib/sequel_core/adapters/db2.rb +4 -2
  11. data/lib/sequel_core/adapters/dbi.rb +127 -113
  12. data/lib/sequel_core/adapters/informix.rb +4 -2
  13. data/lib/sequel_core/adapters/jdbc.rb +5 -3
  14. data/lib/sequel_core/adapters/mysql.rb +112 -46
  15. data/lib/sequel_core/adapters/odbc.rb +5 -7
  16. data/lib/sequel_core/adapters/odbc_mssql.rb +12 -3
  17. data/lib/sequel_core/adapters/openbase.rb +3 -1
  18. data/lib/sequel_core/adapters/oracle.rb +11 -9
  19. data/lib/sequel_core/adapters/postgres.rb +261 -262
  20. data/lib/sequel_core/adapters/sqlite.rb +72 -22
  21. data/lib/sequel_core/connection_pool.rb +140 -73
  22. data/lib/sequel_core/core_ext.rb +201 -66
  23. data/lib/sequel_core/core_sql.rb +123 -153
  24. data/lib/sequel_core/database/schema.rb +156 -0
  25. data/lib/sequel_core/database.rb +321 -338
  26. data/lib/sequel_core/dataset/callback.rb +11 -12
  27. data/lib/sequel_core/dataset/convenience.rb +213 -240
  28. data/lib/sequel_core/dataset/pagination.rb +58 -43
  29. data/lib/sequel_core/dataset/parse_tree_sequelizer.rb +331 -0
  30. data/lib/sequel_core/dataset/query.rb +41 -0
  31. data/lib/sequel_core/dataset/schema.rb +15 -0
  32. data/lib/sequel_core/dataset/sequelizer.rb +41 -373
  33. data/lib/sequel_core/dataset/sql.rb +741 -632
  34. data/lib/sequel_core/dataset.rb +183 -168
  35. data/lib/sequel_core/deprecated.rb +1 -169
  36. data/lib/sequel_core/exceptions.rb +24 -19
  37. data/lib/sequel_core/migration.rb +44 -52
  38. data/lib/sequel_core/object_graph.rb +43 -42
  39. data/lib/sequel_core/pretty_table.rb +71 -76
  40. data/lib/sequel_core/schema/generator.rb +163 -105
  41. data/lib/sequel_core/schema/sql.rb +250 -93
  42. data/lib/sequel_core/schema.rb +2 -8
  43. data/lib/sequel_core/sql.rb +394 -0
  44. data/lib/sequel_core/worker.rb +37 -27
  45. data/lib/sequel_core.rb +99 -45
  46. data/spec/adapters/informix_spec.rb +0 -1
  47. data/spec/adapters/mysql_spec.rb +177 -124
  48. data/spec/adapters/oracle_spec.rb +0 -1
  49. data/spec/adapters/postgres_spec.rb +98 -58
  50. data/spec/adapters/sqlite_spec.rb +45 -4
  51. data/spec/blockless_filters_spec.rb +269 -0
  52. data/spec/connection_pool_spec.rb +21 -18
  53. data/spec/core_ext_spec.rb +169 -19
  54. data/spec/core_sql_spec.rb +56 -49
  55. data/spec/database_spec.rb +78 -17
  56. data/spec/dataset_spec.rb +300 -428
  57. data/spec/migration_spec.rb +1 -1
  58. data/spec/object_graph_spec.rb +5 -11
  59. data/spec/rcov.opts +1 -1
  60. data/spec/schema_generator_spec.rb +16 -4
  61. data/spec/schema_spec.rb +89 -10
  62. data/spec/sequelizer_spec.rb +56 -56
  63. data/spec/spec.opts +0 -5
  64. data/spec/spec_config.rb +7 -0
  65. data/spec/spec_config.rb.example +5 -5
  66. data/spec/spec_helper.rb +6 -0
  67. data/spec/worker_spec.rb +1 -1
  68. metadata +78 -63
data/spec/dataset_spec.rb CHANGED
@@ -66,7 +66,7 @@ context "Dataset#clone" do
66
66
  specify "should deep-copy the dataset opts" do
67
67
  @clone = @dataset.clone
68
68
 
69
- @clone.opts.should_not eql(@dataset.opts)
69
+ @clone.opts.should_not equal(@dataset.opts)
70
70
  @dataset.filter!(:a => 'b')
71
71
  @clone.opts[:filter].should be_nil
72
72
  end
@@ -162,7 +162,9 @@ context "A simple dataset" do
162
162
  specify "should format an update statement" do
163
163
  @dataset.update_sql(:name => 'abc').should ==
164
164
  "UPDATE test SET name = 'abc'"
165
+ end
165
166
 
167
+ pt_specify "should format an update statement with block" do
166
168
  @dataset.update_sql {:x << :y}.should ==
167
169
  "UPDATE test SET x = y"
168
170
  end
@@ -195,23 +197,23 @@ context "Dataset#where" do
195
197
  setup do
196
198
  @dataset = Sequel::Dataset.new(nil).from(:test)
197
199
  @d1 = @dataset.where(:region => 'Asia')
198
- @d2 = @dataset.where('(region = ?)', 'Asia')
199
- @d3 = @dataset.where("(a = 1)")
200
+ @d2 = @dataset.where('region = ?', 'Asia')
201
+ @d3 = @dataset.where("a = 1")
200
202
  end
201
203
 
202
204
  specify "should work with hashes" do
203
205
  @dataset.where(:name => 'xyz', :price => 342).select_sql.
204
- should match(/WHERE \(name = 'xyz'\) AND \(price = 342\)|WHERE \(price = 342\) AND \(name = 'xyz'\)/)
206
+ should match(/WHERE \(\(name = 'xyz'\) AND \(price = 342\)\)|WHERE \(\(price = 342\) AND \(name = 'xyz'\)\)/)
205
207
  end
206
208
 
207
209
  specify "should work with arrays (ala ActiveRecord)" do
208
- @dataset.where('price < ? AND id in (?)', 100, [1, 2, 3]).select_sql.should ==
209
- "SELECT * FROM test WHERE price < 100 AND id in (1, 2, 3)"
210
+ @dataset.where('price < ? AND id in ?', 100, [1, 2, 3]).select_sql.should ==
211
+ "SELECT * FROM test WHERE (price < 100 AND id in (1, 2, 3))"
210
212
  end
211
213
 
212
214
  specify "should work with strings (custom SQL expressions)" do
213
215
  @dataset.where('(a = 1 AND b = 2)').select_sql.should ==
214
- "SELECT * FROM test WHERE (a = 1 AND b = 2)"
216
+ "SELECT * FROM test WHERE ((a = 1 AND b = 2))"
215
217
  end
216
218
 
217
219
  specify "should affect select, delete and update statements" do
@@ -232,45 +234,46 @@ context "Dataset#where" do
232
234
  specify "should be composable using AND operator (for scoping)" do
233
235
  # hashes are merged, no problem
234
236
  @d1.where(:size => 'big').select_sql.should ==
235
- "SELECT * FROM test WHERE (region = 'Asia') AND (size = 'big')"
237
+ "SELECT * FROM test WHERE ((region = 'Asia') AND (size = 'big'))"
236
238
 
237
239
  # hash and string
238
240
  @d1.where('population > 1000').select_sql.should ==
239
- "SELECT * FROM test WHERE (region = 'Asia') AND (population > 1000)"
241
+ "SELECT * FROM test WHERE ((region = 'Asia') AND (population > 1000))"
240
242
  @d1.where('(a > 1) OR (b < 2)').select_sql.should ==
241
- "SELECT * FROM test WHERE (region = 'Asia') AND ((a > 1) OR (b < 2))"
243
+ "SELECT * FROM test WHERE ((region = 'Asia') AND ((a > 1) OR (b < 2)))"
242
244
 
243
245
  # hash and array
244
- @d1.where('(GDP > ?)', 1000).select_sql.should ==
245
- "SELECT * FROM test WHERE (region = 'Asia') AND (GDP > 1000)"
246
+ @d1.where('GDP > ?', 1000).select_sql.should ==
247
+ "SELECT * FROM test WHERE ((region = 'Asia') AND (GDP > 1000))"
246
248
 
247
249
  # array and array
248
- @d2.where('(GDP > ?)', 1000).select_sql.should ==
249
- "SELECT * FROM test WHERE (region = 'Asia') AND (GDP > 1000)"
250
+ @d2.where('GDP > ?', 1000).select_sql.should ==
251
+ "SELECT * FROM test WHERE ((region = 'Asia') AND (GDP > 1000))"
250
252
 
251
253
  # array and hash
252
254
  @d2.where(:name => ['Japan', 'China']).select_sql.should ==
253
- "SELECT * FROM test WHERE (region = 'Asia') AND (name IN ('Japan', 'China'))"
255
+ "SELECT * FROM test WHERE ((region = 'Asia') AND (name IN ('Japan', 'China')))"
254
256
 
255
257
  # array and string
256
258
  @d2.where('GDP > ?').select_sql.should ==
257
- "SELECT * FROM test WHERE (region = 'Asia') AND (GDP > ?)"
259
+ "SELECT * FROM test WHERE ((region = 'Asia') AND (GDP > ?))"
258
260
 
259
261
  # string and string
260
262
  @d3.where('b = 2').select_sql.should ==
261
- "SELECT * FROM test WHERE (a = 1) AND (b = 2)"
263
+ "SELECT * FROM test WHERE ((a = 1) AND (b = 2))"
262
264
 
263
265
  # string and hash
264
266
  @d3.where(:c => 3).select_sql.should ==
265
- "SELECT * FROM test WHERE (a = 1) AND (c = 3)"
267
+ "SELECT * FROM test WHERE ((a = 1) AND (c = 3))"
266
268
 
267
269
  # string and array
268
- @d3.where('(d = ?)', 4).select_sql.should ==
269
- "SELECT * FROM test WHERE (a = 1) AND (d = 4)"
270
+ @d3.where('d = ?', 4).select_sql.should ==
271
+ "SELECT * FROM test WHERE ((a = 1) AND (d = 4))"
272
+ end
270
273
 
271
- # string and proc expr
272
- @d3.where {:e < 5}.select_sql.should ==
273
- "SELECT * FROM test WHERE (a = 1) AND (e < 5)"
274
+ pt_specify "should be composable using AND operator (for scoping) with block" do
275
+ @d3.where{:e < 5}.select_sql.should ==
276
+ "SELECT * FROM test WHERE ((a = 1) AND (e < 5))"
274
277
  end
275
278
 
276
279
  specify "should raise if the dataset is grouped" do
@@ -281,21 +284,22 @@ context "Dataset#where" do
281
284
 
282
285
  specify "should accept ranges" do
283
286
  @dataset.filter(:id => 4..7).sql.should ==
284
- 'SELECT * FROM test WHERE (id >= 4 AND id <= 7)'
287
+ 'SELECT * FROM test WHERE ((id >= 4) AND (id <= 7))'
285
288
  @dataset.filter(:id => 4...7).sql.should ==
286
- 'SELECT * FROM test WHERE (id >= 4 AND id < 7)'
289
+ 'SELECT * FROM test WHERE ((id >= 4) AND (id < 7))'
287
290
 
291
+ @dataset.filter(:table__id => 4..7).sql.should ==
292
+ 'SELECT * FROM test WHERE ((table.id >= 4) AND (table.id <= 7))'
293
+ @dataset.filter(:table__id => 4...7).sql.should ==
294
+ 'SELECT * FROM test WHERE ((table.id >= 4) AND (table.id < 7))'
295
+ end
296
+
297
+ pt_specify "should accept ranges with a block" do
288
298
  @dataset.filter {:id == (4..7)}.sql.should ==
289
299
  'SELECT * FROM test WHERE (id >= 4 AND id <= 7)'
290
-
291
300
  @dataset.filter {:id.in?(4..7)}.sql.should ==
292
301
  'SELECT * FROM test WHERE (id >= 4 AND id <= 7)'
293
302
 
294
- @dataset.filter(:table__id => 4..7).sql.should ==
295
- 'SELECT * FROM test WHERE (table.id >= 4 AND table.id <= 7)'
296
- @dataset.filter(:table__id => 4...7).sql.should ==
297
- 'SELECT * FROM test WHERE (table.id >= 4 AND table.id < 7)'
298
-
299
303
  @dataset.filter {:table__id == (4..7)}.sql.should ==
300
304
  'SELECT * FROM test WHERE (table.id >= 4 AND table.id <= 7)'
301
305
  @dataset.filter {:table__id.in?(4..7)}.sql.should ==
@@ -305,7 +309,9 @@ context "Dataset#where" do
305
309
  specify "should accept nil" do
306
310
  @dataset.filter(:owner_id => nil).sql.should ==
307
311
  'SELECT * FROM test WHERE (owner_id IS NULL)'
312
+ end
308
313
 
314
+ pt_specify "should accept nil with a block" do
309
315
  @dataset.filter{:owner_id.nil?}.sql.should ==
310
316
  'SELECT * FROM test WHERE (owner_id IS NULL)'
311
317
  end
@@ -313,19 +319,19 @@ context "Dataset#where" do
313
319
  specify "should accept a subquery" do
314
320
  # select all countries that have GDP greater than the average for Asia
315
321
  @dataset.filter('gdp > ?', @d1.select(:avg[:gdp])).sql.should ==
316
- "SELECT * FROM test WHERE gdp > (SELECT avg(gdp) FROM test WHERE (region = 'Asia'))"
322
+ "SELECT * FROM test WHERE (gdp > (SELECT avg(gdp) FROM test WHERE (region = 'Asia')))"
317
323
 
318
324
  @dataset.filter(:id => @d1.select(:id)).sql.should ==
319
325
  "SELECT * FROM test WHERE (id IN (SELECT id FROM test WHERE (region = 'Asia')))"
320
326
  end
321
327
 
322
328
  specify "should accept a subquery for an EXISTS clause" do
323
- a = @dataset.filter {:price < 100}
329
+ a = @dataset.filter(:price < 100)
324
330
  @dataset.filter(a.exists).sql.should ==
325
- 'SELECT * FROM test WHERE EXISTS (SELECT * FROM test WHERE (price < 100))'
331
+ 'SELECT * FROM test WHERE (EXISTS (SELECT * FROM test WHERE (price < 100)))'
326
332
  end
327
333
 
328
- specify "should accept proc expressions" do
334
+ pt_specify "should accept proc expressions" do
329
335
  d = @d1.select(:avg[:gdp])
330
336
  @dataset.filter {:gdp > d}.sql.should ==
331
337
  "SELECT * FROM test WHERE (gdp > (SELECT avg(gdp) FROM test WHERE (region = 'Asia')))"
@@ -384,24 +390,27 @@ context "Dataset#or" do
384
390
 
385
391
  specify "should add an alternative expression to the where clause" do
386
392
  @d1.or(:y => 2).sql.should ==
387
- 'SELECT * FROM test WHERE (x = 1) OR (y = 2)'
393
+ 'SELECT * FROM test WHERE ((x = 1) OR (y = 2))'
388
394
  end
389
395
 
390
396
  specify "should accept all forms of filters" do
391
- # probably not exhaustive, but good enough
392
- @d1.or('(y > ?)', 2).sql.should ==
393
- 'SELECT * FROM test WHERE (x = 1) OR (y > 2)'
394
-
395
- (@d1.or {:yy > 3}).sql.should ==
396
- 'SELECT * FROM test WHERE (x = 1) OR (yy > 3)'
397
+ @d1.or('y > ?', 2).sql.should ==
398
+ 'SELECT * FROM test WHERE ((x = 1) OR (y > 2))'
399
+ @d1.or(:yy > 3).sql.should ==
400
+ 'SELECT * FROM test WHERE ((x = 1) OR (yy > 3))'
401
+ end
402
+
403
+ pt_specify "should accept blocks passed to filter" do
404
+ @d1.or{:yy > 3}.sql.should ==
405
+ 'SELECT * FROM test WHERE ((x = 1) OR (yy > 3))'
397
406
  end
398
407
 
399
408
  specify "should correctly add parens to give predictable results" do
400
409
  @d1.filter(:y => 2).or(:z => 3).sql.should ==
401
- 'SELECT * FROM test WHERE ((x = 1) AND (y = 2)) OR (z = 3)'
410
+ 'SELECT * FROM test WHERE (((x = 1) AND (y = 2)) OR (z = 3))'
402
411
 
403
412
  @d1.or(:y => 2).filter(:z => 3).sql.should ==
404
- 'SELECT * FROM test WHERE ((x = 1) OR (y = 2)) AND (z = 3)'
413
+ 'SELECT * FROM test WHERE (((x = 1) OR (y = 2)) AND (z = 3))'
405
414
  end
406
415
  end
407
416
 
@@ -420,24 +429,28 @@ context "Dataset#and" do
420
429
 
421
430
  specify "should add an alternative expression to the where clause" do
422
431
  @d1.and(:y => 2).sql.should ==
423
- 'SELECT * FROM test WHERE (x = 1) AND (y = 2)'
432
+ 'SELECT * FROM test WHERE ((x = 1) AND (y = 2))'
424
433
  end
425
434
 
426
435
  specify "should accept all forms of filters" do
427
436
  # probably not exhaustive, but good enough
428
- @d1.and('(y > ?)', 2).sql.should ==
429
- 'SELECT * FROM test WHERE (x = 1) AND (y > 2)'
437
+ @d1.and('y > ?', 2).sql.should ==
438
+ 'SELECT * FROM test WHERE ((x = 1) AND (y > 2))'
439
+ @d1.and(:yy > 3).sql.should ==
440
+ 'SELECT * FROM test WHERE ((x = 1) AND (yy > 3))'
441
+ end
430
442
 
431
- (@d1.and {:yy > 3}).sql.should ==
432
- 'SELECT * FROM test WHERE (x = 1) AND (yy > 3)'
443
+ pt_specify "should accept blocks passed to filter" do
444
+ @d1.and {:yy > 3}.sql.should ==
445
+ 'SELECT * FROM test WHERE ((x = 1) AND (yy > 3))'
433
446
  end
434
447
 
435
448
  specify "should correctly add parens to give predictable results" do
436
449
  @d1.or(:y => 2).and(:z => 3).sql.should ==
437
- 'SELECT * FROM test WHERE ((x = 1) OR (y = 2)) AND (z = 3)'
450
+ 'SELECT * FROM test WHERE (((x = 1) OR (y = 2)) AND (z = 3))'
438
451
 
439
452
  @d1.and(:y => 2).or(:z => 3).sql.should ==
440
- 'SELECT * FROM test WHERE ((x = 1) AND (y = 2)) OR (z = 3)'
453
+ 'SELECT * FROM test WHERE (((x = 1) AND (y = 2)) OR (z = 3))'
441
454
  end
442
455
  end
443
456
 
@@ -448,33 +461,51 @@ context "Dataset#exclude" do
448
461
 
449
462
  specify "should correctly include the NOT operator when one condition is given" do
450
463
  @dataset.exclude(:region=>'Asia').select_sql.should ==
451
- "SELECT * FROM test WHERE (NOT (region = 'Asia'))"
464
+ "SELECT * FROM test WHERE (region != 'Asia')"
452
465
  end
453
466
 
454
467
  specify "should take multiple conditions as a hash and express the logic correctly in SQL" do
455
468
  @dataset.exclude(:region => 'Asia', :name => 'Japan').select_sql.
456
- should match(Regexp.union(/WHERE \(NOT \(\(region = 'Asia'\) AND \(name = 'Japan'\)\)\)/,
457
- /WHERE \(NOT \(\(name = 'Japan'\) AND \(region = 'Asia'\)\)\)/))
469
+ should match(Regexp.union(/WHERE \(\(region != 'Asia'\) AND \(name != 'Japan'\)\)/,
470
+ /WHERE \(\(name != 'Japan'\) AND \(region != 'Asia'\)\)/))
458
471
  end
459
472
 
460
473
  specify "should parenthesize a single string condition correctly" do
461
474
  @dataset.exclude("region = 'Asia' AND name = 'Japan'").select_sql.should ==
462
- "SELECT * FROM test WHERE (NOT (region = 'Asia' AND name = 'Japan'))"
475
+ "SELECT * FROM test WHERE NOT (region = 'Asia' AND name = 'Japan')"
463
476
  end
464
477
 
465
478
  specify "should parenthesize an array condition correctly" do
466
479
  @dataset.exclude('region = ? AND name = ?', 'Asia', 'Japan').select_sql.should ==
467
- "SELECT * FROM test WHERE (NOT (region = 'Asia' AND name = 'Japan'))"
480
+ "SELECT * FROM test WHERE NOT (region = 'Asia' AND name = 'Japan')"
468
481
  end
469
482
 
470
483
  specify "should correctly parenthesize when it is used twice" do
471
484
  @dataset.exclude(:region => 'Asia').exclude(:name => 'Japan').select_sql.should ==
472
- "SELECT * FROM test WHERE (NOT (region = 'Asia')) AND (NOT (name = 'Japan'))"
485
+ "SELECT * FROM test WHERE ((region != 'Asia') AND (name != 'Japan'))"
473
486
  end
474
487
 
475
- specify "should support proc expressions" do
488
+ pt_specify "should support proc expressions" do
476
489
  @dataset.exclude {:id == (6...12)}.sql.should ==
477
- 'SELECT * FROM test WHERE (NOT ((id >= 6 AND id < 12)))'
490
+ 'SELECT * FROM test WHERE NOT (id >= 6 AND id < 12)'
491
+ end
492
+ end
493
+
494
+ context "Dataset#invert" do
495
+ setup do
496
+ @d = Sequel::Dataset.new(nil).from(:test)
497
+ end
498
+
499
+ specify "should raise error if the dataset is not filtered" do
500
+ proc{@d.invert}.should raise_error(Sequel::Error)
501
+ end
502
+
503
+ specify "should invert current filter if dataset is filtered" do
504
+ @d.filter(:x).invert.sql.should == 'SELECT * FROM test WHERE NOT x'
505
+ end
506
+
507
+ specify "should invert both having and where if both are preset" do
508
+ @d.filter(:x).group(:x).having(:x).invert.sql.should == 'SELECT * FROM test WHERE NOT x GROUP BY x HAVING NOT x'
478
509
  end
479
510
  end
480
511
 
@@ -493,17 +524,17 @@ context "Dataset#having" do
493
524
 
494
525
  specify "should affect select statements" do
495
526
  @d1.select_sql.should ==
496
- "SELECT #{@columns} FROM test GROUP BY region HAVING sum(population) > 10"
527
+ "SELECT #{@columns} FROM test GROUP BY region HAVING (sum(population) > 10)"
497
528
  end
498
529
 
499
- specify "should support proc expressions" do
530
+ pt_specify "should support proc expressions" do
500
531
  @grouped.having {:sum[:population] > 10}.sql.should ==
501
532
  "SELECT #{@columns} FROM test GROUP BY region HAVING (sum(population) > 10)"
502
533
  end
503
534
 
504
535
  specify "should work with and on the having clause" do
505
- @grouped.having{ :a > 1 }.and{ :b < 2 }.sql.should ==
506
- "SELECT #{@columns} FROM test GROUP BY region HAVING (a > 1) AND (b < 2)"
536
+ @grouped.having( :a > 1 ).and( :b < 2 ).sql.should ==
537
+ "SELECT #{@columns} FROM test GROUP BY region HAVING ((a > 1) AND (b < 2))"
507
538
  end
508
539
  end
509
540
 
@@ -528,7 +559,7 @@ context "a grouped dataset" do
528
559
  specify "should format the right statement for counting (as a subquery)" do
529
560
  db = MockDatabase.new
530
561
  db[:test].select(:name).group(:name).count
531
- db.sqls.should == ["SELECT COUNT(*) FROM (SELECT name FROM test GROUP BY name) t1"]
562
+ db.sqls.should == ["SELECT COUNT(*) FROM (SELECT name FROM test GROUP BY name) t1 LIMIT 1"]
532
563
  end
533
564
  end
534
565
 
@@ -551,6 +582,14 @@ context "Dataset#group_by" do
551
582
  end
552
583
  end
553
584
 
585
+ context "Dataset#as" do
586
+ specify "should set up an alias" do
587
+ dataset = Sequel::Dataset.new(nil).from(:test)
588
+ dataset.select(dataset.limit(1).select(:name).as(:n)).sql.should == \
589
+ 'SELECT (SELECT name FROM test LIMIT 1) AS n FROM test'
590
+ end
591
+ end
592
+
554
593
  context "Dataset#literal" do
555
594
  setup do
556
595
  @dataset = Sequel::Dataset.new(nil).from(:test)
@@ -576,9 +615,9 @@ context "Dataset#literal" do
576
615
  end
577
616
 
578
617
  specify "should literalize an array properly" do
579
- @dataset.literal([]).should == "NULL"
580
- @dataset.literal([1, 'abc', 3]).should == "1, 'abc', 3"
581
- @dataset.literal([1, "a'b''c", 3]).should == "1, 'a''b''''c', 3"
618
+ @dataset.literal([]).should == "(NULL)"
619
+ @dataset.literal([1, 'abc', 3]).should == "(1, 'abc', 3)"
620
+ @dataset.literal([1, "a'b''c", 3]).should == "(1, 'a''b''''c', 3)"
582
621
  end
583
622
 
584
623
  specify "should literalize symbols as column references" do
@@ -658,9 +697,9 @@ context "Dataset#from" do
658
697
  "SELECT * FROM (SELECT * FROM a GROUP BY b) c"
659
698
  end
660
699
 
661
- specify "should use the relevant table name if given a simple dataset" do
700
+ specify "should always use a subquery if given a dataset" do
662
701
  @dataset.from(@dataset.from(:a)).select_sql.should ==
663
- "SELECT * FROM a"
702
+ "SELECT * FROM (SELECT * FROM a) t1"
664
703
  end
665
704
 
666
705
  specify "should raise if no source is given" do
@@ -671,8 +710,8 @@ context "Dataset#from" do
671
710
  @dataset.from(:abc[:def]).select_sql.should ==
672
711
  "SELECT * FROM abc(def)"
673
712
 
674
- @dataset.from(:a|:i).select_sql.should ==
675
- "SELECT * FROM a[i]"
713
+ @dataset.from(:a[:i]).select_sql.should ==
714
+ "SELECT * FROM a(i)"
676
715
 
677
716
  @dataset.from(:generate_series[1, 2].as(:a[:i])).select_sql.should ==
678
717
  "SELECT * FROM generate_series(1, 2) AS a(i)"
@@ -794,6 +833,17 @@ context "Dataset#order" do
794
833
  end
795
834
  end
796
835
 
836
+ context "Dataset#unfiltered" do
837
+ setup do
838
+ @dataset = Sequel::Dataset.new(nil).from(:test)
839
+ end
840
+
841
+ specify "should remove filtering from the dataset" do
842
+ @dataset.filter(:score=>1).unfiltered.sql.should ==
843
+ 'SELECT * FROM test'
844
+ end
845
+ end
846
+
797
847
  context "Dataset#unordered" do
798
848
  setup do
799
849
  @dataset = Sequel::Dataset.new(nil).from(:test)
@@ -923,6 +973,15 @@ context "Dataset#limit" do
923
973
  @dataset.limit(6, 10).sql.should ==
924
974
  'SELECT * FROM (select * from cccc) t1 LIMIT 6 OFFSET 10'
925
975
  end
976
+
977
+ specify "should raise an error if an invalid limit or offset is used" do
978
+ proc{@dataset.limit(-1)}.should raise_error(Sequel::Error)
979
+ proc{@dataset.limit(0)}.should raise_error(Sequel::Error)
980
+ proc{@dataset.limit(1)}.should_not raise_error(Sequel::Error)
981
+ proc{@dataset.limit(1, -1)}.should raise_error(Sequel::Error)
982
+ proc{@dataset.limit(1, 0)}.should_not raise_error(Sequel::Error)
983
+ proc{@dataset.limit(1, 1)}.should_not raise_error(Sequel::Error)
984
+ end
926
985
  end
927
986
 
928
987
  context "Dataset#naked" do
@@ -947,14 +1006,18 @@ context "Dataset#qualified_column_name" do
947
1006
  @dataset = Sequel::Dataset.new(nil).from(:test)
948
1007
  end
949
1008
 
950
- specify "should return the same if already qualified" do
951
- @dataset.send(:qualified_column_name, 'test.a'.lit, :items).should == 'test.a'
952
- @dataset.send(:qualified_column_name, :ccc__b, :items).should == :ccc__b
1009
+ specify "should return the literal value if not given a symbol" do
1010
+ @dataset.literal(@dataset.send(:qualified_column_name, 'ccc__b', :items)).should == "'ccc__b'"
1011
+ @dataset.literal(@dataset.send(:qualified_column_name, 3, :items)).should == '3'
1012
+ @dataset.literal(@dataset.send(:qualified_column_name, 'a'.lit, :items)).should == 'a'
953
1013
  end
954
1014
 
955
- specify "should qualify the column with the supplied table name" do
956
- @dataset.send(:qualified_column_name, 'a'.lit, :items).to_s(@dataset).should == 'items.a'
957
- @dataset.send(:qualified_column_name, :b1, :items).to_s(@dataset).should == 'items.b1'
1015
+ specify "should qualify the column with the supplied table name if given an unqualified symbol" do
1016
+ @dataset.literal(@dataset.send(:qualified_column_name, :b1, :items)).should == 'items.b1'
1017
+ end
1018
+
1019
+ specify "should not changed the qualifed column's table if given a qualified symbol" do
1020
+ @dataset.literal(@dataset.send(:qualified_column_name, :ccc__b, :items)).should == 'ccc.b'
958
1021
  end
959
1022
  end
960
1023
 
@@ -983,7 +1046,7 @@ context "Dataset#map" do
983
1046
  end
984
1047
 
985
1048
  specify "should return the complete dataset values if nothing is given" do
986
- @d.map.should == DummyDataset::VALUES
1049
+ @d.map.to_a.should == DummyDataset::VALUES
987
1050
  end
988
1051
  end
989
1052
 
@@ -1035,7 +1098,7 @@ context "Dataset#count" do
1035
1098
 
1036
1099
  specify "should format SQL properly" do
1037
1100
  @dataset.count.should == 1
1038
- @c.sql.should == 'SELECT COUNT(*) FROM test'
1101
+ @c.sql.should == 'SELECT COUNT(*) FROM test LIMIT 1'
1039
1102
  end
1040
1103
 
1041
1104
  specify "should be aliased by #size" do
@@ -1043,14 +1106,14 @@ context "Dataset#count" do
1043
1106
  end
1044
1107
 
1045
1108
  specify "should include the where clause if it's there" do
1046
- @dataset.filter {:abc < 30}.count.should == 1
1047
- @c.sql.should == 'SELECT COUNT(*) FROM test WHERE (abc < 30)'
1109
+ @dataset.filter(:abc < 30).count.should == 1
1110
+ @c.sql.should == 'SELECT COUNT(*) FROM test WHERE (abc < 30) LIMIT 1'
1048
1111
  end
1049
1112
 
1050
1113
  specify "should count properly for datasets with fixed sql" do
1051
1114
  @dataset.opts[:sql] = "select abc from xyz"
1052
1115
  @dataset.count.should == 1
1053
- @c.sql.should == "SELECT COUNT(*) FROM (select abc from xyz) t1"
1116
+ @c.sql.should == "SELECT COUNT(*) FROM (select abc from xyz) t1 LIMIT 1"
1054
1117
  end
1055
1118
  end
1056
1119
 
@@ -1106,7 +1169,7 @@ context "Dataset#empty?" do
1106
1169
  @dataset = Sequel::Dataset.new(@db).from(:test)
1107
1170
 
1108
1171
  @dataset.should_not be_empty
1109
- @db.sqls.last.should == 'SELECT 1 WHERE EXISTS (SELECT * FROM test)'
1172
+ @db.sqls.last.should == 'SELECT 1 WHERE (EXISTS (SELECT * FROM test)) LIMIT 1'
1110
1173
 
1111
1174
  @db.meta_def(:dataset) do
1112
1175
  ds = $cccc.new(self)
@@ -1121,26 +1184,32 @@ end
1121
1184
  context "Dataset#join_table" do
1122
1185
  setup do
1123
1186
  @d = Sequel::Dataset.new(nil).from(:items)
1187
+ @d.quote_identifiers = true
1124
1188
  end
1125
1189
 
1126
1190
  specify "should format the JOIN clause properly" do
1127
1191
  @d.join_table(:left_outer, :categories, :category_id => :id).sql.should ==
1128
- 'SELECT * FROM items LEFT OUTER JOIN categories ON (categories.category_id = items.id)'
1192
+ 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1193
+ end
1194
+
1195
+ specify "should handle multiple conditions on the same join table column" do
1196
+ @d.join_table(:left_outer, :categories, [[:category_id, :id], [:category_id, 0..100]]).sql.should ==
1197
+ 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON (("categories"."category_id" = "items"."id") AND (("categories"."category_id" >= 0) AND ("categories"."category_id" <= 100)))'
1129
1198
  end
1130
1199
 
1131
1200
  specify "should include WHERE clause if applicable" do
1132
- @d.filter {:price < 100}.join_table(:right_outer, :categories, :category_id => :id).sql.should ==
1133
- 'SELECT * FROM items RIGHT OUTER JOIN categories ON (categories.category_id = items.id) WHERE (price < 100)'
1201
+ @d.filter(:price < 100).join_table(:right_outer, :categories, :category_id => :id).sql.should ==
1202
+ 'SELECT * FROM "items" RIGHT OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id") WHERE ("price" < 100)'
1134
1203
  end
1135
1204
 
1136
1205
  specify "should include ORDER BY clause if applicable" do
1137
1206
  @d.order(:stamp).join_table(:full_outer, :categories, :category_id => :id).sql.should ==
1138
- 'SELECT * FROM items FULL OUTER JOIN categories ON (categories.category_id = items.id) ORDER BY stamp'
1207
+ 'SELECT * FROM "items" FULL OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id") ORDER BY "stamp"'
1139
1208
  end
1140
1209
 
1141
1210
  specify "should support multiple joins" do
1142
1211
  @d.join_table(:inner, :b, :items_id).join_table(:left_outer, :c, :b_id => :b__id).sql.should ==
1143
- 'SELECT * FROM items INNER JOIN b ON (b.items_id = items.id) LEFT OUTER JOIN c ON (c.b_id = b.id)'
1212
+ 'SELECT * FROM "items" INNER JOIN "b" ON ("b"."items_id" = "items"."id") LEFT OUTER JOIN "c" ON ("c"."b_id" = "b"."id")'
1144
1213
  end
1145
1214
 
1146
1215
  specify "should use id as implicit relation primary key if omitted" do
@@ -1149,47 +1218,47 @@ context "Dataset#join_table" do
1149
1218
 
1150
1219
  # when doing multiple joins, id should be qualified using the last joined table
1151
1220
  @d.join_table(:right_outer, :b, :items_id).join_table(:full_outer, :c, :b_id).sql.should ==
1152
- 'SELECT * FROM items RIGHT OUTER JOIN b ON (b.items_id = items.id) FULL OUTER JOIN c ON (c.b_id = b.id)'
1221
+ 'SELECT * FROM "items" RIGHT OUTER JOIN "b" ON ("b"."items_id" = "items"."id") FULL OUTER JOIN "c" ON ("c"."b_id" = "b"."id")'
1153
1222
  end
1154
1223
 
1155
1224
  specify "should support left outer joins" do
1156
1225
  @d.join_table(:left_outer, :categories, :category_id).sql.should ==
1157
- 'SELECT * FROM items LEFT OUTER JOIN categories ON (categories.category_id = items.id)'
1226
+ 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1158
1227
 
1159
1228
  @d.left_outer_join(:categories, :category_id).sql.should ==
1160
- 'SELECT * FROM items LEFT OUTER JOIN categories ON (categories.category_id = items.id)'
1229
+ 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1161
1230
  end
1162
1231
 
1163
1232
  specify "should support right outer joins" do
1164
1233
  @d.join_table(:right_outer, :categories, :category_id).sql.should ==
1165
- 'SELECT * FROM items RIGHT OUTER JOIN categories ON (categories.category_id = items.id)'
1234
+ 'SELECT * FROM "items" RIGHT OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1166
1235
 
1167
1236
  @d.right_outer_join(:categories, :category_id).sql.should ==
1168
- 'SELECT * FROM items RIGHT OUTER JOIN categories ON (categories.category_id = items.id)'
1237
+ 'SELECT * FROM "items" RIGHT OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1169
1238
  end
1170
1239
 
1171
1240
  specify "should support full outer joins" do
1172
1241
  @d.join_table(:full_outer, :categories, :category_id).sql.should ==
1173
- 'SELECT * FROM items FULL OUTER JOIN categories ON (categories.category_id = items.id)'
1242
+ 'SELECT * FROM "items" FULL OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1174
1243
 
1175
1244
  @d.full_outer_join(:categories, :category_id).sql.should ==
1176
- 'SELECT * FROM items FULL OUTER JOIN categories ON (categories.category_id = items.id)'
1245
+ 'SELECT * FROM "items" FULL OUTER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1177
1246
  end
1178
1247
 
1179
1248
  specify "should support inner joins" do
1180
1249
  @d.join_table(:inner, :categories, :category_id).sql.should ==
1181
- 'SELECT * FROM items INNER JOIN categories ON (categories.category_id = items.id)'
1250
+ 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1182
1251
 
1183
1252
  @d.inner_join(:categories, :category_id).sql.should ==
1184
- 'SELECT * FROM items INNER JOIN categories ON (categories.category_id = items.id)'
1253
+ 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1185
1254
  end
1186
1255
 
1187
1256
  specify "should default to an inner join" do
1188
1257
  @d.join_table(nil, :categories, :category_id).sql.should ==
1189
- 'SELECT * FROM items INNER JOIN categories ON (categories.category_id = items.id)'
1258
+ 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1190
1259
 
1191
1260
  @d.join(:categories, :category_id).sql.should ==
1192
- 'SELECT * FROM items INNER JOIN categories ON (categories.category_id = items.id)'
1261
+ 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."category_id" = "items"."id")'
1193
1262
  end
1194
1263
 
1195
1264
  specify "should raise if an invalid join type is specified" do
@@ -1197,27 +1266,28 @@ context "Dataset#join_table" do
1197
1266
  end
1198
1267
 
1199
1268
  specify "should support aliased tables" do
1200
- @d.from('stats s').join('players p', :id => :player_id).sql.should ==
1201
- 'SELECT * FROM stats s INNER JOIN players p ON (p.id = s.player_id)'
1269
+ @d.from('stats').join('players', {:id => :player_id}, 'p').sql.should ==
1270
+ 'SELECT * FROM "stats" INNER JOIN "players" "p" ON ("p"."id" = "stats"."player_id")'
1202
1271
 
1203
- ds = Sequel::Dataset.new(nil).from(:foo => :f). \
1204
- join_table(:inner, :bar, :id => :bar_id).sql.should ==
1205
- 'SELECT * FROM foo f INNER JOIN bar ON (bar.id = f.bar_id)'
1272
+ ds = Sequel::Dataset.new(nil).from(:foo => :f)
1273
+ ds.quote_identifiers = true
1274
+ ds.join_table(:inner, :bar, :id => :bar_id).sql.should ==
1275
+ 'SELECT * FROM "foo" "f" INNER JOIN "bar" ON ("bar"."id" = "f"."bar_id")'
1206
1276
  end
1207
1277
 
1208
1278
  specify "should allow for arbitrary conditions in the JOIN clause" do
1209
1279
  @d.join_table(:left_outer, :categories, :status => 0).sql.should ==
1210
- 'SELECT * FROM items LEFT OUTER JOIN categories ON (categories.status = 0)'
1280
+ 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."status" = 0)'
1211
1281
  @d.join_table(:left_outer, :categories, :categorizable_type => "Post").sql.should ==
1212
- "SELECT * FROM items LEFT OUTER JOIN categories ON (categories.categorizable_type = 'Post')"
1282
+ 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."categorizable_type" = \'Post\')'
1213
1283
  @d.join_table(:left_outer, :categories, :timestamp => "CURRENT_TIMESTAMP".lit).sql.should ==
1214
- "SELECT * FROM items LEFT OUTER JOIN categories ON (categories.timestamp = CURRENT_TIMESTAMP)"
1284
+ 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."timestamp" = CURRENT_TIMESTAMP)'
1215
1285
  @d.join_table(:left_outer, :categories, :status => [1, 2, 3]).sql.should ==
1216
- "SELECT * FROM items LEFT OUTER JOIN categories ON (categories.status IN (1, 2, 3))"
1286
+ 'SELECT * FROM "items" LEFT OUTER JOIN "categories" ON ("categories"."status" IN (1, 2, 3))'
1217
1287
  end
1218
1288
 
1219
1289
  specify "should raise error for a table without a source" do
1220
- proc {Sequel::Dataset.new(nil).join('players p', :id => :player_id)}. \
1290
+ proc {Sequel::Dataset.new(nil).join('players', :id => :player_id)}. \
1221
1291
  should raise_error(Sequel::Error)
1222
1292
  end
1223
1293
 
@@ -1225,12 +1295,12 @@ context "Dataset#join_table" do
1225
1295
  ds = Sequel::Dataset.new(nil).from(:categories)
1226
1296
 
1227
1297
  @d.join_table(:left_outer, ds, :item_id => :id).sql.should ==
1228
- 'SELECT * FROM items LEFT OUTER JOIN (SELECT * FROM categories) t1 ON (t1.item_id = items.id)'
1298
+ 'SELECT * FROM "items" LEFT OUTER JOIN (SELECT * FROM categories) "t1" ON ("t1"."item_id" = "items"."id")'
1229
1299
 
1230
1300
  ds.filter!(:active => true)
1231
1301
 
1232
1302
  @d.join_table(:left_outer, ds, :item_id => :id).sql.should ==
1233
- 'SELECT * FROM items LEFT OUTER JOIN (SELECT * FROM categories WHERE (active = \'t\')) t1 ON (t1.item_id = items.id)'
1303
+ 'SELECT * FROM "items" LEFT OUTER JOIN (SELECT * FROM categories WHERE (active = \'t\')) "t1" ON ("t1"."item_id" = "items"."id")'
1234
1304
  end
1235
1305
 
1236
1306
  specify "should support joining multiple datasets" do
@@ -1239,9 +1309,9 @@ context "Dataset#join_table" do
1239
1309
  ds3 = Sequel::Dataset.new(nil).from(:attributes).filter("name = 'blah'")
1240
1310
 
1241
1311
  @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 ==
1242
- 'SELECT * FROM items LEFT OUTER JOIN (SELECT * FROM categories) t1 ON (t1.item_id = items.id) ' \
1243
- 'INNER JOIN (SELECT name FROM nodes) t2 ON (t2.node_id = t1.id) ' \
1244
- "RIGHT OUTER JOIN (SELECT * FROM attributes WHERE name = 'blah') t3 ON (t3.attribute_id = t2.id)"
1312
+ 'SELECT * FROM "items" LEFT OUTER JOIN (SELECT * FROM categories) "t1" ON ("t1"."item_id" = "items"."id") ' \
1313
+ 'INNER JOIN (SELECT name FROM nodes) "t2" ON ("t2"."node_id" = "t1"."id") ' \
1314
+ 'RIGHT OUTER JOIN (SELECT * FROM attributes WHERE (name = \'blah\')) "t3" ON ("t3"."attribute_id" = "t2"."id")'
1245
1315
  end
1246
1316
 
1247
1317
  specify "should support joining objects that respond to :table_name" do
@@ -1249,7 +1319,7 @@ context "Dataset#join_table" do
1249
1319
  def ds.table_name; :categories end
1250
1320
 
1251
1321
  @d.join(ds, :item_id => :id).sql.should ==
1252
- 'SELECT * FROM items INNER JOIN categories ON (categories.item_id = items.id)'
1322
+ 'SELECT * FROM "items" INNER JOIN "categories" ON ("categories"."item_id" = "items"."id")'
1253
1323
  end
1254
1324
  end
1255
1325
 
@@ -1292,11 +1362,13 @@ context "Dataset#set" do
1292
1362
  specify "should act as alias to #update" do
1293
1363
  @d.set({:x => 3})
1294
1364
  @d.last_sql.should == 'UPDATE items SET x = 3'
1365
+ end
1295
1366
 
1296
- @d.set {:x << :x + 1}
1367
+ pt_specify "should accept a block" do
1368
+ @d.set{:x << :x + 1}
1297
1369
  @d.last_sql.should == 'UPDATE items SET x = (x + 1)'
1298
1370
 
1299
- @d.set {(:x|1) << (:x|2) + 1}
1371
+ @d.set{(:x|1) << (:x|2) + 1}
1300
1372
  @d.last_sql.should == 'UPDATE items SET x[1] = (x[2] + 1)'
1301
1373
  end
1302
1374
  end
@@ -1338,23 +1410,23 @@ context "Dataset aggregate methods" do
1338
1410
  end
1339
1411
 
1340
1412
  specify "should include min" do
1341
- @d.min(:a).should == 'SELECT min(a) AS v FROM test'
1413
+ @d.min(:a).should == 'SELECT min(a) FROM test LIMIT 1'
1342
1414
  end
1343
1415
 
1344
1416
  specify "should include max" do
1345
- @d.max(:b).should == 'SELECT max(b) AS v FROM test'
1417
+ @d.max(:b).should == 'SELECT max(b) FROM test LIMIT 1'
1346
1418
  end
1347
1419
 
1348
1420
  specify "should include sum" do
1349
- @d.sum(:c).should == 'SELECT sum(c) AS v FROM test'
1421
+ @d.sum(:c).should == 'SELECT sum(c) FROM test LIMIT 1'
1350
1422
  end
1351
1423
 
1352
1424
  specify "should include avg" do
1353
- @d.avg(:d).should == 'SELECT avg(d) AS v FROM test'
1425
+ @d.avg(:d).should == 'SELECT avg(d) FROM test LIMIT 1'
1354
1426
  end
1355
1427
 
1356
1428
  specify "should accept qualified columns" do
1357
- @d.avg(:test__bc).should == 'SELECT avg(test.bc) AS v FROM test'
1429
+ @d.avg(:test__bc).should == 'SELECT avg(test.bc) FROM test LIMIT 1'
1358
1430
  end
1359
1431
  end
1360
1432
 
@@ -1377,17 +1449,16 @@ context "Dataset#range" do
1377
1449
  @d.range(:stamp)
1378
1450
  @d.last_sql.should == "SELECT min(stamp) AS v1, max(stamp) AS v2 FROM test LIMIT 1"
1379
1451
 
1380
- @d.filter {:price > 100}.range(:stamp)
1452
+ @d.filter(:price > 100).range(:stamp)
1381
1453
  @d.last_sql.should == "SELECT min(stamp) AS v1, max(stamp) AS v2 FROM test WHERE (price > 100) LIMIT 1"
1382
1454
  end
1383
1455
 
1384
1456
  specify "should return a range object" do
1385
1457
  @d.range(:tryme).should == (1..10)
1386
- @d.last_sql.should == "SELECT min(tryme) AS v1, max(tryme) AS v2 FROM test LIMIT 1"
1387
1458
  end
1388
1459
  end
1389
1460
 
1390
- context "Dataset#range" do
1461
+ context "Dataset#interval" do
1391
1462
  setup do
1392
1463
  c = Class.new(Sequel::Dataset) do
1393
1464
  @@sql = nil
@@ -1404,147 +1475,77 @@ context "Dataset#range" do
1404
1475
 
1405
1476
  specify "should generate a correct SQL statement" do
1406
1477
  @d.interval(:stamp)
1407
- @d.last_sql.should == "SELECT (max(stamp) - min(stamp)) AS v FROM test LIMIT 1"
1478
+ @d.last_sql.should == "SELECT (max(stamp) - min(stamp)) FROM test LIMIT 1"
1408
1479
 
1409
- @d.filter {:price > 100}.interval(:stamp)
1410
- @d.last_sql.should == "SELECT (max(stamp) - min(stamp)) AS v FROM test WHERE (price > 100) LIMIT 1"
1480
+ @d.filter(:price > 100).interval(:stamp)
1481
+ @d.last_sql.should == "SELECT (max(stamp) - min(stamp)) FROM test WHERE (price > 100) LIMIT 1"
1411
1482
  end
1412
1483
 
1413
- specify "should return a range object" do
1484
+ specify "should return an integer" do
1414
1485
  @d.interval(:tryme).should == 1234
1415
- @d.last_sql.should == "SELECT (max(tryme) - min(tryme)) AS v FROM test LIMIT 1"
1416
1486
  end
1417
1487
  end
1418
1488
 
1419
- context "Dataset#first" do
1489
+ context "Dataset #first and #last" do
1420
1490
  setup do
1421
1491
  @c = Class.new(Sequel::Dataset) do
1422
- @@last_dataset = nil
1423
- @@last_opts = nil
1424
-
1425
- def self.last_dataset
1426
- @@last_dataset
1427
- end
1428
-
1429
- def self.last_opts
1430
- @@last_opts
1431
- end
1432
-
1433
- def single_record(opts = nil)
1434
- @@last_opts = @opts.merge(opts || {})
1435
- {:a => 1, :b => 2}
1436
- end
1437
-
1438
- def all
1439
- @@last_dataset = self
1440
- [{:a => 1, :b => 2}] * @opts[:limit]
1492
+ def each(opts = nil, &block)
1493
+ s = select_sql(opts)
1494
+ x = [:a,1,:b,2,s]
1495
+ i = /LIMIT (\d+)/.match(s)[1].to_i.times{yield x}
1441
1496
  end
1442
1497
  end
1443
1498
  @d = @c.new(nil).from(:test)
1444
1499
  end
1445
1500
 
1446
- specify "should return the first matching record if a hash is specified" do
1447
- @d.first(:z => 26).should == {:a => 1, :b => 2}
1448
- @c.last_opts[:where].should == ('(z = 26)')
1501
+ specify "should return a single record if no argument is given" do
1502
+ @d.order(:a).first.should == [:a,1,:b,2, 'SELECT * FROM test ORDER BY a LIMIT 1']
1503
+ @d.order(:a).last.should == [:a,1,:b,2, 'SELECT * FROM test ORDER BY a DESC LIMIT 1']
1504
+ end
1449
1505
 
1450
- @d.first('z = ?', 15)
1451
- @c.last_opts[:where].should == ('z = 15')
1506
+ specify "should return the first/last matching record if argument is not an Integer" do
1507
+ @d.order(:a).first(:z => 26).should == [:a,1,:b,2, 'SELECT * FROM test WHERE (z = 26) ORDER BY a LIMIT 1']
1508
+ @d.order(:a).first('z = ?', 15).should == [:a,1,:b,2, 'SELECT * FROM test WHERE (z = 15) ORDER BY a LIMIT 1']
1509
+ @d.order(:a).last(:z => 26).should == [:a,1,:b,2, 'SELECT * FROM test WHERE (z = 26) ORDER BY a DESC LIMIT 1']
1510
+ @d.order(:a).last('z = ?', 15).should == [:a,1,:b,2, 'SELECT * FROM test WHERE (z = 15) ORDER BY a DESC LIMIT 1']
1452
1511
  end
1453
1512
 
1454
- specify "should return the first matching record if a block is given" do
1455
- @d.first {:z > 26}.should == {:a => 1, :b => 2}
1456
- @c.last_opts[:where].should == ('(z > 26)')
1513
+ specify "should set the limit and return an array of records if the given number is > 1" do
1514
+ i = rand(10) + 10
1515
+ r = @d.order(:a).first(i).should == [[:a,1,:b,2, "SELECT * FROM test ORDER BY a LIMIT #{i}"]] * i
1516
+ i = rand(10) + 10
1517
+ r = @d.order(:a).last(i).should == [[:a,1,:b,2, "SELECT * FROM test ORDER BY a DESC LIMIT #{i}"]] * i
1457
1518
  end
1458
1519
 
1459
- specify "should return a single record if no argument is given" do
1460
- @d.first.should == {:a => 1, :b => 2}
1520
+ pt_specify "should return the first matching record if a block is given without an argument" do
1521
+ @d.first{:z > 26}.should == [:a,1,:b,2, 'SELECT * FROM test WHERE (z > 26) LIMIT 1']
1522
+ @d.order(:name).last{:z > 26}.should == [:a,1,:b,2, 'SELECT * FROM test WHERE (z > 26) ORDER BY name DESC LIMIT 1']
1461
1523
  end
1462
1524
 
1463
- specify "should set the limit according to the given number" do
1464
- @d.first
1465
- @c.last_opts[:limit].should == 1
1466
-
1467
- i = rand(10) + 10
1468
- @d.first(i)
1469
- @c.last_dataset.opts[:limit].should == i
1525
+ pt_specify "should combine block and standard argument filters if argument is not an Integer" do
1526
+ @d.first(:y=>25){:z > 26}.should == [:a,1,:b,2, 'SELECT * FROM test WHERE ((z > 26) AND (y = 25)) LIMIT 1']
1527
+ @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']
1470
1528
  end
1471
1529
 
1472
- specify "should return an array with the records if argument is greater than 1" do
1530
+ pt_specify "should filter and return an array of records if an Integer argument is provided and a block is given" do
1473
1531
  i = rand(10) + 10
1474
- r = @d.first(i)
1475
- r.should be_a_kind_of(Array)
1476
- r.size.should == i
1477
- r.each {|row| row.should == {:a => 1, :b => 2}}
1478
- end
1479
- end
1480
-
1481
- context "Dataset#last" do
1482
- setup do
1483
- @c = Class.new(Sequel::Dataset) do
1484
- @@last_dataset = nil
1485
-
1486
- def self.last_dataset
1487
- @@last_dataset
1488
- end
1489
-
1490
- def single_record(opts = nil)
1491
- @@last_dataset = clone(opts) if opts
1492
- {:a => 1, :b => 2}
1493
- end
1494
-
1495
- def all
1496
- @@last_dataset = self
1497
- [{:a => 1, :b => 2}] * @opts[:limit]
1498
- end
1499
- end
1500
- @d = @c.new(nil).from(:test)
1532
+ 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
1533
+ i = rand(10) + 10
1534
+ 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
1501
1535
  end
1502
1536
 
1503
- specify "should raise if no order is given" do
1537
+ specify "#last should raise if no order is given" do
1504
1538
  proc {@d.last}.should raise_error(Sequel::Error)
1505
1539
  proc {@d.last(2)}.should raise_error(Sequel::Error)
1506
1540
  proc {@d.order(:a).last}.should_not raise_error
1507
1541
  proc {@d.order(:a).last(2)}.should_not raise_error
1508
1542
  end
1509
1543
 
1510
- specify "should invert the order" do
1511
- @d.order(:a).last
1512
- @d.literal(@c.last_dataset.opts[:order]).should == @d.literal([:a.desc])
1513
-
1514
- @d.order(:b.desc).last
1515
- @d.literal(@c.last_dataset.opts[:order]).should == @d.literal(:b)
1516
-
1517
- @d.order(:c, :d).last
1518
- @d.literal(@c.last_dataset.opts[:order]).should == @d.literal([:c.desc, :d.desc])
1519
-
1520
- @d.order(:e.desc, :f).last
1521
- @d.literal(@c.last_dataset.opts[:order]).should == @d.literal([:e, :f.desc])
1522
- end
1523
-
1524
- specify "should return the first matching record if a hash is specified" do
1525
- @d.order(:a).last(:z => 26).should == {:a => 1, :b => 2}
1526
- @c.last_dataset.opts[:where].should == ('(z = 26)')
1527
-
1528
- @d.order(:a).last('z = ?', 15)
1529
- @c.last_dataset.opts[:where].should == ('z = 15')
1530
- end
1531
-
1532
- specify "should return a single record if no argument is given" do
1533
- @d.order(:a).last.should == {:a => 1, :b => 2}
1534
- end
1535
-
1536
- specify "should set the limit according to the given number" do
1537
- i = rand(10) + 10
1538
- r = @d.order(:a).last(i)
1539
- @c.last_dataset.opts[:limit].should == i
1540
- end
1541
-
1542
- specify "should return an array with the records if argument is greater than 1" do
1543
- i = rand(10) + 10
1544
- r = @d.order(:a).last(i)
1545
- r.should be_a_kind_of(Array)
1546
- r.size.should == i
1547
- r.each {|row| row.should == {:a => 1, :b => 2}}
1544
+ specify "#last should invert the order" do
1545
+ @d.order(:a).last.pop.should == 'SELECT * FROM test ORDER BY a DESC LIMIT 1'
1546
+ @d.order(:b.desc).last.pop.should == 'SELECT * FROM test ORDER BY b LIMIT 1'
1547
+ @d.order(:c, :d).last.pop.should == 'SELECT * FROM test ORDER BY c DESC, d DESC LIMIT 1'
1548
+ @d.order(:e.desc, :f).last.pop.should == 'SELECT * FROM test ORDER BY e, f DESC LIMIT 1'
1548
1549
  end
1549
1550
  end
1550
1551
 
@@ -1595,10 +1596,10 @@ context "Dataset#[]" do
1595
1596
 
1596
1597
  specify "should return a single record filtered according to the given conditions" do
1597
1598
  @d[:name => 'didi'].should == {1 => 2, 3 => 4}
1598
- @c.last_dataset.opts[:where].should == "(name = 'didi')"
1599
+ @c.last_dataset.literal(@c.last_dataset.opts[:where]).should == "(name = 'didi')"
1599
1600
 
1600
1601
  @d[:id => 5..45].should == {1 => 2, 3 => 4}
1601
- @c.last_dataset.opts[:where].should == "(id >= 5 AND id <= 45)"
1602
+ @c.last_dataset.literal(@c.last_dataset.opts[:where]).should == "((id >= 5) AND (id <= 45))"
1602
1603
  end
1603
1604
  end
1604
1605
 
@@ -1617,14 +1618,18 @@ context "Dataset#single_record" do
1617
1618
  @e = @cc.new(nil).from(:test)
1618
1619
  end
1619
1620
 
1620
- specify "should call each and return the first record" do
1621
- @d.single_record.should == 'SELECT * FROM test'
1621
+ specify "should call each with a limit of 1 and return the record" do
1622
+ @d.single_record.should == 'SELECT * FROM test LIMIT 1'
1622
1623
  end
1623
1624
 
1624
1625
  specify "should pass opts to each" do
1625
- @d.single_record(:limit => 3).should == 'SELECT * FROM test LIMIT 3'
1626
+ @d.single_record(:order => [:name]).should == 'SELECT * FROM test ORDER BY name LIMIT 1'
1626
1627
  end
1627
1628
 
1629
+ specify "should override the limit if passed as an option" do
1630
+ @d.single_record(:limit => 3).should == 'SELECT * FROM test LIMIT 1'
1631
+ end
1632
+
1628
1633
  specify "should return nil if no record is present" do
1629
1634
  @e.single_record.should be_nil
1630
1635
  end
@@ -1646,14 +1651,14 @@ context "Dataset#single_value" do
1646
1651
  end
1647
1652
 
1648
1653
  specify "should call each and return the first value of the first record" do
1649
- @d.single_value.should == 'SELECT * FROM test'
1654
+ @d.single_value.should == 'SELECT * FROM test LIMIT 1'
1650
1655
  end
1651
1656
 
1652
1657
  specify "should pass opts to each" do
1653
- @d.single_value(:limit => 3).should == 'SELECT * FROM test LIMIT 3'
1658
+ @d.single_value(:from => [:blah]).should == 'SELECT * FROM blah LIMIT 1'
1654
1659
  end
1655
1660
 
1656
- specify "should return nil" do
1661
+ specify "should return nil if no records" do
1657
1662
  @e.single_value.should be_nil
1658
1663
  end
1659
1664
  end
@@ -1673,16 +1678,16 @@ context "Dataset#get" do
1673
1678
  end
1674
1679
 
1675
1680
  specify "should select the specified column and fetch its value" do
1676
- @d.get(:name).should == "SELECT name FROM test"
1677
- @d.get(:abc).should == "SELECT abc FROM test" # the first available value is returned always
1681
+ @d.get(:name).should == "SELECT name FROM test LIMIT 1"
1682
+ @d.get(:abc).should == "SELECT abc FROM test LIMIT 1" # the first available value is returned always
1678
1683
  end
1679
1684
 
1680
1685
  specify "should work with filters" do
1681
- @d.filter(:id => 1).get(:name).should == "SELECT name FROM test WHERE (id = 1)"
1686
+ @d.filter(:id => 1).get(:name).should == "SELECT name FROM test WHERE (id = 1) LIMIT 1"
1682
1687
  end
1683
1688
 
1684
1689
  specify "should work with aliased fields" do
1685
- @d.get(:x__b.as(:name)).should == "SELECT x.b AS name FROM test"
1690
+ @d.get(:x__b.as(:name)).should == "SELECT x.b AS name FROM test LIMIT 1"
1686
1691
  end
1687
1692
  end
1688
1693
 
@@ -1749,12 +1754,6 @@ context "Dataset#set_model" do
1749
1754
  @dataset.first.should == @m.new({:kind => 1})
1750
1755
  end
1751
1756
 
1752
- specify "should extend the dataset with a #destroy method" do
1753
- @dataset.should_not respond_to(:destroy)
1754
- @dataset.set_model(@m)
1755
- @dataset.should respond_to(:destroy)
1756
- end
1757
-
1758
1757
  specify "should set opts[:naked] to nil" do
1759
1758
  @dataset.opts[:naked] = true
1760
1759
  @dataset.set_model(@m)
@@ -1964,58 +1963,10 @@ context "A dataset with associated model class(es)" do
1964
1963
  end
1965
1964
  end
1966
1965
 
1967
- context "Dataset#destroy" do
1968
- setup do
1969
- db = Object.new
1970
- m = Module.new do
1971
- def transaction; yield; end
1972
- end
1973
- db.extend(m)
1974
-
1975
- $DESTROYED = []
1976
-
1977
- @m = Class.new do
1978
- def initialize(c)
1979
- @c = c
1980
- end
1981
-
1982
- attr_accessor :c
1983
-
1984
- def ==(o)
1985
- @c == o.c
1986
- end
1987
-
1988
- def destroy
1989
- $DESTROYED << self
1990
- end
1991
- end
1992
- $MODELS = [@m.new(12), @m.new(13)]
1993
-
1994
- c = Class.new(Sequel::Dataset) do
1995
- def fetch_rows(sql, &block)
1996
- (12..13).each(&block)
1997
- end
1998
- end
1999
-
2000
- @d = c.new(db).from(:test)
2001
- @d.set_model(@m)
2002
- end
2003
-
2004
- specify "should call destroy for every model instance in the dataset" do
2005
- count = @d.destroy
2006
- count.should == 2
2007
- $DESTROYED.should == $MODELS
2008
- end
2009
-
2010
- specify "should raise error if no models are associated with the dataset" do
2011
- proc {@d.naked.destroy}.should raise_error(Sequel::Error)
2012
- end
2013
- end
2014
-
2015
1966
  context "Dataset#<<" do
2016
1967
  setup do
2017
1968
  @d = Sequel::Dataset.new(nil)
2018
- @d.meta_def(:insert) do
1969
+ @d.meta_def(:insert) do |*args|
2019
1970
  1234567890
2020
1971
  end
2021
1972
  end
@@ -2080,7 +2031,20 @@ context "A paginated dataset" do
2080
2031
  @d.paginate(4, 50).current_page_record_count.should == 3
2081
2032
  @d.paginate(5, 50).current_page_record_count.should == 0
2082
2033
  end
2083
-
2034
+
2035
+ specify "should know if current page is last page" do
2036
+ @paginated.last_page?.should be_false
2037
+ @d.paginate(2, 20).last_page?.should be_false
2038
+ @d.paginate(5, 30).last_page?.should be_false
2039
+ @d.paginate(6, 30).last_page?.should be_true
2040
+ end
2041
+
2042
+ specify "should know if current page is first page" do
2043
+ @paginated.first_page?.should be_true
2044
+ @d.paginate(1, 20).first_page?.should be_true
2045
+ @d.paginate(2, 20).first_page?.should be_false
2046
+ end
2047
+
2084
2048
  specify "should work with fixed sql" do
2085
2049
  ds = @d.clone(:sql => 'select * from blah')
2086
2050
  ds.meta_def(:count) {150}
@@ -2114,34 +2078,35 @@ context "Dataset#columns" do
2114
2078
  setup do
2115
2079
  @dataset = DummyDataset.new(nil).from(:items)
2116
2080
  @dataset.meta_def(:columns=) {|c| @columns = c}
2117
- @dataset.meta_def(:first) {@columns = select_sql(nil)}
2081
+ i = 'a'
2082
+ @dataset.meta_def(:each) {|o| @columns = select_sql(o||@opts) + i; i = i.next}
2118
2083
  end
2119
2084
 
2120
- specify "should return the value of @columns" do
2085
+ specify "should return the value of @columns if @columns is not nil" do
2121
2086
  @dataset.columns = [:a, :b, :c]
2122
2087
  @dataset.columns.should == [:a, :b, :c]
2123
2088
  end
2124
2089
 
2125
- specify "should call first if @columns is nil" do
2090
+ specify "should attempt to get a single record and return @columns if @columns is nil" do
2126
2091
  @dataset.columns = nil
2127
- @dataset.columns.should == 'SELECT * FROM items'
2092
+ @dataset.columns.should == 'SELECT * FROM items LIMIT 1a'
2128
2093
  @dataset.opts[:from] = [:nana]
2129
- @dataset.columns.should == 'SELECT * FROM items'
2094
+ @dataset.columns.should == 'SELECT * FROM items LIMIT 1a'
2130
2095
  end
2131
2096
  end
2132
2097
 
2133
2098
  context "Dataset#columns!" do
2134
2099
  setup do
2135
2100
  @dataset = DummyDataset.new(nil).from(:items)
2136
- @dataset.meta_def(:columns=) {|c| @columns = c}
2137
- @dataset.meta_def(:first) {@columns = select_sql(nil)}
2101
+ i = 'a'
2102
+ @dataset.meta_def(:each) {|o| @columns = select_sql(o||@opts) + i; i = i.next}
2138
2103
  end
2139
2104
 
2140
- specify "should always call first" do
2141
- @dataset.columns = nil
2142
- @dataset.columns!.should == 'SELECT * FROM items'
2105
+ specify "should always attempt to get a record and return @columns" do
2106
+ @dataset.columns!.should == 'SELECT * FROM items LIMIT 1a'
2107
+ @dataset.columns!.should == 'SELECT * FROM items LIMIT 1b'
2143
2108
  @dataset.opts[:from] = [:nana]
2144
- @dataset.columns!.should == 'SELECT * FROM nana'
2109
+ @dataset.columns!.should == 'SELECT * FROM nana LIMIT 1c'
2145
2110
  end
2146
2111
  end
2147
2112
 
@@ -2336,13 +2301,13 @@ context "Dataset#query" do
2336
2301
  specify "should support #where" do
2337
2302
  q = @d.query do
2338
2303
  from :zzz
2339
- where {:x + 2 > :y + 3}
2304
+ where(:x + 2 > :y + 3)
2340
2305
  end
2341
2306
  q.class.should == @d.class
2342
2307
  q.sql.should == "SELECT * FROM zzz WHERE ((x + 2) > (y + 3))"
2343
2308
 
2344
2309
  q = @d.from(:zzz).query do
2345
- where {:x > 1 && :y > 2}
2310
+ where((:x > 1) & (:y > 2))
2346
2311
  end
2347
2312
  q.class.should == @d.class
2348
2313
  q.sql.should == "SELECT * FROM zzz WHERE ((x > 1) AND (y > 2))"
@@ -2358,7 +2323,7 @@ context "Dataset#query" do
2358
2323
  q = @d.query do
2359
2324
  from :abc
2360
2325
  group_by :id
2361
- having {:x >= 2}
2326
+ having(:x >= 2)
2362
2327
  end
2363
2328
  q.class.should == @d.class
2364
2329
  q.sql.should == "SELECT * FROM abc GROUP BY id HAVING (x >= 2)"
@@ -2374,7 +2339,7 @@ context "Dataset#query" do
2374
2339
  end
2375
2340
 
2376
2341
  specify "should raise on non-chainable method calls" do
2377
- proc {@d.query {count}}.should raise_error(Sequel::Error)
2342
+ proc {@d.query {first_source}}.should raise_error(Sequel::Error)
2378
2343
  end
2379
2344
 
2380
2345
  specify "should raise on each, insert, update, delete" do
@@ -2410,8 +2375,8 @@ context "Dataset" do
2410
2375
  @d.sql.should == "SELECT * FROM x WHERE (y = 1)"
2411
2376
  end
2412
2377
 
2413
- specify "should support self-changing filter! with block" do
2414
- @d.filter! {:y == 2}
2378
+ pt_specify "should support self-changing filter! with block" do
2379
+ @d.filter!{:y == 2}
2415
2380
  @d.sql.should == "SELECT * FROM x WHERE (y = 2)"
2416
2381
  end
2417
2382
 
@@ -2565,27 +2530,27 @@ context "Dataset#transform" do
2565
2530
  specify "should support stock Marshal transformation with Base64 encoding" do
2566
2531
  @ds.transform(:x => :marshal)
2567
2532
 
2568
- @ds.raw = {:x => Base64.encode64(Marshal.dump([1, 2, 3])), :y => 'hello'}
2533
+ @ds.raw = {:x => [Marshal.dump([1, 2, 3])].pack('m'), :y => 'hello'}
2569
2534
  @ds.first.should == {:x => [1, 2, 3], :y => 'hello'}
2570
2535
 
2571
2536
  @ds.insert(:x => :toast)
2572
- @ds.sql.should == "INSERT INTO items (x) VALUES ('#{Base64.encode64(Marshal.dump(:toast))}')"
2537
+ @ds.sql.should == "INSERT INTO items (x) VALUES ('#{[Marshal.dump(:toast)].pack('m')}')"
2573
2538
  @ds.insert(:y => 'butter')
2574
2539
  @ds.sql.should == "INSERT INTO items (y) VALUES ('butter')"
2575
2540
  @ds.update(:x => ['dream'])
2576
- @ds.sql.should == "UPDATE items SET x = '#{Base64.encode64(Marshal.dump(['dream']))}'"
2541
+ @ds.sql.should == "UPDATE items SET x = '#{[Marshal.dump(['dream'])].pack('m')}'"
2577
2542
 
2578
2543
  @ds2 = @ds.filter(:a => 1)
2579
- @ds2.raw = {:x => Base64.encode64(Marshal.dump([1, 2, 3])), :y => 'hello'}
2544
+ @ds2.raw = {:x => [Marshal.dump([1, 2, 3])].pack('m'), :y => 'hello'}
2580
2545
  @ds2.first.should == {:x => [1, 2, 3], :y => 'hello'}
2581
2546
  @ds2.insert(:x => :toast)
2582
- @ds2.sql.should == "INSERT INTO items (x) VALUES ('#{Base64.encode64(Marshal.dump(:toast))}')"
2547
+ @ds2.sql.should == "INSERT INTO items (x) VALUES ('#{[Marshal.dump(:toast)].pack('m')}')"
2583
2548
 
2584
2549
  @ds.row_proc = proc{|r| r[:z] = r[:x] * 2; r}
2585
- @ds.raw = {:x => Base64.encode64(Marshal.dump("wow")), :y => 'hello'}
2550
+ @ds.raw = {:x => [Marshal.dump("wow")].pack('m'), :y => 'hello'}
2586
2551
  @ds.first.should == {:x => "wow", :y => 'hello', :z => "wowwow"}
2587
2552
  f = nil
2588
- @ds.raw = {:x => Base64.encode64(Marshal.dump("wow")), :y => 'hello'}
2553
+ @ds.raw = {:x => [Marshal.dump("wow")].pack('m'), :y => 'hello'}
2589
2554
  @ds.each(:naked => true) {|r| f = r}
2590
2555
  f.should == {:x => "wow", :y => 'hello'}
2591
2556
  end
@@ -2611,7 +2576,7 @@ context "A dataset with a transform" do
2611
2576
  specify "should automatically transform hash filters" do
2612
2577
  @ds.filter(:y => 2).sql.should == 'SELECT * FROM items WHERE (y = 2)'
2613
2578
 
2614
- @ds.filter(:x => 2).sql.should == "SELECT * FROM items WHERE (x = '#{Base64.encode64(Marshal.dump(2))}')"
2579
+ @ds.filter(:x => 2).sql.should == "SELECT * FROM items WHERE (x = '#{[Marshal.dump(2)].pack('m')}')"
2615
2580
  end
2616
2581
  end
2617
2582
 
@@ -2647,88 +2612,6 @@ context "Dataset#to_csv" do
2647
2612
  end
2648
2613
  end
2649
2614
 
2650
- ### DEPRECATED
2651
- context "Dataset magic methods" do
2652
- setup do
2653
- @c = Class.new(Sequel::Dataset) do
2654
- @@sqls = []
2655
-
2656
- def self.sqls; @@sqls; end
2657
-
2658
- def fetch_rows(sql)
2659
- @@sqls << sql
2660
- yield({:a => 1, :b => 2})
2661
- end
2662
- end
2663
-
2664
- @ds = @c.new(nil).from(:items)
2665
- end
2666
-
2667
- specify "should support order_by_xxx" do
2668
- @ds.should_not respond_to(:order_by_name)
2669
- proc {@ds.order_by_name}.should_not raise_error
2670
- @ds.should respond_to(:order_by_name)
2671
- @ds.order_by_name.should be_a_kind_of(@c)
2672
- @ds.order_by_name.sql.should == "SELECT * FROM items ORDER BY name"
2673
- end
2674
-
2675
- specify "should support group_by_xxx" do
2676
- @ds.should_not respond_to(:group_by_name)
2677
- proc {@ds.group_by_name}.should_not raise_error
2678
- @ds.should respond_to(:group_by_name)
2679
- @ds.group_by_name.should be_a_kind_of(@c)
2680
- @ds.group_by_name.sql.should == "SELECT * FROM items GROUP BY name"
2681
- end
2682
-
2683
- specify "should support count_by_xxx" do
2684
- @ds.should_not respond_to(:count_by_name)
2685
- proc {@ds.count_by_name}.should_not raise_error
2686
- @ds.should respond_to(:count_by_name)
2687
- @ds.count_by_name.should be_a_kind_of(@c)
2688
- @ds.count_by_name.sql.should == "SELECT name, count(*) AS count FROM items GROUP BY name ORDER BY count"
2689
- end
2690
-
2691
- specify "should support filter_by_xxx" do
2692
- @ds.should_not respond_to(:filter_by_name)
2693
- proc {@ds.filter_by_name('sharon')}.should_not raise_error
2694
- @ds.should respond_to(:filter_by_name)
2695
- @ds.filter_by_name('sharon').should be_a_kind_of(@c)
2696
- @ds.filter_by_name('sharon').sql.should == "SELECT * FROM items WHERE (name = 'sharon')"
2697
- end
2698
-
2699
- specify "should support all_by_xxx" do
2700
- @ds.should_not respond_to(:all_by_name)
2701
- proc {@ds.all_by_name('sharon')}.should_not raise_error
2702
- @ds.should respond_to(:all_by_name)
2703
- @ds.all_by_name('sharon').should == [{:a => 1, :b => 2}]
2704
- @c.sqls.should == ["SELECT * FROM items WHERE (name = 'sharon')"] * 2
2705
- end
2706
-
2707
- specify "should support find_by_xxx" do
2708
- @ds.should_not respond_to(:find_by_name)
2709
- proc {@ds.find_by_name('sharon')}.should_not raise_error
2710
- @ds.should respond_to(:find_by_name)
2711
- @ds.find_by_name('sharon').should == {:a => 1, :b => 2}
2712
- @c.sqls.should == ["SELECT * FROM items WHERE (name = 'sharon') LIMIT 1"] * 2
2713
- end
2714
-
2715
- specify "should support first_by_xxx" do
2716
- @ds.should_not respond_to(:first_by_name)
2717
- proc {@ds.first_by_name('sharon')}.should_not raise_error
2718
- @ds.should respond_to(:first_by_name)
2719
- @ds.first_by_name('sharon').should == {:a => 1, :b => 2}
2720
- @c.sqls.should == ["SELECT * FROM items ORDER BY name LIMIT 1"] * 2
2721
- end
2722
-
2723
- specify "should support last_by_xxx" do
2724
- @ds.should_not respond_to(:last_by_name)
2725
- proc {@ds.last_by_name('sharon')}.should_not raise_error
2726
- @ds.should respond_to(:last_by_name)
2727
- @ds.last_by_name('sharon').should == {:a => 1, :b => 2}
2728
- @c.sqls.should == ["SELECT * FROM items ORDER BY name DESC LIMIT 1"] * 2
2729
- end
2730
- end
2731
-
2732
2615
  context "Dataset#create_view" do
2733
2616
  setup do
2734
2617
  @dbc = Class.new(Sequel::Database) do
@@ -2916,7 +2799,7 @@ context "Dataset#grep" do
2916
2799
 
2917
2800
  specify "should format a SQL filter correctly" do
2918
2801
  @ds.grep(:title, 'ruby').sql.should ==
2919
- "SELECT * FROM posts WHERE (title LIKE 'ruby')"
2802
+ "SELECT * FROM posts WHERE ((title LIKE 'ruby'))"
2920
2803
  end
2921
2804
 
2922
2805
  specify "should support multiple columns" do
@@ -2926,30 +2809,19 @@ context "Dataset#grep" do
2926
2809
 
2927
2810
  specify "should support multiple search terms" do
2928
2811
  @ds.grep(:title, ['abc', 'def']).sql.should ==
2929
- "SELECT * FROM posts WHERE ((title LIKE 'abc') OR (title LIKE 'def'))"
2812
+ "SELECT * FROM posts WHERE (((title LIKE 'abc') OR (title LIKE 'def')))"
2930
2813
  end
2931
2814
 
2932
2815
  specify "should support multiple columns and search terms" do
2933
2816
  @ds.grep([:title, :body], ['abc', 'def']).sql.should ==
2934
- "SELECT * FROM posts WHERE ((title LIKE 'abc') OR (title LIKE 'def') OR (body LIKE 'abc') OR (body LIKE 'def'))"
2935
- end
2936
-
2937
- specify "should support regexps if the dataset allows it" do
2938
- @ds.meta_def(:match_expr) do |l, r|
2939
- case r
2940
- when String
2941
- "(#{literal(l)} LIKE #{literal(r)})"
2942
- when Regexp
2943
- "(#{literal(l)} =~ #{literal(r.source)})"
2944
- else
2945
- raise Sequel::Error, "Unsupported match pattern class (#{r.class})."
2946
- end
2947
- end
2948
-
2817
+ "SELECT * FROM posts WHERE (((title LIKE 'abc') OR (title LIKE 'def')) OR ((body LIKE 'abc') OR (body LIKE 'def')))"
2818
+ end
2819
+
2820
+ specify "should support regexps though the database may not support it" do
2949
2821
  @ds.grep(:title, /ruby/).sql.should ==
2950
- "SELECT * FROM posts WHERE (title =~ 'ruby')"
2822
+ "SELECT * FROM posts WHERE ((title ~ 'ruby'))"
2951
2823
 
2952
2824
  @ds.grep(:title, [/^ruby/, 'ruby']).sql.should ==
2953
- "SELECT * FROM posts WHERE ((title =~ '^ruby') OR (title LIKE 'ruby'))"
2825
+ "SELECT * FROM posts WHERE (((title ~ '^ruby') OR (title LIKE 'ruby')))"
2954
2826
  end
2955
2827
  end