activerecord 6.0.6 → 6.1.0

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 (242) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +783 -910
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +3 -3
  5. data/lib/active_record/aggregations.rb +1 -1
  6. data/lib/active_record/association_relation.rb +22 -14
  7. data/lib/active_record/associations/alias_tracker.rb +19 -15
  8. data/lib/active_record/associations/association.rb +43 -26
  9. data/lib/active_record/associations/association_scope.rb +11 -15
  10. data/lib/active_record/associations/belongs_to_association.rb +15 -5
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  12. data/lib/active_record/associations/builder/association.rb +9 -3
  13. data/lib/active_record/associations/builder/belongs_to.rb +10 -7
  14. data/lib/active_record/associations/builder/collection_association.rb +5 -4
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -1
  16. data/lib/active_record/associations/builder/has_many.rb +6 -2
  17. data/lib/active_record/associations/builder/has_one.rb +11 -14
  18. data/lib/active_record/associations/builder/singular_association.rb +1 -1
  19. data/lib/active_record/associations/collection_association.rb +19 -13
  20. data/lib/active_record/associations/collection_proxy.rb +12 -5
  21. data/lib/active_record/associations/foreign_association.rb +13 -0
  22. data/lib/active_record/associations/has_many_association.rb +24 -2
  23. data/lib/active_record/associations/has_many_through_association.rb +10 -4
  24. data/lib/active_record/associations/has_one_association.rb +15 -1
  25. data/lib/active_record/associations/join_dependency/join_association.rb +29 -14
  26. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  27. data/lib/active_record/associations/join_dependency.rb +63 -49
  28. data/lib/active_record/associations/preloader/association.rb +13 -5
  29. data/lib/active_record/associations/preloader/through_association.rb +1 -1
  30. data/lib/active_record/associations/preloader.rb +5 -3
  31. data/lib/active_record/associations/singular_association.rb +1 -1
  32. data/lib/active_record/associations.rb +114 -11
  33. data/lib/active_record/attribute_assignment.rb +10 -8
  34. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
  35. data/lib/active_record/attribute_methods/dirty.rb +1 -11
  36. data/lib/active_record/attribute_methods/primary_key.rb +6 -2
  37. data/lib/active_record/attribute_methods/query.rb +3 -6
  38. data/lib/active_record/attribute_methods/read.rb +8 -11
  39. data/lib/active_record/attribute_methods/serialization.rb +11 -5
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -13
  41. data/lib/active_record/attribute_methods/write.rb +12 -20
  42. data/lib/active_record/attribute_methods.rb +64 -54
  43. data/lib/active_record/attributes.rb +32 -7
  44. data/lib/active_record/autosave_association.rb +47 -30
  45. data/lib/active_record/base.rb +2 -14
  46. data/lib/active_record/callbacks.rb +152 -22
  47. data/lib/active_record/coders/yaml_column.rb +2 -24
  48. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +185 -134
  49. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  50. data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -22
  51. data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -7
  52. data/lib/active_record/connection_adapters/abstract/quoting.rb +34 -34
  53. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  54. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
  55. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +110 -30
  56. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +224 -85
  58. data/lib/active_record/connection_adapters/abstract/transaction.rb +80 -32
  59. data/lib/active_record/connection_adapters/abstract_adapter.rb +49 -72
  60. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +123 -87
  61. data/lib/active_record/connection_adapters/column.rb +15 -1
  62. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  63. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +31 -0
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +22 -24
  65. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
  66. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  67. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -6
  68. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  69. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
  70. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +3 -3
  71. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  72. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -12
  73. data/lib/active_record/connection_adapters/pool_config.rb +63 -0
  74. data/lib/active_record/connection_adapters/pool_manager.rb +43 -0
  75. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  76. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +12 -53
  77. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  78. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  79. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
  80. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  84. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
  86. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  87. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  88. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  89. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
  90. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
  91. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  92. data/lib/active_record/connection_adapters/postgresql_adapter.rb +72 -55
  93. data/lib/active_record/connection_adapters/schema_cache.rb +98 -15
  94. data/lib/active_record/connection_adapters/sql_type_metadata.rb +10 -0
  95. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +30 -5
  96. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
  97. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  98. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +36 -3
  99. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +48 -50
  100. data/lib/active_record/connection_adapters.rb +50 -0
  101. data/lib/active_record/connection_handling.rb +210 -71
  102. data/lib/active_record/core.rb +223 -66
  103. data/lib/active_record/database_configurations/connection_url_resolver.rb +98 -0
  104. data/lib/active_record/database_configurations/database_config.rb +52 -9
  105. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  106. data/lib/active_record/database_configurations/url_config.rb +15 -40
  107. data/lib/active_record/database_configurations.rb +124 -85
  108. data/lib/active_record/delegated_type.rb +209 -0
  109. data/lib/active_record/destroy_association_async_job.rb +36 -0
  110. data/lib/active_record/enum.rb +27 -10
  111. data/lib/active_record/errors.rb +47 -12
  112. data/lib/active_record/explain.rb +9 -4
  113. data/lib/active_record/explain_subscriber.rb +1 -1
  114. data/lib/active_record/fixture_set/file.rb +10 -17
  115. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  116. data/lib/active_record/fixture_set/render_context.rb +1 -1
  117. data/lib/active_record/fixture_set/table_row.rb +2 -2
  118. data/lib/active_record/fixtures.rb +54 -8
  119. data/lib/active_record/gem_version.rb +2 -2
  120. data/lib/active_record/inheritance.rb +40 -18
  121. data/lib/active_record/insert_all.rb +34 -5
  122. data/lib/active_record/integration.rb +3 -5
  123. data/lib/active_record/internal_metadata.rb +16 -7
  124. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  125. data/lib/active_record/locking/optimistic.rb +13 -16
  126. data/lib/active_record/locking/pessimistic.rb +6 -2
  127. data/lib/active_record/log_subscriber.rb +26 -8
  128. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  129. data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
  130. data/lib/active_record/middleware/database_selector.rb +4 -1
  131. data/lib/active_record/migration/command_recorder.rb +47 -27
  132. data/lib/active_record/migration/compatibility.rb +67 -17
  133. data/lib/active_record/migration.rb +113 -83
  134. data/lib/active_record/model_schema.rb +88 -13
  135. data/lib/active_record/nested_attributes.rb +2 -3
  136. data/lib/active_record/no_touching.rb +1 -1
  137. data/lib/active_record/persistence.rb +50 -45
  138. data/lib/active_record/query_cache.rb +15 -5
  139. data/lib/active_record/querying.rb +11 -6
  140. data/lib/active_record/railtie.rb +64 -44
  141. data/lib/active_record/railties/databases.rake +266 -95
  142. data/lib/active_record/readonly_attributes.rb +4 -0
  143. data/lib/active_record/reflection.rb +60 -44
  144. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  145. data/lib/active_record/relation/batches.rb +38 -31
  146. data/lib/active_record/relation/calculations.rb +100 -43
  147. data/lib/active_record/relation/finder_methods.rb +44 -14
  148. data/lib/active_record/relation/from_clause.rb +1 -1
  149. data/lib/active_record/relation/merger.rb +20 -23
  150. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  151. data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
  152. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +3 -3
  153. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  154. data/lib/active_record/relation/predicate_builder.rb +57 -33
  155. data/lib/active_record/relation/query_methods.rb +318 -195
  156. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  157. data/lib/active_record/relation/spawn_methods.rb +8 -7
  158. data/lib/active_record/relation/where_clause.rb +104 -57
  159. data/lib/active_record/relation.rb +90 -64
  160. data/lib/active_record/result.rb +41 -33
  161. data/lib/active_record/runtime_registry.rb +2 -2
  162. data/lib/active_record/sanitization.rb +6 -17
  163. data/lib/active_record/schema_dumper.rb +34 -4
  164. data/lib/active_record/schema_migration.rb +2 -8
  165. data/lib/active_record/scoping/named.rb +1 -17
  166. data/lib/active_record/secure_token.rb +16 -8
  167. data/lib/active_record/serialization.rb +5 -3
  168. data/lib/active_record/signed_id.rb +116 -0
  169. data/lib/active_record/statement_cache.rb +20 -4
  170. data/lib/active_record/store.rb +2 -2
  171. data/lib/active_record/suppressor.rb +2 -2
  172. data/lib/active_record/table_metadata.rb +39 -51
  173. data/lib/active_record/tasks/database_tasks.rb +139 -113
  174. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
  175. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
  176. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
  177. data/lib/active_record/test_databases.rb +5 -4
  178. data/lib/active_record/test_fixtures.rb +36 -33
  179. data/lib/active_record/timestamp.rb +4 -6
  180. data/lib/active_record/touch_later.rb +21 -21
  181. data/lib/active_record/transactions.rb +15 -64
  182. data/lib/active_record/type/serialized.rb +6 -2
  183. data/lib/active_record/type.rb +8 -1
  184. data/lib/active_record/type_caster/connection.rb +0 -1
  185. data/lib/active_record/type_caster/map.rb +8 -5
  186. data/lib/active_record/validations/associated.rb +1 -1
  187. data/lib/active_record/validations/numericality.rb +35 -0
  188. data/lib/active_record/validations/uniqueness.rb +24 -4
  189. data/lib/active_record/validations.rb +1 -0
  190. data/lib/active_record.rb +7 -14
  191. data/lib/arel/attributes/attribute.rb +4 -0
  192. data/lib/arel/collectors/bind.rb +5 -0
  193. data/lib/arel/collectors/composite.rb +8 -0
  194. data/lib/arel/collectors/sql_string.rb +7 -0
  195. data/lib/arel/collectors/substitute_binds.rb +7 -0
  196. data/lib/arel/nodes/binary.rb +82 -8
  197. data/lib/arel/nodes/bind_param.rb +8 -0
  198. data/lib/arel/nodes/casted.rb +21 -9
  199. data/lib/arel/nodes/equality.rb +6 -9
  200. data/lib/arel/nodes/grouping.rb +3 -0
  201. data/lib/arel/nodes/homogeneous_in.rb +72 -0
  202. data/lib/arel/nodes/in.rb +8 -1
  203. data/lib/arel/nodes/infix_operation.rb +13 -1
  204. data/lib/arel/nodes/join_source.rb +1 -1
  205. data/lib/arel/nodes/node.rb +7 -6
  206. data/lib/arel/nodes/ordering.rb +27 -0
  207. data/lib/arel/nodes/sql_literal.rb +3 -0
  208. data/lib/arel/nodes/table_alias.rb +7 -3
  209. data/lib/arel/nodes/unary.rb +0 -1
  210. data/lib/arel/nodes.rb +3 -1
  211. data/lib/arel/predications.rb +12 -18
  212. data/lib/arel/select_manager.rb +1 -2
  213. data/lib/arel/table.rb +13 -5
  214. data/lib/arel/visitors/dot.rb +14 -2
  215. data/lib/arel/visitors/mysql.rb +11 -1
  216. data/lib/arel/visitors/postgresql.rb +15 -4
  217. data/lib/arel/visitors/to_sql.rb +89 -78
  218. data/lib/arel/visitors.rb +0 -7
  219. data/lib/arel.rb +5 -13
  220. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  221. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  222. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +3 -3
  223. data/lib/rails/generators/active_record/migration.rb +6 -1
  224. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  225. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  226. metadata +28 -30
  227. data/lib/active_record/advisory_lock_base.rb +0 -18
  228. data/lib/active_record/attribute_decorators.rb +0 -88
  229. data/lib/active_record/connection_adapters/connection_specification.rb +0 -296
  230. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  231. data/lib/active_record/define_callbacks.rb +0 -22
  232. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  233. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  234. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  235. data/lib/arel/attributes.rb +0 -22
  236. data/lib/arel/visitors/depth_first.rb +0 -203
  237. data/lib/arel/visitors/ibm_db.rb +0 -34
  238. data/lib/arel/visitors/informix.rb +0 -62
  239. data/lib/arel/visitors/mssql.rb +0 -156
  240. data/lib/arel/visitors/oracle.rb +0 -158
  241. data/lib/arel/visitors/oracle12.rb +0 -65
  242. data/lib/arel/visitors/where_sql.rb +0 -22
@@ -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 owner_name
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+.
@@ -193,7 +182,7 @@ module ActiveRecord
193
182
  @queue.pop
194
183
  end
195
184
 
196
- # Remove and return the head the queue if the number of
185
+ # Remove and return the head of the queue if the number of
197
186
  # available elements is strictly greater than the number of
198
187
  # threads currently waiting. Otherwise, return +nil+.
199
188
  def no_wait_poll
@@ -332,11 +321,17 @@ module ActiveRecord
332
321
  private
333
322
  def spawn_thread(frequency)
334
323
  Thread.new(frequency) do |t|
324
+ # Advise multi-threaded app servers to ignore this thread for
325
+ # the purposes of fork safety warnings
326
+ Thread.current.thread_variable_set(:fork_safe, true)
335
327
  running = true
336
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
@@ -364,35 +359,34 @@ module ActiveRecord
364
359
  include QueryCache::ConnectionPoolConfiguration
365
360
  include ConnectionAdapters::AbstractPool
366
361
 
367
- attr_accessor :automatic_reconnect, :checkout_timeout, :schema_cache
368
- attr_reader :spec, :size, :reaper
362
+ attr_accessor :automatic_reconnect, :checkout_timeout
363
+ attr_reader :db_config, :size, :reaper, :pool_config, :owner_name
369
364
 
370
- # 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
371
368
  # object which describes database connection information (e.g. adapter,
372
369
  # host name, username, password, etc), as well as the maximum size for
373
370
  # this ConnectionPool.
374
371
  #
375
372
  # The default ConnectionPool maximum size is 5.
376
- def initialize(spec)
373
+ def initialize(pool_config)
377
374
  super()
378
375
 
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
376
+ @pool_config = pool_config
377
+ @db_config = pool_config.db_config
378
+ @owner_name = pool_config.connection_specification_name
386
379
 
387
- # default max pool size to 5
388
- @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
389
383
 
390
384
  # This variable tracks the cache of threads mapped to reserved connections, with the
391
385
  # sole purpose of speeding up the +connection+ method. It is not the authoritative
392
386
  # registry of which thread owns which connection. Connection ownership is tracked by
393
387
  # the +connection.owner+ attr on each +connection+ instance.
394
388
  # The invariant works like this: if there is mapping of <tt>thread => conn</tt>,
395
- # 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
396
390
  # mapping does not mean that the +thread+ doesn't own the said connection. In
397
391
  # that case +conn.owner+ attr should be consulted.
398
392
  # Access and modification of <tt>@thread_cached_conns</tt> does not require
@@ -413,10 +407,7 @@ module ActiveRecord
413
407
 
414
408
  @lock_thread = false
415
409
 
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)
410
+ @reaper = Reaper.new(self, db_config.reaping_frequency)
420
411
  @reaper.run
421
412
  end
422
413
 
@@ -498,7 +489,7 @@ module ActiveRecord
498
489
  # Raises:
499
490
  # - ActiveRecord::ExclusiveConnectionTimeoutError if unable to gain ownership of all
500
491
  # connections in the pool within a timeout interval (default duration is
501
- # <tt>spec.config[:checkout_timeout] * 2</tt> seconds).
492
+ # <tt>spec.db_config.checkout_timeout * 2</tt> seconds).
502
493
  def disconnect(raise_on_acquisition_timeout = true)
503
494
  with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
504
495
  synchronize do
@@ -519,7 +510,7 @@ module ActiveRecord
519
510
  #
520
511
  # The pool first tries to gain ownership of all connections. If unable to
521
512
  # do so within a timeout interval (default duration is
522
- # <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
523
514
  # disconnected without any regard for other connection owning threads.
524
515
  def disconnect!
525
516
  disconnect(false)
@@ -532,7 +523,7 @@ module ActiveRecord
532
523
  # See AbstractAdapter#discard!
533
524
  def discard! # :nodoc:
534
525
  synchronize do
535
- return if @connections.nil? # already discarded
526
+ return if self.discarded?
536
527
  @connections.each do |conn|
537
528
  conn.discard!
538
529
  end
@@ -540,13 +531,17 @@ module ActiveRecord
540
531
  end
541
532
  end
542
533
 
534
+ def discarded? # :nodoc:
535
+ @connections.nil?
536
+ end
537
+
543
538
  # Clears the cache which maps classes and re-connects connections that
544
539
  # require reloading.
545
540
  #
546
541
  # Raises:
547
542
  # - ActiveRecord::ExclusiveConnectionTimeoutError if unable to gain ownership of all
548
543
  # connections in the pool within a timeout interval (default duration is
549
- # <tt>spec.config[:checkout_timeout] * 2</tt> seconds).
544
+ # <tt>spec.db_config.checkout_timeout * 2</tt> seconds).
550
545
  def clear_reloadable_connections(raise_on_acquisition_timeout = true)
551
546
  with_exclusively_acquired_all_connections(raise_on_acquisition_timeout) do
552
547
  synchronize do
@@ -568,7 +563,7 @@ module ActiveRecord
568
563
  #
569
564
  # The pool first tries to gain ownership of all connections. If unable to
570
565
  # do so within a timeout interval (default duration is
571
- # <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
572
567
  # clears the cache and reloads connections without any regard for other
573
568
  # connection owning threads.
574
569
  def clear_reloadable_connections!
@@ -648,7 +643,7 @@ module ActiveRecord
648
643
  # or a thread dies unexpectedly.
649
644
  def reap
650
645
  stale_connections = synchronize do
651
- return unless @connections
646
+ return if self.discarded?
652
647
  @connections.select do |conn|
653
648
  conn.in_use? && !conn.owner.alive?
654
649
  end.each do |conn|
@@ -673,7 +668,7 @@ module ActiveRecord
673
668
  return if minimum_idle.nil?
674
669
 
675
670
  idle_connections = synchronize do
676
- return unless @connections
671
+ return if self.discarded?
677
672
  @connections.select do |conn|
678
673
  !conn.in_use? && conn.seconds_idle >= minimum_idle
679
674
  end.each do |conn|
@@ -884,7 +879,7 @@ module ActiveRecord
884
879
  alias_method :release, :remove_connection_from_thread_cache
885
880
 
886
881
  def new_connection
887
- 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|
888
883
  conn.check_version
889
884
  end
890
885
  end
@@ -988,38 +983,18 @@ module ActiveRecord
988
983
  # should use.
989
984
  #
990
985
  # 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,
986
+ # about the model. The model needs to pass a connection specification name to the handler,
992
987
  # in order to look up the correct connection pool.
993
988
  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
989
+ FINALIZER = lambda { |_| ActiveSupport::ForkTracker.check! }
990
+ private_constant :FINALIZER
1015
991
 
1016
992
  def initialize
1017
- # These caches are keyed by spec.name (ConnectionSpecification#name).
1018
- @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)
1019
995
 
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)
996
+ # Backup finalizer: if the forked child skipped Kernel#fork the early discard has not occurred
997
+ ObjectSpace.define_finalizer self, FINALIZER
1023
998
  end
1024
999
 
1025
1000
  def prevent_writes # :nodoc:
@@ -1042,84 +1017,112 @@ module ActiveRecord
1042
1017
  # See `READ_QUERY` for the queries that are blocked by this
1043
1018
  # method.
1044
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
+
1045
1024
  original, self.prevent_writes = self.prevent_writes, enabled
1046
1025
  yield
1047
1026
  ensure
1048
1027
  self.prevent_writes = original
1049
1028
  end
1050
1029
 
1051
- def connection_pool_list
1052
- 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) }
1053
1040
  end
1054
1041
  alias :connection_pools :connection_pool_list
1055
1042
 
1056
- def establish_connection(config)
1057
- resolver = ConnectionSpecification::Resolver.new(Base.configurations)
1058
- spec = resolver.spec(config)
1043
+ def establish_connection(config, owner_name: Base.name, role: ActiveRecord::Base.current_role, shard: Base.current_shard)
1044
+ owner_name = config.to_s if config.is_a?(Symbol)
1059
1045
 
1060
- remove_connection(spec.name)
1046
+ pool_config = resolve_pool_config(config, owner_name)
1047
+ db_config = pool_config.db_config
1048
+
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
1061
1054
 
1062
1055
  message_bus = ActiveSupport::Notifications.instrumenter
1063
- payload = {
1064
- connection_id: object_id
1065
- }
1066
- if spec
1067
- payload[:spec_name] = spec.name
1068
- 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
1069
1061
  end
1070
1062
 
1071
- message_bus.instrument("!connection.active_record", payload) do
1072
- 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
1073
1067
  end
1068
+ pool_manager = get_pool_manager(pool_config.connection_specification_name)
1069
+ pool_manager.set_pool_config(role, shard, pool_config)
1074
1070
 
1075
- owner_to_pool[spec.name]
1071
+ message_bus.instrument("!connection.active_record", payload) do
1072
+ pool_config.pool
1073
+ end
1076
1074
  end
1077
1075
 
1078
1076
  # Returns true if there are any active connections among the connection
1079
1077
  # pools that the ConnectionHandler is managing.
1080
- def active_connections?
1081
- connection_pool_list.any?(&:active_connection?)
1078
+ def active_connections?(role = ActiveRecord::Base.current_role)
1079
+ connection_pool_list(role).any?(&:active_connection?)
1082
1080
  end
1083
1081
 
1084
1082
  # Returns any connections in use by the current thread back to the pool,
1085
1083
  # and also returns connections to the pool cached by threads that are no
1086
1084
  # longer alive.
1087
- def clear_active_connections!
1088
- connection_pool_list.each(&:release_connection)
1085
+ def clear_active_connections!(role = ActiveRecord::Base.current_role)
1086
+ connection_pool_list(role).each(&:release_connection)
1089
1087
  end
1090
1088
 
1091
1089
  # Clears the cache which maps classes.
1092
1090
  #
1093
1091
  # See ConnectionPool#clear_reloadable_connections! for details.
1094
- def clear_reloadable_connections!
1095
- 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!)
1096
1094
  end
1097
1095
 
1098
- def clear_all_connections!
1099
- connection_pool_list.each(&:disconnect!)
1096
+ def clear_all_connections!(role = ActiveRecord::Base.current_role)
1097
+ connection_pool_list(role).each(&:disconnect!)
1100
1098
  end
1101
1099
 
1102
1100
  # Disconnects all currently idle connections.
1103
1101
  #
1104
1102
  # See ConnectionPool#flush! for details.
1105
- def flush_idle_connections!
1106
- connection_pool_list.each(&:flush!)
1103
+ def flush_idle_connections!(role = ActiveRecord::Base.current_role)
1104
+ connection_pool_list(role).each(&:flush!)
1107
1105
  end
1108
1106
 
1109
1107
  # Locate the connection of the nearest super class. This can be an
1110
1108
  # active or defined connection: if it is the latter, it will be
1111
1109
  # opened and set as the active connection for the class it was defined
1112
1110
  # for (not necessarily the current class).
1113
- def retrieve_connection(spec_name) #:nodoc:
1114
- 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)
1115
1113
 
1116
1114
  unless pool
1117
- # multiple database application
1118
- if ActiveRecord::Base.connection_handler != ActiveRecord::Base.default_connection_handler
1119
- 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."
1120
1121
  else
1121
- raise ConnectionNotEstablished, "No connection pool with '#{spec_name}' found."
1122
+ message = "No connection pool for '#{spec_name}' found."
1122
1123
  end
1124
+
1125
+ raise ConnectionNotEstablished, message
1123
1126
  end
1124
1127
 
1125
1128
  pool.connection
@@ -1127,8 +1130,8 @@ module ActiveRecord
1127
1130
 
1128
1131
  # Returns true if a connection that's accessible to this class has
1129
1132
  # already been opened.
1130
- def connected?(spec_name)
1131
- 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)
1132
1135
  pool && pool.connected?
1133
1136
  end
1134
1137
 
@@ -1136,42 +1139,90 @@ module ActiveRecord
1136
1139
  # connection and the defined connection (if they exist). The result
1137
1140
  # can be used as an argument for #establish_connection, for easily
1138
1141
  # re-establishing the connection.
1139
- def remove_connection(spec_name)
1140
- if pool = owner_to_pool.delete(spec_name)
1141
- pool.automatic_reconnect = false
1142
- pool.disconnect!
1143
- 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
1144
1155
  end
1145
1156
  end
1146
1157
 
1147
- # 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.
1148
1159
  # This makes retrieving the connection pool O(1) once the process is warm.
1149
1160
  # When a connection is established or removed, we invalidate the cache.
1150
- def retrieve_connection_pool(spec_name)
1151
- owner_to_pool.fetch(spec_name) do
1152
- # Check if a connection was previously established in an ancestor process,
1153
- # which may have been forked.
1154
- if ancestor_pool = pool_from_any_process_for(spec_name)
1155
- # A connection was established in an ancestor process that must have
1156
- # subsequently forked. We can't reuse the connection, but we can copy
1157
- # the specification and establish a new connection with it.
1158
- establish_connection(ancestor_pool.spec.to_hash).tap do |pool|
1159
- pool.schema_cache = ancestor_pool.schema_cache if ancestor_pool.schema_cache
1160
- end
1161
- else
1162
- owner_to_pool[spec_name] = nil
1163
- end
1164
- 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
1165
1164
  end
1166
1165
 
1167
1166
  private
1168
- def owner_to_pool
1169
- @owner_to_pool[Process.pid]
1167
+ attr_reader :owner_to_pool_manager
1168
+
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
1170
1183
  end
1171
1184
 
1172
- def pool_from_any_process_for(spec_name)
1173
- owner_to_pool = @owner_to_pool.values.reverse.find { |v| v[spec_name] }
1174
- 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)
1175
1226
  end
1176
1227
  end
1177
1228
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/deprecation"
4
-
5
3
  module ActiveRecord
6
4
  module ConnectionAdapters # :nodoc:
7
5
  module DatabaseLimits
@@ -14,18 +12,6 @@ module ActiveRecord
14
12
  max_identifier_length
15
13
  end
16
14
 
17
- # Returns the maximum length of a column name.
18
- def column_name_length
19
- max_identifier_length
20
- end
21
- deprecate :column_name_length
22
-
23
- # Returns the maximum length of a table name.
24
- def table_name_length
25
- max_identifier_length
26
- end
27
- deprecate :table_name_length
28
-
29
15
  # Returns the maximum allowed length for an index name. This
30
16
  # limit is enforced by \Rails and is less than or equal to
31
17
  # #index_name_length. The gap between
@@ -34,47 +20,19 @@ module ActiveRecord
34
20
  def allowed_index_name_length
35
21
  index_name_length
36
22
  end
23
+ deprecate :allowed_index_name_length
37
24
 
38
25
  # Returns the maximum length of an index name.
39
26
  def index_name_length
40
27
  max_identifier_length
41
28
  end
42
29
 
43
- # Returns the maximum number of columns per table.
44
- def columns_per_table
45
- 1024
46
- end
47
- deprecate :columns_per_table
48
-
49
- # Returns the maximum number of indexes per table.
50
- def indexes_per_table
51
- 16
52
- end
53
- deprecate :indexes_per_table
54
-
55
- # Returns the maximum number of columns in a multicolumn index.
56
- def columns_per_multicolumn_index
57
- 16
58
- end
59
- deprecate :columns_per_multicolumn_index
60
-
61
30
  # Returns the maximum number of elements in an IN (x,y,z) clause.
62
31
  # +nil+ means no limit.
63
32
  def in_clause_length
64
33
  nil
65
34
  end
66
-
67
- # Returns the maximum length of an SQL query.
68
- def sql_query_length
69
- 1048575
70
- end
71
- deprecate :sql_query_length
72
-
73
- # Returns maximum number of joins in a single query.
74
- def joins_per_query
75
- 256
76
- end
77
- deprecate :joins_per_query
35
+ deprecate :in_clause_length
78
36
 
79
37
  private
80
38
  def bind_params_length