sequel 5.39.0 → 5.63.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 +308 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +57 -25
- data/bin/sequel +11 -3
- data/doc/advanced_associations.rdoc +13 -13
- data/doc/association_basics.rdoc +89 -24
- data/doc/cheat_sheet.rdoc +11 -3
- data/doc/migration.rdoc +12 -6
- data/doc/model_hooks.rdoc +1 -1
- data/doc/object_model.rdoc +8 -8
- data/doc/opening_databases.rdoc +18 -11
- data/doc/postgresql.rdoc +16 -8
- data/doc/querying.rdoc +5 -3
- 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/release_notes/5.59.0.txt +73 -0
- data/doc/release_notes/5.60.0.txt +22 -0
- data/doc/release_notes/5.61.0.txt +43 -0
- data/doc/release_notes/5.62.0.txt +132 -0
- data/doc/release_notes/5.63.0.txt +33 -0
- data/doc/schema_modification.rdoc +1 -1
- data/doc/security.rdoc +9 -9
- data/doc/sql.rdoc +27 -15
- data/doc/testing.rdoc +22 -11
- data/doc/transactions.rdoc +6 -6
- data/doc/virtual_rows.rdoc +2 -2
- 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/postgresql.rb +4 -4
- data/lib/sequel/adapters/jdbc.rb +16 -18
- data/lib/sequel/adapters/mysql.rb +80 -67
- data/lib/sequel/adapters/mysql2.rb +54 -49
- data/lib/sequel/adapters/odbc.rb +6 -2
- data/lib/sequel/adapters/oracle.rb +3 -3
- data/lib/sequel/adapters/postgres.rb +83 -40
- data/lib/sequel/adapters/shared/access.rb +11 -1
- data/lib/sequel/adapters/shared/db2.rb +30 -0
- data/lib/sequel/adapters/shared/mssql.rb +58 -7
- data/lib/sequel/adapters/shared/mysql.rb +40 -2
- data/lib/sequel/adapters/shared/oracle.rb +76 -0
- data/lib/sequel/adapters/shared/postgres.rb +418 -174
- data/lib/sequel/adapters/shared/sqlanywhere.rb +10 -0
- data/lib/sequel/adapters/shared/sqlite.rb +102 -11
- data/lib/sequel/adapters/sqlanywhere.rb +1 -1
- data/lib/sequel/adapters/sqlite.rb +60 -18
- data/lib/sequel/adapters/tinytds.rb +1 -1
- data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
- data/lib/sequel/ast_transformer.rb +6 -0
- data/lib/sequel/connection_pool/sharded_single.rb +5 -7
- data/lib/sequel/connection_pool/sharded_threaded.rb +5 -1
- data/lib/sequel/connection_pool/single.rb +6 -8
- data/lib/sequel/connection_pool/threaded.rb +8 -8
- data/lib/sequel/connection_pool/timed_queue.rb +257 -0
- data/lib/sequel/connection_pool.rb +47 -30
- data/lib/sequel/core.rb +28 -18
- data/lib/sequel/database/connecting.rb +26 -2
- data/lib/sequel/database/misc.rb +69 -14
- data/lib/sequel/database/query.rb +38 -1
- data/lib/sequel/database/schema_generator.rb +45 -52
- data/lib/sequel/database/schema_methods.rb +17 -1
- data/lib/sequel/dataset/actions.rb +107 -13
- data/lib/sequel/dataset/features.rb +20 -0
- data/lib/sequel/dataset/misc.rb +1 -1
- data/lib/sequel/dataset/prepared_statements.rb +2 -0
- data/lib/sequel/dataset/query.rb +118 -16
- data/lib/sequel/dataset/sql.rb +177 -47
- data/lib/sequel/extensions/_model_pg_row.rb +0 -12
- data/lib/sequel/extensions/_pretty_table.rb +1 -1
- data/lib/sequel/extensions/any_not_empty.rb +1 -1
- data/lib/sequel/extensions/async_thread_pool.rb +438 -0
- data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
- data/lib/sequel/extensions/blank.rb +8 -0
- data/lib/sequel/extensions/constraint_validations.rb +1 -1
- data/lib/sequel/extensions/core_refinements.rb +36 -11
- data/lib/sequel/extensions/date_arithmetic.rb +71 -31
- 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 +1 -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 +141 -0
- data/lib/sequel/extensions/looser_typecasting.rb +3 -0
- data/lib/sequel/extensions/migration.rb +7 -2
- data/lib/sequel/extensions/named_timezones.rb +26 -6
- data/lib/sequel/extensions/pagination.rb +1 -1
- data/lib/sequel/extensions/pg_array.rb +23 -3
- data/lib/sequel/extensions/pg_array_ops.rb +2 -2
- data/lib/sequel/extensions/pg_auto_parameterize.rb +478 -0
- data/lib/sequel/extensions/pg_enum.rb +1 -1
- data/lib/sequel/extensions/pg_extended_date_support.rb +28 -25
- data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
- data/lib/sequel/extensions/pg_hstore.rb +6 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
- data/lib/sequel/extensions/pg_inet.rb +10 -11
- data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
- data/lib/sequel/extensions/pg_interval.rb +45 -19
- data/lib/sequel/extensions/pg_json.rb +13 -15
- data/lib/sequel/extensions/pg_json_ops.rb +73 -2
- data/lib/sequel/extensions/pg_loose_count.rb +3 -1
- data/lib/sequel/extensions/pg_multirange.rb +367 -0
- data/lib/sequel/extensions/pg_range.rb +10 -23
- data/lib/sequel/extensions/pg_range_ops.rb +37 -9
- data/lib/sequel/extensions/pg_row.rb +19 -13
- data/lib/sequel/extensions/pg_row_ops.rb +1 -1
- data/lib/sequel/extensions/query.rb +2 -0
- data/lib/sequel/extensions/s.rb +2 -1
- data/lib/sequel/extensions/schema_dumper.rb +13 -2
- 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.rb +2 -0
- data/lib/sequel/model/associations.rb +325 -96
- data/lib/sequel/model/base.rb +51 -27
- data/lib/sequel/model/errors.rb +10 -1
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/model/plugins.rb +5 -0
- data/lib/sequel/plugins/association_proxies.rb +2 -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 +87 -15
- data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
- data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
- data/lib/sequel/plugins/column_encryption.rb +728 -0
- data/lib/sequel/plugins/composition.rb +10 -4
- data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
- data/lib/sequel/plugins/constraint_validations.rb +2 -1
- data/lib/sequel/plugins/dataset_associations.rb +4 -1
- data/lib/sequel/plugins/dirty.rb +1 -1
- data/lib/sequel/plugins/enum.rb +124 -0
- data/lib/sequel/plugins/finder.rb +3 -1
- data/lib/sequel/plugins/insert_conflict.rb +4 -0
- data/lib/sequel/plugins/instance_specific_default.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +39 -24
- data/lib/sequel/plugins/lazy_attributes.rb +3 -0
- data/lib/sequel/plugins/list.rb +3 -1
- data/lib/sequel/plugins/many_through_many.rb +108 -9
- data/lib/sequel/plugins/nested_attributes.rb +12 -7
- data/lib/sequel/plugins/pg_array_associations.rb +56 -38
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +3 -1
- data/lib/sequel/plugins/prepared_statements.rb +10 -1
- data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
- data/lib/sequel/plugins/rcte_tree.rb +27 -19
- data/lib/sequel/plugins/require_valid_schema.rb +67 -0
- 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 +8 -0
- data/lib/sequel/plugins/sql_comments.rb +189 -0
- data/lib/sequel/plugins/static_cache.rb +1 -1
- data/lib/sequel/plugins/subclasses.rb +28 -11
- data/lib/sequel/plugins/tactical_eager_loading.rb +23 -10
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/unused_associations.rb +521 -0
- data/lib/sequel/plugins/update_or_create.rb +1 -1
- data/lib/sequel/plugins/validate_associated.rb +22 -12
- data/lib/sequel/plugins/validation_helpers.rb +38 -11
- data/lib/sequel/plugins/xml_serializer.rb +1 -1
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/timezones.rb +12 -14
- data/lib/sequel/version.rb +1 -1
- metadata +97 -43
|
@@ -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 || !use_placeholder_loader?
|
|
276
280
|
ds = eager_loading_dataset(eo)
|
|
277
281
|
|
|
278
282
|
strategy = ds.opts[:eager_limit_strategy] || strategy
|
|
@@ -295,6 +299,11 @@ module Sequel
|
|
|
295
299
|
strategy = :ruby if strategy == :correlated_subquery
|
|
296
300
|
strategy = nil if strategy == :ruby && assign_singular?
|
|
297
301
|
objects = apply_eager_limit_strategy(ds, strategy, eager_limit).all
|
|
302
|
+
|
|
303
|
+
if strategy == :window_function
|
|
304
|
+
delete_rn = ds.row_number_column
|
|
305
|
+
objects.each{|obj| obj.values.delete(delete_rn)}
|
|
306
|
+
end
|
|
298
307
|
elsif strategy == :union
|
|
299
308
|
objects = []
|
|
300
309
|
ds = associated_dataset
|
|
@@ -311,7 +320,8 @@ module Sequel
|
|
|
311
320
|
objects = loader.all(ids)
|
|
312
321
|
end
|
|
313
322
|
|
|
314
|
-
objects.each(&block)
|
|
323
|
+
Sequel.synchronize_with(eo[:mutex]){objects.each(&block)} unless no_results
|
|
324
|
+
|
|
315
325
|
if strategy == :ruby
|
|
316
326
|
apply_ruby_eager_limit_strategy(rows, eager_limit || limit_and_offset)
|
|
317
327
|
end
|
|
@@ -635,9 +645,7 @@ module Sequel
|
|
|
635
645
|
# given the hash passed to the eager loader.
|
|
636
646
|
def eager_loading_dataset(eo=OPTS)
|
|
637
647
|
ds = eo[:dataset] || associated_eager_dataset
|
|
638
|
-
|
|
639
|
-
ds = ds.where(eager_loading_predicate_condition(id_map.keys))
|
|
640
|
-
end
|
|
648
|
+
ds = eager_loading_set_predicate_condition(ds, eo)
|
|
641
649
|
if associations = eo[:associations]
|
|
642
650
|
ds = ds.eager(associations)
|
|
643
651
|
end
|
|
@@ -664,6 +672,15 @@ module Sequel
|
|
|
664
672
|
self[:model].default_eager_limit_strategy || :ruby
|
|
665
673
|
end
|
|
666
674
|
|
|
675
|
+
# Set the predicate condition for the eager loading dataset based on the id map
|
|
676
|
+
# in the eager loading options.
|
|
677
|
+
def eager_loading_set_predicate_condition(ds, eo)
|
|
678
|
+
if id_map = eo[:id_map]
|
|
679
|
+
ds = ds.where(eager_loading_predicate_condition(id_map.keys))
|
|
680
|
+
end
|
|
681
|
+
ds
|
|
682
|
+
end
|
|
683
|
+
|
|
667
684
|
# The predicate condition to use for the eager_loader.
|
|
668
685
|
def eager_loading_predicate_condition(keys)
|
|
669
686
|
{predicate_key=>keys}
|
|
@@ -805,7 +822,7 @@ module Sequel
|
|
|
805
822
|
|
|
806
823
|
# Whether the placeholder loader can be used to load the association.
|
|
807
824
|
def use_placeholder_loader?
|
|
808
|
-
self[:use_placeholder_loader]
|
|
825
|
+
self[:use_placeholder_loader] && _associated_dataset.supports_placeholder_literalizer?
|
|
809
826
|
end
|
|
810
827
|
end
|
|
811
828
|
|
|
@@ -1315,7 +1332,7 @@ module Sequel
|
|
|
1315
1332
|
|
|
1316
1333
|
# many_to_many associations need to select a key in an associated table to eagerly load
|
|
1317
1334
|
def eager_loading_use_associated_key?
|
|
1318
|
-
|
|
1335
|
+
!separate_query_per_table?
|
|
1319
1336
|
end
|
|
1320
1337
|
|
|
1321
1338
|
# The source of the join table. This is the join table itself, unless it
|
|
@@ -1372,10 +1389,30 @@ module Sequel
|
|
|
1372
1389
|
cached_fetch(:select){default_select}
|
|
1373
1390
|
end
|
|
1374
1391
|
|
|
1392
|
+
# Whether a separate query should be used for the join table.
|
|
1393
|
+
def separate_query_per_table?
|
|
1394
|
+
self[:join_table_db]
|
|
1395
|
+
end
|
|
1396
|
+
|
|
1375
1397
|
private
|
|
1376
1398
|
|
|
1399
|
+
# Join to the the join table, unless using a separate query per table.
|
|
1377
1400
|
def _associated_dataset
|
|
1378
|
-
|
|
1401
|
+
if separate_query_per_table?
|
|
1402
|
+
super
|
|
1403
|
+
else
|
|
1404
|
+
super.inner_join(self[:join_table], self[:right_keys].zip(right_primary_keys), :qualify=>:deep)
|
|
1405
|
+
end
|
|
1406
|
+
end
|
|
1407
|
+
|
|
1408
|
+
# Use the right_keys from the eager loading options if
|
|
1409
|
+
# using a separate query per table.
|
|
1410
|
+
def eager_loading_set_predicate_condition(ds, eo)
|
|
1411
|
+
if separate_query_per_table?
|
|
1412
|
+
ds.where(right_primary_key=>eo[:right_keys])
|
|
1413
|
+
else
|
|
1414
|
+
super
|
|
1415
|
+
end
|
|
1379
1416
|
end
|
|
1380
1417
|
|
|
1381
1418
|
# The default selection for associations that require joins. These do not use the default
|
|
@@ -1592,6 +1629,7 @@ module Sequel
|
|
|
1592
1629
|
# === Multiple Types
|
|
1593
1630
|
# :adder :: Proc used to define the private _add_* method for doing the database work
|
|
1594
1631
|
# to associate the given object to the current object (*_to_many assocations).
|
|
1632
|
+
# Set to nil to not define a add_* method for the association.
|
|
1595
1633
|
# :after_add :: Symbol, Proc, or array of both/either specifying a callback to call
|
|
1596
1634
|
# after a new item is added to the association.
|
|
1597
1635
|
# :after_load :: Symbol, Proc, or array of both/either specifying a callback to call
|
|
@@ -1602,6 +1640,8 @@ module Sequel
|
|
|
1602
1640
|
# after an item is set using the association setter method.
|
|
1603
1641
|
# :allow_eager :: If set to false, you cannot load the association eagerly
|
|
1604
1642
|
# via eager or eager_graph
|
|
1643
|
+
# :allow_eager_graph :: If set to false, you cannot load the association eagerly via eager_graph.
|
|
1644
|
+
# :allow_filtering_by :: If set to false, you cannot use the association when filtering
|
|
1605
1645
|
# :before_add :: Symbol, Proc, or array of both/either specifying a callback to call
|
|
1606
1646
|
# before a new item is added to the association.
|
|
1607
1647
|
# :before_remove :: Symbol, Proc, or array of both/either specifying a callback to call
|
|
@@ -1620,6 +1660,7 @@ module Sequel
|
|
|
1620
1660
|
# the class. <tt>class: 'Foo', class_namespace: 'Bar'</tt> looks for <tt>::Bar::Foo</tt>.)
|
|
1621
1661
|
# :clearer :: Proc used to define the private _remove_all_* method for doing the database work
|
|
1622
1662
|
# to remove all objects associated to the current object (*_to_many assocations).
|
|
1663
|
+
# Set to nil to not define a remove_all_* method for the association.
|
|
1623
1664
|
# :clone :: Merge the current options and block into the options and block used in defining
|
|
1624
1665
|
# the given association. Can be used to DRY up a bunch of similar associations that
|
|
1625
1666
|
# all share the same options such as :class and :key, while changing the order and block used.
|
|
@@ -1674,18 +1715,24 @@ module Sequel
|
|
|
1674
1715
|
# :graph_only_conditions :: The conditions to use on the SQL join when eagerly loading
|
|
1675
1716
|
# the association via +eager_graph+, instead of the default conditions specified by the
|
|
1676
1717
|
# foreign/primary keys. This option causes the :graph_conditions option to be ignored.
|
|
1677
|
-
# :graph_order ::
|
|
1718
|
+
# :graph_order :: the order to use when using eager_graph, instead of the default order. This should be used
|
|
1678
1719
|
# in the case where :order contains an identifier qualified by the table's name, which may not match
|
|
1679
1720
|
# the alias used when eager graphing. By setting this to the unqualified identifier, it will be
|
|
1680
1721
|
# automatically qualified when using eager_graph.
|
|
1681
1722
|
# :graph_select :: A column or array of columns to select from the associated table
|
|
1682
1723
|
# when eagerly loading the association via +eager_graph+. Defaults to all
|
|
1683
1724
|
# columns in the associated table.
|
|
1725
|
+
# :instance_specific :: Marks the association as instance specific. Should be used if the association block
|
|
1726
|
+
# uses instance specific state, or transient state (accessing current date/time, etc.).
|
|
1684
1727
|
# :limit :: Limit the number of records to the provided value. Use
|
|
1685
1728
|
# an array with two elements for the value to specify a
|
|
1686
1729
|
# limit (first element) and an offset (second element).
|
|
1687
1730
|
# :methods_module :: The module that methods the association creates will be placed into. Defaults
|
|
1688
1731
|
# to the module containing the model's columns.
|
|
1732
|
+
# :no_association_method :: Do not add a method for the association. This can save memory if the association
|
|
1733
|
+
# method is never used.
|
|
1734
|
+
# :no_dataset_method :: Do not add a method for the association dataset. This can save memory if the dataset
|
|
1735
|
+
# method is never used.
|
|
1689
1736
|
# :order :: the column(s) by which to order the association dataset. Can be a
|
|
1690
1737
|
# singular column symbol or an array of column symbols.
|
|
1691
1738
|
# :order_eager_graph :: Whether to add the association's order to the graphed dataset's order when graphing
|
|
@@ -1698,6 +1745,7 @@ module Sequel
|
|
|
1698
1745
|
# the current association's key(s). Set to nil to not use a reciprocal.
|
|
1699
1746
|
# :remover :: Proc used to define the private _remove_* method for doing the database work
|
|
1700
1747
|
# to remove the association between the given object and the current object (*_to_many assocations).
|
|
1748
|
+
# Set to nil to not define a remove_* method for the association.
|
|
1701
1749
|
# :select :: the columns to select. Defaults to the associated class's table_name.* in an association
|
|
1702
1750
|
# that uses joins, which means it doesn't include the attributes from the
|
|
1703
1751
|
# join table. If you want to include the join table attributes, you can
|
|
@@ -1706,6 +1754,7 @@ module Sequel
|
|
|
1706
1754
|
# the same name in both the join table and the associated table.
|
|
1707
1755
|
# :setter :: Proc used to define the private _*= method for doing the work to setup the assocation
|
|
1708
1756
|
# between the given object and the current object (*_to_one associations).
|
|
1757
|
+
# Set to nil to not define a setter method for the association.
|
|
1709
1758
|
# :subqueries_per_union :: The number of subqueries to use in each UNION query, for eager
|
|
1710
1759
|
# loading limited associations using the default :union strategy.
|
|
1711
1760
|
# :validate :: Set to false to not validate when implicitly saving any associated object.
|
|
@@ -1762,6 +1811,9 @@ module Sequel
|
|
|
1762
1811
|
# underscored, sorted, and joined with '_'.
|
|
1763
1812
|
# :join_table_block :: proc that can be used to modify the dataset used in the add/remove/remove_all
|
|
1764
1813
|
# methods. Should accept a dataset argument and return a modified dataset if present.
|
|
1814
|
+
# :join_table_db :: When retrieving records when using lazy loading or eager loading via +eager+, instead of
|
|
1815
|
+
# a join between to the join table and the associated table, use a separate query for the
|
|
1816
|
+
# join table using the given Database object.
|
|
1765
1817
|
# :left_key :: foreign key in join table that points to current model's
|
|
1766
1818
|
# primary key, as a symbol. Defaults to :"#{self.name.underscore}_id".
|
|
1767
1819
|
# Can use an array of symbols for a composite key association.
|
|
@@ -1791,7 +1843,9 @@ module Sequel
|
|
|
1791
1843
|
|
|
1792
1844
|
if opts[:clone]
|
|
1793
1845
|
cloned_assoc = association_reflection(opts[:clone])
|
|
1846
|
+
remove_class_name = orig_opts[:class] && !orig_opts[:class_name]
|
|
1794
1847
|
orig_opts = cloned_assoc[:orig_opts].merge(orig_opts)
|
|
1848
|
+
orig_opts.delete(:class_name) if remove_class_name
|
|
1795
1849
|
end
|
|
1796
1850
|
|
|
1797
1851
|
opts = Hash[default_association_options]
|
|
@@ -1809,6 +1863,16 @@ module Sequel
|
|
|
1809
1863
|
# in certain places to disable optimizations.
|
|
1810
1864
|
opts[:instance_specific] = _association_instance_specific_default(name)
|
|
1811
1865
|
end
|
|
1866
|
+
if (orig_opts[:instance_specific] || orig_opts[:dataset]) && !opts.has_key?(:allow_eager) && !opts[:eager_loader]
|
|
1867
|
+
# For associations explicitly marked as instance specific, or that use the
|
|
1868
|
+
# :dataset option, where :allow_eager is not set, and no :eager_loader is
|
|
1869
|
+
# provided, disallow eager loading. In these cases, eager loading is
|
|
1870
|
+
# unlikely to work. This is not done for implicit setting of :instance_specific,
|
|
1871
|
+
# because implicit use is done by default for all associations with blocks,
|
|
1872
|
+
# and the vast majority of associations with blocks use the block for filtering
|
|
1873
|
+
# in a manner compatible with eager loading.
|
|
1874
|
+
opts[:allow_eager] = false
|
|
1875
|
+
end
|
|
1812
1876
|
opts = assoc_class.new.merge!(opts)
|
|
1813
1877
|
|
|
1814
1878
|
if opts[:clone] && !opts.cloneable?(cloned_assoc)
|
|
@@ -1838,8 +1902,7 @@ module Sequel
|
|
|
1838
1902
|
# Remove :class entry if it exists and is nil, to work with cached_fetch
|
|
1839
1903
|
opts.delete(:class) unless opts[:class]
|
|
1840
1904
|
|
|
1841
|
-
|
|
1842
|
-
def_association_instance_methods(opts)
|
|
1905
|
+
def_association(opts)
|
|
1843
1906
|
|
|
1844
1907
|
orig_opts.delete(:clone)
|
|
1845
1908
|
opts[:orig_class] = orig_opts[:class] || orig_opts[:class_name]
|
|
@@ -1929,7 +1992,22 @@ module Sequel
|
|
|
1929
1992
|
# can be easily overridden in the class itself while allowing for
|
|
1930
1993
|
# super to be called.
|
|
1931
1994
|
def association_module_def(name, opts=OPTS, &block)
|
|
1932
|
-
association_module(opts)
|
|
1995
|
+
mod = association_module(opts)
|
|
1996
|
+
mod.send(:define_method, name, &block)
|
|
1997
|
+
mod.send(:alias_method, name, name)
|
|
1998
|
+
end
|
|
1999
|
+
|
|
2000
|
+
# Add a method to the module included in the class, so the method
|
|
2001
|
+
# can be easily overridden in the class itself while allowing for
|
|
2002
|
+
# super to be called. This method allows passing keywords through
|
|
2003
|
+
# the defined methods.
|
|
2004
|
+
def association_module_delegate_def(name, opts, &block)
|
|
2005
|
+
mod = association_module(opts)
|
|
2006
|
+
mod.send(:define_method, name, &block)
|
|
2007
|
+
# :nocov:
|
|
2008
|
+
mod.send(:ruby2_keywords, name) if mod.respond_to?(:ruby2_keywords, true)
|
|
2009
|
+
# :nocov:
|
|
2010
|
+
mod.send(:alias_method, name, name)
|
|
1933
2011
|
end
|
|
1934
2012
|
|
|
1935
2013
|
# Add a private method to the module included in the class.
|
|
@@ -1938,6 +2016,13 @@ module Sequel
|
|
|
1938
2016
|
association_module(opts).send(:private, name)
|
|
1939
2017
|
end
|
|
1940
2018
|
|
|
2019
|
+
# Delegate to the type-specific association method to setup the
|
|
2020
|
+
# association, and define the association instance methods.
|
|
2021
|
+
def def_association(opts)
|
|
2022
|
+
send(:"def_#{opts[:type]}", opts)
|
|
2023
|
+
def_association_instance_methods(opts)
|
|
2024
|
+
end
|
|
2025
|
+
|
|
1941
2026
|
# Adds the association method to the association methods module.
|
|
1942
2027
|
def def_association_method(opts)
|
|
1943
2028
|
association_module_def(opts.association_method, opts) do |dynamic_opts=OPTS, &block|
|
|
@@ -1963,13 +2048,13 @@ module Sequel
|
|
|
1963
2048
|
opts[:setter_method] = :"#{opts[:name]}="
|
|
1964
2049
|
end
|
|
1965
2050
|
|
|
1966
|
-
association_module_def(opts.dataset_method, opts){_dataset(opts)}
|
|
2051
|
+
association_module_def(opts.dataset_method, opts){_dataset(opts)} unless opts[:no_dataset_method]
|
|
1967
2052
|
if opts[:block]
|
|
1968
2053
|
opts[:block_method] = Plugins.def_sequel_method(association_module(opts), "#{opts[:name]}_block", 1, &opts[:block])
|
|
1969
2054
|
end
|
|
1970
2055
|
opts[:dataset_opt_arity] = opts[:dataset].arity == 0 ? 0 : 1
|
|
1971
2056
|
opts[:dataset_opt_method] = Plugins.def_sequel_method(association_module(opts), "#{opts[:name]}_dataset_opt", opts[:dataset_opt_arity], &opts[:dataset])
|
|
1972
|
-
def_association_method(opts)
|
|
2057
|
+
def_association_method(opts) unless opts[:no_association_method]
|
|
1973
2058
|
|
|
1974
2059
|
return if opts[:read_only]
|
|
1975
2060
|
|
|
@@ -1981,17 +2066,17 @@ module Sequel
|
|
|
1981
2066
|
|
|
1982
2067
|
if adder = opts[:adder]
|
|
1983
2068
|
association_module_private_def(opts[:_add_method], opts, &adder)
|
|
1984
|
-
|
|
2069
|
+
association_module_delegate_def(opts[:add_method], opts){|o,*args| add_associated_object(opts, o, *args)}
|
|
1985
2070
|
end
|
|
1986
2071
|
|
|
1987
2072
|
if remover = opts[:remover]
|
|
1988
2073
|
association_module_private_def(opts[:_remove_method], opts, &remover)
|
|
1989
|
-
|
|
2074
|
+
association_module_delegate_def(opts[:remove_method], opts){|o,*args| remove_associated_object(opts, o, *args)}
|
|
1990
2075
|
end
|
|
1991
2076
|
|
|
1992
2077
|
if clearer = opts[:clearer]
|
|
1993
2078
|
association_module_private_def(opts[:_remove_all_method], opts, &clearer)
|
|
1994
|
-
|
|
2079
|
+
association_module_delegate_def(opts[:remove_all_method], opts){|*args| remove_all_associated_objects(opts, *args)}
|
|
1995
2080
|
end
|
|
1996
2081
|
end
|
|
1997
2082
|
|
|
@@ -2013,7 +2098,7 @@ module Sequel
|
|
|
2013
2098
|
raise(Error, "mismatched number of right keys: #{rcks.inspect} vs #{rcpks.inspect}") unless rcks.length == rcpks.length
|
|
2014
2099
|
end
|
|
2015
2100
|
opts[:uses_left_composite_keys] = lcks.length > 1
|
|
2016
|
-
opts[:uses_right_composite_keys] = rcks.length > 1
|
|
2101
|
+
uses_rcks = opts[:uses_right_composite_keys] = rcks.length > 1
|
|
2017
2102
|
opts[:cartesian_product_number] ||= one_through_one ? 0 : 1
|
|
2018
2103
|
join_table = (opts[:join_table] ||= opts.default_join_table)
|
|
2019
2104
|
opts[:left_key_alias] ||= opts.default_associated_key_alias
|
|
@@ -2022,8 +2107,75 @@ module Sequel
|
|
|
2022
2107
|
opts[:after_load] ||= []
|
|
2023
2108
|
opts[:after_load].unshift(:array_uniq!)
|
|
2024
2109
|
end
|
|
2025
|
-
opts[:
|
|
2026
|
-
|
|
2110
|
+
if join_table_db = opts[:join_table_db]
|
|
2111
|
+
opts[:use_placeholder_loader] = false
|
|
2112
|
+
opts[:allow_eager_graph] = false
|
|
2113
|
+
opts[:allow_filtering_by] = false
|
|
2114
|
+
opts[:eager_limit_strategy] = nil
|
|
2115
|
+
join_table_ds = join_table_db.from(join_table)
|
|
2116
|
+
opts[:dataset] ||= proc do |r|
|
|
2117
|
+
vals = join_table_ds.where(lcks.zip(lcpks.map{|k| get_column_value(k)})).select_map(right)
|
|
2118
|
+
ds = r.associated_dataset.where(opts.right_primary_key => vals)
|
|
2119
|
+
if uses_rcks
|
|
2120
|
+
vals.delete_if{|v| v.any?(&:nil?)}
|
|
2121
|
+
else
|
|
2122
|
+
vals.delete(nil)
|
|
2123
|
+
end
|
|
2124
|
+
ds = ds.clone(:no_results=>true) if vals.empty?
|
|
2125
|
+
ds
|
|
2126
|
+
end
|
|
2127
|
+
opts[:eager_loader] ||= proc do |eo|
|
|
2128
|
+
h = eo[:id_map]
|
|
2129
|
+
assign_singular = opts.assign_singular?
|
|
2130
|
+
rpk = opts.right_primary_key
|
|
2131
|
+
name = opts[:name]
|
|
2132
|
+
|
|
2133
|
+
join_map = join_table_ds.where(left=>h.keys).select_hash_groups(right, left)
|
|
2134
|
+
|
|
2135
|
+
if uses_rcks
|
|
2136
|
+
join_map.delete_if{|v,| v.any?(&:nil?)}
|
|
2137
|
+
else
|
|
2138
|
+
join_map.delete(nil)
|
|
2139
|
+
end
|
|
2140
|
+
|
|
2141
|
+
eo = Hash[eo]
|
|
2142
|
+
|
|
2143
|
+
if join_map.empty?
|
|
2144
|
+
eo[:no_results] = true
|
|
2145
|
+
else
|
|
2146
|
+
join_map.each_value do |vs|
|
|
2147
|
+
vs.replace(vs.flat_map{|v| h[v]})
|
|
2148
|
+
vs.uniq!
|
|
2149
|
+
end
|
|
2150
|
+
|
|
2151
|
+
eo[:loader] = false
|
|
2152
|
+
eo[:right_keys] = join_map.keys
|
|
2153
|
+
end
|
|
2154
|
+
|
|
2155
|
+
opts[:model].eager_load_results(opts, eo) do |assoc_record|
|
|
2156
|
+
rpkv = if uses_rcks
|
|
2157
|
+
assoc_record.values.values_at(*rpk)
|
|
2158
|
+
else
|
|
2159
|
+
assoc_record.values[rpk]
|
|
2160
|
+
end
|
|
2161
|
+
|
|
2162
|
+
objects = join_map[rpkv]
|
|
2163
|
+
|
|
2164
|
+
if assign_singular
|
|
2165
|
+
objects.each do |object|
|
|
2166
|
+
object.associations[name] ||= assoc_record
|
|
2167
|
+
end
|
|
2168
|
+
else
|
|
2169
|
+
objects.each do |object|
|
|
2170
|
+
object.associations[name].push(assoc_record)
|
|
2171
|
+
end
|
|
2172
|
+
end
|
|
2173
|
+
end
|
|
2174
|
+
end
|
|
2175
|
+
else
|
|
2176
|
+
opts[:dataset] ||= opts.association_dataset_proc
|
|
2177
|
+
opts[:eager_loader] ||= opts.method(:default_eager_loader)
|
|
2178
|
+
end
|
|
2027
2179
|
|
|
2028
2180
|
join_type = opts[:graph_join_type]
|
|
2029
2181
|
select = opts[:graph_select]
|
|
@@ -2057,50 +2209,60 @@ module Sequel
|
|
|
2057
2209
|
return if opts[:read_only]
|
|
2058
2210
|
|
|
2059
2211
|
if one_through_one
|
|
2060
|
-
opts
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
checked_transaction do
|
|
2066
|
-
current = jtds.first
|
|
2212
|
+
unless opts.has_key?(:setter)
|
|
2213
|
+
opts[:setter] = proc do |o|
|
|
2214
|
+
h = {}
|
|
2215
|
+
lh = lcks.zip(lcpks.map{|k| get_column_value(k)})
|
|
2216
|
+
jtds = _join_table_dataset(opts).where(lh)
|
|
2067
2217
|
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
rcks.zip(opts.right_primary_key_methods).each{|k, pk| new_values << (h[k] = o.get_column_value(pk))}
|
|
2071
|
-
end
|
|
2218
|
+
checked_transaction do
|
|
2219
|
+
current = jtds.first
|
|
2072
2220
|
|
|
2073
|
-
if current
|
|
2074
|
-
current_values = rcks.map{|k| current[k]}
|
|
2075
|
-
jtds = jtds.where(rcks.zip(current_values))
|
|
2076
2221
|
if o
|
|
2077
|
-
|
|
2078
|
-
|
|
2222
|
+
new_values = []
|
|
2223
|
+
rcks.zip(opts.right_primary_key_methods).each{|k, pk| new_values << (h[k] = o.get_column_value(pk))}
|
|
2224
|
+
end
|
|
2225
|
+
|
|
2226
|
+
if current
|
|
2227
|
+
current_values = rcks.map{|k| current[k]}
|
|
2228
|
+
jtds = jtds.where(rcks.zip(current_values))
|
|
2229
|
+
if o
|
|
2230
|
+
if current_values != new_values
|
|
2231
|
+
jtds.update(h)
|
|
2232
|
+
end
|
|
2233
|
+
else
|
|
2234
|
+
jtds.delete
|
|
2079
2235
|
end
|
|
2080
|
-
|
|
2081
|
-
|
|
2236
|
+
elsif o
|
|
2237
|
+
lh.each{|k,v| h[k] = v}
|
|
2238
|
+
jtds.insert(h)
|
|
2082
2239
|
end
|
|
2083
|
-
elsif o
|
|
2084
|
-
lh.each{|k,v| h[k] = v}
|
|
2085
|
-
jtds.insert(h)
|
|
2086
2240
|
end
|
|
2087
2241
|
end
|
|
2088
2242
|
end
|
|
2089
|
-
opts
|
|
2243
|
+
if opts.fetch(:setter, true)
|
|
2244
|
+
opts[:_setter] = proc{|o| set_one_through_one_associated_object(opts, o)}
|
|
2245
|
+
end
|
|
2090
2246
|
else
|
|
2091
|
-
opts
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2247
|
+
unless opts.has_key?(:adder)
|
|
2248
|
+
opts[:adder] = proc do |o|
|
|
2249
|
+
h = {}
|
|
2250
|
+
lcks.zip(lcpks).each{|k, pk| h[k] = get_column_value(pk)}
|
|
2251
|
+
rcks.zip(opts.right_primary_key_methods).each{|k, pk| h[k] = o.get_column_value(pk)}
|
|
2252
|
+
_join_table_dataset(opts).insert(h)
|
|
2253
|
+
end
|
|
2096
2254
|
end
|
|
2097
2255
|
|
|
2098
|
-
opts
|
|
2099
|
-
|
|
2256
|
+
unless opts.has_key?(:remover)
|
|
2257
|
+
opts[:remover] = proc do |o|
|
|
2258
|
+
_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
|
|
2259
|
+
end
|
|
2100
2260
|
end
|
|
2101
2261
|
|
|
2102
|
-
opts
|
|
2103
|
-
|
|
2262
|
+
unless opts.has_key?(:clearer)
|
|
2263
|
+
opts[:clearer] = proc do
|
|
2264
|
+
_join_table_dataset(opts).where(lcks.zip(lcpks.map{|k| get_column_value(k)})).delete
|
|
2265
|
+
end
|
|
2104
2266
|
end
|
|
2105
2267
|
end
|
|
2106
2268
|
end
|
|
@@ -2157,8 +2319,12 @@ module Sequel
|
|
|
2157
2319
|
|
|
2158
2320
|
return if opts[:read_only]
|
|
2159
2321
|
|
|
2160
|
-
|
|
2161
|
-
|
|
2322
|
+
unless opts.has_key?(:setter)
|
|
2323
|
+
opts[:setter] = proc{|o| cks.zip(opts.primary_key_methods).each{|k, pk| set_column_value(:"#{k}=", (o.get_column_value(pk) if o))}}
|
|
2324
|
+
end
|
|
2325
|
+
if opts.fetch(:setter, true)
|
|
2326
|
+
opts[:_setter] = proc{|o| set_associated_object(opts, o)}
|
|
2327
|
+
end
|
|
2162
2328
|
end
|
|
2163
2329
|
|
|
2164
2330
|
# Configures one_to_many and one_to_one association reflections and adds the related association methods
|
|
@@ -2225,49 +2391,59 @@ module Sequel
|
|
|
2225
2391
|
cks.each{|k| ck_nil_hash[k] = nil}
|
|
2226
2392
|
|
|
2227
2393
|
if one_to_one
|
|
2228
|
-
opts
|
|
2229
|
-
|
|
2394
|
+
unless opts.has_key?(:setter)
|
|
2395
|
+
opts[:setter] = proc do |o|
|
|
2396
|
+
up_ds = _apply_association_options(opts, opts.associated_dataset.where(cks.zip(cpks.map{|k| get_column_value(k)})))
|
|
2230
2397
|
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2398
|
+
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)))
|
|
2399
|
+
if old = up_ds.first
|
|
2400
|
+
cks.each{|k| old.set_column_value(:"#{k}=", nil)}
|
|
2401
|
+
end
|
|
2402
|
+
save_old = true
|
|
2234
2403
|
end
|
|
2235
|
-
save_old = true
|
|
2236
|
-
end
|
|
2237
2404
|
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2405
|
+
if o
|
|
2406
|
+
if !o.new? && !save_old
|
|
2407
|
+
up_ds = up_ds.exclude(o.pk_hash)
|
|
2408
|
+
end
|
|
2409
|
+
cks.zip(cpks).each{|k, pk| o.set_column_value(:"#{k}=", get_column_value(pk))}
|
|
2241
2410
|
end
|
|
2242
|
-
cks.zip(cpks).each{|k, pk| o.set_column_value(:"#{k}=", get_column_value(pk))}
|
|
2243
|
-
end
|
|
2244
2411
|
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2412
|
+
checked_transaction do
|
|
2413
|
+
if save_old
|
|
2414
|
+
old.save(save_opts) || raise(Sequel::Error, "invalid previously associated object, cannot save") if old
|
|
2415
|
+
else
|
|
2416
|
+
up_ds.skip_limit_check.update(ck_nil_hash)
|
|
2417
|
+
end
|
|
2251
2418
|
|
|
2252
|
-
|
|
2419
|
+
o.save(save_opts) || raise(Sequel::Error, "invalid associated object, cannot save") if o
|
|
2420
|
+
end
|
|
2253
2421
|
end
|
|
2254
2422
|
end
|
|
2255
|
-
opts
|
|
2423
|
+
if opts.fetch(:setter, true)
|
|
2424
|
+
opts[:_setter] = proc{|o| set_one_to_one_associated_object(opts, o)}
|
|
2425
|
+
end
|
|
2256
2426
|
else
|
|
2257
2427
|
save_opts[:raise_on_failure] = opts[:raise_on_save_failure] != false
|
|
2258
2428
|
|
|
2259
|
-
opts
|
|
2260
|
-
|
|
2261
|
-
|
|
2429
|
+
unless opts.has_key?(:adder)
|
|
2430
|
+
opts[:adder] = proc do |o|
|
|
2431
|
+
cks.zip(cpks).each{|k, pk| o.set_column_value(:"#{k}=", get_column_value(pk))}
|
|
2432
|
+
o.save(save_opts)
|
|
2433
|
+
end
|
|
2262
2434
|
end
|
|
2263
2435
|
|
|
2264
|
-
opts
|
|
2265
|
-
|
|
2266
|
-
|
|
2436
|
+
unless opts.has_key?(:remover)
|
|
2437
|
+
opts[:remover] = proc do |o|
|
|
2438
|
+
cks.each{|k| o.set_column_value(:"#{k}=", nil)}
|
|
2439
|
+
o.save(save_opts)
|
|
2440
|
+
end
|
|
2267
2441
|
end
|
|
2268
2442
|
|
|
2269
|
-
opts
|
|
2270
|
-
|
|
2443
|
+
unless opts.has_key?(:clearer)
|
|
2444
|
+
opts[:clearer] = proc do
|
|
2445
|
+
_apply_association_options(opts, opts.associated_dataset.where(cks.zip(cpks.map{|k| get_column_value(k)}))).update(ck_nil_hash)
|
|
2446
|
+
end
|
|
2271
2447
|
end
|
|
2272
2448
|
end
|
|
2273
2449
|
end
|
|
@@ -2361,7 +2537,7 @@ module Sequel
|
|
|
2361
2537
|
|
|
2362
2538
|
# Dataset for the join table of the given many to many association reflection
|
|
2363
2539
|
def _join_table_dataset(opts)
|
|
2364
|
-
ds = model.db.from(opts.join_table_source)
|
|
2540
|
+
ds = (opts[:join_table_db] || model.db).from(opts.join_table_source)
|
|
2365
2541
|
opts[:join_table_block] ? opts[:join_table_block].call(ds) : ds
|
|
2366
2542
|
end
|
|
2367
2543
|
|
|
@@ -2382,7 +2558,12 @@ module Sequel
|
|
|
2382
2558
|
if loader = _associated_object_loader(opts, dynamic_opts)
|
|
2383
2559
|
loader.all(*opts.predicate_key_values(self))
|
|
2384
2560
|
else
|
|
2385
|
-
_associated_dataset(opts, dynamic_opts)
|
|
2561
|
+
ds = _associated_dataset(opts, dynamic_opts)
|
|
2562
|
+
if ds.opts[:no_results]
|
|
2563
|
+
[]
|
|
2564
|
+
else
|
|
2565
|
+
ds.all
|
|
2566
|
+
end
|
|
2386
2567
|
end
|
|
2387
2568
|
end
|
|
2388
2569
|
|
|
@@ -2423,6 +2604,9 @@ module Sequel
|
|
|
2423
2604
|
run_association_callbacks(opts, :after_add, o)
|
|
2424
2605
|
o
|
|
2425
2606
|
end
|
|
2607
|
+
# :nocov:
|
|
2608
|
+
ruby2_keywords(:add_associated_object) if respond_to?(:ruby2_keywords, true)
|
|
2609
|
+
# :nocov:
|
|
2426
2610
|
|
|
2427
2611
|
# Add/Set the current object to/as the given object's reciprocal association.
|
|
2428
2612
|
def add_reciprocal_object(opts, o)
|
|
@@ -2565,6 +2749,9 @@ module Sequel
|
|
|
2565
2749
|
associations[opts[:name]] = []
|
|
2566
2750
|
ret
|
|
2567
2751
|
end
|
|
2752
|
+
# :nocov:
|
|
2753
|
+
ruby2_keywords(:remove_all_associated_objects) if respond_to?(:ruby2_keywords, true)
|
|
2754
|
+
# :nocov:
|
|
2568
2755
|
|
|
2569
2756
|
# Remove the given associated object from the given association
|
|
2570
2757
|
def remove_associated_object(opts, o, *args)
|
|
@@ -2586,6 +2773,9 @@ module Sequel
|
|
|
2586
2773
|
run_association_callbacks(opts, :after_remove, o)
|
|
2587
2774
|
o
|
|
2588
2775
|
end
|
|
2776
|
+
# :nocov:
|
|
2777
|
+
ruby2_keywords(:remove_associated_object) if respond_to?(:ruby2_keywords, true)
|
|
2778
|
+
# :nocov:
|
|
2589
2779
|
|
|
2590
2780
|
# Check that the object from the associated table specified by the primary key
|
|
2591
2781
|
# is currently associated to the receiver. If it is associated, return the object, otherwise
|
|
@@ -2834,6 +3024,8 @@ module Sequel
|
|
|
2834
3024
|
(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)})))))
|
|
2835
3025
|
l = args[0]
|
|
2836
3026
|
if ar = model.association_reflections[l]
|
|
3027
|
+
raise Error, "filtering by associations is not allowed for #{ar.inspect}" if ar[:allow_filtering_by] == false
|
|
3028
|
+
|
|
2837
3029
|
if multiple
|
|
2838
3030
|
klass = ar.associated_class
|
|
2839
3031
|
if is_ds
|
|
@@ -2955,7 +3147,7 @@ module Sequel
|
|
|
2955
3147
|
|
|
2956
3148
|
# The secondary eager loading method. Loads all associations in a single query. This
|
|
2957
3149
|
# method should only be used if you need to filter or order based on columns in associated tables,
|
|
2958
|
-
# or if you have done comparative benchmarking
|
|
3150
|
+
# or if you have done comparative benchmarking and determined it is faster.
|
|
2959
3151
|
#
|
|
2960
3152
|
# This method uses <tt>Dataset#graph</tt> to create appropriate aliases for columns in all the
|
|
2961
3153
|
# tables. Then it uses the graph's metadata to build the associations from the single hash, and
|
|
@@ -2984,6 +3176,8 @@ module Sequel
|
|
|
2984
3176
|
# You can specify an custom alias and/or join type on a per-association basis by providing an
|
|
2985
3177
|
# Sequel::SQL::AliasedExpression object instead of an a Symbol for the association name.
|
|
2986
3178
|
#
|
|
3179
|
+
# You cannot mix calls to +eager_graph+ and +graph+ on the same dataset.
|
|
3180
|
+
#
|
|
2987
3181
|
# Examples:
|
|
2988
3182
|
#
|
|
2989
3183
|
# # For each album, eager_graph load the artist
|
|
@@ -3327,7 +3521,7 @@ module Sequel
|
|
|
3327
3521
|
# Allow associations that are eagerly graphed to be specified as an SQL::AliasedExpression, for
|
|
3328
3522
|
# per-call determining of the alias base.
|
|
3329
3523
|
def eager_graph_check_association(model, association)
|
|
3330
|
-
if association.is_a?(SQL::AliasedExpression)
|
|
3524
|
+
reflection = if association.is_a?(SQL::AliasedExpression)
|
|
3331
3525
|
expr = association.expression
|
|
3332
3526
|
if expr.is_a?(SQL::Identifier)
|
|
3333
3527
|
expr = expr.value
|
|
@@ -3336,10 +3530,17 @@ module Sequel
|
|
|
3336
3530
|
end
|
|
3337
3531
|
end
|
|
3338
3532
|
|
|
3339
|
-
|
|
3533
|
+
check_reflection = check_association(model, expr)
|
|
3534
|
+
SQL::AliasedExpression.new(check_reflection, association.alias || expr, association.columns)
|
|
3340
3535
|
else
|
|
3341
|
-
check_association(model, association)
|
|
3536
|
+
check_reflection = check_association(model, association)
|
|
3537
|
+
end
|
|
3538
|
+
|
|
3539
|
+
if check_reflection && check_reflection[:allow_eager_graph] == false
|
|
3540
|
+
raise Error, "eager_graph not allowed for #{reflection.inspect}"
|
|
3342
3541
|
end
|
|
3542
|
+
|
|
3543
|
+
reflection
|
|
3343
3544
|
end
|
|
3344
3545
|
|
|
3345
3546
|
# The EagerGraphLoader instance used for converting eager_graph results.
|
|
@@ -3350,15 +3551,30 @@ module Sequel
|
|
|
3350
3551
|
egl.dup
|
|
3351
3552
|
end
|
|
3352
3553
|
|
|
3353
|
-
# Eagerly load all specified associations
|
|
3354
|
-
def eager_load(a, eager_assoc=@opts[:eager])
|
|
3554
|
+
# Eagerly load all specified associations.
|
|
3555
|
+
def eager_load(a, eager_assoc=@opts[:eager], m=model)
|
|
3355
3556
|
return if a.empty?
|
|
3557
|
+
|
|
3558
|
+
# Reflections for all associations to eager load
|
|
3559
|
+
reflections = eager_assoc.keys.map{|assoc| m.association_reflection(assoc) || (raise Sequel::UndefinedAssociation, "Model: #{self}, Association: #{assoc}")}
|
|
3560
|
+
|
|
3561
|
+
perform_eager_loads(prepare_eager_load(a, reflections, eager_assoc))
|
|
3562
|
+
|
|
3563
|
+
reflections.each do |r|
|
|
3564
|
+
a.each{|object| object.send(:run_association_callbacks, r, :after_load, object.associations[r[:name]])} if r[:after_load]
|
|
3565
|
+
end
|
|
3566
|
+
|
|
3567
|
+
nil
|
|
3568
|
+
end
|
|
3569
|
+
|
|
3570
|
+
# Prepare a hash loaders and eager options which will be used to implement the eager loading.
|
|
3571
|
+
def prepare_eager_load(a, reflections, eager_assoc)
|
|
3572
|
+
eager_load_data = {}
|
|
3573
|
+
|
|
3356
3574
|
# Key is foreign/primary key name symbol.
|
|
3357
3575
|
# Value is hash with keys being foreign/primary key values (generally integers)
|
|
3358
3576
|
# and values being an array of current model objects with that specific foreign/primary key
|
|
3359
3577
|
key_hash = {}
|
|
3360
|
-
# Reflections for all associations to eager load
|
|
3361
|
-
reflections = eager_assoc.keys.map{|assoc| model.association_reflection(assoc) || (raise Sequel::UndefinedAssociation, "Model: #{self}, Association: #{assoc}")}
|
|
3362
3578
|
|
|
3363
3579
|
# Populate the key_hash entry for each association being eagerly loaded
|
|
3364
3580
|
reflections.each do |r|
|
|
@@ -3389,7 +3605,6 @@ module Sequel
|
|
|
3389
3605
|
id_map = nil
|
|
3390
3606
|
end
|
|
3391
3607
|
|
|
3392
|
-
loader = r[:eager_loader]
|
|
3393
3608
|
associations = eager_assoc[r[:name]]
|
|
3394
3609
|
if associations.respond_to?(:call)
|
|
3395
3610
|
eager_block = associations
|
|
@@ -3397,9 +3612,23 @@ module Sequel
|
|
|
3397
3612
|
elsif associations.is_a?(Hash) && associations.length == 1 && (pr_assoc = associations.to_a.first) && pr_assoc.first.respond_to?(:call)
|
|
3398
3613
|
eager_block, associations = pr_assoc
|
|
3399
3614
|
end
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
end
|
|
3615
|
+
|
|
3616
|
+
eager_load_data[r[:eager_loader]] = {:key_hash=>key_hash, :rows=>a, :associations=>associations, :self=>self, :eager_block=>eager_block, :id_map=>id_map}
|
|
3617
|
+
end
|
|
3618
|
+
|
|
3619
|
+
eager_load_data
|
|
3620
|
+
end
|
|
3621
|
+
|
|
3622
|
+
# Using the hash of loaders and eager options, perform the eager loading.
|
|
3623
|
+
def perform_eager_loads(eager_load_data)
|
|
3624
|
+
eager_load_data.map do |loader, eo|
|
|
3625
|
+
perform_eager_load(loader, eo)
|
|
3626
|
+
end
|
|
3627
|
+
end
|
|
3628
|
+
|
|
3629
|
+
# Perform eager loading for a single association using the loader and eager options.
|
|
3630
|
+
def perform_eager_load(loader, eo)
|
|
3631
|
+
loader.call(eo)
|
|
3403
3632
|
end
|
|
3404
3633
|
|
|
3405
3634
|
# Return a subquery expression for filering by a many_to_many association
|