activerecord 7.0.4.3 → 7.1.3.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 +1657 -1274
- data/MIT-LICENSE +1 -1
- data/README.rdoc +18 -18
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/association.rb +20 -4
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +14 -6
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +21 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +16 -10
- data/lib/active_record/associations/collection_proxy.rb +20 -10
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +20 -13
- data/lib/active_record/associations/has_many_through_association.rb +10 -6
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency.rb +10 -8
- data/lib/active_record/associations/preloader/association.rb +31 -7
- data/lib/active_record/associations/preloader/through_association.rb +1 -1
- data/lib/active_record/associations/preloader.rb +13 -10
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +327 -222
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/dirty.rb +52 -34
- data/lib/active_record/attribute_methods/primary_key.rb +76 -24
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +18 -5
- data/lib/active_record/attribute_methods/serialization.rb +150 -31
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +0 -4
- data/lib/active_record/attribute_methods/write.rb +3 -3
- data/lib/active_record/attribute_methods.rb +108 -26
- data/lib/active_record/attributes.rb +3 -3
- data/lib/active_record/autosave_association.rb +55 -9
- data/lib/active_record/base.rb +7 -2
- data/lib/active_record/callbacks.rb +16 -32
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -42
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +163 -88
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +74 -51
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +129 -31
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +62 -23
- data/lib/active_record/connection_adapters/abstract/quoting.rb +41 -6
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +18 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +155 -25
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +290 -124
- data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
- data/lib/active_record/connection_adapters/abstract_adapter.rb +509 -102
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +217 -112
- data/lib/active_record/connection_adapters/column.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +23 -144
- data/lib/active_record/connection_adapters/mysql/quoting.rb +29 -14
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +18 -13
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +151 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
- data/lib/active_record/connection_adapters/pool_config.rb +14 -5
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +16 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +75 -45
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +11 -2
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +15 -8
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +3 -9
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +362 -60
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +354 -193
- data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
- data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +52 -39
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +9 -5
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +28 -9
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +211 -83
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +99 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +262 -0
- data/lib/active_record/connection_adapters.rb +3 -1
- data/lib/active_record/connection_handling.rb +72 -95
- data/lib/active_record/core.rb +175 -153
- data/lib/active_record/counter_cache.rb +46 -25
- data/lib/active_record/database_configurations/database_config.rb +9 -3
- data/lib/active_record/database_configurations/hash_config.rb +22 -12
- data/lib/active_record/database_configurations/url_config.rb +17 -11
- data/lib/active_record/database_configurations.rb +86 -33
- data/lib/active_record/delegated_type.rb +9 -4
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +2 -0
- data/lib/active_record/disable_joins_association_relation.rb +1 -1
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
- data/lib/active_record/encryption/config.rb +25 -1
- data/lib/active_record/encryption/configurable.rb +12 -19
- data/lib/active_record/encryption/context.rb +10 -3
- data/lib/active_record/encryption/contexts.rb +5 -1
- data/lib/active_record/encryption/derived_secret_key_provider.rb +8 -2
- data/lib/active_record/encryption/encryptable_record.rb +42 -18
- data/lib/active_record/encryption/encrypted_attribute_type.rb +21 -6
- data/lib/active_record/encryption/extended_deterministic_queries.rb +66 -69
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
- data/lib/active_record/encryption/key_generator.rb +12 -1
- data/lib/active_record/encryption/message_serializer.rb +2 -0
- data/lib/active_record/encryption/properties.rb +3 -3
- data/lib/active_record/encryption/scheme.rb +19 -22
- data/lib/active_record/encryption.rb +1 -0
- data/lib/active_record/enum.rb +112 -28
- data/lib/active_record/errors.rb +112 -18
- data/lib/active_record/explain.rb +23 -3
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +29 -8
- data/lib/active_record/fixtures.rb +135 -71
- data/lib/active_record/future_result.rb +31 -5
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +30 -16
- data/lib/active_record/insert_all.rb +57 -10
- data/lib/active_record/integration.rb +8 -8
- data/lib/active_record/internal_metadata.rb +120 -30
- data/lib/active_record/locking/optimistic.rb +32 -18
- data/lib/active_record/locking/pessimistic.rb +5 -2
- data/lib/active_record/log_subscriber.rb +29 -12
- data/lib/active_record/marshalling.rb +56 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
- data/lib/active_record/middleware/database_selector.rb +9 -11
- data/lib/active_record/middleware/shard_selector.rb +3 -1
- data/lib/active_record/migration/command_recorder.rb +105 -7
- data/lib/active_record/migration/compatibility.rb +157 -58
- data/lib/active_record/migration/default_strategy.rb +23 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +271 -114
- data/lib/active_record/model_schema.rb +64 -44
- data/lib/active_record/nested_attributes.rb +24 -6
- data/lib/active_record/normalization.rb +167 -0
- data/lib/active_record/persistence.rb +195 -42
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +3 -21
- data/lib/active_record/query_logs.rb +77 -52
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +15 -2
- data/lib/active_record/railtie.rb +109 -47
- data/lib/active_record/railties/controller_runtime.rb +14 -9
- data/lib/active_record/railties/databases.rake +142 -148
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +32 -5
- data/lib/active_record/reflection.rb +182 -44
- data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
- data/lib/active_record/relation/batches.rb +190 -61
- data/lib/active_record/relation/calculations.rb +232 -81
- data/lib/active_record/relation/delegation.rb +23 -9
- data/lib/active_record/relation/finder_methods.rb +77 -16
- data/lib/active_record/relation/merger.rb +2 -0
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +26 -14
- data/lib/active_record/relation/query_attribute.rb +25 -1
- data/lib/active_record/relation/query_methods.rb +387 -71
- data/lib/active_record/relation/spawn_methods.rb +18 -1
- data/lib/active_record/relation.rb +91 -35
- data/lib/active_record/result.rb +25 -9
- data/lib/active_record/runtime_registry.rb +24 -1
- data/lib/active_record/sanitization.rb +51 -11
- data/lib/active_record/schema.rb +2 -3
- data/lib/active_record/schema_dumper.rb +50 -7
- data/lib/active_record/schema_migration.rb +68 -33
- data/lib/active_record/scoping/default.rb +15 -5
- data/lib/active_record/scoping/named.rb +2 -2
- data/lib/active_record/scoping.rb +2 -1
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/signed_id.rb +7 -5
- data/lib/active_record/store.rb +9 -9
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +16 -3
- data/lib/active_record/tasks/database_tasks.rb +127 -105
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +16 -13
- data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
- data/lib/active_record/test_fixtures.rb +113 -96
- data/lib/active_record/timestamp.rb +27 -15
- data/lib/active_record/token_for.rb +113 -0
- data/lib/active_record/touch_later.rb +11 -6
- data/lib/active_record/transactions.rb +39 -13
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +8 -4
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/numericality.rb +5 -4
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +47 -2
- data/lib/active_record/validations.rb +8 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +121 -16
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +1 -1
- data/lib/arel/nodes/and.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -1
- data/lib/arel/nodes/bound_sql_literal.rb +61 -0
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/filter.rb +1 -1
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/homogeneous_in.rb +1 -9
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/node.rb +111 -2
- data/lib/arel/nodes/sql_literal.rb +6 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes.rb +4 -0
- data/lib/arel/predications.rb +2 -0
- data/lib/arel/table.rb +9 -5
- data/lib/arel/visitors/mysql.rb +8 -1
- data/lib/arel/visitors/to_sql.rb +81 -17
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +16 -2
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/migration.rb +3 -1
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
- metadata +48 -12
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
@@ -4,37 +4,83 @@ require "yaml"
|
|
4
4
|
|
5
5
|
module ActiveRecord
|
6
6
|
module Coders # :nodoc:
|
7
|
-
class YAMLColumn # :nodoc:
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
check_arity_of_constructor
|
14
|
-
end
|
7
|
+
class YAMLColumn < ColumnSerializer # :nodoc:
|
8
|
+
class SafeCoder
|
9
|
+
def initialize(permitted_classes: [], unsafe_load: nil)
|
10
|
+
@permitted_classes = permitted_classes
|
11
|
+
@unsafe_load = unsafe_load
|
12
|
+
end
|
15
13
|
|
16
|
-
|
17
|
-
|
14
|
+
if Gem::Version.new(Psych::VERSION) >= Gem::Version.new("5.1")
|
15
|
+
def dump(object)
|
16
|
+
if @unsafe_load.nil? ? ActiveRecord.use_yaml_unsafe_load : @unsafe_load
|
17
|
+
::YAML.dump(object)
|
18
|
+
else
|
19
|
+
::YAML.safe_dump(
|
20
|
+
object,
|
21
|
+
permitted_classes: @permitted_classes + ActiveRecord.yaml_column_permitted_classes,
|
22
|
+
aliases: true,
|
23
|
+
)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
else
|
27
|
+
def dump(object)
|
28
|
+
YAML.dump(object)
|
29
|
+
end
|
30
|
+
end
|
18
31
|
|
19
|
-
|
20
|
-
|
32
|
+
if YAML.respond_to?(:unsafe_load)
|
33
|
+
def load(payload)
|
34
|
+
if @unsafe_load.nil? ? ActiveRecord.use_yaml_unsafe_load : @unsafe_load
|
35
|
+
YAML.unsafe_load(payload)
|
36
|
+
else
|
37
|
+
YAML.safe_load(
|
38
|
+
payload,
|
39
|
+
permitted_classes: @permitted_classes + ActiveRecord.yaml_column_permitted_classes,
|
40
|
+
aliases: true,
|
41
|
+
)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
else
|
45
|
+
def load(payload)
|
46
|
+
if @unsafe_load.nil? ? ActiveRecord.use_yaml_unsafe_load : @unsafe_load
|
47
|
+
YAML.load(payload)
|
48
|
+
else
|
49
|
+
YAML.safe_load(
|
50
|
+
payload,
|
51
|
+
permitted_classes: @permitted_classes + ActiveRecord.yaml_column_permitted_classes,
|
52
|
+
aliases: true,
|
53
|
+
)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
21
57
|
end
|
22
58
|
|
23
|
-
def
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
59
|
+
def initialize(attr_name, object_class = Object, permitted_classes: [], unsafe_load: nil)
|
60
|
+
super(
|
61
|
+
attr_name,
|
62
|
+
SafeCoder.new(permitted_classes: permitted_classes || [], unsafe_load: unsafe_load),
|
63
|
+
object_class,
|
64
|
+
)
|
65
|
+
check_arity_of_constructor
|
66
|
+
end
|
30
67
|
|
31
|
-
|
68
|
+
def init_with(coder) # :nodoc:
|
69
|
+
unless coder["coder"]
|
70
|
+
permitted_classes = coder["permitted_classes"] || []
|
71
|
+
unsafe_load = coder["unsafe_load"] || false
|
72
|
+
coder["coder"] = SafeCoder.new(permitted_classes: permitted_classes, unsafe_load: unsafe_load)
|
73
|
+
end
|
74
|
+
super(coder)
|
32
75
|
end
|
33
76
|
|
34
|
-
def
|
35
|
-
|
36
|
-
|
37
|
-
|
77
|
+
def coder
|
78
|
+
# This is to retain forward compatibility when loading records serialized with Marshal
|
79
|
+
# from a previous version of Rails.
|
80
|
+
@coder ||= begin
|
81
|
+
permitted_classes = defined?(@permitted_classes) ? @permitted_classes : []
|
82
|
+
unsafe_load = defined?(@unsafe_load) && @unsafe_load.nil?
|
83
|
+
SafeCoder.new(permitted_classes: permitted_classes, unsafe_load: unsafe_load)
|
38
84
|
end
|
39
85
|
end
|
40
86
|
|
@@ -44,24 +90,6 @@ module ActiveRecord
|
|
44
90
|
rescue ArgumentError
|
45
91
|
raise ArgumentError, "Cannot serialize #{object_class}. Classes passed to `serialize` must have a 0 argument constructor."
|
46
92
|
end
|
47
|
-
|
48
|
-
if YAML.respond_to?(:unsafe_load)
|
49
|
-
def yaml_load(payload)
|
50
|
-
if ActiveRecord.use_yaml_unsafe_load
|
51
|
-
YAML.unsafe_load(payload)
|
52
|
-
else
|
53
|
-
YAML.safe_load(payload, permitted_classes: ActiveRecord.yaml_column_permitted_classes, aliases: true)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
else
|
57
|
-
def yaml_load(payload)
|
58
|
-
if ActiveRecord.use_yaml_unsafe_load
|
59
|
-
YAML.load(payload)
|
60
|
-
else
|
61
|
-
YAML.safe_load(payload, permitted_classes: ActiveRecord.yaml_column_permitted_classes, aliases: true)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
93
|
end
|
66
94
|
end
|
67
95
|
end
|
@@ -5,6 +5,8 @@ require "concurrent/map"
|
|
5
5
|
|
6
6
|
module ActiveRecord
|
7
7
|
module ConnectionAdapters
|
8
|
+
# = Active Record Connection Handler
|
9
|
+
#
|
8
10
|
# ConnectionHandler is a collection of ConnectionPool objects. It is used
|
9
11
|
# for keeping separate connection pools that connect to different databases.
|
10
12
|
#
|
@@ -56,7 +58,7 @@ module ActiveRecord
|
|
56
58
|
FINALIZER = lambda { |_| ActiveSupport::ForkTracker.check! }
|
57
59
|
private_constant :FINALIZER
|
58
60
|
|
59
|
-
class
|
61
|
+
class StringConnectionName # :nodoc:
|
60
62
|
attr_reader :name
|
61
63
|
|
62
64
|
def initialize(name)
|
@@ -73,8 +75,8 @@ module ActiveRecord
|
|
73
75
|
end
|
74
76
|
|
75
77
|
def initialize
|
76
|
-
# These caches are keyed by pool_config.
|
77
|
-
@
|
78
|
+
# These caches are keyed by pool_config.connection_name (PoolConfig#connection_name).
|
79
|
+
@connection_name_to_pool_manager = Concurrent::Map.new(initial_capacity: 2)
|
78
80
|
|
79
81
|
# Backup finalizer: if the forked child skipped Kernel#fork the early discard has not occurred
|
80
82
|
ObjectSpace.define_finalizer self, FINALIZER
|
@@ -88,121 +90,154 @@ module ActiveRecord
|
|
88
90
|
ActiveSupport::IsolatedExecutionState[:active_record_prevent_writes] = prevent_writes
|
89
91
|
end
|
90
92
|
|
91
|
-
# Prevent writing to the database regardless of role.
|
92
|
-
#
|
93
|
-
# In some cases you may want to prevent writes to the database
|
94
|
-
# even if you are on a database that can write. +while_preventing_writes+
|
95
|
-
# will prevent writes to the database for the duration of the block.
|
96
|
-
#
|
97
|
-
# This method does not provide the same protection as a readonly
|
98
|
-
# user and is meant to be a safeguard against accidental writes.
|
99
|
-
#
|
100
|
-
# See +READ_QUERY+ for the queries that are blocked by this
|
101
|
-
# method.
|
102
|
-
def while_preventing_writes(enabled = true)
|
103
|
-
unless ActiveRecord.legacy_connection_handling
|
104
|
-
raise NotImplementedError, "`while_preventing_writes` is only available on the connection_handler with legacy_connection_handling"
|
105
|
-
end
|
106
|
-
|
107
|
-
original, self.prevent_writes = self.prevent_writes, enabled
|
108
|
-
yield
|
109
|
-
ensure
|
110
|
-
self.prevent_writes = original
|
111
|
-
end
|
112
|
-
|
113
93
|
def connection_pool_names # :nodoc:
|
114
|
-
|
94
|
+
connection_name_to_pool_manager.keys
|
115
95
|
end
|
116
96
|
|
117
97
|
def all_connection_pools
|
118
|
-
|
98
|
+
ActiveRecord.deprecator.warn(<<-MSG.squish)
|
99
|
+
The `all_connection_pools` method is deprecated in favor of `connection_pool_list`.
|
100
|
+
Call `connection_pool_list(:all)` to get the same behavior as `all_connection_pools`.
|
101
|
+
MSG
|
102
|
+
connection_name_to_pool_manager.values.flat_map { |m| m.pool_configs.map(&:pool) }
|
119
103
|
end
|
120
104
|
|
121
|
-
|
122
|
-
|
105
|
+
# Returns the pools for a connection handler and given role. If +:all+ is passed,
|
106
|
+
# all pools belonging to the connection handler will be returned.
|
107
|
+
def connection_pool_list(role = nil)
|
108
|
+
if role.nil?
|
109
|
+
deprecation_for_pool_handling(__method__)
|
110
|
+
role = ActiveRecord::Base.current_role
|
111
|
+
connection_name_to_pool_manager.values.flat_map { |m| m.pool_configs(role).map(&:pool) }
|
112
|
+
elsif role == :all
|
113
|
+
connection_name_to_pool_manager.values.flat_map { |m| m.pool_configs.map(&:pool) }
|
114
|
+
else
|
115
|
+
connection_name_to_pool_manager.values.flat_map { |m| m.pool_configs(role).map(&:pool) }
|
116
|
+
end
|
123
117
|
end
|
124
118
|
alias :connection_pools :connection_pool_list
|
125
119
|
|
126
|
-
def
|
127
|
-
|
120
|
+
def each_connection_pool(role = nil, &block) # :nodoc:
|
121
|
+
role = nil if role == :all
|
122
|
+
return enum_for(__method__, role) unless block_given?
|
123
|
+
|
124
|
+
connection_name_to_pool_manager.each_value do |manager|
|
125
|
+
manager.each_pool_config(role) do |pool_config|
|
126
|
+
yield pool_config.pool
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def establish_connection(config, owner_name: Base, role: Base.current_role, shard: Base.current_shard, clobber: false)
|
132
|
+
owner_name = determine_owner_name(owner_name, config)
|
128
133
|
|
129
134
|
pool_config = resolve_pool_config(config, owner_name, role, shard)
|
130
135
|
db_config = pool_config.db_config
|
131
136
|
|
132
|
-
|
133
|
-
# if the user calls `establish_connection :primary`.
|
134
|
-
if owner_to_pool_manager.key?(pool_config.connection_specification_name)
|
135
|
-
remove_connection_pool(pool_config.connection_specification_name, role: role, shard: shard)
|
136
|
-
end
|
137
|
+
pool_manager = set_pool_manager(pool_config.connection_name)
|
137
138
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
payload[:config] = db_config.configuration_hash
|
144
|
-
end
|
139
|
+
# If there is an existing pool with the same values as the pool_config
|
140
|
+
# don't remove the connection. Connections should only be removed if we are
|
141
|
+
# establishing a connection on a class that is already connected to a different
|
142
|
+
# configuration.
|
143
|
+
existing_pool_config = pool_manager.get_pool_config(role, shard)
|
145
144
|
|
146
|
-
if
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
145
|
+
if !clobber && existing_pool_config && existing_pool_config.db_config == db_config
|
146
|
+
# Update the pool_config's connection class if it differs. This is used
|
147
|
+
# for ensuring that ActiveRecord::Base and the primary_abstract_class use
|
148
|
+
# the same pool. Without this granular swapping will not work correctly.
|
149
|
+
if owner_name.primary_class? && (existing_pool_config.connection_class != owner_name)
|
150
|
+
existing_pool_config.connection_class = owner_name
|
151
|
+
end
|
153
152
|
|
154
|
-
|
155
|
-
|
153
|
+
existing_pool_config.pool
|
154
|
+
else
|
155
|
+
disconnect_pool_from_pool_manager(pool_manager, role, shard)
|
156
|
+
pool_manager.set_pool_config(role, shard, pool_config)
|
157
|
+
|
158
|
+
payload = {
|
159
|
+
connection_name: pool_config.connection_name,
|
160
|
+
role: role,
|
161
|
+
shard: shard,
|
162
|
+
config: db_config.configuration_hash
|
163
|
+
}
|
164
|
+
|
165
|
+
ActiveSupport::Notifications.instrumenter.instrument("!connection.active_record", payload) do
|
166
|
+
pool_config.pool
|
167
|
+
end
|
156
168
|
end
|
157
169
|
end
|
158
170
|
|
159
171
|
# Returns true if there are any active connections among the connection
|
160
172
|
# pools that the ConnectionHandler is managing.
|
161
|
-
def active_connections?(role =
|
162
|
-
|
173
|
+
def active_connections?(role = nil)
|
174
|
+
if role.nil?
|
175
|
+
deprecation_for_pool_handling(__method__)
|
176
|
+
role = ActiveRecord::Base.current_role
|
177
|
+
end
|
178
|
+
|
179
|
+
each_connection_pool(role).any?(&:active_connection?)
|
163
180
|
end
|
164
181
|
|
165
182
|
# Returns any connections in use by the current thread back to the pool,
|
166
183
|
# and also returns connections to the pool cached by threads that are no
|
167
184
|
# longer alive.
|
168
|
-
def clear_active_connections!(role =
|
169
|
-
|
185
|
+
def clear_active_connections!(role = nil)
|
186
|
+
if role.nil?
|
187
|
+
deprecation_for_pool_handling(__method__)
|
188
|
+
role = ActiveRecord::Base.current_role
|
189
|
+
end
|
190
|
+
|
191
|
+
each_connection_pool(role).each(&:release_connection)
|
170
192
|
end
|
171
193
|
|
172
194
|
# Clears the cache which maps classes.
|
173
195
|
#
|
174
196
|
# See ConnectionPool#clear_reloadable_connections! for details.
|
175
|
-
def clear_reloadable_connections!(role =
|
176
|
-
|
197
|
+
def clear_reloadable_connections!(role = nil)
|
198
|
+
if role.nil?
|
199
|
+
deprecation_for_pool_handling(__method__)
|
200
|
+
role = ActiveRecord::Base.current_role
|
201
|
+
end
|
202
|
+
|
203
|
+
each_connection_pool(role).each(&:clear_reloadable_connections!)
|
177
204
|
end
|
178
205
|
|
179
|
-
def clear_all_connections!(role =
|
180
|
-
|
206
|
+
def clear_all_connections!(role = nil)
|
207
|
+
if role.nil?
|
208
|
+
deprecation_for_pool_handling(__method__)
|
209
|
+
role = ActiveRecord::Base.current_role
|
210
|
+
end
|
211
|
+
|
212
|
+
each_connection_pool(role).each(&:disconnect!)
|
181
213
|
end
|
182
214
|
|
183
215
|
# Disconnects all currently idle connections.
|
184
216
|
#
|
185
217
|
# See ConnectionPool#flush! for details.
|
186
|
-
def flush_idle_connections!(role =
|
187
|
-
|
218
|
+
def flush_idle_connections!(role = nil)
|
219
|
+
if role.nil?
|
220
|
+
deprecation_for_pool_handling(__method__)
|
221
|
+
role = ActiveRecord::Base.current_role
|
222
|
+
end
|
223
|
+
|
224
|
+
each_connection_pool(role).each(&:flush!)
|
188
225
|
end
|
189
226
|
|
190
227
|
# Locate the connection of the nearest super class. This can be an
|
191
228
|
# active or defined connection: if it is the latter, it will be
|
192
229
|
# opened and set as the active connection for the class it was defined
|
193
230
|
# for (not necessarily the current class).
|
194
|
-
def retrieve_connection(
|
195
|
-
pool = retrieve_connection_pool(
|
231
|
+
def retrieve_connection(connection_name, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard) # :nodoc:
|
232
|
+
pool = retrieve_connection_pool(connection_name, role: role, shard: shard)
|
196
233
|
|
197
234
|
unless pool
|
198
235
|
if shard != ActiveRecord::Base.default_shard
|
199
|
-
message = "No connection pool for '#{
|
200
|
-
elsif ActiveRecord::Base.connection_handler != ActiveRecord::Base.default_connection_handler
|
201
|
-
message = "No connection pool for '#{spec_name}' found for the '#{ActiveRecord::Base.current_role}' role."
|
236
|
+
message = "No connection pool for '#{connection_name}' found for the '#{shard}' shard."
|
202
237
|
elsif role != ActiveRecord::Base.default_role
|
203
|
-
message = "No connection pool for '#{
|
238
|
+
message = "No connection pool for '#{connection_name}' found for the '#{role}' role."
|
204
239
|
else
|
205
|
-
message = "No connection pool for '#{
|
240
|
+
message = "No connection pool for '#{connection_name}' found."
|
206
241
|
end
|
207
242
|
|
208
243
|
raise ConnectionNotEstablished, message
|
@@ -213,36 +248,66 @@ module ActiveRecord
|
|
213
248
|
|
214
249
|
# Returns true if a connection that's accessible to this class has
|
215
250
|
# already been opened.
|
216
|
-
def connected?(
|
217
|
-
pool = retrieve_connection_pool(
|
251
|
+
def connected?(connection_name, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard)
|
252
|
+
pool = retrieve_connection_pool(connection_name, role: role, shard: shard)
|
218
253
|
pool && pool.connected?
|
219
254
|
end
|
220
255
|
|
221
|
-
def remove_connection_pool(
|
222
|
-
if pool_manager = get_pool_manager(
|
223
|
-
|
224
|
-
|
225
|
-
if pool_config
|
226
|
-
pool_config.disconnect!
|
227
|
-
pool_config.db_config
|
228
|
-
end
|
256
|
+
def remove_connection_pool(connection_name, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard)
|
257
|
+
if pool_manager = get_pool_manager(connection_name)
|
258
|
+
disconnect_pool_from_pool_manager(pool_manager, role, shard)
|
229
259
|
end
|
230
260
|
end
|
231
261
|
|
232
|
-
# Retrieving the connection pool happens a lot, so we cache it in @
|
262
|
+
# Retrieving the connection pool happens a lot, so we cache it in @connection_name_to_pool_manager.
|
233
263
|
# This makes retrieving the connection pool O(1) once the process is warm.
|
234
264
|
# When a connection is established or removed, we invalidate the cache.
|
235
|
-
def retrieve_connection_pool(
|
236
|
-
pool_config = get_pool_manager(
|
265
|
+
def retrieve_connection_pool(connection_name, role: ActiveRecord::Base.current_role, shard: ActiveRecord::Base.current_shard)
|
266
|
+
pool_config = get_pool_manager(connection_name)&.get_pool_config(role, shard)
|
237
267
|
pool_config&.pool
|
238
268
|
end
|
239
269
|
|
240
270
|
private
|
241
|
-
attr_reader :
|
271
|
+
attr_reader :connection_name_to_pool_manager
|
242
272
|
|
243
|
-
# Returns the pool manager for
|
244
|
-
def get_pool_manager(
|
245
|
-
|
273
|
+
# Returns the pool manager for a connection name / identifier.
|
274
|
+
def get_pool_manager(connection_name)
|
275
|
+
connection_name_to_pool_manager[connection_name]
|
276
|
+
end
|
277
|
+
|
278
|
+
# Get the existing pool manager or initialize and assign a new one.
|
279
|
+
def set_pool_manager(connection_name)
|
280
|
+
connection_name_to_pool_manager[connection_name] ||= PoolManager.new
|
281
|
+
end
|
282
|
+
|
283
|
+
def pool_managers
|
284
|
+
connection_name_to_pool_manager.values
|
285
|
+
end
|
286
|
+
|
287
|
+
def deprecation_for_pool_handling(method)
|
288
|
+
roles = []
|
289
|
+
pool_managers.each do |pool_manager|
|
290
|
+
roles << pool_manager.role_names
|
291
|
+
end
|
292
|
+
|
293
|
+
if roles.flatten.uniq.count > 1
|
294
|
+
ActiveRecord.deprecator.warn(<<-MSG.squish)
|
295
|
+
`#{method}` currently only applies to connection pools in the current
|
296
|
+
role (`#{ActiveRecord::Base.current_role}`). In Rails 7.2, this method
|
297
|
+
will apply to all known pools, regardless of role. To affect only those
|
298
|
+
connections belonging to a specific role, pass the role name as an
|
299
|
+
argument. To switch to the new behavior, pass `:all` as the role name.
|
300
|
+
MSG
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
def disconnect_pool_from_pool_manager(pool_manager, role, shard)
|
305
|
+
pool_config = pool_manager.remove_pool_config(role, shard)
|
306
|
+
|
307
|
+
if pool_config
|
308
|
+
pool_config.disconnect!
|
309
|
+
pool_config.db_config
|
310
|
+
end
|
246
311
|
end
|
247
312
|
|
248
313
|
# Returns an instance of PoolConfig for a given adapter.
|
@@ -255,7 +320,7 @@ module ActiveRecord
|
|
255
320
|
# pool_config.db_config.configuration_hash
|
256
321
|
# # => { host: "localhost", database: "foo", adapter: "sqlite3" }
|
257
322
|
#
|
258
|
-
def resolve_pool_config(config,
|
323
|
+
def resolve_pool_config(config, connection_name, role, shard)
|
259
324
|
db_config = Base.configurations.resolve(config)
|
260
325
|
|
261
326
|
raise(AdapterNotSpecified, "database configuration does not specify adapter") unless db_config.adapter
|
@@ -285,7 +350,17 @@ module ActiveRecord
|
|
285
350
|
raise AdapterNotFound, "database configuration specifies nonexistent #{db_config.adapter} adapter"
|
286
351
|
end
|
287
352
|
|
288
|
-
ConnectionAdapters::PoolConfig.new(
|
353
|
+
ConnectionAdapters::PoolConfig.new(connection_name, db_config, role, shard)
|
354
|
+
end
|
355
|
+
|
356
|
+
def determine_owner_name(owner_name, config)
|
357
|
+
if owner_name.is_a?(String) || owner_name.is_a?(Symbol)
|
358
|
+
StringConnectionName.new(owner_name.to_s)
|
359
|
+
elsif config.is_a?(Symbol)
|
360
|
+
StringConnectionName.new(config.to_s)
|
361
|
+
else
|
362
|
+
owner_name
|
363
|
+
end
|
289
364
|
end
|
290
365
|
end
|
291
366
|
end
|
@@ -6,12 +6,14 @@ require "weakref"
|
|
6
6
|
module ActiveRecord
|
7
7
|
module ConnectionAdapters
|
8
8
|
class ConnectionPool
|
9
|
+
# = Active Record Connection Pool \Reaper
|
10
|
+
#
|
9
11
|
# Every +frequency+ seconds, the reaper will call +reap+ and +flush+ on
|
10
12
|
# +pool+. A reaper instantiated with a zero frequency will never reap
|
11
13
|
# the connection pool.
|
12
14
|
#
|
13
15
|
# Configure the frequency by setting +reaping_frequency+ in your database
|
14
|
-
#
|
16
|
+
# YAML file (default 60 seconds).
|
15
17
|
class Reaper
|
16
18
|
attr_reader :pool, :frequency
|
17
19
|
|