activerecord 7.1.5.1 → 8.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (206) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +369 -2484
  3. data/README.rdoc +15 -15
  4. data/examples/performance.rb +2 -2
  5. data/lib/active_record/association_relation.rb +2 -1
  6. data/lib/active_record/associations/alias_tracker.rb +31 -23
  7. data/lib/active_record/associations/association.rb +43 -12
  8. data/lib/active_record/associations/belongs_to_association.rb +21 -8
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  10. data/lib/active_record/associations/builder/association.rb +7 -6
  11. data/lib/active_record/associations/builder/belongs_to.rb +1 -0
  12. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
  13. data/lib/active_record/associations/builder/has_many.rb +3 -4
  14. data/lib/active_record/associations/builder/has_one.rb +3 -4
  15. data/lib/active_record/associations/collection_association.rb +17 -9
  16. data/lib/active_record/associations/collection_proxy.rb +14 -1
  17. data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
  18. data/lib/active_record/associations/errors.rb +265 -0
  19. data/lib/active_record/associations/has_many_association.rb +1 -1
  20. data/lib/active_record/associations/has_many_through_association.rb +10 -3
  21. data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
  22. data/lib/active_record/associations/nested_error.rb +47 -0
  23. data/lib/active_record/associations/preloader/association.rb +4 -3
  24. data/lib/active_record/associations/preloader/branch.rb +7 -1
  25. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  26. data/lib/active_record/associations/singular_association.rb +14 -3
  27. data/lib/active_record/associations/through_association.rb +1 -1
  28. data/lib/active_record/associations.rb +92 -295
  29. data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
  30. data/lib/active_record/attribute_assignment.rb +0 -2
  31. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  32. data/lib/active_record/attribute_methods/primary_key.rb +25 -61
  33. data/lib/active_record/attribute_methods/read.rb +1 -13
  34. data/lib/active_record/attribute_methods/serialization.rb +4 -24
  35. data/lib/active_record/attribute_methods/time_zone_conversion.rb +9 -18
  36. data/lib/active_record/attribute_methods.rb +71 -75
  37. data/lib/active_record/attributes.rb +63 -49
  38. data/lib/active_record/autosave_association.rb +92 -57
  39. data/lib/active_record/base.rb +2 -3
  40. data/lib/active_record/callbacks.rb +1 -1
  41. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +48 -122
  42. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
  43. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -1
  44. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +286 -77
  45. data/lib/active_record/connection_adapters/abstract/database_statements.rb +119 -55
  46. data/lib/active_record/connection_adapters/abstract/query_cache.rb +197 -76
  47. data/lib/active_record/connection_adapters/abstract/quoting.rb +66 -92
  48. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
  49. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +12 -3
  50. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +48 -12
  51. data/lib/active_record/connection_adapters/abstract/transaction.rb +140 -67
  52. data/lib/active_record/connection_adapters/abstract_adapter.rb +85 -90
  53. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +71 -52
  54. data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
  55. data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -57
  56. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
  57. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +56 -45
  58. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +92 -101
  59. data/lib/active_record/connection_adapters/mysql2_adapter.rb +13 -31
  60. data/lib/active_record/connection_adapters/pool_config.rb +14 -13
  61. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +86 -41
  62. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  63. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  64. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
  65. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  66. data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
  67. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
  68. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -11
  69. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +36 -20
  70. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +3 -2
  71. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +75 -28
  72. data/lib/active_record/connection_adapters/postgresql_adapter.rb +73 -113
  73. data/lib/active_record/connection_adapters/schema_cache.rb +124 -131
  74. data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
  75. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +81 -97
  76. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -46
  77. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +16 -0
  78. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
  79. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +29 -0
  80. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +35 -3
  81. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +183 -87
  82. data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
  83. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +39 -69
  84. data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -65
  85. data/lib/active_record/connection_adapters.rb +65 -0
  86. data/lib/active_record/connection_handling.rb +74 -37
  87. data/lib/active_record/core.rb +132 -51
  88. data/lib/active_record/counter_cache.rb +19 -10
  89. data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
  90. data/lib/active_record/database_configurations/database_config.rb +23 -4
  91. data/lib/active_record/database_configurations/hash_config.rb +46 -34
  92. data/lib/active_record/database_configurations/url_config.rb +20 -1
  93. data/lib/active_record/database_configurations.rb +1 -1
  94. data/lib/active_record/delegated_type.rb +41 -17
  95. data/lib/active_record/dynamic_matchers.rb +2 -2
  96. data/lib/active_record/encryption/config.rb +3 -1
  97. data/lib/active_record/encryption/encryptable_record.rb +7 -7
  98. data/lib/active_record/encryption/encrypted_attribute_type.rb +33 -4
  99. data/lib/active_record/encryption/encryptor.rb +28 -6
  100. data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
  101. data/lib/active_record/encryption/key_provider.rb +1 -1
  102. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  103. data/lib/active_record/encryption/message_serializer.rb +4 -0
  104. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  105. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  106. data/lib/active_record/encryption/scheme.rb +8 -1
  107. data/lib/active_record/enum.rb +20 -16
  108. data/lib/active_record/errors.rb +54 -20
  109. data/lib/active_record/explain.rb +13 -24
  110. data/lib/active_record/fixtures.rb +37 -33
  111. data/lib/active_record/future_result.rb +21 -13
  112. data/lib/active_record/gem_version.rb +4 -4
  113. data/lib/active_record/inheritance.rb +4 -2
  114. data/lib/active_record/insert_all.rb +19 -16
  115. data/lib/active_record/integration.rb +4 -1
  116. data/lib/active_record/internal_metadata.rb +48 -34
  117. data/lib/active_record/locking/optimistic.rb +8 -7
  118. data/lib/active_record/log_subscriber.rb +5 -32
  119. data/lib/active_record/message_pack.rb +1 -1
  120. data/lib/active_record/migration/command_recorder.rb +33 -14
  121. data/lib/active_record/migration/compatibility.rb +8 -3
  122. data/lib/active_record/migration/default_strategy.rb +4 -5
  123. data/lib/active_record/migration/pending_migration_connection.rb +2 -2
  124. data/lib/active_record/migration.rb +104 -98
  125. data/lib/active_record/model_schema.rb +32 -70
  126. data/lib/active_record/nested_attributes.rb +15 -9
  127. data/lib/active_record/normalization.rb +3 -7
  128. data/lib/active_record/persistence.rb +127 -451
  129. data/lib/active_record/query_cache.rb +19 -8
  130. data/lib/active_record/query_logs.rb +104 -37
  131. data/lib/active_record/query_logs_formatter.rb +17 -28
  132. data/lib/active_record/querying.rb +24 -12
  133. data/lib/active_record/railtie.rb +26 -68
  134. data/lib/active_record/railties/controller_runtime.rb +13 -4
  135. data/lib/active_record/railties/databases.rake +43 -61
  136. data/lib/active_record/reflection.rb +112 -53
  137. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  138. data/lib/active_record/relation/batches.rb +138 -72
  139. data/lib/active_record/relation/calculations.rb +122 -82
  140. data/lib/active_record/relation/delegation.rb +30 -22
  141. data/lib/active_record/relation/finder_methods.rb +32 -18
  142. data/lib/active_record/relation/merger.rb +12 -14
  143. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  144. data/lib/active_record/relation/predicate_builder/association_query_value.rb +10 -2
  145. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
  146. data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
  147. data/lib/active_record/relation/predicate_builder.rb +16 -3
  148. data/lib/active_record/relation/query_attribute.rb +1 -1
  149. data/lib/active_record/relation/query_methods.rb +317 -101
  150. data/lib/active_record/relation/spawn_methods.rb +3 -19
  151. data/lib/active_record/relation/where_clause.rb +7 -19
  152. data/lib/active_record/relation.rb +561 -119
  153. data/lib/active_record/result.rb +95 -46
  154. data/lib/active_record/runtime_registry.rb +39 -0
  155. data/lib/active_record/sanitization.rb +31 -25
  156. data/lib/active_record/schema.rb +8 -6
  157. data/lib/active_record/schema_dumper.rb +53 -20
  158. data/lib/active_record/schema_migration.rb +31 -14
  159. data/lib/active_record/scoping/named.rb +6 -2
  160. data/lib/active_record/signed_id.rb +24 -4
  161. data/lib/active_record/statement_cache.rb +19 -19
  162. data/lib/active_record/store.rb +7 -3
  163. data/lib/active_record/table_metadata.rb +2 -13
  164. data/lib/active_record/tasks/database_tasks.rb +87 -58
  165. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -3
  166. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  167. data/lib/active_record/tasks/sqlite_database_tasks.rb +4 -3
  168. data/lib/active_record/test_fixtures.rb +98 -89
  169. data/lib/active_record/testing/query_assertions.rb +121 -0
  170. data/lib/active_record/timestamp.rb +2 -2
  171. data/lib/active_record/token_for.rb +22 -12
  172. data/lib/active_record/touch_later.rb +1 -1
  173. data/lib/active_record/transaction.rb +132 -0
  174. data/lib/active_record/transactions.rb +72 -17
  175. data/lib/active_record/translation.rb +0 -2
  176. data/lib/active_record/type/serialized.rb +1 -3
  177. data/lib/active_record/type_caster/connection.rb +4 -4
  178. data/lib/active_record/validations/associated.rb +9 -3
  179. data/lib/active_record/validations/uniqueness.rb +23 -18
  180. data/lib/active_record/validations.rb +4 -1
  181. data/lib/active_record.rb +138 -57
  182. data/lib/arel/alias_predication.rb +1 -1
  183. data/lib/arel/collectors/bind.rb +4 -2
  184. data/lib/arel/collectors/composite.rb +7 -0
  185. data/lib/arel/collectors/sql_string.rb +2 -2
  186. data/lib/arel/collectors/substitute_binds.rb +3 -3
  187. data/lib/arel/nodes/binary.rb +1 -7
  188. data/lib/arel/nodes/bound_sql_literal.rb +9 -5
  189. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  190. data/lib/arel/nodes/node.rb +5 -4
  191. data/lib/arel/nodes/sql_literal.rb +8 -1
  192. data/lib/arel/nodes.rb +2 -2
  193. data/lib/arel/predications.rb +1 -1
  194. data/lib/arel/select_manager.rb +1 -1
  195. data/lib/arel/table.rb +3 -7
  196. data/lib/arel/tree_manager.rb +3 -2
  197. data/lib/arel/update_manager.rb +2 -1
  198. data/lib/arel/visitors/dot.rb +1 -0
  199. data/lib/arel/visitors/mysql.rb +9 -4
  200. data/lib/arel/visitors/postgresql.rb +1 -12
  201. data/lib/arel/visitors/sqlite.rb +25 -0
  202. data/lib/arel/visitors/to_sql.rb +29 -16
  203. data/lib/arel.rb +7 -3
  204. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  205. metadata +18 -16
  206. data/lib/active_record/relation/record_fetch_warning.rb +0 -49
@@ -9,10 +9,10 @@ databases = ActiveRecord::Tasks::DatabaseTasks.setup_initial_database_yaml
9
9
  db_namespace = namespace :db do
10
10
  desc "Set the environment value for the database"
11
11
  task "environment:set" => :load_config do
12
- connection = ActiveRecord::Tasks::DatabaseTasks.migration_connection
13
- raise ActiveRecord::EnvironmentStorageError unless connection.internal_metadata.enabled?
12
+ pool = ActiveRecord::Tasks::DatabaseTasks.migration_connection_pool
13
+ raise ActiveRecord::EnvironmentStorageError unless pool.internal_metadata.enabled?
14
14
 
15
- connection.internal_metadata.create_table_and_set_flags(connection.migration_context.current_environment)
15
+ pool.internal_metadata.create_table_and_set_flags(pool.migration_context.current_environment)
16
16
  end
17
17
 
18
18
  task check_protected_environments: :load_config do
@@ -87,22 +87,7 @@ db_namespace = namespace :db do
87
87
 
88
88
  desc "Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)."
89
89
  task migrate: :load_config do
90
- db_configs = ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env)
91
-
92
- if db_configs.size == 1 && db_configs.first.primary?
93
- ActiveRecord::Tasks::DatabaseTasks.migrate
94
- else
95
- mapped_versions = ActiveRecord::Tasks::DatabaseTasks.db_configs_with_versions
96
-
97
- mapped_versions.sort.each do |version, db_configs|
98
- db_configs.each do |db_config|
99
- ActiveRecord::Tasks::DatabaseTasks.with_temporary_connection(db_config) do
100
- ActiveRecord::Tasks::DatabaseTasks.migrate(version)
101
- end
102
- end
103
- end
104
- end
105
-
90
+ ActiveRecord::Tasks::DatabaseTasks.migrate_all
106
91
  db_namespace["_dump"].invoke
107
92
  end
108
93
 
@@ -135,7 +120,7 @@ db_namespace = namespace :db do
135
120
  ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |name|
136
121
  desc "Migrate #{name} database for current environment"
137
122
  task name => :load_config do
138
- ActiveRecord::Tasks::DatabaseTasks.with_temporary_connection_for_each(env: Rails.env, name: name) do |conn|
123
+ ActiveRecord::Tasks::DatabaseTasks.with_temporary_pool_for_each(env: Rails.env, name: name) do
139
124
  ActiveRecord::Tasks::DatabaseTasks.migrate
140
125
  end
141
126
 
@@ -175,8 +160,8 @@ db_namespace = namespace :db do
175
160
  end
176
161
  end
177
162
 
178
- # desc 'Resets your database using your migrations for the current environment'
179
- task reset: ["db:drop", "db:create", "db:migrate"]
163
+ desc "Resets your database using your migrations for the current environment"
164
+ task reset: ["db:drop", "db:create", "db:schema:dump", "db:migrate"]
180
165
 
181
166
  desc 'Run the "up" for a given migration VERSION.'
182
167
  task up: :load_config do
@@ -186,7 +171,7 @@ db_namespace = namespace :db do
186
171
 
187
172
  ActiveRecord::Tasks::DatabaseTasks.check_target_version
188
173
 
189
- ActiveRecord::Tasks::DatabaseTasks.migration_connection.migration_context.run(
174
+ ActiveRecord::Tasks::DatabaseTasks.migration_connection_pool.migration_context.run(
190
175
  :up,
191
176
  ActiveRecord::Tasks::DatabaseTasks.target_version
192
177
  )
@@ -199,9 +184,9 @@ db_namespace = namespace :db do
199
184
  task name => :load_config do
200
185
  raise "VERSION is required" if !ENV["VERSION"] || ENV["VERSION"].empty?
201
186
 
202
- ActiveRecord::Tasks::DatabaseTasks.with_temporary_connection_for_each(env: Rails.env, name: name) do |conn|
187
+ ActiveRecord::Tasks::DatabaseTasks.with_temporary_pool_for_each(env: Rails.env, name: name) do |pool|
203
188
  ActiveRecord::Tasks::DatabaseTasks.check_target_version
204
- conn.migration_context.run(:up, ActiveRecord::Tasks::DatabaseTasks.target_version)
189
+ pool.migration_context.run(:up, ActiveRecord::Tasks::DatabaseTasks.target_version)
205
190
  end
206
191
 
207
192
  db_namespace["_dump:#{name}"].invoke
@@ -217,7 +202,7 @@ db_namespace = namespace :db do
217
202
 
218
203
  ActiveRecord::Tasks::DatabaseTasks.check_target_version
219
204
 
220
- ActiveRecord::Tasks::DatabaseTasks.migration_connection.migration_context.run(
205
+ ActiveRecord::Tasks::DatabaseTasks.migration_connection_pool.migration_context.run(
221
206
  :down,
222
207
  ActiveRecord::Tasks::DatabaseTasks.target_version
223
208
  )
@@ -230,9 +215,9 @@ db_namespace = namespace :db do
230
215
  task name => :load_config do
231
216
  raise "VERSION is required" if !ENV["VERSION"] || ENV["VERSION"].empty?
232
217
 
233
- ActiveRecord::Tasks::DatabaseTasks.with_temporary_connection_for_each(env: Rails.env, name: name) do |conn|
218
+ ActiveRecord::Tasks::DatabaseTasks.with_temporary_pool_for_each(env: Rails.env, name: name) do |pool|
234
219
  ActiveRecord::Tasks::DatabaseTasks.check_target_version
235
- conn.migration_context.run(:down, ActiveRecord::Tasks::DatabaseTasks.target_version)
220
+ pool.migration_context.run(:down, ActiveRecord::Tasks::DatabaseTasks.target_version)
236
221
  end
237
222
 
238
223
  db_namespace["_dump:#{name}"].invoke
@@ -242,7 +227,7 @@ db_namespace = namespace :db do
242
227
 
243
228
  desc "Display status of migrations"
244
229
  task status: :load_config do
245
- ActiveRecord::Tasks::DatabaseTasks.with_temporary_connection_for_each do
230
+ ActiveRecord::Tasks::DatabaseTasks.with_temporary_pool_for_each do
246
231
  ActiveRecord::Tasks::DatabaseTasks.migrate_status
247
232
  end
248
233
  end
@@ -251,7 +236,7 @@ db_namespace = namespace :db do
251
236
  ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |name|
252
237
  desc "Display status of migrations for #{name} database"
253
238
  task name => :load_config do
254
- ActiveRecord::Tasks::DatabaseTasks.with_temporary_connection_for_each(env: Rails.env, name: name) do
239
+ ActiveRecord::Tasks::DatabaseTasks.with_temporary_pool_for_each(env: Rails.env, name: name) do
255
240
  ActiveRecord::Tasks::DatabaseTasks.migrate_status
256
241
  end
257
242
  end
@@ -265,8 +250,8 @@ db_namespace = namespace :db do
265
250
  task name => :load_config do
266
251
  step = ENV["STEP"] ? ENV["STEP"].to_i : 1
267
252
 
268
- ActiveRecord::Tasks::DatabaseTasks.with_temporary_connection_for_each(env: Rails.env, name: name) do |conn|
269
- conn.migration_context.rollback(step)
253
+ ActiveRecord::Tasks::DatabaseTasks.with_temporary_pool_for_each(env: Rails.env, name: name) do |pool|
254
+ pool.migration_context.rollback(step)
270
255
  end
271
256
 
272
257
  db_namespace["_dump:#{name}"].invoke
@@ -281,7 +266,7 @@ db_namespace = namespace :db do
281
266
 
282
267
  step = ENV["STEP"] ? ENV["STEP"].to_i : 1
283
268
 
284
- ActiveRecord::Tasks::DatabaseTasks.migration_connection.migration_context.rollback(step)
269
+ ActiveRecord::Tasks::DatabaseTasks.migration_connection_pool.migration_context.rollback(step)
285
270
 
286
271
  db_namespace["_dump"].invoke
287
272
  end
@@ -290,7 +275,7 @@ db_namespace = namespace :db do
290
275
  task forward: :load_config do
291
276
  step = ENV["STEP"] ? ENV["STEP"].to_i : 1
292
277
 
293
- ActiveRecord::Tasks::DatabaseTasks.migration_connection.migration_context.forward(step)
278
+ ActiveRecord::Tasks::DatabaseTasks.migration_connection_pool.migration_context.forward(step)
294
279
 
295
280
  db_namespace["_dump"].invoke
296
281
  end
@@ -321,9 +306,9 @@ db_namespace = namespace :db do
321
306
 
322
307
  desc "Retrieve the current schema version number"
323
308
  task version: :load_config do
324
- ActiveRecord::Tasks::DatabaseTasks.with_temporary_connection_for_each(env: Rails.env) do |connection|
325
- puts "\ndatabase: #{connection.pool.db_config.database}\n"
326
- puts "Current version: #{connection.schema_version}"
309
+ ActiveRecord::Tasks::DatabaseTasks.with_temporary_pool_for_each(env: Rails.env) do |pool|
310
+ puts "\ndatabase: #{pool.db_config.database}\n"
311
+ puts "Current version: #{pool.migration_context.current_version}"
327
312
  puts
328
313
  end
329
314
  end
@@ -344,8 +329,8 @@ db_namespace = namespace :db do
344
329
  task abort_if_pending_migrations: :load_config do
345
330
  pending_migrations = []
346
331
 
347
- ActiveRecord::Tasks::DatabaseTasks.with_temporary_connection_for_each do |conn|
348
- pending_migrations << conn.migration_context.open.pending_migrations
332
+ ActiveRecord::Tasks::DatabaseTasks.with_temporary_pool_for_each do |pool|
333
+ pending_migrations << pool.migration_context.open.pending_migrations
349
334
  end
350
335
 
351
336
  pending_migrations = pending_migrations.flatten!
@@ -365,8 +350,8 @@ db_namespace = namespace :db do
365
350
  ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |name|
366
351
  # desc "Raise an error if there are pending migrations for #{name} database"
367
352
  task name => :load_config do
368
- ActiveRecord::Tasks::DatabaseTasks.with_temporary_connection_for_each(env: Rails.env, name: name) do |conn|
369
- pending_migrations = conn.migration_context.open.pending_migrations
353
+ ActiveRecord::Tasks::DatabaseTasks.with_temporary_pool_for_each(env: Rails.env, name: name) do |pool|
354
+ pending_migrations = pool.migration_context.open.pending_migrations
370
355
 
371
356
  if pending_migrations.any?
372
357
  puts "You have #{pending_migrations.size} pending #{pending_migrations.size > 1 ? 'migrations:' : 'migration:'}"
@@ -462,8 +447,8 @@ db_namespace = namespace :db do
462
447
  namespace :schema do
463
448
  desc "Create a database schema file (either db/schema.rb or db/structure.sql, depending on `ENV['SCHEMA_FORMAT']` or `config.active_record.schema_format`)"
464
449
  task dump: :load_config do
465
- ActiveRecord::Tasks::DatabaseTasks.with_temporary_connection_for_each do |conn|
466
- db_config = conn.pool.db_config
450
+ ActiveRecord::Tasks::DatabaseTasks.with_temporary_pool_for_each do |pool|
451
+ db_config = pool.db_config
467
452
  schema_format = ENV.fetch("SCHEMA_FORMAT", ActiveRecord.schema_format).to_sym
468
453
  ActiveRecord::Tasks::DatabaseTasks.dump_schema(db_config, schema_format)
469
454
  end
@@ -480,8 +465,8 @@ db_namespace = namespace :db do
480
465
  ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |name|
481
466
  desc "Create a database schema file (either db/schema.rb or db/structure.sql, depending on `ENV['SCHEMA_FORMAT']` or `config.active_record.schema_format`) for #{name} database"
482
467
  task name => :load_config do
483
- ActiveRecord::Tasks::DatabaseTasks.with_temporary_connection_for_each(name: name) do |conn|
484
- db_config = conn.pool.db_config
468
+ ActiveRecord::Tasks::DatabaseTasks.with_temporary_pool_for_each(name: name) do |pool|
469
+ db_config = pool.db_config
485
470
  schema_format = ENV.fetch("SCHEMA_FORMAT", ActiveRecord.schema_format).to_sym
486
471
  ActiveRecord::Tasks::DatabaseTasks.dump_schema(db_config, schema_format)
487
472
  end
@@ -495,8 +480,8 @@ db_namespace = namespace :db do
495
480
  ActiveRecord::Tasks::DatabaseTasks.for_each(databases) do |name|
496
481
  desc "Load a database schema file (either db/schema.rb or db/structure.sql, depending on `ENV['SCHEMA_FORMAT']` or `config.active_record.schema_format`) into the #{name} database"
497
482
  task name => "db:test:purge:#{name}" do
498
- ActiveRecord::Tasks::DatabaseTasks.with_temporary_connection_for_each(name: name) do |conn|
499
- db_config = conn.pool.db_config
483
+ ActiveRecord::Tasks::DatabaseTasks.with_temporary_pool_for_each(name: name) do |pool|
484
+ db_config = pool.db_config
500
485
  schema_format = ENV.fetch("SCHEMA_FORMAT", ActiveRecord.schema_format).to_sym
501
486
  ActiveRecord::Tasks::DatabaseTasks.load_schema(db_config, schema_format)
502
487
  end
@@ -507,21 +492,18 @@ db_namespace = namespace :db do
507
492
  namespace :cache do
508
493
  desc "Create a db/schema_cache.yml file."
509
494
  task dump: :load_config do
510
- ActiveRecord::Tasks::DatabaseTasks.with_temporary_connection_for_each do |conn|
511
- db_config = conn.pool.db_config
512
- filename = ActiveRecord::Tasks::DatabaseTasks.cache_dump_filename(db_config.name, schema_cache_path: db_config.schema_cache_path)
495
+ ActiveRecord::Tasks::DatabaseTasks.with_temporary_pool_for_each do |pool|
496
+ db_config = pool.db_config
497
+ filename = ActiveRecord::Tasks::DatabaseTasks.cache_dump_filename(db_config)
513
498
 
514
- ActiveRecord::Tasks::DatabaseTasks.dump_schema_cache(conn, filename)
499
+ ActiveRecord::Tasks::DatabaseTasks.dump_schema_cache(pool, filename)
515
500
  end
516
501
  end
517
502
 
518
503
  desc "Clear a db/schema_cache.yml file."
519
504
  task clear: :load_config do
520
505
  ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env).each do |db_config|
521
- filename = ActiveRecord::Tasks::DatabaseTasks.cache_dump_filename(
522
- db_config.name,
523
- schema_cache_path: db_config.schema_cache_path,
524
- )
506
+ filename = ActiveRecord::Tasks::DatabaseTasks.cache_dump_filename(db_config)
525
507
  ActiveRecord::Tasks::DatabaseTasks.clear_schema_cache(
526
508
  filename,
527
509
  )
@@ -547,8 +529,8 @@ db_namespace = namespace :db do
547
529
  namespace :test do
548
530
  # desc "Recreate the test database from an existent schema file (schema.rb or structure.sql, depending on `ENV['SCHEMA_FORMAT']` or `config.active_record.schema_format`)"
549
531
  task load_schema: %w(db:test:purge) do
550
- ActiveRecord::Tasks::DatabaseTasks.with_temporary_connection_for_each(env: "test") do |conn|
551
- db_config = conn.pool.db_config
532
+ ActiveRecord::Tasks::DatabaseTasks.with_temporary_pool_for_each(env: "test") do |pool|
533
+ db_config = pool.db_config
552
534
  ActiveRecord::Schema.verbose = false
553
535
  schema_format = ENV.fetch("SCHEMA_FORMAT", ActiveRecord.schema_format).to_sym
554
536
  ActiveRecord::Tasks::DatabaseTasks.load_schema(db_config, schema_format)
@@ -573,8 +555,8 @@ db_namespace = namespace :db do
573
555
  # desc "Recreate the #{name} test database from an existent schema.rb file"
574
556
  namespace :load_schema do
575
557
  task name => "db:test:purge:#{name}" do
576
- ActiveRecord::Tasks::DatabaseTasks.with_temporary_connection_for_each(env: "test", name: name) do |conn|
577
- db_config = conn.pool.db_config
558
+ ActiveRecord::Tasks::DatabaseTasks.with_temporary_pool_for_each(env: "test", name: name) do |pool|
559
+ db_config = pool.db_config
578
560
  ActiveRecord::Schema.verbose = false
579
561
  schema_format = ENV.fetch("SCHEMA_FORMAT", ActiveRecord.schema_format).to_sym
580
562
  ActiveRecord::Tasks::DatabaseTasks.load_schema(db_config, schema_format)
@@ -585,8 +567,8 @@ db_namespace = namespace :db do
585
567
  # desc "Empty the #{name} test database"
586
568
  namespace :purge do
587
569
  task name => %w(load_config check_protected_environments) do
588
- ActiveRecord::Tasks::DatabaseTasks.with_temporary_connection_for_each(env: "test", name: name) do |conn|
589
- db_config = conn.pool.db_config
570
+ ActiveRecord::Tasks::DatabaseTasks.with_temporary_pool_for_each(env: "test", name: name) do |pool|
571
+ db_config = pool.db_config
590
572
  ActiveRecord::Tasks::DatabaseTasks.purge(db_config)
591
573
  end
592
574
  end
@@ -11,6 +11,7 @@ module ActiveRecord
11
11
  class_attribute :_reflections, instance_writer: false, default: {}
12
12
  class_attribute :aggregate_reflections, instance_writer: false, default: {}
13
13
  class_attribute :automatic_scope_inversing, instance_writer: false, default: false
14
+ class_attribute :automatically_invert_plural_associations, instance_writer: false, default: false
14
15
  end
15
16
 
16
17
  class << self
@@ -21,12 +22,12 @@ module ActiveRecord
21
22
 
22
23
  def add_reflection(ar, name, reflection)
23
24
  ar.clear_reflections_cache
24
- name = -name.to_s
25
+ name = name.to_sym
25
26
  ar._reflections = ar._reflections.except(name).merge!(name => reflection)
26
27
  end
27
28
 
28
29
  def add_aggregate_reflection(ar, name, reflection)
29
- ar.aggregate_reflections = ar.aggregate_reflections.merge(-name.to_s => reflection)
30
+ ar.aggregate_reflections = ar.aggregate_reflections.merge(name.to_sym => reflection)
30
31
  end
31
32
 
32
33
  private
@@ -67,7 +68,7 @@ module ActiveRecord
67
68
  # Account.reflect_on_aggregation(:balance) # => the balance AggregateReflection
68
69
  #
69
70
  def reflect_on_aggregation(aggregation)
70
- aggregate_reflections[aggregation.to_s]
71
+ aggregate_reflections[aggregation.to_sym]
71
72
  end
72
73
 
73
74
  # Returns a Hash of name of the reflection as the key and an AssociationReflection as the value.
@@ -75,6 +76,10 @@ module ActiveRecord
75
76
  # Account.reflections # => {"balance" => AggregateReflection}
76
77
  #
77
78
  def reflections
79
+ normalized_reflections.stringify_keys
80
+ end
81
+
82
+ def normalized_reflections # :nodoc:
78
83
  @__reflections ||= begin
79
84
  ref = {}
80
85
 
@@ -83,13 +88,13 @@ module ActiveRecord
83
88
 
84
89
  if parent_reflection
85
90
  parent_name = parent_reflection.name
86
- ref[parent_name.to_s] = parent_reflection
91
+ ref[parent_name] = parent_reflection
87
92
  else
88
93
  ref[name] = reflection
89
94
  end
90
95
  end
91
96
 
92
- ref
97
+ ref.freeze
93
98
  end
94
99
  end
95
100
 
@@ -104,7 +109,7 @@ module ActiveRecord
104
109
  # Account.reflect_on_all_associations(:has_many) # returns an array of all has_many associations
105
110
  #
106
111
  def reflect_on_all_associations(macro = nil)
107
- association_reflections = reflections.values
112
+ association_reflections = normalized_reflections.values
108
113
  association_reflections.select! { |reflection| reflection.macro == macro } if macro
109
114
  association_reflections
110
115
  end
@@ -115,16 +120,18 @@ module ActiveRecord
115
120
  # Invoice.reflect_on_association(:line_items).macro # returns :has_many
116
121
  #
117
122
  def reflect_on_association(association)
118
- reflections[association.to_s]
123
+ normalized_reflections[association.to_sym]
119
124
  end
120
125
 
121
126
  def _reflect_on_association(association) # :nodoc:
122
- _reflections[association.to_s]
127
+ _reflections[association.to_sym]
123
128
  end
124
129
 
125
130
  # Returns an array of AssociationReflection objects for all associations which have <tt>:autosave</tt> enabled.
126
131
  def reflect_on_all_autosave_associations
127
- reflections.values.select { |reflection| reflection.options[:autosave] }
132
+ reflections = normalized_reflections.values
133
+ reflections.select! { |reflection| reflection.options[:autosave] }
134
+ reflections
128
135
  end
129
136
 
130
137
  def clear_reflections_cache # :nodoc:
@@ -191,7 +198,7 @@ module ActiveRecord
191
198
  end
192
199
 
193
200
  def join_scope(table, foreign_table, foreign_klass)
194
- predicate_builder = predicate_builder(table)
201
+ predicate_builder = klass.predicate_builder.with(TableMetadata.new(klass, table))
195
202
  scope_chain_items = join_scopes(table, predicate_builder)
196
203
  klass_scope = klass_join_scope(table, predicate_builder)
197
204
 
@@ -217,7 +224,7 @@ module ActiveRecord
217
224
  klass_scope
218
225
  end
219
226
 
220
- def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc:
227
+ def join_scopes(table, predicate_builder = nil, klass = self.klass, record = nil) # :nodoc:
221
228
  if scope
222
229
  [scope_for(build_scope(table, predicate_builder, klass), record)]
223
230
  else
@@ -225,7 +232,7 @@ module ActiveRecord
225
232
  end
226
233
  end
227
234
 
228
- def klass_join_scope(table, predicate_builder) # :nodoc:
235
+ def klass_join_scope(table, predicate_builder = nil) # :nodoc:
229
236
  relation = build_scope(table, predicate_builder)
230
237
  klass.scope_for_association(relation)
231
238
  end
@@ -235,14 +242,16 @@ module ActiveRecord
235
242
  end
236
243
 
237
244
  def counter_cache_column
238
- @counter_cache_column ||= if belongs_to?
239
- if options[:counter_cache] == true
240
- -"#{active_record.name.demodulize.underscore.pluralize}_count"
241
- elsif options[:counter_cache]
242
- -options[:counter_cache].to_s
245
+ @counter_cache_column ||= begin
246
+ counter_cache = options[:counter_cache]
247
+
248
+ if belongs_to?
249
+ if counter_cache
250
+ counter_cache[:column] || -"#{active_record.name.demodulize.underscore.pluralize}_count"
251
+ end
252
+ else
253
+ -((counter_cache && -counter_cache[:column]) || "#{name}_count")
243
254
  end
244
- else
245
- -(options[:counter_cache]&.to_s || "#{name}_count")
246
255
  end
247
256
  end
248
257
 
@@ -291,7 +300,7 @@ module ActiveRecord
291
300
  inverse_of && inverse_which_updates_counter_cache == inverse_of
292
301
  end
293
302
 
294
- # Returns whether a counter cache should be used for this association.
303
+ # Returns whether this association has a counter cache.
295
304
  #
296
305
  # The counter_cache option must be given on either the owner or inverse
297
306
  # association, and the column must be present on the owner.
@@ -301,6 +310,17 @@ module ActiveRecord
301
310
  active_record.has_attribute?(counter_cache_column)
302
311
  end
303
312
 
313
+ # Returns whether this association has a counter cache and its column values were backfilled
314
+ # (and so it is used internally by methods like +size+/+any?+/etc).
315
+ def has_active_cached_counter?
316
+ return false unless has_cached_counter?
317
+
318
+ counter_cache = options[:counter_cache] ||
319
+ (inverse_which_updates_counter_cache && inverse_which_updates_counter_cache.options[:counter_cache])
320
+
321
+ counter_cache[:active] != false
322
+ end
323
+
304
324
  def counter_must_be_updated_by_has_many?
305
325
  !inverse_updates_counter_in_memory? && has_cached_counter?
306
326
  end
@@ -313,12 +333,8 @@ module ActiveRecord
313
333
  collect_join_chain
314
334
  end
315
335
 
316
- def build_scope(table, predicate_builder = predicate_builder(table), klass = self.klass)
317
- Relation.create(
318
- klass,
319
- table: table,
320
- predicate_builder: predicate_builder
321
- )
336
+ def build_scope(table, predicate_builder = nil, klass = self.klass)
337
+ Relation.create(klass, table:, predicate_builder:)
322
338
  end
323
339
 
324
340
  def strict_loading?
@@ -337,10 +353,6 @@ module ActiveRecord
337
353
  end
338
354
 
339
355
  private
340
- def predicate_builder(table)
341
- PredicateBuilder.new(TableMetadata.new(klass, table))
342
- end
343
-
344
356
  def primary_key(klass)
345
357
  klass.primary_key || raise(UnknownPrimaryKey.new(klass))
346
358
  end
@@ -377,12 +389,11 @@ module ActiveRecord
377
389
  super()
378
390
  @name = name
379
391
  @scope = scope
380
- @options = options
392
+ @options = normalize_options(options)
381
393
  @active_record = active_record
382
394
  @klass = options[:anonymous_class]
383
395
  @plural_name = active_record.pluralize_table_names ?
384
396
  name.to_s.pluralize : name.to_s
385
- validate_reflection!
386
397
  end
387
398
 
388
399
  def autosave=(autosave)
@@ -409,7 +420,15 @@ module ActiveRecord
409
420
  # a new association object. Use +build_association+ or +create_association+
410
421
  # instead. This allows plugins to hook into association object creation.
411
422
  def klass
412
- @klass ||= compute_class(class_name)
423
+ @klass ||= _klass(class_name)
424
+ end
425
+
426
+ def _klass(class_name) # :nodoc:
427
+ if active_record.name.demodulize == class_name
428
+ return compute_class("::#{class_name}") rescue NameError
429
+ end
430
+
431
+ compute_class(class_name)
413
432
  end
414
433
 
415
434
  def compute_class(name)
@@ -435,15 +454,24 @@ module ActiveRecord
435
454
  name.to_s.camelize
436
455
  end
437
456
 
438
- def validate_reflection!
439
- return unless options[:foreign_key].is_a?(Array)
457
+ def normalize_options(options)
458
+ counter_cache = options.delete(:counter_cache)
440
459
 
441
- message = <<~MSG.squish
442
- Passing #{options[:foreign_key]} array to :foreign_key option
443
- on the #{active_record}##{name} association is not supported.
444
- Use the query_constraints: #{options[:foreign_key]} option instead to represent a composite foreign key.
445
- MSG
446
- raise ArgumentError, message
460
+ if counter_cache
461
+ active = true
462
+
463
+ case counter_cache
464
+ when String, Symbol
465
+ column = -counter_cache.to_s
466
+ when Hash
467
+ active = counter_cache.fetch(:active, true)
468
+ column = counter_cache[:column]&.to_s
469
+ end
470
+
471
+ options[:counter_cache] = { active: active, column: column }
472
+ end
473
+
474
+ options
447
475
  end
448
476
  end
449
477
 
@@ -494,6 +522,17 @@ module ActiveRecord
494
522
  @foreign_key = nil
495
523
  @association_foreign_key = nil
496
524
  @association_primary_key = nil
525
+ if options[:query_constraints]
526
+ raise ConfigurationError, <<~MSG.squish
527
+ Setting `query_constraints:` option on `#{active_record}.#{macro} :#{name}` is not allowed.
528
+ To get the same behavior, use the `foreign_key` option instead.
529
+ MSG
530
+ end
531
+
532
+ # If the foreign key is an array, set query constraints options and don't use the foreign key
533
+ if options[:foreign_key].is_a?(Array)
534
+ options[:query_constraints] = options.delete(:foreign_key)
535
+ end
497
536
 
498
537
  ensure_option_not_given_as_class!(:class_name)
499
538
  end
@@ -503,7 +542,9 @@ module ActiveRecord
503
542
  if polymorphic?
504
543
  key = [key, owner._read_attribute(@foreign_type)]
505
544
  end
506
- klass.cached_find_by_statement(key, &block)
545
+ klass.with_connection do |connection|
546
+ klass.cached_find_by_statement(connection, key, &block)
547
+ end
507
548
  end
508
549
 
509
550
  def join_table
@@ -511,10 +552,14 @@ module ActiveRecord
511
552
  end
512
553
 
513
554
  def foreign_key(infer_from_inverse_of: true)
514
- @foreign_key ||= if options[:query_constraints]
515
- options[:query_constraints].map { |fk| fk.to_s.freeze }.freeze
516
- elsif options[:foreign_key]
517
- options[:foreign_key].to_s
555
+ @foreign_key ||= if options[:foreign_key]
556
+ if options[:foreign_key].is_a?(Array)
557
+ options[:foreign_key].map { |fk| -fk.to_s.freeze }.freeze
558
+ else
559
+ options[:foreign_key].to_s.freeze
560
+ end
561
+ elsif options[:query_constraints]
562
+ options[:query_constraints].map { |fk| -fk.to_s.freeze }.freeze
518
563
  else
519
564
  derived_fk = derive_foreign_key(infer_from_inverse_of: infer_from_inverse_of)
520
565
 
@@ -522,7 +567,12 @@ module ActiveRecord
522
567
  derived_fk = derive_fk_query_constraints(derived_fk)
523
568
  end
524
569
 
525
- derived_fk
570
+ if derived_fk.is_a?(Array)
571
+ derived_fk.map! { |fk| -fk.freeze }
572
+ derived_fk.freeze
573
+ else
574
+ -derived_fk.freeze
575
+ end
526
576
  end
527
577
  end
528
578
 
@@ -711,6 +761,10 @@ module ActiveRecord
711
761
 
712
762
  begin
713
763
  reflection = klass._reflect_on_association(inverse_name)
764
+ if !reflection && active_record.automatically_invert_plural_associations
765
+ plural_inverse_name = ActiveSupport::Inflector.pluralize(inverse_name)
766
+ reflection = klass._reflect_on_association(plural_inverse_name)
767
+ end
714
768
  rescue NameError => error
715
769
  raise unless error.name.to_s == class_name
716
770
 
@@ -720,7 +774,7 @@ module ActiveRecord
720
774
  end
721
775
 
722
776
  if valid_inverse_reflection?(reflection)
723
- inverse_name
777
+ reflection.name
724
778
  end
725
779
  end
726
780
  end
@@ -933,7 +987,7 @@ module ActiveRecord
933
987
  end
934
988
 
935
989
  def klass
936
- @klass ||= delegate_reflection.compute_class(class_name)
990
+ @klass ||= delegate_reflection._klass(class_name)
937
991
  end
938
992
 
939
993
  # Returns the source of the through reflection. It checks both a singularized
@@ -954,6 +1008,8 @@ module ActiveRecord
954
1008
  # # => <ActiveRecord::Reflection::BelongsToReflection: @name=:tag, @active_record=Tagging, @plural_name="tags">
955
1009
  #
956
1010
  def source_reflection
1011
+ return unless source_reflection_name
1012
+
957
1013
  through_reflection.klass._reflect_on_association(source_reflection_name)
958
1014
  end
959
1015
 
@@ -1006,7 +1062,7 @@ module ActiveRecord
1006
1062
  source_reflection.scopes + super
1007
1063
  end
1008
1064
 
1009
- def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc:
1065
+ def join_scopes(table, predicate_builder = nil, klass = self.klass, record = nil) # :nodoc:
1010
1066
  source_reflection.join_scopes(table, predicate_builder, klass, record) + super
1011
1067
  end
1012
1068
 
@@ -1111,7 +1167,7 @@ module ActiveRecord
1111
1167
  end
1112
1168
 
1113
1169
  if parent_reflection.nil?
1114
- reflections = active_record.reflections.keys.map(&:to_sym)
1170
+ reflections = active_record.normalized_reflections.keys
1115
1171
 
1116
1172
  if reflections.index(through_reflection.name) > reflections.index(name)
1117
1173
  raise HasManyThroughOrderError.new(active_record.name, self, through_reflection)
@@ -1179,8 +1235,11 @@ module ActiveRecord
1179
1235
  @previous_reflection = previous_reflection
1180
1236
  end
1181
1237
 
1182
- def join_scopes(table, predicate_builder, klass = self.klass, record = nil) # :nodoc:
1183
- scopes = @previous_reflection.join_scopes(table, predicate_builder, klass, record) + super
1238
+ def join_scopes(table, predicate_builder = nil, klass = self.klass, record = nil) # :nodoc:
1239
+ scopes = super
1240
+ unless @previous_reflection.through_reflection?
1241
+ scopes += @previous_reflection.join_scopes(table, predicate_builder, klass, record)
1242
+ end
1184
1243
  scopes << build_scope(table, predicate_builder, klass).instance_exec(record, &source_type_scope)
1185
1244
  end
1186
1245