activerecord 7.1.5.2 → 8.1.2.1

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 (244) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +567 -2443
  3. data/README.rdoc +16 -16
  4. data/examples/performance.rb +2 -2
  5. data/lib/active_record/association_relation.rb +3 -2
  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 +31 -8
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  10. data/lib/active_record/associations/builder/association.rb +23 -11
  11. data/lib/active_record/associations/builder/belongs_to.rb +18 -4
  12. data/lib/active_record/associations/builder/collection_association.rb +7 -3
  13. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
  14. data/lib/active_record/associations/builder/has_many.rb +3 -4
  15. data/lib/active_record/associations/builder/has_one.rb +3 -4
  16. data/lib/active_record/associations/builder/singular_association.rb +33 -5
  17. data/lib/active_record/associations/collection_association.rb +17 -9
  18. data/lib/active_record/associations/collection_proxy.rb +36 -5
  19. data/lib/active_record/associations/deprecation.rb +88 -0
  20. data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
  21. data/lib/active_record/associations/errors.rb +268 -0
  22. data/lib/active_record/associations/has_many_association.rb +1 -1
  23. data/lib/active_record/associations/has_many_through_association.rb +10 -3
  24. data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
  25. data/lib/active_record/associations/join_dependency.rb +4 -2
  26. data/lib/active_record/associations/nested_error.rb +47 -0
  27. data/lib/active_record/associations/preloader/association.rb +4 -3
  28. data/lib/active_record/associations/preloader/batch.rb +7 -1
  29. data/lib/active_record/associations/preloader/branch.rb +8 -1
  30. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  31. data/lib/active_record/associations/singular_association.rb +14 -3
  32. data/lib/active_record/associations/through_association.rb +1 -1
  33. data/lib/active_record/associations.rb +250 -315
  34. data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
  35. data/lib/active_record/attribute_assignment.rb +0 -2
  36. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  37. data/lib/active_record/attribute_methods/primary_key.rb +25 -61
  38. data/lib/active_record/attribute_methods/query.rb +34 -0
  39. data/lib/active_record/attribute_methods/read.rb +1 -13
  40. data/lib/active_record/attribute_methods/serialization.rb +20 -27
  41. data/lib/active_record/attribute_methods/time_zone_conversion.rb +19 -20
  42. data/lib/active_record/attribute_methods.rb +71 -75
  43. data/lib/active_record/attributes.rb +96 -68
  44. data/lib/active_record/autosave_association.rb +93 -58
  45. data/lib/active_record/base.rb +5 -7
  46. data/lib/active_record/callbacks.rb +1 -1
  47. data/lib/active_record/coders/json.rb +14 -5
  48. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +50 -126
  49. data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -4
  50. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +52 -13
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +681 -135
  52. data/lib/active_record/connection_adapters/abstract/database_statements.rb +165 -86
  53. data/lib/active_record/connection_adapters/abstract/query_cache.rb +226 -78
  54. data/lib/active_record/connection_adapters/abstract/quoting.rb +79 -114
  55. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +11 -7
  56. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +37 -36
  57. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
  58. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +136 -34
  59. data/lib/active_record/connection_adapters/abstract/transaction.rb +163 -68
  60. data/lib/active_record/connection_adapters/abstract_adapter.rb +197 -129
  61. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +112 -63
  62. data/lib/active_record/connection_adapters/column.rb +17 -4
  63. data/lib/active_record/connection_adapters/mysql/database_statements.rb +11 -3
  64. data/lib/active_record/connection_adapters/mysql/quoting.rb +50 -57
  65. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
  66. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +41 -10
  67. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +79 -46
  68. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +97 -101
  69. data/lib/active_record/connection_adapters/mysql2_adapter.rb +14 -33
  70. data/lib/active_record/connection_adapters/pool_config.rb +14 -13
  71. data/lib/active_record/connection_adapters/postgresql/column.rb +4 -0
  72. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +98 -44
  73. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -3
  74. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  75. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
  77. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  78. data/lib/active_record/connection_adapters/postgresql/quoting.rb +79 -68
  79. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
  80. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +9 -17
  81. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +44 -41
  82. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +69 -32
  83. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +155 -75
  84. data/lib/active_record/connection_adapters/postgresql_adapter.rb +104 -121
  85. data/lib/active_record/connection_adapters/schema_cache.rb +126 -133
  86. data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
  87. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +91 -95
  88. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -54
  89. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +16 -0
  90. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
  91. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +43 -2
  92. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +38 -15
  93. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +231 -111
  94. data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
  95. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +40 -69
  96. data/lib/active_record/connection_adapters/trilogy_adapter.rb +19 -65
  97. data/lib/active_record/connection_adapters.rb +66 -0
  98. data/lib/active_record/connection_handling.rb +88 -46
  99. data/lib/active_record/core.rb +142 -58
  100. data/lib/active_record/counter_cache.rb +52 -18
  101. data/lib/active_record/database_configurations/connection_url_resolver.rb +9 -2
  102. data/lib/active_record/database_configurations/database_config.rb +28 -5
  103. data/lib/active_record/database_configurations/hash_config.rb +103 -41
  104. data/lib/active_record/database_configurations/url_config.rb +30 -1
  105. data/lib/active_record/database_configurations.rb +8 -4
  106. data/lib/active_record/delegated_type.rb +43 -19
  107. data/lib/active_record/dynamic_matchers.rb +56 -71
  108. data/lib/active_record/encryption/config.rb +3 -1
  109. data/lib/active_record/encryption/encryptable_record.rb +12 -12
  110. data/lib/active_record/encryption/encrypted_attribute_type.rb +35 -6
  111. data/lib/active_record/encryption/encryptor.rb +58 -22
  112. data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
  113. data/lib/active_record/encryption/key_provider.rb +1 -1
  114. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  115. data/lib/active_record/encryption/message_serializer.rb +4 -0
  116. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  117. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  118. data/lib/active_record/encryption/scheme.rb +9 -2
  119. data/lib/active_record/enum.rb +57 -36
  120. data/lib/active_record/errors.rb +76 -26
  121. data/lib/active_record/explain.rb +14 -25
  122. data/lib/active_record/explain_registry.rb +51 -2
  123. data/lib/active_record/filter_attribute_handler.rb +73 -0
  124. data/lib/active_record/fixture_set/table_row.rb +19 -2
  125. data/lib/active_record/fixtures.rb +39 -35
  126. data/lib/active_record/future_result.rb +21 -13
  127. data/lib/active_record/gem_version.rb +3 -3
  128. data/lib/active_record/inheritance.rb +5 -3
  129. data/lib/active_record/insert_all.rb +28 -20
  130. data/lib/active_record/integration.rb +4 -1
  131. data/lib/active_record/internal_metadata.rb +48 -34
  132. data/lib/active_record/locking/optimistic.rb +15 -7
  133. data/lib/active_record/locking/pessimistic.rb +5 -0
  134. data/lib/active_record/log_subscriber.rb +3 -34
  135. data/lib/active_record/message_pack.rb +1 -1
  136. data/lib/active_record/middleware/shard_selector.rb +34 -17
  137. data/lib/active_record/migration/command_recorder.rb +46 -14
  138. data/lib/active_record/migration/compatibility.rb +40 -25
  139. data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
  140. data/lib/active_record/migration/default_strategy.rb +4 -5
  141. data/lib/active_record/migration/pending_migration_connection.rb +2 -2
  142. data/lib/active_record/migration.rb +119 -103
  143. data/lib/active_record/model_schema.rb +66 -78
  144. data/lib/active_record/nested_attributes.rb +17 -9
  145. data/lib/active_record/persistence.rb +161 -454
  146. data/lib/active_record/query_cache.rb +33 -15
  147. data/lib/active_record/query_logs.rb +106 -39
  148. data/lib/active_record/query_logs_formatter.rb +17 -28
  149. data/lib/active_record/querying.rb +28 -16
  150. data/lib/active_record/railtie.rb +61 -74
  151. data/lib/active_record/railties/controller_runtime.rb +19 -5
  152. data/lib/active_record/railties/databases.rake +64 -78
  153. data/lib/active_record/railties/job_checkpoints.rb +15 -0
  154. data/lib/active_record/railties/job_runtime.rb +10 -11
  155. data/lib/active_record/reflection.rb +147 -53
  156. data/lib/active_record/relation/batches/batch_enumerator.rb +19 -5
  157. data/lib/active_record/relation/batches.rb +153 -73
  158. data/lib/active_record/relation/calculations.rb +146 -96
  159. data/lib/active_record/relation/delegation.rb +30 -23
  160. data/lib/active_record/relation/finder_methods.rb +64 -34
  161. data/lib/active_record/relation/merger.rb +12 -14
  162. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  163. data/lib/active_record/relation/predicate_builder/association_query_value.rb +19 -11
  164. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +8 -8
  165. data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
  166. data/lib/active_record/relation/predicate_builder.rb +25 -10
  167. data/lib/active_record/relation/query_attribute.rb +4 -2
  168. data/lib/active_record/relation/query_methods.rb +351 -124
  169. data/lib/active_record/relation/spawn_methods.rb +9 -25
  170. data/lib/active_record/relation/where_clause.rb +17 -30
  171. data/lib/active_record/relation.rb +590 -124
  172. data/lib/active_record/result.rb +131 -59
  173. data/lib/active_record/runtime_registry.rb +44 -21
  174. data/lib/active_record/sanitization.rb +33 -25
  175. data/lib/active_record/schema.rb +8 -6
  176. data/lib/active_record/schema_dumper.rb +66 -31
  177. data/lib/active_record/schema_migration.rb +31 -14
  178. data/lib/active_record/scoping/named.rb +6 -2
  179. data/lib/active_record/scoping.rb +0 -1
  180. data/lib/active_record/secure_token.rb +3 -3
  181. data/lib/active_record/signed_id.rb +68 -20
  182. data/lib/active_record/statement_cache.rb +29 -25
  183. data/lib/active_record/store.rb +51 -22
  184. data/lib/active_record/structured_event_subscriber.rb +85 -0
  185. data/lib/active_record/table_metadata.rb +7 -33
  186. data/lib/active_record/tasks/abstract_tasks.rb +76 -0
  187. data/lib/active_record/tasks/database_tasks.rb +118 -90
  188. data/lib/active_record/tasks/mysql_database_tasks.rb +3 -42
  189. data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
  190. data/lib/active_record/tasks/sqlite_database_tasks.rb +17 -28
  191. data/lib/active_record/test_databases.rb +14 -4
  192. data/lib/active_record/test_fixtures.rb +123 -89
  193. data/lib/active_record/testing/query_assertions.rb +127 -0
  194. data/lib/active_record/timestamp.rb +6 -4
  195. data/lib/active_record/token_for.rb +22 -12
  196. data/lib/active_record/touch_later.rb +1 -1
  197. data/lib/active_record/transaction.rb +129 -0
  198. data/lib/active_record/transactions.rb +97 -18
  199. data/lib/active_record/translation.rb +0 -2
  200. data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
  201. data/lib/active_record/type/internal/timezone.rb +7 -0
  202. data/lib/active_record/type/json.rb +15 -2
  203. data/lib/active_record/type/serialized.rb +12 -7
  204. data/lib/active_record/type/type_map.rb +1 -1
  205. data/lib/active_record/type_caster/connection.rb +5 -4
  206. data/lib/active_record/validations/associated.rb +10 -4
  207. data/lib/active_record/validations/uniqueness.rb +23 -18
  208. data/lib/active_record/validations.rb +4 -1
  209. data/lib/active_record.rb +203 -59
  210. data/lib/arel/alias_predication.rb +3 -1
  211. data/lib/arel/collectors/bind.rb +4 -2
  212. data/lib/arel/collectors/composite.rb +7 -0
  213. data/lib/arel/collectors/sql_string.rb +2 -2
  214. data/lib/arel/collectors/substitute_binds.rb +3 -3
  215. data/lib/arel/crud.rb +8 -11
  216. data/lib/arel/delete_manager.rb +5 -0
  217. data/lib/arel/nodes/binary.rb +1 -7
  218. data/lib/arel/nodes/bound_sql_literal.rb +9 -5
  219. data/lib/arel/nodes/count.rb +2 -2
  220. data/lib/arel/nodes/delete_statement.rb +4 -2
  221. data/lib/arel/nodes/function.rb +4 -10
  222. data/lib/arel/nodes/named_function.rb +2 -2
  223. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  224. data/lib/arel/nodes/node.rb +6 -5
  225. data/lib/arel/nodes/sql_literal.rb +8 -1
  226. data/lib/arel/nodes/update_statement.rb +4 -2
  227. data/lib/arel/nodes.rb +2 -4
  228. data/lib/arel/predications.rb +1 -1
  229. data/lib/arel/select_manager.rb +14 -5
  230. data/lib/arel/table.rb +3 -7
  231. data/lib/arel/tree_manager.rb +3 -2
  232. data/lib/arel/update_manager.rb +7 -1
  233. data/lib/arel/visitors/dot.rb +3 -3
  234. data/lib/arel/visitors/mysql.rb +9 -4
  235. data/lib/arel/visitors/postgresql.rb +56 -12
  236. data/lib/arel/visitors/sqlite.rb +80 -8
  237. data/lib/arel/visitors/to_sql.rb +34 -37
  238. data/lib/arel.rb +10 -4
  239. data/lib/rails/generators/active_record/application_record/USAGE +1 -1
  240. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  241. metadata +23 -14
  242. data/lib/active_record/explain_subscriber.rb +0 -34
  243. data/lib/active_record/normalization.rb +0 -167
  244. data/lib/active_record/relation/record_fetch_warning.rb +0 -49
@@ -1,23 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+
3
4
  module ActiveRecord
5
+ include ActiveSupport::Deprecation::DeprecatedConstantAccessor
6
+
4
7
  # = Active Record Errors
5
8
  #
6
9
  # Generic Active Record exception class.
7
10
  class ActiveRecordError < StandardError
8
11
  end
9
12
 
10
- # DEPRECATED: Previously raised when trying to use a feature in Active Record which
11
- # requires Active Job but the gem is not present. Now raises a NameError.
12
- include ActiveSupport::Deprecation::DeprecatedConstantAccessor
13
- DeprecatedActiveJobRequiredError = Class.new(ActiveRecordError) # :nodoc:
14
- deprecate_constant "ActiveJobRequiredError", "ActiveRecord::DeprecatedActiveJobRequiredError",
15
- message: "ActiveRecord::ActiveJobRequiredError has been deprecated. If Active Job is not present, a NameError will be raised instead.",
16
- deprecator: ActiveRecord.deprecator
17
-
18
13
  # Raised when the single-table inheritance mechanism fails to locate the subclass
19
14
  # (for example due to improper usage of column that
20
- # {ActiveRecord::Base.inheritance_column}[rdoc-ref:ModelSchema::ClassMethods#inheritance_column]
15
+ # {ActiveRecord::Base.inheritance_column}[rdoc-ref:ModelSchema.inheritance_column]
21
16
  # points to).
22
17
  class SubclassNotFound < ActiveRecordError
23
18
  end
@@ -66,7 +61,7 @@ module ActiveRecord
66
61
  end
67
62
 
68
63
  # Raised when connection to the database could not been established (for example when
69
- # {ActiveRecord::Base.connection=}[rdoc-ref:ConnectionHandling#connection]
64
+ # {ActiveRecord::Base.lease_connection=}[rdoc-ref:ConnectionHandling#lease_connection]
70
65
  # is given a +nil+ object).
71
66
  class ConnectionNotEstablished < AdapterError
72
67
  def initialize(message = nil, connection_pool: nil)
@@ -88,6 +83,19 @@ module ActiveRecord
88
83
  class ConnectionTimeoutError < ConnectionNotEstablished
89
84
  end
90
85
 
86
+ # Raised when a database connection pool is requested but
87
+ # has not been defined.
88
+ class ConnectionNotDefined < ConnectionNotEstablished
89
+ def initialize(message = nil, connection_name: nil, role: nil, shard: nil)
90
+ super(message)
91
+ @connection_name = connection_name
92
+ @role = role
93
+ @shard = shard
94
+ end
95
+
96
+ attr_reader :connection_name, :role, :shard
97
+ end
98
+
91
99
  # Raised when connection to the database could not been established because it was not
92
100
  # able to connect to the host or when the authorization failed.
93
101
  class DatabaseConnectionError < ConnectionNotEstablished
@@ -137,8 +145,18 @@ module ActiveRecord
137
145
  end
138
146
 
139
147
  # Raised by {ActiveRecord::Base#save!}[rdoc-ref:Persistence#save!] and
140
- # {ActiveRecord::Base.create!}[rdoc-ref:Persistence::ClassMethods#create!]
141
- # methods when a record is invalid and cannot be saved.
148
+ # {ActiveRecord::Base.update_attribute!}[rdoc-ref:Persistence#update_attribute!]
149
+ # methods when a record failed to validate or cannot be saved due to any of the
150
+ # <tt>before_*</tt> callbacks throwing +:abort+. See
151
+ # ActiveRecord::Callbacks for further details.
152
+ #
153
+ # class Product < ActiveRecord::Base
154
+ # before_save do
155
+ # throw :abort if price < 0
156
+ # end
157
+ # end
158
+ #
159
+ # Product.create! # => raises an ActiveRecord::RecordNotSaved
142
160
  class RecordNotSaved < ActiveRecordError
143
161
  attr_reader :record
144
162
 
@@ -149,15 +167,17 @@ module ActiveRecord
149
167
  end
150
168
 
151
169
  # Raised by {ActiveRecord::Base#destroy!}[rdoc-ref:Persistence#destroy!]
152
- # when a call to {#destroy}[rdoc-ref:Persistence#destroy]
153
- # would return false.
170
+ # when a record cannot be destroyed due to any of the
171
+ # <tt>before_destroy</tt> callbacks throwing +:abort+. See
172
+ # ActiveRecord::Callbacks for further details.
154
173
  #
155
- # begin
156
- # complex_operation_that_internally_calls_destroy!
157
- # rescue ActiveRecord::RecordNotDestroyed => invalid
158
- # puts invalid.record.errors
174
+ # class User < ActiveRecord::Base
175
+ # before_destroy do
176
+ # throw :abort if still_active?
177
+ # end
159
178
  # end
160
179
  #
180
+ # User.first.destroy! # => raises an ActiveRecord::RecordNotDestroyed
161
181
  class RecordNotDestroyed < ActiveRecordError
162
182
  attr_reader :record
163
183
 
@@ -272,6 +292,14 @@ module ActiveRecord
272
292
  class NotNullViolation < StatementInvalid
273
293
  end
274
294
 
295
+ # Raised when a record cannot be inserted or updated because it would violate a check constraint.
296
+ class CheckViolation < StatementInvalid
297
+ end
298
+
299
+ # Raised when a record cannot be inserted or updated because it would violate an exclusion constraint.
300
+ class ExclusionViolation < StatementInvalid
301
+ end
302
+
275
303
  # Raised when a record cannot be inserted or updated because a value too long for a column type.
276
304
  class ValueTooLong < StatementInvalid
277
305
  end
@@ -318,15 +346,15 @@ module ActiveRecord
318
346
  class << self
319
347
  def db_error(db_name)
320
348
  NoDatabaseError.new(<<~MSG)
321
- We could not find your database: #{db_name}. Available database configurations can be found in config/database.yml.
349
+ Database not found: #{db_name}. Available database configurations can be found in config/database.yml.
322
350
 
323
351
  To resolve this error:
324
352
 
325
- - Did you not create the database, or did you delete it? To create the database, run:
353
+ - Create the database by running:
326
354
 
327
355
  bin/rails db:create
328
356
 
329
- - Has the database name changed? Verify that config/database.yml contains the correct database name.
357
+ - Verify that config/database.yml contains the correct database name.
330
358
  MSG
331
359
  end
332
360
  end
@@ -374,6 +402,12 @@ module ActiveRecord
374
402
  end
375
403
 
376
404
  # Raised on attempt to lazily load records that are marked as strict loading.
405
+ #
406
+ # You can resolve this error by eager loading marked records before accessing
407
+ # them. The
408
+ # {Eager Loading Associations}[https://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations]
409
+ # guide covers solutions, such as using
410
+ # {ActiveRecord::Base.includes}[rdoc-ref:QueryMethods#includes].
377
411
  class StrictLoadingViolationError < ActiveRecordError
378
412
  end
379
413
 
@@ -417,7 +451,7 @@ module ActiveRecord
417
451
  UnknownAttributeError = ActiveModel::UnknownAttributeError
418
452
 
419
453
  # Raised when an error occurred while doing a mass assignment to an attribute through the
420
- # {ActiveRecord::Base#attributes=}[rdoc-ref:AttributeAssignment#attributes=] method.
454
+ # {ActiveRecord::Base#attributes=}[rdoc-ref:ActiveModel::AttributeAssignment#attributes=] method.
421
455
  # The exception has an +attribute+ property that is the name of the offending attribute.
422
456
  class AttributeAssignmentError < ActiveRecordError
423
457
  attr_reader :exception, :attribute
@@ -430,7 +464,7 @@ module ActiveRecord
430
464
  end
431
465
 
432
466
  # Raised when there are multiple errors while doing a mass assignment through the
433
- # {ActiveRecord::Base#attributes=}[rdoc-ref:AttributeAssignment#attributes=]
467
+ # {ActiveRecord::Base#attributes=}[rdoc-ref:ActiveModel::AttributeAssignment#attributes=]
434
468
  # method. The exception has an +errors+ property that contains an array of AttributeAssignmentError
435
469
  # objects, each corresponding to the error while assigning to an attribute.
436
470
  class MultiparameterAssignmentErrors < ActiveRecordError
@@ -463,12 +497,13 @@ module ActiveRecord
463
497
  # end
464
498
  #
465
499
  # relation = Task.all
500
+ # relation.load
466
501
  # relation.loaded? # => true
467
502
  #
468
503
  # # Methods which try to mutate a loaded relation fail.
469
- # relation.where!(title: 'TODO') # => ActiveRecord::ImmutableRelation
470
- # relation.limit!(5) # => ActiveRecord::ImmutableRelation
471
- class ImmutableRelation < ActiveRecordError
504
+ # relation.where!(title: 'TODO') # => ActiveRecord::UnmodifiableRelation
505
+ # relation.limit!(5) # => ActiveRecord::UnmodifiableRelation
506
+ class UnmodifiableRelation < ActiveRecordError
472
507
  end
473
508
 
474
509
  # TransactionIsolationError will be raised under the following conditions:
@@ -525,6 +560,11 @@ module ActiveRecord
525
560
  class Deadlocked < TransactionRollbackError
526
561
  end
527
562
 
563
+ # MissingRequiredOrderError is raised when a relation requires ordering but
564
+ # lacks any +order+ values in scope or any model order columns to use.
565
+ class MissingRequiredOrderError < ActiveRecordError
566
+ end
567
+
528
568
  # IrreversibleOrderError is raised when a relation's order is too complex for
529
569
  # +reverse_order+ to automatically reverse.
530
570
  class IrreversibleOrderError < ActiveRecordError
@@ -577,4 +617,14 @@ module ActiveRecord
577
617
  # values, such as request parameters or model attributes to query methods.
578
618
  class UnknownAttributeReference < ActiveRecordError
579
619
  end
620
+
621
+ # DatabaseVersionError will be raised when the database version is not supported, or when
622
+ # the database version cannot be determined.
623
+ class DatabaseVersionError < ActiveRecordError
624
+ end
625
+
626
+ class DeprecatedAssociationError < ActiveRecordError
627
+ end
580
628
  end
629
+
630
+ require "active_record/associations/errors"
@@ -7,7 +7,7 @@ module ActiveRecord
7
7
  # Executes the block with the collect flag enabled. Queries are collected
8
8
  # asynchronously by the subscriber and returned.
9
9
  def collecting_queries_for_explain # :nodoc:
10
- ExplainRegistry.collect = true
10
+ ExplainRegistry.start
11
11
  yield
12
12
  ExplainRegistry.queries
13
13
  ensure
@@ -17,16 +17,17 @@ module ActiveRecord
17
17
  # Makes the adapter execute EXPLAIN for the tuples of queries and bindings.
18
18
  # Returns a formatted string ready to be logged.
19
19
  def exec_explain(queries, options = []) # :nodoc:
20
- str = queries.map do |sql, binds|
21
- msg = +"#{build_explain_clause(options)} #{sql}"
22
- unless binds.empty?
23
- msg << " "
24
- msg << binds.map { |attr| render_bind(attr) }.inspect
25
- end
26
- msg << "\n"
27
- msg << connection_explain(sql, binds, options)
28
- end.join("\n")
29
-
20
+ str = with_connection do |c|
21
+ queries.map do |sql, binds|
22
+ msg = +"#{build_explain_clause(c, options)} #{sql}"
23
+ unless binds.empty?
24
+ msg << " "
25
+ msg << binds.map { |attr| render_bind(c, attr) }.inspect
26
+ end
27
+ msg << "\n"
28
+ msg << c.explain(sql, binds, options)
29
+ end.join("\n")
30
+ end
30
31
  # Overriding inspect to be more human readable, especially in the console.
31
32
  def str.inspect
32
33
  self
@@ -36,7 +37,7 @@ module ActiveRecord
36
37
  end
37
38
 
38
39
  private
39
- def render_bind(attr)
40
+ def render_bind(connection, attr)
40
41
  if ActiveModel::Attribute === attr
41
42
  value = if attr.type.binary? && attr.value
42
43
  "<#{attr.value_for_database.to_s.bytesize} bytes of binary data>"
@@ -51,24 +52,12 @@ module ActiveRecord
51
52
  [attr&.name, value]
52
53
  end
53
54
 
54
- def build_explain_clause(options = [])
55
+ def build_explain_clause(connection, options = [])
55
56
  if connection.respond_to?(:build_explain_clause, true)
56
57
  connection.build_explain_clause(options)
57
58
  else
58
59
  "EXPLAIN for:"
59
60
  end
60
61
  end
61
-
62
- def connection_explain(sql, binds, options)
63
- if connection.method(:explain).parameters.size == 2
64
- ActiveRecord.deprecator.warn(<<~MSG.squish)
65
- The current database adapter, #{connection.adapter_name}, does not support explain options.
66
- To remove this warning, the adapter must implement `build_explain_clause(options = [])`.
67
- MSG
68
- connection.explain(sql, binds)
69
- else
70
- connection.explain(sql, binds, options)
71
- end
72
- end
73
62
  end
74
63
  end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/module/delegation"
4
3
 
5
4
  module ActiveRecord
6
5
  # This is a thread locals registry for EXPLAIN. For example
@@ -9,8 +8,53 @@ module ActiveRecord
9
8
  #
10
9
  # returns the collected queries local to the current thread.
11
10
  class ExplainRegistry # :nodoc:
11
+ class Subscriber
12
+ MUTEX = Mutex.new
13
+ @subscribed = false
14
+
15
+ class << self
16
+ def ensure_subscribed
17
+ return if @subscribed
18
+ MUTEX.synchronize do
19
+ return if @subscribed
20
+
21
+ ActiveSupport::Notifications.subscribe("sql.active_record", new)
22
+ @subscribed = true
23
+ end
24
+ end
25
+ end
26
+
27
+ def start(name, id, payload)
28
+ # unused
29
+ end
30
+
31
+ def finish(name, id, payload)
32
+ if ExplainRegistry.collect? && !ignore_payload?(payload)
33
+ ExplainRegistry.queries << payload.values_at(:sql, :binds)
34
+ end
35
+ end
36
+
37
+ def silenced?(_name)
38
+ !ExplainRegistry.collect?
39
+ end
40
+
41
+ # SCHEMA queries cannot be EXPLAINed, also we do not want to run EXPLAIN on
42
+ # our own EXPLAINs no matter how loopingly beautiful that would be.
43
+ #
44
+ # On the other hand, we want to monitor the performance of our real database
45
+ # queries, not the performance of the access to the query cache.
46
+ IGNORED_PAYLOADS = %w(SCHEMA EXPLAIN)
47
+ EXPLAINED_SQLS = /\A\s*(\/\*.*\*\/)?\s*(with|select|update|delete|insert)\b/i
48
+ def ignore_payload?(payload)
49
+ payload[:exception] ||
50
+ payload[:cached] ||
51
+ IGNORED_PAYLOADS.include?(payload[:name]) ||
52
+ !payload[:sql].match?(EXPLAINED_SQLS)
53
+ end
54
+ end
55
+
12
56
  class << self
13
- delegate :reset, :collect, :collect=, :collect?, :queries, to: :instance
57
+ delegate :start, :reset, :collect, :collect=, :collect?, :queries, to: :instance
14
58
 
15
59
  private
16
60
  def instance
@@ -25,6 +69,11 @@ module ActiveRecord
25
69
  reset
26
70
  end
27
71
 
72
+ def start
73
+ Subscriber.ensure_subscribed
74
+ @collect = true
75
+ end
76
+
28
77
  def collect?
29
78
  @collect
30
79
  end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ class FilterAttributeHandler # :nodoc:
5
+ class << self
6
+ def on_sensitive_attribute_declared(&block)
7
+ @sensitive_attribute_declaration_listeners ||= Concurrent::Array.new
8
+ @sensitive_attribute_declaration_listeners << block
9
+ end
10
+
11
+ def sensitive_attribute_was_declared(klass, list)
12
+ @sensitive_attribute_declaration_listeners&.each do |block|
13
+ block.call(klass, list)
14
+ end
15
+ end
16
+ end
17
+
18
+ def initialize(app)
19
+ @app = app
20
+ @attributes_by_class = Concurrent::Map.new
21
+ @collecting = true
22
+ end
23
+
24
+ def enable
25
+ install_collecting_hook
26
+
27
+ apply_collected_attributes
28
+ @collecting = false
29
+ end
30
+
31
+ private
32
+ attr_reader :app
33
+
34
+ def install_collecting_hook
35
+ self.class.on_sensitive_attribute_declared do |klass, list|
36
+ attribute_was_declared(klass, list)
37
+ end
38
+ end
39
+
40
+ def attribute_was_declared(klass, list)
41
+ if collecting?
42
+ collect_for_later(klass, list)
43
+ else
44
+ apply_filter(klass, list)
45
+ end
46
+ end
47
+
48
+ def apply_collected_attributes
49
+ @attributes_by_class.each do |klass, list|
50
+ apply_filter(klass, list)
51
+ end
52
+ end
53
+
54
+ def collecting?
55
+ @collecting
56
+ end
57
+
58
+ def collect_for_later(klass, list)
59
+ @attributes_by_class[klass] ||= Concurrent::Array.new
60
+ @attributes_by_class[klass] += list
61
+ end
62
+
63
+ def apply_filter(klass, list)
64
+ list.each do |attribute|
65
+ next if klass.abstract_class? || klass == Base
66
+
67
+ klass_name = klass.name ? klass.model_name.element : nil
68
+ filter = [klass_name, attribute.to_s].compact.join(".")
69
+ app.config.filter_parameters << filter unless app.config.filter_parameters.include?(filter)
70
+ end
71
+ end
72
+ end
73
+ end
@@ -193,8 +193,25 @@ module ActiveRecord
193
193
 
194
194
  targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
195
195
  joins = targets.map do |target|
196
- join = { lhs_key => @row[model_metadata.primary_key_name],
197
- rhs_key => ActiveRecord::FixtureSet.identify(target, column_type) }
196
+ join = {}
197
+
198
+ if rhs_key.is_a?(Array)
199
+ composite_key = ActiveRecord::FixtureSet.composite_identify(target, rhs_key)
200
+ composite_key.each do |column, value|
201
+ join[column] = value
202
+ end
203
+ else
204
+ join[rhs_key] = ActiveRecord::FixtureSet.identify(target, column_type)
205
+ end
206
+
207
+ if lhs_key.is_a?(Array)
208
+ lhs_key.zip(model_metadata.primary_key_name).each do |fkey, pkey|
209
+ join[fkey] = @row[pkey]
210
+ end
211
+ else
212
+ join[lhs_key] = @row[model_metadata.primary_key_name]
213
+ end
214
+
198
215
  association.timestamp_column_names.each do |col|
199
216
  join[col] = @now
200
217
  end
@@ -2,8 +2,6 @@
2
2
 
3
3
  require "erb"
4
4
  require "yaml"
5
- require "zlib"
6
- require "set"
7
5
  require "active_support/dependencies"
8
6
  require "active_support/core_ext/digest/uuid"
9
7
  require "active_record/test_fixtures"
@@ -110,6 +108,12 @@ module ActiveRecord
110
108
  # assert_raise(StandardError) { web_sites(:reddit) }
111
109
  # end
112
110
  #
111
+ # If the model names conflicts with a +TestCase+ methods, you can use the generic +fixture+ accessor
112
+ #
113
+ # test "generic find" do
114
+ # assert_equal "Ruby on Rails", fixture(:web_sites, :rubyonrails).name
115
+ # end
116
+ #
113
117
  # Alternatively, you may enable auto-instantiation of the fixture data. For instance, take the
114
118
  # following tests:
115
119
  #
@@ -238,10 +242,10 @@ module ActiveRecord
238
242
  # and one for the humans. Why don't we generate the primary key instead?
239
243
  # Hashing each fixture's label yields a consistent ID:
240
244
  #
241
- # george: # generated id: 503576764
245
+ # george: # generated id: 380982691
242
246
  # name: George the Monkey
243
247
  #
244
- # reginald: # generated id: 324201669
248
+ # reginald: # generated id: 41001176
245
249
  # name: Reginald the Pirate
246
250
  #
247
251
  # Active Record looks at the fixture's model class, discovers the correct
@@ -492,8 +496,8 @@ module ActiveRecord
492
496
  # # app/models/book_orders.rb
493
497
  # class BookOrder < ApplicationRecord
494
498
  # self.primary_key = [:shop_id, :id]
495
- # belongs_to :order, query_constraints: [:shop_id, :order_id]
496
- # belongs_to :book, query_constraints: [:author_id, :book_id]
499
+ # belongs_to :order, foreign_key: [:shop_id, :order_id]
500
+ # belongs_to :book, foreign_key: [:author_id, :book_id]
497
501
  # end
498
502
  #
499
503
  # <code></code>
@@ -553,24 +557,24 @@ module ActiveRecord
553
557
  @@all_cached_fixtures.clear
554
558
  end
555
559
 
556
- def cache_for_connection(connection)
557
- @@all_cached_fixtures[connection]
560
+ def cache_for_connection_pool(connection_pool)
561
+ @@all_cached_fixtures[connection_pool]
558
562
  end
559
563
 
560
- def fixture_is_cached?(connection, table_name)
561
- cache_for_connection(connection)[table_name]
564
+ def fixture_is_cached?(connection_pool, table_name)
565
+ cache_for_connection_pool(connection_pool)[table_name]
562
566
  end
563
567
 
564
- def cached_fixtures(connection, keys_to_fetch = nil)
568
+ def cached_fixtures(connection_pool, keys_to_fetch = nil)
565
569
  if keys_to_fetch
566
- cache_for_connection(connection).values_at(*keys_to_fetch)
570
+ cache_for_connection_pool(connection_pool).values_at(*keys_to_fetch)
567
571
  else
568
- cache_for_connection(connection).values
572
+ cache_for_connection_pool(connection_pool).values
569
573
  end
570
574
  end
571
575
 
572
- def cache_fixtures(connection, fixtures_map)
573
- cache_for_connection(connection).update(fixtures_map)
576
+ def cache_fixtures(connection_pool, fixtures_map)
577
+ cache_for_connection_pool(connection_pool).update(fixtures_map)
574
578
  end
575
579
 
576
580
  def instantiate_fixtures(object, fixture_set, load_instances = true)
@@ -588,15 +592,13 @@ module ActiveRecord
588
592
  end
589
593
  end
590
594
 
591
- def create_fixtures(fixtures_directories, fixture_set_names, class_names = {}, config = ActiveRecord::Base, &block)
595
+ def create_fixtures(fixtures_directories, fixture_set_names, class_names = {}, config = ActiveRecord::Base)
592
596
  fixture_set_names = Array(fixture_set_names).map(&:to_s)
593
597
  class_names.stringify_keys!
594
598
 
595
- # FIXME: Apparently JK uses this.
596
- connection = block_given? ? block : lambda { ActiveRecord::Base.connection }
597
-
599
+ connection_pool = config.connection_pool
598
600
  fixture_files_to_read = fixture_set_names.reject do |fs_name|
599
- fixture_is_cached?(connection.call, fs_name)
601
+ fixture_is_cached?(connection_pool, fs_name)
600
602
  end
601
603
 
602
604
  if fixture_files_to_read.any?
@@ -604,11 +606,11 @@ module ActiveRecord
604
606
  Array(fixtures_directories),
605
607
  fixture_files_to_read,
606
608
  class_names,
607
- connection,
609
+ connection_pool,
608
610
  )
609
- cache_fixtures(connection.call, fixtures_map)
611
+ cache_fixtures(connection_pool, fixtures_map)
610
612
  end
611
- cached_fixtures(connection.call, fixture_set_names)
613
+ cached_fixtures(connection_pool, fixture_set_names)
612
614
  end
613
615
 
614
616
  # Returns a consistent, platform-independent identifier for +label+.
@@ -641,7 +643,7 @@ module ActiveRecord
641
643
  end
642
644
 
643
645
  private
644
- def read_and_insert(fixtures_directories, fixture_files, class_names, connection) # :nodoc:
646
+ def read_and_insert(fixtures_directories, fixture_files, class_names, connection_pool) # :nodoc:
645
647
  fixtures_map = {}
646
648
  directory_glob = "{#{fixtures_directories.join(",")}}"
647
649
  fixture_sets = fixture_files.map do |fixture_set_name|
@@ -655,21 +657,21 @@ module ActiveRecord
655
657
  end
656
658
  update_all_loaded_fixtures(fixtures_map)
657
659
 
658
- insert(fixture_sets, connection)
660
+ insert(fixture_sets, connection_pool)
659
661
 
660
662
  fixtures_map
661
663
  end
662
664
 
663
- def insert(fixture_sets, connection) # :nodoc:
664
- fixture_sets_by_connection = fixture_sets.group_by do |fixture_set|
665
+ def insert(fixture_sets, connection_pool) # :nodoc:
666
+ fixture_sets_by_pool = fixture_sets.group_by do |fixture_set|
665
667
  if fixture_set.model_class
666
- fixture_set.model_class.connection
668
+ fixture_set.model_class.connection_pool
667
669
  else
668
- connection.call
670
+ connection_pool
669
671
  end
670
672
  end
671
673
 
672
- fixture_sets_by_connection.each do |conn, set|
674
+ fixture_sets_by_pool.each do |pool, set|
673
675
  table_rows_for_connection = Hash.new { |h, k| h[k] = [] }
674
676
 
675
677
  set.each do |fixture_set|
@@ -678,13 +680,15 @@ module ActiveRecord
678
680
  end
679
681
  end
680
682
 
681
- conn.insert_fixtures_set(table_rows_for_connection, table_rows_for_connection.keys)
683
+ pool.with_connection do |conn|
684
+ conn.insert_fixtures_set(table_rows_for_connection, table_rows_for_connection.keys)
682
685
 
683
- check_all_foreign_keys_valid!(conn)
686
+ check_all_foreign_keys_valid!(conn)
684
687
 
685
- # Cap primary key sequences to max(pk).
686
- if conn.respond_to?(:reset_pk_sequence!)
687
- set.each { |fs| conn.reset_pk_sequence!(fs.table_name) }
688
+ # Cap primary key sequences to max(pk).
689
+ if conn.respond_to?(:reset_pk_sequence!)
690
+ set.each { |fs| conn.reset_pk_sequence!(fs.table_name) }
691
+ end
688
692
  end
689
693
  end
690
694
  end