sequel 5.8.0 → 5.38.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (510) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +409 -1795
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  5. data/bin/sequel +4 -0
  6. data/doc/advanced_associations.rdoc +136 -18
  7. data/doc/association_basics.rdoc +10 -5
  8. data/doc/cheat_sheet.rdoc +1 -0
  9. data/doc/code_order.rdoc +12 -2
  10. data/doc/dataset_filtering.rdoc +17 -2
  11. data/doc/mass_assignment.rdoc +3 -3
  12. data/doc/model_dataset_method_design.rdoc +1 -1
  13. data/doc/model_plugins.rdoc +1 -1
  14. data/doc/opening_databases.rdoc +30 -8
  15. data/doc/postgresql.rdoc +107 -2
  16. data/doc/release_notes/5.10.0.txt +84 -0
  17. data/doc/release_notes/5.11.0.txt +83 -0
  18. data/doc/release_notes/5.12.0.txt +141 -0
  19. data/doc/release_notes/5.13.0.txt +27 -0
  20. data/doc/release_notes/5.14.0.txt +63 -0
  21. data/doc/release_notes/5.15.0.txt +39 -0
  22. data/doc/release_notes/5.16.0.txt +110 -0
  23. data/doc/release_notes/5.17.0.txt +31 -0
  24. data/doc/release_notes/5.18.0.txt +69 -0
  25. data/doc/release_notes/5.19.0.txt +28 -0
  26. data/doc/release_notes/5.20.0.txt +89 -0
  27. data/doc/release_notes/5.21.0.txt +87 -0
  28. data/doc/release_notes/5.22.0.txt +48 -0
  29. data/doc/release_notes/5.23.0.txt +56 -0
  30. data/doc/release_notes/5.24.0.txt +56 -0
  31. data/doc/release_notes/5.25.0.txt +32 -0
  32. data/doc/release_notes/5.26.0.txt +35 -0
  33. data/doc/release_notes/5.27.0.txt +21 -0
  34. data/doc/release_notes/5.28.0.txt +16 -0
  35. data/doc/release_notes/5.29.0.txt +22 -0
  36. data/doc/release_notes/5.30.0.txt +20 -0
  37. data/doc/release_notes/5.31.0.txt +148 -0
  38. data/doc/release_notes/5.32.0.txt +46 -0
  39. data/doc/release_notes/5.33.0.txt +24 -0
  40. data/doc/release_notes/5.34.0.txt +40 -0
  41. data/doc/release_notes/5.35.0.txt +56 -0
  42. data/doc/release_notes/5.36.0.txt +60 -0
  43. data/doc/release_notes/5.37.0.txt +30 -0
  44. data/doc/release_notes/5.38.0.txt +28 -0
  45. data/doc/release_notes/5.9.0.txt +99 -0
  46. data/doc/security.rdoc +10 -0
  47. data/doc/sharding.rdoc +42 -28
  48. data/doc/sql.rdoc +12 -0
  49. data/doc/testing.rdoc +24 -17
  50. data/doc/transactions.rdoc +78 -0
  51. data/doc/validations.rdoc +2 -2
  52. data/lib/sequel/adapters/ado.rb +26 -18
  53. data/lib/sequel/adapters/ado/access.rb +2 -2
  54. data/lib/sequel/adapters/ado/mssql.rb +5 -8
  55. data/lib/sequel/adapters/amalgalite.rb +1 -1
  56. data/lib/sequel/adapters/jdbc.rb +71 -27
  57. data/lib/sequel/adapters/jdbc/mysql.rb +6 -6
  58. data/lib/sequel/adapters/jdbc/oracle.rb +7 -6
  59. data/lib/sequel/adapters/jdbc/postgresql.rb +17 -28
  60. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +5 -6
  61. data/lib/sequel/adapters/jdbc/sqlite.rb +33 -2
  62. data/lib/sequel/adapters/jdbc/sqlserver.rb +4 -3
  63. data/lib/sequel/adapters/jdbc/transactions.rb +14 -28
  64. data/lib/sequel/adapters/mysql.rb +14 -15
  65. data/lib/sequel/adapters/mysql2.rb +5 -3
  66. data/lib/sequel/adapters/odbc.rb +4 -6
  67. data/lib/sequel/adapters/oracle.rb +7 -7
  68. data/lib/sequel/adapters/postgres.rb +52 -16
  69. data/lib/sequel/adapters/shared/access.rb +16 -12
  70. data/lib/sequel/adapters/shared/db2.rb +5 -0
  71. data/lib/sequel/adapters/shared/mssql.rb +41 -18
  72. data/lib/sequel/adapters/shared/mysql.rb +66 -19
  73. data/lib/sequel/adapters/shared/oracle.rb +29 -23
  74. data/lib/sequel/adapters/shared/postgres.rb +341 -95
  75. data/lib/sequel/adapters/shared/sqlanywhere.rb +23 -10
  76. data/lib/sequel/adapters/shared/sqlite.rb +174 -21
  77. data/lib/sequel/adapters/sqlanywhere.rb +33 -17
  78. data/lib/sequel/adapters/sqlite.rb +78 -68
  79. data/lib/sequel/adapters/tinytds.rb +14 -6
  80. data/lib/sequel/adapters/utils/emulate_offset_with_reverse_and_count.rb +2 -5
  81. data/lib/sequel/adapters/utils/mysql_mysql2.rb +5 -1
  82. data/lib/sequel/connection_pool.rb +2 -6
  83. data/lib/sequel/connection_pool/sharded_single.rb +7 -4
  84. data/lib/sequel/connection_pool/sharded_threaded.rb +32 -21
  85. data/lib/sequel/connection_pool/single.rb +1 -1
  86. data/lib/sequel/connection_pool/threaded.rb +26 -11
  87. data/lib/sequel/core.rb +327 -319
  88. data/lib/sequel/database/connecting.rb +7 -8
  89. data/lib/sequel/database/logging.rb +7 -1
  90. data/lib/sequel/database/misc.rb +68 -34
  91. data/lib/sequel/database/query.rb +6 -4
  92. data/lib/sequel/database/schema_generator.rb +31 -11
  93. data/lib/sequel/database/schema_methods.rb +32 -22
  94. data/lib/sequel/database/transactions.rb +129 -25
  95. data/lib/sequel/dataset.rb +4 -2
  96. data/lib/sequel/dataset/actions.rb +34 -23
  97. data/lib/sequel/dataset/features.rb +34 -0
  98. data/lib/sequel/dataset/graph.rb +27 -11
  99. data/lib/sequel/dataset/misc.rb +17 -3
  100. data/lib/sequel/dataset/placeholder_literalizer.rb +50 -21
  101. data/lib/sequel/dataset/prepared_statements.rb +96 -26
  102. data/lib/sequel/dataset/query.rb +43 -8
  103. data/lib/sequel/dataset/sql.rb +189 -41
  104. data/lib/sequel/deprecated.rb +3 -1
  105. data/lib/sequel/exceptions.rb +2 -0
  106. data/lib/sequel/extensions/_pretty_table.rb +1 -2
  107. data/lib/sequel/extensions/any_not_empty.rb +45 -0
  108. data/lib/sequel/extensions/caller_logging.rb +79 -0
  109. data/lib/sequel/extensions/columns_introspection.rb +1 -2
  110. data/lib/sequel/extensions/connection_expiration.rb +6 -6
  111. data/lib/sequel/extensions/connection_validator.rb +7 -6
  112. data/lib/sequel/extensions/constant_sql_override.rb +65 -0
  113. data/lib/sequel/extensions/constraint_validations.rb +53 -28
  114. data/lib/sequel/extensions/core_refinements.rb +2 -0
  115. data/lib/sequel/extensions/duplicate_columns_handler.rb +2 -0
  116. data/lib/sequel/extensions/escaped_like.rb +100 -0
  117. data/lib/sequel/extensions/eval_inspect.rb +3 -1
  118. data/lib/sequel/extensions/exclude_or_null.rb +68 -0
  119. data/lib/sequel/extensions/fiber_concurrency.rb +24 -0
  120. data/lib/sequel/extensions/index_caching.rb +9 -7
  121. data/lib/sequel/extensions/integer64.rb +3 -1
  122. data/lib/sequel/extensions/looser_typecasting.rb +3 -3
  123. data/lib/sequel/extensions/migration.rb +13 -6
  124. data/lib/sequel/extensions/named_timezones.rb +84 -23
  125. data/lib/sequel/extensions/pg_array.rb +87 -79
  126. data/lib/sequel/extensions/pg_array_ops.rb +14 -6
  127. data/lib/sequel/extensions/pg_enum.rb +34 -18
  128. data/lib/sequel/extensions/pg_extended_date_support.rb +34 -14
  129. data/lib/sequel/extensions/pg_hstore.rb +6 -0
  130. data/lib/sequel/extensions/pg_hstore_ops.rb +2 -0
  131. data/lib/sequel/extensions/pg_inet.rb +15 -5
  132. data/lib/sequel/extensions/pg_interval.rb +2 -0
  133. data/lib/sequel/extensions/pg_json.rb +387 -123
  134. data/lib/sequel/extensions/pg_json_ops.rb +168 -0
  135. data/lib/sequel/extensions/pg_range.rb +20 -10
  136. data/lib/sequel/extensions/pg_range_ops.rb +2 -0
  137. data/lib/sequel/extensions/pg_row.rb +3 -2
  138. data/lib/sequel/extensions/pg_row_ops.rb +24 -0
  139. data/lib/sequel/extensions/pg_static_cache_updater.rb +2 -2
  140. data/lib/sequel/extensions/pg_timestamptz.rb +2 -0
  141. data/lib/sequel/extensions/query.rb +1 -0
  142. data/lib/sequel/extensions/run_transaction_hooks.rb +72 -0
  143. data/lib/sequel/extensions/s.rb +2 -0
  144. data/lib/sequel/extensions/schema_dumper.rb +13 -7
  145. data/lib/sequel/extensions/sequel_4_dataset_methods.rb +4 -2
  146. data/lib/sequel/extensions/server_block.rb +18 -7
  147. data/lib/sequel/extensions/sql_comments.rb +2 -2
  148. data/lib/sequel/extensions/symbol_aref_refinement.rb +2 -0
  149. data/lib/sequel/extensions/symbol_as_refinement.rb +2 -0
  150. data/lib/sequel/extensions/to_dot.rb +9 -3
  151. data/lib/sequel/model.rb +3 -1
  152. data/lib/sequel/model/associations.rb +403 -69
  153. data/lib/sequel/model/base.rb +170 -90
  154. data/lib/sequel/model/plugins.rb +105 -0
  155. data/lib/sequel/plugins/after_initialize.rb +1 -1
  156. data/lib/sequel/plugins/association_dependencies.rb +3 -3
  157. data/lib/sequel/plugins/association_lazy_eager_option.rb +66 -0
  158. data/lib/sequel/plugins/association_multi_add_remove.rb +85 -0
  159. data/lib/sequel/plugins/association_pks.rb +74 -22
  160. data/lib/sequel/plugins/association_proxies.rb +6 -2
  161. data/lib/sequel/plugins/auto_validations.rb +36 -17
  162. data/lib/sequel/plugins/blacklist_security.rb +1 -2
  163. data/lib/sequel/plugins/boolean_subsets.rb +4 -1
  164. data/lib/sequel/plugins/caching.rb +3 -0
  165. data/lib/sequel/plugins/class_table_inheritance.rb +62 -34
  166. data/lib/sequel/plugins/composition.rb +13 -9
  167. data/lib/sequel/plugins/csv_serializer.rb +28 -9
  168. data/lib/sequel/plugins/defaults_setter.rb +2 -2
  169. data/lib/sequel/plugins/dirty.rb +60 -22
  170. data/lib/sequel/plugins/eager_graph_eager.rb +139 -0
  171. data/lib/sequel/plugins/empty_failure_backtraces.rb +38 -0
  172. data/lib/sequel/plugins/finder.rb +2 -2
  173. data/lib/sequel/plugins/forbid_lazy_load.rb +216 -0
  174. data/lib/sequel/plugins/hook_class_methods.rb +17 -5
  175. data/lib/sequel/plugins/insert_conflict.rb +72 -0
  176. data/lib/sequel/plugins/instance_specific_default.rb +113 -0
  177. data/lib/sequel/plugins/inverted_subsets.rb +2 -2
  178. data/lib/sequel/plugins/json_serializer.rb +21 -14
  179. data/lib/sequel/plugins/lazy_attributes.rb +1 -1
  180. data/lib/sequel/plugins/list.rb +22 -10
  181. data/lib/sequel/plugins/many_through_many.rb +1 -1
  182. data/lib/sequel/plugins/nested_attributes.rb +27 -5
  183. data/lib/sequel/plugins/pg_array_associations.rb +12 -9
  184. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +149 -61
  185. data/lib/sequel/plugins/prepared_statements.rb +6 -12
  186. data/lib/sequel/plugins/prepared_statements_safe.rb +1 -3
  187. data/lib/sequel/plugins/rcte_tree.rb +20 -22
  188. data/lib/sequel/plugins/sharding.rb +13 -7
  189. data/lib/sequel/plugins/single_table_inheritance.rb +20 -15
  190. data/lib/sequel/plugins/skip_saving_columns.rb +108 -0
  191. data/lib/sequel/plugins/static_cache.rb +36 -17
  192. data/lib/sequel/plugins/static_cache_cache.rb +53 -0
  193. data/lib/sequel/plugins/string_stripper.rb +1 -1
  194. data/lib/sequel/plugins/subclasses.rb +2 -0
  195. data/lib/sequel/plugins/subset_conditions.rb +2 -2
  196. data/lib/sequel/plugins/tactical_eager_loading.rb +73 -2
  197. data/lib/sequel/plugins/throw_failures.rb +110 -0
  198. data/lib/sequel/plugins/tree.rb +49 -31
  199. data/lib/sequel/plugins/typecast_on_load.rb +3 -2
  200. data/lib/sequel/plugins/validation_class_methods.rb +11 -5
  201. data/lib/sequel/plugins/validation_helpers.rb +2 -2
  202. data/lib/sequel/sql.rb +120 -30
  203. data/lib/sequel/timezones.rb +55 -14
  204. data/lib/sequel/version.rb +6 -1
  205. metadata +101 -361
  206. data/Rakefile +0 -151
  207. data/doc/release_notes/4.0.0.txt +0 -262
  208. data/doc/release_notes/4.1.0.txt +0 -85
  209. data/doc/release_notes/4.10.0.txt +0 -226
  210. data/doc/release_notes/4.11.0.txt +0 -147
  211. data/doc/release_notes/4.12.0.txt +0 -105
  212. data/doc/release_notes/4.13.0.txt +0 -169
  213. data/doc/release_notes/4.14.0.txt +0 -68
  214. data/doc/release_notes/4.15.0.txt +0 -56
  215. data/doc/release_notes/4.16.0.txt +0 -36
  216. data/doc/release_notes/4.17.0.txt +0 -38
  217. data/doc/release_notes/4.18.0.txt +0 -36
  218. data/doc/release_notes/4.19.0.txt +0 -45
  219. data/doc/release_notes/4.2.0.txt +0 -129
  220. data/doc/release_notes/4.20.0.txt +0 -79
  221. data/doc/release_notes/4.21.0.txt +0 -94
  222. data/doc/release_notes/4.22.0.txt +0 -72
  223. data/doc/release_notes/4.23.0.txt +0 -65
  224. data/doc/release_notes/4.24.0.txt +0 -99
  225. data/doc/release_notes/4.25.0.txt +0 -181
  226. data/doc/release_notes/4.26.0.txt +0 -44
  227. data/doc/release_notes/4.27.0.txt +0 -78
  228. data/doc/release_notes/4.28.0.txt +0 -57
  229. data/doc/release_notes/4.29.0.txt +0 -41
  230. data/doc/release_notes/4.3.0.txt +0 -40
  231. data/doc/release_notes/4.30.0.txt +0 -37
  232. data/doc/release_notes/4.31.0.txt +0 -57
  233. data/doc/release_notes/4.32.0.txt +0 -132
  234. data/doc/release_notes/4.33.0.txt +0 -88
  235. data/doc/release_notes/4.34.0.txt +0 -86
  236. data/doc/release_notes/4.35.0.txt +0 -130
  237. data/doc/release_notes/4.36.0.txt +0 -116
  238. data/doc/release_notes/4.37.0.txt +0 -50
  239. data/doc/release_notes/4.38.0.txt +0 -67
  240. data/doc/release_notes/4.39.0.txt +0 -127
  241. data/doc/release_notes/4.4.0.txt +0 -92
  242. data/doc/release_notes/4.40.0.txt +0 -179
  243. data/doc/release_notes/4.41.0.txt +0 -77
  244. data/doc/release_notes/4.42.0.txt +0 -221
  245. data/doc/release_notes/4.43.0.txt +0 -87
  246. data/doc/release_notes/4.44.0.txt +0 -125
  247. data/doc/release_notes/4.45.0.txt +0 -370
  248. data/doc/release_notes/4.46.0.txt +0 -404
  249. data/doc/release_notes/4.47.0.txt +0 -56
  250. data/doc/release_notes/4.48.0.txt +0 -293
  251. data/doc/release_notes/4.49.0.txt +0 -222
  252. data/doc/release_notes/4.5.0.txt +0 -34
  253. data/doc/release_notes/4.6.0.txt +0 -30
  254. data/doc/release_notes/4.7.0.txt +0 -103
  255. data/doc/release_notes/4.8.0.txt +0 -175
  256. data/doc/release_notes/4.9.0.txt +0 -190
  257. data/spec/adapter_spec.rb +0 -4
  258. data/spec/adapters/db2_spec.rb +0 -170
  259. data/spec/adapters/mssql_spec.rb +0 -804
  260. data/spec/adapters/mysql_spec.rb +0 -1041
  261. data/spec/adapters/oracle_spec.rb +0 -327
  262. data/spec/adapters/postgres_spec.rb +0 -4000
  263. data/spec/adapters/spec_helper.rb +0 -43
  264. data/spec/adapters/sqlanywhere_spec.rb +0 -97
  265. data/spec/adapters/sqlite_spec.rb +0 -600
  266. data/spec/bin_spec.rb +0 -269
  267. data/spec/core/connection_pool_spec.rb +0 -1228
  268. data/spec/core/database_spec.rb +0 -2673
  269. data/spec/core/dataset_spec.rb +0 -5419
  270. data/spec/core/deprecated_spec.rb +0 -70
  271. data/spec/core/expression_filters_spec.rb +0 -1344
  272. data/spec/core/mock_adapter_spec.rb +0 -722
  273. data/spec/core/object_graph_spec.rb +0 -306
  274. data/spec/core/placeholder_literalizer_spec.rb +0 -166
  275. data/spec/core/schema_generator_spec.rb +0 -214
  276. data/spec/core/schema_spec.rb +0 -1820
  277. data/spec/core/spec_helper.rb +0 -23
  278. data/spec/core/version_spec.rb +0 -7
  279. data/spec/core_extensions_spec.rb +0 -762
  280. data/spec/core_model_spec.rb +0 -2
  281. data/spec/core_spec.rb +0 -1
  282. data/spec/deprecation_helper.rb +0 -30
  283. data/spec/extensions/accessed_columns_spec.rb +0 -51
  284. data/spec/extensions/active_model_spec.rb +0 -99
  285. data/spec/extensions/after_initialize_spec.rb +0 -24
  286. data/spec/extensions/arbitrary_servers_spec.rb +0 -109
  287. data/spec/extensions/association_dependencies_spec.rb +0 -125
  288. data/spec/extensions/association_pks_spec.rb +0 -423
  289. data/spec/extensions/association_proxies_spec.rb +0 -100
  290. data/spec/extensions/auto_literal_strings_spec.rb +0 -205
  291. data/spec/extensions/auto_validations_spec.rb +0 -202
  292. data/spec/extensions/blacklist_security_spec.rb +0 -95
  293. data/spec/extensions/blank_spec.rb +0 -69
  294. data/spec/extensions/boolean_readers_spec.rb +0 -93
  295. data/spec/extensions/boolean_subsets_spec.rb +0 -47
  296. data/spec/extensions/caching_spec.rb +0 -273
  297. data/spec/extensions/class_table_inheritance_spec.rb +0 -568
  298. data/spec/extensions/column_conflicts_spec.rb +0 -75
  299. data/spec/extensions/column_select_spec.rb +0 -129
  300. data/spec/extensions/columns_introspection_spec.rb +0 -90
  301. data/spec/extensions/columns_updated_spec.rb +0 -35
  302. data/spec/extensions/composition_spec.rb +0 -248
  303. data/spec/extensions/connection_expiration_spec.rb +0 -133
  304. data/spec/extensions/connection_validator_spec.rb +0 -127
  305. data/spec/extensions/constraint_validations_plugin_spec.rb +0 -300
  306. data/spec/extensions/constraint_validations_spec.rb +0 -395
  307. data/spec/extensions/core_refinements_spec.rb +0 -528
  308. data/spec/extensions/csv_serializer_spec.rb +0 -183
  309. data/spec/extensions/current_datetime_timestamp_spec.rb +0 -27
  310. data/spec/extensions/dataset_associations_spec.rb +0 -365
  311. data/spec/extensions/dataset_source_alias_spec.rb +0 -51
  312. data/spec/extensions/date_arithmetic_spec.rb +0 -181
  313. data/spec/extensions/datetime_parse_to_time_spec.rb +0 -169
  314. data/spec/extensions/def_dataset_method_spec.rb +0 -100
  315. data/spec/extensions/defaults_setter_spec.rb +0 -141
  316. data/spec/extensions/delay_add_association_spec.rb +0 -73
  317. data/spec/extensions/dirty_spec.rb +0 -189
  318. data/spec/extensions/duplicate_columns_handler_spec.rb +0 -104
  319. data/spec/extensions/eager_each_spec.rb +0 -62
  320. data/spec/extensions/empty_array_consider_nulls_spec.rb +0 -24
  321. data/spec/extensions/error_splitter_spec.rb +0 -18
  322. data/spec/extensions/error_sql_spec.rb +0 -20
  323. data/spec/extensions/eval_inspect_spec.rb +0 -74
  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 -380
  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 -275
  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 -840
  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 -109
  348. data/spec/extensions/nested_attributes_spec.rb +0 -703
  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 -165
  356. data/spec/extensions/pg_enum_spec.rb +0 -113
  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 -487
  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 -182
  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 -868
  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 -61
  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 -410
  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 -141
  409. data/spec/extensions/thread_local_timezones_spec.rb +0 -67
  410. data/spec/extensions/timestamps_spec.rb +0 -209
  411. data/spec/extensions/to_dot_spec.rb +0 -153
  412. data/spec/extensions/touch_spec.rb +0 -226
  413. data/spec/extensions/tree_spec.rb +0 -284
  414. data/spec/extensions/typecast_on_load_spec.rb +0 -86
  415. data/spec/extensions/unlimited_update_spec.rb +0 -21
  416. data/spec/extensions/update_or_create_spec.rb +0 -83
  417. data/spec/extensions/update_primary_key_spec.rb +0 -105
  418. data/spec/extensions/update_refresh_spec.rb +0 -59
  419. data/spec/extensions/uuid_spec.rb +0 -101
  420. data/spec/extensions/validate_associated_spec.rb +0 -52
  421. data/spec/extensions/validation_class_methods_spec.rb +0 -1040
  422. data/spec/extensions/validation_contexts_spec.rb +0 -31
  423. data/spec/extensions/validation_helpers_spec.rb +0 -525
  424. data/spec/extensions/whitelist_security_spec.rb +0 -157
  425. data/spec/extensions/xml_serializer_spec.rb +0 -213
  426. data/spec/files/bad_down_migration/001_create_alt_basic.rb +0 -4
  427. data/spec/files/bad_down_migration/002_create_alt_advanced.rb +0 -4
  428. data/spec/files/bad_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  429. data/spec/files/bad_timestamped_migrations/1273253851_create_nodes.rb +0 -9
  430. data/spec/files/bad_timestamped_migrations/1273253853_3_create_users.rb +0 -3
  431. data/spec/files/bad_up_migration/001_create_alt_basic.rb +0 -4
  432. data/spec/files/bad_up_migration/002_create_alt_advanced.rb +0 -3
  433. data/spec/files/convert_to_timestamp_migrations/001_create_sessions.rb +0 -9
  434. data/spec/files/convert_to_timestamp_migrations/002_create_nodes.rb +0 -9
  435. data/spec/files/convert_to_timestamp_migrations/003_3_create_users.rb +0 -4
  436. data/spec/files/convert_to_timestamp_migrations/1273253850_create_artists.rb +0 -9
  437. data/spec/files/convert_to_timestamp_migrations/1273253852_create_albums.rb +0 -9
  438. data/spec/files/double_migration/001_create_sessions.rb +0 -9
  439. data/spec/files/double_migration/002_create_nodes.rb +0 -19
  440. data/spec/files/double_migration/003_3_create_users.rb +0 -4
  441. data/spec/files/duplicate_integer_migrations/001_create_alt_advanced.rb +0 -4
  442. data/spec/files/duplicate_integer_migrations/001_create_alt_basic.rb +0 -4
  443. data/spec/files/duplicate_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  444. data/spec/files/duplicate_timestamped_migrations/1273253853_create_nodes.rb +0 -9
  445. data/spec/files/duplicate_timestamped_migrations/1273253853_create_users.rb +0 -4
  446. data/spec/files/empty_migration/001_create_sessions.rb +0 -9
  447. data/spec/files/empty_migration/002_create_nodes.rb +0 -0
  448. data/spec/files/empty_migration/003_3_create_users.rb +0 -4
  449. data/spec/files/integer_migrations/001_create_sessions.rb +0 -9
  450. data/spec/files/integer_migrations/002_create_nodes.rb +0 -9
  451. data/spec/files/integer_migrations/003_3_create_users.rb +0 -4
  452. data/spec/files/interleaved_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  453. data/spec/files/interleaved_timestamped_migrations/1273253850_create_artists.rb +0 -9
  454. data/spec/files/interleaved_timestamped_migrations/1273253851_create_nodes.rb +0 -9
  455. data/spec/files/interleaved_timestamped_migrations/1273253852_create_albums.rb +0 -9
  456. data/spec/files/interleaved_timestamped_migrations/1273253853_3_create_users.rb +0 -4
  457. data/spec/files/missing_integer_migrations/001_create_alt_basic.rb +0 -4
  458. data/spec/files/missing_integer_migrations/003_create_alt_advanced.rb +0 -4
  459. data/spec/files/missing_timestamped_migrations/1273253849_create_sessions.rb +0 -9
  460. data/spec/files/missing_timestamped_migrations/1273253853_3_create_users.rb +0 -4
  461. data/spec/files/reversible_migrations/001_reversible.rb +0 -5
  462. data/spec/files/reversible_migrations/002_reversible.rb +0 -5
  463. data/spec/files/reversible_migrations/003_reversible.rb +0 -5
  464. data/spec/files/reversible_migrations/004_reversible.rb +0 -5
  465. data/spec/files/reversible_migrations/005_reversible.rb +0 -10
  466. data/spec/files/reversible_migrations/006_reversible.rb +0 -10
  467. data/spec/files/reversible_migrations/007_reversible.rb +0 -10
  468. data/spec/files/timestamped_migrations/1273253849_create_sessions.rb +0 -9
  469. data/spec/files/timestamped_migrations/1273253851_create_nodes.rb +0 -9
  470. data/spec/files/timestamped_migrations/1273253853_3_create_users.rb +0 -4
  471. data/spec/files/transaction_specified_migrations/001_create_alt_basic.rb +0 -4
  472. data/spec/files/transaction_specified_migrations/002_create_basic.rb +0 -4
  473. data/spec/files/transaction_unspecified_migrations/001_create_alt_basic.rb +0 -3
  474. data/spec/files/transaction_unspecified_migrations/002_create_basic.rb +0 -3
  475. data/spec/files/uppercase_timestamped_migrations/1273253849_CREATE_SESSIONS.RB +0 -9
  476. data/spec/files/uppercase_timestamped_migrations/1273253851_CREATE_NODES.RB +0 -9
  477. data/spec/files/uppercase_timestamped_migrations/1273253853_3_CREATE_USERS.RB +0 -4
  478. data/spec/guards_helper.rb +0 -58
  479. data/spec/integration/associations_test.rb +0 -2513
  480. data/spec/integration/database_test.rb +0 -113
  481. data/spec/integration/dataset_test.rb +0 -1880
  482. data/spec/integration/eager_loader_test.rb +0 -687
  483. data/spec/integration/migrator_test.rb +0 -262
  484. data/spec/integration/model_test.rb +0 -203
  485. data/spec/integration/plugin_test.rb +0 -2302
  486. data/spec/integration/prepared_statement_test.rb +0 -398
  487. data/spec/integration/schema_test.rb +0 -869
  488. data/spec/integration/spec_helper.rb +0 -64
  489. data/spec/integration/timezone_test.rb +0 -86
  490. data/spec/integration/transaction_test.rb +0 -354
  491. data/spec/integration/type_test.rb +0 -127
  492. data/spec/model/association_reflection_spec.rb +0 -803
  493. data/spec/model/associations_spec.rb +0 -4538
  494. data/spec/model/base_spec.rb +0 -817
  495. data/spec/model/class_dataset_methods_spec.rb +0 -146
  496. data/spec/model/dataset_methods_spec.rb +0 -198
  497. data/spec/model/eager_loading_spec.rb +0 -2262
  498. data/spec/model/hooks_spec.rb +0 -370
  499. data/spec/model/inflector_spec.rb +0 -26
  500. data/spec/model/model_spec.rb +0 -953
  501. data/spec/model/plugins_spec.rb +0 -318
  502. data/spec/model/record_spec.rb +0 -2107
  503. data/spec/model/spec_helper.rb +0 -45
  504. data/spec/model/validations_spec.rb +0 -193
  505. data/spec/model_no_assoc_spec.rb +0 -1
  506. data/spec/model_spec.rb +0 -1
  507. data/spec/plugin_spec.rb +0 -1
  508. data/spec/sequel_coverage.rb +0 -15
  509. data/spec/sequel_warning.rb +0 -4
  510. data/spec/spec_config.rb +0 -12
@@ -49,7 +49,9 @@ module Sequel::S
49
49
  Sequel.expr(*a, &block)
50
50
  end
51
51
 
52
+ # :nocov:
52
53
  if RUBY_VERSION >= '2.0.0'
54
+ # :nocov:
53
55
  refine Object do
54
56
  include Sequel::S
55
57
  end
@@ -37,7 +37,7 @@ module Sequel
37
37
  {:type =>schema[:type] == :boolean ? TrueClass : Integer}
38
38
  when /\Abigint(?:\((?:\d+)\))?(?: unsigned)?\z/
39
39
  {:type=>:Bignum}
40
- when /\A(?:real|float|double(?: precision)?|double\(\d+,\d+\)(?: unsigned)?)\z/
40
+ when /\A(?:real|float|double(?: precision)?|double\(\d+,\d+\))(?: unsigned)?\z/
41
41
  {:type=>Float}
42
42
  when 'boolean', 'bit', 'bool'
43
43
  {:type=>TrueClass}
@@ -57,7 +57,7 @@ module Sequel
57
57
  {:type=>String, :size=>($1.to_i if $1)}
58
58
  when /\A(?:small)?money\z/
59
59
  {:type=>BigDecimal, :size=>[19,2]}
60
- when /\A(?:decimal|numeric|number)(?:\((\d+)(?:,\s*(\d+))?\))?\z/
60
+ when /\A(?:decimal|numeric|number)(?:\((\d+)(?:,\s*(\d+))?\))?(?: unsigned)?\z/
61
61
  s = [($1.to_i if $1), ($2.to_i if $2)].compact
62
62
  {:type=>BigDecimal, :size=>(s.empty? ? nil : s)}
63
63
  when /\A(?:bytea|(?:tiny|medium|long)?blob|(?:var)?binary)(?:\((\d+)\))?\z/
@@ -199,12 +199,18 @@ END_MIG
199
199
  end
200
200
  type = col_opts.delete(:type)
201
201
  col_opts.delete(:size) if col_opts[:size].nil?
202
- col_opts[:default] = if schema[:ruby_default].nil?
203
- column_schema_to_ruby_default_fallback(schema[:default], options)
202
+ if schema[:generated]
203
+ if options[:same_db] && database_type == :postgres
204
+ col_opts[:generated_always_as] = column_schema_to_ruby_default_fallback(schema[:default], options)
205
+ end
204
206
  else
205
- schema[:ruby_default]
207
+ col_opts[:default] = if schema[:ruby_default].nil?
208
+ column_schema_to_ruby_default_fallback(schema[:default], options)
209
+ else
210
+ schema[:ruby_default]
211
+ end
212
+ col_opts.delete(:default) if col_opts[:default].nil?
206
213
  end
207
- col_opts.delete(:default) if col_opts[:default].nil?
208
214
  col_opts[:null] = false if schema[:allow_null] == false
209
215
  if table = schema[:table]
210
216
  [:key, :on_delete, :on_update, :deferrable].each{|f| col_opts[f] = schema[f] if schema[f]}
@@ -212,7 +218,7 @@ END_MIG
212
218
  gen.foreign_key(name, table, col_opts)
213
219
  else
214
220
  gen.column(name, type, col_opts)
215
- if [Integer, :Bignum, Float].include?(type) && schema[:db_type] =~ / unsigned\z/io
221
+ if [Integer, :Bignum, Float, BigDecimal].include?(type) && schema[:db_type] =~ / unsigned\z/io
216
222
  gen.check(Sequel::SQL::Identifier.new(name) >= 0)
217
223
  end
218
224
  end
@@ -41,7 +41,8 @@ module Sequel
41
41
  # # => 6
42
42
  # DB[:table].interval{function(column)} # SELECT (max(function(column)) - min(function(column))) FROM table LIMIT 1
43
43
  # # => 7
44
- def interval(column=Sequel.virtual_row(&Proc.new))
44
+ def interval(column=(no_arg = true), &block)
45
+ column = Sequel.virtual_row(&block) if no_arg
45
46
  if loader = cached_placeholder_literalizer(:_interval_loader) do |pl|
46
47
  arg = pl.arg
47
48
  aggregate_dataset.limit(1).select((SQL::Function.new(:max, arg) - SQL::Function.new(:min, arg)).as(:interval))
@@ -60,7 +61,8 @@ module Sequel
60
61
  # # => 1..10
61
62
  # DB[:table].interval{function(column)} # SELECT max(function(column)) AS v1, min(function(column)) AS v2 FROM table LIMIT 1
62
63
  # # => 0..7
63
- def range(column=Sequel.virtual_row(&Proc.new))
64
+ def range(column=(no_arg = true), &block)
65
+ column = Sequel.virtual_row(&block) if no_arg
64
66
  r = if loader = cached_placeholder_literalizer(:_range_loader) do |pl|
65
67
  arg = pl.arg
66
68
  aggregate_dataset.limit(1).select(SQL::Function.new(:min, arg).as(:v1), SQL::Function.new(:max, arg).as(:v2))
@@ -52,6 +52,12 @@
52
52
  # DB[:a].server(:read_only).delete # Uses shard2
53
53
  # end
54
54
  #
55
+ # If you use an invalid server when calling with_server, it will be
56
+ # treated the same way as if you called Dataset#server with an invalid
57
+ # server. By default, the default server will be used in such cases.
58
+ # If you would like a different server to be used, or an exception to
59
+ # be raised, then use the :servers_hash Database option.
60
+ #
55
61
  # Related modules: Sequel::ServerBlock, Sequel::UnthreadedServerBlock,
56
62
  # Sequel::ThreadedServerBlock
57
63
 
@@ -110,9 +116,9 @@ module Sequel
110
116
  else
111
117
  case server
112
118
  when :default, nil
113
- @default_servers[-1][0]
119
+ @servers[@default_servers[-1][0]]
114
120
  when :read_only
115
- @default_servers[-1][1]
121
+ @servers[@default_servers[-1][1]]
116
122
  else
117
123
  super
118
124
  end
@@ -137,13 +143,13 @@ module Sequel
137
143
 
138
144
  # Make the given server the new default server for the current thread.
139
145
  def set_default_server(default_server, read_only_server=default_server)
140
- sync{(@default_servers[Thread.current] ||= [])} << [default_server, read_only_server]
146
+ sync{(@default_servers[Sequel.current] ||= [])} << [default_server, read_only_server]
141
147
  end
142
148
 
143
149
  # Remove the current default server for the current thread, restoring the
144
150
  # previous default server.
145
151
  def clear_default_server
146
- t = Thread.current
152
+ t = Sequel.current
147
153
  a = sync{@default_servers[t]}
148
154
  a.pop
149
155
  sync{@default_servers.delete(t)} if a.empty?
@@ -151,15 +157,20 @@ module Sequel
151
157
 
152
158
  # Use the server given to with_server for the given thread, if appropriate.
153
159
  def pick_server(server)
154
- a = sync{@default_servers[Thread.current]}
160
+ a = sync{@default_servers[Sequel.current]}
155
161
  if !a || a.empty?
156
162
  super
157
163
  else
164
+ # Hash handling required to work when loaded after arbitrary servers plugin.
158
165
  case server
159
166
  when :default, nil
160
- a[-1][0]
167
+ v = a[-1][0]
168
+ v = @servers[v] unless v.is_a?(Hash)
169
+ v
161
170
  when :read_only
162
- a[-1][1]
171
+ v = a[-1][1]
172
+ v = @servers[v] unless v.is_a?(Hash)
173
+ v
163
174
  else
164
175
  super
165
176
  end
@@ -9,8 +9,8 @@
9
9
  # #
10
10
  #
11
11
  # As you can see, this uses single line SQL comments (--) suffixed
12
- # by a newline. This # plugin transforms all consecutive
13
- # whitespace in the comment to # a single string:
12
+ # by a newline. This plugin transforms all consecutive whitespace
13
+ # in the comment to a single string:
14
14
  #
15
15
  # ds = DB[:table].comment("Some\r\nComment Here").all
16
16
  # # SELECT * FROM table -- Some Comment Here
@@ -25,7 +25,9 @@
25
25
  #
26
26
  # Related module: Sequel::SymbolAref
27
27
 
28
+ # :nocov:
28
29
  raise(Sequel::Error, "Refinements require ruby 2.0.0 or greater") unless RUBY_VERSION >= '2.0.0'
30
+ # :nocov:
29
31
 
30
32
  module Sequel::SymbolAref
31
33
  refine Symbol do
@@ -23,7 +23,9 @@
23
23
  #
24
24
  # Related module: Sequel::SymbolAs
25
25
 
26
+ # :nocov:
26
27
  raise(Sequel::Error, "Refinements require ruby 2.0.0 or greater") unless RUBY_VERSION >= '2.0.0'
28
+ # :nocov:
27
29
 
28
30
  module Sequel::SymbolAs
29
31
  refine Symbol do
@@ -53,7 +53,13 @@ module Sequel
53
53
  # is given, it is used directly as the node or transition. Otherwise
54
54
  # a node is created for the current object.
55
55
  def dot(label, j=nil)
56
- @dot << "#{j||@i} [label=#{label.to_s.inspect}];"
56
+ label = case label
57
+ when nil
58
+ "<nil>"
59
+ else
60
+ label.to_s
61
+ end
62
+ @dot << "#{j||@i} [label=#{label.inspect}];"
57
63
  end
58
64
 
59
65
  # Recursive method that parses all of Sequel's internal datastructures,
@@ -61,7 +67,7 @@ module Sequel
61
67
  # structure.
62
68
  def v(e, l)
63
69
  @i += 1
64
- dot(l, "#{@stack.last} -> #{@i}") if l
70
+ dot(l, "#{@stack.last} -> #{@i}")
65
71
  @stack.push(@i)
66
72
  case e
67
73
  when LiteralString
@@ -144,7 +150,7 @@ module Sequel
144
150
  dot "Dataset"
145
151
  TO_DOT_OPTIONS.each do |k|
146
152
  if val = e.opts[k]
147
- v(val, k.to_s)
153
+ v(val, k)
148
154
  end
149
155
  end
150
156
  else
@@ -69,7 +69,9 @@ module Sequel
69
69
  require_relative "model/base"
70
70
  require_relative "model/exceptions"
71
71
  require_relative "model/errors"
72
+ # :nocov:
72
73
  if !defined?(::SEQUEL_NO_ASSOCIATIONS) && !ENV.has_key?('SEQUEL_NO_ASSOCIATIONS')
74
+ # :nocov:
73
75
  require_relative 'model/associations'
74
76
  plugin Model::Associations
75
77
  end
@@ -77,7 +79,7 @@ module Sequel
77
79
  def_Model(::Sequel)
78
80
 
79
81
  # The setter methods (methods ending with =) that are never allowed
80
- # to be called automatically via +set+/+update+/+new+/etc..
82
+ # to be called automatically via +set+/+update+/+new+/etc.
81
83
  RESTRICTED_SETTER_METHODS = instance_methods.map(&:to_s).select{|l| l.end_with?('=')}.freeze
82
84
  end
83
85
  end
@@ -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
@@ -356,7 +356,7 @@ module Sequel
356
356
  def finalize
357
357
  return unless cache = self[:cache]
358
358
 
359
- finalize_settings.each do |meth, key|
359
+ finalizer = proc do |meth, key|
360
360
  next if has_key?(key)
361
361
 
362
362
  # Allow calling private methods to make sure caching is done appropriately
@@ -364,6 +364,13 @@ module Sequel
364
364
  self[key] = cache.delete(key) if cache.has_key?(key)
365
365
  end
366
366
 
367
+ finalize_settings.each(&finalizer)
368
+
369
+ unless self[:instance_specific]
370
+ finalizer.call(:associated_eager_dataset, :associated_eager_dataset)
371
+ finalizer.call(:filter_by_associations_conditions_dataset, :filter_by_associations_conditions_dataset)
372
+ end
373
+
367
374
  nil
368
375
  end
369
376
 
@@ -371,9 +378,7 @@ module Sequel
371
378
  FINALIZE_SETTINGS = {
372
379
  :associated_class=>:class,
373
380
  :associated_dataset=>:_dataset,
374
- :associated_eager_dataset=>:associated_eager_dataset,
375
381
  :eager_limit_strategy=>:_eager_limit_strategy,
376
- :filter_by_associations_conditions_dataset=>:filter_by_associations_conditions_dataset,
377
382
  :placeholder_loader=>:placeholder_loader,
378
383
  :predicate_key=>:predicate_key,
379
384
  :predicate_keys=>:predicate_keys,
@@ -432,7 +437,11 @@ module Sequel
432
437
  if use_placeholder_loader?
433
438
  cached_fetch(:placeholder_loader) do
434
439
  Sequel::Dataset::PlaceholderLiteralizer.loader(associated_dataset) do |pl, ds|
435
- ds.where(Sequel.&(*predicate_keys.map{|k| SQL::BooleanExpression.new(:'=', k, pl.arg)}))
440
+ ds = ds.where(Sequel.&(*predicate_keys.map{|k| SQL::BooleanExpression.new(:'=', k, pl.arg)}))
441
+ if self[:block]
442
+ ds = self[:block].call(ds)
443
+ end
444
+ ds
436
445
  end
437
446
  end
438
447
  end
@@ -633,10 +642,15 @@ module Sequel
633
642
  ds = ds.eager(associations)
634
643
  end
635
644
  if block = eo[:eager_block]
645
+ orig_ds = ds
636
646
  ds = block.call(ds)
637
647
  end
638
648
  if eager_loading_use_associated_key?
639
- ds = ds.select_append(*associated_key_array)
649
+ ds = if ds.opts[:eager_graph] && !orig_ds.opts[:eager_graph]
650
+ block.call(orig_ds.select_append(*associated_key_array))
651
+ else
652
+ ds.select_append(*associated_key_array)
653
+ end
640
654
  end
641
655
  if self[:eager_graph]
642
656
  raise(Error, "cannot eagerly load a #{self[:type]} association that uses :eager_graph") if eager_loading_use_associated_key?
@@ -747,8 +761,8 @@ module Sequel
747
761
 
748
762
  # If +s+ is an array, map +s+ over the block. Otherwise, just call the
749
763
  # block with +s+.
750
- def transform(s)
751
- s.is_a?(Array) ? s.map(&Proc.new) : (yield s)
764
+ def transform(s, &block)
765
+ s.is_a?(Array) ? s.map(&block) : (yield s)
752
766
  end
753
767
 
754
768
  # What eager limit strategy should be used when true is given as the value,
@@ -791,7 +805,7 @@ module Sequel
791
805
 
792
806
  # Whether the placeholder loader can be used to load the association.
793
807
  def use_placeholder_loader?
794
- !self[:instance_specific] && !self[:eager_graph]
808
+ self[:use_placeholder_loader]
795
809
  end
796
810
  end
797
811
 
@@ -1239,7 +1253,9 @@ module Sequel
1239
1253
  else
1240
1254
  assoc_record.values.delete(left_key_alias)
1241
1255
  end
1242
- next unless objects = h[hash_key]
1256
+
1257
+ objects = h[hash_key]
1258
+
1243
1259
  if assign_singular
1244
1260
  objects.each do |object|
1245
1261
  object.associations[name] ||= assoc_record
@@ -1612,9 +1628,10 @@ module Sequel
1612
1628
  # is hash or array of two element arrays. Consider also specifying the :graph_block
1613
1629
  # option if the value for this option is not a hash or array of two element arrays
1614
1630
  # and you plan to use this association in eager_graph or association_join.
1615
- # :dataset :: A proc that is instance_execed to get the base dataset to use (before the other
1631
+ # :dataset :: A proc that is used to define the method to get the base dataset to use (before the other
1616
1632
  # options are applied). If the proc accepts an argument, it is passed the related
1617
- # association reflection.
1633
+ # association reflection. It is a best practice to always have the dataset accept an argument
1634
+ # and use the argument to return the appropriate dataset.
1618
1635
  # :distinct :: Use the DISTINCT clause when selecting associating object, both when
1619
1636
  # lazy loading and eager loading via .eager (but not when using .eager_graph).
1620
1637
  # :eager :: The associations to eagerly load via +eager+ when loading the associated object(s).
@@ -1785,11 +1802,12 @@ module Sequel
1785
1802
  opts.merge!(:type => type, :name => name, :cache=>({} if cache_associations), :model => self)
1786
1803
 
1787
1804
  opts[:block] = block if block
1788
- if !opts.has_key?(:instance_specific) && (block || orig_opts[:block] || orig_opts[:dataset])
1805
+ opts[:instance_specific] = true if orig_opts[:dataset]
1806
+ if !opts.has_key?(:instance_specific) && (block || orig_opts[:block])
1789
1807
  # It's possible the association is instance specific, in that it depends on
1790
1808
  # values other than the foreign key value. This needs to be checked for
1791
1809
  # in certain places to disable optimizations.
1792
- opts[:instance_specific] = true
1810
+ opts[:instance_specific] = _association_instance_specific_default(name)
1793
1811
  end
1794
1812
  opts = assoc_class.new.merge!(opts)
1795
1813
 
@@ -1797,6 +1815,7 @@ module Sequel
1797
1815
  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]})")
1798
1816
  end
1799
1817
 
1818
+ opts[:use_placeholder_loader] = !opts[:instance_specific] && !opts[:eager_graph]
1800
1819
  opts[:eager_block] = opts[:block] unless opts.include?(:eager_block)
1801
1820
  opts[:graph_join_type] ||= :left_outer
1802
1821
  opts[:order_eager_graph] = true unless opts.include?(:order_eager_graph)
@@ -1893,6 +1912,12 @@ module Sequel
1893
1912
  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])
1894
1913
 
1895
1914
  private
1915
+
1916
+ # The default value for the instance_specific option, if the association
1917
+ # could be instance specific and the :instance_specific option is not specified.
1918
+ def _association_instance_specific_default(_)
1919
+ true
1920
+ end
1896
1921
 
1897
1922
  # The module to use for the association's methods. Defaults to
1898
1923
  # the overridable_methods_module.
@@ -1904,7 +1929,7 @@ module Sequel
1904
1929
  # can be easily overridden in the class itself while allowing for
1905
1930
  # super to be called.
1906
1931
  def association_module_def(name, opts=OPTS, &block)
1907
- association_module(opts).module_eval{define_method(name, &block)}
1932
+ association_module(opts).send(:define_method, name, &block)
1908
1933
  end
1909
1934
 
1910
1935
  # Add a private method to the module included in the class.
@@ -1939,6 +1964,11 @@ module Sequel
1939
1964
  end
1940
1965
 
1941
1966
  association_module_def(opts.dataset_method, opts){_dataset(opts)}
1967
+ if opts[:block]
1968
+ opts[:block_method] = Plugins.def_sequel_method(association_module(opts), "#{opts[:name]}_block", 1, &opts[:block])
1969
+ end
1970
+ opts[:dataset_opt_arity] = opts[:dataset].arity == 0 ? 0 : 1
1971
+ opts[:dataset_opt_method] = Plugins.def_sequel_method(association_module(opts), "#{opts[:name]}_dataset_opt", opts[:dataset_opt_arity], &opts[:dataset])
1942
1972
  def_association_method(opts)
1943
1973
 
1944
1974
  return if opts[:read_only]
@@ -2109,9 +2139,7 @@ module Sequel
2109
2139
 
2110
2140
  eager_load_results(opts, eo) do |assoc_record|
2111
2141
  hash_key = uses_cks ? pk_meths.map{|k| assoc_record.get_column_value(k)} : assoc_record.get_column_value(opts.primary_key_method)
2112
- if objects = h[hash_key]
2113
- objects.each{|object| object.associations[name] = assoc_record}
2114
- end
2142
+ h[hash_key].each{|object| object.associations[name] = assoc_record}
2115
2143
  end
2116
2144
  end
2117
2145
 
@@ -2124,7 +2152,7 @@ module Sequel
2124
2152
  graph_cks = opts[:graph_keys]
2125
2153
  opts[:eager_grapher] ||= proc do |eo|
2126
2154
  ds = eo[:self]
2127
- ds.graph(eager_graph_dataset(opts, eo), use_only_conditions ? only_conditions : opts.primary_keys.zip(graph_cks) + conditions, Hash[eo].merge!(:select=>select, :join_type=>eo[:join_type]||join_type, :qualify=>:deep, :from_self_alias=>eo[:from_self_alias]), &graph_block)
2155
+ ds.graph(eager_graph_dataset(opts, eo), use_only_conditions ? only_conditions : opts.primary_keys.zip(graph_cks) + conditions, eo.merge(:select=>select, :join_type=>eo[:join_type]||join_type, :qualify=>:deep), &graph_block)
2128
2156
  end
2129
2157
 
2130
2158
  return if opts[:read_only]
@@ -2158,7 +2186,7 @@ module Sequel
2158
2186
  eager_load_results(opts, eo) do |assoc_record|
2159
2187
  assoc_record.values.delete(delete_rn) if delete_rn
2160
2188
  hash_key = uses_cks ? km.map{|k| assoc_record.get_column_value(k)} : assoc_record.get_column_value(km)
2161
- next unless objects = h[hash_key]
2189
+ objects = h[hash_key]
2162
2190
  if assign_singular
2163
2191
  objects.each do |object|
2164
2192
  unless object.associations[name]
@@ -2184,7 +2212,7 @@ module Sequel
2184
2212
  graph_block = opts[:graph_block]
2185
2213
  opts[:eager_grapher] ||= proc do |eo|
2186
2214
  ds = eo[:self]
2187
- ds = ds.graph(opts.apply_eager_graph_limit_strategy(eo[:limit_strategy], eager_graph_dataset(opts, eo)), use_only_conditions ? only_conditions : cks.zip(pkcs) + conditions, Hash[eo].merge!(:select=>select, :join_type=>eo[:join_type]||join_type, :qualify=>:deep, :from_self_alias=>eo[:from_self_alias]), &graph_block)
2215
+ ds = ds.graph(opts.apply_eager_graph_limit_strategy(eo[:limit_strategy], eager_graph_dataset(opts, eo)), use_only_conditions ? only_conditions : cks.zip(pkcs) + conditions, eo.merge(:select=>select, :join_type=>eo[:join_type]||join_type, :qualify=>:deep), &graph_block)
2188
2216
  # We only load reciprocals for one_to_many associations, as other reciprocals don't make sense
2189
2217
  ds.opts[:eager_graph][:reciprocals][eo[:table_alias]] = opts.reciprocal
2190
2218
  ds
@@ -2199,12 +2227,28 @@ module Sequel
2199
2227
  if one_to_one
2200
2228
  opts[:setter] ||= proc do |o|
2201
2229
  up_ds = _apply_association_options(opts, opts.associated_dataset.where(cks.zip(cpks.map{|k| get_column_value(k)})))
2230
+
2231
+ 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)))
2232
+ if old = up_ds.first
2233
+ cks.each{|k| old.set_column_value(:"#{k}=", nil)}
2234
+ end
2235
+ save_old = true
2236
+ end
2237
+
2202
2238
  if o
2203
- up_ds = up_ds.exclude(o.pk_hash) unless o.new?
2239
+ if !o.new? && !save_old
2240
+ up_ds = up_ds.exclude(o.pk_hash)
2241
+ end
2204
2242
  cks.zip(cpks).each{|k, pk| o.set_column_value(:"#{k}=", get_column_value(pk))}
2205
2243
  end
2244
+
2206
2245
  checked_transaction do
2207
- up_ds.skip_limit_check.update(ck_nil_hash)
2246
+ if save_old
2247
+ old.save(save_opts) || raise(Sequel::Error, "invalid previously associated object, cannot save") if old
2248
+ else
2249
+ up_ds.skip_limit_check.update(ck_nil_hash)
2250
+ end
2251
+
2208
2252
  o.save(save_opts) || raise(Sequel::Error, "invalid associated object, cannot save") if o
2209
2253
  end
2210
2254
  end
@@ -2282,7 +2326,8 @@ module Sequel
2282
2326
  end
2283
2327
  ds = ds.clone(:model_object => self)
2284
2328
  ds = ds.eager_graph(opts[:eager_graph]) if opts[:eager_graph] && opts.eager_graph_lazy_dataset?
2285
- ds = instance_exec(ds, &opts[:block]) if opts[:block]
2329
+ # block method is private
2330
+ ds = send(opts[:block_method], ds) if opts[:block_method]
2286
2331
  ds
2287
2332
  end
2288
2333
 
@@ -2305,10 +2350,11 @@ module Sequel
2305
2350
  # Return an association dataset for the given association reflection
2306
2351
  def _dataset(opts)
2307
2352
  raise(Sequel::Error, "model object #{inspect} does not have a primary key") if opts.dataset_need_primary_key? && !pk
2308
- ds = if opts[:dataset].arity == 1
2309
- instance_exec(opts, &opts[:dataset])
2353
+ ds = if opts[:dataset_opt_arity] == 1
2354
+ # dataset_opt_method is private
2355
+ send(opts[:dataset_opt_method], opts)
2310
2356
  else
2311
- instance_exec(&opts[:dataset])
2357
+ send(opts[:dataset_opt_method])
2312
2358
  end
2313
2359
  _apply_association_options(opts, ds)
2314
2360
  end
@@ -2401,7 +2447,32 @@ module Sequel
2401
2447
  # cached associations.
2402
2448
  def change_column_value(column, value)
2403
2449
  if assocs = model.autoreloading_associations[column]
2404
- assocs.each{|a| associations.delete(a)}
2450
+ vals = @values
2451
+ if new?
2452
+ # Do deeper checking for new objects, so that associations are
2453
+ # not deleted when values do not change. This code is run at
2454
+ # a higher level for existing objects.
2455
+ if value == (c = vals[column]) && value.class == c.class
2456
+ # If the value is the same, there is no reason to delete
2457
+ # the related associations, so exit early in that case.
2458
+ return super
2459
+ end
2460
+
2461
+ only_delete_nil = c.nil?
2462
+ elsif vals[column].nil?
2463
+ only_delete_nil = true
2464
+ end
2465
+
2466
+ if only_delete_nil
2467
+ # If the current foreign key value is nil, but the association
2468
+ # is already present in the cache, it was probably added to the
2469
+ # cache for a reason, and we do not want to delete it in that case.
2470
+ # However, we still want to delete associations with nil values
2471
+ # to remove the cached false negative.
2472
+ assocs.each{|a| associations.delete(a) if associations[a].nil?}
2473
+ else
2474
+ assocs.each{|a| associations.delete(a)}
2475
+ end
2405
2476
  end
2406
2477
  super
2407
2478
  end
@@ -2567,13 +2638,25 @@ module Sequel
2567
2638
  # Set the given object as the associated object for the given *_to_one association reflection
2568
2639
  def _set_associated_object(opts, o)
2569
2640
  a = associations[opts[:name]]
2570
- return if a && a == o && !set_associated_object_if_same?
2641
+ reciprocal = opts.reciprocal
2642
+ if set_associated_object_if_same?
2643
+ if reciprocal
2644
+ remove_reciprocal = a && (a != o || a.associations[reciprocal] != self)
2645
+ add_reciprocal = o && o.associations[reciprocal] != self
2646
+ end
2647
+ else
2648
+ return if a && a == o
2649
+ if reciprocal
2650
+ remove_reciprocal = a
2651
+ add_reciprocal = o
2652
+ end
2653
+ end
2571
2654
  run_association_callbacks(opts, :before_set, o)
2572
- remove_reciprocal_object(opts, a) if a
2655
+ remove_reciprocal_object(opts, a) if remove_reciprocal
2573
2656
  # Allow calling private _setter method
2574
2657
  send(opts[:_setter_method], o)
2575
2658
  associations[opts[:name]] = o
2576
- add_reciprocal_object(opts, o) if o
2659
+ add_reciprocal_object(opts, o) if add_reciprocal
2577
2660
  run_association_callbacks(opts, :after_set, o)
2578
2661
  o
2579
2662
  end
@@ -2628,7 +2711,7 @@ module Sequel
2628
2711
  # Album.eager(:artist, :genre).all
2629
2712
  # Album.eager_graph(:artist, :genre).all
2630
2713
  # Album.eager(:artist).eager(:genre).all
2631
- # Album.eager_graph(:artist).eager(:genre).all
2714
+ # Album.eager_graph(:artist).eager_graph(:genre).all
2632
2715
  # Artist.eager(albums: :tracks).all
2633
2716
  # Artist.eager_graph(albums: :tracks).all
2634
2717
  # Artist.eager(albums: {tracks: :genre}).all
@@ -2661,13 +2744,79 @@ module Sequel
2661
2744
  end
2662
2745
 
2663
2746
  # Adds one or more INNER JOINs to the existing dataset using the keys and conditions
2664
- # specified by the given association. The following methods also exist for specifying
2665
- # a different type of JOIN:
2747
+ # specified by the given association(s). Take the same arguments as eager_graph, and
2748
+ # operates similarly, but only adds the joins as opposed to making the other changes
2749
+ # (such as adding selected columns and setting up eager loading).
2750
+ #
2751
+ # The following methods also exist for specifying a different type of JOIN:
2666
2752
  #
2667
2753
  # association_full_join :: FULL JOIN
2668
2754
  # association_inner_join :: INNER JOIN
2669
2755
  # association_left_join :: LEFT JOIN
2670
2756
  # association_right_join :: RIGHT JOIN
2757
+ #
2758
+ # Examples:
2759
+ #
2760
+ # # For each album, association_join load the artist
2761
+ # Album.association_join(:artist).all
2762
+ # # SELECT *
2763
+ # # FROM albums
2764
+ # # INNER JOIN artists AS artist ON (artists.id = albums.artist_id)
2765
+ #
2766
+ # # For each album, association_join load the artist, using a specified alias
2767
+ # Album.association_join(Sequel[:artist].as(:a)).all
2768
+ # # SELECT *
2769
+ # # FROM albums
2770
+ # # INNER JOIN artists AS a ON (a.id = albums.artist_id)
2771
+ #
2772
+ # # For each album, association_join load the artist and genre
2773
+ # Album.association_join(:artist, :genre).all
2774
+ # Album.association_join(:artist).association_join(:genre).all
2775
+ # # SELECT *
2776
+ # # FROM albums
2777
+ # # INNER JOIN artists AS artist ON (artist.id = albums.artist_id)
2778
+ # # INNER JOIN genres AS genre ON (genre.id = albums.genre_id)
2779
+ #
2780
+ # # For each artist, association_join load albums and tracks for each album
2781
+ # Artist.association_join(albums: :tracks).all
2782
+ # # SELECT *
2783
+ # # FROM artists
2784
+ # # INNER JOIN albums ON (albums.artist_id = artists.id)
2785
+ # # INNER JOIN tracks ON (tracks.album_id = albums.id)
2786
+ #
2787
+ # # For each artist, association_join load albums, tracks for each album, and genre for each track
2788
+ # Artist.association_join(albums: {tracks: :genre}).all
2789
+ # # SELECT *
2790
+ # # FROM artists
2791
+ # # INNER JOIN albums ON (albums.artist_id = artists.id)
2792
+ # # INNER JOIN tracks ON (tracks.album_id = albums.id)
2793
+ # # INNER JOIN genres AS genre ON (genre.id = tracks.genre_id)
2794
+ #
2795
+ # # For each artist, association_join load albums with year > 1990
2796
+ # Artist.association_join(albums: proc{|ds| ds.where{year > 1990}}).all
2797
+ # # SELECT *
2798
+ # # FROM artists
2799
+ # # INNER JOIN (
2800
+ # # SELECT * FROM albums WHERE (year > 1990)
2801
+ # # ) AS albums ON (albums.artist_id = artists.id)
2802
+ #
2803
+ # # For each artist, association_join load albums and tracks 1-10 for each album
2804
+ # Artist.association_join(albums: {tracks: proc{|ds| ds.where(number: 1..10)}}).all
2805
+ # # SELECT *
2806
+ # # FROM artists
2807
+ # # INNER JOIN albums ON (albums.artist_id = artists.id)
2808
+ # # INNER JOIN (
2809
+ # # SELECT * FROM tracks WHERE ((number >= 1) AND (number <= 10))
2810
+ # # ) AS tracks ON (tracks.albums_id = albums.id)
2811
+ #
2812
+ # # For each artist, association_join load albums with year > 1990, and tracks for those albums
2813
+ # Artist.association_join(albums: {proc{|ds| ds.where{year > 1990}}=>:tracks}).all
2814
+ # # SELECT *
2815
+ # # FROM artists
2816
+ # # INNER JOIN (
2817
+ # # SELECT * FROM albums WHERE (year > 1990)
2818
+ # # ) AS albums ON (albums.artist_id = artists.id)
2819
+ # # INNER JOIN tracks ON (tracks.album_id = albums.id)
2671
2820
  def association_join(*associations)
2672
2821
  association_inner_join(*associations)
2673
2822
  end
@@ -2681,8 +2830,8 @@ module Sequel
2681
2830
  # creates a subquery to the join table.
2682
2831
  def complex_expression_sql_append(sql, op, args)
2683
2832
  r = args[1]
2684
- if (((op == :'=' || op == :'!=') and r.is_a?(Sequel::Model)) ||
2685
- (multiple = ((op == :IN || op == :'NOT IN') and ((is_ds = r.is_a?(Sequel::Dataset)) or r.all?{|x| x.is_a?(Sequel::Model)}))))
2833
+ if (((op == :'=' || op == :'!=') && r.is_a?(Sequel::Model)) ||
2834
+ (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)})))))
2686
2835
  l = args[0]
2687
2836
  if ar = model.association_reflections[l]
2688
2837
  if multiple
@@ -2747,11 +2896,61 @@ module Sequel
2747
2896
  #
2748
2897
  # Each association's order, if defined, is respected.
2749
2898
  # If the association uses a block or has an :eager_block argument, it is used.
2899
+ #
2900
+ # To modify the associated dataset that will be used for the eager load, you should use a
2901
+ # hash for the association, with the key being the association name symbol, and the value being
2902
+ # a callable object that is called with the associated dataset and should return a modified
2903
+ # dataset. If that association also has dependent associations, instead of a callable object,
2904
+ # use a hash with the callable object being the key, and the dependent association(s) as the value.
2905
+ #
2906
+ # Examples:
2907
+ #
2908
+ # # For each album, eager load the artist
2909
+ # Album.eager(:artist).all
2910
+ # # SELECT * FROM albums
2911
+ # # SELECT * FROM artists WHERE (id IN (...))
2912
+ #
2913
+ # # For each album, eager load the artist and genre
2914
+ # Album.eager(:artist, :genre).all
2915
+ # Album.eager(:artist).eager(:genre).all
2916
+ # # SELECT * FROM albums
2917
+ # # SELECT * FROM artists WHERE (id IN (...))
2918
+ # # SELECT * FROM genres WHERE (id IN (...))
2919
+ #
2920
+ # # For each artist, eager load albums and tracks for each album
2921
+ # Artist.eager(albums: :tracks).all
2922
+ # # SELECT * FROM artists
2923
+ # # SELECT * FROM albums WHERE (artist_id IN (...))
2924
+ # # SELECT * FROM tracks WHERE (album_id IN (...))
2925
+ #
2926
+ # # For each artist, eager load albums, tracks for each album, and genre for each track
2927
+ # Artist.eager(albums: {tracks: :genre}).all
2928
+ # # SELECT * FROM artists
2929
+ # # SELECT * FROM albums WHERE (artist_id IN (...))
2930
+ # # SELECT * FROM tracks WHERE (album_id IN (...))
2931
+ # # SELECT * FROM genre WHERE (id IN (...))
2932
+ #
2933
+ # # For each artist, eager load albums with year > 1990
2934
+ # Artist.eager(albums: proc{|ds| ds.where{year > 1990}}).all
2935
+ # # SELECT * FROM artists
2936
+ # # SELECT * FROM albums WHERE ((year > 1990) AND (artist_id IN (...)))
2937
+ #
2938
+ # # For each artist, eager load albums and tracks 1-10 for each album
2939
+ # Artist.eager(albums: {tracks: proc{|ds| ds.where(number: 1..10)}}).all
2940
+ # # SELECT * FROM artists
2941
+ # # SELECT * FROM albums WHERE (artist_id IN (...))
2942
+ # # SELECT * FROM tracks WHERE ((number >= 1) AND (number <= 10) AND (album_id IN (...)))
2943
+ #
2944
+ # # For each artist, eager load albums with year > 1990, and tracks for those albums
2945
+ # Artist.eager(albums: {proc{|ds| ds.where{year > 1990}}=>:tracks}).all
2946
+ # # SELECT * FROM artists
2947
+ # # SELECT * FROM albums WHERE ((year > 1990) AND (artist_id IN (...)))
2948
+ # # SELECT * FROM albums WHERE (artist_id IN (...))
2750
2949
  def eager(*associations)
2751
2950
  opts = @opts[:eager]
2752
2951
  association_opts = eager_options_for_associations(associations)
2753
- opts = opts ? Hash[opts].merge!(association_opts) : association_opts
2754
- clone(:eager=>opts)
2952
+ opts = opts ? opts.merge(association_opts) : association_opts
2953
+ clone(:eager=>opts.freeze)
2755
2954
  end
2756
2955
 
2757
2956
  # The secondary eager loading method. Loads all associations in a single query. This
@@ -2775,6 +2974,86 @@ module Sequel
2775
2974
  #
2776
2975
  # Like +eager+, you need to call +all+ on the dataset for the eager loading to work. If you just
2777
2976
  # call +each+, it will yield plain hashes, each containing all columns from all the tables.
2977
+ #
2978
+ # To modify the associated dataset that will be joined to the current dataset, you should use a
2979
+ # hash for the association, with the key being the association name symbol, and the value being
2980
+ # a callable object that is called with the associated dataset and should return a modified
2981
+ # dataset. If that association also has dependent associations, instead of a callable object,
2982
+ # use a hash with the callable object being the key, and the dependent association(s) as the value.
2983
+ #
2984
+ # You can specify an custom alias and/or join type on a per-association basis by providing an
2985
+ # Sequel::SQL::AliasedExpression object instead of an a Symbol for the association name.
2986
+ #
2987
+ # Examples:
2988
+ #
2989
+ # # For each album, eager_graph load the artist
2990
+ # Album.eager_graph(:artist).all
2991
+ # # SELECT ...
2992
+ # # FROM albums
2993
+ # # LEFT OUTER JOIN artists AS artist ON (artists.id = albums.artist_id)
2994
+ #
2995
+ # # For each album, eager_graph load the artist, using a specified alias
2996
+ # Album.eager_graph(Sequel[:artist].as(:a)).all
2997
+ # # SELECT ...
2998
+ # # FROM albums
2999
+ # # LEFT OUTER JOIN artists AS a ON (a.id = albums.artist_id)
3000
+ #
3001
+ # # For each album, eager_graph load the artist, using a specified alias
3002
+ # # and custom join type
3003
+ #
3004
+ # Album.eager_graph(Sequel[:artist].as(:a, join_type: :inner)).all
3005
+ # # SELECT ...
3006
+ # # FROM albums
3007
+ # # INNER JOIN artists AS a ON (a.id = albums.artist_id)
3008
+ #
3009
+ # # For each album, eager_graph load the artist and genre
3010
+ # Album.eager_graph(:artist, :genre).all
3011
+ # Album.eager_graph(:artist).eager_graph(:genre).all
3012
+ # # SELECT ...
3013
+ # # FROM albums
3014
+ # # LEFT OUTER JOIN artists AS artist ON (artist.id = albums.artist_id)
3015
+ # # LEFT OUTER JOIN genres AS genre ON (genre.id = albums.genre_id)
3016
+ #
3017
+ # # For each artist, eager_graph load albums and tracks for each album
3018
+ # Artist.eager_graph(albums: :tracks).all
3019
+ # # SELECT ...
3020
+ # # FROM artists
3021
+ # # LEFT OUTER JOIN albums ON (albums.artist_id = artists.id)
3022
+ # # LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id)
3023
+ #
3024
+ # # For each artist, eager_graph load albums, tracks for each album, and genre for each track
3025
+ # Artist.eager_graph(albums: {tracks: :genre}).all
3026
+ # # SELECT ...
3027
+ # # FROM artists
3028
+ # # LEFT OUTER JOIN albums ON (albums.artist_id = artists.id)
3029
+ # # LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id)
3030
+ # # LEFT OUTER JOIN genres AS genre ON (genre.id = tracks.genre_id)
3031
+ #
3032
+ # # For each artist, eager_graph load albums with year > 1990
3033
+ # Artist.eager_graph(albums: proc{|ds| ds.where{year > 1990}}).all
3034
+ # # SELECT ...
3035
+ # # FROM artists
3036
+ # # LEFT OUTER JOIN (
3037
+ # # SELECT * FROM albums WHERE (year > 1990)
3038
+ # # ) AS albums ON (albums.artist_id = artists.id)
3039
+ #
3040
+ # # For each artist, eager_graph load albums and tracks 1-10 for each album
3041
+ # Artist.eager_graph(albums: {tracks: proc{|ds| ds.where(number: 1..10)}}).all
3042
+ # # SELECT ...
3043
+ # # FROM artists
3044
+ # # LEFT OUTER JOIN albums ON (albums.artist_id = artists.id)
3045
+ # # LEFT OUTER JOIN (
3046
+ # # SELECT * FROM tracks WHERE ((number >= 1) AND (number <= 10))
3047
+ # # ) AS tracks ON (tracks.albums_id = albums.id)
3048
+ #
3049
+ # # For each artist, eager_graph load albums with year > 1990, and tracks for those albums
3050
+ # Artist.eager_graph(albums: {proc{|ds| ds.where{year > 1990}}=>:tracks}).all
3051
+ # # SELECT ...
3052
+ # # FROM artists
3053
+ # # LEFT OUTER JOIN (
3054
+ # # SELECT * FROM albums WHERE (year > 1990)
3055
+ # # ) AS albums ON (albums.artist_id = artists.id)
3056
+ # # LEFT OUTER JOIN tracks ON (tracks.album_id = albums.id)
2778
3057
  def eager_graph(*associations)
2779
3058
  eager_graph_with_options(associations)
2780
3059
  end
@@ -2800,8 +3079,11 @@ module Sequel
2800
3079
  # significantly slower in some cases (perhaps even the majority of cases), so you should
2801
3080
  # only use this if you have benchmarked that it is faster for your use cases.
2802
3081
  def eager_graph_with_options(associations, opts=OPTS)
3082
+ return self if associations.empty?
3083
+
3084
+ opts = opts.dup unless opts.frozen?
2803
3085
  associations = [associations] unless associations.is_a?(Array)
2804
- if eg = @opts[:eager_graph]
3086
+ ds = if eg = @opts[:eager_graph]
2805
3087
  eg = eg.dup
2806
3088
  [:requirements, :reflections, :reciprocals, :limits].each{|k| eg[k] = eg[k].dup}
2807
3089
  eg[:local] = opts
@@ -2815,8 +3097,12 @@ module Sequel
2815
3097
  # :limits :: Any limit/offset array slicing that need to be handled in ruby land after loading
2816
3098
  opts = {:requirements=>{}, :master=>alias_symbol(first_source), :reflections=>{}, :reciprocals=>{}, :limits=>{}, :local=>opts, :cartesian_product_number=>0, :row_proc=>row_proc}
2817
3099
  ds = clone(:eager_graph=>opts)
2818
- ds.eager_graph_associations(ds, model, ds.opts[:eager_graph][:master], [], *associations).naked
3100
+ ds = ds.eager_graph_associations(ds, model, ds.opts[:eager_graph][:master], [], *associations).naked
2819
3101
  end
3102
+
3103
+ ds.opts[:eager_graph].freeze
3104
+ ds.opts[:eager_graph].each_value{|v| v.freeze if v.is_a?(Hash)}
3105
+ ds
2820
3106
  end
2821
3107
 
2822
3108
  # If the dataset is being eagerly loaded, default to calling all
@@ -2864,11 +3150,16 @@ module Sequel
2864
3150
  # ta :: table_alias used for the parent association
2865
3151
  # requirements :: an array, used as a stack for requirements
2866
3152
  # r :: association reflection for the current association, or an SQL::AliasedExpression
2867
- # with the reflection as the expression and the alias base as the aliaz.
3153
+ # with the reflection as the expression, the alias base as the alias (or nil to
3154
+ # use the default alias), and an optional hash with a :join_type entry as the columns
3155
+ # to use a custom join type.
2868
3156
  # *associations :: any associations dependent on this one
2869
3157
  def eager_graph_association(ds, model, ta, requirements, r, *associations)
2870
3158
  if r.is_a?(SQL::AliasedExpression)
2871
3159
  alias_base = r.alias
3160
+ if r.columns.is_a?(Hash)
3161
+ join_type = r.columns[:join_type]
3162
+ end
2872
3163
  r = r.expression
2873
3164
  else
2874
3165
  alias_base = r[:graph_alias_base]
@@ -2891,7 +3182,7 @@ module Sequel
2891
3182
  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]}"
2892
3183
  end
2893
3184
 
2894
- 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])
3185
+ 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])
2895
3186
  if r[:order_eager_graph] && (order = r.fetch(:graph_order, r[:order]))
2896
3187
  ds = ds.order_append(*qualified_expression(order, assoc_table_alias))
2897
3188
  end
@@ -2916,7 +3207,6 @@ module Sequel
2916
3207
  # requirements :: an array, used as a stack for requirements
2917
3208
  # *associations :: the associations to add to the graph
2918
3209
  def eager_graph_associations(ds, model, ta, requirements, *associations)
2919
- return ds if associations.empty?
2920
3210
  associations.flatten.each do |association|
2921
3211
  ds = case association
2922
3212
  when Symbol, SQL::AliasedExpression
@@ -2936,7 +3226,7 @@ module Sequel
2936
3226
  # Replace the array of plain hashes with an array of model objects will all eager_graphed
2937
3227
  # associations set in the associations cache for each object.
2938
3228
  def eager_graph_build_associations(hashes)
2939
- hashes.replace(EagerGraphLoader.new(self).load(hashes))
3229
+ hashes.replace(_eager_graph_build_associations(hashes, eager_graph_loader))
2940
3230
  end
2941
3231
 
2942
3232
  private
@@ -2947,6 +3237,12 @@ module Sequel
2947
3237
  clone(:join=>clone(:graph_from_self=>false).eager_graph_with_options(associations, :join_type=>type, :join_only=>true).opts[:join])
2948
3238
  end
2949
3239
 
3240
+ # Process the array of hashes using the eager graph loader to return an array
3241
+ # of model objects with the associations set.
3242
+ def _eager_graph_build_associations(hashes, egl)
3243
+ egl.load(hashes)
3244
+ end
3245
+
2950
3246
  # If the association has conditions itself, then it requires additional filters be
2951
3247
  # added to the current dataset to ensure that the passed in object would also be
2952
3248
  # included by the association's conditions.
@@ -3032,12 +3328,28 @@ module Sequel
3032
3328
  # per-call determining of the alias base.
3033
3329
  def eager_graph_check_association(model, association)
3034
3330
  if association.is_a?(SQL::AliasedExpression)
3035
- SQL::AliasedExpression.new(check_association(model, association.expression), association.alias)
3331
+ expr = association.expression
3332
+ if expr.is_a?(SQL::Identifier)
3333
+ expr = expr.value
3334
+ if expr.is_a?(String)
3335
+ expr = expr.to_sym
3336
+ end
3337
+ end
3338
+
3339
+ SQL::AliasedExpression.new(check_association(model, expr), association.alias || expr, association.columns)
3036
3340
  else
3037
3341
  check_association(model, association)
3038
3342
  end
3039
3343
  end
3040
3344
 
3345
+ # The EagerGraphLoader instance used for converting eager_graph results.
3346
+ def eager_graph_loader
3347
+ unless egl = cache_get(:_model_eager_graph_loader)
3348
+ egl = cache_set(:_model_eager_graph_loader, EagerGraphLoader.new(self))
3349
+ end
3350
+ egl.dup
3351
+ end
3352
+
3041
3353
  # Eagerly load all specified associations
3042
3354
  def eager_load(a, eager_assoc=@opts[:eager])
3043
3355
  return if a.empty?
@@ -3081,7 +3393,7 @@ module Sequel
3081
3393
  associations = eager_assoc[r[:name]]
3082
3394
  if associations.respond_to?(:call)
3083
3395
  eager_block = associations
3084
- associations = {}
3396
+ associations = OPTS
3085
3397
  elsif associations.is_a?(Hash) && associations.length == 1 && (pr_assoc = associations.to_a.first) && pr_assoc.first.respond_to?(:call)
3086
3398
  eager_block, associations = pr_assoc
3087
3399
  end
@@ -3175,7 +3487,6 @@ module Sequel
3175
3487
  # Hash with table alias symbol keys and [limit, offset] values
3176
3488
  attr_reader :limit_map
3177
3489
 
3178
- # Hash with table alias symbol keys and callable values used to create model instances
3179
3490
  # The table alias symbol for the primary model
3180
3491
  attr_reader :master
3181
3492
 
@@ -3225,12 +3536,15 @@ module Sequel
3225
3536
  :offset
3226
3537
  end
3227
3538
  end
3539
+ after_load_map.freeze
3540
+ alias_map.freeze
3541
+ type_map.freeze
3228
3542
 
3229
3543
  # Make dependency map hash out of requirements array for each association.
3230
3544
  # This builds a tree of dependencies that will be used for recursion
3231
3545
  # to ensure that all parts of the object graph are loaded into the
3232
3546
  # appropriate subordinate association.
3233
- @dependency_map = {}
3547
+ dependency_map = @dependency_map = {}
3234
3548
  # Sort the associations by requirements length, so that
3235
3549
  # requirements are added to the dependency hash before their
3236
3550
  # dependencies.
@@ -3246,18 +3560,12 @@ module Sequel
3246
3560
  hash[ta] = {}
3247
3561
  end
3248
3562
  end
3563
+ freezer = lambda do |h|
3564
+ h.freeze
3565
+ h.each_value(&freezer)
3566
+ end
3567
+ freezer.call(dependency_map)
3249
3568
 
3250
- # This mapping is used to make sure that duplicate entries in the
3251
- # result set are mapped to a single record. For example, using a
3252
- # single one_to_many association with 10 associated records,
3253
- # the main object column values appear in the object graph 10 times.
3254
- # We map by primary key, if available, or by the object's entire values,
3255
- # if not. The mapping must be per table, so create sub maps for each table
3256
- # alias.
3257
- records_map = {@master=>{}}
3258
- alias_map.keys.each{|ta| records_map[ta] = {}}
3259
- @records_map = records_map
3260
-
3261
3569
  datasets = opts[:graph][:table_aliases].to_a.reject{|ta,ds| ds.nil?}
3262
3570
  column_aliases = opts[:graph][:column_aliases]
3263
3571
  primary_keys = {}
@@ -3283,9 +3591,9 @@ module Sequel
3283
3591
  h.select{|ca, c| primary_keys[ta] = ca if pk == c}
3284
3592
  end
3285
3593
  end
3286
- @column_maps = column_maps
3287
- @primary_keys = primary_keys
3288
- @row_procs = row_procs
3594
+ @column_maps = column_maps.freeze
3595
+ @primary_keys = primary_keys.freeze
3596
+ @row_procs = row_procs.freeze
3289
3597
 
3290
3598
  # For performance, create two special maps for the master table,
3291
3599
  # so you can skip a hash lookup.
@@ -3297,22 +3605,35 @@ module Sequel
3297
3605
  # used for performance, to get all values in one hash lookup instead of
3298
3606
  # separate hash lookups for each data structure.
3299
3607
  ta_map = {}
3300
- alias_map.keys.each do |ta|
3301
- ta_map[ta] = [records_map[ta], row_procs[ta], alias_map[ta], type_map[ta], reciprocal_map[ta]]
3608
+ alias_map.each_key do |ta|
3609
+ ta_map[ta] = [row_procs[ta], alias_map[ta], type_map[ta], reciprocal_map[ta]].freeze
3302
3610
  end
3303
- @ta_map = ta_map
3611
+ @ta_map = ta_map.freeze
3612
+ freeze
3304
3613
  end
3305
3614
 
3306
3615
  # Return an array of primary model instances with the associations cache prepopulated
3307
3616
  # for all model objects (both primary and associated).
3308
3617
  def load(hashes)
3618
+ # This mapping is used to make sure that duplicate entries in the
3619
+ # result set are mapped to a single record. For example, using a
3620
+ # single one_to_many association with 10 associated records,
3621
+ # the main object column values appear in the object graph 10 times.
3622
+ # We map by primary key, if available, or by the object's entire values,
3623
+ # if not. The mapping must be per table, so create sub maps for each table
3624
+ # alias.
3625
+ @records_map = records_map = {}
3626
+ alias_map.keys.each{|ta| records_map[ta] = {}}
3627
+
3309
3628
  master = master()
3310
3629
 
3311
3630
  # Assign to local variables for speed increase
3312
3631
  rp = row_procs[master]
3313
- rm = records_map[master]
3632
+ rm = records_map[master] = {}
3314
3633
  dm = dependency_map
3315
3634
 
3635
+ records_map.freeze
3636
+
3316
3637
  # This will hold the final record set that we will be replacing the object graph with.
3317
3638
  records = []
3318
3639
 
@@ -3333,6 +3654,9 @@ module Sequel
3333
3654
  # Run after_load procs if there are any
3334
3655
  post_process(records, dm) if @unique || !after_load_map.empty? || !limit_map.empty?
3335
3656
 
3657
+ records_map.each_value(&:freeze)
3658
+ freeze
3659
+
3336
3660
  records
3337
3661
  end
3338
3662
 
@@ -3352,7 +3676,17 @@ module Sequel
3352
3676
  end
3353
3677
  key = hkey(ta_h)
3354
3678
  end
3355
- rm, rp, assoc_name, tm, rcm = @ta_map[ta]
3679
+ rp, assoc_name, tm, rcm = @ta_map[ta]
3680
+ rm = records_map[ta]
3681
+
3682
+ # Check type map for all dependencies, and use a unique
3683
+ # object if any are dependencies for multiple objects,
3684
+ # to prevent duplicate objects from showing up in the case
3685
+ # the normal duplicate removal code is not being used.
3686
+ if !@unique && !deps.empty? && deps.any?{|dep_key,_| @ta_map[dep_key][2]}
3687
+ key = [current.object_id, key]
3688
+ end
3689
+
3356
3690
  unless rec = rm[key]
3357
3691
  rec = rm[key] = rp.call(hfor(ta, h))
3358
3692
  end