activerecord 7.1.3.2 → 7.2.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|