sequel 5.19.0 → 5.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +102 -0
  3. data/doc/dataset_filtering.rdoc +15 -0
  4. data/doc/opening_databases.rdoc +5 -1
  5. data/doc/release_notes/5.20.0.txt +89 -0
  6. data/doc/release_notes/5.21.0.txt +87 -0
  7. data/doc/release_notes/5.22.0.txt +48 -0
  8. data/doc/release_notes/5.23.0.txt +56 -0
  9. data/doc/release_notes/5.24.0.txt +56 -0
  10. data/doc/sharding.rdoc +2 -0
  11. data/doc/testing.rdoc +1 -0
  12. data/doc/transactions.rdoc +38 -0
  13. data/lib/sequel/adapters/ado.rb +27 -19
  14. data/lib/sequel/adapters/jdbc.rb +7 -1
  15. data/lib/sequel/adapters/jdbc/mysql.rb +2 -2
  16. data/lib/sequel/adapters/jdbc/postgresql.rb +1 -13
  17. data/lib/sequel/adapters/jdbc/sqlite.rb +29 -0
  18. data/lib/sequel/adapters/mysql2.rb +2 -3
  19. data/lib/sequel/adapters/shared/mssql.rb +7 -7
  20. data/lib/sequel/adapters/shared/postgres.rb +37 -19
  21. data/lib/sequel/adapters/shared/sqlite.rb +27 -3
  22. data/lib/sequel/adapters/sqlite.rb +1 -1
  23. data/lib/sequel/adapters/tinytds.rb +12 -0
  24. data/lib/sequel/adapters/utils/mysql_mysql2.rb +2 -0
  25. data/lib/sequel/database/logging.rb +7 -1
  26. data/lib/sequel/database/query.rb +1 -1
  27. data/lib/sequel/database/schema_generator.rb +12 -3
  28. data/lib/sequel/database/schema_methods.rb +2 -0
  29. data/lib/sequel/database/transactions.rb +57 -5
  30. data/lib/sequel/dataset.rb +4 -2
  31. data/lib/sequel/dataset/actions.rb +3 -2
  32. data/lib/sequel/dataset/placeholder_literalizer.rb +4 -1
  33. data/lib/sequel/dataset/query.rb +5 -1
  34. data/lib/sequel/dataset/sql.rb +11 -7
  35. data/lib/sequel/extensions/named_timezones.rb +52 -8
  36. data/lib/sequel/extensions/pg_array.rb +4 -0
  37. data/lib/sequel/extensions/pg_json.rb +387 -123
  38. data/lib/sequel/extensions/pg_range.rb +3 -2
  39. data/lib/sequel/extensions/pg_row.rb +3 -1
  40. data/lib/sequel/extensions/schema_dumper.rb +1 -1
  41. data/lib/sequel/extensions/server_block.rb +15 -4
  42. data/lib/sequel/model/associations.rb +35 -9
  43. data/lib/sequel/model/plugins.rb +104 -0
  44. data/lib/sequel/plugins/association_dependencies.rb +3 -3
  45. data/lib/sequel/plugins/association_pks.rb +14 -4
  46. data/lib/sequel/plugins/association_proxies.rb +3 -2
  47. data/lib/sequel/plugins/class_table_inheritance.rb +11 -0
  48. data/lib/sequel/plugins/composition.rb +13 -9
  49. data/lib/sequel/plugins/finder.rb +2 -2
  50. data/lib/sequel/plugins/hook_class_methods.rb +17 -5
  51. data/lib/sequel/plugins/insert_conflict.rb +72 -0
  52. data/lib/sequel/plugins/inverted_subsets.rb +2 -2
  53. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +147 -59
  54. data/lib/sequel/plugins/rcte_tree.rb +6 -0
  55. data/lib/sequel/plugins/static_cache.rb +8 -3
  56. data/lib/sequel/plugins/static_cache_cache.rb +53 -0
  57. data/lib/sequel/plugins/subset_conditions.rb +2 -2
  58. data/lib/sequel/plugins/validation_class_methods.rb +5 -3
  59. data/lib/sequel/sql.rb +15 -3
  60. data/lib/sequel/timezones.rb +50 -11
  61. data/lib/sequel/version.rb +1 -1
  62. data/spec/adapters/mssql_spec.rb +24 -0
  63. data/spec/adapters/mysql_spec.rb +0 -5
  64. data/spec/adapters/postgres_spec.rb +319 -1
  65. data/spec/bin_spec.rb +1 -1
  66. data/spec/core/database_spec.rb +123 -2
  67. data/spec/core/dataset_spec.rb +33 -1
  68. data/spec/core/expression_filters_spec.rb +25 -1
  69. data/spec/core/schema_spec.rb +24 -0
  70. data/spec/extensions/class_table_inheritance_spec.rb +30 -8
  71. data/spec/extensions/core_refinements_spec.rb +1 -1
  72. data/spec/extensions/hook_class_methods_spec.rb +22 -0
  73. data/spec/extensions/insert_conflict_spec.rb +103 -0
  74. data/spec/extensions/migration_spec.rb +13 -0
  75. data/spec/extensions/named_timezones_spec.rb +109 -2
  76. data/spec/extensions/pg_auto_constraint_validations_spec.rb +45 -0
  77. data/spec/extensions/pg_json_spec.rb +218 -29
  78. data/spec/extensions/pg_range_spec.rb +76 -9
  79. data/spec/extensions/rcte_tree_spec.rb +6 -0
  80. data/spec/extensions/s_spec.rb +1 -1
  81. data/spec/extensions/schema_dumper_spec.rb +4 -2
  82. data/spec/extensions/server_block_spec.rb +38 -0
  83. data/spec/extensions/spec_helper.rb +8 -1
  84. data/spec/extensions/static_cache_cache_spec.rb +35 -0
  85. data/spec/integration/dataset_test.rb +25 -9
  86. data/spec/integration/plugin_test.rb +42 -0
  87. data/spec/integration/schema_test.rb +7 -2
  88. data/spec/integration/transaction_test.rb +50 -0
  89. data/spec/model/associations_spec.rb +84 -4
  90. data/spec/model/plugins_spec.rb +111 -0
  91. metadata +16 -2
@@ -195,6 +195,19 @@ describe "Reversible Migrations with Sequel.migration{change{}}" do
195
195
  [:drop_table, :a, {:foo=>:bar}]]
196
196
  end
197
197
 
198
+ it "should reverse add_foreign_key with :type option" do
199
+ Sequel.migration{change{alter_table(:t){add_foreign_key :b, :c, :type=>:f}}}.apply(@db, :down)
200
+ actions = @db.actions
201
+ actions.must_equal [[:alter_table, [[:drop_foreign_key, :b, {:type=>:f}]]]]
202
+ @db.sqls
203
+ db = Sequel.mock
204
+ args = nil
205
+ db.define_singleton_method(:foreign_key_list){|*a| args = a; [{:name=>:fbc, :columns=>[:b]}]}
206
+ db.alter_table(:t){send(*actions[0][1][0])}
207
+ db.sqls.must_equal ["ALTER TABLE t DROP CONSTRAINT fbc", "ALTER TABLE t DROP COLUMN b"]
208
+ args.must_equal [:t]
209
+ end
210
+
198
211
  it "should reverse add_foreign_key with :foreign_key_constraint_name option" do
199
212
  Sequel.migration{change{alter_table(:t){add_foreign_key :b, :c, :foreign_key_constraint_name=>:f}}}.apply(@db, :down)
200
213
  actions = @db.actions
@@ -9,7 +9,7 @@ Sequel.extension :thread_local_timezones
9
9
  Sequel.extension :named_timezones
10
10
  Sequel.datetime_class = Time
11
11
 
12
- describe "Sequel named_timezones extension" do
12
+ describe "Sequel named_timezones extension with DateTime class" do
13
13
  before do
14
14
  @tz_in = TZInfo::Timezone.get('America/Los_Angeles')
15
15
  @tz_out = TZInfo::Timezone.get('America/New_York')
@@ -54,10 +54,12 @@ describe "Sequel named_timezones extension" do
54
54
 
55
55
  it "should convert datetimes coming out of the database from database_timezone to application_timezone" do
56
56
  dt = Sequel.database_to_application_timestamp('2009-06-01 06:20:30-0400')
57
+ dt.must_be_instance_of DateTime
57
58
  dt.must_equal @dt
58
59
  dt.offset.must_equal(-7/24.0)
59
60
 
60
61
  dt = Sequel.database_to_application_timestamp('2009-06-01 10:20:30+0000')
62
+ dt.must_be_instance_of DateTime
61
63
  dt.must_equal @dt
62
64
  dt.offset.must_equal(-7/24.0)
63
65
  end
@@ -68,7 +70,9 @@ describe "Sequel named_timezones extension" do
68
70
 
69
71
  it "should support tzinfo_disambiguator= to handle ambiguous timezones automatically" do
70
72
  Sequel.tzinfo_disambiguator = proc{|datetime, periods| periods.first}
71
- Sequel.database_to_application_timestamp('2004-10-31T01:30:00').must_equal DateTime.parse('2004-10-30T22:30:00-07:00')
73
+ dt = Sequel.database_to_application_timestamp('2004-10-31T01:30:00')
74
+ dt.must_equal DateTime.parse('2004-10-30T22:30:00-07:00')
75
+ dt.offset.must_equal(-7/24.0)
72
76
  end
73
77
 
74
78
  it "should assume datetimes coming out of the database that don't have an offset as coming from database_timezone" do
@@ -108,4 +112,107 @@ describe "Sequel named_timezones extension" do
108
112
  tz2.must_equal @tz_in
109
113
  end
110
114
  end
115
+
116
+ describe "Sequel named_timezones extension with Time class" do
117
+ before do
118
+ @tz_in = TZInfo::Timezone.get('America/Los_Angeles')
119
+ @tz_out = TZInfo::Timezone.get('America/New_York')
120
+ @db = Sequel.mock
121
+ Sequel.application_timezone = 'America/Los_Angeles'
122
+ Sequel.database_timezone = 'America/New_York'
123
+ end
124
+ after do
125
+ Sequel.tzinfo_disambiguator = nil
126
+ Sequel.default_timezone = nil
127
+ Sequel.datetime_class = Time
128
+ end
129
+
130
+ it "should convert string arguments to *_timezone= to TZInfo::Timezone instances" do
131
+ Sequel.application_timezone.must_equal @tz_in
132
+ Sequel.database_timezone.must_equal @tz_out
133
+ end
134
+
135
+ it "should convert string arguments for Database#timezone= to TZInfo::Timezone instances for database-specific timezones" do
136
+ @db.extension :named_timezones
137
+ @db.timezone = 'America/Los_Angeles'
138
+ @db.timezone.must_equal @tz_in
139
+ end
140
+
141
+ it "should accept TZInfo::Timezone instances in *_timezone=" do
142
+ Sequel.application_timezone = @tz_in
143
+ Sequel.database_timezone = @tz_out
144
+ Sequel.application_timezone.must_equal @tz_in
145
+ Sequel.database_timezone.must_equal @tz_out
146
+ end
147
+
148
+ it "should convert datetimes going into the database to named database_timezone" do
149
+ ds = @db[:a].with_extend do
150
+ def supports_timestamp_timezones?; true; end
151
+ def supports_timestamp_usecs?; false; end
152
+ end
153
+ ds.insert([Time.new(2009,6,1,3,20,30, RUBY_VERSION >= '2.6' ? @tz_in : -25200), Time.new(2009,6,1,3,20,30,-25200), Time.new(2009,6,1,6,20,30,-14400)])
154
+ @db.sqls.must_equal ["INSERT INTO a VALUES ('2009-06-01 06:20:30-0400', '2009-06-01 06:20:30-0400', '2009-06-01 06:20:30-0400')"]
155
+ end
156
+
157
+ it "should convert datetimes coming out of the database from database_timezone to application_timezone" do
158
+ dt = Sequel.database_to_application_timestamp('2009-06-01 06:20:30-0400')
159
+ dt.must_be_instance_of Time
160
+ dt.must_equal Time.new(2009,6,1,3,20,30,-25200)
161
+ dt.utc_offset.must_equal -25200
162
+
163
+ dt = Sequel.database_to_application_timestamp('2009-06-01 10:20:30+0000')
164
+ dt.must_be_instance_of Time
165
+ dt.must_equal Time.new(2009,6,1,3,20,30,-25200)
166
+ dt.utc_offset.must_equal -25200
167
+ end
168
+
169
+ it "should raise an error for ambiguous timezones by default" do
170
+ proc{Sequel.database_to_application_timestamp('2004-10-31T01:30:00')}.must_raise(Sequel::InvalidValue)
171
+ end
172
+
173
+ it "should support tzinfo_disambiguator= to handle ambiguous timezones automatically" do
174
+ Sequel.tzinfo_disambiguator = proc{|datetime, periods| periods.first}
175
+ Sequel.database_to_application_timestamp('2004-10-31T01:30:00').must_equal Time.new(2004, 10, 30, 22, 30, 0, -25200)
176
+ dt = Sequel.database_to_application_timestamp('2004-10-31T01:30:00')
177
+ dt.must_equal Time.new(2004, 10, 30, 22, 30, 0, -25200)
178
+ dt.utc_offset.must_equal -25200
179
+ end
180
+
181
+ it "should assume datetimes coming out of the database that don't have an offset as coming from database_timezone" do
182
+ dt = Sequel.database_to_application_timestamp('2009-06-01 06:20:30')
183
+ dt.must_be_instance_of Time
184
+ dt.must_equal Time.new(2009,6,1,3,20,30, -25200)
185
+ dt.utc_offset.must_equal -25200
186
+
187
+ dt = Sequel.database_to_application_timestamp('2009-06-01 10:20:30')
188
+ dt.must_be_instance_of Time
189
+ dt.must_equal Time.new(2009,6,1,7,20,30, -25200)
190
+ dt.utc_offset.must_equal -25200
191
+ end
192
+
193
+ it "should work with the thread_local_timezones extension" do
194
+ q, q1, q2 = Queue.new, Queue.new, Queue.new
195
+ tz1, tz2 = nil, nil
196
+ t1 = Thread.new do
197
+ Sequel.thread_application_timezone = 'America/New_York'
198
+ q2.push nil
199
+ q.pop
200
+ tz1 = Sequel.application_timezone
201
+ end
202
+ t2 = Thread.new do
203
+ Sequel.thread_application_timezone = 'America/Los_Angeles'
204
+ q2.push nil
205
+ q1.pop
206
+ tz2 = Sequel.application_timezone
207
+ end
208
+ q2.pop
209
+ q2.pop
210
+ q.push nil
211
+ q1.push nil
212
+ t1.join
213
+ t2.join
214
+ tz1.must_equal @tz_out
215
+ tz2.must_equal @tz_in
216
+ end
217
+ end
111
218
  end
@@ -161,4 +161,49 @@ describe "pg_auto_constraint_validations plugin" do
161
161
  proc{o.save}.must_raise Sequel::ValidationFailed
162
162
  o.errors.must_equal(:i=>['is invalid'], :id=>['is invalid'])
163
163
  end
164
+
165
+ it "should handle overridden constraint failures as validation errors when updating" do
166
+ o = @c.load(:i=>3)
167
+ @c.pg_auto_constraint_validation_override(:items_i_ocheck, :i, "foo bar")
168
+ @set_error[Sequel::CheckConstraintViolation, :constraint=>'items_i_ocheck']
169
+ proc{o.update(:i=>12)}.must_raise Sequel::ValidationFailed
170
+ o.errors.must_equal(:i=>['foo bar'])
171
+ end
172
+
173
+ it "should handle dumping cached metadata and loading metadata from cache" do
174
+ cache_file = "spec/files/pgacv-spec-#{$$}.cache"
175
+ begin
176
+ @ds = @db[:items]
177
+ @ds.send(:columns=, [:id, :i])
178
+ @db.fetch = @metadata_results.dup
179
+ c = Sequel::Model(@ds)
180
+ def c.name; 'Foo' end
181
+ @db.sqls
182
+ c.plugin :pg_auto_constraint_validations, :cache_file=>cache_file
183
+ @db.sqls.length.must_equal 5
184
+
185
+ o = c.new(:i=>12)
186
+ @set_error[Sequel::CheckConstraintViolation, :constraint=>'items_i_id_check']
187
+ proc{o.save}.must_raise Sequel::ValidationFailed
188
+
189
+ c.dump_pg_auto_constraint_validations_cache
190
+
191
+ @db.fetch = []
192
+ c = Sequel::Model(@ds)
193
+ def c.name; 'Foo' end
194
+ @db.sqls
195
+ c.plugin :pg_auto_constraint_validations, :cache_file=>cache_file
196
+ @db.sqls.must_be_empty
197
+
198
+ o = c.new(:i=>12)
199
+ @set_error[Sequel::CheckConstraintViolation, :constraint=>'items_i_id_check']
200
+ proc{o.save}.must_raise Sequel::ValidationFailed
201
+ ensure
202
+ File.delete(cache_file) if File.file?(cache_file)
203
+ end
204
+ end
205
+
206
+ it "should raise error if attempting to dump cached metadata when not using caching" do
207
+ proc{@c.dump_pg_auto_constraint_validations_cache}.must_raise Sequel::Error
208
+ end
164
209
  end
@@ -3,6 +3,8 @@ require_relative "spec_helper"
3
3
  Sequel.extension :pg_array, :pg_json
4
4
 
5
5
  describe "pg_json extension" do
6
+ integer_class = RUBY_VERSION >= '2.4' ? Integer : Fixnum
7
+
6
8
  before(:all) do
7
9
  m = Sequel::Postgres
8
10
  @m = m::JSONDatabaseMethods
@@ -29,7 +31,7 @@ describe "pg_json extension" do
29
31
  cp[3807].call("{[]}").must_equal [@bac.new([])]
30
32
  end
31
33
 
32
- it "should parse json strings correctly" do
34
+ deprecated "should parse json strings correctly" do
33
35
  @m.parse_json('[]').class.must_equal(@ac)
34
36
  @m.parse_json('[]').to_a.must_equal []
35
37
  @m.parse_json('[1]').to_a.must_equal [1]
@@ -40,46 +42,82 @@ describe "pg_json extension" do
40
42
  @m.parse_json('{"a": "b"}').to_hash.must_equal('a'=>'b')
41
43
  @m.parse_json('{"a": "b", "c": [1, 2, 3]}').to_hash.must_equal('a'=>'b', 'c'=>[1, 2, 3])
42
44
  @m.parse_json('{"a": "b", "c": {"d": "e"}}').to_hash.must_equal('a'=>'b', 'c'=>{'d'=>'e'})
45
+ proc{@m.parse_json("a")}.must_raise Sequel::InvalidValue
46
+
47
+ begin
48
+ Sequel.instance_eval do
49
+ alias pj parse_json
50
+ def parse_json(v)
51
+ {'1'=>1, "'a'"=>'a', 'true'=>true, 'false'=>false, 'null'=>nil, 'o'=>Object.new, '[one]'=>[1]}.fetch(v){pj(v)}
52
+ end
53
+ end
54
+ proc{@m.parse_json('o')}.must_raise(Sequel::InvalidValue)
55
+ ensure
56
+ Sequel.instance_eval do
57
+ alias parse_json pj
58
+ end
59
+ end
43
60
  end
44
61
 
45
- it "should parse json and non-json plain strings, integers, and floats correctly in db_parse_json" do
62
+ deprecated "should parse json and non-json plain strings, integers, and floats correctly in db_parse_json" do
46
63
  @m.db_parse_json('{"a": "b", "c": {"d": "e"}}').to_hash.must_equal('a'=>'b', 'c'=>{'d'=>'e'})
47
64
  @m.db_parse_json('[1, [2], {"a": "b"}]').to_a.must_equal [1, [2], {'a'=>'b'}]
48
65
  @m.db_parse_json('1').must_equal 1
49
66
  @m.db_parse_json('"b"').must_equal 'b'
50
67
  @m.db_parse_json('1.1').must_equal 1.1
68
+ proc{@m.db_parse_json("a")}.must_raise Sequel::InvalidValue
51
69
  end
52
70
 
53
- it "should parse json and non-json plain strings, integers, and floats correctly in db_parse_jsonb" do
71
+ deprecated "should parse jsonb and non-jsonb plain strings, integers, and floats correctly in db_parse_jsonb" do
54
72
  @m.db_parse_jsonb('{"a": "b", "c": {"d": "e"}}').to_hash.must_equal('a'=>'b', 'c'=>{'d'=>'e'})
55
73
  @m.db_parse_jsonb('[1, [2], {"a": "b"}]').to_a.must_equal [1, [2], {'a'=>'b'}]
56
74
  @m.db_parse_jsonb('1').must_equal 1
57
75
  @m.db_parse_jsonb('"b"').must_equal 'b'
58
76
  @m.db_parse_jsonb('1.1').must_equal 1.1
77
+ proc{@m.db_parse_jsonb("a")}.must_raise Sequel::InvalidValue
59
78
  end
60
79
 
61
- it "should raise an error when attempting to parse invalid json" do
62
- proc{@m.parse_json('')}.must_raise(Sequel::InvalidValue)
63
- proc{@m.parse_json('a')}.must_raise(Sequel::InvalidValue)
80
+ it "should parse json and non-json plain strings, integers, and floats correctly in conversion_proc" do
81
+ cp = @db.conversion_procs[114]
82
+ cp.call('{"a": "b", "c": {"d": "e"}}').to_hash.must_equal('a'=>'b', 'c'=>{'d'=>'e'})
83
+ cp.call('[1, [2], {"a": "b"}]').to_a.must_equal [1, [2], {'a'=>'b'}]
84
+ cp.call('1').must_equal 1
85
+ cp.call('"b"').must_equal 'b'
86
+ cp.call('1.1').must_equal 1.1
87
+ end
64
88
 
65
- begin
66
- Sequel.instance_eval do
67
- alias pj parse_json
68
- def parse_json(v)
69
- {'1'=>1, "'a'"=>'a', 'true'=>true, 'false'=>false, 'null'=>nil, 'o'=>Object.new, '[one]'=>[1]}.fetch(v){pj(v)}
89
+ it "should parse jsonb and non-jsonb plain strings, integers, and floats correctly in conversion_proc" do
90
+ cp = @db.conversion_procs[3802]
91
+ cp.call('{"a": "b", "c": {"d": "e"}}').to_hash.must_equal('a'=>'b', 'c'=>{'d'=>'e'})
92
+ cp.call('[1, [2], {"a": "b"}]').to_a.must_equal [1, [2], {'a'=>'b'}]
93
+ cp.call('1').must_equal 1
94
+ cp.call('"b"').must_equal 'b'
95
+ cp.call('1.1').must_equal 1.1
96
+ end
97
+
98
+ it "should raise an error when attempting to parse invalid json" do
99
+ [114, 3802].each do |oid|
100
+ cp = @db.conversion_procs[oid]
101
+ proc{cp.call('a')}.must_raise(Sequel::InvalidValue)
102
+
103
+ begin
104
+ Sequel.instance_eval do
105
+ alias pj parse_json
106
+ def parse_json(v)
107
+ {'1'=>1, "'a'"=>'a', 'true'=>true, 'false'=>false, 'null'=>nil, 'o'=>Object.new, '[one]'=>[1]}.fetch(v){pj(v)}
108
+ end
109
+ end
110
+ cp.call('1').must_equal 1
111
+ cp.call("'a'").must_equal 'a'
112
+ cp.call('true').must_equal true
113
+ cp.call('false').must_equal false
114
+ cp.call('null').must_be_nil
115
+ proc{cp.call('o')}.must_raise(Sequel::InvalidValue)
116
+ cp.call('one').must_equal 1
117
+ ensure
118
+ Sequel.instance_eval do
119
+ alias parse_json pj
70
120
  end
71
- end
72
- @m.parse_json('1').must_equal 1
73
- @m.parse_json("'a'").must_equal 'a'
74
- @m.parse_json('true').must_equal true
75
- @m.parse_json('false').must_equal false
76
- @m.parse_json('null').must_be_nil
77
- proc{@m.parse_json('o')}.must_raise(Sequel::InvalidValue)
78
- @m.db_parse_json('one').must_equal 1
79
- @m.db_parse_jsonb('one').must_equal 1
80
- ensure
81
- Sequel.instance_eval do
82
- alias parse_json pj
83
121
  end
84
122
  end
85
123
  end
@@ -173,6 +211,129 @@ describe "pg_json extension" do
173
211
  @db.bound_variable_arg(Sequel.pg_array([Sequel.pg_jsonb([{"a"=>1}]), Sequel.pg_jsonb("b"=>[1, 2])]), nil).must_equal '{"[{\\"a\\":1}]","{\\"b\\":[1,2]}"}'
174
212
  end
175
213
 
214
+ it "should support using wrapped JSON and JSONB primitives as bound variables" do
215
+ @db.bound_variable_arg(Sequel.pg_json_wrap(1), nil).must_equal '1'
216
+ @db.bound_variable_arg(Sequel.pg_json_wrap(2.5), nil).must_equal '2.5'
217
+ @db.bound_variable_arg(Sequel.pg_json_wrap('a'), nil).must_equal '"a"'
218
+ @db.bound_variable_arg(Sequel.pg_json_wrap(true), nil).must_equal 'true'
219
+ @db.bound_variable_arg(Sequel.pg_json_wrap(false), nil).must_equal 'false'
220
+ @db.bound_variable_arg(Sequel.pg_json_wrap(nil), nil).must_equal 'null'
221
+ end
222
+
223
+ it "should support using json[] and jsonb[] types in bound variables with ruby primitives" do
224
+ @db.bound_variable_arg(Sequel.pg_array([1, 2.5, 'a', true, false, nil].map{|v| Sequel.pg_json_wrap(v)}), nil).must_equal '{"1","2.5","\"a\"","true","false","null"}'
225
+ end
226
+
227
+ it "Sequel.pg_json_wrap should wrap Ruby primitives in JSON wrappers" do
228
+ Sequel.pg_json_wrap({}).class.must_equal Sequel::Postgres::JSONHash
229
+ Sequel.pg_json_wrap({}).must_equal({})
230
+ Sequel.pg_json_wrap([]).class.must_equal Sequel::Postgres::JSONArray
231
+ Sequel.pg_json_wrap([]).must_equal []
232
+ Sequel.pg_json_wrap('a').class.must_equal Sequel::Postgres::JSONString
233
+ Sequel.pg_json_wrap('a').must_equal 'a'
234
+ Sequel.pg_json_wrap(1).class.must_equal Sequel::Postgres::JSONInteger
235
+ Sequel.pg_json_wrap(1).must_equal 1
236
+ Sequel.pg_json_wrap(2.5).class.must_equal Sequel::Postgres::JSONFloat
237
+ Sequel.pg_json_wrap(2.5).must_equal 2.5
238
+ Sequel.pg_json_wrap(true).class.must_equal Sequel::Postgres::JSONTrue
239
+ Sequel.pg_json_wrap(true).must_equal true
240
+ Sequel.pg_json_wrap(false).class.must_equal Sequel::Postgres::JSONFalse
241
+ Sequel.pg_json_wrap(false).must_equal false
242
+ Sequel.pg_json_wrap(nil).class.must_equal Sequel::Postgres::JSONNull
243
+ Sequel.pg_json_wrap(nil).must_be_nil
244
+
245
+ c = Class.new(Hash).new
246
+ Sequel.pg_json_wrap(c).class.must_equal Sequel::Postgres::JSONHash
247
+ Sequel.pg_json_wrap(c).must_equal(c)
248
+
249
+ c = Class.new(Array).new
250
+ Sequel.pg_json_wrap(c).class.must_equal Sequel::Postgres::JSONArray
251
+ Sequel.pg_json_wrap(c).must_equal c
252
+
253
+ c = Class.new(String).new('a')
254
+ Sequel.pg_json_wrap(c).class.must_equal Sequel::Postgres::JSONString
255
+ Sequel.pg_json_wrap(c).must_equal c
256
+ end
257
+
258
+ it "Sequel.pg_json_wrap should fail when passed an unsupported object" do
259
+ proc{Sequel.pg_json_wrap(Object.new)}.must_raise Sequel::Error
260
+ end
261
+
262
+ it "Sequel.pg_jsonb_wrap should wrap Ruby primitives in JSONB wrappers" do
263
+ Sequel.pg_jsonb_wrap({}).class.must_equal Sequel::Postgres::JSONBHash
264
+ Sequel.pg_jsonb_wrap({}).must_equal({})
265
+ Sequel.pg_jsonb_wrap([]).class.must_equal Sequel::Postgres::JSONBArray
266
+ Sequel.pg_jsonb_wrap([]).must_equal []
267
+ Sequel.pg_jsonb_wrap('a').class.must_equal Sequel::Postgres::JSONBString
268
+ Sequel.pg_jsonb_wrap('a').must_equal 'a'
269
+ Sequel.pg_jsonb_wrap(1).class.must_equal Sequel::Postgres::JSONBInteger
270
+ Sequel.pg_jsonb_wrap(1).must_equal 1
271
+ Sequel.pg_jsonb_wrap(2.5).class.must_equal Sequel::Postgres::JSONBFloat
272
+ Sequel.pg_jsonb_wrap(2.5).must_equal 2.5
273
+ Sequel.pg_jsonb_wrap(true).class.must_equal Sequel::Postgres::JSONBTrue
274
+ Sequel.pg_jsonb_wrap(true).must_equal true
275
+ Sequel.pg_jsonb_wrap(false).class.must_equal Sequel::Postgres::JSONBFalse
276
+ Sequel.pg_jsonb_wrap(false).must_equal false
277
+ Sequel.pg_jsonb_wrap(nil).class.must_equal Sequel::Postgres::JSONBNull
278
+ Sequel.pg_jsonb_wrap(nil).must_be_nil
279
+ end
280
+
281
+ it "Sequel.pg_jsonb_wrap should fail when passed an unsupported object" do
282
+ proc{Sequel.pg_jsonb_wrap(Object.new)}.must_raise Sequel::Error
283
+ end
284
+
285
+ it "should not wrap JSON primitives in json and jsonb conversion_proc when not setting wrap_json_primitives" do
286
+ [114, 3802].each do |oid|
287
+ cp = @db.conversion_procs[oid]
288
+ cp.call('1').class.must_equal(integer_class)
289
+ cp.call('1').must_equal 1
290
+ cp.call('2.5').class.must_equal Float
291
+ cp.call('2.5').must_equal 2.5
292
+ cp.call('"a"').class.must_equal String
293
+ cp.call('"a"').must_equal 'a'
294
+ cp.call('true').class.must_equal TrueClass
295
+ cp.call('true').must_equal true
296
+ cp.call('false').class.must_equal FalseClass
297
+ cp.call('false').must_equal false
298
+ cp.call('null').class.must_equal NilClass
299
+ cp.call('null').must_be_nil
300
+ end
301
+ end
302
+
303
+ it "should wrap JSON primitives in json conversion_proc when setting wrap_json_primitives" do
304
+ cp = @db.conversion_procs[114]
305
+ @db.wrap_json_primitives = true
306
+ cp.call('1').class.must_equal Sequel::Postgres::JSONInteger
307
+ cp.call('1').must_equal 1
308
+ cp.call('2.5').class.must_equal Sequel::Postgres::JSONFloat
309
+ cp.call('2.5').must_equal 2.5
310
+ cp.call('"a"').class.must_equal Sequel::Postgres::JSONString
311
+ cp.call('"a"').must_equal "a"
312
+ cp.call('true').class.must_equal Sequel::Postgres::JSONTrue
313
+ cp.call('true').must_equal true
314
+ cp.call('false').class.must_equal Sequel::Postgres::JSONFalse
315
+ cp.call('false').must_equal false
316
+ cp.call('null').class.must_equal Sequel::Postgres::JSONNull
317
+ cp.call('null').must_be_nil
318
+ end
319
+
320
+ it "should wrap JSON primitives in jsonb conversion_proc when setting wrap_json_primitives" do
321
+ cp = @db.conversion_procs[3802]
322
+ @db.wrap_json_primitives = true
323
+ cp.call('1').class.must_equal Sequel::Postgres::JSONBInteger
324
+ cp.call('1').must_equal 1
325
+ cp.call('2.5').class.must_equal Sequel::Postgres::JSONBFloat
326
+ cp.call('2.5').must_equal 2.5
327
+ cp.call('"a"').class.must_equal Sequel::Postgres::JSONBString
328
+ cp.call('"a"').must_equal "a"
329
+ cp.call('true').class.must_equal Sequel::Postgres::JSONBTrue
330
+ cp.call('true').must_equal true
331
+ cp.call('false').class.must_equal Sequel::Postgres::JSONBFalse
332
+ cp.call('false').must_equal false
333
+ cp.call('null').class.must_equal Sequel::Postgres::JSONBNull
334
+ cp.call('null').must_be_nil
335
+ end
336
+
176
337
  it "should parse json type from the schema correctly" do
177
338
  @db.fetch = [{:name=>'id', :db_type=>'integer'}, {:name=>'i', :db_type=>'json'}]
178
339
  @db.schema(:items).map{|e| e[1][:type]}.must_equal [:integer, :json]
@@ -229,8 +390,22 @@ describe "pg_json extension" do
229
390
  @db.typecast_value(:json, '[]').class.must_equal(@ac)
230
391
  @db.typecast_value(:json, '{"a": "b"}').must_equal Sequel.pg_json("a"=>"b")
231
392
  @db.typecast_value(:json, '{"a": "b"}').class.must_equal(@hc)
232
- proc{@db.typecast_value(:json, '')}.must_raise(Sequel::InvalidValue)
233
- proc{@db.typecast_value(:json, 1)}.must_raise(Sequel::InvalidValue)
393
+ @db.typecast_value(:json, 1).class.must_equal Sequel::Postgres::JSONInteger
394
+ @db.typecast_value(:json, 1).must_equal 1
395
+ @db.typecast_value(:json, 2.5).class.must_equal Sequel::Postgres::JSONFloat
396
+ @db.typecast_value(:json, 2.5).must_equal 2.5
397
+ @db.typecast_value(:json, true).class.must_equal Sequel::Postgres::JSONTrue
398
+ @db.typecast_value(:json, true).must_equal true
399
+ @db.typecast_value(:json, false).class.must_equal Sequel::Postgres::JSONFalse
400
+ @db.typecast_value(:json, false).must_equal false
401
+ @db.typecast_value(:json, nil).class.must_equal NilClass
402
+ @db.typecast_value(:json, nil).must_be_nil
403
+ proc{@db.typecast_value(:json, 'a')}.must_raise(Sequel::InvalidValue)
404
+ proc{@db.typecast_value(:json, Object.new)}.must_raise(Sequel::InvalidValue)
405
+
406
+ @db.typecast_json_strings = true
407
+ @db.typecast_value(:json, '[]').class.must_equal(Sequel::Postgres::JSONString)
408
+ @db.typecast_value(:json, '[]').must_equal '[]'
234
409
  end
235
410
 
236
411
  it "should support typecasting for the jsonb type" do
@@ -250,13 +425,27 @@ describe "pg_json extension" do
250
425
  @db.typecast_value(:jsonb, '[]').class.must_equal(@bac)
251
426
  @db.typecast_value(:jsonb, '{"a": "b"}').must_equal Sequel.pg_jsonb("a"=>"b")
252
427
  @db.typecast_value(:jsonb, '{"a": "b"}').class.must_equal(@bhc)
253
- proc{@db.typecast_value(:jsonb, '')}.must_raise(Sequel::InvalidValue)
254
- proc{@db.typecast_value(:jsonb, 1)}.must_raise(Sequel::InvalidValue)
428
+ @db.typecast_value(:jsonb, 1).class.must_equal Sequel::Postgres::JSONBInteger
429
+ @db.typecast_value(:jsonb, 1).must_equal 1
430
+ @db.typecast_value(:jsonb, 2.5).class.must_equal Sequel::Postgres::JSONBFloat
431
+ @db.typecast_value(:jsonb, 2.5).must_equal 2.5
432
+ @db.typecast_value(:jsonb, true).class.must_equal Sequel::Postgres::JSONBTrue
433
+ @db.typecast_value(:jsonb, true).must_equal true
434
+ @db.typecast_value(:jsonb, false).class.must_equal Sequel::Postgres::JSONBFalse
435
+ @db.typecast_value(:jsonb, false).must_equal false
436
+ @db.typecast_value(:jsonb, nil).class.must_equal NilClass
437
+ @db.typecast_value(:jsonb, nil).must_be_nil
438
+ proc{@db.typecast_value(:jsonb, 'a')}.must_raise(Sequel::InvalidValue)
439
+ proc{@db.typecast_value(:jsonb, Object.new)}.must_raise(Sequel::InvalidValue)
440
+
441
+ @db.typecast_json_strings = true
442
+ @db.typecast_value(:jsonb, '[]').class.must_equal(Sequel::Postgres::JSONBString)
443
+ @db.typecast_value(:jsonb, '[]').must_equal '[]'
255
444
  end
256
445
 
257
446
  it "should return correct results for Database#schema_type_class" do
258
- @db.schema_type_class(:json).must_equal [Sequel::Postgres::JSONHash, Sequel::Postgres::JSONArray]
259
- @db.schema_type_class(:jsonb).must_equal [Sequel::Postgres::JSONBHash, Sequel::Postgres::JSONBArray]
447
+ @db.schema_type_class(:json).must_equal [Sequel::Postgres::JSONObject]
448
+ @db.schema_type_class(:jsonb).must_equal [Sequel::Postgres::JSONBObject]
260
449
  @db.schema_type_class(:integer).must_equal Integer
261
450
  end
262
451
  end