sequel 5.20.0 → 5.49.0

Sign up to get free protection for your applications and to get access to all the features.
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