sequel 5.45.0 → 5.77.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.
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