activerecord 7.0.0 → 7.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (251) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1701 -1039
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +18 -18
  5. data/lib/active_record/aggregations.rb +16 -13
  6. data/lib/active_record/association_relation.rb +1 -1
  7. data/lib/active_record/associations/association.rb +18 -3
  8. data/lib/active_record/associations/association_scope.rb +16 -9
  9. data/lib/active_record/associations/belongs_to_association.rb +14 -6
  10. data/lib/active_record/associations/builder/association.rb +3 -3
  11. data/lib/active_record/associations/builder/belongs_to.rb +21 -8
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
  13. data/lib/active_record/associations/builder/singular_association.rb +4 -0
  14. data/lib/active_record/associations/collection_association.rb +17 -12
  15. data/lib/active_record/associations/collection_proxy.rb +22 -12
  16. data/lib/active_record/associations/foreign_association.rb +10 -3
  17. data/lib/active_record/associations/has_many_association.rb +27 -17
  18. data/lib/active_record/associations/has_many_through_association.rb +10 -6
  19. data/lib/active_record/associations/has_one_association.rb +10 -3
  20. data/lib/active_record/associations/join_dependency.rb +20 -14
  21. data/lib/active_record/associations/preloader/association.rb +27 -6
  22. data/lib/active_record/associations/preloader/through_association.rb +1 -1
  23. data/lib/active_record/associations/preloader.rb +13 -10
  24. data/lib/active_record/associations/singular_association.rb +1 -1
  25. data/lib/active_record/associations/through_association.rb +22 -11
  26. data/lib/active_record/associations.rb +362 -236
  27. data/lib/active_record/attribute_assignment.rb +0 -2
  28. data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
  29. data/lib/active_record/attribute_methods/dirty.rb +52 -34
  30. data/lib/active_record/attribute_methods/primary_key.rb +76 -24
  31. data/lib/active_record/attribute_methods/query.rb +28 -16
  32. data/lib/active_record/attribute_methods/read.rb +18 -5
  33. data/lib/active_record/attribute_methods/serialization.rb +172 -69
  34. data/lib/active_record/attribute_methods/write.rb +3 -3
  35. data/lib/active_record/attribute_methods.rb +110 -28
  36. data/lib/active_record/attributes.rb +3 -3
  37. data/lib/active_record/autosave_association.rb +56 -10
  38. data/lib/active_record/base.rb +10 -5
  39. data/lib/active_record/callbacks.rb +16 -32
  40. data/lib/active_record/coders/column_serializer.rb +61 -0
  41. data/lib/active_record/coders/json.rb +1 -1
  42. data/lib/active_record/coders/yaml_column.rb +70 -34
  43. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +164 -89
  44. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
  45. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
  46. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +63 -43
  47. data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
  48. data/lib/active_record/connection_adapters/abstract/database_statements.rb +129 -31
  49. data/lib/active_record/connection_adapters/abstract/query_cache.rb +60 -22
  50. data/lib/active_record/connection_adapters/abstract/quoting.rb +52 -8
  51. data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
  52. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
  53. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +163 -29
  54. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  55. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +302 -131
  56. data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
  57. data/lib/active_record/connection_adapters/abstract_adapter.rb +513 -106
  58. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +217 -104
  59. data/lib/active_record/connection_adapters/column.rb +9 -0
  60. data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
  61. data/lib/active_record/connection_adapters/mysql/database_statements.rb +23 -144
  62. data/lib/active_record/connection_adapters/mysql/quoting.rb +29 -12
  63. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
  64. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
  65. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
  66. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +38 -14
  67. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
  68. data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
  69. data/lib/active_record/connection_adapters/pool_config.rb +14 -5
  70. data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
  71. data/lib/active_record/connection_adapters/postgresql/column.rb +16 -3
  72. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +75 -45
  73. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  74. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -2
  75. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
  76. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
  77. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +4 -2
  78. data/lib/active_record/connection_adapters/postgresql/quoting.rb +41 -8
  79. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +6 -10
  80. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
  81. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
  82. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
  83. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +372 -63
  84. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  85. data/lib/active_record/connection_adapters/postgresql_adapter.rb +359 -197
  86. data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
  87. data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
  88. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +52 -39
  89. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +22 -5
  90. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
  91. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +41 -22
  92. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +242 -81
  93. data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
  94. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
  95. data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
  96. data/lib/active_record/connection_adapters.rb +3 -1
  97. data/lib/active_record/connection_handling.rb +73 -96
  98. data/lib/active_record/core.rb +142 -153
  99. data/lib/active_record/counter_cache.rb +46 -25
  100. data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -0
  101. data/lib/active_record/database_configurations/database_config.rb +9 -3
  102. data/lib/active_record/database_configurations/hash_config.rb +22 -12
  103. data/lib/active_record/database_configurations/url_config.rb +17 -11
  104. data/lib/active_record/database_configurations.rb +87 -34
  105. data/lib/active_record/delegated_type.rb +9 -4
  106. data/lib/active_record/deprecator.rb +7 -0
  107. data/lib/active_record/destroy_association_async_job.rb +2 -0
  108. data/lib/active_record/disable_joins_association_relation.rb +1 -1
  109. data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
  110. data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
  111. data/lib/active_record/encryption/config.rb +25 -1
  112. data/lib/active_record/encryption/configurable.rb +13 -14
  113. data/lib/active_record/encryption/context.rb +10 -3
  114. data/lib/active_record/encryption/contexts.rb +8 -4
  115. data/lib/active_record/encryption/derived_secret_key_provider.rb +9 -3
  116. data/lib/active_record/encryption/deterministic_key_provider.rb +1 -1
  117. data/lib/active_record/encryption/encryptable_record.rb +38 -22
  118. data/lib/active_record/encryption/encrypted_attribute_type.rb +19 -8
  119. data/lib/active_record/encryption/encryptor.rb +7 -7
  120. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +3 -3
  121. data/lib/active_record/encryption/extended_deterministic_queries.rb +83 -86
  122. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
  123. data/lib/active_record/encryption/key_generator.rb +12 -1
  124. data/lib/active_record/encryption/message.rb +1 -1
  125. data/lib/active_record/encryption/message_serializer.rb +2 -0
  126. data/lib/active_record/encryption/properties.rb +4 -4
  127. data/lib/active_record/encryption/scheme.rb +20 -23
  128. data/lib/active_record/encryption.rb +1 -0
  129. data/lib/active_record/enum.rb +113 -29
  130. data/lib/active_record/errors.rb +108 -15
  131. data/lib/active_record/explain.rb +23 -3
  132. data/lib/active_record/explain_subscriber.rb +1 -1
  133. data/lib/active_record/fixture_set/model_metadata.rb +14 -4
  134. data/lib/active_record/fixture_set/render_context.rb +2 -0
  135. data/lib/active_record/fixture_set/table_row.rb +29 -8
  136. data/lib/active_record/fixtures.rb +121 -73
  137. data/lib/active_record/future_result.rb +30 -5
  138. data/lib/active_record/gem_version.rb +3 -3
  139. data/lib/active_record/inheritance.rb +30 -16
  140. data/lib/active_record/insert_all.rb +57 -10
  141. data/lib/active_record/integration.rb +10 -10
  142. data/lib/active_record/internal_metadata.rb +120 -30
  143. data/lib/active_record/locking/optimistic.rb +32 -18
  144. data/lib/active_record/locking/pessimistic.rb +8 -5
  145. data/lib/active_record/log_subscriber.rb +39 -17
  146. data/lib/active_record/marshalling.rb +56 -0
  147. data/lib/active_record/message_pack.rb +124 -0
  148. data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
  149. data/lib/active_record/middleware/database_selector.rb +18 -13
  150. data/lib/active_record/middleware/shard_selector.rb +7 -5
  151. data/lib/active_record/migration/command_recorder.rb +108 -10
  152. data/lib/active_record/migration/compatibility.rb +158 -64
  153. data/lib/active_record/migration/default_strategy.rb +23 -0
  154. data/lib/active_record/migration/execution_strategy.rb +19 -0
  155. data/lib/active_record/migration/pending_migration_connection.rb +21 -0
  156. data/lib/active_record/migration.rb +274 -117
  157. data/lib/active_record/model_schema.rb +86 -54
  158. data/lib/active_record/nested_attributes.rb +24 -6
  159. data/lib/active_record/normalization.rb +167 -0
  160. data/lib/active_record/persistence.rb +200 -47
  161. data/lib/active_record/promise.rb +84 -0
  162. data/lib/active_record/query_cache.rb +3 -21
  163. data/lib/active_record/query_logs.rb +87 -51
  164. data/lib/active_record/query_logs_formatter.rb +41 -0
  165. data/lib/active_record/querying.rb +16 -3
  166. data/lib/active_record/railtie.rb +128 -62
  167. data/lib/active_record/railties/controller_runtime.rb +12 -8
  168. data/lib/active_record/railties/databases.rake +145 -146
  169. data/lib/active_record/railties/job_runtime.rb +23 -0
  170. data/lib/active_record/readonly_attributes.rb +32 -5
  171. data/lib/active_record/reflection.rb +189 -45
  172. data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
  173. data/lib/active_record/relation/batches.rb +190 -61
  174. data/lib/active_record/relation/calculations.rb +208 -83
  175. data/lib/active_record/relation/delegation.rb +23 -9
  176. data/lib/active_record/relation/finder_methods.rb +77 -16
  177. data/lib/active_record/relation/merger.rb +2 -0
  178. data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
  179. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
  180. data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
  181. data/lib/active_record/relation/predicate_builder.rb +26 -14
  182. data/lib/active_record/relation/query_attribute.rb +25 -1
  183. data/lib/active_record/relation/query_methods.rb +430 -77
  184. data/lib/active_record/relation/spawn_methods.rb +18 -1
  185. data/lib/active_record/relation.rb +98 -41
  186. data/lib/active_record/result.rb +25 -9
  187. data/lib/active_record/runtime_registry.rb +10 -1
  188. data/lib/active_record/sanitization.rb +57 -16
  189. data/lib/active_record/schema.rb +36 -22
  190. data/lib/active_record/schema_dumper.rb +65 -23
  191. data/lib/active_record/schema_migration.rb +68 -33
  192. data/lib/active_record/scoping/default.rb +20 -12
  193. data/lib/active_record/scoping/named.rb +2 -2
  194. data/lib/active_record/scoping.rb +2 -1
  195. data/lib/active_record/secure_password.rb +60 -0
  196. data/lib/active_record/secure_token.rb +21 -3
  197. data/lib/active_record/serialization.rb +5 -0
  198. data/lib/active_record/signed_id.rb +9 -7
  199. data/lib/active_record/store.rb +16 -11
  200. data/lib/active_record/suppressor.rb +3 -1
  201. data/lib/active_record/table_metadata.rb +16 -3
  202. data/lib/active_record/tasks/database_tasks.rb +138 -107
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +17 -15
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
  206. data/lib/active_record/test_fixtures.rb +123 -99
  207. data/lib/active_record/timestamp.rb +27 -15
  208. data/lib/active_record/token_for.rb +113 -0
  209. data/lib/active_record/touch_later.rb +11 -6
  210. data/lib/active_record/transactions.rb +39 -13
  211. data/lib/active_record/translation.rb +1 -1
  212. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  213. data/lib/active_record/type/internal/timezone.rb +7 -2
  214. data/lib/active_record/type/serialized.rb +8 -4
  215. data/lib/active_record/type/time.rb +4 -0
  216. data/lib/active_record/validations/absence.rb +1 -1
  217. data/lib/active_record/validations/associated.rb +3 -3
  218. data/lib/active_record/validations/numericality.rb +5 -4
  219. data/lib/active_record/validations/presence.rb +5 -28
  220. data/lib/active_record/validations/uniqueness.rb +50 -5
  221. data/lib/active_record/validations.rb +8 -4
  222. data/lib/active_record/version.rb +1 -1
  223. data/lib/active_record.rb +143 -16
  224. data/lib/arel/errors.rb +10 -0
  225. data/lib/arel/factory_methods.rb +4 -0
  226. data/lib/arel/filter_predications.rb +1 -1
  227. data/lib/arel/nodes/and.rb +4 -0
  228. data/lib/arel/nodes/binary.rb +6 -1
  229. data/lib/arel/nodes/bound_sql_literal.rb +61 -0
  230. data/lib/arel/nodes/cte.rb +36 -0
  231. data/lib/arel/nodes/filter.rb +1 -1
  232. data/lib/arel/nodes/fragments.rb +35 -0
  233. data/lib/arel/nodes/homogeneous_in.rb +1 -9
  234. data/lib/arel/nodes/leading_join.rb +8 -0
  235. data/lib/arel/nodes/node.rb +111 -2
  236. data/lib/arel/nodes/sql_literal.rb +6 -0
  237. data/lib/arel/nodes/table_alias.rb +4 -0
  238. data/lib/arel/nodes.rb +4 -0
  239. data/lib/arel/predications.rb +2 -0
  240. data/lib/arel/table.rb +9 -5
  241. data/lib/arel/visitors/mysql.rb +8 -1
  242. data/lib/arel/visitors/to_sql.rb +81 -17
  243. data/lib/arel/visitors/visitor.rb +2 -2
  244. data/lib/arel.rb +16 -2
  245. data/lib/rails/generators/active_record/application_record/USAGE +8 -0
  246. data/lib/rails/generators/active_record/migration.rb +3 -1
  247. data/lib/rails/generators/active_record/model/USAGE +113 -0
  248. data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
  249. metadata +51 -15
  250. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
  251. data/lib/active_record/null_relation.rb +0 -63
@@ -5,6 +5,8 @@ require "concurrent/map"
5
5
 
6
6
  module ActiveRecord
7
7
  module ConnectionAdapters
8
+ # = Active Record Connection Handler
9
+ #
8
10
  # ConnectionHandler is a collection of ConnectionPool objects. It is used
9
11
  # for keeping separate connection pools that connect to different databases.
10
12
  #
@@ -40,7 +42,7 @@ module ActiveRecord
40
42
  # but the Book model connects to a separate database called "library_db"
41
43
  # (this can even be a database on a different machine).
42
44
  #
43
- # Book, ScaryBook and GoodBook will all use the same connection pool to
45
+ # Book, ScaryBook, and GoodBook will all use the same connection pool to
44
46
  # "library_db" while Author, BankAccount, and any other models you create
45
47
  # will use the default connection pool to "my_application".
46
48
  #
@@ -56,7 +58,7 @@ module ActiveRecord
56
58
  FINALIZER = lambda { |_| ActiveSupport::ForkTracker.check! }
57
59
  private_constant :FINALIZER
58
60
 
59
- class StringConnectionOwner # :nodoc:
61
+ class StringConnectionName # :nodoc:
60
62
  attr_reader :name
61
63
 
62
64
  def initialize(name)
@@ -73,8 +75,8 @@ module ActiveRecord
73
75
  end
74
76
 
75
77
  def initialize
76
- # These caches are keyed by pool_config.connection_specification_name (PoolConfig#connection_specification_name).
77
- @owner_to_pool_manager = Concurrent::Map.new(initial_capacity: 2)
78
+ # These caches are keyed by pool_config.connection_name (PoolConfig#connection_name).
79
+ @connection_name_to_pool_manager = Concurrent::Map.new(initial_capacity: 2)
78
80
 
79
81
  # Backup finalizer: if the forked child skipped Kernel#fork the early discard has not occurred
80
82
  ObjectSpace.define_finalizer self, FINALIZER
@@ -88,121 +90,154 @@ module ActiveRecord
88
90
  ActiveSupport::IsolatedExecutionState[:active_record_prevent_writes] = prevent_writes
89
91
  end
90
92
 
91
- # Prevent writing to the database regardless of role.
92
- #
93
- # In some cases you may want to prevent writes to the database
94
- # even if you are on a database that can write. +while_preventing_writes+
95
- # will prevent writes to the database for the duration of the block.
96
- #
97
- # This method does not provide the same protection as a readonly
98
- # user and is meant to be a safeguard against accidental writes.
99
- #
100
- # See +READ_QUERY+ for the queries that are blocked by this
101
- # method.
102
- def while_preventing_writes(enabled = true)
103
- unless ActiveRecord.legacy_connection_handling
104
- raise NotImplementedError, "`while_preventing_writes` is only available on the connection_handler with legacy_connection_handling"
105
- end
106
-
107
- original, self.prevent_writes = self.prevent_writes, enabled
108
- yield
109
- ensure
110
- self.prevent_writes = original
111
- end
112
-
113
93
  def connection_pool_names # :nodoc:
114
- owner_to_pool_manager.keys
94
+ connection_name_to_pool_manager.keys
115
95
  end
116
96
 
117
97
  def all_connection_pools
118
- owner_to_pool_manager.values.flat_map { |m| m.pool_configs.map(&:pool) }
98
+ ActiveRecord.deprecator.warn(<<-MSG.squish)
99
+ The `all_connection_pools` method is deprecated in favor of `connection_pool_list`.
100
+ Call `connection_pool_list(:all)` to get the same behavior as `all_connection_pools`.
101
+ MSG
102
+ connection_name_to_pool_manager.values.flat_map { |m| m.pool_configs.map(&:pool) }
119
103
  end
120
104
 
121
- def connection_pool_list(role = ActiveRecord::Base.current_role)
122
- owner_to_pool_manager.values.flat_map { |m| m.pool_configs(role).map(&:pool) }
105
+ # Returns the pools for a connection handler and given role. If +:all+ is passed,
106
+ # all pools belonging to the connection handler will be returned.
107
+ def connection_pool_list(role = nil)
108
+ if role.nil?
109
+ deprecation_for_pool_handling(__method__)
110
+ role = ActiveRecord::Base.current_role
111
+ connection_name_to_pool_manager.values.flat_map { |m| m.pool_configs(role).map(&:pool) }
112
+ elsif role == :all
113
+ connection_name_to_pool_manager.values.flat_map { |m| m.pool_configs.map(&:pool) }
114
+ else
115
+ connection_name_to_pool_manager.values.flat_map { |m| m.pool_configs(role).map(&:pool) }
116
+ end
123
117
  end
124
118
  alias :connection_pools :connection_pool_list
125
119
 
126
- def establish_connection(config, owner_name: Base, role: ActiveRecord::Base.current_role, shard: Base.current_shard)
127
- owner_name = StringConnectionOwner.new(config.to_s) if config.is_a?(Symbol)
120
+ def each_connection_pool(role = nil, &block) # :nodoc:
121
+ role = nil if role == :all
122
+ return enum_for(__method__, role) unless block_given?
123
+
124
+ connection_name_to_pool_manager.each_value do |manager|
125
+ manager.each_pool_config(role) do |pool_config|
126
+ yield pool_config.pool
127
+ end
128
+ end
129
+ end
130
+
131
+ def establish_connection(config, owner_name: Base, role: Base.current_role, shard: Base.current_shard, clobber: false)
132
+ owner_name = determine_owner_name(owner_name, config)
128
133
 
129
134
  pool_config = resolve_pool_config(config, owner_name, role, shard)
130
135
  db_config = pool_config.db_config
131
136
 
132
- # Protects the connection named `ActiveRecord::Base` from being removed
133
- # if the user calls `establish_connection :primary`.
134
- if owner_to_pool_manager.key?(pool_config.connection_specification_name)
135
- remove_connection_pool(pool_config.connection_specification_name, role: role, shard: shard)
136
- end
137
+ pool_manager = set_pool_manager(pool_config.connection_name)
137
138
 
138
- message_bus = ActiveSupport::Notifications.instrumenter
139
- payload = {}
140
- if pool_config
141
- payload[:spec_name] = pool_config.connection_specification_name
142
- payload[:shard] = shard
143
- payload[:config] = db_config.configuration_hash
144
- end
139
+ # If there is an existing pool with the same values as the pool_config
140
+ # don't remove the connection. Connections should only be removed if we are
141
+ # establishing a connection on a class that is already connected to a different
142
+ # configuration.
143
+ existing_pool_config = pool_manager.get_pool_config(role, shard)
145
144
 
146
- if ActiveRecord.legacy_connection_handling
147
- owner_to_pool_manager[pool_config.connection_specification_name] ||= LegacyPoolManager.new
148
- else
149
- owner_to_pool_manager[pool_config.connection_specification_name] ||= PoolManager.new
150
- end
151
- pool_manager = get_pool_manager(pool_config.connection_specification_name)
152
- pool_manager.set_pool_config(role, shard, pool_config)
145
+ if !clobber && existing_pool_config && existing_pool_config.db_config == db_config
146
+ # Update the pool_config's connection class if it differs. This is used
147
+ # for ensuring that ActiveRecord::Base and the primary_abstract_class use
148
+ # the same pool. Without this granular swapping will not work correctly.
149
+ if owner_name.primary_class? && (existing_pool_config.connection_class != owner_name)
150
+ existing_pool_config.connection_class = owner_name
151
+ end
153
152
 
154
- message_bus.instrument("!connection.active_record", payload) do
155
- pool_config.pool
153
+ existing_pool_config.pool
154
+ else
155
+ disconnect_pool_from_pool_manager(pool_manager, role, shard)
156
+ pool_manager.set_pool_config(role, shard, pool_config)
157
+
158
+ payload = {
159
+ connection_name: pool_config.connection_name,
160
+ role: role,
161
+ shard: shard,
162
+ config: db_config.configuration_hash
163
+ }
164
+
165
+ ActiveSupport::Notifications.instrumenter.instrument("!connection.active_record", payload) do
166
+ pool_config.pool
167
+ end
156
168
  end
157
169
  end
158
170
 
159
171
  # Returns true if there are any active connections among the connection
160
172
  # pools that the ConnectionHandler is managing.
161
- def active_connections?(role = ActiveRecord::Base.current_role)
162
- connection_pool_list(role).any?(&:active_connection?)
173
+ def active_connections?(role = nil)
174
+ if role.nil?
175
+ deprecation_for_pool_handling(__method__)
176
+ role = ActiveRecord::Base.current_role
177
+ end
178
+
179
+ each_connection_pool(role).any?(&:active_connection?)
163
180
  end
164
181
 
165
182
  # Returns any connections in use by the current thread back to the pool,
166
183
  # and also returns connections to the pool cached by threads that are no
167
184
  # longer alive.
168
- def clear_active_connections!(role = ActiveRecord::Base.current_role)
169
- connection_pool_list(role).each(&:release_connection)
185
+ def clear_active_connections!(role = nil)
186
+ if role.nil?
187
+ deprecation_for_pool_handling(__method__)
188
+ role = ActiveRecord::Base.current_role
189
+ end
190
+
191
+ each_connection_pool(role).each(&:release_connection)
170
192
  end
171
193
 
172
194
  # Clears the cache which maps classes.
173
195
  #
174
196
  # See ConnectionPool#clear_reloadable_connections! for details.
175
- def clear_reloadable_connections!(role = ActiveRecord::Base.current_role)
176
- connection_pool_list(role).each(&:clear_reloadable_connections!)
197
+ def clear_reloadable_connections!(role = nil)
198
+ if role.nil?
199
+ deprecation_for_pool_handling(__method__)
200
+ role = ActiveRecord::Base.current_role
201
+ end
202
+
203
+ each_connection_pool(role).each(&:clear_reloadable_connections!)
177
204
  end
178
205
 
179
- def clear_all_connections!(role = ActiveRecord::Base.current_role)
180
- connection_pool_list(role).each(&:disconnect!)
206
+ def clear_all_connections!(role = nil)
207
+ if role.nil?
208
+ deprecation_for_pool_handling(__method__)
209
+ role = ActiveRecord::Base.current_role
210
+ end
211
+
212
+ each_connection_pool(role).each(&:disconnect!)
181
213
  end
182
214
 
183
215
  # Disconnects all currently idle connections.
184
216
  #
185
217
  # See ConnectionPool#flush! for details.
186
- def flush_idle_connections!(role = ActiveRecord::Base.current_role)
187
- connection_pool_list(role).each(&:flush!)
218
+ def flush_idle_connections!(role = nil)
219
+ if role.nil?
220
+ deprecation_for_pool_handling(__method__)
221
+ role = ActiveRecord::Base.current_role
222
+ end
223
+
224
+ each_connection_pool(role).each(&:flush!)
188
225
  end
189
226
 
190
227
  # Locate the connection of the nearest super class. This can be an
191
228
  # active or defined connection: if it is the latter, it will be
192
229
  # opened and set as the active connection for the class it was defined
193
230
  # for (not necessarily the current class).
194
- def retrieve_connection(spec_name, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard) # :nodoc:
195
- pool = retrieve_connection_pool(spec_name, role: role, shard: shard)
231
+ def retrieve_connection(connection_name, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard) # :nodoc:
232
+ pool = retrieve_connection_pool(connection_name, role: role, shard: shard)
196
233
 
197
234
  unless pool
198
235
  if shard != ActiveRecord::Base.default_shard
199
- message = "No connection pool for '#{spec_name}' found for the '#{shard}' shard."
200
- elsif ActiveRecord::Base.connection_handler != ActiveRecord::Base.default_connection_handler
201
- message = "No connection pool for '#{spec_name}' found for the '#{ActiveRecord::Base.current_role}' role."
236
+ message = "No connection pool for '#{connection_name}' found for the '#{shard}' shard."
202
237
  elsif role != ActiveRecord::Base.default_role
203
- message = "No connection pool for '#{spec_name}' found for the '#{role}' role."
238
+ message = "No connection pool for '#{connection_name}' found for the '#{role}' role."
204
239
  else
205
- message = "No connection pool for '#{spec_name}' found."
240
+ message = "No connection pool for '#{connection_name}' found."
206
241
  end
207
242
 
208
243
  raise ConnectionNotEstablished, message
@@ -213,36 +248,66 @@ module ActiveRecord
213
248
 
214
249
  # Returns true if a connection that's accessible to this class has
215
250
  # already been opened.
216
- def connected?(spec_name, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard)
217
- pool = retrieve_connection_pool(spec_name, role: role, shard: shard)
251
+ def connected?(connection_name, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard)
252
+ pool = retrieve_connection_pool(connection_name, role: role, shard: shard)
218
253
  pool && pool.connected?
219
254
  end
220
255
 
221
- def remove_connection_pool(owner, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard)
222
- if pool_manager = get_pool_manager(owner)
223
- pool_config = pool_manager.remove_pool_config(role, shard)
224
-
225
- if pool_config
226
- pool_config.disconnect!
227
- pool_config.db_config
228
- end
256
+ def remove_connection_pool(connection_name, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard)
257
+ if pool_manager = get_pool_manager(connection_name)
258
+ disconnect_pool_from_pool_manager(pool_manager, role, shard)
229
259
  end
230
260
  end
231
261
 
232
- # Retrieving the connection pool happens a lot, so we cache it in @owner_to_pool_manager.
262
+ # Retrieving the connection pool happens a lot, so we cache it in @connection_name_to_pool_manager.
233
263
  # This makes retrieving the connection pool O(1) once the process is warm.
234
264
  # When a connection is established or removed, we invalidate the cache.
235
- def retrieve_connection_pool(owner, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard)
236
- pool_config = get_pool_manager(owner)&.get_pool_config(role, shard)
265
+ def retrieve_connection_pool(connection_name, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard)
266
+ pool_config = get_pool_manager(connection_name)&.get_pool_config(role, shard)
237
267
  pool_config&.pool
238
268
  end
239
269
 
240
270
  private
241
- attr_reader :owner_to_pool_manager
271
+ attr_reader :connection_name_to_pool_manager
242
272
 
243
- # Returns the pool manager for an owner.
244
- def get_pool_manager(owner)
245
- owner_to_pool_manager[owner]
273
+ # Returns the pool manager for a connection name / identifier.
274
+ def get_pool_manager(connection_name)
275
+ connection_name_to_pool_manager[connection_name]
276
+ end
277
+
278
+ # Get the existing pool manager or initialize and assign a new one.
279
+ def set_pool_manager(connection_name)
280
+ connection_name_to_pool_manager[connection_name] ||= PoolManager.new
281
+ end
282
+
283
+ def pool_managers
284
+ connection_name_to_pool_manager.values
285
+ end
286
+
287
+ def deprecation_for_pool_handling(method)
288
+ roles = []
289
+ pool_managers.each do |pool_manager|
290
+ roles << pool_manager.role_names
291
+ end
292
+
293
+ if roles.flatten.uniq.count > 1
294
+ ActiveRecord.deprecator.warn(<<-MSG.squish)
295
+ `#{method}` currently only applies to connection pools in the current
296
+ role (`#{ActiveRecord::Base.current_role}`). In Rails 7.2, this method
297
+ will apply to all known pools, regardless of role. To affect only those
298
+ connections belonging to a specific role, pass the role name as an
299
+ argument. To switch to the new behavior, pass `:all` as the role name.
300
+ MSG
301
+ end
302
+ end
303
+
304
+ def disconnect_pool_from_pool_manager(pool_manager, role, shard)
305
+ pool_config = pool_manager.remove_pool_config(role, shard)
306
+
307
+ if pool_config
308
+ pool_config.disconnect!
309
+ pool_config.db_config
310
+ end
246
311
  end
247
312
 
248
313
  # Returns an instance of PoolConfig for a given adapter.
@@ -255,7 +320,7 @@ module ActiveRecord
255
320
  # pool_config.db_config.configuration_hash
256
321
  # # => { host: "localhost", database: "foo", adapter: "sqlite3" }
257
322
  #
258
- def resolve_pool_config(config, owner_name, role, shard)
323
+ def resolve_pool_config(config, connection_name, role, shard)
259
324
  db_config = Base.configurations.resolve(config)
260
325
 
261
326
  raise(AdapterNotSpecified, "database configuration does not specify adapter") unless db_config.adapter
@@ -285,7 +350,17 @@ module ActiveRecord
285
350
  raise AdapterNotFound, "database configuration specifies nonexistent #{db_config.adapter} adapter"
286
351
  end
287
352
 
288
- ConnectionAdapters::PoolConfig.new(owner_name, db_config, role, shard)
353
+ ConnectionAdapters::PoolConfig.new(connection_name, db_config, role, shard)
354
+ end
355
+
356
+ def determine_owner_name(owner_name, config)
357
+ if owner_name.is_a?(String) || owner_name.is_a?(Symbol)
358
+ StringConnectionName.new(owner_name.to_s)
359
+ elsif config.is_a?(Symbol)
360
+ StringConnectionName.new(config.to_s)
361
+ else
362
+ owner_name
363
+ end
289
364
  end
290
365
  end
291
366
  end
@@ -6,6 +6,8 @@ require "monitor"
6
6
  module ActiveRecord
7
7
  module ConnectionAdapters
8
8
  class ConnectionPool
9
+ # = Active Record Connection Pool \Queue
10
+ #
9
11
  # Threadsafe, fair, LIFO queue. Meant to be used by ConnectionPool
10
12
  # with which it shares a Monitor.
11
13
  class Queue
@@ -6,12 +6,14 @@ require "weakref"
6
6
  module ActiveRecord
7
7
  module ConnectionAdapters
8
8
  class ConnectionPool
9
+ # = Active Record Connection Pool \Reaper
10
+ #
9
11
  # Every +frequency+ seconds, the reaper will call +reap+ and +flush+ on
10
12
  # +pool+. A reaper instantiated with a zero frequency will never reap
11
13
  # the connection pool.
12
14
  #
13
15
  # Configure the frequency by setting +reaping_frequency+ in your database
14
- # yaml file (default 60 seconds).
16
+ # YAML file (default 60 seconds).
15
17
  class Reaper
16
18
  attr_reader :pool, :frequency
17
19
 
@@ -10,35 +10,33 @@ require "active_record/connection_adapters/abstract/connection_pool/reaper"
10
10
  module ActiveRecord
11
11
  module ConnectionAdapters
12
12
  module AbstractPool # :nodoc:
13
- def get_schema_cache(connection)
14
- self.schema_cache ||= SchemaCache.new(connection)
15
- schema_cache.connection = connection
16
- schema_cache
17
- end
18
-
19
- def set_schema_cache(cache)
20
- self.schema_cache = cache
21
- end
22
-
23
- def lazily_set_schema_cache
24
- return unless ActiveRecord.lazily_load_schema_cache
25
-
26
- cache = SchemaCache.load_from(db_config.lazy_schema_cache_path)
27
- set_schema_cache(cache)
28
- end
29
13
  end
30
14
 
31
15
  class NullPool # :nodoc:
32
16
  include ConnectionAdapters::AbstractPool
33
17
 
34
- attr_accessor :schema_cache
18
+ class NullConfig # :nodoc:
19
+ def method_missing(*)
20
+ nil
21
+ end
22
+ end
23
+ NULL_CONFIG = NullConfig.new # :nodoc:
24
+
25
+ def schema_reflection
26
+ SchemaReflection.new(nil)
27
+ end
35
28
 
36
29
  def connection_class; end
37
30
  def checkin(_); end
38
31
  def remove(_); end
39
32
  def async_executor; end
33
+ def db_config
34
+ NULL_CONFIG
35
+ end
40
36
  end
41
37
 
38
+ # = Active Record Connection Pool
39
+ #
42
40
  # Connection pool base class for managing Active Record database
43
41
  # connections.
44
42
  #
@@ -53,7 +51,7 @@ module ActiveRecord
53
51
  # handle cases in which there are more threads than connections: if all
54
52
  # connections have been checked out, and a thread tries to checkout a
55
53
  # connection anyway, then ConnectionPool will wait until some other thread
56
- # has checked in a connection.
54
+ # has checked in a connection, or the +checkout_timeout+ has expired.
57
55
  #
58
56
  # == Obtaining (checking out) a connection
59
57
  #
@@ -64,7 +62,7 @@ module ActiveRecord
64
62
  # as with Active Record 2.1 and
65
63
  # earlier (pre-connection-pooling). Eventually, when you're done with
66
64
  # the connection(s) and wish it to be returned to the pool, you call
67
- # {ActiveRecord::Base.clear_active_connections!}[rdoc-ref:ConnectionAdapters::ConnectionHandler#clear_active_connections!].
65
+ # {ActiveRecord::Base.connection_handler.clear_active_connections!}[rdoc-ref:ConnectionAdapters::ConnectionHandler#clear_active_connections!].
68
66
  # This will be the default behavior for Active Record when used in conjunction with
69
67
  # Action Pack's request handling cycle.
70
68
  # 2. Manually check out a connection from the pool with
@@ -78,6 +76,12 @@ module ActiveRecord
78
76
  # Connections in the pool are actually AbstractAdapter objects (or objects
79
77
  # compatible with AbstractAdapter's interface).
80
78
  #
79
+ # While a thread has a connection checked out from the pool using one of the
80
+ # above three methods, that connection will automatically be the one used
81
+ # by ActiveRecord queries executing on that thread. It is not required to
82
+ # explicitly pass the checked out connection to \Rails models or queries, for
83
+ # example.
84
+ #
81
85
  # == Options
82
86
  #
83
87
  # There are several connection-pooling-related options that you can add to
@@ -105,11 +109,9 @@ module ActiveRecord
105
109
  include ConnectionAdapters::AbstractPool
106
110
 
107
111
  attr_accessor :automatic_reconnect, :checkout_timeout
108
- attr_reader :db_config, :size, :reaper, :pool_config, :connection_class, :async_executor, :role, :shard
112
+ attr_reader :db_config, :size, :reaper, :pool_config, :async_executor, :role, :shard
109
113
 
110
- alias_method :connection_klass, :connection_class
111
- deprecate :connection_klass
112
- delegate :schema_cache, :schema_cache=, to: :pool_config
114
+ delegate :schema_reflection, :schema_reflection=, to: :pool_config
113
115
 
114
116
  # Creates a new ConnectionPool object. +pool_config+ is a PoolConfig
115
117
  # object which describes database connection information (e.g. adapter,
@@ -122,7 +124,6 @@ module ActiveRecord
122
124
 
123
125
  @pool_config = pool_config
124
126
  @db_config = pool_config.db_config
125
- @connection_class = pool_config.connection_class
126
127
  @role = pool_config.role
127
128
  @shard = pool_config.shard
128
129
 
@@ -158,18 +159,20 @@ module ActiveRecord
158
159
 
159
160
  @async_executor = build_async_executor
160
161
 
161
- lazily_set_schema_cache
162
-
163
162
  @reaper = Reaper.new(self, db_config.reaping_frequency)
164
163
  @reaper.run
165
164
  end
166
165
 
167
166
  def lock_thread=(lock_thread)
168
167
  if lock_thread
169
- @lock_thread = Thread.current
168
+ @lock_thread = ActiveSupport::IsolatedExecutionState.context
170
169
  else
171
170
  @lock_thread = nil
172
171
  end
172
+
173
+ if (active_connection = @thread_cached_conns[connection_cache_key(current_thread)])
174
+ active_connection.lock_thread = @lock_thread
175
+ end
173
176
  end
174
177
 
175
178
  # Retrieve the connection associated with the current thread, or call
@@ -181,6 +184,12 @@ module ActiveRecord
181
184
  @thread_cached_conns[connection_cache_key(current_thread)] ||= checkout
182
185
  end
183
186
 
187
+ def connection_class # :nodoc:
188
+ pool_config.connection_class
189
+ end
190
+ alias :connection_klass :connection_class
191
+ deprecate :connection_klass, deprecator: ActiveRecord.deprecator
192
+
184
193
  # Returns true if there is an open connection being used for the current thread.
185
194
  #
186
195
  # This method only works for connections that have been obtained through
@@ -197,18 +206,23 @@ module ActiveRecord
197
206
  # This method only works for connections that have been obtained through
198
207
  # #connection or #with_connection methods, connections obtained through
199
208
  # #checkout will not be automatically released.
200
- def release_connection(owner_thread = Thread.current)
209
+ def release_connection(owner_thread = ActiveSupport::IsolatedExecutionState.context)
201
210
  if conn = @thread_cached_conns.delete(connection_cache_key(owner_thread))
202
211
  checkin conn
203
212
  end
204
213
  end
205
214
 
206
- # If a connection obtained through #connection or #with_connection methods
207
- # already exists yield it to the block. If no such connection
208
- # exists checkout a connection, yield it to the block, and checkin the
209
- # connection when finished.
215
+ # Yields a connection from the connection pool to the block. If no connection
216
+ # is already checked out by the current thread, a connection will be checked
217
+ # out from the pool, yielded to the block, and then returned to the pool when
218
+ # the block is finished. If a connection has already been checked out on the
219
+ # current thread, such as via #connection or #with_connection, that existing
220
+ # connection will be the one yielded and it will not be returned to the pool
221
+ # automatically at the end of the block; it is expected that such an existing
222
+ # connection will be properly returned to the pool by the code that checked
223
+ # it out.
210
224
  def with_connection
211
- unless conn = @thread_cached_conns[connection_cache_key(Thread.current)]
225
+ unless conn = @thread_cached_conns[connection_cache_key(ActiveSupport::IsolatedExecutionState.context)]
212
226
  conn = connection
213
227
  fresh_connection = true
214
228
  end
@@ -338,7 +352,9 @@ module ActiveRecord
338
352
  # Raises:
339
353
  # - ActiveRecord::ConnectionTimeoutError no connection can be obtained from the pool.
340
354
  def checkout(checkout_timeout = @checkout_timeout)
341
- checkout_and_verify(acquire_connection(checkout_timeout))
355
+ connection = checkout_and_verify(acquire_connection(checkout_timeout))
356
+ connection.lock_thread = @lock_thread
357
+ connection
342
358
  end
343
359
 
344
360
  # Check-in a database connection back into the pool, indicating that you
@@ -355,6 +371,7 @@ module ActiveRecord
355
371
  conn.expire
356
372
  end
357
373
 
374
+ conn.lock_thread = nil
358
375
  @available.add conn
359
376
  end
360
377
  end
@@ -510,7 +527,7 @@ module ActiveRecord
510
527
  end
511
528
 
512
529
  def current_thread
513
- @lock_thread || Thread.current
530
+ @lock_thread || ActiveSupport::IsolatedExecutionState.context
514
531
  end
515
532
 
516
533
  # Take control of all existing connections so a "group" action such as
@@ -527,13 +544,13 @@ module ActiveRecord
527
544
  def attempt_to_checkout_all_existing_connections(raise_on_acquisition_timeout = true)
528
545
  collected_conns = synchronize do
529
546
  # account for our own connections
530
- @connections.select { |conn| conn.owner == Thread.current }
547
+ @connections.select { |conn| conn.owner == ActiveSupport::IsolatedExecutionState.context }
531
548
  end
532
549
 
533
550
  newly_checked_out = []
534
551
  timeout_time = Process.clock_gettime(Process::CLOCK_MONOTONIC) + (@checkout_timeout * 2)
535
552
 
536
- @available.with_a_bias_for(Thread.current) do
553
+ @available.with_a_bias_for(ActiveSupport::IsolatedExecutionState.context) do
537
554
  loop do
538
555
  synchronize do
539
556
  return if collected_conns.size == @connections.size && @now_connecting == 0
@@ -580,7 +597,7 @@ module ActiveRecord
580
597
 
581
598
  thread_report = []
582
599
  @connections.each do |conn|
583
- unless conn.owner == Thread.current
600
+ unless conn.owner == ActiveSupport::IsolatedExecutionState.context
584
601
  thread_report << "#{conn} is owned by #{conn.owner}"
585
602
  end
586
603
  end
@@ -653,9 +670,12 @@ module ActiveRecord
653
670
  alias_method :release, :remove_connection_from_thread_cache
654
671
 
655
672
  def new_connection
656
- Base.public_send(db_config.adapter_method, db_config.configuration_hash).tap do |conn|
657
- conn.check_version
658
- end
673
+ connection = Base.public_send(db_config.adapter_method, db_config.configuration_hash)
674
+ connection.pool = self
675
+ connection.check_version
676
+ connection
677
+ rescue ConnectionNotEstablished => ex
678
+ raise ex.set_pool(self)
659
679
  end
660
680
 
661
681
  # If the pool is not at a <tt>@size</tt> limit, establish new connection. Connecting
@@ -702,10 +722,10 @@ module ActiveRecord
702
722
 
703
723
  def checkout_and_verify(c)
704
724
  c._run_checkout_callbacks do
705
- c.verify!
725
+ c.clean!
706
726
  end
707
727
  c
708
- rescue
728
+ rescue Exception
709
729
  remove c
710
730
  c.disconnect!
711
731
  raise
@@ -7,6 +7,11 @@ module ActiveRecord
7
7
  64
8
8
  end
9
9
 
10
+ # Returns the maximum length of a table name.
11
+ def table_name_length
12
+ max_identifier_length
13
+ end
14
+
10
15
  # Returns the maximum length of a table alias.
11
16
  def table_alias_length
12
17
  max_identifier_length