sequel 5.19.0 → 5.20.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e092b5439a78eec1bb50a0bbc8a62c1d84ee5b27b9c5f034c250b632d03e17af
|
4
|
+
data.tar.gz: 231708d52405407b11113c55d5fdcd95e7d6155b0a8283f736876569ced6e4b1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4c2d7e19056e53f2d780f57f5da3c4675531f9678c6a54e922e0d59895bd2e485f12ee0b2644ed801c832a53653f75531d849cb7d60dcd9fb6a4edbfa8cb4c0b
|
7
|
+
data.tar.gz: 701b041185cc57c1966ea3b4be040e26a9e0aabfd6df45f0192f5b0b51133bfb94629f0634fa6edfc513a1c9193d455cb6222b388683d638e474b344e663ca50
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,27 @@
|
|
1
|
+
=== 5.20.0 (2019-05-01)
|
2
|
+
|
3
|
+
* Fix reversing of alter_table add_foreign_key when :type option is used (jeremyevans) (#1615)
|
4
|
+
|
5
|
+
* Switch from using instance_exec to define_method for model associations and in some plugins (jeremyevans)
|
6
|
+
|
7
|
+
* Fix Database#server_version when using mysql2 adapter with mysql driver on MariaDB 10+ database (v-kolesnikov) (#1614)
|
8
|
+
|
9
|
+
* Make one_to_one setter method handle models that use joined datasets (jeremyevans) (#1612)
|
10
|
+
|
11
|
+
* Make auto_validations plugin work with the class_table_inheritance plugin (jeremyevans) (#1611)
|
12
|
+
|
13
|
+
* Avoid use of instance_exec for PlaceholderLiteralString#with_dataset (jeremyevans)
|
14
|
+
|
15
|
+
* Recognize float unsigned database types as float (keeguon, jeremyevans) (#1609)
|
16
|
+
|
17
|
+
* Support :savepoint options to Database#{after_commit,after_rollback} for making the hooks handle savepoints (jeremyevans)
|
18
|
+
|
19
|
+
* Avoid use of instance_exec in association_dependencies plugin (jeremyevans)
|
20
|
+
|
21
|
+
* Add pg_auto_constraint_validation_override to the pg_auto_constraint_validations plugin, for customizing columns and error message per constraint (jeremyevans)
|
22
|
+
|
23
|
+
* Make Database#check_constraints on PostgreSQL also include constraints where the related columns are not known (jeremyevans)
|
24
|
+
|
1
25
|
=== 5.19.0 (2019-04-02)
|
2
26
|
|
3
27
|
* Use more optimized approach to merging hashes in ruby 2.5+ (jeremyevans)
|
@@ -0,0 +1,89 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* Database#after_commit and #after_rollback transaction hook methods
|
4
|
+
now support a :savepoint option. Using the :savepoint option makes
|
5
|
+
the hooks savepoint-aware, so after_commit will only be called if
|
6
|
+
all enclosing savepoints and the transaction are committed, and
|
7
|
+
after_rollback will be called when any of the enclosing savepoints
|
8
|
+
are rolled back (which may be before transaction commit/rollback).
|
9
|
+
Examples:
|
10
|
+
|
11
|
+
x = nil
|
12
|
+
DB.transaction do # BEGIN
|
13
|
+
DB.transaction(savepoint: true) do # SAVEPOINT
|
14
|
+
DB.after_commit(savepoint: true){x = 1}
|
15
|
+
DB.after_rollback(savepoint: true){x = 2}
|
16
|
+
x # nil
|
17
|
+
end # RELEASE SAVEPOINT
|
18
|
+
x # nil
|
19
|
+
end # COMMIT
|
20
|
+
x # 1
|
21
|
+
|
22
|
+
x = nil
|
23
|
+
DB.transaction do # BEGIN
|
24
|
+
DB.transaction(savepoint: true) do # SAVEPOINT
|
25
|
+
DB.after_commit(savepoint: true){x = 1}
|
26
|
+
DB.after_rollback(savepoint: true){x = 2}
|
27
|
+
x # nil
|
28
|
+
raise Sequel::Rollback
|
29
|
+
end # ROLLBACK TO SAVEPOINT
|
30
|
+
x # 2
|
31
|
+
end # COMMIT
|
32
|
+
x # 2
|
33
|
+
|
34
|
+
x = nil
|
35
|
+
DB.transaction do # BEGIN
|
36
|
+
DB.transaction(savepoint: true) do # SAVEPOINT
|
37
|
+
DB.after_commit(savepoint: true){x = 1}
|
38
|
+
DB.after_rollback(savepoint: true){x = 2}
|
39
|
+
end # RELEASE SAVEPOINT
|
40
|
+
x # nil
|
41
|
+
raise Sequel::Rollback
|
42
|
+
end
|
43
|
+
x # 2
|
44
|
+
|
45
|
+
* The pg_auto_constraint_validations plugin now supports a
|
46
|
+
pg_auto_constraint_validation_override method for overriding
|
47
|
+
the columns and message for a specific constraint. This is
|
48
|
+
useful if the database cannot determine the columns (due
|
49
|
+
to the constraint containing a database function call), or
|
50
|
+
if you would like to customize the message per constraint.
|
51
|
+
|
52
|
+
= Other Improvements
|
53
|
+
|
54
|
+
* The one_to_one association setter now works with models that use
|
55
|
+
joined datasets, such as child models when using the
|
56
|
+
class_table_inheritance plugin.
|
57
|
+
|
58
|
+
* Database#check_constraints on PostgreSQL now also includes CHECK
|
59
|
+
constraints where the related columns are not known. The :columns
|
60
|
+
entry in the hash will be an empty array in such cases. The
|
61
|
+
exclusion of such constraints in previous versions was not
|
62
|
+
intentional, and the documentation implied that all CHECK
|
63
|
+
constraints were returned.
|
64
|
+
|
65
|
+
* Many cases where instance_exec was previously used on model
|
66
|
+
instances have been changed so that instance methods are defined
|
67
|
+
and called instead. This avoids the creation of singleton classes
|
68
|
+
for model instances, and can significantly improve performance in
|
69
|
+
some cases. This affects all associations as well as the
|
70
|
+
following plugins:
|
71
|
+
|
72
|
+
* composition
|
73
|
+
* hook_class_methods
|
74
|
+
* validation_class_methods
|
75
|
+
|
76
|
+
Other cases where instance_exec is now avoided and a different
|
77
|
+
approach is used:
|
78
|
+
|
79
|
+
* association_dependencies plugin
|
80
|
+
* PlaceholderLiteralString#with_dataset
|
81
|
+
|
82
|
+
* The auto_validations plugin now works with child models when using
|
83
|
+
the class_table_inheritance plugin.
|
84
|
+
|
85
|
+
* Database#server_version now works correctly in the mysql2 adapter
|
86
|
+
when using the MySQL driver with MariaDB 10+.
|
87
|
+
|
88
|
+
* The float unsigned type is now recognized and supported in the
|
89
|
+
schema parser and schema_dumper extension.
|
data/doc/transactions.rdoc
CHANGED
@@ -169,6 +169,44 @@ If you want the current savepoint and potentially enclosing savepoints to be rol
|
|
169
169
|
end # ROLLBACK TO SAVEPOINT
|
170
170
|
end # ROLLBACK
|
171
171
|
|
172
|
+
=== Savepoint Hooks
|
173
|
+
|
174
|
+
When using savepoints, you can use the +:savepoint+ option to +after_commit+ or +after_rollback+ to use a savepoint hook. For +after_commit+, this will only run the hook after transaction commit if all enclosing savepoints are released (not rolled back). For +after_rollback+, this will run the hook after any enclosing savepoint is rolled back (before transaction commit), or after the transaction is rolled back if all enclosing savepoints are released:
|
175
|
+
|
176
|
+
x = nil
|
177
|
+
DB.transaction do # BEGIN
|
178
|
+
DB.transaction(savepoint: true) do # SAVEPOINT
|
179
|
+
DB.after_commit(savepoint: true){x = 1}
|
180
|
+
DB.after_rollback(savepoint: true){x = 2}
|
181
|
+
x # nil
|
182
|
+
end # RELEASE SAVEPOINT
|
183
|
+
x # nil
|
184
|
+
end # COMMIT
|
185
|
+
x # 1
|
186
|
+
|
187
|
+
x = nil
|
188
|
+
DB.transaction do # BEGIN
|
189
|
+
DB.transaction(savepoint: true) do # SAVEPOINT
|
190
|
+
DB.after_commit(savepoint: true){x = 1}
|
191
|
+
DB.after_rollback(savepoint: true){x = 2}
|
192
|
+
x # nil
|
193
|
+
raise Sequel::Rollback
|
194
|
+
end # ROLLBACK TO SAVEPOINT
|
195
|
+
x # 2
|
196
|
+
end # COMMIT
|
197
|
+
x # 2
|
198
|
+
|
199
|
+
x = nil
|
200
|
+
DB.transaction do # BEGIN
|
201
|
+
DB.transaction(savepoint: true) do # SAVEPOINT
|
202
|
+
DB.after_commit(savepoint: true){x = 1}
|
203
|
+
DB.after_rollback(savepoint: true){x = 2}
|
204
|
+
end # RELEASE SAVEPOINT
|
205
|
+
x # nil
|
206
|
+
raise Sequel::Rollback
|
207
|
+
end
|
208
|
+
x # 2
|
209
|
+
|
172
210
|
== Prepared Transactions / Two-Phase Commit
|
173
211
|
|
174
212
|
Sequel supports database prepared transactions on PostgreSQL, MySQL, and H2. With prepared transactions, at the end of the transaction, the transaction is not immediately committed (it acts like a rollback). Later, you can call +commit_prepared_transaction+ to commit the transaction or +rollback_prepared_transaction+ to roll the transaction back. Prepared transactions are usually used with distributed databases to make sure all databases commit the same transaction or none of them do.
|
@@ -78,8 +78,8 @@ module Sequel
|
|
78
78
|
end
|
79
79
|
|
80
80
|
# Return the version of the MySQL server to which we are connecting.
|
81
|
-
def server_version(
|
82
|
-
@server_version ||= (
|
81
|
+
def server_version(_server=nil)
|
82
|
+
@server_version ||= super()
|
83
83
|
end
|
84
84
|
|
85
85
|
private
|
@@ -220,24 +220,22 @@ module Sequel
|
|
220
220
|
# A hash of metadata for CHECK constraints on the table.
|
221
221
|
# Keys are CHECK constraint name symbols. Values are hashes with the following keys:
|
222
222
|
# :definition :: An SQL fragment for the definition of the constraint
|
223
|
-
# :columns :: An array of column symbols for the columns referenced in the constraint
|
223
|
+
# :columns :: An array of column symbols for the columns referenced in the constraint,
|
224
|
+
# can be an empty array if the database cannot deteremine the column symbols.
|
224
225
|
def check_constraints(table)
|
225
226
|
m = output_identifier_meth
|
226
227
|
|
227
228
|
rows = metadata_dataset.
|
228
229
|
from{pg_constraint.as(:co)}.
|
229
|
-
|
230
|
+
left_join(Sequel[:pg_attribute].as(:att), :attrelid=>:conrelid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
|
230
231
|
where(:conrelid=>regclass_oid(table), :contype=>'c').
|
231
232
|
select{[co[:conname].as(:constraint), att[:attname].as(:column), pg_get_constraintdef(co[:oid]).as(:definition)]}
|
232
233
|
|
233
234
|
hash = {}
|
234
235
|
rows.each do |row|
|
235
236
|
constraint = m.call(row[:constraint])
|
236
|
-
|
237
|
-
|
238
|
-
else
|
239
|
-
hash[constraint] = {:definition=>row[:definition], :columns=>[m.call(row[:column])]}
|
240
|
-
end
|
237
|
+
entry = hash[constraint] ||= {:definition=>row[:definition], :columns=>[]}
|
238
|
+
entry[:columns] << m.call(row[:column]) if row[:column]
|
241
239
|
end
|
242
240
|
|
243
241
|
hash
|
@@ -331,7 +331,7 @@ module Sequel
|
|
331
331
|
:time
|
332
332
|
when /\A(bool(ean)?)\z/io
|
333
333
|
:boolean
|
334
|
-
when /\A(real|float
|
334
|
+
when /\A(real|float( unsigned)?|double( precision)?|double\(\d+,\d+\)( unsigned)?)\z/io
|
335
335
|
:float
|
336
336
|
when /\A(?:(?:(?:num(?:ber|eric)?|decimal)(?:\(\d+,\s*(\d+|false|true)\))?))\z/io
|
337
337
|
$1 && ['0', 'false'].include?($1) ? :integer : :decimal
|
@@ -634,7 +634,7 @@ module Sequel
|
|
634
634
|
|
635
635
|
# Drop a composite foreign key constraint
|
636
636
|
def drop_composite_foreign_key(columns, opts)
|
637
|
-
@operations <<
|
637
|
+
@operations << opts.merge(:op => :drop_constraint, :type => :foreign_key, :columns => columns)
|
638
638
|
nil
|
639
639
|
end
|
640
640
|
end
|
@@ -25,13 +25,19 @@ module Sequel
|
|
25
25
|
# Otherwise, add the block to the list of blocks to call after the currently
|
26
26
|
# in progress transaction commits (and only if it commits).
|
27
27
|
# Options:
|
28
|
+
# :savepoint :: If currently inside a savepoint, only run this hook on transaction
|
29
|
+
# commit if all enclosing savepoints have been released.
|
28
30
|
# :server :: The server/shard to use.
|
29
31
|
def after_commit(opts=OPTS, &block)
|
30
32
|
raise Error, "must provide block to after_commit" unless block
|
31
33
|
synchronize(opts[:server]) do |conn|
|
32
34
|
if h = _trans(conn)
|
33
35
|
raise Error, "cannot call after_commit in a prepared transaction" if h[:prepare]
|
34
|
-
|
36
|
+
if opts[:savepoint] && in_savepoint?(conn)
|
37
|
+
add_savepoint_hook(conn, :after_commit, block)
|
38
|
+
else
|
39
|
+
add_transaction_hook(conn, :after_commit, block)
|
40
|
+
end
|
35
41
|
else
|
36
42
|
yield
|
37
43
|
end
|
@@ -42,13 +48,20 @@ module Sequel
|
|
42
48
|
# Otherwise, add the block to the list of the blocks to call after the currently
|
43
49
|
# in progress transaction rolls back (and only if it rolls back).
|
44
50
|
# Options:
|
51
|
+
# :savepoint :: If currently inside a savepoint, run this hook immediately when
|
52
|
+
# any enclosing savepoint is rolled back, which may be before the transaction
|
53
|
+
# commits or rollsback.
|
45
54
|
# :server :: The server/shard to use.
|
46
55
|
def after_rollback(opts=OPTS, &block)
|
47
56
|
raise Error, "must provide block to after_rollback" unless block
|
48
57
|
synchronize(opts[:server]) do |conn|
|
49
58
|
if h = _trans(conn)
|
50
59
|
raise Error, "cannot call after_rollback in a prepared transaction" if h[:prepare]
|
51
|
-
|
60
|
+
if opts[:savepoint] && in_savepoint?(conn)
|
61
|
+
add_savepoint_hook(conn, :after_rollback, block)
|
62
|
+
else
|
63
|
+
add_transaction_hook(conn, :after_rollback, block)
|
64
|
+
end
|
52
65
|
end
|
53
66
|
end
|
54
67
|
end
|
@@ -298,6 +311,13 @@ module Sequel
|
|
298
311
|
Sequel.synchronize{@transactions[conn] = hash}
|
299
312
|
end
|
300
313
|
|
314
|
+
# Set the given callable as a hook to be called. Type should be either
|
315
|
+
# :after_commit or :after_rollback.
|
316
|
+
def add_savepoint_hook(conn, type, block)
|
317
|
+
savepoint = _trans(conn)[:savepoints].last
|
318
|
+
(savepoint[type] ||= []) << block
|
319
|
+
end
|
320
|
+
|
301
321
|
# Set the given callable as a hook to be called. Type should be either
|
302
322
|
# :after_commit or :after_rollback.
|
303
323
|
def add_transaction_hook(conn, type, block)
|
@@ -401,6 +421,14 @@ module Sequel
|
|
401
421
|
supports_savepoints? && savepoint_level(conn) > 1
|
402
422
|
end
|
403
423
|
|
424
|
+
# Retrieve the savepoint hooks that should be run for the given
|
425
|
+
# connection and commit status.
|
426
|
+
def savepoint_hooks(conn, committed)
|
427
|
+
if in_savepoint?(conn)
|
428
|
+
_trans(conn)[:savepoints].last[committed ? :after_commit : :after_rollback]
|
429
|
+
end
|
430
|
+
end
|
431
|
+
|
404
432
|
# Retrieve the transaction hooks that should be run for the given
|
405
433
|
# connection and commit status.
|
406
434
|
def transaction_hooks(conn, committed)
|
@@ -411,16 +439,40 @@ module Sequel
|
|
411
439
|
|
412
440
|
# Remove the current thread from the list of active transactions
|
413
441
|
def remove_transaction(conn, committed)
|
414
|
-
|
442
|
+
if in_savepoint?(conn)
|
443
|
+
savepoint_callbacks = savepoint_hooks(conn, committed)
|
444
|
+
if committed
|
445
|
+
savepoint_rollback_callbacks = savepoint_hooks(conn, false)
|
446
|
+
end
|
447
|
+
else
|
448
|
+
callbacks = transaction_hooks(conn, committed)
|
449
|
+
end
|
415
450
|
|
416
451
|
if transaction_finished?(conn)
|
417
452
|
h = _trans(conn)
|
418
453
|
rolled_back = !committed
|
419
454
|
Sequel.synchronize{h[:rolled_back] = rolled_back}
|
420
455
|
Sequel.synchronize{@transactions.delete(conn)}
|
456
|
+
callbacks.each(&:call) if callbacks
|
457
|
+
elsif savepoint_callbacks || savepoint_rollback_callbacks
|
458
|
+
if committed
|
459
|
+
meth = in_savepoint?(conn) ? :add_savepoint_hook : :add_transaction_hook
|
460
|
+
|
461
|
+
if savepoint_callbacks
|
462
|
+
savepoint_callbacks.each do |block|
|
463
|
+
send(meth, conn, :after_commit, block)
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
if savepoint_rollback_callbacks
|
468
|
+
savepoint_rollback_callbacks.each do |block|
|
469
|
+
send(meth, conn, :after_rollback, block)
|
470
|
+
end
|
471
|
+
end
|
472
|
+
else
|
473
|
+
savepoint_callbacks.each(&:call)
|
474
|
+
end
|
421
475
|
end
|
422
|
-
|
423
|
-
callbacks.each(&:call) if callbacks
|
424
476
|
end
|
425
477
|
|
426
478
|
# SQL to rollback to a savepoint
|
@@ -170,7 +170,10 @@ module Sequel
|
|
170
170
|
# receiver's dataset to the block, and the block should return the new dataset
|
171
171
|
# to use.
|
172
172
|
def with_dataset
|
173
|
-
|
173
|
+
dataset = yield @dataset
|
174
|
+
other = dup
|
175
|
+
other.instance_variable_set(:@dataset, dataset)
|
176
|
+
other.freeze
|
174
177
|
end
|
175
178
|
|
176
179
|
# Return an array of all objects by running the SQL query for the given arguments.
|
data/lib/sequel/dataset/query.rb
CHANGED
@@ -1091,7 +1091,7 @@ module Sequel
|
|
1091
1091
|
# # SELECT i1.id, i1.parent_id FROM i1 INNER JOIN t ON (t.id = i1.parent_id)
|
1092
1092
|
# # ) SELECT * FROM t
|
1093
1093
|
def with_recursive(name, nonrecursive, recursive, opts=OPTS)
|
1094
|
-
raise(Error, 'This
|
1094
|
+
raise(Error, 'This dataset does not support common table expressions') unless supports_cte?
|
1095
1095
|
if hoist_cte?(nonrecursive)
|
1096
1096
|
s, ds = hoist_cte(nonrecursive)
|
1097
1097
|
s.with_recursive(name, ds, recursive, opts)
|
@@ -37,7 +37,7 @@ module Sequel
|
|
37
37
|
{:type =>schema[:type] == :boolean ? TrueClass : Integer}
|
38
38
|
when /\Abigint(?:\((?:\d+)\))?(?: unsigned)?\z/
|
39
39
|
{:type=>:Bignum}
|
40
|
-
when /\A(?:real|float
|
40
|
+
when /\A(?:real|float(?: unsigned)?|double(?: precision)?|double\(\d+,\d+\)(?: unsigned)?)\z/
|
41
41
|
{:type=>Float}
|
42
42
|
when 'boolean', 'bit', 'bool'
|
43
43
|
{:type=>TrueClass}
|
@@ -1617,9 +1617,10 @@ module Sequel
|
|
1617
1617
|
# is hash or array of two element arrays. Consider also specifying the :graph_block
|
1618
1618
|
# option if the value for this option is not a hash or array of two element arrays
|
1619
1619
|
# and you plan to use this association in eager_graph or association_join.
|
1620
|
-
# :dataset :: A proc that is
|
1620
|
+
# :dataset :: A proc that is used to define the method to get the base dataset to use (before the other
|
1621
1621
|
# options are applied). If the proc accepts an argument, it is passed the related
|
1622
|
-
# association reflection.
|
1622
|
+
# association reflection. It is a best practice to always have the dataset accept an argument
|
1623
|
+
# and use the argument to return the appropriate dataset.
|
1623
1624
|
# :distinct :: Use the DISTINCT clause when selecting associating object, both when
|
1624
1625
|
# lazy loading and eager loading via .eager (but not when using .eager_graph).
|
1625
1626
|
# :eager :: The associations to eagerly load via +eager+ when loading the associated object(s).
|
@@ -1909,7 +1910,7 @@ module Sequel
|
|
1909
1910
|
# can be easily overridden in the class itself while allowing for
|
1910
1911
|
# super to be called.
|
1911
1912
|
def association_module_def(name, opts=OPTS, &block)
|
1912
|
-
association_module(opts).
|
1913
|
+
association_module(opts).send(:define_method, name, &block)
|
1913
1914
|
end
|
1914
1915
|
|
1915
1916
|
# Add a private method to the module included in the class.
|
@@ -1944,6 +1945,13 @@ module Sequel
|
|
1944
1945
|
end
|
1945
1946
|
|
1946
1947
|
association_module_def(opts.dataset_method, opts){_dataset(opts)}
|
1948
|
+
if opts[:block]
|
1949
|
+
opts[:block_method] = Plugins.def_sequel_method(association_module(opts), "#{opts[:name]}_block", 1, &opts[:block])
|
1950
|
+
end
|
1951
|
+
if opts[:dataset]
|
1952
|
+
opts[:dataset_opt_arity] = opts[:dataset].arity == 0 ? 0 : 1
|
1953
|
+
opts[:dataset_opt_method] = Plugins.def_sequel_method(association_module(opts), "#{opts[:name]}_dataset_opt", opts[:dataset_opt_arity], &opts[:dataset])
|
1954
|
+
end
|
1947
1955
|
def_association_method(opts)
|
1948
1956
|
|
1949
1957
|
return if opts[:read_only]
|
@@ -2204,12 +2212,28 @@ module Sequel
|
|
2204
2212
|
if one_to_one
|
2205
2213
|
opts[:setter] ||= proc do |o|
|
2206
2214
|
up_ds = _apply_association_options(opts, opts.associated_dataset.where(cks.zip(cpks.map{|k| get_column_value(k)})))
|
2215
|
+
|
2216
|
+
if (froms = up_ds.opts[:from]) && (from = froms[0]) && (from.is_a?(Sequel::Dataset) || (from.is_a?(Sequel::SQL::AliasedExpression) && from.expression.is_a?(Sequel::Dataset)))
|
2217
|
+
if old = up_ds.first
|
2218
|
+
cks.each{|k| old.set_column_value(:"#{k}=", nil)}
|
2219
|
+
end
|
2220
|
+
save_old = true
|
2221
|
+
end
|
2222
|
+
|
2207
2223
|
if o
|
2208
|
-
|
2224
|
+
if !o.new? && !save_old
|
2225
|
+
up_ds = up_ds.exclude(o.pk_hash)
|
2226
|
+
end
|
2209
2227
|
cks.zip(cpks).each{|k, pk| o.set_column_value(:"#{k}=", get_column_value(pk))}
|
2210
2228
|
end
|
2229
|
+
|
2211
2230
|
checked_transaction do
|
2212
|
-
|
2231
|
+
if save_old
|
2232
|
+
old.save(save_opts) || raise(Sequel::Error, "invalid previously associated object, cannot save") if old
|
2233
|
+
else
|
2234
|
+
up_ds.skip_limit_check.update(ck_nil_hash)
|
2235
|
+
end
|
2236
|
+
|
2213
2237
|
o.save(save_opts) || raise(Sequel::Error, "invalid associated object, cannot save") if o
|
2214
2238
|
end
|
2215
2239
|
end
|
@@ -2287,7 +2311,8 @@ module Sequel
|
|
2287
2311
|
end
|
2288
2312
|
ds = ds.clone(:model_object => self)
|
2289
2313
|
ds = ds.eager_graph(opts[:eager_graph]) if opts[:eager_graph] && opts.eager_graph_lazy_dataset?
|
2290
|
-
|
2314
|
+
# block method is private
|
2315
|
+
ds = send(opts[:block_method], ds) if opts[:block_method]
|
2291
2316
|
ds
|
2292
2317
|
end
|
2293
2318
|
|
@@ -2310,10 +2335,11 @@ module Sequel
|
|
2310
2335
|
# Return an association dataset for the given association reflection
|
2311
2336
|
def _dataset(opts)
|
2312
2337
|
raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
|
2313
|
-
ds = if opts[:
|
2314
|
-
|
2338
|
+
ds = if opts[:dataset_opt_arity] == 1
|
2339
|
+
# dataset_opt_method is private
|
2340
|
+
send(opts[:dataset_opt_method], opts)
|
2315
2341
|
else
|
2316
|
-
|
2342
|
+
send(opts[:dataset_opt_method])
|
2317
2343
|
end
|
2318
2344
|
_apply_association_options(opts, ds)
|
2319
2345
|
end
|