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
|
@@ -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
|
|
@@ -26,8 +26,8 @@ module Sequel
|
|
|
26
26
|
# Album.where(Album.published_conditions | {ready: true}).sql
|
|
27
27
|
# # SELECT * FROM albums WHERE ((published IS TRUE) OR (ready IS TRUE))
|
|
28
28
|
module SubsetConditions
|
|
29
|
-
def self.apply(
|
|
30
|
-
|
|
29
|
+
def self.apply(model, &block)
|
|
30
|
+
model.instance_exec do
|
|
31
31
|
@dataset_module_class = Class.new(@dataset_module_class) do
|
|
32
32
|
include DatasetModuleMethods
|
|
33
33
|
end
|
|
@@ -188,13 +188,17 @@ module Sequel
|
|
|
188
188
|
# Sequel will attempt to insert a NULL value into the database, instead of using the
|
|
189
189
|
# database's default.
|
|
190
190
|
# :allow_nil :: Whether to skip the validation if the value is nil.
|
|
191
|
-
# :if :: A symbol (indicating an instance_method) or proc (which is
|
|
191
|
+
# :if :: A symbol (indicating an instance_method) or proc (which is used to define an instance method)
|
|
192
192
|
# skipping this validation if it returns nil or false.
|
|
193
193
|
# :tag :: The tag to use for this validation.
|
|
194
194
|
def validates_each(*atts, &block)
|
|
195
195
|
opts = extract_options!(atts)
|
|
196
196
|
blank_meth = db.method(:blank_object?).to_proc
|
|
197
197
|
blk = if (i = opts[:if]) || (am = opts[:allow_missing]) || (an = opts[:allow_nil]) || (ab = opts[:allow_blank])
|
|
198
|
+
if i.is_a?(Proc)
|
|
199
|
+
i = Plugins.def_sequel_method(self, "validation_class_methods_if", 0, &i)
|
|
200
|
+
end
|
|
201
|
+
|
|
198
202
|
proc do |o,a,v|
|
|
199
203
|
next if i && !validation_if_proc(o, i)
|
|
200
204
|
next if an && Array(v).all?(&:nil?)
|
|
@@ -434,8 +438,6 @@ module Sequel
|
|
|
434
438
|
case i
|
|
435
439
|
when Symbol
|
|
436
440
|
o.get_column_value(i)
|
|
437
|
-
when Proc
|
|
438
|
-
o.instance_exec(&i)
|
|
439
441
|
else
|
|
440
442
|
raise(::Sequel::Error, "invalid value for :if validation option")
|
|
441
443
|
end
|
data/lib/sequel/sql.rb
CHANGED
|
@@ -1086,11 +1086,23 @@ module Sequel
|
|
|
1086
1086
|
def self.from_value_pair(l, r)
|
|
1087
1087
|
case r
|
|
1088
1088
|
when Range
|
|
1089
|
-
|
|
1089
|
+
unless r.begin.nil?
|
|
1090
|
+
begin_expr = new(:>=, l, r.begin)
|
|
1091
|
+
end
|
|
1090
1092
|
unless r.end.nil?
|
|
1091
|
-
|
|
1093
|
+
end_expr = new(r.exclude_end? ? :< : :<=, l, r.end)
|
|
1094
|
+
end
|
|
1095
|
+
if begin_expr
|
|
1096
|
+
if end_expr
|
|
1097
|
+
new(:AND, begin_expr, end_expr)
|
|
1098
|
+
else
|
|
1099
|
+
begin_expr
|
|
1100
|
+
end
|
|
1101
|
+
elsif end_expr
|
|
1102
|
+
end_expr
|
|
1103
|
+
else
|
|
1104
|
+
new(:'=', 1, 1)
|
|
1092
1105
|
end
|
|
1093
|
-
expr
|
|
1094
1106
|
when ::Array
|
|
1095
1107
|
r = r.dup.freeze unless r.frozen?
|
|
1096
1108
|
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 = 24
|
|
10
10
|
|
|
11
11
|
# The tiny version of Sequel. Usually 0, only bumped for bugfix
|
|
12
12
|
# releases that fix regressions from previous versions.
|
data/spec/adapters/mssql_spec.rb
CHANGED
|
@@ -40,6 +40,30 @@ describe "A MSSQL database" do
|
|
|
40
40
|
end
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
+
describe "MSSQL decimal locale handling" do
|
|
44
|
+
before do
|
|
45
|
+
@locale = WIN32OLE.locale
|
|
46
|
+
@decimal = BigDecimal('1234.56')
|
|
47
|
+
end
|
|
48
|
+
after do
|
|
49
|
+
WIN32OLE.locale = @locale
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "should work with current locale" do
|
|
53
|
+
DB.get(Sequel.cast(@decimal, 'decimal(16,4)').as(:v)).must_equal @decimal
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it "should work with 1031 locale" do
|
|
57
|
+
WIN32OLE.locale = 1031
|
|
58
|
+
DB.get(Sequel.cast(@decimal, 'decimal(16,4)').as(:v)).must_equal @decimal
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it "should work with 1033 locale" do
|
|
62
|
+
WIN32OLE.locale = 1033
|
|
63
|
+
DB.get(Sequel.cast(@decimal, 'decimal(16,4)').as(:v)).must_equal @decimal
|
|
64
|
+
end
|
|
65
|
+
end if DB.adapter_scheme == :ado
|
|
66
|
+
|
|
43
67
|
describe "MSSQL" do
|
|
44
68
|
before(:all) do
|
|
45
69
|
@db = DB
|
data/spec/adapters/mysql_spec.rb
CHANGED
|
@@ -551,11 +551,6 @@ describe "A MySQL database" do
|
|
|
551
551
|
db = Sequel.connect(DB.opts.merge(:read_timeout=>22342))
|
|
552
552
|
db.test_connection
|
|
553
553
|
end
|
|
554
|
-
|
|
555
|
-
it "should accept a connect_timeout option when connecting" do
|
|
556
|
-
db = Sequel.connect(DB.opts.merge(:connect_timeout=>22342))
|
|
557
|
-
db.test_connection
|
|
558
|
-
end
|
|
559
554
|
end
|
|
560
555
|
|
|
561
556
|
describe "MySQL foreign key support" do
|
|
@@ -104,6 +104,24 @@ describe "PostgreSQL", '#create_table' do
|
|
|
104
104
|
@db.check_constraints(:tmp_dolls).must_equal(:ic=>{:definition=>"CHECK ((i > 2))", :columns=>[:i]}, :jc=>{:definition=>"CHECK ((j > 2))", :columns=>[:j]}, :ijc=>{:definition=>"CHECK (((i - j) > 2))", :columns=>[:i, :j]})
|
|
105
105
|
end
|
|
106
106
|
|
|
107
|
+
it "should have #check_constraints return check constraints where columns are unknown" do
|
|
108
|
+
begin
|
|
109
|
+
@db.create_table(:tmp_dolls) do
|
|
110
|
+
Integer :i
|
|
111
|
+
Integer :j
|
|
112
|
+
end
|
|
113
|
+
@db.run "CREATE OR REPLACE FUNCTION valid_tmp_dolls(t1 tmp_dolls) RETURNS boolean AS 'SELECT false' LANGUAGE SQL;"
|
|
114
|
+
@db.alter_table(:tmp_dolls) do
|
|
115
|
+
add_constraint(:valid_tmp_dolls, Sequel.function(:valid_tmp_dolls, :tmp_dolls))
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
@db.check_constraints(:tmp_dolls).must_equal(:valid_tmp_dolls=>{:definition=>"CHECK (valid_tmp_dolls(tmp_dolls.*))", :columns=>[]})
|
|
119
|
+
ensure
|
|
120
|
+
@db.run "ALTER TABLE tmp_dolls DROP CONSTRAINT IF EXISTS valid_tmp_dolls"
|
|
121
|
+
@db.run "DROP FUNCTION IF EXISTS valid_tmp_dolls(tmp_dolls)"
|
|
122
|
+
end
|
|
123
|
+
end if DB.server_version >= 90000
|
|
124
|
+
|
|
107
125
|
it "should not allow to pass both :temp and :unlogged" do
|
|
108
126
|
proc do
|
|
109
127
|
@db.create_table(:temp_unlogged_dolls, :temp => true, :unlogged => true){text :name}
|
|
@@ -209,6 +227,24 @@ describe "PostgreSQL", '#create_table' do
|
|
|
209
227
|
@db.convert_serial_to_identity(:tmp_dolls, :column=>:id)
|
|
210
228
|
end if DB.server_version >= 100002 && DB.get{current_setting('is_superuser')} == 'on'
|
|
211
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
|
+
|
|
212
248
|
it "should support pg_loose_count extension" do
|
|
213
249
|
@db.extension :pg_loose_count
|
|
214
250
|
@db.create_table(:tmp_dolls){text :name}
|
|
@@ -334,6 +370,26 @@ describe "PostgreSQL", 'INSERT ON CONFLICT' do
|
|
|
334
370
|
end
|
|
335
371
|
end if DB.server_version >= 90500
|
|
336
372
|
|
|
373
|
+
describe "A PostgreSQL database" do
|
|
374
|
+
before do
|
|
375
|
+
@db = DB
|
|
376
|
+
@db.create_table!(:cte_test){Integer :id}
|
|
377
|
+
end
|
|
378
|
+
after do
|
|
379
|
+
@db.drop_table?(:cte_test)
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
it "should give correct results for WITH AS [NOT] MATERIALIZED" do
|
|
383
|
+
@ds = @db[:cte_test]
|
|
384
|
+
@ds.insert(1)
|
|
385
|
+
@ds.insert(2)
|
|
386
|
+
|
|
387
|
+
@db[:t].with(:t, @ds, :materialized=>nil).order(:id).map(:id).must_equal [1, 2]
|
|
388
|
+
@db[:t].with(:t, @ds, :materialized=>true).order(:id).map(:id).must_equal [1, 2]
|
|
389
|
+
@db[:t].with(:t, @ds, :materialized=>false).order(:id).map(:id).must_equal [1, 2]
|
|
390
|
+
end
|
|
391
|
+
end if DB.server_version >= 120000
|
|
392
|
+
|
|
337
393
|
describe "A PostgreSQL database" do
|
|
338
394
|
before(:all) do
|
|
339
395
|
@db = DB
|
|
@@ -2926,6 +2982,8 @@ describe 'PostgreSQL json type' do
|
|
|
2926
2982
|
@h = {'a'=>'b', '1'=>[3, 4, 5]}
|
|
2927
2983
|
end
|
|
2928
2984
|
after do
|
|
2985
|
+
@db.wrap_json_primitives = nil
|
|
2986
|
+
@db.typecast_json_strings = nil
|
|
2929
2987
|
@db.drop_table?(:items)
|
|
2930
2988
|
end
|
|
2931
2989
|
|
|
@@ -2933,9 +2991,12 @@ describe 'PostgreSQL json type' do
|
|
|
2933
2991
|
json_types << :jsonb if DB.server_version >= 90400
|
|
2934
2992
|
json_types.each do |json_type|
|
|
2935
2993
|
json_array_type = "#{json_type}[]"
|
|
2936
|
-
pg_json =
|
|
2994
|
+
pg_json = Sequel.method(:"pg_#{json_type}")
|
|
2995
|
+
pg_json_wrap = Sequel.method(:"pg_#{json_type}_wrap")
|
|
2937
2996
|
hash_class = json_type == :jsonb ? Sequel::Postgres::JSONBHash : Sequel::Postgres::JSONHash
|
|
2938
2997
|
array_class = json_type == :jsonb ? Sequel::Postgres::JSONBArray : Sequel::Postgres::JSONArray
|
|
2998
|
+
str_class = json_type == :jsonb ? Sequel::Postgres::JSONBString : Sequel::Postgres::JSONString
|
|
2999
|
+
object_class = json_type == :jsonb ? Sequel::Postgres::JSONBObject : Sequel::Postgres::JSONObject
|
|
2939
3000
|
|
|
2940
3001
|
it 'insert and retrieve json values' do
|
|
2941
3002
|
@db.create_table!(:items){column :j, json_type}
|
|
@@ -2965,6 +3026,44 @@ describe 'PostgreSQL json type' do
|
|
|
2965
3026
|
@ds.all.must_equal rs
|
|
2966
3027
|
end
|
|
2967
3028
|
|
|
3029
|
+
it 'insert and retrieve json primitive values' do
|
|
3030
|
+
@db.create_table!(:items){column :j, json_type}
|
|
3031
|
+
['str', 1, 2.5, nil, true, false].each do |rv|
|
|
3032
|
+
@ds.delete
|
|
3033
|
+
@ds.insert(pg_json_wrap.call(rv))
|
|
3034
|
+
@ds.count.must_equal 1
|
|
3035
|
+
rs = @ds.all
|
|
3036
|
+
v = rs.first[:j]
|
|
3037
|
+
v.class.must_equal(rv.class)
|
|
3038
|
+
if rv.nil?
|
|
3039
|
+
v.must_be_nil
|
|
3040
|
+
else
|
|
3041
|
+
v.must_equal rv
|
|
3042
|
+
end
|
|
3043
|
+
end
|
|
3044
|
+
|
|
3045
|
+
@db.wrap_json_primitives = true
|
|
3046
|
+
['str', 1, 2.5, nil, true, false].each do |rv|
|
|
3047
|
+
@ds.delete
|
|
3048
|
+
@ds.insert(pg_json_wrap.call(rv))
|
|
3049
|
+
@ds.count.must_equal 1
|
|
3050
|
+
rs = @ds.all
|
|
3051
|
+
v = rs.first[:j]
|
|
3052
|
+
v.class.ancestors.must_include(object_class)
|
|
3053
|
+
v.__getobj__.must_be_kind_of(rv.class)
|
|
3054
|
+
if rv.nil?
|
|
3055
|
+
v.must_be_nil
|
|
3056
|
+
v.__getobj__.must_be_nil
|
|
3057
|
+
else
|
|
3058
|
+
v.must_equal rv
|
|
3059
|
+
v.__getobj__.must_equal rv
|
|
3060
|
+
end
|
|
3061
|
+
@ds.delete
|
|
3062
|
+
@ds.insert(rs.first)
|
|
3063
|
+
@ds.all[0][:j].must_equal rs[0][:j]
|
|
3064
|
+
end
|
|
3065
|
+
end
|
|
3066
|
+
|
|
2968
3067
|
it 'insert and retrieve json[] values' do
|
|
2969
3068
|
@db.create_table!(:items){column :j, json_array_type}
|
|
2970
3069
|
j = Sequel.pg_array([pg_json.call('a'=>1), pg_json.call(['b', 2])])
|
|
@@ -2981,16 +3080,122 @@ describe 'PostgreSQL json type' do
|
|
|
2981
3080
|
@ds.all.must_equal rs
|
|
2982
3081
|
end
|
|
2983
3082
|
|
|
3083
|
+
it 'insert and retrieve json[] values with json primitives' do
|
|
3084
|
+
@db.create_table!(:items){column :j, json_array_type}
|
|
3085
|
+
raw = ['str', 1, 2.5, nil, true, false]
|
|
3086
|
+
j = Sequel.pg_array(raw.map(&pg_json_wrap), json_type)
|
|
3087
|
+
@ds.insert(j)
|
|
3088
|
+
@ds.count.must_equal 1
|
|
3089
|
+
rs = @ds.all
|
|
3090
|
+
v = rs.first[:j]
|
|
3091
|
+
v.class.must_equal(Sequel::Postgres::PGArray)
|
|
3092
|
+
v.to_a.must_be_kind_of(Array)
|
|
3093
|
+
v.map(&:class).must_equal raw.map(&:class)
|
|
3094
|
+
v.must_equal raw
|
|
3095
|
+
v.to_a.must_equal raw
|
|
3096
|
+
|
|
3097
|
+
@db.wrap_json_primitives = true
|
|
3098
|
+
j = Sequel.pg_array(raw.map(&pg_json_wrap), json_type)
|
|
3099
|
+
@ds.insert(j)
|
|
3100
|
+
rs = @ds.all
|
|
3101
|
+
v = rs.first[:j]
|
|
3102
|
+
v.class.must_equal(Sequel::Postgres::PGArray)
|
|
3103
|
+
v.to_a.must_be_kind_of(Array)
|
|
3104
|
+
v.map(&:class).each{|c| c.ancestors.must_include(object_class)}
|
|
3105
|
+
[v, v.to_a].each do |v0|
|
|
3106
|
+
v0.zip(raw) do |v1, r1|
|
|
3107
|
+
if r1.nil?
|
|
3108
|
+
v1.must_be_nil
|
|
3109
|
+
v1.__getobj__.must_be_nil
|
|
3110
|
+
else
|
|
3111
|
+
v1.must_equal r1
|
|
3112
|
+
v1.__getobj__.must_equal r1
|
|
3113
|
+
end
|
|
3114
|
+
end
|
|
3115
|
+
end
|
|
3116
|
+
@ds.delete
|
|
3117
|
+
@ds.insert(rs.first)
|
|
3118
|
+
@ds.all[0][:j].zip(rs[0][:j]) do |v1, r1|
|
|
3119
|
+
if v1.__getobj__.nil?
|
|
3120
|
+
v1.must_be_nil
|
|
3121
|
+
v1.__getobj__.must_be_nil
|
|
3122
|
+
else
|
|
3123
|
+
v1.must_equal r1
|
|
3124
|
+
v1.must_equal r1.__getobj__
|
|
3125
|
+
v1.__getobj__.must_equal r1
|
|
3126
|
+
v1.__getobj__.must_equal r1.__getobj__
|
|
3127
|
+
end
|
|
3128
|
+
end
|
|
3129
|
+
end
|
|
3130
|
+
|
|
2984
3131
|
it 'with models' do
|
|
2985
3132
|
@db.create_table!(:items) do
|
|
2986
3133
|
primary_key :id
|
|
2987
3134
|
column :h, json_type
|
|
2988
3135
|
end
|
|
2989
3136
|
c = Class.new(Sequel::Model(@db[:items]))
|
|
3137
|
+
c.create(:h=>@h).h.must_equal @h
|
|
3138
|
+
c.create(:h=>@a).h.must_equal @a
|
|
2990
3139
|
c.create(:h=>pg_json.call(@h)).h.must_equal @h
|
|
2991
3140
|
c.create(:h=>pg_json.call(@a)).h.must_equal @a
|
|
2992
3141
|
end
|
|
2993
3142
|
|
|
3143
|
+
it 'with models with json primitives' do
|
|
3144
|
+
@db.create_table!(:items) do
|
|
3145
|
+
primary_key :id
|
|
3146
|
+
column :h, json_type
|
|
3147
|
+
end
|
|
3148
|
+
c = Class.new(Sequel::Model(@db[:items]))
|
|
3149
|
+
|
|
3150
|
+
['str', 1, 2.5, nil, true, false].each do |v|
|
|
3151
|
+
@db.wrap_json_primitives = nil
|
|
3152
|
+
cv = c[c.insert(:h=>pg_json_wrap.call(v))]
|
|
3153
|
+
cv.h.class.ancestors.wont_include(object_class)
|
|
3154
|
+
if v.nil?
|
|
3155
|
+
cv.h.must_be_nil
|
|
3156
|
+
else
|
|
3157
|
+
cv.h.must_equal v
|
|
3158
|
+
end
|
|
3159
|
+
|
|
3160
|
+
@db.wrap_json_primitives = true
|
|
3161
|
+
cv.refresh
|
|
3162
|
+
cv.h.class.ancestors.must_include(object_class)
|
|
3163
|
+
cv.save
|
|
3164
|
+
cv.refresh
|
|
3165
|
+
cv.h.class
|
|
3166
|
+
|
|
3167
|
+
if v.nil?
|
|
3168
|
+
cv.h.must_be_nil
|
|
3169
|
+
else
|
|
3170
|
+
cv.h.must_equal v
|
|
3171
|
+
end
|
|
3172
|
+
|
|
3173
|
+
c.new(:h=>cv.h).h.class.ancestors.must_include(object_class)
|
|
3174
|
+
end
|
|
3175
|
+
|
|
3176
|
+
v = c.new(:h=>'{}').h
|
|
3177
|
+
v.class.must_equal hash_class
|
|
3178
|
+
v.must_equal({})
|
|
3179
|
+
@db.typecast_json_strings = true
|
|
3180
|
+
v = c.new(:h=>'{}').h
|
|
3181
|
+
v.class.must_equal str_class
|
|
3182
|
+
v.must_equal '{}'
|
|
3183
|
+
|
|
3184
|
+
c.new(:h=>'str').h.class.ancestors.must_include(object_class)
|
|
3185
|
+
c.new(:h=>'str').h.must_equal 'str'
|
|
3186
|
+
c.new(:h=>1).h.class.ancestors.must_include(object_class)
|
|
3187
|
+
c.new(:h=>1).h.must_equal 1
|
|
3188
|
+
c.new(:h=>2.5).h.class.ancestors.must_include(object_class)
|
|
3189
|
+
c.new(:h=>2.5).h.must_equal 2.5
|
|
3190
|
+
c.new(:h=>true).h.class.ancestors.must_include(object_class)
|
|
3191
|
+
c.new(:h=>true).h.must_equal true
|
|
3192
|
+
c.new(:h=>false).h.class.ancestors.must_include(object_class)
|
|
3193
|
+
c.new(:h=>false).h.must_equal false
|
|
3194
|
+
|
|
3195
|
+
c.new(:h=>nil).h.class.ancestors.wont_include(object_class)
|
|
3196
|
+
c.new(:h=>nil).h.must_be_nil
|
|
3197
|
+
end
|
|
3198
|
+
|
|
2994
3199
|
it 'with empty json default values and defaults_setter plugin' do
|
|
2995
3200
|
@db.create_table!(:items) do
|
|
2996
3201
|
column :h, json_type, :default=>hash_class.new({})
|
|
@@ -3025,6 +3230,36 @@ describe 'PostgreSQL json type' do
|
|
|
3025
3230
|
@ds.get(:i).must_equal j
|
|
3026
3231
|
end if uses_pg_or_jdbc
|
|
3027
3232
|
|
|
3233
|
+
it 'use json primitives in bound variables' do
|
|
3234
|
+
@db.create_table!(:items){column :i, json_type}
|
|
3235
|
+
@db.wrap_json_primitives = true
|
|
3236
|
+
raw = ['str', 1, 2.5, nil, true, false]
|
|
3237
|
+
raw.each do |v|
|
|
3238
|
+
@ds.delete
|
|
3239
|
+
@ds.call(:insert, {:i=>@db.get(pg_json_wrap.call(v))}, {:i=>:$i})
|
|
3240
|
+
rv = @ds.get(:i)
|
|
3241
|
+
rv.class.ancestors.must_include(object_class)
|
|
3242
|
+
if v.nil?
|
|
3243
|
+
rv.must_be_nil
|
|
3244
|
+
else
|
|
3245
|
+
rv.must_equal v
|
|
3246
|
+
end
|
|
3247
|
+
end
|
|
3248
|
+
|
|
3249
|
+
@db.create_table!(:items){column :i, json_array_type}
|
|
3250
|
+
j = Sequel.pg_array(raw.map(&pg_json_wrap), json_type)
|
|
3251
|
+
@ds.call(:insert, {:i=>j}, {:i=>:$i})
|
|
3252
|
+
@ds.all[0][:i].zip(raw) do |v1, r1|
|
|
3253
|
+
if v1.__getobj__.nil?
|
|
3254
|
+
v1.must_be_nil
|
|
3255
|
+
v1.__getobj__.must_be_nil
|
|
3256
|
+
else
|
|
3257
|
+
v1.must_equal r1
|
|
3258
|
+
v1.__getobj__.must_equal r1
|
|
3259
|
+
end
|
|
3260
|
+
end
|
|
3261
|
+
end if uses_pg_or_jdbc
|
|
3262
|
+
|
|
3028
3263
|
it 'operations/functions with pg_json_ops' do
|
|
3029
3264
|
Sequel.extension :pg_json_ops
|
|
3030
3265
|
jo = pg_json.call('a'=>1, 'b'=>{'c'=>2, 'd'=>{'e'=>3}}).op
|
|
@@ -3384,6 +3619,30 @@ describe 'PostgreSQL range types' do
|
|
|
3384
3619
|
@ds.filter(h).call(:delete, @ra).must_equal 1
|
|
3385
3620
|
end if uses_pg_or_jdbc
|
|
3386
3621
|
|
|
3622
|
+
it 'handle endless ranges' do
|
|
3623
|
+
@db.get(Sequel.cast(eval('1...'), :int4range)).must_be :==, eval('1...')
|
|
3624
|
+
@db.get(Sequel.cast(eval('1...'), :int4range)).wont_be :==, eval('2...')
|
|
3625
|
+
@db.get(Sequel.cast(eval('1...'), :int4range)).wont_be :==, eval('1..')
|
|
3626
|
+
@db.get(Sequel.cast(eval('2...'), :int4range)).must_be :==, eval('2...')
|
|
3627
|
+
@db.get(Sequel.cast(eval('2...'), :int4range)).wont_be :==, eval('2..')
|
|
3628
|
+
@db.get(Sequel.cast(eval('2...'), :int4range)).wont_be :==, eval('1...')
|
|
3629
|
+
end if RUBY_VERSION >= '2.6'
|
|
3630
|
+
|
|
3631
|
+
it 'handle startless ranges' do
|
|
3632
|
+
@db.get(Sequel.cast(eval('...1'), :int4range)).must_be :==, Sequel::Postgres::PGRange.new(nil, 1, :exclude_begin=>true, :exclude_end=>true, :db_type=>"int4range")
|
|
3633
|
+
@db.get(Sequel.cast(eval('...1'), :int4range)).wont_be :==, Sequel::Postgres::PGRange.new(nil, 2, :exclude_begin=>true, :exclude_end=>true, :db_type=>"int4range")
|
|
3634
|
+
@db.get(Sequel.cast(eval('...1'), :int4range)).wont_be :==, Sequel::Postgres::PGRange.new(nil, 1, :exclude_end=>true, :db_type=>"int4range")
|
|
3635
|
+
@db.get(Sequel.cast(eval('...1'), :int4range)).wont_be :==, Sequel::Postgres::PGRange.new(nil, 1, :exclude_begin=>true, :db_type=>"int4range")
|
|
3636
|
+
end if RUBY_VERSION >= '2.7'
|
|
3637
|
+
|
|
3638
|
+
it 'handle startless ranges' do
|
|
3639
|
+
@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")
|
|
3640
|
+
@db.get(Sequel.cast(eval('nil...nil'), :int4range)).wont_be :==, Sequel::Postgres::PGRange.new(nil, nil, :exclude_begin=>true, :db_type=>"int4range")
|
|
3641
|
+
@db.get(Sequel.cast(eval('nil...nil'), :int4range)).wont_be :==, Sequel::Postgres::PGRange.new(nil, nil, :exclude_end=>true, :db_type=>"int4range")
|
|
3642
|
+
@db.get(Sequel.cast(eval('nil...nil'), :int4range)).wont_be :==, Sequel::Postgres::PGRange.new(1, nil, :exclude_begin=>true, :db_type=>"int4range")
|
|
3643
|
+
@db.get(Sequel.cast(eval('nil...nil'), :int4range)).wont_be :==, Sequel::Postgres::PGRange.new(nil, 1, :exclude_begin=>true, :db_type=>"int4range")
|
|
3644
|
+
end if RUBY_VERSION >= '2.7'
|
|
3645
|
+
|
|
3387
3646
|
it 'parse default values for schema' do
|
|
3388
3647
|
@db.create_table!(:items) do
|
|
3389
3648
|
Integer :j
|
|
@@ -3988,6 +4247,10 @@ describe "pg_auto_constraint_validations plugin" do
|
|
|
3988
4247
|
constraint :valid_i, Sequel[:i] < 10
|
|
3989
4248
|
constraint(:valid_i_id, Sequel[:i] + Sequel[:id] < 20)
|
|
3990
4249
|
end
|
|
4250
|
+
@db.run "CREATE OR REPLACE FUNCTION valid_test1(t1 test1) RETURNS boolean AS 'SELECT t1.i != -100' LANGUAGE SQL;"
|
|
4251
|
+
@db.alter_table(:test1) do
|
|
4252
|
+
add_constraint(:valid_test1, Sequel.function(:valid_test1, :test1))
|
|
4253
|
+
end
|
|
3991
4254
|
@db.create_table!(:test2) do
|
|
3992
4255
|
Integer :test2_id, :primary_key=>true
|
|
3993
4256
|
foreign_key :test1_id, :test1
|
|
@@ -4008,6 +4271,8 @@ describe "pg_auto_constraint_validations plugin" do
|
|
|
4008
4271
|
@c2.insert(:test2_id=>3, :test1_id=>1)
|
|
4009
4272
|
end
|
|
4010
4273
|
after(:all) do
|
|
4274
|
+
@db.run "ALTER TABLE test1 DROP CONSTRAINT IF EXISTS valid_test1"
|
|
4275
|
+
@db.run "DROP FUNCTION IF EXISTS valid_test1(test1)"
|
|
4011
4276
|
@db.drop_table?(:test2, :test1)
|
|
4012
4277
|
end
|
|
4013
4278
|
|
|
@@ -4017,6 +4282,14 @@ describe "pg_auto_constraint_validations plugin" do
|
|
|
4017
4282
|
o.errors.must_equal(:i=>['is invalid'])
|
|
4018
4283
|
end
|
|
4019
4284
|
|
|
4285
|
+
it "should handle check constraint failures where the columns are unknown, if columns are explicitly specified" do
|
|
4286
|
+
o = @c1.new(:id=>5, :i=>-100)
|
|
4287
|
+
proc{o.save}.must_raise Sequel::CheckConstraintViolation
|
|
4288
|
+
@c1.pg_auto_constraint_validation_override(:valid_test1, :i, "should not be -100")
|
|
4289
|
+
proc{o.save}.must_raise Sequel::ValidationFailed
|
|
4290
|
+
o.errors.must_equal(:i=>['should not be -100'])
|
|
4291
|
+
end
|
|
4292
|
+
|
|
4020
4293
|
it "should handle check constraint failures as validation errors when updating" do
|
|
4021
4294
|
o = @c1.new(:id=>5, :i=>3)
|
|
4022
4295
|
o.save
|
|
@@ -4090,4 +4363,49 @@ describe "pg_auto_constraint_validations plugin" do
|
|
|
4090
4363
|
proc{o.save}.must_raise Sequel::ValidationFailed
|
|
4091
4364
|
o.errors.must_equal(:i=>['is invalid'], :id=>['is invalid'])
|
|
4092
4365
|
end
|
|
4366
|
+
|
|
4367
|
+
it "should handle dumping cached metadata and loading metadata from cache" do
|
|
4368
|
+
cache_file = "spec/files/pgacv-#{$$}.cache"
|
|
4369
|
+
begin
|
|
4370
|
+
c = Class.new(Sequel::Model)
|
|
4371
|
+
c.plugin :pg_auto_constraint_validations, :cache_file=>cache_file
|
|
4372
|
+
c1 = Class.new(c)
|
|
4373
|
+
def c1.name; 'Foo' end
|
|
4374
|
+
c1.dataset = DB[:test1]
|
|
4375
|
+
c2 = Class.new(c)
|
|
4376
|
+
def c2.name; 'Bar' end
|
|
4377
|
+
c2.dataset = DB[:test2]
|
|
4378
|
+
c1.unrestrict_primary_key
|
|
4379
|
+
c2.unrestrict_primary_key
|
|
4380
|
+
|
|
4381
|
+
o = c1.new(:id=>5, :i=>12)
|
|
4382
|
+
proc{o.save}.must_raise Sequel::ValidationFailed
|
|
4383
|
+
o.errors.must_equal(:i=>['is invalid'])
|
|
4384
|
+
o = c2.new(:test2_id=>4, :test1_id=>2)
|
|
4385
|
+
proc{o.save}.must_raise Sequel::ValidationFailed
|
|
4386
|
+
o.errors.must_equal(:test1_id=>['is invalid'])
|
|
4387
|
+
|
|
4388
|
+
c.dump_pg_auto_constraint_validations_cache
|
|
4389
|
+
|
|
4390
|
+
c = Class.new(Sequel::Model)
|
|
4391
|
+
c.plugin :pg_auto_constraint_validations, :cache_file=>cache_file
|
|
4392
|
+
c1 = Class.new(c)
|
|
4393
|
+
def c1.name; 'Foo' end
|
|
4394
|
+
c1.dataset = DB[:test1]
|
|
4395
|
+
c2 = Class.new(c)
|
|
4396
|
+
def c2.name; 'Bar' end
|
|
4397
|
+
c2.dataset = DB[:test2]
|
|
4398
|
+
c1.unrestrict_primary_key
|
|
4399
|
+
c2.unrestrict_primary_key
|
|
4400
|
+
|
|
4401
|
+
o = c1.new(:id=>5, :i=>12)
|
|
4402
|
+
proc{o.save}.must_raise Sequel::ValidationFailed
|
|
4403
|
+
o.errors.must_equal(:i=>['is invalid'])
|
|
4404
|
+
o = c2.new(:test2_id=>4, :test1_id=>2)
|
|
4405
|
+
proc{o.save}.must_raise Sequel::ValidationFailed
|
|
4406
|
+
o.errors.must_equal(:test1_id=>['is invalid'])
|
|
4407
|
+
ensure
|
|
4408
|
+
File.delete(cache_file) if File.file?(cache_file)
|
|
4409
|
+
end
|
|
4410
|
+
end
|
|
4093
4411
|
end if DB.respond_to?(:error_info)
|