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
|
@@ -59,7 +59,14 @@ module Sequel
|
|
|
59
59
|
|
|
60
60
|
# Yield every block related to the given hook.
|
|
61
61
|
def hook_blocks(hook)
|
|
62
|
-
|
|
62
|
+
# SEQUEL6: Remove
|
|
63
|
+
Sequel::Deprecation.deprecate("The hook_blocks class method in the hook_class_methods plugin is deprecated and will be removed in Sequel 6.")
|
|
64
|
+
@hooks[hook].each{|_,v,_| yield v}
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Yield every method related to the given hook.
|
|
68
|
+
def hook_methods_for(hook)
|
|
69
|
+
@hooks[hook].each{|_,_,m| yield m}
|
|
63
70
|
end
|
|
64
71
|
|
|
65
72
|
Plugins.inherited_instance_variables(self, :@hooks=>:hash_dup)
|
|
@@ -75,23 +82,28 @@ module Sequel
|
|
|
75
82
|
# Allow calling private hook methods
|
|
76
83
|
block = proc {send(tag)}
|
|
77
84
|
end
|
|
85
|
+
|
|
78
86
|
h = @hooks[hook]
|
|
87
|
+
|
|
79
88
|
if tag && (old = h.find{|x| x[0] == tag})
|
|
80
89
|
old[1] = block
|
|
90
|
+
Plugins.def_sequel_method(self, old[2], 0, &block)
|
|
81
91
|
else
|
|
92
|
+
meth = Plugins.def_sequel_method(self, "validation_class_methods_#{hook}", 0, &block)
|
|
82
93
|
if hook.to_s =~ /^before/
|
|
83
|
-
h.unshift([tag,block])
|
|
94
|
+
h.unshift([tag, block, meth])
|
|
84
95
|
else
|
|
85
|
-
h << [tag, block]
|
|
96
|
+
h << [tag, block, meth]
|
|
86
97
|
end
|
|
87
98
|
end
|
|
88
99
|
end
|
|
89
100
|
end
|
|
90
101
|
|
|
91
102
|
module InstanceMethods
|
|
92
|
-
|
|
103
|
+
# hook methods are private
|
|
104
|
+
[:before_create, :before_update, :before_validation, :before_save, :before_destroy].each{|h| class_eval("def #{h}; model.hook_methods_for(:#{h}){|m| send(m)}; super end", __FILE__, __LINE__)}
|
|
93
105
|
|
|
94
|
-
[:after_create, :after_update, :after_validation, :after_save, :after_destroy].each{|h| class_eval("def #{h}; super; model.
|
|
106
|
+
[:after_create, :after_update, :after_validation, :after_save, :after_destroy].each{|h| class_eval("def #{h}; super; model.hook_methods_for(:#{h}){|m| send(m)}; end", __FILE__, __LINE__)}
|
|
95
107
|
end
|
|
96
108
|
end
|
|
97
109
|
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
module Sequel
|
|
4
|
+
module Plugins
|
|
5
|
+
# The insert_conflict plugin allows handling conflicts due to unique
|
|
6
|
+
# constraints when saving new model instance, using the INSERT ON CONFLICT
|
|
7
|
+
# support in PostgreSQL 9.5+ and SQLite 3.24.0+. Example:
|
|
8
|
+
#
|
|
9
|
+
# class Album < Sequel::Model
|
|
10
|
+
# plugin :insert_conflict
|
|
11
|
+
# end
|
|
12
|
+
#
|
|
13
|
+
# Album.new(name: 'Foo', copies_sold: 1000).
|
|
14
|
+
# insert_conflict(
|
|
15
|
+
# target: :name,
|
|
16
|
+
# update: {copies_sold: Sequel[:excluded][:b]}
|
|
17
|
+
# ).
|
|
18
|
+
# save
|
|
19
|
+
#
|
|
20
|
+
# This example will try to insert the album, but if there is an existing
|
|
21
|
+
# album with the name 'Foo', this will update the copies_sold attribute
|
|
22
|
+
# for that album. See the PostgreSQL and SQLite adapter documention for
|
|
23
|
+
# the options you can pass to the insert_conflict method.
|
|
24
|
+
#
|
|
25
|
+
# Usage:
|
|
26
|
+
#
|
|
27
|
+
# # Make all model subclasses support insert_conflict
|
|
28
|
+
# Sequel::Model.plugin :insert_conflict
|
|
29
|
+
#
|
|
30
|
+
# # Make the Album class support insert_conflict
|
|
31
|
+
# Album.plugin :insert_conflict
|
|
32
|
+
module InsertConflict
|
|
33
|
+
def self.configure(model)
|
|
34
|
+
model.instance_exec do
|
|
35
|
+
if @dataset && !@dataset.respond_to?(:insert_conflict)
|
|
36
|
+
raise Error, "#{self}'s dataset does not support insert_conflict"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
module InstanceMethods
|
|
42
|
+
# Set the insert_conflict options to pass to the dataset when inserting.
|
|
43
|
+
def insert_conflict(opts=OPTS)
|
|
44
|
+
raise Error, "Model#insert_conflict is only supported on new model instances" unless new?
|
|
45
|
+
@insert_conflict_opts = opts
|
|
46
|
+
self
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
# Set the dataset used for inserting to use INSERT ON CONFLICT
|
|
52
|
+
# Model#insert_conflict has been called on the instance previously.
|
|
53
|
+
def _insert_dataset
|
|
54
|
+
ds = super
|
|
55
|
+
|
|
56
|
+
if @insert_conflict_opts
|
|
57
|
+
ds = ds.insert_conflict(@insert_conflict_opts)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
ds
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Disable the use of prepared insert statements, as they are not compatible
|
|
64
|
+
# with this plugin.
|
|
65
|
+
def use_prepared_statements_for?(type)
|
|
66
|
+
return false if type == :insert || type == :insert_select
|
|
67
|
+
super if defined?(super)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -29,8 +29,8 @@ module Sequel
|
|
|
29
29
|
# # SELECT * FROM albums WHERE (published IS NOT TRUE)
|
|
30
30
|
#
|
|
31
31
|
module InvertedSubsets
|
|
32
|
-
def self.apply(
|
|
33
|
-
|
|
32
|
+
def self.apply(model, &block)
|
|
33
|
+
model.instance_exec do
|
|
34
34
|
@dataset_module_class = Class.new(@dataset_module_class) do
|
|
35
35
|
include DatasetModuleMethods
|
|
36
36
|
if block
|
|
@@ -22,8 +22,9 @@ module Sequel
|
|
|
22
22
|
# This plugin is not intended as a replacement for other validations,
|
|
23
23
|
# it is intended as a last resort. The purpose of validations is to provide nice
|
|
24
24
|
# error messages for the user, and the error messages generated by this plugin are
|
|
25
|
-
# fairly generic. The error messages can be customized
|
|
26
|
-
#
|
|
25
|
+
# fairly generic by default. The error messages can be customized per constraint type
|
|
26
|
+
# using the :messages plugin option, and individually per constraint using
|
|
27
|
+
# +pg_auto_constraint_validation_override+ (see below).
|
|
27
28
|
#
|
|
28
29
|
# This plugin only works on the postgres adapter when using the pg 0.16+ driver,
|
|
29
30
|
# PostgreSQL 9.3+ server, and PostgreSQL 9.3+ client library (libpq). In other cases
|
|
@@ -37,6 +38,38 @@ module Sequel
|
|
|
37
38
|
# rescue Sequel::ValidationFailed
|
|
38
39
|
# album.errors.on(:artist_id) # ['is invalid']
|
|
39
40
|
# end
|
|
41
|
+
#
|
|
42
|
+
# While the database usually provides enough information to correctly associated
|
|
43
|
+
# constraint violations with model columns, there are cases where it does not.
|
|
44
|
+
# In those cases, you can override the handling of specific constraint violations
|
|
45
|
+
# to be associated to particular column(s), and use a specific error message:
|
|
46
|
+
#
|
|
47
|
+
# Album.pg_auto_constraint_validation_override(:constraint_name, [:column1], "validation error message")
|
|
48
|
+
#
|
|
49
|
+
# Using the pg_auto_constraint_validations plugin requires 5 queries per
|
|
50
|
+
# model at load time in order to gather the necessary metadata. For applications
|
|
51
|
+
# with a large number of models, this can result in a noticeable delay during model
|
|
52
|
+
# initialization. To mitigate this issue, you can cache the necessary metadata in
|
|
53
|
+
# a file with the :cache_file option:
|
|
54
|
+
#
|
|
55
|
+
# Sequel::Model.plugin :pg_auto_constraint_validations, cache_file: 'db/pgacv.cache'
|
|
56
|
+
#
|
|
57
|
+
# The file does not have to exist when loading the plugin. If it exists, the plugin
|
|
58
|
+
# will load the cache and use the cached results instead of issuing queries if there
|
|
59
|
+
# is an entry in the cache. If there is no entry in the cache, it will update the
|
|
60
|
+
# in-memory cache with the metadata results. To save the in in-memory cache back to
|
|
61
|
+
# the cache file, run:
|
|
62
|
+
#
|
|
63
|
+
# Sequel::Model.dump_pg_auto_constraint_validations_cache
|
|
64
|
+
#
|
|
65
|
+
# Note that when using the :cache_file option, it is up to the application to ensure
|
|
66
|
+
# that the dumped cached metadata reflects the current state of the database. Sequel
|
|
67
|
+
# does no checking to ensure this, as checking would take time and the
|
|
68
|
+
# purpose of this code is to take a shortcut.
|
|
69
|
+
#
|
|
70
|
+
# The cached schema is dumped in Marshal format, since it is the fastest
|
|
71
|
+
# and it handles all ruby objects used in the metadata. Because of this,
|
|
72
|
+
# you should not attempt to load the metadata from a untrusted file.
|
|
40
73
|
#
|
|
41
74
|
# Usage:
|
|
42
75
|
#
|
|
@@ -59,13 +92,28 @@ module Sequel
|
|
|
59
92
|
}.freeze).each_value(&:freeze)
|
|
60
93
|
|
|
61
94
|
# Setup the constraint violation metadata. Options:
|
|
95
|
+
# :cache_file :: File storing cached metadata, to avoid queries for each model
|
|
62
96
|
# :messages :: Override the default error messages for each constraint
|
|
63
97
|
# violation type (:not_null, :check, :unique, :foreign_key, :referenced_by)
|
|
64
98
|
def self.configure(model, opts=OPTS)
|
|
65
99
|
model.instance_exec do
|
|
100
|
+
if @pg_auto_constraint_validations_cache_file = opts[:cache_file]
|
|
101
|
+
@pg_auto_constraint_validations_cache = if ::File.file?(@pg_auto_constraint_validations_cache_file)
|
|
102
|
+
cache = Marshal.load(File.read(@pg_auto_constraint_validations_cache_file))
|
|
103
|
+
cache.each_value do |hash|
|
|
104
|
+
hash.freeze.each_value(&:freeze)
|
|
105
|
+
end
|
|
106
|
+
else
|
|
107
|
+
{}
|
|
108
|
+
end
|
|
109
|
+
else
|
|
110
|
+
@pg_auto_constraint_validations_cache = nil
|
|
111
|
+
end
|
|
112
|
+
|
|
66
113
|
setup_pg_auto_constraint_validations
|
|
67
114
|
@pg_auto_constraint_validations_messages = (@pg_auto_constraint_validations_messages || DEFAULT_ERROR_MESSAGES).merge(opts[:messages] || OPTS).freeze
|
|
68
115
|
end
|
|
116
|
+
nil
|
|
69
117
|
end
|
|
70
118
|
|
|
71
119
|
module ClassMethods
|
|
@@ -77,9 +125,26 @@ module Sequel
|
|
|
77
125
|
# generated validation failures.
|
|
78
126
|
attr_reader :pg_auto_constraint_validations_messages
|
|
79
127
|
|
|
80
|
-
Plugins.inherited_instance_variables(self, :@pg_auto_constraint_validations=>nil, :@pg_auto_constraint_validations_messages=>nil)
|
|
128
|
+
Plugins.inherited_instance_variables(self, :@pg_auto_constraint_validations=>nil, :@pg_auto_constraint_validations_messages=>nil, :@pg_auto_constraint_validations_cache=>nil, :@pg_auto_constraint_validations_cache_file=>nil)
|
|
81
129
|
Plugins.after_set_dataset(self, :setup_pg_auto_constraint_validations)
|
|
82
130
|
|
|
131
|
+
# Dump the in-memory cached metadata to the cache file.
|
|
132
|
+
def dump_pg_auto_constraint_validations_cache
|
|
133
|
+
raise Error, "No pg_auto_constraint_validations setup" unless file = @pg_auto_constraint_validations_cache_file
|
|
134
|
+
File.open(file, 'wb'){|f| f.write(Marshal.dump(@pg_auto_constraint_validations_cache))}
|
|
135
|
+
nil
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Override the constraint validation columns and message for a given constraint
|
|
139
|
+
def pg_auto_constraint_validation_override(constraint, columns, message)
|
|
140
|
+
pgacv = Hash[@pg_auto_constraint_validations]
|
|
141
|
+
overrides = pgacv[:overrides] = Hash[pgacv[:overrides]]
|
|
142
|
+
overrides[constraint] = [Array(columns), message].freeze
|
|
143
|
+
overrides.freeze
|
|
144
|
+
@pg_auto_constraint_validations = pgacv.freeze
|
|
145
|
+
nil
|
|
146
|
+
end
|
|
147
|
+
|
|
83
148
|
private
|
|
84
149
|
|
|
85
150
|
# Get the list of constraints, unique indexes, foreign keys in the current
|
|
@@ -104,38 +169,51 @@ module Sequel
|
|
|
104
169
|
return
|
|
105
170
|
end
|
|
106
171
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
172
|
+
cache = @pg_auto_constraint_validations_cache
|
|
173
|
+
literal_table_name = dataset.literal(table_name)
|
|
174
|
+
unless cache && (metadata = cache[literal_table_name])
|
|
175
|
+
checks = {}
|
|
176
|
+
indexes = {}
|
|
177
|
+
foreign_keys = {}
|
|
178
|
+
referenced_by = {}
|
|
111
179
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
180
|
+
db.check_constraints(table_name).each do |k, v|
|
|
181
|
+
checks[k] = v[:columns].dup.freeze unless v[:columns].empty?
|
|
182
|
+
end
|
|
183
|
+
db.indexes(table_name, :include_partial=>true).each do |k, v|
|
|
184
|
+
if v[:unique]
|
|
185
|
+
indexes[k] = v[:columns].dup.freeze
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
db.foreign_key_list(table_name, :schema=>false).each do |fk|
|
|
189
|
+
foreign_keys[fk[:name]] = fk[:columns].dup.freeze
|
|
190
|
+
end
|
|
191
|
+
db.foreign_key_list(table_name, :reverse=>true, :schema=>false).each do |fk|
|
|
192
|
+
referenced_by[[fk[:schema], fk[:table], fk[:name]].freeze] = fk[:key].dup.freeze
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
schema, table = db[:pg_class].
|
|
196
|
+
join(:pg_namespace, :oid=>:relnamespace, db.send(:regclass_oid, table_name)=>:oid).
|
|
197
|
+
get([:nspname, :relname])
|
|
198
|
+
|
|
199
|
+
metadata = {
|
|
200
|
+
:schema=>schema,
|
|
201
|
+
:table=>table,
|
|
202
|
+
:check=>checks,
|
|
203
|
+
:unique=>indexes,
|
|
204
|
+
:foreign_key=>foreign_keys,
|
|
205
|
+
:referenced_by=>referenced_by,
|
|
206
|
+
:overrides=>OPTS
|
|
207
|
+
}.freeze
|
|
208
|
+
metadata.each_value(&:freeze)
|
|
209
|
+
|
|
210
|
+
if cache
|
|
211
|
+
cache[literal_table_name] = metadata
|
|
118
212
|
end
|
|
119
|
-
end
|
|
120
|
-
db.foreign_key_list(table_name, :schema=>false).each do |fk|
|
|
121
|
-
foreign_keys[fk[:name]] = fk[:columns].dup.freeze
|
|
122
|
-
end
|
|
123
|
-
db.foreign_key_list(table_name, :reverse=>true, :schema=>false).each do |fk|
|
|
124
|
-
referenced_by[[fk[:schema], fk[:table], fk[:name]].freeze] = fk[:key].dup.freeze
|
|
125
213
|
end
|
|
126
214
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
get([:nspname, :relname])
|
|
130
|
-
|
|
131
|
-
(@pg_auto_constraint_validations = {
|
|
132
|
-
:schema=>schema,
|
|
133
|
-
:table=>table,
|
|
134
|
-
:check=>checks,
|
|
135
|
-
:unique=>indexes,
|
|
136
|
-
:foreign_key=>foreign_keys,
|
|
137
|
-
:referenced_by=>referenced_by
|
|
138
|
-
}.freeze).each_value(&:freeze)
|
|
215
|
+
@pg_auto_constraint_validations = metadata
|
|
216
|
+
nil
|
|
139
217
|
end
|
|
140
218
|
end
|
|
141
219
|
|
|
@@ -158,40 +236,50 @@ module Sequel
|
|
|
158
236
|
m = ds.method(:output_identifier)
|
|
159
237
|
schema = info[:schema]
|
|
160
238
|
table = info[:table]
|
|
239
|
+
|
|
161
240
|
if constraint = info[:constraint]
|
|
162
241
|
constraint = m.call(constraint)
|
|
242
|
+
|
|
243
|
+
columns, message = cv_info[:overrides][constraint]
|
|
244
|
+
if columns
|
|
245
|
+
override = true
|
|
246
|
+
add_pg_constraint_validation_error(columns, message)
|
|
247
|
+
end
|
|
163
248
|
end
|
|
249
|
+
|
|
164
250
|
messages = model.pg_auto_constraint_validations_messages
|
|
165
251
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
when Sequel::ForeignKeyConstraintViolation
|
|
180
|
-
message_primary = info[:message_primary]
|
|
181
|
-
if message_primary.start_with?('update')
|
|
182
|
-
# This constraint violation is different from the others, because the constraint
|
|
183
|
-
# referenced is a constraint for a different table, not for this table. This
|
|
184
|
-
# happens when another table references the current table, and the referenced
|
|
185
|
-
# column in the current update is modified such that referential integrity
|
|
186
|
-
# would be broken. Use the reverse foreign key information to figure out
|
|
187
|
-
# which column is affected in that case.
|
|
188
|
-
skip_schema_table_check = true
|
|
189
|
-
if columns = cv_info[:referenced_by][[m.call(schema), m.call(table), constraint]]
|
|
190
|
-
add_pg_constraint_validation_error(columns, messages[:referenced_by])
|
|
252
|
+
unless override
|
|
253
|
+
case e
|
|
254
|
+
when Sequel::NotNullConstraintViolation
|
|
255
|
+
if column = info[:column]
|
|
256
|
+
add_pg_constraint_validation_error([m.call(column)], messages[:not_null])
|
|
257
|
+
end
|
|
258
|
+
when Sequel::CheckConstraintViolation
|
|
259
|
+
if columns = cv_info[:check][constraint]
|
|
260
|
+
add_pg_constraint_validation_error(columns, messages[:check])
|
|
261
|
+
end
|
|
262
|
+
when Sequel::UniqueConstraintViolation
|
|
263
|
+
if columns = cv_info[:unique][constraint]
|
|
264
|
+
add_pg_constraint_validation_error(columns, messages[:unique])
|
|
191
265
|
end
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
266
|
+
when Sequel::ForeignKeyConstraintViolation
|
|
267
|
+
message_primary = info[:message_primary]
|
|
268
|
+
if message_primary.start_with?('update')
|
|
269
|
+
# This constraint violation is different from the others, because the constraint
|
|
270
|
+
# referenced is a constraint for a different table, not for this table. This
|
|
271
|
+
# happens when another table references the current table, and the referenced
|
|
272
|
+
# column in the current update is modified such that referential integrity
|
|
273
|
+
# would be broken. Use the reverse foreign key information to figure out
|
|
274
|
+
# which column is affected in that case.
|
|
275
|
+
skip_schema_table_check = true
|
|
276
|
+
if columns = cv_info[:referenced_by][[m.call(schema), m.call(table), constraint]]
|
|
277
|
+
add_pg_constraint_validation_error(columns, messages[:referenced_by])
|
|
278
|
+
end
|
|
279
|
+
elsif message_primary.start_with?('insert')
|
|
280
|
+
if columns = cv_info[:foreign_key][constraint]
|
|
281
|
+
add_pg_constraint_validation_error(columns, messages[:foreign_key])
|
|
282
|
+
end
|
|
195
283
|
end
|
|
196
284
|
end
|
|
197
285
|
end
|
|
@@ -126,6 +126,9 @@ module Sequel
|
|
|
126
126
|
a = opts.merge(opts.fetch(:ancestors, OPTS))
|
|
127
127
|
ancestors = a.fetch(:name, :ancestors)
|
|
128
128
|
a[:read_only] = true unless a.has_key?(:read_only)
|
|
129
|
+
a[:eager_grapher] = proc do |_|
|
|
130
|
+
raise Sequel::Error, "the #{ancestors} association for #{self} does not support eager graphing"
|
|
131
|
+
end
|
|
129
132
|
a[:eager_loader_key] = key
|
|
130
133
|
a[:dataset] ||= proc do
|
|
131
134
|
base_ds = model.where(prkey_array.zip(key_array.map{|k| get_column_value(k)}))
|
|
@@ -221,6 +224,9 @@ module Sequel
|
|
|
221
224
|
d = opts.merge(opts.fetch(:descendants, OPTS))
|
|
222
225
|
descendants = d.fetch(:name, :descendants)
|
|
223
226
|
d[:read_only] = true unless d.has_key?(:read_only)
|
|
227
|
+
d[:eager_grapher] = proc do |_|
|
|
228
|
+
raise Sequel::Error, "the #{descendants} association for #{self} does not support eager graphing"
|
|
229
|
+
end
|
|
224
230
|
la = d[:level_alias] ||= :x_level_x
|
|
225
231
|
d[:dataset] ||= proc do
|
|
226
232
|
base_ds = model.where(key_array.zip(prkey_array.map{|k| get_column_value(k)}))
|
|
@@ -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)
|