sequel 5.33.0 → 5.58.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +318 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +40 -9
- data/doc/association_basics.rdoc +77 -13
- data/doc/cheat_sheet.rdoc +13 -5
- data/doc/code_order.rdoc +0 -12
- data/doc/dataset_filtering.rdoc +2 -2
- data/doc/fork_safety.rdoc +84 -0
- data/doc/migration.rdoc +12 -6
- data/doc/model_plugins.rdoc +1 -1
- data/doc/opening_databases.rdoc +15 -3
- data/doc/postgresql.rdoc +9 -1
- data/doc/querying.rdoc +7 -5
- data/doc/release_notes/5.34.0.txt +40 -0
- data/doc/release_notes/5.35.0.txt +56 -0
- data/doc/release_notes/5.36.0.txt +60 -0
- data/doc/release_notes/5.37.0.txt +30 -0
- data/doc/release_notes/5.38.0.txt +28 -0
- data/doc/release_notes/5.39.0.txt +19 -0
- data/doc/release_notes/5.40.0.txt +40 -0
- data/doc/release_notes/5.41.0.txt +25 -0
- data/doc/release_notes/5.42.0.txt +136 -0
- data/doc/release_notes/5.43.0.txt +98 -0
- data/doc/release_notes/5.44.0.txt +32 -0
- data/doc/release_notes/5.45.0.txt +34 -0
- data/doc/release_notes/5.46.0.txt +87 -0
- data/doc/release_notes/5.47.0.txt +59 -0
- data/doc/release_notes/5.48.0.txt +14 -0
- data/doc/release_notes/5.49.0.txt +59 -0
- data/doc/release_notes/5.50.0.txt +78 -0
- data/doc/release_notes/5.51.0.txt +47 -0
- data/doc/release_notes/5.52.0.txt +87 -0
- data/doc/release_notes/5.53.0.txt +23 -0
- data/doc/release_notes/5.54.0.txt +27 -0
- data/doc/release_notes/5.55.0.txt +21 -0
- data/doc/release_notes/5.56.0.txt +51 -0
- data/doc/release_notes/5.57.0.txt +23 -0
- data/doc/release_notes/5.58.0.txt +31 -0
- data/doc/sql.rdoc +14 -2
- data/doc/testing.rdoc +10 -1
- data/doc/transactions.rdoc +0 -8
- data/doc/validations.rdoc +1 -1
- data/doc/virtual_rows.rdoc +1 -1
- data/lib/sequel/adapters/ado/access.rb +1 -1
- data/lib/sequel/adapters/ado.rb +17 -17
- data/lib/sequel/adapters/amalgalite.rb +3 -5
- data/lib/sequel/adapters/ibmdb.rb +2 -2
- data/lib/sequel/adapters/jdbc/derby.rb +8 -0
- data/lib/sequel/adapters/jdbc/h2.rb +60 -10
- data/lib/sequel/adapters/jdbc/hsqldb.rb +6 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +4 -4
- data/lib/sequel/adapters/jdbc/postgresql.rb +4 -4
- data/lib/sequel/adapters/jdbc.rb +29 -19
- data/lib/sequel/adapters/mysql.rb +80 -67
- data/lib/sequel/adapters/mysql2.rb +54 -49
- data/lib/sequel/adapters/odbc.rb +8 -6
- data/lib/sequel/adapters/oracle.rb +5 -4
- data/lib/sequel/adapters/postgres.rb +27 -29
- data/lib/sequel/adapters/shared/access.rb +2 -0
- data/lib/sequel/adapters/shared/db2.rb +30 -0
- data/lib/sequel/adapters/shared/mssql.rb +84 -7
- data/lib/sequel/adapters/shared/mysql.rb +33 -2
- data/lib/sequel/adapters/shared/oracle.rb +82 -7
- data/lib/sequel/adapters/shared/postgres.rb +158 -20
- data/lib/sequel/adapters/shared/sqlanywhere.rb +3 -0
- data/lib/sequel/adapters/shared/sqlite.rb +102 -10
- data/lib/sequel/adapters/sqlanywhere.rb +1 -1
- data/lib/sequel/adapters/sqlite.rb +60 -18
- data/lib/sequel/adapters/tinytds.rb +2 -1
- data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +2 -1
- data/lib/sequel/ast_transformer.rb +6 -0
- data/lib/sequel/connection_pool/sharded_single.rb +9 -8
- data/lib/sequel/connection_pool/sharded_threaded.rb +10 -10
- data/lib/sequel/connection_pool/single.rb +7 -9
- data/lib/sequel/connection_pool/threaded.rb +1 -1
- data/lib/sequel/core.rb +33 -24
- data/lib/sequel/database/connecting.rb +3 -4
- data/lib/sequel/database/misc.rb +37 -12
- data/lib/sequel/database/query.rb +3 -1
- data/lib/sequel/database/schema_generator.rb +50 -53
- data/lib/sequel/database/schema_methods.rb +45 -23
- data/lib/sequel/database/transactions.rb +9 -6
- data/lib/sequel/dataset/actions.rb +61 -8
- data/lib/sequel/dataset/features.rb +15 -0
- data/lib/sequel/dataset/placeholder_literalizer.rb +3 -7
- data/lib/sequel/dataset/prepared_statements.rb +2 -0
- data/lib/sequel/dataset/query.rb +114 -11
- data/lib/sequel/dataset/sql.rb +172 -46
- data/lib/sequel/deprecated.rb +3 -1
- data/lib/sequel/exceptions.rb +2 -0
- data/lib/sequel/extensions/_pretty_table.rb +1 -2
- data/lib/sequel/extensions/any_not_empty.rb +1 -1
- data/lib/sequel/extensions/async_thread_pool.rb +438 -0
- data/lib/sequel/extensions/blank.rb +8 -0
- data/lib/sequel/extensions/columns_introspection.rb +1 -2
- data/lib/sequel/extensions/core_refinements.rb +38 -11
- data/lib/sequel/extensions/date_arithmetic.rb +36 -24
- data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
- data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
- data/lib/sequel/extensions/duplicate_columns_handler.rb +3 -1
- data/lib/sequel/extensions/eval_inspect.rb +2 -0
- data/lib/sequel/extensions/inflector.rb +9 -1
- data/lib/sequel/extensions/is_distinct_from.rb +139 -0
- data/lib/sequel/extensions/migration.rb +13 -2
- data/lib/sequel/extensions/named_timezones.rb +5 -1
- data/lib/sequel/extensions/pagination.rb +1 -1
- data/lib/sequel/extensions/pg_array.rb +1 -0
- data/lib/sequel/extensions/pg_array_ops.rb +6 -2
- data/lib/sequel/extensions/pg_enum.rb +3 -1
- data/lib/sequel/extensions/pg_extended_date_support.rb +2 -2
- data/lib/sequel/extensions/pg_hstore.rb +1 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +55 -3
- data/lib/sequel/extensions/pg_inet.rb +2 -0
- data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
- data/lib/sequel/extensions/pg_interval.rb +35 -8
- data/lib/sequel/extensions/pg_json.rb +3 -5
- data/lib/sequel/extensions/pg_json_ops.rb +119 -4
- data/lib/sequel/extensions/pg_loose_count.rb +3 -1
- data/lib/sequel/extensions/pg_multirange.rb +372 -0
- data/lib/sequel/extensions/pg_range.rb +7 -19
- data/lib/sequel/extensions/pg_range_ops.rb +39 -9
- data/lib/sequel/extensions/pg_row.rb +1 -1
- data/lib/sequel/extensions/pg_row_ops.rb +25 -1
- data/lib/sequel/extensions/query.rb +3 -0
- data/lib/sequel/extensions/run_transaction_hooks.rb +1 -1
- data/lib/sequel/extensions/s.rb +4 -1
- data/lib/sequel/extensions/schema_dumper.rb +16 -5
- data/lib/sequel/extensions/server_block.rb +8 -12
- data/lib/sequel/extensions/sql_comments.rb +110 -3
- data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
- data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
- data/lib/sequel/extensions/string_agg.rb +1 -1
- data/lib/sequel/extensions/string_date_time.rb +19 -23
- data/lib/sequel/extensions/symbol_aref_refinement.rb +2 -0
- data/lib/sequel/extensions/symbol_as_refinement.rb +2 -0
- data/lib/sequel/extensions/to_dot.rb +9 -3
- data/lib/sequel/model/associations.rb +342 -114
- data/lib/sequel/model/base.rb +45 -24
- data/lib/sequel/model/errors.rb +10 -1
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/model/plugins.rb +8 -3
- data/lib/sequel/model.rb +3 -1
- data/lib/sequel/plugins/association_pks.rb +60 -18
- data/lib/sequel/plugins/association_proxies.rb +3 -0
- data/lib/sequel/plugins/async_thread_pool.rb +39 -0
- data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
- data/lib/sequel/plugins/auto_validations.rb +39 -5
- data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
- data/lib/sequel/plugins/blacklist_security.rb +1 -2
- data/lib/sequel/plugins/class_table_inheritance.rb +3 -8
- data/lib/sequel/plugins/column_encryption.rb +728 -0
- data/lib/sequel/plugins/composition.rb +8 -2
- data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
- data/lib/sequel/plugins/constraint_validations.rb +2 -1
- data/lib/sequel/plugins/csv_serializer.rb +2 -0
- data/lib/sequel/plugins/dataset_associations.rb +4 -1
- data/lib/sequel/plugins/dirty.rb +44 -0
- data/lib/sequel/plugins/enum.rb +124 -0
- data/lib/sequel/plugins/forbid_lazy_load.rb +2 -0
- data/lib/sequel/plugins/insert_conflict.rb +4 -0
- data/lib/sequel/plugins/instance_specific_default.rb +113 -0
- data/lib/sequel/plugins/json_serializer.rb +39 -24
- data/lib/sequel/plugins/lazy_attributes.rb +4 -1
- data/lib/sequel/plugins/many_through_many.rb +108 -9
- data/lib/sequel/plugins/nested_attributes.rb +8 -3
- data/lib/sequel/plugins/pg_array_associations.rb +58 -41
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +2 -0
- data/lib/sequel/plugins/prepared_statements.rb +15 -12
- data/lib/sequel/plugins/prepared_statements_safe.rb +1 -3
- data/lib/sequel/plugins/rcte_tree.rb +37 -35
- data/lib/sequel/plugins/serialization.rb +9 -3
- data/lib/sequel/plugins/serialization_modification_detection.rb +2 -1
- data/lib/sequel/plugins/single_table_inheritance.rb +7 -0
- data/lib/sequel/plugins/sql_comments.rb +189 -0
- data/lib/sequel/plugins/static_cache.rb +1 -1
- data/lib/sequel/plugins/string_stripper.rb +1 -1
- data/lib/sequel/plugins/subclasses.rb +28 -11
- data/lib/sequel/plugins/tactical_eager_loading.rb +8 -2
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/tree.rb +9 -4
- data/lib/sequel/plugins/unused_associations.rb +521 -0
- data/lib/sequel/plugins/update_or_create.rb +1 -1
- data/lib/sequel/plugins/validation_class_methods.rb +5 -1
- data/lib/sequel/plugins/validation_helpers.rb +18 -11
- data/lib/sequel/plugins/xml_serializer.rb +1 -1
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/timezones.rb +20 -17
- data/lib/sequel/version.rb +1 -1
- metadata +93 -39
@@ -164,11 +164,11 @@ module Sequel
|
|
164
164
|
# range to return the object(s) at the correct offset/limit.
|
165
165
|
def apply_ruby_eager_limit_strategy(rows, limit_and_offset = limit_and_offset())
|
166
166
|
name = self[:name]
|
167
|
+
return unless range = slice_range(limit_and_offset)
|
167
168
|
if returns_array?
|
168
|
-
range = slice_range(limit_and_offset)
|
169
169
|
rows.each{|o| o.associations[name] = o.associations[name][range] || []}
|
170
|
-
|
171
|
-
offset =
|
170
|
+
else
|
171
|
+
offset = range.begin
|
172
172
|
rows.each{|o| o.associations[name] = o.associations[name][offset]}
|
173
173
|
end
|
174
174
|
end
|
@@ -263,7 +263,9 @@ module Sequel
|
|
263
263
|
# yielding each row to the block.
|
264
264
|
def eager_load_results(eo, &block)
|
265
265
|
rows = eo[:rows]
|
266
|
-
|
266
|
+
unless eo[:initialize_rows] == false
|
267
|
+
Sequel.synchronize_with(eo[:mutex]){initialize_association_cache(rows)}
|
268
|
+
end
|
267
269
|
if eo[:id_map]
|
268
270
|
ids = eo[:id_map].keys
|
269
271
|
return ids if ids.empty?
|
@@ -272,7 +274,9 @@ module Sequel
|
|
272
274
|
cascade = eo[:associations]
|
273
275
|
eager_limit = nil
|
274
276
|
|
275
|
-
if eo[:
|
277
|
+
if eo[:no_results]
|
278
|
+
no_results = true
|
279
|
+
elsif eo[:eager_block] || eo[:loader] == false
|
276
280
|
ds = eager_loading_dataset(eo)
|
277
281
|
|
278
282
|
strategy = ds.opts[:eager_limit_strategy] || strategy
|
@@ -311,7 +315,8 @@ module Sequel
|
|
311
315
|
objects = loader.all(ids)
|
312
316
|
end
|
313
317
|
|
314
|
-
objects.each(&block)
|
318
|
+
Sequel.synchronize_with(eo[:mutex]){objects.each(&block)} unless no_results
|
319
|
+
|
315
320
|
if strategy == :ruby
|
316
321
|
apply_ruby_eager_limit_strategy(rows, eager_limit || limit_and_offset)
|
317
322
|
end
|
@@ -356,7 +361,7 @@ module Sequel
|
|
356
361
|
def finalize
|
357
362
|
return unless cache = self[:cache]
|
358
363
|
|
359
|
-
|
364
|
+
finalizer = proc do |meth, key|
|
360
365
|
next if has_key?(key)
|
361
366
|
|
362
367
|
# Allow calling private methods to make sure caching is done appropriately
|
@@ -364,6 +369,13 @@ module Sequel
|
|
364
369
|
self[key] = cache.delete(key) if cache.has_key?(key)
|
365
370
|
end
|
366
371
|
|
372
|
+
finalize_settings.each(&finalizer)
|
373
|
+
|
374
|
+
unless self[:instance_specific]
|
375
|
+
finalizer.call(:associated_eager_dataset, :associated_eager_dataset)
|
376
|
+
finalizer.call(:filter_by_associations_conditions_dataset, :filter_by_associations_conditions_dataset)
|
377
|
+
end
|
378
|
+
|
367
379
|
nil
|
368
380
|
end
|
369
381
|
|
@@ -371,9 +383,7 @@ module Sequel
|
|
371
383
|
FINALIZE_SETTINGS = {
|
372
384
|
:associated_class=>:class,
|
373
385
|
:associated_dataset=>:_dataset,
|
374
|
-
:associated_eager_dataset=>:associated_eager_dataset,
|
375
386
|
:eager_limit_strategy=>:_eager_limit_strategy,
|
376
|
-
:filter_by_associations_conditions_dataset=>:filter_by_associations_conditions_dataset,
|
377
387
|
:placeholder_loader=>:placeholder_loader,
|
378
388
|
:predicate_key=>:predicate_key,
|
379
389
|
:predicate_keys=>:predicate_keys,
|
@@ -432,7 +442,11 @@ module Sequel
|
|
432
442
|
if use_placeholder_loader?
|
433
443
|
cached_fetch(:placeholder_loader) do
|
434
444
|
Sequel::Dataset::PlaceholderLiteralizer.loader(associated_dataset) do |pl, ds|
|
435
|
-
ds.where(Sequel.&(*predicate_keys.map{|k| SQL::BooleanExpression.new(:'=', k, pl.arg)}))
|
445
|
+
ds = ds.where(Sequel.&(*predicate_keys.map{|k| SQL::BooleanExpression.new(:'=', k, pl.arg)}))
|
446
|
+
if self[:block]
|
447
|
+
ds = self[:block].call(ds)
|
448
|
+
end
|
449
|
+
ds
|
436
450
|
end
|
437
451
|
end
|
438
452
|
end
|
@@ -626,9 +640,7 @@ module Sequel
|
|
626
640
|
# given the hash passed to the eager loader.
|
627
641
|
def eager_loading_dataset(eo=OPTS)
|
628
642
|
ds = eo[:dataset] || associated_eager_dataset
|
629
|
-
|
630
|
-
ds = ds.where(eager_loading_predicate_condition(id_map.keys))
|
631
|
-
end
|
643
|
+
ds = eager_loading_set_predicate_condition(ds, eo)
|
632
644
|
if associations = eo[:associations]
|
633
645
|
ds = ds.eager(associations)
|
634
646
|
end
|
@@ -655,6 +667,15 @@ module Sequel
|
|
655
667
|
self[:model].default_eager_limit_strategy || :ruby
|
656
668
|
end
|
657
669
|
|
670
|
+
# Set the predicate condition for the eager loading dataset based on the id map
|
671
|
+
# in the eager loading options.
|
672
|
+
def eager_loading_set_predicate_condition(ds, eo)
|
673
|
+
if id_map = eo[:id_map]
|
674
|
+
ds = ds.where(eager_loading_predicate_condition(id_map.keys))
|
675
|
+
end
|
676
|
+
ds
|
677
|
+
end
|
678
|
+
|
658
679
|
# The predicate condition to use for the eager_loader.
|
659
680
|
def eager_loading_predicate_condition(keys)
|
660
681
|
{predicate_key=>keys}
|
@@ -796,7 +817,7 @@ module Sequel
|
|
796
817
|
|
797
818
|
# Whether the placeholder loader can be used to load the association.
|
798
819
|
def use_placeholder_loader?
|
799
|
-
|
820
|
+
self[:use_placeholder_loader]
|
800
821
|
end
|
801
822
|
end
|
802
823
|
|
@@ -1244,7 +1265,9 @@ module Sequel
|
|
1244
1265
|
else
|
1245
1266
|
assoc_record.values.delete(left_key_alias)
|
1246
1267
|
end
|
1247
|
-
|
1268
|
+
|
1269
|
+
objects = h[hash_key]
|
1270
|
+
|
1248
1271
|
if assign_singular
|
1249
1272
|
objects.each do |object|
|
1250
1273
|
object.associations[name] ||= assoc_record
|
@@ -1304,7 +1327,7 @@ module Sequel
|
|
1304
1327
|
|
1305
1328
|
# many_to_many associations need to select a key in an associated table to eagerly load
|
1306
1329
|
def eager_loading_use_associated_key?
|
1307
|
-
|
1330
|
+
!separate_query_per_table?
|
1308
1331
|
end
|
1309
1332
|
|
1310
1333
|
# The source of the join table. This is the join table itself, unless it
|
@@ -1361,10 +1384,30 @@ module Sequel
|
|
1361
1384
|
cached_fetch(:select){default_select}
|
1362
1385
|
end
|
1363
1386
|
|
1387
|
+
# Whether a separate query should be used for the join table.
|
1388
|
+
def separate_query_per_table?
|
1389
|
+
self[:join_table_db]
|
1390
|
+
end
|
1391
|
+
|
1364
1392
|
private
|
1365
1393
|
|
1394
|
+
# Join to the the join table, unless using a separate query per table.
|
1366
1395
|
def _associated_dataset
|
1367
|
-
|
1396
|
+
if separate_query_per_table?
|
1397
|
+
super
|
1398
|
+
else
|
1399
|
+
super.inner_join(self[:join_table], self[:right_keys].zip(right_primary_keys), :qualify=>:deep)
|
1400
|
+
end
|
1401
|
+
end
|
1402
|
+
|
1403
|
+
# Use the right_keys from the eager loading options if
|
1404
|
+
# using a separate query per table.
|
1405
|
+
def eager_loading_set_predicate_condition(ds, eo)
|
1406
|
+
if separate_query_per_table?
|
1407
|
+
ds.where(right_primary_key=>eo[:right_keys])
|
1408
|
+
else
|
1409
|
+
super
|
1410
|
+
end
|
1368
1411
|
end
|
1369
1412
|
|
1370
1413
|
# The default selection for associations that require joins. These do not use the default
|
@@ -1581,6 +1624,7 @@ module Sequel
|
|
1581
1624
|
# === Multiple Types
|
1582
1625
|
# :adder :: Proc used to define the private _add_* method for doing the database work
|
1583
1626
|
# to associate the given object to the current object (*_to_many assocations).
|
1627
|
+
# Set to nil to not define a add_* method for the association.
|
1584
1628
|
# :after_add :: Symbol, Proc, or array of both/either specifying a callback to call
|
1585
1629
|
# after a new item is added to the association.
|
1586
1630
|
# :after_load :: Symbol, Proc, or array of both/either specifying a callback to call
|
@@ -1591,6 +1635,8 @@ module Sequel
|
|
1591
1635
|
# after an item is set using the association setter method.
|
1592
1636
|
# :allow_eager :: If set to false, you cannot load the association eagerly
|
1593
1637
|
# via eager or eager_graph
|
1638
|
+
# :allow_eager_graph :: If set to false, you cannot load the association eagerly via eager_graph.
|
1639
|
+
# :allow_filtering_by :: If set to false, you cannot use the association when filtering
|
1594
1640
|
# :before_add :: Symbol, Proc, or array of both/either specifying a callback to call
|
1595
1641
|
# before a new item is added to the association.
|
1596
1642
|
# :before_remove :: Symbol, Proc, or array of both/either specifying a callback to call
|
@@ -1609,6 +1655,7 @@ module Sequel
|
|
1609
1655
|
# the class. <tt>class: 'Foo', class_namespace: 'Bar'</tt> looks for <tt>::Bar::Foo</tt>.)
|
1610
1656
|
# :clearer :: Proc used to define the private _remove_all_* method for doing the database work
|
1611
1657
|
# to remove all objects associated to the current object (*_to_many assocations).
|
1658
|
+
# Set to nil to not define a remove_all_* method for the association.
|
1612
1659
|
# :clone :: Merge the current options and block into the options and block used in defining
|
1613
1660
|
# the given association. Can be used to DRY up a bunch of similar associations that
|
1614
1661
|
# all share the same options such as :class and :key, while changing the order and block used.
|
@@ -1663,7 +1710,7 @@ module Sequel
|
|
1663
1710
|
# :graph_only_conditions :: The conditions to use on the SQL join when eagerly loading
|
1664
1711
|
# the association via +eager_graph+, instead of the default conditions specified by the
|
1665
1712
|
# foreign/primary keys. This option causes the :graph_conditions option to be ignored.
|
1666
|
-
# :graph_order ::
|
1713
|
+
# :graph_order :: the order to use when using eager_graph, instead of the default order. This should be used
|
1667
1714
|
# in the case where :order contains an identifier qualified by the table's name, which may not match
|
1668
1715
|
# the alias used when eager graphing. By setting this to the unqualified identifier, it will be
|
1669
1716
|
# automatically qualified when using eager_graph.
|
@@ -1675,6 +1722,10 @@ module Sequel
|
|
1675
1722
|
# limit (first element) and an offset (second element).
|
1676
1723
|
# :methods_module :: The module that methods the association creates will be placed into. Defaults
|
1677
1724
|
# to the module containing the model's columns.
|
1725
|
+
# :no_association_method :: Do not add a method for the association. This can save memory if the association
|
1726
|
+
# method is never used.
|
1727
|
+
# :no_dataset_method :: Do not add a method for the association dataset. This can save memory if the dataset
|
1728
|
+
# method is never used.
|
1678
1729
|
# :order :: the column(s) by which to order the association dataset. Can be a
|
1679
1730
|
# singular column symbol or an array of column symbols.
|
1680
1731
|
# :order_eager_graph :: Whether to add the association's order to the graphed dataset's order when graphing
|
@@ -1687,6 +1738,7 @@ module Sequel
|
|
1687
1738
|
# the current association's key(s). Set to nil to not use a reciprocal.
|
1688
1739
|
# :remover :: Proc used to define the private _remove_* method for doing the database work
|
1689
1740
|
# to remove the association between the given object and the current object (*_to_many assocations).
|
1741
|
+
# Set to nil to not define a remove_* method for the association.
|
1690
1742
|
# :select :: the columns to select. Defaults to the associated class's table_name.* in an association
|
1691
1743
|
# that uses joins, which means it doesn't include the attributes from the
|
1692
1744
|
# join table. If you want to include the join table attributes, you can
|
@@ -1695,6 +1747,7 @@ module Sequel
|
|
1695
1747
|
# the same name in both the join table and the associated table.
|
1696
1748
|
# :setter :: Proc used to define the private _*= method for doing the work to setup the assocation
|
1697
1749
|
# between the given object and the current object (*_to_one associations).
|
1750
|
+
# Set to nil to not define a setter method for the association.
|
1698
1751
|
# :subqueries_per_union :: The number of subqueries to use in each UNION query, for eager
|
1699
1752
|
# loading limited associations using the default :union strategy.
|
1700
1753
|
# :validate :: Set to false to not validate when implicitly saving any associated object.
|
@@ -1751,6 +1804,9 @@ module Sequel
|
|
1751
1804
|
# underscored, sorted, and joined with '_'.
|
1752
1805
|
# :join_table_block :: proc that can be used to modify the dataset used in the add/remove/remove_all
|
1753
1806
|
# methods. Should accept a dataset argument and return a modified dataset if present.
|
1807
|
+
# :join_table_db :: When retrieving records when using lazy loading or eager loading via +eager+, instead of
|
1808
|
+
# a join between to the join table and the associated table, use a separate query for the
|
1809
|
+
# join table using the given Database object.
|
1754
1810
|
# :left_key :: foreign key in join table that points to current model's
|
1755
1811
|
# primary key, as a symbol. Defaults to :"#{self.name.underscore}_id".
|
1756
1812
|
# Can use an array of symbols for a composite key association.
|
@@ -1780,7 +1836,9 @@ module Sequel
|
|
1780
1836
|
|
1781
1837
|
if opts[:clone]
|
1782
1838
|
cloned_assoc = association_reflection(opts[:clone])
|
1839
|
+
remove_class_name = orig_opts[:class] && !orig_opts[:class_name]
|
1783
1840
|
orig_opts = cloned_assoc[:orig_opts].merge(orig_opts)
|
1841
|
+
orig_opts.delete(:class_name) if remove_class_name
|
1784
1842
|
end
|
1785
1843
|
|
1786
1844
|
opts = Hash[default_association_options]
|
@@ -1791,11 +1849,12 @@ module Sequel
|
|
1791
1849
|
opts.merge!(:type => type, :name => name, :cache=>({} if cache_associations), :model => self)
|
1792
1850
|
|
1793
1851
|
opts[:block] = block if block
|
1794
|
-
|
1852
|
+
opts[:instance_specific] = true if orig_opts[:dataset]
|
1853
|
+
if !opts.has_key?(:instance_specific) && (block || orig_opts[:block])
|
1795
1854
|
# It's possible the association is instance specific, in that it depends on
|
1796
1855
|
# values other than the foreign key value. This needs to be checked for
|
1797
1856
|
# in certain places to disable optimizations.
|
1798
|
-
opts[:instance_specific] =
|
1857
|
+
opts[:instance_specific] = _association_instance_specific_default(name)
|
1799
1858
|
end
|
1800
1859
|
opts = assoc_class.new.merge!(opts)
|
1801
1860
|
|
@@ -1803,6 +1862,7 @@ module Sequel
|
|
1803
1862
|
raise(Error, "cannot clone an association to an association of different type (association #{name} with type #{type} cloning #{opts[:clone]} with type #{cloned_assoc[:type]})")
|
1804
1863
|
end
|
1805
1864
|
|
1865
|
+
opts[:use_placeholder_loader] = !opts[:instance_specific] && !opts[:eager_graph]
|
1806
1866
|
opts[:eager_block] = opts[:block] unless opts.include?(:eager_block)
|
1807
1867
|
opts[:graph_join_type] ||= :left_outer
|
1808
1868
|
opts[:order_eager_graph] = true unless opts.include?(:order_eager_graph)
|
@@ -1825,8 +1885,7 @@ module Sequel
|
|
1825
1885
|
# Remove :class entry if it exists and is nil, to work with cached_fetch
|
1826
1886
|
opts.delete(:class) unless opts[:class]
|
1827
1887
|
|
1828
|
-
|
1829
|
-
def_association_instance_methods(opts)
|
1888
|
+
def_association(opts)
|
1830
1889
|
|
1831
1890
|
orig_opts.delete(:clone)
|
1832
1891
|
opts[:orig_class] = orig_opts[:class] || orig_opts[:class_name]
|
@@ -1899,6 +1958,12 @@ module Sequel
|
|
1899
1958
|
Plugins.def_dataset_methods(self, [:eager, :eager_graph, :eager_graph_with_options, :association_join, :association_full_join, :association_inner_join, :association_left_join, :association_right_join])
|
1900
1959
|
|
1901
1960
|
private
|
1961
|
+
|
1962
|
+
# The default value for the instance_specific option, if the association
|
1963
|
+
# could be instance specific and the :instance_specific option is not specified.
|
1964
|
+
def _association_instance_specific_default(_)
|
1965
|
+
true
|
1966
|
+
end
|
1902
1967
|
|
1903
1968
|
# The module to use for the association's methods. Defaults to
|
1904
1969
|
# the overridable_methods_module.
|
@@ -1910,7 +1975,22 @@ module Sequel
|
|
1910
1975
|
# can be easily overridden in the class itself while allowing for
|
1911
1976
|
# super to be called.
|
1912
1977
|
def association_module_def(name, opts=OPTS, &block)
|
1913
|
-
association_module(opts)
|
1978
|
+
mod = association_module(opts)
|
1979
|
+
mod.send(:define_method, name, &block)
|
1980
|
+
mod.send(:alias_method, name, name)
|
1981
|
+
end
|
1982
|
+
|
1983
|
+
# Add a method to the module included in the class, so the method
|
1984
|
+
# can be easily overridden in the class itself while allowing for
|
1985
|
+
# super to be called. This method allows passing keywords through
|
1986
|
+
# the defined methods.
|
1987
|
+
def association_module_delegate_def(name, opts, &block)
|
1988
|
+
mod = association_module(opts)
|
1989
|
+
mod.send(:define_method, name, &block)
|
1990
|
+
# :nocov:
|
1991
|
+
mod.send(:ruby2_keywords, name) if mod.respond_to?(:ruby2_keywords, true)
|
1992
|
+
# :nocov:
|
1993
|
+
mod.send(:alias_method, name, name)
|
1914
1994
|
end
|
1915
1995
|
|
1916
1996
|
# Add a private method to the module included in the class.
|
@@ -1919,6 +1999,13 @@ module Sequel
|
|
1919
1999
|
association_module(opts).send(:private, name)
|
1920
2000
|
end
|
1921
2001
|
|
2002
|
+
# Delegate to the type-specific association method to setup the
|
2003
|
+
# association, and define the association instance methods.
|
2004
|
+
def def_association(opts)
|
2005
|
+
send(:"def_#{opts[:type]}", opts)
|
2006
|
+
def_association_instance_methods(opts)
|
2007
|
+
end
|
2008
|
+
|
1922
2009
|
# Adds the association method to the association methods module.
|
1923
2010
|
def def_association_method(opts)
|
1924
2011
|
association_module_def(opts.association_method, opts) do |dynamic_opts=OPTS, &block|
|
@@ -1944,15 +2031,13 @@ module Sequel
|
|
1944
2031
|
opts[:setter_method] = :"#{opts[:name]}="
|
1945
2032
|
end
|
1946
2033
|
|
1947
|
-
association_module_def(opts.dataset_method, opts){_dataset(opts)}
|
2034
|
+
association_module_def(opts.dataset_method, opts){_dataset(opts)} unless opts[:no_dataset_method]
|
1948
2035
|
if opts[:block]
|
1949
2036
|
opts[:block_method] = Plugins.def_sequel_method(association_module(opts), "#{opts[:name]}_block", 1, &opts[:block])
|
1950
2037
|
end
|
1951
|
-
|
1952
|
-
|
1953
|
-
|
1954
|
-
end
|
1955
|
-
def_association_method(opts)
|
2038
|
+
opts[:dataset_opt_arity] = opts[:dataset].arity == 0 ? 0 : 1
|
2039
|
+
opts[:dataset_opt_method] = Plugins.def_sequel_method(association_module(opts), "#{opts[:name]}_dataset_opt", opts[:dataset_opt_arity], &opts[:dataset])
|
2040
|
+
def_association_method(opts) unless opts[:no_association_method]
|
1956
2041
|
|
1957
2042
|
return if opts[:read_only]
|
1958
2043
|
|
@@ -1964,17 +2049,17 @@ module Sequel
|
|
1964
2049
|
|
1965
2050
|
if adder = opts[:adder]
|
1966
2051
|
association_module_private_def(opts[:_add_method], opts, &adder)
|
1967
|
-
|
2052
|
+
association_module_delegate_def(opts[:add_method], opts){|o,*args| add_associated_object(opts, o, *args)}
|
1968
2053
|
end
|
1969
2054
|
|
1970
2055
|
if remover = opts[:remover]
|
1971
2056
|
association_module_private_def(opts[:_remove_method], opts, &remover)
|
1972
|
-
|
2057
|
+
association_module_delegate_def(opts[:remove_method], opts){|o,*args| remove_associated_object(opts, o, *args)}
|
1973
2058
|
end
|
1974
2059
|
|
1975
2060
|
if clearer = opts[:clearer]
|
1976
2061
|
association_module_private_def(opts[:_remove_all_method], opts, &clearer)
|
1977
|
-
|
2062
|
+
association_module_delegate_def(opts[:remove_all_method], opts){|*args| remove_all_associated_objects(opts, *args)}
|
1978
2063
|
end
|
1979
2064
|
end
|
1980
2065
|
|
@@ -1996,7 +2081,7 @@ module Sequel
|
|
1996
2081
|
raise(Error, "mismatched number of right keys: #{rcks.inspect} vs #{rcpks.inspect}") unless rcks.length == rcpks.length
|
1997
2082
|
end
|
1998
2083
|
opts[:uses_left_composite_keys] = lcks.length > 1
|
1999
|
-
opts[:uses_right_composite_keys] = rcks.length > 1
|
2084
|
+
uses_rcks = opts[:uses_right_composite_keys] = rcks.length > 1
|
2000
2085
|
opts[:cartesian_product_number] ||= one_through_one ? 0 : 1
|
2001
2086
|
join_table = (opts[:join_table] ||= opts.default_join_table)
|
2002
2087
|
opts[:left_key_alias] ||= opts.default_associated_key_alias
|
@@ -2005,8 +2090,75 @@ module Sequel
|
|
2005
2090
|
opts[:after_load] ||= []
|
2006
2091
|
opts[:after_load].unshift(:array_uniq!)
|
2007
2092
|
end
|
2008
|
-
opts[:
|
2009
|
-
|
2093
|
+
if join_table_db = opts[:join_table_db]
|
2094
|
+
opts[:use_placeholder_loader] = false
|
2095
|
+
opts[:allow_eager_graph] = false
|
2096
|
+
opts[:allow_filtering_by] = false
|
2097
|
+
opts[:eager_limit_strategy] = nil
|
2098
|
+
join_table_ds = join_table_db.from(join_table)
|
2099
|
+
opts[:dataset] ||= proc do |r|
|
2100
|
+
vals = join_table_ds.where(lcks.zip(lcpks.map{|k| get_column_value(k)})).select_map(right)
|
2101
|
+
ds = r.associated_dataset.where(opts.right_primary_key => vals)
|
2102
|
+
if uses_rcks
|
2103
|
+
vals.delete_if{|v| v.any?(&:nil?)}
|
2104
|
+
else
|
2105
|
+
vals.delete(nil)
|
2106
|
+
end
|
2107
|
+
ds = ds.clone(:no_results=>true) if vals.empty?
|
2108
|
+
ds
|
2109
|
+
end
|
2110
|
+
opts[:eager_loader] ||= proc do |eo|
|
2111
|
+
h = eo[:id_map]
|
2112
|
+
assign_singular = opts.assign_singular?
|
2113
|
+
rpk = opts.right_primary_key
|
2114
|
+
name = opts[:name]
|
2115
|
+
|
2116
|
+
join_map = join_table_ds.where(left=>h.keys).select_hash_groups(right, left)
|
2117
|
+
|
2118
|
+
if uses_rcks
|
2119
|
+
join_map.delete_if{|v,| v.any?(&:nil?)}
|
2120
|
+
else
|
2121
|
+
join_map.delete(nil)
|
2122
|
+
end
|
2123
|
+
|
2124
|
+
eo = Hash[eo]
|
2125
|
+
|
2126
|
+
if join_map.empty?
|
2127
|
+
eo[:no_results] = true
|
2128
|
+
else
|
2129
|
+
join_map.each_value do |vs|
|
2130
|
+
vs.replace(vs.flat_map{|v| h[v]})
|
2131
|
+
vs.uniq!
|
2132
|
+
end
|
2133
|
+
|
2134
|
+
eo[:loader] = false
|
2135
|
+
eo[:right_keys] = join_map.keys
|
2136
|
+
end
|
2137
|
+
|
2138
|
+
opts[:model].eager_load_results(opts, eo) do |assoc_record|
|
2139
|
+
rpkv = if uses_rcks
|
2140
|
+
assoc_record.values.values_at(*rpk)
|
2141
|
+
else
|
2142
|
+
assoc_record.values[rpk]
|
2143
|
+
end
|
2144
|
+
|
2145
|
+
objects = join_map[rpkv]
|
2146
|
+
|
2147
|
+
if assign_singular
|
2148
|
+
objects.each do |object|
|
2149
|
+
object.associations[name] ||= assoc_record
|
2150
|
+
end
|
2151
|
+
else
|
2152
|
+
objects.each do |object|
|
2153
|
+
object.associations[name].push(assoc_record)
|
2154
|
+
end
|
2155
|
+
end
|
2156
|
+
end
|
2157
|
+
end
|
2158
|
+
else
|
2159
|
+
opts[:dataset] ||= opts.association_dataset_proc
|
2160
|
+
opts[:eager_loader] ||= opts.method(:default_eager_loader)
|
2161
|
+
end
|
2010
2162
|
|
2011
2163
|
join_type = opts[:graph_join_type]
|
2012
2164
|
select = opts[:graph_select]
|
@@ -2040,50 +2192,60 @@ module Sequel
|
|
2040
2192
|
return if opts[:read_only]
|
2041
2193
|
|
2042
2194
|
if one_through_one
|
2043
|
-
opts
|
2044
|
-
|
2045
|
-
|
2046
|
-
|
2195
|
+
unless opts.has_key?(:setter)
|
2196
|
+
opts[:setter] = proc do |o|
|
2197
|
+
h = {}
|
2198
|
+
lh = lcks.zip(lcpks.map{|k| get_column_value(k)})
|
2199
|
+
jtds = _join_table_dataset(opts).where(lh)
|
2047
2200
|
|
2048
|
-
|
2049
|
-
|
2201
|
+
checked_transaction do
|
2202
|
+
current = jtds.first
|
2050
2203
|
|
2051
|
-
if o
|
2052
|
-
new_values = []
|
2053
|
-
rcks.zip(opts.right_primary_key_methods).each{|k, pk| new_values << (h[k] = o.get_column_value(pk))}
|
2054
|
-
end
|
2055
|
-
|
2056
|
-
if current
|
2057
|
-
current_values = rcks.map{|k| current[k]}
|
2058
|
-
jtds = jtds.where(rcks.zip(current_values))
|
2059
2204
|
if o
|
2060
|
-
|
2061
|
-
|
2205
|
+
new_values = []
|
2206
|
+
rcks.zip(opts.right_primary_key_methods).each{|k, pk| new_values << (h[k] = o.get_column_value(pk))}
|
2207
|
+
end
|
2208
|
+
|
2209
|
+
if current
|
2210
|
+
current_values = rcks.map{|k| current[k]}
|
2211
|
+
jtds = jtds.where(rcks.zip(current_values))
|
2212
|
+
if o
|
2213
|
+
if current_values != new_values
|
2214
|
+
jtds.update(h)
|
2215
|
+
end
|
2216
|
+
else
|
2217
|
+
jtds.delete
|
2062
2218
|
end
|
2063
|
-
|
2064
|
-
|
2219
|
+
elsif o
|
2220
|
+
lh.each{|k,v| h[k] = v}
|
2221
|
+
jtds.insert(h)
|
2065
2222
|
end
|
2066
|
-
elsif o
|
2067
|
-
lh.each{|k,v| h[k] = v}
|
2068
|
-
jtds.insert(h)
|
2069
2223
|
end
|
2070
2224
|
end
|
2071
2225
|
end
|
2072
|
-
opts
|
2226
|
+
if opts.fetch(:setter, true)
|
2227
|
+
opts[:_setter] = proc{|o| set_one_through_one_associated_object(opts, o)}
|
2228
|
+
end
|
2073
2229
|
else
|
2074
|
-
opts
|
2075
|
-
|
2076
|
-
|
2077
|
-
|
2078
|
-
|
2230
|
+
unless opts.has_key?(:adder)
|
2231
|
+
opts[:adder] = proc do |o|
|
2232
|
+
h = {}
|
2233
|
+
lcks.zip(lcpks).each{|k, pk| h[k] = get_column_value(pk)}
|
2234
|
+
rcks.zip(opts.right_primary_key_methods).each{|k, pk| h[k] = o.get_column_value(pk)}
|
2235
|
+
_join_table_dataset(opts).insert(h)
|
2236
|
+
end
|
2079
2237
|
end
|
2080
2238
|
|
2081
|
-
opts
|
2082
|
-
|
2239
|
+
unless opts.has_key?(:remover)
|
2240
|
+
opts[:remover] = proc do |o|
|
2241
|
+
_join_table_dataset(opts).where(lcks.zip(lcpks.map{|k| get_column_value(k)}) + rcks.zip(opts.right_primary_key_methods.map{|k| o.get_column_value(k)})).delete
|
2242
|
+
end
|
2083
2243
|
end
|
2084
2244
|
|
2085
|
-
opts
|
2086
|
-
|
2245
|
+
unless opts.has_key?(:clearer)
|
2246
|
+
opts[:clearer] = proc do
|
2247
|
+
_join_table_dataset(opts).where(lcks.zip(lcpks.map{|k| get_column_value(k)})).delete
|
2248
|
+
end
|
2087
2249
|
end
|
2088
2250
|
end
|
2089
2251
|
end
|
@@ -2122,9 +2284,7 @@ module Sequel
|
|
2122
2284
|
|
2123
2285
|
eager_load_results(opts, eo) do |assoc_record|
|
2124
2286
|
hash_key = uses_cks ? pk_meths.map{|k| assoc_record.get_column_value(k)} : assoc_record.get_column_value(opts.primary_key_method)
|
2125
|
-
|
2126
|
-
objects.each{|object| object.associations[name] = assoc_record}
|
2127
|
-
end
|
2287
|
+
h[hash_key].each{|object| object.associations[name] = assoc_record}
|
2128
2288
|
end
|
2129
2289
|
end
|
2130
2290
|
|
@@ -2142,8 +2302,12 @@ module Sequel
|
|
2142
2302
|
|
2143
2303
|
return if opts[:read_only]
|
2144
2304
|
|
2145
|
-
|
2146
|
-
|
2305
|
+
unless opts.has_key?(:setter)
|
2306
|
+
opts[:setter] = proc{|o| cks.zip(opts.primary_key_methods).each{|k, pk| set_column_value(:"#{k}=", (o.get_column_value(pk) if o))}}
|
2307
|
+
end
|
2308
|
+
if opts.fetch(:setter, true)
|
2309
|
+
opts[:_setter] = proc{|o| set_associated_object(opts, o)}
|
2310
|
+
end
|
2147
2311
|
end
|
2148
2312
|
|
2149
2313
|
# Configures one_to_many and one_to_one association reflections and adds the related association methods
|
@@ -2171,7 +2335,7 @@ module Sequel
|
|
2171
2335
|
eager_load_results(opts, eo) do |assoc_record|
|
2172
2336
|
assoc_record.values.delete(delete_rn) if delete_rn
|
2173
2337
|
hash_key = uses_cks ? km.map{|k| assoc_record.get_column_value(k)} : assoc_record.get_column_value(km)
|
2174
|
-
|
2338
|
+
objects = h[hash_key]
|
2175
2339
|
if assign_singular
|
2176
2340
|
objects.each do |object|
|
2177
2341
|
unless object.associations[name]
|
@@ -2210,49 +2374,59 @@ module Sequel
|
|
2210
2374
|
cks.each{|k| ck_nil_hash[k] = nil}
|
2211
2375
|
|
2212
2376
|
if one_to_one
|
2213
|
-
opts
|
2214
|
-
|
2377
|
+
unless opts.has_key?(:setter)
|
2378
|
+
opts[:setter] = proc do |o|
|
2379
|
+
up_ds = _apply_association_options(opts, opts.associated_dataset.where(cks.zip(cpks.map{|k| get_column_value(k)})))
|
2215
2380
|
|
2216
|
-
|
2217
|
-
|
2218
|
-
|
2381
|
+
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)))
|
2382
|
+
if old = up_ds.first
|
2383
|
+
cks.each{|k| old.set_column_value(:"#{k}=", nil)}
|
2384
|
+
end
|
2385
|
+
save_old = true
|
2219
2386
|
end
|
2220
|
-
save_old = true
|
2221
|
-
end
|
2222
2387
|
|
2223
|
-
|
2224
|
-
|
2225
|
-
|
2388
|
+
if o
|
2389
|
+
if !o.new? && !save_old
|
2390
|
+
up_ds = up_ds.exclude(o.pk_hash)
|
2391
|
+
end
|
2392
|
+
cks.zip(cpks).each{|k, pk| o.set_column_value(:"#{k}=", get_column_value(pk))}
|
2226
2393
|
end
|
2227
|
-
cks.zip(cpks).each{|k, pk| o.set_column_value(:"#{k}=", get_column_value(pk))}
|
2228
|
-
end
|
2229
2394
|
|
2230
|
-
|
2231
|
-
|
2232
|
-
|
2233
|
-
|
2234
|
-
|
2235
|
-
|
2395
|
+
checked_transaction do
|
2396
|
+
if save_old
|
2397
|
+
old.save(save_opts) || raise(Sequel::Error, "invalid previously associated object, cannot save") if old
|
2398
|
+
else
|
2399
|
+
up_ds.skip_limit_check.update(ck_nil_hash)
|
2400
|
+
end
|
2236
2401
|
|
2237
|
-
|
2402
|
+
o.save(save_opts) || raise(Sequel::Error, "invalid associated object, cannot save") if o
|
2403
|
+
end
|
2238
2404
|
end
|
2239
2405
|
end
|
2240
|
-
opts
|
2406
|
+
if opts.fetch(:setter, true)
|
2407
|
+
opts[:_setter] = proc{|o| set_one_to_one_associated_object(opts, o)}
|
2408
|
+
end
|
2241
2409
|
else
|
2242
2410
|
save_opts[:raise_on_failure] = opts[:raise_on_save_failure] != false
|
2243
2411
|
|
2244
|
-
opts
|
2245
|
-
|
2246
|
-
|
2412
|
+
unless opts.has_key?(:adder)
|
2413
|
+
opts[:adder] = proc do |o|
|
2414
|
+
cks.zip(cpks).each{|k, pk| o.set_column_value(:"#{k}=", get_column_value(pk))}
|
2415
|
+
o.save(save_opts)
|
2416
|
+
end
|
2247
2417
|
end
|
2248
2418
|
|
2249
|
-
opts
|
2250
|
-
|
2251
|
-
|
2419
|
+
unless opts.has_key?(:remover)
|
2420
|
+
opts[:remover] = proc do |o|
|
2421
|
+
cks.each{|k| o.set_column_value(:"#{k}=", nil)}
|
2422
|
+
o.save(save_opts)
|
2423
|
+
end
|
2252
2424
|
end
|
2253
2425
|
|
2254
|
-
opts
|
2255
|
-
|
2426
|
+
unless opts.has_key?(:clearer)
|
2427
|
+
opts[:clearer] = proc do
|
2428
|
+
_apply_association_options(opts, opts.associated_dataset.where(cks.zip(cpks.map{|k| get_column_value(k)}))).update(ck_nil_hash)
|
2429
|
+
end
|
2256
2430
|
end
|
2257
2431
|
end
|
2258
2432
|
end
|
@@ -2346,7 +2520,7 @@ module Sequel
|
|
2346
2520
|
|
2347
2521
|
# Dataset for the join table of the given many to many association reflection
|
2348
2522
|
def _join_table_dataset(opts)
|
2349
|
-
ds = model.db.from(opts.join_table_source)
|
2523
|
+
ds = (opts[:join_table_db] || model.db).from(opts.join_table_source)
|
2350
2524
|
opts[:join_table_block] ? opts[:join_table_block].call(ds) : ds
|
2351
2525
|
end
|
2352
2526
|
|
@@ -2367,7 +2541,12 @@ module Sequel
|
|
2367
2541
|
if loader = _associated_object_loader(opts, dynamic_opts)
|
2368
2542
|
loader.all(*opts.predicate_key_values(self))
|
2369
2543
|
else
|
2370
|
-
_associated_dataset(opts, dynamic_opts)
|
2544
|
+
ds = _associated_dataset(opts, dynamic_opts)
|
2545
|
+
if ds.opts[:no_results]
|
2546
|
+
[]
|
2547
|
+
else
|
2548
|
+
ds.all
|
2549
|
+
end
|
2371
2550
|
end
|
2372
2551
|
end
|
2373
2552
|
|
@@ -2408,6 +2587,9 @@ module Sequel
|
|
2408
2587
|
run_association_callbacks(opts, :after_add, o)
|
2409
2588
|
o
|
2410
2589
|
end
|
2590
|
+
# :nocov:
|
2591
|
+
ruby2_keywords(:add_associated_object) if respond_to?(:ruby2_keywords, true)
|
2592
|
+
# :nocov:
|
2411
2593
|
|
2412
2594
|
# Add/Set the current object to/as the given object's reciprocal association.
|
2413
2595
|
def add_reciprocal_object(opts, o)
|
@@ -2550,6 +2732,9 @@ module Sequel
|
|
2550
2732
|
associations[opts[:name]] = []
|
2551
2733
|
ret
|
2552
2734
|
end
|
2735
|
+
# :nocov:
|
2736
|
+
ruby2_keywords(:remove_all_associated_objects) if respond_to?(:ruby2_keywords, true)
|
2737
|
+
# :nocov:
|
2553
2738
|
|
2554
2739
|
# Remove the given associated object from the given association
|
2555
2740
|
def remove_associated_object(opts, o, *args)
|
@@ -2571,6 +2756,9 @@ module Sequel
|
|
2571
2756
|
run_association_callbacks(opts, :after_remove, o)
|
2572
2757
|
o
|
2573
2758
|
end
|
2759
|
+
# :nocov:
|
2760
|
+
ruby2_keywords(:remove_associated_object) if respond_to?(:ruby2_keywords, true)
|
2761
|
+
# :nocov:
|
2574
2762
|
|
2575
2763
|
# Check that the object from the associated table specified by the primary key
|
2576
2764
|
# is currently associated to the receiver. If it is associated, return the object, otherwise
|
@@ -2819,6 +3007,8 @@ module Sequel
|
|
2819
3007
|
(multiple = ((op == :IN || op == :'NOT IN') && ((is_ds = r.is_a?(Sequel::Dataset)) || (r.respond_to?(:all?) && r.all?{|x| x.is_a?(Sequel::Model)})))))
|
2820
3008
|
l = args[0]
|
2821
3009
|
if ar = model.association_reflections[l]
|
3010
|
+
raise Error, "filtering by associations is not allowed for #{ar.inspect}" if ar[:allow_filtering_by] == false
|
3011
|
+
|
2822
3012
|
if multiple
|
2823
3013
|
klass = ar.associated_class
|
2824
3014
|
if is_ds
|
@@ -2940,7 +3130,7 @@ module Sequel
|
|
2940
3130
|
|
2941
3131
|
# The secondary eager loading method. Loads all associations in a single query. This
|
2942
3132
|
# method should only be used if you need to filter or order based on columns in associated tables,
|
2943
|
-
# or if you have done comparative benchmarking
|
3133
|
+
# or if you have done comparative benchmarking and determined it is faster.
|
2944
3134
|
#
|
2945
3135
|
# This method uses <tt>Dataset#graph</tt> to create appropriate aliases for columns in all the
|
2946
3136
|
# tables. Then it uses the graph's metadata to build the associations from the single hash, and
|
@@ -2969,6 +3159,8 @@ module Sequel
|
|
2969
3159
|
# You can specify an custom alias and/or join type on a per-association basis by providing an
|
2970
3160
|
# Sequel::SQL::AliasedExpression object instead of an a Symbol for the association name.
|
2971
3161
|
#
|
3162
|
+
# You cannot mix calls to +eager_graph+ and +graph+ on the same dataset.
|
3163
|
+
#
|
2972
3164
|
# Examples:
|
2973
3165
|
#
|
2974
3166
|
# # For each album, eager_graph load the artist
|
@@ -3064,6 +3256,8 @@ module Sequel
|
|
3064
3256
|
# significantly slower in some cases (perhaps even the majority of cases), so you should
|
3065
3257
|
# only use this if you have benchmarked that it is faster for your use cases.
|
3066
3258
|
def eager_graph_with_options(associations, opts=OPTS)
|
3259
|
+
return self if associations.empty?
|
3260
|
+
|
3067
3261
|
opts = opts.dup unless opts.frozen?
|
3068
3262
|
associations = [associations] unless associations.is_a?(Array)
|
3069
3263
|
ds = if eg = @opts[:eager_graph]
|
@@ -3190,7 +3384,6 @@ module Sequel
|
|
3190
3384
|
# requirements :: an array, used as a stack for requirements
|
3191
3385
|
# *associations :: the associations to add to the graph
|
3192
3386
|
def eager_graph_associations(ds, model, ta, requirements, *associations)
|
3193
|
-
return ds if associations.empty?
|
3194
3387
|
associations.flatten.each do |association|
|
3195
3388
|
ds = case association
|
3196
3389
|
when Symbol, SQL::AliasedExpression
|
@@ -3311,7 +3504,7 @@ module Sequel
|
|
3311
3504
|
# Allow associations that are eagerly graphed to be specified as an SQL::AliasedExpression, for
|
3312
3505
|
# per-call determining of the alias base.
|
3313
3506
|
def eager_graph_check_association(model, association)
|
3314
|
-
if association.is_a?(SQL::AliasedExpression)
|
3507
|
+
reflection = if association.is_a?(SQL::AliasedExpression)
|
3315
3508
|
expr = association.expression
|
3316
3509
|
if expr.is_a?(SQL::Identifier)
|
3317
3510
|
expr = expr.value
|
@@ -3320,10 +3513,17 @@ module Sequel
|
|
3320
3513
|
end
|
3321
3514
|
end
|
3322
3515
|
|
3323
|
-
|
3516
|
+
check_reflection = check_association(model, expr)
|
3517
|
+
SQL::AliasedExpression.new(check_reflection, association.alias || expr, association.columns)
|
3324
3518
|
else
|
3325
|
-
check_association(model, association)
|
3519
|
+
check_reflection = check_association(model, association)
|
3326
3520
|
end
|
3521
|
+
|
3522
|
+
if check_reflection && check_reflection[:allow_eager_graph] == false
|
3523
|
+
raise Error, "eager_graph not allowed for #{reflection.inspect}"
|
3524
|
+
end
|
3525
|
+
|
3526
|
+
reflection
|
3327
3527
|
end
|
3328
3528
|
|
3329
3529
|
# The EagerGraphLoader instance used for converting eager_graph results.
|
@@ -3334,15 +3534,30 @@ module Sequel
|
|
3334
3534
|
egl.dup
|
3335
3535
|
end
|
3336
3536
|
|
3337
|
-
# Eagerly load all specified associations
|
3537
|
+
# Eagerly load all specified associations.
|
3338
3538
|
def eager_load(a, eager_assoc=@opts[:eager])
|
3339
3539
|
return if a.empty?
|
3540
|
+
|
3541
|
+
# Reflections for all associations to eager load
|
3542
|
+
reflections = eager_assoc.keys.map{|assoc| model.association_reflection(assoc) || (raise Sequel::UndefinedAssociation, "Model: #{self}, Association: #{assoc}")}
|
3543
|
+
|
3544
|
+
perform_eager_loads(prepare_eager_load(a, reflections, eager_assoc))
|
3545
|
+
|
3546
|
+
reflections.each do |r|
|
3547
|
+
a.each{|object| object.send(:run_association_callbacks, r, :after_load, object.associations[r[:name]])} if r[:after_load]
|
3548
|
+
end
|
3549
|
+
|
3550
|
+
nil
|
3551
|
+
end
|
3552
|
+
|
3553
|
+
# Prepare a hash loaders and eager options which will be used to implement the eager loading.
|
3554
|
+
def prepare_eager_load(a, reflections, eager_assoc)
|
3555
|
+
eager_load_data = {}
|
3556
|
+
|
3340
3557
|
# Key is foreign/primary key name symbol.
|
3341
3558
|
# Value is hash with keys being foreign/primary key values (generally integers)
|
3342
3559
|
# and values being an array of current model objects with that specific foreign/primary key
|
3343
3560
|
key_hash = {}
|
3344
|
-
# Reflections for all associations to eager load
|
3345
|
-
reflections = eager_assoc.keys.map{|assoc| model.association_reflection(assoc) || (raise Sequel::UndefinedAssociation, "Model: #{self}, Association: #{assoc}")}
|
3346
3561
|
|
3347
3562
|
# Populate the key_hash entry for each association being eagerly loaded
|
3348
3563
|
reflections.each do |r|
|
@@ -3373,7 +3588,6 @@ module Sequel
|
|
3373
3588
|
id_map = nil
|
3374
3589
|
end
|
3375
3590
|
|
3376
|
-
loader = r[:eager_loader]
|
3377
3591
|
associations = eager_assoc[r[:name]]
|
3378
3592
|
if associations.respond_to?(:call)
|
3379
3593
|
eager_block = associations
|
@@ -3381,9 +3595,23 @@ module Sequel
|
|
3381
3595
|
elsif associations.is_a?(Hash) && associations.length == 1 && (pr_assoc = associations.to_a.first) && pr_assoc.first.respond_to?(:call)
|
3382
3596
|
eager_block, associations = pr_assoc
|
3383
3597
|
end
|
3384
|
-
|
3385
|
-
|
3386
|
-
end
|
3598
|
+
|
3599
|
+
eager_load_data[r[:eager_loader]] = {:key_hash=>key_hash, :rows=>a, :associations=>associations, :self=>self, :eager_block=>eager_block, :id_map=>id_map}
|
3600
|
+
end
|
3601
|
+
|
3602
|
+
eager_load_data
|
3603
|
+
end
|
3604
|
+
|
3605
|
+
# Using the hash of loaders and eager options, perform the eager loading.
|
3606
|
+
def perform_eager_loads(eager_load_data)
|
3607
|
+
eager_load_data.map do |loader, eo|
|
3608
|
+
perform_eager_load(loader, eo)
|
3609
|
+
end
|
3610
|
+
end
|
3611
|
+
|
3612
|
+
# Perform eager loading for a single association using the loader and eager options.
|
3613
|
+
def perform_eager_load(loader, eo)
|
3614
|
+
loader.call(eo)
|
3387
3615
|
end
|
3388
3616
|
|
3389
3617
|
# Return a subquery expression for filering by a many_to_many association
|