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.
- checksums.yaml +4 -4
- data/CHANGELOG +102 -0
- data/doc/dataset_filtering.rdoc +15 -0
- data/doc/opening_databases.rdoc +5 -1
- data/doc/release_notes/5.20.0.txt +89 -0
- data/doc/release_notes/5.21.0.txt +87 -0
- data/doc/release_notes/5.22.0.txt +48 -0
- data/doc/release_notes/5.23.0.txt +56 -0
- data/doc/release_notes/5.24.0.txt +56 -0
- data/doc/sharding.rdoc +2 -0
- data/doc/testing.rdoc +1 -0
- data/doc/transactions.rdoc +38 -0
- data/lib/sequel/adapters/ado.rb +27 -19
- data/lib/sequel/adapters/jdbc.rb +7 -1
- data/lib/sequel/adapters/jdbc/mysql.rb +2 -2
- data/lib/sequel/adapters/jdbc/postgresql.rb +1 -13
- data/lib/sequel/adapters/jdbc/sqlite.rb +29 -0
- data/lib/sequel/adapters/mysql2.rb +2 -3
- data/lib/sequel/adapters/shared/mssql.rb +7 -7
- data/lib/sequel/adapters/shared/postgres.rb +37 -19
- data/lib/sequel/adapters/shared/sqlite.rb +27 -3
- data/lib/sequel/adapters/sqlite.rb +1 -1
- data/lib/sequel/adapters/tinytds.rb +12 -0
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +2 -0
- data/lib/sequel/database/logging.rb +7 -1
- data/lib/sequel/database/query.rb +1 -1
- data/lib/sequel/database/schema_generator.rb +12 -3
- data/lib/sequel/database/schema_methods.rb +2 -0
- data/lib/sequel/database/transactions.rb +57 -5
- data/lib/sequel/dataset.rb +4 -2
- data/lib/sequel/dataset/actions.rb +3 -2
- data/lib/sequel/dataset/placeholder_literalizer.rb +4 -1
- data/lib/sequel/dataset/query.rb +5 -1
- data/lib/sequel/dataset/sql.rb +11 -7
- data/lib/sequel/extensions/named_timezones.rb +52 -8
- data/lib/sequel/extensions/pg_array.rb +4 -0
- data/lib/sequel/extensions/pg_json.rb +387 -123
- data/lib/sequel/extensions/pg_range.rb +3 -2
- data/lib/sequel/extensions/pg_row.rb +3 -1
- data/lib/sequel/extensions/schema_dumper.rb +1 -1
- data/lib/sequel/extensions/server_block.rb +15 -4
- data/lib/sequel/model/associations.rb +35 -9
- data/lib/sequel/model/plugins.rb +104 -0
- data/lib/sequel/plugins/association_dependencies.rb +3 -3
- data/lib/sequel/plugins/association_pks.rb +14 -4
- data/lib/sequel/plugins/association_proxies.rb +3 -2
- data/lib/sequel/plugins/class_table_inheritance.rb +11 -0
- data/lib/sequel/plugins/composition.rb +13 -9
- data/lib/sequel/plugins/finder.rb +2 -2
- data/lib/sequel/plugins/hook_class_methods.rb +17 -5
- data/lib/sequel/plugins/insert_conflict.rb +72 -0
- data/lib/sequel/plugins/inverted_subsets.rb +2 -2
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +147 -59
- data/lib/sequel/plugins/rcte_tree.rb +6 -0
- data/lib/sequel/plugins/static_cache.rb +8 -3
- data/lib/sequel/plugins/static_cache_cache.rb +53 -0
- data/lib/sequel/plugins/subset_conditions.rb +2 -2
- data/lib/sequel/plugins/validation_class_methods.rb +5 -3
- data/lib/sequel/sql.rb +15 -3
- data/lib/sequel/timezones.rb +50 -11
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/mssql_spec.rb +24 -0
- data/spec/adapters/mysql_spec.rb +0 -5
- data/spec/adapters/postgres_spec.rb +319 -1
- data/spec/bin_spec.rb +1 -1
- data/spec/core/database_spec.rb +123 -2
- data/spec/core/dataset_spec.rb +33 -1
- data/spec/core/expression_filters_spec.rb +25 -1
- data/spec/core/schema_spec.rb +24 -0
- data/spec/extensions/class_table_inheritance_spec.rb +30 -8
- data/spec/extensions/core_refinements_spec.rb +1 -1
- data/spec/extensions/hook_class_methods_spec.rb +22 -0
- data/spec/extensions/insert_conflict_spec.rb +103 -0
- data/spec/extensions/migration_spec.rb +13 -0
- data/spec/extensions/named_timezones_spec.rb +109 -2
- data/spec/extensions/pg_auto_constraint_validations_spec.rb +45 -0
- data/spec/extensions/pg_json_spec.rb +218 -29
- data/spec/extensions/pg_range_spec.rb +76 -9
- data/spec/extensions/rcte_tree_spec.rb +6 -0
- data/spec/extensions/s_spec.rb +1 -1
- data/spec/extensions/schema_dumper_spec.rb +4 -2
- data/spec/extensions/server_block_spec.rb +38 -0
- data/spec/extensions/spec_helper.rb +8 -1
- data/spec/extensions/static_cache_cache_spec.rb +35 -0
- data/spec/integration/dataset_test.rb +25 -9
- data/spec/integration/plugin_test.rb +42 -0
- data/spec/integration/schema_test.rb +7 -2
- data/spec/integration/transaction_test.rb +50 -0
- data/spec/model/associations_spec.rb +84 -4
- data/spec/model/plugins_spec.rb +111 -0
- 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')
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
233
|
-
|
|
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
|
-
|
|
254
|
-
|
|
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::
|
|
259
|
-
@db.schema_type_class(:jsonb).must_equal [Sequel::Postgres::
|
|
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
|