sequel 3.46.0 → 3.47.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (170) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +96 -0
  3. data/Rakefile +7 -1
  4. data/bin/sequel +6 -4
  5. data/doc/active_record.rdoc +1 -1
  6. data/doc/advanced_associations.rdoc +14 -35
  7. data/doc/association_basics.rdoc +66 -4
  8. data/doc/migration.rdoc +4 -0
  9. data/doc/opening_databases.rdoc +6 -0
  10. data/doc/postgresql.rdoc +302 -0
  11. data/doc/release_notes/3.47.0.txt +270 -0
  12. data/doc/security.rdoc +6 -0
  13. data/lib/sequel/adapters/ibmdb.rb +9 -9
  14. data/lib/sequel/adapters/jdbc.rb +22 -7
  15. data/lib/sequel/adapters/jdbc/postgresql.rb +7 -2
  16. data/lib/sequel/adapters/mock.rb +2 -0
  17. data/lib/sequel/adapters/postgres.rb +44 -13
  18. data/lib/sequel/adapters/shared/mssql.rb +1 -1
  19. data/lib/sequel/adapters/shared/mysql.rb +2 -2
  20. data/lib/sequel/adapters/shared/postgres.rb +94 -55
  21. data/lib/sequel/adapters/shared/sqlite.rb +3 -1
  22. data/lib/sequel/adapters/sqlite.rb +2 -2
  23. data/lib/sequel/adapters/utils/pg_types.rb +1 -14
  24. data/lib/sequel/adapters/utils/split_alter_table.rb +3 -3
  25. data/lib/sequel/connection_pool/threaded.rb +1 -1
  26. data/lib/sequel/core.rb +1 -1
  27. data/lib/sequel/database/connecting.rb +2 -2
  28. data/lib/sequel/database/features.rb +5 -0
  29. data/lib/sequel/database/misc.rb +47 -5
  30. data/lib/sequel/database/query.rb +2 -2
  31. data/lib/sequel/dataset/actions.rb +4 -2
  32. data/lib/sequel/dataset/misc.rb +1 -1
  33. data/lib/sequel/dataset/prepared_statements.rb +1 -1
  34. data/lib/sequel/dataset/query.rb +8 -6
  35. data/lib/sequel/dataset/sql.rb +8 -6
  36. data/lib/sequel/extensions/constraint_validations.rb +5 -2
  37. data/lib/sequel/extensions/migration.rb +10 -8
  38. data/lib/sequel/extensions/pagination.rb +3 -0
  39. data/lib/sequel/extensions/pg_array.rb +85 -25
  40. data/lib/sequel/extensions/pg_hstore.rb +8 -1
  41. data/lib/sequel/extensions/pg_hstore_ops.rb +4 -1
  42. data/lib/sequel/extensions/pg_inet.rb +16 -13
  43. data/lib/sequel/extensions/pg_interval.rb +6 -2
  44. data/lib/sequel/extensions/pg_json.rb +18 -11
  45. data/lib/sequel/extensions/pg_range.rb +17 -2
  46. data/lib/sequel/extensions/pg_range_ops.rb +7 -5
  47. data/lib/sequel/extensions/pg_row.rb +29 -12
  48. data/lib/sequel/extensions/pretty_table.rb +3 -0
  49. data/lib/sequel/extensions/query.rb +3 -0
  50. data/lib/sequel/extensions/schema_caching.rb +2 -0
  51. data/lib/sequel/extensions/schema_dumper.rb +3 -1
  52. data/lib/sequel/extensions/select_remove.rb +3 -0
  53. data/lib/sequel/model.rb +8 -2
  54. data/lib/sequel/model/associations.rb +39 -27
  55. data/lib/sequel/model/base.rb +99 -38
  56. data/lib/sequel/model/plugins.rb +25 -0
  57. data/lib/sequel/plugins/association_autoreloading.rb +27 -22
  58. data/lib/sequel/plugins/association_dependencies.rb +1 -7
  59. data/lib/sequel/plugins/auto_validations.rb +110 -0
  60. data/lib/sequel/plugins/boolean_readers.rb +1 -6
  61. data/lib/sequel/plugins/caching.rb +6 -13
  62. data/lib/sequel/plugins/class_table_inheritance.rb +1 -0
  63. data/lib/sequel/plugins/composition.rb +14 -7
  64. data/lib/sequel/plugins/constraint_validations.rb +2 -13
  65. data/lib/sequel/plugins/defaults_setter.rb +1 -6
  66. data/lib/sequel/plugins/dirty.rb +8 -0
  67. data/lib/sequel/plugins/error_splitter.rb +54 -0
  68. data/lib/sequel/plugins/force_encoding.rb +1 -5
  69. data/lib/sequel/plugins/hook_class_methods.rb +1 -6
  70. data/lib/sequel/plugins/input_transformer.rb +79 -0
  71. data/lib/sequel/plugins/instance_filters.rb +7 -1
  72. data/lib/sequel/plugins/instance_hooks.rb +7 -1
  73. data/lib/sequel/plugins/json_serializer.rb +5 -10
  74. data/lib/sequel/plugins/lazy_attributes.rb +20 -7
  75. data/lib/sequel/plugins/list.rb +1 -6
  76. data/lib/sequel/plugins/many_through_many.rb +1 -2
  77. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +23 -39
  78. data/lib/sequel/plugins/optimistic_locking.rb +1 -5
  79. data/lib/sequel/plugins/pg_row.rb +4 -2
  80. data/lib/sequel/plugins/pg_typecast_on_load.rb +3 -7
  81. data/lib/sequel/plugins/prepared_statements.rb +1 -5
  82. data/lib/sequel/plugins/prepared_statements_safe.rb +2 -11
  83. data/lib/sequel/plugins/rcte_tree.rb +2 -2
  84. data/lib/sequel/plugins/serialization.rb +11 -13
  85. data/lib/sequel/plugins/serialization_modification_detection.rb +13 -1
  86. data/lib/sequel/plugins/single_table_inheritance.rb +4 -4
  87. data/lib/sequel/plugins/static_cache.rb +67 -19
  88. data/lib/sequel/plugins/string_stripper.rb +7 -27
  89. data/lib/sequel/plugins/subclasses.rb +3 -5
  90. data/lib/sequel/plugins/tactical_eager_loading.rb +2 -2
  91. data/lib/sequel/plugins/timestamps.rb +2 -7
  92. data/lib/sequel/plugins/touch.rb +5 -8
  93. data/lib/sequel/plugins/tree.rb +1 -6
  94. data/lib/sequel/plugins/typecast_on_load.rb +1 -5
  95. data/lib/sequel/plugins/update_primary_key.rb +26 -14
  96. data/lib/sequel/plugins/validation_class_methods.rb +31 -16
  97. data/lib/sequel/plugins/validation_helpers.rb +50 -26
  98. data/lib/sequel/plugins/xml_serializer.rb +3 -6
  99. data/lib/sequel/sql.rb +1 -1
  100. data/lib/sequel/version.rb +1 -1
  101. data/spec/adapters/postgres_spec.rb +131 -15
  102. data/spec/adapters/sqlite_spec.rb +1 -1
  103. data/spec/core/connection_pool_spec.rb +16 -17
  104. data/spec/core/database_spec.rb +111 -40
  105. data/spec/core/dataset_spec.rb +65 -74
  106. data/spec/core/expression_filters_spec.rb +6 -5
  107. data/spec/core/object_graph_spec.rb +0 -1
  108. data/spec/core/schema_spec.rb +23 -23
  109. data/spec/core/spec_helper.rb +5 -1
  110. data/spec/extensions/association_dependencies_spec.rb +1 -1
  111. data/spec/extensions/association_proxies_spec.rb +1 -1
  112. data/spec/extensions/auto_validations_spec.rb +90 -0
  113. data/spec/extensions/caching_spec.rb +6 -0
  114. data/spec/extensions/class_table_inheritance_spec.rb +8 -1
  115. data/spec/extensions/composition_spec.rb +12 -5
  116. data/spec/extensions/constraint_validations_spec.rb +4 -4
  117. data/spec/extensions/core_refinements_spec.rb +29 -79
  118. data/spec/extensions/dirty_spec.rb +14 -0
  119. data/spec/extensions/error_splitter_spec.rb +18 -0
  120. data/spec/extensions/identity_map_spec.rb +0 -1
  121. data/spec/extensions/input_transformer_spec.rb +54 -0
  122. data/spec/extensions/instance_filters_spec.rb +6 -0
  123. data/spec/extensions/instance_hooks_spec.rb +12 -1
  124. data/spec/extensions/json_serializer_spec.rb +0 -1
  125. data/spec/extensions/lazy_attributes_spec.rb +64 -55
  126. data/spec/extensions/looser_typecasting_spec.rb +1 -1
  127. data/spec/extensions/many_through_many_spec.rb +3 -4
  128. data/spec/extensions/many_to_one_pk_lookup_spec.rb +53 -15
  129. data/spec/extensions/migration_spec.rb +16 -0
  130. data/spec/extensions/null_dataset_spec.rb +1 -1
  131. data/spec/extensions/pg_array_spec.rb +48 -1
  132. data/spec/extensions/pg_hstore_ops_spec.rb +10 -2
  133. data/spec/extensions/pg_hstore_spec.rb +5 -0
  134. data/spec/extensions/pg_inet_spec.rb +5 -0
  135. data/spec/extensions/pg_interval_spec.rb +7 -3
  136. data/spec/extensions/pg_json_spec.rb +6 -1
  137. data/spec/extensions/pg_range_ops_spec.rb +4 -1
  138. data/spec/extensions/pg_range_spec.rb +5 -0
  139. data/spec/extensions/pg_row_plugin_spec.rb +13 -0
  140. data/spec/extensions/pg_row_spec.rb +28 -19
  141. data/spec/extensions/pg_typecast_on_load_spec.rb +6 -1
  142. data/spec/extensions/prepared_statements_associations_spec.rb +1 -1
  143. data/spec/extensions/query_literals_spec.rb +1 -1
  144. data/spec/extensions/rcte_tree_spec.rb +2 -2
  145. data/spec/extensions/schema_spec.rb +2 -2
  146. data/spec/extensions/serialization_modification_detection_spec.rb +8 -0
  147. data/spec/extensions/serialization_spec.rb +15 -1
  148. data/spec/extensions/sharding_spec.rb +1 -1
  149. data/spec/extensions/single_table_inheritance_spec.rb +1 -1
  150. data/spec/extensions/static_cache_spec.rb +59 -9
  151. data/spec/extensions/tactical_eager_loading_spec.rb +19 -4
  152. data/spec/extensions/update_primary_key_spec.rb +17 -1
  153. data/spec/extensions/validation_class_methods_spec.rb +25 -0
  154. data/spec/extensions/validation_helpers_spec.rb +59 -3
  155. data/spec/integration/associations_test.rb +5 -5
  156. data/spec/integration/eager_loader_test.rb +32 -63
  157. data/spec/integration/model_test.rb +2 -2
  158. data/spec/integration/plugin_test.rb +88 -56
  159. data/spec/integration/prepared_statement_test.rb +1 -1
  160. data/spec/integration/schema_test.rb +1 -1
  161. data/spec/integration/timezone_test.rb +0 -1
  162. data/spec/integration/transaction_test.rb +0 -1
  163. data/spec/model/association_reflection_spec.rb +1 -1
  164. data/spec/model/associations_spec.rb +106 -84
  165. data/spec/model/base_spec.rb +4 -4
  166. data/spec/model/eager_loading_spec.rb +8 -8
  167. data/spec/model/model_spec.rb +27 -9
  168. data/spec/model/plugins_spec.rb +71 -0
  169. data/spec/model/record_spec.rb +99 -13
  170. metadata +12 -2
@@ -31,6 +31,6 @@ describe "LooserTypecasting Extension" do
31
31
 
32
32
  specify "should not affect conversions of other types in decimal columns" do
33
33
  @c.new(:d=>1).d.should == 1
34
- @c.new(:d=>'a').d.should be_a_kind_of(BigDecimal)
34
+ @c.new(:d=>1).d.should be_a_kind_of(BigDecimal)
35
35
  end
36
36
  end
@@ -297,7 +297,7 @@ describe Sequel::Model, "many_through_many" do
297
297
  MODEL_DB.sqls.should == ['SELECT tags.* FROM tags INNER JOIN albums_tags ON (albums_tags.tag_id = tags.id) INNER JOIN albums ON (albums.id = albums_tags.album_id) INNER JOIN albums_artists ON ((albums_artists.album_id = albums.id) AND (albums_artists.artist_id = 1234))']
298
298
  end
299
299
 
300
- it "should set cached instance variable when accessed" do
300
+ it "should populate cache when accessed" do
301
301
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
302
302
  n = @c1.load(:id => 1234)
303
303
  n.associations[:tags].should == nil
@@ -308,7 +308,7 @@ describe Sequel::Model, "many_through_many" do
308
308
  MODEL_DB.sqls.length.should == 0
309
309
  end
310
310
 
311
- it "should use cached instance variable if available" do
311
+ it "should use cache if available" do
312
312
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
313
313
  n = @c1.load(:id => 1234)
314
314
  n.associations[:tags] = []
@@ -316,7 +316,7 @@ describe Sequel::Model, "many_through_many" do
316
316
  MODEL_DB.sqls.should == []
317
317
  end
318
318
 
319
- it "should not use cached instance variable if asked to reload" do
319
+ it "should not use cache if asked to reload" do
320
320
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]]
321
321
  n = @c1.load(:id => 1234)
322
322
  n.associations[:tags] = []
@@ -354,7 +354,6 @@ describe Sequel::Model, "many_through_many" do
354
354
  end
355
355
 
356
356
  it "should support a :uniq option that removes duplicates from the association" do
357
- h = []
358
357
  @c1.many_through_many :tags, [[:albums_artists, :artist_id, :album_id], [:albums, :id, :id], [:albums_tags, :album_id, :tag_id]], :uniq=>true
359
358
  @c2.dataset._fetch = [{:id=>20}, {:id=>30}, {:id=>20}, {:id=>30}]
360
359
  @c1.load(:id=>10).tags.should == [@c2.load(:id=>20), @c2.load(:id=>30)]
@@ -2,7 +2,9 @@ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
2
 
3
3
  describe "Sequel::Plugins::ManyToOnePkLookup" do
4
4
  before do
5
- class ::LookupModel < ::Sequel::Model
5
+ @db = Sequel.mock
6
+
7
+ class ::LookupModel < ::Sequel::Model(@db)
6
8
  plugin :many_to_one_pk_lookup
7
9
  columns :id, :caching_model_id, :caching_model_id2
8
10
  many_to_one :caching_model
@@ -10,12 +12,10 @@ describe "Sequel::Plugins::ManyToOnePkLookup" do
10
12
  end
11
13
  @c = LookupModel
12
14
 
13
- class ::CachingModel < Sequel::Model
15
+ class ::CachingModel < Sequel::Model(@db)
14
16
  columns :id, :id2
15
17
  end
16
18
  @cc = CachingModel
17
-
18
- @db = MODEL_DB
19
19
  end
20
20
  after do
21
21
  Object.send(:remove_const, :CachingModel)
@@ -23,25 +23,25 @@ describe "Sequel::Plugins::ManyToOnePkLookup" do
23
23
  end
24
24
 
25
25
  shared_examples_for "many_to_one_pk_lookup with composite keys" do
26
- it "should use a simple primary key lookup when retrieving many_to_one associated records" do
26
+ it "should use a simple primary key lookup when retrieving many_to_one associated records with a composite key" do
27
27
  @db.sqls.should == []
28
28
  @c.load(:id=>3, :caching_model_id=>1, :caching_model_id2=>2).caching_model2.should equal(@cm12)
29
29
  @c.load(:id=>3, :caching_model_id=>2, :caching_model_id2=>1).caching_model2.should equal(@cm21)
30
30
  @db.sqls.should == []
31
- @c.load(:id=>4, :caching_model_id=>2, :caching_model_id2=>2).caching_model2
32
- @db.sqls.should_not == []
31
+ @cc.dataset._fetch = []
32
+ @c.load(:id=>4, :caching_model_id=>2, :caching_model_id2=>2).caching_model2.should == nil
33
33
  end
34
34
  end
35
35
 
36
36
  shared_examples_for "many_to_one_pk_lookup" do
37
- it "should use a simple primary key lookup when retrieving many_to_one associated records via a composite key" do
37
+ it "should use a simple primary key lookup when retrieving many_to_one associated records" do
38
38
  @cc.set_primary_key([:id, :id2])
39
39
  @db.sqls.should == []
40
40
  @c.load(:id=>3, :caching_model_id=>1).caching_model.should equal(@cm1)
41
41
  @c.load(:id=>4, :caching_model_id=>2).caching_model.should equal(@cm2)
42
42
  @db.sqls.should == []
43
- @c.load(:id=>4, :caching_model_id=>3).caching_model
44
- @db.sqls.should_not == []
43
+ @cc.dataset._fetch = []
44
+ @c.load(:id=>4, :caching_model_id=>3).caching_model.should == nil
45
45
  end
46
46
 
47
47
  it "should not use a simple primary key lookup if the assocation has a nil :key option" do
@@ -62,10 +62,41 @@ describe "Sequel::Plugins::ManyToOnePkLookup" do
62
62
  @db.sqls.should_not == []
63
63
  end
64
64
 
65
+ it "should not use a simple primary key lookup if the assocation has :conditions" do
66
+ @c.many_to_one :caching_model, :conditions=>{:a=>1}
67
+ @c.load(:id=>3, :caching_model_id=>1).caching_model
68
+ @db.sqls.should_not == []
69
+ end
70
+
71
+ it "should not use a simple primary key lookup if the assocation has :select" do
72
+ @c.many_to_one :caching_model, :select=>[:a, :b]
73
+ @c.load(:id=>3, :caching_model_id=>1).caching_model
74
+ @db.sqls.should_not == []
75
+ end
76
+
77
+ it "should not use a simple primary key lookup if the assocation has a block" do
78
+ @c.many_to_one(:caching_model){|ds| ds.where{a > 1}}
79
+ @c.load(:id=>3, :caching_model_id=>1).caching_model
80
+ @db.sqls.should_not == []
81
+ end
82
+
83
+ it "should not use a simple primary key lookup if the assocation has a non-default :dataset option" do
84
+ cc = @cc
85
+ @c.many_to_one :caching_model, :dataset=>proc{cc.where(:id=>caching_model_id)}
86
+ @c.load(:id=>3, :caching_model_id=>1).caching_model
87
+ @db.sqls.should_not == []
88
+ end
89
+
90
+ it "should use a simple primary key lookup if explicitly set" do
91
+ @c.many_to_one :caching_model, :select=>[:a, :b], :many_to_one_pk_lookup=>true
92
+ @c.load(:id=>3, :caching_model_id=>1).caching_model
93
+ @db.sqls.should == []
94
+ end
95
+
65
96
  it "should not use a simple primary key lookup if the prepared_statements_associations method is being used" do
66
- c2 = Class.new(Sequel::Model(:not_caching_model))
97
+ c2 = Class.new(Sequel::Model(@db[:not_caching_model]))
67
98
  c2.dataset._fetch = {:id=>1}
68
- c = Class.new(Sequel::Model(:lookup_model))
99
+ c = Class.new(Sequel::Model(@db[:lookup_model]))
69
100
  c.class_eval do
70
101
  plugin :prepared_statements_associations
71
102
  plugin :many_to_one_pk_lookup
@@ -106,7 +137,7 @@ describe "Sequel::Plugins::ManyToOnePkLookup" do
106
137
  @cm12 = @cc[1, 2]
107
138
  @cm21 = @cc[2, 1]
108
139
 
109
- @db.reset
140
+ @db.sqls
110
141
  end
111
142
 
112
143
  it_should_behave_like "many_to_one_pk_lookup"
@@ -119,10 +150,17 @@ describe "Sequel::Plugins::ManyToOnePkLookup" do
119
150
  @cc.plugin :static_cache
120
151
  @cm1 = @cc[1]
121
152
  @cm2 = @cc[2]
122
- @db.reset
153
+ @db.sqls
123
154
  end
124
155
 
125
156
  it_should_behave_like "many_to_one_pk_lookup"
157
+
158
+ it "should not issue regular query if primary key lookup returns no rows" do
159
+ def @cc.primary_key_lookup(pk); end
160
+ @c.many_to_one :caching_model
161
+ @c.load(:id=>3, :caching_model_id=>1).caching_model
162
+ @db.sqls.should == []
163
+ end
126
164
  end
127
165
 
128
166
  describe "With static_cache plugin with composite key" do
@@ -132,7 +170,7 @@ describe "Sequel::Plugins::ManyToOnePkLookup" do
132
170
  @cc.plugin :static_cache
133
171
  @cm12 = @cc[[1, 2]]
134
172
  @cm21 = @cc[[2, 1]]
135
- @db.reset
173
+ @db.sqls
136
174
  end
137
175
 
138
176
  it_should_behave_like "many_to_one_pk_lookup with composite keys"
@@ -259,6 +259,10 @@ describe "Sequel::IntegerMigrator" do
259
259
  specify "should raise and error if there is a missing integer migration version" do
260
260
  proc{Sequel::Migrator.apply(@db, "spec/files/missing_integer_migrations")}.should raise_error(Sequel::Migrator::Error)
261
261
  end
262
+
263
+ specify "should not raise and error if there is a missing integer migration version and allow_missing_migration_files is true" do
264
+ Sequel::Migrator.run(@db, "spec/files/missing_integer_migrations", :allow_missing_migration_files => true).should_not raise_error(Sequel::Migrator::Error)
265
+ end
262
266
 
263
267
  specify "should raise and error if there is a duplicate integer migration version" do
264
268
  proc{Sequel::Migrator.apply(@db, "spec/files/duplicate_integer_migrations")}.should raise_error(Sequel::Migrator::Error)
@@ -609,6 +613,18 @@ describe "Sequel::TimestampMigrator" do
609
613
  @db[:schema_migrations].select_order_map(:filename).should == %w'1273253849_create_sessions.rb 1273253851_create_nodes.rb 1273253853_3_create_users.rb'
610
614
  end
611
615
 
616
+ specify "should not raise error for applied migrations not in file system if :allow_missing_migration_files is true" do
617
+ @dir = 'spec/files/timestamped_migrations'
618
+ @m.apply(@db, @dir)
619
+ [:schema_migrations, :sm1111, :sm2222, :sm3333].each{|n| @db.table_exists?(n).should be_true}
620
+ @db[:schema_migrations].select_order_map(:filename).should == %w'1273253849_create_sessions.rb 1273253851_create_nodes.rb 1273253853_3_create_users.rb'
621
+
622
+ @dir = 'spec/files/missing_timestamped_migrations'
623
+ proc{@m.run(@db, @dir, :allow_missing_migration_files => true)}.should_not raise_error(Sequel::Migrator::Error)
624
+ [:schema_migrations, :sm1111, :sm2222, :sm3333].each{|n| @db.table_exists?(n).should be_true}
625
+ @db[:schema_migrations].select_order_map(:filename).should == %w'1273253849_create_sessions.rb 1273253851_create_nodes.rb 1273253853_3_create_users.rb'
626
+ end
627
+
612
628
  specify "should raise error missing column name in existing schema_migrations table" do
613
629
  @dir = 'spec/files/timestamped_migrations'
614
630
  @m.apply(@db, @dir)
@@ -71,7 +71,7 @@ describe "null_dataset extension" do
71
71
  @i.should == 1
72
72
  end
73
73
 
74
- it "should have null! method modify receiver" do
74
+ it "should have nullify! method modify receiver" do
75
75
  ds = @db[:table]
76
76
  ds.nullify!.should equal(ds)
77
77
  ds.each(&@pr)
@@ -14,6 +14,7 @@ describe "pg_array extension" do
14
14
  @db.extension(:pg_array)
15
15
  @m = Sequel::Postgres
16
16
  @converter = @m::PG_TYPES
17
+ @db.sqls
17
18
  end
18
19
 
19
20
  it "should parse single dimensional text arrays" do
@@ -300,7 +301,7 @@ describe "pg_array extension" do
300
301
  Sequel::Postgres::PG_TYPES[2].should be_a_kind_of(Sequel::Postgres::PGArray::JSONCreator)
301
302
  end
302
303
 
303
- it "should support registering converters with :parser=>:json option" do
304
+ it "should support registering converters with :parser=>:json option and blocks" do
304
305
  Sequel::Postgres::PGArray.register('foo', :oid=>4, :parser=>:json){|s| s * 2}
305
306
  Sequel::Postgres::PG_TYPES[4].call('{{1, 2}, {3, 4}}').should == [[2, 4], [6, 8]]
306
307
  end
@@ -316,9 +317,55 @@ describe "pg_array extension" do
316
317
  @db.schema(:items).map{|e| e[1][:type]}.should == [:float_array]
317
318
  end
318
319
 
320
+ it "should support registering custom array types on a per-Database basis" do
321
+ @db.register_array_type('banana', :oid=>7865){|s| s}
322
+ @db.typecast_value(:banana_array, []).should be_a_kind_of(Sequel::Postgres::PGArray)
323
+ @db.fetch = [{:name=>'id', :db_type=>'banana[]'}]
324
+ @db.schema(:items).map{|e| e[1][:type]}.should == [:banana_array]
325
+ @db.conversion_procs.should have_key(7865)
326
+ @db.respond_to?(:typecast_value_banana_array, true).should be_true
327
+
328
+ db = Sequel.connect('mock://postgres', :quote_identifiers=>false)
329
+ db.extend_datasets(Module.new{def supports_timestamp_timezones?; false; end; def supports_timestamp_usecs?; false; end})
330
+ db.extension(:pg_array)
331
+ db.fetch = [{:name=>'id', :db_type=>'banana[]'}]
332
+ db.schema(:items).map{|e| e[1][:type]}.should == [nil]
333
+ db.conversion_procs.should_not have_key(7865)
334
+ db.respond_to?(:typecast_value_banana_array, true).should be_false
335
+ end
336
+
337
+ it "should automatically look up the array and scalar oids when registering per-Database types" do
338
+ @db.fetch = [[{:oid=>21, :typarray=>7866}], [{:name=>'id', :db_type=>'banana[]'}]]
339
+ @db.register_array_type('banana', :scalar_typecast=>:integer)
340
+ @db.sqls.should == ["SELECT typarray, oid FROM pg_type WHERE (typname = 'banana') LIMIT 1"]
341
+ @db.schema(:items).map{|e| e[1][:type]}.should == [:banana_array]
342
+ @db.conversion_procs[7866].call("{1,2}").should == [1,2]
343
+ @db.typecast_value(:banana_array, %w'1 2').should == [1,2]
344
+ end
345
+
346
+ it "should not automatically look up oids if given both scalar and array oids" do
347
+ @db.register_array_type('banana', :oid=>7866, :scalar_oid=>21, :scalar_typecast=>:integer)
348
+ @db.sqls.should == []
349
+ @db.conversion_procs[7866].call("{1,2}").should == [1,2]
350
+ @db.typecast_value(:banana_array, %w'1 2').should == [1,2]
351
+ end
352
+
353
+ it "should not automatically look up oids if given array oid and block" do
354
+ @db.register_array_type('banana', :oid=>7866, :scalar_typecast=>:integer){|s| s.to_i}
355
+ @db.sqls.should == []
356
+ @db.conversion_procs[7866].call("{1,2}").should == [1,2]
357
+ @db.typecast_value(:banana_array, %w'1 2').should == [1,2]
358
+ end
359
+
319
360
  it "should set appropriate timestamp conversion procs when getting conversion procs" do
320
361
  procs = @db.conversion_procs
321
362
  procs[1185].call('{"2011-10-20 11:12:13"}').should == [Time.local(2011, 10, 20, 11, 12, 13)]
322
363
  procs[1115].call('{"2011-10-20 11:12:13"}').should == [Time.local(2011, 10, 20, 11, 12, 13)]
323
364
  end
365
+
366
+ it "should return correct results for Database#schema_type_class" do
367
+ @db.register_array_type('banana', :oid=>7866, :scalar_typecast=>:integer){|s| s.to_i}
368
+ @db.schema_type_class(:banana_array).should == Sequel::Postgres::PGArray
369
+ @db.schema_type_class(:integer).should == Integer
370
+ end
324
371
  end
@@ -7,11 +7,19 @@ describe "Sequel::Postgres::HStoreOp" do
7
7
  end
8
8
 
9
9
  it "#- should use the - operator" do
10
- @ds.literal(@h - 'a').should == "(h - 'a')"
10
+ @ds.literal(@h - :a).should == "(h - a)"
11
+ end
12
+
13
+ it "#- should cast String argument to text when using - operator" do
14
+ @ds.literal(@h - 'a').should == "(h - CAST('a' AS text))"
15
+ end
16
+
17
+ it "#- should not cast LiteralString argument to text when using - operator" do
18
+ @ds.literal(@h - Sequel.lit('a')).should == "(h - a)"
11
19
  end
12
20
 
13
21
  it "#- should return an HStoreOp" do
14
- @ds.literal((@h - 'a')['a']).should == "((h - 'a') -> 'a')"
22
+ @ds.literal((@h - :a)['a']).should == "((h - a) -> 'a')"
15
23
  end
16
24
 
17
25
  it "#[] should use the -> operator" do
@@ -197,4 +197,9 @@ describe "pg_hstore extension" do
197
197
  dump = Marshal.dump(v)
198
198
  Marshal.load(dump).should == v
199
199
  end
200
+
201
+ it "should return correct results for Database#schema_type_class" do
202
+ @db.schema_type_class(:hstore).should == Sequel::Postgres::HStore
203
+ @db.schema_type_class(:integer).should == Integer
204
+ end
200
205
  end
@@ -44,4 +44,9 @@ describe "pg_inet extension" do
44
44
  proc{@db.typecast_value(:ipaddr, '')}.should raise_error(Sequel::InvalidValue)
45
45
  proc{@db.typecast_value(:ipaddr, 1)}.should raise_error(Sequel::InvalidValue)
46
46
  end
47
+
48
+ it "should return correct results for Database#schema_type_class" do
49
+ @db.schema_type_class(:ipaddr).should == IPAddr
50
+ @db.schema_type_class(:integer).should == Integer
51
+ end
47
52
  end
@@ -2,8 +2,8 @@ require File.join(File.dirname(File.expand_path(__FILE__)), "spec_helper")
2
2
 
3
3
  begin
4
4
  require 'active_support/duration'
5
- rescue LoadError => e
6
- skip_warn "pg_interval plugin: can't load active_support/duration (#{e.class}: #{e})"
5
+ rescue LoadError => exc
6
+ skip_warn "pg_interval plugin: can't load active_support/duration (#{exc.class}: #{exc})"
7
7
  else
8
8
  describe "pg_interval extension" do
9
9
  before do
@@ -46,7 +46,6 @@ describe "pg_interval extension" do
46
46
  end
47
47
 
48
48
  it "should support typecasting for the interval type" do
49
- ip = IPAddr.new('127.0.0.1')
50
49
  d = ActiveSupport::Duration.new(31557600 + 2*86400*30 + 3*86400*7 + 4*86400 + 5*3600 + 6*60 + 7, [[:years, 1], [:months, 2], [:days, 25], [:seconds, 18367]])
51
50
  @db.typecast_value(:interval, d).object_id.should == d.object_id
52
51
 
@@ -68,5 +67,10 @@ describe "pg_interval extension" do
68
67
  proc{@db.typecast_value(:interval, 'foo')}.should raise_error(Sequel::InvalidValue)
69
68
  proc{@db.typecast_value(:interval, Object.new)}.should raise_error(Sequel::InvalidValue)
70
69
  end
70
+
71
+ it "should return correct results for Database#schema_type_class" do
72
+ @db.schema_type_class(:interval).should == ActiveSupport::Duration
73
+ @db.schema_type_class(:integer).should == Integer
74
+ end
71
75
  end
72
76
  end
@@ -55,7 +55,7 @@ describe "pg_json extension" do
55
55
  end
56
56
  end
57
57
 
58
- it "should literalize HStores to strings correctly" do
58
+ it "should literalize JSONHash and JSONArray to strings correctly" do
59
59
  @db.literal(Sequel.pg_json([])).should == "'[]'::json"
60
60
  @db.literal(Sequel.pg_json([1, [2], {'a'=>'b'}])).should == "'[1,[2],{\"a\":\"b\"}]'::json"
61
61
  @db.literal(Sequel.pg_json({})).should == "'{}'::json"
@@ -110,4 +110,9 @@ describe "pg_json extension" do
110
110
  proc{@db.typecast_value(:json, '')}.should raise_error(Sequel::InvalidValue)
111
111
  proc{@db.typecast_value(:json, 1)}.should raise_error(Sequel::InvalidValue)
112
112
  end
113
+
114
+ it "should return correct results for Database#schema_type_class" do
115
+ @db.schema_type_class(:json).should == [Sequel::Postgres::JSONHash, Sequel::Postgres::JSONArray]
116
+ @db.schema_type_class(:integer).should == Integer
117
+ end
113
118
  end
@@ -33,9 +33,12 @@ describe "Sequel::Postgres::RangeOp" do
33
33
  @ds.literal(@h.overlaps(@h)).should == "(h && h)"
34
34
  @ds.literal(@h.left_of(@h)).should == "(h << h)"
35
35
  @ds.literal(@h.right_of(@h)).should == "(h >> h)"
36
+ @ds.literal(@h.ends_before(@h)).should == "(h &< h)"
37
+ @ds.literal(@h.starts_after(@h)).should == "(h &> h)"
38
+ @ds.literal(@h.adjacent_to(@h)).should == "(h -|- h)"
39
+
36
40
  @ds.literal(@h.starts_before(@h)).should == "(h &< h)"
37
41
  @ds.literal(@h.ends_after(@h)).should == "(h &> h)"
38
- @ds.literal(@h.adjacent_to(@h)).should == "(h -|- h)"
39
42
  end
40
43
 
41
44
  it "should define methods for all of the the PostgreSQL range functions" do
@@ -163,6 +163,11 @@ describe "pg_range extension" do
163
163
  Sequel::Postgres::PG_TYPES[331].call('[1,3)').should be_a_kind_of(@R)
164
164
  end
165
165
 
166
+ it "should return correct results for Database#schema_type_class" do
167
+ @db.schema_type_class(:int4range).should == Sequel::Postgres::PGRange
168
+ @db.schema_type_class(:integer).should == Integer
169
+ end
170
+
166
171
  describe "parser" do
167
172
  before do
168
173
  @p = Sequel::Postgres::PG_TYPES[3904]
@@ -15,6 +15,14 @@ describe "Sequel::Plugins::PgRow" do
15
15
  @c2.columns :address
16
16
  @c2.db_schema[:address].merge!(:type=>:pg_row_address)
17
17
  end
18
+ after do
19
+ @c.dataset.opts[:from] = [:address]
20
+ end
21
+
22
+ it "should have schema_type_class include Sequel::Model" do
23
+ @c2.new.send(:schema_type_class, :address).should == @c
24
+ @db.conversion_procs[1098].call('(123 Foo St,Bar City)').should == @c.load(:street=>'123 Foo St', :city=>'Bar City')
25
+ end
18
26
 
19
27
  it "should set up a parser for the type that creates a model class" do
20
28
  @db.conversion_procs[1098].call('(123 Foo St,Bar City)').should == @c.load(:street=>'123 Foo St', :city=>'Bar City')
@@ -33,6 +41,11 @@ describe "Sequel::Plugins::PgRow" do
33
41
  @db.literal(@c.load(:street=>'123 Foo St', :city=>'Bar City')).should == "ROW('123 Foo St', 'Bar City')::address"
34
42
  end
35
43
 
44
+ it "should handle literalizing model instances when model table is aliased" do
45
+ @c.dataset.opts[:from] = [Sequel.as(:address, :a)]
46
+ @db.literal(@c.load(:street=>'123 Foo St', :city=>'Bar City')).should == "ROW('123 Foo St', 'Bar City')::address"
47
+ end
48
+
36
49
  it "should handle model instances in bound variables" do
37
50
  @db.bound_variable_arg(1, nil).should == 1
38
51
  @db.bound_variable_arg(@c.load(:street=>'123 Foo St', :city=>'Bar City'), nil).should == '("123 Foo St","Bar City")'
@@ -137,13 +137,13 @@ describe "pg_row extension" do
137
137
  it "should reload registered row types when reseting conversion procs" do
138
138
  db = Sequel.mock(:host=>'postgres')
139
139
  db.extension(:pg_row)
140
- db.conversion_procs[4] = p4 = proc{|s| s.to_i}
141
- db.conversion_procs[5] = p5 = proc{|s| s * 2}
140
+ db.conversion_procs[4] = proc{|s| s.to_i}
141
+ db.conversion_procs[5] = proc{|s| s * 2}
142
142
  db.sqls
143
143
  db.fetch = [[{:oid=>1, :typrelid=>2, :typarray=>3}], [{:attname=>'bar', :atttypid=>4}, {:attname=>'baz', :atttypid=>5}]]
144
144
  db.register_row_type(:foo)
145
145
  db.sqls.should == ["SELECT pg_type.oid, typrelid, typarray FROM pg_type WHERE ((typtype = 'c') AND (typname = 'foo')) LIMIT 1",
146
- "SELECT attname, atttypid FROM pg_attribute WHERE ((attrelid = 2) AND (attnum > 0) AND NOT attisdropped) ORDER BY attnum"]
146
+ "SELECT attname, (CASE pg_type.typbasetype WHEN 0 THEN atttypid ELSE pg_type.typbasetype END) AS atttypid FROM pg_attribute INNER JOIN pg_type ON (pg_type.oid = pg_attribute.atttypid) WHERE ((attrelid = 2) AND (attnum > 0) AND NOT attisdropped) ORDER BY attnum"]
147
147
 
148
148
  begin
149
149
  pgnt = Sequel::Postgres::PG_NAMED_TYPES.dup
@@ -151,7 +151,7 @@ describe "pg_row extension" do
151
151
  db.fetch = [[{:oid=>1, :typrelid=>2, :typarray=>3}], [{:attname=>'bar', :atttypid=>4}, {:attname=>'baz', :atttypid=>5}]]
152
152
  db.reset_conversion_procs
153
153
  db.sqls.should == ["SELECT pg_type.oid, typrelid, typarray FROM pg_type WHERE ((typtype = 'c') AND (typname = 'foo')) LIMIT 1",
154
- "SELECT attname, atttypid FROM pg_attribute WHERE ((attrelid = 2) AND (attnum > 0) AND NOT attisdropped) ORDER BY attnum"]
154
+ "SELECT attname, (CASE pg_type.typbasetype WHEN 0 THEN atttypid ELSE pg_type.typbasetype END) AS atttypid FROM pg_attribute INNER JOIN pg_type ON (pg_type.oid = pg_attribute.atttypid) WHERE ((attrelid = 2) AND (attnum > 0) AND NOT attisdropped) ORDER BY attnum"]
155
155
  ensure
156
156
  Sequel::Postgres::PG_NAMED_TYPES.replace pgnt
157
157
  end
@@ -182,7 +182,7 @@ describe "pg_row extension" do
182
182
  @db.fetch = [[{:oid=>1, :typrelid=>2, :typarray=>3}], [{:attname=>'bar', :atttypid=>4}, {:attname=>'baz', :atttypid=>5}]]
183
183
  @db.register_row_type(:foo)
184
184
  @db.sqls.should == ["SELECT pg_type.oid, typrelid, typarray FROM pg_type WHERE ((typtype = 'c') AND (typname = 'foo')) LIMIT 1",
185
- "SELECT attname, atttypid FROM pg_attribute WHERE ((attrelid = 2) AND (attnum > 0) AND NOT attisdropped) ORDER BY attnum"]
185
+ "SELECT attname, (CASE pg_type.typbasetype WHEN 0 THEN atttypid ELSE pg_type.typbasetype END) AS atttypid FROM pg_attribute INNER JOIN pg_type ON (pg_type.oid = pg_attribute.atttypid) WHERE ((attrelid = 2) AND (attnum > 0) AND NOT attisdropped) ORDER BY attnum"]
186
186
  p1 = @db.conversion_procs[1]
187
187
  p1.columns.should == [:bar, :baz]
188
188
  p1.column_oids.should == [4, 5]
@@ -210,7 +210,7 @@ describe "pg_row extension" do
210
210
  @db.fetch = [[{:oid=>1, :typrelid=>2, :typarray=>3}], [{:attname=>'bar', :atttypid=>4}, {:attname=>'baz', :atttypid=>5}]]
211
211
  @db.register_row_type(:foo__bar)
212
212
  @db.sqls.should == ["SELECT pg_type.oid, typrelid, typarray FROM pg_type INNER JOIN pg_namespace ON ((pg_namespace.oid = pg_type.typnamespace) AND (pg_namespace.nspname = 'foo')) WHERE ((typtype = 'c') AND (typname = 'bar')) LIMIT 1",
213
- "SELECT attname, atttypid FROM pg_attribute WHERE ((attrelid = 2) AND (attnum > 0) AND NOT attisdropped) ORDER BY attnum"]
213
+ "SELECT attname, (CASE pg_type.typbasetype WHEN 0 THEN atttypid ELSE pg_type.typbasetype END) AS atttypid FROM pg_attribute INNER JOIN pg_type ON (pg_type.oid = pg_attribute.atttypid) WHERE ((attrelid = 2) AND (attnum > 0) AND NOT attisdropped) ORDER BY attnum"]
214
214
  p1 = @db.conversion_procs[1]
215
215
  p1.columns.should == [:bar, :baz]
216
216
  p1.column_oids.should == [4, 5]
@@ -230,8 +230,8 @@ describe "pg_row extension" do
230
230
  end
231
231
 
232
232
  it "should allow registering with a custom converter" do
233
- @db.conversion_procs[4] = p4 = proc{|s| s.to_i}
234
- @db.conversion_procs[5] = p5 = proc{|s| s * 2}
233
+ @db.conversion_procs[4] = proc{|s| s.to_i}
234
+ @db.conversion_procs[5] = proc{|s| s * 2}
235
235
  @db.fetch = [[{:oid=>1, :typrelid=>2, :typarray=>3}], [{:attname=>'bar', :atttypid=>4}, {:attname=>'baz', :atttypid=>5}]]
236
236
  c = proc{|h| [h]}
237
237
  @db.register_row_type(:foo, :converter=>c)
@@ -241,8 +241,8 @@ describe "pg_row extension" do
241
241
  end
242
242
 
243
243
  it "should allow registering with a custom typecaster" do
244
- @db.conversion_procs[4] = p4 = proc{|s| s.to_i}
245
- @db.conversion_procs[5] = p5 = proc{|s| s * 2}
244
+ @db.conversion_procs[4] = proc{|s| s.to_i}
245
+ @db.conversion_procs[5] = proc{|s| s * 2}
246
246
  @db.fetch = [[{:oid=>1, :typrelid=>2, :typarray=>3}], [{:attname=>'bar', :atttypid=>4}, {:attname=>'baz', :atttypid=>5}]]
247
247
  @db.register_row_type(:foo, :typecaster=>proc{|h| {:bar=>(h[:bar]||0).to_i, :baz=>(h[:baz] || 'a')*2}})
248
248
  @db.typecast_value(:pg_row_foo, %w'1 b').should be_a_kind_of(Hash)
@@ -257,16 +257,16 @@ describe "pg_row extension" do
257
257
  end
258
258
 
259
259
  it "should handle conversion procs that aren't added until later" do
260
- @db.conversion_procs[5] = p5 = proc{|s| s * 2}
260
+ @db.conversion_procs[5] = proc{|s| s * 2}
261
261
  @db.fetch = [[{:oid=>1, :typrelid=>2, :typarray=>3}], [{:attname=>'bar', :atttypid=>4}, {:attname=>'baz', :atttypid=>5}]]
262
262
  c = proc{|h| [h]}
263
263
  @db.register_row_type(:foo, :converter=>c)
264
- @db.conversion_procs[4] = p4 = proc{|s| s.to_i}
264
+ @db.conversion_procs[4] = proc{|s| s.to_i}
265
265
  @db.conversion_procs[1].call('(1,b)').should == [{:bar=>1, :baz=>'bb'}]
266
266
  end
267
267
 
268
268
  it "should handle nil values when converting columns" do
269
- @db.conversion_procs[5] = p5 = proc{|s| s * 2}
269
+ @db.conversion_procs[5] = proc{|s| s * 2}
270
270
  @db.fetch = [[{:oid=>1, :typrelid=>2, :typarray=>3}], [{:attname=>'bar', :atttypid=>4}]]
271
271
  called = false
272
272
  @db.conversion_procs[4] = proc{|s| called = true; s}
@@ -276,8 +276,8 @@ describe "pg_row extension" do
276
276
  end
277
277
 
278
278
  it "should registering array type for row type if type has an array oid" do
279
- @db.conversion_procs[4] = p4 = proc{|s| s.to_i}
280
- @db.conversion_procs[5] = p5 = proc{|s| s * 2}
279
+ @db.conversion_procs[4] = proc{|s| s.to_i}
280
+ @db.conversion_procs[5] = proc{|s| s * 2}
281
281
  @db.fetch = [[{:oid=>1, :typrelid=>2, :typarray=>3}], [{:attname=>'bar', :atttypid=>4}, {:attname=>'baz', :atttypid=>5}]]
282
282
  @db.register_row_type(:foo, :typecaster=>proc{|h| {:bar=>(h[:bar]||0).to_i, :baz=>(h[:baz] || 'a')*2}})
283
283
  p3 = @db.conversion_procs[3]
@@ -292,8 +292,8 @@ describe "pg_row extension" do
292
292
  end
293
293
 
294
294
  it "should allow typecasting of registered row types via Database#row_type" do
295
- @db.conversion_procs[4] = p4 = proc{|s| s.to_i}
296
- @db.conversion_procs[5] = p5 = proc{|s| s * 2}
295
+ @db.conversion_procs[4] = proc{|s| s.to_i}
296
+ @db.conversion_procs[5] = proc{|s| s * 2}
297
297
  @db.fetch = [[{:oid=>1, :typrelid=>2, :typarray=>3}], [{:attname=>'bar', :atttypid=>4}, {:attname=>'baz', :atttypid=>5}]]
298
298
  @db.register_row_type(:foo, :typecaster=>proc{|h| @m::HashRow.subclass(:foo, [:bar, :baz]).new({:bar=>(h[:bar]||0).to_i, :baz=>(h[:baz] || 'a')*2})})
299
299
  @db.literal(@db.row_type(:foo, ['1', 'b'])).should == "ROW(1, 'bb')::foo"
@@ -301,8 +301,8 @@ describe "pg_row extension" do
301
301
  end
302
302
 
303
303
  it "should allow parsing when typecasting registered row types via Database#row_type" do
304
- @db.conversion_procs[4] = p4 = proc{|s| s.to_i}
305
- @db.conversion_procs[5] = p5 = proc{|s| s * 2}
304
+ @db.conversion_procs[4] = proc{|s| s.to_i}
305
+ @db.conversion_procs[5] = proc{|s| s * 2}
306
306
  @db.fetch = [[{:oid=>1, :typrelid=>2, :typarray=>3}], [{:attname=>'bar', :atttypid=>4}, {:attname=>'baz', :atttypid=>5}]]
307
307
  @db.register_row_type(:foo, :typecaster=>proc{|h| @m::HashRow.subclass(:foo, [:bar, :baz]).new(:bar=>(h[:bar]||0).to_i, :baz=>(h[:baz] || 'a')*2)})
308
308
  @db.literal(@db.row_type(:foo, ['1', 'b'])).should == "ROW(1, 'bb')::foo"
@@ -348,4 +348,13 @@ describe "pg_row extension" do
348
348
  @db.fetch = []
349
349
  proc{@db.register_row_type(:foo)}.should raise_error(Sequel::Error)
350
350
  end
351
+
352
+ it "should return correct results for Database#schema_type_class" do
353
+ @db.conversion_procs[4] = proc{|s| s.to_i}
354
+ @db.conversion_procs[5] = proc{|s| s * 2}
355
+ @db.fetch = [[{:oid=>1, :typrelid=>2, :typarray=>3}], [{:attname=>'bar', :atttypid=>4}, {:attname=>'baz', :atttypid=>5}]]
356
+ @db.register_row_type(:foo, :typecaster=>proc{|h| {:bar=>(h[:bar]||0).to_i, :baz=>(h[:baz] || 'a')*2}})
357
+ @db.schema_type_class(:pg_row_foo).should == [Sequel::Postgres::PGRow::HashRow, Sequel::Postgres::PGRow::ArrayRow]
358
+ @db.schema_type_class(:integer).should == Integer
359
+ end
351
360
  end