sequel 5.83.0 → 5.106.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 (259) hide show
  1. checksums.yaml +4 -4
  2. data/MIT-LICENSE +1 -1
  3. data/lib/sequel/adapters/ado/access.rb +2 -2
  4. data/lib/sequel/adapters/ado.rb +2 -2
  5. data/lib/sequel/adapters/amalgalite.rb +16 -1
  6. data/lib/sequel/adapters/ibmdb.rb +2 -1
  7. data/lib/sequel/adapters/jdbc/db2.rb +2 -2
  8. data/lib/sequel/adapters/jdbc/derby.rb +4 -2
  9. data/lib/sequel/adapters/jdbc/h2.rb +4 -4
  10. data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -2
  11. data/lib/sequel/adapters/jdbc/jtds.rb +2 -2
  12. data/lib/sequel/adapters/jdbc/mysql.rb +3 -3
  13. data/lib/sequel/adapters/jdbc/oracle.rb +6 -6
  14. data/lib/sequel/adapters/jdbc/postgresql.rb +5 -5
  15. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +6 -6
  16. data/lib/sequel/adapters/jdbc/sqlite.rb +3 -3
  17. data/lib/sequel/adapters/jdbc/sqlserver.rb +3 -3
  18. data/lib/sequel/adapters/jdbc.rb +29 -15
  19. data/lib/sequel/adapters/mysql.rb +1 -1
  20. data/lib/sequel/adapters/mysql2.rb +10 -3
  21. data/lib/sequel/adapters/odbc.rb +1 -1
  22. data/lib/sequel/adapters/oracle.rb +16 -0
  23. data/lib/sequel/adapters/postgres.rb +2 -1
  24. data/lib/sequel/adapters/shared/access.rb +1 -0
  25. data/lib/sequel/adapters/shared/db2.rb +8 -3
  26. data/lib/sequel/adapters/shared/mssql.rb +9 -8
  27. data/lib/sequel/adapters/shared/mysql.rb +16 -11
  28. data/lib/sequel/adapters/shared/oracle.rb +1 -0
  29. data/lib/sequel/adapters/shared/postgres.rb +438 -47
  30. data/lib/sequel/adapters/shared/sqlite.rb +9 -7
  31. data/lib/sequel/adapters/sqlite.rb +20 -2
  32. data/lib/sequel/adapters/tinytds.rb +2 -2
  33. data/lib/sequel/adapters/trilogy.rb +2 -3
  34. data/lib/sequel/ast_transformer.rb +2 -0
  35. data/lib/sequel/connection_pool/sharded_timed_queue.rb +42 -11
  36. data/lib/sequel/connection_pool/timed_queue.rb +33 -10
  37. data/lib/sequel/connection_pool.rb +8 -2
  38. data/lib/sequel/core.rb +31 -2
  39. data/lib/sequel/database/connecting.rb +1 -1
  40. data/lib/sequel/database/dataset_defaults.rb +3 -3
  41. data/lib/sequel/database/misc.rb +29 -11
  42. data/lib/sequel/database/query.rb +12 -12
  43. data/lib/sequel/database/schema_generator.rb +117 -16
  44. data/lib/sequel/database/schema_methods.rb +85 -30
  45. data/lib/sequel/dataset/actions.rb +83 -11
  46. data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +1 -1
  47. data/lib/sequel/dataset/placeholder_literalizer.rb +3 -0
  48. data/lib/sequel/dataset/prepared_statements.rb +76 -28
  49. data/lib/sequel/dataset/query.rb +26 -11
  50. data/lib/sequel/dataset/sql.rb +40 -17
  51. data/lib/sequel/extensions/async_thread_pool.rb +1 -1
  52. data/lib/sequel/extensions/caller_logging.rb +1 -3
  53. data/lib/sequel/extensions/connection_checkout_event_callback.rb +151 -0
  54. data/lib/sequel/extensions/connection_expiration.rb +44 -5
  55. data/lib/sequel/extensions/connection_validator.rb +15 -10
  56. data/lib/sequel/extensions/constraint_validations.rb +3 -3
  57. data/lib/sequel/extensions/dataset_run.rb +41 -0
  58. data/lib/sequel/extensions/date_arithmetic.rb +6 -6
  59. data/lib/sequel/extensions/empty_array_consider_nulls.rb +7 -0
  60. data/lib/sequel/extensions/eval_inspect.rb +3 -1
  61. data/lib/sequel/extensions/inflector.rb +2 -2
  62. data/lib/sequel/extensions/lit_require_frozen.rb +131 -0
  63. data/lib/sequel/extensions/looser_typecasting.rb +5 -12
  64. data/lib/sequel/extensions/migration.rb +38 -21
  65. data/lib/sequel/extensions/null_dataset.rb +2 -2
  66. data/lib/sequel/extensions/pg_array_ops.rb +41 -0
  67. data/lib/sequel/extensions/pg_auto_parameterize.rb +10 -3
  68. data/lib/sequel/extensions/pg_auto_parameterize_duplicate_query_detection.rb +191 -0
  69. data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +94 -10
  70. data/lib/sequel/extensions/pg_enum.rb +4 -4
  71. data/lib/sequel/extensions/pg_hstore.rb +1 -1
  72. data/lib/sequel/extensions/pg_hstore_ops.rb +10 -5
  73. data/lib/sequel/extensions/pg_json_ops.rb +674 -18
  74. data/lib/sequel/extensions/pg_multirange.rb +1 -1
  75. data/lib/sequel/extensions/pg_range.rb +14 -2
  76. data/lib/sequel/extensions/pg_row.rb +5 -3
  77. data/lib/sequel/extensions/pg_schema_caching.rb +90 -0
  78. data/lib/sequel/extensions/provenance.rb +1 -3
  79. data/lib/sequel/extensions/query_blocker.rb +172 -0
  80. data/lib/sequel/extensions/schema_caching.rb +24 -9
  81. data/lib/sequel/extensions/schema_dumper.rb +17 -5
  82. data/lib/sequel/extensions/set_literalizer.rb +20 -39
  83. data/lib/sequel/extensions/split_array_nil.rb +12 -2
  84. data/lib/sequel/extensions/sqlite_json_ops.rb +1 -1
  85. data/lib/sequel/extensions/string_agg.rb +2 -2
  86. data/lib/sequel/extensions/to_dot.rb +5 -0
  87. data/lib/sequel/extensions/virtual_row_method_block.rb +1 -0
  88. data/lib/sequel/model/associations.rb +215 -15
  89. data/lib/sequel/model/base.rb +198 -35
  90. data/lib/sequel/model/plugins.rb +12 -1
  91. data/lib/sequel/model.rb +1 -0
  92. data/lib/sequel/plugins/accessed_columns.rb +4 -0
  93. data/lib/sequel/plugins/active_model.rb +5 -6
  94. data/lib/sequel/plugins/association_pks.rb +2 -0
  95. data/lib/sequel/plugins/auto_validations.rb +2 -0
  96. data/lib/sequel/plugins/class_table_inheritance_constraint_validations.rb +82 -0
  97. data/lib/sequel/plugins/columns_updated.rb +4 -0
  98. data/lib/sequel/plugins/composition.rb +3 -1
  99. data/lib/sequel/plugins/constraint_validations.rb +15 -10
  100. data/lib/sequel/plugins/dataset_associations.rb +20 -1
  101. data/lib/sequel/plugins/deprecated_associations.rb +151 -0
  102. data/lib/sequel/plugins/detect_unnecessary_association_options.rb +164 -0
  103. data/lib/sequel/plugins/dirty.rb +9 -2
  104. data/lib/sequel/plugins/enum.rb +1 -1
  105. data/lib/sequel/plugins/forbid_lazy_load.rb +15 -1
  106. data/lib/sequel/plugins/insert_conflict.rb +4 -0
  107. data/lib/sequel/plugins/insert_returning_select.rb +10 -1
  108. data/lib/sequel/plugins/inspect_pk.rb +44 -0
  109. data/lib/sequel/plugins/instance_filters.rb +8 -1
  110. data/lib/sequel/plugins/instance_hooks.rb +4 -0
  111. data/lib/sequel/plugins/inverted_subsets.rb +1 -0
  112. data/lib/sequel/plugins/json_serializer.rb +6 -10
  113. data/lib/sequel/plugins/lazy_attributes.rb +1 -1
  114. data/lib/sequel/plugins/many_through_many.rb +21 -0
  115. data/lib/sequel/plugins/modification_detection.rb +2 -0
  116. data/lib/sequel/plugins/mssql_optimistic_locking.rb +1 -1
  117. data/lib/sequel/plugins/nested_attributes.rb +10 -5
  118. data/lib/sequel/plugins/paged_operations.rb +5 -2
  119. data/lib/sequel/plugins/pg_array_associations.rb +2 -2
  120. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +6 -1
  121. data/lib/sequel/plugins/pg_auto_validate_enums.rb +88 -0
  122. data/lib/sequel/plugins/pg_eager_any_typed_array.rb +95 -0
  123. data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +1 -1
  124. data/lib/sequel/plugins/rcte_tree.rb +6 -6
  125. data/lib/sequel/plugins/serialization.rb +23 -6
  126. data/lib/sequel/plugins/serialization_modification_detection.rb +4 -0
  127. data/lib/sequel/plugins/single_statement_dataset_destroy.rb +49 -0
  128. data/lib/sequel/plugins/split_values.rb +12 -0
  129. data/lib/sequel/plugins/sql_comments.rb +7 -2
  130. data/lib/sequel/plugins/static_cache.rb +13 -0
  131. data/lib/sequel/plugins/static_cache_cache.rb +50 -13
  132. data/lib/sequel/plugins/subset_conditions.rb +85 -5
  133. data/lib/sequel/plugins/subset_static_cache.rb +278 -0
  134. data/lib/sequel/plugins/table_select.rb +7 -0
  135. data/lib/sequel/plugins/tactical_eager_loading.rb +4 -0
  136. data/lib/sequel/plugins/unused_associations.rb +11 -3
  137. data/lib/sequel/plugins/update_primary_key.rb +4 -0
  138. data/lib/sequel/plugins/validation_class_methods.rb +1 -1
  139. data/lib/sequel/plugins/validation_contexts.rb +4 -0
  140. data/lib/sequel/sql.rb +27 -12
  141. data/lib/sequel/version.rb +1 -1
  142. metadata +17 -240
  143. data/CHANGELOG +0 -1393
  144. data/README.rdoc +0 -936
  145. data/doc/advanced_associations.rdoc +0 -884
  146. data/doc/association_basics.rdoc +0 -1859
  147. data/doc/bin_sequel.rdoc +0 -146
  148. data/doc/cheat_sheet.rdoc +0 -255
  149. data/doc/code_order.rdoc +0 -104
  150. data/doc/core_extensions.rdoc +0 -405
  151. data/doc/dataset_basics.rdoc +0 -96
  152. data/doc/dataset_filtering.rdoc +0 -222
  153. data/doc/extensions.rdoc +0 -77
  154. data/doc/fork_safety.rdoc +0 -84
  155. data/doc/mass_assignment.rdoc +0 -98
  156. data/doc/migration.rdoc +0 -660
  157. data/doc/model_dataset_method_design.rdoc +0 -129
  158. data/doc/model_hooks.rdoc +0 -254
  159. data/doc/model_plugins.rdoc +0 -270
  160. data/doc/mssql_stored_procedures.rdoc +0 -43
  161. data/doc/object_model.rdoc +0 -563
  162. data/doc/opening_databases.rdoc +0 -439
  163. data/doc/postgresql.rdoc +0 -611
  164. data/doc/prepared_statements.rdoc +0 -144
  165. data/doc/querying.rdoc +0 -1070
  166. data/doc/reflection.rdoc +0 -120
  167. data/doc/release_notes/5.0.0.txt +0 -159
  168. data/doc/release_notes/5.1.0.txt +0 -31
  169. data/doc/release_notes/5.10.0.txt +0 -84
  170. data/doc/release_notes/5.11.0.txt +0 -83
  171. data/doc/release_notes/5.12.0.txt +0 -141
  172. data/doc/release_notes/5.13.0.txt +0 -27
  173. data/doc/release_notes/5.14.0.txt +0 -63
  174. data/doc/release_notes/5.15.0.txt +0 -39
  175. data/doc/release_notes/5.16.0.txt +0 -110
  176. data/doc/release_notes/5.17.0.txt +0 -31
  177. data/doc/release_notes/5.18.0.txt +0 -69
  178. data/doc/release_notes/5.19.0.txt +0 -28
  179. data/doc/release_notes/5.2.0.txt +0 -33
  180. data/doc/release_notes/5.20.0.txt +0 -89
  181. data/doc/release_notes/5.21.0.txt +0 -87
  182. data/doc/release_notes/5.22.0.txt +0 -48
  183. data/doc/release_notes/5.23.0.txt +0 -56
  184. data/doc/release_notes/5.24.0.txt +0 -56
  185. data/doc/release_notes/5.25.0.txt +0 -32
  186. data/doc/release_notes/5.26.0.txt +0 -35
  187. data/doc/release_notes/5.27.0.txt +0 -21
  188. data/doc/release_notes/5.28.0.txt +0 -16
  189. data/doc/release_notes/5.29.0.txt +0 -22
  190. data/doc/release_notes/5.3.0.txt +0 -121
  191. data/doc/release_notes/5.30.0.txt +0 -20
  192. data/doc/release_notes/5.31.0.txt +0 -148
  193. data/doc/release_notes/5.32.0.txt +0 -46
  194. data/doc/release_notes/5.33.0.txt +0 -24
  195. data/doc/release_notes/5.34.0.txt +0 -40
  196. data/doc/release_notes/5.35.0.txt +0 -56
  197. data/doc/release_notes/5.36.0.txt +0 -60
  198. data/doc/release_notes/5.37.0.txt +0 -30
  199. data/doc/release_notes/5.38.0.txt +0 -28
  200. data/doc/release_notes/5.39.0.txt +0 -19
  201. data/doc/release_notes/5.4.0.txt +0 -80
  202. data/doc/release_notes/5.40.0.txt +0 -40
  203. data/doc/release_notes/5.41.0.txt +0 -25
  204. data/doc/release_notes/5.42.0.txt +0 -136
  205. data/doc/release_notes/5.43.0.txt +0 -98
  206. data/doc/release_notes/5.44.0.txt +0 -32
  207. data/doc/release_notes/5.45.0.txt +0 -34
  208. data/doc/release_notes/5.46.0.txt +0 -87
  209. data/doc/release_notes/5.47.0.txt +0 -59
  210. data/doc/release_notes/5.48.0.txt +0 -14
  211. data/doc/release_notes/5.49.0.txt +0 -59
  212. data/doc/release_notes/5.5.0.txt +0 -61
  213. data/doc/release_notes/5.50.0.txt +0 -78
  214. data/doc/release_notes/5.51.0.txt +0 -47
  215. data/doc/release_notes/5.52.0.txt +0 -87
  216. data/doc/release_notes/5.53.0.txt +0 -23
  217. data/doc/release_notes/5.54.0.txt +0 -27
  218. data/doc/release_notes/5.55.0.txt +0 -21
  219. data/doc/release_notes/5.56.0.txt +0 -51
  220. data/doc/release_notes/5.57.0.txt +0 -23
  221. data/doc/release_notes/5.58.0.txt +0 -31
  222. data/doc/release_notes/5.59.0.txt +0 -73
  223. data/doc/release_notes/5.6.0.txt +0 -31
  224. data/doc/release_notes/5.60.0.txt +0 -22
  225. data/doc/release_notes/5.61.0.txt +0 -43
  226. data/doc/release_notes/5.62.0.txt +0 -132
  227. data/doc/release_notes/5.63.0.txt +0 -33
  228. data/doc/release_notes/5.64.0.txt +0 -50
  229. data/doc/release_notes/5.65.0.txt +0 -21
  230. data/doc/release_notes/5.66.0.txt +0 -24
  231. data/doc/release_notes/5.67.0.txt +0 -32
  232. data/doc/release_notes/5.68.0.txt +0 -61
  233. data/doc/release_notes/5.69.0.txt +0 -26
  234. data/doc/release_notes/5.7.0.txt +0 -108
  235. data/doc/release_notes/5.70.0.txt +0 -35
  236. data/doc/release_notes/5.71.0.txt +0 -21
  237. data/doc/release_notes/5.72.0.txt +0 -33
  238. data/doc/release_notes/5.73.0.txt +0 -66
  239. data/doc/release_notes/5.74.0.txt +0 -45
  240. data/doc/release_notes/5.75.0.txt +0 -35
  241. data/doc/release_notes/5.76.0.txt +0 -86
  242. data/doc/release_notes/5.77.0.txt +0 -63
  243. data/doc/release_notes/5.78.0.txt +0 -67
  244. data/doc/release_notes/5.79.0.txt +0 -28
  245. data/doc/release_notes/5.8.0.txt +0 -170
  246. data/doc/release_notes/5.80.0.txt +0 -40
  247. data/doc/release_notes/5.81.0.txt +0 -31
  248. data/doc/release_notes/5.82.0.txt +0 -61
  249. data/doc/release_notes/5.83.0.txt +0 -56
  250. data/doc/release_notes/5.9.0.txt +0 -99
  251. data/doc/schema_modification.rdoc +0 -679
  252. data/doc/security.rdoc +0 -443
  253. data/doc/sharding.rdoc +0 -286
  254. data/doc/sql.rdoc +0 -648
  255. data/doc/testing.rdoc +0 -204
  256. data/doc/thread_safety.rdoc +0 -15
  257. data/doc/transactions.rdoc +0 -250
  258. data/doc/validations.rdoc +0 -558
  259. data/doc/virtual_rows.rdoc +0 -265
@@ -112,6 +112,8 @@ module Sequel
112
112
  # the dataset unmodified if no SQL limit strategy is needed.
113
113
  def apply_eager_graph_limit_strategy(strategy, ds)
114
114
  case strategy
115
+ when :lateral_subquery
116
+ apply_lateral_subquery_eager_graph_limit_strategy(ds)
115
117
  when :distinct_on
116
118
  apply_distinct_on_eager_limit_strategy(ds.order_prepend(*self[:order]))
117
119
  when :window_function
@@ -298,11 +300,16 @@ module Sequel
298
300
  # Correlated subqueries are not supported for regular eager loading
299
301
  strategy = :ruby if strategy == :correlated_subquery
300
302
  strategy = nil if strategy == :ruby && assign_singular?
301
- objects = apply_eager_limit_strategy(ds, strategy, eager_limit).all
303
+
304
+ objects = if strategy == :lateral_subquery
305
+ apply_lateral_subquery_eager_limit_strategy(ds, ids, eager_limit).all
306
+ else
307
+ apply_eager_limit_strategy(ds, strategy, eager_limit).all
308
+ end
302
309
 
303
310
  if strategy == :window_function
304
311
  delete_rn = ds.row_number_column
305
- objects.each{|obj| obj.values.delete(delete_rn)}
312
+ objects.each{|obj| obj.remove_key!(delete_rn)}
306
313
  end
307
314
  elsif strategy == :union
308
315
  objects = []
@@ -320,7 +327,7 @@ module Sequel
320
327
  end
321
328
  loader.append_sql(sql, *k)
322
329
  end
323
- objects.concat(ds.with_sql(sql).to_a)
330
+ objects.concat(ds.with_sql(sql.freeze).to_a)
324
331
  end
325
332
  ds = ds.eager(cascade) if cascade
326
333
  ds.send(:post_load, objects)
@@ -366,7 +373,12 @@ module Sequel
366
373
  # filtered. Works by using a subquery to test that the objects passed
367
374
  # also meet the association filter criteria.
368
375
  def filter_by_associations_conditions_expression(obj)
369
- ds = filter_by_associations_conditions_dataset.where(filter_by_associations_conditions_subquery_conditions(obj))
376
+ ds = if filter_by_associations_limit_strategy == :lateral_subquery
377
+ apply_lateral_subquery_filter_limit_strategy(associated_eager_dataset, obj)
378
+ else
379
+ filter_by_associations_conditions_dataset.where(filter_by_associations_conditions_subquery_conditions(obj))
380
+ end
381
+
370
382
  {filter_by_associations_conditions_key=>ds}
371
383
  end
372
384
 
@@ -414,6 +426,12 @@ module Sequel
414
426
  false
415
427
  end
416
428
 
429
+ # Hash value for the association reflection. This is precomputed to avoid
430
+ # concurrency issues at runtime.
431
+ def hash
432
+ self[:_hash]
433
+ end
434
+
417
435
  # Initialize the associations cache for the current association for the given objects.
418
436
  def initialize_association_cache(objects)
419
437
  name = self[:name]
@@ -693,6 +711,9 @@ module Sequel
693
711
 
694
712
  # The predicate condition to use for the eager_loader.
695
713
  def eager_loading_predicate_condition(keys)
714
+ if transform = self[:eager_loading_predicate_transform]
715
+ keys = transform.call(keys, self)
716
+ end
696
717
  {predicate_key=>keys}
697
718
  end
698
719
 
@@ -745,6 +766,61 @@ module Sequel
745
766
  end
746
767
  end
747
768
 
769
+ def lateral_subquery_eager_limit_strategy_lateral_dataset(ds, limit_and_offset)
770
+ ds.
771
+ where(Array(filter_by_associations_conditions_key).zip(Array(filter_by_associations_conditions_associated_keys))).
772
+ limit(*limit_and_offset).
773
+ lateral
774
+ end
775
+
776
+ def apply_lateral_subquery_eager_limit_strategy(ds, ids, limit_and_offset)
777
+ table_name = self[:model].table_name
778
+ associated_table_name = associated_class.table_name
779
+
780
+ associated_class.
781
+ from(table_name).
782
+ select_all(associated_table_name).
783
+ join(lateral_subquery_eager_limit_strategy_lateral_dataset(ds, limit_and_offset).as(associated_table_name), true).
784
+ order(*self[:order])
785
+ end
786
+
787
+ # Return an expression to filter the filter by associations dataset to only
788
+ # rows related to given objects.
789
+ def _lateral_subquery_filter_limit_strategy_conditions(obj, key, value_method, value_column)
790
+ value = case obj
791
+ when Array
792
+ if key.is_a?(Array)
793
+ key_methods = Array(value_method)
794
+ obj.map{|o| key_methods.map{|meth| o.send(meth)}}
795
+ else
796
+ obj.map{|o| o.send(value_method)}
797
+ end
798
+ when Sequel::Dataset
799
+ obj.select(*Array(qualify(associated_class.table_name, value_column)))
800
+ else
801
+ if key.is_a?(Array)
802
+ return Array(key).zip(Array(value_method).map{|meth| obj.send(meth)})
803
+ else
804
+ obj.send(value_method)
805
+ end
806
+ end
807
+
808
+ {key => value}
809
+ end
810
+
811
+ def lateral_subquery_filter_limit_strategy_lateral_dataset(ds, obj)
812
+ lateral_subquery_filter_limit_strategy_filter_lateral_dataset(ds.
813
+ select(*qualify(associated_class.table_name, associated_class.primary_key)).
814
+ limit(*limit_and_offset).
815
+ lateral)
816
+ end
817
+
818
+ def apply_lateral_subquery_filter_limit_strategy(ds, obj)
819
+ lateral_subquery_filter_limit_strategy_filter_dataset(self[:model].
820
+ select(*lateral_subquery_filter_limit_strategy_lateral_dataset_select).
821
+ join(lateral_subquery_filter_limit_strategy_lateral_dataset(ds, obj).as(associated_class.table_name), filter_by_associations_conditions_subquery_conditions(obj)), obj)
822
+ end
823
+
748
824
  # Whether to limit the associated dataset to a single row.
749
825
  def limit_to_single_row?
750
826
  !returns_array?
@@ -759,7 +835,20 @@ module Sequel
759
835
  def placeholder_eager_loader
760
836
  cached_fetch(:placeholder_eager_loader) do
761
837
  eager_loading_dataset.placeholder_literalizer_loader do |pl, ds|
762
- apply_eager_limit_strategy(ds.where(predicate_key=>pl.arg), eager_limit_strategy)
838
+ arg = pl.arg
839
+
840
+ if transform = self[:eager_loading_predicate_transform]
841
+ arg = arg.transform do |v|
842
+ transform.call(v, self)
843
+ end
844
+ end
845
+
846
+ strategy = eager_limit_strategy
847
+ if strategy == :lateral_subquery
848
+ apply_lateral_subquery_eager_limit_strategy(ds, arg, limit_and_offset)
849
+ else
850
+ apply_eager_limit_strategy(ds.where(predicate_key=>arg), strategy)
851
+ end
763
852
  end
764
853
  end
765
854
  end
@@ -1001,7 +1090,7 @@ module Sequel
1001
1090
  class OneToManyAssociationReflection < AssociationReflection
1002
1091
  ASSOCIATION_TYPES[:one_to_many] = self
1003
1092
 
1004
- # Support a correlated subquery limit strategy when using eager_graph.
1093
+ # Support a lateral_subquery and correlated_subquery limit strategy when using eager_graph.
1005
1094
  def apply_eager_graph_limit_strategy(strategy, ds)
1006
1095
  case strategy
1007
1096
  when :correlated_subquery
@@ -1101,6 +1190,54 @@ module Sequel
1101
1190
  ds.where(qualify(table_alias, primary_key)=>cs)
1102
1191
  end
1103
1192
 
1193
+ # Use a LATERAL subquery to limit the dataset. Note that this will not
1194
+ # work correctly if the associated dataset uses qualified identifers in the WHERE clause,
1195
+ # as they would reference the containing query instead of the subquery.
1196
+ #
1197
+ # This does not contain the conditions that are necessary to join to the
1198
+ # query, since the necessary qualifier is not passed as an argument.
1199
+ def apply_lateral_subquery_eager_graph_limit_strategy(ds)
1200
+ table_name = ds.first_source_alias
1201
+ qualifier = ds.opts[:eager_options][:implicit_qualifier]
1202
+ graph_conditions = self[:_graph_conditions]
1203
+
1204
+ unless Sequel.condition_specifier?(graph_conditions)
1205
+ raise Error, "lateral_subquery eager graph limit strategy only supported when graph conditions are a hash or array of pairs"
1206
+ end
1207
+
1208
+ ds.
1209
+ limit(*limit_and_offset).
1210
+ order(*self[:order]).
1211
+ where(graph_conditions.map{|k, v| [qualify(table_name, k), qualify(qualifier, v)]}).
1212
+ lateral
1213
+ end
1214
+
1215
+ # Avoid setting duplicate predicate condition when using the lateral subquery
1216
+ # eager limit strategy.
1217
+ def eager_loading_set_predicate_condition(ds, eo)
1218
+ eager_limit_strategy == :lateral_subquery ? ds : super
1219
+ end
1220
+
1221
+ def apply_lateral_subquery_eager_limit_strategy(ds, ids, limit_and_offset)
1222
+ super.where(qualify(self[:model].table_name, self[:primary_key]) => ids)
1223
+ end
1224
+
1225
+ def lateral_subquery_filter_limit_strategy_conditions(obj)
1226
+ _lateral_subquery_filter_limit_strategy_conditions(obj, filter_by_associations_conditions_key, self[:key_method], self[:key])
1227
+ end
1228
+
1229
+ def lateral_subquery_filter_limit_strategy_filter_lateral_dataset(ds)
1230
+ ds.where(Array(filter_by_associations_conditions_key).zip(Array(filter_by_associations_conditions_associated_keys)))
1231
+ end
1232
+
1233
+ def lateral_subquery_filter_limit_strategy_filter_dataset(ds, obj)
1234
+ ds.where(lateral_subquery_filter_limit_strategy_conditions(obj))
1235
+ end
1236
+
1237
+ def lateral_subquery_filter_limit_strategy_lateral_dataset_select
1238
+ qualified_primary_key
1239
+ end
1240
+
1104
1241
  # Support correlated subquery strategy when filtering by limited associations.
1105
1242
  def apply_filter_by_associations_limit_strategy(ds)
1106
1243
  case filter_by_associations_limit_strategy
@@ -1274,11 +1411,11 @@ module Sequel
1274
1411
  name = self[:name]
1275
1412
 
1276
1413
  self[:model].eager_load_results(self, eo) do |assoc_record|
1277
- assoc_record.values.delete(delete_rn) if delete_rn
1414
+ assoc_record.remove_key!(delete_rn) if delete_rn
1278
1415
  hash_key = if uses_lcks
1279
- left_key_alias.map{|k| assoc_record.values.delete(k)}
1416
+ left_key_alias.map{|k| assoc_record.remove_key!(k)}
1280
1417
  else
1281
- assoc_record.values.delete(left_key_alias)
1418
+ assoc_record.remove_key!(left_key_alias)
1282
1419
  end
1283
1420
 
1284
1421
  objects = h[hash_key]
@@ -1415,6 +1552,52 @@ module Sequel
1415
1552
  end
1416
1553
  end
1417
1554
 
1555
+ def lateral_subquery_filter_limit_strategy_conditions(obj)
1556
+ _lateral_subquery_filter_limit_strategy_conditions(obj, lateral_subquery_filter_limit_strategy_conditions_key, right_primary_key_method, right_primary_key)
1557
+ end
1558
+
1559
+ def lateral_subquery_filter_limit_strategy_conditions_key
1560
+ self[:right_key]
1561
+ end
1562
+
1563
+ def lateral_subquery_filter_limit_strategy_filter_lateral_dataset(ds)
1564
+ ds.where(Array(filter_by_associations_conditions_key).zip(Array(filter_by_associations_conditions_associated_keys)))
1565
+ end
1566
+
1567
+ def lateral_subquery_filter_limit_strategy_filter_dataset(ds, obj)
1568
+ ds.where(filter_by_associations_conditions_key => ds.db.from(self[:join_table]).
1569
+ select(*self[:left_key]).
1570
+ where(lateral_subquery_filter_limit_strategy_conditions(obj)))
1571
+ end
1572
+
1573
+ def lateral_subquery_filter_limit_strategy_lateral_dataset_select
1574
+ qualify(self[:model].table_name, self[:left_primary_key])
1575
+ end
1576
+
1577
+ def lateral_subquery_eager_limit_strategy_lateral_dataset(ds, limit_and_offset)
1578
+ ds = super
1579
+ select = ds.opts[:select].dup
1580
+ left_key_alias = self[:left_key_alias]
1581
+ select.pop while (s = select.last).is_a?(Sequel::SQL::AliasedExpression) && (left_key_alias.is_a?(Array) ? left_key_alias.include?(s.alias) : s.alias == left_key_alias)
1582
+ ds = ds.clone(:select=>select)
1583
+ end
1584
+
1585
+ def apply_lateral_subquery_eager_limit_strategy(ds, ids, limit_and_offset)
1586
+ table_name = self[:model].table_name
1587
+ super.
1588
+ select_all(associated_class.table_name).
1589
+ select_append(*qualify(table_name, self[:left_primary_keys]).zip(Array(self[:left_key_alias])).map{|id, aliaz| Sequel.as(id, aliaz)}).
1590
+ where(qualify(table_name, self[:left_primary_key]) => ids)
1591
+ end
1592
+
1593
+ def apply_lateral_subquery_eager_graph_limit_strategy(ds)
1594
+ ds.
1595
+ limit(*limit_and_offset).
1596
+ where(qualify(join_table_alias, self[:left_keys]).zip(qualify(ds.opts[:eager_options][:implicit_qualifier], self[:left_primary_key_columns]))).
1597
+ order(*self[:order]).
1598
+ lateral
1599
+ end
1600
+
1418
1601
  # Use the right_keys from the eager loading options if
1419
1602
  # using a separate query per table.
1420
1603
  def eager_loading_set_predicate_condition(ds, eo)
@@ -1707,6 +1890,9 @@ module Sequel
1707
1890
  # record should be populated.
1708
1891
  # :eager_loader_key :: A symbol for the key column to use to populate the key_hash
1709
1892
  # for the eager loader. Can be set to nil to not populate the key_hash.
1893
+ # :eager_loading_predicate_transform :: A callable object with which to transform the predicate key values used
1894
+ # when eager loading. Called with two arguments, the array of predicate key
1895
+ # values, and a the reflection for the association being eager loaded.
1710
1896
  # :extend :: A module or array of modules to extend the dataset with.
1711
1897
  # :filter_limit_strategy :: Determines the strategy used for enforcing limits and offsets when filtering by
1712
1898
  # limited associations. Possible options are :window_function, :distinct_on, or
@@ -1769,6 +1955,9 @@ module Sequel
1769
1955
  # Set to nil to not define a setter method for the association.
1770
1956
  # :subqueries_per_union :: The number of subqueries to use in each UNION query, for eager
1771
1957
  # loading limited associations using the default :union strategy.
1958
+ # :use_placeholder_loader :: Whether to use a placeholder loader when eager loading the
1959
+ # association. Can be set to false to disable the use of a placeholder
1960
+ # loader if one would be used by default.
1772
1961
  # :validate :: Set to false to not validate when implicitly saving any associated object.
1773
1962
  # === :many_to_one
1774
1963
  # :key :: foreign key in current model's table that references
@@ -1891,7 +2080,7 @@ module Sequel
1891
2080
  raise(Error, "cannot clone an association to an association of different type (association #{name} with type #{type} cloning #{opts[:clone]} with type #{cloned_assoc[:type]})")
1892
2081
  end
1893
2082
 
1894
- opts[:use_placeholder_loader] = !opts[:instance_specific] && !opts[:eager_graph]
2083
+ opts[:use_placeholder_loader] = !opts[:instance_specific] && !opts[:eager_graph] unless opts.include?(:use_placeholder_loader)
1895
2084
  opts[:eager_block] = opts[:block] unless opts.include?(:eager_block)
1896
2085
  opts[:graph_join_type] ||= :left_outer
1897
2086
  opts[:order_eager_graph] = true unless opts.include?(:order_eager_graph)
@@ -1914,6 +2103,8 @@ module Sequel
1914
2103
  # Remove :class entry if it exists and is nil, to work with cached_fetch
1915
2104
  opts.delete(:class) unless opts[:class]
1916
2105
 
2106
+ opts[:_hash] = [self, name].hash
2107
+
1917
2108
  def_association(opts)
1918
2109
 
1919
2110
  orig_opts.delete(:clone)
@@ -1983,6 +2174,7 @@ module Sequel
1983
2174
  associate(:one_to_one, name, opts, &block)
1984
2175
  end
1985
2176
 
2177
+ Plugins.model_instance_variables(self, :@associations, :@set_associated_object_if_same)
1986
2178
  Plugins.inherited_instance_variables(self, :@association_reflections=>:dup, :@autoreloading_associations=>:hash_dup, :@default_association_options=>:dup, :@default_association_type_options=>:hash_dup, :@cache_associations=>nil, :@default_eager_limit_strategy=>nil)
1987
2179
  Plugins.def_dataset_methods(self, [:eager, :eager_graph, :eager_graph_with_options, :association_join, :association_full_join, :association_inner_join, :association_left_join, :association_right_join])
1988
2180
 
@@ -2362,7 +2554,7 @@ module Sequel
2362
2554
  delete_rn = opts.delete_row_number_column
2363
2555
 
2364
2556
  eager_load_results(opts, eo) do |assoc_record|
2365
- assoc_record.values.delete(delete_rn) if delete_rn
2557
+ assoc_record.remove_key!(delete_rn) if delete_rn
2366
2558
  hash_key = uses_cks ? km.map{|k| assoc_record.get_column_value(k)} : assoc_record.get_column_value(km)
2367
2559
  objects = h[hash_key]
2368
2560
  if assign_singular
@@ -2388,9 +2580,14 @@ module Sequel
2388
2580
  conditions = opts[:graph_conditions]
2389
2581
  opts[:cartesian_product_number] ||= one_to_one ? 0 : 1
2390
2582
  graph_block = opts[:graph_block]
2583
+ graph_conditions = opts[:_graph_conditions] = use_only_conditions ? only_conditions : cks.zip(pkcs) + conditions
2391
2584
  opts[:eager_grapher] ||= proc do |eo|
2392
2585
  ds = eo[:self]
2393
- ds = ds.graph(opts.apply_eager_graph_limit_strategy(eo[:limit_strategy], eager_graph_dataset(opts, eo)), use_only_conditions ? only_conditions : cks.zip(pkcs) + conditions, eo.merge(:select=>select, :join_type=>eo[:join_type]||join_type, :qualify=>:deep), &graph_block)
2586
+ graph_limit_strategy = eo[:limit_strategy]
2587
+ egds = opts.apply_eager_graph_limit_strategy(graph_limit_strategy, eager_graph_dataset(opts, eo))
2588
+ graph_conditions_true = true if graph_limit_strategy == :lateral_subquery
2589
+
2590
+ ds = ds.graph(egds, graph_conditions_true || graph_conditions, eo.merge(:select=>select, :join_type=>eo[:join_type]||join_type, :qualify=>:deep), &graph_block)
2394
2591
  # We only load reciprocals for one_to_many associations, as other reciprocals don't make sense
2395
2592
  ds.opts[:eager_graph][:reciprocals][eo[:table_alias]] = opts.reciprocal
2396
2593
  ds
@@ -2473,6 +2670,9 @@ module Sequel
2473
2670
  # Return dataset to graph into given the association reflection, applying the :callback option if set.
2474
2671
  def eager_graph_dataset(opts, eager_options)
2475
2672
  ds = opts.associated_class.dataset
2673
+ if eager_options[:limit_strategy] == :lateral_subquery
2674
+ ds = ds.clone(:eager_options=>eager_options)
2675
+ end
2476
2676
  if opts[:graph_use_association_block] && (b = opts[:block])
2477
2677
  ds = b.call(ds)
2478
2678
  end
@@ -3038,7 +3238,7 @@ module Sequel
3038
3238
  if (((op == :'=' || op == :'!=') && r.is_a?(Sequel::Model)) ||
3039
3239
  (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)})))))
3040
3240
  l = args[0]
3041
- if ar = model.association_reflections[l]
3241
+ if ar = model.association_reflection(l)
3042
3242
  raise Error, "filtering by associations is not allowed for #{ar.inspect}" if ar[:allow_filtering_by] == false
3043
3243
 
3044
3244
  if multiple
@@ -3519,7 +3719,7 @@ module Sequel
3519
3719
  else
3520
3720
  vals = Array(obj).reject{|o| !meths.all?{|m| o.get_column_value(m)}}
3521
3721
  return SQL::Constants::FALSE if vals.empty?
3522
- if obj.is_a?(Array)
3722
+ if obj.is_a?(Array) || obj.is_a?(Set)
3523
3723
  if keys.length == 1
3524
3724
  meth = meths.first
3525
3725
  {keys.first=>vals.map{|o| o.get_column_value(meth)}}
@@ -3591,7 +3791,7 @@ module Sequel
3591
3791
 
3592
3792
  # Prepare a hash loaders and eager options which will be used to implement the eager loading.
3593
3793
  def prepare_eager_load(a, reflections, eager_assoc)
3594
- eager_load_data = {}
3794
+ eager_load_data = {}.compare_by_identity
3595
3795
 
3596
3796
  # Key is foreign/primary key name symbol.
3597
3797
  # Value is hash with keys being foreign/primary key values (generally integers)