activerecord 7.2.3 → 8.1.3
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 +612 -1055
- data/README.rdoc +1 -1
- data/lib/active_record/association_relation.rb +2 -1
- data/lib/active_record/associations/association.rb +35 -11
- data/lib/active_record/associations/builder/association.rb +23 -11
- data/lib/active_record/associations/builder/belongs_to.rb +17 -4
- data/lib/active_record/associations/builder/collection_association.rb +7 -3
- data/lib/active_record/associations/builder/has_one.rb +1 -1
- data/lib/active_record/associations/builder/singular_association.rb +33 -5
- data/lib/active_record/associations/collection_association.rb +1 -1
- data/lib/active_record/associations/collection_proxy.rb +22 -4
- data/lib/active_record/associations/deprecation.rb +88 -0
- data/lib/active_record/associations/disable_joins_association_scope.rb +1 -1
- data/lib/active_record/associations/errors.rb +3 -0
- data/lib/active_record/associations/has_many_through_association.rb +3 -2
- data/lib/active_record/associations/join_dependency.rb +4 -2
- data/lib/active_record/associations/preloader/association.rb +2 -2
- data/lib/active_record/associations/preloader/batch.rb +7 -1
- data/lib/active_record/associations/preloader/branch.rb +1 -0
- data/lib/active_record/associations/singular_association.rb +8 -3
- data/lib/active_record/associations.rb +192 -24
- data/lib/active_record/asynchronous_queries_tracker.rb +28 -24
- data/lib/active_record/attribute_methods/primary_key.rb +4 -8
- data/lib/active_record/attribute_methods/query.rb +34 -0
- data/lib/active_record/attribute_methods/serialization.rb +16 -3
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -14
- data/lib/active_record/attributes.rb +3 -0
- data/lib/active_record/autosave_association.rb +69 -27
- data/lib/active_record/base.rb +1 -2
- data/lib/active_record/coders/json.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +35 -28
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -4
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +51 -13
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +412 -88
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +137 -75
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +27 -5
- data/lib/active_record/connection_adapters/abstract/quoting.rb +16 -25
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +11 -7
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +32 -35
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +122 -32
- data/lib/active_record/connection_adapters/abstract/transaction.rb +40 -8
- data/lib/active_record/connection_adapters/abstract_adapter.rb +150 -91
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +63 -52
- data/lib/active_record/connection_adapters/column.rb +17 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
- data/lib/active_record/connection_adapters/mysql/quoting.rb +0 -8
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +41 -10
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +73 -46
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +89 -94
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +2 -10
- data/lib/active_record/connection_adapters/pool_config.rb +7 -7
- data/lib/active_record/connection_adapters/postgresql/column.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +76 -45
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +10 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +21 -10
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +2 -4
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +9 -17
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +14 -33
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +71 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +139 -63
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +78 -105
- data/lib/active_record/connection_adapters/schema_cache.rb +3 -5
- data/lib/active_record/connection_adapters/sqlite3/column.rb +8 -2
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +90 -98
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +0 -8
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +0 -6
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +27 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +13 -14
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +102 -37
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +38 -67
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +1 -18
- data/lib/active_record/connection_adapters.rb +1 -56
- data/lib/active_record/connection_handling.rb +25 -2
- data/lib/active_record/core.rb +33 -17
- data/lib/active_record/counter_cache.rb +33 -8
- data/lib/active_record/database_configurations/database_config.rb +9 -1
- data/lib/active_record/database_configurations/hash_config.rb +67 -9
- data/lib/active_record/database_configurations/url_config.rb +13 -3
- data/lib/active_record/database_configurations.rb +7 -3
- data/lib/active_record/delegated_type.rb +1 -1
- data/lib/active_record/dynamic_matchers.rb +54 -69
- data/lib/active_record/encryption/config.rb +3 -1
- data/lib/active_record/encryption/encryptable_record.rb +8 -8
- data/lib/active_record/encryption/encrypted_attribute_type.rb +11 -2
- data/lib/active_record/encryption/encryptor.rb +28 -8
- data/lib/active_record/encryption/extended_deterministic_queries.rb +4 -2
- data/lib/active_record/encryption/scheme.rb +9 -2
- data/lib/active_record/enum.rb +33 -30
- data/lib/active_record/errors.rb +33 -9
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/explain_registry.rb +51 -2
- data/lib/active_record/filter_attribute_handler.rb +73 -0
- data/lib/active_record/fixtures.rb +2 -4
- data/lib/active_record/future_result.rb +15 -9
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +1 -1
- data/lib/active_record/insert_all.rb +14 -9
- data/lib/active_record/locking/optimistic.rb +8 -1
- data/lib/active_record/locking/pessimistic.rb +5 -0
- data/lib/active_record/log_subscriber.rb +3 -13
- data/lib/active_record/middleware/shard_selector.rb +34 -17
- data/lib/active_record/migration/command_recorder.rb +45 -12
- data/lib/active_record/migration/compatibility.rb +37 -24
- data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
- data/lib/active_record/migration.rb +48 -42
- data/lib/active_record/model_schema.rb +38 -13
- data/lib/active_record/nested_attributes.rb +6 -6
- data/lib/active_record/persistence.rb +162 -133
- data/lib/active_record/query_cache.rb +22 -15
- data/lib/active_record/query_logs.rb +100 -52
- data/lib/active_record/query_logs_formatter.rb +17 -28
- data/lib/active_record/querying.rb +8 -8
- data/lib/active_record/railtie.rb +35 -30
- data/lib/active_record/railties/controller_runtime.rb +11 -6
- data/lib/active_record/railties/databases.rake +26 -38
- data/lib/active_record/railties/job_checkpoints.rb +15 -0
- data/lib/active_record/railties/job_runtime.rb +10 -11
- data/lib/active_record/reflection.rb +53 -21
- data/lib/active_record/relation/batches/batch_enumerator.rb +4 -3
- data/lib/active_record/relation/batches.rb +147 -73
- data/lib/active_record/relation/calculations.rb +52 -40
- data/lib/active_record/relation/delegation.rb +25 -15
- data/lib/active_record/relation/finder_methods.rb +40 -24
- data/lib/active_record/relation/merger.rb +8 -8
- data/lib/active_record/relation/predicate_builder/array_handler.rb +3 -1
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +9 -9
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +8 -8
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +4 -3
- data/lib/active_record/relation/predicate_builder.rb +22 -7
- data/lib/active_record/relation/query_attribute.rb +3 -1
- data/lib/active_record/relation/query_methods.rb +140 -86
- data/lib/active_record/relation/spawn_methods.rb +7 -7
- data/lib/active_record/relation/where_clause.rb +2 -9
- data/lib/active_record/relation.rb +107 -75
- data/lib/active_record/result.rb +109 -24
- data/lib/active_record/runtime_registry.rb +42 -58
- data/lib/active_record/sanitization.rb +9 -6
- data/lib/active_record/schema_dumper.rb +18 -11
- data/lib/active_record/schema_migration.rb +2 -1
- data/lib/active_record/scoping/named.rb +5 -2
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/signed_id.rb +43 -15
- data/lib/active_record/statement_cache.rb +24 -20
- data/lib/active_record/store.rb +51 -22
- data/lib/active_record/structured_event_subscriber.rb +85 -0
- data/lib/active_record/table_metadata.rb +6 -23
- data/lib/active_record/tasks/abstract_tasks.rb +76 -0
- data/lib/active_record/tasks/database_tasks.rb +85 -85
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -42
- data/lib/active_record/tasks/postgresql_database_tasks.rb +7 -40
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -28
- data/lib/active_record/test_databases.rb +14 -4
- data/lib/active_record/test_fixtures.rb +39 -2
- data/lib/active_record/testing/query_assertions.rb +8 -2
- data/lib/active_record/timestamp.rb +4 -2
- data/lib/active_record/token_for.rb +1 -1
- data/lib/active_record/transaction.rb +2 -5
- data/lib/active_record/transactions.rb +37 -16
- data/lib/active_record/type/hash_lookup_type_map.rb +2 -1
- data/lib/active_record/type/internal/timezone.rb +7 -0
- data/lib/active_record/type/json.rb +13 -2
- data/lib/active_record/type/serialized.rb +16 -4
- data/lib/active_record/type/type_map.rb +1 -1
- data/lib/active_record/type_caster/connection.rb +2 -1
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record/validations/uniqueness.rb +8 -8
- data/lib/active_record.rb +84 -49
- data/lib/arel/alias_predication.rb +2 -0
- data/lib/arel/collectors/bind.rb +2 -2
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +2 -2
- data/lib/arel/crud.rb +6 -11
- data/lib/arel/nodes/binary.rb +1 -1
- data/lib/arel/nodes/count.rb +2 -2
- data/lib/arel/nodes/function.rb +4 -10
- data/lib/arel/nodes/named_function.rb +2 -2
- data/lib/arel/nodes/node.rb +2 -2
- data/lib/arel/nodes/sql_literal.rb +1 -1
- data/lib/arel/nodes.rb +0 -2
- data/lib/arel/predications.rb +1 -3
- data/lib/arel/select_manager.rb +7 -2
- data/lib/arel/table.rb +3 -7
- data/lib/arel/visitors/dot.rb +0 -3
- data/lib/arel/visitors/postgresql.rb +55 -0
- data/lib/arel/visitors/sqlite.rb +55 -8
- data/lib/arel/visitors/to_sql.rb +3 -21
- data/lib/arel.rb +3 -1
- data/lib/rails/generators/active_record/application_record/USAGE +1 -1
- metadata +16 -13
- data/lib/active_record/explain_subscriber.rb +0 -34
- data/lib/active_record/normalization.rb +0 -163
- data/lib/active_record/relation/record_fetch_warning.rb +0 -52
|
@@ -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
|
|
@@ -30,10 +30,10 @@ module ActiveRecord
|
|
|
30
30
|
# will use the oldest encryption scheme to encrypt new data by default. You can change this by setting
|
|
31
31
|
# <tt>deterministic: { fixed: false }</tt>. That will make it use the newest encryption scheme for encrypting new
|
|
32
32
|
# data.
|
|
33
|
-
# * <tt>:support_unencrypted_data</tt> -
|
|
34
|
-
#
|
|
35
|
-
# scenarios where you encrypt one column, and want to disable support for unencrypted data
|
|
36
|
-
# the global setting.
|
|
33
|
+
# * <tt>:support_unencrypted_data</tt> - When true, unencrypted data can be read normally. When false, it will raise errors.
|
|
34
|
+
# Falls back to +config.active_record.encryption.support_unencrypted_data+ if no value is provided.
|
|
35
|
+
# This is useful for scenarios where you encrypt one column, and want to disable support for unencrypted data
|
|
36
|
+
# without having to tweak the global setting.
|
|
37
37
|
# * <tt>:downcase</tt> - When true, it converts the encrypted content to downcase automatically. This allows to
|
|
38
38
|
# effectively ignore case when querying data. Notice that the case is lost. Use +:ignore_case+ if you are interested
|
|
39
39
|
# in preserving it.
|
|
@@ -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
|
|
@@ -59,7 +59,7 @@ module ActiveRecord
|
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
def support_unencrypted_data?
|
|
62
|
-
|
|
62
|
+
scheme.support_unencrypted_data? && !previous_type?
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
private
|
|
@@ -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,13 +11,22 @@ 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
|
# [+:compress+]
|
|
18
20
|
# Boolean indicating whether records should be compressed before
|
|
19
21
|
# encryption. Defaults to +true+.
|
|
20
|
-
|
|
22
|
+
#
|
|
23
|
+
# [+:compressor+]
|
|
24
|
+
# The compressor to use. It must respond to +deflate+ and +inflate+.
|
|
25
|
+
# If not provided, will default to +ActiveRecord::Encryption.config.compressor+,
|
|
26
|
+
# which itself defaults to +Zlib+.
|
|
27
|
+
def initialize(compress: true, compressor: nil)
|
|
21
28
|
@compress = compress
|
|
29
|
+
@compressor = compressor || ActiveRecord::Encryption.config.compressor
|
|
22
30
|
end
|
|
23
31
|
|
|
24
32
|
# Encrypts +clean_text+ and returns the encrypted result.
|
|
@@ -79,9 +87,25 @@ module ActiveRecord
|
|
|
79
87
|
serializer.binary?
|
|
80
88
|
end
|
|
81
89
|
|
|
90
|
+
def compress? # :nodoc:
|
|
91
|
+
@compress
|
|
92
|
+
end
|
|
93
|
+
|
|
82
94
|
private
|
|
83
95
|
DECRYPT_ERRORS = [OpenSSL::Cipher::CipherError, Errors::EncryptedContentIntegrity, Errors::Decryption]
|
|
84
96
|
ENCODING_ERRORS = [EncodingError, Errors::Encoding]
|
|
97
|
+
|
|
98
|
+
# This threshold cannot be changed.
|
|
99
|
+
#
|
|
100
|
+
# Users can search for attributes encrypted with `deterministic: true`.
|
|
101
|
+
# That is possible because we are able to generate the message for the
|
|
102
|
+
# given clear text deterministically, and with that perform a regular
|
|
103
|
+
# string lookup in SQL.
|
|
104
|
+
#
|
|
105
|
+
# Problem is, messages may have a "c" header that is present or not
|
|
106
|
+
# depending on whether compression was applied on encryption. If this
|
|
107
|
+
# threshold was modified, the message generated for lookup could vary
|
|
108
|
+
# for the same clear text, and searches on exisiting data could fail.
|
|
85
109
|
THRESHOLD_TO_JUSTIFY_COMPRESSION = 140.bytes
|
|
86
110
|
|
|
87
111
|
def default_key_provider
|
|
@@ -131,12 +155,8 @@ module ActiveRecord
|
|
|
131
155
|
end
|
|
132
156
|
end
|
|
133
157
|
|
|
134
|
-
def compress?
|
|
135
|
-
@compress
|
|
136
|
-
end
|
|
137
|
-
|
|
138
158
|
def compress(data)
|
|
139
|
-
|
|
159
|
+
@compressor.deflate(data).tap do |compressed_data|
|
|
140
160
|
compressed_data.force_encoding(data.encoding)
|
|
141
161
|
end
|
|
142
162
|
end
|
|
@@ -150,7 +170,7 @@ module ActiveRecord
|
|
|
150
170
|
end
|
|
151
171
|
|
|
152
172
|
def uncompress(data)
|
|
153
|
-
|
|
173
|
+
@compressor.inflate(data).tap do |uncompressed_data|
|
|
154
174
|
uncompressed_data.force_encoding(data.encoding)
|
|
155
175
|
end
|
|
156
176
|
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?
|
|
@@ -54,7 +59,7 @@ module ActiveRecord
|
|
|
54
59
|
end
|
|
55
60
|
|
|
56
61
|
def merge(other_scheme)
|
|
57
|
-
self.class.new(**to_h
|
|
62
|
+
self.class.new(**to_h, **other_scheme.to_h)
|
|
58
63
|
end
|
|
59
64
|
|
|
60
65
|
def to_h
|
|
@@ -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
|
@@ -214,34 +214,16 @@ module ActiveRecord
|
|
|
214
214
|
attr_reader :name, :mapping
|
|
215
215
|
end
|
|
216
216
|
|
|
217
|
-
def enum(name
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
return _enum(name, values, **options)
|
|
221
|
-
end
|
|
222
|
-
|
|
223
|
-
definitions = options.slice!(:_prefix, :_suffix, :_scopes, :_default, :_instance_methods)
|
|
224
|
-
options.transform_keys! { |key| :"#{key[1..-1]}" }
|
|
225
|
-
|
|
226
|
-
definitions.each { |name, values| _enum(name, values, **options) }
|
|
227
|
-
|
|
228
|
-
ActiveRecord.deprecator.warn(<<~MSG)
|
|
229
|
-
Defining enums with keyword arguments is deprecated and will be removed
|
|
230
|
-
in Rails 8.0. Positional arguments should be used instead:
|
|
231
|
-
|
|
232
|
-
#{definitions.map { |name, values| "enum :#{name}, #{values}" }.join("\n")}
|
|
233
|
-
MSG
|
|
217
|
+
def enum(name, values = nil, **options)
|
|
218
|
+
values, options = options, {} unless values
|
|
219
|
+
_enum(name, values, **options)
|
|
234
220
|
end
|
|
235
221
|
|
|
236
222
|
private
|
|
237
|
-
def inherited(base)
|
|
238
|
-
base.defined_enums = defined_enums.deep_dup
|
|
239
|
-
super
|
|
240
|
-
end
|
|
241
|
-
|
|
242
223
|
def _enum(name, values, prefix: nil, suffix: nil, scopes: true, instance_methods: true, validate: false, **options)
|
|
243
|
-
assert_valid_enum_definition_values(values)
|
|
224
|
+
values = assert_valid_enum_definition_values(values)
|
|
244
225
|
assert_valid_enum_options(options)
|
|
226
|
+
|
|
245
227
|
# statuses = { }
|
|
246
228
|
enum_values = ActiveSupport::HashWithIndifferentAccess.new
|
|
247
229
|
name = name.to_s
|
|
@@ -305,6 +287,11 @@ module ActiveRecord
|
|
|
305
287
|
enum_values.freeze
|
|
306
288
|
end
|
|
307
289
|
|
|
290
|
+
def inherited(base)
|
|
291
|
+
base.defined_enums = defined_enums.deep_dup
|
|
292
|
+
super
|
|
293
|
+
end
|
|
294
|
+
|
|
308
295
|
class EnumMethods < Module # :nodoc:
|
|
309
296
|
def initialize(klass)
|
|
310
297
|
@klass = klass
|
|
@@ -355,6 +342,20 @@ module ActiveRecord
|
|
|
355
342
|
if values.keys.any?(&:blank?)
|
|
356
343
|
raise ArgumentError, "Enum values #{values} must not contain a blank name."
|
|
357
344
|
end
|
|
345
|
+
|
|
346
|
+
values = values.transform_values do |value|
|
|
347
|
+
value.is_a?(Symbol) ? value.name : value
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
values.each_value do |value|
|
|
351
|
+
case value
|
|
352
|
+
when String, Integer, Float, true, false, nil
|
|
353
|
+
# noop
|
|
354
|
+
else
|
|
355
|
+
raise ArgumentError, "Enum values #{values} must be only booleans, integers, floats, symbols or strings, got: #{value.class}"
|
|
356
|
+
end
|
|
357
|
+
end
|
|
358
|
+
|
|
358
359
|
when Array
|
|
359
360
|
if values.empty?
|
|
360
361
|
raise ArgumentError, "Enum values #{values} must not be empty."
|
|
@@ -370,6 +371,8 @@ module ActiveRecord
|
|
|
370
371
|
else
|
|
371
372
|
raise ArgumentError, "Enum values #{values} must be either a non-empty hash or an array."
|
|
372
373
|
end
|
|
374
|
+
|
|
375
|
+
values
|
|
373
376
|
end
|
|
374
377
|
|
|
375
378
|
def assert_valid_enum_options(options)
|
|
@@ -381,25 +384,25 @@ module ActiveRecord
|
|
|
381
384
|
|
|
382
385
|
ENUM_CONFLICT_MESSAGE = \
|
|
383
386
|
"You tried to define an enum named \"%{enum}\" on the model \"%{klass}\", but " \
|
|
384
|
-
"this will generate
|
|
387
|
+
"this will generate %{type} method \"%{method}\", which is already defined " \
|
|
385
388
|
"by %{source}."
|
|
386
389
|
private_constant :ENUM_CONFLICT_MESSAGE
|
|
387
390
|
|
|
388
391
|
def detect_enum_conflict!(enum_name, method_name, klass_method = false)
|
|
389
392
|
if klass_method && dangerous_class_method?(method_name)
|
|
390
|
-
raise_conflict_error(enum_name, method_name,
|
|
393
|
+
raise_conflict_error(enum_name, method_name, "a class")
|
|
391
394
|
elsif klass_method && method_defined_within?(method_name, Relation)
|
|
392
|
-
raise_conflict_error(enum_name, method_name,
|
|
395
|
+
raise_conflict_error(enum_name, method_name, "a class", source: Relation.name)
|
|
393
396
|
elsif klass_method && method_name.to_sym == :id
|
|
394
|
-
raise_conflict_error(enum_name, method_name)
|
|
397
|
+
raise_conflict_error(enum_name, method_name, "an instance")
|
|
395
398
|
elsif !klass_method && dangerous_attribute_method?(method_name)
|
|
396
|
-
raise_conflict_error(enum_name, method_name)
|
|
399
|
+
raise_conflict_error(enum_name, method_name, "an instance")
|
|
397
400
|
elsif !klass_method && method_defined_within?(method_name, _enum_methods_module, Module)
|
|
398
|
-
raise_conflict_error(enum_name, method_name, source: "another enum")
|
|
401
|
+
raise_conflict_error(enum_name, method_name, "an instance", source: "another enum")
|
|
399
402
|
end
|
|
400
403
|
end
|
|
401
404
|
|
|
402
|
-
def raise_conflict_error(enum_name, method_name, type
|
|
405
|
+
def raise_conflict_error(enum_name, method_name, type, source: "Active Record")
|
|
403
406
|
raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
|
|
404
407
|
enum: enum_name,
|
|
405
408
|
klass: name,
|
data/lib/active_record/errors.rb
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "active_support/deprecation"
|
|
4
3
|
|
|
5
4
|
module ActiveRecord
|
|
6
5
|
include ActiveSupport::Deprecation::DeprecatedConstantAccessor
|
|
@@ -84,6 +83,19 @@ module ActiveRecord
|
|
|
84
83
|
class ConnectionTimeoutError < ConnectionNotEstablished
|
|
85
84
|
end
|
|
86
85
|
|
|
86
|
+
# Raised when a database connection pool is requested but
|
|
87
|
+
# has not been defined.
|
|
88
|
+
class ConnectionNotDefined < ConnectionNotEstablished
|
|
89
|
+
def initialize(message = nil, connection_name: nil, role: nil, shard: nil)
|
|
90
|
+
super(message)
|
|
91
|
+
@connection_name = connection_name
|
|
92
|
+
@role = role
|
|
93
|
+
@shard = shard
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
attr_reader :connection_name, :role, :shard
|
|
97
|
+
end
|
|
98
|
+
|
|
87
99
|
# Raised when connection to the database could not been established because it was not
|
|
88
100
|
# able to connect to the host or when the authorization failed.
|
|
89
101
|
class DatabaseConnectionError < ConnectionNotEstablished
|
|
@@ -280,6 +292,14 @@ module ActiveRecord
|
|
|
280
292
|
class NotNullViolation < StatementInvalid
|
|
281
293
|
end
|
|
282
294
|
|
|
295
|
+
# Raised when a record cannot be inserted or updated because it would violate a check constraint.
|
|
296
|
+
class CheckViolation < StatementInvalid
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
# Raised when a record cannot be inserted or updated because it would violate an exclusion constraint.
|
|
300
|
+
class ExclusionViolation < StatementInvalid
|
|
301
|
+
end
|
|
302
|
+
|
|
283
303
|
# Raised when a record cannot be inserted or updated because a value too long for a column type.
|
|
284
304
|
class ValueTooLong < StatementInvalid
|
|
285
305
|
end
|
|
@@ -326,15 +346,15 @@ module ActiveRecord
|
|
|
326
346
|
class << self
|
|
327
347
|
def db_error(db_name)
|
|
328
348
|
NoDatabaseError.new(<<~MSG)
|
|
329
|
-
|
|
349
|
+
Database not found: #{db_name}. Available database configurations can be found in config/database.yml.
|
|
330
350
|
|
|
331
351
|
To resolve this error:
|
|
332
352
|
|
|
333
|
-
-
|
|
353
|
+
- Create the database by running:
|
|
334
354
|
|
|
335
355
|
bin/rails db:create
|
|
336
356
|
|
|
337
|
-
-
|
|
357
|
+
- Verify that config/database.yml contains the correct database name.
|
|
338
358
|
MSG
|
|
339
359
|
end
|
|
340
360
|
end
|
|
@@ -477,6 +497,7 @@ module ActiveRecord
|
|
|
477
497
|
# end
|
|
478
498
|
#
|
|
479
499
|
# relation = Task.all
|
|
500
|
+
# relation.load
|
|
480
501
|
# relation.loaded? # => true
|
|
481
502
|
#
|
|
482
503
|
# # Methods which try to mutate a loaded relation fail.
|
|
@@ -484,11 +505,6 @@ module ActiveRecord
|
|
|
484
505
|
# relation.limit!(5) # => ActiveRecord::UnmodifiableRelation
|
|
485
506
|
class UnmodifiableRelation < ActiveRecordError
|
|
486
507
|
end
|
|
487
|
-
deprecate_constant(
|
|
488
|
-
:ImmutableRelation,
|
|
489
|
-
"ActiveRecord::UnmodifiableRelation",
|
|
490
|
-
deprecator: ActiveRecord.deprecator
|
|
491
|
-
)
|
|
492
508
|
|
|
493
509
|
# TransactionIsolationError will be raised under the following conditions:
|
|
494
510
|
#
|
|
@@ -544,6 +560,11 @@ module ActiveRecord
|
|
|
544
560
|
class Deadlocked < TransactionRollbackError
|
|
545
561
|
end
|
|
546
562
|
|
|
563
|
+
# MissingRequiredOrderError is raised when a relation requires ordering but
|
|
564
|
+
# lacks any +order+ values in scope or any model order columns to use.
|
|
565
|
+
class MissingRequiredOrderError < ActiveRecordError
|
|
566
|
+
end
|
|
567
|
+
|
|
547
568
|
# IrreversibleOrderError is raised when a relation's order is too complex for
|
|
548
569
|
# +reverse_order+ to automatically reverse.
|
|
549
570
|
class IrreversibleOrderError < ActiveRecordError
|
|
@@ -601,6 +622,9 @@ module ActiveRecord
|
|
|
601
622
|
# the database version cannot be determined.
|
|
602
623
|
class DatabaseVersionError < ActiveRecordError
|
|
603
624
|
end
|
|
625
|
+
|
|
626
|
+
class DeprecatedAssociationError < ActiveRecordError
|
|
627
|
+
end
|
|
604
628
|
end
|
|
605
629
|
|
|
606
630
|
require "active_record/associations/errors"
|
|
@@ -7,7 +7,7 @@ module ActiveRecord
|
|
|
7
7
|
# Executes the block with the collect flag enabled. Queries are collected
|
|
8
8
|
# asynchronously by the subscriber and returned.
|
|
9
9
|
def collecting_queries_for_explain # :nodoc:
|
|
10
|
-
ExplainRegistry.
|
|
10
|
+
ExplainRegistry.start
|
|
11
11
|
yield
|
|
12
12
|
ExplainRegistry.queries
|
|
13
13
|
ensure
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "active_support/core_ext/module/delegation"
|
|
4
3
|
|
|
5
4
|
module ActiveRecord
|
|
6
5
|
# This is a thread locals registry for EXPLAIN. For example
|
|
@@ -9,8 +8,53 @@ module ActiveRecord
|
|
|
9
8
|
#
|
|
10
9
|
# returns the collected queries local to the current thread.
|
|
11
10
|
class ExplainRegistry # :nodoc:
|
|
11
|
+
class Subscriber
|
|
12
|
+
MUTEX = Mutex.new
|
|
13
|
+
@subscribed = false
|
|
14
|
+
|
|
15
|
+
class << self
|
|
16
|
+
def ensure_subscribed
|
|
17
|
+
return if @subscribed
|
|
18
|
+
MUTEX.synchronize do
|
|
19
|
+
return if @subscribed
|
|
20
|
+
|
|
21
|
+
ActiveSupport::Notifications.subscribe("sql.active_record", new)
|
|
22
|
+
@subscribed = true
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def start(name, id, payload)
|
|
28
|
+
# unused
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def finish(name, id, payload)
|
|
32
|
+
if ExplainRegistry.collect? && !ignore_payload?(payload)
|
|
33
|
+
ExplainRegistry.queries << payload.values_at(:sql, :binds)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def silenced?(_name)
|
|
38
|
+
!ExplainRegistry.collect?
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# SCHEMA queries cannot be EXPLAINed, also we do not want to run EXPLAIN on
|
|
42
|
+
# our own EXPLAINs no matter how loopingly beautiful that would be.
|
|
43
|
+
#
|
|
44
|
+
# On the other hand, we want to monitor the performance of our real database
|
|
45
|
+
# queries, not the performance of the access to the query cache.
|
|
46
|
+
IGNORED_PAYLOADS = %w(SCHEMA EXPLAIN)
|
|
47
|
+
EXPLAINED_SQLS = /\A\s*(\/\*.*\*\/)?\s*(with|select|update|delete|insert)\b/i
|
|
48
|
+
def ignore_payload?(payload)
|
|
49
|
+
payload[:exception] ||
|
|
50
|
+
payload[:cached] ||
|
|
51
|
+
IGNORED_PAYLOADS.include?(payload[:name]) ||
|
|
52
|
+
!payload[:sql].match?(EXPLAINED_SQLS)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
12
56
|
class << self
|
|
13
|
-
delegate :reset, :collect, :collect=, :collect?, :queries, to: :instance
|
|
57
|
+
delegate :start, :reset, :collect, :collect=, :collect?, :queries, to: :instance
|
|
14
58
|
|
|
15
59
|
private
|
|
16
60
|
def instance
|
|
@@ -25,6 +69,11 @@ module ActiveRecord
|
|
|
25
69
|
reset
|
|
26
70
|
end
|
|
27
71
|
|
|
72
|
+
def start
|
|
73
|
+
Subscriber.ensure_subscribed
|
|
74
|
+
@collect = true
|
|
75
|
+
end
|
|
76
|
+
|
|
28
77
|
def collect?
|
|
29
78
|
@collect
|
|
30
79
|
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActiveRecord
|
|
4
|
+
class FilterAttributeHandler # :nodoc:
|
|
5
|
+
class << self
|
|
6
|
+
def on_sensitive_attribute_declared(&block)
|
|
7
|
+
@sensitive_attribute_declaration_listeners ||= Concurrent::Array.new
|
|
8
|
+
@sensitive_attribute_declaration_listeners << block
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def sensitive_attribute_was_declared(klass, list)
|
|
12
|
+
@sensitive_attribute_declaration_listeners&.each do |block|
|
|
13
|
+
block.call(klass, list)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def initialize(app)
|
|
19
|
+
@app = app
|
|
20
|
+
@attributes_by_class = Concurrent::Map.new
|
|
21
|
+
@collecting = true
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def enable
|
|
25
|
+
install_collecting_hook
|
|
26
|
+
|
|
27
|
+
apply_collected_attributes
|
|
28
|
+
@collecting = false
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
attr_reader :app
|
|
33
|
+
|
|
34
|
+
def install_collecting_hook
|
|
35
|
+
self.class.on_sensitive_attribute_declared do |klass, list|
|
|
36
|
+
attribute_was_declared(klass, list)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def attribute_was_declared(klass, list)
|
|
41
|
+
if collecting?
|
|
42
|
+
collect_for_later(klass, list)
|
|
43
|
+
else
|
|
44
|
+
apply_filter(klass, list)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def apply_collected_attributes
|
|
49
|
+
@attributes_by_class.each do |klass, list|
|
|
50
|
+
apply_filter(klass, list)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def collecting?
|
|
55
|
+
@collecting
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def collect_for_later(klass, list)
|
|
59
|
+
@attributes_by_class[klass] ||= Concurrent::Array.new
|
|
60
|
+
@attributes_by_class[klass] += list
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def apply_filter(klass, list)
|
|
64
|
+
list.each do |attribute|
|
|
65
|
+
next if klass.abstract_class? || klass == Base
|
|
66
|
+
|
|
67
|
+
klass_name = klass.name ? klass.model_name.element : nil
|
|
68
|
+
filter = [klass_name, attribute.to_s].compact.join(".")
|
|
69
|
+
app.config.filter_parameters << filter unless app.config.filter_parameters.include?(filter)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -2,8 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
require "erb"
|
|
4
4
|
require "yaml"
|
|
5
|
-
require "zlib"
|
|
6
|
-
require "set"
|
|
7
5
|
require "active_support/dependencies"
|
|
8
6
|
require "active_support/core_ext/digest/uuid"
|
|
9
7
|
require "active_record/test_fixtures"
|
|
@@ -244,10 +242,10 @@ module ActiveRecord
|
|
|
244
242
|
# and one for the humans. Why don't we generate the primary key instead?
|
|
245
243
|
# Hashing each fixture's label yields a consistent ID:
|
|
246
244
|
#
|
|
247
|
-
# george: # generated id:
|
|
245
|
+
# george: # generated id: 380982691
|
|
248
246
|
# name: George the Monkey
|
|
249
247
|
#
|
|
250
|
-
# reginald: # generated id:
|
|
248
|
+
# reginald: # generated id: 41001176
|
|
251
249
|
# name: Reginald the Pirate
|
|
252
250
|
#
|
|
253
251
|
# Active Record looks at the fixture's model class, discovers the correct
|
|
@@ -100,17 +100,23 @@ module ActiveRecord
|
|
|
100
100
|
def execute_or_skip
|
|
101
101
|
return unless pending?
|
|
102
102
|
|
|
103
|
-
@
|
|
104
|
-
return unless
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
103
|
+
@session.synchronize do
|
|
104
|
+
return unless pending?
|
|
105
|
+
|
|
106
|
+
@pool.with_connection do |connection|
|
|
107
|
+
return unless @mutex.try_lock
|
|
108
|
+
previous_instrumenter = ActiveSupport::IsolatedExecutionState[:active_record_instrumenter]
|
|
109
|
+
begin
|
|
110
|
+
if pending?
|
|
111
|
+
@event_buffer = EventBuffer.new(self, @instrumenter)
|
|
112
|
+
ActiveSupport::IsolatedExecutionState[:active_record_instrumenter] = @event_buffer
|
|
113
|
+
|
|
109
114
|
execute_query(connection, async: true)
|
|
110
115
|
end
|
|
116
|
+
ensure
|
|
117
|
+
ActiveSupport::IsolatedExecutionState[:active_record_instrumenter] = previous_instrumenter
|
|
118
|
+
@mutex.unlock
|
|
111
119
|
end
|
|
112
|
-
ensure
|
|
113
|
-
@mutex.unlock
|
|
114
120
|
end
|
|
115
121
|
end
|
|
116
122
|
end
|
|
@@ -163,7 +169,7 @@ module ActiveRecord
|
|
|
163
169
|
end
|
|
164
170
|
|
|
165
171
|
def exec_query(connection, *args, **kwargs)
|
|
166
|
-
connection.
|
|
172
|
+
connection.raw_exec_query(*args, **kwargs)
|
|
167
173
|
end
|
|
168
174
|
|
|
169
175
|
class SelectAll < FutureResult # :nodoc:
|