activerecord 6.1.6 → 7.0.4

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 (243) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1314 -975
  3. data/README.rdoc +1 -1
  4. data/lib/active_record/aggregations.rb +1 -1
  5. data/lib/active_record/association_relation.rb +0 -10
  6. data/lib/active_record/associations/association.rb +33 -17
  7. data/lib/active_record/associations/association_scope.rb +1 -3
  8. data/lib/active_record/associations/belongs_to_association.rb +15 -4
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
  10. data/lib/active_record/associations/builder/association.rb +8 -2
  11. data/lib/active_record/associations/builder/belongs_to.rb +19 -6
  12. data/lib/active_record/associations/builder/collection_association.rb +10 -3
  13. data/lib/active_record/associations/builder/has_many.rb +3 -2
  14. data/lib/active_record/associations/builder/has_one.rb +2 -1
  15. data/lib/active_record/associations/builder/singular_association.rb +2 -2
  16. data/lib/active_record/associations/collection_association.rb +19 -21
  17. data/lib/active_record/associations/collection_proxy.rb +10 -5
  18. data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
  19. data/lib/active_record/associations/has_many_association.rb +8 -5
  20. data/lib/active_record/associations/has_many_through_association.rb +2 -1
  21. data/lib/active_record/associations/has_one_association.rb +10 -7
  22. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  23. data/lib/active_record/associations/join_dependency.rb +23 -15
  24. data/lib/active_record/associations/preloader/association.rb +186 -52
  25. data/lib/active_record/associations/preloader/batch.rb +48 -0
  26. data/lib/active_record/associations/preloader/branch.rb +147 -0
  27. data/lib/active_record/associations/preloader/through_association.rb +49 -13
  28. data/lib/active_record/associations/preloader.rb +39 -113
  29. data/lib/active_record/associations/singular_association.rb +8 -2
  30. data/lib/active_record/associations/through_association.rb +3 -3
  31. data/lib/active_record/associations.rb +124 -95
  32. data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
  33. data/lib/active_record/attribute_assignment.rb +1 -1
  34. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  35. data/lib/active_record/attribute_methods/dirty.rb +49 -16
  36. data/lib/active_record/attribute_methods/primary_key.rb +2 -2
  37. data/lib/active_record/attribute_methods/query.rb +2 -2
  38. data/lib/active_record/attribute_methods/read.rb +7 -5
  39. data/lib/active_record/attribute_methods/serialization.rb +57 -19
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +8 -3
  41. data/lib/active_record/attribute_methods/write.rb +7 -10
  42. data/lib/active_record/attribute_methods.rb +14 -15
  43. data/lib/active_record/attributes.rb +24 -35
  44. data/lib/active_record/autosave_association.rb +8 -23
  45. data/lib/active_record/base.rb +19 -1
  46. data/lib/active_record/callbacks.rb +2 -2
  47. data/lib/active_record/coders/yaml_column.rb +10 -2
  48. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +292 -0
  49. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +209 -0
  50. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +76 -0
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +47 -561
  52. data/lib/active_record/connection_adapters/abstract/database_limits.rb +0 -17
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +46 -22
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +24 -12
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +42 -72
  56. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -17
  57. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +38 -13
  58. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +80 -24
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +149 -74
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +105 -81
  63. data/lib/active_record/connection_adapters/column.rb +4 -0
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +36 -24
  65. data/lib/active_record/connection_adapters/mysql/quoting.rb +37 -21
  66. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -1
  67. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +20 -1
  68. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
  69. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  70. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -1
  71. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +19 -12
  72. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  75. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  76. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  79. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  80. data/lib/active_record/connection_adapters/postgresql/quoting.rb +51 -51
  81. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -0
  82. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
  83. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
  84. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
  85. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +37 -19
  86. data/lib/active_record/connection_adapters/postgresql_adapter.rb +208 -107
  87. data/lib/active_record/connection_adapters/schema_cache.rb +39 -38
  88. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +25 -19
  89. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +28 -16
  90. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +17 -15
  91. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +96 -32
  92. data/lib/active_record/connection_adapters.rb +6 -5
  93. data/lib/active_record/connection_handling.rb +49 -55
  94. data/lib/active_record/core.rb +124 -134
  95. data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
  96. data/lib/active_record/database_configurations/database_config.rb +12 -9
  97. data/lib/active_record/database_configurations/hash_config.rb +63 -5
  98. data/lib/active_record/database_configurations/url_config.rb +2 -2
  99. data/lib/active_record/database_configurations.rb +15 -32
  100. data/lib/active_record/delegated_type.rb +53 -12
  101. data/lib/active_record/destroy_association_async_job.rb +1 -1
  102. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  103. data/lib/active_record/dynamic_matchers.rb +1 -1
  104. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  105. data/lib/active_record/encryption/cipher.rb +53 -0
  106. data/lib/active_record/encryption/config.rb +44 -0
  107. data/lib/active_record/encryption/configurable.rb +67 -0
  108. data/lib/active_record/encryption/context.rb +35 -0
  109. data/lib/active_record/encryption/contexts.rb +72 -0
  110. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  111. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  112. data/lib/active_record/encryption/encryptable_record.rb +206 -0
  113. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  114. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  115. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  116. data/lib/active_record/encryption/encryptor.rb +155 -0
  117. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  118. data/lib/active_record/encryption/errors.rb +15 -0
  119. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  120. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  121. data/lib/active_record/encryption/key.rb +28 -0
  122. data/lib/active_record/encryption/key_generator.rb +42 -0
  123. data/lib/active_record/encryption/key_provider.rb +46 -0
  124. data/lib/active_record/encryption/message.rb +33 -0
  125. data/lib/active_record/encryption/message_serializer.rb +90 -0
  126. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  127. data/lib/active_record/encryption/properties.rb +76 -0
  128. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  129. data/lib/active_record/encryption/scheme.rb +99 -0
  130. data/lib/active_record/encryption.rb +55 -0
  131. data/lib/active_record/enum.rb +50 -43
  132. data/lib/active_record/errors.rb +67 -4
  133. data/lib/active_record/explain_registry.rb +11 -6
  134. data/lib/active_record/fixture_set/file.rb +15 -1
  135. data/lib/active_record/fixture_set/table_row.rb +41 -6
  136. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  137. data/lib/active_record/fixtures.rb +20 -23
  138. data/lib/active_record/future_result.rb +139 -0
  139. data/lib/active_record/gem_version.rb +4 -4
  140. data/lib/active_record/inheritance.rb +55 -17
  141. data/lib/active_record/insert_all.rb +80 -14
  142. data/lib/active_record/integration.rb +4 -3
  143. data/lib/active_record/internal_metadata.rb +1 -5
  144. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  145. data/lib/active_record/locking/optimistic.rb +10 -9
  146. data/lib/active_record/locking/pessimistic.rb +10 -4
  147. data/lib/active_record/log_subscriber.rb +23 -7
  148. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  149. data/lib/active_record/middleware/database_selector.rb +18 -6
  150. data/lib/active_record/middleware/shard_selector.rb +60 -0
  151. data/lib/active_record/migration/command_recorder.rb +7 -7
  152. data/lib/active_record/migration/compatibility.rb +84 -2
  153. data/lib/active_record/migration/join_table.rb +1 -1
  154. data/lib/active_record/migration.rb +114 -83
  155. data/lib/active_record/model_schema.rb +58 -59
  156. data/lib/active_record/nested_attributes.rb +13 -12
  157. data/lib/active_record/no_touching.rb +3 -3
  158. data/lib/active_record/null_relation.rb +2 -6
  159. data/lib/active_record/persistence.rb +228 -60
  160. data/lib/active_record/query_cache.rb +2 -2
  161. data/lib/active_record/query_logs.rb +138 -0
  162. data/lib/active_record/querying.rb +16 -6
  163. data/lib/active_record/railtie.rb +136 -22
  164. data/lib/active_record/railties/controller_runtime.rb +1 -1
  165. data/lib/active_record/railties/databases.rake +78 -136
  166. data/lib/active_record/readonly_attributes.rb +11 -0
  167. data/lib/active_record/reflection.rb +73 -50
  168. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  169. data/lib/active_record/relation/batches.rb +6 -6
  170. data/lib/active_record/relation/calculations.rb +43 -38
  171. data/lib/active_record/relation/delegation.rb +7 -7
  172. data/lib/active_record/relation/finder_methods.rb +31 -35
  173. data/lib/active_record/relation/merger.rb +20 -13
  174. data/lib/active_record/relation/predicate_builder.rb +1 -6
  175. data/lib/active_record/relation/query_attribute.rb +5 -11
  176. data/lib/active_record/relation/query_methods.rb +276 -67
  177. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  178. data/lib/active_record/relation/spawn_methods.rb +2 -2
  179. data/lib/active_record/relation/where_clause.rb +10 -19
  180. data/lib/active_record/relation.rb +189 -88
  181. data/lib/active_record/result.rb +17 -7
  182. data/lib/active_record/runtime_registry.rb +9 -13
  183. data/lib/active_record/sanitization.rb +17 -12
  184. data/lib/active_record/schema.rb +38 -23
  185. data/lib/active_record/schema_dumper.rb +25 -19
  186. data/lib/active_record/schema_migration.rb +4 -4
  187. data/lib/active_record/scoping/default.rb +60 -13
  188. data/lib/active_record/scoping/named.rb +3 -11
  189. data/lib/active_record/scoping.rb +64 -34
  190. data/lib/active_record/serialization.rb +6 -1
  191. data/lib/active_record/signed_id.rb +3 -3
  192. data/lib/active_record/store.rb +7 -2
  193. data/lib/active_record/suppressor.rb +11 -15
  194. data/lib/active_record/tasks/database_tasks.rb +127 -60
  195. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  196. data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -13
  197. data/lib/active_record/test_databases.rb +1 -1
  198. data/lib/active_record/test_fixtures.rb +16 -9
  199. data/lib/active_record/timestamp.rb +3 -4
  200. data/lib/active_record/transactions.rb +9 -14
  201. data/lib/active_record/translation.rb +3 -3
  202. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  203. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  204. data/lib/active_record/type/internal/timezone.rb +2 -2
  205. data/lib/active_record/type/serialized.rb +1 -1
  206. data/lib/active_record/type/type_map.rb +17 -20
  207. data/lib/active_record/type.rb +1 -2
  208. data/lib/active_record/validations/associated.rb +4 -4
  209. data/lib/active_record/validations/presence.rb +2 -2
  210. data/lib/active_record/validations/uniqueness.rb +4 -4
  211. data/lib/active_record/version.rb +1 -1
  212. data/lib/active_record.rb +217 -27
  213. data/lib/arel/attributes/attribute.rb +0 -8
  214. data/lib/arel/crud.rb +28 -22
  215. data/lib/arel/delete_manager.rb +18 -4
  216. data/lib/arel/filter_predications.rb +9 -0
  217. data/lib/arel/insert_manager.rb +2 -3
  218. data/lib/arel/nodes/casted.rb +1 -1
  219. data/lib/arel/nodes/delete_statement.rb +12 -13
  220. data/lib/arel/nodes/filter.rb +10 -0
  221. data/lib/arel/nodes/function.rb +1 -0
  222. data/lib/arel/nodes/insert_statement.rb +2 -2
  223. data/lib/arel/nodes/select_core.rb +2 -2
  224. data/lib/arel/nodes/select_statement.rb +2 -2
  225. data/lib/arel/nodes/update_statement.rb +8 -3
  226. data/lib/arel/nodes.rb +1 -0
  227. data/lib/arel/predications.rb +11 -3
  228. data/lib/arel/select_manager.rb +10 -4
  229. data/lib/arel/table.rb +0 -1
  230. data/lib/arel/tree_manager.rb +0 -12
  231. data/lib/arel/update_manager.rb +18 -4
  232. data/lib/arel/visitors/dot.rb +80 -90
  233. data/lib/arel/visitors/mysql.rb +8 -2
  234. data/lib/arel/visitors/postgresql.rb +0 -10
  235. data/lib/arel/visitors/to_sql.rb +58 -2
  236. data/lib/arel.rb +2 -1
  237. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  238. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  239. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  240. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  241. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  242. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  243. metadata +55 -11
@@ -44,7 +44,7 @@ module ActiveRecord
44
44
  #
45
45
  # ActiveRecord::Base.establish_connection(:production)
46
46
  #
47
- # The exceptions AdapterNotSpecified, AdapterNotFound and +ArgumentError+
47
+ # The exceptions AdapterNotSpecified, AdapterNotFound, and +ArgumentError+
48
48
  # may be returned on an error.
49
49
  def establish_connection(config_or_env = nil)
50
50
  config_or_env ||= DEFAULT_ENV.call.to_sym
@@ -108,7 +108,7 @@ module ActiveRecord
108
108
  connections
109
109
  end
110
110
 
111
- # Connects to a role (ex writing, reading or a custom role) and/or
111
+ # Connects to a role (e.g. writing, reading, or a custom role) and/or
112
112
  # shard for the duration of the block. At the end of the block the
113
113
  # connection will be returned to the original role / shard.
114
114
  #
@@ -134,10 +134,8 @@ module ActiveRecord
134
134
  # ActiveRecord::Base.connected_to(role: :reading, shard: :shard_one_replica) do
135
135
  # Dog.first # finds first Dog record stored on the shard one replica
136
136
  # end
137
- #
138
- # The database kwarg is deprecated and will be removed in Rails 7.0.0 without replacement.
139
- def connected_to(database: nil, role: nil, shard: nil, prevent_writes: false, &blk)
140
- if legacy_connection_handling
137
+ def connected_to(role: nil, shard: nil, prevent_writes: false, &blk)
138
+ if ActiveRecord.legacy_connection_handling
141
139
  if self != Base
142
140
  raise NotImplementedError, "`connected_to` can only be called on ActiveRecord::Base with legacy connection handling."
143
141
  end
@@ -151,31 +149,11 @@ module ActiveRecord
151
149
  end
152
150
  end
153
151
 
154
- if database && (role || shard)
155
- raise ArgumentError, "`connected_to` cannot accept a `database` argument with any other arguments."
156
- elsif database
157
- ActiveSupport::Deprecation.warn("The database key in `connected_to` is deprecated. It will be removed in Rails 7.0.0 without replacement.")
158
-
159
- if database.is_a?(Hash)
160
- role, database = database.first
161
- role = role.to_sym
162
- end
163
-
164
- db_config, owner_name = resolve_config_for_connection(database)
165
- handler = lookup_connection_handler(role)
166
-
167
- handler.establish_connection(db_config, owner_name: owner_name, role: role)
168
-
169
- with_handler(role, &blk)
170
- elsif role || shard
171
- unless role
172
- raise ArgumentError, "`connected_to` cannot accept a `shard` argument without a `role`."
173
- end
174
-
175
- with_role_and_shard(role, shard, prevent_writes, &blk)
176
- else
152
+ unless role || shard
177
153
  raise ArgumentError, "must provide a `shard` and/or `role`."
178
154
  end
155
+
156
+ with_role_and_shard(role, shard, prevent_writes, &blk)
179
157
  end
180
158
 
181
159
  # Connects a role and/or shard to the provided connection names. Optionally +prevent_writes+
@@ -194,7 +172,7 @@ module ActiveRecord
194
172
  def connected_to_many(*classes, role:, shard: nil, prevent_writes: false)
195
173
  classes = classes.flatten
196
174
 
197
- if legacy_connection_handling
175
+ if ActiveRecord.legacy_connection_handling
198
176
  raise NotImplementedError, "connected_to_many is not available with legacy connection handling"
199
177
  end
200
178
 
@@ -202,9 +180,9 @@ module ActiveRecord
202
180
  raise NotImplementedError, "connected_to_many can only be called on ActiveRecord::Base."
203
181
  end
204
182
 
205
- prevent_writes = true if role == reading_role
183
+ prevent_writes = true if role == ActiveRecord.reading_role
206
184
 
207
- connected_to_stack << { role: role, shard: shard, prevent_writes: prevent_writes, klasses: classes }
185
+ append_to_connected_to_stack(role: role, shard: shard, prevent_writes: prevent_writes, klasses: classes)
208
186
  yield
209
187
  ensure
210
188
  connected_to_stack.pop
@@ -218,13 +196,32 @@ module ActiveRecord
218
196
  # It is not recommended to use this method in a request since it
219
197
  # does not yield to a block like +connected_to+.
220
198
  def connecting_to(role: default_role, shard: default_shard, prevent_writes: false)
221
- if legacy_connection_handling
199
+ if ActiveRecord.legacy_connection_handling
222
200
  raise NotImplementedError, "`connecting_to` is not available with `legacy_connection_handling`."
223
201
  end
224
202
 
225
- prevent_writes = true if role == reading_role
203
+ prevent_writes = true if role == ActiveRecord.reading_role
226
204
 
227
- self.connected_to_stack << { role: role, shard: shard, prevent_writes: prevent_writes, klasses: [self] }
205
+ append_to_connected_to_stack(role: role, shard: shard, prevent_writes: prevent_writes, klasses: [self])
206
+ end
207
+
208
+ # Prohibit swapping shards while inside of the passed block.
209
+ #
210
+ # In some cases you may want to be able to swap shards but not allow a
211
+ # nested call to connected_to or connected_to_many to swap again. This
212
+ # is useful in cases you're using sharding to provide per-request
213
+ # database isolation.
214
+ def prohibit_shard_swapping(enabled = true)
215
+ prev_value = ActiveSupport::IsolatedExecutionState[:active_record_prohibit_shard_swapping]
216
+ ActiveSupport::IsolatedExecutionState[:active_record_prohibit_shard_swapping] = enabled
217
+ yield
218
+ ensure
219
+ ActiveSupport::IsolatedExecutionState[:active_record_prohibit_shard_swapping] = prev_value
220
+ end
221
+
222
+ # Determine whether or not shard swapping is currently prohibited
223
+ def shard_swapping_prohibited?
224
+ ActiveSupport::IsolatedExecutionState[:active_record_prohibit_shard_swapping]
228
225
  end
229
226
 
230
227
  # Prevent writing to the database regardless of role.
@@ -239,7 +236,7 @@ module ActiveRecord
239
236
  # See +READ_QUERY+ for the queries that are blocked by this
240
237
  # method.
241
238
  def while_preventing_writes(enabled = true, &block)
242
- if legacy_connection_handling
239
+ if ActiveRecord.legacy_connection_handling
243
240
  connection_handler.while_preventing_writes(enabled, &block)
244
241
  else
245
242
  connected_to(role: current_role, prevent_writes: enabled, &block)
@@ -257,8 +254,8 @@ module ActiveRecord
257
254
  end
258
255
 
259
256
  def lookup_connection_handler(handler_key) # :nodoc:
260
- if ActiveRecord::Base.legacy_connection_handling
261
- handler_key ||= ActiveRecord::Base.writing_role
257
+ if ActiveRecord.legacy_connection_handling
258
+ handler_key ||= ActiveRecord.writing_role
262
259
  connection_handlers[handler_key] ||= ActiveRecord::ConnectionAdapters::ConnectionHandler.new
263
260
  else
264
261
  ActiveRecord::Base.connection_handler
@@ -267,7 +264,7 @@ module ActiveRecord
267
264
 
268
265
  # Clears the query cache for all connections associated with the current thread.
269
266
  def clear_query_caches_for_current_thread
270
- if ActiveRecord::Base.legacy_connection_handling
267
+ if ActiveRecord.legacy_connection_handling
271
268
  ActiveRecord::Base.connection_handlers.each_value do |handler|
272
269
  clear_on_handler(handler)
273
270
  end
@@ -294,19 +291,8 @@ module ActiveRecord
294
291
  end
295
292
 
296
293
  def primary_class? # :nodoc:
297
- self == Base || defined?(ApplicationRecord) && self == ApplicationRecord
298
- end
299
-
300
- # Returns the configuration of the associated connection as a hash:
301
- #
302
- # ActiveRecord::Base.connection_config
303
- # # => {pool: 5, timeout: 5000, database: "db/development.sqlite3", adapter: "sqlite3"}
304
- #
305
- # Please use only for reading.
306
- def connection_config
307
- connection_pool.db_config.configuration_hash
294
+ self == Base || application_record_class?
308
295
  end
309
- deprecate connection_config: "Use connection_db_config instead"
310
296
 
311
297
  # Returns the db_config object from the associated connection:
312
298
  #
@@ -374,17 +360,17 @@ module ActiveRecord
374
360
  end
375
361
 
376
362
  def with_role_and_shard(role, shard, prevent_writes)
377
- prevent_writes = true if role == reading_role
363
+ prevent_writes = true if role == ActiveRecord.reading_role
378
364
 
379
- if ActiveRecord::Base.legacy_connection_handling
365
+ if ActiveRecord.legacy_connection_handling
380
366
  with_handler(role.to_sym) do
381
367
  connection_handler.while_preventing_writes(prevent_writes) do
382
- self.connected_to_stack << { shard: shard, klasses: [self] }
368
+ append_to_connected_to_stack(shard: shard, klasses: [self])
383
369
  yield
384
370
  end
385
371
  end
386
372
  else
387
- self.connected_to_stack << { role: role, shard: shard, prevent_writes: prevent_writes, klasses: [self] }
373
+ append_to_connected_to_stack(role: role, shard: shard, prevent_writes: prevent_writes, klasses: [self])
388
374
  return_value = yield
389
375
  return_value.load if return_value.is_a? ActiveRecord::Relation
390
376
  return_value
@@ -393,6 +379,14 @@ module ActiveRecord
393
379
  self.connected_to_stack.pop
394
380
  end
395
381
 
382
+ def append_to_connected_to_stack(entry)
383
+ if shard_swapping_prohibited? && entry[:shard].present?
384
+ raise ArgumentError, "cannot swap `shard` while shard swapping is prohibited."
385
+ end
386
+
387
+ connected_to_stack << entry
388
+ end
389
+
396
390
  def swap_connection_handler(handler, &blk) # :nodoc:
397
391
  old_handler, ActiveRecord::Base.connection_handler = ActiveRecord::Base.connection_handler, handler
398
392
  return_value = yield
@@ -17,20 +17,7 @@ module ActiveRecord
17
17
  # Accepts a logger conforming to the interface of Log4r which is then
18
18
  # passed on to any new database connections made and which can be
19
19
  # retrieved on both a class and instance level by calling +logger+.
20
- mattr_accessor :logger, instance_writer: false
21
-
22
- ##
23
- # :singleton-method:
24
- #
25
- # Specifies if the methods calling database queries should be logged below
26
- # their relevant queries. Defaults to false.
27
- mattr_accessor :verbose_query_logs, instance_writer: false, default: false
28
-
29
- ##
30
- # :singleton-method:
31
- #
32
- # Specifies the names of the queues used by background jobs.
33
- mattr_accessor :queues, instance_accessor: false, default: {}
20
+ class_attribute :logger, instance_writer: false
34
21
 
35
22
  ##
36
23
  # :singleton-method:
@@ -72,80 +59,17 @@ module ActiveRecord
72
59
 
73
60
  ##
74
61
  # :singleton-method:
75
- # Determines whether to use Time.utc (using :utc) or Time.local (using :local) when pulling
76
- # dates and times from the database. This is set to :utc by default.
77
- mattr_accessor :default_timezone, instance_writer: false, default: :utc
78
-
79
- ##
80
- # :singleton-method:
81
- # Specifies the format to use when dumping the database schema with Rails'
82
- # Rakefile. If :sql, the schema is dumped as (potentially database-
83
- # specific) SQL statements. If :ruby, the schema is dumped as an
84
- # ActiveRecord::Schema file which can be loaded into any database that
85
- # supports migrations. Use :ruby if you want to have different database
86
- # adapters for, e.g., your development and test environments.
87
- mattr_accessor :schema_format, instance_writer: false, default: :ruby
88
-
89
- ##
90
- # :singleton-method:
91
- # Specifies if an error should be raised if the query has an order being
92
- # ignored when doing batch queries. Useful in applications where the
93
- # scope being ignored is error-worthy, rather than a warning.
94
- mattr_accessor :error_on_ignored_order, instance_writer: false, default: false
95
-
96
- ##
97
- # :singleton-method:
98
- # Specify whether or not to use timestamps for migration versions
99
- mattr_accessor :timestamped_migrations, instance_writer: false, default: true
100
-
101
- ##
102
- # :singleton-method:
103
- # Specify whether schema dump should happen at the end of the
104
- # db:migrate rails command. This is true by default, which is useful for the
105
- # development environment. This should ideally be false in the production
106
- # environment where dumping schema is rarely needed.
107
- mattr_accessor :dump_schema_after_migration, instance_writer: false, default: true
108
-
109
- ##
110
- # :singleton-method:
111
- # Specifies which database schemas to dump when calling db:schema:dump.
112
- # If the value is :schema_search_path (the default), any schemas listed in
113
- # schema_search_path are dumped. Use :all to dump all schemas regardless
114
- # of schema_search_path, or a string of comma separated schemas for a
115
- # custom list.
116
- mattr_accessor :dump_schemas, instance_writer: false, default: :schema_search_path
117
-
118
- ##
119
- # :singleton-method:
120
- # Specify a threshold for the size of query result sets. If the number of
121
- # records in the set exceeds the threshold, a warning is logged. This can
122
- # be used to identify queries which load thousands of records and
123
- # potentially cause memory bloat.
124
- mattr_accessor :warn_on_records_fetched_greater_than, instance_writer: false
125
-
126
- ##
127
- # :singleton-method:
128
- # Show a warning when Rails couldn't parse your database.yml
129
- # for multiple databases.
130
- mattr_accessor :suppress_multiple_database_warning, instance_writer: false, default: false
131
-
132
- mattr_accessor :maintain_test_schema, instance_accessor: false
62
+ # Force enumeration of all columns in SELECT statements.
63
+ # e.g. <tt>SELECT first_name, last_name FROM ...</tt> instead of <tt>SELECT * FROM ...</tt>
64
+ # This avoids +PreparedStatementCacheExpired+ errors when a column is added
65
+ # to the database while the app is running.
66
+ class_attribute :enumerate_columns_in_select_statements, instance_accessor: false, default: false
133
67
 
134
68
  class_attribute :belongs_to_required_by_default, instance_accessor: false
135
69
 
136
- ##
137
- # :singleton-method:
138
- # Set the application to log or raise when an association violates strict loading.
139
- # Defaults to :raise.
140
- mattr_accessor :action_on_strict_loading_violation, instance_accessor: false, default: :raise
141
-
142
70
  class_attribute :strict_loading_by_default, instance_accessor: false, default: false
143
71
 
144
- mattr_accessor :writing_role, instance_accessor: false, default: :writing
145
-
146
- mattr_accessor :reading_role, instance_accessor: false, default: :reading
147
-
148
- mattr_accessor :has_many_inversing, instance_accessor: false, default: false
72
+ class_attribute :has_many_inversing, instance_accessor: false, default: false
149
73
 
150
74
  class_attribute :default_connection_handler, instance_writer: false
151
75
 
@@ -153,20 +77,31 @@ module ActiveRecord
153
77
 
154
78
  class_attribute :default_shard, instance_writer: false
155
79
 
156
- mattr_accessor :legacy_connection_handling, instance_writer: false, default: true
80
+ class_attribute :shard_selector, instance_accessor: false, default: nil
81
+
82
+ def self.application_record_class? # :nodoc:
83
+ if ActiveRecord.application_record_class
84
+ self == ActiveRecord.application_record_class
85
+ else
86
+ if defined?(ApplicationRecord) && self == ApplicationRecord
87
+ true
88
+ end
89
+ end
90
+ end
157
91
 
158
92
  self.filter_attributes = []
159
93
 
160
94
  def self.connection_handler
161
- Thread.current.thread_variable_get(:ar_connection_handler) || default_connection_handler
95
+ ActiveSupport::IsolatedExecutionState[:active_record_connection_handler] || default_connection_handler
162
96
  end
163
97
 
164
98
  def self.connection_handler=(handler)
165
- Thread.current.thread_variable_set(:ar_connection_handler, handler)
99
+ ActiveSupport::IsolatedExecutionState[:active_record_connection_handler] = handler
166
100
  end
167
101
 
168
102
  def self.connection_handlers
169
- unless legacy_connection_handling
103
+ if ActiveRecord.legacy_connection_handling
104
+ else
170
105
  raise NotImplementedError, "The new connection handling does not support accessing multiple connection handlers."
171
106
  end
172
107
 
@@ -174,13 +109,32 @@ module ActiveRecord
174
109
  end
175
110
 
176
111
  def self.connection_handlers=(handlers)
177
- unless legacy_connection_handling
178
- raise NotImplementedError, "The new connection handling does not setting support multiple connection handlers."
112
+ if ActiveRecord.legacy_connection_handling
113
+ ActiveSupport::Deprecation.warn(<<~MSG)
114
+ Using legacy connection handling is deprecated. Please set
115
+ `legacy_connection_handling` to `false` in your application.
116
+
117
+ The new connection handling does not support `connection_handlers`
118
+ getter and setter.
119
+
120
+ Read more about how to migrate at: https://guides.rubyonrails.org/active_record_multiple_databases.html#migrate-to-the-new-connection-handling
121
+ MSG
122
+ else
123
+ raise NotImplementedError, "The new connection handling does not support multiple connection handlers."
179
124
  end
180
125
 
181
126
  @@connection_handlers = handlers
182
127
  end
183
128
 
129
+ def self.asynchronous_queries_session # :nodoc:
130
+ asynchronous_queries_tracker.current_session
131
+ end
132
+
133
+ def self.asynchronous_queries_tracker # :nodoc:
134
+ ActiveSupport::IsolatedExecutionState[:active_record_asynchronous_queries_tracker] ||= \
135
+ AsynchronousQueriesTracker.new
136
+ end
137
+
184
138
  # Returns the symbol representing the current connected role.
185
139
  #
186
140
  # ActiveRecord::Base.connected_to(role: :writing) do
@@ -191,12 +145,12 @@ module ActiveRecord
191
145
  # ActiveRecord::Base.current_role #=> :reading
192
146
  # end
193
147
  def self.current_role
194
- if ActiveRecord::Base.legacy_connection_handling
148
+ if ActiveRecord.legacy_connection_handling
195
149
  connection_handlers.key(connection_handler) || default_role
196
150
  else
197
151
  connected_to_stack.reverse_each do |hash|
198
152
  return hash[:role] if hash[:role] && hash[:klasses].include?(Base)
199
- return hash[:role] if hash[:role] && hash[:klasses].include?(connection_classes)
153
+ return hash[:role] if hash[:role] && hash[:klasses].include?(connection_class_for_self)
200
154
  end
201
155
 
202
156
  default_role
@@ -215,7 +169,7 @@ module ActiveRecord
215
169
  def self.current_shard
216
170
  connected_to_stack.reverse_each do |hash|
217
171
  return hash[:shard] if hash[:shard] && hash[:klasses].include?(Base)
218
- return hash[:shard] if hash[:shard] && hash[:klasses].include?(connection_classes)
172
+ return hash[:shard] if hash[:shard] && hash[:klasses].include?(connection_class_for_self)
219
173
  end
220
174
 
221
175
  default_shard
@@ -232,12 +186,12 @@ module ActiveRecord
232
186
  # ActiveRecord::Base.current_preventing_writes #=> false
233
187
  # end
234
188
  def self.current_preventing_writes
235
- if legacy_connection_handling
189
+ if ActiveRecord.legacy_connection_handling
236
190
  connection_handler.prevent_writes
237
191
  else
238
192
  connected_to_stack.reverse_each do |hash|
239
193
  return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(Base)
240
- return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(connection_classes)
194
+ return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(connection_class_for_self)
241
195
  end
242
196
 
243
197
  false
@@ -245,11 +199,11 @@ module ActiveRecord
245
199
  end
246
200
 
247
201
  def self.connected_to_stack # :nodoc:
248
- if connected_to_stack = Thread.current.thread_variable_get(:ar_connected_to_stack)
202
+ if connected_to_stack = ActiveSupport::IsolatedExecutionState[:active_record_connected_to_stack]
249
203
  connected_to_stack
250
204
  else
251
205
  connected_to_stack = Concurrent::Array.new
252
- Thread.current.thread_variable_set(:ar_connected_to_stack, connected_to_stack)
206
+ ActiveSupport::IsolatedExecutionState[:active_record_connected_to_stack] = connected_to_stack
253
207
  connected_to_stack
254
208
  end
255
209
  end
@@ -258,7 +212,7 @@ module ActiveRecord
258
212
  @connection_class = b
259
213
  end
260
214
 
261
- def self.connection_class # :nodoc
215
+ def self.connection_class # :nodoc:
262
216
  @connection_class ||= false
263
217
  end
264
218
 
@@ -266,7 +220,7 @@ module ActiveRecord
266
220
  self.connection_class
267
221
  end
268
222
 
269
- def self.connection_classes # :nodoc:
223
+ def self.connection_class_for_self # :nodoc:
270
224
  klass = self
271
225
 
272
226
  until klass == Base
@@ -277,22 +231,14 @@ module ActiveRecord
277
231
  klass
278
232
  end
279
233
 
280
- def self.allow_unsafe_raw_sql # :nodoc:
281
- ActiveSupport::Deprecation.warn("ActiveRecord::Base.allow_unsafe_raw_sql is deprecated and will be removed in Rails 7.0")
282
- end
283
-
284
- def self.allow_unsafe_raw_sql=(value) # :nodoc:
285
- ActiveSupport::Deprecation.warn("ActiveRecord::Base.allow_unsafe_raw_sql= is deprecated and will be removed in Rails 7.0")
286
- end
287
-
288
234
  self.default_connection_handler = ConnectionAdapters::ConnectionHandler.new
289
- self.default_role = writing_role
235
+ self.default_role = ActiveRecord.writing_role
290
236
  self.default_shard = :default
291
237
 
292
238
  def self.strict_loading_violation!(owner:, reflection:) # :nodoc:
293
- case action_on_strict_loading_violation
239
+ case ActiveRecord.action_on_strict_loading_violation
294
240
  when :raise
295
- message = "`#{owner}` is marked for strict_loading. The `#{reflection.klass}` association named `:#{reflection.name}` cannot be lazily loaded."
241
+ message = reflection.strict_loading_violation_message(owner)
296
242
  raise ActiveRecord::StrictLoadingViolationError.new(message)
297
243
  when :log
298
244
  name = "strict_loading_violation.active_record"
@@ -381,7 +327,32 @@ module ActiveRecord
381
327
  end
382
328
 
383
329
  def find_by!(*args) # :nodoc:
384
- find_by(*args) || raise(RecordNotFound.new("Couldn't find #{name}", name))
330
+ find_by(*args) || where(*args).raise_record_not_found_exception!
331
+ end
332
+
333
+ %w(
334
+ reading_role writing_role legacy_connection_handling default_timezone index_nested_attribute_errors
335
+ verbose_query_logs queues warn_on_records_fetched_greater_than maintain_test_schema
336
+ application_record_class action_on_strict_loading_violation schema_format error_on_ignored_order
337
+ timestamped_migrations dump_schema_after_migration dump_schemas suppress_multiple_database_warning
338
+ ).each do |attr|
339
+ module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
340
+ def #{attr}
341
+ ActiveSupport::Deprecation.warn(<<~MSG)
342
+ ActiveRecord::Base.#{attr} is deprecated and will be removed in Rails 7.1.
343
+ Use `ActiveRecord.#{attr}` instead.
344
+ MSG
345
+ ActiveRecord.#{attr}
346
+ end
347
+
348
+ def #{attr}=(value)
349
+ ActiveSupport::Deprecation.warn(<<~MSG)
350
+ ActiveRecord::Base.#{attr}= is deprecated and will be removed in Rails 7.1.
351
+ Use `ActiveRecord.#{attr}=` instead.
352
+ MSG
353
+ ActiveRecord.#{attr} = value
354
+ end
355
+ RUBY
385
356
  end
386
357
 
387
358
  def initialize_generated_modules # :nodoc:
@@ -440,25 +411,16 @@ module ActiveRecord
440
411
  end
441
412
  end
442
413
 
443
- # Overwrite the default class equality method to provide support for decorated models.
414
+ # Override the default class equality method to provide support for decorated models.
444
415
  def ===(object) # :nodoc:
445
416
  object.is_a?(self)
446
417
  end
447
418
 
448
419
  # Returns an instance of <tt>Arel::Table</tt> loaded with the current table name.
449
- #
450
- # class Post < ActiveRecord::Base
451
- # scope :published_and_commented, -> { published.and(arel_table[:comments_count].gt(0)) }
452
- # end
453
420
  def arel_table # :nodoc:
454
421
  @arel_table ||= Arel::Table.new(table_name, klass: self)
455
422
  end
456
423
 
457
- def arel_attribute(name, table = arel_table) # :nodoc:
458
- table[name]
459
- end
460
- deprecate :arel_attribute
461
-
462
424
  def predicate_builder # :nodoc:
463
425
  @predicate_builder ||= PredicateBuilder.new(table_metadata)
464
426
  end
@@ -467,10 +429,6 @@ module ActiveRecord
467
429
  TypeCaster::Map.new(self)
468
430
  end
469
431
 
470
- def _internal? # :nodoc:
471
- false
472
- end
473
-
474
432
  def cached_find_by_statement(key, &block) # :nodoc:
475
433
  cache = @find_by_statement_cache[connection.prepared_statements]
476
434
  cache.compute_if_absent(key) { StatementCache.create(connection, &block) }
@@ -528,7 +486,7 @@ module ActiveRecord
528
486
  # post.init_with(coder)
529
487
  # post.title # => 'hello world'
530
488
  def init_with(coder, &block)
531
- coder = LegacyYamlAdapter.convert(self.class, coder)
489
+ coder = LegacyYamlAdapter.convert(coder)
532
490
  attributes = self.class.yaml_encoder.decode(coder)
533
491
  init_with_attributes(attributes, coder["new_record"], &block)
534
492
  end
@@ -630,6 +588,8 @@ module ActiveRecord
630
588
  # Delegates to id in order to allow two records of the same type and id to work with something like:
631
589
  # [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
632
590
  def hash
591
+ id = self.id
592
+
633
593
  if id
634
594
  self.class.hash ^ id.hash
635
595
  else
@@ -681,11 +641,37 @@ module ActiveRecord
681
641
  # if the record tries to lazily load an association.
682
642
  #
683
643
  # user = User.first
684
- # user.strict_loading!
685
- # user.comments.to_a
644
+ # user.strict_loading! # => true
645
+ # user.comments
686
646
  # => ActiveRecord::StrictLoadingViolationError
687
- def strict_loading!
688
- @strict_loading = true
647
+ #
648
+ # === Parameters:
649
+ #
650
+ # * value - Boolean specifying whether to enable or disable strict loading.
651
+ # * mode - Symbol specifying strict loading mode. Defaults to :all. Using
652
+ # :n_plus_one_only mode will only raise an error if an association
653
+ # that will lead to an n plus one query is lazily loaded.
654
+ #
655
+ # === Example:
656
+ #
657
+ # user = User.first
658
+ # user.strict_loading!(false) # => false
659
+ # user.comments
660
+ # => #<ActiveRecord::Associations::CollectionProxy>
661
+ def strict_loading!(value = true, mode: :all)
662
+ unless [:all, :n_plus_one_only].include?(mode)
663
+ raise ArgumentError, "The :mode option must be one of [:all, :n_plus_one_only]."
664
+ end
665
+
666
+ @strict_loading_mode = mode
667
+ @strict_loading = value
668
+ end
669
+
670
+ attr_reader :strict_loading_mode
671
+
672
+ # Returns +true+ if the record uses strict_loading with +:n_plus_one_only+ mode enabled.
673
+ def strict_loading_n_plus_one_only?
674
+ @strict_loading_mode == :n_plus_one_only
689
675
  end
690
676
 
691
677
  # Marks this record as read only.
@@ -702,11 +688,11 @@ module ActiveRecord
702
688
  # We check defined?(@attributes) not to issue warnings if the object is
703
689
  # allocated but not initialized.
704
690
  inspection = if defined?(@attributes) && @attributes
705
- self.class.attribute_names.collect do |name|
691
+ self.class.attribute_names.filter_map do |name|
706
692
  if _has_attribute?(name)
707
693
  "#{name}: #{attribute_for_inspect(name)}"
708
694
  end
709
- end.compact.join(", ")
695
+ end.join(", ")
710
696
  else
711
697
  "not initialized"
712
698
  end
@@ -763,16 +749,20 @@ module ActiveRecord
763
749
  end
764
750
 
765
751
  def init_internals
766
- @primary_key = self.class.primary_key
767
752
  @readonly = false
768
753
  @previously_new_record = false
769
754
  @destroyed = false
770
755
  @marked_for_destruction = false
771
756
  @destroyed_by_association = nil
772
757
  @_start_transaction_state = nil
773
- @strict_loading = self.class.strict_loading_by_default
774
758
 
775
- self.class.define_attribute_methods
759
+ klass = self.class
760
+
761
+ @primary_key = klass.primary_key
762
+ @strict_loading = klass.strict_loading_by_default
763
+ @strict_loading_mode = :all
764
+
765
+ klass.define_attribute_methods
776
766
  end
777
767
 
778
768
  def initialize_internals_callback
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "uri"
4
4
  require "active_support/core_ext/enumerable"
5
+ require "active_support/core_ext/hash/reverse_merge"
5
6
 
6
7
  module ActiveRecord
7
8
  class DatabaseConfigurations
@@ -68,7 +69,7 @@ module ActiveRecord
68
69
  database: uri.opaque
69
70
  )
70
71
  else
71
- query_hash.merge(
72
+ query_hash.reverse_merge(
72
73
  adapter: @adapter,
73
74
  username: uri.user,
74
75
  password: uri.password,