sequel 5.45.0 → 5.77.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (218) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +434 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +59 -27
  5. data/bin/sequel +11 -3
  6. data/doc/advanced_associations.rdoc +16 -14
  7. data/doc/association_basics.rdoc +119 -24
  8. data/doc/cheat_sheet.rdoc +11 -3
  9. data/doc/mass_assignment.rdoc +1 -1
  10. data/doc/migration.rdoc +27 -6
  11. data/doc/model_hooks.rdoc +1 -1
  12. data/doc/object_model.rdoc +8 -8
  13. data/doc/opening_databases.rdoc +28 -12
  14. data/doc/postgresql.rdoc +16 -8
  15. data/doc/querying.rdoc +5 -3
  16. data/doc/release_notes/5.46.0.txt +87 -0
  17. data/doc/release_notes/5.47.0.txt +59 -0
  18. data/doc/release_notes/5.48.0.txt +14 -0
  19. data/doc/release_notes/5.49.0.txt +59 -0
  20. data/doc/release_notes/5.50.0.txt +78 -0
  21. data/doc/release_notes/5.51.0.txt +47 -0
  22. data/doc/release_notes/5.52.0.txt +87 -0
  23. data/doc/release_notes/5.53.0.txt +23 -0
  24. data/doc/release_notes/5.54.0.txt +27 -0
  25. data/doc/release_notes/5.55.0.txt +21 -0
  26. data/doc/release_notes/5.56.0.txt +51 -0
  27. data/doc/release_notes/5.57.0.txt +23 -0
  28. data/doc/release_notes/5.58.0.txt +31 -0
  29. data/doc/release_notes/5.59.0.txt +73 -0
  30. data/doc/release_notes/5.60.0.txt +22 -0
  31. data/doc/release_notes/5.61.0.txt +43 -0
  32. data/doc/release_notes/5.62.0.txt +132 -0
  33. data/doc/release_notes/5.63.0.txt +33 -0
  34. data/doc/release_notes/5.64.0.txt +50 -0
  35. data/doc/release_notes/5.65.0.txt +21 -0
  36. data/doc/release_notes/5.66.0.txt +24 -0
  37. data/doc/release_notes/5.67.0.txt +32 -0
  38. data/doc/release_notes/5.68.0.txt +61 -0
  39. data/doc/release_notes/5.69.0.txt +26 -0
  40. data/doc/release_notes/5.70.0.txt +35 -0
  41. data/doc/release_notes/5.71.0.txt +21 -0
  42. data/doc/release_notes/5.72.0.txt +33 -0
  43. data/doc/release_notes/5.73.0.txt +66 -0
  44. data/doc/release_notes/5.74.0.txt +45 -0
  45. data/doc/release_notes/5.75.0.txt +35 -0
  46. data/doc/release_notes/5.76.0.txt +86 -0
  47. data/doc/release_notes/5.77.0.txt +63 -0
  48. data/doc/schema_modification.rdoc +1 -1
  49. data/doc/security.rdoc +9 -9
  50. data/doc/sharding.rdoc +3 -1
  51. data/doc/sql.rdoc +27 -15
  52. data/doc/testing.rdoc +23 -13
  53. data/doc/transactions.rdoc +6 -6
  54. data/doc/virtual_rows.rdoc +1 -1
  55. data/lib/sequel/adapters/ado/access.rb +1 -1
  56. data/lib/sequel/adapters/ado.rb +1 -1
  57. data/lib/sequel/adapters/amalgalite.rb +3 -5
  58. data/lib/sequel/adapters/ibmdb.rb +3 -3
  59. data/lib/sequel/adapters/jdbc/derby.rb +8 -0
  60. data/lib/sequel/adapters/jdbc/h2.rb +63 -10
  61. data/lib/sequel/adapters/jdbc/hsqldb.rb +8 -0
  62. data/lib/sequel/adapters/jdbc/postgresql.rb +7 -4
  63. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +15 -0
  64. data/lib/sequel/adapters/jdbc/sqlserver.rb +4 -0
  65. data/lib/sequel/adapters/jdbc.rb +24 -22
  66. data/lib/sequel/adapters/mysql.rb +92 -67
  67. data/lib/sequel/adapters/mysql2.rb +56 -51
  68. data/lib/sequel/adapters/odbc/mssql.rb +1 -1
  69. data/lib/sequel/adapters/odbc.rb +1 -1
  70. data/lib/sequel/adapters/oracle.rb +4 -3
  71. data/lib/sequel/adapters/postgres.rb +89 -45
  72. data/lib/sequel/adapters/shared/access.rb +11 -1
  73. data/lib/sequel/adapters/shared/db2.rb +42 -0
  74. data/lib/sequel/adapters/shared/mssql.rb +91 -10
  75. data/lib/sequel/adapters/shared/mysql.rb +78 -3
  76. data/lib/sequel/adapters/shared/oracle.rb +86 -7
  77. data/lib/sequel/adapters/shared/postgres.rb +576 -171
  78. data/lib/sequel/adapters/shared/sqlanywhere.rb +21 -5
  79. data/lib/sequel/adapters/shared/sqlite.rb +92 -8
  80. data/lib/sequel/adapters/sqlanywhere.rb +1 -1
  81. data/lib/sequel/adapters/sqlite.rb +99 -18
  82. data/lib/sequel/adapters/tinytds.rb +1 -1
  83. data/lib/sequel/adapters/trilogy.rb +117 -0
  84. data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
  85. data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
  86. data/lib/sequel/ast_transformer.rb +6 -0
  87. data/lib/sequel/connection_pool/sharded_single.rb +5 -7
  88. data/lib/sequel/connection_pool/sharded_threaded.rb +16 -11
  89. data/lib/sequel/connection_pool/sharded_timed_queue.rb +374 -0
  90. data/lib/sequel/connection_pool/single.rb +6 -8
  91. data/lib/sequel/connection_pool/threaded.rb +14 -8
  92. data/lib/sequel/connection_pool/timed_queue.rb +270 -0
  93. data/lib/sequel/connection_pool.rb +57 -31
  94. data/lib/sequel/core.rb +17 -18
  95. data/lib/sequel/database/connecting.rb +27 -3
  96. data/lib/sequel/database/dataset.rb +16 -6
  97. data/lib/sequel/database/misc.rb +70 -14
  98. data/lib/sequel/database/query.rb +73 -2
  99. data/lib/sequel/database/schema_generator.rb +11 -6
  100. data/lib/sequel/database/schema_methods.rb +23 -4
  101. data/lib/sequel/database/transactions.rb +6 -0
  102. data/lib/sequel/dataset/actions.rb +111 -15
  103. data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
  104. data/lib/sequel/dataset/features.rb +20 -1
  105. data/lib/sequel/dataset/misc.rb +12 -2
  106. data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
  107. data/lib/sequel/dataset/query.rb +170 -41
  108. data/lib/sequel/dataset/sql.rb +190 -71
  109. data/lib/sequel/dataset.rb +4 -0
  110. data/lib/sequel/extensions/_model_pg_row.rb +0 -12
  111. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  112. data/lib/sequel/extensions/any_not_empty.rb +2 -2
  113. data/lib/sequel/extensions/async_thread_pool.rb +14 -13
  114. data/lib/sequel/extensions/auto_cast_date_and_time.rb +94 -0
  115. data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
  116. data/lib/sequel/extensions/connection_expiration.rb +15 -9
  117. data/lib/sequel/extensions/connection_validator.rb +16 -11
  118. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  119. data/lib/sequel/extensions/core_refinements.rb +36 -11
  120. data/lib/sequel/extensions/date_arithmetic.rb +36 -8
  121. data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
  122. data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
  123. data/lib/sequel/extensions/duplicate_columns_handler.rb +11 -10
  124. data/lib/sequel/extensions/index_caching.rb +5 -1
  125. data/lib/sequel/extensions/inflector.rb +1 -1
  126. data/lib/sequel/extensions/is_distinct_from.rb +141 -0
  127. data/lib/sequel/extensions/looser_typecasting.rb +3 -0
  128. data/lib/sequel/extensions/migration.rb +57 -15
  129. data/lib/sequel/extensions/named_timezones.rb +22 -6
  130. data/lib/sequel/extensions/pagination.rb +1 -1
  131. data/lib/sequel/extensions/pg_array.rb +33 -4
  132. data/lib/sequel/extensions/pg_array_ops.rb +2 -2
  133. data/lib/sequel/extensions/pg_auto_parameterize.rb +509 -0
  134. data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +110 -0
  135. data/lib/sequel/extensions/pg_enum.rb +1 -2
  136. data/lib/sequel/extensions/pg_extended_date_support.rb +39 -28
  137. data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
  138. data/lib/sequel/extensions/pg_hstore.rb +6 -1
  139. data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
  140. data/lib/sequel/extensions/pg_inet.rb +10 -11
  141. data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
  142. data/lib/sequel/extensions/pg_interval.rb +11 -11
  143. data/lib/sequel/extensions/pg_json.rb +13 -15
  144. data/lib/sequel/extensions/pg_json_ops.rb +125 -2
  145. data/lib/sequel/extensions/pg_multirange.rb +367 -0
  146. data/lib/sequel/extensions/pg_range.rb +13 -26
  147. data/lib/sequel/extensions/pg_range_ops.rb +37 -9
  148. data/lib/sequel/extensions/pg_row.rb +20 -19
  149. data/lib/sequel/extensions/pg_row_ops.rb +1 -1
  150. data/lib/sequel/extensions/pg_timestamptz.rb +27 -3
  151. data/lib/sequel/extensions/round_timestamps.rb +1 -1
  152. data/lib/sequel/extensions/s.rb +2 -1
  153. data/lib/sequel/extensions/schema_caching.rb +1 -1
  154. data/lib/sequel/extensions/schema_dumper.rb +45 -11
  155. data/lib/sequel/extensions/server_block.rb +10 -13
  156. data/lib/sequel/extensions/set_literalizer.rb +58 -0
  157. data/lib/sequel/extensions/sql_comments.rb +110 -3
  158. data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
  159. data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
  160. data/lib/sequel/extensions/string_agg.rb +1 -1
  161. data/lib/sequel/extensions/string_date_time.rb +19 -23
  162. data/lib/sequel/extensions/symbol_aref.rb +2 -0
  163. data/lib/sequel/extensions/transaction_connection_validator.rb +78 -0
  164. data/lib/sequel/model/associations.rb +286 -92
  165. data/lib/sequel/model/base.rb +53 -33
  166. data/lib/sequel/model/dataset_module.rb +3 -0
  167. data/lib/sequel/model/errors.rb +10 -1
  168. data/lib/sequel/model/exceptions.rb +15 -3
  169. data/lib/sequel/model/inflections.rb +1 -1
  170. data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
  171. data/lib/sequel/plugins/auto_validations.rb +74 -16
  172. data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
  173. data/lib/sequel/plugins/column_encryption.rb +29 -8
  174. data/lib/sequel/plugins/composition.rb +3 -2
  175. data/lib/sequel/plugins/concurrent_eager_loading.rb +4 -4
  176. data/lib/sequel/plugins/constraint_validations.rb +8 -5
  177. data/lib/sequel/plugins/defaults_setter.rb +16 -0
  178. data/lib/sequel/plugins/dirty.rb +1 -1
  179. data/lib/sequel/plugins/enum.rb +124 -0
  180. data/lib/sequel/plugins/finder.rb +4 -2
  181. data/lib/sequel/plugins/insert_conflict.rb +4 -0
  182. data/lib/sequel/plugins/instance_specific_default.rb +1 -1
  183. data/lib/sequel/plugins/json_serializer.rb +2 -2
  184. data/lib/sequel/plugins/lazy_attributes.rb +3 -0
  185. data/lib/sequel/plugins/list.rb +8 -3
  186. data/lib/sequel/plugins/many_through_many.rb +109 -10
  187. data/lib/sequel/plugins/mssql_optimistic_locking.rb +8 -38
  188. data/lib/sequel/plugins/nested_attributes.rb +4 -4
  189. data/lib/sequel/plugins/optimistic_locking.rb +9 -42
  190. data/lib/sequel/plugins/optimistic_locking_base.rb +55 -0
  191. data/lib/sequel/plugins/paged_operations.rb +181 -0
  192. data/lib/sequel/plugins/pg_array_associations.rb +46 -34
  193. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +9 -3
  194. data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +109 -0
  195. data/lib/sequel/plugins/prepared_statements.rb +12 -2
  196. data/lib/sequel/plugins/prepared_statements_safe.rb +2 -1
  197. data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
  198. data/lib/sequel/plugins/rcte_tree.rb +7 -4
  199. data/lib/sequel/plugins/require_valid_schema.rb +67 -0
  200. data/lib/sequel/plugins/serialization.rb +1 -0
  201. data/lib/sequel/plugins/serialization_modification_detection.rb +1 -0
  202. data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
  203. data/lib/sequel/plugins/sql_comments.rb +189 -0
  204. data/lib/sequel/plugins/static_cache.rb +39 -1
  205. data/lib/sequel/plugins/static_cache_cache.rb +5 -1
  206. data/lib/sequel/plugins/subclasses.rb +28 -11
  207. data/lib/sequel/plugins/tactical_eager_loading.rb +23 -10
  208. data/lib/sequel/plugins/timestamps.rb +1 -1
  209. data/lib/sequel/plugins/unused_associations.rb +521 -0
  210. data/lib/sequel/plugins/update_or_create.rb +1 -1
  211. data/lib/sequel/plugins/validate_associated.rb +22 -12
  212. data/lib/sequel/plugins/validation_helpers.rb +41 -11
  213. data/lib/sequel/plugins/validation_helpers_generic_type_messages.rb +73 -0
  214. data/lib/sequel/plugins/xml_serializer.rb +1 -1
  215. data/lib/sequel/sql.rb +1 -1
  216. data/lib/sequel/timezones.rb +12 -14
  217. data/lib/sequel/version.rb +1 -1
  218. metadata +109 -19
@@ -274,7 +274,9 @@ module Sequel
274
274
  cascade = eo[:associations]
275
275
  eager_limit = nil
276
276
 
277
- if eo[:eager_block] || eo[:loader] == false
277
+ if eo[:no_results]
278
+ no_results = true
279
+ elsif eo[:eager_block] || eo[:loader] == false || !use_placeholder_loader?
278
280
  ds = eager_loading_dataset(eo)
279
281
 
280
282
  strategy = ds.opts[:eager_limit_strategy] || strategy
@@ -297,13 +299,28 @@ module Sequel
297
299
  strategy = :ruby if strategy == :correlated_subquery
298
300
  strategy = nil if strategy == :ruby && assign_singular?
299
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
300
307
  elsif strategy == :union
301
308
  objects = []
302
309
  ds = associated_dataset
303
310
  loader = union_eager_loader
304
311
  joiner = " UNION ALL "
305
312
  ids.each_slice(subqueries_per_union).each do |slice|
306
- objects.concat(ds.with_sql(slice.map{|k| loader.sql(*k)}.join(joiner)).to_a)
313
+ sql = loader.send(:sql_origin)
314
+ join = false
315
+ slice.each do |k|
316
+ if join
317
+ sql << joiner
318
+ else
319
+ join = true
320
+ end
321
+ loader.append_sql(sql, *k)
322
+ end
323
+ objects.concat(ds.with_sql(sql).to_a)
307
324
  end
308
325
  ds = ds.eager(cascade) if cascade
309
326
  ds.send(:post_load, objects)
@@ -313,7 +330,7 @@ module Sequel
313
330
  objects = loader.all(ids)
314
331
  end
315
332
 
316
- Sequel.synchronize_with(eo[:mutex]){objects.each(&block)}
333
+ Sequel.synchronize_with(eo[:mutex]){objects.each(&block)} unless no_results
317
334
 
318
335
  if strategy == :ruby
319
336
  apply_ruby_eager_limit_strategy(rows, eager_limit || limit_and_offset)
@@ -439,7 +456,7 @@ module Sequel
439
456
  def placeholder_loader
440
457
  if use_placeholder_loader?
441
458
  cached_fetch(:placeholder_loader) do
442
- Sequel::Dataset::PlaceholderLiteralizer.loader(associated_dataset) do |pl, ds|
459
+ associated_dataset.placeholder_literalizer_loader do |pl, ds|
443
460
  ds = ds.where(Sequel.&(*predicate_keys.map{|k| SQL::BooleanExpression.new(:'=', k, pl.arg)}))
444
461
  if self[:block]
445
462
  ds = self[:block].call(ds)
@@ -638,9 +655,7 @@ module Sequel
638
655
  # given the hash passed to the eager loader.
639
656
  def eager_loading_dataset(eo=OPTS)
640
657
  ds = eo[:dataset] || associated_eager_dataset
641
- if id_map = eo[:id_map]
642
- ds = ds.where(eager_loading_predicate_condition(id_map.keys))
643
- end
658
+ ds = eager_loading_set_predicate_condition(ds, eo)
644
659
  if associations = eo[:associations]
645
660
  ds = ds.eager(associations)
646
661
  end
@@ -667,6 +682,15 @@ module Sequel
667
682
  self[:model].default_eager_limit_strategy || :ruby
668
683
  end
669
684
 
685
+ # Set the predicate condition for the eager loading dataset based on the id map
686
+ # in the eager loading options.
687
+ def eager_loading_set_predicate_condition(ds, eo)
688
+ if id_map = eo[:id_map]
689
+ ds = ds.where(eager_loading_predicate_condition(id_map.keys))
690
+ end
691
+ ds
692
+ end
693
+
670
694
  # The predicate condition to use for the eager_loader.
671
695
  def eager_loading_predicate_condition(keys)
672
696
  {predicate_key=>keys}
@@ -734,8 +758,8 @@ module Sequel
734
758
  # A placeholder literalizer used to speed up eager loading.
735
759
  def placeholder_eager_loader
736
760
  cached_fetch(:placeholder_eager_loader) do
737
- Sequel::Dataset::PlaceholderLiteralizer.loader(associated_dataset) do |pl, ds|
738
- apply_eager_limit_strategy(eager_loading_dataset.where(predicate_key=>pl.arg), eager_limit_strategy)
761
+ eager_loading_dataset.placeholder_literalizer_loader do |pl, ds|
762
+ apply_eager_limit_strategy(ds.where(predicate_key=>pl.arg), eager_limit_strategy)
739
763
  end
740
764
  end
741
765
  end
@@ -794,7 +818,7 @@ module Sequel
794
818
  # loading a limited association.
795
819
  def union_eager_loader
796
820
  cached_fetch(:union_eager_loader) do
797
- Sequel::Dataset::PlaceholderLiteralizer.loader(associated_dataset) do |pl, ds|
821
+ associated_dataset.placeholder_literalizer_loader do |pl, ds|
798
822
  ds = self[:eager_block].call(ds) if self[:eager_block]
799
823
  keys = predicate_keys
800
824
  ds = ds.where(keys.map{pl.arg}.zip(keys))
@@ -808,7 +832,7 @@ module Sequel
808
832
 
809
833
  # Whether the placeholder loader can be used to load the association.
810
834
  def use_placeholder_loader?
811
- self[:use_placeholder_loader]
835
+ self[:use_placeholder_loader] && _associated_dataset.supports_placeholder_literalizer?
812
836
  end
813
837
  end
814
838
 
@@ -1318,7 +1342,7 @@ module Sequel
1318
1342
 
1319
1343
  # many_to_many associations need to select a key in an associated table to eagerly load
1320
1344
  def eager_loading_use_associated_key?
1321
- true
1345
+ !separate_query_per_table?
1322
1346
  end
1323
1347
 
1324
1348
  # The source of the join table. This is the join table itself, unless it
@@ -1375,10 +1399,30 @@ module Sequel
1375
1399
  cached_fetch(:select){default_select}
1376
1400
  end
1377
1401
 
1402
+ # Whether a separate query should be used for the join table.
1403
+ def separate_query_per_table?
1404
+ self[:join_table_db]
1405
+ end
1406
+
1378
1407
  private
1379
1408
 
1409
+ # Join to the the join table, unless using a separate query per table.
1380
1410
  def _associated_dataset
1381
- super.inner_join(self[:join_table], self[:right_keys].zip(right_primary_keys), :qualify=>:deep)
1411
+ if separate_query_per_table?
1412
+ super
1413
+ else
1414
+ super.inner_join(self[:join_table], self[:right_keys].zip(right_primary_keys), :qualify=>:deep)
1415
+ end
1416
+ end
1417
+
1418
+ # Use the right_keys from the eager loading options if
1419
+ # using a separate query per table.
1420
+ def eager_loading_set_predicate_condition(ds, eo)
1421
+ if separate_query_per_table?
1422
+ ds.where(right_primary_key=>eo[:right_keys])
1423
+ else
1424
+ super
1425
+ end
1382
1426
  end
1383
1427
 
1384
1428
  # The default selection for associations that require joins. These do not use the default
@@ -1595,6 +1639,7 @@ module Sequel
1595
1639
  # === Multiple Types
1596
1640
  # :adder :: Proc used to define the private _add_* method for doing the database work
1597
1641
  # to associate the given object to the current object (*_to_many assocations).
1642
+ # Set to nil to not define a add_* method for the association.
1598
1643
  # :after_add :: Symbol, Proc, or array of both/either specifying a callback to call
1599
1644
  # after a new item is added to the association.
1600
1645
  # :after_load :: Symbol, Proc, or array of both/either specifying a callback to call
@@ -1605,6 +1650,8 @@ module Sequel
1605
1650
  # after an item is set using the association setter method.
1606
1651
  # :allow_eager :: If set to false, you cannot load the association eagerly
1607
1652
  # via eager or eager_graph
1653
+ # :allow_eager_graph :: If set to false, you cannot load the association eagerly via eager_graph.
1654
+ # :allow_filtering_by :: If set to false, you cannot use the association when filtering
1608
1655
  # :before_add :: Symbol, Proc, or array of both/either specifying a callback to call
1609
1656
  # before a new item is added to the association.
1610
1657
  # :before_remove :: Symbol, Proc, or array of both/either specifying a callback to call
@@ -1623,6 +1670,7 @@ module Sequel
1623
1670
  # the class. <tt>class: 'Foo', class_namespace: 'Bar'</tt> looks for <tt>::Bar::Foo</tt>.)
1624
1671
  # :clearer :: Proc used to define the private _remove_all_* method for doing the database work
1625
1672
  # to remove all objects associated to the current object (*_to_many assocations).
1673
+ # Set to nil to not define a remove_all_* method for the association.
1626
1674
  # :clone :: Merge the current options and block into the options and block used in defining
1627
1675
  # the given association. Can be used to DRY up a bunch of similar associations that
1628
1676
  # all share the same options such as :class and :key, while changing the order and block used.
@@ -1677,18 +1725,26 @@ module Sequel
1677
1725
  # :graph_only_conditions :: The conditions to use on the SQL join when eagerly loading
1678
1726
  # the association via +eager_graph+, instead of the default conditions specified by the
1679
1727
  # foreign/primary keys. This option causes the :graph_conditions option to be ignored.
1680
- # :graph_order :: Over the order to use when using eager_graph, instead of the default order. This should be used
1728
+ # :graph_order :: the order to use when using eager_graph, instead of the default order. This should be used
1681
1729
  # in the case where :order contains an identifier qualified by the table's name, which may not match
1682
1730
  # the alias used when eager graphing. By setting this to the unqualified identifier, it will be
1683
1731
  # automatically qualified when using eager_graph.
1684
1732
  # :graph_select :: A column or array of columns to select from the associated table
1685
1733
  # when eagerly loading the association via +eager_graph+. Defaults to all
1686
1734
  # columns in the associated table.
1735
+ # :graph_use_association_block :: Makes eager_graph consider the association block. Without this, eager_graph
1736
+ # ignores the bock and only use the :graph_* options.
1737
+ # :instance_specific :: Marks the association as instance specific. Should be used if the association block
1738
+ # uses instance specific state, or transient state (accessing current date/time, etc.).
1687
1739
  # :limit :: Limit the number of records to the provided value. Use
1688
1740
  # an array with two elements for the value to specify a
1689
1741
  # limit (first element) and an offset (second element).
1690
1742
  # :methods_module :: The module that methods the association creates will be placed into. Defaults
1691
1743
  # to the module containing the model's columns.
1744
+ # :no_association_method :: Do not add a method for the association. This can save memory if the association
1745
+ # method is never used.
1746
+ # :no_dataset_method :: Do not add a method for the association dataset. This can save memory if the dataset
1747
+ # method is never used.
1692
1748
  # :order :: the column(s) by which to order the association dataset. Can be a
1693
1749
  # singular column symbol or an array of column symbols.
1694
1750
  # :order_eager_graph :: Whether to add the association's order to the graphed dataset's order when graphing
@@ -1701,6 +1757,7 @@ module Sequel
1701
1757
  # the current association's key(s). Set to nil to not use a reciprocal.
1702
1758
  # :remover :: Proc used to define the private _remove_* method for doing the database work
1703
1759
  # to remove the association between the given object and the current object (*_to_many assocations).
1760
+ # Set to nil to not define a remove_* method for the association.
1704
1761
  # :select :: the columns to select. Defaults to the associated class's table_name.* in an association
1705
1762
  # that uses joins, which means it doesn't include the attributes from the
1706
1763
  # join table. If you want to include the join table attributes, you can
@@ -1709,6 +1766,7 @@ module Sequel
1709
1766
  # the same name in both the join table and the associated table.
1710
1767
  # :setter :: Proc used to define the private _*= method for doing the work to setup the assocation
1711
1768
  # between the given object and the current object (*_to_one associations).
1769
+ # Set to nil to not define a setter method for the association.
1712
1770
  # :subqueries_per_union :: The number of subqueries to use in each UNION query, for eager
1713
1771
  # loading limited associations using the default :union strategy.
1714
1772
  # :validate :: Set to false to not validate when implicitly saving any associated object.
@@ -1765,6 +1823,9 @@ module Sequel
1765
1823
  # underscored, sorted, and joined with '_'.
1766
1824
  # :join_table_block :: proc that can be used to modify the dataset used in the add/remove/remove_all
1767
1825
  # methods. Should accept a dataset argument and return a modified dataset if present.
1826
+ # :join_table_db :: When retrieving records when using lazy loading or eager loading via +eager+, instead of
1827
+ # a join between to the join table and the associated table, use a separate query for the
1828
+ # join table using the given Database object.
1768
1829
  # :left_key :: foreign key in join table that points to current model's
1769
1830
  # primary key, as a symbol. Defaults to :"#{self.name.underscore}_id".
1770
1831
  # Can use an array of symbols for a composite key association.
@@ -1794,7 +1855,9 @@ module Sequel
1794
1855
 
1795
1856
  if opts[:clone]
1796
1857
  cloned_assoc = association_reflection(opts[:clone])
1858
+ remove_class_name = orig_opts[:class] && !orig_opts[:class_name]
1797
1859
  orig_opts = cloned_assoc[:orig_opts].merge(orig_opts)
1860
+ orig_opts.delete(:class_name) if remove_class_name
1798
1861
  end
1799
1862
 
1800
1863
  opts = Hash[default_association_options]
@@ -1812,6 +1875,16 @@ module Sequel
1812
1875
  # in certain places to disable optimizations.
1813
1876
  opts[:instance_specific] = _association_instance_specific_default(name)
1814
1877
  end
1878
+ if (orig_opts[:instance_specific] || orig_opts[:dataset]) && !opts.has_key?(:allow_eager) && !opts[:eager_loader]
1879
+ # For associations explicitly marked as instance specific, or that use the
1880
+ # :dataset option, where :allow_eager is not set, and no :eager_loader is
1881
+ # provided, disallow eager loading. In these cases, eager loading is
1882
+ # unlikely to work. This is not done for implicit setting of :instance_specific,
1883
+ # because implicit use is done by default for all associations with blocks,
1884
+ # and the vast majority of associations with blocks use the block for filtering
1885
+ # in a manner compatible with eager loading.
1886
+ opts[:allow_eager] = false
1887
+ end
1815
1888
  opts = assoc_class.new.merge!(opts)
1816
1889
 
1817
1890
  if opts[:clone] && !opts.cloneable?(cloned_assoc)
@@ -1841,8 +1914,7 @@ module Sequel
1841
1914
  # Remove :class entry if it exists and is nil, to work with cached_fetch
1842
1915
  opts.delete(:class) unless opts[:class]
1843
1916
 
1844
- send(:"def_#{type}", opts)
1845
- def_association_instance_methods(opts)
1917
+ def_association(opts)
1846
1918
 
1847
1919
  orig_opts.delete(:clone)
1848
1920
  opts[:orig_class] = orig_opts[:class] || orig_opts[:class_name]
@@ -1956,6 +2028,13 @@ module Sequel
1956
2028
  association_module(opts).send(:private, name)
1957
2029
  end
1958
2030
 
2031
+ # Delegate to the type-specific association method to setup the
2032
+ # association, and define the association instance methods.
2033
+ def def_association(opts)
2034
+ send(:"def_#{opts[:type]}", opts)
2035
+ def_association_instance_methods(opts)
2036
+ end
2037
+
1959
2038
  # Adds the association method to the association methods module.
1960
2039
  def def_association_method(opts)
1961
2040
  association_module_def(opts.association_method, opts) do |dynamic_opts=OPTS, &block|
@@ -1981,13 +2060,13 @@ module Sequel
1981
2060
  opts[:setter_method] = :"#{opts[:name]}="
1982
2061
  end
1983
2062
 
1984
- association_module_def(opts.dataset_method, opts){_dataset(opts)}
2063
+ association_module_def(opts.dataset_method, opts){_dataset(opts)} unless opts[:no_dataset_method]
1985
2064
  if opts[:block]
1986
2065
  opts[:block_method] = Plugins.def_sequel_method(association_module(opts), "#{opts[:name]}_block", 1, &opts[:block])
1987
2066
  end
1988
2067
  opts[:dataset_opt_arity] = opts[:dataset].arity == 0 ? 0 : 1
1989
2068
  opts[:dataset_opt_method] = Plugins.def_sequel_method(association_module(opts), "#{opts[:name]}_dataset_opt", opts[:dataset_opt_arity], &opts[:dataset])
1990
- def_association_method(opts)
2069
+ def_association_method(opts) unless opts[:no_association_method]
1991
2070
 
1992
2071
  return if opts[:read_only]
1993
2072
 
@@ -2031,7 +2110,7 @@ module Sequel
2031
2110
  raise(Error, "mismatched number of right keys: #{rcks.inspect} vs #{rcpks.inspect}") unless rcks.length == rcpks.length
2032
2111
  end
2033
2112
  opts[:uses_left_composite_keys] = lcks.length > 1
2034
- opts[:uses_right_composite_keys] = rcks.length > 1
2113
+ uses_rcks = opts[:uses_right_composite_keys] = rcks.length > 1
2035
2114
  opts[:cartesian_product_number] ||= one_through_one ? 0 : 1
2036
2115
  join_table = (opts[:join_table] ||= opts.default_join_table)
2037
2116
  opts[:left_key_alias] ||= opts.default_associated_key_alias
@@ -2040,8 +2119,75 @@ module Sequel
2040
2119
  opts[:after_load] ||= []
2041
2120
  opts[:after_load].unshift(:array_uniq!)
2042
2121
  end
2043
- opts[:dataset] ||= opts.association_dataset_proc
2044
- opts[:eager_loader] ||= opts.method(:default_eager_loader)
2122
+ if join_table_db = opts[:join_table_db]
2123
+ opts[:use_placeholder_loader] = false
2124
+ opts[:allow_eager_graph] = false
2125
+ opts[:allow_filtering_by] = false
2126
+ opts[:eager_limit_strategy] = nil
2127
+ join_table_ds = join_table_db.from(join_table)
2128
+ opts[:dataset] ||= proc do |r|
2129
+ vals = join_table_ds.where(lcks.zip(lcpks.map{|k| get_column_value(k)})).select_map(right)
2130
+ ds = r.associated_dataset.where(opts.right_primary_key => vals)
2131
+ if uses_rcks
2132
+ vals.delete_if{|v| v.any?(&:nil?)}
2133
+ else
2134
+ vals.delete(nil)
2135
+ end
2136
+ ds = ds.clone(:no_results=>true) if vals.empty?
2137
+ ds
2138
+ end
2139
+ opts[:eager_loader] ||= proc do |eo|
2140
+ h = eo[:id_map]
2141
+ assign_singular = opts.assign_singular?
2142
+ rpk = opts.right_primary_key
2143
+ name = opts[:name]
2144
+
2145
+ join_map = join_table_ds.where(left=>h.keys).select_hash_groups(right, left)
2146
+
2147
+ if uses_rcks
2148
+ join_map.delete_if{|v,| v.any?(&:nil?)}
2149
+ else
2150
+ join_map.delete(nil)
2151
+ end
2152
+
2153
+ eo = Hash[eo]
2154
+
2155
+ if join_map.empty?
2156
+ eo[:no_results] = true
2157
+ else
2158
+ join_map.each_value do |vs|
2159
+ vs.replace(vs.flat_map{|v| h[v]})
2160
+ vs.uniq!
2161
+ end
2162
+
2163
+ eo[:loader] = false
2164
+ eo[:right_keys] = join_map.keys
2165
+ end
2166
+
2167
+ opts[:model].eager_load_results(opts, eo) do |assoc_record|
2168
+ rpkv = if uses_rcks
2169
+ assoc_record.values.values_at(*rpk)
2170
+ else
2171
+ assoc_record.values[rpk]
2172
+ end
2173
+
2174
+ objects = join_map[rpkv]
2175
+
2176
+ if assign_singular
2177
+ objects.each do |object|
2178
+ object.associations[name] ||= assoc_record
2179
+ end
2180
+ else
2181
+ objects.each do |object|
2182
+ object.associations[name].push(assoc_record)
2183
+ end
2184
+ end
2185
+ end
2186
+ end
2187
+ else
2188
+ opts[:dataset] ||= opts.association_dataset_proc
2189
+ opts[:eager_loader] ||= opts.method(:default_eager_loader)
2190
+ end
2045
2191
 
2046
2192
  join_type = opts[:graph_join_type]
2047
2193
  select = opts[:graph_select]
@@ -2075,50 +2221,60 @@ module Sequel
2075
2221
  return if opts[:read_only]
2076
2222
 
2077
2223
  if one_through_one
2078
- opts[:setter] ||= proc do |o|
2079
- h = {}
2080
- lh = lcks.zip(lcpks.map{|k| get_column_value(k)})
2081
- jtds = _join_table_dataset(opts).where(lh)
2224
+ unless opts.has_key?(:setter)
2225
+ opts[:setter] = proc do |o|
2226
+ h = {}
2227
+ lh = lcks.zip(lcpks.map{|k| get_column_value(k)})
2228
+ jtds = _join_table_dataset(opts).where(lh)
2082
2229
 
2083
- checked_transaction do
2084
- current = jtds.first
2085
-
2086
- if o
2087
- new_values = []
2088
- rcks.zip(opts.right_primary_key_methods).each{|k, pk| new_values << (h[k] = o.get_column_value(pk))}
2089
- end
2230
+ checked_transaction do
2231
+ current = jtds.first
2090
2232
 
2091
- if current
2092
- current_values = rcks.map{|k| current[k]}
2093
- jtds = jtds.where(rcks.zip(current_values))
2094
2233
  if o
2095
- if current_values != new_values
2096
- jtds.update(h)
2234
+ new_values = []
2235
+ rcks.zip(opts.right_primary_key_methods).each{|k, pk| new_values << (h[k] = o.get_column_value(pk))}
2236
+ end
2237
+
2238
+ if current
2239
+ current_values = rcks.map{|k| current[k]}
2240
+ jtds = jtds.where(rcks.zip(current_values))
2241
+ if o
2242
+ if current_values != new_values
2243
+ jtds.update(h)
2244
+ end
2245
+ else
2246
+ jtds.delete
2097
2247
  end
2098
- else
2099
- jtds.delete
2248
+ elsif o
2249
+ lh.each{|k,v| h[k] = v}
2250
+ jtds.insert(h)
2100
2251
  end
2101
- elsif o
2102
- lh.each{|k,v| h[k] = v}
2103
- jtds.insert(h)
2104
2252
  end
2105
2253
  end
2106
2254
  end
2107
- opts[:_setter] = proc{|o| set_one_through_one_associated_object(opts, o)}
2255
+ if opts.fetch(:setter, true)
2256
+ opts[:_setter] = proc{|o| set_one_through_one_associated_object(opts, o)}
2257
+ end
2108
2258
  else
2109
- opts[:adder] ||= proc do |o|
2110
- h = {}
2111
- lcks.zip(lcpks).each{|k, pk| h[k] = get_column_value(pk)}
2112
- rcks.zip(opts.right_primary_key_methods).each{|k, pk| h[k] = o.get_column_value(pk)}
2113
- _join_table_dataset(opts).insert(h)
2259
+ unless opts.has_key?(:adder)
2260
+ opts[:adder] = proc do |o|
2261
+ h = {}
2262
+ lcks.zip(lcpks).each{|k, pk| h[k] = get_column_value(pk)}
2263
+ rcks.zip(opts.right_primary_key_methods).each{|k, pk| h[k] = o.get_column_value(pk)}
2264
+ _join_table_dataset(opts).insert(h)
2265
+ end
2114
2266
  end
2115
2267
 
2116
- opts[:remover] ||= proc do |o|
2117
- _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
2268
+ unless opts.has_key?(:remover)
2269
+ opts[:remover] = proc do |o|
2270
+ _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
2271
+ end
2118
2272
  end
2119
2273
 
2120
- opts[:clearer] ||= proc do
2121
- _join_table_dataset(opts).where(lcks.zip(lcpks.map{|k| get_column_value(k)})).delete
2274
+ unless opts.has_key?(:clearer)
2275
+ opts[:clearer] = proc do
2276
+ _join_table_dataset(opts).where(lcks.zip(lcpks.map{|k| get_column_value(k)})).delete
2277
+ end
2122
2278
  end
2123
2279
  end
2124
2280
  end
@@ -2175,8 +2331,12 @@ module Sequel
2175
2331
 
2176
2332
  return if opts[:read_only]
2177
2333
 
2178
- opts[:setter] ||= proc{|o| cks.zip(opts.primary_key_methods).each{|k, pk| set_column_value(:"#{k}=", (o.get_column_value(pk) if o))}}
2179
- opts[:_setter] = proc{|o| set_associated_object(opts, o)}
2334
+ unless opts.has_key?(:setter)
2335
+ opts[:setter] = proc{|o| cks.zip(opts.primary_key_methods).each{|k, pk| set_column_value(:"#{k}=", (o.get_column_value(pk) if o))}}
2336
+ end
2337
+ if opts.fetch(:setter, true)
2338
+ opts[:_setter] = proc{|o| set_associated_object(opts, o)}
2339
+ end
2180
2340
  end
2181
2341
 
2182
2342
  # Configures one_to_many and one_to_one association reflections and adds the related association methods
@@ -2243,49 +2403,59 @@ module Sequel
2243
2403
  cks.each{|k| ck_nil_hash[k] = nil}
2244
2404
 
2245
2405
  if one_to_one
2246
- opts[:setter] ||= proc do |o|
2247
- up_ds = _apply_association_options(opts, opts.associated_dataset.where(cks.zip(cpks.map{|k| get_column_value(k)})))
2406
+ unless opts.has_key?(:setter)
2407
+ opts[:setter] = proc do |o|
2408
+ up_ds = _apply_association_options(opts, opts.associated_dataset.where(cks.zip(cpks.map{|k| get_column_value(k)})))
2248
2409
 
2249
- 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)))
2250
- if old = up_ds.first
2251
- cks.each{|k| old.set_column_value(:"#{k}=", nil)}
2410
+ 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)))
2411
+ if old = up_ds.first
2412
+ cks.each{|k| old.set_column_value(:"#{k}=", nil)}
2413
+ end
2414
+ save_old = true
2252
2415
  end
2253
- save_old = true
2254
- end
2255
2416
 
2256
- if o
2257
- if !o.new? && !save_old
2258
- up_ds = up_ds.exclude(o.pk_hash)
2417
+ if o
2418
+ if !o.new? && !save_old
2419
+ up_ds = up_ds.exclude(o.pk_hash)
2420
+ end
2421
+ cks.zip(cpks).each{|k, pk| o.set_column_value(:"#{k}=", get_column_value(pk))}
2259
2422
  end
2260
- cks.zip(cpks).each{|k, pk| o.set_column_value(:"#{k}=", get_column_value(pk))}
2261
- end
2262
2423
 
2263
- checked_transaction do
2264
- if save_old
2265
- old.save(save_opts) || raise(Sequel::Error, "invalid previously associated object, cannot save") if old
2266
- else
2267
- up_ds.skip_limit_check.update(ck_nil_hash)
2268
- end
2424
+ checked_transaction do
2425
+ if save_old
2426
+ old.save(save_opts) || raise(Sequel::Error, "invalid previously associated object, cannot save") if old
2427
+ else
2428
+ up_ds.skip_limit_check.update(ck_nil_hash)
2429
+ end
2269
2430
 
2270
- o.save(save_opts) || raise(Sequel::Error, "invalid associated object, cannot save") if o
2431
+ o.save(save_opts) || raise(Sequel::Error, "invalid associated object, cannot save") if o
2432
+ end
2271
2433
  end
2272
2434
  end
2273
- opts[:_setter] = proc{|o| set_one_to_one_associated_object(opts, o)}
2435
+ if opts.fetch(:setter, true)
2436
+ opts[:_setter] = proc{|o| set_one_to_one_associated_object(opts, o)}
2437
+ end
2274
2438
  else
2275
2439
  save_opts[:raise_on_failure] = opts[:raise_on_save_failure] != false
2276
2440
 
2277
- opts[:adder] ||= proc do |o|
2278
- cks.zip(cpks).each{|k, pk| o.set_column_value(:"#{k}=", get_column_value(pk))}
2279
- o.save(save_opts)
2441
+ unless opts.has_key?(:adder)
2442
+ opts[:adder] = proc do |o|
2443
+ cks.zip(cpks).each{|k, pk| o.set_column_value(:"#{k}=", get_column_value(pk))}
2444
+ o.save(save_opts)
2445
+ end
2280
2446
  end
2281
2447
 
2282
- opts[:remover] ||= proc do |o|
2283
- cks.each{|k| o.set_column_value(:"#{k}=", nil)}
2284
- o.save(save_opts)
2448
+ unless opts.has_key?(:remover)
2449
+ opts[:remover] = proc do |o|
2450
+ cks.each{|k| o.set_column_value(:"#{k}=", nil)}
2451
+ o.save(save_opts)
2452
+ end
2285
2453
  end
2286
2454
 
2287
- opts[:clearer] ||= proc do
2288
- _apply_association_options(opts, opts.associated_dataset.where(cks.zip(cpks.map{|k| get_column_value(k)}))).update(ck_nil_hash)
2455
+ unless opts.has_key?(:clearer)
2456
+ opts[:clearer] = proc do
2457
+ _apply_association_options(opts, opts.associated_dataset.where(cks.zip(cpks.map{|k| get_column_value(k)}))).update(ck_nil_hash)
2458
+ end
2289
2459
  end
2290
2460
  end
2291
2461
  end
@@ -2303,6 +2473,9 @@ module Sequel
2303
2473
  # Return dataset to graph into given the association reflection, applying the :callback option if set.
2304
2474
  def eager_graph_dataset(opts, eager_options)
2305
2475
  ds = opts.associated_class.dataset
2476
+ if opts[:graph_use_association_block] && (b = opts[:block])
2477
+ ds = b.call(ds)
2478
+ end
2306
2479
  if cb = eager_options[:callback]
2307
2480
  ds = cb.call(ds)
2308
2481
  end
@@ -2379,7 +2552,7 @@ module Sequel
2379
2552
 
2380
2553
  # Dataset for the join table of the given many to many association reflection
2381
2554
  def _join_table_dataset(opts)
2382
- ds = model.db.from(opts.join_table_source)
2555
+ ds = (opts[:join_table_db] || model.db).from(opts.join_table_source)
2383
2556
  opts[:join_table_block] ? opts[:join_table_block].call(ds) : ds
2384
2557
  end
2385
2558
 
@@ -2400,7 +2573,12 @@ module Sequel
2400
2573
  if loader = _associated_object_loader(opts, dynamic_opts)
2401
2574
  loader.all(*opts.predicate_key_values(self))
2402
2575
  else
2403
- _associated_dataset(opts, dynamic_opts).all
2576
+ ds = _associated_dataset(opts, dynamic_opts)
2577
+ if ds.opts[:no_results]
2578
+ []
2579
+ else
2580
+ ds.all
2581
+ end
2404
2582
  end
2405
2583
  end
2406
2584
 
@@ -2861,6 +3039,8 @@ module Sequel
2861
3039
  (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)})))))
2862
3040
  l = args[0]
2863
3041
  if ar = model.association_reflections[l]
3042
+ raise Error, "filtering by associations is not allowed for #{ar.inspect}" if ar[:allow_filtering_by] == false
3043
+
2864
3044
  if multiple
2865
3045
  klass = ar.associated_class
2866
3046
  if is_ds
@@ -2982,7 +3162,7 @@ module Sequel
2982
3162
 
2983
3163
  # The secondary eager loading method. Loads all associations in a single query. This
2984
3164
  # method should only be used if you need to filter or order based on columns in associated tables,
2985
- # or if you have done comparative benchmarking it and determined it is faster.
3165
+ # or if you have done comparative benchmarking and determined it is faster.
2986
3166
  #
2987
3167
  # This method uses <tt>Dataset#graph</tt> to create appropriate aliases for columns in all the
2988
3168
  # tables. Then it uses the graph's metadata to build the associations from the single hash, and
@@ -3207,8 +3387,15 @@ module Sequel
3207
3387
  local_opts = ds.opts[:eager_graph][:local]
3208
3388
  limit_strategy = r.eager_graph_limit_strategy(local_opts[:limit_strategy])
3209
3389
 
3210
- if r[:conditions] && !Sequel.condition_specifier?(r[:conditions]) && !r[:orig_opts].has_key?(:graph_conditions) && !r[:orig_opts].has_key?(:graph_only_conditions) && !r.has_key?(:graph_block)
3211
- raise Error, "Cannot eager_graph association when :conditions specified and not a hash or an array of pairs. Specify :graph_conditions, :graph_only_conditions, or :graph_block for the association. Model: #{r[:model]}, association: #{r[:name]}"
3390
+ # SEQUEL6: remove and integrate the auto_restrict_eager_graph plugin
3391
+ if !r[:orig_opts].has_key?(:graph_conditions) && !r[:orig_opts].has_key?(:graph_only_conditions) && !r.has_key?(:graph_block) && !r[:allow_eager_graph]
3392
+ if r[:conditions] && !Sequel.condition_specifier?(r[:conditions])
3393
+ raise Error, "Cannot eager_graph association when :conditions specified and not a hash or an array of pairs. Specify :graph_conditions, :graph_only_conditions, or :graph_block for the association. Model: #{r[:model]}, association: #{r[:name]}"
3394
+ end
3395
+
3396
+ if r[:block] && !r[:graph_use_association_block]
3397
+ warn "eager_graph used for association when association given a block without graph options. The block is ignored in this case. This will result in an exception starting in Sequel 6. Model: #{r[:model]}, association: #{r[:name]}"
3398
+ end
3212
3399
  end
3213
3400
 
3214
3401
  ds = loader.call(:self=>ds, :table_alias=>assoc_table_alias, :implicit_qualifier=>(ta == ds.opts[:eager_graph][:master]) ? first_source : qualifier_from_alias_symbol(ta, first_source), :callback=>callback, :join_type=>join_type || local_opts[:join_type], :join_only=>local_opts[:join_only], :limit_strategy=>limit_strategy, :from_self_alias=>ds.opts[:eager_graph][:master])
@@ -3356,7 +3543,7 @@ module Sequel
3356
3543
  # Allow associations that are eagerly graphed to be specified as an SQL::AliasedExpression, for
3357
3544
  # per-call determining of the alias base.
3358
3545
  def eager_graph_check_association(model, association)
3359
- if association.is_a?(SQL::AliasedExpression)
3546
+ reflection = if association.is_a?(SQL::AliasedExpression)
3360
3547
  expr = association.expression
3361
3548
  if expr.is_a?(SQL::Identifier)
3362
3549
  expr = expr.value
@@ -3365,10 +3552,17 @@ module Sequel
3365
3552
  end
3366
3553
  end
3367
3554
 
3368
- SQL::AliasedExpression.new(check_association(model, expr), association.alias || expr, association.columns)
3555
+ check_reflection = check_association(model, expr)
3556
+ SQL::AliasedExpression.new(check_reflection, association.alias || expr, association.columns)
3369
3557
  else
3370
- check_association(model, association)
3558
+ check_reflection = check_association(model, association)
3559
+ end
3560
+
3561
+ if check_reflection && check_reflection[:allow_eager_graph] == false
3562
+ raise Error, "eager_graph not allowed for #{reflection.inspect}"
3371
3563
  end
3564
+
3565
+ reflection
3372
3566
  end
3373
3567
 
3374
3568
  # The EagerGraphLoader instance used for converting eager_graph results.
@@ -3380,11 +3574,11 @@ module Sequel
3380
3574
  end
3381
3575
 
3382
3576
  # Eagerly load all specified associations.
3383
- def eager_load(a, eager_assoc=@opts[:eager])
3577
+ def eager_load(a, eager_assoc=@opts[:eager], m=model)
3384
3578
  return if a.empty?
3385
3579
 
3386
3580
  # Reflections for all associations to eager load
3387
- reflections = eager_assoc.keys.map{|assoc| model.association_reflection(assoc) || (raise Sequel::UndefinedAssociation, "Model: #{self}, Association: #{assoc}")}
3581
+ reflections = eager_assoc.keys.map{|assoc| m.association_reflection(assoc) || (raise Sequel::UndefinedAssociation, "Model: #{self}, Association: #{assoc}")}
3388
3582
 
3389
3583
  perform_eager_loads(prepare_eager_load(a, reflections, eager_assoc))
3390
3584