sequel 3.33.0 → 3.34.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 (152) hide show
  1. data/CHANGELOG +140 -0
  2. data/Rakefile +7 -0
  3. data/bin/sequel +22 -2
  4. data/doc/dataset_basics.rdoc +1 -1
  5. data/doc/mass_assignment.rdoc +3 -1
  6. data/doc/querying.rdoc +28 -4
  7. data/doc/reflection.rdoc +23 -3
  8. data/doc/release_notes/3.34.0.txt +671 -0
  9. data/doc/schema_modification.rdoc +18 -2
  10. data/doc/virtual_rows.rdoc +49 -0
  11. data/lib/sequel/adapters/do/mysql.rb +0 -5
  12. data/lib/sequel/adapters/ibmdb.rb +9 -4
  13. data/lib/sequel/adapters/jdbc.rb +9 -4
  14. data/lib/sequel/adapters/jdbc/h2.rb +8 -2
  15. data/lib/sequel/adapters/jdbc/mysql.rb +0 -5
  16. data/lib/sequel/adapters/jdbc/postgresql.rb +43 -0
  17. data/lib/sequel/adapters/jdbc/sqlite.rb +19 -0
  18. data/lib/sequel/adapters/mock.rb +24 -3
  19. data/lib/sequel/adapters/mysql.rb +29 -50
  20. data/lib/sequel/adapters/mysql2.rb +13 -28
  21. data/lib/sequel/adapters/oracle.rb +8 -2
  22. data/lib/sequel/adapters/postgres.rb +115 -20
  23. data/lib/sequel/adapters/shared/db2.rb +1 -1
  24. data/lib/sequel/adapters/shared/mssql.rb +14 -3
  25. data/lib/sequel/adapters/shared/mysql.rb +59 -11
  26. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +6 -0
  27. data/lib/sequel/adapters/shared/oracle.rb +1 -1
  28. data/lib/sequel/adapters/shared/postgres.rb +127 -30
  29. data/lib/sequel/adapters/shared/sqlite.rb +55 -38
  30. data/lib/sequel/adapters/sqlite.rb +9 -3
  31. data/lib/sequel/adapters/swift.rb +2 -2
  32. data/lib/sequel/adapters/swift/mysql.rb +0 -5
  33. data/lib/sequel/adapters/swift/postgres.rb +10 -0
  34. data/lib/sequel/ast_transformer.rb +4 -0
  35. data/lib/sequel/connection_pool.rb +8 -0
  36. data/lib/sequel/connection_pool/sharded_single.rb +5 -0
  37. data/lib/sequel/connection_pool/sharded_threaded.rb +17 -0
  38. data/lib/sequel/connection_pool/single.rb +5 -0
  39. data/lib/sequel/connection_pool/threaded.rb +14 -0
  40. data/lib/sequel/core.rb +24 -3
  41. data/lib/sequel/database/connecting.rb +24 -14
  42. data/lib/sequel/database/dataset_defaults.rb +1 -0
  43. data/lib/sequel/database/misc.rb +16 -25
  44. data/lib/sequel/database/query.rb +20 -2
  45. data/lib/sequel/database/schema_generator.rb +2 -2
  46. data/lib/sequel/database/schema_methods.rb +120 -23
  47. data/lib/sequel/dataset/actions.rb +91 -18
  48. data/lib/sequel/dataset/features.rb +5 -0
  49. data/lib/sequel/dataset/prepared_statements.rb +6 -2
  50. data/lib/sequel/dataset/sql.rb +68 -51
  51. data/lib/sequel/extensions/_pretty_table.rb +79 -0
  52. data/lib/sequel/{core_sql.rb → extensions/core_extensions.rb} +18 -13
  53. data/lib/sequel/extensions/migration.rb +4 -0
  54. data/lib/sequel/extensions/null_dataset.rb +90 -0
  55. data/lib/sequel/extensions/pg_array.rb +460 -0
  56. data/lib/sequel/extensions/pg_array_ops.rb +220 -0
  57. data/lib/sequel/extensions/pg_auto_parameterize.rb +174 -0
  58. data/lib/sequel/extensions/pg_hstore.rb +296 -0
  59. data/lib/sequel/extensions/pg_hstore_ops.rb +259 -0
  60. data/lib/sequel/extensions/pg_statement_cache.rb +316 -0
  61. data/lib/sequel/extensions/pretty_table.rb +5 -71
  62. data/lib/sequel/extensions/query_literals.rb +79 -0
  63. data/lib/sequel/extensions/schema_caching.rb +76 -0
  64. data/lib/sequel/extensions/schema_dumper.rb +227 -31
  65. data/lib/sequel/extensions/select_remove.rb +35 -0
  66. data/lib/sequel/extensions/sql_expr.rb +4 -110
  67. data/lib/sequel/extensions/to_dot.rb +1 -1
  68. data/lib/sequel/model.rb +11 -2
  69. data/lib/sequel/model/associations.rb +35 -7
  70. data/lib/sequel/model/base.rb +159 -36
  71. data/lib/sequel/no_core_ext.rb +2 -0
  72. data/lib/sequel/plugins/caching.rb +25 -18
  73. data/lib/sequel/plugins/composition.rb +1 -1
  74. data/lib/sequel/plugins/hook_class_methods.rb +1 -1
  75. data/lib/sequel/plugins/identity_map.rb +11 -3
  76. data/lib/sequel/plugins/instance_filters.rb +10 -0
  77. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +71 -0
  78. data/lib/sequel/plugins/nested_attributes.rb +4 -3
  79. data/lib/sequel/plugins/prepared_statements.rb +3 -1
  80. data/lib/sequel/plugins/prepared_statements_associations.rb +5 -1
  81. data/lib/sequel/plugins/schema.rb +7 -2
  82. data/lib/sequel/plugins/single_table_inheritance.rb +1 -1
  83. data/lib/sequel/plugins/static_cache.rb +99 -0
  84. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  85. data/lib/sequel/sql.rb +417 -7
  86. data/lib/sequel/version.rb +1 -1
  87. data/spec/adapters/firebird_spec.rb +1 -1
  88. data/spec/adapters/mssql_spec.rb +12 -15
  89. data/spec/adapters/mysql_spec.rb +81 -23
  90. data/spec/adapters/postgres_spec.rb +444 -77
  91. data/spec/adapters/spec_helper.rb +2 -0
  92. data/spec/adapters/sqlite_spec.rb +8 -8
  93. data/spec/core/connection_pool_spec.rb +85 -0
  94. data/spec/core/database_spec.rb +29 -5
  95. data/spec/core/dataset_spec.rb +171 -3
  96. data/spec/core/expression_filters_spec.rb +364 -0
  97. data/spec/core/mock_adapter_spec.rb +17 -3
  98. data/spec/core/schema_spec.rb +133 -0
  99. data/spec/extensions/association_dependencies_spec.rb +13 -13
  100. data/spec/extensions/caching_spec.rb +26 -3
  101. data/spec/extensions/class_table_inheritance_spec.rb +2 -2
  102. data/spec/{core/core_sql_spec.rb → extensions/core_extensions_spec.rb} +23 -94
  103. data/spec/extensions/force_encoding_spec.rb +4 -2
  104. data/spec/extensions/hook_class_methods_spec.rb +5 -2
  105. data/spec/extensions/identity_map_spec.rb +17 -0
  106. data/spec/extensions/instance_filters_spec.rb +1 -1
  107. data/spec/extensions/lazy_attributes_spec.rb +2 -2
  108. data/spec/extensions/list_spec.rb +4 -4
  109. data/spec/extensions/many_to_one_pk_lookup_spec.rb +140 -0
  110. data/spec/extensions/migration_spec.rb +6 -2
  111. data/spec/extensions/nested_attributes_spec.rb +20 -0
  112. data/spec/extensions/null_dataset_spec.rb +85 -0
  113. data/spec/extensions/optimistic_locking_spec.rb +2 -2
  114. data/spec/extensions/pg_array_ops_spec.rb +105 -0
  115. data/spec/extensions/pg_array_spec.rb +196 -0
  116. data/spec/extensions/pg_auto_parameterize_spec.rb +64 -0
  117. data/spec/extensions/pg_hstore_ops_spec.rb +136 -0
  118. data/spec/extensions/pg_hstore_spec.rb +195 -0
  119. data/spec/extensions/pg_statement_cache_spec.rb +209 -0
  120. data/spec/extensions/prepared_statements_spec.rb +4 -0
  121. data/spec/extensions/pretty_table_spec.rb +6 -0
  122. data/spec/extensions/query_literals_spec.rb +168 -0
  123. data/spec/extensions/schema_caching_spec.rb +41 -0
  124. data/spec/extensions/schema_dumper_spec.rb +231 -11
  125. data/spec/extensions/schema_spec.rb +14 -2
  126. data/spec/extensions/select_remove_spec.rb +38 -0
  127. data/spec/extensions/sharding_spec.rb +6 -6
  128. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  129. data/spec/extensions/spec_helper.rb +2 -1
  130. data/spec/extensions/sql_expr_spec.rb +28 -19
  131. data/spec/extensions/static_cache_spec.rb +145 -0
  132. data/spec/extensions/touch_spec.rb +1 -1
  133. data/spec/extensions/typecast_on_load_spec.rb +9 -1
  134. data/spec/integration/associations_test.rb +6 -6
  135. data/spec/integration/database_test.rb +1 -1
  136. data/spec/integration/dataset_test.rb +89 -26
  137. data/spec/integration/migrator_test.rb +2 -3
  138. data/spec/integration/model_test.rb +3 -3
  139. data/spec/integration/plugin_test.rb +85 -22
  140. data/spec/integration/prepared_statement_test.rb +28 -8
  141. data/spec/integration/schema_test.rb +78 -7
  142. data/spec/integration/spec_helper.rb +1 -0
  143. data/spec/integration/timezone_test.rb +1 -1
  144. data/spec/integration/transaction_test.rb +4 -6
  145. data/spec/integration/type_test.rb +2 -2
  146. data/spec/model/associations_spec.rb +94 -8
  147. data/spec/model/base_spec.rb +4 -4
  148. data/spec/model/hooks_spec.rb +2 -2
  149. data/spec/model/model_spec.rb +19 -7
  150. data/spec/model/record_spec.rb +135 -58
  151. data/spec/model/spec_helper.rb +1 -0
  152. metadata +35 -7
@@ -3,7 +3,7 @@ module Sequel
3
3
  MAJOR = 3
4
4
  # The minor version of Sequel. Bumped for every non-patch level
5
5
  # release, generally around once a month.
6
- MINOR = 33
6
+ MINOR = 34
7
7
  # The tiny version of Sequel. Usually 0, only bumped for bugfix
8
8
  # releases that fix regressions from previous versions.
9
9
  TINY = 0
@@ -247,7 +247,7 @@ end
247
247
  describe "A Firebird database" do
248
248
  before do
249
249
  @db = FIREBIRD_DB
250
- @db.drop_table(:posts) rescue nil
250
+ @db.drop_table?(:posts)
251
251
  @db.sqls.clear
252
252
  end
253
253
 
@@ -67,10 +67,10 @@ end
67
67
  describe "MSSQL full_text_search" do
68
68
  before do
69
69
  @db = MSSQL_DB
70
- @db.drop_table(:posts) rescue nil
70
+ @db.drop_table?(:posts)
71
71
  end
72
72
  after do
73
- @db.drop_table(:posts) rescue nil
73
+ @db.drop_table?(:posts)
74
74
  end
75
75
 
76
76
  specify "should support fulltext indexes and full_text_search" do
@@ -109,8 +109,7 @@ describe "MSSQL Dataset#output" do
109
109
  @ds = @db[:items]
110
110
  end
111
111
  after do
112
- @db.drop_table(:items)
113
- @db.drop_table(:out)
112
+ @db.drop_table?(:items, :out)
114
113
  end
115
114
 
116
115
  specify "should format OUTPUT clauses without INTO for DELETE statements" do
@@ -256,7 +255,7 @@ describe "MSSQL::Dataset#import" do
256
255
  @ds = @db[:test]
257
256
  end
258
257
  after do
259
- @db.drop_table(:test) rescue nil
258
+ @db.drop_table?(:test)
260
259
  end
261
260
 
262
261
  specify "#import should work correctly with an arbitrary output value" do
@@ -291,7 +290,7 @@ describe "Offset support" do
291
290
  @ds.import [:id, :parent_id], [[1,nil],[2,nil],[3,1],[4,1],[5,3],[6,5]]
292
291
  end
293
292
  after do
294
- @db.drop_table(:i)
293
+ @db.drop_table?(:i)
295
294
  end
296
295
 
297
296
  specify "should return correct rows" do
@@ -314,8 +313,7 @@ describe "Common Table Expressions" do
314
313
  @ds.import [:id, :parent_id], [[1,nil],[2,nil],[3,1],[4,1],[5,3],[6,5]]
315
314
  end
316
315
  after do
317
- @db.drop_table(:i1)
318
- @db.drop_table(:i2)
316
+ @db.drop_table?(:i1, :i2)
319
317
  end
320
318
 
321
319
  specify "using #with should be able to update" do
@@ -385,7 +383,7 @@ describe "MSSSQL::Dataset#insert" do
385
383
  @ds = @db[:test5]
386
384
  end
387
385
  after do
388
- @db.drop_table(:test5) rescue nil
386
+ @db.drop_table?(:test5)
389
387
  end
390
388
 
391
389
  specify "should have insert_select return nil if disable_insert_output is used" do
@@ -424,8 +422,7 @@ describe "MSSSQL::Dataset#into" do
424
422
  @db[:t].insert(:id => 1, :value => "test")
425
423
  @db << @db[:t].into(:new).select_sql
426
424
  @db[:new].all.should == [{:id => 1, :value => "test"}]
427
- @db.drop_table(:t)
428
- @db.drop_table(:new)
425
+ @db.drop_table?(:t, :new)
429
426
  end
430
427
  end
431
428
 
@@ -434,7 +431,7 @@ describe "A MSSQL database" do
434
431
  @db = MSSQL_DB
435
432
  end
436
433
  after do
437
- @db.drop_table(:a)
434
+ @db.drop_table?(:a)
438
435
  end
439
436
 
440
437
  specify "should handle many existing types for set_column_allow_null" do
@@ -461,7 +458,7 @@ describe "MSSQL::Database#rename_table" do
461
458
  MSSQL_DB.create_table! :'foo bar' do
462
459
  text :name
463
460
  end
464
- MSSQL_DB.drop_table :baz rescue nil
461
+ MSSQL_DB.drop_table? :baz
465
462
  proc { MSSQL_DB.rename_table 'foo bar', 'baz' }.should_not raise_error
466
463
  end
467
464
 
@@ -501,7 +498,7 @@ describe "MSSQL::Database#mssql_unicode_strings = false" do
501
498
  MSSQL_DB.mssql_unicode_strings = false
502
499
  end
503
500
  after do
504
- MSSQL_DB.drop_table(:items)
501
+ MSSQL_DB.drop_table?(:items)
505
502
  MSSQL_DB.mssql_unicode_strings = true
506
503
  end
507
504
 
@@ -535,7 +532,7 @@ describe "A MSSQL database adds index with include" do
535
532
  end
536
533
 
537
534
  after :all do
538
- @db.drop_table @table_name
535
+ @db.drop_table? @table_name
539
536
  end
540
537
 
541
538
  cspecify "should be able add index with include" do
@@ -26,9 +26,7 @@ def logger.method_missing(m, msg)
26
26
  MYSQL_DB.sqls << msg
27
27
  end
28
28
  MYSQL_DB.loggers = [logger]
29
- MYSQL_DB.drop_table(:items) rescue nil
30
- MYSQL_DB.drop_table(:dolls) rescue nil
31
- MYSQL_DB.drop_table(:booltest) rescue nil
29
+ MYSQL_DB.drop_table?(:items, :dolls, :booltest)
32
30
 
33
31
  SQL_BEGIN = 'BEGIN'
34
32
  SQL_ROLLBACK = 'ROLLBACK'
@@ -40,7 +38,7 @@ describe "MySQL", '#create_table' do
40
38
  MYSQL_DB.sqls.clear
41
39
  end
42
40
  after do
43
- @db.drop_table(:dolls) rescue nil
41
+ @db.drop_table?(:dolls)
44
42
  end
45
43
 
46
44
  specify "should allow to specify options for MySQL" do
@@ -83,7 +81,7 @@ describe "MySQL", '#create_table' do
83
81
  @db.schema(:dolls).map{|k, v| v[:auto_increment]}.should == [nil, nil, true]
84
82
  end
85
83
 
86
- specify "should support collate with various other column options" do
84
+ cspecify "should support collate with various other column options", :swift do
87
85
  @db.create_table!(:dolls){ String :name, :size=>128, :collate=>:utf8_bin, :default=>'foo', :null=>false, :unique=>true}
88
86
  @db[:dolls].insert
89
87
  @db[:dolls].select_map(:name).should == ["foo"]
@@ -113,7 +111,7 @@ if MYSQL_DB.adapter_scheme == :mysql
113
111
  end
114
112
  after do
115
113
  @db.convert_tinyint_to_bool = true
116
- @db.drop_table(:booltest)
114
+ @db.drop_table?(:booltest)
117
115
  end
118
116
 
119
117
  specify "should consider tinyint(1) datatypes as boolean if set, but not larger tinyints" do
@@ -161,7 +159,7 @@ describe "A MySQL dataset" do
161
159
  MYSQL_DB.sqls.clear
162
160
  end
163
161
  after do
164
- MYSQL_DB.drop_table(:items)
162
+ MYSQL_DB.drop_table?(:items)
165
163
  end
166
164
 
167
165
  specify "should quote columns and tables using back-ticks if quoting identifiers" do
@@ -241,6 +239,12 @@ describe "A MySQL dataset" do
241
239
  @d.filter(:name => /^bc/).count.should == 1
242
240
  end
243
241
 
242
+ specify "should have explain output" do
243
+ @d.explain.should be_a_kind_of(String)
244
+ @d.explain(:extended=>true).should be_a_kind_of(String)
245
+ @d.explain.should_not == @d.explain(:extended=>true)
246
+ end
247
+
244
248
  specify "should correctly literalize strings with comment backslashes in them" do
245
249
  @d.delete
246
250
  proc {@d << {:name => ':\\'}}.should_not raise_error
@@ -285,7 +289,7 @@ describe "Dataset#distinct" do
285
289
  @ds = @db[:a]
286
290
  end
287
291
  after do
288
- @db.drop_table(:a)
292
+ @db.drop_table?(:a)
289
293
  end
290
294
 
291
295
  it "#distinct with arguments should return results distinct on those arguments" do
@@ -450,12 +454,12 @@ describe "A MySQL database with table options" do
450
454
  Sequel::MySQL.default_collate = 'utf8_general_ci'
451
455
 
452
456
  @db = MYSQL_DB
453
- @db.drop_table(:items) rescue nil
457
+ @db.drop_table?(:items)
454
458
 
455
459
  MYSQL_DB.sqls.clear
456
460
  end
457
461
  after do
458
- @db.drop_table(:items) rescue nil
462
+ @db.drop_table?(:items)
459
463
 
460
464
  Sequel::MySQL.default_engine = nil
461
465
  Sequel::MySQL.default_charset = nil
@@ -481,12 +485,11 @@ end
481
485
  describe "A MySQL database" do
482
486
  before do
483
487
  @db = MYSQL_DB
484
- @db.drop_table(:items) rescue nil
488
+ @db.drop_table?(:items)
485
489
  MYSQL_DB.sqls.clear
486
490
  end
487
491
  after do
488
- @db.drop_table(:items) rescue nil
489
- @db.drop_table(:users) rescue nil
492
+ @db.drop_table?(:items, :users)
490
493
  end
491
494
 
492
495
  specify "should support defaults for boolean columns" do
@@ -601,8 +604,7 @@ end
601
604
 
602
605
  describe "MySQL foreign key support" do
603
606
  after do
604
- MYSQL_DB.drop_table(:testfk) rescue nil
605
- MYSQL_DB.drop_table(:testpk) rescue nil
607
+ MYSQL_DB.drop_table?(:testfk, :testpk)
606
608
  end
607
609
 
608
610
  specify "should create table without :key" do
@@ -674,11 +676,11 @@ end
674
676
  describe "A MySQL database" do
675
677
  before do
676
678
  @db = MYSQL_DB
677
- @db.drop_table(:posts) rescue nil
679
+ @db.drop_table?(:posts)
678
680
  @db.sqls.clear
679
681
  end
680
682
  after do
681
- @db.drop_table(:posts) rescue nil
683
+ @db.drop_table?(:posts)
682
684
  end
683
685
 
684
686
  specify "should support fulltext indexes and full_text_search" do
@@ -750,7 +752,7 @@ describe "MySQL::Dataset#insert and related methods" do
750
752
  MYSQL_DB.sqls.clear
751
753
  end
752
754
  after do
753
- MYSQL_DB.drop_table(:items)
755
+ MYSQL_DB.drop_table?(:items)
754
756
  end
755
757
 
756
758
  specify "#insert should insert record with default values when no arguments given" do
@@ -786,6 +788,62 @@ describe "MySQL::Dataset#insert and related methods" do
786
788
  @d.all.should == [{:name => 'abc', :value => 6}, {:name => 'def', :value => 2}]
787
789
  end
788
790
 
791
+ specify "#multi_replace should insert multiple records in a single statement" do
792
+ @d.multi_replace([{:name => 'abc'}, {:name => 'def'}])
793
+
794
+ MYSQL_DB.sqls.should == [
795
+ SQL_BEGIN,
796
+ "REPLACE INTO `items` (`name`) VALUES ('abc'), ('def')",
797
+ SQL_COMMIT
798
+ ]
799
+
800
+ @d.all.should == [
801
+ {:name => 'abc', :value => nil}, {:name => 'def', :value => nil}
802
+ ]
803
+ end
804
+
805
+ specify "#multi_replace should split the list of records into batches if :commit_every option is given" do
806
+ @d.multi_replace([{:value => 1}, {:value => 2}, {:value => 3}, {:value => 4}],
807
+ :commit_every => 2)
808
+
809
+ MYSQL_DB.sqls.should == [
810
+ SQL_BEGIN,
811
+ "REPLACE INTO `items` (`value`) VALUES (1), (2)",
812
+ SQL_COMMIT,
813
+ SQL_BEGIN,
814
+ "REPLACE INTO `items` (`value`) VALUES (3), (4)",
815
+ SQL_COMMIT
816
+ ]
817
+
818
+ @d.all.should == [
819
+ {:name => nil, :value => 1},
820
+ {:name => nil, :value => 2},
821
+ {:name => nil, :value => 3},
822
+ {:name => nil, :value => 4}
823
+ ]
824
+ end
825
+
826
+ specify "#multi_replace should split the list of records into batches if :slice option is given" do
827
+ @d.multi_replace([{:value => 1}, {:value => 2}, {:value => 3}, {:value => 4}],
828
+ :slice => 2)
829
+
830
+ MYSQL_DB.sqls.should == [
831
+ SQL_BEGIN,
832
+ "REPLACE INTO `items` (`value`) VALUES (1), (2)",
833
+ SQL_COMMIT,
834
+ SQL_BEGIN,
835
+ "REPLACE INTO `items` (`value`) VALUES (3), (4)",
836
+ SQL_COMMIT
837
+ ]
838
+
839
+ @d.all.should == [
840
+ {:name => nil, :value => 1},
841
+ {:name => nil, :value => 2},
842
+ {:name => nil, :value => 3},
843
+ {:name => nil, :value => 4}
844
+ ]
845
+ end
846
+
789
847
  specify "#multi_insert should insert multiple records in a single statement" do
790
848
  @d.multi_insert([{:name => 'abc'}, {:name => 'def'}])
791
849
 
@@ -916,7 +974,7 @@ describe "MySQL::Dataset#update and related methods" do
916
974
  @d = MYSQL_DB[:items]
917
975
  end
918
976
  after do
919
- MYSQL_DB.drop_table(:items)
977
+ MYSQL_DB.drop_table?(:items)
920
978
  end
921
979
 
922
980
  specify "#update_ignore should not raise error where normal update would fail" do
@@ -937,7 +995,7 @@ describe "MySQL::Dataset#replace" do
937
995
  MYSQL_DB.sqls.clear
938
996
  end
939
997
  after do
940
- MYSQL_DB.drop_table(:items)
998
+ MYSQL_DB.drop_table?(:items)
941
999
  end
942
1000
 
943
1001
  specify "should use default values if they exist" do
@@ -1005,7 +1063,7 @@ describe "MySQL::Dataset#calc_found_rows" do
1005
1063
  MYSQL_DB.create_table!(:items){Integer :a}
1006
1064
  end
1007
1065
  after do
1008
- MYSQL_DB.drop_table(:items)
1066
+ MYSQL_DB.drop_table?(:items)
1009
1067
  end
1010
1068
 
1011
1069
  specify "should add the SQL_CALC_FOUND_ROWS keyword when selecting" do
@@ -1035,7 +1093,7 @@ if MYSQL_DB.adapter_scheme == :mysql or MYSQL_DB.adapter_scheme == :jdbc or MYSQ
1035
1093
  MYSQL_DB.sqls.clear
1036
1094
  end
1037
1095
  after do
1038
- MYSQL_DB.drop_table(:items)
1096
+ MYSQL_DB.drop_table?(:items)
1039
1097
  MYSQL_DB.execute('DROP PROCEDURE test_sproc')
1040
1098
  end
1041
1099
 
@@ -1126,7 +1184,7 @@ if MYSQL_DB.adapter_scheme == :mysql
1126
1184
  MYSQL_DB[:b].insert(25)
1127
1185
  end
1128
1186
  after do
1129
- MYSQL_DB.drop_table(:a, :b)
1187
+ MYSQL_DB.drop_table?(:a, :b)
1130
1188
  end
1131
1189
 
1132
1190
  specify "should combine all results by default" do
@@ -6,6 +6,14 @@ unless defined?(POSTGRES_DB)
6
6
  end
7
7
  INTEGRATION_DB = POSTGRES_DB unless defined?(INTEGRATION_DB)
8
8
 
9
+ # Automatic parameterization changes the SQL used, so don't check
10
+ # for expected SQL if it is being used.
11
+ if defined?(Sequel::Postgres::AutoParameterize)
12
+ check_sqls = false
13
+ else
14
+ check_sqls = true
15
+ end
16
+
9
17
  def POSTGRES_DB.sqls
10
18
  (@sqls ||= [])
11
19
  end
@@ -13,7 +21,7 @@ logger = Object.new
13
21
  def logger.method_missing(m, msg)
14
22
  POSTGRES_DB.sqls << msg
15
23
  end
16
- POSTGRES_DB.loggers = [logger]
24
+ POSTGRES_DB.loggers << logger
17
25
 
18
26
  #POSTGRES_DB.instance_variable_set(:@server_version, 80100)
19
27
  POSTGRES_DB.create_table! :test do
@@ -61,7 +69,6 @@ describe "A PostgreSQL dataset" do
61
69
  end
62
70
 
63
71
  specify "should quote columns and tables using double quotes if quoting identifiers" do
64
- @d.quote_identifiers = true
65
72
  @d.select(:name).sql.should == \
66
73
  'SELECT "name" FROM "test"'
67
74
 
@@ -89,27 +96,28 @@ describe "A PostgreSQL dataset" do
89
96
  @d.select('max(test."name") AS "max_name"'.lit).sql.should == \
90
97
  'SELECT max(test."name") AS "max_name" FROM "test"'
91
98
 
92
- @d.select(:test.sql_function(:abc, 'hello')).sql.should == \
93
- "SELECT test(\"abc\", 'hello') FROM \"test\""
99
+ @d.insert_sql(:x => :y).should =~ \
100
+ /\AINSERT INTO "test" \("x"\) VALUES \("y"\)( RETURNING NULL)?\z/
94
101
 
95
- @d.select(:test.sql_function(:abc__def, 'hello')).sql.should == \
96
- "SELECT test(\"abc\".\"def\", 'hello') FROM \"test\""
102
+ if check_sqls
103
+ @d.select(:test.sql_function(:abc, 'hello')).sql.should == \
104
+ "SELECT test(\"abc\", 'hello') FROM \"test\""
97
105
 
98
- @d.select(:test.sql_function(:abc__def, 'hello').as(:x2)).sql.should == \
99
- "SELECT test(\"abc\".\"def\", 'hello') AS \"x2\" FROM \"test\""
106
+ @d.select(:test.sql_function(:abc__def, 'hello')).sql.should == \
107
+ "SELECT test(\"abc\".\"def\", 'hello') FROM \"test\""
100
108
 
101
- @d.insert_sql(:value => 333).should =~ \
102
- /\AINSERT INTO "test" \("value"\) VALUES \(333\)( RETURNING NULL)?\z/
109
+ @d.select(:test.sql_function(:abc__def, 'hello').as(:x2)).sql.should == \
110
+ "SELECT test(\"abc\".\"def\", 'hello') AS \"x2\" FROM \"test\""
103
111
 
104
- @d.insert_sql(:x => :y).should =~ \
105
- /\AINSERT INTO "test" \("x"\) VALUES \("y"\)( RETURNING NULL)?\z/
112
+ @d.insert_sql(:value => 333).should =~ \
113
+ /\AINSERT INTO "test" \("value"\) VALUES \(333\)( RETURNING NULL)?\z/
106
114
 
107
- @d.disable_insert_returning.insert_sql(:value => 333).should =~ \
108
- /\AINSERT INTO "test" \("value"\) VALUES \(333\)\z/
115
+ @d.disable_insert_returning.insert_sql(:value => 333).should =~ \
116
+ /\AINSERT INTO "test" \("value"\) VALUES \(333\)\z/
117
+ end
109
118
  end
110
119
 
111
120
  specify "should quote fields correctly when reversing the order if quoting identifiers" do
112
- @d.quote_identifiers = true
113
121
  @d.reverse_order(:name).sql.should == \
114
122
  'SELECT * FROM "test" ORDER BY "name" DESC'
115
123
 
@@ -167,7 +175,7 @@ describe "Dataset#distinct" do
167
175
  @ds = @db[:a]
168
176
  end
169
177
  after do
170
- @db.drop_table(:a)
178
+ @db.drop_table?(:a)
171
179
  end
172
180
 
173
181
  it "#distinct with arguments should return results distinct on those arguments" do
@@ -191,7 +199,7 @@ if POSTGRES_DB.pool.respond_to?(:max_size) and POSTGRES_DB.pool.max_size > 1
191
199
  @ds = POSTGRES_DB[:items]
192
200
  end
193
201
  after do
194
- POSTGRES_DB.drop_table(:items)
202
+ POSTGRES_DB.drop_table?(:items)
195
203
  POSTGRES_DB.disconnect
196
204
  end
197
205
 
@@ -240,27 +248,60 @@ end
240
248
 
241
249
  describe "A PostgreSQL dataset with a timestamp field" do
242
250
  before do
243
- @d = POSTGRES_DB[:test3]
251
+ @db = POSTGRES_DB
252
+ @d = @db[:test3]
244
253
  @d.delete
245
254
  end
255
+ after do
256
+ @db.convert_infinite_timestamps = false if @db.adapter_scheme == :postgres
257
+ end
246
258
 
247
- cspecify "should store milliseconds in time fields for Time objects", :do do
259
+ cspecify "should store milliseconds in time fields for Time objects", :do, :swift do
248
260
  t = Time.now
249
261
  @d << {:value=>1, :time=>t}
250
- t2 = @d[:value =>'1'][:time]
262
+ t2 = @d[:value =>1][:time]
251
263
  @d.literal(t2).should == @d.literal(t)
252
264
  t2.strftime('%Y-%m-%d %H:%M:%S').should == t.strftime('%Y-%m-%d %H:%M:%S')
253
265
  t2.is_a?(Time) ? t2.usec : t2.strftime('%N').to_i/1000 == t.usec
254
266
  end
255
267
 
256
- cspecify "should store milliseconds in time fields for DateTime objects", :do do
268
+ cspecify "should store milliseconds in time fields for DateTime objects", :do, :swift do
257
269
  t = DateTime.now
258
270
  @d << {:value=>1, :time=>t}
259
- t2 = @d[:value =>'1'][:time]
271
+ t2 = @d[:value =>1][:time]
260
272
  @d.literal(t2).should == @d.literal(t)
261
273
  t2.strftime('%Y-%m-%d %H:%M:%S').should == t.strftime('%Y-%m-%d %H:%M:%S')
262
274
  t2.is_a?(Time) ? t2.usec : t2.strftime('%N').to_i/1000 == t.strftime('%N').to_i/1000
263
275
  end
276
+
277
+ if POSTGRES_DB.adapter_scheme == :postgres
278
+ specify "should handle infinite timestamps if convert_infinite_timestamps is set" do
279
+ @d << {:time=>'infinity'.cast(:timestamp)}
280
+ @db.convert_infinite_timestamps = :nil
281
+ @db[:test3].get(:time).should == nil
282
+ @db.convert_infinite_timestamps = :string
283
+ @db[:test3].get(:time).should == 'infinity'
284
+ @db.convert_infinite_timestamps = :float
285
+ @db[:test3].get(:time).should == 1.0/0.0
286
+
287
+ @d.update(:time=>'-infinity'.cast(:timestamp))
288
+ @db.convert_infinite_timestamps = :nil
289
+ @db[:test3].get(:time).should == nil
290
+ @db.convert_infinite_timestamps = :string
291
+ @db[:test3].get(:time).should == '-infinity'
292
+ @db.convert_infinite_timestamps = :float
293
+ @db[:test3].get(:time).should == -1.0/0.0
294
+ end
295
+
296
+ specify "should handle conversions from infinite strings/floats in models" do
297
+ c = Class.new(Sequel::Model(:test3))
298
+ @db.convert_infinite_timestamps = :float
299
+ c.new(:time=>'infinity').time.should == 'infinity'
300
+ c.new(:time=>'-infinity').time.should == '-infinity'
301
+ c.new(:time=>1.0/0.0).time.should == 1.0/0.0
302
+ c.new(:time=>-1.0/0.0).time.should == -1.0/0.0
303
+ end
304
+ end
264
305
  end
265
306
 
266
307
  describe "PostgreSQL's EXPLAIN and ANALYZE" do
@@ -317,11 +358,11 @@ end
317
358
  describe "A PostgreSQL database" do
318
359
  before do
319
360
  @db = POSTGRES_DB
320
- @db.drop_table(:posts) rescue nil
361
+ @db.drop_table?(:posts)
321
362
  @db.sqls.clear
322
363
  end
323
364
  after do
324
- @db.drop_table(:posts) rescue nil
365
+ @db.drop_table?(:posts)
325
366
  end
326
367
 
327
368
  specify "should support resetting the primary key sequence" do
@@ -364,9 +405,9 @@ describe "A PostgreSQL database" do
364
405
  @db.create_table(:posts){text :title; text :body; full_text_index [:title, :body]; full_text_index :title, :language => 'french'}
365
406
  @db.sqls.should == [
366
407
  %{CREATE TABLE "posts" ("title" text, "body" text)},
367
- %{CREATE INDEX "posts_title_body_index" ON "posts" USING gin (to_tsvector('simple', (COALESCE("title", '') || ' ' || COALESCE("body", ''))))},
368
- %{CREATE INDEX "posts_title_index" ON "posts" USING gin (to_tsvector('french', (COALESCE("title", ''))))}
369
- ]
408
+ %{CREATE INDEX "posts_title_body_index" ON "posts" USING gin (to_tsvector('simple'::regconfig, (COALESCE("title", '') || ' ' || COALESCE("body", ''))))},
409
+ %{CREATE INDEX "posts_title_index" ON "posts" USING gin (to_tsvector('french'::regconfig, (COALESCE("title", ''))))}
410
+ ] if check_sqls
370
411
 
371
412
  @db[:posts].insert(:title=>'ruby rails', :body=>'yowsa')
372
413
  @db[:posts].insert(:title=>'sequel', :body=>'ruby')
@@ -377,9 +418,9 @@ describe "A PostgreSQL database" do
377
418
  @db[:posts].full_text_search([:title, :body], ['yowsa', 'rails']).all.should == [:title=>'ruby rails', :body=>'yowsa']
378
419
  @db[:posts].full_text_search(:title, 'scooby', :language => 'french').all.should == [{:title=>'ruby scooby', :body=>'x'}]
379
420
  @db.sqls.should == [
380
- %{SELECT * FROM "posts" WHERE (to_tsvector('simple', (COALESCE("title", ''))) @@ to_tsquery('simple', 'rails'))},
381
- %{SELECT * FROM "posts" WHERE (to_tsvector('simple', (COALESCE("title", '') || ' ' || COALESCE("body", ''))) @@ to_tsquery('simple', 'yowsa | rails'))},
382
- %{SELECT * FROM "posts" WHERE (to_tsvector('french', (COALESCE("title", ''))) @@ to_tsquery('french', 'scooby'))}]
421
+ %{SELECT * FROM "posts" WHERE (to_tsvector('simple'::regconfig, (COALESCE("title", ''))) @@ to_tsquery('simple'::regconfig, 'rails'))},
422
+ %{SELECT * FROM "posts" WHERE (to_tsvector('simple'::regconfig, (COALESCE("title", '') || ' ' || COALESCE("body", ''))) @@ to_tsquery('simple'::regconfig, 'yowsa | rails'))},
423
+ %{SELECT * FROM "posts" WHERE (to_tsvector('french'::regconfig, (COALESCE("title", ''))) @@ to_tsquery('french'::regconfig, 'scooby'))}] if check_sqls
383
424
 
384
425
  @db[:posts].full_text_search(:title, :$n).call(:select, :n=>'rails').should == [{:title=>'ruby rails', :body=>'yowsa'}]
385
426
  @db[:posts].full_text_search(:title, :$n).prepare(:select, :fts_select).call(:n=>'rails').should == [{:title=>'ruby rails', :body=>'yowsa'}]
@@ -439,13 +480,13 @@ describe "Postgres::Dataset#import" do
439
480
  @ds = @db[:test]
440
481
  end
441
482
  after do
442
- @db.drop_table(:test) rescue nil
483
+ @db.drop_table?(:test)
443
484
  end
444
485
 
445
486
  specify "#import should return separate insert statements if server_version < 80200" do
446
487
  @ds.meta_def(:server_version){80199}
447
488
  @ds.import([:x, :y], [[1, 2], [3, 4]])
448
- @db.sqls.should == ['BEGIN', 'INSERT INTO "test" ("x", "y") VALUES (1, 2)', 'INSERT INTO "test" ("x", "y") VALUES (3, 4)', 'COMMIT']
489
+ @db.sqls.should == ['BEGIN', 'INSERT INTO "test" ("x", "y") VALUES (1, 2)', 'INSERT INTO "test" ("x", "y") VALUES (3, 4)', 'COMMIT'] if check_sqls
449
490
  @ds.all.should == [{:x=>1, :y=>2}, {:x=>3, :y=>4}]
450
491
  end
451
492
 
@@ -495,7 +536,7 @@ describe "Postgres::Dataset#insert" do
495
536
  @ds = @db[:test5]
496
537
  end
497
538
  after do
498
- @db.drop_table(:test5) rescue nil
539
+ @db.drop_table?(:test5)
499
540
  end
500
541
 
501
542
  specify "should work with static SQL" do
@@ -516,7 +557,7 @@ describe "Postgres::Dataset#insert" do
516
557
  "SELECT currval('\"public\".test5_xid_seq')",
517
558
  'INSERT INTO "test5" ("value") VALUES (13)',
518
559
  "SELECT currval('\"public\".test5_xid_seq')"
519
- ]
560
+ ] if check_sqls
520
561
  @ds.all.should == [{:xid=>1, :value=>10}, {:xid=>2, :value=>20}, {:xid=>3, :value=>13}]
521
562
  end
522
563
 
@@ -540,7 +581,7 @@ describe "Postgres::Dataset#insert" do
540
581
  specify "should use INSERT RETURNING if server_version >= 80200" do
541
582
  @ds.meta_def(:server_version){80201}
542
583
  @ds.insert(:value=>10).should == 1
543
- @db.sqls.last.should == 'INSERT INTO "test5" ("value") VALUES (10) RETURNING "xid"'
584
+ @db.sqls.last.should == 'INSERT INTO "test5" ("value") VALUES (10) RETURNING "xid"' if check_sqls
544
585
  end
545
586
 
546
587
  specify "should have insert_select return nil if server_version < 80200" do
@@ -582,7 +623,6 @@ describe "Postgres::Database schema qualified tables" do
582
623
  POSTGRES_DB.instance_variable_set(:@primary_key_sequences, {})
583
624
  end
584
625
  after do
585
- POSTGRES_DB.quote_identifiers = false
586
626
  POSTGRES_DB << "DROP SCHEMA schema_test CASCADE"
587
627
  POSTGRES_DB.default_schema = nil
588
628
  end
@@ -638,7 +678,7 @@ describe "Postgres::Database schema qualified tables" do
638
678
  POSTGRES_DB.schema(:public__domains).map{|x| x.first}.should == [:d]
639
679
  POSTGRES_DB.schema(:schema_test__domains).map{|x| x.first}.should == [:i]
640
680
  ensure
641
- POSTGRES_DB.drop_table(:public__domains)
681
+ POSTGRES_DB.drop_table?(:public__domains)
642
682
  end
643
683
  end
644
684
 
@@ -658,7 +698,6 @@ describe "Postgres::Database schema qualified tables" do
658
698
  end
659
699
 
660
700
  specify "should be able to get serial sequences for tables that have spaces in the name in a given schema" do
661
- POSTGRES_DB.quote_identifiers = true
662
701
  POSTGRES_DB.create_table(:"schema_test__schema test"){primary_key :i}
663
702
  POSTGRES_DB.primary_key_sequence(:"schema_test__schema test").should == '"schema_test"."schema test_i_seq"'
664
703
  end
@@ -670,7 +709,6 @@ describe "Postgres::Database schema qualified tables" do
670
709
  end
671
710
 
672
711
  specify "should be able to get custom sequences for tables that have spaces in the name in a given schema" do
673
- POSTGRES_DB.quote_identifiers = true
674
712
  POSTGRES_DB << "CREATE SEQUENCE schema_test.\"ks eq\""
675
713
  POSTGRES_DB.create_table(:"schema_test__schema test"){integer :j; primary_key :k, :type=>:integer, :default=>"nextval('schema_test.\"ks eq\"'::regclass)".lit}
676
714
  POSTGRES_DB.primary_key_sequence(:"schema_test__schema test").should == '"schema_test"."ks eq"'
@@ -691,7 +729,6 @@ describe "Postgres::Database schema qualified tables and eager graphing" do
691
729
  @db = POSTGRES_DB
692
730
  @db.run "DROP SCHEMA s CASCADE" rescue nil
693
731
  @db.run "CREATE SCHEMA s"
694
- @db.quote_identifiers = true
695
732
 
696
733
  @db.create_table(:s__bands){primary_key :id; String :name}
697
734
  @db.create_table(:s__albums){primary_key :id; String :name; foreign_key :band_id, :s__bands}
@@ -733,7 +770,6 @@ describe "Postgres::Database schema qualified tables and eager graphing" do
733
770
  @m4 = @Member.create(:name=>"JC", :band=>@b2)
734
771
  end
735
772
  after(:all) do
736
- @db.quote_identifiers = false
737
773
  @db.run "DROP SCHEMA s CASCADE"
738
774
  end
739
775
 
@@ -916,7 +952,7 @@ if POSTGRES_DB.dataset.supports_window_functions?
916
952
  @ds.insert(:id=>6, :group_id=>2, :amount=>100000)
917
953
  end
918
954
  after do
919
- @db.drop_table(:i1)
955
+ @db.drop_table?(:i1)
920
956
  end
921
957
 
922
958
  specify "should give correct results for window functions" do
@@ -941,7 +977,7 @@ describe "Postgres::Database functions, languages, schemas, and triggers" do
941
977
  @d.drop_function('tf', :if_exists=>true, :cascade=>true, :args=>%w'integer integer')
942
978
  @d.drop_language(:plpgsql, :if_exists=>true, :cascade=>true) if @d.server_version < 90000
943
979
  @d.drop_schema(:sequel, :if_exists=>true, :cascade=>true)
944
- @d.drop_table(:test) rescue nil
980
+ @d.drop_table?(:test)
945
981
  end
946
982
 
947
983
  specify "#create_function and #drop_function should create and drop functions" do
@@ -983,9 +1019,9 @@ describe "Postgres::Database functions, languages, schemas, and triggers" do
983
1019
  end
984
1020
 
985
1021
  specify "#create_schema and #drop_schema should create and drop schemas" do
986
- @d.send(:create_schema_sql, :sequel).should == 'CREATE SCHEMA sequel'
987
- @d.send(:drop_schema_sql, :sequel).should == 'DROP SCHEMA sequel'
988
- @d.send(:drop_schema_sql, :sequel, :if_exists=>true, :cascade=>true).should == 'DROP SCHEMA IF EXISTS sequel CASCADE'
1022
+ @d.send(:create_schema_sql, :sequel).should == 'CREATE SCHEMA "sequel"'
1023
+ @d.send(:drop_schema_sql, :sequel).should == 'DROP SCHEMA "sequel"'
1024
+ @d.send(:drop_schema_sql, :sequel, :if_exists=>true, :cascade=>true).should == 'DROP SCHEMA IF EXISTS "sequel" CASCADE'
989
1025
  @d.create_schema(:sequel)
990
1026
  @d.create_table(:sequel__test){Integer :a}
991
1027
  if @d.server_version >= 80200
@@ -998,7 +1034,7 @@ describe "Postgres::Database functions, languages, schemas, and triggers" do
998
1034
  specify "#create_trigger and #drop_trigger should create and drop triggers" do
999
1035
  @d.create_language(:plpgsql) if @d.server_version < 90000
1000
1036
  @d.create_function(:tf, 'BEGIN IF NEW.value IS NULL THEN RAISE EXCEPTION \'Blah\'; END IF; RETURN NEW; END;', :language=>:plpgsql, :returns=>:trigger)
1001
- @d.send(:create_trigger_sql, :test, :identity, :tf, :each_row=>true).should == 'CREATE TRIGGER identity BEFORE INSERT OR UPDATE OR DELETE ON test FOR EACH ROW EXECUTE PROCEDURE tf()'
1037
+ @d.send(:create_trigger_sql, :test, :identity, :tf, :each_row=>true).should == 'CREATE TRIGGER identity BEFORE INSERT OR UPDATE OR DELETE ON "test" FOR EACH ROW EXECUTE PROCEDURE tf()'
1002
1038
  @d.create_table(:test){String :name; Integer :value}
1003
1039
  @d.create_trigger(:test, :identity, :tf, :each_row=>true)
1004
1040
  @d[:test].insert(:name=>'a', :value=>1)
@@ -1007,10 +1043,10 @@ describe "Postgres::Database functions, languages, schemas, and triggers" do
1007
1043
  @d[:test].filter(:name=>'a').all.should == [{:name=>'a', :value=>1}]
1008
1044
  @d[:test].filter(:name=>'a').update(:value=>3)
1009
1045
  @d[:test].filter(:name=>'a').all.should == [{:name=>'a', :value=>3}]
1010
- @d.send(:drop_trigger_sql, :test, :identity).should == 'DROP TRIGGER identity ON test'
1046
+ @d.send(:drop_trigger_sql, :test, :identity).should == 'DROP TRIGGER identity ON "test"'
1011
1047
  @d.drop_trigger(:test, :identity)
1012
- @d.send(:create_trigger_sql, :test, :identity, :tf, :after=>true, :events=>:insert, :args=>[1, 'a']).should == 'CREATE TRIGGER identity AFTER INSERT ON test EXECUTE PROCEDURE tf(1, \'a\')'
1013
- @d.send(:drop_trigger_sql, :test, :identity, :if_exists=>true, :cascade=>true).should == 'DROP TRIGGER IF EXISTS identity ON test CASCADE'
1048
+ @d.send(:create_trigger_sql, :test, :identity, :tf, :after=>true, :events=>:insert, :args=>[1, 'a']).should == 'CREATE TRIGGER identity AFTER INSERT ON "test" EXECUTE PROCEDURE tf(1, \'a\')'
1049
+ @d.send(:drop_trigger_sql, :test, :identity, :if_exists=>true, :cascade=>true).should == 'DROP TRIGGER IF EXISTS identity ON "test" CASCADE'
1014
1050
  # Make sure if exists works
1015
1051
  @d.drop_trigger(:test, :identity, :if_exists=>true, :cascade=>true)
1016
1052
  end
@@ -1026,7 +1062,7 @@ if POSTGRES_DB.adapter_scheme == :postgres
1026
1062
  @db.transaction{1001.times{|i| @ds.insert(i)}}
1027
1063
  end
1028
1064
  after(:all) do
1029
- @db.drop_table(:test_cursor) rescue nil
1065
+ @db.drop_table?(:test_cursor)
1030
1066
  end
1031
1067
 
1032
1068
  specify "should return the same results as the non-cursor use" do
@@ -1055,23 +1091,17 @@ if POSTGRES_DB.adapter_scheme == :postgres
1055
1091
  before do
1056
1092
  @db = POSTGRES_DB
1057
1093
  Sequel::Postgres::PG_NAMED_TYPES[:interval] = lambda{|v| v.reverse}
1058
- @db.instance_eval do
1059
- disconnect
1060
- @conversion_procs = nil
1061
- end
1094
+ @db.reset_conversion_procs
1062
1095
  end
1063
1096
  after do
1064
1097
  Sequel::Postgres::PG_NAMED_TYPES.delete(:interval)
1065
- @db.instance_eval do
1066
- disconnect
1067
- @conversion_procs = nil
1068
- end
1069
- @db.drop_table(:foo)
1098
+ @db.reset_conversion_procs
1099
+ @db.drop_table?(:foo)
1070
1100
  end
1071
1101
 
1072
1102
  specify "should look up conversion procs by name" do
1073
1103
  @db.create_table!(:foo){interval :bar}
1074
- @db[:foo].insert('21 days')
1104
+ @db[:foo].insert('21 days'.cast(:interval))
1075
1105
  @db[:foo].get(:bar).should == 'syad 12'
1076
1106
  end
1077
1107
  end
@@ -1087,7 +1117,7 @@ if POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG && POSTGRE
1087
1117
  ds.insert(3, 4)
1088
1118
  end
1089
1119
  after(:all) do
1090
- @db.drop_table(:test_copy) rescue nil
1120
+ @db.drop_table?(:test_copy)
1091
1121
  end
1092
1122
 
1093
1123
  specify "without a block or options should return a text version of the table as a single string" do
@@ -1111,7 +1141,7 @@ if POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG && POSTGRE
1111
1141
  end
1112
1142
 
1113
1143
  specify "should accept dataset as first argument" do
1114
- @db.copy_table(@db[:test_copy].cross_join(:test_copy___tc).order(1, 2, 3, 4)).should == "1\t2\t1\t2\n1\t2\t3\t4\n3\t4\t1\t2\n3\t4\t3\t4\n"
1144
+ @db.copy_table(@db[:test_copy].cross_join(:test_copy___tc).order(:test_copy__x, :test_copy__y, :tc__x, :tc__y)).should == "1\t2\t1\t2\n1\t2\t3\t4\n3\t4\t1\t2\n3\t4\t3\t4\n"
1115
1145
  end
1116
1146
 
1117
1147
  specify "with a block and no options should yield each row as a string in text format" do
@@ -1207,11 +1237,11 @@ if POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG && POSTGRE
1207
1237
  i = 0
1208
1238
  @db.listen('foo2', :timeout=>0.001, :loop=>proc{i+=1; throw :stop if i > 3}){|ev, pid, payload| called = true}.should == nil
1209
1239
  i.should == 4
1210
- end
1240
+ end unless RUBY_VERSION == '1.9.2' && RUBY_PLATFORM =~ /mingw/ # Ruby freezes on this spec on this platform/version
1211
1241
  end
1212
1242
  end
1213
1243
 
1214
- describe 'POSTGRES special float handling' do
1244
+ describe 'PostgreSQL special float handling' do
1215
1245
  before do
1216
1246
  @db = POSTGRES_DB
1217
1247
  @db.create_table!(:test5){Float :value}
@@ -1219,22 +1249,24 @@ describe 'POSTGRES special float handling' do
1219
1249
  @ds = @db[:test5]
1220
1250
  end
1221
1251
  after do
1222
- @db.drop_table(:test5) rescue nil
1252
+ @db.drop_table?(:test5)
1223
1253
  end
1224
1254
 
1225
- specify 'should quote NaN' do
1226
- nan = 0.0/0.0
1227
- @ds.insert_sql(:value => nan).should == %q{INSERT INTO test5 (value) VALUES ('NaN')}
1228
- end
1255
+ if check_sqls
1256
+ specify 'should quote NaN' do
1257
+ nan = 0.0/0.0
1258
+ @ds.insert_sql(:value => nan).should == %q{INSERT INTO "test5" ("value") VALUES ('NaN')}
1259
+ end
1229
1260
 
1230
- specify 'should quote +Infinity' do
1231
- inf = 1.0/0.0
1232
- @ds.insert_sql(:value => inf).should == %q{INSERT INTO test5 (value) VALUES ('Infinity')}
1233
- end
1261
+ specify 'should quote +Infinity' do
1262
+ inf = 1.0/0.0
1263
+ @ds.insert_sql(:value => inf).should == %q{INSERT INTO "test5" ("value") VALUES ('Infinity')}
1264
+ end
1234
1265
 
1235
- specify 'should quote -Infinity' do
1236
- inf = -1.0/0.0
1237
- @ds.insert_sql(:value => inf).should == %q{INSERT INTO test5 (value) VALUES ('-Infinity')}
1266
+ specify 'should quote -Infinity' do
1267
+ inf = -1.0/0.0
1268
+ @ds.insert_sql(:value => inf).should == %q{INSERT INTO "test5" ("value") VALUES ('-Infinity')}
1269
+ end
1238
1270
  end
1239
1271
 
1240
1272
  if POSTGRES_DB.adapter_scheme == :postgres
@@ -1257,3 +1289,338 @@ describe 'POSTGRES special float handling' do
1257
1289
  end
1258
1290
  end
1259
1291
  end
1292
+
1293
+ describe 'PostgreSQL array handling' do
1294
+ before(:all) do
1295
+ Sequel.extension :pg_array
1296
+ @db = POSTGRES_DB
1297
+ @db.extend Sequel::Postgres::PGArray::DatabaseMethods
1298
+ @ds = @db[:items]
1299
+ @native = POSTGRES_DB.adapter_scheme == :postgres
1300
+ end
1301
+ after do
1302
+ @db.drop_table?(:items)
1303
+ end
1304
+
1305
+ specify 'insert and retrieve integer and float arrays of various sizes' do
1306
+ @db.create_table!(:items) do
1307
+ column :i2, 'int2[]'
1308
+ column :i4, 'int4[]'
1309
+ column :i8, 'int8[]'
1310
+ column :r, 'real[]'
1311
+ column :dp, 'double precision[]'
1312
+ end
1313
+ @ds.insert([1].pg_array(:int2), [nil, 2].pg_array(:int4), [3, nil].pg_array(:int8), [4, nil, 4.5].pg_array(:real), [5, nil, 5.5].pg_array("double precision"))
1314
+ @ds.count.should == 1
1315
+ if @native
1316
+ rs = @ds.all
1317
+ rs.should == [{:i2=>[1], :i4=>[nil, 2], :i8=>[3, nil], :r=>[4.0, nil, 4.5], :dp=>[5.0, nil, 5.5]}]
1318
+ rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
1319
+ rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
1320
+ @ds.delete
1321
+ @ds.insert(rs.first)
1322
+ @ds.all.should == rs
1323
+
1324
+ @ds.delete
1325
+ @ds.insert([[1], [2]].pg_array(:int2), [[nil, 2], [3, 4]].pg_array(:int4), [[3, nil], [nil, nil]].pg_array(:int8), [[4, nil], [nil, 4.5]].pg_array(:real), [[5, nil], [nil, 5.5]].pg_array("double precision"))
1326
+ rs = @ds.all
1327
+ rs.should == [{:i2=>[[1], [2]], :i4=>[[nil, 2], [3, 4]], :i8=>[[3, nil], [nil, nil]], :r=>[[4, nil], [nil, 4.5]], :dp=>[[5, nil], [nil, 5.5]]}]
1328
+ rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
1329
+ rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
1330
+ @ds.delete
1331
+ @ds.insert(rs.first)
1332
+ @ds.all.should == rs
1333
+ end
1334
+ end
1335
+
1336
+ specify 'insert and retrieve decimal arrays' do
1337
+ @db.create_table!(:items) do
1338
+ column :n, 'numeric[]'
1339
+ end
1340
+ @ds.insert([BigDecimal.new('1.000000000000000000001'), nil, BigDecimal.new('1')].pg_array(:numeric))
1341
+ @ds.count.should == 1
1342
+ if @native
1343
+ rs = @ds.all
1344
+ rs.should == [{:n=>[BigDecimal.new('1.000000000000000000001'), nil, BigDecimal.new('1')]}]
1345
+ rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
1346
+ rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
1347
+ @ds.delete
1348
+ @ds.insert(rs.first)
1349
+ @ds.all.should == rs
1350
+
1351
+ @ds.delete
1352
+ @ds.insert([[BigDecimal.new('1.0000000000000000000000000000001'), nil], [nil, BigDecimal.new('1')]].pg_array(:numeric))
1353
+ rs = @ds.all
1354
+ rs.should == [{:n=>[[BigDecimal.new('1.0000000000000000000000000000001'), nil], [nil, BigDecimal.new('1')]]}]
1355
+ rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
1356
+ rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
1357
+ @ds.delete
1358
+ @ds.insert(rs.first)
1359
+ @ds.all.should == rs
1360
+ end
1361
+ end
1362
+
1363
+ specify 'insert and retrieve string arrays' do
1364
+ @db.create_table!(:items) do
1365
+ column :c, 'char(4)[]'
1366
+ column :vc, 'varchar[]'
1367
+ column :t, 'text[]'
1368
+ end
1369
+ @ds.insert(['a', nil, 'NULL', 'b"\'c'].pg_array('char(4)'), ['a', nil, 'NULL', 'b"\'c'].pg_array(:varchar), ['a', nil, 'NULL', 'b"\'c'].pg_array(:text))
1370
+ @ds.count.should == 1
1371
+ if @native
1372
+ rs = @ds.all
1373
+ rs.should == [{:c=>['a ', nil, 'NULL', 'b"\'c'], :vc=>['a', nil, 'NULL', 'b"\'c'], :t=>['a', nil, 'NULL', 'b"\'c']}]
1374
+ rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
1375
+ rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
1376
+ @ds.delete
1377
+ @ds.insert(rs.first)
1378
+ @ds.all.should == rs
1379
+
1380
+ @ds.delete
1381
+ @ds.insert([[['a'], [nil]], [['NULL'], ['b"\'c']]].pg_array('char(4)'), [[['a'], ['']], [['NULL'], ['b"\'c']]].pg_array(:varchar), [[['a'], [nil]], [['NULL'], ['b"\'c']]].pg_array(:text))
1382
+ rs = @ds.all
1383
+ rs.should == [{:c=>[[['a '], [nil]], [['NULL'], ['b"\'c']]], :vc=>[[['a'], ['']], [['NULL'], ['b"\'c']]], :t=>[[['a'], [nil]], [['NULL'], ['b"\'c']]]}]
1384
+ rs.first.values.each{|v| v.should_not be_a_kind_of(Array)}
1385
+ rs.first.values.each{|v| v.to_a.should be_a_kind_of(Array)}
1386
+ @ds.delete
1387
+ @ds.insert(rs.first)
1388
+ @ds.all.should == rs
1389
+ end
1390
+ end
1391
+
1392
+ specify 'use arrays in bound variables' do
1393
+ @db.create_table!(:items) do
1394
+ column :i, 'int4[]'
1395
+ end
1396
+ @ds.call(:insert, {:i=>[1,2]}, {:i=>:$i})
1397
+ @ds.get(:i).should == [1, 2]
1398
+ @ds.filter(:i=>:$i).call(:first, :i=>[1,2]).should == {:i=>[1,2]}
1399
+ @ds.filter(:i=>:$i).call(:first, :i=>[1,3]).should == nil
1400
+
1401
+ @db.create_table!(:items) do
1402
+ column :i, 'text[]'
1403
+ end
1404
+ a = ["\"\\\\\"{}\n\t\r \v\b123afP", 'NULL', nil, '']
1405
+ @ds.call(:insert, {:i=>:$i}, :i=>a.pg_array)
1406
+ @ds.get(:i).should == a
1407
+ @ds.filter(:i=>:$i).call(:first, :i=>a).should == {:i=>a}
1408
+ @ds.filter(:i=>:$i).call(:first, :i=>['', nil, nil, 'a']).should == nil
1409
+ end if POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG
1410
+
1411
+ specify 'with models' do
1412
+ @db.create_table!(:items) do
1413
+ primary_key :id
1414
+ column :i, 'integer[]'
1415
+ column :f, 'double precision[]'
1416
+ column :d, 'numeric[]'
1417
+ column :t, 'text[]'
1418
+ end
1419
+ c = Class.new(Sequel::Model(@db[:items]))
1420
+ c.plugin :typecast_on_load, :i, :f, :d, :t unless @native
1421
+ o = c.create(:i=>[1,2, nil], :f=>[[1, 2.5], [3, 4.5]], :d=>[1, BigDecimal.new('1.000000000000000000001')], :t=>[%w'a b c', ['NULL', nil, '1']])
1422
+ o.i.should == [1, 2, nil]
1423
+ o.f.should == [[1, 2.5], [3, 4.5]]
1424
+ o.d.should == [BigDecimal.new('1'), BigDecimal.new('1.000000000000000000001')]
1425
+ o.t.should == [%w'a b c', ['NULL', nil, '1']]
1426
+ end
1427
+
1428
+ specify 'operations/functions with pg_array_ops' do
1429
+ Sequel.extension :pg_array_ops
1430
+ @db.create_table!(:items){column :i, 'integer[]'; column :i2, 'integer[]'; column :i3, 'integer[]'; column :i4, 'integer[]'; column :i5, 'integer[]'}
1431
+ @ds.insert([1, 2, 3].pg_array, [2, 1].pg_array, [4, 4].pg_array, [[5, 5], [4, 3]].pg_array, [1, nil, 5].pg_array)
1432
+
1433
+ @ds.get(:i.pg_array > :i3).should be_false
1434
+ @ds.get(:i3.pg_array > :i).should be_true
1435
+
1436
+ @ds.get(:i.pg_array >= :i3).should be_false
1437
+ @ds.get(:i.pg_array >= :i).should be_true
1438
+
1439
+ @ds.get(:i3.pg_array < :i).should be_false
1440
+ @ds.get(:i.pg_array < :i3).should be_true
1441
+
1442
+ @ds.get(:i3.pg_array <= :i).should be_false
1443
+ @ds.get(:i.pg_array <= :i).should be_true
1444
+
1445
+ @ds.get({5=>:i.pg_array.any}.sql_expr).should be_false
1446
+ @ds.get({1=>:i.pg_array.any}.sql_expr).should be_true
1447
+
1448
+ @ds.get({1=>:i3.pg_array.all}.sql_expr).should be_false
1449
+ @ds.get({4=>:i3.pg_array.all}.sql_expr).should be_true
1450
+
1451
+ @ds.get(:i2.pg_array[1]).should == 2
1452
+ @ds.get(:i2.pg_array[2]).should == 1
1453
+
1454
+ @ds.get(:i4.pg_array[2][1]).should == 4
1455
+ @ds.get(:i4.pg_array[2][2]).should == 3
1456
+
1457
+ @ds.get(:i.pg_array.contains(:i2)).should be_true
1458
+ @ds.get(:i.pg_array.contains(:i3)).should be_false
1459
+
1460
+ @ds.get(:i2.pg_array.contained_by(:i)).should be_true
1461
+ @ds.get(:i.pg_array.contained_by(:i2)).should be_false
1462
+
1463
+ @ds.get(:i.pg_array.overlaps(:i2)).should be_true
1464
+ @ds.get(:i2.pg_array.overlaps(:i3)).should be_false
1465
+
1466
+ @ds.get(:i.pg_array.dims).should == '[1:3]'
1467
+ @ds.get(:i.pg_array.length).should == 3
1468
+ @ds.get(:i.pg_array.lower).should == 1
1469
+
1470
+ if @db.server_version >= 90000
1471
+ @ds.get(:i5.pg_array.join).should == '15'
1472
+ @ds.get(:i5.pg_array.join(':')).should == '1:5'
1473
+ @ds.get(:i5.pg_array.join(':', '*')).should == '1:*:5'
1474
+ end
1475
+ @ds.select(:i.pg_array.unnest).from_self.count.should == 3 if @db.server_version >= 80400
1476
+
1477
+ if @native
1478
+ @ds.get(:i.pg_array.push(4)).should == [1, 2, 3, 4]
1479
+ @ds.get(:i.pg_array.unshift(4)).should == [4, 1, 2, 3]
1480
+ @ds.get(:i.pg_array.concat(:i2)).should == [1, 2, 3, 2, 1]
1481
+ end
1482
+ end
1483
+ end
1484
+
1485
+ describe 'PostgreSQL hstore handling' do
1486
+ before(:all) do
1487
+ Sequel.extension :pg_hstore
1488
+ @db = POSTGRES_DB
1489
+ @db.extend Sequel::Postgres::HStore::DatabaseMethods
1490
+ @ds = @db[:items]
1491
+ @h = {'a'=>'b', 'c'=>nil, 'd'=>'NULL', 'e'=>'\\\\" \\\' ,=>'}
1492
+ @native = POSTGRES_DB.adapter_scheme == :postgres
1493
+ end
1494
+ after do
1495
+ @db.drop_table?(:items)
1496
+ end
1497
+
1498
+ specify 'insert and retrieve hstore values' do
1499
+ @db.create_table!(:items) do
1500
+ column :h, :hstore
1501
+ end
1502
+ @ds.insert(@h.hstore)
1503
+ @ds.count.should == 1
1504
+ if @native
1505
+ rs = @ds.all
1506
+ v = rs.first[:h]
1507
+ v.should_not be_a_kind_of(Hash)
1508
+ v.to_hash.should be_a_kind_of(Hash)
1509
+ v.to_hash.should == @h
1510
+ @ds.delete
1511
+ @ds.insert(rs.first)
1512
+ @ds.all.should == rs
1513
+ end
1514
+ end
1515
+
1516
+ specify 'use hstore in bound variables' do
1517
+ @db.create_table!(:items) do
1518
+ column :i, :hstore
1519
+ end
1520
+ @ds.call(:insert, {:i=>@h.hstore}, {:i=>:$i})
1521
+ @ds.get(:i).should == @h
1522
+ @ds.filter(:i=>:$i).call(:first, :i=>@h.hstore).should == {:i=>@h}
1523
+ @ds.filter(:i=>:$i).call(:first, :i=>{}.hstore).should == nil
1524
+
1525
+ @ds.delete
1526
+ @ds.call(:insert, {:i=>@h}, {:i=>:$i})
1527
+ @ds.get(:i).should == @h
1528
+ @ds.filter(:i=>:$i).call(:first, :i=>@h).should == {:i=>@h}
1529
+ @ds.filter(:i=>:$i).call(:first, :i=>{}).should == nil
1530
+ end if POSTGRES_DB.adapter_scheme == :postgres && SEQUEL_POSTGRES_USES_PG
1531
+
1532
+ specify 'with models' do
1533
+ @db.create_table!(:items) do
1534
+ primary_key :id
1535
+ column :h, :hstore
1536
+ end
1537
+ c = Class.new(Sequel::Model(@db[:items]))
1538
+ c.plugin :typecast_on_load, :h unless @native
1539
+ c.create(:h=>@h.hstore).h.should == @h
1540
+ end
1541
+
1542
+ specify 'operations/functions with pg_hstore_ops' do
1543
+ Sequel.extension :pg_hstore_ops
1544
+ Sequel.extension :pg_array
1545
+ Sequel.extension :pg_array_ops
1546
+ @db.create_table!(:items){hstore :h1; hstore :h2; hstore :h3; String :t}
1547
+ @ds.insert({'a'=>'b', 'c'=>nil}.hstore, {'a'=>'b'}.hstore, {'d'=>'e'}.hstore)
1548
+ h1 = :h1.hstore
1549
+ h2 = :h2.hstore
1550
+ h3 = :h3.hstore
1551
+
1552
+ @ds.get(h1['a']).should == 'b'
1553
+ @ds.get(h1['d']).should == nil
1554
+
1555
+ @ds.get(h2.concat(h3).keys.pg_array.length).should == 2
1556
+ @ds.get(h1.concat(h3).keys.pg_array.length).should == 3
1557
+ @ds.get(h2.merge(h3).keys.pg_array.length).should == 2
1558
+ @ds.get(h1.merge(h3).keys.pg_array.length).should == 3
1559
+
1560
+ unless @db.adapter_scheme == :do
1561
+ # Broken DataObjects thinks operators with ? represent placeholders
1562
+ @ds.get(h1.contain_all(%w'a c'.pg_array)).should == true
1563
+ @ds.get(h1.contain_all(%w'a d'.pg_array)).should == false
1564
+
1565
+ @ds.get(h1.contain_any(%w'a d'.pg_array)).should == true
1566
+ @ds.get(h1.contain_any(%w'e d'.pg_array)).should == false
1567
+ end
1568
+
1569
+ @ds.get(h1.contains(h2)).should == true
1570
+ @ds.get(h1.contains(h3)).should == false
1571
+
1572
+ @ds.get(h2.contained_by(h1)).should == true
1573
+ @ds.get(h2.contained_by(h3)).should == false
1574
+
1575
+ @ds.get(h1.defined('a')).should == true
1576
+ @ds.get(h1.defined('c')).should == false
1577
+ @ds.get(h1.defined('d')).should == false
1578
+
1579
+ @ds.get(h1.delete('a')['c']).should == nil
1580
+ @ds.get(h1.delete(%w'a d'.pg_array)['c']).should == nil
1581
+ @ds.get(h1.delete(h2)['c']).should == nil
1582
+
1583
+ @ds.from({'a'=>'b', 'c'=>nil}.hstore.op.each).order(:key).all.should == [{:key=>'a', :value=>'b'}, {:key=>'c', :value=>nil}]
1584
+
1585
+ unless @db.adapter_scheme == :do
1586
+ @ds.get(h1.has_key?('c')).should == true
1587
+ @ds.get(h1.include?('c')).should == true
1588
+ @ds.get(h1.key?('c')).should == true
1589
+ @ds.get(h1.member?('c')).should == true
1590
+ @ds.get(h1.exist?('c')).should == true
1591
+ @ds.get(h1.has_key?('d')).should == false
1592
+ @ds.get(h1.include?('d')).should == false
1593
+ @ds.get(h1.key?('d')).should == false
1594
+ @ds.get(h1.member?('d')).should == false
1595
+ @ds.get(h1.exist?('d')).should == false
1596
+ end
1597
+
1598
+ @ds.get(h1.hstore.hstore.hstore.keys.pg_array.length).should == 2
1599
+ @ds.get(h1.keys.pg_array.length).should == 2
1600
+ @ds.get(h2.keys.pg_array.length).should == 1
1601
+ @ds.get(h1.akeys.pg_array.length).should == 2
1602
+ @ds.get(h2.akeys.pg_array.length).should == 1
1603
+
1604
+ @ds.from({'t'=>'s'}.hstore.op.populate(Sequel::SQL::Cast.new(nil, :items))).select_map(:t).should == ['s']
1605
+ @ds.from(:items___i).select({'t'=>'s'}.hstore.op.record_set(:i).as(:r)).from_self(:alias=>:s).select('(r).*'.lit).from_self.select_map(:t).should == ['s']
1606
+
1607
+ @ds.from({'t'=>'s', 'a'=>'b'}.hstore.op.skeys.as(:s)).select_order_map(:s).should == %w'a t'
1608
+
1609
+ @ds.get(h1.slice(%w'a c'.pg_array).keys.pg_array.length).should == 2
1610
+ @ds.get(h1.slice(%w'd c'.pg_array).keys.pg_array.length).should == 1
1611
+ @ds.get(h1.slice(%w'd e'.pg_array).keys.pg_array.length).should == nil
1612
+
1613
+ @ds.from({'t'=>'s', 'a'=>'b'}.hstore.op.svals.as(:s)).select_order_map(:s).should == %w'b s'
1614
+
1615
+ @ds.get(h1.to_array.pg_array.length).should == 4
1616
+ @ds.get(h2.to_array.pg_array.length).should == 2
1617
+
1618
+ @ds.get(h1.to_matrix.pg_array.length).should == 2
1619
+ @ds.get(h2.to_matrix.pg_array.length).should == 1
1620
+
1621
+ @ds.get(h1.values.pg_array.length).should == 2
1622
+ @ds.get(h2.values.pg_array.length).should == 1
1623
+ @ds.get(h1.avals.pg_array.length).should == 2
1624
+ @ds.get(h2.avals.pg_array.length).should == 1
1625
+ end
1626
+ end if POSTGRES_DB.type_supported?(:hstore)