sequel 3.35.0 → 3.36.0

Sign up to get free protection for your applications and to get access to all the features.
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)