activerecord 6.0.3.5 → 6.1.0.rc1

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 (245) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +774 -735
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -2
  5. data/lib/active_record.rb +7 -14
  6. data/lib/active_record/aggregations.rb +1 -1
  7. data/lib/active_record/association_relation.rb +22 -14
  8. data/lib/active_record/associations.rb +114 -11
  9. data/lib/active_record/associations/alias_tracker.rb +19 -15
  10. data/lib/active_record/associations/association.rb +40 -29
  11. data/lib/active_record/associations/association_scope.rb +17 -15
  12. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  14. data/lib/active_record/associations/builder/association.rb +9 -3
  15. data/lib/active_record/associations/builder/belongs_to.rb +10 -7
  16. data/lib/active_record/associations/builder/collection_association.rb +5 -4
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -1
  18. data/lib/active_record/associations/builder/has_many.rb +6 -2
  19. data/lib/active_record/associations/builder/has_one.rb +11 -14
  20. data/lib/active_record/associations/builder/singular_association.rb +1 -1
  21. data/lib/active_record/associations/collection_association.rb +19 -6
  22. data/lib/active_record/associations/collection_proxy.rb +13 -5
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +24 -2
  25. data/lib/active_record/associations/has_many_through_association.rb +10 -4
  26. data/lib/active_record/associations/has_one_association.rb +15 -1
  27. data/lib/active_record/associations/join_dependency.rb +72 -50
  28. data/lib/active_record/associations/join_dependency/join_association.rb +36 -14
  29. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  30. data/lib/active_record/associations/preloader.rb +11 -5
  31. data/lib/active_record/associations/preloader/association.rb +51 -25
  32. data/lib/active_record/associations/preloader/through_association.rb +2 -2
  33. data/lib/active_record/associations/singular_association.rb +1 -1
  34. data/lib/active_record/associations/through_association.rb +1 -1
  35. data/lib/active_record/attribute_assignment.rb +10 -8
  36. data/lib/active_record/attribute_methods.rb +52 -48
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
  38. data/lib/active_record/attribute_methods/dirty.rb +1 -11
  39. data/lib/active_record/attribute_methods/primary_key.rb +6 -2
  40. data/lib/active_record/attribute_methods/query.rb +3 -6
  41. data/lib/active_record/attribute_methods/read.rb +8 -11
  42. data/lib/active_record/attribute_methods/serialization.rb +4 -4
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -13
  44. data/lib/active_record/attribute_methods/write.rb +12 -20
  45. data/lib/active_record/attributes.rb +27 -7
  46. data/lib/active_record/autosave_association.rb +57 -40
  47. data/lib/active_record/base.rb +2 -14
  48. data/lib/active_record/callbacks.rb +32 -22
  49. data/lib/active_record/coders/yaml_column.rb +1 -1
  50. data/lib/active_record/connection_adapters.rb +50 -0
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +186 -134
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -22
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -7
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
  56. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
  58. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +112 -27
  59. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  60. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +224 -85
  61. data/lib/active_record/connection_adapters/abstract/transaction.rb +66 -24
  62. data/lib/active_record/connection_adapters/abstract_adapter.rb +36 -69
  63. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +129 -88
  64. data/lib/active_record/connection_adapters/column.rb +15 -1
  65. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  66. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  67. data/lib/active_record/connection_adapters/mysql/database_statements.rb +23 -25
  68. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
  69. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  70. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +33 -6
  71. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  72. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  73. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +11 -7
  74. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  75. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -12
  76. data/lib/active_record/connection_adapters/pool_config.rb +63 -0
  77. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  78. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  79. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +13 -54
  80. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  82. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  83. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
  84. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -5
  90. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
  91. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  92. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  93. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
  94. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
  95. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  96. data/lib/active_record/connection_adapters/postgresql_adapter.rb +72 -55
  97. data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
  98. data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
  99. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +31 -6
  100. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
  101. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  102. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +37 -4
  103. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +49 -50
  104. data/lib/active_record/connection_handling.rb +210 -71
  105. data/lib/active_record/core.rb +220 -55
  106. data/lib/active_record/database_configurations.rb +124 -85
  107. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  108. data/lib/active_record/database_configurations/database_config.rb +52 -9
  109. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  110. data/lib/active_record/database_configurations/url_config.rb +15 -40
  111. data/lib/active_record/delegated_type.rb +209 -0
  112. data/lib/active_record/destroy_association_async_job.rb +36 -0
  113. data/lib/active_record/enum.rb +27 -10
  114. data/lib/active_record/errors.rb +47 -12
  115. data/lib/active_record/explain.rb +9 -4
  116. data/lib/active_record/explain_subscriber.rb +1 -1
  117. data/lib/active_record/fixture_set/file.rb +10 -17
  118. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  119. data/lib/active_record/fixture_set/render_context.rb +1 -1
  120. data/lib/active_record/fixture_set/table_row.rb +2 -2
  121. data/lib/active_record/fixtures.rb +54 -8
  122. data/lib/active_record/gem_version.rb +3 -3
  123. data/lib/active_record/inheritance.rb +40 -18
  124. data/lib/active_record/insert_all.rb +33 -6
  125. data/lib/active_record/integration.rb +3 -5
  126. data/lib/active_record/internal_metadata.rb +15 -4
  127. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  128. data/lib/active_record/locking/optimistic.rb +22 -16
  129. data/lib/active_record/locking/pessimistic.rb +6 -2
  130. data/lib/active_record/log_subscriber.rb +26 -8
  131. data/lib/active_record/middleware/database_selector.rb +4 -1
  132. data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
  133. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  134. data/lib/active_record/migration.rb +113 -83
  135. data/lib/active_record/migration/command_recorder.rb +47 -27
  136. data/lib/active_record/migration/compatibility.rb +67 -17
  137. data/lib/active_record/model_schema.rb +88 -13
  138. data/lib/active_record/nested_attributes.rb +2 -3
  139. data/lib/active_record/no_touching.rb +1 -1
  140. data/lib/active_record/persistence.rb +50 -45
  141. data/lib/active_record/query_cache.rb +15 -5
  142. data/lib/active_record/querying.rb +11 -6
  143. data/lib/active_record/railtie.rb +64 -44
  144. data/lib/active_record/railties/databases.rake +253 -98
  145. data/lib/active_record/readonly_attributes.rb +4 -0
  146. data/lib/active_record/reflection.rb +70 -57
  147. data/lib/active_record/relation.rb +96 -67
  148. data/lib/active_record/relation/batches.rb +38 -31
  149. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  150. data/lib/active_record/relation/calculations.rb +101 -44
  151. data/lib/active_record/relation/delegation.rb +2 -1
  152. data/lib/active_record/relation/finder_methods.rb +45 -15
  153. data/lib/active_record/relation/from_clause.rb +1 -1
  154. data/lib/active_record/relation/merger.rb +27 -25
  155. data/lib/active_record/relation/predicate_builder.rb +57 -33
  156. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  157. data/lib/active_record/relation/predicate_builder/association_query_value.rb +2 -2
  158. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +3 -3
  159. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  160. data/lib/active_record/relation/query_methods.rb +330 -195
  161. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  162. data/lib/active_record/relation/spawn_methods.rb +6 -5
  163. data/lib/active_record/relation/where_clause.rb +104 -57
  164. data/lib/active_record/result.rb +41 -33
  165. data/lib/active_record/runtime_registry.rb +2 -2
  166. data/lib/active_record/sanitization.rb +6 -17
  167. data/lib/active_record/schema_dumper.rb +34 -4
  168. data/lib/active_record/schema_migration.rb +0 -4
  169. data/lib/active_record/scoping/named.rb +6 -17
  170. data/lib/active_record/secure_token.rb +16 -8
  171. data/lib/active_record/serialization.rb +5 -3
  172. data/lib/active_record/signed_id.rb +116 -0
  173. data/lib/active_record/statement_cache.rb +20 -4
  174. data/lib/active_record/store.rb +2 -2
  175. data/lib/active_record/suppressor.rb +2 -2
  176. data/lib/active_record/table_metadata.rb +36 -52
  177. data/lib/active_record/tasks/database_tasks.rb +139 -113
  178. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
  179. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
  180. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
  181. data/lib/active_record/test_databases.rb +5 -4
  182. data/lib/active_record/test_fixtures.rb +37 -16
  183. data/lib/active_record/timestamp.rb +4 -6
  184. data/lib/active_record/touch_later.rb +21 -21
  185. data/lib/active_record/transactions.rb +15 -64
  186. data/lib/active_record/type.rb +8 -1
  187. data/lib/active_record/type/serialized.rb +6 -2
  188. data/lib/active_record/type/time.rb +10 -0
  189. data/lib/active_record/type_caster/connection.rb +0 -1
  190. data/lib/active_record/type_caster/map.rb +8 -5
  191. data/lib/active_record/validations.rb +1 -0
  192. data/lib/active_record/validations/numericality.rb +35 -0
  193. data/lib/active_record/validations/uniqueness.rb +24 -4
  194. data/lib/arel.rb +5 -13
  195. data/lib/arel/attributes/attribute.rb +4 -0
  196. data/lib/arel/collectors/bind.rb +5 -0
  197. data/lib/arel/collectors/composite.rb +8 -0
  198. data/lib/arel/collectors/sql_string.rb +7 -0
  199. data/lib/arel/collectors/substitute_binds.rb +7 -0
  200. data/lib/arel/nodes.rb +3 -1
  201. data/lib/arel/nodes/binary.rb +82 -8
  202. data/lib/arel/nodes/bind_param.rb +8 -0
  203. data/lib/arel/nodes/casted.rb +21 -9
  204. data/lib/arel/nodes/equality.rb +6 -9
  205. data/lib/arel/nodes/grouping.rb +3 -0
  206. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  207. data/lib/arel/nodes/in.rb +8 -1
  208. data/lib/arel/nodes/infix_operation.rb +13 -1
  209. data/lib/arel/nodes/join_source.rb +1 -1
  210. data/lib/arel/nodes/node.rb +7 -6
  211. data/lib/arel/nodes/ordering.rb +27 -0
  212. data/lib/arel/nodes/sql_literal.rb +3 -0
  213. data/lib/arel/nodes/table_alias.rb +7 -3
  214. data/lib/arel/nodes/unary.rb +0 -1
  215. data/lib/arel/predications.rb +12 -18
  216. data/lib/arel/select_manager.rb +1 -2
  217. data/lib/arel/table.rb +13 -5
  218. data/lib/arel/visitors.rb +0 -7
  219. data/lib/arel/visitors/dot.rb +14 -2
  220. data/lib/arel/visitors/mysql.rb +11 -1
  221. data/lib/arel/visitors/postgresql.rb +15 -4
  222. data/lib/arel/visitors/to_sql.rb +89 -78
  223. data/lib/rails/generators/active_record/migration.rb +6 -1
  224. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  225. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  226. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +3 -3
  227. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  228. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  229. metadata +27 -28
  230. data/lib/active_record/advisory_lock_base.rb +0 -18
  231. data/lib/active_record/attribute_decorators.rb +0 -88
  232. data/lib/active_record/connection_adapters/connection_specification.rb +0 -296
  233. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  234. data/lib/active_record/define_callbacks.rb +0 -22
  235. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  236. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  237. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  238. data/lib/arel/attributes.rb +0 -22
  239. data/lib/arel/visitors/depth_first.rb +0 -203
  240. data/lib/arel/visitors/ibm_db.rb +0 -34
  241. data/lib/arel/visitors/informix.rb +0 -62
  242. data/lib/arel/visitors/mssql.rb +0 -156
  243. data/lib/arel/visitors/oracle.rb +0 -158
  244. data/lib/arel/visitors/oracle12.rb +0 -65
  245. data/lib/arel/visitors/where_sql.rb +0 -22
@@ -1,22 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "yaml"
4
3
  require "active_support/benchmarkable"
5
4
  require "active_support/dependencies"
6
5
  require "active_support/descendants_tracker"
7
6
  require "active_support/time"
8
- require "active_support/core_ext/module/attribute_accessors"
9
- require "active_support/core_ext/array/extract_options"
10
- require "active_support/core_ext/hash/deep_merge"
11
- require "active_support/core_ext/hash/slice"
12
- require "active_support/core_ext/string/behavior"
13
- require "active_support/core_ext/kernel/singleton_class"
14
- require "active_support/core_ext/module/introspection"
15
- require "active_support/core_ext/object/duplicable"
16
7
  require "active_support/core_ext/class/subclasses"
17
- require "active_record/attribute_decorators"
18
- require "active_record/define_callbacks"
19
- require "active_record/errors"
20
8
  require "active_record/log_subscriber"
21
9
  require "active_record/explain_subscriber"
22
10
  require "active_record/relation/delegation"
@@ -285,6 +273,7 @@ module ActiveRecord #:nodoc:
285
273
  extend Querying
286
274
  extend Translation
287
275
  extend DynamicMatchers
276
+ extend DelegatedType
288
277
  extend Explain
289
278
  extend Enum
290
279
  extend Delegation::DelegateCache
@@ -303,10 +292,8 @@ module ActiveRecord #:nodoc:
303
292
  include Validations
304
293
  include CounterCache
305
294
  include Attributes
306
- include AttributeDecorators
307
295
  include Locking::Optimistic
308
296
  include Locking::Pessimistic
309
- include DefineCallbacks
310
297
  include AttributeMethods
311
298
  include Callbacks
312
299
  include Timestamp
@@ -321,6 +308,7 @@ module ActiveRecord #:nodoc:
321
308
  include Serialization
322
309
  include Store
323
310
  include SecureToken
311
+ include SignedId
324
312
  include Suppressor
325
313
  end
326
314
 
@@ -4,7 +4,7 @@ module ActiveRecord
4
4
  # = Active Record \Callbacks
5
5
  #
6
6
  # \Callbacks are hooks into the life cycle of an Active Record object that allow you to trigger logic
7
- # before or after an alteration of the object state. This can be used to make sure that associated and
7
+ # before or after a change in the object state. This can be used to make sure that associated and
8
8
  # dependent objects are deleted when {ActiveRecord::Base#destroy}[rdoc-ref:Persistence#destroy] is called (by overwriting +before_destroy+) or
9
9
  # to massage attributes before they're validated (by overwriting +before_validation+).
10
10
  # As an example of the callbacks initiated, consider the {ActiveRecord::Base#save}[rdoc-ref:Persistence#save] call for a new record:
@@ -32,7 +32,7 @@ module ActiveRecord
32
32
  # is found and instantiated by a finder, with <tt>after_initialize</tt> being triggered after new objects
33
33
  # are instantiated as well.
34
34
  #
35
- # There are nineteen callbacks in total, which give you immense power to react and prepare for each state in the
35
+ # There are nineteen callbacks in total, which give a lot of control over how to react and prepare for each state in the
36
36
  # Active Record life cycle. The sequence for calling {ActiveRecord::Base#save}[rdoc-ref:Persistence#save] for an existing record is similar,
37
37
  # except that each <tt>_create</tt> callback is replaced by the corresponding <tt>_update</tt> callback.
38
38
  #
@@ -64,7 +64,7 @@ module ActiveRecord
64
64
  #
65
65
  # Besides the overwritable callback methods, it's also possible to register callbacks through the
66
66
  # use of the callback macros. Their main advantage is that the macros add behavior into a callback
67
- # queue that is kept intact down through an inheritance hierarchy.
67
+ # queue that is kept intact through an inheritance hierarchy.
68
68
  #
69
69
  # class Topic < ActiveRecord::Base
70
70
  # before_destroy :destroy_author
@@ -74,7 +74,7 @@ module ActiveRecord
74
74
  # before_destroy :destroy_readers
75
75
  # end
76
76
  #
77
- # Now, when <tt>Topic#destroy</tt> is run only +destroy_author+ is called. When <tt>Reply#destroy</tt> is
77
+ # When <tt>Topic#destroy</tt> is run only +destroy_author+ is called. When <tt>Reply#destroy</tt> is
78
78
  # run, both +destroy_author+ and +destroy_readers+ are called.
79
79
  #
80
80
  # *IMPORTANT:* In order for inheritance to work for the callback queues, you must specify the
@@ -83,10 +83,9 @@ module ActiveRecord
83
83
  #
84
84
  # == Types of callbacks
85
85
  #
86
- # There are four types of callbacks accepted by the callback macros: Method references (symbol), callback objects,
87
- # inline methods (using a proc). Method references and callback objects
88
- # are the recommended approaches, inline methods using a proc are sometimes appropriate (such as for
89
- # creating mix-ins).
86
+ # There are three types of callbacks accepted by the callback macros: method references (symbol), callback objects,
87
+ # inline methods (using a proc). Method references and callback objects are the recommended approaches,
88
+ # inline methods using a proc are sometimes appropriate (such as for creating mix-ins).
90
89
  #
91
90
  # The method reference callbacks work by specifying a protected or private method available in the object, like this:
92
91
  #
@@ -179,8 +178,8 @@ module ActiveRecord
179
178
  #
180
179
  # == Ordering callbacks
181
180
  #
182
- # Sometimes the code needs that the callbacks execute in a specific order. For example, a +before_destroy+
183
- # callback (+log_children+ in this case) should be executed before the children get destroyed by the
181
+ # Sometimes application code requires that callbacks execute in a specific order. For example, a +before_destroy+
182
+ # callback (+log_children+ in this case) should be executed before records in the +children+ association are destroyed by the
184
183
  # <tt>dependent: :destroy</tt> option.
185
184
  #
186
185
  # Let's look at the code below:
@@ -196,8 +195,8 @@ module ActiveRecord
196
195
  # end
197
196
  # end
198
197
  #
199
- # In this case, the problem is that when the +before_destroy+ callback is executed, the children are not available
200
- # because the {ActiveRecord::Base#destroy}[rdoc-ref:Persistence#destroy] callback gets executed first.
198
+ # In this case, the problem is that when the +before_destroy+ callback is executed, records in the +children+ association no
199
+ # longer exist because the {ActiveRecord::Base#destroy}[rdoc-ref:Persistence#destroy] callback was executed first.
201
200
  # You can use the +prepend+ option on the +before_destroy+ callback to avoid this.
202
201
  #
203
202
  # class Topic < ActiveRecord::Base
@@ -211,7 +210,7 @@ module ActiveRecord
211
210
  # end
212
211
  # end
213
212
  #
214
- # This way, the +before_destroy+ gets executed before the <tt>dependent: :destroy</tt> is called, and the data is still available.
213
+ # This way, the +before_destroy+ is executed before the <tt>dependent: :destroy</tt> is called, and the data is still available.
215
214
  #
216
215
  # Also, there are cases when you want several callbacks of the same type to
217
216
  # be executed in order.
@@ -235,10 +234,10 @@ module ActiveRecord
235
234
  # end
236
235
  # end
237
236
  #
238
- # In this case the +log_children+ gets executed before +do_something_else+.
237
+ # In this case the +log_children+ is executed before +do_something_else+.
239
238
  # The same applies to all non-transactional callbacks.
240
239
  #
241
- # In case there are multiple transactional callbacks as seen below, the order
240
+ # As seen below, in case there are multiple transactional callbacks the order
242
241
  # is reversed.
243
242
  #
244
243
  # For example:
@@ -260,16 +259,16 @@ module ActiveRecord
260
259
  # end
261
260
  # end
262
261
  #
263
- # In this case the +do_something_else+ gets executed before +log_children+.
262
+ # In this case the +do_something_else+ is executed before +log_children+.
264
263
  #
265
264
  # == \Transactions
266
265
  #
267
266
  # The entire callback chain of a {#save}[rdoc-ref:Persistence#save], {#save!}[rdoc-ref:Persistence#save!],
268
267
  # or {#destroy}[rdoc-ref:Persistence#destroy] call runs within a transaction. That includes <tt>after_*</tt> hooks.
269
- # If everything goes fine a COMMIT is executed once the chain has been completed.
268
+ # If everything goes fine a +COMMIT+ is executed once the chain has been completed.
270
269
  #
271
- # If a <tt>before_*</tt> callback cancels the action a ROLLBACK is issued. You
272
- # can also trigger a ROLLBACK raising an exception in any of the callbacks,
270
+ # If a <tt>before_*</tt> callback cancels the action a +ROLLBACK+ is issued. You
271
+ # can also trigger a +ROLLBACK+ raising an exception in any of the callbacks,
273
272
  # including <tt>after_*</tt> hooks. Note, however, that in that case the client
274
273
  # needs to be aware of it because an ordinary {#save}[rdoc-ref:Persistence#save] will raise such exception
275
274
  # instead of quietly returning +false+.
@@ -280,17 +279,17 @@ module ActiveRecord
280
279
  # <tt>:before</tt>, <tt>:after</tt> and <tt>:around</tt> as values for the <tt>kind</tt> property. The <tt>kind</tt> property
281
280
  # defines what part of the chain the callback runs in.
282
281
  #
283
- # To find all callbacks in the before_save callback chain:
282
+ # To find all callbacks in the +before_save+ callback chain:
284
283
  #
285
284
  # Topic._save_callbacks.select { |cb| cb.kind.eql?(:before) }
286
285
  #
287
- # Returns an array of callback objects that form the before_save chain.
286
+ # Returns an array of callback objects that form the +before_save+ chain.
288
287
  #
289
288
  # To further check if the before_save chain contains a proc defined as <tt>rest_when_dead</tt> use the <tt>filter</tt> property of the callback object:
290
289
  #
291
290
  # Topic._save_callbacks.select { |cb| cb.kind.eql?(:before) }.collect(&:filter).include?(:rest_when_dead)
292
291
  #
293
- # Returns true or false depending on whether the proc is contained in the before_save callback chain on a Topic model.
292
+ # Returns true or false depending on whether the proc is contained in the +before_save+ callback chain on a Topic model.
294
293
  #
295
294
  module Callbacks
296
295
  extend ActiveSupport::Concern
@@ -302,6 +301,17 @@ module ActiveRecord
302
301
  :before_destroy, :around_destroy, :after_destroy, :after_commit, :after_rollback
303
302
  ]
304
303
 
304
+ module ClassMethods # :nodoc:
305
+ include ActiveModel::Callbacks
306
+ end
307
+
308
+ included do
309
+ include ActiveModel::Validations::Callbacks
310
+
311
+ define_model_callbacks :initialize, :find, :touch, only: :after
312
+ define_model_callbacks :save, :create, :update, :destroy
313
+ end
314
+
305
315
  def destroy #:nodoc:
306
316
  @_destroy_callback_already_called ||= false
307
317
  return if @_destroy_callback_already_called
@@ -22,7 +22,7 @@ module ActiveRecord
22
22
 
23
23
  def load(yaml)
24
24
  return object_class.new if object_class != Object && yaml.nil?
25
- return yaml unless yaml.is_a?(String) && /^---/.match?(yaml)
25
+ return yaml unless yaml.is_a?(String) && yaml.start_with?("---")
26
26
  obj = YAML.load(yaml)
27
27
 
28
28
  assert_valid_value(obj, action: "load")
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module ConnectionAdapters
5
+ extend ActiveSupport::Autoload
6
+
7
+ eager_autoload do
8
+ autoload :AbstractAdapter
9
+ end
10
+
11
+ autoload :Column
12
+ autoload :PoolConfig
13
+ autoload :PoolManager
14
+ autoload :LegacyPoolManager
15
+
16
+ autoload_at "active_record/connection_adapters/abstract/schema_definitions" do
17
+ autoload :IndexDefinition
18
+ autoload :ColumnDefinition
19
+ autoload :ChangeColumnDefinition
20
+ autoload :ForeignKeyDefinition
21
+ autoload :CheckConstraintDefinition
22
+ autoload :TableDefinition
23
+ autoload :Table
24
+ autoload :AlterTable
25
+ autoload :ReferenceDefinition
26
+ end
27
+
28
+ autoload_at "active_record/connection_adapters/abstract/connection_pool" do
29
+ autoload :ConnectionHandler
30
+ end
31
+
32
+ autoload_under "abstract" do
33
+ autoload :SchemaStatements
34
+ autoload :DatabaseStatements
35
+ autoload :DatabaseLimits
36
+ autoload :Quoting
37
+ autoload :ConnectionPool
38
+ autoload :QueryCache
39
+ autoload :Savepoints
40
+ end
41
+
42
+ autoload_at "active_record/connection_adapters/abstract/transaction" do
43
+ autoload :TransactionManager
44
+ autoload :NullTransaction
45
+ autoload :RealTransaction
46
+ autoload :SavepointTransaction
47
+ autoload :TransactionState
48
+ end
49
+ end
50
+ end
@@ -6,38 +6,23 @@ require "monitor"
6
6
  require "weakref"
7
7
 
8
8
  module ActiveRecord
9
- # Raised when a connection could not be obtained within the connection
10
- # acquisition timeout period: because max connections in pool
11
- # are in use.
12
- class ConnectionTimeoutError < ConnectionNotEstablished
13
- end
14
-
15
- # Raised when a pool was unable to get ahold of all its connections
16
- # to perform a "group" action such as
17
- # {ActiveRecord::Base.connection_pool.disconnect!}[rdoc-ref:ConnectionAdapters::ConnectionPool#disconnect!]
18
- # or {ActiveRecord::Base.clear_reloadable_connections!}[rdoc-ref:ConnectionAdapters::ConnectionHandler#clear_reloadable_connections!].
19
- class ExclusiveConnectionTimeoutError < ConnectionTimeoutError
20
- end
21
-
22
9
  module ConnectionAdapters
23
10
  module AbstractPool # :nodoc:
24
11
  def get_schema_cache(connection)
25
- @schema_cache ||= SchemaCache.new(connection)
26
- @schema_cache.connection = connection
27
- @schema_cache
12
+ self.schema_cache ||= SchemaCache.new(connection)
13
+ schema_cache.connection = connection
14
+ schema_cache
28
15
  end
29
16
 
30
17
  def set_schema_cache(cache)
31
- @schema_cache = cache
18
+ self.schema_cache = cache
32
19
  end
33
20
  end
34
21
 
35
22
  class NullPool # :nodoc:
36
23
  include ConnectionAdapters::AbstractPool
37
24
 
38
- def initialize
39
- @schema_cache = nil
40
- end
25
+ attr_accessor :schema_cache
41
26
  end
42
27
 
43
28
  # Connection pool base class for managing Active Record database
@@ -150,7 +135,7 @@ module ActiveRecord
150
135
 
151
136
  # Remove the head of the queue.
152
137
  #
153
- # If +timeout+ is not given, remove and return the head the
138
+ # If +timeout+ is not given, remove and return the head of the
154
139
  # queue if the number of available elements is strictly
155
140
  # greater than the number of threads currently waiting (that
156
141
  # is, don't jump ahead in line). Otherwise, return +nil+.
@@ -193,7 +178,7 @@ module ActiveRecord
193
178
  @queue.pop
194
179
  end
195
180
 
196
- # Remove and return the head the queue if the number of
181
+ # Remove and return the head of the queue if the number of
197
182
  # available elements is strictly greater than the number of
198
183
  # threads currently waiting. Otherwise, return +nil+.
199
184
  def no_wait_poll
@@ -332,11 +317,17 @@ module ActiveRecord
332
317
  private
333
318
  def spawn_thread(frequency)
334
319
  Thread.new(frequency) do |t|
320
+ # Advise multi-threaded app servers to ignore this thread for
321
+ # the purposes of fork safety warnings
322
+ Thread.current.thread_variable_set(:fork_safe, true)
335
323
  running = true
336
324
  while running
337
325
  sleep t
338
326
  @mutex.synchronize do
339
- @pools[frequency].select!(&:weakref_alive?)
327
+ @pools[frequency].select! do |pool|
328
+ pool.weakref_alive? && !pool.discarded?
329
+ end
330
+
340
331
  @pools[frequency].each do |p|
341
332
  p.reap
342
333
  p.flush
@@ -364,28 +355,26 @@ module ActiveRecord
364
355
  include QueryCache::ConnectionPoolConfiguration
365
356
  include ConnectionAdapters::AbstractPool
366
357
 
367
- attr_accessor :automatic_reconnect, :checkout_timeout, :schema_cache
368
- attr_reader :spec, :size, :reaper
358
+ attr_accessor :automatic_reconnect, :checkout_timeout
359
+ attr_reader :db_config, :size, :reaper, :pool_config
369
360
 
370
- # Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
361
+ delegate :schema_cache, :schema_cache=, to: :pool_config
362
+
363
+ # Creates a new ConnectionPool object. +pool_config+ is a PoolConfig
371
364
  # object which describes database connection information (e.g. adapter,
372
365
  # host name, username, password, etc), as well as the maximum size for
373
366
  # this ConnectionPool.
374
367
  #
375
368
  # The default ConnectionPool maximum size is 5.
376
- def initialize(spec)
369
+ def initialize(pool_config)
377
370
  super()
378
371
 
379
- @spec = spec
380
-
381
- @checkout_timeout = (spec.config[:checkout_timeout] && spec.config[:checkout_timeout].to_f) || 5
382
- if @idle_timeout = spec.config.fetch(:idle_timeout, 300)
383
- @idle_timeout = @idle_timeout.to_f
384
- @idle_timeout = nil if @idle_timeout <= 0
385
- end
372
+ @pool_config = pool_config
373
+ @db_config = pool_config.db_config
386
374
 
387
- # default max pool size to 5
388
- @size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
375
+ @checkout_timeout = db_config.checkout_timeout
376
+ @idle_timeout = db_config.idle_timeout
377
+ @size = db_config.pool
389
378
 
390
379
  # This variable tracks the cache of threads mapped to reserved connections, with the
391
380
  # sole purpose of speeding up the +connection+ method. It is not the authoritative
@@ -413,10 +402,7 @@ module ActiveRecord
413
402
 
414
403
  @lock_thread = false
415
404
 
416
- # +reaping_frequency+ is configurable mostly for historical reasons, but it could
417
- # also be useful if someone wants a very low +idle_timeout+.
418
- reaping_frequency = spec.config.fetch(:reaping_frequency, 60)
419
- @reaper = Reaper.new(self, reaping_frequency && reaping_frequency.to_f)
405
+ @reaper = Reaper.new(self, db_config.reaping_frequency)
420
406
  @reaper.run
421
407
  end
422
408
 
@@ -498,7 +484,7 @@ module ActiveRecord
498
484
  # Raises:
499
485
  # - ActiveRecord::ExclusiveConnectionTimeoutError if unable to gain ownership of all
500
486
  # connections in the pool within a timeout interval (default duration is
501
- # <tt>spec.config[:checkout_timeout] * 2</tt> seconds).
487
+ # <tt>spec.db_config.checkout_timeout * 2</tt> seconds).
502
488
  def disconnect(raise_on_acquisition_timeout = true)
503
489
  with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
504
490
  synchronize do
@@ -519,7 +505,7 @@ module ActiveRecord
519
505
  #
520
506
  # The pool first tries to gain ownership of all connections. If unable to
521
507
  # do so within a timeout interval (default duration is
522
- # <tt>spec.config[:checkout_timeout] * 2</tt> seconds), then the pool is forcefully
508
+ # <tt>spec.db_config.checkout_timeout * 2</tt> seconds), then the pool is forcefully
523
509
  # disconnected without any regard for other connection owning threads.
524
510
  def disconnect!
525
511
  disconnect(false)
@@ -532,7 +518,7 @@ module ActiveRecord
532
518
  # See AbstractAdapter#discard!
533
519
  def discard! # :nodoc:
534
520
  synchronize do
535
- return if @connections.nil? # already discarded
521
+ return if self.discarded?
536
522
  @connections.each do |conn|
537
523
  conn.discard!
538
524
  end
@@ -540,13 +526,17 @@ module ActiveRecord
540
526
  end
541
527
  end
542
528
 
529
+ def discarded? # :nodoc:
530
+ @connections.nil?
531
+ end
532
+
543
533
  # Clears the cache which maps classes and re-connects connections that
544
534
  # require reloading.
545
535
  #
546
536
  # Raises:
547
537
  # - ActiveRecord::ExclusiveConnectionTimeoutError if unable to gain ownership of all
548
538
  # connections in the pool within a timeout interval (default duration is
549
- # <tt>spec.config[:checkout_timeout] * 2</tt> seconds).
539
+ # <tt>spec.db_config.checkout_timeout * 2</tt> seconds).
550
540
  def clear_reloadable_connections(raise_on_acquisition_timeout = true)
551
541
  with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
552
542
  synchronize do
@@ -568,7 +558,7 @@ module ActiveRecord
568
558
  #
569
559
  # The pool first tries to gain ownership of all connections. If unable to
570
560
  # do so within a timeout interval (default duration is
571
- # <tt>spec.config[:checkout_timeout] * 2</tt> seconds), then the pool forcefully
561
+ # <tt>spec.db_config.checkout_timeout * 2</tt> seconds), then the pool forcefully
572
562
  # clears the cache and reloads connections without any regard for other
573
563
  # connection owning threads.
574
564
  def clear_reloadable_connections!
@@ -648,7 +638,7 @@ module ActiveRecord
648
638
  # or a thread dies unexpectedly.
649
639
  def reap
650
640
  stale_connections = synchronize do
651
- return unless @connections
641
+ return if self.discarded?
652
642
  @connections.select do |conn|
653
643
  conn.in_use? && !conn.owner.alive?
654
644
  end.each do |conn|
@@ -673,7 +663,7 @@ module ActiveRecord
673
663
  return if minimum_idle.nil?
674
664
 
675
665
  idle_connections = synchronize do
676
- return unless @connections
666
+ return if self.discarded?
677
667
  @connections.select do |conn|
678
668
  !conn.in_use? && conn.seconds_idle >= minimum_idle
679
669
  end.each do |conn|
@@ -884,7 +874,7 @@ module ActiveRecord
884
874
  alias_method :release, :remove_connection_from_thread_cache
885
875
 
886
876
  def new_connection
887
- Base.send(spec.adapter_method, spec.config).tap do |conn|
877
+ Base.public_send(db_config.adapter_method, db_config.configuration_hash).tap do |conn|
888
878
  conn.check_version
889
879
  end
890
880
  end
@@ -988,38 +978,18 @@ module ActiveRecord
988
978
  # should use.
989
979
  #
990
980
  # The ConnectionHandler class is not coupled with the Active models, as it has no knowledge
991
- # about the model. The model needs to pass a specification name to the handler,
981
+ # about the model. The model needs to pass a connection specification name to the handler,
992
982
  # in order to look up the correct connection pool.
993
983
  class ConnectionHandler
994
- def self.create_owner_to_pool # :nodoc:
995
- Concurrent::Map.new(initial_capacity: 2) do |h, k|
996
- # Discard the parent's connection pools immediately; we have no need
997
- # of them
998
- discard_unowned_pools(h)
999
-
1000
- h[k] = Concurrent::Map.new(initial_capacity: 2)
1001
- end
1002
- end
1003
-
1004
- def self.unowned_pool_finalizer(pid_map) # :nodoc:
1005
- lambda do |_|
1006
- discard_unowned_pools(pid_map)
1007
- end
1008
- end
1009
-
1010
- def self.discard_unowned_pools(pid_map) # :nodoc:
1011
- pid_map.each do |pid, pools|
1012
- pools.values.compact.each(&:discard!) unless pid == Process.pid
1013
- end
1014
- end
984
+ FINALIZER = lambda { |_| ActiveSupport::ForkTracker.check! }
985
+ private_constant :FINALIZER
1015
986
 
1016
987
  def initialize
1017
- # These caches are keyed by spec.name (ConnectionSpecification#name).
1018
- @owner_to_pool = ConnectionHandler.create_owner_to_pool
988
+ # These caches are keyed by pool_config.connection_specification_name (PoolConfig#connection_specification_name).
989
+ @owner_to_pool_manager = Concurrent::Map.new(initial_capacity: 2)
1019
990
 
1020
- # Backup finalizer: if the forked child never needed a pool, the above
1021
- # early discard has not occurred
1022
- ObjectSpace.define_finalizer self, ConnectionHandler.unowned_pool_finalizer(@owner_to_pool)
991
+ # Backup finalizer: if the forked child skipped Kernel#fork the early discard has not occurred
992
+ ObjectSpace.define_finalizer self, FINALIZER
1023
993
  end
1024
994
 
1025
995
  def prevent_writes # :nodoc:
@@ -1035,85 +1005,119 @@ module ActiveRecord
1035
1005
  # In some cases you may want to prevent writes to the database
1036
1006
  # even if you are on a database that can write. `while_preventing_writes`
1037
1007
  # will prevent writes to the database for the duration of the block.
1008
+ #
1009
+ # This method does not provide the same protection as a readonly
1010
+ # user and is meant to be a safeguard against accidental writes.
1011
+ #
1012
+ # See `READ_QUERY` for the queries that are blocked by this
1013
+ # method.
1038
1014
  def while_preventing_writes(enabled = true)
1015
+ unless ActiveRecord::Base.legacy_connection_handling
1016
+ raise NotImplementedError, "`while_preventing_writes` is only available on the connection_handler with legacy_connection_handling"
1017
+ end
1018
+
1039
1019
  original, self.prevent_writes = self.prevent_writes, enabled
1040
1020
  yield
1041
1021
  ensure
1042
1022
  self.prevent_writes = original
1043
1023
  end
1044
1024
 
1045
- def connection_pool_list
1046
- owner_to_pool.values.compact
1025
+ def connection_pool_names # :nodoc:
1026
+ owner_to_pool_manager.keys
1027
+ end
1028
+
1029
+ def all_connection_pools
1030
+ owner_to_pool_manager.values.flat_map { |m| m.pool_configs.map(&:pool) }
1031
+ end
1032
+
1033
+ def connection_pool_list(role = ActiveRecord::Base.current_role)
1034
+ owner_to_pool_manager.values.flat_map { |m| m.pool_configs(role).map(&:pool) }
1047
1035
  end
1048
1036
  alias :connection_pools :connection_pool_list
1049
1037
 
1050
- def establish_connection(config)
1051
- resolver = ConnectionSpecification::Resolver.new(Base.configurations)
1052
- spec = resolver.spec(config)
1038
+ def establish_connection(config, owner_name: Base.name, role: ActiveRecord::Base.current_role, shard: Base.current_shard)
1039
+ owner_name = config.to_s if config.is_a?(Symbol)
1053
1040
 
1054
- remove_connection(spec.name)
1041
+ pool_config = resolve_pool_config(config, owner_name)
1042
+ db_config = pool_config.db_config
1043
+
1044
+ # Protects the connection named `ActiveRecord::Base` from being removed
1045
+ # if the user calls `establish_connection :primary`.
1046
+ if owner_to_pool_manager.key?(pool_config.connection_specification_name)
1047
+ remove_connection_pool(pool_config.connection_specification_name, role: role, shard: shard)
1048
+ end
1055
1049
 
1056
1050
  message_bus = ActiveSupport::Notifications.instrumenter
1057
- payload = {
1058
- connection_id: object_id
1059
- }
1060
- if spec
1061
- payload[:spec_name] = spec.name
1062
- payload[:config] = spec.config
1051
+ payload = {}
1052
+ if pool_config
1053
+ payload[:spec_name] = pool_config.connection_specification_name
1054
+ payload[:shard] = shard
1055
+ payload[:config] = db_config.configuration_hash
1063
1056
  end
1064
1057
 
1065
- message_bus.instrument("!connection.active_record", payload) do
1066
- owner_to_pool[spec.name] = ConnectionAdapters::ConnectionPool.new(spec)
1058
+ if ActiveRecord::Base.legacy_connection_handling
1059
+ owner_to_pool_manager[pool_config.connection_specification_name] ||= LegacyPoolManager.new
1060
+ else
1061
+ owner_to_pool_manager[pool_config.connection_specification_name] ||= PoolManager.new
1067
1062
  end
1063
+ pool_manager = get_pool_manager(pool_config.connection_specification_name)
1064
+ pool_manager.set_pool_config(role, shard, pool_config)
1068
1065
 
1069
- owner_to_pool[spec.name]
1066
+ message_bus.instrument("!connection.active_record", payload) do
1067
+ pool_config.pool
1068
+ end
1070
1069
  end
1071
1070
 
1072
1071
  # Returns true if there are any active connections among the connection
1073
1072
  # pools that the ConnectionHandler is managing.
1074
- def active_connections?
1075
- connection_pool_list.any?(&:active_connection?)
1073
+ def active_connections?(role = ActiveRecord::Base.current_role)
1074
+ connection_pool_list(role).any?(&:active_connection?)
1076
1075
  end
1077
1076
 
1078
1077
  # Returns any connections in use by the current thread back to the pool,
1079
1078
  # and also returns connections to the pool cached by threads that are no
1080
1079
  # longer alive.
1081
- def clear_active_connections!
1082
- connection_pool_list.each(&:release_connection)
1080
+ def clear_active_connections!(role = ActiveRecord::Base.current_role)
1081
+ connection_pool_list(role).each(&:release_connection)
1083
1082
  end
1084
1083
 
1085
1084
  # Clears the cache which maps classes.
1086
1085
  #
1087
1086
  # See ConnectionPool#clear_reloadable_connections! for details.
1088
- def clear_reloadable_connections!
1089
- connection_pool_list.each(&:clear_reloadable_connections!)
1087
+ def clear_reloadable_connections!(role = ActiveRecord::Base.current_role)
1088
+ connection_pool_list(role).each(&:clear_reloadable_connections!)
1090
1089
  end
1091
1090
 
1092
- def clear_all_connections!
1093
- connection_pool_list.each(&:disconnect!)
1091
+ def clear_all_connections!(role = ActiveRecord::Base.current_role)
1092
+ connection_pool_list(role).each(&:disconnect!)
1094
1093
  end
1095
1094
 
1096
1095
  # Disconnects all currently idle connections.
1097
1096
  #
1098
1097
  # See ConnectionPool#flush! for details.
1099
- def flush_idle_connections!
1100
- connection_pool_list.each(&:flush!)
1098
+ def flush_idle_connections!(role = ActiveRecord::Base.current_role)
1099
+ connection_pool_list(role).each(&:flush!)
1101
1100
  end
1102
1101
 
1103
1102
  # Locate the connection of the nearest super class. This can be an
1104
1103
  # active or defined connection: if it is the latter, it will be
1105
1104
  # opened and set as the active connection for the class it was defined
1106
1105
  # for (not necessarily the current class).
1107
- def retrieve_connection(spec_name) #:nodoc:
1108
- pool = retrieve_connection_pool(spec_name)
1106
+ def retrieve_connection(spec_name, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard) # :nodoc:
1107
+ pool = retrieve_connection_pool(spec_name, role: role, shard: shard)
1109
1108
 
1110
1109
  unless pool
1111
- # multiple database application
1112
- if ActiveRecord::Base.connection_handler != ActiveRecord::Base.default_connection_handler
1113
- raise ConnectionNotEstablished, "No connection pool with '#{spec_name}' found for the '#{ActiveRecord::Base.current_role}' role."
1110
+ if shard != ActiveRecord::Base.default_shard
1111
+ message = "No connection pool for '#{spec_name}' found for the '#{shard}' shard."
1112
+ elsif ActiveRecord::Base.connection_handler != ActiveRecord::Base.default_connection_handler
1113
+ message = "No connection pool for '#{spec_name}' found for the '#{ActiveRecord::Base.current_role}' role."
1114
+ elsif role != ActiveRecord::Base.default_role
1115
+ message = "No connection pool for '#{spec_name}' found for the '#{role}' role."
1114
1116
  else
1115
- raise ConnectionNotEstablished, "No connection pool with '#{spec_name}' found."
1117
+ message = "No connection pool for '#{spec_name}' found."
1116
1118
  end
1119
+
1120
+ raise ConnectionNotEstablished, message
1117
1121
  end
1118
1122
 
1119
1123
  pool.connection
@@ -1121,8 +1125,8 @@ module ActiveRecord
1121
1125
 
1122
1126
  # Returns true if a connection that's accessible to this class has
1123
1127
  # already been opened.
1124
- def connected?(spec_name)
1125
- pool = retrieve_connection_pool(spec_name)
1128
+ def connected?(spec_name, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard)
1129
+ pool = retrieve_connection_pool(spec_name, role: role, shard: shard)
1126
1130
  pool && pool.connected?
1127
1131
  end
1128
1132
 
@@ -1130,42 +1134,90 @@ module ActiveRecord
1130
1134
  # connection and the defined connection (if they exist). The result
1131
1135
  # can be used as an argument for #establish_connection, for easily
1132
1136
  # re-establishing the connection.
1133
- def remove_connection(spec_name)
1134
- if pool = owner_to_pool.delete(spec_name)
1135
- pool.automatic_reconnect = false
1136
- pool.disconnect!
1137
- pool.spec.config
1137
+ def remove_connection(owner, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard)
1138
+ remove_connection_pool(owner, role: role, shard: shard)&.configuration_hash
1139
+ end
1140
+ deprecate remove_connection: "Use #remove_connection_pool, which now returns a DatabaseConfig object instead of a Hash"
1141
+
1142
+ def remove_connection_pool(owner, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard)
1143
+ if pool_manager = get_pool_manager(owner)
1144
+ pool_config = pool_manager.remove_pool_config(role, shard)
1145
+
1146
+ if pool_config
1147
+ pool_config.disconnect!
1148
+ pool_config.db_config
1149
+ end
1138
1150
  end
1139
1151
  end
1140
1152
 
1141
- # Retrieving the connection pool happens a lot, so we cache it in @owner_to_pool.
1153
+ # Retrieving the connection pool happens a lot, so we cache it in @owner_to_pool_manager.
1142
1154
  # This makes retrieving the connection pool O(1) once the process is warm.
1143
1155
  # When a connection is established or removed, we invalidate the cache.
1144
- def retrieve_connection_pool(spec_name)
1145
- owner_to_pool.fetch(spec_name) do
1146
- # Check if a connection was previously established in an ancestor process,
1147
- # which may have been forked.
1148
- if ancestor_pool = pool_from_any_process_for(spec_name)
1149
- # A connection was established in an ancestor process that must have
1150
- # subsequently forked. We can't reuse the connection, but we can copy
1151
- # the specification and establish a new connection with it.
1152
- establish_connection(ancestor_pool.spec.to_hash).tap do |pool|
1153
- pool.schema_cache = ancestor_pool.schema_cache if ancestor_pool.schema_cache
1154
- end
1155
- else
1156
- owner_to_pool[spec_name] = nil
1157
- end
1158
- end
1156
+ def retrieve_connection_pool(owner, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard)
1157
+ pool_config = get_pool_manager(owner)&.get_pool_config(role, shard)
1158
+ pool_config&.pool
1159
1159
  end
1160
1160
 
1161
1161
  private
1162
- def owner_to_pool
1163
- @owner_to_pool[Process.pid]
1162
+ attr_reader :owner_to_pool_manager
1163
+
1164
+ # Returns the pool manager for an owner.
1165
+ #
1166
+ # Using `"primary"` to look up the pool manager for `ActiveRecord::Base` is
1167
+ # deprecated in favor of looking it up by `"ActiveRecord::Base"`.
1168
+ #
1169
+ # During the deprecation period, if `"primary"` is passed, the pool manager
1170
+ # for `ActiveRecord::Base` will still be returned.
1171
+ def get_pool_manager(owner)
1172
+ return owner_to_pool_manager[owner] if owner_to_pool_manager.key?(owner)
1173
+
1174
+ if owner == "primary"
1175
+ 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`.")
1176
+ owner_to_pool_manager[Base.name]
1177
+ end
1164
1178
  end
1165
1179
 
1166
- def pool_from_any_process_for(spec_name)
1167
- owner_to_pool = @owner_to_pool.values.reverse.find { |v| v[spec_name] }
1168
- owner_to_pool && owner_to_pool[spec_name]
1180
+ # Returns an instance of PoolConfig for a given adapter.
1181
+ # Accepts a hash one layer deep that contains all connection information.
1182
+ #
1183
+ # == Example
1184
+ #
1185
+ # config = { "production" => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3" } }
1186
+ # pool_config = Base.configurations.resolve_pool_config(:production)
1187
+ # pool_config.db_config.configuration_hash
1188
+ # # => { host: "localhost", database: "foo", adapter: "sqlite3" }
1189
+ #
1190
+ def resolve_pool_config(config, owner_name)
1191
+ db_config = Base.configurations.resolve(config)
1192
+
1193
+ raise(AdapterNotSpecified, "database configuration does not specify adapter") unless db_config.adapter
1194
+
1195
+ # Require the adapter itself and give useful feedback about
1196
+ # 1. Missing adapter gems and
1197
+ # 2. Adapter gems' missing dependencies.
1198
+ path_to_adapter = "active_record/connection_adapters/#{db_config.adapter}_adapter"
1199
+ begin
1200
+ require path_to_adapter
1201
+ rescue LoadError => e
1202
+ # We couldn't require the adapter itself. Raise an exception that
1203
+ # points out config typos and missing gems.
1204
+ if e.path == path_to_adapter
1205
+ # We can assume that a non-builtin adapter was specified, so it's
1206
+ # either misspelled or missing from Gemfile.
1207
+ 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
1208
+
1209
+ # Bubbled up from the adapter require. Prefix the exception message
1210
+ # with some guidance about how to address it and reraise.
1211
+ else
1212
+ raise LoadError, "Error loading the '#{db_config.adapter}' Active Record adapter. Missing a gem it depends on? #{e.message}", e.backtrace
1213
+ end
1214
+ end
1215
+
1216
+ unless ActiveRecord::Base.respond_to?(db_config.adapter_method)
1217
+ raise AdapterNotFound, "database configuration specifies nonexistent #{db_config.adapter} adapter"
1218
+ end
1219
+
1220
+ ConnectionAdapters::PoolConfig.new(owner_name, db_config)
1169
1221
  end
1170
1222
  end
1171
1223
  end