activerecord 7.1.3.2 → 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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +507 -2123
- data/README.rdoc +15 -15
- data/examples/performance.rb +2 -2
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +9 -8
- data/lib/active_record/associations/belongs_to_association.rb +18 -11
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/belongs_to.rb +1 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -2
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/collection_association.rb +4 -2
- data/lib/active_record/associations/collection_proxy.rb +14 -1
- data/lib/active_record/associations/has_many_association.rb +3 -3
- data/lib/active_record/associations/has_one_association.rb +2 -2
- data/lib/active_record/associations/join_dependency/join_association.rb +27 -25
- data/lib/active_record/associations/join_dependency.rb +5 -7
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +2 -1
- data/lib/active_record/associations/preloader/branch.rb +7 -1
- data/lib/active_record/associations/preloader/through_association.rb +1 -3
- data/lib/active_record/associations/singular_association.rb +6 -0
- data/lib/active_record/associations/through_association.rb +1 -1
- data/lib/active_record/associations.rb +34 -11
- data/lib/active_record/attribute_assignment.rb +1 -11
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +1 -1
- data/lib/active_record/attribute_methods/primary_key.rb +23 -55
- data/lib/active_record/attribute_methods/read.rb +1 -13
- data/lib/active_record/attribute_methods/serialization.rb +4 -24
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -6
- data/lib/active_record/attribute_methods.rb +87 -58
- data/lib/active_record/attributes.rb +55 -42
- data/lib/active_record/autosave_association.rb +14 -30
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +24 -107
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +248 -58
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +35 -18
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +160 -75
- data/lib/active_record/connection_adapters/abstract/quoting.rb +65 -91
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +22 -9
- data/lib/active_record/connection_adapters/abstract/transaction.rb +60 -57
- data/lib/active_record/connection_adapters/abstract_adapter.rb +32 -61
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +69 -19
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +9 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +43 -48
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +7 -0
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +11 -5
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -32
- data/lib/active_record/connection_adapters/pool_config.rb +7 -6
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +27 -4
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +58 -58
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +16 -12
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +26 -21
- data/lib/active_record/connection_adapters/schema_cache.rb +123 -128
- data/lib/active_record/connection_adapters/sqlite3/column.rb +14 -1
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +10 -6
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +44 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +25 -2
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +109 -77
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +12 -6
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +32 -65
- data/lib/active_record/connection_adapters.rb +121 -0
- data/lib/active_record/connection_handling.rb +56 -41
- data/lib/active_record/core.rb +59 -38
- data/lib/active_record/counter_cache.rb +23 -10
- data/lib/active_record/database_configurations/connection_url_resolver.rb +7 -2
- data/lib/active_record/database_configurations/database_config.rb +15 -4
- data/lib/active_record/database_configurations/hash_config.rb +44 -36
- data/lib/active_record/database_configurations/url_config.rb +20 -1
- data/lib/active_record/database_configurations.rb +1 -1
- data/lib/active_record/delegated_type.rb +30 -6
- data/lib/active_record/destroy_association_async_job.rb +1 -1
- data/lib/active_record/dynamic_matchers.rb +2 -2
- data/lib/active_record/encryption/encryptable_record.rb +2 -2
- data/lib/active_record/encryption/encrypted_attribute_type.rb +24 -4
- data/lib/active_record/encryption/encryptor.rb +17 -2
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +4 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +8 -4
- data/lib/active_record/enum.rb +11 -2
- data/lib/active_record/errors.rb +16 -11
- data/lib/active_record/explain.rb +13 -24
- data/lib/active_record/fixtures.rb +37 -31
- data/lib/active_record/future_result.rb +17 -4
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +4 -2
- data/lib/active_record/insert_all.rb +18 -15
- data/lib/active_record/integration.rb +4 -1
- data/lib/active_record/internal_metadata.rb +48 -34
- data/lib/active_record/locking/optimistic.rb +8 -7
- data/lib/active_record/log_subscriber.rb +0 -21
- data/lib/active_record/marshalling.rb +1 -1
- data/lib/active_record/message_pack.rb +2 -2
- data/lib/active_record/migration/command_recorder.rb +2 -3
- data/lib/active_record/migration/compatibility.rb +11 -3
- data/lib/active_record/migration/default_strategy.rb +4 -5
- data/lib/active_record/migration/pending_migration_connection.rb +2 -2
- data/lib/active_record/migration.rb +85 -76
- data/lib/active_record/model_schema.rb +34 -69
- data/lib/active_record/nested_attributes.rb +11 -3
- data/lib/active_record/normalization.rb +3 -7
- data/lib/active_record/persistence.rb +32 -354
- data/lib/active_record/query_cache.rb +18 -6
- data/lib/active_record/query_logs.rb +15 -0
- data/lib/active_record/query_logs_formatter.rb +1 -1
- data/lib/active_record/querying.rb +21 -9
- data/lib/active_record/railtie.rb +52 -64
- data/lib/active_record/railties/controller_runtime.rb +13 -4
- data/lib/active_record/railties/databases.rake +41 -44
- data/lib/active_record/reflection.rb +98 -37
- data/lib/active_record/relation/batches/batch_enumerator.rb +15 -2
- data/lib/active_record/relation/batches.rb +3 -3
- data/lib/active_record/relation/calculations.rb +94 -61
- data/lib/active_record/relation/delegation.rb +8 -11
- data/lib/active_record/relation/finder_methods.rb +16 -2
- data/lib/active_record/relation/merger.rb +4 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +6 -1
- data/lib/active_record/relation/predicate_builder.rb +3 -3
- data/lib/active_record/relation/query_methods.rb +196 -43
- data/lib/active_record/relation/record_fetch_warning.rb +3 -0
- data/lib/active_record/relation/spawn_methods.rb +2 -18
- data/lib/active_record/relation/where_clause.rb +7 -19
- data/lib/active_record/relation.rb +500 -66
- data/lib/active_record/result.rb +32 -45
- data/lib/active_record/runtime_registry.rb +39 -0
- data/lib/active_record/sanitization.rb +24 -19
- data/lib/active_record/schema.rb +8 -6
- data/lib/active_record/schema_dumper.rb +19 -9
- data/lib/active_record/schema_migration.rb +30 -14
- data/lib/active_record/signed_id.rb +11 -1
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/table_metadata.rb +1 -10
- data/lib/active_record/tasks/database_tasks.rb +70 -42
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -1
- data/lib/active_record/test_fixtures.rb +82 -91
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +4 -2
- data/lib/active_record/token_for.rb +22 -12
- data/lib/active_record/touch_later.rb +1 -1
- data/lib/active_record/transaction.rb +68 -0
- data/lib/active_record/transactions.rb +43 -14
- data/lib/active_record/translation.rb +0 -2
- data/lib/active_record/type/serialized.rb +1 -3
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/associated.rb +9 -3
- data/lib/active_record/validations/uniqueness.rb +14 -10
- data/lib/active_record/validations.rb +4 -1
- data/lib/active_record.rb +149 -40
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +2 -0
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/nodes/binary.rb +0 -6
- data/lib/arel/nodes/bound_sql_literal.rb +9 -5
- data/lib/arel/nodes/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +4 -3
- data/lib/arel/nodes/sql_literal.rb +7 -0
- data/lib/arel/nodes.rb +2 -2
- data/lib/arel/predications.rb +1 -1
- data/lib/arel/select_manager.rb +1 -1
- data/lib/arel/tree_manager.rb +8 -3
- data/lib/arel/update_manager.rb +2 -1
- data/lib/arel/visitors/dot.rb +1 -0
- data/lib/arel/visitors/mysql.rb +9 -4
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/to_sql.rb +31 -17
- data/lib/arel.rb +7 -3
- metadata +17 -12
@@ -81,7 +81,7 @@ module ActiveRecord
|
|
81
81
|
@previous_type
|
82
82
|
end
|
83
83
|
|
84
|
-
def
|
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
|
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
|
-
|
155
|
+
{ key_provider: key_provider, cipher_options: { deterministic: deterministic? } }.compact
|
144
156
|
end
|
145
157
|
|
146
158
|
def decryption_options
|
147
|
-
|
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
|
@@ -50,7 +50,7 @@ module ActiveRecord
|
|
50
50
|
end
|
51
51
|
|
52
52
|
def key_provider
|
53
|
-
@
|
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
|
84
|
-
|
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
|
-
|
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
|
data/lib/active_record/enum.rb
CHANGED
@@ -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)
|
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
|
data/lib/active_record/errors.rb
CHANGED
@@ -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.
|
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.
|
141
|
-
# methods when a record is
|
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 =
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
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,
|
496
|
-
# belongs_to :book,
|
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
|
557
|
-
@@all_cached_fixtures[
|
562
|
+
def cache_for_connection_pool(connection_pool)
|
563
|
+
@@all_cached_fixtures[connection_pool]
|
558
564
|
end
|
559
565
|
|
560
|
-
def fixture_is_cached?(
|
561
|
-
|
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(
|
570
|
+
def cached_fixtures(connection_pool, keys_to_fetch = nil)
|
565
571
|
if keys_to_fetch
|
566
|
-
|
572
|
+
cache_for_connection_pool(connection_pool).values_at(*keys_to_fetch)
|
567
573
|
else
|
568
|
-
|
574
|
+
cache_for_connection_pool(connection_pool).values
|
569
575
|
end
|
570
576
|
end
|
571
577
|
|
572
|
-
def cache_fixtures(
|
573
|
-
|
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
|
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
|
-
|
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?(
|
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
|
-
|
611
|
+
connection_pool,
|
608
612
|
)
|
609
|
-
cache_fixtures(
|
613
|
+
cache_fixtures(connection_pool, fixtures_map)
|
610
614
|
end
|
611
|
-
cached_fixtures(
|
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,
|
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,
|
662
|
+
insert(fixture_sets, connection_pool)
|
659
663
|
|
660
664
|
fixtures_map
|
661
665
|
end
|
662
666
|
|
663
|
-
def insert(fixture_sets,
|
664
|
-
|
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.
|
670
|
+
fixture_set.model_class.connection_pool
|
667
671
|
else
|
668
|
-
|
672
|
+
connection_pool
|
669
673
|
end
|
670
674
|
end
|
671
675
|
|
672
|
-
|
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
|
-
|
685
|
+
pool.with_connection do |conn|
|
686
|
+
conn.insert_fixtures_set(table_rows_for_connection, table_rows_for_connection.keys)
|
682
687
|
|
683
|
-
|
688
|
+
check_all_foreign_keys_valid!(conn)
|
684
689
|
|
685
|
-
|
686
|
-
|
687
|
-
|
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
|
-
|
36
|
-
|
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
|
-
|
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
|
@@ -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
|
-
|
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.
|