sequel 3.33.0 → 3.34.0

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