sequel 3.35.0 → 3.36.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 (81) hide show
  1. data/CHANGELOG +78 -0
  2. data/Rakefile +3 -3
  3. data/bin/sequel +3 -1
  4. data/doc/advanced_associations.rdoc +154 -11
  5. data/doc/migration.rdoc +18 -0
  6. data/doc/object_model.rdoc +541 -0
  7. data/doc/opening_databases.rdoc +4 -1
  8. data/doc/release_notes/3.36.0.txt +245 -0
  9. data/doc/schema_modification.rdoc +0 -6
  10. data/lib/sequel/adapters/do/mysql.rb +7 -0
  11. data/lib/sequel/adapters/jdbc.rb +11 -3
  12. data/lib/sequel/adapters/jdbc/mysql.rb +3 -5
  13. data/lib/sequel/adapters/jdbc/postgresql.rb +10 -8
  14. data/lib/sequel/adapters/jdbc/progress.rb +21 -0
  15. data/lib/sequel/adapters/mock.rb +2 -6
  16. data/lib/sequel/adapters/mysql.rb +3 -9
  17. data/lib/sequel/adapters/mysql2.rb +12 -11
  18. data/lib/sequel/adapters/postgres.rb +32 -40
  19. data/lib/sequel/adapters/shared/mssql.rb +15 -11
  20. data/lib/sequel/adapters/shared/mysql.rb +28 -3
  21. data/lib/sequel/adapters/shared/oracle.rb +5 -0
  22. data/lib/sequel/adapters/shared/postgres.rb +59 -5
  23. data/lib/sequel/adapters/shared/sqlite.rb +3 -13
  24. data/lib/sequel/adapters/sqlite.rb +0 -7
  25. data/lib/sequel/adapters/swift/mysql.rb +2 -5
  26. data/lib/sequel/adapters/tinytds.rb +1 -2
  27. data/lib/sequel/connection_pool/sharded_threaded.rb +5 -1
  28. data/lib/sequel/connection_pool/threaded.rb +9 -1
  29. data/lib/sequel/database/dataset_defaults.rb +3 -1
  30. data/lib/sequel/database/misc.rb +7 -1
  31. data/lib/sequel/database/query.rb +11 -3
  32. data/lib/sequel/database/schema_generator.rb +40 -9
  33. data/lib/sequel/database/schema_methods.rb +6 -1
  34. data/lib/sequel/dataset/actions.rb +5 -5
  35. data/lib/sequel/dataset/prepared_statements.rb +3 -1
  36. data/lib/sequel/dataset/query.rb +1 -1
  37. data/lib/sequel/extensions/migration.rb +28 -0
  38. data/lib/sequel/extensions/pg_auto_parameterize.rb +0 -9
  39. data/lib/sequel/extensions/pg_inet.rb +89 -0
  40. data/lib/sequel/extensions/pg_json.rb +178 -0
  41. data/lib/sequel/extensions/schema_dumper.rb +24 -6
  42. data/lib/sequel/model/associations.rb +19 -15
  43. data/lib/sequel/model/base.rb +11 -12
  44. data/lib/sequel/plugins/composition.rb +1 -2
  45. data/lib/sequel/plugins/eager_each.rb +59 -0
  46. data/lib/sequel/plugins/json_serializer.rb +41 -4
  47. data/lib/sequel/plugins/nested_attributes.rb +72 -52
  48. data/lib/sequel/plugins/optimistic_locking.rb +8 -0
  49. data/lib/sequel/plugins/tactical_eager_loading.rb +7 -7
  50. data/lib/sequel/version.rb +1 -1
  51. data/spec/adapters/postgres_spec.rb +271 -1
  52. data/spec/adapters/sqlite_spec.rb +11 -0
  53. data/spec/core/connection_pool_spec.rb +26 -1
  54. data/spec/core/database_spec.rb +19 -0
  55. data/spec/core/dataset_spec.rb +45 -5
  56. data/spec/core/expression_filters_spec.rb +31 -67
  57. data/spec/core/mock_adapter_spec.rb +4 -0
  58. data/spec/extensions/core_extensions_spec.rb +83 -0
  59. data/spec/extensions/eager_each_spec.rb +34 -0
  60. data/spec/extensions/inflector_spec.rb +0 -4
  61. data/spec/extensions/json_serializer_spec.rb +32 -1
  62. data/spec/extensions/migration_spec.rb +28 -0
  63. data/spec/extensions/nested_attributes_spec.rb +134 -1
  64. data/spec/extensions/optimistic_locking_spec.rb +15 -1
  65. data/spec/extensions/pg_hstore_spec.rb +1 -1
  66. data/spec/extensions/pg_inet_spec.rb +44 -0
  67. data/spec/extensions/pg_json_spec.rb +101 -0
  68. data/spec/extensions/prepared_statements_spec.rb +30 -0
  69. data/spec/extensions/rcte_tree_spec.rb +9 -0
  70. data/spec/extensions/schema_dumper_spec.rb +195 -7
  71. data/spec/extensions/serialization_spec.rb +4 -0
  72. data/spec/extensions/spec_helper.rb +9 -1
  73. data/spec/extensions/tactical_eager_loading_spec.rb +8 -0
  74. data/spec/integration/database_test.rb +5 -1
  75. data/spec/integration/prepared_statement_test.rb +20 -2
  76. data/spec/model/associations_spec.rb +27 -0
  77. data/spec/model/base_spec.rb +54 -0
  78. data/spec/model/model_spec.rb +6 -0
  79. data/spec/model/record_spec.rb +18 -0
  80. data/spec/rcov.opts +2 -0
  81. metadata +14 -3
@@ -13,6 +13,7 @@ describe "An SQLite database" do
13
13
  end
14
14
  after do
15
15
  @db.foreign_keys = @fk
16
+ @db.case_sensitive_like = true
16
17
  Sequel.datetime_class = Time
17
18
  end
18
19
 
@@ -38,6 +39,16 @@ describe "An SQLite database" do
38
39
  end
39
40
  end
40
41
 
42
+ specify "should respect case sensitive like false" do
43
+ @db.case_sensitive_like = false
44
+ @db.get(Sequel.like('a', 'A')).to_s.should == '1'
45
+ end
46
+
47
+ specify "should respect case sensitive like true" do
48
+ @db.case_sensitive_like = true
49
+ @db.get(Sequel.like('a', 'A')).to_s.should == '0'
50
+ end
51
+
41
52
  specify "should provide the SQLite version as an integer" do
42
53
  @db.sqlite_version.should be_a_kind_of(Integer)
43
54
  end
@@ -187,7 +187,7 @@ describe "A connection pool with a max size of 1" do
187
187
  q.push nil
188
188
  q1.pop
189
189
 
190
- t1.should_not be_alive
190
+ t1.join
191
191
  t2.should be_alive
192
192
 
193
193
  c2.should == 'hello'
@@ -340,6 +340,31 @@ shared_examples_for "A threaded connection pool" do
340
340
  @pool.available_connections.size.should == 5
341
341
  @pool.allocated.should be_empty
342
342
  end
343
+
344
+ specify "should store connections in a stack by default" do
345
+ c2 = nil
346
+ c = @pool.hold{|cc| Thread.new{@pool.hold{|cc2| c2 = cc2}}.join; cc}
347
+ @pool.size.should == 2
348
+ @pool.hold{|cc| cc.should == c}
349
+ @pool.hold{|cc| cc.should == c}
350
+ @pool.hold do |cc|
351
+ cc.should == c
352
+ Thread.new{@pool.hold{|cc2| cc2.should == c2}}
353
+ end
354
+ end
355
+
356
+ specify "should store connections in a queue if :connection_handling=>:queue" do
357
+ @pool = Sequel::ConnectionPool.get_pool(@cp_opts.merge(:connection_handling=>:queue)){@invoked_count += 1}
358
+ c2 = nil
359
+ c = @pool.hold{|cc| Thread.new{@pool.hold{|cc2| c2 = cc2}}.join; cc}
360
+ @pool.size.should == 2
361
+ @pool.hold{|cc| cc.should == c2}
362
+ @pool.hold{|cc| cc.should == c}
363
+ @pool.hold do |cc|
364
+ cc.should == c2
365
+ Thread.new{@pool.hold{|cc2| cc2.should == c}}
366
+ end
367
+ end
343
368
  end
344
369
 
345
370
  describe "Threaded Unsharded Connection Pool" do
@@ -1996,12 +1996,31 @@ describe "Database#supports_transactional_ddl?" do
1996
1996
  end
1997
1997
  end
1998
1998
 
1999
+ describe "Database#global_index_namespace?" do
2000
+ specify "should be true by default" do
2001
+ Sequel::Database.new.global_index_namespace?.should == true
2002
+ end
2003
+ end
2004
+
1999
2005
  describe "Database#supports_savepoints?" do
2000
2006
  specify "should be false by default" do
2001
2007
  Sequel::Database.new.supports_savepoints?.should == false
2002
2008
  end
2003
2009
  end
2004
2010
 
2011
+ describe "Database#supports_savepoints_in_prepared_transactions?" do
2012
+ specify "should be false by default" do
2013
+ Sequel::Database.new.supports_savepoints_in_prepared_transactions?.should == false
2014
+ end
2015
+
2016
+ specify "should be true if both savepoints and prepared transactions are supported" do
2017
+ db = Sequel::Database.new
2018
+ db.meta_def(:supports_savepoints?){true}
2019
+ db.meta_def(:supports_prepared_transactions?){true}
2020
+ db.supports_savepoints_in_prepared_transactions?.should == true
2021
+ end
2022
+ end
2023
+
2005
2024
  describe "Database#supports_prepared_transactions?" do
2006
2025
  specify "should be false by default" do
2007
2026
  Sequel::Database.new.supports_prepared_transactions?.should == false
@@ -1199,6 +1199,17 @@ describe "Dataset#from" do
1199
1199
  @dataset.from(@dataset.from(:a)).select_sql.should == "SELECT * FROM (SELECT * FROM a) AS t1"
1200
1200
  end
1201
1201
 
1202
+ specify "should treat string arguments as identifiers" do
1203
+ @dataset.quote_identifiers = true
1204
+ @dataset.from('a').select_sql.should == "SELECT * FROM \"a\""
1205
+ end
1206
+
1207
+ specify "should not treat literal strings or blobs as identifiers" do
1208
+ @dataset.quote_identifiers = true
1209
+ @dataset.from('a'.lit).select_sql.should == "SELECT * FROM a"
1210
+ @dataset.from('a'.to_sequel_blob).select_sql.should == "SELECT * FROM 'a'"
1211
+ end
1212
+
1202
1213
  specify "should remove all FROM tables if called with no arguments" do
1203
1214
  @dataset.from.sql.should == 'SELECT *'
1204
1215
  end
@@ -1262,6 +1273,10 @@ describe "Dataset#select" do
1262
1273
  @d.select.sql.should == 'SELECT * FROM test'
1263
1274
  end
1264
1275
 
1276
+ specify "should handle array condition specifiers that are aliased" do
1277
+ @d.select(Sequel.as([[:b, :c]], :n)).sql.should == 'SELECT (b = c) AS n FROM test'
1278
+ end
1279
+
1265
1280
  specify "should accept a hash for AS values" do
1266
1281
  @d.select(:name => 'n', :__ggh => 'age').sql.should =~ /SELECT ((name AS n, __ggh AS age)|(__ggh AS age, name AS n)) FROM test/
1267
1282
  end
@@ -2647,6 +2662,11 @@ describe "Dataset#get" do
2647
2662
  specify "should raise an error if both a regular argument and block argument are used" do
2648
2663
  proc{@d.get(:name){|o| o.x__b.as(:name)}}.should raise_error(Sequel::Error)
2649
2664
  end
2665
+
2666
+ specify "should support false and nil values" do
2667
+ @d.get(false).should == "SELECT 'f' FROM test LIMIT 1"
2668
+ @d.get(nil).should == "SELECT NULL FROM test LIMIT 1"
2669
+ end
2650
2670
  end
2651
2671
 
2652
2672
  describe "Dataset#set_row_proc" do
@@ -3238,6 +3258,7 @@ describe "Dataset prepared statements and bound variables " do
3238
3258
  end
3239
3259
 
3240
3260
  specify "#call should take a type and bind hash and interpolate it" do
3261
+ @ds.filter(:num=>:$n).call(:each, :n=>1)
3241
3262
  @ds.filter(:num=>:$n).call(:select, :n=>1)
3242
3263
  @ds.filter(:num=>:$n).call([:map, :a], :n=>1)
3243
3264
  @ds.filter(:num=>:$n).call([:to_hash, :a, :b], :n=>1)
@@ -3247,7 +3268,9 @@ describe "Dataset prepared statements and bound variables " do
3247
3268
  @ds.filter(:num=>:$n).call(:update, {:n=>1, :n2=>2}, :num=>:$n2)
3248
3269
  @ds.call(:insert, {:n=>1}, :num=>:$n)
3249
3270
  @ds.call(:insert_select, {:n=>1}, :num=>:$n)
3250
- @db.sqls.should == ['SELECT * FROM items WHERE (num = 1)',
3271
+ @db.sqls.should == [
3272
+ 'SELECT * FROM items WHERE (num = 1)',
3273
+ 'SELECT * FROM items WHERE (num = 1)',
3251
3274
  'SELECT * FROM items WHERE (num = 1)',
3252
3275
  'SELECT * FROM items WHERE (num = 1)',
3253
3276
  'SELECT * FROM items WHERE (num = 1)',
@@ -3260,6 +3283,7 @@ describe "Dataset prepared statements and bound variables " do
3260
3283
 
3261
3284
  specify "#prepare should take a type and name and store it in the database for later use with call" do
3262
3285
  pss = []
3286
+ pss << @ds.filter(:num=>:$n).prepare(:each, :en)
3263
3287
  pss << @ds.filter(:num=>:$n).prepare(:select, :sn)
3264
3288
  pss << @ds.filter(:num=>:$n).prepare([:map, :a], :sm)
3265
3289
  pss << @ds.filter(:num=>:$n).prepare([:to_hash, :a, :b], :sh)
@@ -3269,8 +3293,9 @@ describe "Dataset prepared statements and bound variables " do
3269
3293
  pss << @ds.filter(:num=>:$n).prepare(:update, :un, :num=>:$n2)
3270
3294
  pss << @ds.prepare(:insert, :in, :num=>:$n)
3271
3295
  pss << @ds.prepare(:insert_select, :ins, :num=>:$n)
3272
- @db.prepared_statements.keys.sort_by{|k| k.to_s}.should == [:dn, :fn, :in, :ins, :sh, :shg, :sm, :sn, :un]
3273
- [:sn, :sm, :sh, :shg, :fn, :dn, :un, :in, :ins].each_with_index{|x, i| @db.prepared_statements[x].should == pss[i]}
3296
+ @db.prepared_statements.keys.sort_by{|k| k.to_s}.should == [:dn, :en, :fn, :in, :ins, :sh, :shg, :sm, :sn, :un]
3297
+ [:en, :sn, :sm, :sh, :shg, :fn, :dn, :un, :in, :ins].each_with_index{|x, i| @db.prepared_statements[x].should == pss[i]}
3298
+ @db.call(:en, :n=>1){}
3274
3299
  @db.call(:sn, :n=>1)
3275
3300
  @db.call(:sm, :n=>1)
3276
3301
  @db.call(:sh, :n=>1)
@@ -3280,7 +3305,9 @@ describe "Dataset prepared statements and bound variables " do
3280
3305
  @db.call(:un, :n=>1, :n2=>2)
3281
3306
  @db.call(:in, :n=>1)
3282
3307
  @db.call(:ins, :n=>1)
3283
- @db.sqls.should == ['SELECT * FROM items WHERE (num = 1)',
3308
+ @db.sqls.should == [
3309
+ 'SELECT * FROM items WHERE (num = 1)',
3310
+ 'SELECT * FROM items WHERE (num = 1)',
3284
3311
  'SELECT * FROM items WHERE (num = 1)',
3285
3312
  'SELECT * FROM items WHERE (num = 1)',
3286
3313
  'SELECT * FROM items WHERE (num = 1)',
@@ -3291,6 +3318,11 @@ describe "Dataset prepared statements and bound variables " do
3291
3318
  'INSERT INTO items (num) VALUES (1) RETURNING *']
3292
3319
  end
3293
3320
 
3321
+ specify "#call should default to using :all if an invalid type is given" do
3322
+ @ds.filter(:num=>:$n).call(:select_all, :n=>1)
3323
+ @db.sqls.should == ['SELECT * FROM items WHERE (num = 1)']
3324
+ end
3325
+
3294
3326
  specify "#inspect should indicate it is a prepared statement with the prepared SQL" do
3295
3327
  @ds.filter(:num=>:$n).prepare(:select, :sn).inspect.should == \
3296
3328
  '<Sequel::Mock::Dataset/PreparedStatement "SELECT * FROM items WHERE (num = $n)">'
@@ -3361,7 +3393,7 @@ describe Sequel::Dataset::UnnumberedArgumentMapper do
3361
3393
  @ps.first.inspect.should == '<Sequel::Mock::Dataset/PreparedStatement "SELECT * FROM items WHERE (num = ?)">'
3362
3394
  end
3363
3395
 
3364
- specify "should submitted the SQL to the database with placeholders and bind variables" do
3396
+ specify "should submit the SQL to the database with placeholders and bind variables" do
3365
3397
  @ps.each{|p| p.prepared_sql; p.call(:n=>1)}
3366
3398
  @db.sqls.should == ["SELECT * FROM items WHERE (num = ?) -- args: [1]",
3367
3399
  "SELECT * FROM items WHERE (num = ?) -- args: [1]",
@@ -3370,6 +3402,14 @@ describe Sequel::Dataset::UnnumberedArgumentMapper do
3370
3402
  "INSERT INTO items (num) VALUES (?) -- args: [1]",
3371
3403
  "UPDATE items SET num = ? WHERE (num = ?) -- args: [1, 1]"]
3372
3404
  end
3405
+
3406
+ specify "should handle unrecognized statement types as :all" do
3407
+ ps = @ds.prepare(:select_all, :s)
3408
+ ps.extend(Sequel::Dataset::UnnumberedArgumentMapper)
3409
+ ps.prepared_sql
3410
+ ps.call(:n=>1)
3411
+ @db.sqls.should == ["SELECT * FROM items WHERE (num = ?) -- args: [1]"]
3412
+ end
3373
3413
  end
3374
3414
 
3375
3415
  describe "Sequel::Dataset#server" do
@@ -212,8 +212,6 @@ describe "Blockless Ruby Filters" do
212
212
  @d.l(:x.sql_boolean & :y).should == '(x AND y)'
213
213
  @d.l(:x & :y & :z).should == '(x AND y AND z)'
214
214
  @d.l(:x & {:y => :z}).should == '(x AND (y = z))'
215
- @d.l({:y => :z} & :x).should == '((y = z) AND x)'
216
- @d.l({:x => :a} & {:y => :z}).should == '((x = a) AND (y = z))'
217
215
  @d.l((:x + 200 < 0) & (:y - 200 < 0)).should == '(((x + 200) < 0) AND ((y - 200) < 0))'
218
216
  @d.l(:x & ~:y).should == '(x AND NOT y)'
219
217
  @d.l(~:x & :y).should == '(NOT x AND y)'
@@ -225,8 +223,6 @@ describe "Blockless Ruby Filters" do
225
223
  @d.l(:x.sql_boolean | :y).should == '(x OR y)'
226
224
  @d.l(:x | :y | :z).should == '(x OR y OR z)'
227
225
  @d.l(:x | {:y => :z}).should == '(x OR (y = z))'
228
- @d.l({:y => :z} | :x).should == '((y = z) OR x)'
229
- @d.l({:x => :a} | {:y => :z}).should == '((x = a) OR (y = z))'
230
226
  @d.l((:x.sql_number > 200) | (:y.sql_number < 200)).should == '((x > 200) OR (y < 200))'
231
227
  end
232
228
 
@@ -267,60 +263,12 @@ describe "Blockless Ruby Filters" do
267
263
  @d.l(:x => nil, :y => [1,2,3])[1...-1].split(' AND ').sort.should == ['(x IS NULL)', '(y IN (1, 2, 3))']
268
264
  end
269
265
 
270
- it "should support sql_expr on hashes" do
271
- @d.l({:x => 100, :y => 'a'}.sql_expr)[1...-1].split(' AND ').sort.should == ['(x = 100)', '(y = \'a\')']
272
- @d.l({:x => true, :y => false}.sql_expr)[1...-1].split(' AND ').sort.should == ['(x IS TRUE)', '(y IS FALSE)']
273
- @d.l({:x => nil, :y => [1,2,3]}.sql_expr)[1...-1].split(' AND ').sort.should == ['(x IS NULL)', '(y IN (1, 2, 3))']
274
- end
275
-
276
- it "should support sql_negate on hashes" do
277
- @d.l({:x => 100, :y => 'a'}.sql_negate)[1...-1].split(' AND ').sort.should == ['(x != 100)', '(y != \'a\')']
278
- @d.l({:x => true, :y => false}.sql_negate)[1...-1].split(' AND ').sort.should == ['(x IS NOT TRUE)', '(y IS NOT FALSE)']
279
- @d.l({:x => nil, :y => [1,2,3]}.sql_negate)[1...-1].split(' AND ').sort.should == ['(x IS NOT NULL)', '(y NOT IN (1, 2, 3))']
280
- end
281
-
282
- it "should support ~ on hashes" do
283
- @d.l(~{:x => 100, :y => 'a'})[1...-1].split(' OR ').sort.should == ['(x != 100)', '(y != \'a\')']
284
- @d.l(~{:x => true, :y => false})[1...-1].split(' OR ').sort.should == ['(x IS NOT TRUE)', '(y IS NOT FALSE)']
285
- @d.l(~{:x => nil, :y => [1,2,3]})[1...-1].split(' OR ').sort.should == ['(x IS NOT NULL)', '(y NOT IN (1, 2, 3))']
286
- end
287
-
288
- it "should support sql_or on hashes" do
289
- @d.l({:x => 100, :y => 'a'}.sql_or)[1...-1].split(' OR ').sort.should == ['(x = 100)', '(y = \'a\')']
290
- @d.l({:x => true, :y => false}.sql_or)[1...-1].split(' OR ').sort.should == ['(x IS TRUE)', '(y IS FALSE)']
291
- @d.l({:x => nil, :y => [1,2,3]}.sql_or)[1...-1].split(' OR ').sort.should == ['(x IS NULL)', '(y IN (1, 2, 3))']
292
- end
293
-
294
266
  it "should support arrays with all two pairs the same as hashes" do
295
267
  @d.l([[:x, 100],[:y, 'a']]).should == '((x = 100) AND (y = \'a\'))'
296
268
  @d.l([[:x, true], [:y, false]]).should == '((x IS TRUE) AND (y IS FALSE))'
297
269
  @d.l([[:x, nil], [:y, [1,2,3]]]).should == '((x IS NULL) AND (y IN (1, 2, 3)))'
298
270
  end
299
271
 
300
- it "should support sql_expr on arrays with all two pairs" do
301
- @d.l([[:x, 100],[:y, 'a']].sql_expr).should == '((x = 100) AND (y = \'a\'))'
302
- @d.l([[:x, true], [:y, false]].sql_expr).should == '((x IS TRUE) AND (y IS FALSE))'
303
- @d.l([[:x, nil], [:y, [1,2,3]]].sql_expr).should == '((x IS NULL) AND (y IN (1, 2, 3)))'
304
- end
305
-
306
- it "should support sql_negate on arrays with all two pairs" do
307
- @d.l([[:x, 100],[:y, 'a']].sql_negate).should == '((x != 100) AND (y != \'a\'))'
308
- @d.l([[:x, true], [:y, false]].sql_negate).should == '((x IS NOT TRUE) AND (y IS NOT FALSE))'
309
- @d.l([[:x, nil], [:y, [1,2,3]]].sql_negate).should == '((x IS NOT NULL) AND (y NOT IN (1, 2, 3)))'
310
- end
311
-
312
- it "should support ~ on arrays with all two pairs" do
313
- @d.l(~[[:x, 100],[:y, 'a']]).should == '((x != 100) OR (y != \'a\'))'
314
- @d.l(~[[:x, true], [:y, false]]).should == '((x IS NOT TRUE) OR (y IS NOT FALSE))'
315
- @d.l(~[[:x, nil], [:y, [1,2,3]]]).should == '((x IS NOT NULL) OR (y NOT IN (1, 2, 3)))'
316
- end
317
-
318
- it "should support sql_or on arrays with all two pairs" do
319
- @d.l([[:x, 100],[:y, 'a']].sql_or).should == '((x = 100) OR (y = \'a\'))'
320
- @d.l([[:x, true], [:y, false]].sql_or).should == '((x IS TRUE) OR (y IS FALSE))'
321
- @d.l([[:x, nil], [:y, [1,2,3]]].sql_or).should == '((x IS NULL) OR (y IN (1, 2, 3)))'
322
- end
323
-
324
272
  it "should emulate columns for array values" do
325
273
  @d.l([:x, :y]=>[[1,2], [3,4]].sql_array).should == '((x, y) IN ((1, 2), (3, 4)))'
326
274
  @d.l([:x, :y, :z]=>[[1,2,5], [3,4,6]]).should == '((x, y, z) IN ((1, 2, 5), (3, 4, 6)))'
@@ -332,21 +280,6 @@ describe "Blockless Ruby Filters" do
332
280
  @d.l([:x, :y, :z]=>[[1,2,5], [3,4,6]]).should == '(((x = 1) AND (y = 2) AND (z = 5)) OR ((x = 3) AND (y = 4) AND (z = 6)))'
333
281
  end
334
282
 
335
- it "should support Array#sql_string_join for concatenation of SQL strings" do
336
- @d.lit([:x].sql_string_join).should == '(x)'
337
- @d.lit([:x].sql_string_join(', ')).should == '(x)'
338
- @d.lit([:x, :y].sql_string_join).should == '(x || y)'
339
- @d.lit([:x, :y].sql_string_join(', ')).should == "(x || ', ' || y)"
340
- @d.lit([:x.sql_function(1), :y.sql_subscript(1)].sql_string_join).should == '(x(1) || y[1])'
341
- @d.lit([:x.sql_function(1), 'y.z'.lit].sql_string_join(', ')).should == "(x(1) || ', ' || y.z)"
342
- @d.lit([:x, 1, :y].sql_string_join).should == "(x || '1' || y)"
343
- @d.lit([:x, 1, :y].sql_string_join(', ')).should == "(x || ', ' || '1' || ', ' || y)"
344
- @d.lit([:x, 1, :y].sql_string_join(:y__z)).should == "(x || y.z || '1' || y.z || y)"
345
- @d.lit([:x, 1, :y].sql_string_join(1)).should == "(x || '1' || '1' || '1' || y)"
346
- @d.lit([:x, :y].sql_string_join('y.x || x.y'.lit)).should == "(x || y.x || x.y || y)"
347
- @d.lit([[:x, :y].sql_string_join, [:a, :b].sql_string_join].sql_string_join).should == "(x || y || a || b)"
348
- end
349
-
350
283
  it "should support StringExpression#+ for concatenation of SQL strings" do
351
284
  @d.lit(:x.sql_string + :y).should == '(x || y)'
352
285
  @d.lit([:x].sql_string_join + :y).should == '(x || y)'
@@ -762,6 +695,11 @@ describe "Sequel core extension replacements" do
762
695
  l(Sequel.expr([[:a, 1], [:b, 2]]) & nil, "((a = 1) AND (b = 2) AND NULL)")
763
696
  end
764
697
 
698
+ it "Sequel.expr should handle arrays that are not condition specifiers" do
699
+ l(Sequel.expr([1]), "(1)")
700
+ l(Sequel.expr([1, 2]), "(1, 2)")
701
+ end
702
+
765
703
  it "Sequel.expr should treat blocks/procs as virtual rows and wrap the output" do
766
704
  l(Sequel.expr{1} + 1, "(1 + 1)")
767
705
  l(Sequel.expr{o__a} + 1, "(o.a + 1)")
@@ -1078,3 +1016,29 @@ describe "Sequel::SQL::Wrapper" do
1078
1016
  @ds.literal(s.sql_string + '1').should == "(foo || '1')"
1079
1017
  end
1080
1018
  end
1019
+
1020
+ describe "Sequel::SQL::Blob#to_sequel_blob" do
1021
+ specify "should return self" do
1022
+ c = Sequel::SQL::Blob.new('a')
1023
+ c.to_sequel_blob.should equal(c)
1024
+ end
1025
+ end
1026
+
1027
+ describe Sequel::SQL::Subscript do
1028
+ before do
1029
+ @s = Sequel::SQL::Subscript.new(:a, [1])
1030
+ @ds = Sequel::Dataset.new(nil)
1031
+ end
1032
+
1033
+ specify "should have | return a new non-nested subscript" do
1034
+ s = (@s | 2)
1035
+ s.should_not equal(@s)
1036
+ @ds.literal(s).should == 'a[1, 2]'
1037
+ end
1038
+
1039
+ specify "should have [] return a new nested subscript" do
1040
+ s = @s[2]
1041
+ s.should_not equal(@s)
1042
+ @ds.literal(s).should == 'a[1][2]'
1043
+ end
1044
+ end
@@ -442,4 +442,8 @@ describe "Sequel Mock Adapter" do
442
442
  specify "should automatically set version for postgres" do
443
443
  Sequel.mock(:host=>'postgres').server_version.should == 90103
444
444
  end
445
+
446
+ specify "should stub out the primary_key method for postgres" do
447
+ Sequel.mock(:host=>'postgres').primary_key(:t).should == :id
448
+ end
445
449
  end
@@ -18,6 +18,89 @@ describe "Sequel core extensions" do
18
18
  end
19
19
  end
20
20
 
21
+ describe "Array and Hash extensions" do
22
+ before do
23
+ db = Sequel::Database.new
24
+ @d = db[:items]
25
+ def @d.l(*args, &block)
26
+ literal(filter_expr(*args, &block))
27
+ end
28
+ def @d.lit(*args)
29
+ literal(*args)
30
+ end
31
+ end
32
+
33
+ it "should support sql_expr on arrays with all two pairs" do
34
+ @d.l([[:x, 100],[:y, 'a']].sql_expr).should == '((x = 100) AND (y = \'a\'))'
35
+ @d.l([[:x, true], [:y, false]].sql_expr).should == '((x IS TRUE) AND (y IS FALSE))'
36
+ @d.l([[:x, nil], [:y, [1,2,3]]].sql_expr).should == '((x IS NULL) AND (y IN (1, 2, 3)))'
37
+ end
38
+
39
+ it "should support sql_negate on arrays with all two pairs" do
40
+ @d.l([[:x, 100],[:y, 'a']].sql_negate).should == '((x != 100) AND (y != \'a\'))'
41
+ @d.l([[:x, true], [:y, false]].sql_negate).should == '((x IS NOT TRUE) AND (y IS NOT FALSE))'
42
+ @d.l([[:x, nil], [:y, [1,2,3]]].sql_negate).should == '((x IS NOT NULL) AND (y NOT IN (1, 2, 3)))'
43
+ end
44
+
45
+ it "should support ~ on arrays with all two pairs" do
46
+ @d.l(~[[:x, 100],[:y, 'a']]).should == '((x != 100) OR (y != \'a\'))'
47
+ @d.l(~[[:x, true], [:y, false]]).should == '((x IS NOT TRUE) OR (y IS NOT FALSE))'
48
+ @d.l(~[[:x, nil], [:y, [1,2,3]]]).should == '((x IS NOT NULL) OR (y NOT IN (1, 2, 3)))'
49
+ end
50
+
51
+ it "should support sql_or on arrays with all two pairs" do
52
+ @d.l([[:x, 100],[:y, 'a']].sql_or).should == '((x = 100) OR (y = \'a\'))'
53
+ @d.l([[:x, true], [:y, false]].sql_or).should == '((x IS TRUE) OR (y IS FALSE))'
54
+ @d.l([[:x, nil], [:y, [1,2,3]]].sql_or).should == '((x IS NULL) OR (y IN (1, 2, 3)))'
55
+ end
56
+
57
+ it "should support Array#sql_string_join for concatenation of SQL strings" do
58
+ @d.lit([:x].sql_string_join).should == '(x)'
59
+ @d.lit([:x].sql_string_join(', ')).should == '(x)'
60
+ @d.lit([:x, :y].sql_string_join).should == '(x || y)'
61
+ @d.lit([:x, :y].sql_string_join(', ')).should == "(x || ', ' || y)"
62
+ @d.lit([:x.sql_function(1), :y.sql_subscript(1)].sql_string_join).should == '(x(1) || y[1])'
63
+ @d.lit([:x.sql_function(1), 'y.z'.lit].sql_string_join(', ')).should == "(x(1) || ', ' || y.z)"
64
+ @d.lit([:x, 1, :y].sql_string_join).should == "(x || '1' || y)"
65
+ @d.lit([:x, 1, :y].sql_string_join(', ')).should == "(x || ', ' || '1' || ', ' || y)"
66
+ @d.lit([:x, 1, :y].sql_string_join(:y__z)).should == "(x || y.z || '1' || y.z || y)"
67
+ @d.lit([:x, 1, :y].sql_string_join(1)).should == "(x || '1' || '1' || '1' || y)"
68
+ @d.lit([:x, :y].sql_string_join('y.x || x.y'.lit)).should == "(x || y.x || x.y || y)"
69
+ @d.lit([[:x, :y].sql_string_join, [:a, :b].sql_string_join].sql_string_join).should == "(x || y || a || b)"
70
+ end
71
+
72
+ it "should support sql_expr on hashes" do
73
+ @d.l({:x => 100, :y => 'a'}.sql_expr)[1...-1].split(' AND ').sort.should == ['(x = 100)', '(y = \'a\')']
74
+ @d.l({:x => true, :y => false}.sql_expr)[1...-1].split(' AND ').sort.should == ['(x IS TRUE)', '(y IS FALSE)']
75
+ @d.l({:x => nil, :y => [1,2,3]}.sql_expr)[1...-1].split(' AND ').sort.should == ['(x IS NULL)', '(y IN (1, 2, 3))']
76
+ end
77
+
78
+ it "should support sql_negate on hashes" do
79
+ @d.l({:x => 100, :y => 'a'}.sql_negate)[1...-1].split(' AND ').sort.should == ['(x != 100)', '(y != \'a\')']
80
+ @d.l({:x => true, :y => false}.sql_negate)[1...-1].split(' AND ').sort.should == ['(x IS NOT TRUE)', '(y IS NOT FALSE)']
81
+ @d.l({:x => nil, :y => [1,2,3]}.sql_negate)[1...-1].split(' AND ').sort.should == ['(x IS NOT NULL)', '(y NOT IN (1, 2, 3))']
82
+ end
83
+
84
+ it "should support ~ on hashes" do
85
+ @d.l(~{:x => 100, :y => 'a'})[1...-1].split(' OR ').sort.should == ['(x != 100)', '(y != \'a\')']
86
+ @d.l(~{:x => true, :y => false})[1...-1].split(' OR ').sort.should == ['(x IS NOT TRUE)', '(y IS NOT FALSE)']
87
+ @d.l(~{:x => nil, :y => [1,2,3]})[1...-1].split(' OR ').sort.should == ['(x IS NOT NULL)', '(y NOT IN (1, 2, 3))']
88
+ end
89
+
90
+ it "should support sql_or on hashes" do
91
+ @d.l({:x => 100, :y => 'a'}.sql_or)[1...-1].split(' OR ').sort.should == ['(x = 100)', '(y = \'a\')']
92
+ @d.l({:x => true, :y => false}.sql_or)[1...-1].split(' OR ').sort.should == ['(x IS TRUE)', '(y IS FALSE)']
93
+ @d.l({:x => nil, :y => [1,2,3]}.sql_or)[1...-1].split(' OR ').sort.should == ['(x IS NULL)', '(y IN (1, 2, 3))']
94
+ end
95
+
96
+ it "should Hash#& and Hash#|" do
97
+ @d.l({:y => :z} & :x).should == '((y = z) AND x)'
98
+ @d.l({:x => :a} & {:y => :z}).should == '((x = a) AND (y = z))'
99
+ @d.l({:y => :z} | :x).should == '((y = z) OR x)'
100
+ @d.l({:x => :a} | {:y => :z}).should == '((x = a) OR (y = z))'
101
+ end
102
+ end
103
+
21
104
  describe "Array#case and Hash#case" do
22
105
  before do
23
106
  @d = Sequel::Dataset.new(nil)