sequel 5.21.0 → 5.26.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 +80 -0
- data/README.rdoc +1 -1
- data/doc/dataset_filtering.rdoc +15 -0
- data/doc/opening_databases.rdoc +3 -0
- data/doc/postgresql.rdoc +2 -2
- 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/release_notes/5.25.0.txt +32 -0
- data/doc/release_notes/5.26.0.txt +35 -0
- data/doc/testing.rdoc +1 -0
- data/lib/sequel/adapters/jdbc.rb +7 -1
- data/lib/sequel/adapters/jdbc/postgresql.rb +1 -13
- data/lib/sequel/adapters/jdbc/sqlite.rb +29 -0
- data/lib/sequel/adapters/mysql2.rb +0 -1
- data/lib/sequel/adapters/shared/mssql.rb +11 -9
- data/lib/sequel/adapters/shared/postgres.rb +42 -12
- data/lib/sequel/adapters/shared/sqlite.rb +16 -2
- data/lib/sequel/adapters/tinytds.rb +12 -0
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
- data/lib/sequel/database/logging.rb +7 -1
- data/lib/sequel/database/schema_generator.rb +11 -2
- data/lib/sequel/database/schema_methods.rb +2 -0
- data/lib/sequel/dataset.rb +4 -2
- data/lib/sequel/dataset/actions.rb +3 -2
- data/lib/sequel/dataset/query.rb +4 -0
- data/lib/sequel/dataset/sql.rb +11 -7
- data/lib/sequel/extensions/named_timezones.rb +51 -9
- data/lib/sequel/extensions/pg_array.rb +4 -0
- data/lib/sequel/extensions/pg_json.rb +88 -17
- data/lib/sequel/extensions/pg_json_ops.rb +124 -0
- data/lib/sequel/extensions/pg_range.rb +12 -2
- data/lib/sequel/extensions/pg_row.rb +3 -1
- data/lib/sequel/extensions/sql_comments.rb +2 -2
- data/lib/sequel/model/base.rb +12 -5
- data/lib/sequel/plugins/association_multi_add_remove.rb +83 -0
- data/lib/sequel/plugins/association_proxies.rb +3 -2
- data/lib/sequel/plugins/caching.rb +3 -0
- data/lib/sequel/plugins/class_table_inheritance.rb +10 -0
- data/lib/sequel/plugins/csv_serializer.rb +26 -9
- data/lib/sequel/plugins/dirty.rb +3 -9
- data/lib/sequel/plugins/insert_conflict.rb +72 -0
- data/lib/sequel/plugins/nested_attributes.rb +7 -0
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +89 -30
- data/lib/sequel/plugins/sharding.rb +11 -5
- data/lib/sequel/plugins/static_cache.rb +8 -3
- data/lib/sequel/plugins/static_cache_cache.rb +53 -0
- data/lib/sequel/plugins/typecast_on_load.rb +3 -2
- data/lib/sequel/sql.rb +18 -4
- data/lib/sequel/timezones.rb +50 -11
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +174 -0
- data/spec/bin_spec.rb +2 -2
- data/spec/core/database_spec.rb +50 -0
- data/spec/core/dataset_spec.rb +33 -1
- data/spec/core/expression_filters_spec.rb +32 -3
- data/spec/core/schema_spec.rb +18 -0
- data/spec/core/spec_helper.rb +1 -1
- data/spec/core_extensions_spec.rb +1 -1
- data/spec/extensions/association_multi_add_remove_spec.rb +1041 -0
- data/spec/extensions/dirty_spec.rb +33 -0
- data/spec/extensions/insert_conflict_spec.rb +103 -0
- data/spec/extensions/named_timezones_spec.rb +109 -2
- data/spec/extensions/nested_attributes_spec.rb +48 -0
- data/spec/extensions/pg_auto_constraint_validations_spec.rb +37 -0
- data/spec/extensions/pg_json_ops_spec.rb +67 -0
- data/spec/extensions/pg_json_spec.rb +12 -0
- data/spec/extensions/pg_range_spec.rb +90 -9
- data/spec/extensions/sharding_spec.rb +8 -0
- data/spec/extensions/spec_helper.rb +9 -2
- data/spec/extensions/static_cache_cache_spec.rb +35 -0
- data/spec/guards_helper.rb +1 -1
- data/spec/integration/dataset_test.rb +24 -8
- data/spec/integration/plugin_test.rb +27 -0
- data/spec/integration/schema_test.rb +16 -2
- data/spec/model/spec_helper.rb +1 -1
- metadata +32 -2
@@ -107,12 +107,18 @@ module Sequel
|
|
107
107
|
# previous row_proc, but calls set_server on the output of that row_proc,
|
108
108
|
# ensuring that objects retrieved by a specific shard know which shard they
|
109
109
|
# are tied to.
|
110
|
-
def
|
111
|
-
|
112
|
-
if rp
|
113
|
-
|
110
|
+
def row_proc
|
111
|
+
rp = super
|
112
|
+
if rp
|
113
|
+
case server = db.pool.send(:pick_server, opts[:server])
|
114
|
+
when nil, :default, :read_only
|
115
|
+
# nothing
|
116
|
+
else
|
117
|
+
old_rp = rp
|
118
|
+
rp = proc{|r| old_rp.call(r).set_server(server)}
|
119
|
+
end
|
114
120
|
end
|
115
|
-
|
121
|
+
rp
|
116
122
|
end
|
117
123
|
end
|
118
124
|
end
|
@@ -211,18 +211,23 @@ module Sequel
|
|
211
211
|
# Reload the cache for this model by retrieving all of the instances in the dataset
|
212
212
|
# freezing them, and populating the cached array and hash.
|
213
213
|
def load_cache
|
214
|
-
|
214
|
+
@all = load_static_cache_rows
|
215
215
|
h = {}
|
216
|
-
|
216
|
+
@all.each do |o|
|
217
217
|
o.errors.freeze
|
218
218
|
h[o.pk.freeze] = o.freeze
|
219
219
|
end
|
220
|
-
@all = a.freeze
|
221
220
|
@cache = h.freeze
|
222
221
|
end
|
223
222
|
|
224
223
|
private
|
225
224
|
|
225
|
+
# Load the static cache rows from the database.
|
226
|
+
def load_static_cache_rows
|
227
|
+
ret = super if defined?(super)
|
228
|
+
ret || dataset.all.freeze
|
229
|
+
end
|
230
|
+
|
226
231
|
# Return the frozen object with the given pk, or nil if no such object exists
|
227
232
|
# in the cache, without issuing a database query.
|
228
233
|
def primary_key_lookup(pk)
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
module Sequel
|
4
|
+
module Plugins
|
5
|
+
# The static_cache_cache plugin allows for caching the row content for subclasses
|
6
|
+
# that use the static cache plugin (or just the current class). Using this plugin
|
7
|
+
# can avoid the need to query the database every time loading the plugin into a
|
8
|
+
# model, which can save time when you have a lot of models using the static_cache
|
9
|
+
# plugin.
|
10
|
+
#
|
11
|
+
# Usage:
|
12
|
+
#
|
13
|
+
# # Make all model subclasses that use the static_cache plugin use
|
14
|
+
# # the cached values in the given file
|
15
|
+
# Sequel::Model.plugin :static_cache_cache, "static_cache.cache"
|
16
|
+
#
|
17
|
+
# # Make the AlbumType model the cached values in the given file,
|
18
|
+
# # should be loaded before the static_cache plugin
|
19
|
+
# AlbumType.plugin :static_cache_cache, "static_cache.cache"
|
20
|
+
module StaticCacheCache
|
21
|
+
def self.configure(model, file)
|
22
|
+
model.instance_variable_set(:@static_cache_cache_file, file)
|
23
|
+
model.instance_variable_set(:@static_cache_cache, File.exist?(file) ? Marshal.load(File.read(file)) : {})
|
24
|
+
end
|
25
|
+
|
26
|
+
module ClassMethods
|
27
|
+
# Dump the in-memory cached rows to the cache file.
|
28
|
+
def dump_static_cache_cache
|
29
|
+
File.open(@static_cache_cache_file, 'wb'){|f| f.write(Marshal.dump(@static_cache_cache))}
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
|
33
|
+
Plugins.inherited_instance_variables(self, :@static_cache_cache_file=>nil, :@static_cache_cache=>nil)
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# Load the rows for the model from the cache if available.
|
38
|
+
# If not available, load the rows from the database, and
|
39
|
+
# then update the cache with the raw rows.
|
40
|
+
def load_static_cache_rows
|
41
|
+
if rows = Sequel.synchronize{@static_cache_cache[name]}
|
42
|
+
rows.map{|row| call(row)}.freeze
|
43
|
+
else
|
44
|
+
rows = dataset.all.freeze
|
45
|
+
raw_rows = rows.map(&:values)
|
46
|
+
Sequel.synchronize{@static_cache_cache[name] = raw_rows}
|
47
|
+
rows
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -41,7 +41,9 @@ module Sequel
|
|
41
41
|
# Typecast values using #load_typecast when the values are retrieved
|
42
42
|
# from the database.
|
43
43
|
def call(values)
|
44
|
-
super.load_typecast
|
44
|
+
o = super.load_typecast
|
45
|
+
o.send(:_clear_changed_columns, :initialize)
|
46
|
+
o
|
45
47
|
end
|
46
48
|
|
47
49
|
# Freeze typecast on load columns when freezing model class.
|
@@ -63,7 +65,6 @@ module Sequel
|
|
63
65
|
set_column_value("#{c}=", v)
|
64
66
|
end
|
65
67
|
end
|
66
|
-
_changed_columns.clear
|
67
68
|
self
|
68
69
|
end
|
69
70
|
|
data/lib/sequel/sql.rb
CHANGED
@@ -788,8 +788,10 @@ module Sequel
|
|
788
788
|
def coerce(other)
|
789
789
|
if other.is_a?(Numeric)
|
790
790
|
[SQL::NumericExpression.new(:NOOP, other), self]
|
791
|
-
|
791
|
+
elsif defined?(super)
|
792
792
|
super
|
793
|
+
else
|
794
|
+
[self, other]
|
793
795
|
end
|
794
796
|
end
|
795
797
|
|
@@ -1086,11 +1088,23 @@ module Sequel
|
|
1086
1088
|
def self.from_value_pair(l, r)
|
1087
1089
|
case r
|
1088
1090
|
when Range
|
1089
|
-
|
1091
|
+
unless r.begin.nil?
|
1092
|
+
begin_expr = new(:>=, l, r.begin)
|
1093
|
+
end
|
1090
1094
|
unless r.end.nil?
|
1091
|
-
|
1095
|
+
end_expr = new(r.exclude_end? ? :< : :<=, l, r.end)
|
1096
|
+
end
|
1097
|
+
if begin_expr
|
1098
|
+
if end_expr
|
1099
|
+
new(:AND, begin_expr, end_expr)
|
1100
|
+
else
|
1101
|
+
begin_expr
|
1102
|
+
end
|
1103
|
+
elsif end_expr
|
1104
|
+
end_expr
|
1105
|
+
else
|
1106
|
+
new(:'=', 1, 1)
|
1092
1107
|
end
|
1093
|
-
expr
|
1094
1108
|
when ::Array
|
1095
1109
|
r = r.dup.freeze unless r.frozen?
|
1096
1110
|
new(:IN, l, r)
|
data/lib/sequel/timezones.rb
CHANGED
@@ -54,7 +54,14 @@ module Sequel
|
|
54
54
|
convert_output_datetime_other(v, output_timezone)
|
55
55
|
end
|
56
56
|
else
|
57
|
-
|
57
|
+
case output_timezone
|
58
|
+
when :utc
|
59
|
+
v.getutc
|
60
|
+
when :local
|
61
|
+
v.getlocal
|
62
|
+
else
|
63
|
+
convert_output_time_other(v, output_timezone)
|
64
|
+
end
|
58
65
|
end
|
59
66
|
else
|
60
67
|
v
|
@@ -110,7 +117,7 @@ module Sequel
|
|
110
117
|
# same time and just modifying the timezone.
|
111
118
|
def convert_input_datetime_no_offset(v, input_timezone)
|
112
119
|
case input_timezone
|
113
|
-
when :utc
|
120
|
+
when nil, :utc
|
114
121
|
v # DateTime assumes UTC if no offset is given
|
115
122
|
when :local
|
116
123
|
offset = local_offset_for_datetime(v)
|
@@ -119,7 +126,7 @@ module Sequel
|
|
119
126
|
convert_input_datetime_other(v, input_timezone)
|
120
127
|
end
|
121
128
|
end
|
122
|
-
|
129
|
+
|
123
130
|
# Convert the given +DateTime+ to the given input_timezone that is not supported
|
124
131
|
# by default (i.e. one other than +nil+, <tt>:local</tt>, or <tt>:utc</tt>). Raises an +InvalidValue+ by default.
|
125
132
|
# Can be overridden in extensions.
|
@@ -127,6 +134,13 @@ module Sequel
|
|
127
134
|
raise InvalidValue, "Invalid input_timezone: #{input_timezone.inspect}"
|
128
135
|
end
|
129
136
|
|
137
|
+
# Convert the given +Time+ to the given input_timezone that is not supported
|
138
|
+
# by default (i.e. one other than +nil+, <tt>:local</tt>, or <tt>:utc</tt>). Raises an +InvalidValue+ by default.
|
139
|
+
# Can be overridden in extensions.
|
140
|
+
def convert_input_time_other(v, input_timezone)
|
141
|
+
raise InvalidValue, "Invalid input_timezone: #{input_timezone.inspect}"
|
142
|
+
end
|
143
|
+
|
130
144
|
# Converts the object from a +String+, +Array+, +Date+, +DateTime+, or +Time+ into an
|
131
145
|
# instance of <tt>Sequel.datetime_class</tt>. If given an array or a string that doesn't
|
132
146
|
# contain an offset, assume that the array/string is already in the given +input_timezone+.
|
@@ -139,12 +153,17 @@ module Sequel
|
|
139
153
|
else
|
140
154
|
# Correct for potentially wrong offset if string doesn't include offset
|
141
155
|
if v2.is_a?(DateTime)
|
142
|
-
|
156
|
+
convert_input_datetime_no_offset(v2, input_timezone)
|
143
157
|
else
|
144
|
-
|
145
|
-
|
158
|
+
case input_timezone
|
159
|
+
when nil, :local
|
160
|
+
v2
|
161
|
+
when :utc
|
162
|
+
(v2 + v2.utc_offset).utc
|
163
|
+
else
|
164
|
+
convert_input_time_other((v2 + v2.utc_offset).utc, input_timezone)
|
165
|
+
end
|
146
166
|
end
|
147
|
-
v2
|
148
167
|
end
|
149
168
|
when Array
|
150
169
|
y, mo, d, h, mi, s, ns, off = v
|
@@ -155,8 +174,18 @@ module Sequel
|
|
155
174
|
else
|
156
175
|
convert_input_datetime_no_offset(DateTime.civil(y, mo, d, h, mi, s), input_timezone)
|
157
176
|
end
|
177
|
+
elsif off
|
178
|
+
s += Rational(ns, 1000000000) if ns
|
179
|
+
Time.new(y, mo, d, h, mi, s, (off*86400).to_i)
|
158
180
|
else
|
159
|
-
|
181
|
+
case input_timezone
|
182
|
+
when nil, :local
|
183
|
+
Time.local(y, mo, d, h, mi, s, (ns ? ns / 1000.0 : 0))
|
184
|
+
when :utc
|
185
|
+
Time.utc(y, mo, d, h, mi, s, (ns ? ns / 1000.0 : 0))
|
186
|
+
else
|
187
|
+
convert_input_time_other(Time.utc(y, mo, d, h, mi, s, (ns ? ns / 1000.0 : 0)), input_timezone)
|
188
|
+
end
|
160
189
|
end
|
161
190
|
when Hash
|
162
191
|
ary = [:year, :month, :day, :hour, :minute, :second, :nanos].map{|x| (v[x] || v[x.to_s]).to_i}
|
@@ -188,23 +217,33 @@ module Sequel
|
|
188
217
|
raise InvalidValue, "Invalid output_timezone: #{output_timezone.inspect}"
|
189
218
|
end
|
190
219
|
|
220
|
+
# Convert the given +Time+ to the given output_timezone that is not supported
|
221
|
+
# by default (i.e. one other than +nil+, <tt>:local</tt>, or <tt>:utc</tt>). Raises an +InvalidValue+ by default.
|
222
|
+
# Can be overridden in extensions.
|
223
|
+
def convert_output_time_other(v, output_timezone)
|
224
|
+
raise InvalidValue, "Invalid output_timezone: #{output_timezone.inspect}"
|
225
|
+
end
|
226
|
+
|
191
227
|
# Convert the timezone setter argument. Returns argument given by default,
|
192
228
|
# exists for easier overriding in extensions.
|
193
229
|
def convert_timezone_setter_arg(tz)
|
194
230
|
tz
|
195
231
|
end
|
196
232
|
|
197
|
-
# Takes a DateTime dt, and returns the correct local offset for that dt, daylight savings included.
|
233
|
+
# Takes a DateTime dt, and returns the correct local offset for that dt, daylight savings included, in fraction of a day.
|
198
234
|
def local_offset_for_datetime(dt)
|
199
235
|
time_offset_to_datetime_offset Time.local(dt.year, dt.month, dt.day, dt.hour, dt.min, dt.sec).utc_offset
|
200
236
|
end
|
201
237
|
|
202
238
|
# Caches offset conversions to avoid excess Rational math.
|
203
239
|
def time_offset_to_datetime_offset(offset_secs)
|
204
|
-
|
205
|
-
|
240
|
+
if offset = Sequel.synchronize{@local_offsets[offset_secs]}
|
241
|
+
return offset
|
242
|
+
end
|
243
|
+
Sequel.synchronize{@local_offsets[offset_secs] = Rational(offset_secs, 86400)}
|
206
244
|
end
|
207
245
|
end
|
208
246
|
|
247
|
+
@local_offsets = {}
|
209
248
|
extend Timezones
|
210
249
|
end
|
data/lib/sequel/version.rb
CHANGED
@@ -6,7 +6,7 @@ module Sequel
|
|
6
6
|
|
7
7
|
# The minor version of Sequel. Bumped for every non-patch level
|
8
8
|
# release, generally around once a month.
|
9
|
-
MINOR =
|
9
|
+
MINOR = 26
|
10
10
|
|
11
11
|
# The tiny version of Sequel. Usually 0, only bumped for bugfix
|
12
12
|
# releases that fix regressions from previous versions.
|
@@ -227,6 +227,24 @@ describe "PostgreSQL", '#create_table' do
|
|
227
227
|
@db.convert_serial_to_identity(:tmp_dolls, :column=>:id)
|
228
228
|
end if DB.server_version >= 100002 && DB.get{current_setting('is_superuser')} == 'on'
|
229
229
|
|
230
|
+
it "should support creating generated columns" do
|
231
|
+
@db.create_table(:tmp_dolls){Integer :a; Integer :b; Integer :c, :generated_always_as=>Sequel[:a] * 2 + :b + 1}
|
232
|
+
@db[:tmp_dolls].insert(:a=>100, :b=>10)
|
233
|
+
@db[:tmp_dolls].select_order_map([:a, :b, :c]).must_equal [[100, 10, 211]]
|
234
|
+
end if DB.server_version >= 120000
|
235
|
+
|
236
|
+
it "should support deferred primary key and unique constraints on columns" do
|
237
|
+
@db.create_table(:tmp_dolls){primary_key :id, :primary_key_deferrable=>true; Integer :i, :unique=>true, :unique_deferrable=>true}
|
238
|
+
@db[:tmp_dolls].insert(:i=>10)
|
239
|
+
DB.transaction do
|
240
|
+
@db[:tmp_dolls].insert(:id=>1, :i=>1)
|
241
|
+
@db[:tmp_dolls].insert(:id=>10, :i=>10)
|
242
|
+
@db[:tmp_dolls].where(:i=>1).update(:id=>2)
|
243
|
+
@db[:tmp_dolls].where(:id=>10).update(:i=>2)
|
244
|
+
end
|
245
|
+
@db[:tmp_dolls].select_order_map([:id, :i]).must_equal [[1, 10], [2, 1], [10, 2]]
|
246
|
+
end if DB.server_version >= 90000
|
247
|
+
|
230
248
|
it "should support pg_loose_count extension" do
|
231
249
|
@db.extension :pg_loose_count
|
232
250
|
@db.create_table(:tmp_dolls){text :name}
|
@@ -350,8 +368,36 @@ describe "PostgreSQL", 'INSERT ON CONFLICT' do
|
|
350
368
|
@ds.insert_conflict(:constraint=>:ic_test_a_uidx, :update=>{:b=>6}, :update_where=>{Sequel[:ic_test][:b]=>4}).insert(1, 3, 4).must_be_nil
|
351
369
|
@ds.all.must_equal [{:a=>1, :b=>5, :c=>5, :c_is_unique=>false}]
|
352
370
|
end
|
371
|
+
|
372
|
+
it "Dataset#insert_conflict should support table aliases" do
|
373
|
+
@ds = @db[Sequel[:ic_test].as(:foo)]
|
374
|
+
@ds.insert(1, 2, 5)
|
375
|
+
proc{@ds.insert(1, 3, 4)}.must_raise Sequel::UniqueConstraintViolation
|
376
|
+
@ds.insert_conflict(:target=>:a, :update=>{:b=>Sequel[:foo][:c] + Sequel[:excluded][:c]}).insert(1, 7, 10)
|
377
|
+
@ds.all.must_equal [{:a=>1, :b=>15, :c=>5, :c_is_unique=>false}]
|
378
|
+
end
|
353
379
|
end if DB.server_version >= 90500
|
354
380
|
|
381
|
+
describe "A PostgreSQL database" do
|
382
|
+
before do
|
383
|
+
@db = DB
|
384
|
+
@db.create_table!(:cte_test){Integer :id}
|
385
|
+
end
|
386
|
+
after do
|
387
|
+
@db.drop_table?(:cte_test)
|
388
|
+
end
|
389
|
+
|
390
|
+
it "should give correct results for WITH AS [NOT] MATERIALIZED" do
|
391
|
+
@ds = @db[:cte_test]
|
392
|
+
@ds.insert(1)
|
393
|
+
@ds.insert(2)
|
394
|
+
|
395
|
+
@db[:t].with(:t, @ds, :materialized=>nil).order(:id).map(:id).must_equal [1, 2]
|
396
|
+
@db[:t].with(:t, @ds, :materialized=>true).order(:id).map(:id).must_equal [1, 2]
|
397
|
+
@db[:t].with(:t, @ds, :materialized=>false).order(:id).map(:id).must_equal [1, 2]
|
398
|
+
end
|
399
|
+
end if DB.server_version >= 120000
|
400
|
+
|
355
401
|
describe "A PostgreSQL database" do
|
356
402
|
before(:all) do
|
357
403
|
@db = DB
|
@@ -3303,6 +3349,65 @@ describe 'PostgreSQL json type' do
|
|
3303
3349
|
@db.from(jo.each_text).select_order_map(:key).must_equal %w'a b'
|
3304
3350
|
@db.from(jo.each_text).order(:key).where(:key=>'b').get(:value).gsub(' ', '').must_match(/\{"d":\{"e":3\},"c":2\}|\{"c":2,"d":\{"e":3\}\}/)
|
3305
3351
|
|
3352
|
+
if DB.server_version >= 120000 && json_type == :jsonb
|
3353
|
+
@db.get(jo.path_exists('$.b.d.e')).must_equal true
|
3354
|
+
@db.get(jo.path_exists('$.b.d.f')).must_equal false
|
3355
|
+
|
3356
|
+
@db.get(jo.path_exists!('$.b.d.e')).must_equal true
|
3357
|
+
@db.get(jo.path_exists!('$.b.d.f')).must_equal false
|
3358
|
+
@db.get(jo.path_exists!('$.b.d.e ? (@ > $x)', '{"x":2}')).must_equal true
|
3359
|
+
@db.get(jo.path_exists!('$.b.d.e ? (@ > $x)', '{"x":4}')).must_equal false
|
3360
|
+
@db.get(jo.path_exists!('$.b.d.e ? (@ > $x)', x: 2)).must_equal true
|
3361
|
+
@db.get(jo.path_exists!('$.b.d.e ? (@ > $x)', x: 4)).must_equal false
|
3362
|
+
@db.get(jo.path_exists!('$.b.d.e ? (@ > $x)', {x: 2}, true)).must_equal true
|
3363
|
+
@db.get(jo.path_exists!('$.b.d.e ? (@ > $x)', {x: 4}, false)).must_equal false
|
3364
|
+
|
3365
|
+
@db.get(jo.path_match('$.b.d.e')).must_be_nil
|
3366
|
+
@db.get(jo.path_match('$.b.d.f')).must_be_nil
|
3367
|
+
@db.get(pg_json.call('b'=>{'d'=>{'e'=>true}}).op.path_match('$.b.d.e')).must_equal true
|
3368
|
+
@db.get(pg_json.call('b'=>{'d'=>{'e'=>false}}).op.path_match('$.b.d.e')).must_equal false
|
3369
|
+
|
3370
|
+
proc{@db.get(jo.path_match!('$.b.d.e'))}.must_raise(Sequel::DatabaseError)
|
3371
|
+
proc{@db.get(jo.path_match!('$.b.d.f'))}.must_raise(Sequel::DatabaseError)
|
3372
|
+
@db.get(jo.path_match!('$.b.d.e', {}, true)).must_be_nil
|
3373
|
+
@db.get(jo.path_match!('$.b.d.f', {}, true)).must_be_nil
|
3374
|
+
@db.get(pg_json.call('b'=>{'d'=>{'e'=>true}}).op.path_match!('$.b.d.e')).must_equal true
|
3375
|
+
@db.get(pg_json.call('b'=>{'d'=>{'e'=>false}}).op.path_match!('$.b.d.e')).must_equal false
|
3376
|
+
@db.get(jo.path_match!('$.b.d.e > $x', '{"x":2}')).must_equal true
|
3377
|
+
@db.get(jo.path_match!('$.b.d.e > $x', '{"x":4}')).must_equal false
|
3378
|
+
@db.get(jo.path_match!('$.b.d.e > $x', x: 2)).must_equal true
|
3379
|
+
@db.get(jo.path_match!('$.b.d.e > $x', x: 4)).must_equal false
|
3380
|
+
@db.get(jo.path_match!('$.b.d.e > $x', {x: 2}, false)).must_equal true
|
3381
|
+
@db.get(jo.path_match!('$.b.d.e > $x', {x: 4}, true)).must_equal false
|
3382
|
+
|
3383
|
+
@db.get(jo.path_query_first('$.b.d.e')).must_equal 3
|
3384
|
+
@db.get(jo.path_query_first('$.b.d.f')).must_be_nil
|
3385
|
+
@db.get(jo.path_query_first('$.b.d.e ? (@ > $x)', '{"x":2}')).must_equal 3
|
3386
|
+
@db.get(jo.path_query_first('$.b.d.e ? (@ > $x)', '{"x":4}')).must_be_nil
|
3387
|
+
@db.get(jo.path_query_first('$.b.d.e ? (@ > $x)', x: 2)).must_equal 3
|
3388
|
+
@db.get(jo.path_query_first('$.b.d.e ? (@ > $x)', x: 4)).must_be_nil
|
3389
|
+
@db.get(jo.path_query_first('$.b.d.e ? (@ > $x)', {x: 2}, true)).must_equal 3
|
3390
|
+
@db.get(jo.path_query_first('$.b.d.e ? (@ > $x)', {x: 4}, false)).must_be_nil
|
3391
|
+
|
3392
|
+
@db.get(jo.path_query_array('$.b.d.e')).must_equal [3]
|
3393
|
+
@db.get(jo.path_query_array('$.b.d.f')).must_equal []
|
3394
|
+
@db.get(jo.path_query_array('$.b.d.e ? (@ > $x)', '{"x":2}')).must_equal [3]
|
3395
|
+
@db.get(jo.path_query_array('$.b.d.e ? (@ > $x)', '{"x":4}')).must_equal []
|
3396
|
+
@db.get(jo.path_query_array('$.b.d.e ? (@ > $x)', x: 2)).must_equal [3]
|
3397
|
+
@db.get(jo.path_query_array('$.b.d.e ? (@ > $x)', x: 4)).must_equal []
|
3398
|
+
@db.get(jo.path_query_array('$.b.d.e ? (@ > $x)', {x: 2}, true)).must_equal [3]
|
3399
|
+
@db.get(jo.path_query_array('$.b.d.e ? (@ > $x)', {x: 4}, false)).must_equal []
|
3400
|
+
|
3401
|
+
@db.from(jo.path_query('$.b.d.e').as(:a, [:b])).get(:b).must_equal 3
|
3402
|
+
@db.from(jo.path_query('$.b.d.f').as(:a, [:b])).get(:b).must_be_nil
|
3403
|
+
@db.from(jo.path_query('$.b.d.e ? (@ > $x)', '{"x":2}').as(:a, [:b])).get(:b).must_equal 3
|
3404
|
+
@db.from(jo.path_query('$.b.d.e ? (@ > $x)', '{"x":4}').as(:a, [:b])).get(:b).must_be_nil
|
3405
|
+
@db.from(jo.path_query('$.b.d.e ? (@ > $x)', x: 2).as(:a, [:b])).get(:b).must_equal 3
|
3406
|
+
@db.from(jo.path_query('$.b.d.e ? (@ > $x)', x: 4).as(:a, [:b])).get(:b).must_be_nil
|
3407
|
+
@db.from(jo.path_query('$.b.d.e ? (@ > $x)', {x: 2}, true).as(:a, [:b])).get(:b).must_equal 3
|
3408
|
+
@db.from(jo.path_query('$.b.d.e ? (@ > $x)', {x: 4}, false).as(:a, [:b])).get(:b).must_be_nil
|
3409
|
+
end
|
3410
|
+
|
3306
3411
|
Sequel.extension :pg_row_ops
|
3307
3412
|
@db.create_table!(:items) do
|
3308
3413
|
Integer :a
|
@@ -3581,6 +3686,30 @@ describe 'PostgreSQL range types' do
|
|
3581
3686
|
@ds.filter(h).call(:delete, @ra).must_equal 1
|
3582
3687
|
end if uses_pg_or_jdbc
|
3583
3688
|
|
3689
|
+
it 'handle endless ranges' do
|
3690
|
+
@db.get(Sequel.cast(eval('1...'), :int4range)).must_be :==, eval('1...')
|
3691
|
+
@db.get(Sequel.cast(eval('1...'), :int4range)).wont_be :==, eval('2...')
|
3692
|
+
@db.get(Sequel.cast(eval('1...'), :int4range)).wont_be :==, eval('1..')
|
3693
|
+
@db.get(Sequel.cast(eval('2...'), :int4range)).must_be :==, eval('2...')
|
3694
|
+
@db.get(Sequel.cast(eval('2...'), :int4range)).wont_be :==, eval('2..')
|
3695
|
+
@db.get(Sequel.cast(eval('2...'), :int4range)).wont_be :==, eval('1...')
|
3696
|
+
end if RUBY_VERSION >= '2.6'
|
3697
|
+
|
3698
|
+
it 'handle startless ranges' do
|
3699
|
+
@db.get(Sequel.cast(eval('...1'), :int4range)).must_be :==, Sequel::Postgres::PGRange.new(nil, 1, :exclude_begin=>true, :exclude_end=>true, :db_type=>"int4range")
|
3700
|
+
@db.get(Sequel.cast(eval('...1'), :int4range)).wont_be :==, Sequel::Postgres::PGRange.new(nil, 2, :exclude_begin=>true, :exclude_end=>true, :db_type=>"int4range")
|
3701
|
+
@db.get(Sequel.cast(eval('...1'), :int4range)).wont_be :==, Sequel::Postgres::PGRange.new(nil, 1, :exclude_end=>true, :db_type=>"int4range")
|
3702
|
+
@db.get(Sequel.cast(eval('...1'), :int4range)).wont_be :==, Sequel::Postgres::PGRange.new(nil, 1, :exclude_begin=>true, :db_type=>"int4range")
|
3703
|
+
end if RUBY_VERSION >= '2.7'
|
3704
|
+
|
3705
|
+
it 'handle startless ranges' do
|
3706
|
+
@db.get(Sequel.cast(eval('nil...nil'), :int4range)).must_be :==, Sequel::Postgres::PGRange.new(nil, nil, :exclude_begin=>true, :exclude_end=>true, :db_type=>"int4range")
|
3707
|
+
@db.get(Sequel.cast(eval('nil...nil'), :int4range)).wont_be :==, Sequel::Postgres::PGRange.new(nil, nil, :exclude_begin=>true, :db_type=>"int4range")
|
3708
|
+
@db.get(Sequel.cast(eval('nil...nil'), :int4range)).wont_be :==, Sequel::Postgres::PGRange.new(nil, nil, :exclude_end=>true, :db_type=>"int4range")
|
3709
|
+
@db.get(Sequel.cast(eval('nil...nil'), :int4range)).wont_be :==, Sequel::Postgres::PGRange.new(1, nil, :exclude_begin=>true, :db_type=>"int4range")
|
3710
|
+
@db.get(Sequel.cast(eval('nil...nil'), :int4range)).wont_be :==, Sequel::Postgres::PGRange.new(nil, 1, :exclude_begin=>true, :db_type=>"int4range")
|
3711
|
+
end if RUBY_VERSION >= '2.7'
|
3712
|
+
|
3584
3713
|
it 'parse default values for schema' do
|
3585
3714
|
@db.create_table!(:items) do
|
3586
3715
|
Integer :j
|
@@ -4301,4 +4430,49 @@ describe "pg_auto_constraint_validations plugin" do
|
|
4301
4430
|
proc{o.save}.must_raise Sequel::ValidationFailed
|
4302
4431
|
o.errors.must_equal(:i=>['is invalid'], :id=>['is invalid'])
|
4303
4432
|
end
|
4433
|
+
|
4434
|
+
it "should handle dumping cached metadata and loading metadata from cache" do
|
4435
|
+
cache_file = "spec/files/pgacv-#{$$}.cache"
|
4436
|
+
begin
|
4437
|
+
c = Class.new(Sequel::Model)
|
4438
|
+
c.plugin :pg_auto_constraint_validations, :cache_file=>cache_file
|
4439
|
+
c1 = Class.new(c)
|
4440
|
+
def c1.name; 'Foo' end
|
4441
|
+
c1.dataset = DB[:test1]
|
4442
|
+
c2 = Class.new(c)
|
4443
|
+
def c2.name; 'Bar' end
|
4444
|
+
c2.dataset = DB[:test2]
|
4445
|
+
c1.unrestrict_primary_key
|
4446
|
+
c2.unrestrict_primary_key
|
4447
|
+
|
4448
|
+
o = c1.new(:id=>5, :i=>12)
|
4449
|
+
proc{o.save}.must_raise Sequel::ValidationFailed
|
4450
|
+
o.errors.must_equal(:i=>['is invalid'])
|
4451
|
+
o = c2.new(:test2_id=>4, :test1_id=>2)
|
4452
|
+
proc{o.save}.must_raise Sequel::ValidationFailed
|
4453
|
+
o.errors.must_equal(:test1_id=>['is invalid'])
|
4454
|
+
|
4455
|
+
c.dump_pg_auto_constraint_validations_cache
|
4456
|
+
|
4457
|
+
c = Class.new(Sequel::Model)
|
4458
|
+
c.plugin :pg_auto_constraint_validations, :cache_file=>cache_file
|
4459
|
+
c1 = Class.new(c)
|
4460
|
+
def c1.name; 'Foo' end
|
4461
|
+
c1.dataset = DB[:test1]
|
4462
|
+
c2 = Class.new(c)
|
4463
|
+
def c2.name; 'Bar' end
|
4464
|
+
c2.dataset = DB[:test2]
|
4465
|
+
c1.unrestrict_primary_key
|
4466
|
+
c2.unrestrict_primary_key
|
4467
|
+
|
4468
|
+
o = c1.new(:id=>5, :i=>12)
|
4469
|
+
proc{o.save}.must_raise Sequel::ValidationFailed
|
4470
|
+
o.errors.must_equal(:i=>['is invalid'])
|
4471
|
+
o = c2.new(:test2_id=>4, :test1_id=>2)
|
4472
|
+
proc{o.save}.must_raise Sequel::ValidationFailed
|
4473
|
+
o.errors.must_equal(:test1_id=>['is invalid'])
|
4474
|
+
ensure
|
4475
|
+
File.delete(cache_file) if File.file?(cache_file)
|
4476
|
+
end
|
4477
|
+
end
|
4304
4478
|
end if DB.respond_to?(:error_info)
|