activerecord 7.1.3.4 → 7.2.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 (196) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +652 -2032
  3. data/README.rdoc +15 -15
  4. data/examples/performance.rb +2 -2
  5. data/lib/active_record/association_relation.rb +1 -1
  6. data/lib/active_record/associations/alias_tracker.rb +25 -19
  7. data/lib/active_record/associations/association.rb +15 -8
  8. data/lib/active_record/associations/belongs_to_association.rb +18 -11
  9. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
  10. data/lib/active_record/associations/builder/belongs_to.rb +1 -0
  11. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
  12. data/lib/active_record/associations/builder/has_many.rb +3 -4
  13. data/lib/active_record/associations/builder/has_one.rb +3 -4
  14. data/lib/active_record/associations/collection_association.rb +11 -5
  15. data/lib/active_record/associations/collection_proxy.rb +14 -1
  16. data/lib/active_record/associations/errors.rb +265 -0
  17. data/lib/active_record/associations/has_many_association.rb +3 -3
  18. data/lib/active_record/associations/has_many_through_association.rb +7 -1
  19. data/lib/active_record/associations/has_one_association.rb +2 -2
  20. data/lib/active_record/associations/join_dependency/join_association.rb +30 -27
  21. data/lib/active_record/associations/join_dependency.rb +10 -12
  22. data/lib/active_record/associations/nested_error.rb +47 -0
  23. data/lib/active_record/associations/preloader/association.rb +2 -1
  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 +6 -0
  27. data/lib/active_record/associations/through_association.rb +1 -1
  28. data/lib/active_record/associations.rb +62 -289
  29. data/lib/active_record/attribute_assignment.rb +0 -2
  30. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  31. data/lib/active_record/attribute_methods/dirty.rb +2 -2
  32. data/lib/active_record/attribute_methods/primary_key.rb +23 -55
  33. data/lib/active_record/attribute_methods/read.rb +4 -16
  34. data/lib/active_record/attribute_methods/serialization.rb +4 -24
  35. data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -6
  36. data/lib/active_record/attribute_methods/write.rb +3 -3
  37. data/lib/active_record/attribute_methods.rb +89 -58
  38. data/lib/active_record/attributes.rb +61 -47
  39. data/lib/active_record/autosave_association.rb +17 -31
  40. data/lib/active_record/base.rb +2 -3
  41. data/lib/active_record/callbacks.rb +1 -1
  42. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +24 -107
  43. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -0
  44. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +270 -58
  45. data/lib/active_record/connection_adapters/abstract/database_statements.rb +35 -18
  46. data/lib/active_record/connection_adapters/abstract/query_cache.rb +190 -75
  47. data/lib/active_record/connection_adapters/abstract/quoting.rb +65 -91
  48. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
  49. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +23 -10
  50. data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -62
  51. data/lib/active_record/connection_adapters/abstract_adapter.rb +38 -59
  52. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +73 -19
  53. data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
  54. data/lib/active_record/connection_adapters/mysql/quoting.rb +43 -48
  55. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +8 -1
  56. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +16 -15
  57. data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -32
  58. data/lib/active_record/connection_adapters/pool_config.rb +7 -6
  59. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +27 -4
  60. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  61. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  62. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  63. data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
  64. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +20 -0
  65. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +18 -12
  66. data/lib/active_record/connection_adapters/postgresql_adapter.rb +29 -24
  67. data/lib/active_record/connection_adapters/schema_cache.rb +123 -128
  68. data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
  69. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -6
  70. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +44 -46
  71. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  72. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
  73. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  74. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +25 -2
  75. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +127 -77
  76. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +15 -15
  77. data/lib/active_record/connection_adapters/trilogy_adapter.rb +32 -65
  78. data/lib/active_record/connection_adapters.rb +121 -0
  79. data/lib/active_record/connection_handling.rb +56 -41
  80. data/lib/active_record/core.rb +93 -40
  81. data/lib/active_record/counter_cache.rb +23 -10
  82. data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -3
  83. data/lib/active_record/database_configurations/database_config.rb +19 -4
  84. data/lib/active_record/database_configurations/hash_config.rb +44 -36
  85. data/lib/active_record/database_configurations/url_config.rb +20 -1
  86. data/lib/active_record/database_configurations.rb +1 -1
  87. data/lib/active_record/delegated_type.rb +30 -6
  88. data/lib/active_record/destroy_association_async_job.rb +1 -1
  89. data/lib/active_record/dynamic_matchers.rb +2 -2
  90. data/lib/active_record/encryption/encryptable_record.rb +3 -3
  91. data/lib/active_record/encryption/encrypted_attribute_type.rb +26 -6
  92. data/lib/active_record/encryption/encryptor.rb +18 -3
  93. data/lib/active_record/encryption/key_provider.rb +1 -1
  94. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  95. data/lib/active_record/encryption/message_serializer.rb +4 -0
  96. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  97. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  98. data/lib/active_record/encryption/scheme.rb +8 -4
  99. data/lib/active_record/encryption.rb +2 -0
  100. data/lib/active_record/enum.rb +19 -2
  101. data/lib/active_record/errors.rb +46 -20
  102. data/lib/active_record/explain.rb +13 -24
  103. data/lib/active_record/fixtures.rb +37 -31
  104. data/lib/active_record/future_result.rb +17 -4
  105. data/lib/active_record/gem_version.rb +3 -3
  106. data/lib/active_record/inheritance.rb +4 -2
  107. data/lib/active_record/insert_all.rb +18 -15
  108. data/lib/active_record/integration.rb +4 -1
  109. data/lib/active_record/internal_metadata.rb +48 -34
  110. data/lib/active_record/locking/optimistic.rb +8 -7
  111. data/lib/active_record/log_subscriber.rb +0 -21
  112. data/lib/active_record/marshalling.rb +4 -1
  113. data/lib/active_record/message_pack.rb +2 -2
  114. data/lib/active_record/migration/command_recorder.rb +2 -3
  115. data/lib/active_record/migration/compatibility.rb +11 -3
  116. data/lib/active_record/migration/default_strategy.rb +4 -5
  117. data/lib/active_record/migration/pending_migration_connection.rb +2 -2
  118. data/lib/active_record/migration.rb +85 -76
  119. data/lib/active_record/model_schema.rb +38 -70
  120. data/lib/active_record/nested_attributes.rb +24 -5
  121. data/lib/active_record/normalization.rb +3 -7
  122. data/lib/active_record/persistence.rb +32 -354
  123. data/lib/active_record/query_cache.rb +19 -8
  124. data/lib/active_record/query_logs.rb +15 -0
  125. data/lib/active_record/query_logs_formatter.rb +1 -1
  126. data/lib/active_record/querying.rb +21 -9
  127. data/lib/active_record/railtie.rb +50 -68
  128. data/lib/active_record/railties/controller_runtime.rb +13 -4
  129. data/lib/active_record/railties/databases.rake +42 -45
  130. data/lib/active_record/reflection.rb +106 -38
  131. data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
  132. data/lib/active_record/relation/batches.rb +14 -8
  133. data/lib/active_record/relation/calculations.rb +96 -63
  134. data/lib/active_record/relation/delegation.rb +8 -11
  135. data/lib/active_record/relation/finder_methods.rb +16 -2
  136. data/lib/active_record/relation/merger.rb +4 -6
  137. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  138. data/lib/active_record/relation/predicate_builder/association_query_value.rb +9 -3
  139. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +6 -1
  140. data/lib/active_record/relation/predicate_builder.rb +3 -3
  141. data/lib/active_record/relation/query_methods.rb +245 -65
  142. data/lib/active_record/relation/record_fetch_warning.rb +3 -0
  143. data/lib/active_record/relation/spawn_methods.rb +2 -18
  144. data/lib/active_record/relation/where_clause.rb +7 -19
  145. data/lib/active_record/relation.rb +500 -66
  146. data/lib/active_record/result.rb +32 -45
  147. data/lib/active_record/runtime_registry.rb +39 -0
  148. data/lib/active_record/sanitization.rb +24 -19
  149. data/lib/active_record/schema.rb +8 -6
  150. data/lib/active_record/schema_dumper.rb +19 -9
  151. data/lib/active_record/schema_migration.rb +30 -14
  152. data/lib/active_record/scoping/named.rb +1 -0
  153. data/lib/active_record/signed_id.rb +20 -1
  154. data/lib/active_record/statement_cache.rb +7 -7
  155. data/lib/active_record/table_metadata.rb +1 -10
  156. data/lib/active_record/tasks/database_tasks.rb +98 -48
  157. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  158. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  159. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -1
  160. data/lib/active_record/test_fixtures.rb +87 -89
  161. data/lib/active_record/testing/query_assertions.rb +121 -0
  162. data/lib/active_record/timestamp.rb +5 -3
  163. data/lib/active_record/token_for.rb +22 -12
  164. data/lib/active_record/touch_later.rb +1 -1
  165. data/lib/active_record/transaction.rb +132 -0
  166. data/lib/active_record/transactions.rb +70 -14
  167. data/lib/active_record/translation.rb +0 -2
  168. data/lib/active_record/type/serialized.rb +1 -3
  169. data/lib/active_record/type_caster/connection.rb +4 -4
  170. data/lib/active_record/validations/associated.rb +9 -3
  171. data/lib/active_record/validations/uniqueness.rb +15 -10
  172. data/lib/active_record/validations.rb +4 -1
  173. data/lib/active_record.rb +150 -41
  174. data/lib/arel/alias_predication.rb +1 -1
  175. data/lib/arel/collectors/bind.rb +2 -0
  176. data/lib/arel/collectors/composite.rb +7 -0
  177. data/lib/arel/collectors/sql_string.rb +1 -1
  178. data/lib/arel/collectors/substitute_binds.rb +1 -1
  179. data/lib/arel/nodes/binary.rb +0 -6
  180. data/lib/arel/nodes/bound_sql_literal.rb +9 -5
  181. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  182. data/lib/arel/nodes/node.rb +4 -3
  183. data/lib/arel/nodes/sql_literal.rb +7 -0
  184. data/lib/arel/nodes.rb +2 -2
  185. data/lib/arel/predications.rb +1 -1
  186. data/lib/arel/select_manager.rb +1 -1
  187. data/lib/arel/tree_manager.rb +8 -3
  188. data/lib/arel/update_manager.rb +2 -1
  189. data/lib/arel/visitors/dot.rb +1 -0
  190. data/lib/arel/visitors/mysql.rb +9 -4
  191. data/lib/arel/visitors/postgresql.rb +1 -12
  192. data/lib/arel/visitors/sqlite.rb +25 -0
  193. data/lib/arel/visitors/to_sql.rb +31 -17
  194. data/lib/arel.rb +7 -3
  195. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
  196. metadata +21 -15
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/message_pack"
4
+
5
+ module ActiveRecord
6
+ module Encryption
7
+ # A message serializer that serializes +Messages+ with MessagePack.
8
+ #
9
+ # The message is converted to a hash with this structure:
10
+ #
11
+ # {
12
+ # p: <payload>,
13
+ # h: {
14
+ # header1: value1,
15
+ # header2: value2,
16
+ # ...
17
+ # }
18
+ # }
19
+ #
20
+ # Then it is converted to the MessagePack format.
21
+ class MessagePackMessageSerializer
22
+ def dump(message)
23
+ raise Errors::ForbiddenClass unless message.is_a?(Message)
24
+ ActiveSupport::MessagePack.dump(message_to_hash(message))
25
+ end
26
+
27
+ def load(serialized_content)
28
+ data = ActiveSupport::MessagePack.load(serialized_content)
29
+ hash_to_message(data, 1)
30
+ rescue RuntimeError
31
+ raise Errors::Decryption
32
+ end
33
+
34
+ def binary?
35
+ true
36
+ end
37
+
38
+ private
39
+ def message_to_hash(message)
40
+ {
41
+ "p" => message.payload,
42
+ "h" => headers_to_hash(message.headers)
43
+ }
44
+ end
45
+
46
+ def headers_to_hash(headers)
47
+ headers.transform_values do |value|
48
+ value.is_a?(Message) ? message_to_hash(value) : value
49
+ end
50
+ end
51
+
52
+ def hash_to_message(data, level)
53
+ validate_message_data_format(data, level)
54
+ Message.new(payload: data["p"], headers: parse_properties(data["h"], level))
55
+ end
56
+
57
+ def validate_message_data_format(data, level)
58
+ if level > 2
59
+ raise Errors::Decryption, "More than one level of hash nesting in headers is not supported"
60
+ end
61
+
62
+ unless data.is_a?(Hash) && data.has_key?("p")
63
+ raise Errors::Decryption, "Invalid data format: hash without payload"
64
+ end
65
+ end
66
+
67
+ def parse_properties(headers, level)
68
+ Properties.new.tap do |properties|
69
+ headers&.each do |key, value|
70
+ properties[key] = value.is_a?(Hash) ? hash_to_message(value, level + 1) : value
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -33,6 +33,10 @@ module ActiveRecord
33
33
  JSON.dump message_to_json(message)
34
34
  end
35
35
 
36
+ def binary?
37
+ false
38
+ end
39
+
36
40
  private
37
41
  def parse_message(data, level)
38
42
  validate_message_data_format(data, level)
@@ -16,6 +16,10 @@ module ActiveRecord
16
16
  def encrypted?(text)
17
17
  false
18
18
  end
19
+
20
+ def binary?
21
+ false
22
+ end
19
23
  end
20
24
  end
21
25
  end
@@ -19,6 +19,10 @@ module ActiveRecord
19
19
  def encrypted?(text)
20
20
  false
21
21
  end
22
+
23
+ def binary?
24
+ false
25
+ end
22
26
  end
23
27
  end
24
28
  end
@@ -50,7 +50,7 @@ module ActiveRecord
50
50
  end
51
51
 
52
52
  def key_provider
53
- @key_provider ||= @key_provider_param || build_key_provider || default_key_provider
53
+ @key_provider_param || key_provider_from_key || deterministic_key_provider || default_key_provider
54
54
  end
55
55
 
56
56
  def merge(other_scheme)
@@ -80,10 +80,14 @@ module ActiveRecord
80
80
  raise Errors::Configuration, "key_provider: and key: can't be used simultaneously" if @key_provider_param && @key
81
81
  end
82
82
 
83
- def build_key_provider
84
- return DerivedSecretKeyProvider.new(@key) if @key.present?
83
+ def key_provider_from_key
84
+ @key_provider_from_key ||= if @key.present?
85
+ DerivedSecretKeyProvider.new(@key)
86
+ end
87
+ end
85
88
 
86
- if @deterministic
89
+ def deterministic_key_provider
90
+ @deterministic_key_provider ||= if @deterministic
87
91
  DeterministicKeyProvider.new(ActiveRecord::Encryption.config.deterministic_key)
88
92
  end
89
93
  end
@@ -53,4 +53,6 @@ module ActiveRecord
53
53
  Cipher.eager_load!
54
54
  end
55
55
  end
56
+
57
+ ActiveSupport.run_load_hooks :active_record_encryption, Encryption
56
58
  end
@@ -223,6 +223,13 @@ module ActiveRecord
223
223
  options.transform_keys! { |key| :"#{key[1..-1]}" }
224
224
 
225
225
  definitions.each { |name, values| _enum(name, values, **options) }
226
+
227
+ ActiveRecord.deprecator.warn(<<~MSG)
228
+ Defining enums with keyword arguments is deprecated and will be removed
229
+ in Rails 8.0. Positional arguments should be used instead:
230
+
231
+ #{definitions.map { |name, values| "enum :#{name}, #{values}" }.join("\n")}
232
+ MSG
226
233
  end
227
234
 
228
235
  private
@@ -233,6 +240,7 @@ module ActiveRecord
233
240
 
234
241
  def _enum(name, values, prefix: nil, suffix: nil, scopes: true, instance_methods: true, validate: false, **options)
235
242
  assert_valid_enum_definition_values(values)
243
+ assert_valid_enum_options(options)
236
244
  # statuses = { }
237
245
  enum_values = ActiveSupport::HashWithIndifferentAccess.new
238
246
  name = name.to_s
@@ -245,9 +253,11 @@ module ActiveRecord
245
253
  detect_enum_conflict!(name, name)
246
254
  detect_enum_conflict!(name, "#{name}=")
247
255
 
248
- attribute(name, **options) do |subtype|
256
+ attribute(name, **options)
257
+
258
+ decorate_attributes([name]) do |_name, subtype|
249
259
  if subtype == ActiveModel::Type.default_value
250
- raise "Undeclared attribute type for enum '#{name}'. Enums must be" \
260
+ raise "Undeclared attribute type for enum '#{name}' in #{self.name}. Enums must be" \
251
261
  " backed by a database column or declared with an explicit type" \
252
262
  " via `attribute`."
253
263
  end
@@ -361,6 +371,13 @@ module ActiveRecord
361
371
  end
362
372
  end
363
373
 
374
+ def assert_valid_enum_options(options)
375
+ invalid_keys = options.keys & %i[_prefix _suffix _scopes _default _instance_methods]
376
+ unless invalid_keys.empty?
377
+ raise ArgumentError, "invalid option(s): #{invalid_keys.map(&:inspect).join(", ")}. Valid options are: :prefix, :suffix, :scopes, :default, :instance_methods, and :validate."
378
+ end
379
+ end
380
+
364
381
  ENUM_CONFLICT_MESSAGE = \
365
382
  "You tried to define an enum named \"%{enum}\" on the model \"%{klass}\", but " \
366
383
  "this will generate a %{type} method \"%{method}\", which is already defined " \
@@ -1,20 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/deprecation"
4
+
3
5
  module ActiveRecord
6
+ include ActiveSupport::Deprecation::DeprecatedConstantAccessor
7
+
4
8
  # = Active Record Errors
5
9
  #
6
10
  # Generic Active Record exception class.
7
11
  class ActiveRecordError < StandardError
8
12
  end
9
13
 
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
14
  # Raised when the single-table inheritance mechanism fails to locate the subclass
19
15
  # (for example due to improper usage of column that
20
16
  # {ActiveRecord::Base.inheritance_column}[rdoc-ref:ModelSchema::ClassMethods#inheritance_column]
@@ -66,7 +62,7 @@ module ActiveRecord
66
62
  end
67
63
 
68
64
  # Raised when connection to the database could not been established (for example when
69
- # {ActiveRecord::Base.connection=}[rdoc-ref:ConnectionHandling#connection]
65
+ # {ActiveRecord::Base.lease_connection=}[rdoc-ref:ConnectionHandling#lease_connection]
70
66
  # is given a +nil+ object).
71
67
  class ConnectionNotEstablished < AdapterError
72
68
  def initialize(message = nil, connection_pool: nil)
@@ -137,8 +133,18 @@ module ActiveRecord
137
133
  end
138
134
 
139
135
  # 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.
136
+ # {ActiveRecord::Base.update_attribute!}[rdoc-ref:Persistence#update_attribute!]
137
+ # methods when a record failed to validate or cannot be saved due to any of the
138
+ # <tt>before_*</tt> callbacks throwing +:abort+. See
139
+ # ActiveRecord::Callbacks for further details.
140
+ #
141
+ # class Product < ActiveRecord::Base
142
+ # before_save do
143
+ # throw :abort if price < 0
144
+ # end
145
+ # end
146
+ #
147
+ # Product.create! # => raises an ActiveRecord::RecordNotSaved
142
148
  class RecordNotSaved < ActiveRecordError
143
149
  attr_reader :record
144
150
 
@@ -149,15 +155,17 @@ module ActiveRecord
149
155
  end
150
156
 
151
157
  # Raised by {ActiveRecord::Base#destroy!}[rdoc-ref:Persistence#destroy!]
152
- # when a call to {#destroy}[rdoc-ref:Persistence#destroy]
153
- # would return false.
158
+ # when a record cannot be destroyed due to any of the
159
+ # <tt>before_destroy</tt> callbacks throwing +:abort+. See
160
+ # ActiveRecord::Callbacks for further details.
154
161
  #
155
- # begin
156
- # complex_operation_that_internally_calls_destroy!
157
- # rescue ActiveRecord::RecordNotDestroyed => invalid
158
- # puts invalid.record.errors
162
+ # class User < ActiveRecord::Base
163
+ # before_destroy do
164
+ # throw :abort if still_active?
165
+ # end
159
166
  # end
160
167
  #
168
+ # User.first.destroy! # => raises an ActiveRecord::RecordNotDestroyed
161
169
  class RecordNotDestroyed < ActiveRecordError
162
170
  attr_reader :record
163
171
 
@@ -374,6 +382,12 @@ module ActiveRecord
374
382
  end
375
383
 
376
384
  # Raised on attempt to lazily load records that are marked as strict loading.
385
+ #
386
+ # You can resolve this error by eager loading marked records before accessing
387
+ # them. The
388
+ # {Eager Loading Associations}[https://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations]
389
+ # guide covers solutions, such as using
390
+ # {ActiveRecord::Base.includes}[rdoc-ref:QueryMethods#includes].
377
391
  class StrictLoadingViolationError < ActiveRecordError
378
392
  end
379
393
 
@@ -466,10 +480,15 @@ module ActiveRecord
466
480
  # relation.loaded? # => true
467
481
  #
468
482
  # # 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
483
+ # relation.where!(title: 'TODO') # => ActiveRecord::UnmodifiableRelation
484
+ # relation.limit!(5) # => ActiveRecord::UnmodifiableRelation
485
+ class UnmodifiableRelation < ActiveRecordError
472
486
  end
487
+ deprecate_constant(
488
+ :ImmutableRelation,
489
+ "ActiveRecord::UnmodifiableRelation",
490
+ deprecator: ActiveRecord.deprecator
491
+ )
473
492
 
474
493
  # TransactionIsolationError will be raised under the following conditions:
475
494
  #
@@ -577,4 +596,11 @@ module ActiveRecord
577
596
  # values, such as request parameters or model attributes to query methods.
578
597
  class UnknownAttributeReference < ActiveRecordError
579
598
  end
599
+
600
+ # DatabaseVersionError will be raised when the database version is not supported, or when
601
+ # the database version cannot be determined.
602
+ class DatabaseVersionError < ActiveRecordError
603
+ end
580
604
  end
605
+
606
+ require "active_record/associations/errors"
@@ -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
@@ -110,6 +110,12 @@ module ActiveRecord
110
110
  # assert_raise(StandardError) { web_sites(:reddit) }
111
111
  # end
112
112
  #
113
+ # If the model names conflicts with a +TestCase+ methods, you can use the generic +fixture+ accessor
114
+ #
115
+ # test "generic find" do
116
+ # assert_equal "Ruby on Rails", fixture(:web_sites, :rubyonrails).name
117
+ # end
118
+ #
113
119
  # Alternatively, you may enable auto-instantiation of the fixture data. For instance, take the
114
120
  # following tests:
115
121
  #
@@ -492,8 +498,8 @@ module ActiveRecord
492
498
  # # app/models/book_orders.rb
493
499
  # class BookOrder < ApplicationRecord
494
500
  # 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]
501
+ # belongs_to :order, foreign_key: [:shop_id, :order_id]
502
+ # belongs_to :book, foreign_key: [:author_id, :book_id]
497
503
  # end
498
504
  #
499
505
  # <code></code>
@@ -553,24 +559,24 @@ module ActiveRecord
553
559
  @@all_cached_fixtures.clear
554
560
  end
555
561
 
556
- def cache_for_connection(connection)
557
- @@all_cached_fixtures[connection]
562
+ def cache_for_connection_pool(connection_pool)
563
+ @@all_cached_fixtures[connection_pool]
558
564
  end
559
565
 
560
- def fixture_is_cached?(connection, table_name)
561
- cache_for_connection(connection)[table_name]
566
+ def fixture_is_cached?(connection_pool, table_name)
567
+ cache_for_connection_pool(connection_pool)[table_name]
562
568
  end
563
569
 
564
- def cached_fixtures(connection, keys_to_fetch = nil)
570
+ def cached_fixtures(connection_pool, keys_to_fetch = nil)
565
571
  if keys_to_fetch
566
- cache_for_connection(connection).values_at(*keys_to_fetch)
572
+ cache_for_connection_pool(connection_pool).values_at(*keys_to_fetch)
567
573
  else
568
- cache_for_connection(connection).values
574
+ cache_for_connection_pool(connection_pool).values
569
575
  end
570
576
  end
571
577
 
572
- def cache_fixtures(connection, fixtures_map)
573
- cache_for_connection(connection).update(fixtures_map)
578
+ def cache_fixtures(connection_pool, fixtures_map)
579
+ cache_for_connection_pool(connection_pool).update(fixtures_map)
574
580
  end
575
581
 
576
582
  def instantiate_fixtures(object, fixture_set, load_instances = true)
@@ -588,15 +594,13 @@ module ActiveRecord
588
594
  end
589
595
  end
590
596
 
591
- def create_fixtures(fixtures_directories, fixture_set_names, class_names = {}, config = ActiveRecord::Base, &block)
597
+ def create_fixtures(fixtures_directories, fixture_set_names, class_names = {}, config = ActiveRecord::Base)
592
598
  fixture_set_names = Array(fixture_set_names).map(&:to_s)
593
599
  class_names.stringify_keys!
594
600
 
595
- # FIXME: Apparently JK uses this.
596
- connection = block_given? ? block : lambda { ActiveRecord::Base.connection }
597
-
601
+ connection_pool = config.connection_pool
598
602
  fixture_files_to_read = fixture_set_names.reject do |fs_name|
599
- fixture_is_cached?(connection.call, fs_name)
603
+ fixture_is_cached?(connection_pool, fs_name)
600
604
  end
601
605
 
602
606
  if fixture_files_to_read.any?
@@ -604,11 +608,11 @@ module ActiveRecord
604
608
  Array(fixtures_directories),
605
609
  fixture_files_to_read,
606
610
  class_names,
607
- connection,
611
+ connection_pool,
608
612
  )
609
- cache_fixtures(connection.call, fixtures_map)
613
+ cache_fixtures(connection_pool, fixtures_map)
610
614
  end
611
- cached_fixtures(connection.call, fixture_set_names)
615
+ cached_fixtures(connection_pool, fixture_set_names)
612
616
  end
613
617
 
614
618
  # Returns a consistent, platform-independent identifier for +label+.
@@ -641,7 +645,7 @@ module ActiveRecord
641
645
  end
642
646
 
643
647
  private
644
- def read_and_insert(fixtures_directories, fixture_files, class_names, connection) # :nodoc:
648
+ def read_and_insert(fixtures_directories, fixture_files, class_names, connection_pool) # :nodoc:
645
649
  fixtures_map = {}
646
650
  directory_glob = "{#{fixtures_directories.join(",")}}"
647
651
  fixture_sets = fixture_files.map do |fixture_set_name|
@@ -655,21 +659,21 @@ module ActiveRecord
655
659
  end
656
660
  update_all_loaded_fixtures(fixtures_map)
657
661
 
658
- insert(fixture_sets, connection)
662
+ insert(fixture_sets, connection_pool)
659
663
 
660
664
  fixtures_map
661
665
  end
662
666
 
663
- def insert(fixture_sets, connection) # :nodoc:
664
- fixture_sets_by_connection = fixture_sets.group_by do |fixture_set|
667
+ def insert(fixture_sets, connection_pool) # :nodoc:
668
+ fixture_sets_by_pool = fixture_sets.group_by do |fixture_set|
665
669
  if fixture_set.model_class
666
- fixture_set.model_class.connection
670
+ fixture_set.model_class.connection_pool
667
671
  else
668
- connection.call
672
+ connection_pool
669
673
  end
670
674
  end
671
675
 
672
- fixture_sets_by_connection.each do |conn, set|
676
+ fixture_sets_by_pool.each do |pool, set|
673
677
  table_rows_for_connection = Hash.new { |h, k| h[k] = [] }
674
678
 
675
679
  set.each do |fixture_set|
@@ -678,13 +682,15 @@ module ActiveRecord
678
682
  end
679
683
  end
680
684
 
681
- conn.insert_fixtures_set(table_rows_for_connection, table_rows_for_connection.keys)
685
+ pool.with_connection do |conn|
686
+ conn.insert_fixtures_set(table_rows_for_connection, table_rows_for_connection.keys)
682
687
 
683
- check_all_foreign_keys_valid!(conn)
688
+ check_all_foreign_keys_valid!(conn)
684
689
 
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) }
690
+ # Cap primary key sequences to max(pk).
691
+ if conn.respond_to?(:reset_pk_sequence!)
692
+ set.each { |fs| conn.reset_pk_sequence!(fs.table_name) }
693
+ end
688
694
  end
689
695
  end
690
696
  end
@@ -32,8 +32,11 @@ module ActiveRecord
32
32
 
33
33
  def instrument(name, payload = {}, &block)
34
34
  event = @instrumenter.new_event(name, payload)
35
- @events << event
36
- event.record(&block)
35
+ begin
36
+ event.record(&block)
37
+ ensure
38
+ @events << event
39
+ end
37
40
  end
38
41
 
39
42
  def flush
@@ -47,8 +50,16 @@ module ActiveRecord
47
50
 
48
51
  Canceled = Class.new(ActiveRecordError)
49
52
 
53
+ def self.wrap(result)
54
+ case result
55
+ when self, Complete
56
+ result
57
+ else
58
+ Complete.new(result)
59
+ end
60
+ end
61
+
50
62
  delegate :empty?, :to_a, to: :result
51
- delegate_missing_to :result
52
63
 
53
64
  attr_reader :lock_wait
54
65
 
@@ -131,7 +142,9 @@ module ActiveRecord
131
142
  start = Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond)
132
143
  @mutex.synchronize do
133
144
  if pending?
134
- execute_query(@pool.connection)
145
+ @pool.with_connection do |connection|
146
+ execute_query(connection)
147
+ end
135
148
  else
136
149
  @lock_wait = (Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_millisecond) - start)
137
150
  end
@@ -8,9 +8,9 @@ module ActiveRecord
8
8
 
9
9
  module VERSION
10
10
  MAJOR = 7
11
- MINOR = 1
12
- TINY = 3
13
- PRE = "4"
11
+ MINOR = 2
12
+ TINY = 2
13
+ PRE = "1"
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -165,7 +165,7 @@ module ActiveRecord
165
165
 
166
166
  # Returns whether this class is an abstract class or not.
167
167
  def abstract_class?
168
- defined?(@abstract_class) && @abstract_class == true
168
+ @abstract_class == true
169
169
  end
170
170
 
171
171
  # Sets the application record class for Active Record
@@ -202,7 +202,9 @@ module ActiveRecord
202
202
  "The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " \
203
203
  "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " \
204
204
  "Please rename this column if you didn't intend it to be used for storing the inheritance class " \
205
- "or overwrite #{name}.inheritance_column to use another column for that information."
205
+ "or overwrite #{name}.inheritance_column to use another column for that information. " \
206
+ "If you wish to disable single-table inheritance for #{name} set " \
207
+ "#{name}.inheritance_column to nil"
206
208
  end
207
209
 
208
210
  # Returns the value to be stored in the polymorphic type column for Polymorphic Associations.