sequel 5.20.0 → 5.49.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 (511) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +398 -1922
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +7 -7
  5. data/doc/advanced_associations.rdoc +4 -4
  6. data/doc/association_basics.rdoc +80 -16
  7. data/doc/cheat_sheet.rdoc +6 -5
  8. data/doc/code_order.rdoc +10 -12
  9. data/doc/dataset_filtering.rdoc +17 -2
  10. data/doc/fork_safety.rdoc +84 -0
  11. data/doc/migration.rdoc +11 -5
  12. data/doc/model_dataset_method_design.rdoc +1 -1
  13. data/doc/model_plugins.rdoc +1 -1
  14. data/doc/opening_databases.rdoc +10 -2
  15. data/doc/postgresql.rdoc +82 -3
  16. data/doc/querying.rdoc +4 -4
  17. data/doc/release_notes/5.21.0.txt +87 -0
  18. data/doc/release_notes/5.22.0.txt +48 -0
  19. data/doc/release_notes/5.23.0.txt +56 -0
  20. data/doc/release_notes/5.24.0.txt +56 -0
  21. data/doc/release_notes/5.25.0.txt +32 -0
  22. data/doc/release_notes/5.26.0.txt +35 -0
  23. data/doc/release_notes/5.27.0.txt +21 -0
  24. data/doc/release_notes/5.28.0.txt +16 -0
  25. data/doc/release_notes/5.29.0.txt +22 -0
  26. data/doc/release_notes/5.30.0.txt +20 -0
  27. data/doc/release_notes/5.31.0.txt +148 -0
  28. data/doc/release_notes/5.32.0.txt +46 -0
  29. data/doc/release_notes/5.33.0.txt +24 -0
  30. data/doc/release_notes/5.34.0.txt +40 -0
  31. data/doc/release_notes/5.35.0.txt +56 -0
  32. data/doc/release_notes/5.36.0.txt +60 -0
  33. data/doc/release_notes/5.37.0.txt +30 -0
  34. data/doc/release_notes/5.38.0.txt +28 -0
  35. data/doc/release_notes/5.39.0.txt +19 -0
  36. data/doc/release_notes/5.40.0.txt +40 -0
  37. data/doc/release_notes/5.41.0.txt +25 -0
  38. data/doc/release_notes/5.42.0.txt +136 -0
  39. data/doc/release_notes/5.43.0.txt +98 -0
  40. data/doc/release_notes/5.44.0.txt +32 -0
  41. data/doc/release_notes/5.45.0.txt +34 -0
  42. data/doc/release_notes/5.46.0.txt +87 -0
  43. data/doc/release_notes/5.47.0.txt +59 -0
  44. data/doc/release_notes/5.48.0.txt +14 -0
  45. data/doc/release_notes/5.49.0.txt +59 -0
  46. data/doc/sharding.rdoc +2 -0
  47. data/doc/sql.rdoc +13 -1
  48. data/doc/testing.rdoc +20 -7
  49. data/doc/transactions.rdoc +0 -8
  50. data/doc/validations.rdoc +1 -1
  51. data/doc/virtual_rows.rdoc +1 -1
  52. data/lib/sequel/adapters/ado/access.rb +1 -1
  53. data/lib/sequel/adapters/ado.rb +43 -35
  54. data/lib/sequel/adapters/ibmdb.rb +2 -2
  55. data/lib/sequel/adapters/jdbc/mysql.rb +6 -6
  56. data/lib/sequel/adapters/jdbc/postgresql.rb +11 -17
  57. data/lib/sequel/adapters/jdbc/sqlite.rb +29 -0
  58. data/lib/sequel/adapters/jdbc.rb +24 -6
  59. data/lib/sequel/adapters/mysql.rb +1 -1
  60. data/lib/sequel/adapters/mysql2.rb +2 -3
  61. data/lib/sequel/adapters/odbc.rb +8 -6
  62. data/lib/sequel/adapters/oracle.rb +5 -4
  63. data/lib/sequel/adapters/postgres.rb +15 -9
  64. data/lib/sequel/adapters/shared/access.rb +6 -6
  65. data/lib/sequel/adapters/shared/mssql.rb +66 -21
  66. data/lib/sequel/adapters/shared/mysql.rb +27 -10
  67. data/lib/sequel/adapters/shared/oracle.rb +29 -23
  68. data/lib/sequel/adapters/shared/postgres.rb +271 -32
  69. data/lib/sequel/adapters/shared/sqlanywhere.rb +9 -9
  70. data/lib/sequel/adapters/shared/sqlite.rb +161 -19
  71. data/lib/sequel/adapters/sqlanywhere.rb +1 -1
  72. data/lib/sequel/adapters/sqlite.rb +1 -1
  73. data/lib/sequel/adapters/tinytds.rb +15 -2
  74. data/lib/sequel/adapters/utils/mysql_mysql2.rb +4 -1
  75. data/lib/sequel/ast_transformer.rb +6 -0
  76. data/lib/sequel/connection_pool/sharded_single.rb +4 -1
  77. data/lib/sequel/connection_pool/sharded_threaded.rb +12 -12
  78. data/lib/sequel/connection_pool/single.rb +1 -1
  79. data/lib/sequel/connection_pool/threaded.rb +2 -2
  80. data/lib/sequel/core.rb +333 -319
  81. data/lib/sequel/database/connecting.rb +3 -4
  82. data/lib/sequel/database/logging.rb +7 -1
  83. data/lib/sequel/database/misc.rb +31 -12
  84. data/lib/sequel/database/query.rb +3 -1
  85. data/lib/sequel/database/schema_generator.rb +53 -51
  86. data/lib/sequel/database/schema_methods.rb +38 -23
  87. data/lib/sequel/database/transactions.rb +17 -18
  88. data/lib/sequel/dataset/actions.rb +14 -9
  89. data/lib/sequel/dataset/features.rb +16 -0
  90. data/lib/sequel/dataset/misc.rb +2 -2
  91. data/lib/sequel/dataset/placeholder_literalizer.rb +3 -7
  92. data/lib/sequel/dataset/prepared_statements.rb +2 -0
  93. data/lib/sequel/dataset/query.rb +26 -9
  94. data/lib/sequel/dataset/sql.rb +76 -25
  95. data/lib/sequel/dataset.rb +4 -2
  96. data/lib/sequel/deprecated.rb +3 -1
  97. data/lib/sequel/exceptions.rb +2 -0
  98. data/lib/sequel/extensions/_pretty_table.rb +1 -2
  99. data/lib/sequel/extensions/any_not_empty.rb +45 -0
  100. data/lib/sequel/extensions/async_thread_pool.rb +438 -0
  101. data/lib/sequel/extensions/blank.rb +8 -0
  102. data/lib/sequel/extensions/columns_introspection.rb +1 -2
  103. data/lib/sequel/extensions/connection_expiration.rb +2 -2
  104. data/lib/sequel/extensions/connection_validator.rb +2 -2
  105. data/lib/sequel/extensions/core_refinements.rb +2 -0
  106. data/lib/sequel/extensions/date_arithmetic.rb +36 -24
  107. data/lib/sequel/extensions/duplicate_columns_handler.rb +3 -1
  108. data/lib/sequel/extensions/eval_inspect.rb +2 -0
  109. data/lib/sequel/extensions/exclude_or_null.rb +68 -0
  110. data/lib/sequel/extensions/fiber_concurrency.rb +24 -0
  111. data/lib/sequel/extensions/index_caching.rb +9 -7
  112. data/lib/sequel/extensions/inflector.rb +9 -1
  113. data/lib/sequel/extensions/integer64.rb +2 -0
  114. data/lib/sequel/extensions/migration.rb +11 -3
  115. data/lib/sequel/extensions/named_timezones.rb +56 -8
  116. data/lib/sequel/extensions/pagination.rb +1 -1
  117. data/lib/sequel/extensions/pg_array.rb +5 -0
  118. data/lib/sequel/extensions/pg_array_ops.rb +14 -6
  119. data/lib/sequel/extensions/pg_enum.rb +11 -3
  120. data/lib/sequel/extensions/pg_extended_date_support.rb +2 -2
  121. data/lib/sequel/extensions/pg_hstore.rb +6 -0
  122. data/lib/sequel/extensions/pg_hstore_ops.rb +54 -2
  123. data/lib/sequel/extensions/pg_inet.rb +15 -5
  124. data/lib/sequel/extensions/pg_interval.rb +36 -8
  125. data/lib/sequel/extensions/pg_json.rb +387 -123
  126. data/lib/sequel/extensions/pg_json_ops.rb +238 -0
  127. data/lib/sequel/extensions/pg_loose_count.rb +3 -1
  128. data/lib/sequel/extensions/pg_range.rb +17 -9
  129. data/lib/sequel/extensions/pg_range_ops.rb +2 -0
  130. data/lib/sequel/extensions/pg_row.rb +4 -2
  131. data/lib/sequel/extensions/pg_row_ops.rb +24 -0
  132. data/lib/sequel/extensions/pg_timestamptz.rb +2 -0
  133. data/lib/sequel/extensions/query.rb +3 -0
  134. data/lib/sequel/extensions/run_transaction_hooks.rb +72 -0
  135. data/lib/sequel/extensions/s.rb +2 -0
  136. data/lib/sequel/extensions/schema_dumper.rb +24 -7
  137. data/lib/sequel/extensions/server_block.rb +18 -7
  138. data/lib/sequel/extensions/sql_comments.rb +2 -2
  139. data/lib/sequel/extensions/string_agg.rb +1 -1
  140. data/lib/sequel/extensions/symbol_aref_refinement.rb +2 -0
  141. data/lib/sequel/extensions/symbol_as_refinement.rb +2 -0
  142. data/lib/sequel/extensions/to_dot.rb +9 -3
  143. data/lib/sequel/model/associations.rb +356 -117
  144. data/lib/sequel/model/base.rb +107 -68
  145. data/lib/sequel/model/errors.rb +10 -1
  146. data/lib/sequel/model/inflections.rb +1 -1
  147. data/lib/sequel/model/plugins.rb +9 -3
  148. data/lib/sequel/model.rb +3 -1
  149. data/lib/sequel/plugins/association_lazy_eager_option.rb +66 -0
  150. data/lib/sequel/plugins/association_multi_add_remove.rb +85 -0
  151. data/lib/sequel/plugins/association_pks.rb +60 -18
  152. data/lib/sequel/plugins/association_proxies.rb +8 -2
  153. data/lib/sequel/plugins/async_thread_pool.rb +39 -0
  154. data/lib/sequel/plugins/auto_validations.rb +39 -5
  155. data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
  156. data/lib/sequel/plugins/blacklist_security.rb +1 -2
  157. data/lib/sequel/plugins/boolean_subsets.rb +4 -1
  158. data/lib/sequel/plugins/caching.rb +3 -0
  159. data/lib/sequel/plugins/class_table_inheritance.rb +33 -28
  160. data/lib/sequel/plugins/column_encryption.rb +728 -0
  161. data/lib/sequel/plugins/composition.rb +7 -2
  162. data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
  163. data/lib/sequel/plugins/constraint_validations.rb +2 -1
  164. data/lib/sequel/plugins/csv_serializer.rb +28 -9
  165. data/lib/sequel/plugins/dataset_associations.rb +4 -1
  166. data/lib/sequel/plugins/dirty.rb +60 -22
  167. data/lib/sequel/plugins/empty_failure_backtraces.rb +38 -0
  168. data/lib/sequel/plugins/forbid_lazy_load.rb +216 -0
  169. data/lib/sequel/plugins/insert_conflict.rb +72 -0
  170. data/lib/sequel/plugins/instance_specific_default.rb +113 -0
  171. data/lib/sequel/plugins/json_serializer.rb +57 -35
  172. data/lib/sequel/plugins/lazy_attributes.rb +1 -1
  173. data/lib/sequel/plugins/many_through_many.rb +108 -9
  174. data/lib/sequel/plugins/nested_attributes.rb +15 -3
  175. data/lib/sequel/plugins/pg_array_associations.rb +58 -41
  176. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +91 -30
  177. data/lib/sequel/plugins/prepared_statements.rb +15 -12
  178. data/lib/sequel/plugins/prepared_statements_safe.rb +1 -3
  179. data/lib/sequel/plugins/rcte_tree.rb +43 -35
  180. data/lib/sequel/plugins/serialization.rb +8 -3
  181. data/lib/sequel/plugins/serialization_modification_detection.rb +1 -1
  182. data/lib/sequel/plugins/sharding.rb +11 -5
  183. data/lib/sequel/plugins/single_table_inheritance.rb +22 -15
  184. data/lib/sequel/plugins/skip_saving_columns.rb +108 -0
  185. data/lib/sequel/plugins/static_cache.rb +9 -4
  186. data/lib/sequel/plugins/static_cache_cache.rb +53 -0
  187. data/lib/sequel/plugins/string_stripper.rb +1 -1
  188. data/lib/sequel/plugins/subclasses.rb +2 -0
  189. data/lib/sequel/plugins/throw_failures.rb +1 -1
  190. data/lib/sequel/plugins/timestamps.rb +1 -1
  191. data/lib/sequel/plugins/tree.rb +9 -4
  192. data/lib/sequel/plugins/typecast_on_load.rb +3 -2
  193. data/lib/sequel/plugins/unused_associations.rb +521 -0
  194. data/lib/sequel/plugins/update_or_create.rb +1 -1
  195. data/lib/sequel/plugins/validation_class_methods.rb +5 -1
  196. data/lib/sequel/plugins/validation_helpers.rb +18 -11
  197. data/lib/sequel/plugins/xml_serializer.rb +1 -1
  198. data/lib/sequel/sql.rb +20 -5
  199. data/lib/sequel/timezones.rb +63 -17
  200. data/lib/sequel/version.rb +1 -1
  201. metadata +113 -381
  202. data/Rakefile +0 -151
  203. data/doc/release_notes/4.0.0.txt +0 -262
  204. data/doc/release_notes/4.1.0.txt +0 -85
  205. data/doc/release_notes/4.10.0.txt +0 -226
  206. data/doc/release_notes/4.11.0.txt +0 -147
  207. data/doc/release_notes/4.12.0.txt +0 -105
  208. data/doc/release_notes/4.13.0.txt +0 -169
  209. data/doc/release_notes/4.14.0.txt +0 -68
  210. data/doc/release_notes/4.15.0.txt +0 -56
  211. data/doc/release_notes/4.16.0.txt +0 -36
  212. data/doc/release_notes/4.17.0.txt +0 -38
  213. data/doc/release_notes/4.18.0.txt +0 -36
  214. data/doc/release_notes/4.19.0.txt +0 -45
  215. data/doc/release_notes/4.2.0.txt +0 -129
  216. data/doc/release_notes/4.20.0.txt +0 -79
  217. data/doc/release_notes/4.21.0.txt +0 -94
  218. data/doc/release_notes/4.22.0.txt +0 -72
  219. data/doc/release_notes/4.23.0.txt +0 -65
  220. data/doc/release_notes/4.24.0.txt +0 -99
  221. data/doc/release_notes/4.25.0.txt +0 -181
  222. data/doc/release_notes/4.26.0.txt +0 -44
  223. data/doc/release_notes/4.27.0.txt +0 -78
  224. data/doc/release_notes/4.28.0.txt +0 -57
  225. data/doc/release_notes/4.29.0.txt +0 -41
  226. data/doc/release_notes/4.3.0.txt +0 -40
  227. data/doc/release_notes/4.30.0.txt +0 -37
  228. data/doc/release_notes/4.31.0.txt +0 -57
  229. data/doc/release_notes/4.32.0.txt +0 -132
  230. data/doc/release_notes/4.33.0.txt +0 -88
  231. data/doc/release_notes/4.34.0.txt +0 -86
  232. data/doc/release_notes/4.35.0.txt +0 -130
  233. data/doc/release_notes/4.36.0.txt +0 -116
  234. data/doc/release_notes/4.37.0.txt +0 -50
  235. data/doc/release_notes/4.38.0.txt +0 -67
  236. data/doc/release_notes/4.39.0.txt +0 -127
  237. data/doc/release_notes/4.4.0.txt +0 -92
  238. data/doc/release_notes/4.40.0.txt +0 -179
  239. data/doc/release_notes/4.41.0.txt +0 -77
  240. data/doc/release_notes/4.42.0.txt +0 -221
  241. data/doc/release_notes/4.43.0.txt +0 -87
  242. data/doc/release_notes/4.44.0.txt +0 -125
  243. data/doc/release_notes/4.45.0.txt +0 -370
  244. data/doc/release_notes/4.46.0.txt +0 -404
  245. data/doc/release_notes/4.47.0.txt +0 -56
  246. data/doc/release_notes/4.48.0.txt +0 -293
  247. data/doc/release_notes/4.49.0.txt +0 -222
  248. data/doc/release_notes/4.5.0.txt +0 -34
  249. data/doc/release_notes/4.6.0.txt +0 -30
  250. data/doc/release_notes/4.7.0.txt +0 -103
  251. data/doc/release_notes/4.8.0.txt +0 -175
  252. data/doc/release_notes/4.9.0.txt +0 -190
  253. data/spec/adapter_spec.rb +0 -4
  254. data/spec/adapters/db2_spec.rb +0 -170
  255. data/spec/adapters/mssql_spec.rb +0 -804
  256. data/spec/adapters/mysql_spec.rb +0 -1065
  257. data/spec/adapters/oracle_spec.rb +0 -371
  258. data/spec/adapters/postgres_spec.rb +0 -4125
  259. data/spec/adapters/spec_helper.rb +0 -44
  260. data/spec/adapters/sqlanywhere_spec.rb +0 -97
  261. data/spec/adapters/sqlite_spec.rb +0 -652
  262. data/spec/bin_spec.rb +0 -278
  263. data/spec/core/connection_pool_spec.rb +0 -1250
  264. data/spec/core/database_spec.rb +0 -2865
  265. data/spec/core/dataset_spec.rb +0 -5515
  266. data/spec/core/deprecated_spec.rb +0 -70
  267. data/spec/core/expression_filters_spec.rb +0 -1455
  268. data/spec/core/mock_adapter_spec.rb +0 -722
  269. data/spec/core/object_graph_spec.rb +0 -336
  270. data/spec/core/placeholder_literalizer_spec.rb +0 -166
  271. data/spec/core/schema_generator_spec.rb +0 -214
  272. data/spec/core/schema_spec.rb +0 -1826
  273. data/spec/core/spec_helper.rb +0 -24
  274. data/spec/core/version_spec.rb +0 -14
  275. data/spec/core_extensions_spec.rb +0 -763
  276. data/spec/core_model_spec.rb +0 -2
  277. data/spec/core_spec.rb +0 -1
  278. data/spec/deprecation_helper.rb +0 -30
  279. data/spec/extensions/accessed_columns_spec.rb +0 -51
  280. data/spec/extensions/active_model_spec.rb +0 -99
  281. data/spec/extensions/after_initialize_spec.rb +0 -28
  282. data/spec/extensions/arbitrary_servers_spec.rb +0 -109
  283. data/spec/extensions/association_dependencies_spec.rb +0 -125
  284. data/spec/extensions/association_pks_spec.rb +0 -423
  285. data/spec/extensions/association_proxies_spec.rb +0 -100
  286. data/spec/extensions/auto_literal_strings_spec.rb +0 -205
  287. data/spec/extensions/auto_validations_spec.rb +0 -229
  288. data/spec/extensions/blacklist_security_spec.rb +0 -95
  289. data/spec/extensions/blank_spec.rb +0 -69
  290. data/spec/extensions/boolean_readers_spec.rb +0 -93
  291. data/spec/extensions/boolean_subsets_spec.rb +0 -47
  292. data/spec/extensions/caching_spec.rb +0 -273
  293. data/spec/extensions/caller_logging_spec.rb +0 -52
  294. data/spec/extensions/class_table_inheritance_spec.rb +0 -750
  295. data/spec/extensions/column_conflicts_spec.rb +0 -75
  296. data/spec/extensions/column_select_spec.rb +0 -129
  297. data/spec/extensions/columns_introspection_spec.rb +0 -90
  298. data/spec/extensions/columns_updated_spec.rb +0 -35
  299. data/spec/extensions/composition_spec.rb +0 -248
  300. data/spec/extensions/connection_expiration_spec.rb +0 -151
  301. data/spec/extensions/connection_validator_spec.rb +0 -144
  302. data/spec/extensions/constant_sql_override_spec.rb +0 -24
  303. data/spec/extensions/constraint_validations_plugin_spec.rb +0 -300
  304. data/spec/extensions/constraint_validations_spec.rb +0 -439
  305. data/spec/extensions/core_refinements_spec.rb +0 -528
  306. data/spec/extensions/csv_serializer_spec.rb +0 -183
  307. data/spec/extensions/current_datetime_timestamp_spec.rb +0 -27
  308. data/spec/extensions/dataset_associations_spec.rb +0 -365
  309. data/spec/extensions/dataset_source_alias_spec.rb +0 -51
  310. data/spec/extensions/date_arithmetic_spec.rb +0 -181
  311. data/spec/extensions/datetime_parse_to_time_spec.rb +0 -169
  312. data/spec/extensions/def_dataset_method_spec.rb +0 -100
  313. data/spec/extensions/defaults_setter_spec.rb +0 -150
  314. data/spec/extensions/delay_add_association_spec.rb +0 -73
  315. data/spec/extensions/dirty_spec.rb +0 -189
  316. data/spec/extensions/duplicate_columns_handler_spec.rb +0 -104
  317. data/spec/extensions/eager_each_spec.rb +0 -62
  318. data/spec/extensions/eager_graph_eager_spec.rb +0 -100
  319. data/spec/extensions/empty_array_consider_nulls_spec.rb +0 -24
  320. data/spec/extensions/error_splitter_spec.rb +0 -18
  321. data/spec/extensions/error_sql_spec.rb +0 -20
  322. data/spec/extensions/escaped_like_spec.rb +0 -40
  323. data/spec/extensions/eval_inspect_spec.rb +0 -81
  324. data/spec/extensions/finder_spec.rb +0 -260
  325. data/spec/extensions/force_encoding_spec.rb +0 -126
  326. data/spec/extensions/freeze_datasets_spec.rb +0 -31
  327. data/spec/extensions/graph_each_spec.rb +0 -113
  328. data/spec/extensions/hook_class_methods_spec.rb +0 -402
  329. data/spec/extensions/identifier_mangling_spec.rb +0 -201
  330. data/spec/extensions/implicit_subquery_spec.rb +0 -58
  331. data/spec/extensions/index_caching_spec.rb +0 -66
  332. data/spec/extensions/inflector_spec.rb +0 -183
  333. data/spec/extensions/input_transformer_spec.rb +0 -69
  334. data/spec/extensions/insert_returning_select_spec.rb +0 -72
  335. data/spec/extensions/instance_filters_spec.rb +0 -79
  336. data/spec/extensions/instance_hooks_spec.rb +0 -246
  337. data/spec/extensions/integer64_spec.rb +0 -22
  338. data/spec/extensions/inverted_subsets_spec.rb +0 -33
  339. data/spec/extensions/json_serializer_spec.rb +0 -336
  340. data/spec/extensions/lazy_attributes_spec.rb +0 -183
  341. data/spec/extensions/list_spec.rb +0 -291
  342. data/spec/extensions/looser_typecasting_spec.rb +0 -43
  343. data/spec/extensions/many_through_many_spec.rb +0 -2177
  344. data/spec/extensions/migration_spec.rb +0 -864
  345. data/spec/extensions/modification_detection_spec.rb +0 -93
  346. data/spec/extensions/mssql_optimistic_locking_spec.rb +0 -92
  347. data/spec/extensions/named_timezones_spec.rb +0 -111
  348. data/spec/extensions/nested_attributes_spec.rb +0 -767
  349. data/spec/extensions/null_dataset_spec.rb +0 -85
  350. data/spec/extensions/optimistic_locking_spec.rb +0 -127
  351. data/spec/extensions/pagination_spec.rb +0 -116
  352. data/spec/extensions/pg_array_associations_spec.rb +0 -802
  353. data/spec/extensions/pg_array_ops_spec.rb +0 -144
  354. data/spec/extensions/pg_array_spec.rb +0 -398
  355. data/spec/extensions/pg_auto_constraint_validations_spec.rb +0 -172
  356. data/spec/extensions/pg_enum_spec.rb +0 -118
  357. data/spec/extensions/pg_extended_date_support_spec.rb +0 -126
  358. data/spec/extensions/pg_hstore_ops_spec.rb +0 -238
  359. data/spec/extensions/pg_hstore_spec.rb +0 -219
  360. data/spec/extensions/pg_inet_ops_spec.rb +0 -102
  361. data/spec/extensions/pg_inet_spec.rb +0 -72
  362. data/spec/extensions/pg_interval_spec.rb +0 -103
  363. data/spec/extensions/pg_json_ops_spec.rb +0 -289
  364. data/spec/extensions/pg_json_spec.rb +0 -262
  365. data/spec/extensions/pg_loose_count_spec.rb +0 -23
  366. data/spec/extensions/pg_range_ops_spec.rb +0 -60
  367. data/spec/extensions/pg_range_spec.rb +0 -519
  368. data/spec/extensions/pg_row_ops_spec.rb +0 -61
  369. data/spec/extensions/pg_row_plugin_spec.rb +0 -60
  370. data/spec/extensions/pg_row_spec.rb +0 -363
  371. data/spec/extensions/pg_static_cache_updater_spec.rb +0 -93
  372. data/spec/extensions/pg_timestamptz_spec.rb +0 -17
  373. data/spec/extensions/prepared_statements_safe_spec.rb +0 -66
  374. data/spec/extensions/prepared_statements_spec.rb +0 -177
  375. data/spec/extensions/pretty_table_spec.rb +0 -123
  376. data/spec/extensions/query_spec.rb +0 -94
  377. data/spec/extensions/rcte_tree_spec.rb +0 -381
  378. data/spec/extensions/round_timestamps_spec.rb +0 -39
  379. data/spec/extensions/s_spec.rb +0 -60
  380. data/spec/extensions/schema_caching_spec.rb +0 -64
  381. data/spec/extensions/schema_dumper_spec.rb +0 -870
  382. data/spec/extensions/select_remove_spec.rb +0 -38
  383. data/spec/extensions/sequel_4_dataset_methods_spec.rb +0 -121
  384. data/spec/extensions/serialization_modification_detection_spec.rb +0 -98
  385. data/spec/extensions/serialization_spec.rb +0 -365
  386. data/spec/extensions/server_block_spec.rb +0 -97
  387. data/spec/extensions/server_logging_spec.rb +0 -45
  388. data/spec/extensions/sharding_spec.rb +0 -189
  389. data/spec/extensions/shared_caching_spec.rb +0 -151
  390. data/spec/extensions/single_table_inheritance_spec.rb +0 -347
  391. data/spec/extensions/singular_table_names_spec.rb +0 -22
  392. data/spec/extensions/skip_create_refresh_spec.rb +0 -18
  393. data/spec/extensions/spec_helper.rb +0 -63
  394. data/spec/extensions/split_array_nil_spec.rb +0 -24
  395. data/spec/extensions/split_values_spec.rb +0 -57
  396. data/spec/extensions/sql_comments_spec.rb +0 -33
  397. data/spec/extensions/sql_expr_spec.rb +0 -59
  398. data/spec/extensions/static_cache_spec.rb +0 -471
  399. data/spec/extensions/string_agg_spec.rb +0 -90
  400. data/spec/extensions/string_date_time_spec.rb +0 -95
  401. data/spec/extensions/string_stripper_spec.rb +0 -68
  402. data/spec/extensions/subclasses_spec.rb +0 -79
  403. data/spec/extensions/subset_conditions_spec.rb +0 -38
  404. data/spec/extensions/symbol_aref_refinement_spec.rb +0 -28
  405. data/spec/extensions/symbol_as_refinement_spec.rb +0 -21
  406. data/spec/extensions/synchronize_sql_spec.rb +0 -124
  407. data/spec/extensions/table_select_spec.rb +0 -83
  408. data/spec/extensions/tactical_eager_loading_spec.rb +0 -402
  409. data/spec/extensions/thread_local_timezones_spec.rb +0 -67
  410. data/spec/extensions/throw_failures_spec.rb +0 -74
  411. data/spec/extensions/timestamps_spec.rb +0 -209
  412. data/spec/extensions/to_dot_spec.rb +0 -153
  413. data/spec/extensions/touch_spec.rb +0 -226
  414. data/spec/extensions/tree_spec.rb +0 -334
  415. data/spec/extensions/typecast_on_load_spec.rb +0 -86
  416. data/spec/extensions/unlimited_update_spec.rb +0 -21
  417. data/spec/extensions/update_or_create_spec.rb +0 -83
  418. data/spec/extensions/update_primary_key_spec.rb +0 -105
  419. data/spec/extensions/update_refresh_spec.rb +0 -59
  420. data/spec/extensions/uuid_spec.rb +0 -101
  421. data/spec/extensions/validate_associated_spec.rb +0 -52
  422. data/spec/extensions/validation_class_methods_spec.rb +0 -1040
  423. data/spec/extensions/validation_contexts_spec.rb +0 -31
  424. data/spec/extensions/validation_helpers_spec.rb +0 -525
  425. data/spec/extensions/whitelist_security_spec.rb +0 -157
  426. data/spec/extensions/xml_serializer_spec.rb +0 -213
  427. data/spec/files/bad_down_migration/001_create_alt_basic.rb +0 -4
  428. data/spec/files/bad_down_migration/002_create_alt_advanced.rb +0 -4
  429. data/spec/files/bad_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  430. data/spec/files/bad_timestamped_migrations/1273253851_create_nodes.rb +0 -9
  431. data/spec/files/bad_timestamped_migrations/1273253853_3_create_users.rb +0 -3
  432. data/spec/files/bad_up_migration/001_create_alt_basic.rb +0 -4
  433. data/spec/files/bad_up_migration/002_create_alt_advanced.rb +0 -3
  434. data/spec/files/convert_to_timestamp_migrations/001_create_sessions.rb +0 -9
  435. data/spec/files/convert_to_timestamp_migrations/002_create_nodes.rb +0 -9
  436. data/spec/files/convert_to_timestamp_migrations/003_3_create_users.rb +0 -4
  437. data/spec/files/convert_to_timestamp_migrations/1273253850_create_artists.rb +0 -9
  438. data/spec/files/convert_to_timestamp_migrations/1273253852_create_albums.rb +0 -9
  439. data/spec/files/double_migration/001_create_sessions.rb +0 -9
  440. data/spec/files/double_migration/002_create_nodes.rb +0 -19
  441. data/spec/files/double_migration/003_3_create_users.rb +0 -4
  442. data/spec/files/duplicate_integer_migrations/001_create_alt_advanced.rb +0 -4
  443. data/spec/files/duplicate_integer_migrations/001_create_alt_basic.rb +0 -4
  444. data/spec/files/duplicate_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  445. data/spec/files/duplicate_timestamped_migrations/1273253853_create_nodes.rb +0 -9
  446. data/spec/files/duplicate_timestamped_migrations/1273253853_create_users.rb +0 -4
  447. data/spec/files/empty_migration/001_create_sessions.rb +0 -9
  448. data/spec/files/empty_migration/002_create_nodes.rb +0 -0
  449. data/spec/files/empty_migration/003_3_create_users.rb +0 -4
  450. data/spec/files/integer_migrations/001_create_sessions.rb +0 -9
  451. data/spec/files/integer_migrations/002_create_nodes.rb +0 -9
  452. data/spec/files/integer_migrations/003_3_create_users.rb +0 -4
  453. data/spec/files/interleaved_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  454. data/spec/files/interleaved_timestamped_migrations/1273253850_create_artists.rb +0 -9
  455. data/spec/files/interleaved_timestamped_migrations/1273253851_create_nodes.rb +0 -9
  456. data/spec/files/interleaved_timestamped_migrations/1273253852_create_albums.rb +0 -9
  457. data/spec/files/interleaved_timestamped_migrations/1273253853_3_create_users.rb +0 -4
  458. data/spec/files/missing_integer_migrations/001_create_alt_basic.rb +0 -4
  459. data/spec/files/missing_integer_migrations/003_create_alt_advanced.rb +0 -4
  460. data/spec/files/missing_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  461. data/spec/files/missing_timestamped_migrations/1273253853_3_create_users.rb +0 -4
  462. data/spec/files/reversible_migrations/001_reversible.rb +0 -5
  463. data/spec/files/reversible_migrations/002_reversible.rb +0 -5
  464. data/spec/files/reversible_migrations/003_reversible.rb +0 -5
  465. data/spec/files/reversible_migrations/004_reversible.rb +0 -5
  466. data/spec/files/reversible_migrations/005_reversible.rb +0 -10
  467. data/spec/files/reversible_migrations/006_reversible.rb +0 -10
  468. data/spec/files/reversible_migrations/007_reversible.rb +0 -10
  469. data/spec/files/timestamped_migrations/1273253849_create_sessions.rb +0 -9
  470. data/spec/files/timestamped_migrations/1273253851_create_nodes.rb +0 -9
  471. data/spec/files/timestamped_migrations/1273253853_3_create_users.rb +0 -4
  472. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +0 -4
  473. data/spec/files/transaction_specified_migrations/002_create_basic.rb +0 -4
  474. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +0 -3
  475. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +0 -3
  476. data/spec/files/uppercase_timestamped_migrations/1273253849_CREATE_SESSIONS.RB +0 -9
  477. data/spec/files/uppercase_timestamped_migrations/1273253851_CREATE_NODES.RB +0 -9
  478. data/spec/files/uppercase_timestamped_migrations/1273253853_3_CREATE_USERS.RB +0 -4
  479. data/spec/guards_helper.rb +0 -59
  480. data/spec/integration/associations_test.rb +0 -2597
  481. data/spec/integration/database_test.rb +0 -113
  482. data/spec/integration/dataset_test.rb +0 -1981
  483. data/spec/integration/eager_loader_test.rb +0 -687
  484. data/spec/integration/migrator_test.rb +0 -262
  485. data/spec/integration/model_test.rb +0 -203
  486. data/spec/integration/plugin_test.rb +0 -2396
  487. data/spec/integration/prepared_statement_test.rb +0 -405
  488. data/spec/integration/schema_test.rb +0 -889
  489. data/spec/integration/spec_helper.rb +0 -65
  490. data/spec/integration/timezone_test.rb +0 -86
  491. data/spec/integration/transaction_test.rb +0 -603
  492. data/spec/integration/type_test.rb +0 -127
  493. data/spec/model/association_reflection_spec.rb +0 -803
  494. data/spec/model/associations_spec.rb +0 -4738
  495. data/spec/model/base_spec.rb +0 -875
  496. data/spec/model/class_dataset_methods_spec.rb +0 -146
  497. data/spec/model/dataset_methods_spec.rb +0 -198
  498. data/spec/model/eager_loading_spec.rb +0 -2377
  499. data/spec/model/hooks_spec.rb +0 -370
  500. data/spec/model/inflector_spec.rb +0 -26
  501. data/spec/model/model_spec.rb +0 -956
  502. data/spec/model/plugins_spec.rb +0 -429
  503. data/spec/model/record_spec.rb +0 -2118
  504. data/spec/model/spec_helper.rb +0 -46
  505. data/spec/model/validations_spec.rb +0 -220
  506. data/spec/model_no_assoc_spec.rb +0 -1
  507. data/spec/model_spec.rb +0 -1
  508. data/spec/plugin_spec.rb +0 -1
  509. data/spec/sequel_coverage.rb +0 -15
  510. data/spec/sequel_warning.rb +0 -4
  511. data/spec/spec_config.rb +0 -12
@@ -164,11 +164,11 @@ module Sequel
164
164
  # range to return the object(s) at the correct offset/limit.
165
165
  def apply_ruby_eager_limit_strategy(rows, limit_and_offset = limit_and_offset())
166
166
  name = self[:name]
167
+ return unless range = slice_range(limit_and_offset)
167
168
  if returns_array?
168
- range = slice_range(limit_and_offset)
169
169
  rows.each{|o| o.associations[name] = o.associations[name][range] || []}
170
- elsif sr = slice_range(limit_and_offset)
171
- offset = sr.begin
170
+ else
171
+ offset = range.begin
172
172
  rows.each{|o| o.associations[name] = o.associations[name][offset]}
173
173
  end
174
174
  end
@@ -263,7 +263,9 @@ module Sequel
263
263
  # yielding each row to the block.
264
264
  def eager_load_results(eo, &block)
265
265
  rows = eo[:rows]
266
- initialize_association_cache(rows) unless eo[:initialize_rows] == false
266
+ unless eo[:initialize_rows] == false
267
+ Sequel.synchronize_with(eo[:mutex]){initialize_association_cache(rows)}
268
+ end
267
269
  if eo[:id_map]
268
270
  ids = eo[:id_map].keys
269
271
  return ids if ids.empty?
@@ -272,7 +274,9 @@ module Sequel
272
274
  cascade = eo[:associations]
273
275
  eager_limit = nil
274
276
 
275
- if eo[:eager_block] || eo[:loader] == false
277
+ if eo[:no_results]
278
+ no_results = true
279
+ elsif eo[:eager_block] || eo[:loader] == false
276
280
  ds = eager_loading_dataset(eo)
277
281
 
278
282
  strategy = ds.opts[:eager_limit_strategy] || strategy
@@ -311,7 +315,8 @@ module Sequel
311
315
  objects = loader.all(ids)
312
316
  end
313
317
 
314
- objects.each(&block)
318
+ Sequel.synchronize_with(eo[:mutex]){objects.each(&block)} unless no_results
319
+
315
320
  if strategy == :ruby
316
321
  apply_ruby_eager_limit_strategy(rows, eager_limit || limit_and_offset)
317
322
  end
@@ -356,7 +361,7 @@ module Sequel
356
361
  def finalize
357
362
  return unless cache = self[:cache]
358
363
 
359
- finalize_settings.each do |meth, key|
364
+ finalizer = proc do |meth, key|
360
365
  next if has_key?(key)
361
366
 
362
367
  # Allow calling private methods to make sure caching is done appropriately
@@ -364,6 +369,13 @@ module Sequel
364
369
  self[key] = cache.delete(key) if cache.has_key?(key)
365
370
  end
366
371
 
372
+ finalize_settings.each(&finalizer)
373
+
374
+ unless self[:instance_specific]
375
+ finalizer.call(:associated_eager_dataset, :associated_eager_dataset)
376
+ finalizer.call(:filter_by_associations_conditions_dataset, :filter_by_associations_conditions_dataset)
377
+ end
378
+
367
379
  nil
368
380
  end
369
381
 
@@ -371,9 +383,7 @@ module Sequel
371
383
  FINALIZE_SETTINGS = {
372
384
  :associated_class=>:class,
373
385
  :associated_dataset=>:_dataset,
374
- :associated_eager_dataset=>:associated_eager_dataset,
375
386
  :eager_limit_strategy=>:_eager_limit_strategy,
376
- :filter_by_associations_conditions_dataset=>:filter_by_associations_conditions_dataset,
377
387
  :placeholder_loader=>:placeholder_loader,
378
388
  :predicate_key=>:predicate_key,
379
389
  :predicate_keys=>:predicate_keys,
@@ -432,7 +442,11 @@ module Sequel
432
442
  if use_placeholder_loader?
433
443
  cached_fetch(:placeholder_loader) do
434
444
  Sequel::Dataset::PlaceholderLiteralizer.loader(associated_dataset) do |pl, ds|
435
- ds.where(Sequel.&(*predicate_keys.map{|k| SQL::BooleanExpression.new(:'=', k, pl.arg)}))
445
+ ds = ds.where(Sequel.&(*predicate_keys.map{|k| SQL::BooleanExpression.new(:'=', k, pl.arg)}))
446
+ if self[:block]
447
+ ds = self[:block].call(ds)
448
+ end
449
+ ds
436
450
  end
437
451
  end
438
452
  end
@@ -626,9 +640,7 @@ module Sequel
626
640
  # given the hash passed to the eager loader.
627
641
  def eager_loading_dataset(eo=OPTS)
628
642
  ds = eo[:dataset] || associated_eager_dataset
629
- if id_map = eo[:id_map]
630
- ds = ds.where(eager_loading_predicate_condition(id_map.keys))
631
- end
643
+ ds = eager_loading_set_predicate_condition(ds, eo)
632
644
  if associations = eo[:associations]
633
645
  ds = ds.eager(associations)
634
646
  end
@@ -655,6 +667,15 @@ module Sequel
655
667
  self[:model].default_eager_limit_strategy || :ruby
656
668
  end
657
669
 
670
+ # Set the predicate condition for the eager loading dataset based on the id map
671
+ # in the eager loading options.
672
+ def eager_loading_set_predicate_condition(ds, eo)
673
+ if id_map = eo[:id_map]
674
+ ds = ds.where(eager_loading_predicate_condition(id_map.keys))
675
+ end
676
+ ds
677
+ end
678
+
658
679
  # The predicate condition to use for the eager_loader.
659
680
  def eager_loading_predicate_condition(keys)
660
681
  {predicate_key=>keys}
@@ -796,7 +817,7 @@ module Sequel
796
817
 
797
818
  # Whether the placeholder loader can be used to load the association.
798
819
  def use_placeholder_loader?
799
- !self[:instance_specific] && !self[:eager_graph]
820
+ self[:use_placeholder_loader]
800
821
  end
801
822
  end
802
823
 
@@ -1244,7 +1265,9 @@ module Sequel
1244
1265
  else
1245
1266
  assoc_record.values.delete(left_key_alias)
1246
1267
  end
1247
- next unless objects = h[hash_key]
1268
+
1269
+ objects = h[hash_key]
1270
+
1248
1271
  if assign_singular
1249
1272
  objects.each do |object|
1250
1273
  object.associations[name] ||= assoc_record
@@ -1304,7 +1327,7 @@ module Sequel
1304
1327
 
1305
1328
  # many_to_many associations need to select a key in an associated table to eagerly load
1306
1329
  def eager_loading_use_associated_key?
1307
- true
1330
+ !separate_query_per_table?
1308
1331
  end
1309
1332
 
1310
1333
  # The source of the join table. This is the join table itself, unless it
@@ -1361,10 +1384,30 @@ module Sequel
1361
1384
  cached_fetch(:select){default_select}
1362
1385
  end
1363
1386
 
1387
+ # Whether a separate query should be used for the join table.
1388
+ def separate_query_per_table?
1389
+ self[:join_table_db]
1390
+ end
1391
+
1364
1392
  private
1365
1393
 
1394
+ # Join to the the join table, unless using a separate query per table.
1366
1395
  def _associated_dataset
1367
- super.inner_join(self[:join_table], self[:right_keys].zip(right_primary_keys), :qualify=>:deep)
1396
+ if separate_query_per_table?
1397
+ super
1398
+ else
1399
+ super.inner_join(self[:join_table], self[:right_keys].zip(right_primary_keys), :qualify=>:deep)
1400
+ end
1401
+ end
1402
+
1403
+ # Use the right_keys from the eager loading options if
1404
+ # using a separate query per table.
1405
+ def eager_loading_set_predicate_condition(ds, eo)
1406
+ if separate_query_per_table?
1407
+ ds.where(right_primary_key=>eo[:right_keys])
1408
+ else
1409
+ super
1410
+ end
1368
1411
  end
1369
1412
 
1370
1413
  # The default selection for associations that require joins. These do not use the default
@@ -1581,6 +1624,7 @@ module Sequel
1581
1624
  # === Multiple Types
1582
1625
  # :adder :: Proc used to define the private _add_* method for doing the database work
1583
1626
  # to associate the given object to the current object (*_to_many assocations).
1627
+ # Set to nil to not define a add_* method for the association.
1584
1628
  # :after_add :: Symbol, Proc, or array of both/either specifying a callback to call
1585
1629
  # after a new item is added to the association.
1586
1630
  # :after_load :: Symbol, Proc, or array of both/either specifying a callback to call
@@ -1591,6 +1635,8 @@ module Sequel
1591
1635
  # after an item is set using the association setter method.
1592
1636
  # :allow_eager :: If set to false, you cannot load the association eagerly
1593
1637
  # via eager or eager_graph
1638
+ # :allow_eager_graph :: If set to false, you cannot load the association eagerly via eager_graph.
1639
+ # :allow_filtering_by :: If set to false, you cannot use the association when filtering
1594
1640
  # :before_add :: Symbol, Proc, or array of both/either specifying a callback to call
1595
1641
  # before a new item is added to the association.
1596
1642
  # :before_remove :: Symbol, Proc, or array of both/either specifying a callback to call
@@ -1609,6 +1655,7 @@ module Sequel
1609
1655
  # the class. <tt>class: 'Foo', class_namespace: 'Bar'</tt> looks for <tt>::Bar::Foo</tt>.)
1610
1656
  # :clearer :: Proc used to define the private _remove_all_* method for doing the database work
1611
1657
  # to remove all objects associated to the current object (*_to_many assocations).
1658
+ # Set to nil to not define a remove_all_* method for the association.
1612
1659
  # :clone :: Merge the current options and block into the options and block used in defining
1613
1660
  # the given association. Can be used to DRY up a bunch of similar associations that
1614
1661
  # all share the same options such as :class and :key, while changing the order and block used.
@@ -1663,7 +1710,7 @@ module Sequel
1663
1710
  # :graph_only_conditions :: The conditions to use on the SQL join when eagerly loading
1664
1711
  # the association via +eager_graph+, instead of the default conditions specified by the
1665
1712
  # foreign/primary keys. This option causes the :graph_conditions option to be ignored.
1666
- # :graph_order :: Over the order to use when using eager_graph, instead of the default order. This should be used
1713
+ # :graph_order :: the order to use when using eager_graph, instead of the default order. This should be used
1667
1714
  # in the case where :order contains an identifier qualified by the table's name, which may not match
1668
1715
  # the alias used when eager graphing. By setting this to the unqualified identifier, it will be
1669
1716
  # automatically qualified when using eager_graph.
@@ -1675,6 +1722,10 @@ module Sequel
1675
1722
  # limit (first element) and an offset (second element).
1676
1723
  # :methods_module :: The module that methods the association creates will be placed into. Defaults
1677
1724
  # to the module containing the model's columns.
1725
+ # :no_association_method :: Do not add a method for the association. This can save memory if the association
1726
+ # method is never used.
1727
+ # :no_dataset_method :: Do not add a method for the association dataset. This can save memory if the dataset
1728
+ # method is never used.
1678
1729
  # :order :: the column(s) by which to order the association dataset. Can be a
1679
1730
  # singular column symbol or an array of column symbols.
1680
1731
  # :order_eager_graph :: Whether to add the association's order to the graphed dataset's order when graphing
@@ -1687,6 +1738,7 @@ module Sequel
1687
1738
  # the current association's key(s). Set to nil to not use a reciprocal.
1688
1739
  # :remover :: Proc used to define the private _remove_* method for doing the database work
1689
1740
  # to remove the association between the given object and the current object (*_to_many assocations).
1741
+ # Set to nil to not define a remove_* method for the association.
1690
1742
  # :select :: the columns to select. Defaults to the associated class's table_name.* in an association
1691
1743
  # that uses joins, which means it doesn't include the attributes from the
1692
1744
  # join table. If you want to include the join table attributes, you can
@@ -1695,6 +1747,7 @@ module Sequel
1695
1747
  # the same name in both the join table and the associated table.
1696
1748
  # :setter :: Proc used to define the private _*= method for doing the work to setup the assocation
1697
1749
  # between the given object and the current object (*_to_one associations).
1750
+ # Set to nil to not define a setter method for the association.
1698
1751
  # :subqueries_per_union :: The number of subqueries to use in each UNION query, for eager
1699
1752
  # loading limited associations using the default :union strategy.
1700
1753
  # :validate :: Set to false to not validate when implicitly saving any associated object.
@@ -1751,6 +1804,9 @@ module Sequel
1751
1804
  # underscored, sorted, and joined with '_'.
1752
1805
  # :join_table_block :: proc that can be used to modify the dataset used in the add/remove/remove_all
1753
1806
  # methods. Should accept a dataset argument and return a modified dataset if present.
1807
+ # :join_table_db :: When retrieving records when using lazy loading or eager loading via +eager+, instead of
1808
+ # a join between to the join table and the associated table, use a separate query for the
1809
+ # join table using the given Database object.
1754
1810
  # :left_key :: foreign key in join table that points to current model's
1755
1811
  # primary key, as a symbol. Defaults to :"#{self.name.underscore}_id".
1756
1812
  # Can use an array of symbols for a composite key association.
@@ -1791,11 +1847,12 @@ module Sequel
1791
1847
  opts.merge!(:type => type, :name => name, :cache=>({} if cache_associations), :model => self)
1792
1848
 
1793
1849
  opts[:block] = block if block
1794
- if !opts.has_key?(:instance_specific) && (block || orig_opts[:block] || orig_opts[:dataset])
1850
+ opts[:instance_specific] = true if orig_opts[:dataset]
1851
+ if !opts.has_key?(:instance_specific) && (block || orig_opts[:block])
1795
1852
  # It's possible the association is instance specific, in that it depends on
1796
1853
  # values other than the foreign key value. This needs to be checked for
1797
1854
  # in certain places to disable optimizations.
1798
- opts[:instance_specific] = true
1855
+ opts[:instance_specific] = _association_instance_specific_default(name)
1799
1856
  end
1800
1857
  opts = assoc_class.new.merge!(opts)
1801
1858
 
@@ -1803,6 +1860,7 @@ module Sequel
1803
1860
  raise(Error, "cannot clone an association to an association of different type (association #{name} with type #{type} cloning #{opts[:clone]} with type #{cloned_assoc[:type]})")
1804
1861
  end
1805
1862
 
1863
+ opts[:use_placeholder_loader] = !opts[:instance_specific] && !opts[:eager_graph]
1806
1864
  opts[:eager_block] = opts[:block] unless opts.include?(:eager_block)
1807
1865
  opts[:graph_join_type] ||= :left_outer
1808
1866
  opts[:order_eager_graph] = true unless opts.include?(:order_eager_graph)
@@ -1825,8 +1883,7 @@ module Sequel
1825
1883
  # Remove :class entry if it exists and is nil, to work with cached_fetch
1826
1884
  opts.delete(:class) unless opts[:class]
1827
1885
 
1828
- send(:"def_#{type}", opts)
1829
- def_association_instance_methods(opts)
1886
+ def_association(opts)
1830
1887
 
1831
1888
  orig_opts.delete(:clone)
1832
1889
  opts[:orig_class] = orig_opts[:class] || orig_opts[:class_name]
@@ -1899,6 +1956,12 @@ module Sequel
1899
1956
  Plugins.def_dataset_methods(self, [:eager, :eager_graph, :eager_graph_with_options, :association_join, :association_full_join, :association_inner_join, :association_left_join, :association_right_join])
1900
1957
 
1901
1958
  private
1959
+
1960
+ # The default value for the instance_specific option, if the association
1961
+ # could be instance specific and the :instance_specific option is not specified.
1962
+ def _association_instance_specific_default(_)
1963
+ true
1964
+ end
1902
1965
 
1903
1966
  # The module to use for the association's methods. Defaults to
1904
1967
  # the overridable_methods_module.
@@ -1910,7 +1973,22 @@ module Sequel
1910
1973
  # can be easily overridden in the class itself while allowing for
1911
1974
  # super to be called.
1912
1975
  def association_module_def(name, opts=OPTS, &block)
1913
- association_module(opts).send(:define_method, name, &block)
1976
+ mod = association_module(opts)
1977
+ mod.send(:define_method, name, &block)
1978
+ mod.send(:alias_method, name, name)
1979
+ end
1980
+
1981
+ # Add a method to the module included in the class, so the method
1982
+ # can be easily overridden in the class itself while allowing for
1983
+ # super to be called. This method allows passing keywords through
1984
+ # the defined methods.
1985
+ def association_module_delegate_def(name, opts, &block)
1986
+ mod = association_module(opts)
1987
+ mod.send(:define_method, name, &block)
1988
+ # :nocov:
1989
+ mod.send(:ruby2_keywords, name) if mod.respond_to?(:ruby2_keywords, true)
1990
+ # :nocov:
1991
+ mod.send(:alias_method, name, name)
1914
1992
  end
1915
1993
 
1916
1994
  # Add a private method to the module included in the class.
@@ -1919,6 +1997,13 @@ module Sequel
1919
1997
  association_module(opts).send(:private, name)
1920
1998
  end
1921
1999
 
2000
+ # Delegate to the type-specific association method to setup the
2001
+ # association, and define the association instance methods.
2002
+ def def_association(opts)
2003
+ send(:"def_#{opts[:type]}", opts)
2004
+ def_association_instance_methods(opts)
2005
+ end
2006
+
1922
2007
  # Adds the association method to the association methods module.
1923
2008
  def def_association_method(opts)
1924
2009
  association_module_def(opts.association_method, opts) do |dynamic_opts=OPTS, &block|
@@ -1944,15 +2029,13 @@ module Sequel
1944
2029
  opts[:setter_method] = :"#{opts[:name]}="
1945
2030
  end
1946
2031
 
1947
- association_module_def(opts.dataset_method, opts){_dataset(opts)}
2032
+ association_module_def(opts.dataset_method, opts){_dataset(opts)} unless opts[:no_dataset_method]
1948
2033
  if opts[:block]
1949
2034
  opts[:block_method] = Plugins.def_sequel_method(association_module(opts), "#{opts[:name]}_block", 1, &opts[:block])
1950
2035
  end
1951
- if opts[:dataset]
1952
- opts[:dataset_opt_arity] = opts[:dataset].arity == 0 ? 0 : 1
1953
- opts[:dataset_opt_method] = Plugins.def_sequel_method(association_module(opts), "#{opts[:name]}_dataset_opt", opts[:dataset_opt_arity], &opts[:dataset])
1954
- end
1955
- def_association_method(opts)
2036
+ opts[:dataset_opt_arity] = opts[:dataset].arity == 0 ? 0 : 1
2037
+ opts[:dataset_opt_method] = Plugins.def_sequel_method(association_module(opts), "#{opts[:name]}_dataset_opt", opts[:dataset_opt_arity], &opts[:dataset])
2038
+ def_association_method(opts) unless opts[:no_association_method]
1956
2039
 
1957
2040
  return if opts[:read_only]
1958
2041
 
@@ -1964,17 +2047,17 @@ module Sequel
1964
2047
 
1965
2048
  if adder = opts[:adder]
1966
2049
  association_module_private_def(opts[:_add_method], opts, &adder)
1967
- association_module_def(opts[:add_method], opts){|o,*args| add_associated_object(opts, o, *args)}
2050
+ association_module_delegate_def(opts[:add_method], opts){|o,*args| add_associated_object(opts, o, *args)}
1968
2051
  end
1969
2052
 
1970
2053
  if remover = opts[:remover]
1971
2054
  association_module_private_def(opts[:_remove_method], opts, &remover)
1972
- association_module_def(opts[:remove_method], opts){|o,*args| remove_associated_object(opts, o, *args)}
2055
+ association_module_delegate_def(opts[:remove_method], opts){|o,*args| remove_associated_object(opts, o, *args)}
1973
2056
  end
1974
2057
 
1975
2058
  if clearer = opts[:clearer]
1976
2059
  association_module_private_def(opts[:_remove_all_method], opts, &clearer)
1977
- association_module_def(opts[:remove_all_method], opts){|*args| remove_all_associated_objects(opts, *args)}
2060
+ association_module_delegate_def(opts[:remove_all_method], opts){|*args| remove_all_associated_objects(opts, *args)}
1978
2061
  end
1979
2062
  end
1980
2063
 
@@ -1996,7 +2079,7 @@ module Sequel
1996
2079
  raise(Error, "mismatched number of right keys: #{rcks.inspect} vs #{rcpks.inspect}") unless rcks.length == rcpks.length
1997
2080
  end
1998
2081
  opts[:uses_left_composite_keys] = lcks.length > 1
1999
- opts[:uses_right_composite_keys] = rcks.length > 1
2082
+ uses_rcks = opts[:uses_right_composite_keys] = rcks.length > 1
2000
2083
  opts[:cartesian_product_number] ||= one_through_one ? 0 : 1
2001
2084
  join_table = (opts[:join_table] ||= opts.default_join_table)
2002
2085
  opts[:left_key_alias] ||= opts.default_associated_key_alias
@@ -2005,8 +2088,75 @@ module Sequel
2005
2088
  opts[:after_load] ||= []
2006
2089
  opts[:after_load].unshift(:array_uniq!)
2007
2090
  end
2008
- opts[:dataset] ||= opts.association_dataset_proc
2009
- opts[:eager_loader] ||= opts.method(:default_eager_loader)
2091
+ if join_table_db = opts[:join_table_db]
2092
+ opts[:use_placeholder_loader] = false
2093
+ opts[:allow_eager_graph] = false
2094
+ opts[:allow_filtering_by] = false
2095
+ opts[:eager_limit_strategy] = nil
2096
+ join_table_ds = join_table_db.from(join_table)
2097
+ opts[:dataset] ||= proc do |r|
2098
+ vals = join_table_ds.where(lcks.zip(lcpks.map{|k| get_column_value(k)})).select_map(right)
2099
+ ds = r.associated_dataset.where(opts.right_primary_key => vals)
2100
+ if uses_rcks
2101
+ vals.delete_if{|v| v.any?(&:nil?)}
2102
+ else
2103
+ vals.delete(nil)
2104
+ end
2105
+ ds = ds.clone(:no_results=>true) if vals.empty?
2106
+ ds
2107
+ end
2108
+ opts[:eager_loader] ||= proc do |eo|
2109
+ h = eo[:id_map]
2110
+ assign_singular = opts.assign_singular?
2111
+ rpk = opts.right_primary_key
2112
+ name = opts[:name]
2113
+
2114
+ join_map = join_table_ds.where(left=>h.keys).select_hash_groups(right, left)
2115
+
2116
+ if uses_rcks
2117
+ join_map.delete_if{|v,| v.any?(&:nil?)}
2118
+ else
2119
+ join_map.delete(nil)
2120
+ end
2121
+
2122
+ eo = Hash[eo]
2123
+
2124
+ if join_map.empty?
2125
+ eo[:no_results] = true
2126
+ else
2127
+ join_map.each_value do |vs|
2128
+ vs.replace(vs.flat_map{|v| h[v]})
2129
+ vs.uniq!
2130
+ end
2131
+
2132
+ eo[:loader] = false
2133
+ eo[:right_keys] = join_map.keys
2134
+ end
2135
+
2136
+ opts[:model].eager_load_results(opts, eo) do |assoc_record|
2137
+ rpkv = if uses_rcks
2138
+ assoc_record.values.values_at(*rpk)
2139
+ else
2140
+ assoc_record.values[rpk]
2141
+ end
2142
+
2143
+ objects = join_map[rpkv]
2144
+
2145
+ if assign_singular
2146
+ objects.each do |object|
2147
+ object.associations[name] ||= assoc_record
2148
+ end
2149
+ else
2150
+ objects.each do |object|
2151
+ object.associations[name].push(assoc_record)
2152
+ end
2153
+ end
2154
+ end
2155
+ end
2156
+ else
2157
+ opts[:dataset] ||= opts.association_dataset_proc
2158
+ opts[:eager_loader] ||= opts.method(:default_eager_loader)
2159
+ end
2010
2160
 
2011
2161
  join_type = opts[:graph_join_type]
2012
2162
  select = opts[:graph_select]
@@ -2040,50 +2190,60 @@ module Sequel
2040
2190
  return if opts[:read_only]
2041
2191
 
2042
2192
  if one_through_one
2043
- opts[:setter] ||= proc do |o|
2044
- h = {}
2045
- lh = lcks.zip(lcpks.map{|k| get_column_value(k)})
2046
- jtds = _join_table_dataset(opts).where(lh)
2193
+ unless opts.has_key?(:setter)
2194
+ opts[:setter] = proc do |o|
2195
+ h = {}
2196
+ lh = lcks.zip(lcpks.map{|k| get_column_value(k)})
2197
+ jtds = _join_table_dataset(opts).where(lh)
2047
2198
 
2048
- checked_transaction do
2049
- current = jtds.first
2199
+ checked_transaction do
2200
+ current = jtds.first
2050
2201
 
2051
- if o
2052
- new_values = []
2053
- rcks.zip(opts.right_primary_key_methods).each{|k, pk| new_values << (h[k] = o.get_column_value(pk))}
2054
- end
2055
-
2056
- if current
2057
- current_values = rcks.map{|k| current[k]}
2058
- jtds = jtds.where(rcks.zip(current_values))
2059
2202
  if o
2060
- if current_values != new_values
2061
- jtds.update(h)
2203
+ new_values = []
2204
+ rcks.zip(opts.right_primary_key_methods).each{|k, pk| new_values << (h[k] = o.get_column_value(pk))}
2205
+ end
2206
+
2207
+ if current
2208
+ current_values = rcks.map{|k| current[k]}
2209
+ jtds = jtds.where(rcks.zip(current_values))
2210
+ if o
2211
+ if current_values != new_values
2212
+ jtds.update(h)
2213
+ end
2214
+ else
2215
+ jtds.delete
2062
2216
  end
2063
- else
2064
- jtds.delete
2217
+ elsif o
2218
+ lh.each{|k,v| h[k] = v}
2219
+ jtds.insert(h)
2065
2220
  end
2066
- elsif o
2067
- lh.each{|k,v| h[k] = v}
2068
- jtds.insert(h)
2069
2221
  end
2070
2222
  end
2071
2223
  end
2072
- opts[:_setter] = proc{|o| set_one_through_one_associated_object(opts, o)}
2224
+ if opts.fetch(:setter, true)
2225
+ opts[:_setter] = proc{|o| set_one_through_one_associated_object(opts, o)}
2226
+ end
2073
2227
  else
2074
- opts[:adder] ||= proc do |o|
2075
- h = {}
2076
- lcks.zip(lcpks).each{|k, pk| h[k] = get_column_value(pk)}
2077
- rcks.zip(opts.right_primary_key_methods).each{|k, pk| h[k] = o.get_column_value(pk)}
2078
- _join_table_dataset(opts).insert(h)
2228
+ unless opts.has_key?(:adder)
2229
+ opts[:adder] = proc do |o|
2230
+ h = {}
2231
+ lcks.zip(lcpks).each{|k, pk| h[k] = get_column_value(pk)}
2232
+ rcks.zip(opts.right_primary_key_methods).each{|k, pk| h[k] = o.get_column_value(pk)}
2233
+ _join_table_dataset(opts).insert(h)
2234
+ end
2079
2235
  end
2080
2236
 
2081
- opts[:remover] ||= proc do |o|
2082
- _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
2237
+ unless opts.has_key?(:remover)
2238
+ opts[:remover] = proc do |o|
2239
+ _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
2240
+ end
2083
2241
  end
2084
2242
 
2085
- opts[:clearer] ||= proc do
2086
- _join_table_dataset(opts).where(lcks.zip(lcpks.map{|k| get_column_value(k)})).delete
2243
+ unless opts.has_key?(:clearer)
2244
+ opts[:clearer] = proc do
2245
+ _join_table_dataset(opts).where(lcks.zip(lcpks.map{|k| get_column_value(k)})).delete
2246
+ end
2087
2247
  end
2088
2248
  end
2089
2249
  end
@@ -2122,9 +2282,7 @@ module Sequel
2122
2282
 
2123
2283
  eager_load_results(opts, eo) do |assoc_record|
2124
2284
  hash_key = uses_cks ? pk_meths.map{|k| assoc_record.get_column_value(k)} : assoc_record.get_column_value(opts.primary_key_method)
2125
- if objects = h[hash_key]
2126
- objects.each{|object| object.associations[name] = assoc_record}
2127
- end
2285
+ h[hash_key].each{|object| object.associations[name] = assoc_record}
2128
2286
  end
2129
2287
  end
2130
2288
 
@@ -2142,8 +2300,12 @@ module Sequel
2142
2300
 
2143
2301
  return if opts[:read_only]
2144
2302
 
2145
- opts[:setter] ||= proc{|o| cks.zip(opts.primary_key_methods).each{|k, pk| set_column_value(:"#{k}=", (o.get_column_value(pk) if o))}}
2146
- opts[:_setter] = proc{|o| set_associated_object(opts, o)}
2303
+ unless opts.has_key?(:setter)
2304
+ opts[:setter] = proc{|o| cks.zip(opts.primary_key_methods).each{|k, pk| set_column_value(:"#{k}=", (o.get_column_value(pk) if o))}}
2305
+ end
2306
+ if opts.fetch(:setter, true)
2307
+ opts[:_setter] = proc{|o| set_associated_object(opts, o)}
2308
+ end
2147
2309
  end
2148
2310
 
2149
2311
  # Configures one_to_many and one_to_one association reflections and adds the related association methods
@@ -2171,7 +2333,7 @@ module Sequel
2171
2333
  eager_load_results(opts, eo) do |assoc_record|
2172
2334
  assoc_record.values.delete(delete_rn) if delete_rn
2173
2335
  hash_key = uses_cks ? km.map{|k| assoc_record.get_column_value(k)} : assoc_record.get_column_value(km)
2174
- next unless objects = h[hash_key]
2336
+ objects = h[hash_key]
2175
2337
  if assign_singular
2176
2338
  objects.each do |object|
2177
2339
  unless object.associations[name]
@@ -2210,49 +2372,59 @@ module Sequel
2210
2372
  cks.each{|k| ck_nil_hash[k] = nil}
2211
2373
 
2212
2374
  if one_to_one
2213
- opts[:setter] ||= proc do |o|
2214
- up_ds = _apply_association_options(opts, opts.associated_dataset.where(cks.zip(cpks.map{|k| get_column_value(k)})))
2375
+ unless opts.has_key?(:setter)
2376
+ opts[:setter] = proc do |o|
2377
+ up_ds = _apply_association_options(opts, opts.associated_dataset.where(cks.zip(cpks.map{|k| get_column_value(k)})))
2215
2378
 
2216
- 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)))
2217
- if old = up_ds.first
2218
- cks.each{|k| old.set_column_value(:"#{k}=", nil)}
2379
+ 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)))
2380
+ if old = up_ds.first
2381
+ cks.each{|k| old.set_column_value(:"#{k}=", nil)}
2382
+ end
2383
+ save_old = true
2219
2384
  end
2220
- save_old = true
2221
- end
2222
2385
 
2223
- if o
2224
- if !o.new? && !save_old
2225
- up_ds = up_ds.exclude(o.pk_hash)
2386
+ if o
2387
+ if !o.new? && !save_old
2388
+ up_ds = up_ds.exclude(o.pk_hash)
2389
+ end
2390
+ cks.zip(cpks).each{|k, pk| o.set_column_value(:"#{k}=", get_column_value(pk))}
2226
2391
  end
2227
- cks.zip(cpks).each{|k, pk| o.set_column_value(:"#{k}=", get_column_value(pk))}
2228
- end
2229
2392
 
2230
- checked_transaction do
2231
- if save_old
2232
- old.save(save_opts) || raise(Sequel::Error, "invalid previously associated object, cannot save") if old
2233
- else
2234
- up_ds.skip_limit_check.update(ck_nil_hash)
2235
- end
2393
+ checked_transaction do
2394
+ if save_old
2395
+ old.save(save_opts) || raise(Sequel::Error, "invalid previously associated object, cannot save") if old
2396
+ else
2397
+ up_ds.skip_limit_check.update(ck_nil_hash)
2398
+ end
2236
2399
 
2237
- o.save(save_opts) || raise(Sequel::Error, "invalid associated object, cannot save") if o
2400
+ o.save(save_opts) || raise(Sequel::Error, "invalid associated object, cannot save") if o
2401
+ end
2238
2402
  end
2239
2403
  end
2240
- opts[:_setter] = proc{|o| set_one_to_one_associated_object(opts, o)}
2404
+ if opts.fetch(:setter, true)
2405
+ opts[:_setter] = proc{|o| set_one_to_one_associated_object(opts, o)}
2406
+ end
2241
2407
  else
2242
2408
  save_opts[:raise_on_failure] = opts[:raise_on_save_failure] != false
2243
2409
 
2244
- opts[:adder] ||= proc do |o|
2245
- cks.zip(cpks).each{|k, pk| o.set_column_value(:"#{k}=", get_column_value(pk))}
2246
- o.save(save_opts)
2410
+ unless opts.has_key?(:adder)
2411
+ opts[:adder] = proc do |o|
2412
+ cks.zip(cpks).each{|k, pk| o.set_column_value(:"#{k}=", get_column_value(pk))}
2413
+ o.save(save_opts)
2414
+ end
2247
2415
  end
2248
2416
 
2249
- opts[:remover] ||= proc do |o|
2250
- cks.each{|k| o.set_column_value(:"#{k}=", nil)}
2251
- o.save(save_opts)
2417
+ unless opts.has_key?(:remover)
2418
+ opts[:remover] = proc do |o|
2419
+ cks.each{|k| o.set_column_value(:"#{k}=", nil)}
2420
+ o.save(save_opts)
2421
+ end
2252
2422
  end
2253
2423
 
2254
- opts[:clearer] ||= proc do
2255
- _apply_association_options(opts, opts.associated_dataset.where(cks.zip(cpks.map{|k| get_column_value(k)}))).update(ck_nil_hash)
2424
+ unless opts.has_key?(:clearer)
2425
+ opts[:clearer] = proc do
2426
+ _apply_association_options(opts, opts.associated_dataset.where(cks.zip(cpks.map{|k| get_column_value(k)}))).update(ck_nil_hash)
2427
+ end
2256
2428
  end
2257
2429
  end
2258
2430
  end
@@ -2346,7 +2518,7 @@ module Sequel
2346
2518
 
2347
2519
  # Dataset for the join table of the given many to many association reflection
2348
2520
  def _join_table_dataset(opts)
2349
- ds = model.db.from(opts.join_table_source)
2521
+ ds = (opts[:join_table_db] || model.db).from(opts.join_table_source)
2350
2522
  opts[:join_table_block] ? opts[:join_table_block].call(ds) : ds
2351
2523
  end
2352
2524
 
@@ -2367,7 +2539,12 @@ module Sequel
2367
2539
  if loader = _associated_object_loader(opts, dynamic_opts)
2368
2540
  loader.all(*opts.predicate_key_values(self))
2369
2541
  else
2370
- _associated_dataset(opts, dynamic_opts).all
2542
+ ds = _associated_dataset(opts, dynamic_opts)
2543
+ if ds.opts[:no_results]
2544
+ []
2545
+ else
2546
+ ds.all
2547
+ end
2371
2548
  end
2372
2549
  end
2373
2550
 
@@ -2408,6 +2585,9 @@ module Sequel
2408
2585
  run_association_callbacks(opts, :after_add, o)
2409
2586
  o
2410
2587
  end
2588
+ # :nocov:
2589
+ ruby2_keywords(:add_associated_object) if respond_to?(:ruby2_keywords, true)
2590
+ # :nocov:
2411
2591
 
2412
2592
  # Add/Set the current object to/as the given object's reciprocal association.
2413
2593
  def add_reciprocal_object(opts, o)
@@ -2550,6 +2730,9 @@ module Sequel
2550
2730
  associations[opts[:name]] = []
2551
2731
  ret
2552
2732
  end
2733
+ # :nocov:
2734
+ ruby2_keywords(:remove_all_associated_objects) if respond_to?(:ruby2_keywords, true)
2735
+ # :nocov:
2553
2736
 
2554
2737
  # Remove the given associated object from the given association
2555
2738
  def remove_associated_object(opts, o, *args)
@@ -2571,6 +2754,9 @@ module Sequel
2571
2754
  run_association_callbacks(opts, :after_remove, o)
2572
2755
  o
2573
2756
  end
2757
+ # :nocov:
2758
+ ruby2_keywords(:remove_associated_object) if respond_to?(:ruby2_keywords, true)
2759
+ # :nocov:
2574
2760
 
2575
2761
  # Check that the object from the associated table specified by the primary key
2576
2762
  # is currently associated to the receiver. If it is associated, return the object, otherwise
@@ -2819,6 +3005,8 @@ module Sequel
2819
3005
  (multiple = ((op == :IN || op == :'NOT IN') && ((is_ds = r.is_a?(Sequel::Dataset)) || (r.respond_to?(:all?) && r.all?{|x| x.is_a?(Sequel::Model)})))))
2820
3006
  l = args[0]
2821
3007
  if ar = model.association_reflections[l]
3008
+ raise Error, "filtering by associations is not allowed for #{ar.inspect}" if ar[:allow_filtering_by] == false
3009
+
2822
3010
  if multiple
2823
3011
  klass = ar.associated_class
2824
3012
  if is_ds
@@ -2966,8 +3154,10 @@ module Sequel
2966
3154
  # dataset. If that association also has dependent associations, instead of a callable object,
2967
3155
  # use a hash with the callable object being the key, and the dependent association(s) as the value.
2968
3156
  #
2969
- # You can specify an alias by providing a Sequel::SQL::AliasedExpression object instead of
2970
- # an a Symbol for the assocation name.
3157
+ # You can specify an custom alias and/or join type on a per-association basis by providing an
3158
+ # Sequel::SQL::AliasedExpression object instead of an a Symbol for the association name.
3159
+ #
3160
+ # You cannot mix calls to +eager_graph+ and +graph+ on the same dataset.
2971
3161
  #
2972
3162
  # Examples:
2973
3163
  #
@@ -2983,6 +3173,14 @@ module Sequel
2983
3173
  # # FROM albums
2984
3174
  # # LEFT OUTER JOIN artists AS a ON (a.id = albums.artist_id)
2985
3175
  #
3176
+ # # For each album, eager_graph load the artist, using a specified alias
3177
+ # # and custom join type
3178
+ #
3179
+ # Album.eager_graph(Sequel[:artist].as(:a, join_type: :inner)).all
3180
+ # # SELECT ...
3181
+ # # FROM albums
3182
+ # # INNER JOIN artists AS a ON (a.id = albums.artist_id)
3183
+ #
2986
3184
  # # For each album, eager_graph load the artist and genre
2987
3185
  # Album.eager_graph(:artist, :genre).all
2988
3186
  # Album.eager_graph(:artist).eager_graph(:genre).all
@@ -3056,6 +3254,8 @@ module Sequel
3056
3254
  # significantly slower in some cases (perhaps even the majority of cases), so you should
3057
3255
  # only use this if you have benchmarked that it is faster for your use cases.
3058
3256
  def eager_graph_with_options(associations, opts=OPTS)
3257
+ return self if associations.empty?
3258
+
3059
3259
  opts = opts.dup unless opts.frozen?
3060
3260
  associations = [associations] unless associations.is_a?(Array)
3061
3261
  ds = if eg = @opts[:eager_graph]
@@ -3125,11 +3325,16 @@ module Sequel
3125
3325
  # ta :: table_alias used for the parent association
3126
3326
  # requirements :: an array, used as a stack for requirements
3127
3327
  # r :: association reflection for the current association, or an SQL::AliasedExpression
3128
- # with the reflection as the expression and the alias base as the aliaz.
3328
+ # with the reflection as the expression, the alias base as the alias (or nil to
3329
+ # use the default alias), and an optional hash with a :join_type entry as the columns
3330
+ # to use a custom join type.
3129
3331
  # *associations :: any associations dependent on this one
3130
3332
  def eager_graph_association(ds, model, ta, requirements, r, *associations)
3131
3333
  if r.is_a?(SQL::AliasedExpression)
3132
3334
  alias_base = r.alias
3335
+ if r.columns.is_a?(Hash)
3336
+ join_type = r.columns[:join_type]
3337
+ end
3133
3338
  r = r.expression
3134
3339
  else
3135
3340
  alias_base = r[:graph_alias_base]
@@ -3152,7 +3357,7 @@ module Sequel
3152
3357
  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]}"
3153
3358
  end
3154
3359
 
3155
- 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=>local_opts[:join_type], :join_only=>local_opts[:join_only], :limit_strategy=>limit_strategy, :from_self_alias=>ds.opts[:eager_graph][:master])
3360
+ 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])
3156
3361
  if r[:order_eager_graph] && (order = r.fetch(:graph_order, r[:order]))
3157
3362
  ds = ds.order_append(*qualified_expression(order, assoc_table_alias))
3158
3363
  end
@@ -3177,7 +3382,6 @@ module Sequel
3177
3382
  # requirements :: an array, used as a stack for requirements
3178
3383
  # *associations :: the associations to add to the graph
3179
3384
  def eager_graph_associations(ds, model, ta, requirements, *associations)
3180
- return ds if associations.empty?
3181
3385
  associations.flatten.each do |association|
3182
3386
  ds = case association
3183
3387
  when Symbol, SQL::AliasedExpression
@@ -3298,7 +3502,7 @@ module Sequel
3298
3502
  # Allow associations that are eagerly graphed to be specified as an SQL::AliasedExpression, for
3299
3503
  # per-call determining of the alias base.
3300
3504
  def eager_graph_check_association(model, association)
3301
- if association.is_a?(SQL::AliasedExpression)
3505
+ reflection = if association.is_a?(SQL::AliasedExpression)
3302
3506
  expr = association.expression
3303
3507
  if expr.is_a?(SQL::Identifier)
3304
3508
  expr = expr.value
@@ -3307,10 +3511,17 @@ module Sequel
3307
3511
  end
3308
3512
  end
3309
3513
 
3310
- SQL::AliasedExpression.new(check_association(model, expr), association.alias)
3514
+ check_reflection = check_association(model, expr)
3515
+ SQL::AliasedExpression.new(check_reflection, association.alias || expr, association.columns)
3311
3516
  else
3312
- check_association(model, association)
3517
+ check_reflection = check_association(model, association)
3313
3518
  end
3519
+
3520
+ if check_reflection && check_reflection[:allow_eager_graph] == false
3521
+ raise Error, "eager_graph not allowed for #{reflection.inspect}"
3522
+ end
3523
+
3524
+ reflection
3314
3525
  end
3315
3526
 
3316
3527
  # The EagerGraphLoader instance used for converting eager_graph results.
@@ -3321,15 +3532,30 @@ module Sequel
3321
3532
  egl.dup
3322
3533
  end
3323
3534
 
3324
- # Eagerly load all specified associations
3535
+ # Eagerly load all specified associations.
3325
3536
  def eager_load(a, eager_assoc=@opts[:eager])
3326
3537
  return if a.empty?
3538
+
3539
+ # Reflections for all associations to eager load
3540
+ reflections = eager_assoc.keys.map{|assoc| model.association_reflection(assoc) || (raise Sequel::UndefinedAssociation, "Model: #{self}, Association: #{assoc}")}
3541
+
3542
+ perform_eager_loads(prepare_eager_load(a, reflections, eager_assoc))
3543
+
3544
+ reflections.each do |r|
3545
+ a.each{|object| object.send(:run_association_callbacks, r, :after_load, object.associations[r[:name]])} if r[:after_load]
3546
+ end
3547
+
3548
+ nil
3549
+ end
3550
+
3551
+ # Prepare a hash loaders and eager options which will be used to implement the eager loading.
3552
+ def prepare_eager_load(a, reflections, eager_assoc)
3553
+ eager_load_data = {}
3554
+
3327
3555
  # Key is foreign/primary key name symbol.
3328
3556
  # Value is hash with keys being foreign/primary key values (generally integers)
3329
3557
  # and values being an array of current model objects with that specific foreign/primary key
3330
3558
  key_hash = {}
3331
- # Reflections for all associations to eager load
3332
- reflections = eager_assoc.keys.map{|assoc| model.association_reflection(assoc) || (raise Sequel::UndefinedAssociation, "Model: #{self}, Association: #{assoc}")}
3333
3559
 
3334
3560
  # Populate the key_hash entry for each association being eagerly loaded
3335
3561
  reflections.each do |r|
@@ -3360,7 +3586,6 @@ module Sequel
3360
3586
  id_map = nil
3361
3587
  end
3362
3588
 
3363
- loader = r[:eager_loader]
3364
3589
  associations = eager_assoc[r[:name]]
3365
3590
  if associations.respond_to?(:call)
3366
3591
  eager_block = associations
@@ -3368,9 +3593,23 @@ module Sequel
3368
3593
  elsif associations.is_a?(Hash) && associations.length == 1 && (pr_assoc = associations.to_a.first) && pr_assoc.first.respond_to?(:call)
3369
3594
  eager_block, associations = pr_assoc
3370
3595
  end
3371
- loader.call(:key_hash=>key_hash, :rows=>a, :associations=>associations, :self=>self, :eager_block=>eager_block, :id_map=>id_map)
3372
- a.each{|object| object.send(:run_association_callbacks, r, :after_load, object.associations[r[:name]])} if r[:after_load]
3373
- end
3596
+
3597
+ eager_load_data[r[:eager_loader]] = {:key_hash=>key_hash, :rows=>a, :associations=>associations, :self=>self, :eager_block=>eager_block, :id_map=>id_map}
3598
+ end
3599
+
3600
+ eager_load_data
3601
+ end
3602
+
3603
+ # Using the hash of loaders and eager options, perform the eager loading.
3604
+ def perform_eager_loads(eager_load_data)
3605
+ eager_load_data.map do |loader, eo|
3606
+ perform_eager_load(loader, eo)
3607
+ end
3608
+ end
3609
+
3610
+ # Perform eager loading for a single association using the loader and eager options.
3611
+ def perform_eager_load(loader, eo)
3612
+ loader.call(eo)
3374
3613
  end
3375
3614
 
3376
3615
  # Return a subquery expression for filering by a many_to_many association