activerecord 8.0.0 → 8.1.4
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 +1381 -241
- data/README.rdoc +2 -2
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/alias_tracker.rb +6 -4
- data/lib/active_record/associations/association.rb +35 -17
- data/lib/active_record/associations/association_scope.rb +1 -1
- data/lib/active_record/associations/belongs_to_association.rb +20 -4
- data/lib/active_record/associations/builder/association.rb +16 -5
- data/lib/active_record/associations/builder/belongs_to.rb +36 -8
- 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 +9 -5
- data/lib/active_record/associations/collection_proxy.rb +26 -4
- data/lib/active_record/associations/deprecation.rb +88 -0
- data/lib/active_record/associations/errors.rb +3 -0
- data/lib/active_record/associations/foreign_association.rb +3 -1
- data/lib/active_record/associations/has_one_association.rb +1 -0
- data/lib/active_record/associations/join_dependency/join_association.rb +25 -27
- data/lib/active_record/associations/join_dependency.rb +5 -4
- data/lib/active_record/associations/preloader/association.rb +1 -1
- 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.rb +159 -21
- data/lib/active_record/attribute_methods/primary_key.rb +2 -1
- data/lib/active_record/attribute_methods/query.rb +34 -0
- data/lib/active_record/attribute_methods/serialization.rb +19 -6
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +10 -2
- data/lib/active_record/attribute_methods.rb +24 -17
- data/lib/active_record/attributes.rb +40 -26
- data/lib/active_record/autosave_association.rb +22 -12
- data/lib/active_record/base.rb +3 -4
- data/lib/active_record/callbacks.rb +1 -2
- data/lib/active_record/coders/column_serializer.rb +1 -1
- data/lib/active_record/coders/json.rb +16 -6
- data/lib/active_record/coders/yaml_column.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +19 -18
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +16 -3
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +68 -12
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +474 -112
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +68 -41
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +36 -9
- data/lib/active_record/connection_adapters/abstract/quoting.rb +15 -24
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +7 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +31 -35
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +2 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +94 -28
- data/lib/active_record/connection_adapters/abstract/transaction.rb +25 -3
- data/lib/active_record/connection_adapters/abstract_adapter.rb +166 -64
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +133 -40
- data/lib/active_record/connection_adapters/column.rb +14 -4
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +4 -4
- data/lib/active_record/connection_adapters/mysql/quoting.rb +7 -1
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +42 -5
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +40 -25
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +66 -15
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +9 -3
- 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 +29 -33
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +46 -3
- 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 +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +8 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +22 -33
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +70 -32
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +99 -54
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +40 -12
- data/lib/active_record/connection_adapters/sqlite3/column.rb +8 -2
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +39 -23
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +13 -8
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +15 -3
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +7 -14
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +68 -37
- data/lib/active_record/connection_adapters/statement_pool.rb +4 -2
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +8 -3
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +16 -2
- data/lib/active_record/connection_adapters.rb +1 -0
- data/lib/active_record/connection_handling.rb +15 -10
- data/lib/active_record/core.rb +44 -12
- data/lib/active_record/counter_cache.rb +36 -9
- data/lib/active_record/database_configurations/connection_url_resolver.rb +3 -1
- data/lib/active_record/database_configurations/database_config.rb +5 -1
- data/lib/active_record/database_configurations/hash_config.rb +66 -9
- data/lib/active_record/database_configurations/url_config.rb +15 -3
- data/lib/active_record/database_configurations.rb +7 -3
- data/lib/active_record/delegated_type.rb +19 -19
- data/lib/active_record/disable_joins_association_relation.rb +5 -1
- data/lib/active_record/dynamic_matchers.rb +54 -69
- data/lib/active_record/encryption/encryptable_record.rb +5 -5
- data/lib/active_record/encryption/encrypted_attribute_type.rb +2 -2
- data/lib/active_record/encryption/encrypted_fixtures.rb +12 -7
- data/lib/active_record/encryption/encryptor.rb +39 -25
- data/lib/active_record/encryption/scheme.rb +1 -1
- data/lib/active_record/enum.rb +37 -20
- data/lib/active_record/errors.rb +23 -7
- 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 +76 -0
- data/lib/active_record/fixture_set/table_row.rb +19 -2
- data/lib/active_record/fixtures.rb +2 -2
- data/lib/active_record/future_result.rb +5 -3
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +9 -4
- data/lib/active_record/insert_all.rb +14 -9
- data/lib/active_record/locking/optimistic.rb +7 -0
- data/lib/active_record/locking/pessimistic.rb +5 -0
- data/lib/active_record/log_subscriber.rb +30 -22
- data/lib/active_record/message_pack.rb +13 -0
- data/lib/active_record/middleware/shard_selector.rb +34 -17
- data/lib/active_record/migration/command_recorder.rb +32 -6
- data/lib/active_record/migration/compatibility.rb +34 -24
- data/lib/active_record/migration/default_schema_versions_formatter.rb +30 -0
- data/lib/active_record/migration.rb +31 -21
- data/lib/active_record/model_schema.rb +38 -12
- data/lib/active_record/nested_attributes.rb +10 -6
- data/lib/active_record/persistence.rb +35 -3
- data/lib/active_record/query_cache.rb +22 -15
- data/lib/active_record/query_logs.rb +7 -7
- data/lib/active_record/querying.rb +17 -5
- data/lib/active_record/railtie.rb +35 -6
- data/lib/active_record/railties/controller_runtime.rb +11 -6
- data/lib/active_record/railties/databases.rake +24 -20
- 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 +36 -1
- data/lib/active_record/relation/batches.rb +25 -11
- data/lib/active_record/relation/calculations.rb +62 -41
- data/lib/active_record/relation/delegation.rb +0 -1
- data/lib/active_record/relation/finder_methods.rb +76 -45
- data/lib/active_record/relation/merger.rb +4 -2
- data/lib/active_record/relation/predicate_builder/array_handler.rb +3 -1
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +11 -9
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +7 -7
- data/lib/active_record/relation/predicate_builder.rb +10 -8
- data/lib/active_record/relation/query_attribute.rb +4 -2
- data/lib/active_record/relation/query_methods.rb +106 -45
- data/lib/active_record/relation/spawn_methods.rb +6 -6
- data/lib/active_record/relation/where_clause.rb +10 -11
- data/lib/active_record/relation.rb +63 -31
- data/lib/active_record/result.rb +44 -21
- data/lib/active_record/runtime_registry.rb +42 -58
- data/lib/active_record/sanitization.rb +2 -0
- data/lib/active_record/schema.rb +1 -1
- data/lib/active_record/schema_dumper.rb +41 -21
- data/lib/active_record/scoping.rb +0 -1
- data/lib/active_record/secure_token.rb +3 -3
- data/lib/active_record/signed_id.rb +48 -19
- data/lib/active_record/statement_cache.rb +16 -12
- data/lib/active_record/store.rb +46 -19
- data/lib/active_record/structured_event_subscriber.rb +85 -0
- data/lib/active_record/table_metadata.rb +5 -20
- data/lib/active_record/tasks/abstract_tasks.rb +83 -0
- data/lib/active_record/tasks/database_tasks.rb +44 -45
- data/lib/active_record/tasks/mysql_database_tasks.rb +4 -56
- data/lib/active_record/tasks/postgresql_database_tasks.rb +14 -40
- data/lib/active_record/tasks/sqlite_database_tasks.rb +14 -26
- data/lib/active_record/test_databases.rb +18 -4
- data/lib/active_record/test_fixtures.rb +40 -6
- data/lib/active_record/testing/query_assertions.rb +8 -2
- data/lib/active_record/timestamp.rb +4 -2
- data/lib/active_record/transaction.rb +2 -5
- data/lib/active_record/transactions.rb +58 -32
- 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.rb +72 -7
- data/lib/arel/alias_predication.rb +2 -0
- data/lib/arel/collectors/bind.rb +1 -1
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +2 -2
- data/lib/arel/crud.rb +8 -11
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/nodes/binary.rb +1 -1
- data/lib/arel/nodes/count.rb +2 -2
- data/lib/arel/nodes/delete_statement.rb +4 -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/update_statement.rb +4 -2
- data/lib/arel/nodes.rb +0 -2
- data/lib/arel/predications.rb +1 -3
- data/lib/arel/select_manager.rb +13 -4
- data/lib/arel/update_manager.rb +5 -0
- data/lib/arel/visitors/dot.rb +2 -3
- data/lib/arel/visitors/postgresql.rb +55 -0
- data/lib/arel/visitors/sqlite.rb +55 -8
- data/lib/arel/visitors/to_sql.rb +7 -23
- data/lib/arel.rb +3 -1
- data/lib/rails/generators/active_record/application_record/USAGE +1 -1
- metadata +16 -15
- data/lib/active_record/explain_subscriber.rb +0 -34
- data/lib/active_record/normalization.rb +0 -163
|
@@ -113,13 +113,14 @@ module ActiveRecord
|
|
|
113
113
|
unless abstract_class?
|
|
114
114
|
load_schema
|
|
115
115
|
super(attribute_names)
|
|
116
|
-
alias_attribute :id_value, :id if _has_attribute?("id")
|
|
116
|
+
alias_attribute :id_value, :id if _has_attribute?("id") && !_has_attribute?("id_value")
|
|
117
117
|
end
|
|
118
118
|
|
|
119
|
-
@attribute_methods_generated = true
|
|
120
|
-
|
|
121
119
|
generate_alias_attributes
|
|
120
|
+
|
|
121
|
+
@attribute_methods_generated = true
|
|
122
122
|
end
|
|
123
|
+
|
|
123
124
|
true
|
|
124
125
|
end
|
|
125
126
|
|
|
@@ -472,23 +473,29 @@ module ActiveRecord
|
|
|
472
473
|
end
|
|
473
474
|
|
|
474
475
|
def method_missing(name, ...)
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
return public_send(name, ...)
|
|
476
|
+
# We can't know whether some method was defined or not because
|
|
477
|
+
# multiple thread might be concurrently be in this code path.
|
|
478
|
+
# So the first one would define the methods and the others would
|
|
479
|
+
# appear to already have them.
|
|
480
|
+
self.class.define_attribute_methods
|
|
481
|
+
|
|
482
|
+
# So in all cases we must behave as if the method was just defined.
|
|
483
|
+
method = if self.class.public_method_defined?(name)
|
|
484
|
+
begin
|
|
485
|
+
self.class.public_instance_method(name)
|
|
486
|
+
rescue NameError
|
|
487
|
+
nil
|
|
488
488
|
end
|
|
489
489
|
end
|
|
490
490
|
|
|
491
|
-
|
|
491
|
+
# The method might be explicitly defined in the model, but call a generated
|
|
492
|
+
# method with super. So we must resume the call chain at the right step.
|
|
493
|
+
method = method.super_method while method && !method.owner.is_a?(GeneratedAttributeMethods)
|
|
494
|
+
if method
|
|
495
|
+
method.bind_call(self, ...)
|
|
496
|
+
else
|
|
497
|
+
super
|
|
498
|
+
end
|
|
492
499
|
end
|
|
493
500
|
|
|
494
501
|
def attribute_method?(attr_name)
|
|
@@ -7,6 +7,7 @@ module ActiveRecord
|
|
|
7
7
|
module Attributes
|
|
8
8
|
extend ActiveSupport::Concern
|
|
9
9
|
include ActiveModel::AttributeRegistration
|
|
10
|
+
include ActiveModel::Attributes::Normalization
|
|
10
11
|
|
|
11
12
|
# = Active Record \Attributes
|
|
12
13
|
module ClassMethods
|
|
@@ -21,28 +22,34 @@ module ActiveRecord
|
|
|
21
22
|
# your domain objects across much of Active Record, without having to
|
|
22
23
|
# rely on implementation details or monkey patching.
|
|
23
24
|
#
|
|
24
|
-
#
|
|
25
|
-
# column which this will persist to.
|
|
25
|
+
# ==== Parameters
|
|
26
26
|
#
|
|
27
|
-
# +
|
|
28
|
-
#
|
|
29
|
-
#
|
|
30
|
-
# Otherwise, the type will be ActiveModel::Type::Value.
|
|
31
|
-
# See the examples below for more information about providing custom type objects.
|
|
27
|
+
# [+name+]
|
|
28
|
+
# The name of the methods to define attribute methods for, and the
|
|
29
|
+
# column which this will persist to.
|
|
32
30
|
#
|
|
33
|
-
#
|
|
31
|
+
# [+cast_type+]
|
|
32
|
+
# A symbol such as +:string+ or +:integer+, or a type object to be used
|
|
33
|
+
# for this attribute. If this parameter is not passed, the previously
|
|
34
|
+
# defined type (if any) will be used. Otherwise, the type will be
|
|
35
|
+
# ActiveModel::Type::Value. See the examples below for more information
|
|
36
|
+
# about providing custom type objects.
|
|
34
37
|
#
|
|
35
|
-
#
|
|
38
|
+
# ==== Options
|
|
36
39
|
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
+
# [+:default+]
|
|
41
|
+
# The default value to use when no value is provided. If this option is
|
|
42
|
+
# not passed, the previously defined default value (if any) on the
|
|
43
|
+
# superclass or in the schema will be used. Otherwise, the default will
|
|
44
|
+
# be +nil+.
|
|
40
45
|
#
|
|
41
|
-
#
|
|
42
|
-
#
|
|
46
|
+
# [+:array+]
|
|
47
|
+
# (PostgreSQL only) Specifies that the type should be an array. See the
|
|
48
|
+
# examples below.
|
|
43
49
|
#
|
|
44
|
-
#
|
|
45
|
-
#
|
|
50
|
+
# [+:range+]
|
|
51
|
+
# (PostgreSQL only) Specifies that the type should be a range. See the
|
|
52
|
+
# examples below.
|
|
46
53
|
#
|
|
47
54
|
# When using a symbol for +cast_type+, extra options are forwarded to the
|
|
48
55
|
# constructor of the type object.
|
|
@@ -178,8 +185,8 @@ module ActiveRecord
|
|
|
178
185
|
# @currency_converter = currency_converter
|
|
179
186
|
# end
|
|
180
187
|
#
|
|
181
|
-
# # value will be the result of
|
|
182
|
-
# #
|
|
188
|
+
# # value will be the result of #deserialize or
|
|
189
|
+
# # #cast. Assumed to be an instance of Money in
|
|
183
190
|
# # this case.
|
|
184
191
|
# def serialize(value)
|
|
185
192
|
# value_in_bitcoins = @currency_converter.convert_to_bitcoins(value)
|
|
@@ -217,17 +224,22 @@ module ActiveRecord
|
|
|
217
224
|
# is provided so it can be used by plugin authors, application code
|
|
218
225
|
# should probably use ClassMethods#attribute.
|
|
219
226
|
#
|
|
220
|
-
#
|
|
227
|
+
# ==== Parameters
|
|
228
|
+
#
|
|
229
|
+
# [+name+]
|
|
230
|
+
# The name of the attribute being defined. Expected to be a +String+.
|
|
221
231
|
#
|
|
222
|
-
# +cast_type+
|
|
232
|
+
# [+cast_type+]
|
|
233
|
+
# The type object to use for this attribute.
|
|
223
234
|
#
|
|
224
|
-
# +default+
|
|
225
|
-
#
|
|
226
|
-
#
|
|
227
|
-
# will be
|
|
235
|
+
# [+default+]
|
|
236
|
+
# The default value to use when no value is provided. If this option
|
|
237
|
+
# is not passed, the previous default value (if any) will be used.
|
|
238
|
+
# Otherwise, the default will be +nil+. A proc can also be passed, and
|
|
239
|
+
# will be called once each time a new value is needed.
|
|
228
240
|
#
|
|
229
|
-
# +user_provided_default+
|
|
230
|
-
# +cast+ or +deserialize+.
|
|
241
|
+
# [+user_provided_default+]
|
|
242
|
+
# Whether the default value should be cast using +cast+ or +deserialize+.
|
|
231
243
|
def define_attribute(
|
|
232
244
|
name,
|
|
233
245
|
cast_type,
|
|
@@ -240,6 +252,7 @@ module ActiveRecord
|
|
|
240
252
|
|
|
241
253
|
def _default_attributes # :nodoc:
|
|
242
254
|
@default_attributes ||= begin
|
|
255
|
+
# TODO: Remove the need for a connection after we release 8.1.
|
|
243
256
|
attributes_hash = with_connection do |connection|
|
|
244
257
|
columns_hash.transform_values do |column|
|
|
245
258
|
ActiveModel::Attribute.from_database(column.name, column.default, type_for_column(connection, column))
|
|
@@ -299,6 +312,7 @@ module ActiveRecord
|
|
|
299
312
|
end
|
|
300
313
|
|
|
301
314
|
def type_for_column(connection, column)
|
|
315
|
+
# TODO: Remove the need for a connection after we release 8.1.
|
|
302
316
|
hook_attribute_type(column.name, super)
|
|
303
317
|
end
|
|
304
318
|
end
|
|
@@ -372,19 +372,29 @@ module ActiveRecord
|
|
|
372
372
|
return true if record.destroyed? || (association.options[:autosave] && record.marked_for_destruction?)
|
|
373
373
|
|
|
374
374
|
context = validation_context if custom_validation_context?
|
|
375
|
+
return true if record.valid?(context)
|
|
375
376
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
}
|
|
383
|
-
else
|
|
384
|
-
errors.add(association.reflection.name)
|
|
385
|
-
end
|
|
377
|
+
if context || record.changed_for_autosave?
|
|
378
|
+
associated_errors = record.errors.objects
|
|
379
|
+
else
|
|
380
|
+
# If there are existing invalid records in the DB, we should still be able to reference them.
|
|
381
|
+
# Unless a record (no matter where in the association chain) is invalid and is being changed.
|
|
382
|
+
associated_errors = record.errors.objects.select { |error| error.is_a?(Associations::NestedError) }
|
|
386
383
|
end
|
|
387
|
-
|
|
384
|
+
|
|
385
|
+
if association.options[:autosave]
|
|
386
|
+
return if equal?(record)
|
|
387
|
+
|
|
388
|
+
associated_errors.each { |error|
|
|
389
|
+
errors.objects.append(
|
|
390
|
+
Associations::NestedError.new(association, error)
|
|
391
|
+
)
|
|
392
|
+
}
|
|
393
|
+
elsif associated_errors.any?
|
|
394
|
+
errors.add(association.reflection.name)
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
errors.any?
|
|
388
398
|
end
|
|
389
399
|
|
|
390
400
|
# Is used as an around_save callback to check while saving a collection
|
|
@@ -517,7 +527,7 @@ module ActiveRecord
|
|
|
517
527
|
return false unless reflection.inverse_of&.polymorphic?
|
|
518
528
|
|
|
519
529
|
class_name = record._read_attribute(reflection.inverse_of.foreign_type)
|
|
520
|
-
reflection.active_record !=
|
|
530
|
+
reflection.active_record.polymorphic_name != class_name
|
|
521
531
|
end
|
|
522
532
|
|
|
523
533
|
# Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
|
data/lib/active_record/base.rb
CHANGED
|
@@ -6,7 +6,7 @@ require "active_support/descendants_tracker"
|
|
|
6
6
|
require "active_support/time"
|
|
7
7
|
require "active_support/core_ext/class/subclasses"
|
|
8
8
|
require "active_record/log_subscriber"
|
|
9
|
-
require "active_record/
|
|
9
|
+
require "active_record/structured_event_subscriber"
|
|
10
10
|
require "active_record/relation/delegation"
|
|
11
11
|
require "active_record/attributes"
|
|
12
12
|
require "active_record/type_caster"
|
|
@@ -256,13 +256,13 @@ module ActiveRecord # :nodoc:
|
|
|
256
256
|
# * AssociationTypeMismatch - The object assigned to the association wasn't of the type
|
|
257
257
|
# specified in the association definition.
|
|
258
258
|
# * AttributeAssignmentError - An error occurred while doing a mass assignment through the
|
|
259
|
-
# {ActiveRecord::Base#attributes=}[rdoc-ref:AttributeAssignment#attributes=] method.
|
|
259
|
+
# {ActiveRecord::Base#attributes=}[rdoc-ref:ActiveModel::AttributeAssignment#attributes=] method.
|
|
260
260
|
# You can inspect the +attribute+ property of the exception object to determine which attribute
|
|
261
261
|
# triggered the error.
|
|
262
262
|
# * ConnectionNotEstablished - No connection has been established.
|
|
263
263
|
# Use {ActiveRecord::Base.establish_connection}[rdoc-ref:ConnectionHandling#establish_connection] before querying.
|
|
264
264
|
# * MultiparameterAssignmentErrors - Collection of errors that occurred during a mass assignment using the
|
|
265
|
-
# {ActiveRecord::Base#attributes=}[rdoc-ref:AttributeAssignment#attributes=] method.
|
|
265
|
+
# {ActiveRecord::Base#attributes=}[rdoc-ref:ActiveModel::AttributeAssignment#attributes=] method.
|
|
266
266
|
# The +errors+ property of this exception contains an array of
|
|
267
267
|
# AttributeAssignmentError
|
|
268
268
|
# objects that should be inspected to determine which attributes triggered the errors.
|
|
@@ -328,7 +328,6 @@ module ActiveRecord # :nodoc:
|
|
|
328
328
|
include TokenFor
|
|
329
329
|
include SignedId
|
|
330
330
|
include Suppressor
|
|
331
|
-
include Normalization
|
|
332
331
|
include Marshalling::Methods
|
|
333
332
|
|
|
334
333
|
self.param_delimiter = "_"
|
|
@@ -173,8 +173,7 @@ module ActiveRecord
|
|
|
173
173
|
#
|
|
174
174
|
# If a <tt>before_*</tt> callback throws +:abort+, all the later callbacks and
|
|
175
175
|
# the associated action are cancelled.
|
|
176
|
-
# \Callbacks are
|
|
177
|
-
# methods on the model, which are called last.
|
|
176
|
+
# \Callbacks are run in the order they are defined.
|
|
178
177
|
#
|
|
179
178
|
# == Ordering callbacks
|
|
180
179
|
#
|
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require "active_support/json"
|
|
4
|
+
|
|
3
5
|
module ActiveRecord
|
|
4
|
-
module Coders
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
module Coders
|
|
7
|
+
class JSON # :nodoc:
|
|
8
|
+
DEFAULT_ENCODE_OPTIONS = { escape: false }.freeze
|
|
9
|
+
|
|
10
|
+
def initialize(encode_options: nil, decode_options: nil)
|
|
11
|
+
encode_options = encode_options ? DEFAULT_ENCODE_OPTIONS.merge(encode_options) : DEFAULT_ENCODE_OPTIONS
|
|
12
|
+
@decode_options = decode_options
|
|
13
|
+
@encoder = ActiveSupport::JSON::Encoding.json_encoder.new(encode_options)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def dump(obj)
|
|
17
|
+
@encoder.encode(obj)
|
|
8
18
|
end
|
|
9
19
|
|
|
10
|
-
def
|
|
11
|
-
ActiveSupport::JSON.decode(json) unless json.blank?
|
|
20
|
+
def load(json)
|
|
21
|
+
ActiveSupport::JSON.decode(json, @decode_options) unless json.blank?
|
|
12
22
|
end
|
|
13
23
|
end
|
|
14
24
|
end
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
require "yaml"
|
|
4
4
|
|
|
5
5
|
module ActiveRecord
|
|
6
|
-
module Coders
|
|
6
|
+
module Coders
|
|
7
7
|
class YAMLColumn < ColumnSerializer # :nodoc:
|
|
8
|
-
class SafeCoder
|
|
8
|
+
class SafeCoder # :nodoc:
|
|
9
9
|
def initialize(permitted_classes: [], unsafe_load: nil)
|
|
10
10
|
@permitted_classes = permitted_classes
|
|
11
11
|
@unsafe_load = unsafe_load
|
|
@@ -54,19 +54,22 @@ module ActiveRecord
|
|
|
54
54
|
# about the model. The model needs to pass a connection specification name to the handler,
|
|
55
55
|
# in order to look up the correct connection pool.
|
|
56
56
|
class ConnectionHandler
|
|
57
|
-
class
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
def initialize(name)
|
|
57
|
+
class ConnectionDescriptor # :nodoc:
|
|
58
|
+
def initialize(name, primary = false)
|
|
61
59
|
@name = name
|
|
60
|
+
@primary = primary
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def name
|
|
64
|
+
primary_class? ? "ActiveRecord::Base" : @name
|
|
62
65
|
end
|
|
63
66
|
|
|
64
67
|
def primary_class?
|
|
65
|
-
|
|
68
|
+
@primary
|
|
66
69
|
end
|
|
67
70
|
|
|
68
71
|
def current_preventing_writes
|
|
69
|
-
|
|
72
|
+
ActiveRecord::Base.preventing_writes?(@name)
|
|
70
73
|
end
|
|
71
74
|
end
|
|
72
75
|
|
|
@@ -115,7 +118,7 @@ module ActiveRecord
|
|
|
115
118
|
pool_config = resolve_pool_config(config, owner_name, role, shard)
|
|
116
119
|
db_config = pool_config.db_config
|
|
117
120
|
|
|
118
|
-
pool_manager = set_pool_manager(pool_config.
|
|
121
|
+
pool_manager = set_pool_manager(pool_config.connection_descriptor)
|
|
119
122
|
|
|
120
123
|
# If there is an existing pool with the same values as the pool_config
|
|
121
124
|
# don't remove the connection. Connections should only be removed if we are
|
|
@@ -127,8 +130,8 @@ module ActiveRecord
|
|
|
127
130
|
# Update the pool_config's connection class if it differs. This is used
|
|
128
131
|
# for ensuring that ActiveRecord::Base and the primary_abstract_class use
|
|
129
132
|
# the same pool. Without this granular swapping will not work correctly.
|
|
130
|
-
if owner_name.primary_class? && (existing_pool_config.
|
|
131
|
-
existing_pool_config.
|
|
133
|
+
if owner_name.primary_class? && (existing_pool_config.connection_descriptor != owner_name)
|
|
134
|
+
existing_pool_config.connection_descriptor = owner_name
|
|
132
135
|
end
|
|
133
136
|
|
|
134
137
|
existing_pool_config.pool
|
|
@@ -137,7 +140,7 @@ module ActiveRecord
|
|
|
137
140
|
pool_manager.set_pool_config(role, shard, pool_config)
|
|
138
141
|
|
|
139
142
|
payload = {
|
|
140
|
-
connection_name: pool_config.
|
|
143
|
+
connection_name: pool_config.connection_descriptor.name,
|
|
141
144
|
role: role,
|
|
142
145
|
shard: shard,
|
|
143
146
|
config: db_config.configuration_hash
|
|
@@ -155,9 +158,7 @@ module ActiveRecord
|
|
|
155
158
|
each_connection_pool(role).any?(&:active_connection?)
|
|
156
159
|
end
|
|
157
160
|
|
|
158
|
-
# Returns any connections in use by the current thread back to the pool
|
|
159
|
-
# and also returns connections to the pool cached by threads that are no
|
|
160
|
-
# longer alive.
|
|
161
|
+
# Returns any connections in use by the current thread back to the pool.
|
|
161
162
|
def clear_active_connections!(role = nil)
|
|
162
163
|
each_connection_pool(role).each do |pool|
|
|
163
164
|
pool.release_connection
|
|
@@ -165,7 +166,7 @@ module ActiveRecord
|
|
|
165
166
|
end
|
|
166
167
|
end
|
|
167
168
|
|
|
168
|
-
# Clears
|
|
169
|
+
# Clears reloadable connection caches in all connection pools.
|
|
169
170
|
#
|
|
170
171
|
# See ConnectionPool#clear_reloadable_connections! for details.
|
|
171
172
|
def clear_reloadable_connections!(role = nil)
|
|
@@ -242,8 +243,8 @@ module ActiveRecord
|
|
|
242
243
|
end
|
|
243
244
|
|
|
244
245
|
# Get the existing pool manager or initialize and assign a new one.
|
|
245
|
-
def set_pool_manager(
|
|
246
|
-
connection_name_to_pool_manager[
|
|
246
|
+
def set_pool_manager(connection_descriptor)
|
|
247
|
+
connection_name_to_pool_manager[connection_descriptor.name] ||= PoolManager.new
|
|
247
248
|
end
|
|
248
249
|
|
|
249
250
|
def pool_managers
|
|
@@ -278,9 +279,9 @@ module ActiveRecord
|
|
|
278
279
|
|
|
279
280
|
def determine_owner_name(owner_name, config)
|
|
280
281
|
if owner_name.is_a?(String) || owner_name.is_a?(Symbol)
|
|
281
|
-
|
|
282
|
+
ConnectionDescriptor.new(owner_name.to_s)
|
|
282
283
|
elsif config.is_a?(Symbol)
|
|
283
|
-
|
|
284
|
+
ConnectionDescriptor.new(config.to_s)
|
|
284
285
|
else
|
|
285
286
|
owner_name
|
|
286
287
|
end
|
|
@@ -40,6 +40,14 @@ module ActiveRecord
|
|
|
40
40
|
end
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
+
# Add +element+ to the back of the queue. Never blocks.
|
|
44
|
+
def add_back(element)
|
|
45
|
+
synchronize do
|
|
46
|
+
@queue.unshift element
|
|
47
|
+
@cond.signal
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
43
51
|
# If +element+ is in the queue, remove and return it, or +nil+.
|
|
44
52
|
def delete(element)
|
|
45
53
|
synchronize do
|
|
@@ -54,6 +62,13 @@ module ActiveRecord
|
|
|
54
62
|
end
|
|
55
63
|
end
|
|
56
64
|
|
|
65
|
+
# Number of elements in the queue.
|
|
66
|
+
def size
|
|
67
|
+
synchronize do
|
|
68
|
+
@queue.size
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
57
72
|
# Remove the head of the queue.
|
|
58
73
|
#
|
|
59
74
|
# If +timeout+ is not given, remove and return the head of the
|
|
@@ -114,9 +129,7 @@ module ActiveRecord
|
|
|
114
129
|
t0 = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
115
130
|
elapsed = 0
|
|
116
131
|
loop do
|
|
117
|
-
|
|
118
|
-
@cond.wait(timeout - elapsed)
|
|
119
|
-
end
|
|
132
|
+
@cond.wait(timeout - elapsed)
|
|
120
133
|
|
|
121
134
|
return remove if any?
|
|
122
135
|
|
|
@@ -7,12 +7,29 @@ module ActiveRecord
|
|
|
7
7
|
class ConnectionPool
|
|
8
8
|
# = Active Record Connection Pool \Reaper
|
|
9
9
|
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
# the connection pool.
|
|
10
|
+
# The reaper is a singleton that exists in the background of the process
|
|
11
|
+
# and is responsible for general maintenance of all the connection pools.
|
|
13
12
|
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
13
|
+
# It will reclaim connections that are leased to now-dead threads,
|
|
14
|
+
# ensuring that a bad thread can't leak a pool slot forever. By definition,
|
|
15
|
+
# this involves touching currently-leased connections, but that is safe
|
|
16
|
+
# because the owning thread is known to be dead.
|
|
17
|
+
#
|
|
18
|
+
# Beyond that, it manages the health of available / unleased connections:
|
|
19
|
+
# * retiring connections that have been idle[1] for too long
|
|
20
|
+
# * creating occasional activity on inactive[1] connections
|
|
21
|
+
# * keeping the pool prepopulated up to its minimum size
|
|
22
|
+
# * proactively connecting to the target database from any pooled
|
|
23
|
+
# connections that had lazily deferred that step
|
|
24
|
+
# * resetting or replacing connections that are known to be broken
|
|
25
|
+
#
|
|
26
|
+
#
|
|
27
|
+
# [1]: "idle" and "inactive" here distinguish between connections that
|
|
28
|
+
# have not been requested by the application in a while (idle) and those
|
|
29
|
+
# that have not spoken to their remote server in a while (inactive). The
|
|
30
|
+
# former is a desirable opportunity to reduce our connection count
|
|
31
|
+
# (`idle_timeout`); the latter is a risk that the server or a firewall may
|
|
32
|
+
# drop a connection we still anticipate using (avoided by `keepalive`).
|
|
16
33
|
class Reaper
|
|
17
34
|
attr_reader :pool, :frequency
|
|
18
35
|
|
|
@@ -36,6 +53,32 @@ module ActiveRecord
|
|
|
36
53
|
end
|
|
37
54
|
end
|
|
38
55
|
|
|
56
|
+
def discard_pool(pool) # :nodoc:
|
|
57
|
+
@mutex.synchronize do
|
|
58
|
+
@pools.each do |frequency, refs|
|
|
59
|
+
refs.reject! do |ref|
|
|
60
|
+
ref.__getobj__ == pool
|
|
61
|
+
rescue WeakRef::RefError
|
|
62
|
+
true
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
if refs.empty?
|
|
66
|
+
@pools.delete(frequency)
|
|
67
|
+
@threads.delete(frequency)&.tap { |t| t.kill; t.join }
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def pools(refs = nil) # :nodoc:
|
|
74
|
+
refs ||= @mutex.synchronize { @pools.values.flatten(1) }
|
|
75
|
+
|
|
76
|
+
refs.filter_map do |ref|
|
|
77
|
+
ref.__getobj__ if ref.weakref_alive?
|
|
78
|
+
rescue WeakRef::RefError
|
|
79
|
+
end.select(&:maintainable?)
|
|
80
|
+
end
|
|
81
|
+
|
|
39
82
|
private
|
|
40
83
|
def spawn_thread(frequency)
|
|
41
84
|
Thread.new(frequency) do |t|
|
|
@@ -46,23 +89,36 @@ module ActiveRecord
|
|
|
46
89
|
running = true
|
|
47
90
|
while running
|
|
48
91
|
sleep t
|
|
92
|
+
|
|
93
|
+
refs = nil
|
|
94
|
+
|
|
49
95
|
@mutex.synchronize do
|
|
50
|
-
@pools[frequency]
|
|
51
|
-
pool.weakref_alive? && !pool.discarded?
|
|
52
|
-
end
|
|
96
|
+
refs = @pools[frequency]
|
|
53
97
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
p.flush
|
|
98
|
+
refs.select! do |pool|
|
|
99
|
+
pool.weakref_alive? && !pool.discarded?
|
|
57
100
|
rescue WeakRef::RefError
|
|
58
101
|
end
|
|
59
102
|
|
|
60
|
-
if
|
|
103
|
+
if refs.empty?
|
|
61
104
|
@pools.delete(frequency)
|
|
62
105
|
@threads.delete(frequency)
|
|
63
106
|
running = false
|
|
64
107
|
end
|
|
65
108
|
end
|
|
109
|
+
|
|
110
|
+
if running
|
|
111
|
+
pools(refs).each do |pool|
|
|
112
|
+
pool.reaper_lock do
|
|
113
|
+
pool.reap
|
|
114
|
+
pool.flush
|
|
115
|
+
pool.prepopulate
|
|
116
|
+
pool.retire_old_connections
|
|
117
|
+
pool.keep_alive
|
|
118
|
+
pool.preconnect
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
66
122
|
end
|
|
67
123
|
end
|
|
68
124
|
end
|