activerecord 5.2.7.1 → 6.1.4.6

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (316) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1145 -575
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +7 -5
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/aggregations.rb +9 -8
  7. data/lib/active_record/association_relation.rb +30 -10
  8. data/lib/active_record/associations/alias_tracker.rb +19 -16
  9. data/lib/active_record/associations/association.rb +100 -41
  10. data/lib/active_record/associations/association_scope.rb +23 -21
  11. data/lib/active_record/associations/belongs_to_association.rb +55 -48
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -6
  13. data/lib/active_record/associations/builder/association.rb +45 -22
  14. data/lib/active_record/associations/builder/belongs_to.rb +29 -59
  15. data/lib/active_record/associations/builder/collection_association.rb +8 -17
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -41
  17. data/lib/active_record/associations/builder/has_many.rb +8 -2
  18. data/lib/active_record/associations/builder/has_one.rb +33 -2
  19. data/lib/active_record/associations/builder/singular_association.rb +3 -1
  20. data/lib/active_record/associations/collection_association.rb +31 -29
  21. data/lib/active_record/associations/collection_proxy.rb +25 -21
  22. data/lib/active_record/associations/foreign_association.rb +20 -0
  23. data/lib/active_record/associations/has_many_association.rb +26 -13
  24. data/lib/active_record/associations/has_many_through_association.rb +24 -18
  25. data/lib/active_record/associations/has_one_association.rb +43 -31
  26. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  27. data/lib/active_record/associations/join_dependency/join_association.rb +44 -22
  28. data/lib/active_record/associations/join_dependency/join_part.rb +5 -5
  29. data/lib/active_record/associations/join_dependency.rb +91 -60
  30. data/lib/active_record/associations/preloader/association.rb +71 -43
  31. data/lib/active_record/associations/preloader/through_association.rb +49 -40
  32. data/lib/active_record/associations/preloader.rb +47 -34
  33. data/lib/active_record/associations/singular_association.rb +3 -17
  34. data/lib/active_record/associations/through_association.rb +1 -1
  35. data/lib/active_record/associations.rb +137 -25
  36. data/lib/active_record/attribute_assignment.rb +17 -19
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -7
  38. data/lib/active_record/attribute_methods/dirty.rb +101 -40
  39. data/lib/active_record/attribute_methods/primary_key.rb +20 -25
  40. data/lib/active_record/attribute_methods/query.rb +4 -8
  41. data/lib/active_record/attribute_methods/read.rb +14 -56
  42. data/lib/active_record/attribute_methods/serialization.rb +12 -7
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
  44. data/lib/active_record/attribute_methods/write.rb +18 -34
  45. data/lib/active_record/attribute_methods.rb +81 -143
  46. data/lib/active_record/attributes.rb +46 -9
  47. data/lib/active_record/autosave_association.rb +57 -42
  48. data/lib/active_record/base.rb +4 -17
  49. data/lib/active_record/callbacks.rb +158 -43
  50. data/lib/active_record/coders/yaml_column.rb +12 -3
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +272 -130
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +7 -36
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +167 -146
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +18 -14
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +98 -47
  56. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -110
  58. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +207 -90
  59. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -4
  60. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +385 -144
  61. data/lib/active_record/connection_adapters/abstract/transaction.rb +167 -69
  62. data/lib/active_record/connection_adapters/abstract_adapter.rb +229 -99
  63. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +243 -275
  64. data/lib/active_record/connection_adapters/column.rb +30 -12
  65. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  66. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
  67. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  68. data/lib/active_record/connection_adapters/mysql/database_statements.rb +86 -32
  69. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  70. data/lib/active_record/connection_adapters/mysql/quoting.rb +59 -7
  71. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +34 -10
  72. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +48 -32
  73. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +18 -7
  74. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +139 -19
  75. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +14 -9
  76. data/lib/active_record/connection_adapters/mysql2_adapter.rb +53 -18
  77. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  78. data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
  79. data/lib/active_record/connection_adapters/postgresql/column.rb +37 -28
  80. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +38 -54
  81. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  83. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  84. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +10 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  87. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +3 -4
  90. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  92. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +3 -4
  93. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +25 -7
  94. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  95. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  96. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +15 -3
  97. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  98. data/lib/active_record/connection_adapters/postgresql/quoting.rb +47 -10
  99. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  100. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +19 -4
  101. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  102. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  103. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +120 -100
  104. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +31 -26
  105. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  106. data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -120
  107. data/lib/active_record/connection_adapters/schema_cache.rb +135 -21
  108. data/lib/active_record/connection_adapters/sql_type_metadata.rb +17 -6
  109. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +144 -0
  110. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -7
  111. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  112. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +77 -13
  113. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +174 -186
  114. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  115. data/lib/active_record/connection_adapters.rb +52 -0
  116. data/lib/active_record/connection_handling.rb +293 -33
  117. data/lib/active_record/core.rb +334 -97
  118. data/lib/active_record/counter_cache.rb +8 -30
  119. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  120. data/lib/active_record/database_configurations/database_config.rb +80 -0
  121. data/lib/active_record/database_configurations/hash_config.rb +96 -0
  122. data/lib/active_record/database_configurations/url_config.rb +53 -0
  123. data/lib/active_record/database_configurations.rb +272 -0
  124. data/lib/active_record/delegated_type.rb +209 -0
  125. data/lib/active_record/destroy_association_async_job.rb +36 -0
  126. data/lib/active_record/dynamic_matchers.rb +3 -4
  127. data/lib/active_record/enum.rb +108 -36
  128. data/lib/active_record/errors.rb +62 -19
  129. data/lib/active_record/explain.rb +10 -6
  130. data/lib/active_record/explain_subscriber.rb +1 -1
  131. data/lib/active_record/fixture_set/file.rb +10 -17
  132. data/lib/active_record/fixture_set/model_metadata.rb +32 -0
  133. data/lib/active_record/fixture_set/render_context.rb +17 -0
  134. data/lib/active_record/fixture_set/table_row.rb +152 -0
  135. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  136. data/lib/active_record/fixtures.rb +200 -481
  137. data/lib/active_record/gem_version.rb +4 -4
  138. data/lib/active_record/inheritance.rb +53 -24
  139. data/lib/active_record/insert_all.rb +212 -0
  140. data/lib/active_record/integration.rb +67 -17
  141. data/lib/active_record/internal_metadata.rb +26 -9
  142. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  143. data/lib/active_record/locking/optimistic.rb +37 -23
  144. data/lib/active_record/locking/pessimistic.rb +9 -5
  145. data/lib/active_record/log_subscriber.rb +35 -35
  146. data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
  147. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  148. data/lib/active_record/middleware/database_selector.rb +77 -0
  149. data/lib/active_record/migration/command_recorder.rb +96 -44
  150. data/lib/active_record/migration/compatibility.rb +142 -64
  151. data/lib/active_record/migration/join_table.rb +0 -1
  152. data/lib/active_record/migration.rb +206 -157
  153. data/lib/active_record/model_schema.rb +148 -22
  154. data/lib/active_record/nested_attributes.rb +4 -7
  155. data/lib/active_record/no_touching.rb +8 -1
  156. data/lib/active_record/null_relation.rb +0 -1
  157. data/lib/active_record/persistence.rb +267 -59
  158. data/lib/active_record/query_cache.rb +21 -4
  159. data/lib/active_record/querying.rb +40 -23
  160. data/lib/active_record/railtie.rb +115 -58
  161. data/lib/active_record/railties/console_sandbox.rb +2 -4
  162. data/lib/active_record/railties/controller_runtime.rb +30 -35
  163. data/lib/active_record/railties/databases.rake +411 -80
  164. data/lib/active_record/readonly_attributes.rb +4 -0
  165. data/lib/active_record/reflection.rb +109 -93
  166. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  167. data/lib/active_record/relation/batches.rb +44 -35
  168. data/lib/active_record/relation/calculations.rb +157 -90
  169. data/lib/active_record/relation/delegation.rb +35 -50
  170. data/lib/active_record/relation/finder_methods.rb +64 -39
  171. data/lib/active_record/relation/from_clause.rb +5 -1
  172. data/lib/active_record/relation/merger.rb +32 -40
  173. data/lib/active_record/relation/predicate_builder/array_handler.rb +13 -13
  174. data/lib/active_record/relation/predicate_builder/association_query_value.rb +5 -9
  175. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  176. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +11 -10
  177. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  179. data/lib/active_record/relation/predicate_builder.rb +62 -45
  180. data/lib/active_record/relation/query_attribute.rb +13 -8
  181. data/lib/active_record/relation/query_methods.rb +476 -187
  182. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  183. data/lib/active_record/relation/spawn_methods.rb +9 -9
  184. data/lib/active_record/relation/where_clause.rb +115 -62
  185. data/lib/active_record/relation.rb +379 -115
  186. data/lib/active_record/result.rb +64 -38
  187. data/lib/active_record/runtime_registry.rb +2 -2
  188. data/lib/active_record/sanitization.rb +22 -41
  189. data/lib/active_record/schema.rb +2 -11
  190. data/lib/active_record/schema_dumper.rb +54 -9
  191. data/lib/active_record/schema_migration.rb +7 -9
  192. data/lib/active_record/scoping/default.rb +4 -8
  193. data/lib/active_record/scoping/named.rb +17 -24
  194. data/lib/active_record/scoping.rb +8 -9
  195. data/lib/active_record/secure_token.rb +16 -8
  196. data/lib/active_record/serialization.rb +5 -3
  197. data/lib/active_record/signed_id.rb +116 -0
  198. data/lib/active_record/statement_cache.rb +49 -6
  199. data/lib/active_record/store.rb +88 -9
  200. data/lib/active_record/suppressor.rb +2 -2
  201. data/lib/active_record/table_metadata.rb +42 -43
  202. data/lib/active_record/tasks/database_tasks.rb +277 -81
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +37 -39
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +27 -32
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -17
  206. data/lib/active_record/test_databases.rb +24 -0
  207. data/lib/active_record/test_fixtures.rb +287 -0
  208. data/lib/active_record/timestamp.rb +43 -32
  209. data/lib/active_record/touch_later.rb +23 -22
  210. data/lib/active_record/transactions.rb +62 -118
  211. data/lib/active_record/translation.rb +1 -1
  212. data/lib/active_record/type/adapter_specific_registry.rb +3 -13
  213. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  214. data/lib/active_record/type/serialized.rb +6 -3
  215. data/lib/active_record/type/time.rb +10 -0
  216. data/lib/active_record/type/type_map.rb +0 -1
  217. data/lib/active_record/type/unsigned_integer.rb +0 -1
  218. data/lib/active_record/type.rb +10 -5
  219. data/lib/active_record/type_caster/connection.rb +15 -15
  220. data/lib/active_record/type_caster/map.rb +8 -8
  221. data/lib/active_record/validations/associated.rb +1 -2
  222. data/lib/active_record/validations/numericality.rb +35 -0
  223. data/lib/active_record/validations/uniqueness.rb +38 -30
  224. data/lib/active_record/validations.rb +4 -3
  225. data/lib/active_record.rb +13 -12
  226. data/lib/arel/alias_predication.rb +9 -0
  227. data/lib/arel/attributes/attribute.rb +41 -0
  228. data/lib/arel/collectors/bind.rb +29 -0
  229. data/lib/arel/collectors/composite.rb +39 -0
  230. data/lib/arel/collectors/plain_string.rb +20 -0
  231. data/lib/arel/collectors/sql_string.rb +27 -0
  232. data/lib/arel/collectors/substitute_binds.rb +35 -0
  233. data/lib/arel/crud.rb +42 -0
  234. data/lib/arel/delete_manager.rb +18 -0
  235. data/lib/arel/errors.rb +9 -0
  236. data/lib/arel/expressions.rb +29 -0
  237. data/lib/arel/factory_methods.rb +49 -0
  238. data/lib/arel/insert_manager.rb +49 -0
  239. data/lib/arel/math.rb +45 -0
  240. data/lib/arel/nodes/and.rb +32 -0
  241. data/lib/arel/nodes/ascending.rb +23 -0
  242. data/lib/arel/nodes/binary.rb +126 -0
  243. data/lib/arel/nodes/bind_param.rb +44 -0
  244. data/lib/arel/nodes/case.rb +55 -0
  245. data/lib/arel/nodes/casted.rb +62 -0
  246. data/lib/arel/nodes/comment.rb +29 -0
  247. data/lib/arel/nodes/count.rb +12 -0
  248. data/lib/arel/nodes/delete_statement.rb +45 -0
  249. data/lib/arel/nodes/descending.rb +23 -0
  250. data/lib/arel/nodes/equality.rb +15 -0
  251. data/lib/arel/nodes/extract.rb +24 -0
  252. data/lib/arel/nodes/false.rb +16 -0
  253. data/lib/arel/nodes/full_outer_join.rb +8 -0
  254. data/lib/arel/nodes/function.rb +44 -0
  255. data/lib/arel/nodes/grouping.rb +11 -0
  256. data/lib/arel/nodes/homogeneous_in.rb +76 -0
  257. data/lib/arel/nodes/in.rb +15 -0
  258. data/lib/arel/nodes/infix_operation.rb +92 -0
  259. data/lib/arel/nodes/inner_join.rb +8 -0
  260. data/lib/arel/nodes/insert_statement.rb +37 -0
  261. data/lib/arel/nodes/join_source.rb +20 -0
  262. data/lib/arel/nodes/matches.rb +18 -0
  263. data/lib/arel/nodes/named_function.rb +23 -0
  264. data/lib/arel/nodes/node.rb +51 -0
  265. data/lib/arel/nodes/node_expression.rb +13 -0
  266. data/lib/arel/nodes/ordering.rb +27 -0
  267. data/lib/arel/nodes/outer_join.rb +8 -0
  268. data/lib/arel/nodes/over.rb +15 -0
  269. data/lib/arel/nodes/regexp.rb +16 -0
  270. data/lib/arel/nodes/right_outer_join.rb +8 -0
  271. data/lib/arel/nodes/select_core.rb +67 -0
  272. data/lib/arel/nodes/select_statement.rb +41 -0
  273. data/lib/arel/nodes/sql_literal.rb +19 -0
  274. data/lib/arel/nodes/string_join.rb +11 -0
  275. data/lib/arel/nodes/table_alias.rb +31 -0
  276. data/lib/arel/nodes/terminal.rb +16 -0
  277. data/lib/arel/nodes/true.rb +16 -0
  278. data/lib/arel/nodes/unary.rb +44 -0
  279. data/lib/arel/nodes/unary_operation.rb +20 -0
  280. data/lib/arel/nodes/unqualified_column.rb +22 -0
  281. data/lib/arel/nodes/update_statement.rb +41 -0
  282. data/lib/arel/nodes/values_list.rb +9 -0
  283. data/lib/arel/nodes/window.rb +126 -0
  284. data/lib/arel/nodes/with.rb +11 -0
  285. data/lib/arel/nodes.rb +70 -0
  286. data/lib/arel/order_predications.rb +13 -0
  287. data/lib/arel/predications.rb +250 -0
  288. data/lib/arel/select_manager.rb +270 -0
  289. data/lib/arel/table.rb +118 -0
  290. data/lib/arel/tree_manager.rb +72 -0
  291. data/lib/arel/update_manager.rb +34 -0
  292. data/lib/arel/visitors/dot.rb +308 -0
  293. data/lib/arel/visitors/mysql.rb +93 -0
  294. data/lib/arel/visitors/postgresql.rb +120 -0
  295. data/lib/arel/visitors/sqlite.rb +38 -0
  296. data/lib/arel/visitors/to_sql.rb +899 -0
  297. data/lib/arel/visitors/visitor.rb +45 -0
  298. data/lib/arel/visitors.rb +13 -0
  299. data/lib/arel/window_predications.rb +9 -0
  300. data/lib/arel.rb +54 -0
  301. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  302. data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -5
  303. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +3 -1
  304. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +7 -5
  305. data/lib/rails/generators/active_record/migration.rb +19 -2
  306. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  307. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  308. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  309. metadata +117 -32
  310. data/lib/active_record/attribute_decorators.rb +0 -90
  311. data/lib/active_record/collection_cache_key.rb +0 -53
  312. data/lib/active_record/connection_adapters/connection_specification.rb +0 -287
  313. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -33
  314. data/lib/active_record/define_callbacks.rb +0 -22
  315. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -19
  316. data/lib/active_record/relation/where_clause_factory.rb +0 -34
@@ -3,22 +3,32 @@
3
3
  require "thread"
4
4
  require "concurrent/map"
5
5
  require "monitor"
6
+ require "weakref"
6
7
 
7
8
  module ActiveRecord
8
- # Raised when a connection could not be obtained within the connection
9
- # acquisition timeout period: because max connections in pool
10
- # are in use.
11
- class ConnectionTimeoutError < ConnectionNotEstablished
12
- end
9
+ module ConnectionAdapters
10
+ module AbstractPool # :nodoc:
11
+ def get_schema_cache(connection)
12
+ self.schema_cache ||= SchemaCache.new(connection)
13
+ schema_cache.connection = connection
14
+ schema_cache
15
+ end
13
16
 
14
- # Raised when a pool was unable to get ahold of all its connections
15
- # to perform a "group" action such as
16
- # {ActiveRecord::Base.connection_pool.disconnect!}[rdoc-ref:ConnectionAdapters::ConnectionPool#disconnect!]
17
- # or {ActiveRecord::Base.clear_reloadable_connections!}[rdoc-ref:ConnectionAdapters::ConnectionHandler#clear_reloadable_connections!].
18
- class ExclusiveConnectionTimeoutError < ConnectionTimeoutError
19
- end
17
+ def set_schema_cache(cache)
18
+ self.schema_cache = cache
19
+ end
20
+ end
21
+
22
+ class NullPool # :nodoc:
23
+ include ConnectionAdapters::AbstractPool
24
+
25
+ attr_accessor :schema_cache
26
+
27
+ def connection_klass
28
+ nil
29
+ end
30
+ end
20
31
 
21
- module ConnectionAdapters
22
32
  # Connection pool base class for managing Active Record database
23
33
  # connections.
24
34
  #
@@ -129,7 +139,7 @@ module ActiveRecord
129
139
 
130
140
  # Remove the head of the queue.
131
141
  #
132
- # If +timeout+ is not given, remove and return the head the
142
+ # If +timeout+ is not given, remove and return the head of the
133
143
  # queue if the number of available elements is strictly
134
144
  # greater than the number of threads currently waiting (that
135
145
  # is, don't jump ahead in line). Otherwise, return +nil+.
@@ -146,7 +156,6 @@ module ActiveRecord
146
156
  end
147
157
 
148
158
  private
149
-
150
159
  def internal_poll(timeout)
151
160
  no_wait_poll || (timeout && wait_poll(timeout))
152
161
  end
@@ -173,7 +182,7 @@ module ActiveRecord
173
182
  @queue.pop
174
183
  end
175
184
 
176
- # Remove and return the head the queue if the number of
185
+ # Remove and return the head of the queue if the number of
177
186
  # available elements is strictly greater than the number of
178
187
  # threads currently waiting. Otherwise, return +nil+.
179
188
  def no_wait_poll
@@ -185,7 +194,7 @@ module ActiveRecord
185
194
  def wait_poll(timeout)
186
195
  @num_waiting += 1
187
196
 
188
- t0 = Time.now
197
+ t0 = Concurrent.monotonic_time
189
198
  elapsed = 0
190
199
  loop do
191
200
  ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
@@ -194,7 +203,7 @@ module ActiveRecord
194
203
 
195
204
  return remove if any?
196
205
 
197
- elapsed = Time.now - t0
206
+ elapsed = Concurrent.monotonic_time - t0
198
207
  if elapsed >= timeout
199
208
  msg = "could not obtain a connection from the pool within %0.3f seconds (waited %0.3f seconds); all pooled connections were in use" %
200
209
  [timeout, elapsed]
@@ -294,50 +303,90 @@ module ActiveRecord
294
303
  @frequency = frequency
295
304
  end
296
305
 
306
+ @mutex = Mutex.new
307
+ @pools = {}
308
+ @threads = {}
309
+
310
+ class << self
311
+ def register_pool(pool, frequency) # :nodoc:
312
+ @mutex.synchronize do
313
+ unless @threads[frequency]&.alive?
314
+ @threads[frequency] = spawn_thread(frequency)
315
+ end
316
+ @pools[frequency] ||= []
317
+ @pools[frequency] << WeakRef.new(pool)
318
+ end
319
+ end
320
+
321
+ private
322
+ def spawn_thread(frequency)
323
+ Thread.new(frequency) do |t|
324
+ # Advise multi-threaded app servers to ignore this thread for
325
+ # the purposes of fork safety warnings
326
+ Thread.current.thread_variable_set(:fork_safe, true)
327
+ running = true
328
+ while running
329
+ sleep t
330
+ @mutex.synchronize do
331
+ @pools[frequency].select! do |pool|
332
+ pool.weakref_alive? && !pool.discarded?
333
+ end
334
+
335
+ @pools[frequency].each do |p|
336
+ p.reap
337
+ p.flush
338
+ rescue WeakRef::RefError
339
+ end
340
+
341
+ if @pools[frequency].empty?
342
+ @pools.delete(frequency)
343
+ @threads.delete(frequency)
344
+ running = false
345
+ end
346
+ end
347
+ end
348
+ end
349
+ end
350
+ end
351
+
297
352
  def run
298
353
  return unless frequency && frequency > 0
299
- Thread.new(frequency, pool) { |t, p|
300
- loop do
301
- sleep t
302
- p.reap
303
- p.flush
304
- end
305
- }
354
+ self.class.register_pool(pool, frequency)
306
355
  end
307
356
  end
308
357
 
309
358
  include MonitorMixin
310
359
  include QueryCache::ConnectionPoolConfiguration
360
+ include ConnectionAdapters::AbstractPool
361
+
362
+ attr_accessor :automatic_reconnect, :checkout_timeout
363
+ attr_reader :db_config, :size, :reaper, :pool_config, :connection_klass
311
364
 
312
- attr_accessor :automatic_reconnect, :checkout_timeout, :schema_cache
313
- attr_reader :spec, :size, :reaper
365
+ delegate :schema_cache, :schema_cache=, to: :pool_config
314
366
 
315
- # Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
367
+ # Creates a new ConnectionPool object. +pool_config+ is a PoolConfig
316
368
  # object which describes database connection information (e.g. adapter,
317
369
  # host name, username, password, etc), as well as the maximum size for
318
370
  # this ConnectionPool.
319
371
  #
320
372
  # The default ConnectionPool maximum size is 5.
321
- def initialize(spec)
373
+ def initialize(pool_config)
322
374
  super()
323
375
 
324
- @spec = spec
325
-
326
- @checkout_timeout = (spec.config[:checkout_timeout] && spec.config[:checkout_timeout].to_f) || 5
327
- if @idle_timeout = spec.config.fetch(:idle_timeout, 300)
328
- @idle_timeout = @idle_timeout.to_f
329
- @idle_timeout = nil if @idle_timeout <= 0
330
- end
376
+ @pool_config = pool_config
377
+ @db_config = pool_config.db_config
378
+ @connection_klass = pool_config.connection_klass
331
379
 
332
- # default max pool size to 5
333
- @size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
380
+ @checkout_timeout = db_config.checkout_timeout
381
+ @idle_timeout = db_config.idle_timeout
382
+ @size = db_config.pool
334
383
 
335
384
  # This variable tracks the cache of threads mapped to reserved connections, with the
336
385
  # sole purpose of speeding up the +connection+ method. It is not the authoritative
337
386
  # registry of which thread owns which connection. Connection ownership is tracked by
338
387
  # the +connection.owner+ attr on each +connection+ instance.
339
388
  # The invariant works like this: if there is mapping of <tt>thread => conn</tt>,
340
- # then that +thread+ does indeed own that +conn+. However, an absence of a such
389
+ # then that +thread+ does indeed own that +conn+. However, an absence of such
341
390
  # mapping does not mean that the +thread+ doesn't own the said connection. In
342
391
  # that case +conn.owner+ attr should be consulted.
343
392
  # Access and modification of <tt>@thread_cached_conns</tt> does not require
@@ -358,10 +407,7 @@ module ActiveRecord
358
407
 
359
408
  @lock_thread = false
360
409
 
361
- # +reaping_frequency+ is configurable mostly for historical reasons, but it could
362
- # also be useful if someone wants a very low +idle_timeout+.
363
- reaping_frequency = spec.config.fetch(:reaping_frequency, 60)
364
- @reaper = Reaper.new(self, reaping_frequency && reaping_frequency.to_f)
410
+ @reaper = Reaper.new(self, db_config.reaping_frequency)
365
411
  @reaper.run
366
412
  end
367
413
 
@@ -443,7 +489,7 @@ module ActiveRecord
443
489
  # Raises:
444
490
  # - ActiveRecord::ExclusiveConnectionTimeoutError if unable to gain ownership of all
445
491
  # connections in the pool within a timeout interval (default duration is
446
- # <tt>spec.config[:checkout_timeout] * 2</tt> seconds).
492
+ # <tt>spec.db_config.checkout_timeout * 2</tt> seconds).
447
493
  def disconnect(raise_on_acquisition_timeout = true)
448
494
  with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
449
495
  synchronize do
@@ -464,7 +510,7 @@ module ActiveRecord
464
510
  #
465
511
  # The pool first tries to gain ownership of all connections. If unable to
466
512
  # do so within a timeout interval (default duration is
467
- # <tt>spec.config[:checkout_timeout] * 2</tt> seconds), then the pool is forcefully
513
+ # <tt>spec.db_config.checkout_timeout * 2</tt> seconds), then the pool is forcefully
468
514
  # disconnected without any regard for other connection owning threads.
469
515
  def disconnect!
470
516
  disconnect(false)
@@ -477,7 +523,7 @@ module ActiveRecord
477
523
  # See AbstractAdapter#discard!
478
524
  def discard! # :nodoc:
479
525
  synchronize do
480
- return if @connections.nil? # already discarded
526
+ return if self.discarded?
481
527
  @connections.each do |conn|
482
528
  conn.discard!
483
529
  end
@@ -485,13 +531,17 @@ module ActiveRecord
485
531
  end
486
532
  end
487
533
 
534
+ def discarded? # :nodoc:
535
+ @connections.nil?
536
+ end
537
+
488
538
  # Clears the cache which maps classes and re-connects connections that
489
539
  # require reloading.
490
540
  #
491
541
  # Raises:
492
542
  # - ActiveRecord::ExclusiveConnectionTimeoutError if unable to gain ownership of all
493
543
  # connections in the pool within a timeout interval (default duration is
494
- # <tt>spec.config[:checkout_timeout] * 2</tt> seconds).
544
+ # <tt>spec.db_config.checkout_timeout * 2</tt> seconds).
495
545
  def clear_reloadable_connections(raise_on_acquisition_timeout = true)
496
546
  with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
497
547
  synchronize do
@@ -513,7 +563,7 @@ module ActiveRecord
513
563
  #
514
564
  # The pool first tries to gain ownership of all connections. If unable to
515
565
  # do so within a timeout interval (default duration is
516
- # <tt>spec.config[:checkout_timeout] * 2</tt> seconds), then the pool forcefully
566
+ # <tt>spec.db_config.checkout_timeout * 2</tt> seconds), then the pool forcefully
517
567
  # clears the cache and reloads connections without any regard for other
518
568
  # connection owning threads.
519
569
  def clear_reloadable_connections!
@@ -593,6 +643,7 @@ module ActiveRecord
593
643
  # or a thread dies unexpectedly.
594
644
  def reap
595
645
  stale_connections = synchronize do
646
+ return if self.discarded?
596
647
  @connections.select do |conn|
597
648
  conn.in_use? && !conn.owner.alive?
598
649
  end.each do |conn|
@@ -617,6 +668,7 @@ module ActiveRecord
617
668
  return if minimum_idle.nil?
618
669
 
619
670
  idle_connections = synchronize do
671
+ return if self.discarded?
620
672
  @connections.select do |conn|
621
673
  !conn.in_use? && conn.seconds_idle >= minimum_idle
622
674
  end.each do |conn|
@@ -705,13 +757,13 @@ module ActiveRecord
705
757
  end
706
758
 
707
759
  newly_checked_out = []
708
- timeout_time = Time.now + (@checkout_timeout * 2)
760
+ timeout_time = Concurrent.monotonic_time + (@checkout_timeout * 2)
709
761
 
710
762
  @available.with_a_bias_for(Thread.current) do
711
763
  loop do
712
764
  synchronize do
713
765
  return if collected_conns.size == @connections.size && @now_connecting == 0
714
- remaining_timeout = timeout_time - Time.now
766
+ remaining_timeout = timeout_time - Concurrent.monotonic_time
715
767
  remaining_timeout = 0 if remaining_timeout < 0
716
768
  conn = checkout_for_exclusive_access(remaining_timeout)
717
769
  collected_conns << conn
@@ -750,7 +802,7 @@ module ActiveRecord
750
802
  # this block can't be easily moved into attempt_to_checkout_all_existing_connections's
751
803
  # rescue block, because doing so would put it outside of synchronize section, without
752
804
  # being in a critical section thread_report might become inaccurate
753
- msg = "could not obtain ownership of all database connections in #{checkout_timeout} seconds".dup
805
+ msg = +"could not obtain ownership of all database connections in #{checkout_timeout} seconds"
754
806
 
755
807
  thread_report = []
756
808
  @connections.each do |conn|
@@ -827,8 +879,8 @@ module ActiveRecord
827
879
  alias_method :release, :remove_connection_from_thread_cache
828
880
 
829
881
  def new_connection
830
- Base.send(spec.adapter_method, spec.config).tap do |conn|
831
- conn.schema_cache = schema_cache.dup if schema_cache
882
+ Base.public_send(db_config.adapter_method, db_config.configuration_hash).tap do |conn|
883
+ conn.check_version
832
884
  end
833
885
  end
834
886
 
@@ -931,156 +983,246 @@ module ActiveRecord
931
983
  # should use.
932
984
  #
933
985
  # The ConnectionHandler class is not coupled with the Active models, as it has no knowledge
934
- # about the model. The model needs to pass a specification name to the handler,
986
+ # about the model. The model needs to pass a connection specification name to the handler,
935
987
  # in order to look up the correct connection pool.
936
988
  class ConnectionHandler
937
- def self.create_owner_to_pool # :nodoc:
938
- Concurrent::Map.new(initial_capacity: 2) do |h, k|
939
- # Discard the parent's connection pools immediately; we have no need
940
- # of them
941
- discard_unowned_pools(h)
989
+ FINALIZER = lambda { |_| ActiveSupport::ForkTracker.check! }
990
+ private_constant :FINALIZER
942
991
 
943
- h[k] = Concurrent::Map.new(initial_capacity: 2)
944
- end
992
+ def initialize
993
+ # These caches are keyed by pool_config.connection_specification_name (PoolConfig#connection_specification_name).
994
+ @owner_to_pool_manager = Concurrent::Map.new(initial_capacity: 2)
995
+
996
+ # Backup finalizer: if the forked child skipped Kernel#fork the early discard has not occurred
997
+ ObjectSpace.define_finalizer self, FINALIZER
945
998
  end
946
999
 
947
- def self.unowned_pool_finalizer(pid_map) # :nodoc:
948
- lambda do |_|
949
- discard_unowned_pools(pid_map)
950
- end
1000
+ def prevent_writes # :nodoc:
1001
+ Thread.current[:prevent_writes]
1002
+ end
1003
+
1004
+ def prevent_writes=(prevent_writes) # :nodoc:
1005
+ Thread.current[:prevent_writes] = prevent_writes
951
1006
  end
952
1007
 
953
- def self.discard_unowned_pools(pid_map) # :nodoc:
954
- pid_map.each do |pid, pools|
955
- pools.values.compact.each(&:discard!) unless pid == Process.pid
1008
+ # Prevent writing to the database regardless of role.
1009
+ #
1010
+ # In some cases you may want to prevent writes to the database
1011
+ # even if you are on a database that can write. `while_preventing_writes`
1012
+ # will prevent writes to the database for the duration of the block.
1013
+ #
1014
+ # This method does not provide the same protection as a readonly
1015
+ # user and is meant to be a safeguard against accidental writes.
1016
+ #
1017
+ # See `READ_QUERY` for the queries that are blocked by this
1018
+ # method.
1019
+ def while_preventing_writes(enabled = true)
1020
+ unless ActiveRecord::Base.legacy_connection_handling
1021
+ raise NotImplementedError, "`while_preventing_writes` is only available on the connection_handler with legacy_connection_handling"
956
1022
  end
1023
+
1024
+ original, self.prevent_writes = self.prevent_writes, enabled
1025
+ yield
1026
+ ensure
1027
+ self.prevent_writes = original
957
1028
  end
958
1029
 
959
- def initialize
960
- # These caches are keyed by spec.name (ConnectionSpecification#name).
961
- @owner_to_pool = ConnectionHandler.create_owner_to_pool
1030
+ def connection_pool_names # :nodoc:
1031
+ owner_to_pool_manager.keys
1032
+ end
962
1033
 
963
- # Backup finalizer: if the forked child never needed a pool, the above
964
- # early discard has not occurred
965
- ObjectSpace.define_finalizer self, ConnectionHandler.unowned_pool_finalizer(@owner_to_pool)
1034
+ def all_connection_pools
1035
+ owner_to_pool_manager.values.flat_map { |m| m.pool_configs.map(&:pool) }
966
1036
  end
967
1037
 
968
- def connection_pool_list
969
- owner_to_pool.values.compact
1038
+ def connection_pool_list(role = ActiveRecord::Base.current_role)
1039
+ owner_to_pool_manager.values.flat_map { |m| m.pool_configs(role).map(&:pool) }
970
1040
  end
971
1041
  alias :connection_pools :connection_pool_list
972
1042
 
973
- def establish_connection(config)
974
- resolver = ConnectionSpecification::Resolver.new(Base.configurations)
975
- spec = resolver.spec(config)
1043
+ def establish_connection(config, owner_name: Base, role: ActiveRecord::Base.current_role, shard: Base.current_shard)
1044
+ owner_name = config.to_s if config.is_a?(Symbol)
1045
+
1046
+ pool_config = resolve_pool_config(config, owner_name)
1047
+ db_config = pool_config.db_config
976
1048
 
977
- remove_connection(spec.name)
1049
+ # Protects the connection named `ActiveRecord::Base` from being removed
1050
+ # if the user calls `establish_connection :primary`.
1051
+ if owner_to_pool_manager.key?(pool_config.connection_specification_name)
1052
+ remove_connection_pool(pool_config.connection_specification_name, role: role, shard: shard)
1053
+ end
978
1054
 
979
1055
  message_bus = ActiveSupport::Notifications.instrumenter
980
- payload = {
981
- connection_id: object_id
982
- }
983
- if spec
984
- payload[:spec_name] = spec.name
985
- payload[:config] = spec.config
1056
+ payload = {}
1057
+ if pool_config
1058
+ payload[:spec_name] = pool_config.connection_specification_name
1059
+ payload[:shard] = shard
1060
+ payload[:config] = db_config.configuration_hash
986
1061
  end
987
1062
 
988
- message_bus.instrument("!connection.active_record", payload) do
989
- owner_to_pool[spec.name] = ConnectionAdapters::ConnectionPool.new(spec)
1063
+ if ActiveRecord::Base.legacy_connection_handling
1064
+ owner_to_pool_manager[pool_config.connection_specification_name] ||= LegacyPoolManager.new
1065
+ else
1066
+ owner_to_pool_manager[pool_config.connection_specification_name] ||= PoolManager.new
990
1067
  end
1068
+ pool_manager = get_pool_manager(pool_config.connection_specification_name)
1069
+ pool_manager.set_pool_config(role, shard, pool_config)
991
1070
 
992
- owner_to_pool[spec.name]
1071
+ message_bus.instrument("!connection.active_record", payload) do
1072
+ pool_config.pool
1073
+ end
993
1074
  end
994
1075
 
995
1076
  # Returns true if there are any active connections among the connection
996
1077
  # pools that the ConnectionHandler is managing.
997
- def active_connections?
998
- connection_pool_list.any?(&:active_connection?)
1078
+ def active_connections?(role = ActiveRecord::Base.current_role)
1079
+ connection_pool_list(role).any?(&:active_connection?)
999
1080
  end
1000
1081
 
1001
1082
  # Returns any connections in use by the current thread back to the pool,
1002
1083
  # and also returns connections to the pool cached by threads that are no
1003
1084
  # longer alive.
1004
- def clear_active_connections!
1005
- connection_pool_list.each(&:release_connection)
1085
+ def clear_active_connections!(role = ActiveRecord::Base.current_role)
1086
+ connection_pool_list(role).each(&:release_connection)
1006
1087
  end
1007
1088
 
1008
1089
  # Clears the cache which maps classes.
1009
1090
  #
1010
1091
  # See ConnectionPool#clear_reloadable_connections! for details.
1011
- def clear_reloadable_connections!
1012
- connection_pool_list.each(&:clear_reloadable_connections!)
1092
+ def clear_reloadable_connections!(role = ActiveRecord::Base.current_role)
1093
+ connection_pool_list(role).each(&:clear_reloadable_connections!)
1013
1094
  end
1014
1095
 
1015
- def clear_all_connections!
1016
- connection_pool_list.each(&:disconnect!)
1096
+ def clear_all_connections!(role = ActiveRecord::Base.current_role)
1097
+ connection_pool_list(role).each(&:disconnect!)
1017
1098
  end
1018
1099
 
1019
1100
  # Disconnects all currently idle connections.
1020
1101
  #
1021
1102
  # See ConnectionPool#flush! for details.
1022
- def flush_idle_connections!
1023
- connection_pool_list.each(&:flush!)
1103
+ def flush_idle_connections!(role = ActiveRecord::Base.current_role)
1104
+ connection_pool_list(role).each(&:flush!)
1024
1105
  end
1025
1106
 
1026
1107
  # Locate the connection of the nearest super class. This can be an
1027
1108
  # active or defined connection: if it is the latter, it will be
1028
1109
  # opened and set as the active connection for the class it was defined
1029
1110
  # for (not necessarily the current class).
1030
- def retrieve_connection(spec_name) #:nodoc:
1031
- pool = retrieve_connection_pool(spec_name)
1032
- raise ConnectionNotEstablished, "No connection pool with '#{spec_name}' found." unless pool
1111
+ def retrieve_connection(spec_name, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard) # :nodoc:
1112
+ pool = retrieve_connection_pool(spec_name, role: role, shard: shard)
1113
+
1114
+ unless pool
1115
+ if shard != ActiveRecord::Base.default_shard
1116
+ message = "No connection pool for '#{spec_name}' found for the '#{shard}' shard."
1117
+ elsif ActiveRecord::Base.connection_handler != ActiveRecord::Base.default_connection_handler
1118
+ message = "No connection pool for '#{spec_name}' found for the '#{ActiveRecord::Base.current_role}' role."
1119
+ elsif role != ActiveRecord::Base.default_role
1120
+ message = "No connection pool for '#{spec_name}' found for the '#{role}' role."
1121
+ else
1122
+ message = "No connection pool for '#{spec_name}' found."
1123
+ end
1124
+
1125
+ raise ConnectionNotEstablished, message
1126
+ end
1127
+
1033
1128
  pool.connection
1034
1129
  end
1035
1130
 
1036
1131
  # Returns true if a connection that's accessible to this class has
1037
1132
  # already been opened.
1038
- def connected?(spec_name)
1039
- conn = retrieve_connection_pool(spec_name)
1040
- conn && conn.connected?
1133
+ def connected?(spec_name, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard)
1134
+ pool = retrieve_connection_pool(spec_name, role: role, shard: shard)
1135
+ pool && pool.connected?
1041
1136
  end
1042
1137
 
1043
1138
  # Remove the connection for this class. This will close the active
1044
1139
  # connection and the defined connection (if they exist). The result
1045
1140
  # can be used as an argument for #establish_connection, for easily
1046
1141
  # re-establishing the connection.
1047
- def remove_connection(spec_name)
1048
- if pool = owner_to_pool.delete(spec_name)
1049
- pool.automatic_reconnect = false
1050
- pool.disconnect!
1051
- pool.spec.config
1142
+ def remove_connection(owner, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard)
1143
+ remove_connection_pool(owner, role: role, shard: shard)&.configuration_hash
1144
+ end
1145
+ deprecate remove_connection: "Use #remove_connection_pool, which now returns a DatabaseConfig object instead of a Hash"
1146
+
1147
+ def remove_connection_pool(owner, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard)
1148
+ if pool_manager = get_pool_manager(owner)
1149
+ pool_config = pool_manager.remove_pool_config(role, shard)
1150
+
1151
+ if pool_config
1152
+ pool_config.disconnect!
1153
+ pool_config.db_config
1154
+ end
1052
1155
  end
1053
1156
  end
1054
1157
 
1055
- # Retrieving the connection pool happens a lot, so we cache it in @owner_to_pool.
1158
+ # Retrieving the connection pool happens a lot, so we cache it in @owner_to_pool_manager.
1056
1159
  # This makes retrieving the connection pool O(1) once the process is warm.
1057
1160
  # When a connection is established or removed, we invalidate the cache.
1058
- def retrieve_connection_pool(spec_name)
1059
- owner_to_pool.fetch(spec_name) do
1060
- # Check if a connection was previously established in an ancestor process,
1061
- # which may have been forked.
1062
- if ancestor_pool = pool_from_any_process_for(spec_name)
1063
- # A connection was established in an ancestor process that must have
1064
- # subsequently forked. We can't reuse the connection, but we can copy
1065
- # the specification and establish a new connection with it.
1066
- establish_connection(ancestor_pool.spec.to_hash).tap do |pool|
1067
- pool.schema_cache = ancestor_pool.schema_cache if ancestor_pool.schema_cache
1068
- end
1069
- else
1070
- owner_to_pool[spec_name] = nil
1071
- end
1072
- end
1161
+ def retrieve_connection_pool(owner, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard)
1162
+ pool_config = get_pool_manager(owner)&.get_pool_config(role, shard)
1163
+ pool_config&.pool
1073
1164
  end
1074
1165
 
1075
1166
  private
1167
+ attr_reader :owner_to_pool_manager
1076
1168
 
1077
- def owner_to_pool
1078
- @owner_to_pool[Process.pid]
1169
+ # Returns the pool manager for an owner.
1170
+ #
1171
+ # Using `"primary"` to look up the pool manager for `ActiveRecord::Base` is
1172
+ # deprecated in favor of looking it up by `"ActiveRecord::Base"`.
1173
+ #
1174
+ # During the deprecation period, if `"primary"` is passed, the pool manager
1175
+ # for `ActiveRecord::Base` will still be returned.
1176
+ def get_pool_manager(owner)
1177
+ return owner_to_pool_manager[owner] if owner_to_pool_manager.key?(owner)
1178
+
1179
+ if owner == "primary"
1180
+ ActiveSupport::Deprecation.warn("Using `\"primary\"` as a `connection_specification_name` is deprecated and will be removed in Rails 6.2.0. Please use `ActiveRecord::Base`.")
1181
+ owner_to_pool_manager[Base.name]
1182
+ end
1079
1183
  end
1080
1184
 
1081
- def pool_from_any_process_for(spec_name)
1082
- owner_to_pool = @owner_to_pool.values.reverse.find { |v| v[spec_name] }
1083
- owner_to_pool && owner_to_pool[spec_name]
1185
+ # Returns an instance of PoolConfig for a given adapter.
1186
+ # Accepts a hash one layer deep that contains all connection information.
1187
+ #
1188
+ # == Example
1189
+ #
1190
+ # config = { "production" => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3" } }
1191
+ # pool_config = Base.configurations.resolve_pool_config(:production)
1192
+ # pool_config.db_config.configuration_hash
1193
+ # # => { host: "localhost", database: "foo", adapter: "sqlite3" }
1194
+ #
1195
+ def resolve_pool_config(config, owner_name)
1196
+ db_config = Base.configurations.resolve(config)
1197
+
1198
+ raise(AdapterNotSpecified, "database configuration does not specify adapter") unless db_config.adapter
1199
+
1200
+ # Require the adapter itself and give useful feedback about
1201
+ # 1. Missing adapter gems and
1202
+ # 2. Adapter gems' missing dependencies.
1203
+ path_to_adapter = "active_record/connection_adapters/#{db_config.adapter}_adapter"
1204
+ begin
1205
+ require path_to_adapter
1206
+ rescue LoadError => e
1207
+ # We couldn't require the adapter itself. Raise an exception that
1208
+ # points out config typos and missing gems.
1209
+ if e.path == path_to_adapter
1210
+ # We can assume that a non-builtin adapter was specified, so it's
1211
+ # either misspelled or missing from Gemfile.
1212
+ raise LoadError, "Could not load the '#{db_config.adapter}' Active Record adapter. Ensure that the adapter is spelled correctly in config/database.yml and that you've added the necessary adapter gem to your Gemfile.", e.backtrace
1213
+
1214
+ # Bubbled up from the adapter require. Prefix the exception message
1215
+ # with some guidance about how to address it and reraise.
1216
+ else
1217
+ raise LoadError, "Error loading the '#{db_config.adapter}' Active Record adapter. Missing a gem it depends on? #{e.message}", e.backtrace
1218
+ end
1219
+ end
1220
+
1221
+ unless ActiveRecord::Base.respond_to?(db_config.adapter_method)
1222
+ raise AdapterNotFound, "database configuration specifies nonexistent #{db_config.adapter} adapter"
1223
+ end
1224
+
1225
+ ConnectionAdapters::PoolConfig.new(owner_name, db_config)
1084
1226
  end
1085
1227
  end
1086
1228
  end