sequel 5.19.0 → 5.24.0

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