activerecord 6.0.0 → 6.1.3

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 (270) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1045 -575
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  5. data/lib/active_record.rb +7 -13
  6. data/lib/active_record/aggregations.rb +5 -6
  7. data/lib/active_record/association_relation.rb +30 -10
  8. data/lib/active_record/associations.rb +120 -13
  9. data/lib/active_record/associations/alias_tracker.rb +19 -16
  10. data/lib/active_record/associations/association.rb +49 -29
  11. data/lib/active_record/associations/association_scope.rb +19 -15
  12. data/lib/active_record/associations/belongs_to_association.rb +22 -8
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -3
  14. data/lib/active_record/associations/builder/association.rb +32 -5
  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 -3
  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 +25 -8
  22. data/lib/active_record/associations/collection_proxy.rb +14 -7
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +24 -3
  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 +77 -42
  28. data/lib/active_record/associations/join_dependency/join_association.rb +39 -16
  29. data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
  30. data/lib/active_record/associations/preloader.rb +13 -8
  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 -9
  36. data/lib/active_record/attribute_methods.rb +64 -54
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -10
  38. data/lib/active_record/attribute_methods/dirty.rb +3 -13
  39. data/lib/active_record/attribute_methods/primary_key.rb +6 -4
  40. data/lib/active_record/attribute_methods/query.rb +3 -6
  41. data/lib/active_record/attribute_methods/read.rb +8 -12
  42. data/lib/active_record/attribute_methods/serialization.rb +11 -6
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -15
  44. data/lib/active_record/attribute_methods/write.rb +12 -21
  45. data/lib/active_record/attributes.rb +33 -9
  46. data/lib/active_record/autosave_association.rb +63 -44
  47. data/lib/active_record/base.rb +2 -14
  48. data/lib/active_record/callbacks.rb +153 -24
  49. data/lib/active_record/coders/yaml_column.rb +1 -2
  50. data/lib/active_record/connection_adapters.rb +50 -0
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +202 -138
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +87 -38
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +5 -10
  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 +152 -116
  58. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +141 -52
  59. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  60. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +267 -105
  61. data/lib/active_record/connection_adapters/abstract/transaction.rb +82 -35
  62. data/lib/active_record/connection_adapters/abstract_adapter.rb +74 -77
  63. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +149 -115
  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/column.rb +1 -1
  68. data/lib/active_record/connection_adapters/mysql/database_statements.rb +30 -36
  69. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
  70. data/lib/active_record/connection_adapters/mysql/quoting.rb +18 -3
  71. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -7
  72. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  73. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +5 -2
  74. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +17 -13
  75. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  76. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -13
  77. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  78. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  79. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  80. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +21 -56
  81. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +0 -1
  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 +0 -1
  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 +2 -3
  90. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  92. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +24 -6
  95. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  96. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -2
  97. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  98. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -2
  99. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +7 -3
  100. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +1 -1
  101. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  102. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +72 -54
  103. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  104. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  105. data/lib/active_record/connection_adapters/postgresql_adapter.rb +83 -65
  106. data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
  107. data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
  108. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +38 -12
  109. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -2
  110. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  111. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -5
  112. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -57
  113. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  114. data/lib/active_record/connection_handling.rb +219 -81
  115. data/lib/active_record/core.rb +253 -67
  116. data/lib/active_record/counter_cache.rb +4 -1
  117. data/lib/active_record/database_configurations.rb +124 -85
  118. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  119. data/lib/active_record/database_configurations/database_config.rb +52 -9
  120. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  121. data/lib/active_record/database_configurations/url_config.rb +15 -41
  122. data/lib/active_record/delegated_type.rb +209 -0
  123. data/lib/active_record/destroy_association_async_job.rb +36 -0
  124. data/lib/active_record/dynamic_matchers.rb +2 -3
  125. data/lib/active_record/enum.rb +82 -38
  126. data/lib/active_record/errors.rb +47 -12
  127. data/lib/active_record/explain.rb +9 -5
  128. data/lib/active_record/explain_subscriber.rb +1 -1
  129. data/lib/active_record/fixture_set/file.rb +10 -17
  130. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  131. data/lib/active_record/fixture_set/render_context.rb +1 -1
  132. data/lib/active_record/fixture_set/table_row.rb +2 -3
  133. data/lib/active_record/fixture_set/table_rows.rb +0 -1
  134. data/lib/active_record/fixtures.rb +58 -12
  135. data/lib/active_record/gem_version.rb +2 -2
  136. data/lib/active_record/inheritance.rb +40 -21
  137. data/lib/active_record/insert_all.rb +39 -10
  138. data/lib/active_record/integration.rb +3 -5
  139. data/lib/active_record/internal_metadata.rb +16 -7
  140. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  141. data/lib/active_record/locking/optimistic.rb +33 -18
  142. data/lib/active_record/locking/pessimistic.rb +6 -2
  143. data/lib/active_record/log_subscriber.rb +28 -9
  144. data/lib/active_record/middleware/database_selector.rb +4 -2
  145. data/lib/active_record/middleware/database_selector/resolver.rb +14 -14
  146. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  147. data/lib/active_record/migration.rb +115 -85
  148. data/lib/active_record/migration/command_recorder.rb +53 -45
  149. data/lib/active_record/migration/compatibility.rb +71 -20
  150. data/lib/active_record/migration/join_table.rb +0 -1
  151. data/lib/active_record/model_schema.rb +120 -15
  152. data/lib/active_record/nested_attributes.rb +2 -5
  153. data/lib/active_record/no_touching.rb +1 -1
  154. data/lib/active_record/null_relation.rb +0 -1
  155. data/lib/active_record/persistence.rb +50 -46
  156. data/lib/active_record/query_cache.rb +15 -5
  157. data/lib/active_record/querying.rb +12 -7
  158. data/lib/active_record/railtie.rb +65 -45
  159. data/lib/active_record/railties/console_sandbox.rb +2 -4
  160. data/lib/active_record/railties/databases.rake +277 -97
  161. data/lib/active_record/readonly_attributes.rb +4 -0
  162. data/lib/active_record/reflection.rb +77 -63
  163. data/lib/active_record/relation.rb +107 -67
  164. data/lib/active_record/relation/batches.rb +38 -32
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  166. data/lib/active_record/relation/calculations.rb +102 -45
  167. data/lib/active_record/relation/delegation.rb +9 -7
  168. data/lib/active_record/relation/finder_methods.rb +55 -17
  169. data/lib/active_record/relation/from_clause.rb +5 -1
  170. data/lib/active_record/relation/merger.rb +27 -26
  171. data/lib/active_record/relation/predicate_builder.rb +59 -40
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  173. data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
  174. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -6
  175. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  176. data/lib/active_record/relation/query_methods.rb +343 -180
  177. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  178. data/lib/active_record/relation/spawn_methods.rb +8 -8
  179. data/lib/active_record/relation/where_clause.rb +107 -61
  180. data/lib/active_record/result.rb +41 -34
  181. data/lib/active_record/runtime_registry.rb +2 -2
  182. data/lib/active_record/sanitization.rb +6 -17
  183. data/lib/active_record/schema_dumper.rb +34 -4
  184. data/lib/active_record/schema_migration.rb +2 -8
  185. data/lib/active_record/scoping.rb +0 -1
  186. data/lib/active_record/scoping/default.rb +0 -1
  187. data/lib/active_record/scoping/named.rb +7 -18
  188. data/lib/active_record/secure_token.rb +16 -8
  189. data/lib/active_record/serialization.rb +5 -3
  190. data/lib/active_record/signed_id.rb +116 -0
  191. data/lib/active_record/statement_cache.rb +20 -4
  192. data/lib/active_record/store.rb +3 -3
  193. data/lib/active_record/suppressor.rb +2 -2
  194. data/lib/active_record/table_metadata.rb +42 -36
  195. data/lib/active_record/tasks/database_tasks.rb +140 -113
  196. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -36
  197. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -27
  198. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -10
  199. data/lib/active_record/test_databases.rb +5 -4
  200. data/lib/active_record/test_fixtures.rb +38 -16
  201. data/lib/active_record/timestamp.rb +4 -7
  202. data/lib/active_record/touch_later.rb +20 -21
  203. data/lib/active_record/transactions.rb +26 -73
  204. data/lib/active_record/type.rb +8 -2
  205. data/lib/active_record/type/adapter_specific_registry.rb +2 -5
  206. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  207. data/lib/active_record/type/serialized.rb +6 -3
  208. data/lib/active_record/type/time.rb +10 -0
  209. data/lib/active_record/type/type_map.rb +0 -1
  210. data/lib/active_record/type/unsigned_integer.rb +0 -1
  211. data/lib/active_record/type_caster/connection.rb +0 -1
  212. data/lib/active_record/type_caster/map.rb +8 -5
  213. data/lib/active_record/validations.rb +3 -3
  214. data/lib/active_record/validations/associated.rb +1 -2
  215. data/lib/active_record/validations/numericality.rb +35 -0
  216. data/lib/active_record/validations/uniqueness.rb +24 -4
  217. data/lib/arel.rb +15 -12
  218. data/lib/arel/attributes/attribute.rb +4 -0
  219. data/lib/arel/collectors/bind.rb +5 -0
  220. data/lib/arel/collectors/composite.rb +8 -0
  221. data/lib/arel/collectors/sql_string.rb +7 -0
  222. data/lib/arel/collectors/substitute_binds.rb +7 -0
  223. data/lib/arel/nodes.rb +3 -1
  224. data/lib/arel/nodes/binary.rb +82 -8
  225. data/lib/arel/nodes/bind_param.rb +8 -0
  226. data/lib/arel/nodes/casted.rb +21 -9
  227. data/lib/arel/nodes/equality.rb +6 -9
  228. data/lib/arel/nodes/grouping.rb +3 -0
  229. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  230. data/lib/arel/nodes/in.rb +8 -1
  231. data/lib/arel/nodes/infix_operation.rb +13 -1
  232. data/lib/arel/nodes/join_source.rb +1 -1
  233. data/lib/arel/nodes/node.rb +7 -6
  234. data/lib/arel/nodes/ordering.rb +27 -0
  235. data/lib/arel/nodes/sql_literal.rb +3 -0
  236. data/lib/arel/nodes/table_alias.rb +7 -3
  237. data/lib/arel/nodes/unary.rb +0 -1
  238. data/lib/arel/predications.rb +17 -24
  239. data/lib/arel/select_manager.rb +1 -2
  240. data/lib/arel/table.rb +13 -5
  241. data/lib/arel/visitors.rb +0 -7
  242. data/lib/arel/visitors/dot.rb +14 -3
  243. data/lib/arel/visitors/mysql.rb +11 -1
  244. data/lib/arel/visitors/postgresql.rb +15 -5
  245. data/lib/arel/visitors/sqlite.rb +0 -1
  246. data/lib/arel/visitors/to_sql.rb +89 -79
  247. data/lib/arel/visitors/visitor.rb +0 -1
  248. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  249. data/lib/rails/generators/active_record/migration.rb +6 -2
  250. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  251. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  252. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
  253. data/lib/rails/generators/active_record/model/model_generator.rb +38 -2
  254. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  255. metadata +30 -27
  256. data/lib/active_record/attribute_decorators.rb +0 -90
  257. data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
  258. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  259. data/lib/active_record/define_callbacks.rb +0 -22
  260. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  261. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  262. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  263. data/lib/arel/attributes.rb +0 -22
  264. data/lib/arel/visitors/depth_first.rb +0 -204
  265. data/lib/arel/visitors/ibm_db.rb +0 -34
  266. data/lib/arel/visitors/informix.rb +0 -62
  267. data/lib/arel/visitors/mssql.rb +0 -157
  268. data/lib/arel/visitors/oracle.rb +0 -159
  269. data/lib/arel/visitors/oracle12.rb +0 -66
  270. data/lib/arel/visitors/where_sql.rb +0 -23
@@ -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,137 @@ module ActiveRecord
302
301
  :before_destroy, :around_destroy, :after_destroy, :after_commit, :after_rollback
303
302
  ]
304
303
 
304
+ module ClassMethods
305
+ include ActiveModel::Callbacks
306
+
307
+ ##
308
+ # :method: after_initialize
309
+ #
310
+ # :call-seq: after_initialize(*args, &block)
311
+ #
312
+ # Registers a callback to be called after a record is instantiated. See
313
+ # ActiveRecord::Callbacks for more information.
314
+
315
+ ##
316
+ # :method: after_find
317
+ #
318
+ # :call-seq: after_find(*args, &block)
319
+ #
320
+ # Registers a callback to be called after a record is instantiated
321
+ # via a finder. See ActiveRecord::Callbacks for more information.
322
+
323
+ ##
324
+ # :method: after_touch
325
+ #
326
+ # :call-seq: after_touch(*args, &block)
327
+ #
328
+ # Registers a callback to be called after a record is touched. See
329
+ # ActiveRecord::Callbacks for more information.
330
+
331
+ ##
332
+ # :method: before_save
333
+ #
334
+ # :call-seq: before_save(*args, &block)
335
+ #
336
+ # Registers a callback to be called before a record is saved. See
337
+ # ActiveRecord::Callbacks for more information.
338
+
339
+ ##
340
+ # :method: around_save
341
+ #
342
+ # :call-seq: around_save(*args, &block)
343
+ #
344
+ # Registers a callback to be called around the save of a record. See
345
+ # ActiveRecord::Callbacks for more information.
346
+
347
+ ##
348
+ # :method: after_save
349
+ #
350
+ # :call-seq: after_save(*args, &block)
351
+ #
352
+ # Registers a callback to be called after a record is saved. See
353
+ # ActiveRecord::Callbacks for more information.
354
+
355
+ ##
356
+ # :method: before_create
357
+ #
358
+ # :call-seq: before_create(*args, &block)
359
+ #
360
+ # Registers a callback to be called before a record is created. See
361
+ # ActiveRecord::Callbacks for more information.
362
+
363
+ ##
364
+ # :method: around_create
365
+ #
366
+ # :call-seq: around_create(*args, &block)
367
+ #
368
+ # Registers a callback to be called around the creation of a record. See
369
+ # ActiveRecord::Callbacks for more information.
370
+
371
+ ##
372
+ # :method: after_create
373
+ #
374
+ # :call-seq: after_create(*args, &block)
375
+ #
376
+ # Registers a callback to be called after a record is created. See
377
+ # ActiveRecord::Callbacks for more information.
378
+
379
+ ##
380
+ # :method: before_update
381
+ #
382
+ # :call-seq: before_update(*args, &block)
383
+ #
384
+ # Registers a callback to be called before a record is updated. See
385
+ # ActiveRecord::Callbacks for more information.
386
+
387
+ ##
388
+ # :method: around_update
389
+ #
390
+ # :call-seq: around_update(*args, &block)
391
+ #
392
+ # Registers a callback to be called around the update of a record. See
393
+ # ActiveRecord::Callbacks for more information.
394
+
395
+ ##
396
+ # :method: after_update
397
+ #
398
+ # :call-seq: after_update(*args, &block)
399
+ #
400
+ # Registers a callback to be called after a record is updated. See
401
+ # ActiveRecord::Callbacks for more information.
402
+
403
+ ##
404
+ # :method: before_destroy
405
+ #
406
+ # :call-seq: before_destroy(*args, &block)
407
+ #
408
+ # Registers a callback to be called before a record is destroyed. See
409
+ # ActiveRecord::Callbacks for more information.
410
+
411
+ ##
412
+ # :method: around_destroy
413
+ #
414
+ # :call-seq: around_destroy(*args, &block)
415
+ #
416
+ # Registers a callback to be called around the destruction of a record.
417
+ # See ActiveRecord::Callbacks for more information.
418
+
419
+ ##
420
+ # :method: after_destroy
421
+ #
422
+ # :call-seq: after_destroy(*args, &block)
423
+ #
424
+ # Registers a callback to be called after a record is destroyed. See
425
+ # ActiveRecord::Callbacks for more information.
426
+ end
427
+
428
+ included do
429
+ include ActiveModel::Validations::Callbacks
430
+
431
+ define_model_callbacks :initialize, :find, :touch, only: :after
432
+ define_model_callbacks :save, :create, :update, :destroy
433
+ end
434
+
305
435
  def destroy #:nodoc:
306
436
  @_destroy_callback_already_called ||= false
307
437
  return if @_destroy_callback_already_called
@@ -314,7 +444,7 @@ module ActiveRecord
314
444
  @_destroy_callback_already_called = false
315
445
  end
316
446
 
317
- def touch(*) #:nodoc:
447
+ def touch(*, **) #:nodoc:
318
448
  _run_touch_callbacks { super }
319
449
  end
320
450
 
@@ -323,7 +453,6 @@ module ActiveRecord
323
453
  end
324
454
 
325
455
  private
326
-
327
456
  def create_or_update(**)
328
457
  _run_save_callbacks { super }
329
458
  end
@@ -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")
@@ -39,7 +39,6 @@ module ActiveRecord
39
39
  end
40
40
 
41
41
  private
42
-
43
42
  def check_arity_of_constructor
44
43
  load(nil)
45
44
  rescue ArgumentError
@@ -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,37 +6,26 @@ 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
25
+ attr_accessor :schema_cache
26
+
27
+ def connection_klass
28
+ nil
40
29
  end
41
30
  end
42
31
 
@@ -150,7 +139,7 @@ module ActiveRecord
150
139
 
151
140
  # Remove the head of the queue.
152
141
  #
153
- # If +timeout+ is not given, remove and return the head the
142
+ # If +timeout+ is not given, remove and return the head of the
154
143
  # queue if the number of available elements is strictly
155
144
  # greater than the number of threads currently waiting (that
156
145
  # is, don't jump ahead in line). Otherwise, return +nil+.
@@ -167,7 +156,6 @@ module ActiveRecord
167
156
  end
168
157
 
169
158
  private
170
-
171
159
  def internal_poll(timeout)
172
160
  no_wait_poll || (timeout && wait_poll(timeout))
173
161
  end
@@ -194,7 +182,7 @@ module ActiveRecord
194
182
  @queue.pop
195
183
  end
196
184
 
197
- # Remove and return the head the queue if the number of
185
+ # Remove and return the head of the queue if the number of
198
186
  # available elements is strictly greater than the number of
199
187
  # threads currently waiting. Otherwise, return +nil+.
200
188
  def no_wait_poll
@@ -317,31 +305,44 @@ module ActiveRecord
317
305
 
318
306
  @mutex = Mutex.new
319
307
  @pools = {}
308
+ @threads = {}
320
309
 
321
310
  class << self
322
311
  def register_pool(pool, frequency) # :nodoc:
323
312
  @mutex.synchronize do
324
- unless @pools.key?(frequency)
325
- @pools[frequency] = []
326
- spawn_thread(frequency)
313
+ unless @threads[frequency]&.alive?
314
+ @threads[frequency] = spawn_thread(frequency)
327
315
  end
316
+ @pools[frequency] ||= []
328
317
  @pools[frequency] << WeakRef.new(pool)
329
318
  end
330
319
  end
331
320
 
332
321
  private
333
-
334
322
  def spawn_thread(frequency)
335
323
  Thread.new(frequency) do |t|
336
- loop do
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
337
329
  sleep t
338
330
  @mutex.synchronize do
339
- @pools[frequency].select!(&:weakref_alive?)
331
+ @pools[frequency].select! do |pool|
332
+ pool.weakref_alive? && !pool.discarded?
333
+ end
334
+
340
335
  @pools[frequency].each do |p|
341
336
  p.reap
342
337
  p.flush
343
338
  rescue WeakRef::RefError
344
339
  end
340
+
341
+ if @pools[frequency].empty?
342
+ @pools.delete(frequency)
343
+ @threads.delete(frequency)
344
+ running = false
345
+ end
345
346
  end
346
347
  end
347
348
  end
@@ -358,35 +359,34 @@ module ActiveRecord
358
359
  include QueryCache::ConnectionPoolConfiguration
359
360
  include ConnectionAdapters::AbstractPool
360
361
 
361
- attr_accessor :automatic_reconnect, :checkout_timeout, :schema_cache
362
- attr_reader :spec, :size, :reaper
362
+ attr_accessor :automatic_reconnect, :checkout_timeout
363
+ attr_reader :db_config, :size, :reaper, :pool_config, :connection_klass
363
364
 
364
- # Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification
365
+ delegate :schema_cache, :schema_cache=, to: :pool_config
366
+
367
+ # Creates a new ConnectionPool object. +pool_config+ is a PoolConfig
365
368
  # object which describes database connection information (e.g. adapter,
366
369
  # host name, username, password, etc), as well as the maximum size for
367
370
  # this ConnectionPool.
368
371
  #
369
372
  # The default ConnectionPool maximum size is 5.
370
- def initialize(spec)
373
+ def initialize(pool_config)
371
374
  super()
372
375
 
373
- @spec = spec
374
-
375
- @checkout_timeout = (spec.config[:checkout_timeout] && spec.config[:checkout_timeout].to_f) || 5
376
- if @idle_timeout = spec.config.fetch(:idle_timeout, 300)
377
- @idle_timeout = @idle_timeout.to_f
378
- @idle_timeout = nil if @idle_timeout <= 0
379
- end
376
+ @pool_config = pool_config
377
+ @db_config = pool_config.db_config
378
+ @connection_klass = pool_config.connection_klass
380
379
 
381
- # default max pool size to 5
382
- @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
383
383
 
384
384
  # This variable tracks the cache of threads mapped to reserved connections, with the
385
385
  # sole purpose of speeding up the +connection+ method. It is not the authoritative
386
386
  # registry of which thread owns which connection. Connection ownership is tracked by
387
387
  # the +connection.owner+ attr on each +connection+ instance.
388
388
  # The invariant works like this: if there is mapping of <tt>thread => conn</tt>,
389
- # 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
390
390
  # mapping does not mean that the +thread+ doesn't own the said connection. In
391
391
  # that case +conn.owner+ attr should be consulted.
392
392
  # Access and modification of <tt>@thread_cached_conns</tt> does not require
@@ -407,10 +407,7 @@ module ActiveRecord
407
407
 
408
408
  @lock_thread = false
409
409
 
410
- # +reaping_frequency+ is configurable mostly for historical reasons, but it could
411
- # also be useful if someone wants a very low +idle_timeout+.
412
- reaping_frequency = spec.config.fetch(:reaping_frequency, 60)
413
- @reaper = Reaper.new(self, reaping_frequency && reaping_frequency.to_f)
410
+ @reaper = Reaper.new(self, db_config.reaping_frequency)
414
411
  @reaper.run
415
412
  end
416
413
 
@@ -492,7 +489,7 @@ module ActiveRecord
492
489
  # Raises:
493
490
  # - ActiveRecord::ExclusiveConnectionTimeoutError if unable to gain ownership of all
494
491
  # connections in the pool within a timeout interval (default duration is
495
- # <tt>spec.config[:checkout_timeout] * 2</tt> seconds).
492
+ # <tt>spec.db_config.checkout_timeout * 2</tt> seconds).
496
493
  def disconnect(raise_on_acquisition_timeout = true)
497
494
  with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
498
495
  synchronize do
@@ -513,7 +510,7 @@ module ActiveRecord
513
510
  #
514
511
  # The pool first tries to gain ownership of all connections. If unable to
515
512
  # do so within a timeout interval (default duration is
516
- # <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
517
514
  # disconnected without any regard for other connection owning threads.
518
515
  def disconnect!
519
516
  disconnect(false)
@@ -526,7 +523,7 @@ module ActiveRecord
526
523
  # See AbstractAdapter#discard!
527
524
  def discard! # :nodoc:
528
525
  synchronize do
529
- return if @connections.nil? # already discarded
526
+ return if self.discarded?
530
527
  @connections.each do |conn|
531
528
  conn.discard!
532
529
  end
@@ -534,13 +531,17 @@ module ActiveRecord
534
531
  end
535
532
  end
536
533
 
534
+ def discarded? # :nodoc:
535
+ @connections.nil?
536
+ end
537
+
537
538
  # Clears the cache which maps classes and re-connects connections that
538
539
  # require reloading.
539
540
  #
540
541
  # Raises:
541
542
  # - ActiveRecord::ExclusiveConnectionTimeoutError if unable to gain ownership of all
542
543
  # connections in the pool within a timeout interval (default duration is
543
- # <tt>spec.config[:checkout_timeout] * 2</tt> seconds).
544
+ # <tt>spec.db_config.checkout_timeout * 2</tt> seconds).
544
545
  def clear_reloadable_connections(raise_on_acquisition_timeout = true)
545
546
  with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
546
547
  synchronize do
@@ -562,7 +563,7 @@ module ActiveRecord
562
563
  #
563
564
  # The pool first tries to gain ownership of all connections. If unable to
564
565
  # do so within a timeout interval (default duration is
565
- # <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
566
567
  # clears the cache and reloads connections without any regard for other
567
568
  # connection owning threads.
568
569
  def clear_reloadable_connections!
@@ -642,6 +643,7 @@ module ActiveRecord
642
643
  # or a thread dies unexpectedly.
643
644
  def reap
644
645
  stale_connections = synchronize do
646
+ return if self.discarded?
645
647
  @connections.select do |conn|
646
648
  conn.in_use? && !conn.owner.alive?
647
649
  end.each do |conn|
@@ -666,6 +668,7 @@ module ActiveRecord
666
668
  return if minimum_idle.nil?
667
669
 
668
670
  idle_connections = synchronize do
671
+ return if self.discarded?
669
672
  @connections.select do |conn|
670
673
  !conn.in_use? && conn.seconds_idle >= minimum_idle
671
674
  end.each do |conn|
@@ -876,7 +879,7 @@ module ActiveRecord
876
879
  alias_method :release, :remove_connection_from_thread_cache
877
880
 
878
881
  def new_connection
879
- Base.send(spec.adapter_method, spec.config).tap do |conn|
882
+ Base.public_send(db_config.adapter_method, db_config.configuration_hash).tap do |conn|
880
883
  conn.check_version
881
884
  end
882
885
  end
@@ -980,38 +983,18 @@ module ActiveRecord
980
983
  # should use.
981
984
  #
982
985
  # The ConnectionHandler class is not coupled with the Active models, as it has no knowledge
983
- # 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,
984
987
  # in order to look up the correct connection pool.
985
988
  class ConnectionHandler
986
- def self.create_owner_to_pool # :nodoc:
987
- Concurrent::Map.new(initial_capacity: 2) do |h, k|
988
- # Discard the parent's connection pools immediately; we have no need
989
- # of them
990
- discard_unowned_pools(h)
991
-
992
- h[k] = Concurrent::Map.new(initial_capacity: 2)
993
- end
994
- end
995
-
996
- def self.unowned_pool_finalizer(pid_map) # :nodoc:
997
- lambda do |_|
998
- discard_unowned_pools(pid_map)
999
- end
1000
- end
1001
-
1002
- def self.discard_unowned_pools(pid_map) # :nodoc:
1003
- pid_map.each do |pid, pools|
1004
- pools.values.compact.each(&:discard!) unless pid == Process.pid
1005
- end
1006
- end
989
+ FINALIZER = lambda { |_| ActiveSupport::ForkTracker.check! }
990
+ private_constant :FINALIZER
1007
991
 
1008
992
  def initialize
1009
- # These caches are keyed by spec.name (ConnectionSpecification#name).
1010
- @owner_to_pool = ConnectionHandler.create_owner_to_pool
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)
1011
995
 
1012
- # Backup finalizer: if the forked child never needed a pool, the above
1013
- # early discard has not occurred
1014
- ObjectSpace.define_finalizer self, ConnectionHandler.unowned_pool_finalizer(@owner_to_pool)
996
+ # Backup finalizer: if the forked child skipped Kernel#fork the early discard has not occurred
997
+ ObjectSpace.define_finalizer self, FINALIZER
1015
998
  end
1016
999
 
1017
1000
  def prevent_writes # :nodoc:
@@ -1027,85 +1010,119 @@ module ActiveRecord
1027
1010
  # In some cases you may want to prevent writes to the database
1028
1011
  # even if you are on a database that can write. `while_preventing_writes`
1029
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.
1030
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"
1022
+ end
1023
+
1031
1024
  original, self.prevent_writes = self.prevent_writes, enabled
1032
1025
  yield
1033
1026
  ensure
1034
1027
  self.prevent_writes = original
1035
1028
  end
1036
1029
 
1037
- def connection_pool_list
1038
- owner_to_pool.values.compact
1030
+ def connection_pool_names # :nodoc:
1031
+ owner_to_pool_manager.keys
1032
+ end
1033
+
1034
+ def all_connection_pools
1035
+ owner_to_pool_manager.values.flat_map { |m| m.pool_configs.map(&:pool) }
1036
+ end
1037
+
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) }
1039
1040
  end
1040
1041
  alias :connection_pools :connection_pool_list
1041
1042
 
1042
- def establish_connection(config)
1043
- resolver = ConnectionSpecification::Resolver.new(Base.configurations)
1044
- 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
1045
1048
 
1046
- 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
1047
1054
 
1048
1055
  message_bus = ActiveSupport::Notifications.instrumenter
1049
- payload = {
1050
- connection_id: object_id
1051
- }
1052
- if spec
1053
- payload[:spec_name] = spec.name
1054
- 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
1055
1061
  end
1056
1062
 
1057
- message_bus.instrument("!connection.active_record", payload) do
1058
- 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
1059
1067
  end
1068
+ pool_manager = get_pool_manager(pool_config.connection_specification_name)
1069
+ pool_manager.set_pool_config(role, shard, pool_config)
1060
1070
 
1061
- owner_to_pool[spec.name]
1071
+ message_bus.instrument("!connection.active_record", payload) do
1072
+ pool_config.pool
1073
+ end
1062
1074
  end
1063
1075
 
1064
1076
  # Returns true if there are any active connections among the connection
1065
1077
  # pools that the ConnectionHandler is managing.
1066
- def active_connections?
1067
- connection_pool_list.any?(&:active_connection?)
1078
+ def active_connections?(role = ActiveRecord::Base.current_role)
1079
+ connection_pool_list(role).any?(&:active_connection?)
1068
1080
  end
1069
1081
 
1070
1082
  # Returns any connections in use by the current thread back to the pool,
1071
1083
  # and also returns connections to the pool cached by threads that are no
1072
1084
  # longer alive.
1073
- def clear_active_connections!
1074
- connection_pool_list.each(&:release_connection)
1085
+ def clear_active_connections!(role = ActiveRecord::Base.current_role)
1086
+ connection_pool_list(role).each(&:release_connection)
1075
1087
  end
1076
1088
 
1077
1089
  # Clears the cache which maps classes.
1078
1090
  #
1079
1091
  # See ConnectionPool#clear_reloadable_connections! for details.
1080
- def clear_reloadable_connections!
1081
- 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!)
1082
1094
  end
1083
1095
 
1084
- def clear_all_connections!
1085
- connection_pool_list.each(&:disconnect!)
1096
+ def clear_all_connections!(role = ActiveRecord::Base.current_role)
1097
+ connection_pool_list(role).each(&:disconnect!)
1086
1098
  end
1087
1099
 
1088
1100
  # Disconnects all currently idle connections.
1089
1101
  #
1090
1102
  # See ConnectionPool#flush! for details.
1091
- def flush_idle_connections!
1092
- connection_pool_list.each(&:flush!)
1103
+ def flush_idle_connections!(role = ActiveRecord::Base.current_role)
1104
+ connection_pool_list(role).each(&:flush!)
1093
1105
  end
1094
1106
 
1095
1107
  # Locate the connection of the nearest super class. This can be an
1096
1108
  # active or defined connection: if it is the latter, it will be
1097
1109
  # opened and set as the active connection for the class it was defined
1098
1110
  # for (not necessarily the current class).
1099
- def retrieve_connection(spec_name) #:nodoc:
1100
- pool = retrieve_connection_pool(spec_name)
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)
1101
1113
 
1102
1114
  unless pool
1103
- # multiple database application
1104
- if ActiveRecord::Base.connection_handler != ActiveRecord::Base.default_connection_handler
1105
- raise ConnectionNotEstablished, "No connection pool with '#{spec_name}' found for the '#{ActiveRecord::Base.current_role}' role."
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."
1106
1121
  else
1107
- raise ConnectionNotEstablished, "No connection pool with '#{spec_name}' found."
1122
+ message = "No connection pool for '#{spec_name}' found."
1108
1123
  end
1124
+
1125
+ raise ConnectionNotEstablished, message
1109
1126
  end
1110
1127
 
1111
1128
  pool.connection
@@ -1113,8 +1130,8 @@ module ActiveRecord
1113
1130
 
1114
1131
  # Returns true if a connection that's accessible to this class has
1115
1132
  # already been opened.
1116
- def connected?(spec_name)
1117
- pool = retrieve_connection_pool(spec_name)
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)
1118
1135
  pool && pool.connected?
1119
1136
  end
1120
1137
 
@@ -1122,43 +1139,90 @@ module ActiveRecord
1122
1139
  # connection and the defined connection (if they exist). The result
1123
1140
  # can be used as an argument for #establish_connection, for easily
1124
1141
  # re-establishing the connection.
1125
- def remove_connection(spec_name)
1126
- if pool = owner_to_pool.delete(spec_name)
1127
- pool.automatic_reconnect = false
1128
- pool.disconnect!
1129
- 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
1130
1155
  end
1131
1156
  end
1132
1157
 
1133
- # 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.
1134
1159
  # This makes retrieving the connection pool O(1) once the process is warm.
1135
1160
  # When a connection is established or removed, we invalidate the cache.
1136
- def retrieve_connection_pool(spec_name)
1137
- owner_to_pool.fetch(spec_name) do
1138
- # Check if a connection was previously established in an ancestor process,
1139
- # which may have been forked.
1140
- if ancestor_pool = pool_from_any_process_for(spec_name)
1141
- # A connection was established in an ancestor process that must have
1142
- # subsequently forked. We can't reuse the connection, but we can copy
1143
- # the specification and establish a new connection with it.
1144
- establish_connection(ancestor_pool.spec.to_hash).tap do |pool|
1145
- pool.schema_cache = ancestor_pool.schema_cache if ancestor_pool.schema_cache
1146
- end
1147
- else
1148
- owner_to_pool[spec_name] = nil
1149
- end
1150
- 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
1151
1164
  end
1152
1165
 
1153
1166
  private
1167
+ attr_reader :owner_to_pool_manager
1154
1168
 
1155
- def owner_to_pool
1156
- @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
1157
1183
  end
1158
1184
 
1159
- def pool_from_any_process_for(spec_name)
1160
- owner_to_pool = @owner_to_pool.values.reverse.find { |v| v[spec_name] }
1161
- 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)
1162
1226
  end
1163
1227
  end
1164
1228
  end