activerecord 7.0.8.7 → 7.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +781 -1777
- data/MIT-LICENSE +1 -1
- data/README.rdoc +30 -30
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +2 -2
- data/lib/active_record/associations/alias_tracker.rb +31 -23
- data/lib/active_record/associations/association.rb +35 -12
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +40 -9
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +3 -2
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +22 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
- data/lib/active_record/associations/builder/has_many.rb +3 -4
- data/lib/active_record/associations/builder/has_one.rb +3 -4
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +35 -21
- data/lib/active_record/associations/collection_proxy.rb +29 -11
- data/lib/active_record/associations/errors.rb +265 -0
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +21 -14
- data/lib/active_record/associations/has_many_through_association.rb +17 -7
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency/join_association.rb +4 -3
- data/lib/active_record/associations/join_dependency.rb +10 -10
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +33 -8
- data/lib/active_record/associations/preloader/branch.rb +7 -1
- data/lib/active_record/associations/preloader/through_association.rb +1 -3
- data/lib/active_record/associations/preloader.rb +13 -10
- data/lib/active_record/associations/singular_association.rb +7 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +354 -485
- data/lib/active_record/attribute_assignment.rb +0 -4
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +53 -35
- data/lib/active_record/attribute_methods/primary_key.rb +45 -25
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +8 -7
- data/lib/active_record/attribute_methods/serialization.rb +131 -32
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +11 -6
- data/lib/active_record/attribute_methods/write.rb +6 -6
- data/lib/active_record/attribute_methods.rb +153 -33
- data/lib/active_record/attributes.rb +96 -71
- data/lib/active_record/autosave_association.rb +81 -39
- data/lib/active_record/base.rb +11 -7
- data/lib/active_record/callbacks.rb +11 -25
- 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 +123 -131
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +4 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +343 -91
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +160 -45
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +229 -64
- data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -63
- 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 +142 -12
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +310 -129
- data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
- data/lib/active_record/connection_adapters/abstract_adapter.rb +539 -111
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +289 -128
- 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 +26 -139
- data/lib/active_record/connection_adapters/mysql/quoting.rb +60 -55
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +6 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +25 -13
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +108 -68
- data/lib/active_record/connection_adapters/pool_config.rb +20 -10
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +14 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +100 -43
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/oid/interval.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 +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +65 -61
- 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 +153 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +54 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +371 -64
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +374 -203
- data/lib/active_record/connection_adapters/schema_cache.rb +302 -79
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +60 -43
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +57 -45
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +14 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +51 -8
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +298 -113
- 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 +229 -0
- data/lib/active_record/connection_adapters.rb +124 -1
- data/lib/active_record/connection_handling.rb +101 -105
- data/lib/active_record/core.rb +273 -178
- data/lib/active_record/counter_cache.rb +69 -35
- data/lib/active_record/database_configurations/connection_url_resolver.rb +10 -3
- data/lib/active_record/database_configurations/database_config.rb +26 -5
- data/lib/active_record/database_configurations/hash_config.rb +52 -34
- data/lib/active_record/database_configurations/url_config.rb +37 -12
- data/lib/active_record/database_configurations.rb +87 -34
- data/lib/active_record/delegated_type.rb +56 -27
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +3 -1
- data/lib/active_record/dynamic_matchers.rb +2 -2
- 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 +46 -22
- data/lib/active_record/encryption/encrypted_attribute_type.rb +48 -13
- data/lib/active_record/encryption/encryptor.rb +35 -19
- 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/key_provider.rb +1 -1
- data/lib/active_record/encryption/message_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +6 -0
- data/lib/active_record/encryption/null_encryptor.rb +4 -0
- data/lib/active_record/encryption/properties.rb +3 -3
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +22 -21
- data/lib/active_record/encryption.rb +3 -0
- data/lib/active_record/enum.rb +130 -28
- data/lib/active_record/errors.rb +154 -34
- data/lib/active_record/explain.rb +21 -12
- 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 +48 -10
- data/lib/active_record/fixtures.rb +167 -97
- data/lib/active_record/future_result.rb +47 -8
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +34 -18
- data/lib/active_record/insert_all.rb +72 -22
- data/lib/active_record/integration.rb +11 -8
- data/lib/active_record/internal_metadata.rb +124 -20
- data/lib/active_record/locking/optimistic.rb +8 -7
- data/lib/active_record/locking/pessimistic.rb +5 -2
- data/lib/active_record/log_subscriber.rb +18 -22
- data/lib/active_record/marshalling.rb +59 -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 +6 -8
- data/lib/active_record/middleware/shard_selector.rb +3 -1
- data/lib/active_record/migration/command_recorder.rb +106 -8
- data/lib/active_record/migration/compatibility.rb +147 -5
- data/lib/active_record/migration/default_strategy.rb +22 -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 +236 -118
- data/lib/active_record/model_schema.rb +90 -102
- data/lib/active_record/nested_attributes.rb +48 -11
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +168 -339
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +18 -25
- data/lib/active_record/query_logs.rb +96 -52
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +35 -10
- data/lib/active_record/railtie.rb +131 -87
- data/lib/active_record/railties/controller_runtime.rb +22 -7
- data/lib/active_record/railties/databases.rake +147 -155
- 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 +267 -69
- data/lib/active_record/relation/batches/batch_enumerator.rb +20 -5
- data/lib/active_record/relation/batches.rb +198 -63
- data/lib/active_record/relation/calculations.rb +270 -108
- data/lib/active_record/relation/delegation.rb +30 -19
- data/lib/active_record/relation/finder_methods.rb +97 -21
- data/lib/active_record/relation/merger.rb +6 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +20 -3
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -7
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +28 -16
- data/lib/active_record/relation/query_attribute.rb +3 -2
- data/lib/active_record/relation/query_methods.rb +585 -109
- data/lib/active_record/relation/record_fetch_warning.rb +3 -0
- data/lib/active_record/relation/spawn_methods.rb +5 -4
- data/lib/active_record/relation/where_clause.rb +15 -21
- data/lib/active_record/relation.rb +592 -92
- data/lib/active_record/result.rb +49 -48
- data/lib/active_record/runtime_registry.rb +63 -1
- data/lib/active_record/sanitization.rb +70 -25
- data/lib/active_record/schema.rb +8 -7
- data/lib/active_record/schema_dumper.rb +90 -23
- data/lib/active_record/schema_migration.rb +75 -24
- data/lib/active_record/scoping/default.rb +15 -5
- data/lib/active_record/scoping/named.rb +3 -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 +33 -11
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/store.rb +8 -8
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +1 -1
- data/lib/active_record/tasks/database_tasks.rb +190 -118
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +23 -13
- data/lib/active_record/tasks/sqlite_database_tasks.rb +16 -7
- data/lib/active_record/test_fixtures.rb +170 -155
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +31 -17
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +12 -7
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +108 -24
- data/lib/active_record/translation.rb +0 -2
- 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 +1 -3
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +9 -3
- 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 +61 -11
- data/lib/active_record/validations.rb +12 -5
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +247 -33
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/collectors/bind.rb +3 -1
- data/lib/arel/collectors/composite.rb +7 -0
- data/lib/arel/collectors/sql_string.rb +1 -1
- data/lib/arel/collectors/substitute_binds.rb +1 -1
- data/lib/arel/crud.rb +2 -0
- data/lib/arel/delete_manager.rb +5 -0
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -7
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/delete_statement.rb +4 -2
- 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/{and.rb → nary.rb} +5 -2
- data/lib/arel/nodes/node.rb +115 -5
- data/lib/arel/nodes/sql_literal.rb +13 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes/update_statement.rb +4 -2
- data/lib/arel/nodes.rb +6 -2
- data/lib/arel/predications.rb +3 -1
- data/lib/arel/select_manager.rb +7 -3
- data/lib/arel/table.rb +9 -5
- data/lib/arel/tree_manager.rb +8 -3
- data/lib/arel/update_manager.rb +7 -1
- data/lib/arel/visitors/dot.rb +3 -0
- data/lib/arel/visitors/mysql.rb +17 -5
- data/lib/arel/visitors/postgresql.rb +1 -12
- data/lib/arel/visitors/sqlite.rb +25 -0
- data/lib/arel/visitors/to_sql.rb +114 -34
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +21 -3
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +4 -1
- 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 +56 -17
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
|
@@ -4,6 +4,7 @@ require "set"
|
|
|
4
4
|
require "active_record/connection_adapters/sql_type_metadata"
|
|
5
5
|
require "active_record/connection_adapters/abstract/schema_dumper"
|
|
6
6
|
require "active_record/connection_adapters/abstract/schema_creation"
|
|
7
|
+
require "active_support/concurrency/null_lock"
|
|
7
8
|
require "active_support/concurrency/load_interlock_aware_monitor"
|
|
8
9
|
require "arel/collectors/bind"
|
|
9
10
|
require "arel/collectors/composite"
|
|
@@ -12,6 +13,8 @@ require "arel/collectors/substitute_binds"
|
|
|
12
13
|
|
|
13
14
|
module ActiveRecord
|
|
14
15
|
module ConnectionAdapters # :nodoc:
|
|
16
|
+
# = Active Record Abstract Adapter
|
|
17
|
+
#
|
|
15
18
|
# Active Record supports multiple database systems. AbstractAdapter and
|
|
16
19
|
# related classes form the abstraction layer which makes this possible.
|
|
17
20
|
# An AbstractAdapter represents a connection to a database, and provides an
|
|
@@ -20,7 +23,7 @@ module ActiveRecord
|
|
|
20
23
|
# and +:limit+ options, etc.
|
|
21
24
|
#
|
|
22
25
|
# All the concrete database adapters follow the interface laid down in this class.
|
|
23
|
-
# {ActiveRecord::Base.
|
|
26
|
+
# {ActiveRecord::Base.lease_connection}[rdoc-ref:ConnectionHandling#lease_connection] returns an AbstractAdapter object, which
|
|
24
27
|
# you can use.
|
|
25
28
|
#
|
|
26
29
|
# Most of the methods in the adapter are useful during migrations. Most
|
|
@@ -36,12 +39,19 @@ module ActiveRecord
|
|
|
36
39
|
include Savepoints
|
|
37
40
|
|
|
38
41
|
SIMPLE_INT = /\A\d+\z/
|
|
39
|
-
COMMENT_REGEX = %r{(?:--.*\n)|/\*(?:[^*]|\*[^/])*\*/}
|
|
42
|
+
COMMENT_REGEX = %r{(?:--.*\n)|/\*(?:[^*]|\*[^/])*\*/}
|
|
40
43
|
|
|
41
|
-
|
|
44
|
+
attr_reader :pool
|
|
42
45
|
attr_reader :visitor, :owner, :logger, :lock
|
|
46
|
+
attr_accessor :pinned # :nodoc:
|
|
43
47
|
alias :in_use? :owner
|
|
44
48
|
|
|
49
|
+
def pool=(value)
|
|
50
|
+
return if value.eql?(@pool)
|
|
51
|
+
@schema_cache = nil
|
|
52
|
+
@pool = value
|
|
53
|
+
end
|
|
54
|
+
|
|
45
55
|
set_callback :checkin, :after, :enable_lazy_transactions!
|
|
46
56
|
|
|
47
57
|
def self.type_cast_config_to_integer(config)
|
|
@@ -62,6 +72,16 @@ module ActiveRecord
|
|
|
62
72
|
end
|
|
63
73
|
end
|
|
64
74
|
|
|
75
|
+
def self.validate_default_timezone(config)
|
|
76
|
+
case config
|
|
77
|
+
when nil
|
|
78
|
+
when "utc", "local"
|
|
79
|
+
config.to_sym
|
|
80
|
+
else
|
|
81
|
+
raise ArgumentError, "default_timezone must be either 'utc' or 'local'"
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
65
85
|
DEFAULT_READ_QUERY = [:begin, :commit, :explain, :release, :rollback, :savepoint, :select, :with] # :nodoc:
|
|
66
86
|
private_constant :DEFAULT_READ_QUERY
|
|
67
87
|
|
|
@@ -71,27 +91,107 @@ module ActiveRecord
|
|
|
71
91
|
/\A(?:[(\s]|#{COMMENT_REGEX})*#{Regexp.union(*parts)}/
|
|
72
92
|
end
|
|
73
93
|
|
|
74
|
-
def
|
|
94
|
+
def self.find_cmd_and_exec(commands, *args) # :doc:
|
|
95
|
+
commands = Array(commands)
|
|
96
|
+
|
|
97
|
+
dirs_on_path = ENV["PATH"].to_s.split(File::PATH_SEPARATOR)
|
|
98
|
+
unless (ext = RbConfig::CONFIG["EXEEXT"]).empty?
|
|
99
|
+
commands = commands.map { |cmd| "#{cmd}#{ext}" }
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
full_path_command = nil
|
|
103
|
+
found = commands.detect do |cmd|
|
|
104
|
+
dirs_on_path.detect do |path|
|
|
105
|
+
full_path_command = File.join(path, cmd)
|
|
106
|
+
begin
|
|
107
|
+
stat = File.stat(full_path_command)
|
|
108
|
+
rescue SystemCallError
|
|
109
|
+
else
|
|
110
|
+
stat.file? && stat.executable?
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
if found
|
|
116
|
+
exec full_path_command, *args
|
|
117
|
+
else
|
|
118
|
+
abort("Couldn't find database client: #{commands.join(', ')}. Check your $PATH and try again.")
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Opens a database console session.
|
|
123
|
+
def self.dbconsole(config, options = {})
|
|
124
|
+
raise NotImplementedError
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def initialize(config_or_deprecated_connection, deprecated_logger = nil, deprecated_connection_options = nil, deprecated_config = nil) # :nodoc:
|
|
75
128
|
super()
|
|
76
129
|
|
|
77
|
-
@
|
|
78
|
-
@
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
130
|
+
@raw_connection = nil
|
|
131
|
+
@unconfigured_connection = nil
|
|
132
|
+
|
|
133
|
+
if config_or_deprecated_connection.is_a?(Hash)
|
|
134
|
+
@config = config_or_deprecated_connection.symbolize_keys
|
|
135
|
+
@logger = ActiveRecord::Base.logger
|
|
136
|
+
|
|
137
|
+
if deprecated_logger || deprecated_connection_options || deprecated_config
|
|
138
|
+
raise ArgumentError, "when initializing an Active Record adapter with a config hash, that should be the only argument"
|
|
139
|
+
end
|
|
140
|
+
else
|
|
141
|
+
# Soft-deprecated for now; we'll probably warn in future.
|
|
142
|
+
|
|
143
|
+
@unconfigured_connection = config_or_deprecated_connection
|
|
144
|
+
@logger = deprecated_logger || ActiveRecord::Base.logger
|
|
145
|
+
if deprecated_config
|
|
146
|
+
@config = (deprecated_config || {}).symbolize_keys
|
|
147
|
+
@connection_parameters = deprecated_connection_options
|
|
148
|
+
else
|
|
149
|
+
@config = (deprecated_connection_options || {}).symbolize_keys
|
|
150
|
+
@connection_parameters = nil
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
@owner = nil
|
|
155
|
+
@pinned = false
|
|
156
|
+
@instrumenter = ActiveSupport::Notifications.instrumenter
|
|
157
|
+
@pool = ActiveRecord::ConnectionAdapters::NullPool.new
|
|
158
|
+
@idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
84
159
|
@visitor = arel_visitor
|
|
85
160
|
@statements = build_statement_pool
|
|
86
|
-
|
|
161
|
+
self.lock_thread = nil
|
|
87
162
|
|
|
88
|
-
@prepared_statements = self.class.type_cast_config_to_boolean(
|
|
89
|
-
config.fetch(:prepared_statements
|
|
163
|
+
@prepared_statements = !ActiveRecord.disable_prepared_statements && self.class.type_cast_config_to_boolean(
|
|
164
|
+
@config.fetch(:prepared_statements) { default_prepared_statements }
|
|
90
165
|
)
|
|
91
166
|
|
|
92
167
|
@advisory_locks_enabled = self.class.type_cast_config_to_boolean(
|
|
93
|
-
config.fetch(:advisory_locks, true)
|
|
168
|
+
@config.fetch(:advisory_locks, true)
|
|
94
169
|
)
|
|
170
|
+
|
|
171
|
+
@default_timezone = self.class.validate_default_timezone(@config[:default_timezone])
|
|
172
|
+
|
|
173
|
+
@raw_connection_dirty = false
|
|
174
|
+
@last_activity = nil
|
|
175
|
+
@verified = false
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def inspect # :nodoc:
|
|
179
|
+
name_field = " name=#{pool.db_config.name.inspect}" unless pool.db_config.name == "primary"
|
|
180
|
+
shard_field = " shard=#{shard.inspect}" unless shard == :default
|
|
181
|
+
|
|
182
|
+
"#<#{self.class.name}:#{'%#016x' % (object_id << 1)} env_name=#{pool.db_config.env_name.inspect}#{name_field} role=#{role.inspect}#{shard_field}>"
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def lock_thread=(lock_thread) # :nodoc:
|
|
186
|
+
@lock =
|
|
187
|
+
case lock_thread
|
|
188
|
+
when Thread
|
|
189
|
+
ActiveSupport::Concurrency::ThreadLoadInterlockAwareMonitor.new
|
|
190
|
+
when Fiber
|
|
191
|
+
ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
|
|
192
|
+
else
|
|
193
|
+
ActiveSupport::Concurrency::NullLock
|
|
194
|
+
end
|
|
95
195
|
end
|
|
96
196
|
|
|
97
197
|
EXCEPTION_NEVER = { Exception => :never }.freeze # :nodoc:
|
|
@@ -117,51 +217,35 @@ module ActiveRecord
|
|
|
117
217
|
@config[:replica] || false
|
|
118
218
|
end
|
|
119
219
|
|
|
120
|
-
def
|
|
121
|
-
@config
|
|
220
|
+
def connection_retries
|
|
221
|
+
(@config[:connection_retries] || 1).to_i
|
|
122
222
|
end
|
|
123
223
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
# Returns true if the connection is a replica.
|
|
127
|
-
#
|
|
128
|
-
# If the application is using legacy handling, returns
|
|
129
|
-
# true if +connection_handler.prevent_writes+ is set.
|
|
130
|
-
#
|
|
131
|
-
# If the application is using the new connection handling
|
|
132
|
-
# will return true based on +current_preventing_writes+.
|
|
133
|
-
def preventing_writes?
|
|
134
|
-
return true if replica?
|
|
135
|
-
return ActiveRecord::Base.connection_handler.prevent_writes if ActiveRecord.legacy_connection_handling
|
|
136
|
-
return false if connection_class.nil?
|
|
137
|
-
|
|
138
|
-
connection_class.current_preventing_writes
|
|
224
|
+
def verify_timeout
|
|
225
|
+
(@config[:verify_timeout] || 2).to_i
|
|
139
226
|
end
|
|
140
227
|
|
|
141
|
-
def
|
|
142
|
-
@config[:
|
|
228
|
+
def retry_deadline
|
|
229
|
+
if @config[:retry_deadline]
|
|
230
|
+
@config[:retry_deadline].to_f
|
|
231
|
+
else
|
|
232
|
+
nil
|
|
233
|
+
end
|
|
143
234
|
end
|
|
144
235
|
|
|
145
|
-
def
|
|
146
|
-
|
|
236
|
+
def default_timezone
|
|
237
|
+
@default_timezone || ActiveRecord.default_timezone
|
|
147
238
|
end
|
|
148
239
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
schema_migration_name = "#{spec_name}::SchemaMigration"
|
|
157
|
-
|
|
158
|
-
Class.new(ActiveRecord::SchemaMigration) do
|
|
159
|
-
define_singleton_method(:name) { schema_migration_name }
|
|
160
|
-
define_singleton_method(:to_s) { schema_migration_name }
|
|
240
|
+
# Determines whether writes are currently being prevented.
|
|
241
|
+
#
|
|
242
|
+
# Returns true if the connection is a replica or returns
|
|
243
|
+
# the value of +current_preventing_writes+.
|
|
244
|
+
def preventing_writes?
|
|
245
|
+
return true if replica?
|
|
246
|
+
return false if connection_class.nil?
|
|
161
247
|
|
|
162
|
-
|
|
163
|
-
end
|
|
164
|
-
end
|
|
248
|
+
connection_class.current_preventing_writes
|
|
165
249
|
end
|
|
166
250
|
|
|
167
251
|
def prepared_statements?
|
|
@@ -200,16 +284,16 @@ module ActiveRecord
|
|
|
200
284
|
def lease
|
|
201
285
|
if in_use?
|
|
202
286
|
msg = +"Cannot lease connection, "
|
|
203
|
-
if @owner ==
|
|
287
|
+
if @owner == ActiveSupport::IsolatedExecutionState.context
|
|
204
288
|
msg << "it is already leased by the current thread."
|
|
205
289
|
else
|
|
206
290
|
msg << "it is already in use by a different thread: #{@owner}. " \
|
|
207
|
-
"Current thread: #{
|
|
291
|
+
"Current thread: #{ActiveSupport::IsolatedExecutionState.context}."
|
|
208
292
|
end
|
|
209
293
|
raise ActiveRecordError, msg
|
|
210
294
|
end
|
|
211
295
|
|
|
212
|
-
@owner =
|
|
296
|
+
@owner = ActiveSupport::IsolatedExecutionState.context
|
|
213
297
|
end
|
|
214
298
|
|
|
215
299
|
def connection_class # :nodoc:
|
|
@@ -229,21 +313,16 @@ module ActiveRecord
|
|
|
229
313
|
end
|
|
230
314
|
|
|
231
315
|
def schema_cache
|
|
232
|
-
@pool.
|
|
233
|
-
end
|
|
234
|
-
|
|
235
|
-
def schema_cache=(cache)
|
|
236
|
-
cache.connection = self
|
|
237
|
-
@pool.set_schema_cache(cache)
|
|
316
|
+
@pool.schema_cache || (@schema_cache ||= BoundSchemaReflection.for_lone_connection(@pool.schema_reflection, self))
|
|
238
317
|
end
|
|
239
318
|
|
|
240
319
|
# this method must only be called while holding connection pool's mutex
|
|
241
320
|
def expire
|
|
242
321
|
if in_use?
|
|
243
|
-
if @owner !=
|
|
322
|
+
if @owner != ActiveSupport::IsolatedExecutionState.context
|
|
244
323
|
raise ActiveRecordError, "Cannot expire connection, " \
|
|
245
324
|
"it is owned by a different thread: #{@owner}. " \
|
|
246
|
-
"Current thread: #{
|
|
325
|
+
"Current thread: #{ActiveSupport::IsolatedExecutionState.context}."
|
|
247
326
|
end
|
|
248
327
|
|
|
249
328
|
@idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
@@ -256,10 +335,10 @@ module ActiveRecord
|
|
|
256
335
|
# this method must only be called while holding connection pool's mutex (and a desire for segfaults)
|
|
257
336
|
def steal! # :nodoc:
|
|
258
337
|
if in_use?
|
|
259
|
-
if @owner !=
|
|
338
|
+
if @owner != ActiveSupport::IsolatedExecutionState.context
|
|
260
339
|
pool.send :remove_connection_from_thread_cache, self, @owner
|
|
261
340
|
|
|
262
|
-
@owner =
|
|
341
|
+
@owner = ActiveSupport::IsolatedExecutionState.context
|
|
263
342
|
end
|
|
264
343
|
else
|
|
265
344
|
raise ActiveRecordError, "Cannot steal connection, it is not currently leased."
|
|
@@ -272,6 +351,13 @@ module ActiveRecord
|
|
|
272
351
|
Process.clock_gettime(Process::CLOCK_MONOTONIC) - @idle_since
|
|
273
352
|
end
|
|
274
353
|
|
|
354
|
+
# Seconds since this connection last communicated with the server
|
|
355
|
+
def seconds_since_last_activity # :nodoc:
|
|
356
|
+
if @raw_connection && @last_activity
|
|
357
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC) - @last_activity
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
|
|
275
361
|
def unprepared_statement
|
|
276
362
|
cache = prepared_statements_disabled_cache.add?(object_id) if @prepared_statements
|
|
277
363
|
yield
|
|
@@ -287,7 +373,14 @@ module ActiveRecord
|
|
|
287
373
|
|
|
288
374
|
# Does the database for this adapter exist?
|
|
289
375
|
def self.database_exists?(config)
|
|
290
|
-
|
|
376
|
+
new(config).database_exists?
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
def database_exists?
|
|
380
|
+
connect!
|
|
381
|
+
true
|
|
382
|
+
rescue ActiveRecord::NoDatabaseError
|
|
383
|
+
false
|
|
291
384
|
end
|
|
292
385
|
|
|
293
386
|
# Does this adapter support DDL rollbacks in transactions? That is, would
|
|
@@ -305,6 +398,16 @@ module ActiveRecord
|
|
|
305
398
|
false
|
|
306
399
|
end
|
|
307
400
|
|
|
401
|
+
# Do TransactionRollbackErrors on savepoints affect the parent
|
|
402
|
+
# transaction?
|
|
403
|
+
def savepoint_errors_invalidate_transactions?
|
|
404
|
+
false
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
def supports_restart_db_transaction?
|
|
408
|
+
false
|
|
409
|
+
end
|
|
410
|
+
|
|
308
411
|
# Does this adapter support application-enforced advisory locking?
|
|
309
412
|
def supports_advisory_locks?
|
|
310
413
|
false
|
|
@@ -331,6 +434,11 @@ module ActiveRecord
|
|
|
331
434
|
false
|
|
332
435
|
end
|
|
333
436
|
|
|
437
|
+
# Does this adapter support including non-key columns?
|
|
438
|
+
def supports_index_include?
|
|
439
|
+
false
|
|
440
|
+
end
|
|
441
|
+
|
|
334
442
|
# Does this adapter support expression indices?
|
|
335
443
|
def supports_expression_index?
|
|
336
444
|
false
|
|
@@ -377,6 +485,16 @@ module ActiveRecord
|
|
|
377
485
|
false
|
|
378
486
|
end
|
|
379
487
|
|
|
488
|
+
# Does this adapter support creating exclusion constraints?
|
|
489
|
+
def supports_exclusion_constraints?
|
|
490
|
+
false
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
# Does this adapter support creating unique constraints?
|
|
494
|
+
def supports_unique_constraints?
|
|
495
|
+
false
|
|
496
|
+
end
|
|
497
|
+
|
|
380
498
|
# Does this adapter support views?
|
|
381
499
|
def supports_views?
|
|
382
500
|
false
|
|
@@ -392,7 +510,7 @@ module ActiveRecord
|
|
|
392
510
|
false
|
|
393
511
|
end
|
|
394
512
|
|
|
395
|
-
# Does this adapter support
|
|
513
|
+
# Does this adapter support JSON data type?
|
|
396
514
|
def supports_json?
|
|
397
515
|
false
|
|
398
516
|
end
|
|
@@ -450,23 +568,47 @@ module ActiveRecord
|
|
|
450
568
|
true
|
|
451
569
|
end
|
|
452
570
|
|
|
571
|
+
def supports_nulls_not_distinct?
|
|
572
|
+
false
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
def return_value_after_insert?(column) # :nodoc:
|
|
576
|
+
column.auto_populated?
|
|
577
|
+
end
|
|
578
|
+
|
|
453
579
|
def async_enabled? # :nodoc:
|
|
454
580
|
supports_concurrent_connections? &&
|
|
455
581
|
!ActiveRecord.async_query_executor.nil? && !pool.async_executor.nil?
|
|
456
582
|
end
|
|
457
583
|
|
|
458
584
|
# This is meant to be implemented by the adapters that support extensions
|
|
459
|
-
def disable_extension(name)
|
|
585
|
+
def disable_extension(name, **)
|
|
460
586
|
end
|
|
461
587
|
|
|
462
588
|
# This is meant to be implemented by the adapters that support extensions
|
|
463
|
-
def enable_extension(name)
|
|
589
|
+
def enable_extension(name, **)
|
|
464
590
|
end
|
|
465
591
|
|
|
466
592
|
# This is meant to be implemented by the adapters that support custom enum types
|
|
467
593
|
def create_enum(*) # :nodoc:
|
|
468
594
|
end
|
|
469
595
|
|
|
596
|
+
# This is meant to be implemented by the adapters that support custom enum types
|
|
597
|
+
def drop_enum(*) # :nodoc:
|
|
598
|
+
end
|
|
599
|
+
|
|
600
|
+
# This is meant to be implemented by the adapters that support custom enum types
|
|
601
|
+
def rename_enum(*) # :nodoc:
|
|
602
|
+
end
|
|
603
|
+
|
|
604
|
+
# This is meant to be implemented by the adapters that support custom enum types
|
|
605
|
+
def add_enum_value(*) # :nodoc:
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
# This is meant to be implemented by the adapters that support custom enum types
|
|
609
|
+
def rename_enum_value(*) # :nodoc:
|
|
610
|
+
end
|
|
611
|
+
|
|
470
612
|
def advisory_locks_enabled? # :nodoc:
|
|
471
613
|
supports_advisory_locks? && @advisory_locks_enabled
|
|
472
614
|
end
|
|
@@ -503,31 +645,73 @@ module ActiveRecord
|
|
|
503
645
|
end
|
|
504
646
|
|
|
505
647
|
# Override to check all foreign key constraints in a database.
|
|
506
|
-
|
|
507
|
-
|
|
648
|
+
# The adapter should raise a +ActiveRecord::StatementInvalid+ if foreign key
|
|
649
|
+
# constraints are not met.
|
|
650
|
+
def check_all_foreign_keys_valid!
|
|
508
651
|
end
|
|
509
652
|
|
|
510
653
|
# CONNECTION MANAGEMENT ====================================
|
|
511
654
|
|
|
655
|
+
# Checks whether the connection to the database was established. This doesn't
|
|
656
|
+
# include checking whether the database is actually capable of responding, i.e.
|
|
657
|
+
# whether the connection is stale.
|
|
658
|
+
def connected?
|
|
659
|
+
!@raw_connection.nil?
|
|
660
|
+
end
|
|
661
|
+
|
|
512
662
|
# Checks whether the connection to the database is still active. This includes
|
|
513
663
|
# checking whether the database is actually capable of responding, i.e. whether
|
|
514
664
|
# the connection isn't stale.
|
|
515
665
|
def active?
|
|
516
666
|
end
|
|
517
667
|
|
|
518
|
-
# Disconnects from the database if already connected, and establishes a
|
|
519
|
-
#
|
|
520
|
-
#
|
|
521
|
-
def reconnect!
|
|
522
|
-
|
|
523
|
-
|
|
668
|
+
# Disconnects from the database if already connected, and establishes a new
|
|
669
|
+
# connection with the database. Implementors should define private #reconnect
|
|
670
|
+
# instead.
|
|
671
|
+
def reconnect!(restore_transactions: false)
|
|
672
|
+
retries_available = connection_retries
|
|
673
|
+
deadline = retry_deadline && Process.clock_gettime(Process::CLOCK_MONOTONIC) + retry_deadline
|
|
674
|
+
|
|
675
|
+
@lock.synchronize do
|
|
676
|
+
reconnect
|
|
677
|
+
|
|
678
|
+
enable_lazy_transactions!
|
|
679
|
+
@raw_connection_dirty = false
|
|
680
|
+
@last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
681
|
+
@verified = true
|
|
682
|
+
|
|
683
|
+
reset_transaction(restore: restore_transactions) do
|
|
684
|
+
clear_cache!(new_connection: true)
|
|
685
|
+
attempt_configure_connection
|
|
686
|
+
end
|
|
687
|
+
rescue => original_exception
|
|
688
|
+
translated_exception = translate_exception_class(original_exception, nil, nil)
|
|
689
|
+
retry_deadline_exceeded = deadline && deadline < Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
690
|
+
|
|
691
|
+
if !retry_deadline_exceeded && retries_available > 0
|
|
692
|
+
retries_available -= 1
|
|
693
|
+
|
|
694
|
+
if retryable_connection_error?(translated_exception)
|
|
695
|
+
backoff(connection_retries - retries_available)
|
|
696
|
+
retry
|
|
697
|
+
end
|
|
698
|
+
end
|
|
699
|
+
|
|
700
|
+
@last_activity = nil
|
|
701
|
+
@verified = false
|
|
702
|
+
|
|
703
|
+
raise translated_exception
|
|
704
|
+
end
|
|
524
705
|
end
|
|
525
706
|
|
|
526
707
|
# Disconnects from the database if already connected. Otherwise, this
|
|
527
708
|
# method does nothing.
|
|
528
709
|
def disconnect!
|
|
529
|
-
|
|
530
|
-
|
|
710
|
+
@lock.synchronize do
|
|
711
|
+
clear_cache!(new_connection: true)
|
|
712
|
+
reset_transaction
|
|
713
|
+
@raw_connection_dirty = false
|
|
714
|
+
end
|
|
531
715
|
end
|
|
532
716
|
|
|
533
717
|
# Immediately forget this connection ever existed. Unlike disconnect!,
|
|
@@ -538,22 +722,20 @@ module ActiveRecord
|
|
|
538
722
|
# rid of a connection that belonged to its parent.
|
|
539
723
|
def discard!
|
|
540
724
|
# This should be overridden by concrete adapters.
|
|
541
|
-
#
|
|
542
|
-
# Prevent @connection's finalizer from touching the socket, or
|
|
543
|
-
# otherwise communicating with its server, when it is collected.
|
|
544
|
-
if schema_cache.connection == self
|
|
545
|
-
schema_cache.connection = nil
|
|
546
|
-
end
|
|
547
725
|
end
|
|
548
726
|
|
|
549
727
|
# Reset the state of this connection, directing the DBMS to clear
|
|
550
728
|
# transactions and other connection-related server-side state. Usually a
|
|
551
729
|
# database-dependent operation.
|
|
552
730
|
#
|
|
553
|
-
#
|
|
554
|
-
#
|
|
731
|
+
# If a database driver or protocol does not support such a feature,
|
|
732
|
+
# implementors may alias this to #reconnect!. Otherwise, implementors
|
|
733
|
+
# should call super immediately after resetting the connection (and while
|
|
734
|
+
# still holding @lock).
|
|
555
735
|
def reset!
|
|
556
|
-
|
|
736
|
+
clear_cache!(new_connection: true)
|
|
737
|
+
reset_transaction
|
|
738
|
+
attempt_configure_connection
|
|
557
739
|
end
|
|
558
740
|
|
|
559
741
|
# Removes the connection from the pool and disconnect it.
|
|
@@ -563,8 +745,16 @@ module ActiveRecord
|
|
|
563
745
|
end
|
|
564
746
|
|
|
565
747
|
# Clear any caching the database adapter may be doing.
|
|
566
|
-
def clear_cache!
|
|
567
|
-
|
|
748
|
+
def clear_cache!(new_connection: false)
|
|
749
|
+
if @statements
|
|
750
|
+
@lock.synchronize do
|
|
751
|
+
if new_connection
|
|
752
|
+
@statements.reset
|
|
753
|
+
else
|
|
754
|
+
@statements.clear
|
|
755
|
+
end
|
|
756
|
+
end
|
|
757
|
+
end
|
|
568
758
|
end
|
|
569
759
|
|
|
570
760
|
# Returns true if its required to reload the connection between requests for development mode.
|
|
@@ -576,7 +766,32 @@ module ActiveRecord
|
|
|
576
766
|
# This is done under the hood by calling #active?. If the connection
|
|
577
767
|
# is no longer active, then this method will reconnect to the database.
|
|
578
768
|
def verify!
|
|
579
|
-
|
|
769
|
+
unless active?
|
|
770
|
+
@lock.synchronize do
|
|
771
|
+
if @unconfigured_connection
|
|
772
|
+
@raw_connection = @unconfigured_connection
|
|
773
|
+
@unconfigured_connection = nil
|
|
774
|
+
attempt_configure_connection
|
|
775
|
+
@last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
776
|
+
@verified = true
|
|
777
|
+
return
|
|
778
|
+
end
|
|
779
|
+
|
|
780
|
+
reconnect!(restore_transactions: true)
|
|
781
|
+
end
|
|
782
|
+
end
|
|
783
|
+
|
|
784
|
+
@verified = true
|
|
785
|
+
end
|
|
786
|
+
|
|
787
|
+
def connect!
|
|
788
|
+
verify!
|
|
789
|
+
self
|
|
790
|
+
end
|
|
791
|
+
|
|
792
|
+
def clean! # :nodoc:
|
|
793
|
+
@raw_connection_dirty = false
|
|
794
|
+
@verified = nil
|
|
580
795
|
end
|
|
581
796
|
|
|
582
797
|
# Provides access to the underlying database driver for this adapter. For
|
|
@@ -590,8 +805,11 @@ module ActiveRecord
|
|
|
590
805
|
# this client. If that is the case, generally you'll want to invalidate
|
|
591
806
|
# the query cache using +ActiveRecord::Base.clear_query_cache+.
|
|
592
807
|
def raw_connection
|
|
593
|
-
|
|
594
|
-
|
|
808
|
+
with_raw_connection do |conn|
|
|
809
|
+
disable_lazy_transactions!
|
|
810
|
+
@raw_connection_dirty = true
|
|
811
|
+
conn
|
|
812
|
+
end
|
|
595
813
|
end
|
|
596
814
|
|
|
597
815
|
def default_uniqueness_comparison(attribute, value) # :nodoc:
|
|
@@ -643,7 +861,7 @@ module ActiveRecord
|
|
|
643
861
|
end
|
|
644
862
|
|
|
645
863
|
def database_version # :nodoc:
|
|
646
|
-
|
|
864
|
+
pool.server_version(self)
|
|
647
865
|
end
|
|
648
866
|
|
|
649
867
|
def check_version # :nodoc:
|
|
@@ -654,10 +872,25 @@ module ActiveRecord
|
|
|
654
872
|
# numbered migration that has been executed, or 0 if no schema
|
|
655
873
|
# information is present / the database is empty.
|
|
656
874
|
def schema_version
|
|
657
|
-
migration_context.current_version
|
|
875
|
+
pool.migration_context.current_version
|
|
658
876
|
end
|
|
659
877
|
|
|
660
878
|
class << self
|
|
879
|
+
def register_class_with_precision(mapping, key, klass, **kwargs) # :nodoc:
|
|
880
|
+
mapping.register_type(key) do |*args|
|
|
881
|
+
precision = extract_precision(args.last)
|
|
882
|
+
klass.new(precision: precision, **kwargs)
|
|
883
|
+
end
|
|
884
|
+
end
|
|
885
|
+
|
|
886
|
+
def extended_type_map(default_timezone:) # :nodoc:
|
|
887
|
+
Type::TypeMap.new(self::TYPE_MAP).tap do |m|
|
|
888
|
+
register_class_with_precision m, %r(\A[^\(]*time)i, Type::Time, timezone: default_timezone
|
|
889
|
+
register_class_with_precision m, %r(\A[^\(]*datetime)i, Type::DateTime, timezone: default_timezone
|
|
890
|
+
m.alias_type %r(\A[^\(]*timestamp)i, "datetime"
|
|
891
|
+
end
|
|
892
|
+
end
|
|
893
|
+
|
|
661
894
|
private
|
|
662
895
|
def initialize_type_map(m)
|
|
663
896
|
register_class_with_limit m, %r(boolean)i, Type::Boolean
|
|
@@ -699,13 +932,6 @@ module ActiveRecord
|
|
|
699
932
|
end
|
|
700
933
|
end
|
|
701
934
|
|
|
702
|
-
def register_class_with_precision(mapping, key, klass)
|
|
703
|
-
mapping.register_type(key) do |*args|
|
|
704
|
-
precision = extract_precision(args.last)
|
|
705
|
-
klass.new(precision: precision)
|
|
706
|
-
end
|
|
707
|
-
end
|
|
708
|
-
|
|
709
935
|
def extract_scale(sql_type)
|
|
710
936
|
case sql_type
|
|
711
937
|
when /\((\d+)\)/ then 0
|
|
@@ -723,10 +949,182 @@ module ActiveRecord
|
|
|
723
949
|
end
|
|
724
950
|
|
|
725
951
|
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
|
|
952
|
+
EXTENDED_TYPE_MAPS = Concurrent::Map.new
|
|
726
953
|
|
|
727
954
|
private
|
|
955
|
+
def reconnect_can_restore_state?
|
|
956
|
+
transaction_manager.restorable? && !@raw_connection_dirty
|
|
957
|
+
end
|
|
958
|
+
|
|
959
|
+
# Lock the monitor, ensure we're properly connected and
|
|
960
|
+
# transactions are materialized, and then yield the underlying
|
|
961
|
+
# raw connection object.
|
|
962
|
+
#
|
|
963
|
+
# If +allow_retry+ is true, a connection-related exception will
|
|
964
|
+
# cause an automatic reconnect and re-run of the block, up to
|
|
965
|
+
# the connection's configured +connection_retries+ setting
|
|
966
|
+
# and the configured +retry_deadline+ limit. (Note that when
|
|
967
|
+
# +allow_retry+ is true, it's possible to return without having marked
|
|
968
|
+
# the connection as verified. If the block is guaranteed to exercise the
|
|
969
|
+
# connection, consider calling `verified!` to avoid needless
|
|
970
|
+
# verification queries in subsequent calls.)
|
|
971
|
+
#
|
|
972
|
+
# If +materialize_transactions+ is false, the block will be run without
|
|
973
|
+
# ensuring virtual transactions have been materialized in the DB
|
|
974
|
+
# server's state. The active transaction will also remain clean
|
|
975
|
+
# (if it is not already dirty), meaning it's able to be restored
|
|
976
|
+
# by reconnecting and opening an equivalent-depth set of new
|
|
977
|
+
# transactions. This should only be used by transaction control
|
|
978
|
+
# methods, and internal transaction-agnostic queries.
|
|
979
|
+
#
|
|
980
|
+
###
|
|
981
|
+
#
|
|
982
|
+
# It's not the primary use case, so not something to optimize
|
|
983
|
+
# for, but note that this method does need to be re-entrant:
|
|
984
|
+
# +materialize_transactions+ will re-enter if it has work to do,
|
|
985
|
+
# and the yield block can also do so under some circumstances.
|
|
986
|
+
#
|
|
987
|
+
# In the latter case, we really ought to guarantee the inner
|
|
988
|
+
# call will not reconnect (which would interfere with the
|
|
989
|
+
# still-yielded connection in the outer block), but we currently
|
|
990
|
+
# provide no special enforcement there.
|
|
991
|
+
#
|
|
992
|
+
def with_raw_connection(allow_retry: false, materialize_transactions: true)
|
|
993
|
+
@lock.synchronize do
|
|
994
|
+
connect! if @raw_connection.nil? && reconnect_can_restore_state?
|
|
995
|
+
|
|
996
|
+
self.materialize_transactions if materialize_transactions
|
|
997
|
+
|
|
998
|
+
retries_available = allow_retry ? connection_retries : 0
|
|
999
|
+
deadline = retry_deadline && Process.clock_gettime(Process::CLOCK_MONOTONIC) + retry_deadline
|
|
1000
|
+
reconnectable = reconnect_can_restore_state?
|
|
1001
|
+
|
|
1002
|
+
if @verified
|
|
1003
|
+
# Cool, we're confident the connection's ready to use. (Note this might have
|
|
1004
|
+
# become true during the above #materialize_transactions.)
|
|
1005
|
+
elsif (last_activity = seconds_since_last_activity) && last_activity < verify_timeout
|
|
1006
|
+
# We haven't actually verified the connection since we acquired it, but it
|
|
1007
|
+
# has been used very recently. We're going to assume it's still okay.
|
|
1008
|
+
elsif reconnectable
|
|
1009
|
+
if allow_retry
|
|
1010
|
+
# Not sure about the connection yet, but if anything goes wrong we can
|
|
1011
|
+
# just reconnect and re-run our query
|
|
1012
|
+
else
|
|
1013
|
+
# We can reconnect if needed, but we don't trust the upcoming query to be
|
|
1014
|
+
# safely re-runnable: let's verify the connection to be sure
|
|
1015
|
+
verify!
|
|
1016
|
+
end
|
|
1017
|
+
else
|
|
1018
|
+
# We don't know whether the connection is okay, but it also doesn't matter:
|
|
1019
|
+
# we wouldn't be able to reconnect anyway. We're just going to run our query
|
|
1020
|
+
# and hope for the best.
|
|
1021
|
+
end
|
|
1022
|
+
|
|
1023
|
+
begin
|
|
1024
|
+
yield @raw_connection
|
|
1025
|
+
rescue => original_exception
|
|
1026
|
+
translated_exception = translate_exception_class(original_exception, nil, nil)
|
|
1027
|
+
invalidate_transaction(translated_exception)
|
|
1028
|
+
retry_deadline_exceeded = deadline && deadline < Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
1029
|
+
|
|
1030
|
+
if !retry_deadline_exceeded && retries_available > 0
|
|
1031
|
+
retries_available -= 1
|
|
1032
|
+
|
|
1033
|
+
if retryable_query_error?(translated_exception)
|
|
1034
|
+
backoff(connection_retries - retries_available)
|
|
1035
|
+
retry
|
|
1036
|
+
elsif reconnectable && retryable_connection_error?(translated_exception)
|
|
1037
|
+
reconnect!(restore_transactions: true)
|
|
1038
|
+
# Only allowed to reconnect once, because reconnect! has its own retry
|
|
1039
|
+
# loop
|
|
1040
|
+
reconnectable = false
|
|
1041
|
+
retry
|
|
1042
|
+
end
|
|
1043
|
+
end
|
|
1044
|
+
|
|
1045
|
+
unless retryable_query_error?(translated_exception)
|
|
1046
|
+
# Barring a known-retryable error inside the query (regardless of
|
|
1047
|
+
# whether we were in a _position_ to retry it), we should infer that
|
|
1048
|
+
# there's likely a real problem with the connection.
|
|
1049
|
+
@last_activity = nil
|
|
1050
|
+
@verified = false
|
|
1051
|
+
end
|
|
1052
|
+
|
|
1053
|
+
raise translated_exception
|
|
1054
|
+
ensure
|
|
1055
|
+
dirty_current_transaction if materialize_transactions
|
|
1056
|
+
end
|
|
1057
|
+
end
|
|
1058
|
+
end
|
|
1059
|
+
|
|
1060
|
+
# Mark the connection as verified. Call this inside a
|
|
1061
|
+
# `with_raw_connection` block only when the block is guaranteed to
|
|
1062
|
+
# exercise the raw connection.
|
|
1063
|
+
def verified!
|
|
1064
|
+
@last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
1065
|
+
@verified = true
|
|
1066
|
+
end
|
|
1067
|
+
|
|
1068
|
+
def retryable_connection_error?(exception)
|
|
1069
|
+
exception.is_a?(ConnectionNotEstablished) || exception.is_a?(ConnectionFailed)
|
|
1070
|
+
end
|
|
1071
|
+
|
|
1072
|
+
def invalidate_transaction(exception)
|
|
1073
|
+
return unless exception.is_a?(TransactionRollbackError)
|
|
1074
|
+
return unless savepoint_errors_invalidate_transactions?
|
|
1075
|
+
|
|
1076
|
+
current_transaction.invalidate!
|
|
1077
|
+
end
|
|
1078
|
+
|
|
1079
|
+
def retryable_query_error?(exception)
|
|
1080
|
+
# We definitely can't retry if we were inside an invalidated transaction.
|
|
1081
|
+
return false if current_transaction.invalidated?
|
|
1082
|
+
|
|
1083
|
+
exception.is_a?(Deadlocked) || exception.is_a?(LockWaitTimeout)
|
|
1084
|
+
end
|
|
1085
|
+
|
|
1086
|
+
def backoff(counter)
|
|
1087
|
+
sleep 0.1 * counter
|
|
1088
|
+
end
|
|
1089
|
+
|
|
1090
|
+
def reconnect
|
|
1091
|
+
raise NotImplementedError
|
|
1092
|
+
end
|
|
1093
|
+
|
|
1094
|
+
# Returns a raw connection for internal use with methods that are known
|
|
1095
|
+
# to both be thread-safe and not rely upon actual server communication.
|
|
1096
|
+
# This is useful for e.g. string escaping methods.
|
|
1097
|
+
def any_raw_connection
|
|
1098
|
+
@raw_connection || valid_raw_connection
|
|
1099
|
+
end
|
|
1100
|
+
|
|
1101
|
+
# Similar to any_raw_connection, but ensures it is validated and
|
|
1102
|
+
# connected. Any method called on this result still needs to be
|
|
1103
|
+
# independently thread-safe, so it probably shouldn't talk to the
|
|
1104
|
+
# server... but some drivers fail if they know the connection has gone
|
|
1105
|
+
# away.
|
|
1106
|
+
def valid_raw_connection
|
|
1107
|
+
(@verified && @raw_connection) ||
|
|
1108
|
+
# `allow_retry: false`, to force verification: the block won't
|
|
1109
|
+
# raise, so a retry wouldn't help us get the valid connection we
|
|
1110
|
+
# need.
|
|
1111
|
+
with_raw_connection(allow_retry: false, materialize_transactions: false) { |conn| conn }
|
|
1112
|
+
end
|
|
1113
|
+
|
|
1114
|
+
def extended_type_map_key
|
|
1115
|
+
if @default_timezone
|
|
1116
|
+
{ default_timezone: @default_timezone }
|
|
1117
|
+
end
|
|
1118
|
+
end
|
|
1119
|
+
|
|
728
1120
|
def type_map
|
|
729
|
-
|
|
1121
|
+
if key = extended_type_map_key
|
|
1122
|
+
self.class::EXTENDED_TYPE_MAPS.compute_if_absent(key) do
|
|
1123
|
+
self.class.extended_type_map(**key)
|
|
1124
|
+
end
|
|
1125
|
+
else
|
|
1126
|
+
self.class::TYPE_MAP
|
|
1127
|
+
end
|
|
730
1128
|
end
|
|
731
1129
|
|
|
732
1130
|
def translate_exception_class(e, sql, binds)
|
|
@@ -748,16 +1146,18 @@ module ActiveRecord
|
|
|
748
1146
|
type_casted_binds: type_casted_binds,
|
|
749
1147
|
statement_name: statement_name,
|
|
750
1148
|
async: async,
|
|
751
|
-
connection: self
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
1149
|
+
connection: self,
|
|
1150
|
+
transaction: current_transaction.user_transaction.presence,
|
|
1151
|
+
row_count: 0,
|
|
1152
|
+
&block
|
|
1153
|
+
)
|
|
1154
|
+
rescue ActiveRecord::StatementInvalid => ex
|
|
1155
|
+
raise ex.set_query(sql, binds)
|
|
756
1156
|
end
|
|
757
1157
|
|
|
758
1158
|
def transform_query(sql)
|
|
759
1159
|
ActiveRecord.query_transformers.each do |transformer|
|
|
760
|
-
sql = transformer.call(sql)
|
|
1160
|
+
sql = transformer.call(sql, self)
|
|
761
1161
|
end
|
|
762
1162
|
sql
|
|
763
1163
|
end
|
|
@@ -765,10 +1165,10 @@ module ActiveRecord
|
|
|
765
1165
|
def translate_exception(exception, message:, sql:, binds:)
|
|
766
1166
|
# override in derived class
|
|
767
1167
|
case exception
|
|
768
|
-
when RuntimeError
|
|
1168
|
+
when RuntimeError, ActiveRecord::ActiveRecordError
|
|
769
1169
|
exception
|
|
770
1170
|
else
|
|
771
|
-
ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds)
|
|
1171
|
+
ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
772
1172
|
end
|
|
773
1173
|
end
|
|
774
1174
|
|
|
@@ -812,9 +1212,37 @@ module ActiveRecord
|
|
|
812
1212
|
#
|
|
813
1213
|
# This is an internal hook to make possible connection adapters to build
|
|
814
1214
|
# custom result objects with connection-specific data.
|
|
815
|
-
def build_result(columns:, rows:, column_types:
|
|
1215
|
+
def build_result(columns:, rows:, column_types: nil)
|
|
816
1216
|
ActiveRecord::Result.new(columns, rows, column_types)
|
|
817
1217
|
end
|
|
1218
|
+
|
|
1219
|
+
# Perform any necessary initialization upon the newly-established
|
|
1220
|
+
# @raw_connection -- this is the place to modify the adapter's
|
|
1221
|
+
# connection settings, run queries to configure any application-global
|
|
1222
|
+
# "session" variables, etc.
|
|
1223
|
+
#
|
|
1224
|
+
# Implementations may assume this method will only be called while
|
|
1225
|
+
# holding @lock (or from #initialize).
|
|
1226
|
+
def configure_connection
|
|
1227
|
+
check_version
|
|
1228
|
+
end
|
|
1229
|
+
|
|
1230
|
+
def attempt_configure_connection
|
|
1231
|
+
configure_connection
|
|
1232
|
+
rescue Exception # Need to handle things such as Timeout::ExitException
|
|
1233
|
+
disconnect!
|
|
1234
|
+
raise
|
|
1235
|
+
end
|
|
1236
|
+
|
|
1237
|
+
def default_prepared_statements
|
|
1238
|
+
true
|
|
1239
|
+
end
|
|
1240
|
+
|
|
1241
|
+
def warning_ignored?(warning)
|
|
1242
|
+
ActiveRecord.db_warnings_ignore.any? do |warning_matcher|
|
|
1243
|
+
warning.message.match?(warning_matcher) || warning.code.to_s.match?(warning_matcher)
|
|
1244
|
+
end
|
|
1245
|
+
end
|
|
818
1246
|
end
|
|
819
1247
|
end
|
|
820
1248
|
end
|