activerecord 6.1.7 → 7.2.2

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 (333) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +616 -1290
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +31 -31
  5. data/examples/performance.rb +2 -2
  6. data/lib/active_record/aggregations.rb +17 -14
  7. data/lib/active_record/association_relation.rb +2 -12
  8. data/lib/active_record/associations/alias_tracker.rb +25 -19
  9. data/lib/active_record/associations/association.rb +60 -21
  10. data/lib/active_record/associations/association_scope.rb +17 -12
  11. data/lib/active_record/associations/belongs_to_association.rb +37 -11
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -4
  13. data/lib/active_record/associations/builder/association.rb +11 -5
  14. data/lib/active_record/associations/builder/belongs_to.rb +41 -14
  15. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
  17. data/lib/active_record/associations/builder/has_many.rb +4 -4
  18. data/lib/active_record/associations/builder/has_one.rb +4 -4
  19. data/lib/active_record/associations/builder/singular_association.rb +6 -2
  20. data/lib/active_record/associations/collection_association.rb +46 -36
  21. data/lib/active_record/associations/collection_proxy.rb +44 -16
  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 +10 -3
  25. data/lib/active_record/associations/has_many_association.rb +29 -19
  26. data/lib/active_record/associations/has_many_through_association.rb +19 -8
  27. data/lib/active_record/associations/has_one_association.rb +20 -10
  28. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  29. data/lib/active_record/associations/join_dependency/join_association.rb +30 -27
  30. data/lib/active_record/associations/join_dependency.rb +28 -20
  31. data/lib/active_record/associations/nested_error.rb +47 -0
  32. data/lib/active_record/associations/preloader/association.rb +212 -53
  33. data/lib/active_record/associations/preloader/batch.rb +48 -0
  34. data/lib/active_record/associations/preloader/branch.rb +153 -0
  35. data/lib/active_record/associations/preloader/through_association.rb +50 -16
  36. data/lib/active_record/associations/preloader.rb +50 -121
  37. data/lib/active_record/associations/singular_association.rb +15 -3
  38. data/lib/active_record/associations/through_association.rb +25 -14
  39. data/lib/active_record/associations.rb +429 -522
  40. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  41. data/lib/active_record/attribute_assignment.rb +1 -5
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
  43. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  44. data/lib/active_record/attribute_methods/dirty.rb +73 -22
  45. data/lib/active_record/attribute_methods/primary_key.rb +47 -27
  46. data/lib/active_record/attribute_methods/query.rb +31 -19
  47. data/lib/active_record/attribute_methods/read.rb +14 -11
  48. data/lib/active_record/attribute_methods/serialization.rb +174 -37
  49. data/lib/active_record/attribute_methods/time_zone_conversion.rb +15 -9
  50. data/lib/active_record/attribute_methods/write.rb +12 -15
  51. data/lib/active_record/attribute_methods.rb +164 -52
  52. data/lib/active_record/attributes.rb +57 -54
  53. data/lib/active_record/autosave_association.rb +74 -57
  54. data/lib/active_record/base.rb +27 -5
  55. data/lib/active_record/callbacks.rb +19 -35
  56. data/lib/active_record/coders/column_serializer.rb +61 -0
  57. data/lib/active_record/coders/json.rb +1 -1
  58. data/lib/active_record/coders/yaml_column.rb +70 -46
  59. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +284 -0
  60. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
  61. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +79 -0
  62. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +325 -604
  63. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
  64. data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -60
  65. data/lib/active_record/connection_adapters/abstract/query_cache.rb +230 -64
  66. data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -131
  67. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  68. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
  69. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
  70. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  71. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +378 -143
  72. data/lib/active_record/connection_adapters/abstract/transaction.rb +361 -76
  73. data/lib/active_record/connection_adapters/abstract_adapter.rb +624 -163
  74. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +348 -165
  75. data/lib/active_record/connection_adapters/column.rb +13 -0
  76. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  77. data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -130
  78. data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -55
  79. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  80. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  81. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  82. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +45 -14
  83. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
  84. data/lib/active_record/connection_adapters/mysql2_adapter.rb +107 -68
  85. data/lib/active_record/connection_adapters/pool_config.rb +26 -16
  86. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  87. data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
  88. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +114 -54
  89. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  94. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  96. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
  97. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  100. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  101. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  102. data/lib/active_record/connection_adapters/postgresql/quoting.rb +137 -104
  103. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
  104. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
  105. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +173 -3
  106. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
  107. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +403 -77
  108. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  109. data/lib/active_record/connection_adapters/postgresql_adapter.rb +520 -253
  110. data/lib/active_record/connection_adapters/schema_cache.rb +326 -102
  111. data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
  112. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +78 -55
  113. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +68 -54
  114. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  115. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
  116. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  117. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +66 -22
  118. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +372 -130
  119. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  120. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  121. data/lib/active_record/connection_adapters/trilogy_adapter.rb +229 -0
  122. data/lib/active_record/connection_adapters.rb +130 -6
  123. data/lib/active_record/connection_handling.rb +132 -146
  124. data/lib/active_record/core.rb +310 -253
  125. data/lib/active_record/counter_cache.rb +68 -34
  126. data/lib/active_record/database_configurations/connection_url_resolver.rb +10 -4
  127. data/lib/active_record/database_configurations/database_config.rb +34 -10
  128. data/lib/active_record/database_configurations/hash_config.rb +107 -31
  129. data/lib/active_record/database_configurations/url_config.rb +38 -13
  130. data/lib/active_record/database_configurations.rb +96 -60
  131. data/lib/active_record/delegated_type.rb +90 -20
  132. data/lib/active_record/deprecator.rb +7 -0
  133. data/lib/active_record/destroy_association_async_job.rb +4 -2
  134. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  135. data/lib/active_record/dynamic_matchers.rb +3 -3
  136. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  137. data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
  138. data/lib/active_record/encryption/cipher.rb +53 -0
  139. data/lib/active_record/encryption/config.rb +68 -0
  140. data/lib/active_record/encryption/configurable.rb +60 -0
  141. data/lib/active_record/encryption/context.rb +42 -0
  142. data/lib/active_record/encryption/contexts.rb +76 -0
  143. data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
  144. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  145. data/lib/active_record/encryption/encryptable_record.rb +230 -0
  146. data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -0
  147. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  148. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  149. data/lib/active_record/encryption/encryptor.rb +170 -0
  150. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  151. data/lib/active_record/encryption/errors.rb +15 -0
  152. data/lib/active_record/encryption/extended_deterministic_queries.rb +157 -0
  153. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  154. data/lib/active_record/encryption/key.rb +28 -0
  155. data/lib/active_record/encryption/key_generator.rb +53 -0
  156. data/lib/active_record/encryption/key_provider.rb +46 -0
  157. data/lib/active_record/encryption/message.rb +33 -0
  158. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  159. data/lib/active_record/encryption/message_serializer.rb +96 -0
  160. data/lib/active_record/encryption/null_encryptor.rb +25 -0
  161. data/lib/active_record/encryption/properties.rb +76 -0
  162. data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
  163. data/lib/active_record/encryption/scheme.rb +100 -0
  164. data/lib/active_record/encryption.rb +58 -0
  165. data/lib/active_record/enum.rb +170 -62
  166. data/lib/active_record/errors.rb +210 -27
  167. data/lib/active_record/explain.rb +21 -12
  168. data/lib/active_record/explain_registry.rb +11 -6
  169. data/lib/active_record/explain_subscriber.rb +1 -1
  170. data/lib/active_record/fixture_set/file.rb +15 -1
  171. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  172. data/lib/active_record/fixture_set/render_context.rb +2 -0
  173. data/lib/active_record/fixture_set/table_row.rb +70 -14
  174. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  175. data/lib/active_record/fixtures.rb +179 -112
  176. data/lib/active_record/future_result.rb +178 -0
  177. data/lib/active_record/gem_version.rb +4 -4
  178. data/lib/active_record/inheritance.rb +85 -31
  179. data/lib/active_record/insert_all.rb +148 -32
  180. data/lib/active_record/integration.rb +14 -10
  181. data/lib/active_record/internal_metadata.rb +123 -23
  182. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  183. data/lib/active_record/locking/optimistic.rb +43 -27
  184. data/lib/active_record/locking/pessimistic.rb +15 -6
  185. data/lib/active_record/log_subscriber.rb +41 -29
  186. data/lib/active_record/marshalling.rb +59 -0
  187. data/lib/active_record/message_pack.rb +124 -0
  188. data/lib/active_record/middleware/database_selector/resolver.rb +10 -10
  189. data/lib/active_record/middleware/database_selector.rb +23 -13
  190. data/lib/active_record/middleware/shard_selector.rb +62 -0
  191. data/lib/active_record/migration/command_recorder.rb +113 -16
  192. data/lib/active_record/migration/compatibility.rb +235 -46
  193. data/lib/active_record/migration/default_strategy.rb +22 -0
  194. data/lib/active_record/migration/execution_strategy.rb +19 -0
  195. data/lib/active_record/migration/join_table.rb +1 -1
  196. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  197. data/lib/active_record/migration.rb +374 -177
  198. data/lib/active_record/model_schema.rb +145 -158
  199. data/lib/active_record/nested_attributes.rb +61 -23
  200. data/lib/active_record/no_touching.rb +3 -3
  201. data/lib/active_record/normalization.rb +163 -0
  202. data/lib/active_record/persistence.rb +282 -283
  203. data/lib/active_record/promise.rb +84 -0
  204. data/lib/active_record/query_cache.rb +18 -25
  205. data/lib/active_record/query_logs.rb +189 -0
  206. data/lib/active_record/query_logs_formatter.rb +41 -0
  207. data/lib/active_record/querying.rb +44 -9
  208. data/lib/active_record/railtie.rb +229 -71
  209. data/lib/active_record/railties/controller_runtime.rb +25 -11
  210. data/lib/active_record/railties/databases.rake +189 -256
  211. data/lib/active_record/railties/job_runtime.rb +23 -0
  212. data/lib/active_record/readonly_attributes.rb +41 -3
  213. data/lib/active_record/reflection.rb +332 -103
  214. data/lib/active_record/relation/batches/batch_enumerator.rb +38 -9
  215. data/lib/active_record/relation/batches.rb +200 -65
  216. data/lib/active_record/relation/calculations.rb +301 -112
  217. data/lib/active_record/relation/delegation.rb +33 -22
  218. data/lib/active_record/relation/finder_methods.rb +123 -52
  219. data/lib/active_record/relation/merger.rb +26 -19
  220. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  221. data/lib/active_record/relation/predicate_builder/association_query_value.rb +38 -4
  222. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
  223. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  224. data/lib/active_record/relation/predicate_builder.rb +29 -22
  225. data/lib/active_record/relation/query_attribute.rb +30 -12
  226. data/lib/active_record/relation/query_methods.rb +870 -163
  227. data/lib/active_record/relation/record_fetch_warning.rb +10 -9
  228. data/lib/active_record/relation/spawn_methods.rb +7 -6
  229. data/lib/active_record/relation/where_clause.rb +15 -36
  230. data/lib/active_record/relation.rb +736 -145
  231. data/lib/active_record/result.rb +67 -54
  232. data/lib/active_record/runtime_registry.rb +71 -13
  233. data/lib/active_record/sanitization.rb +84 -34
  234. data/lib/active_record/schema.rb +39 -23
  235. data/lib/active_record/schema_dumper.rb +90 -31
  236. data/lib/active_record/schema_migration.rb +74 -23
  237. data/lib/active_record/scoping/default.rb +72 -15
  238. data/lib/active_record/scoping/named.rb +6 -13
  239. data/lib/active_record/scoping.rb +65 -34
  240. data/lib/active_record/secure_password.rb +60 -0
  241. data/lib/active_record/secure_token.rb +21 -3
  242. data/lib/active_record/serialization.rb +6 -1
  243. data/lib/active_record/signed_id.rb +30 -9
  244. data/lib/active_record/statement_cache.rb +7 -7
  245. data/lib/active_record/store.rb +10 -10
  246. data/lib/active_record/suppressor.rb +13 -15
  247. data/lib/active_record/table_metadata.rb +7 -3
  248. data/lib/active_record/tasks/database_tasks.rb +288 -149
  249. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
  250. data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
  251. data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
  252. data/lib/active_record/test_databases.rb +1 -1
  253. data/lib/active_record/test_fixtures.rb +173 -155
  254. data/lib/active_record/testing/query_assertions.rb +121 -0
  255. data/lib/active_record/timestamp.rb +32 -19
  256. data/lib/active_record/token_for.rb +123 -0
  257. data/lib/active_record/touch_later.rb +12 -7
  258. data/lib/active_record/transaction.rb +132 -0
  259. data/lib/active_record/transactions.rb +118 -41
  260. data/lib/active_record/translation.rb +3 -5
  261. data/lib/active_record/type/adapter_specific_registry.rb +32 -14
  262. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  263. data/lib/active_record/type/internal/timezone.rb +7 -2
  264. data/lib/active_record/type/serialized.rb +9 -7
  265. data/lib/active_record/type/time.rb +4 -0
  266. data/lib/active_record/type/type_map.rb +17 -20
  267. data/lib/active_record/type.rb +1 -2
  268. data/lib/active_record/type_caster/connection.rb +4 -4
  269. data/lib/active_record/validations/absence.rb +1 -1
  270. data/lib/active_record/validations/associated.rb +13 -7
  271. data/lib/active_record/validations/numericality.rb +5 -4
  272. data/lib/active_record/validations/presence.rb +5 -28
  273. data/lib/active_record/validations/uniqueness.rb +65 -15
  274. data/lib/active_record/validations.rb +12 -5
  275. data/lib/active_record/version.rb +1 -1
  276. data/lib/active_record.rb +444 -32
  277. data/lib/arel/alias_predication.rb +1 -1
  278. data/lib/arel/attributes/attribute.rb +0 -8
  279. data/lib/arel/collectors/bind.rb +2 -0
  280. data/lib/arel/collectors/composite.rb +7 -0
  281. data/lib/arel/collectors/sql_string.rb +1 -1
  282. data/lib/arel/collectors/substitute_binds.rb +1 -1
  283. data/lib/arel/crud.rb +28 -22
  284. data/lib/arel/delete_manager.rb +18 -4
  285. data/lib/arel/errors.rb +10 -0
  286. data/lib/arel/factory_methods.rb +4 -0
  287. data/lib/arel/filter_predications.rb +9 -0
  288. data/lib/arel/insert_manager.rb +2 -3
  289. data/lib/arel/nodes/binary.rb +6 -7
  290. data/lib/arel/nodes/bound_sql_literal.rb +65 -0
  291. data/lib/arel/nodes/casted.rb +1 -1
  292. data/lib/arel/nodes/cte.rb +36 -0
  293. data/lib/arel/nodes/delete_statement.rb +12 -13
  294. data/lib/arel/nodes/filter.rb +10 -0
  295. data/lib/arel/nodes/fragments.rb +35 -0
  296. data/lib/arel/nodes/function.rb +1 -0
  297. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  298. data/lib/arel/nodes/insert_statement.rb +2 -2
  299. data/lib/arel/nodes/leading_join.rb +8 -0
  300. data/lib/arel/nodes/{and.rb → nary.rb} +9 -2
  301. data/lib/arel/nodes/node.rb +115 -5
  302. data/lib/arel/nodes/select_core.rb +2 -2
  303. data/lib/arel/nodes/select_statement.rb +2 -2
  304. data/lib/arel/nodes/sql_literal.rb +13 -0
  305. data/lib/arel/nodes/table_alias.rb +4 -0
  306. data/lib/arel/nodes/update_statement.rb +8 -3
  307. data/lib/arel/nodes.rb +7 -2
  308. data/lib/arel/predications.rb +14 -4
  309. data/lib/arel/select_manager.rb +11 -5
  310. data/lib/arel/table.rb +9 -6
  311. data/lib/arel/tree_manager.rb +8 -15
  312. data/lib/arel/update_manager.rb +20 -5
  313. data/lib/arel/visitors/dot.rb +81 -90
  314. data/lib/arel/visitors/mysql.rb +23 -5
  315. data/lib/arel/visitors/postgresql.rb +1 -22
  316. data/lib/arel/visitors/sqlite.rb +25 -0
  317. data/lib/arel/visitors/to_sql.rb +170 -36
  318. data/lib/arel/visitors/visitor.rb +2 -2
  319. data/lib/arel.rb +23 -4
  320. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  321. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  322. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  323. data/lib/rails/generators/active_record/migration.rb +3 -1
  324. data/lib/rails/generators/active_record/model/USAGE +113 -0
  325. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  326. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  327. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  328. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  329. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  330. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  331. metadata +103 -17
  332. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  333. data/lib/active_record/null_relation.rb +0 -67
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecord
4
+ # = Active Record Connection Handling
4
5
  module ConnectionHandling
5
6
  RAILS_ENV = -> { (Rails.env if defined?(Rails.env)) || ENV["RAILS_ENV"].presence || ENV["RACK_ENV"].presence }
6
7
  DEFAULT_ENV = -> { RAILS_ENV.call || "default_env" }
@@ -38,26 +39,25 @@ module ActiveRecord
38
39
  # )
39
40
  #
40
41
  # In case {ActiveRecord::Base.configurations}[rdoc-ref:Core.configurations]
41
- # is set (Rails automatically loads the contents of config/database.yml into it),
42
+ # is set (\Rails automatically loads the contents of config/database.yml into it),
42
43
  # a symbol can also be given as argument, representing a key in the
43
44
  # configuration hash:
44
45
  #
45
46
  # ActiveRecord::Base.establish_connection(:production)
46
47
  #
47
- # The exceptions AdapterNotSpecified, AdapterNotFound and +ArgumentError+
48
+ # The exceptions AdapterNotSpecified, AdapterNotFound, and +ArgumentError+
48
49
  # may be returned on an error.
49
50
  def establish_connection(config_or_env = nil)
50
51
  config_or_env ||= DEFAULT_ENV.call.to_sym
51
- db_config, owner_name = resolve_config_for_connection(config_or_env)
52
- connection_handler.establish_connection(db_config, owner_name: owner_name, role: current_role, shard: current_shard)
52
+ db_config = resolve_config_for_connection(config_or_env)
53
+ connection_handler.establish_connection(db_config, owner_name: self, role: current_role, shard: current_shard)
53
54
  end
54
55
 
55
56
  # Connects a model to the databases specified. The +database+ keyword
56
57
  # takes a hash consisting of a +role+ and a +database_key+.
57
58
  #
58
- # This will create a connection handler for switching between connections,
59
- # look up the config hash using the +database_key+ and finally
60
- # establishes a connection to that config.
59
+ # This will look up the database config using the +database_key+ and
60
+ # establish a connection to that config.
61
61
  #
62
62
  # class AnimalsModel < ApplicationRecord
63
63
  # self.abstract_class = true
@@ -66,7 +66,7 @@ module ActiveRecord
66
66
  # end
67
67
  #
68
68
  # +connects_to+ also supports horizontal sharding. The horizontal sharding API
69
- # also supports read replicas. Connect a model to a list of shards like this:
69
+ # supports read replicas as well. You can connect a model to a list of shards like this:
70
70
  #
71
71
  # class AnimalsModel < ApplicationRecord
72
72
  # self.abstract_class = true
@@ -87,28 +87,25 @@ module ActiveRecord
87
87
 
88
88
  connections = []
89
89
 
90
- database.each do |role, database_key|
91
- db_config, owner_name = resolve_config_for_connection(database_key)
92
- handler = lookup_connection_handler(role.to_sym)
93
-
94
- self.connection_class = true
95
- connections << handler.establish_connection(db_config, owner_name: owner_name, role: role)
90
+ if shards.empty?
91
+ shards[:default] = database
96
92
  end
97
93
 
94
+ self.default_shard = shards.keys.first
95
+
98
96
  shards.each do |shard, database_keys|
99
97
  database_keys.each do |role, database_key|
100
- db_config, owner_name = resolve_config_for_connection(database_key)
101
- handler = lookup_connection_handler(role.to_sym)
98
+ db_config = resolve_config_for_connection(database_key)
102
99
 
103
100
  self.connection_class = true
104
- connections << handler.establish_connection(db_config, owner_name: owner_name, role: role, shard: shard.to_sym)
101
+ connections << connection_handler.establish_connection(db_config, owner_name: self, role: role, shard: shard.to_sym)
105
102
  end
106
103
  end
107
104
 
108
105
  connections
109
106
  end
110
107
 
111
- # Connects to a role (ex writing, reading or a custom role) and/or
108
+ # Connects to a role (e.g. writing, reading, or a custom role) and/or
112
109
  # shard for the duration of the block. At the end of the block the
113
110
  # connection will be returned to the original role / shard.
114
111
  #
@@ -134,48 +131,20 @@ module ActiveRecord
134
131
  # ActiveRecord::Base.connected_to(role: :reading, shard: :shard_one_replica) do
135
132
  # Dog.first # finds first Dog record stored on the shard one replica
136
133
  # end
137
- #
138
- # The database kwarg is deprecated and will be removed in Rails 7.0.0 without replacement.
139
- def connected_to(database: nil, role: nil, shard: nil, prevent_writes: false, &blk)
140
- if legacy_connection_handling
141
- if self != Base
142
- raise NotImplementedError, "`connected_to` can only be called on ActiveRecord::Base with legacy connection handling."
143
- end
144
- else
145
- if self != Base && !abstract_class
146
- raise NotImplementedError, "calling `connected_to` is only allowed on ActiveRecord::Base or abstract classes."
147
- end
148
-
149
- if name != connection_specification_name && !primary_class?
150
- raise NotImplementedError, "calling `connected_to` is only allowed on the abstract class that established the connection."
151
- end
134
+ def connected_to(role: nil, shard: nil, prevent_writes: false, &blk)
135
+ if self != Base && !abstract_class
136
+ raise NotImplementedError, "calling `connected_to` is only allowed on ActiveRecord::Base or abstract classes."
152
137
  end
153
138
 
154
- if database && (role || shard)
155
- raise ArgumentError, "`connected_to` cannot accept a `database` argument with any other arguments."
156
- elsif database
157
- ActiveSupport::Deprecation.warn("The database key in `connected_to` is deprecated. It will be removed in Rails 7.0.0 without replacement.")
158
-
159
- if database.is_a?(Hash)
160
- role, database = database.first
161
- role = role.to_sym
162
- end
163
-
164
- db_config, owner_name = resolve_config_for_connection(database)
165
- handler = lookup_connection_handler(role)
166
-
167
- handler.establish_connection(db_config, owner_name: owner_name, role: role)
168
-
169
- with_handler(role, &blk)
170
- elsif role || shard
171
- unless role
172
- raise ArgumentError, "`connected_to` cannot accept a `shard` argument without a `role`."
173
- end
139
+ if !connection_class? && !primary_class?
140
+ raise NotImplementedError, "calling `connected_to` is only allowed on the abstract class that established the connection."
141
+ end
174
142
 
175
- with_role_and_shard(role, shard, prevent_writes, &blk)
176
- else
143
+ unless role || shard
177
144
  raise ArgumentError, "must provide a `shard` and/or `role`."
178
145
  end
146
+
147
+ with_role_and_shard(role, shard, prevent_writes, &blk)
179
148
  end
180
149
 
181
150
  # Connects a role and/or shard to the provided connection names. Optionally +prevent_writes+
@@ -194,17 +163,13 @@ module ActiveRecord
194
163
  def connected_to_many(*classes, role:, shard: nil, prevent_writes: false)
195
164
  classes = classes.flatten
196
165
 
197
- if legacy_connection_handling
198
- raise NotImplementedError, "connected_to_many is not available with legacy connection handling"
199
- end
200
-
201
166
  if self != Base || classes.include?(Base)
202
167
  raise NotImplementedError, "connected_to_many can only be called on ActiveRecord::Base."
203
168
  end
204
169
 
205
- prevent_writes = true if role == reading_role
170
+ prevent_writes = true if role == ActiveRecord.reading_role
206
171
 
207
- connected_to_stack << { role: role, shard: shard, prevent_writes: prevent_writes, klasses: classes }
172
+ append_to_connected_to_stack(role: role, shard: shard, prevent_writes: prevent_writes, klasses: classes)
208
173
  yield
209
174
  ensure
210
175
  connected_to_stack.pop
@@ -218,13 +183,28 @@ module ActiveRecord
218
183
  # It is not recommended to use this method in a request since it
219
184
  # does not yield to a block like +connected_to+.
220
185
  def connecting_to(role: default_role, shard: default_shard, prevent_writes: false)
221
- if legacy_connection_handling
222
- raise NotImplementedError, "`connecting_to` is not available with `legacy_connection_handling`."
223
- end
186
+ prevent_writes = true if role == ActiveRecord.reading_role
224
187
 
225
- prevent_writes = true if role == reading_role
188
+ append_to_connected_to_stack(role: role, shard: shard, prevent_writes: prevent_writes, klasses: [self])
189
+ end
226
190
 
227
- self.connected_to_stack << { role: role, shard: shard, prevent_writes: prevent_writes, klasses: [self] }
191
+ # Prohibit swapping shards while inside of the passed block.
192
+ #
193
+ # In some cases you may want to be able to swap shards but not allow a
194
+ # nested call to connected_to or connected_to_many to swap again. This
195
+ # is useful in cases you're using sharding to provide per-request
196
+ # database isolation.
197
+ def prohibit_shard_swapping(enabled = true)
198
+ prev_value = ActiveSupport::IsolatedExecutionState[:active_record_prohibit_shard_swapping]
199
+ ActiveSupport::IsolatedExecutionState[:active_record_prohibit_shard_swapping] = enabled
200
+ yield
201
+ ensure
202
+ ActiveSupport::IsolatedExecutionState[:active_record_prohibit_shard_swapping] = prev_value
203
+ end
204
+
205
+ # Determine whether or not shard swapping is currently prohibited
206
+ def shard_swapping_prohibited?
207
+ ActiveSupport::IsolatedExecutionState[:active_record_prohibit_shard_swapping]
228
208
  end
229
209
 
230
210
  # Prevent writing to the database regardless of role.
@@ -239,88 +219,114 @@ module ActiveRecord
239
219
  # See +READ_QUERY+ for the queries that are blocked by this
240
220
  # method.
241
221
  def while_preventing_writes(enabled = true, &block)
242
- if legacy_connection_handling
243
- connection_handler.while_preventing_writes(enabled, &block)
244
- else
245
- connected_to(role: current_role, prevent_writes: enabled, &block)
246
- end
222
+ connected_to(role: current_role, prevent_writes: enabled, &block)
247
223
  end
248
224
 
249
- # Returns true if role is the current connected role.
225
+ # Returns true if role is the current connected role and/or
226
+ # current connected shard. If no shard is passed, the default will be
227
+ # used.
250
228
  #
251
229
  # ActiveRecord::Base.connected_to(role: :writing) do
252
230
  # ActiveRecord::Base.connected_to?(role: :writing) #=> true
253
231
  # ActiveRecord::Base.connected_to?(role: :reading) #=> false
254
232
  # end
233
+ #
234
+ # ActiveRecord::Base.connected_to(role: :reading, shard: :shard_one) do
235
+ # ActiveRecord::Base.connected_to?(role: :reading, shard: :shard_one) #=> true
236
+ # ActiveRecord::Base.connected_to?(role: :reading, shard: :default) #=> false
237
+ # ActiveRecord::Base.connected_to?(role: :writing, shard: :shard_one) #=> true
238
+ # end
255
239
  def connected_to?(role:, shard: ActiveRecord::Base.default_shard)
256
240
  current_role == role.to_sym && current_shard == shard.to_sym
257
241
  end
258
242
 
259
- def lookup_connection_handler(handler_key) # :nodoc:
260
- if ActiveRecord::Base.legacy_connection_handling
261
- handler_key ||= ActiveRecord::Base.writing_role
262
- connection_handlers[handler_key] ||= ActiveRecord::ConnectionAdapters::ConnectionHandler.new
263
- else
264
- ActiveRecord::Base.connection_handler
265
- end
266
- end
267
-
268
243
  # Clears the query cache for all connections associated with the current thread.
269
244
  def clear_query_caches_for_current_thread
270
- if ActiveRecord::Base.legacy_connection_handling
271
- ActiveRecord::Base.connection_handlers.each_value do |handler|
272
- clear_on_handler(handler)
273
- end
274
- else
275
- clear_on_handler(ActiveRecord::Base.connection_handler)
245
+ connection_handler.each_connection_pool do |pool|
246
+ pool.clear_query_cache
276
247
  end
277
248
  end
278
249
 
279
250
  # Returns the connection currently associated with the class. This can
280
251
  # also be used to "borrow" the connection to do database work unrelated
281
252
  # to any of the specific Active Records.
253
+ # The connection will remain leased for the entire duration of the request
254
+ # or job, or until +#release_connection+ is called.
255
+ def lease_connection
256
+ connection_pool.lease_connection
257
+ end
258
+
259
+ # Soft deprecated. Use +#with_connection+ or +#lease_connection+ instead.
282
260
  def connection
283
- retrieve_connection
261
+ pool = connection_pool
262
+ if pool.permanent_lease?
263
+ case ActiveRecord.permanent_connection_checkout
264
+ when :deprecated
265
+ ActiveRecord.deprecator.warn <<~MESSAGE
266
+ Called deprecated `ActiveRecord::Base.connection` method.
267
+
268
+ Either use `with_connection` or `lease_connection`.
269
+ MESSAGE
270
+ when :disallowed
271
+ raise ActiveRecordError, <<~MESSAGE
272
+ Called deprecated `ActiveRecord::Base.connection` method.
273
+
274
+ Either use `with_connection` or `lease_connection`.
275
+ MESSAGE
276
+ end
277
+ pool.lease_connection
278
+ else
279
+ pool.active_connection
280
+ end
281
+ end
282
+
283
+ # Return the currently leased connection into the pool
284
+ def release_connection
285
+ connection_pool.release_connection
286
+ end
287
+
288
+ # Checkouts a connection from the pool, yield it and then check it back in.
289
+ # If a connection was already leased via #lease_connection or a parent call to
290
+ # #with_connection, that same connection is yieled.
291
+ # If #lease_connection is called inside the block, the connection won't be checked
292
+ # back in.
293
+ # If #connection is called inside the block, the connection won't be checked back in
294
+ # unless the +prevent_permanent_checkout+ argument is set to +true+.
295
+ def with_connection(prevent_permanent_checkout: false, &block)
296
+ connection_pool.with_connection(prevent_permanent_checkout: prevent_permanent_checkout, &block)
284
297
  end
285
298
 
286
299
  attr_writer :connection_specification_name
287
300
 
288
- # Return the connection specification name from the current class or its parent.
301
+ # Returns the connection specification name from the current class or its parent.
289
302
  def connection_specification_name
290
- if !defined?(@connection_specification_name) || @connection_specification_name.nil?
303
+ if @connection_specification_name.nil?
291
304
  return self == Base ? Base.name : superclass.connection_specification_name
292
305
  end
293
306
  @connection_specification_name
294
307
  end
295
308
 
296
309
  def primary_class? # :nodoc:
297
- self == Base || defined?(ApplicationRecord) && self == ApplicationRecord
298
- end
299
-
300
- # Returns the configuration of the associated connection as a hash:
301
- #
302
- # ActiveRecord::Base.connection_config
303
- # # => {pool: 5, timeout: 5000, database: "db/development.sqlite3", adapter: "sqlite3"}
304
- #
305
- # Please use only for reading.
306
- def connection_config
307
- connection_pool.db_config.configuration_hash
310
+ self == Base || application_record_class?
308
311
  end
309
- deprecate connection_config: "Use connection_db_config instead"
310
312
 
311
313
  # Returns the db_config object from the associated connection:
312
314
  #
313
315
  # ActiveRecord::Base.connection_db_config
314
316
  # #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10 @env_name="development",
315
- # @name="primary", @config={pool: 5, timeout: 5000, database: "db/development.sqlite3", adapter: "sqlite3"}>
317
+ # @name="primary", @config={pool: 5, timeout: 5000, database: "storage/development.sqlite3", adapter: "sqlite3"}>
316
318
  #
317
319
  # Use only for reading.
318
320
  def connection_db_config
319
321
  connection_pool.db_config
320
322
  end
321
323
 
324
+ def adapter_class # :nodoc:
325
+ connection_pool.db_config.adapter_class
326
+ end
327
+
322
328
  def connection_pool
323
- connection_handler.retrieve_connection_pool(connection_specification_name, role: current_role, shard: current_shard) || raise(ConnectionNotEstablished)
329
+ connection_handler.retrieve_connection_pool(connection_specification_name, role: current_role, shard: current_shard, strict: true)
324
330
  end
325
331
 
326
332
  def retrieve_connection
@@ -332,8 +338,9 @@ module ActiveRecord
332
338
  connection_handler.connected?(connection_specification_name, role: current_role, shard: current_shard)
333
339
  end
334
340
 
335
- def remove_connection(name = nil)
336
- name ||= @connection_specification_name if defined?(@connection_specification_name)
341
+ def remove_connection
342
+ name = @connection_specification_name if defined?(@connection_specification_name)
343
+
337
344
  # if removing a connection that has a pool, we reset the
338
345
  # connection_specification_name so it will use the parent
339
346
  # pool.
@@ -344,62 +351,41 @@ module ActiveRecord
344
351
  connection_handler.remove_connection_pool(name, role: current_role, shard: current_shard)
345
352
  end
346
353
 
347
- def clear_cache! # :nodoc:
348
- connection.schema_cache.clear!
354
+ def schema_cache # :nodoc:
355
+ connection_pool.schema_cache
349
356
  end
350
357
 
351
- delegate :clear_active_connections!, :clear_reloadable_connections!,
352
- :clear_all_connections!, :flush_idle_connections!, to: :connection_handler
358
+ def clear_cache! # :nodoc:
359
+ connection_pool.schema_cache.clear!
360
+ end
353
361
 
354
362
  private
355
- def clear_on_handler(handler)
356
- handler.all_connection_pools.each do |pool|
357
- pool.connection.clear_query_cache if pool.active_connection?
358
- end
359
- end
360
-
361
363
  def resolve_config_for_connection(config_or_env)
362
364
  raise "Anonymous class is not allowed." unless name
363
365
 
364
- owner_name = primary_class? ? Base.name : name
365
- self.connection_specification_name = owner_name
366
-
367
- db_config = Base.configurations.resolve(config_or_env)
368
- [db_config, self]
369
- end
366
+ connection_name = primary_class? ? Base.name : name
367
+ self.connection_specification_name = connection_name
370
368
 
371
- def with_handler(handler_key, &blk)
372
- handler = lookup_connection_handler(handler_key)
373
- swap_connection_handler(handler, &blk)
369
+ Base.configurations.resolve(config_or_env)
374
370
  end
375
371
 
376
372
  def with_role_and_shard(role, shard, prevent_writes)
377
- prevent_writes = true if role == reading_role
378
-
379
- if ActiveRecord::Base.legacy_connection_handling
380
- with_handler(role.to_sym) do
381
- connection_handler.while_preventing_writes(prevent_writes) do
382
- self.connected_to_stack << { shard: shard, klasses: [self] }
383
- yield
384
- end
385
- end
386
- else
387
- self.connected_to_stack << { role: role, shard: shard, prevent_writes: prevent_writes, klasses: [self] }
388
- return_value = yield
389
- return_value.load if return_value.is_a? ActiveRecord::Relation
390
- return_value
391
- end
392
- ensure
393
- self.connected_to_stack.pop
394
- end
373
+ prevent_writes = true if role == ActiveRecord.reading_role
395
374
 
396
- def swap_connection_handler(handler, &blk) # :nodoc:
397
- old_handler, ActiveRecord::Base.connection_handler = ActiveRecord::Base.connection_handler, handler
375
+ append_to_connected_to_stack(role: role, shard: shard, prevent_writes: prevent_writes, klasses: [self])
398
376
  return_value = yield
399
377
  return_value.load if return_value.is_a? ActiveRecord::Relation
400
378
  return_value
401
379
  ensure
402
- ActiveRecord::Base.connection_handler = old_handler
380
+ self.connected_to_stack.pop
381
+ end
382
+
383
+ def append_to_connected_to_stack(entry)
384
+ if shard_swapping_prohibited? && entry[:shard].present?
385
+ raise ArgumentError, "cannot swap `shard` while shard swapping is prohibited."
386
+ end
387
+
388
+ connected_to_stack << entry
403
389
  end
404
390
  end
405
391
  end