activerecord 7.2.2 → 8.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +239 -878
- data/README.rdoc +1 -1
- data/lib/active_record/association_relation.rb +1 -0
- data/lib/active_record/associations/association.rb +34 -10
- data/lib/active_record/associations/builder/association.rb +7 -6
- data/lib/active_record/associations/collection_association.rb +10 -8
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +3 -2
- data/lib/active_record/associations/preloader/association.rb +2 -2
- data/lib/active_record/associations/singular_association.rb +8 -3
- data/lib/active_record/associations.rb +34 -4
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_methods/primary_key.rb +2 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +2 -12
- data/lib/active_record/attribute_methods.rb +1 -1
- data/lib/active_record/autosave_association.rb +69 -27
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +16 -10
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +0 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +0 -9
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +90 -43
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +8 -2
- data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +4 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +7 -2
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +33 -6
- data/lib/active_record/connection_adapters/abstract/transaction.rb +15 -5
- data/lib/active_record/connection_adapters/abstract_adapter.rb +24 -26
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +21 -39
- data/lib/active_record/connection_adapters/mysql/quoting.rb +0 -8
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +2 -8
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +43 -45
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +42 -98
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -8
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +64 -42
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +1 -11
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +6 -12
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +59 -16
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +45 -95
- data/lib/active_record/connection_adapters/schema_cache.rb +1 -3
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +76 -100
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +13 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +8 -1
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +53 -12
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +37 -67
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +0 -17
- data/lib/active_record/connection_adapters.rb +0 -56
- data/lib/active_record/connection_handling.rb +22 -0
- data/lib/active_record/core.rb +18 -14
- data/lib/active_record/database_configurations/database_config.rb +4 -0
- data/lib/active_record/database_configurations/hash_config.rb +8 -0
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +4 -4
- data/lib/active_record/encryption/encrypted_attribute_type.rb +10 -1
- data/lib/active_record/encryption/encryptor.rb +15 -8
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- data/lib/active_record/encryption/scheme.rb +8 -1
- data/lib/active_record/enum.rb +9 -22
- data/lib/active_record/errors.rb +13 -5
- data/lib/active_record/fixtures.rb +0 -2
- data/lib/active_record/future_result.rb +14 -10
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/insert_all.rb +1 -1
- data/lib/active_record/locking/optimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +5 -11
- data/lib/active_record/migration/command_recorder.rb +27 -10
- data/lib/active_record/migration/compatibility.rb +5 -2
- data/lib/active_record/migration.rb +35 -38
- data/lib/active_record/model_schema.rb +3 -4
- data/lib/active_record/nested_attributes.rb +4 -6
- data/lib/active_record/persistence.rb +128 -130
- data/lib/active_record/query_logs.rb +102 -50
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +8 -8
- data/lib/active_record/railtie.rb +2 -26
- data/lib/active_record/railties/databases.rake +2 -17
- data/lib/active_record/reflection.rb +18 -21
- data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
- data/lib/active_record/relation/batches.rb +132 -72
- data/lib/active_record/relation/calculations.rb +40 -39
- data/lib/active_record/relation/delegation.rb +25 -14
- data/lib/active_record/relation/finder_methods.rb +18 -18
- data/lib/active_record/relation/merger.rb +8 -8
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -1
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +13 -0
- data/lib/active_record/relation/query_methods.rb +115 -65
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/relation.rb +79 -61
- data/lib/active_record/result.rb +66 -4
- data/lib/active_record/sanitization.rb +7 -6
- data/lib/active_record/schema_dumper.rb +5 -0
- data/lib/active_record/schema_migration.rb +2 -1
- data/lib/active_record/scoping/named.rb +5 -2
- data/lib/active_record/statement_cache.rb +12 -12
- data/lib/active_record/store.rb +7 -3
- data/lib/active_record/table_metadata.rb +1 -3
- data/lib/active_record/tasks/database_tasks.rb +48 -47
- data/lib/active_record/tasks/mysql_database_tasks.rb +0 -2
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -2
- data/lib/active_record/test_fixtures.rb +12 -0
- data/lib/active_record/token_for.rb +1 -1
- data/lib/active_record/validations/uniqueness.rb +8 -8
- data/lib/active_record.rb +15 -45
- data/lib/arel/collectors/bind.rb +1 -1
- data/lib/arel/table.rb +3 -7
- metadata +11 -12
- data/lib/active_record/relation/record_fetch_warning.rb +0 -52
data/lib/active_record/core.rb
CHANGED
@@ -89,6 +89,7 @@ module ActiveRecord
|
|
89
89
|
class_attribute :belongs_to_required_by_default, instance_accessor: false
|
90
90
|
|
91
91
|
class_attribute :strict_loading_by_default, instance_accessor: false, default: false
|
92
|
+
class_attribute :strict_loading_mode, instance_accessor: false, default: :all
|
92
93
|
|
93
94
|
class_attribute :has_many_inversing, instance_accessor: false, default: false
|
94
95
|
|
@@ -381,7 +382,7 @@ module ActiveRecord
|
|
381
382
|
end
|
382
383
|
|
383
384
|
def predicate_builder # :nodoc:
|
384
|
-
@predicate_builder ||= PredicateBuilder.new(
|
385
|
+
@predicate_builder ||= PredicateBuilder.new(TableMetadata.new(self, arel_table))
|
385
386
|
end
|
386
387
|
|
387
388
|
def type_caster # :nodoc:
|
@@ -426,10 +427,6 @@ module ActiveRecord
|
|
426
427
|
end
|
427
428
|
end
|
428
429
|
|
429
|
-
def table_metadata
|
430
|
-
TableMetadata.new(self, arel_table)
|
431
|
-
end
|
432
|
-
|
433
430
|
def cached_find_by(keys, values)
|
434
431
|
with_connection do |connection|
|
435
432
|
statement = cached_find_by_statement(connection, keys) { |params|
|
@@ -443,8 +440,8 @@ module ActiveRecord
|
|
443
440
|
where(wheres).limit(1)
|
444
441
|
}
|
445
442
|
|
446
|
-
|
447
|
-
|
443
|
+
statement.execute(values.flatten, connection, allow_retry: true).then do |r|
|
444
|
+
r.first
|
448
445
|
rescue TypeError
|
449
446
|
raise ActiveRecord::StatementInvalid
|
450
447
|
end
|
@@ -540,12 +537,7 @@ module ActiveRecord
|
|
540
537
|
|
541
538
|
##
|
542
539
|
def initialize_dup(other) # :nodoc:
|
543
|
-
@attributes =
|
544
|
-
if self.class.composite_primary_key?
|
545
|
-
@primary_key.each { |key| @attributes.reset(key) }
|
546
|
-
else
|
547
|
-
@attributes.reset(@primary_key)
|
548
|
-
end
|
540
|
+
@attributes = init_attributes(other)
|
549
541
|
|
550
542
|
_run_initialize_callbacks
|
551
543
|
|
@@ -557,6 +549,18 @@ module ActiveRecord
|
|
557
549
|
super
|
558
550
|
end
|
559
551
|
|
552
|
+
def init_attributes(_) # :nodoc:
|
553
|
+
attrs = @attributes.deep_dup
|
554
|
+
|
555
|
+
if self.class.composite_primary_key?
|
556
|
+
@primary_key.each { |key| attrs.reset(key) }
|
557
|
+
else
|
558
|
+
attrs.reset(@primary_key)
|
559
|
+
end
|
560
|
+
|
561
|
+
attrs
|
562
|
+
end
|
563
|
+
|
560
564
|
# Populate +coder+ with attributes about this record that should be
|
561
565
|
# serialized. The structure of +coder+ defined in this method is
|
562
566
|
# guaranteed to match the structure of +coder+ passed to the #init_with
|
@@ -810,7 +814,7 @@ module ActiveRecord
|
|
810
814
|
|
811
815
|
@primary_key = klass.primary_key
|
812
816
|
@strict_loading = klass.strict_loading_by_default
|
813
|
-
@strict_loading_mode =
|
817
|
+
@strict_loading_mode = klass.strict_loading_mode
|
814
818
|
|
815
819
|
klass.define_attribute_methods
|
816
820
|
end
|
@@ -130,6 +130,14 @@ module ActiveRecord
|
|
130
130
|
Base.configurations.primary?(name)
|
131
131
|
end
|
132
132
|
|
133
|
+
# Determines whether the db:prepare task should seed the database from db/seeds.rb.
|
134
|
+
#
|
135
|
+
# If the `seeds` key is present in the config, `seeds?` will return its value. Otherwise, it
|
136
|
+
# will return `true` for the primary database and `false` for all other configs.
|
137
|
+
def seeds?
|
138
|
+
configuration_hash.fetch(:seeds, primary?)
|
139
|
+
end
|
140
|
+
|
133
141
|
# Determines whether to dump the schema/structure files and the filename that
|
134
142
|
# should be used.
|
135
143
|
#
|
@@ -8,7 +8,8 @@ module ActiveRecord
|
|
8
8
|
class Config
|
9
9
|
attr_accessor :primary_key, :deterministic_key, :store_key_references, :key_derivation_salt, :hash_digest_class,
|
10
10
|
:support_unencrypted_data, :encrypt_fixtures, :validate_column_size, :add_to_filter_parameters,
|
11
|
-
:excluded_from_filter_parameters, :extend_queries, :previous_schemes, :forced_encoding_for_deterministic_encryption
|
11
|
+
:excluded_from_filter_parameters, :extend_queries, :previous_schemes, :forced_encoding_for_deterministic_encryption,
|
12
|
+
:compressor
|
12
13
|
|
13
14
|
def initialize
|
14
15
|
set_defaults
|
@@ -55,6 +56,7 @@ module ActiveRecord
|
|
55
56
|
self.previous_schemes = []
|
56
57
|
self.forced_encoding_for_deterministic_encryption = Encoding::UTF_8
|
57
58
|
self.hash_digest_class = OpenSSL::Digest::SHA1
|
59
|
+
self.compressor = Zlib
|
58
60
|
|
59
61
|
# TODO: Setting to false for now as the implementation is a bit experimental
|
60
62
|
self.extend_queries = false
|
@@ -46,11 +46,11 @@ module ActiveRecord
|
|
46
46
|
# * <tt>:previous</tt> - List of previous encryption schemes. When provided, they will be used in order when trying to read
|
47
47
|
# the attribute. Each entry of the list can contain the properties supported by #encrypts. Also, when deterministic
|
48
48
|
# encryption is used, they will be used to generate additional ciphertexts to check in the queries.
|
49
|
-
def encrypts(*names, key_provider: nil, key: nil, deterministic: false, support_unencrypted_data: nil, downcase: false, ignore_case: false, previous: [], **context_properties)
|
49
|
+
def encrypts(*names, key_provider: nil, key: nil, deterministic: false, support_unencrypted_data: nil, downcase: false, ignore_case: false, previous: [], compress: true, compressor: nil, **context_properties)
|
50
50
|
self.encrypted_attributes ||= Set.new # not using :default because the instance would be shared across classes
|
51
51
|
|
52
52
|
names.each do |name|
|
53
|
-
encrypt_attribute name, key_provider: key_provider, key: key, deterministic: deterministic, support_unencrypted_data: support_unencrypted_data, downcase: downcase, ignore_case: ignore_case, previous: previous, **context_properties
|
53
|
+
encrypt_attribute name, key_provider: key_provider, key: key, deterministic: deterministic, support_unencrypted_data: support_unencrypted_data, downcase: downcase, ignore_case: ignore_case, previous: previous, compress: compress, compressor: compressor, **context_properties
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
@@ -81,12 +81,12 @@ module ActiveRecord
|
|
81
81
|
end
|
82
82
|
end
|
83
83
|
|
84
|
-
def encrypt_attribute(name, key_provider: nil, key: nil, deterministic: false, support_unencrypted_data: nil, downcase: false, ignore_case: false, previous: [], **context_properties)
|
84
|
+
def encrypt_attribute(name, key_provider: nil, key: nil, deterministic: false, support_unencrypted_data: nil, downcase: false, ignore_case: false, previous: [], compress: true, compressor: nil, **context_properties)
|
85
85
|
encrypted_attributes << name.to_sym
|
86
86
|
|
87
87
|
decorate_attributes([name]) do |name, cast_type|
|
88
88
|
scheme = scheme_for key_provider: key_provider, key: key, deterministic: deterministic, support_unencrypted_data: support_unencrypted_data, \
|
89
|
-
downcase: downcase, ignore_case: ignore_case, previous: previous, **context_properties
|
89
|
+
downcase: downcase, ignore_case: ignore_case, previous: previous, compress: compress, compressor: compressor, **context_properties
|
90
90
|
|
91
91
|
ActiveRecord::Encryption::EncryptedAttributeType.new(scheme: scheme, cast_type: cast_type, default: columns_hash[name.to_s]&.default)
|
92
92
|
end
|
@@ -100,7 +100,7 @@ module ActiveRecord
|
|
100
100
|
end
|
101
101
|
|
102
102
|
def decrypt(value)
|
103
|
-
text_to_database_type decrypt_as_text(value)
|
103
|
+
text_to_database_type decrypt_as_text(database_type_to_text(value))
|
104
104
|
end
|
105
105
|
|
106
106
|
def try_to_deserialize_with_previous_encrypted_types(value)
|
@@ -170,6 +170,15 @@ module ActiveRecord
|
|
170
170
|
value
|
171
171
|
end
|
172
172
|
end
|
173
|
+
|
174
|
+
def database_type_to_text(value)
|
175
|
+
if value && cast_type.binary?
|
176
|
+
binary_cast_type = cast_type.serialized? ? cast_type.subtype : cast_type
|
177
|
+
binary_cast_type.deserialize(value)
|
178
|
+
else
|
179
|
+
value
|
180
|
+
end
|
181
|
+
end
|
173
182
|
end
|
174
183
|
end
|
175
184
|
end
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "openssl"
|
4
|
-
require "zlib"
|
5
4
|
require "active_support/core_ext/numeric"
|
6
5
|
|
7
6
|
module ActiveRecord
|
@@ -12,12 +11,20 @@ module ActiveRecord
|
|
12
11
|
# It interacts with a KeyProvider for getting the keys, and delegate to
|
13
12
|
# ActiveRecord::Encryption::Cipher the actual encryption algorithm.
|
14
13
|
class Encryptor
|
14
|
+
# The compressor to use for compressing the payload
|
15
|
+
attr_reader :compressor
|
16
|
+
|
15
17
|
# === Options
|
16
18
|
#
|
17
19
|
# * <tt>:compress</tt> - Boolean indicating whether records should be compressed before encryption.
|
18
20
|
# Defaults to +true+.
|
19
|
-
|
21
|
+
# * <tt>:compressor</tt> - The compressor to use.
|
22
|
+
# 1. If compressor is provided, it will be used.
|
23
|
+
# 2. If not, it will use ActiveRecord::Encryption.config.compressor which default value is +Zlib+.
|
24
|
+
# If you want to use a custom compressor, it must respond to +deflate+ and +inflate+.
|
25
|
+
def initialize(compress: true, compressor: nil)
|
20
26
|
@compress = compress
|
27
|
+
@compressor = compressor || ActiveRecord::Encryption.config.compressor
|
21
28
|
end
|
22
29
|
|
23
30
|
# Encrypts +clean_text+ and returns the encrypted result
|
@@ -78,6 +85,10 @@ module ActiveRecord
|
|
78
85
|
serializer.binary?
|
79
86
|
end
|
80
87
|
|
88
|
+
def compress? # :nodoc:
|
89
|
+
@compress
|
90
|
+
end
|
91
|
+
|
81
92
|
private
|
82
93
|
DECRYPT_ERRORS = [OpenSSL::Cipher::CipherError, Errors::EncryptedContentIntegrity, Errors::Decryption]
|
83
94
|
ENCODING_ERRORS = [EncodingError, Errors::Encoding]
|
@@ -130,12 +141,8 @@ module ActiveRecord
|
|
130
141
|
end
|
131
142
|
end
|
132
143
|
|
133
|
-
def compress?
|
134
|
-
@compress
|
135
|
-
end
|
136
|
-
|
137
144
|
def compress(data)
|
138
|
-
|
145
|
+
@compressor.deflate(data).tap do |compressed_data|
|
139
146
|
compressed_data.force_encoding(data.encoding)
|
140
147
|
end
|
141
148
|
end
|
@@ -149,7 +156,7 @@ module ActiveRecord
|
|
149
156
|
end
|
150
157
|
|
151
158
|
def uncompress(data)
|
152
|
-
|
159
|
+
@compressor.inflate(data).tap do |uncompressed_data|
|
153
160
|
uncompressed_data.force_encoding(data.encoding)
|
154
161
|
end
|
155
162
|
end
|
@@ -41,6 +41,8 @@ module ActiveRecord
|
|
41
41
|
module EncryptedQuery # :nodoc:
|
42
42
|
class << self
|
43
43
|
def process_arguments(owner, args, check_for_additional_values)
|
44
|
+
owner = owner.model if owner.is_a?(Relation)
|
45
|
+
|
44
46
|
return args if owner.deterministic_encrypted_attributes&.empty?
|
45
47
|
|
46
48
|
if args.is_a?(Array) && (options = args.first).is_a?(Hash)
|
@@ -102,12 +104,12 @@ module ActiveRecord
|
|
102
104
|
end
|
103
105
|
|
104
106
|
def scope_for_create
|
105
|
-
return super unless
|
107
|
+
return super unless model.deterministic_encrypted_attributes&.any?
|
106
108
|
|
107
109
|
scope_attributes = super
|
108
110
|
wheres = where_values_hash
|
109
111
|
|
110
|
-
|
112
|
+
model.deterministic_encrypted_attributes.each do |attribute_name|
|
111
113
|
attribute_name = attribute_name.to_s
|
112
114
|
values = wheres[attribute_name]
|
113
115
|
if values.is_a?(Array) && values[1..].all?(AdditionalValue)
|
@@ -11,7 +11,7 @@ module ActiveRecord
|
|
11
11
|
attr_accessor :previous_schemes
|
12
12
|
|
13
13
|
def initialize(key_provider: nil, key: nil, deterministic: nil, support_unencrypted_data: nil, downcase: nil, ignore_case: nil,
|
14
|
-
previous_schemes: nil, **context_properties)
|
14
|
+
previous_schemes: nil, compress: true, compressor: nil, **context_properties)
|
15
15
|
# Initializing all attributes to +nil+ as we want to allow a "not set" semantics so that we
|
16
16
|
# can merge schemes without overriding values with defaults. See +#merge+
|
17
17
|
|
@@ -24,8 +24,13 @@ module ActiveRecord
|
|
24
24
|
@previous_schemes_param = previous_schemes
|
25
25
|
@previous_schemes = Array.wrap(previous_schemes)
|
26
26
|
@context_properties = context_properties
|
27
|
+
@compress = compress
|
28
|
+
@compressor = compressor
|
27
29
|
|
28
30
|
validate_config!
|
31
|
+
|
32
|
+
@context_properties[:encryptor] = Encryptor.new(compress: @compress) unless @compress
|
33
|
+
@context_properties[:encryptor] = Encryptor.new(compressor: compressor) if compressor
|
29
34
|
end
|
30
35
|
|
31
36
|
def ignore_case?
|
@@ -78,6 +83,8 @@ module ActiveRecord
|
|
78
83
|
def validate_config!
|
79
84
|
raise Errors::Configuration, "ignore_case: can only be used with deterministic encryption" if @ignore_case && !@deterministic
|
80
85
|
raise Errors::Configuration, "key_provider: and key: can't be used simultaneously" if @key_provider_param && @key
|
86
|
+
raise Errors::Configuration, "compressor: can't be used with compress: false" if !@compress && @compressor
|
87
|
+
raise Errors::Configuration, "compressor: can't be used with encryptor" if @compressor && @context_properties[:encryptor]
|
81
88
|
end
|
82
89
|
|
83
90
|
def key_provider_from_key
|
data/lib/active_record/enum.rb
CHANGED
@@ -213,34 +213,16 @@ module ActiveRecord
|
|
213
213
|
attr_reader :name, :mapping
|
214
214
|
end
|
215
215
|
|
216
|
-
def enum(name
|
217
|
-
|
218
|
-
|
219
|
-
return _enum(name, values, **options)
|
220
|
-
end
|
221
|
-
|
222
|
-
definitions = options.slice!(:_prefix, :_suffix, :_scopes, :_default, :_instance_methods)
|
223
|
-
options.transform_keys! { |key| :"#{key[1..-1]}" }
|
224
|
-
|
225
|
-
definitions.each { |name, values| _enum(name, values, **options) }
|
226
|
-
|
227
|
-
ActiveRecord.deprecator.warn(<<~MSG)
|
228
|
-
Defining enums with keyword arguments is deprecated and will be removed
|
229
|
-
in Rails 8.0. Positional arguments should be used instead:
|
230
|
-
|
231
|
-
#{definitions.map { |name, values| "enum :#{name}, #{values}" }.join("\n")}
|
232
|
-
MSG
|
216
|
+
def enum(name, values = nil, **options)
|
217
|
+
values, options = options, {} unless values
|
218
|
+
_enum(name, values, **options)
|
233
219
|
end
|
234
220
|
|
235
221
|
private
|
236
|
-
def inherited(base)
|
237
|
-
base.defined_enums = defined_enums.deep_dup
|
238
|
-
super
|
239
|
-
end
|
240
|
-
|
241
222
|
def _enum(name, values, prefix: nil, suffix: nil, scopes: true, instance_methods: true, validate: false, **options)
|
242
223
|
assert_valid_enum_definition_values(values)
|
243
224
|
assert_valid_enum_options(options)
|
225
|
+
|
244
226
|
# statuses = { }
|
245
227
|
enum_values = ActiveSupport::HashWithIndifferentAccess.new
|
246
228
|
name = name.to_s
|
@@ -304,6 +286,11 @@ module ActiveRecord
|
|
304
286
|
enum_values.freeze
|
305
287
|
end
|
306
288
|
|
289
|
+
def inherited(base)
|
290
|
+
base.defined_enums = defined_enums.deep_dup
|
291
|
+
super
|
292
|
+
end
|
293
|
+
|
307
294
|
class EnumMethods < Module # :nodoc:
|
308
295
|
def initialize(klass)
|
309
296
|
@klass = klass
|
data/lib/active_record/errors.rb
CHANGED
@@ -84,6 +84,19 @@ module ActiveRecord
|
|
84
84
|
class ConnectionTimeoutError < ConnectionNotEstablished
|
85
85
|
end
|
86
86
|
|
87
|
+
# Raised when a database connection pool is requested but
|
88
|
+
# has not been defined.
|
89
|
+
class ConnectionNotDefined < ConnectionNotEstablished
|
90
|
+
def initialize(message = nil, connection_name: nil, role: nil, shard: nil)
|
91
|
+
super(message)
|
92
|
+
@connection_name = connection_name
|
93
|
+
@role = role
|
94
|
+
@shard = shard
|
95
|
+
end
|
96
|
+
|
97
|
+
attr_reader :connection_name, :role, :shard
|
98
|
+
end
|
99
|
+
|
87
100
|
# Raised when connection to the database could not been established because it was not
|
88
101
|
# able to connect to the host or when the authorization failed.
|
89
102
|
class DatabaseConnectionError < ConnectionNotEstablished
|
@@ -484,11 +497,6 @@ module ActiveRecord
|
|
484
497
|
# relation.limit!(5) # => ActiveRecord::UnmodifiableRelation
|
485
498
|
class UnmodifiableRelation < ActiveRecordError
|
486
499
|
end
|
487
|
-
deprecate_constant(
|
488
|
-
:ImmutableRelation,
|
489
|
-
"ActiveRecord::UnmodifiableRelation",
|
490
|
-
deprecator: ActiveRecord.deprecator
|
491
|
-
)
|
492
500
|
|
493
501
|
# TransactionIsolationError will be raised under the following conditions:
|
494
502
|
#
|
@@ -100,17 +100,21 @@ module ActiveRecord
|
|
100
100
|
def execute_or_skip
|
101
101
|
return unless pending?
|
102
102
|
|
103
|
-
@
|
104
|
-
return unless
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
103
|
+
@session.synchronize do
|
104
|
+
return unless pending?
|
105
|
+
|
106
|
+
@pool.with_connection do |connection|
|
107
|
+
return unless @mutex.try_lock
|
108
|
+
begin
|
109
|
+
if pending?
|
110
|
+
@event_buffer = EventBuffer.new(self, @instrumenter)
|
111
|
+
connection.with_instrumenter(@event_buffer) do
|
112
|
+
execute_query(connection, async: true)
|
113
|
+
end
|
110
114
|
end
|
115
|
+
ensure
|
116
|
+
@mutex.unlock
|
111
117
|
end
|
112
|
-
ensure
|
113
|
-
@mutex.unlock
|
114
118
|
end
|
115
119
|
end
|
116
120
|
end
|
@@ -163,7 +167,7 @@ module ActiveRecord
|
|
163
167
|
end
|
164
168
|
|
165
169
|
def exec_query(connection, *args, **kwargs)
|
166
|
-
connection.
|
170
|
+
connection.raw_exec_query(*args, **kwargs)
|
167
171
|
end
|
168
172
|
|
169
173
|
class SelectAll < FutureResult # :nodoc:
|
@@ -240,7 +240,7 @@ module ActiveRecord
|
|
240
240
|
|
241
241
|
values_list = insert_all.map_key_with_value do |key, value|
|
242
242
|
next value if Arel::Nodes::SqlLiteral === value
|
243
|
-
|
243
|
+
ActiveModel::Type::SerializeCastValue.serialize(type = types[key], type.cast(value))
|
244
244
|
end
|
245
245
|
|
246
246
|
connection.visitor.compile(Arel::Nodes::ValuesList.new(values_list))
|
@@ -9,7 +9,7 @@ module ActiveRecord
|
|
9
9
|
# it was opened, an ActiveRecord::StaleObjectError exception is thrown if that has occurred
|
10
10
|
# and the update is ignored.
|
11
11
|
#
|
12
|
-
# Check out
|
12
|
+
# Check out ActiveRecord::Locking::Pessimistic for an alternative.
|
13
13
|
#
|
14
14
|
# == Usage
|
15
15
|
#
|
@@ -126,18 +126,12 @@ module ActiveRecord
|
|
126
126
|
end
|
127
127
|
end
|
128
128
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
return frame if frame
|
134
|
-
end
|
135
|
-
nil
|
136
|
-
end
|
137
|
-
else
|
138
|
-
def query_source_location
|
139
|
-
backtrace_cleaner.clean(caller(1).lazy).first
|
129
|
+
def query_source_location
|
130
|
+
Thread.each_caller_location do |location|
|
131
|
+
frame = backtrace_cleaner.clean_frame(location)
|
132
|
+
return frame if frame
|
140
133
|
end
|
134
|
+
nil
|
141
135
|
end
|
142
136
|
|
143
137
|
def filter(name, value)
|
@@ -22,10 +22,12 @@ module ActiveRecord
|
|
22
22
|
# * change_table_comment (must supply a +:from+ and +:to+ option)
|
23
23
|
# * create_enum
|
24
24
|
# * create_join_table
|
25
|
+
# * create_virtual_table
|
25
26
|
# * create_table
|
26
27
|
# * disable_extension
|
27
28
|
# * drop_enum (must supply a list of values)
|
28
29
|
# * drop_join_table
|
30
|
+
# * drop_virtual_table (must supply options)
|
29
31
|
# * drop_table (must supply a block)
|
30
32
|
# * enable_extension
|
31
33
|
# * remove_column (must supply a type)
|
@@ -38,7 +40,7 @@ module ActiveRecord
|
|
38
40
|
# * remove_reference
|
39
41
|
# * remove_timestamps
|
40
42
|
# * rename_column
|
41
|
-
# * rename_enum
|
43
|
+
# * rename_enum
|
42
44
|
# * rename_enum_value (must supply a +:from+ and +:to+ option)
|
43
45
|
# * rename_index
|
44
46
|
# * rename_table
|
@@ -55,6 +57,8 @@ module ActiveRecord
|
|
55
57
|
:add_exclusion_constraint, :remove_exclusion_constraint,
|
56
58
|
:add_unique_constraint, :remove_unique_constraint,
|
57
59
|
:create_enum, :drop_enum, :rename_enum, :add_enum_value, :rename_enum_value,
|
60
|
+
:create_schema, :drop_schema,
|
61
|
+
:create_virtual_table, :drop_virtual_table
|
58
62
|
]
|
59
63
|
include JoinTable
|
60
64
|
|
@@ -163,7 +167,9 @@ module ActiveRecord
|
|
163
167
|
add_exclusion_constraint: :remove_exclusion_constraint,
|
164
168
|
add_unique_constraint: :remove_unique_constraint,
|
165
169
|
enable_extension: :disable_extension,
|
166
|
-
create_enum: :drop_enum
|
170
|
+
create_enum: :drop_enum,
|
171
|
+
create_schema: :drop_schema,
|
172
|
+
create_virtual_table: :drop_virtual_table
|
167
173
|
}.each do |cmd, inv|
|
168
174
|
[[inv, cmd], [cmd, inv]].uniq.each do |method, inverse|
|
169
175
|
class_eval <<-EOV, __FILE__, __LINE__ + 1
|
@@ -196,13 +202,18 @@ module ActiveRecord
|
|
196
202
|
end
|
197
203
|
|
198
204
|
def invert_drop_table(args, &block)
|
199
|
-
|
200
|
-
|
205
|
+
options = args.extract_options!
|
206
|
+
options.delete(:if_exists)
|
207
|
+
|
208
|
+
if args.size > 1
|
209
|
+
raise ActiveRecord::IrreversibleMigration, "To avoid mistakes, drop_table is only reversible if given a single table name."
|
201
210
|
end
|
202
|
-
|
211
|
+
|
212
|
+
if args.size == 1 && options == {} && block == nil
|
203
213
|
raise ActiveRecord::IrreversibleMigration, "To avoid mistakes, drop_table is only reversible if given options or a block (can be empty)."
|
204
214
|
end
|
205
|
-
|
215
|
+
|
216
|
+
super(args.push(options), &block)
|
206
217
|
end
|
207
218
|
|
208
219
|
def invert_rename_table(args)
|
@@ -353,13 +364,13 @@ module ActiveRecord
|
|
353
364
|
end
|
354
365
|
|
355
366
|
def invert_rename_enum(args)
|
356
|
-
name,
|
367
|
+
name, new_name, = args
|
357
368
|
|
358
|
-
|
359
|
-
|
369
|
+
if new_name.is_a?(Hash) && new_name.key?(:to)
|
370
|
+
new_name = new_name[:to]
|
360
371
|
end
|
361
372
|
|
362
|
-
[:rename_enum, [
|
373
|
+
[:rename_enum, [new_name, name]]
|
363
374
|
end
|
364
375
|
|
365
376
|
def invert_rename_enum_value(args)
|
@@ -372,6 +383,12 @@ module ActiveRecord
|
|
372
383
|
[:rename_enum_value, [type_name, from: options[:to], to: options[:from]]]
|
373
384
|
end
|
374
385
|
|
386
|
+
def invert_drop_virtual_table(args)
|
387
|
+
_enum, values = args.dup.tap(&:extract_options!)
|
388
|
+
raise ActiveRecord::IrreversibleMigration, "drop_virtual_table is only reversible if given options." unless values
|
389
|
+
super
|
390
|
+
end
|
391
|
+
|
375
392
|
def respond_to_missing?(method, _)
|
376
393
|
super || delegate.respond_to?(method)
|
377
394
|
end
|
@@ -21,7 +21,7 @@ module ActiveRecord
|
|
21
21
|
# New migration functionality that will never be backward compatible should be added directly to `ActiveRecord::Migration`.
|
22
22
|
#
|
23
23
|
# There are classes for each prior Rails version. Each class descends from the *next* Rails version, so:
|
24
|
-
# 5.2 < 6.0 < 6.1 < 7.0 < 7.1 < 7.2
|
24
|
+
# 5.2 < 6.0 < 6.1 < 7.0 < 7.1 < 7.2 < 8.0
|
25
25
|
#
|
26
26
|
# If you are introducing new migration functionality that should only apply from Rails 7 onward, then you should
|
27
27
|
# find the class that immediately precedes it (6.1), and override the relevant migration methods to undo your changes.
|
@@ -29,7 +29,10 @@ module ActiveRecord
|
|
29
29
|
# For example, Rails 6 added a default value for the `precision` option on datetime columns. So in this file, the `V5_2`
|
30
30
|
# class sets the value of `precision` to `nil` if it's not explicitly provided. This way, the default value will not apply
|
31
31
|
# for migrations written for 5.2, but will for migrations written for 6.0.
|
32
|
-
|
32
|
+
V8_0 = Current
|
33
|
+
|
34
|
+
class V7_2 < V8_0
|
35
|
+
end
|
33
36
|
|
34
37
|
class V7_1 < V7_2
|
35
38
|
end
|