activerecord 7.1.3.3 → 7.2.0.beta1

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 (186) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +507 -2128
  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 +9 -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 +4 -2
  15. data/lib/active_record/associations/collection_proxy.rb +14 -1
  16. data/lib/active_record/associations/has_many_association.rb +3 -3
  17. data/lib/active_record/associations/has_one_association.rb +2 -2
  18. data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
  19. data/lib/active_record/associations/join_dependency.rb +5 -7
  20. data/lib/active_record/associations/nested_error.rb +47 -0
  21. data/lib/active_record/associations/preloader/association.rb +2 -1
  22. data/lib/active_record/associations/preloader/branch.rb +7 -1
  23. data/lib/active_record/associations/preloader/through_association.rb +1 -3
  24. data/lib/active_record/associations/singular_association.rb +6 -0
  25. data/lib/active_record/associations/through_association.rb +1 -1
  26. data/lib/active_record/associations.rb +34 -11
  27. data/lib/active_record/attribute_assignment.rb +1 -11
  28. data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
  29. data/lib/active_record/attribute_methods/dirty.rb +1 -1
  30. data/lib/active_record/attribute_methods/primary_key.rb +23 -55
  31. data/lib/active_record/attribute_methods/read.rb +1 -13
  32. data/lib/active_record/attribute_methods/serialization.rb +4 -24
  33. data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -6
  34. data/lib/active_record/attribute_methods.rb +87 -58
  35. data/lib/active_record/attributes.rb +55 -42
  36. data/lib/active_record/autosave_association.rb +14 -30
  37. data/lib/active_record/base.rb +2 -3
  38. data/lib/active_record/connection_adapters/abstract/connection_handler.rb +24 -107
  39. data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -0
  40. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +248 -58
  41. data/lib/active_record/connection_adapters/abstract/database_statements.rb +35 -18
  42. data/lib/active_record/connection_adapters/abstract/query_cache.rb +160 -75
  43. data/lib/active_record/connection_adapters/abstract/quoting.rb +65 -91
  44. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
  45. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +22 -9
  46. data/lib/active_record/connection_adapters/abstract/transaction.rb +60 -57
  47. data/lib/active_record/connection_adapters/abstract_adapter.rb +32 -61
  48. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +69 -19
  49. data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
  50. data/lib/active_record/connection_adapters/mysql/quoting.rb +43 -48
  51. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +7 -0
  52. data/lib/active_record/connection_adapters/mysql2/database_statements.rb +11 -5
  53. data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -32
  54. data/lib/active_record/connection_adapters/pool_config.rb +7 -6
  55. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +27 -4
  56. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
  57. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
  58. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
  59. data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
  60. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +20 -0
  61. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +16 -12
  62. data/lib/active_record/connection_adapters/postgresql_adapter.rb +26 -21
  63. data/lib/active_record/connection_adapters/schema_cache.rb +123 -128
  64. data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
  65. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -6
  66. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +44 -46
  67. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  68. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
  69. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
  70. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +25 -2
  71. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +109 -77
  72. data/lib/active_record/connection_adapters/trilogy/database_statements.rb +12 -6
  73. data/lib/active_record/connection_adapters/trilogy_adapter.rb +32 -65
  74. data/lib/active_record/connection_adapters.rb +121 -0
  75. data/lib/active_record/connection_handling.rb +56 -41
  76. data/lib/active_record/core.rb +59 -38
  77. data/lib/active_record/counter_cache.rb +23 -10
  78. data/lib/active_record/database_configurations/connection_url_resolver.rb +7 -2
  79. data/lib/active_record/database_configurations/database_config.rb +15 -4
  80. data/lib/active_record/database_configurations/hash_config.rb +44 -36
  81. data/lib/active_record/database_configurations/url_config.rb +20 -1
  82. data/lib/active_record/database_configurations.rb +1 -1
  83. data/lib/active_record/delegated_type.rb +30 -6
  84. data/lib/active_record/destroy_association_async_job.rb +1 -1
  85. data/lib/active_record/dynamic_matchers.rb +2 -2
  86. data/lib/active_record/encryption/encryptable_record.rb +2 -2
  87. data/lib/active_record/encryption/encrypted_attribute_type.rb +24 -4
  88. data/lib/active_record/encryption/encryptor.rb +17 -2
  89. data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
  90. data/lib/active_record/encryption/message_serializer.rb +4 -0
  91. data/lib/active_record/encryption/null_encryptor.rb +4 -0
  92. data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
  93. data/lib/active_record/encryption/scheme.rb +8 -4
  94. data/lib/active_record/enum.rb +11 -2
  95. data/lib/active_record/errors.rb +16 -11
  96. data/lib/active_record/explain.rb +13 -24
  97. data/lib/active_record/fixtures.rb +37 -31
  98. data/lib/active_record/future_result.rb +17 -4
  99. data/lib/active_record/gem_version.rb +3 -3
  100. data/lib/active_record/inheritance.rb +4 -2
  101. data/lib/active_record/insert_all.rb +18 -15
  102. data/lib/active_record/integration.rb +4 -1
  103. data/lib/active_record/internal_metadata.rb +48 -34
  104. data/lib/active_record/locking/optimistic.rb +8 -7
  105. data/lib/active_record/log_subscriber.rb +0 -21
  106. data/lib/active_record/marshalling.rb +1 -1
  107. data/lib/active_record/message_pack.rb +2 -2
  108. data/lib/active_record/migration/command_recorder.rb +2 -3
  109. data/lib/active_record/migration/compatibility.rb +11 -3
  110. data/lib/active_record/migration/default_strategy.rb +4 -5
  111. data/lib/active_record/migration/pending_migration_connection.rb +2 -2
  112. data/lib/active_record/migration.rb +85 -76
  113. data/lib/active_record/model_schema.rb +34 -69
  114. data/lib/active_record/nested_attributes.rb +11 -3
  115. data/lib/active_record/normalization.rb +3 -7
  116. data/lib/active_record/persistence.rb +32 -354
  117. data/lib/active_record/query_cache.rb +18 -6
  118. data/lib/active_record/query_logs.rb +15 -0
  119. data/lib/active_record/query_logs_formatter.rb +1 -1
  120. data/lib/active_record/querying.rb +21 -9
  121. data/lib/active_record/railtie.rb +52 -64
  122. data/lib/active_record/railties/controller_runtime.rb +13 -4
  123. data/lib/active_record/railties/databases.rake +41 -44
  124. data/lib/active_record/reflection.rb +98 -37
  125. data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
  126. data/lib/active_record/relation/batches.rb +3 -3
  127. data/lib/active_record/relation/calculations.rb +94 -61
  128. data/lib/active_record/relation/delegation.rb +8 -11
  129. data/lib/active_record/relation/finder_methods.rb +16 -2
  130. data/lib/active_record/relation/merger.rb +4 -6
  131. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
  132. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +6 -1
  133. data/lib/active_record/relation/predicate_builder.rb +3 -3
  134. data/lib/active_record/relation/query_methods.rb +196 -43
  135. data/lib/active_record/relation/record_fetch_warning.rb +3 -0
  136. data/lib/active_record/relation/spawn_methods.rb +2 -18
  137. data/lib/active_record/relation/where_clause.rb +7 -19
  138. data/lib/active_record/relation.rb +500 -66
  139. data/lib/active_record/result.rb +32 -45
  140. data/lib/active_record/runtime_registry.rb +39 -0
  141. data/lib/active_record/sanitization.rb +24 -19
  142. data/lib/active_record/schema.rb +8 -6
  143. data/lib/active_record/schema_dumper.rb +19 -9
  144. data/lib/active_record/schema_migration.rb +30 -14
  145. data/lib/active_record/signed_id.rb +11 -1
  146. data/lib/active_record/statement_cache.rb +7 -7
  147. data/lib/active_record/table_metadata.rb +1 -10
  148. data/lib/active_record/tasks/database_tasks.rb +70 -42
  149. data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
  150. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  151. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -1
  152. data/lib/active_record/test_fixtures.rb +82 -91
  153. data/lib/active_record/testing/query_assertions.rb +121 -0
  154. data/lib/active_record/timestamp.rb +4 -2
  155. data/lib/active_record/token_for.rb +22 -12
  156. data/lib/active_record/touch_later.rb +1 -1
  157. data/lib/active_record/transaction.rb +68 -0
  158. data/lib/active_record/transactions.rb +43 -14
  159. data/lib/active_record/translation.rb +0 -2
  160. data/lib/active_record/type/serialized.rb +1 -3
  161. data/lib/active_record/type_caster/connection.rb +4 -4
  162. data/lib/active_record/validations/associated.rb +9 -3
  163. data/lib/active_record/validations/uniqueness.rb +14 -10
  164. data/lib/active_record/validations.rb +4 -1
  165. data/lib/active_record.rb +149 -40
  166. data/lib/arel/alias_predication.rb +1 -1
  167. data/lib/arel/collectors/bind.rb +2 -0
  168. data/lib/arel/collectors/composite.rb +7 -0
  169. data/lib/arel/collectors/sql_string.rb +1 -1
  170. data/lib/arel/collectors/substitute_binds.rb +1 -1
  171. data/lib/arel/nodes/binary.rb +0 -6
  172. data/lib/arel/nodes/bound_sql_literal.rb +9 -5
  173. data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
  174. data/lib/arel/nodes/node.rb +4 -3
  175. data/lib/arel/nodes/sql_literal.rb +7 -0
  176. data/lib/arel/nodes.rb +2 -2
  177. data/lib/arel/predications.rb +1 -1
  178. data/lib/arel/select_manager.rb +1 -1
  179. data/lib/arel/tree_manager.rb +8 -3
  180. data/lib/arel/update_manager.rb +2 -1
  181. data/lib/arel/visitors/dot.rb +1 -0
  182. data/lib/arel/visitors/mysql.rb +9 -4
  183. data/lib/arel/visitors/postgresql.rb +1 -12
  184. data/lib/arel/visitors/to_sql.rb +31 -17
  185. data/lib/arel.rb +7 -3
  186. metadata +16 -11
@@ -81,7 +81,7 @@ module ActiveRecord
81
81
  @previous_type
82
82
  end
83
83
 
84
- def decrypt(value)
84
+ def decrypt_as_text(value)
85
85
  with_context do
86
86
  unless value.nil?
87
87
  if @default && @default == value
@@ -99,6 +99,10 @@ module ActiveRecord
99
99
  end
100
100
  end
101
101
 
102
+ def decrypt(value)
103
+ text_to_database_type decrypt_as_text(value)
104
+ end
105
+
102
106
  def try_to_deserialize_with_previous_encrypted_types(value)
103
107
  previous_types.each.with_index do |type, index|
104
108
  break type.deserialize(value)
@@ -129,27 +133,43 @@ module ActiveRecord
129
133
  encrypt(casted_value.to_s) unless casted_value.nil?
130
134
  end
131
135
 
132
- def encrypt(value)
136
+ def encrypt_as_text(value)
133
137
  with_context do
138
+ if encryptor.binary? && !cast_type.binary?
139
+ raise Errors::Encoding, "Binary encoded data can only be stored in binary columns"
140
+ end
141
+
134
142
  encryptor.encrypt(value, **encryption_options)
135
143
  end
136
144
  end
137
145
 
146
+ def encrypt(value)
147
+ text_to_database_type encrypt_as_text(value)
148
+ end
149
+
138
150
  def encryptor
139
151
  ActiveRecord::Encryption.encryptor
140
152
  end
141
153
 
142
154
  def encryption_options
143
- @encryption_options ||= { key_provider: key_provider, cipher_options: { deterministic: deterministic? } }.compact
155
+ { key_provider: key_provider, cipher_options: { deterministic: deterministic? } }.compact
144
156
  end
145
157
 
146
158
  def decryption_options
147
- @decryption_options ||= { key_provider: key_provider }.compact
159
+ { key_provider: key_provider }.compact
148
160
  end
149
161
 
150
162
  def clean_text_scheme
151
163
  @clean_text_scheme ||= ActiveRecord::Encryption::Scheme.new(downcase: downcase?, encryptor: ActiveRecord::Encryption::NullEncryptor.new)
152
164
  end
165
+
166
+ def text_to_database_type(value)
167
+ if value && cast_type.binary?
168
+ ActiveModel::Type::Binary::Data.new(value)
169
+ else
170
+ value
171
+ end
172
+ end
153
173
  end
154
174
  end
155
175
  end
@@ -12,6 +12,14 @@ module ActiveRecord
12
12
  # It interacts with a KeyProvider for getting the keys, and delegate to
13
13
  # ActiveRecord::Encryption::Cipher the actual encryption algorithm.
14
14
  class Encryptor
15
+ # === Options
16
+ #
17
+ # * <tt>:compress</tt> - Boolean indicating whether records should be compressed before encryption.
18
+ # Defaults to +true+.
19
+ def initialize(compress: true)
20
+ @compress = compress
21
+ end
22
+
15
23
  # Encrypts +clean_text+ and returns the encrypted result
16
24
  #
17
25
  # Internally, it will:
@@ -66,6 +74,10 @@ module ActiveRecord
66
74
  false
67
75
  end
68
76
 
77
+ def binary?
78
+ serializer.binary?
79
+ end
80
+
69
81
  private
70
82
  DECRYPT_ERRORS = [OpenSSL::Cipher::CipherError, Errors::EncryptedContentIntegrity, Errors::Decryption]
71
83
  ENCODING_ERRORS = [EncodingError, Errors::Encoding]
@@ -100,7 +112,6 @@ module ActiveRecord
100
112
  end
101
113
 
102
114
  def deserialize_message(message)
103
- raise Errors::Encoding unless message.is_a?(String)
104
115
  serializer.load message
105
116
  rescue ArgumentError, TypeError, Errors::ForbiddenClass
106
117
  raise Errors::Encoding
@@ -112,13 +123,17 @@ module ActiveRecord
112
123
 
113
124
  # Under certain threshold, ZIP compression is actually worse that not compressing
114
125
  def compress_if_worth_it(string)
115
- if string.bytesize > THRESHOLD_TO_JUSTIFY_COMPRESSION
126
+ if compress? && string.bytesize > THRESHOLD_TO_JUSTIFY_COMPRESSION
116
127
  [compress(string), true]
117
128
  else
118
129
  [string, false]
119
130
  end
120
131
  end
121
132
 
133
+ def compress?
134
+ @compress
135
+ end
136
+
122
137
  def compress(data)
123
138
  Zlib::Deflate.deflate(data).tap do |compressed_data|
124
139
  compressed_data.force_encoding(data.encoding)
@@ -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
@@ -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 7.3. 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
@@ -245,9 +252,11 @@ module ActiveRecord
245
252
  detect_enum_conflict!(name, name)
246
253
  detect_enum_conflict!(name, "#{name}=")
247
254
 
248
- attribute(name, **options) do |subtype|
255
+ attribute(name, **options)
256
+
257
+ decorate_attributes([name]) do |_name, subtype|
249
258
  if subtype == ActiveModel::Type.default_value
250
- raise "Undeclared attribute type for enum '#{name}'. Enums must be" \
259
+ raise "Undeclared attribute type for enum '#{name}' in #{self.name}. Enums must be" \
251
260
  " backed by a database column or declared with an explicit type" \
252
261
  " via `attribute`."
253
262
  end
@@ -7,14 +7,6 @@ module ActiveRecord
7
7
  class ActiveRecordError < StandardError
8
8
  end
9
9
 
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
10
  # Raised when the single-table inheritance mechanism fails to locate the subclass
19
11
  # (for example due to improper usage of column that
20
12
  # {ActiveRecord::Base.inheritance_column}[rdoc-ref:ModelSchema::ClassMethods#inheritance_column]
@@ -66,7 +58,7 @@ module ActiveRecord
66
58
  end
67
59
 
68
60
  # Raised when connection to the database could not been established (for example when
69
- # {ActiveRecord::Base.connection=}[rdoc-ref:ConnectionHandling#connection]
61
+ # {ActiveRecord::Base.lease_connection=}[rdoc-ref:ConnectionHandling#lease_connection]
70
62
  # is given a +nil+ object).
71
63
  class ConnectionNotEstablished < AdapterError
72
64
  def initialize(message = nil, connection_pool: nil)
@@ -137,8 +129,10 @@ module ActiveRecord
137
129
  end
138
130
 
139
131
  # 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.
132
+ # {ActiveRecord::Base.update_attribute!}[rdoc-ref:Persistence#update_attribute!]
133
+ # methods when a record is failed to validate or cannot be saved due to any of the
134
+ # <tt>before_*</tt> callbacks throwing +:abort+. See
135
+ # ActiveRecord::Callbacks for further details
142
136
  class RecordNotSaved < ActiveRecordError
143
137
  attr_reader :record
144
138
 
@@ -374,6 +368,12 @@ module ActiveRecord
374
368
  end
375
369
 
376
370
  # Raised on attempt to lazily load records that are marked as strict loading.
371
+ #
372
+ # You can resolve this error by eager loading marked records before accessing
373
+ # them. The
374
+ # {Eager Loading Associations}[https://guides.rubyonrails.org/active_record_querying.html#eager-loading-associations]
375
+ # guide covers solutions, such as using
376
+ # {ActiveRecord::Base.includes}[rdoc-ref:QueryMethods#includes].
377
377
  class StrictLoadingViolationError < ActiveRecordError
378
378
  end
379
379
 
@@ -577,4 +577,9 @@ module ActiveRecord
577
577
  # values, such as request parameters or model attributes to query methods.
578
578
  class UnknownAttributeReference < ActiveRecordError
579
579
  end
580
+
581
+ # DatabaseVersionError will be raised when the database version is not supported, or when
582
+ # the database version cannot be determined.
583
+ class DatabaseVersionError < ActiveRecordError
584
+ end
580
585
  end
@@ -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 = "3"
11
+ MINOR = 2
12
+ TINY = 0
13
+ PRE = "beta1"
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.