sequel 5.19.0 → 5.20.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 +24 -0
- data/doc/release_notes/5.20.0.txt +89 -0
- data/doc/transactions.rdoc +38 -0
- data/lib/sequel/adapters/mysql2.rb +2 -2
- data/lib/sequel/adapters/shared/postgres.rb +5 -7
- data/lib/sequel/database/query.rb +1 -1
- data/lib/sequel/database/schema_generator.rb +1 -1
- data/lib/sequel/database/transactions.rb +57 -5
- data/lib/sequel/dataset/placeholder_literalizer.rb +4 -1
- data/lib/sequel/dataset/query.rb +1 -1
- data/lib/sequel/extensions/schema_dumper.rb +1 -1
- 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/class_table_inheritance.rb +1 -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/inverted_subsets.rb +2 -2
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +61 -32
- data/lib/sequel/plugins/subset_conditions.rb +2 -2
- data/lib/sequel/plugins/validation_class_methods.rb +5 -3
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +32 -0
- data/spec/core/database_spec.rb +73 -2
- data/spec/core/schema_spec.rb +6 -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/migration_spec.rb +13 -0
- data/spec/extensions/pg_auto_constraint_validations_spec.rb +8 -0
- data/spec/extensions/s_spec.rb +1 -1
- data/spec/extensions/schema_dumper_spec.rb +4 -2
- data/spec/integration/plugin_test.rb +15 -0
- 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 +4 -2
data/lib/sequel/model/plugins.rb
CHANGED
@@ -51,5 +51,109 @@ module Sequel
|
|
51
51
|
r
|
52
52
|
end
|
53
53
|
end
|
54
|
+
|
55
|
+
method_num = 0
|
56
|
+
method_num_mutex = Mutex.new
|
57
|
+
# Return a unique method name symbol for the given suffix.
|
58
|
+
SEQUEL_METHOD_NAME = lambda do |suffix|
|
59
|
+
:"_sequel_#{suffix}_#{method_num_mutex.synchronize{method_num += 1}}"
|
60
|
+
end
|
61
|
+
|
62
|
+
# Define a private instance method using the block with the provided name and
|
63
|
+
# expected arity. If the name is given as a Symbol, it is used directly.
|
64
|
+
# If the name is given as a String, a unique name will be generated using
|
65
|
+
# that string. The expected_arity should be either 0 (no arguments) or
|
66
|
+
# 1 (single argument).
|
67
|
+
#
|
68
|
+
# If a block with an arity that does not match the expected arity is used,
|
69
|
+
# a deprecation warning will be issued. The method defined should still
|
70
|
+
# work, though it will be slower than a method with the expected arity.
|
71
|
+
#
|
72
|
+
# Sequel only checks arity for regular blocks, not lambdas. Lambdas were
|
73
|
+
# already strict in regards to arity, so there is no need to try to fix
|
74
|
+
# arity to keep backwards compatibility for lambdas.
|
75
|
+
#
|
76
|
+
# Blocks with required keyword arguments are not supported by this method.
|
77
|
+
def self.def_sequel_method(model, meth, expected_arity, &block)
|
78
|
+
if meth.is_a?(String)
|
79
|
+
meth = SEQUEL_METHOD_NAME.call(meth)
|
80
|
+
end
|
81
|
+
call_meth = meth
|
82
|
+
|
83
|
+
unless block.lambda?
|
84
|
+
required_args, optional_args, rest, keyword = _define_sequel_method_arg_numbers(block)
|
85
|
+
|
86
|
+
if keyword == :required
|
87
|
+
raise Error, "cannot use block with required keyword arguments when calling define_sequel_method with expected arity #{expected_arity}"
|
88
|
+
end
|
89
|
+
|
90
|
+
case expected_arity
|
91
|
+
when 0
|
92
|
+
unless required_args == 0
|
93
|
+
# SEQUEL6: remove
|
94
|
+
Sequel::Deprecation.deprecate("Arity mismatch in block passed to define_sequel_method. Expected Arity 0, but arguments required for #{block.inspect}. Support for this will be removed in Sequel 6.")
|
95
|
+
b = block
|
96
|
+
block = lambda{instance_exec(&b)} # Fallback
|
97
|
+
end
|
98
|
+
when 1
|
99
|
+
if required_args == 0 && optional_args == 0 && !rest
|
100
|
+
# SEQUEL6: remove
|
101
|
+
Sequel::Deprecation.deprecate("Arity mismatch in block passed to define_sequel_method. Expected Arity 1, but no arguments accepted for #{block.inspect}. Support for this will be removed in Sequel 6.")
|
102
|
+
temp_method = SEQUEL_METHOD_NAME.call("temp")
|
103
|
+
model.class_eval("def #{temp_method}(_) #{meth =~ /\A\w+\z/ ? "#{meth}_arity" : "send(:\"#{meth}_arity\")"} end", __FILE__, __LINE__)
|
104
|
+
model.send(:alias_method, meth, temp_method)
|
105
|
+
model.send(:undef_method, temp_method)
|
106
|
+
model.send(:private, meth)
|
107
|
+
meth = :"#{meth}_arity"
|
108
|
+
elsif required_args > 1
|
109
|
+
# SEQUEL6: remove
|
110
|
+
Sequel::Deprecation.deprecate("Arity mismatch in block passed to define_sequel_method. Expected Arity 1, but more arguments required for #{block.inspect}. Support for this will be removed in Sequel 6.")
|
111
|
+
b = block
|
112
|
+
block = lambda{|r| instance_exec(r, &b)} # Fallback
|
113
|
+
end
|
114
|
+
else
|
115
|
+
raise Error, "unexpected arity passed to define_sequel_method: #{expected_arity.inspect}"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
model.send(:define_method, meth, &block)
|
120
|
+
model.send(:private, meth)
|
121
|
+
call_meth
|
122
|
+
end
|
123
|
+
|
124
|
+
# Return the number of required argument, optional arguments,
|
125
|
+
# whether the callable accepts any additional arguments,
|
126
|
+
# and whether the callable accepts keyword arguments (true, false
|
127
|
+
# or :required).
|
128
|
+
def self._define_sequel_method_arg_numbers(callable)
|
129
|
+
optional_args = 0
|
130
|
+
rest = false
|
131
|
+
keyword = false
|
132
|
+
callable.parameters.map(&:first).each do |arg_type, _|
|
133
|
+
case arg_type
|
134
|
+
when :opt
|
135
|
+
optional_args += 1
|
136
|
+
when :rest
|
137
|
+
rest = true
|
138
|
+
when :keyreq
|
139
|
+
keyword = :required
|
140
|
+
when :key, :keyrest
|
141
|
+
keyword ||= true
|
142
|
+
end
|
143
|
+
end
|
144
|
+
arity = callable.arity
|
145
|
+
if arity < 0
|
146
|
+
arity = arity.abs - 1
|
147
|
+
end
|
148
|
+
required_args = arity
|
149
|
+
arity -= 1 if keyword == :required
|
150
|
+
|
151
|
+
if callable.is_a?(Proc) && !callable.lambda?
|
152
|
+
optional_args -= arity
|
153
|
+
end
|
154
|
+
|
155
|
+
[required_args, optional_args, rest, keyword]
|
156
|
+
end
|
157
|
+
private_class_method :_define_sequel_method_arg_numbers
|
54
158
|
end
|
55
159
|
end
|
@@ -60,9 +60,9 @@ module Sequel
|
|
60
60
|
association_dependencies[:"#{time}_#{action}"] << if action == :nullify
|
61
61
|
case type
|
62
62
|
when :one_to_many , :many_to_many
|
63
|
-
|
63
|
+
[r[:remove_all_method]]
|
64
64
|
when :one_to_one
|
65
|
-
|
65
|
+
[r[:setter_method], nil]
|
66
66
|
else
|
67
67
|
raise(Error, "Can't nullify many_to_one associated objects: association: #{association}")
|
68
68
|
end
|
@@ -97,7 +97,7 @@ module Sequel
|
|
97
97
|
def before_destroy
|
98
98
|
model.association_dependencies[:before_delete].each{|m| public_send(m).delete}
|
99
99
|
model.association_dependencies[:before_destroy].each{|m| public_send(m).destroy}
|
100
|
-
model.association_dependencies[:before_nullify].each{|
|
100
|
+
model.association_dependencies[:before_nullify].each{|args| public_send(*args)}
|
101
101
|
super
|
102
102
|
end
|
103
103
|
end
|
@@ -60,8 +60,15 @@ module Sequel
|
|
60
60
|
|
61
61
|
# Define a association_pks method using the block for the association reflection
|
62
62
|
def def_association_pks_methods(opts)
|
63
|
+
opts[:pks_getter_method] = :"#{singularize(opts[:name])}_pks_getter"
|
64
|
+
association_module_def(opts[:pks_getter_method], &opts[:pks_getter])
|
63
65
|
association_module_def(:"#{singularize(opts[:name])}_pks", opts){_association_pks_getter(opts)}
|
64
|
-
|
66
|
+
|
67
|
+
if opts[:pks_setter]
|
68
|
+
opts[:pks_setter_method] = :"#{singularize(opts[:name])}_pks_setter"
|
69
|
+
association_module_def(opts[:pks_setter_method], &opts[:pks_setter])
|
70
|
+
association_module_def(:"#{singularize(opts[:name])}_pks=", opts){|pks| _association_pks_setter(opts, pks)}
|
71
|
+
end
|
65
72
|
end
|
66
73
|
|
67
74
|
# Add a getter that checks the join table for matching records and
|
@@ -181,7 +188,8 @@ module Sequel
|
|
181
188
|
def after_save
|
182
189
|
if assoc_pks = @_association_pks
|
183
190
|
assoc_pks.each do |name, pks|
|
184
|
-
|
191
|
+
# pks_setter_method is private
|
192
|
+
send(model.association_reflection(name)[:pks_setter_method], pks)
|
185
193
|
end
|
186
194
|
@_association_pks = nil
|
187
195
|
end
|
@@ -206,7 +214,8 @@ module Sequel
|
|
206
214
|
elsif delay && @_association_pks && (objs = @_association_pks[opts[:name]])
|
207
215
|
objs
|
208
216
|
else
|
209
|
-
|
217
|
+
# pks_getter_method is private
|
218
|
+
send(opts[:pks_getter_method])
|
210
219
|
end
|
211
220
|
end
|
212
221
|
|
@@ -231,7 +240,8 @@ module Sequel
|
|
231
240
|
modified!
|
232
241
|
(@_association_pks ||= {})[opts[:name]] = pks
|
233
242
|
else
|
234
|
-
|
243
|
+
# pks_setter_method is private
|
244
|
+
send(opts[:pks_setter_method], pks)
|
235
245
|
end
|
236
246
|
end
|
237
247
|
|
@@ -32,9 +32,10 @@ module Sequel
|
|
32
32
|
#
|
33
33
|
# The :mapping option is just a shortcut that works in particular
|
34
34
|
# cases. To handle any case, you can define a custom :composer
|
35
|
-
# and :decomposer procs. The :composer
|
36
|
-
#
|
37
|
-
#
|
35
|
+
# and :decomposer procs. The :composer and :decomposer procs will
|
36
|
+
# be used to define instance methods. The :composer will be called
|
37
|
+
# the first time the getter is called, and the :decomposer
|
38
|
+
# will be called before saving. The above example could
|
38
39
|
# also be implemented as:
|
39
40
|
#
|
40
41
|
# Album.composition :date,
|
@@ -74,9 +75,9 @@ module Sequel
|
|
74
75
|
#
|
75
76
|
# Options:
|
76
77
|
# :class :: if using the :mapping option, the class to use, as a Class, String or Symbol.
|
77
|
-
# :composer :: A proc
|
78
|
+
# :composer :: A proc used to define the method that the composition getter method will call
|
78
79
|
# to create the composition.
|
79
|
-
# :decomposer :: A proc
|
80
|
+
# :decomposer :: A proc used to define the method called before saving the model object,
|
80
81
|
# if the composition object exists, which sets the columns in the model object
|
81
82
|
# based on the value of the composition object.
|
82
83
|
# :mapping :: An array where each element is either a symbol or an array of two symbols.
|
@@ -129,15 +130,17 @@ module Sequel
|
|
129
130
|
|
130
131
|
# Define getter and setter methods for the composition object.
|
131
132
|
def define_composition_accessor(name, opts=OPTS)
|
132
|
-
|
133
|
+
composer_meth = opts[:composer_method] = Plugins.def_sequel_method(@composition_module, "#{name}_composer", 0, &opts[:composer])
|
134
|
+
opts[:decomposer_method] = Plugins.def_sequel_method(@composition_module, "#{name}_decomposer", 0, &opts[:decomposer])
|
133
135
|
@composition_module.class_eval do
|
134
136
|
define_method(name) do
|
135
137
|
if compositions.has_key?(name)
|
136
138
|
compositions[name]
|
137
139
|
elsif frozen?
|
138
|
-
|
140
|
+
# composer_meth is private
|
141
|
+
send(composer_meth)
|
139
142
|
else
|
140
|
-
compositions[name] =
|
143
|
+
compositions[name] = send(composer_meth)
|
141
144
|
end
|
142
145
|
end
|
143
146
|
define_method("#{name}=") do |v|
|
@@ -171,7 +174,8 @@ module Sequel
|
|
171
174
|
# For each composition, set the columns in the model class based
|
172
175
|
# on the composition object.
|
173
176
|
def before_validation
|
174
|
-
|
177
|
+
# decomposer_method is private
|
178
|
+
@compositions.keys.each{|n| send(model.compositions[n][:decomposer_method])} if @compositions
|
175
179
|
super
|
176
180
|
end
|
177
181
|
|
@@ -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
|
@@ -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,13 @@ 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")
|
40
48
|
#
|
41
49
|
# Usage:
|
42
50
|
#
|
@@ -80,6 +88,16 @@ module Sequel
|
|
80
88
|
Plugins.inherited_instance_variables(self, :@pg_auto_constraint_validations=>nil, :@pg_auto_constraint_validations_messages=>nil)
|
81
89
|
Plugins.after_set_dataset(self, :setup_pg_auto_constraint_validations)
|
82
90
|
|
91
|
+
# Override the constraint validation columns and message for a given constraint
|
92
|
+
def pg_auto_constraint_validation_override(constraint, columns, message)
|
93
|
+
pgacv = Hash[@pg_auto_constraint_validations]
|
94
|
+
overrides = pgacv[:overrides] = Hash[pgacv[:overrides]]
|
95
|
+
overrides[constraint] = [Array(columns), message].freeze
|
96
|
+
overrides.freeze
|
97
|
+
@pg_auto_constraint_validations = pgacv.freeze
|
98
|
+
nil
|
99
|
+
end
|
100
|
+
|
83
101
|
private
|
84
102
|
|
85
103
|
# Get the list of constraints, unique indexes, foreign keys in the current
|
@@ -110,7 +128,7 @@ module Sequel
|
|
110
128
|
referenced_by = {}
|
111
129
|
|
112
130
|
db.check_constraints(table_name).each do |k, v|
|
113
|
-
checks[k] = v[:columns].dup.freeze
|
131
|
+
checks[k] = v[:columns].dup.freeze unless v[:columns].empty?
|
114
132
|
end
|
115
133
|
db.indexes(table_name, :include_partial=>true).each do |k, v|
|
116
134
|
if v[:unique]
|
@@ -134,7 +152,8 @@ module Sequel
|
|
134
152
|
:check=>checks,
|
135
153
|
:unique=>indexes,
|
136
154
|
:foreign_key=>foreign_keys,
|
137
|
-
:referenced_by=>referenced_by
|
155
|
+
:referenced_by=>referenced_by,
|
156
|
+
:overrides=>OPTS
|
138
157
|
}.freeze).each_value(&:freeze)
|
139
158
|
end
|
140
159
|
end
|
@@ -158,40 +177,50 @@ module Sequel
|
|
158
177
|
m = ds.method(:output_identifier)
|
159
178
|
schema = info[:schema]
|
160
179
|
table = info[:table]
|
180
|
+
|
161
181
|
if constraint = info[:constraint]
|
162
182
|
constraint = m.call(constraint)
|
183
|
+
|
184
|
+
columns, message = cv_info[:overrides][constraint]
|
185
|
+
if columns
|
186
|
+
override = true
|
187
|
+
add_pg_constraint_validation_error(columns, message)
|
188
|
+
end
|
163
189
|
end
|
190
|
+
|
164
191
|
messages = model.pg_auto_constraint_validations_messages
|
165
192
|
|
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])
|
193
|
+
unless override
|
194
|
+
case e
|
195
|
+
when Sequel::NotNullConstraintViolation
|
196
|
+
if column = info[:column]
|
197
|
+
add_pg_constraint_validation_error([m.call(column)], messages[:not_null])
|
198
|
+
end
|
199
|
+
when Sequel::CheckConstraintViolation
|
200
|
+
if columns = cv_info[:check][constraint]
|
201
|
+
add_pg_constraint_validation_error(columns, messages[:check])
|
202
|
+
end
|
203
|
+
when Sequel::UniqueConstraintViolation
|
204
|
+
if columns = cv_info[:unique][constraint]
|
205
|
+
add_pg_constraint_validation_error(columns, messages[:unique])
|
191
206
|
end
|
192
|
-
|
193
|
-
|
194
|
-
|
207
|
+
when Sequel::ForeignKeyConstraintViolation
|
208
|
+
message_primary = info[:message_primary]
|
209
|
+
if message_primary.start_with?('update')
|
210
|
+
# This constraint violation is different from the others, because the constraint
|
211
|
+
# referenced is a constraint for a different table, not for this table. This
|
212
|
+
# happens when another table references the current table, and the referenced
|
213
|
+
# column in the current update is modified such that referential integrity
|
214
|
+
# would be broken. Use the reverse foreign key information to figure out
|
215
|
+
# which column is affected in that case.
|
216
|
+
skip_schema_table_check = true
|
217
|
+
if columns = cv_info[:referenced_by][[m.call(schema), m.call(table), constraint]]
|
218
|
+
add_pg_constraint_validation_error(columns, messages[:referenced_by])
|
219
|
+
end
|
220
|
+
elsif message_primary.start_with?('insert')
|
221
|
+
if columns = cv_info[:foreign_key][constraint]
|
222
|
+
add_pg_constraint_validation_error(columns, messages[:foreign_key])
|
223
|
+
end
|
195
224
|
end
|
196
225
|
end
|
197
226
|
end
|