activerecord 6.1.7.4 → 7.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (249) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1449 -1014
  3. data/README.rdoc +3 -3
  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 +14 -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 +50 -14
  28. data/lib/active_record/associations/preloader.rb +39 -113
  29. data/lib/active_record/associations/singular_association.rb +15 -7
  30. data/lib/active_record/associations/through_association.rb +3 -3
  31. data/lib/active_record/associations.rb +138 -100
  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 +8 -6
  39. data/lib/active_record/attribute_methods/serialization.rb +57 -19
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
  41. data/lib/active_record/attribute_methods/write.rb +7 -10
  42. data/lib/active_record/attribute_methods.rb +19 -22
  43. data/lib/active_record/attributes.rb +24 -35
  44. data/lib/active_record/autosave_association.rb +17 -28
  45. data/lib/active_record/base.rb +19 -1
  46. data/lib/active_record/callbacks.rb +14 -16
  47. data/lib/active_record/coders/yaml_column.rb +4 -8
  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 +52 -23
  58. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +82 -25
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -22
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +153 -74
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +112 -84
  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 +45 -21
  66. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +4 -1
  67. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +7 -1
  68. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +20 -1
  69. data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -6
  70. data/lib/active_record/connection_adapters/pool_config.rb +7 -7
  71. data/lib/active_record/connection_adapters/postgresql/column.rb +19 -1
  72. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +20 -17
  73. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  74. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
  77. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  78. data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
  81. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  82. data/lib/active_record/connection_adapters/postgresql/quoting.rb +71 -71
  83. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +34 -0
  84. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +21 -1
  85. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -1
  86. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +25 -0
  87. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +40 -21
  88. data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
  89. data/lib/active_record/connection_adapters/postgresql_adapter.rb +207 -106
  90. data/lib/active_record/connection_adapters/schema_cache.rb +39 -38
  91. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +25 -19
  92. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +28 -16
  93. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +17 -15
  94. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +97 -32
  95. data/lib/active_record/connection_adapters.rb +6 -5
  96. data/lib/active_record/connection_handling.rb +49 -55
  97. data/lib/active_record/core.rb +123 -148
  98. data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
  99. data/lib/active_record/database_configurations/database_config.rb +12 -9
  100. data/lib/active_record/database_configurations/hash_config.rb +63 -5
  101. data/lib/active_record/database_configurations/url_config.rb +2 -2
  102. data/lib/active_record/database_configurations.rb +15 -32
  103. data/lib/active_record/delegated_type.rb +53 -12
  104. data/lib/active_record/destroy_association_async_job.rb +1 -1
  105. data/lib/active_record/disable_joins_association_relation.rb +39 -0
  106. data/lib/active_record/dynamic_matchers.rb +1 -1
  107. data/lib/active_record/encryption/cipher/aes256_gcm.rb +98 -0
  108. data/lib/active_record/encryption/cipher.rb +53 -0
  109. data/lib/active_record/encryption/config.rb +44 -0
  110. data/lib/active_record/encryption/configurable.rb +67 -0
  111. data/lib/active_record/encryption/context.rb +35 -0
  112. data/lib/active_record/encryption/contexts.rb +72 -0
  113. data/lib/active_record/encryption/derived_secret_key_provider.rb +12 -0
  114. data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
  115. data/lib/active_record/encryption/encryptable_record.rb +206 -0
  116. data/lib/active_record/encryption/encrypted_attribute_type.rb +140 -0
  117. data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
  118. data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
  119. data/lib/active_record/encryption/encryptor.rb +155 -0
  120. data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
  121. data/lib/active_record/encryption/errors.rb +15 -0
  122. data/lib/active_record/encryption/extended_deterministic_queries.rb +160 -0
  123. data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
  124. data/lib/active_record/encryption/key.rb +28 -0
  125. data/lib/active_record/encryption/key_generator.rb +42 -0
  126. data/lib/active_record/encryption/key_provider.rb +46 -0
  127. data/lib/active_record/encryption/message.rb +33 -0
  128. data/lib/active_record/encryption/message_serializer.rb +90 -0
  129. data/lib/active_record/encryption/null_encryptor.rb +21 -0
  130. data/lib/active_record/encryption/properties.rb +76 -0
  131. data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
  132. data/lib/active_record/encryption/scheme.rb +99 -0
  133. data/lib/active_record/encryption.rb +55 -0
  134. data/lib/active_record/enum.rb +50 -43
  135. data/lib/active_record/errors.rb +67 -4
  136. data/lib/active_record/explain_registry.rb +11 -6
  137. data/lib/active_record/explain_subscriber.rb +1 -1
  138. data/lib/active_record/fixture_set/file.rb +15 -1
  139. data/lib/active_record/fixture_set/table_row.rb +41 -6
  140. data/lib/active_record/fixture_set/table_rows.rb +4 -4
  141. data/lib/active_record/fixtures.rb +20 -23
  142. data/lib/active_record/future_result.rb +139 -0
  143. data/lib/active_record/gem_version.rb +5 -5
  144. data/lib/active_record/inheritance.rb +55 -17
  145. data/lib/active_record/insert_all.rb +80 -14
  146. data/lib/active_record/integration.rb +4 -3
  147. data/lib/active_record/internal_metadata.rb +1 -5
  148. data/lib/active_record/legacy_yaml_adapter.rb +2 -39
  149. data/lib/active_record/locking/optimistic.rb +36 -21
  150. data/lib/active_record/locking/pessimistic.rb +10 -4
  151. data/lib/active_record/log_subscriber.rb +23 -7
  152. data/lib/active_record/middleware/database_selector/resolver.rb +6 -10
  153. data/lib/active_record/middleware/database_selector.rb +18 -6
  154. data/lib/active_record/middleware/shard_selector.rb +60 -0
  155. data/lib/active_record/migration/command_recorder.rb +8 -9
  156. data/lib/active_record/migration/compatibility.rb +91 -2
  157. data/lib/active_record/migration/join_table.rb +1 -1
  158. data/lib/active_record/migration.rb +115 -84
  159. data/lib/active_record/model_schema.rb +58 -59
  160. data/lib/active_record/nested_attributes.rb +13 -12
  161. data/lib/active_record/no_touching.rb +3 -3
  162. data/lib/active_record/null_relation.rb +2 -6
  163. data/lib/active_record/persistence.rb +228 -60
  164. data/lib/active_record/query_cache.rb +2 -2
  165. data/lib/active_record/query_logs.rb +149 -0
  166. data/lib/active_record/querying.rb +16 -6
  167. data/lib/active_record/railtie.rb +136 -22
  168. data/lib/active_record/railties/controller_runtime.rb +1 -1
  169. data/lib/active_record/railties/databases.rake +78 -136
  170. data/lib/active_record/readonly_attributes.rb +11 -0
  171. data/lib/active_record/reflection.rb +80 -49
  172. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  173. data/lib/active_record/relation/batches.rb +6 -6
  174. data/lib/active_record/relation/calculations.rb +92 -60
  175. data/lib/active_record/relation/delegation.rb +7 -7
  176. data/lib/active_record/relation/finder_methods.rb +31 -35
  177. data/lib/active_record/relation/merger.rb +20 -13
  178. data/lib/active_record/relation/predicate_builder/association_query_value.rb +20 -1
  179. data/lib/active_record/relation/predicate_builder.rb +2 -6
  180. data/lib/active_record/relation/query_attribute.rb +5 -11
  181. data/lib/active_record/relation/query_methods.rb +285 -68
  182. data/lib/active_record/relation/record_fetch_warning.rb +7 -9
  183. data/lib/active_record/relation/spawn_methods.rb +2 -2
  184. data/lib/active_record/relation/where_clause.rb +10 -19
  185. data/lib/active_record/relation.rb +189 -88
  186. data/lib/active_record/result.rb +23 -11
  187. data/lib/active_record/runtime_registry.rb +9 -13
  188. data/lib/active_record/sanitization.rb +17 -12
  189. data/lib/active_record/schema.rb +38 -23
  190. data/lib/active_record/schema_dumper.rb +29 -19
  191. data/lib/active_record/schema_migration.rb +4 -4
  192. data/lib/active_record/scoping/default.rb +60 -13
  193. data/lib/active_record/scoping/named.rb +3 -11
  194. data/lib/active_record/scoping.rb +64 -34
  195. data/lib/active_record/serialization.rb +6 -1
  196. data/lib/active_record/signed_id.rb +3 -3
  197. data/lib/active_record/store.rb +2 -2
  198. data/lib/active_record/suppressor.rb +11 -15
  199. data/lib/active_record/table_metadata.rb +5 -1
  200. data/lib/active_record/tasks/database_tasks.rb +127 -60
  201. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  202. data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -13
  203. data/lib/active_record/test_databases.rb +1 -1
  204. data/lib/active_record/test_fixtures.rb +9 -6
  205. data/lib/active_record/timestamp.rb +3 -4
  206. data/lib/active_record/transactions.rb +9 -14
  207. data/lib/active_record/translation.rb +3 -3
  208. data/lib/active_record/type/adapter_specific_registry.rb +32 -7
  209. data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
  210. data/lib/active_record/type/internal/timezone.rb +2 -2
  211. data/lib/active_record/type/serialized.rb +5 -5
  212. data/lib/active_record/type/type_map.rb +17 -20
  213. data/lib/active_record/type.rb +1 -2
  214. data/lib/active_record/validations/associated.rb +4 -4
  215. data/lib/active_record/validations/presence.rb +2 -2
  216. data/lib/active_record/validations/uniqueness.rb +4 -4
  217. data/lib/active_record/version.rb +1 -1
  218. data/lib/active_record.rb +225 -27
  219. data/lib/arel/attributes/attribute.rb +0 -8
  220. data/lib/arel/crud.rb +28 -22
  221. data/lib/arel/delete_manager.rb +18 -4
  222. data/lib/arel/filter_predications.rb +9 -0
  223. data/lib/arel/insert_manager.rb +2 -3
  224. data/lib/arel/nodes/casted.rb +1 -1
  225. data/lib/arel/nodes/delete_statement.rb +12 -13
  226. data/lib/arel/nodes/filter.rb +10 -0
  227. data/lib/arel/nodes/function.rb +1 -0
  228. data/lib/arel/nodes/insert_statement.rb +2 -2
  229. data/lib/arel/nodes/select_core.rb +2 -2
  230. data/lib/arel/nodes/select_statement.rb +2 -2
  231. data/lib/arel/nodes/update_statement.rb +8 -3
  232. data/lib/arel/nodes.rb +1 -0
  233. data/lib/arel/predications.rb +11 -3
  234. data/lib/arel/select_manager.rb +10 -4
  235. data/lib/arel/table.rb +0 -1
  236. data/lib/arel/tree_manager.rb +0 -12
  237. data/lib/arel/update_manager.rb +18 -4
  238. data/lib/arel/visitors/dot.rb +80 -90
  239. data/lib/arel/visitors/mysql.rb +8 -2
  240. data/lib/arel/visitors/postgresql.rb +0 -10
  241. data/lib/arel/visitors/to_sql.rb +58 -2
  242. data/lib/arel.rb +2 -1
  243. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
  244. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
  245. data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
  246. data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
  247. data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
  248. data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
  249. metadata +58 -14
@@ -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,35 +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
157
81
 
158
- # Application configurable boolean that instructs the YAML Coder to use
159
- # an unsafe load if set to true.
160
- mattr_accessor :use_yaml_unsafe_load, instance_writer: false, default: false
161
-
162
- # Application configurable array that provides additional permitted classes
163
- # to Psych safe_load in the YAML Coder
164
- mattr_accessor :yaml_column_permitted_classes, instance_writer: false, default: [Symbol]
165
-
166
- ##
167
- # :singleton-method:
168
- # Application configurable boolean that denotes whether or not to raise
169
- # an exception when the PostgreSQLAdapter is provided with an integer that is
170
- # wider than signed 64bit representation
171
- mattr_accessor :raise_int_wider_than_64bit, instance_writer: false, default: true
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
172
91
 
173
92
  self.filter_attributes = []
174
93
 
175
94
  def self.connection_handler
176
- Thread.current.thread_variable_get(:ar_connection_handler) || default_connection_handler
95
+ ActiveSupport::IsolatedExecutionState[:active_record_connection_handler] || default_connection_handler
177
96
  end
178
97
 
179
98
  def self.connection_handler=(handler)
180
- Thread.current.thread_variable_set(:ar_connection_handler, handler)
99
+ ActiveSupport::IsolatedExecutionState[:active_record_connection_handler] = handler
181
100
  end
182
101
 
183
102
  def self.connection_handlers
184
- unless legacy_connection_handling
103
+ if ActiveRecord.legacy_connection_handling
104
+ else
185
105
  raise NotImplementedError, "The new connection handling does not support accessing multiple connection handlers."
186
106
  end
187
107
 
@@ -189,13 +109,32 @@ module ActiveRecord
189
109
  end
190
110
 
191
111
  def self.connection_handlers=(handlers)
192
- unless legacy_connection_handling
193
- 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."
194
124
  end
195
125
 
196
126
  @@connection_handlers = handlers
197
127
  end
198
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
+
199
138
  # Returns the symbol representing the current connected role.
200
139
  #
201
140
  # ActiveRecord::Base.connected_to(role: :writing) do
@@ -206,12 +145,12 @@ module ActiveRecord
206
145
  # ActiveRecord::Base.current_role #=> :reading
207
146
  # end
208
147
  def self.current_role
209
- if ActiveRecord::Base.legacy_connection_handling
148
+ if ActiveRecord.legacy_connection_handling
210
149
  connection_handlers.key(connection_handler) || default_role
211
150
  else
212
151
  connected_to_stack.reverse_each do |hash|
213
152
  return hash[:role] if hash[:role] && hash[:klasses].include?(Base)
214
- 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)
215
154
  end
216
155
 
217
156
  default_role
@@ -230,7 +169,7 @@ module ActiveRecord
230
169
  def self.current_shard
231
170
  connected_to_stack.reverse_each do |hash|
232
171
  return hash[:shard] if hash[:shard] && hash[:klasses].include?(Base)
233
- 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)
234
173
  end
235
174
 
236
175
  default_shard
@@ -247,12 +186,12 @@ module ActiveRecord
247
186
  # ActiveRecord::Base.current_preventing_writes #=> false
248
187
  # end
249
188
  def self.current_preventing_writes
250
- if legacy_connection_handling
189
+ if ActiveRecord.legacy_connection_handling
251
190
  connection_handler.prevent_writes
252
191
  else
253
192
  connected_to_stack.reverse_each do |hash|
254
193
  return hash[:prevent_writes] if !hash[:prevent_writes].nil? && hash[:klasses].include?(Base)
255
- 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)
256
195
  end
257
196
 
258
197
  false
@@ -260,11 +199,11 @@ module ActiveRecord
260
199
  end
261
200
 
262
201
  def self.connected_to_stack # :nodoc:
263
- 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]
264
203
  connected_to_stack
265
204
  else
266
205
  connected_to_stack = Concurrent::Array.new
267
- Thread.current.thread_variable_set(:ar_connected_to_stack, connected_to_stack)
206
+ ActiveSupport::IsolatedExecutionState[:active_record_connected_to_stack] = connected_to_stack
268
207
  connected_to_stack
269
208
  end
270
209
  end
@@ -273,7 +212,7 @@ module ActiveRecord
273
212
  @connection_class = b
274
213
  end
275
214
 
276
- def self.connection_class # :nodoc
215
+ def self.connection_class # :nodoc:
277
216
  @connection_class ||= false
278
217
  end
279
218
 
@@ -281,7 +220,7 @@ module ActiveRecord
281
220
  self.connection_class
282
221
  end
283
222
 
284
- def self.connection_classes # :nodoc:
223
+ def self.connection_class_for_self # :nodoc:
285
224
  klass = self
286
225
 
287
226
  until klass == Base
@@ -292,22 +231,14 @@ module ActiveRecord
292
231
  klass
293
232
  end
294
233
 
295
- def self.allow_unsafe_raw_sql # :nodoc:
296
- ActiveSupport::Deprecation.warn("ActiveRecord::Base.allow_unsafe_raw_sql is deprecated and will be removed in Rails 7.0")
297
- end
298
-
299
- def self.allow_unsafe_raw_sql=(value) # :nodoc:
300
- ActiveSupport::Deprecation.warn("ActiveRecord::Base.allow_unsafe_raw_sql= is deprecated and will be removed in Rails 7.0")
301
- end
302
-
303
234
  self.default_connection_handler = ConnectionAdapters::ConnectionHandler.new
304
- self.default_role = writing_role
235
+ self.default_role = ActiveRecord.writing_role
305
236
  self.default_shard = :default
306
237
 
307
238
  def self.strict_loading_violation!(owner:, reflection:) # :nodoc:
308
- case action_on_strict_loading_violation
239
+ case ActiveRecord.action_on_strict_loading_violation
309
240
  when :raise
310
- 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)
311
242
  raise ActiveRecord::StrictLoadingViolationError.new(message)
312
243
  when :log
313
244
  name = "strict_loading_violation.active_record"
@@ -396,7 +327,32 @@ module ActiveRecord
396
327
  end
397
328
 
398
329
  def find_by!(*args) # :nodoc:
399
- 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
400
356
  end
401
357
 
402
358
  def initialize_generated_modules # :nodoc:
@@ -455,25 +411,16 @@ module ActiveRecord
455
411
  end
456
412
  end
457
413
 
458
- # 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.
459
415
  def ===(object) # :nodoc:
460
416
  object.is_a?(self)
461
417
  end
462
418
 
463
419
  # Returns an instance of <tt>Arel::Table</tt> loaded with the current table name.
464
- #
465
- # class Post < ActiveRecord::Base
466
- # scope :published_and_commented, -> { published.and(arel_table[:comments_count].gt(0)) }
467
- # end
468
420
  def arel_table # :nodoc:
469
421
  @arel_table ||= Arel::Table.new(table_name, klass: self)
470
422
  end
471
423
 
472
- def arel_attribute(name, table = arel_table) # :nodoc:
473
- table[name]
474
- end
475
- deprecate :arel_attribute
476
-
477
424
  def predicate_builder # :nodoc:
478
425
  @predicate_builder ||= PredicateBuilder.new(table_metadata)
479
426
  end
@@ -482,10 +429,6 @@ module ActiveRecord
482
429
  TypeCaster::Map.new(self)
483
430
  end
484
431
 
485
- def _internal? # :nodoc:
486
- false
487
- end
488
-
489
432
  def cached_find_by_statement(key, &block) # :nodoc:
490
433
  cache = @find_by_statement_cache[connection.prepared_statements]
491
434
  cache.compute_if_absent(key) { StatementCache.create(connection, &block) }
@@ -543,7 +486,7 @@ module ActiveRecord
543
486
  # post.init_with(coder)
544
487
  # post.title # => 'hello world'
545
488
  def init_with(coder, &block)
546
- coder = LegacyYamlAdapter.convert(self.class, coder)
489
+ coder = LegacyYamlAdapter.convert(coder)
547
490
  attributes = self.class.yaml_encoder.decode(coder)
548
491
  init_with_attributes(attributes, coder["new_record"], &block)
549
492
  end
@@ -645,6 +588,8 @@ module ActiveRecord
645
588
  # Delegates to id in order to allow two records of the same type and id to work with something like:
646
589
  # [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
647
590
  def hash
591
+ id = self.id
592
+
648
593
  if id
649
594
  self.class.hash ^ id.hash
650
595
  else
@@ -696,11 +641,37 @@ module ActiveRecord
696
641
  # if the record tries to lazily load an association.
697
642
  #
698
643
  # user = User.first
699
- # user.strict_loading!
700
- # user.comments.to_a
644
+ # user.strict_loading! # => true
645
+ # user.comments
701
646
  # => ActiveRecord::StrictLoadingViolationError
702
- def strict_loading!
703
- @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
704
675
  end
705
676
 
706
677
  # Marks this record as read only.
@@ -717,11 +688,11 @@ module ActiveRecord
717
688
  # We check defined?(@attributes) not to issue warnings if the object is
718
689
  # allocated but not initialized.
719
690
  inspection = if defined?(@attributes) && @attributes
720
- self.class.attribute_names.collect do |name|
691
+ self.class.attribute_names.filter_map do |name|
721
692
  if _has_attribute?(name)
722
693
  "#{name}: #{attribute_for_inspect(name)}"
723
694
  end
724
- end.compact.join(", ")
695
+ end.join(", ")
725
696
  else
726
697
  "not initialized"
727
698
  end
@@ -778,16 +749,20 @@ module ActiveRecord
778
749
  end
779
750
 
780
751
  def init_internals
781
- @primary_key = self.class.primary_key
782
752
  @readonly = false
783
753
  @previously_new_record = false
784
754
  @destroyed = false
785
755
  @marked_for_destruction = false
786
756
  @destroyed_by_association = nil
787
757
  @_start_transaction_state = nil
788
- @strict_loading = self.class.strict_loading_by_default
789
758
 
790
- 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
791
766
  end
792
767
 
793
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,