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