activerecord 6.0.0 → 7.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (376) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +996 -594
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +34 -34
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +22 -20
  7. data/lib/active_record/association_relation.rb +22 -12
  8. data/lib/active_record/associations/alias_tracker.rb +41 -30
  9. data/lib/active_record/associations/association.rb +106 -41
  10. data/lib/active_record/associations/association_scope.rb +30 -21
  11. data/lib/active_record/associations/belongs_to_association.rb +69 -14
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +20 -6
  13. data/lib/active_record/associations/builder/association.rb +39 -6
  14. data/lib/active_record/associations/builder/belongs_to.rb +47 -17
  15. data/lib/active_record/associations/builder/collection_association.rb +14 -6
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -10
  17. data/lib/active_record/associations/builder/has_many.rb +7 -3
  18. data/lib/active_record/associations/builder/has_one.rb +13 -16
  19. data/lib/active_record/associations/builder/singular_association.rb +7 -3
  20. data/lib/active_record/associations/collection_association.rb +90 -53
  21. data/lib/active_record/associations/collection_proxy.rb +54 -19
  22. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  23. data/lib/active_record/associations/errors.rb +265 -0
  24. data/lib/active_record/associations/foreign_association.rb +21 -1
  25. data/lib/active_record/associations/has_many_association.rb +41 -10
  26. data/lib/active_record/associations/has_many_through_association.rb +29 -12
  27. data/lib/active_record/associations/has_one_association.rb +33 -9
  28. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  29. data/lib/active_record/associations/join_dependency/join_association.rb +41 -17
  30. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  31. data/lib/active_record/associations/join_dependency.rb +97 -54
  32. data/lib/active_record/associations/nested_error.rb +47 -0
  33. data/lib/active_record/associations/preloader/association.rb +237 -54
  34. data/lib/active_record/associations/preloader/batch.rb +48 -0
  35. data/lib/active_record/associations/preloader/branch.rb +153 -0
  36. data/lib/active_record/associations/preloader/through_association.rb +51 -17
  37. data/lib/active_record/associations/preloader.rb +55 -121
  38. data/lib/active_record/associations/singular_association.rb +16 -4
  39. data/lib/active_record/associations/through_association.rb +26 -15
  40. data/lib/active_record/associations.rb +454 -440
  41. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  42. data/lib/active_record/attribute_assignment.rb +11 -14
  43. data/lib/active_record/attribute_methods/before_type_cast.rb +36 -11
  44. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  45. data/lib/active_record/attribute_methods/dirty.rb +75 -34
  46. data/lib/active_record/attribute_methods/primary_key.rb +53 -31
  47. data/lib/active_record/attribute_methods/query.rb +31 -22
  48. data/lib/active_record/attribute_methods/read.rb +16 -17
  49. data/lib/active_record/attribute_methods/serialization.rb +177 -35
  50. data/lib/active_record/attribute_methods/time_zone_conversion.rb +18 -15
  51. data/lib/active_record/attribute_methods/write.rb +16 -28
  52. data/lib/active_record/attribute_methods.rb +227 -100
  53. data/lib/active_record/attributes.rb +94 -56
  54. data/lib/active_record/autosave_association.rb +119 -73
  55. data/lib/active_record/base.rb +31 -21
  56. data/lib/active_record/callbacks.rb +168 -55
  57. data/lib/active_record/coders/column_serializer.rb +61 -0
  58. data/lib/active_record/coders/json.rb +1 -1
  59. data/lib/active_record/coders/yaml_column.rb +70 -25
  60. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -0
  61. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  62. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +79 -0
  63. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +367 -565
  64. data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -57
  65. data/lib/active_record/connection_adapters/abstract/database_statements.rb +277 -89
  66. data/lib/active_record/connection_adapters/abstract/query_cache.rb +241 -69
  67. data/lib/active_record/connection_adapters/abstract/quoting.rb +122 -134
  68. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  69. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
  70. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +324 -72
  71. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +17 -4
  72. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +611 -211
  73. data/lib/active_record/connection_adapters/abstract/transaction.rb +425 -82
  74. data/lib/active_record/connection_adapters/abstract_adapter.rb +698 -211
  75. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +464 -239
  76. data/lib/active_record/connection_adapters/column.rb +28 -1
  77. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  78. data/lib/active_record/connection_adapters/mysql/column.rb +2 -1
  79. data/lib/active_record/connection_adapters/mysql/database_statements.rb +32 -137
  80. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  81. data/lib/active_record/connection_adapters/mysql/quoting.rb +90 -43
  82. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +41 -7
  83. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +18 -1
  84. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +13 -4
  85. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +53 -15
  86. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  87. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  88. data/lib/active_record/connection_adapters/mysql2_adapter.rb +127 -63
  89. data/lib/active_record/connection_adapters/pool_config.rb +83 -0
  90. data/lib/active_record/connection_adapters/pool_manager.rb +57 -0
  91. data/lib/active_record/connection_adapters/postgresql/column.rb +54 -2
  92. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +127 -100
  93. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  94. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +9 -5
  95. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +10 -2
  96. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +15 -2
  97. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  98. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -15
  99. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
  101. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +5 -4
  103. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  104. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
  105. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +35 -8
  106. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  110. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -4
  111. data/lib/active_record/connection_adapters/postgresql/oid.rb +4 -0
  112. data/lib/active_record/connection_adapters/postgresql/quoting.rb +139 -106
  113. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -2
  114. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +98 -4
  115. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +176 -4
  116. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -1
  117. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -118
  118. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  119. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -11
  120. data/lib/active_record/connection_adapters/postgresql_adapter.rb +585 -295
  121. data/lib/active_record/connection_adapters/schema_cache.rb +399 -60
  122. data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
  123. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  124. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +99 -48
  125. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +80 -54
  126. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +27 -1
  127. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
  128. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  129. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +102 -24
  130. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +425 -174
  131. data/lib/active_record/connection_adapters/statement_pool.rb +7 -1
  132. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  133. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  134. data/lib/active_record/connection_adapters.rb +176 -0
  135. data/lib/active_record/connection_handling.rb +243 -115
  136. data/lib/active_record/core.rb +481 -199
  137. data/lib/active_record/counter_cache.rb +69 -32
  138. data/lib/active_record/database_configurations/connection_url_resolver.rb +107 -0
  139. data/lib/active_record/database_configurations/database_config.rb +77 -10
  140. data/lib/active_record/database_configurations/hash_config.rb +148 -26
  141. data/lib/active_record/database_configurations/url_config.rb +44 -45
  142. data/lib/active_record/database_configurations.rb +190 -114
  143. data/lib/active_record/delegated_type.rb +279 -0
  144. data/lib/active_record/deprecator.rb +7 -0
  145. data/lib/active_record/destroy_association_async_job.rb +38 -0
  146. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  147. data/lib/active_record/dynamic_matchers.rb +5 -6
  148. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  149. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  150. data/lib/active_record/encryption/cipher.rb +53 -0
  151. data/lib/active_record/encryption/config.rb +68 -0
  152. data/lib/active_record/encryption/configurable.rb +60 -0
  153. data/lib/active_record/encryption/context.rb +42 -0
  154. data/lib/active_record/encryption/contexts.rb +76 -0
  155. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  156. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  157. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  158. data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -0
  159. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  160. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  161. data/lib/active_record/encryption/encryptor.rb +171 -0
  162. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  163. data/lib/active_record/encryption/errors.rb +15 -0
  164. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  165. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  166. data/lib/active_record/encryption/key.rb +28 -0
  167. data/lib/active_record/encryption/key_generator.rb +53 -0
  168. data/lib/active_record/encryption/key_provider.rb +46 -0
  169. data/lib/active_record/encryption/message.rb +33 -0
  170. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  171. data/lib/active_record/encryption/message_serializer.rb +96 -0
  172. data/lib/active_record/encryption/null_encryptor.rb +25 -0
  173. data/lib/active_record/encryption/properties.rb +76 -0
  174. data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
  175. data/lib/active_record/encryption/scheme.rb +100 -0
  176. data/lib/active_record/encryption.rb +58 -0
  177. data/lib/active_record/enum.rb +224 -73
  178. data/lib/active_record/errors.rb +254 -36
  179. data/lib/active_record/explain.rb +30 -17
  180. data/lib/active_record/explain_registry.rb +11 -6
  181. data/lib/active_record/explain_subscriber.rb +2 -2
  182. data/lib/active_record/fixture_set/file.rb +22 -15
  183. data/lib/active_record/fixture_set/model_metadata.rb +15 -6
  184. data/lib/active_record/fixture_set/render_context.rb +3 -1
  185. data/lib/active_record/fixture_set/table_row.rb +88 -16
  186. data/lib/active_record/fixture_set/table_rows.rb +4 -5
  187. data/lib/active_record/fixtures.rb +229 -116
  188. data/lib/active_record/future_result.rb +178 -0
  189. data/lib/active_record/gem_version.rb +4 -4
  190. data/lib/active_record/inheritance.rb +121 -48
  191. data/lib/active_record/insert_all.rb +178 -29
  192. data/lib/active_record/integration.rb +16 -14
  193. data/lib/active_record/internal_metadata.rb +132 -21
  194. data/lib/active_record/legacy_yaml_adapter.rb +3 -36
  195. data/lib/active_record/locking/optimistic.rb +64 -33
  196. data/lib/active_record/locking/pessimistic.rb +21 -8
  197. data/lib/active_record/log_subscriber.rb +61 -30
  198. data/lib/active_record/marshalling.rb +59 -0
  199. data/lib/active_record/message_pack.rb +124 -0
  200. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  201. data/lib/active_record/middleware/database_selector/resolver.rb +19 -19
  202. data/lib/active_record/middleware/database_selector.rb +25 -13
  203. data/lib/active_record/middleware/shard_selector.rb +62 -0
  204. data/lib/active_record/migration/command_recorder.rb +160 -55
  205. data/lib/active_record/migration/compatibility.rb +286 -43
  206. data/lib/active_record/migration/default_strategy.rb +22 -0
  207. data/lib/active_record/migration/execution_strategy.rb +19 -0
  208. data/lib/active_record/migration/join_table.rb +1 -2
  209. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  210. data/lib/active_record/migration.rb +421 -193
  211. data/lib/active_record/model_schema.rb +217 -125
  212. data/lib/active_record/nested_attributes.rb +62 -27
  213. data/lib/active_record/no_touching.rb +4 -4
  214. data/lib/active_record/normalization.rb +163 -0
  215. data/lib/active_record/persistence.rb +322 -319
  216. data/lib/active_record/promise.rb +84 -0
  217. data/lib/active_record/query_cache.rb +18 -15
  218. data/lib/active_record/query_logs.rb +193 -0
  219. data/lib/active_record/query_logs_formatter.rb +41 -0
  220. data/lib/active_record/querying.rb +54 -14
  221. data/lib/active_record/railtie.rb +250 -72
  222. data/lib/active_record/railties/console_sandbox.rb +2 -4
  223. data/lib/active_record/railties/controller_runtime.rb +25 -11
  224. data/lib/active_record/railties/databases.rake +312 -197
  225. data/lib/active_record/railties/job_runtime.rb +23 -0
  226. data/lib/active_record/readonly_attributes.rb +45 -3
  227. data/lib/active_record/reflection.rb +389 -146
  228. data/lib/active_record/relation/batches/batch_enumerator.rb +61 -16
  229. data/lib/active_record/relation/batches.rb +214 -73
  230. data/lib/active_record/relation/calculations.rb +379 -124
  231. data/lib/active_record/relation/delegation.rb +36 -23
  232. data/lib/active_record/relation/finder_methods.rb +159 -49
  233. data/lib/active_record/relation/from_clause.rb +5 -1
  234. data/lib/active_record/relation/merger.rb +41 -33
  235. data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -11
  236. data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -7
  237. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +20 -13
  238. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  239. data/lib/active_record/relation/predicate_builder.rb +79 -53
  240. data/lib/active_record/relation/query_attribute.rb +30 -12
  241. data/lib/active_record/relation/query_methods.rb +1156 -279
  242. data/lib/active_record/relation/record_fetch_warning.rb +12 -11
  243. data/lib/active_record/relation/spawn_methods.rb +10 -9
  244. data/lib/active_record/relation/where_clause.rb +100 -66
  245. data/lib/active_record/relation.rb +829 -194
  246. data/lib/active_record/result.rb +76 -56
  247. data/lib/active_record/runtime_registry.rb +71 -13
  248. data/lib/active_record/sanitization.rb +86 -47
  249. data/lib/active_record/schema.rb +39 -23
  250. data/lib/active_record/schema_dumper.rb +140 -33
  251. data/lib/active_record/schema_migration.rb +74 -29
  252. data/lib/active_record/scoping/default.rb +73 -19
  253. data/lib/active_record/scoping/named.rb +10 -28
  254. data/lib/active_record/scoping.rb +65 -35
  255. data/lib/active_record/secure_password.rb +60 -0
  256. data/lib/active_record/secure_token.rb +34 -8
  257. data/lib/active_record/serialization.rb +11 -4
  258. data/lib/active_record/signed_id.rb +138 -0
  259. data/lib/active_record/statement_cache.rb +26 -10
  260. data/lib/active_record/store.rb +19 -14
  261. data/lib/active_record/suppressor.rb +15 -17
  262. data/lib/active_record/table_metadata.rb +46 -36
  263. data/lib/active_record/tasks/database_tasks.rb +371 -205
  264. data/lib/active_record/tasks/mysql_database_tasks.rb +43 -36
  265. data/lib/active_record/tasks/postgresql_database_tasks.rb +54 -41
  266. data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -13
  267. data/lib/active_record/test_databases.rb +5 -4
  268. data/lib/active_record/test_fixtures.rb +189 -104
  269. data/lib/active_record/testing/query_assertions.rb +121 -0
  270. data/lib/active_record/timestamp.rb +35 -25
  271. data/lib/active_record/token_for.rb +123 -0
  272. data/lib/active_record/touch_later.rb +31 -27
  273. data/lib/active_record/transaction.rb +132 -0
  274. data/lib/active_record/transactions.rb +131 -99
  275. data/lib/active_record/translation.rb +3 -5
  276. data/lib/active_record/type/adapter_specific_registry.rb +33 -18
  277. data/lib/active_record/type/hash_lookup_type_map.rb +34 -2
  278. data/lib/active_record/type/internal/timezone.rb +7 -2
  279. data/lib/active_record/type/serialized.rb +11 -6
  280. data/lib/active_record/type/time.rb +14 -0
  281. data/lib/active_record/type/type_map.rb +17 -21
  282. data/lib/active_record/type/unsigned_integer.rb +0 -1
  283. data/lib/active_record/type.rb +7 -2
  284. data/lib/active_record/type_caster/connection.rb +4 -5
  285. data/lib/active_record/type_caster/map.rb +8 -5
  286. data/lib/active_record/validations/absence.rb +1 -1
  287. data/lib/active_record/validations/associated.rb +13 -8
  288. data/lib/active_record/validations/numericality.rb +36 -0
  289. data/lib/active_record/validations/presence.rb +5 -28
  290. data/lib/active_record/validations/uniqueness.rb +88 -18
  291. data/lib/active_record/validations.rb +15 -8
  292. data/lib/active_record/version.rb +1 -1
  293. data/lib/active_record.rb +446 -40
  294. data/lib/arel/alias_predication.rb +1 -1
  295. data/lib/arel/attributes/attribute.rb +4 -8
  296. data/lib/arel/collectors/bind.rb +8 -1
  297. data/lib/arel/collectors/composite.rb +15 -0
  298. data/lib/arel/collectors/sql_string.rb +7 -0
  299. data/lib/arel/collectors/substitute_binds.rb +7 -0
  300. data/lib/arel/crud.rb +30 -22
  301. data/lib/arel/delete_manager.rb +23 -4
  302. data/lib/arel/errors.rb +10 -0
  303. data/lib/arel/factory_methods.rb +4 -0
  304. data/lib/arel/filter_predications.rb +9 -0
  305. data/lib/arel/insert_manager.rb +2 -3
  306. data/lib/arel/nodes/binary.rb +82 -9
  307. data/lib/arel/nodes/bind_param.rb +8 -0
  308. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  309. data/lib/arel/nodes/casted.rb +22 -10
  310. data/lib/arel/nodes/cte.rb +36 -0
  311. data/lib/arel/nodes/delete_statement.rb +14 -13
  312. data/lib/arel/nodes/equality.rb +6 -9
  313. data/lib/arel/nodes/filter.rb +10 -0
  314. data/lib/arel/nodes/fragments.rb +35 -0
  315. data/lib/arel/nodes/function.rb +1 -0
  316. data/lib/arel/nodes/grouping.rb +3 -0
  317. data/lib/arel/nodes/homogeneous_in.rb +68 -0
  318. data/lib/arel/nodes/in.rb +8 -1
  319. data/lib/arel/nodes/infix_operation.rb +13 -1
  320. data/lib/arel/nodes/insert_statement.rb +2 -2
  321. data/lib/arel/nodes/join_source.rb +1 -1
  322. data/lib/arel/nodes/leading_join.rb +8 -0
  323. data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
  324. data/lib/arel/nodes/node.rb +122 -11
  325. data/lib/arel/nodes/ordering.rb +27 -0
  326. data/lib/arel/nodes/select_core.rb +2 -2
  327. data/lib/arel/nodes/select_statement.rb +2 -2
  328. data/lib/arel/nodes/sql_literal.rb +16 -0
  329. data/lib/arel/nodes/table_alias.rb +11 -3
  330. data/lib/arel/nodes/unary.rb +0 -1
  331. data/lib/arel/nodes/update_statement.rb +11 -4
  332. data/lib/arel/nodes.rb +10 -3
  333. data/lib/arel/predications.rb +31 -28
  334. data/lib/arel/select_manager.rb +18 -9
  335. data/lib/arel/table.rb +21 -10
  336. data/lib/arel/tree_manager.rb +8 -15
  337. data/lib/arel/update_manager.rb +25 -5
  338. data/lib/arel/visitors/dot.rb +94 -90
  339. data/lib/arel/visitors/mysql.rb +34 -6
  340. data/lib/arel/visitors/postgresql.rb +5 -16
  341. data/lib/arel/visitors/sqlite.rb +25 -1
  342. data/lib/arel/visitors/to_sql.rb +227 -81
  343. data/lib/arel/visitors/visitor.rb +2 -3
  344. data/lib/arel/visitors.rb +0 -7
  345. data/lib/arel.rb +37 -15
  346. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  347. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  348. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  349. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  350. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +6 -1
  351. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
  352. data/lib/rails/generators/active_record/migration.rb +9 -3
  353. data/lib/rails/generators/active_record/model/USAGE +113 -0
  354. data/lib/rails/generators/active_record/model/model_generator.rb +49 -4
  355. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  356. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  357. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  358. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  359. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  360. metadata +117 -30
  361. data/lib/active_record/attribute_decorators.rb +0 -90
  362. data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
  363. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  364. data/lib/active_record/define_callbacks.rb +0 -22
  365. data/lib/active_record/null_relation.rb +0 -68
  366. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  367. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  368. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  369. data/lib/arel/attributes.rb +0 -22
  370. data/lib/arel/visitors/depth_first.rb +0 -204
  371. data/lib/arel/visitors/ibm_db.rb +0 -34
  372. data/lib/arel/visitors/informix.rb +0 -62
  373. data/lib/arel/visitors/mssql.rb +0 -157
  374. data/lib/arel/visitors/oracle.rb +0 -159
  375. data/lib/arel/visitors/oracle12.rb +0 -66
  376. data/lib/arel/visitors/where_sql.rb +0 -23
@@ -4,17 +4,18 @@ require "active_record/database_configurations"
4
4
 
5
5
  module ActiveRecord
6
6
  module Tasks # :nodoc:
7
- class DatabaseAlreadyExists < StandardError; end # :nodoc:
8
7
  class DatabaseNotSupported < StandardError; end # :nodoc:
9
8
 
9
+ # = Active Record \DatabaseTasks
10
+ #
10
11
  # ActiveRecord::Tasks::DatabaseTasks is a utility class, which encapsulates
11
12
  # logic behind common tasks used to manage database and migrations.
12
13
  #
13
- # The tasks defined here are used with Rails commands provided by Active Record.
14
+ # The tasks defined here are used with \Rails commands provided by Active Record.
14
15
  #
15
16
  # In order to use DatabaseTasks, a few config values need to be set. All the needed
16
- # config values are set by Rails already, so it's necessary to do it only if you
17
- # want to change the defaults or when you want to use Active Record outside of Rails
17
+ # config values are set by \Rails already, so it's necessary to do it only if you
18
+ # want to change the defaults or when you want to use Active Record outside of \Rails
18
19
  # (in such case after configuring the database tasks, you can also use the rake tasks
19
20
  # defined in Active Record).
20
21
  #
@@ -28,7 +29,7 @@ module ActiveRecord
28
29
  # * +seed_loader+: an object which will load seeds, it needs to respond to the +load_seed+ method.
29
30
  # * +root+: a path to the root of the application.
30
31
  #
31
- # Example usage of DatabaseTasks outside Rails could look as such:
32
+ # Example usage of DatabaseTasks outside \Rails could look as such:
32
33
  #
33
34
  # include ActiveRecord::Tasks
34
35
  # DatabaseTasks.database_configuration = YAML.load_file('my_database_config.yml')
@@ -39,35 +40,34 @@ module ActiveRecord
39
40
  module DatabaseTasks
40
41
  ##
41
42
  # :singleton-method:
42
- # Extra flags passed to database CLI tool (mysqldump/pg_dump) when calling db:structure:dump
43
+ # Extra flags passed to database CLI tool (mysqldump/pg_dump) when calling db:schema:dump
44
+ # It can be used as a string/array (the typical case) or a hash (when you use multiple adapters)
45
+ # Example:
46
+ # ActiveRecord::Tasks::DatabaseTasks.structure_dump_flags = {
47
+ # mysql2: ['--no-defaults', '--skip-add-drop-table'],
48
+ # postgres: '--no-tablespaces'
49
+ # }
43
50
  mattr_accessor :structure_dump_flags, instance_accessor: false
44
51
 
45
52
  ##
46
53
  # :singleton-method:
47
- # Extra flags passed to database CLI tool when calling db:structure:load
54
+ # Extra flags passed to database CLI tool when calling db:schema:load
55
+ # It can be used as a string/array (the typical case) or a hash (when you use multiple adapters)
48
56
  mattr_accessor :structure_load_flags, instance_accessor: false
49
57
 
50
58
  extend self
51
59
 
52
- attr_writer :current_config, :db_dir, :migrations_paths, :fixtures_path, :root, :env, :seed_loader
60
+ attr_writer :db_dir, :migrations_paths, :fixtures_path, :root, :env, :seed_loader
53
61
  attr_accessor :database_configuration
54
62
 
55
63
  LOCAL_HOSTS = ["127.0.0.1", "localhost"]
56
64
 
57
- def check_protected_environments!
58
- unless ENV["DISABLE_DATABASE_ENVIRONMENT_CHECK"]
59
- current = ActiveRecord::Base.connection.migration_context.current_environment
60
- stored = ActiveRecord::Base.connection.migration_context.last_stored_environment
61
-
62
- if ActiveRecord::Base.connection.migration_context.protected_environment?
63
- raise ActiveRecord::ProtectedEnvironmentError.new(stored)
64
- end
65
+ def check_protected_environments!(environment = env)
66
+ return if ENV["DISABLE_DATABASE_ENVIRONMENT_CHECK"]
65
67
 
66
- if stored && stored != current
67
- raise ActiveRecord::EnvironmentMismatchError.new(current: current, stored: stored)
68
- end
68
+ configs_for(env_name: environment).each do |db_config|
69
+ check_current_protected_environment!(db_config)
69
70
  end
70
- rescue ActiveRecord::NoDatabaseError
71
71
  end
72
72
 
73
73
  def register_task(pattern, task)
@@ -76,6 +76,7 @@ module ActiveRecord
76
76
  end
77
77
 
78
78
  register_task(/mysql/, "ActiveRecord::Tasks::MySQLDatabaseTasks")
79
+ register_task(/trilogy/, "ActiveRecord::Tasks::MySQLDatabaseTasks")
79
80
  register_task(/postgresql/, "ActiveRecord::Tasks::PostgreSQLDatabaseTasks")
80
81
  register_task(/sqlite/, "ActiveRecord::Tasks::SQLiteDatabaseTasks")
81
82
 
@@ -103,57 +104,41 @@ module ActiveRecord
103
104
  @env ||= Rails.env
104
105
  end
105
106
 
106
- def spec
107
- @spec ||= "primary"
107
+ def name
108
+ @name ||= "primary"
108
109
  end
109
110
 
110
111
  def seed_loader
111
112
  @seed_loader ||= Rails.application
112
113
  end
113
114
 
114
- def current_config(options = {})
115
- options.reverse_merge! env: env
116
- options[:spec] ||= "primary"
117
- if options.has_key?(:config)
118
- @current_config = options[:config]
119
- else
120
- @current_config ||= ActiveRecord::Base.configurations.configs_for(env_name: options[:env], spec_name: options[:spec]).config
121
- end
122
- end
123
-
124
- def create(*arguments)
125
- configuration = arguments.first
126
- class_for_adapter(configuration["adapter"]).new(*arguments).create
127
- $stdout.puts "Created database '#{configuration['database']}'" if verbose?
115
+ def create(configuration, *arguments)
116
+ db_config = resolve_configuration(configuration)
117
+ database_adapter_for(db_config, *arguments).create
118
+ $stdout.puts "Created database '#{db_config.database}'" if verbose?
128
119
  rescue DatabaseAlreadyExists
129
- $stderr.puts "Database '#{configuration['database']}' already exists" if verbose?
120
+ $stderr.puts "Database '#{db_config.database}' already exists" if verbose?
130
121
  rescue Exception => error
131
122
  $stderr.puts error
132
- $stderr.puts "Couldn't create '#{configuration['database']}' database. Please check your configuration."
123
+ $stderr.puts "Couldn't create '#{db_config.database}' database. Please check your configuration."
133
124
  raise
134
125
  end
135
126
 
136
127
  def create_all
137
- old_pool = ActiveRecord::Base.connection_handler.retrieve_connection_pool(ActiveRecord::Base.connection_specification_name)
138
- each_local_configuration { |configuration| create configuration }
139
- if old_pool
140
- ActiveRecord::Base.connection_handler.establish_connection(old_pool.spec.to_hash)
141
- end
128
+ db_config = migration_connection.pool.db_config
129
+
130
+ each_local_configuration { |db_config| create(db_config) }
131
+
132
+ migration_class.establish_connection(db_config)
142
133
  end
143
134
 
144
- def setup_initial_database_yaml
135
+ def setup_initial_database_yaml # :nodoc:
145
136
  return {} unless defined?(Rails)
146
137
 
147
- begin
148
- Rails.application.config.load_database_yaml
149
- rescue
150
- $stderr.puts "Rails couldn't infer whether you are using multiple databases from your database.yml and can't generate the tasks for the non-primary databases. If you'd like to use this feature, please simplify your ERB."
151
-
152
- {}
153
- end
138
+ Rails.application.config.load_database_yaml
154
139
  end
155
140
 
156
- def for_each(databases)
141
+ def for_each(databases) # :nodoc:
157
142
  return {} unless defined?(Rails)
158
143
 
159
144
  database_configs = ActiveRecord::DatabaseConfigurations.new(databases).configs_for(env_name: Rails.env)
@@ -162,105 +147,167 @@ module ActiveRecord
162
147
  return if database_configs.count == 1
163
148
 
164
149
  database_configs.each do |db_config|
165
- yield db_config.spec_name
150
+ next unless db_config.database_tasks?
151
+
152
+ yield db_config.name
166
153
  end
167
154
  end
168
155
 
169
- def raise_for_multi_db(environment = env, command:)
170
- db_configs = ActiveRecord::Base.configurations.configs_for(env_name: environment)
156
+ def raise_for_multi_db(environment = env, command:) # :nodoc:
157
+ db_configs = configs_for(env_name: environment)
171
158
 
172
159
  if db_configs.count > 1
173
160
  dbs_list = []
174
161
 
175
162
  db_configs.each do |db|
176
- dbs_list << "#{command}:#{db.spec_name}"
163
+ dbs_list << "#{command}:#{db.name}"
177
164
  end
178
165
 
179
166
  raise "You're using a multiple database application. To use `#{command}` you must run the namespaced task with a VERSION. Available tasks are #{dbs_list.to_sentence}."
180
167
  end
181
168
  end
182
169
 
183
- def create_current(environment = env, spec_name = nil)
184
- each_current_configuration(environment, spec_name) { |configuration|
185
- create configuration
186
- }
187
- ActiveRecord::Base.establish_connection(environment.to_sym)
170
+ def create_current(environment = env, name = nil)
171
+ each_current_configuration(environment, name) { |db_config| create(db_config) }
172
+
173
+ migration_class.establish_connection(environment.to_sym)
188
174
  end
189
175
 
190
- def drop(*arguments)
191
- configuration = arguments.first
192
- class_for_adapter(configuration["adapter"]).new(*arguments).drop
193
- $stdout.puts "Dropped database '#{configuration['database']}'" if verbose?
176
+ def prepare_all
177
+ seed = false
178
+ dump_db_configs = []
179
+
180
+ each_current_configuration(env) do |db_config|
181
+ with_temporary_pool(db_config) do
182
+ begin
183
+ database_initialized = migration_connection_pool.schema_migration.table_exists?
184
+ rescue ActiveRecord::NoDatabaseError
185
+ create(db_config)
186
+ retry
187
+ end
188
+
189
+ unless database_initialized
190
+ if File.exist?(schema_dump_path(db_config))
191
+ load_schema(db_config, ActiveRecord.schema_format, nil)
192
+ end
193
+
194
+ seed = true
195
+ end
196
+ end
197
+ end
198
+
199
+ each_current_environment(env) do |environment|
200
+ db_configs_with_versions(environment).sort.each do |version, db_configs|
201
+ dump_db_configs |= db_configs
202
+
203
+ db_configs.each do |db_config|
204
+ with_temporary_pool(db_config) do
205
+ migrate(version)
206
+ end
207
+ end
208
+ end
209
+ end
210
+
211
+ # Dump schema for databases that were migrated.
212
+ if ActiveRecord.dump_schema_after_migration
213
+ dump_db_configs.each do |db_config|
214
+ with_temporary_pool(db_config) do
215
+ dump_schema(db_config)
216
+ end
217
+ end
218
+ end
219
+
220
+ load_seed if seed
221
+ end
222
+
223
+ def drop(configuration, *arguments)
224
+ db_config = resolve_configuration(configuration)
225
+ database_adapter_for(db_config, *arguments).drop
226
+ $stdout.puts "Dropped database '#{db_config.database}'" if verbose?
194
227
  rescue ActiveRecord::NoDatabaseError
195
- $stderr.puts "Database '#{configuration['database']}' does not exist"
228
+ $stderr.puts "Database '#{db_config.database}' does not exist"
196
229
  rescue Exception => error
197
230
  $stderr.puts error
198
- $stderr.puts "Couldn't drop database '#{configuration['database']}'"
231
+ $stderr.puts "Couldn't drop database '#{db_config.database}'"
199
232
  raise
200
233
  end
201
234
 
202
235
  def drop_all
203
- each_local_configuration { |configuration| drop configuration }
236
+ each_local_configuration { |db_config| drop(db_config) }
204
237
  end
205
238
 
206
239
  def drop_current(environment = env)
207
- each_current_configuration(environment) { |configuration|
208
- drop configuration
209
- }
240
+ each_current_configuration(environment) { |db_config| drop(db_config) }
210
241
  end
211
242
 
212
- def truncate_tables(configuration)
213
- ActiveRecord::Base.connected_to(database: { truncation: configuration }) do
214
- conn = ActiveRecord::Base.connection
215
- table_names = conn.tables
216
- table_names -= [
217
- conn.schema_migration.table_name,
218
- InternalMetadata.table_name
219
- ]
220
-
221
- ActiveRecord::Base.connection.truncate_tables(*table_names)
243
+ def truncate_tables(db_config)
244
+ with_temporary_connection(db_config) do |conn|
245
+ conn.truncate_tables(*conn.tables)
222
246
  end
223
247
  end
224
248
  private :truncate_tables
225
249
 
226
250
  def truncate_all(environment = env)
227
- ActiveRecord::Base.configurations.configs_for(env_name: environment).each do |db_config|
228
- truncate_tables db_config.config
251
+ configs_for(env_name: environment).each do |db_config|
252
+ truncate_tables(db_config)
229
253
  end
230
254
  end
231
255
 
232
- def migrate
233
- check_target_version
234
-
256
+ def migrate(version = nil)
235
257
  scope = ENV["SCOPE"]
236
258
  verbose_was, Migration.verbose = Migration.verbose, verbose?
237
259
 
238
- Base.connection.migration_context.migrate(target_version) do |migration|
239
- scope.blank? || scope == migration.scope
260
+ check_target_version
261
+
262
+ migration_connection_pool.migration_context.migrate(target_version) do |migration|
263
+ if version.blank?
264
+ scope.blank? || scope == migration.scope
265
+ else
266
+ migration.version == version
267
+ end
268
+ end.tap do |migrations_ran|
269
+ Migration.write("No migrations ran. (using #{scope} scope)") if scope.present? && migrations_ran.empty?
240
270
  end
241
271
 
242
- ActiveRecord::Base.clear_cache!
272
+ migration_connection_pool.schema_cache.clear!
243
273
  ensure
244
274
  Migration.verbose = verbose_was
245
275
  end
246
276
 
277
+ def db_configs_with_versions(environment = env) # :nodoc:
278
+ db_configs_with_versions = Hash.new { |h, k| h[k] = [] }
279
+
280
+ with_temporary_pool_for_each(env: environment) do |pool|
281
+ db_config = pool.db_config
282
+ versions_to_run = pool.migration_context.pending_migration_versions
283
+ target_version = ActiveRecord::Tasks::DatabaseTasks.target_version
284
+
285
+ versions_to_run.each do |version|
286
+ next if target_version && target_version != version
287
+ db_configs_with_versions[version] << db_config
288
+ end
289
+ end
290
+
291
+ db_configs_with_versions
292
+ end
293
+
247
294
  def migrate_status
248
- unless ActiveRecord::Base.connection.schema_migration.table_exists?
295
+ unless migration_connection_pool.schema_migration.table_exists?
249
296
  Kernel.abort "Schema migrations table does not exist yet."
250
297
  end
251
298
 
252
299
  # output
253
- puts "\ndatabase: #{ActiveRecord::Base.connection_config[:database]}\n\n"
300
+ puts "\ndatabase: #{migration_connection_pool.db_config.database}\n\n"
254
301
  puts "#{'Status'.center(8)} #{'Migration ID'.ljust(14)} Migration Name"
255
302
  puts "-" * 50
256
- ActiveRecord::Base.connection.migration_context.migrations_status.each do |status, version, name|
303
+ migration_connection_pool.migration_context.migrations_status.each do |status, version, name|
257
304
  puts "#{status.center(8)} #{version.ljust(14)} #{name}"
258
305
  end
259
306
  puts
260
307
  end
261
308
 
262
309
  def check_target_version
263
- if target_version && !(Migration::MigrationFilenameRegexp.match?(ENV["VERSION"]) || /\A\d+\z/.match?(ENV["VERSION"]))
310
+ if target_version && !Migration.valid_version_format?(ENV["VERSION"])
264
311
  raise "Invalid format of target version: `VERSION=#{ENV['VERSION']}`"
265
312
  end
266
313
  end
@@ -269,167 +316,180 @@ module ActiveRecord
269
316
  ENV["VERSION"].to_i if ENV["VERSION"] && !ENV["VERSION"].empty?
270
317
  end
271
318
 
272
- def charset_current(environment = env, specification_name = spec)
273
- charset ActiveRecord::Base.configurations.configs_for(env_name: environment, spec_name: specification_name).config
319
+ def charset_current(env_name = env, db_name = name)
320
+ db_config = configs_for(env_name: env_name, name: db_name)
321
+ charset(db_config)
274
322
  end
275
323
 
276
- def charset(*arguments)
277
- configuration = arguments.first
278
- class_for_adapter(configuration["adapter"]).new(*arguments).charset
324
+ def charset(configuration, *arguments)
325
+ db_config = resolve_configuration(configuration)
326
+ database_adapter_for(db_config, *arguments).charset
279
327
  end
280
328
 
281
- def collation_current(environment = env, specification_name = spec)
282
- collation ActiveRecord::Base.configurations.configs_for(env_name: environment, spec_name: specification_name).config
329
+ def collation_current(env_name = env, db_name = name)
330
+ db_config = configs_for(env_name: env_name, name: db_name)
331
+ collation(db_config)
283
332
  end
284
333
 
285
- def collation(*arguments)
286
- configuration = arguments.first
287
- class_for_adapter(configuration["adapter"]).new(*arguments).collation
334
+ def collation(configuration, *arguments)
335
+ db_config = resolve_configuration(configuration)
336
+ database_adapter_for(db_config, *arguments).collation
288
337
  end
289
338
 
290
339
  def purge(configuration)
291
- class_for_adapter(configuration["adapter"]).new(configuration).purge
340
+ db_config = resolve_configuration(configuration)
341
+ database_adapter_for(db_config).purge
292
342
  end
293
343
 
294
344
  def purge_all
295
- each_local_configuration { |configuration|
296
- purge configuration
297
- }
345
+ each_local_configuration { |db_config| purge(db_config) }
298
346
  end
299
347
 
300
348
  def purge_current(environment = env)
301
- each_current_configuration(environment) { |configuration|
302
- purge configuration
303
- }
304
- ActiveRecord::Base.establish_connection(environment.to_sym)
349
+ each_current_configuration(environment) { |db_config| purge(db_config) }
350
+
351
+ migration_class.establish_connection(environment.to_sym)
305
352
  end
306
353
 
307
- def structure_dump(*arguments)
308
- configuration = arguments.first
309
- filename = arguments.delete_at 1
310
- class_for_adapter(configuration["adapter"]).new(*arguments).structure_dump(filename, structure_dump_flags)
354
+ def structure_dump(configuration, *arguments)
355
+ db_config = resolve_configuration(configuration)
356
+ filename = arguments.delete_at(0)
357
+ flags = structure_dump_flags_for(db_config.adapter)
358
+ database_adapter_for(db_config, *arguments).structure_dump(filename, flags)
311
359
  end
312
360
 
313
- def structure_load(*arguments)
314
- configuration = arguments.first
315
- filename = arguments.delete_at 1
316
- class_for_adapter(configuration["adapter"]).new(*arguments).structure_load(filename, structure_load_flags)
361
+ def structure_load(configuration, *arguments)
362
+ db_config = resolve_configuration(configuration)
363
+ filename = arguments.delete_at(0)
364
+ flags = structure_load_flags_for(db_config.adapter)
365
+ database_adapter_for(db_config, *arguments).structure_load(filename, flags)
317
366
  end
318
367
 
319
- def load_schema(configuration, format = ActiveRecord::Base.schema_format, file = nil, environment = env, spec_name = "primary") # :nodoc:
320
- file ||= dump_filename(spec_name, format)
368
+ def load_schema(db_config, format = ActiveRecord.schema_format, file = nil) # :nodoc:
369
+ file ||= schema_dump_path(db_config, format)
370
+ return unless file
321
371
 
322
372
  verbose_was, Migration.verbose = Migration.verbose, verbose? && ENV["VERBOSE"]
323
373
  check_schema_file(file)
324
- ActiveRecord::Base.establish_connection(configuration)
325
374
 
326
375
  case format
327
376
  when :ruby
328
377
  load(file)
329
378
  when :sql
330
- structure_load(configuration, file)
379
+ structure_load(db_config, file)
331
380
  else
332
381
  raise ArgumentError, "unknown format #{format.inspect}"
333
382
  end
334
- ActiveRecord::InternalMetadata.create_table
335
- ActiveRecord::InternalMetadata[:environment] = environment
336
- ActiveRecord::InternalMetadata[:schema_sha1] = schema_sha1(file)
383
+
384
+ migration_connection_pool.internal_metadata.create_table_and_set_flags(db_config.env_name, schema_sha1(file))
337
385
  ensure
338
386
  Migration.verbose = verbose_was
339
387
  end
340
388
 
341
- def schema_up_to_date?(configuration, format = ActiveRecord::Base.schema_format, file = nil, environment = env, spec_name = "primary")
342
- file ||= dump_filename(spec_name, format)
389
+ def schema_up_to_date?(configuration, format = ActiveRecord.schema_format, file = nil)
390
+ db_config = resolve_configuration(configuration)
343
391
 
344
- return true unless File.exist?(file)
392
+ file ||= schema_dump_path(db_config)
345
393
 
346
- ActiveRecord::Base.establish_connection(configuration)
347
- return false unless ActiveRecord::InternalMetadata.table_exists?
348
- ActiveRecord::InternalMetadata[:schema_sha1] == schema_sha1(file)
349
- end
394
+ return true unless file && File.exist?(file)
350
395
 
351
- def reconstruct_from_schema(configuration, format = ActiveRecord::Base.schema_format, file = nil, environment = env, spec_name = "primary") # :nodoc:
352
- file ||= dump_filename(spec_name, format)
396
+ with_temporary_pool(db_config) do |pool|
397
+ internal_metadata = pool.internal_metadata
398
+ return false unless internal_metadata.enabled?
399
+ return false unless internal_metadata.table_exists?
353
400
 
354
- check_schema_file(file)
401
+ internal_metadata[:schema_sha1] == schema_sha1(file)
402
+ end
403
+ end
355
404
 
356
- ActiveRecord::Base.establish_connection(configuration)
405
+ def reconstruct_from_schema(db_config, format = ActiveRecord.schema_format, file = nil) # :nodoc:
406
+ file ||= schema_dump_path(db_config, format)
357
407
 
358
- if schema_up_to_date?(configuration, format, file, environment, spec_name)
359
- truncate_tables(configuration)
360
- else
361
- purge(configuration)
362
- load_schema(configuration, format, file, environment, spec_name)
408
+ check_schema_file(file) if file
409
+
410
+ with_temporary_pool(db_config, clobber: true) do
411
+ if schema_up_to_date?(db_config, format, file)
412
+ truncate_tables(db_config) unless ENV["SKIP_TEST_DATABASE_TRUNCATE"]
413
+ else
414
+ purge(db_config)
415
+ load_schema(db_config, format, file)
416
+ end
417
+ rescue ActiveRecord::NoDatabaseError
418
+ create(db_config)
419
+ load_schema(db_config, format, file)
363
420
  end
364
- rescue ActiveRecord::NoDatabaseError
365
- create(configuration)
366
- load_schema(configuration, format, file, environment, spec_name)
367
421
  end
368
422
 
369
- def dump_schema(configuration, format = ActiveRecord::Base.schema_format, spec_name = "primary") # :nodoc:
423
+ def dump_schema(db_config, format = ActiveRecord.schema_format) # :nodoc:
424
+ return unless db_config.schema_dump
425
+
370
426
  require "active_record/schema_dumper"
371
- filename = dump_filename(spec_name, format)
372
- connection = ActiveRecord::Base.connection
427
+ filename = schema_dump_path(db_config, format)
428
+ return unless filename
373
429
 
430
+ FileUtils.mkdir_p(db_dir)
374
431
  case format
375
432
  when :ruby
376
433
  File.open(filename, "w:utf-8") do |file|
377
- ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
434
+ ActiveRecord::SchemaDumper.dump(migration_connection_pool, file)
378
435
  end
379
436
  when :sql
380
- structure_dump(configuration, filename)
381
- if connection.schema_migration.table_exists?
437
+ structure_dump(db_config, filename)
438
+ if migration_connection_pool.schema_migration.table_exists?
382
439
  File.open(filename, "a") do |f|
383
- f.puts connection.dump_schema_information
440
+ f.puts migration_connection.dump_schema_information
384
441
  f.print "\n"
385
442
  end
386
443
  end
387
444
  end
388
445
  end
389
446
 
390
- def schema_file(format = ActiveRecord::Base.schema_format)
391
- File.join(db_dir, schema_file_type(format))
392
- end
447
+ def schema_dump_path(db_config, format = ActiveRecord.schema_format)
448
+ return ENV["SCHEMA"] if ENV["SCHEMA"]
393
449
 
394
- def schema_file_type(format = ActiveRecord::Base.schema_format)
395
- case format
396
- when :ruby
397
- "schema.rb"
398
- when :sql
399
- "structure.sql"
400
- end
401
- end
450
+ filename = db_config.schema_dump(format)
451
+ return unless filename
402
452
 
403
- def dump_filename(namespace, format = ActiveRecord::Base.schema_format)
404
- filename = if namespace == "primary"
405
- schema_file_type(format)
453
+ if File.dirname(filename) == ActiveRecord::Tasks::DatabaseTasks.db_dir
454
+ filename
406
455
  else
407
- "#{namespace}_#{schema_file_type(format)}"
456
+ File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, filename)
408
457
  end
409
-
410
- ENV["SCHEMA"] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, filename)
411
458
  end
412
459
 
413
- def cache_dump_filename(namespace)
414
- filename = if namespace == "primary"
415
- "schema_cache.yml"
460
+ def cache_dump_filename(db_config_or_name, schema_cache_path: nil)
461
+ if db_config_or_name.is_a?(DatabaseConfigurations::DatabaseConfig)
462
+ schema_cache_path ||
463
+ db_config_or_name.schema_cache_path ||
464
+ schema_cache_env ||
465
+ db_config_or_name.default_schema_cache_path(ActiveRecord::Tasks::DatabaseTasks.db_dir)
416
466
  else
417
- "#{namespace}_schema_cache.yml"
418
- end
467
+ ActiveRecord.deprecator.warn(<<~MSG.squish)
468
+ Passing a database name to `cache_dump_filename` is deprecated and will be removed in Rails 8.0. Pass a
469
+ `ActiveRecord::DatabaseConfigurations::DatabaseConfig` object instead.
470
+ MSG
471
+
472
+ filename = if ActiveRecord::Base.configurations.primary?(db_config_or_name)
473
+ "schema_cache.yml"
474
+ else
475
+ "#{db_config_or_name}_schema_cache.yml"
476
+ end
419
477
 
420
- ENV["SCHEMA_CACHE"] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, filename)
478
+ schema_cache_path || schema_cache_env || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, filename)
479
+ end
421
480
  end
422
481
 
423
- def load_schema_current(format = ActiveRecord::Base.schema_format, file = nil, environment = env)
424
- each_current_configuration(environment) { |configuration, spec_name, env|
425
- load_schema(configuration, format, file, env, spec_name)
426
- }
427
- ActiveRecord::Base.establish_connection(environment.to_sym)
482
+ def load_schema_current(format = ActiveRecord.schema_format, file = nil, environment = env)
483
+ each_current_configuration(environment) do |db_config|
484
+ with_temporary_connection(db_config) do
485
+ load_schema(db_config, format, file)
486
+ end
487
+ end
428
488
  end
429
489
 
430
490
  def check_schema_file(filename)
431
491
  unless File.exist?(filename)
432
- message = +%{#{filename} doesn't exist yet. Run `rails db:migrate` to create it, then try again.}
492
+ message = +%{#{filename} doesn't exist yet. Run `bin/rails db:migrate` to create it, then try again.}
433
493
  message << %{ If you do not intend to use a database, you should instead alter #{Rails.root}/config/application.rb to limit the frameworks that will be loaded.} if defined?(::Rails.root)
434
494
  Kernel.abort message
435
495
  end
@@ -447,59 +507,165 @@ module ActiveRecord
447
507
 
448
508
  # Dumps the schema cache in YAML format for the connection into the file
449
509
  #
450
- # ==== Examples:
451
- # ActiveRecord::Tasks::DatabaseTasks.dump_schema_cache(ActiveRecord::Base.connection, "tmp/schema_dump.yaml")
452
- def dump_schema_cache(conn, filename)
453
- conn.schema_cache.clear!
454
- conn.data_sources.each { |table| conn.schema_cache.add(table) }
455
- open(filename, "wb") { |f| f.write(YAML.dump(conn.schema_cache)) }
510
+ # ==== Examples
511
+ # ActiveRecord::Tasks::DatabaseTasks.dump_schema_cache(ActiveRecord::Base.lease_connection, "tmp/schema_dump.yaml")
512
+ def dump_schema_cache(conn_or_pool, filename)
513
+ conn_or_pool.schema_cache.dump_to(filename)
514
+ end
515
+
516
+ def clear_schema_cache(filename)
517
+ FileUtils.rm_f filename, verbose: false
518
+ end
519
+
520
+ def with_temporary_pool_for_each(env: ActiveRecord::Tasks::DatabaseTasks.env, name: nil, clobber: false, &block) # :nodoc:
521
+ if name
522
+ db_config = ActiveRecord::Base.configurations.configs_for(env_name: env, name: name)
523
+ with_temporary_pool(db_config, clobber: clobber, &block)
524
+ else
525
+ ActiveRecord::Base.configurations.configs_for(env_name: env, name: name).each do |db_config|
526
+ with_temporary_pool(db_config, clobber: clobber, &block)
527
+ end
528
+ end
529
+ end
530
+
531
+ def with_temporary_connection(db_config, clobber: false, &block) # :nodoc:
532
+ with_temporary_pool(db_config, clobber: clobber) do |pool|
533
+ pool.with_connection(&block)
534
+ end
535
+ end
536
+
537
+ def migration_class # :nodoc:
538
+ ActiveRecord::Base
539
+ end
540
+
541
+ def migration_connection # :nodoc:
542
+ migration_class.lease_connection
543
+ end
544
+
545
+ def migration_connection_pool # :nodoc:
546
+ migration_class.connection_pool
456
547
  end
457
548
 
458
549
  private
550
+ def schema_cache_env
551
+ if ENV["SCHEMA_CACHE"]
552
+ ActiveRecord.deprecator.warn(<<~MSG.squish)
553
+ Setting `ENV["SCHEMA_CACHE"]` is deprecated and will be removed in Rails 8.0.
554
+ Configure the `:schema_cache_path` in the database configuration instead.
555
+ MSG
556
+
557
+ nil
558
+ end
559
+ end
560
+
561
+ def with_temporary_pool(db_config, clobber: false)
562
+ original_db_config = migration_class.connection_db_config
563
+ pool = migration_class.connection_handler.establish_connection(db_config, clobber: clobber)
564
+
565
+ yield pool
566
+ ensure
567
+ migration_class.connection_handler.establish_connection(original_db_config, clobber: clobber)
568
+ end
569
+
570
+ def configs_for(**options)
571
+ Base.configurations.configs_for(**options)
572
+ end
573
+
574
+ def resolve_configuration(configuration)
575
+ Base.configurations.resolve(configuration)
576
+ end
577
+
459
578
  def verbose?
460
579
  ENV["VERBOSE"] ? ENV["VERBOSE"] != "false" : true
461
580
  end
462
581
 
582
+ # Create a new instance for the specified db configuration object
583
+ # For classes that have been converted to use db_config objects, pass a
584
+ # `DatabaseConfig`, otherwise pass a `Hash`
585
+ def database_adapter_for(db_config, *arguments)
586
+ klass = class_for_adapter(db_config.adapter)
587
+ converted = klass.respond_to?(:using_database_configurations?) && klass.using_database_configurations?
588
+
589
+ config = converted ? db_config : db_config.configuration_hash
590
+ klass.new(config, *arguments)
591
+ end
592
+
463
593
  def class_for_adapter(adapter)
464
- _key, task = @tasks.each_pair.detect { |pattern, _task| adapter[pattern] }
594
+ _key, task = @tasks.reverse_each.detect { |pattern, _task| adapter[pattern] }
465
595
  unless task
466
596
  raise DatabaseNotSupported, "Rake tasks not supported by '#{adapter}' adapter"
467
597
  end
468
598
  task.is_a?(String) ? task.constantize : task
469
599
  end
470
600
 
471
- def each_current_configuration(environment, spec_name = nil)
472
- environments = [environment]
473
- environments << "test" if environment == "development"
601
+ def each_current_configuration(environment, name = nil)
602
+ each_current_environment(environment) do |env|
603
+ configs_for(env_name: env).each do |db_config|
604
+ next if name && name != db_config.name
474
605
 
475
- environments.each do |env|
476
- ActiveRecord::Base.configurations.configs_for(env_name: env).each do |db_config|
477
- next if spec_name && spec_name != db_config.spec_name
478
-
479
- yield db_config.config, db_config.spec_name, env
606
+ yield db_config
480
607
  end
481
608
  end
482
609
  end
483
610
 
611
+ def each_current_environment(environment, &block)
612
+ environments = [environment]
613
+ environments << "test" if environment == "development" && !ENV["SKIP_TEST_DATABASE"] && !ENV["DATABASE_URL"]
614
+ environments.each(&block)
615
+ end
616
+
484
617
  def each_local_configuration
485
- ActiveRecord::Base.configurations.configs_for.each do |db_config|
486
- configuration = db_config.config
487
- next unless configuration["database"]
618
+ configs_for.each do |db_config|
619
+ next unless db_config.database
488
620
 
489
- if local_database?(configuration)
490
- yield configuration
621
+ if local_database?(db_config)
622
+ yield db_config
491
623
  else
492
- $stderr.puts "This task only modifies local databases. #{configuration['database']} is on a remote host."
624
+ $stderr.puts "This task only modifies local databases. #{db_config.database} is on a remote host."
493
625
  end
494
626
  end
495
627
  end
496
628
 
497
- def local_database?(configuration)
498
- configuration["host"].blank? || LOCAL_HOSTS.include?(configuration["host"])
629
+ def local_database?(db_config)
630
+ host = db_config.host
631
+ host.blank? || LOCAL_HOSTS.include?(host)
499
632
  end
500
633
 
501
634
  def schema_sha1(file)
502
- Digest::SHA1.hexdigest(File.read(file))
635
+ OpenSSL::Digest::SHA1.hexdigest(File.read(file))
636
+ end
637
+
638
+ def structure_dump_flags_for(adapter)
639
+ if structure_dump_flags.is_a?(Hash)
640
+ structure_dump_flags[adapter.to_sym]
641
+ else
642
+ structure_dump_flags
643
+ end
644
+ end
645
+
646
+ def structure_load_flags_for(adapter)
647
+ if structure_load_flags.is_a?(Hash)
648
+ structure_load_flags[adapter.to_sym]
649
+ else
650
+ structure_load_flags
651
+ end
652
+ end
653
+
654
+ def check_current_protected_environment!(db_config)
655
+ with_temporary_pool(db_config) do |pool|
656
+ migration_context = pool.migration_context
657
+ current = migration_context.current_environment
658
+ stored = migration_context.last_stored_environment
659
+
660
+ if migration_context.protected_environment?
661
+ raise ActiveRecord::ProtectedEnvironmentError.new(stored)
662
+ end
663
+
664
+ if stored && stored != current
665
+ raise ActiveRecord::EnvironmentMismatchError.new(current: current, stored: stored)
666
+ end
667
+ rescue ActiveRecord::NoDatabaseError
668
+ end
503
669
  end
504
670
  end
505
671
  end