activerecord 7.0.8.6 → 7.2.2.1
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 +631 -1939
- data/MIT-LICENSE +1 -1
- data/README.rdoc +29 -29
- 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 +25 -19
- 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 +23 -8
- 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 +26 -14
- 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 +30 -27
- 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 +148 -33
- data/lib/active_record/attributes.rb +64 -50
- data/lib/active_record/autosave_association.rb +69 -37
- data/lib/active_record/base.rb +9 -5
- 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 +323 -88
- 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 +217 -63
- 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 +137 -11
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +307 -129
- data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
- data/lib/active_record/connection_adapters/abstract_adapter.rb +510 -111
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +278 -125
- 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 +53 -54
- 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 +101 -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 +151 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +370 -63
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +367 -201
- 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 +45 -46
- 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 +50 -8
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +290 -110
- 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 +96 -104
- data/lib/active_record/core.rb +251 -176
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -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 +39 -10
- 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 +45 -21
- data/lib/active_record/encryption/encrypted_attribute_type.rb +47 -12
- data/lib/active_record/encryption/encryptor.rb +18 -3
- 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 +129 -28
- data/lib/active_record/errors.rb +151 -31
- 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 +29 -8
- 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 +234 -117
- 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 +92 -52
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +33 -8
- data/lib/active_record/railtie.rb +129 -85
- data/lib/active_record/railties/controller_runtime.rb +22 -7
- data/lib/active_record/railties/databases.rake +145 -154
- 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 +250 -93
- data/lib/active_record/relation/delegation.rb +30 -19
- data/lib/active_record/relation/finder_methods.rb +93 -18
- 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 +18 -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 +2 -1
- data/lib/active_record/relation/query_methods.rb +576 -107
- 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 +7 -19
- data/lib/active_record/relation.rb +580 -90
- 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 +63 -14
- 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 +27 -6
- 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 +16 -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 +106 -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 +2 -0
- 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/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/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.rb +6 -2
- data/lib/arel/predications.rb +3 -1
- data/lib/arel/select_manager.rb +1 -1
- data/lib/arel/table.rb +9 -5
- data/lib/arel/tree_manager.rb +8 -3
- data/lib/arel/update_manager.rb +2 -1
- data/lib/arel/visitors/dot.rb +1 -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 +112 -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 -14
- 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,18 @@ 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
|
43
46
|
alias :in_use? :owner
|
44
47
|
|
48
|
+
def pool=(value)
|
49
|
+
return if value.eql?(@pool)
|
50
|
+
@schema_cache = nil
|
51
|
+
@pool = value
|
52
|
+
end
|
53
|
+
|
45
54
|
set_callback :checkin, :after, :enable_lazy_transactions!
|
46
55
|
|
47
56
|
def self.type_cast_config_to_integer(config)
|
@@ -62,6 +71,16 @@ module ActiveRecord
|
|
62
71
|
end
|
63
72
|
end
|
64
73
|
|
74
|
+
def self.validate_default_timezone(config)
|
75
|
+
case config
|
76
|
+
when nil
|
77
|
+
when "utc", "local"
|
78
|
+
config.to_sym
|
79
|
+
else
|
80
|
+
raise ArgumentError, "default_timezone must be either 'utc' or 'local'"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
65
84
|
DEFAULT_READ_QUERY = [:begin, :commit, :explain, :release, :rollback, :savepoint, :select, :with] # :nodoc:
|
66
85
|
private_constant :DEFAULT_READ_QUERY
|
67
86
|
|
@@ -71,27 +90,105 @@ module ActiveRecord
|
|
71
90
|
/\A(?:[(\s]|#{COMMENT_REGEX})*#{Regexp.union(*parts)}/
|
72
91
|
end
|
73
92
|
|
74
|
-
def
|
93
|
+
def self.find_cmd_and_exec(commands, *args) # :doc:
|
94
|
+
commands = Array(commands)
|
95
|
+
|
96
|
+
dirs_on_path = ENV["PATH"].to_s.split(File::PATH_SEPARATOR)
|
97
|
+
unless (ext = RbConfig::CONFIG["EXEEXT"]).empty?
|
98
|
+
commands = commands.map { |cmd| "#{cmd}#{ext}" }
|
99
|
+
end
|
100
|
+
|
101
|
+
full_path_command = nil
|
102
|
+
found = commands.detect do |cmd|
|
103
|
+
dirs_on_path.detect do |path|
|
104
|
+
full_path_command = File.join(path, cmd)
|
105
|
+
begin
|
106
|
+
stat = File.stat(full_path_command)
|
107
|
+
rescue SystemCallError
|
108
|
+
else
|
109
|
+
stat.file? && stat.executable?
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
if found
|
115
|
+
exec full_path_command, *args
|
116
|
+
else
|
117
|
+
abort("Couldn't find database client: #{commands.join(', ')}. Check your $PATH and try again.")
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Opens a database console session.
|
122
|
+
def self.dbconsole(config, options = {})
|
123
|
+
raise NotImplementedError
|
124
|
+
end
|
125
|
+
|
126
|
+
def initialize(config_or_deprecated_connection, deprecated_logger = nil, deprecated_connection_options = nil, deprecated_config = nil) # :nodoc:
|
75
127
|
super()
|
76
128
|
|
77
|
-
@
|
78
|
-
@
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
129
|
+
@raw_connection = nil
|
130
|
+
@unconfigured_connection = nil
|
131
|
+
|
132
|
+
if config_or_deprecated_connection.is_a?(Hash)
|
133
|
+
@config = config_or_deprecated_connection.symbolize_keys
|
134
|
+
@logger = ActiveRecord::Base.logger
|
135
|
+
|
136
|
+
if deprecated_logger || deprecated_connection_options || deprecated_config
|
137
|
+
raise ArgumentError, "when initializing an Active Record adapter with a config hash, that should be the only argument"
|
138
|
+
end
|
139
|
+
else
|
140
|
+
# Soft-deprecated for now; we'll probably warn in future.
|
141
|
+
|
142
|
+
@unconfigured_connection = config_or_deprecated_connection
|
143
|
+
@logger = deprecated_logger || ActiveRecord::Base.logger
|
144
|
+
if deprecated_config
|
145
|
+
@config = (deprecated_config || {}).symbolize_keys
|
146
|
+
@connection_parameters = deprecated_connection_options
|
147
|
+
else
|
148
|
+
@config = (deprecated_connection_options || {}).symbolize_keys
|
149
|
+
@connection_parameters = nil
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
@owner = nil
|
154
|
+
@instrumenter = ActiveSupport::Notifications.instrumenter
|
155
|
+
@pool = ActiveRecord::ConnectionAdapters::NullPool.new
|
156
|
+
@idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
84
157
|
@visitor = arel_visitor
|
85
158
|
@statements = build_statement_pool
|
86
|
-
|
159
|
+
self.lock_thread = nil
|
87
160
|
|
88
|
-
@prepared_statements = self.class.type_cast_config_to_boolean(
|
89
|
-
config.fetch(:prepared_statements
|
161
|
+
@prepared_statements = !ActiveRecord.disable_prepared_statements && self.class.type_cast_config_to_boolean(
|
162
|
+
@config.fetch(:prepared_statements) { default_prepared_statements }
|
90
163
|
)
|
91
164
|
|
92
165
|
@advisory_locks_enabled = self.class.type_cast_config_to_boolean(
|
93
|
-
config.fetch(:advisory_locks, true)
|
166
|
+
@config.fetch(:advisory_locks, true)
|
94
167
|
)
|
168
|
+
|
169
|
+
@default_timezone = self.class.validate_default_timezone(@config[:default_timezone])
|
170
|
+
|
171
|
+
@raw_connection_dirty = false
|
172
|
+
@verified = false
|
173
|
+
end
|
174
|
+
|
175
|
+
def inspect # :nodoc:
|
176
|
+
name_field = " name=#{pool.db_config.name.inspect}" unless pool.db_config.name == "primary"
|
177
|
+
shard_field = " shard=#{shard.inspect}" unless shard == :default
|
178
|
+
|
179
|
+
"#<#{self.class.name}:#{'%#016x' % (object_id << 1)} env_name=#{pool.db_config.env_name.inspect}#{name_field} role=#{role.inspect}#{shard_field}>"
|
180
|
+
end
|
181
|
+
|
182
|
+
def lock_thread=(lock_thread) # :nodoc:
|
183
|
+
@lock =
|
184
|
+
case lock_thread
|
185
|
+
when Thread
|
186
|
+
ActiveSupport::Concurrency::ThreadLoadInterlockAwareMonitor.new
|
187
|
+
when Fiber
|
188
|
+
ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
|
189
|
+
else
|
190
|
+
ActiveSupport::Concurrency::NullLock
|
191
|
+
end
|
95
192
|
end
|
96
193
|
|
97
194
|
EXCEPTION_NEVER = { Exception => :never }.freeze # :nodoc:
|
@@ -117,53 +214,33 @@ module ActiveRecord
|
|
117
214
|
@config[:replica] || false
|
118
215
|
end
|
119
216
|
|
120
|
-
def
|
121
|
-
@config
|
217
|
+
def connection_retries
|
218
|
+
(@config[:connection_retries] || 1).to_i
|
219
|
+
end
|
220
|
+
|
221
|
+
def retry_deadline
|
222
|
+
if @config[:retry_deadline]
|
223
|
+
@config[:retry_deadline].to_f
|
224
|
+
else
|
225
|
+
nil
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def default_timezone
|
230
|
+
@default_timezone || ActiveRecord.default_timezone
|
122
231
|
end
|
123
232
|
|
124
233
|
# Determines whether writes are currently being prevented.
|
125
234
|
#
|
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+.
|
235
|
+
# Returns true if the connection is a replica or returns
|
236
|
+
# the value of +current_preventing_writes+.
|
133
237
|
def preventing_writes?
|
134
238
|
return true if replica?
|
135
|
-
return ActiveRecord::Base.connection_handler.prevent_writes if ActiveRecord.legacy_connection_handling
|
136
239
|
return false if connection_class.nil?
|
137
240
|
|
138
241
|
connection_class.current_preventing_writes
|
139
242
|
end
|
140
243
|
|
141
|
-
def migrations_paths # :nodoc:
|
142
|
-
@config[:migrations_paths] || Migrator.migrations_paths
|
143
|
-
end
|
144
|
-
|
145
|
-
def migration_context # :nodoc:
|
146
|
-
MigrationContext.new(migrations_paths, schema_migration)
|
147
|
-
end
|
148
|
-
|
149
|
-
def schema_migration # :nodoc:
|
150
|
-
@schema_migration ||= begin
|
151
|
-
conn = self
|
152
|
-
spec_name = conn.pool.pool_config.connection_specification_name
|
153
|
-
|
154
|
-
return ActiveRecord::SchemaMigration if spec_name == "ActiveRecord::Base"
|
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 }
|
161
|
-
|
162
|
-
self.connection_specification_name = spec_name
|
163
|
-
end
|
164
|
-
end
|
165
|
-
end
|
166
|
-
|
167
244
|
def prepared_statements?
|
168
245
|
@prepared_statements && !prepared_statements_disabled_cache.include?(object_id)
|
169
246
|
end
|
@@ -200,16 +277,16 @@ module ActiveRecord
|
|
200
277
|
def lease
|
201
278
|
if in_use?
|
202
279
|
msg = +"Cannot lease connection, "
|
203
|
-
if @owner ==
|
280
|
+
if @owner == ActiveSupport::IsolatedExecutionState.context
|
204
281
|
msg << "it is already leased by the current thread."
|
205
282
|
else
|
206
283
|
msg << "it is already in use by a different thread: #{@owner}. " \
|
207
|
-
"Current thread: #{
|
284
|
+
"Current thread: #{ActiveSupport::IsolatedExecutionState.context}."
|
208
285
|
end
|
209
286
|
raise ActiveRecordError, msg
|
210
287
|
end
|
211
288
|
|
212
|
-
@owner =
|
289
|
+
@owner = ActiveSupport::IsolatedExecutionState.context
|
213
290
|
end
|
214
291
|
|
215
292
|
def connection_class # :nodoc:
|
@@ -229,21 +306,16 @@ module ActiveRecord
|
|
229
306
|
end
|
230
307
|
|
231
308
|
def schema_cache
|
232
|
-
@pool.
|
233
|
-
end
|
234
|
-
|
235
|
-
def schema_cache=(cache)
|
236
|
-
cache.connection = self
|
237
|
-
@pool.set_schema_cache(cache)
|
309
|
+
@pool.schema_cache || (@schema_cache ||= BoundSchemaReflection.for_lone_connection(@pool.schema_reflection, self))
|
238
310
|
end
|
239
311
|
|
240
312
|
# this method must only be called while holding connection pool's mutex
|
241
313
|
def expire
|
242
314
|
if in_use?
|
243
|
-
if @owner !=
|
315
|
+
if @owner != ActiveSupport::IsolatedExecutionState.context
|
244
316
|
raise ActiveRecordError, "Cannot expire connection, " \
|
245
317
|
"it is owned by a different thread: #{@owner}. " \
|
246
|
-
"Current thread: #{
|
318
|
+
"Current thread: #{ActiveSupport::IsolatedExecutionState.context}."
|
247
319
|
end
|
248
320
|
|
249
321
|
@idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
@@ -256,10 +328,10 @@ module ActiveRecord
|
|
256
328
|
# this method must only be called while holding connection pool's mutex (and a desire for segfaults)
|
257
329
|
def steal! # :nodoc:
|
258
330
|
if in_use?
|
259
|
-
if @owner !=
|
331
|
+
if @owner != ActiveSupport::IsolatedExecutionState.context
|
260
332
|
pool.send :remove_connection_from_thread_cache, self, @owner
|
261
333
|
|
262
|
-
@owner =
|
334
|
+
@owner = ActiveSupport::IsolatedExecutionState.context
|
263
335
|
end
|
264
336
|
else
|
265
337
|
raise ActiveRecordError, "Cannot steal connection, it is not currently leased."
|
@@ -287,7 +359,14 @@ module ActiveRecord
|
|
287
359
|
|
288
360
|
# Does the database for this adapter exist?
|
289
361
|
def self.database_exists?(config)
|
290
|
-
|
362
|
+
new(config).database_exists?
|
363
|
+
end
|
364
|
+
|
365
|
+
def database_exists?
|
366
|
+
connect!
|
367
|
+
true
|
368
|
+
rescue ActiveRecord::NoDatabaseError
|
369
|
+
false
|
291
370
|
end
|
292
371
|
|
293
372
|
# Does this adapter support DDL rollbacks in transactions? That is, would
|
@@ -305,6 +384,16 @@ module ActiveRecord
|
|
305
384
|
false
|
306
385
|
end
|
307
386
|
|
387
|
+
# Do TransactionRollbackErrors on savepoints affect the parent
|
388
|
+
# transaction?
|
389
|
+
def savepoint_errors_invalidate_transactions?
|
390
|
+
false
|
391
|
+
end
|
392
|
+
|
393
|
+
def supports_restart_db_transaction?
|
394
|
+
false
|
395
|
+
end
|
396
|
+
|
308
397
|
# Does this adapter support application-enforced advisory locking?
|
309
398
|
def supports_advisory_locks?
|
310
399
|
false
|
@@ -331,6 +420,11 @@ module ActiveRecord
|
|
331
420
|
false
|
332
421
|
end
|
333
422
|
|
423
|
+
# Does this adapter support including non-key columns?
|
424
|
+
def supports_index_include?
|
425
|
+
false
|
426
|
+
end
|
427
|
+
|
334
428
|
# Does this adapter support expression indices?
|
335
429
|
def supports_expression_index?
|
336
430
|
false
|
@@ -377,6 +471,16 @@ module ActiveRecord
|
|
377
471
|
false
|
378
472
|
end
|
379
473
|
|
474
|
+
# Does this adapter support creating exclusion constraints?
|
475
|
+
def supports_exclusion_constraints?
|
476
|
+
false
|
477
|
+
end
|
478
|
+
|
479
|
+
# Does this adapter support creating unique constraints?
|
480
|
+
def supports_unique_constraints?
|
481
|
+
false
|
482
|
+
end
|
483
|
+
|
380
484
|
# Does this adapter support views?
|
381
485
|
def supports_views?
|
382
486
|
false
|
@@ -392,7 +496,7 @@ module ActiveRecord
|
|
392
496
|
false
|
393
497
|
end
|
394
498
|
|
395
|
-
# Does this adapter support
|
499
|
+
# Does this adapter support JSON data type?
|
396
500
|
def supports_json?
|
397
501
|
false
|
398
502
|
end
|
@@ -450,23 +554,47 @@ module ActiveRecord
|
|
450
554
|
true
|
451
555
|
end
|
452
556
|
|
557
|
+
def supports_nulls_not_distinct?
|
558
|
+
false
|
559
|
+
end
|
560
|
+
|
561
|
+
def return_value_after_insert?(column) # :nodoc:
|
562
|
+
column.auto_populated?
|
563
|
+
end
|
564
|
+
|
453
565
|
def async_enabled? # :nodoc:
|
454
566
|
supports_concurrent_connections? &&
|
455
567
|
!ActiveRecord.async_query_executor.nil? && !pool.async_executor.nil?
|
456
568
|
end
|
457
569
|
|
458
570
|
# This is meant to be implemented by the adapters that support extensions
|
459
|
-
def disable_extension(name)
|
571
|
+
def disable_extension(name, **)
|
460
572
|
end
|
461
573
|
|
462
574
|
# This is meant to be implemented by the adapters that support extensions
|
463
|
-
def enable_extension(name)
|
575
|
+
def enable_extension(name, **)
|
464
576
|
end
|
465
577
|
|
466
578
|
# This is meant to be implemented by the adapters that support custom enum types
|
467
579
|
def create_enum(*) # :nodoc:
|
468
580
|
end
|
469
581
|
|
582
|
+
# This is meant to be implemented by the adapters that support custom enum types
|
583
|
+
def drop_enum(*) # :nodoc:
|
584
|
+
end
|
585
|
+
|
586
|
+
# This is meant to be implemented by the adapters that support custom enum types
|
587
|
+
def rename_enum(*) # :nodoc:
|
588
|
+
end
|
589
|
+
|
590
|
+
# This is meant to be implemented by the adapters that support custom enum types
|
591
|
+
def add_enum_value(*) # :nodoc:
|
592
|
+
end
|
593
|
+
|
594
|
+
# This is meant to be implemented by the adapters that support custom enum types
|
595
|
+
def rename_enum_value(*) # :nodoc:
|
596
|
+
end
|
597
|
+
|
470
598
|
def advisory_locks_enabled? # :nodoc:
|
471
599
|
supports_advisory_locks? && @advisory_locks_enabled
|
472
600
|
end
|
@@ -503,31 +631,71 @@ module ActiveRecord
|
|
503
631
|
end
|
504
632
|
|
505
633
|
# Override to check all foreign key constraints in a database.
|
506
|
-
|
507
|
-
|
634
|
+
# The adapter should raise a +ActiveRecord::StatementInvalid+ if foreign key
|
635
|
+
# constraints are not met.
|
636
|
+
def check_all_foreign_keys_valid!
|
508
637
|
end
|
509
638
|
|
510
639
|
# CONNECTION MANAGEMENT ====================================
|
511
640
|
|
641
|
+
# Checks whether the connection to the database was established. This doesn't
|
642
|
+
# include checking whether the database is actually capable of responding, i.e.
|
643
|
+
# whether the connection is stale.
|
644
|
+
def connected?
|
645
|
+
!@raw_connection.nil?
|
646
|
+
end
|
647
|
+
|
512
648
|
# Checks whether the connection to the database is still active. This includes
|
513
649
|
# checking whether the database is actually capable of responding, i.e. whether
|
514
650
|
# the connection isn't stale.
|
515
651
|
def active?
|
516
652
|
end
|
517
653
|
|
518
|
-
# Disconnects from the database if already connected, and establishes a
|
519
|
-
#
|
520
|
-
#
|
521
|
-
def reconnect!
|
522
|
-
|
523
|
-
|
654
|
+
# Disconnects from the database if already connected, and establishes a new
|
655
|
+
# connection with the database. Implementors should define private #reconnect
|
656
|
+
# instead.
|
657
|
+
def reconnect!(restore_transactions: false)
|
658
|
+
retries_available = connection_retries
|
659
|
+
deadline = retry_deadline && Process.clock_gettime(Process::CLOCK_MONOTONIC) + retry_deadline
|
660
|
+
|
661
|
+
@lock.synchronize do
|
662
|
+
reconnect
|
663
|
+
|
664
|
+
enable_lazy_transactions!
|
665
|
+
@raw_connection_dirty = false
|
666
|
+
@verified = true
|
667
|
+
|
668
|
+
reset_transaction(restore: restore_transactions) do
|
669
|
+
clear_cache!(new_connection: true)
|
670
|
+
configure_connection
|
671
|
+
end
|
672
|
+
rescue => original_exception
|
673
|
+
translated_exception = translate_exception_class(original_exception, nil, nil)
|
674
|
+
retry_deadline_exceeded = deadline && deadline < Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
675
|
+
|
676
|
+
if !retry_deadline_exceeded && retries_available > 0
|
677
|
+
retries_available -= 1
|
678
|
+
|
679
|
+
if retryable_connection_error?(translated_exception)
|
680
|
+
backoff(connection_retries - retries_available)
|
681
|
+
retry
|
682
|
+
end
|
683
|
+
end
|
684
|
+
|
685
|
+
@verified = false
|
686
|
+
|
687
|
+
raise translated_exception
|
688
|
+
end
|
524
689
|
end
|
525
690
|
|
526
691
|
# Disconnects from the database if already connected. Otherwise, this
|
527
692
|
# method does nothing.
|
528
693
|
def disconnect!
|
529
|
-
|
530
|
-
|
694
|
+
@lock.synchronize do
|
695
|
+
clear_cache!(new_connection: true)
|
696
|
+
reset_transaction
|
697
|
+
@raw_connection_dirty = false
|
698
|
+
end
|
531
699
|
end
|
532
700
|
|
533
701
|
# Immediately forget this connection ever existed. Unlike disconnect!,
|
@@ -538,22 +706,20 @@ module ActiveRecord
|
|
538
706
|
# rid of a connection that belonged to its parent.
|
539
707
|
def discard!
|
540
708
|
# 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
709
|
end
|
548
710
|
|
549
711
|
# Reset the state of this connection, directing the DBMS to clear
|
550
712
|
# transactions and other connection-related server-side state. Usually a
|
551
713
|
# database-dependent operation.
|
552
714
|
#
|
553
|
-
#
|
554
|
-
#
|
715
|
+
# If a database driver or protocol does not support such a feature,
|
716
|
+
# implementors may alias this to #reconnect!. Otherwise, implementors
|
717
|
+
# should call super immediately after resetting the connection (and while
|
718
|
+
# still holding @lock).
|
555
719
|
def reset!
|
556
|
-
|
720
|
+
clear_cache!(new_connection: true)
|
721
|
+
reset_transaction
|
722
|
+
configure_connection
|
557
723
|
end
|
558
724
|
|
559
725
|
# Removes the connection from the pool and disconnect it.
|
@@ -563,8 +729,16 @@ module ActiveRecord
|
|
563
729
|
end
|
564
730
|
|
565
731
|
# Clear any caching the database adapter may be doing.
|
566
|
-
def clear_cache!
|
567
|
-
|
732
|
+
def clear_cache!(new_connection: false)
|
733
|
+
if @statements
|
734
|
+
@lock.synchronize do
|
735
|
+
if new_connection
|
736
|
+
@statements.reset
|
737
|
+
else
|
738
|
+
@statements.clear
|
739
|
+
end
|
740
|
+
end
|
741
|
+
end
|
568
742
|
end
|
569
743
|
|
570
744
|
# Returns true if its required to reload the connection between requests for development mode.
|
@@ -576,7 +750,31 @@ module ActiveRecord
|
|
576
750
|
# This is done under the hood by calling #active?. If the connection
|
577
751
|
# is no longer active, then this method will reconnect to the database.
|
578
752
|
def verify!
|
579
|
-
|
753
|
+
unless active?
|
754
|
+
@lock.synchronize do
|
755
|
+
if @unconfigured_connection
|
756
|
+
@raw_connection = @unconfigured_connection
|
757
|
+
@unconfigured_connection = nil
|
758
|
+
configure_connection
|
759
|
+
@verified = true
|
760
|
+
return
|
761
|
+
end
|
762
|
+
|
763
|
+
reconnect!(restore_transactions: true)
|
764
|
+
end
|
765
|
+
end
|
766
|
+
|
767
|
+
@verified = true
|
768
|
+
end
|
769
|
+
|
770
|
+
def connect!
|
771
|
+
verify!
|
772
|
+
self
|
773
|
+
end
|
774
|
+
|
775
|
+
def clean! # :nodoc:
|
776
|
+
@raw_connection_dirty = false
|
777
|
+
@verified = nil
|
580
778
|
end
|
581
779
|
|
582
780
|
# Provides access to the underlying database driver for this adapter. For
|
@@ -590,8 +788,11 @@ module ActiveRecord
|
|
590
788
|
# this client. If that is the case, generally you'll want to invalidate
|
591
789
|
# the query cache using +ActiveRecord::Base.clear_query_cache+.
|
592
790
|
def raw_connection
|
593
|
-
|
594
|
-
|
791
|
+
with_raw_connection do |conn|
|
792
|
+
disable_lazy_transactions!
|
793
|
+
@raw_connection_dirty = true
|
794
|
+
conn
|
795
|
+
end
|
595
796
|
end
|
596
797
|
|
597
798
|
def default_uniqueness_comparison(attribute, value) # :nodoc:
|
@@ -643,7 +844,7 @@ module ActiveRecord
|
|
643
844
|
end
|
644
845
|
|
645
846
|
def database_version # :nodoc:
|
646
|
-
|
847
|
+
pool.server_version(self)
|
647
848
|
end
|
648
849
|
|
649
850
|
def check_version # :nodoc:
|
@@ -654,10 +855,25 @@ module ActiveRecord
|
|
654
855
|
# numbered migration that has been executed, or 0 if no schema
|
655
856
|
# information is present / the database is empty.
|
656
857
|
def schema_version
|
657
|
-
migration_context.current_version
|
858
|
+
pool.migration_context.current_version
|
658
859
|
end
|
659
860
|
|
660
861
|
class << self
|
862
|
+
def register_class_with_precision(mapping, key, klass, **kwargs) # :nodoc:
|
863
|
+
mapping.register_type(key) do |*args|
|
864
|
+
precision = extract_precision(args.last)
|
865
|
+
klass.new(precision: precision, **kwargs)
|
866
|
+
end
|
867
|
+
end
|
868
|
+
|
869
|
+
def extended_type_map(default_timezone:) # :nodoc:
|
870
|
+
Type::TypeMap.new(self::TYPE_MAP).tap do |m|
|
871
|
+
register_class_with_precision m, %r(\A[^\(]*time)i, Type::Time, timezone: default_timezone
|
872
|
+
register_class_with_precision m, %r(\A[^\(]*datetime)i, Type::DateTime, timezone: default_timezone
|
873
|
+
m.alias_type %r(\A[^\(]*timestamp)i, "datetime"
|
874
|
+
end
|
875
|
+
end
|
876
|
+
|
661
877
|
private
|
662
878
|
def initialize_type_map(m)
|
663
879
|
register_class_with_limit m, %r(boolean)i, Type::Boolean
|
@@ -699,13 +915,6 @@ module ActiveRecord
|
|
699
915
|
end
|
700
916
|
end
|
701
917
|
|
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
918
|
def extract_scale(sql_type)
|
710
919
|
case sql_type
|
711
920
|
when /\((\d+)\)/ then 0
|
@@ -723,10 +932,177 @@ module ActiveRecord
|
|
723
932
|
end
|
724
933
|
|
725
934
|
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
|
935
|
+
EXTENDED_TYPE_MAPS = Concurrent::Map.new
|
726
936
|
|
727
937
|
private
|
938
|
+
def reconnect_can_restore_state?
|
939
|
+
transaction_manager.restorable? && !@raw_connection_dirty
|
940
|
+
end
|
941
|
+
|
942
|
+
# Lock the monitor, ensure we're properly connected and
|
943
|
+
# transactions are materialized, and then yield the underlying
|
944
|
+
# raw connection object.
|
945
|
+
#
|
946
|
+
# If +allow_retry+ is true, a connection-related exception will
|
947
|
+
# cause an automatic reconnect and re-run of the block, up to
|
948
|
+
# the connection's configured +connection_retries+ setting
|
949
|
+
# and the configured +retry_deadline+ limit. (Note that when
|
950
|
+
# +allow_retry+ is true, it's possible to return without having marked
|
951
|
+
# the connection as verified. If the block is guaranteed to exercise the
|
952
|
+
# connection, consider calling `verified!` to avoid needless
|
953
|
+
# verification queries in subsequent calls.)
|
954
|
+
#
|
955
|
+
# If +materialize_transactions+ is false, the block will be run without
|
956
|
+
# ensuring virtual transactions have been materialized in the DB
|
957
|
+
# server's state. The active transaction will also remain clean
|
958
|
+
# (if it is not already dirty), meaning it's able to be restored
|
959
|
+
# by reconnecting and opening an equivalent-depth set of new
|
960
|
+
# transactions. This should only be used by transaction control
|
961
|
+
# methods, and internal transaction-agnostic queries.
|
962
|
+
#
|
963
|
+
###
|
964
|
+
#
|
965
|
+
# It's not the primary use case, so not something to optimize
|
966
|
+
# for, but note that this method does need to be re-entrant:
|
967
|
+
# +materialize_transactions+ will re-enter if it has work to do,
|
968
|
+
# and the yield block can also do so under some circumstances.
|
969
|
+
#
|
970
|
+
# In the latter case, we really ought to guarantee the inner
|
971
|
+
# call will not reconnect (which would interfere with the
|
972
|
+
# still-yielded connection in the outer block), but we currently
|
973
|
+
# provide no special enforcement there.
|
974
|
+
#
|
975
|
+
def with_raw_connection(allow_retry: false, materialize_transactions: true)
|
976
|
+
@lock.synchronize do
|
977
|
+
connect! if @raw_connection.nil? && reconnect_can_restore_state?
|
978
|
+
|
979
|
+
self.materialize_transactions if materialize_transactions
|
980
|
+
|
981
|
+
retries_available = allow_retry ? connection_retries : 0
|
982
|
+
deadline = retry_deadline && Process.clock_gettime(Process::CLOCK_MONOTONIC) + retry_deadline
|
983
|
+
reconnectable = reconnect_can_restore_state?
|
984
|
+
|
985
|
+
if @verified
|
986
|
+
# Cool, we're confident the connection's ready to use. (Note this might have
|
987
|
+
# become true during the above #materialize_transactions.)
|
988
|
+
elsif reconnectable
|
989
|
+
if allow_retry
|
990
|
+
# Not sure about the connection yet, but if anything goes wrong we can
|
991
|
+
# just reconnect and re-run our query
|
992
|
+
else
|
993
|
+
# We can reconnect if needed, but we don't trust the upcoming query to be
|
994
|
+
# safely re-runnable: let's verify the connection to be sure
|
995
|
+
verify!
|
996
|
+
end
|
997
|
+
else
|
998
|
+
# We don't know whether the connection is okay, but it also doesn't matter:
|
999
|
+
# we wouldn't be able to reconnect anyway. We're just going to run our query
|
1000
|
+
# and hope for the best.
|
1001
|
+
end
|
1002
|
+
|
1003
|
+
begin
|
1004
|
+
yield @raw_connection
|
1005
|
+
rescue => original_exception
|
1006
|
+
translated_exception = translate_exception_class(original_exception, nil, nil)
|
1007
|
+
invalidate_transaction(translated_exception)
|
1008
|
+
retry_deadline_exceeded = deadline && deadline < Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
1009
|
+
|
1010
|
+
if !retry_deadline_exceeded && retries_available > 0
|
1011
|
+
retries_available -= 1
|
1012
|
+
|
1013
|
+
if retryable_query_error?(translated_exception)
|
1014
|
+
backoff(connection_retries - retries_available)
|
1015
|
+
retry
|
1016
|
+
elsif reconnectable && retryable_connection_error?(translated_exception)
|
1017
|
+
reconnect!(restore_transactions: true)
|
1018
|
+
# Only allowed to reconnect once, because reconnect! has its own retry
|
1019
|
+
# loop
|
1020
|
+
reconnectable = false
|
1021
|
+
retry
|
1022
|
+
end
|
1023
|
+
end
|
1024
|
+
|
1025
|
+
unless retryable_query_error?(translated_exception)
|
1026
|
+
# Barring a known-retryable error inside the query (regardless of
|
1027
|
+
# whether we were in a _position_ to retry it), we should infer that
|
1028
|
+
# there's likely a real problem with the connection.
|
1029
|
+
@verified = false
|
1030
|
+
end
|
1031
|
+
|
1032
|
+
raise translated_exception
|
1033
|
+
ensure
|
1034
|
+
dirty_current_transaction if materialize_transactions
|
1035
|
+
end
|
1036
|
+
end
|
1037
|
+
end
|
1038
|
+
|
1039
|
+
# Mark the connection as verified. Call this inside a
|
1040
|
+
# `with_raw_connection` block only when the block is guaranteed to
|
1041
|
+
# exercise the raw connection.
|
1042
|
+
def verified!
|
1043
|
+
@verified = true
|
1044
|
+
end
|
1045
|
+
|
1046
|
+
def retryable_connection_error?(exception)
|
1047
|
+
exception.is_a?(ConnectionNotEstablished) || exception.is_a?(ConnectionFailed)
|
1048
|
+
end
|
1049
|
+
|
1050
|
+
def invalidate_transaction(exception)
|
1051
|
+
return unless exception.is_a?(TransactionRollbackError)
|
1052
|
+
return unless savepoint_errors_invalidate_transactions?
|
1053
|
+
|
1054
|
+
current_transaction.invalidate!
|
1055
|
+
end
|
1056
|
+
|
1057
|
+
def retryable_query_error?(exception)
|
1058
|
+
# We definitely can't retry if we were inside an invalidated transaction.
|
1059
|
+
return false if current_transaction.invalidated?
|
1060
|
+
|
1061
|
+
exception.is_a?(Deadlocked) || exception.is_a?(LockWaitTimeout)
|
1062
|
+
end
|
1063
|
+
|
1064
|
+
def backoff(counter)
|
1065
|
+
sleep 0.1 * counter
|
1066
|
+
end
|
1067
|
+
|
1068
|
+
def reconnect
|
1069
|
+
raise NotImplementedError
|
1070
|
+
end
|
1071
|
+
|
1072
|
+
# Returns a raw connection for internal use with methods that are known
|
1073
|
+
# to both be thread-safe and not rely upon actual server communication.
|
1074
|
+
# This is useful for e.g. string escaping methods.
|
1075
|
+
def any_raw_connection
|
1076
|
+
@raw_connection || valid_raw_connection
|
1077
|
+
end
|
1078
|
+
|
1079
|
+
# Similar to any_raw_connection, but ensures it is validated and
|
1080
|
+
# connected. Any method called on this result still needs to be
|
1081
|
+
# independently thread-safe, so it probably shouldn't talk to the
|
1082
|
+
# server... but some drivers fail if they know the connection has gone
|
1083
|
+
# away.
|
1084
|
+
def valid_raw_connection
|
1085
|
+
(@verified && @raw_connection) ||
|
1086
|
+
# `allow_retry: false`, to force verification: the block won't
|
1087
|
+
# raise, so a retry wouldn't help us get the valid connection we
|
1088
|
+
# need.
|
1089
|
+
with_raw_connection(allow_retry: false, materialize_transactions: false) { |conn| conn }
|
1090
|
+
end
|
1091
|
+
|
1092
|
+
def extended_type_map_key
|
1093
|
+
if @default_timezone
|
1094
|
+
{ default_timezone: @default_timezone }
|
1095
|
+
end
|
1096
|
+
end
|
1097
|
+
|
728
1098
|
def type_map
|
729
|
-
|
1099
|
+
if key = extended_type_map_key
|
1100
|
+
self.class::EXTENDED_TYPE_MAPS.compute_if_absent(key) do
|
1101
|
+
self.class.extended_type_map(**key)
|
1102
|
+
end
|
1103
|
+
else
|
1104
|
+
self.class::TYPE_MAP
|
1105
|
+
end
|
730
1106
|
end
|
731
1107
|
|
732
1108
|
def translate_exception_class(e, sql, binds)
|
@@ -748,16 +1124,18 @@ module ActiveRecord
|
|
748
1124
|
type_casted_binds: type_casted_binds,
|
749
1125
|
statement_name: statement_name,
|
750
1126
|
async: async,
|
751
|
-
connection: self
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
1127
|
+
connection: self,
|
1128
|
+
transaction: current_transaction.user_transaction.presence,
|
1129
|
+
row_count: 0,
|
1130
|
+
&block
|
1131
|
+
)
|
1132
|
+
rescue ActiveRecord::StatementInvalid => ex
|
1133
|
+
raise ex.set_query(sql, binds)
|
756
1134
|
end
|
757
1135
|
|
758
1136
|
def transform_query(sql)
|
759
1137
|
ActiveRecord.query_transformers.each do |transformer|
|
760
|
-
sql = transformer.call(sql)
|
1138
|
+
sql = transformer.call(sql, self)
|
761
1139
|
end
|
762
1140
|
sql
|
763
1141
|
end
|
@@ -765,10 +1143,10 @@ module ActiveRecord
|
|
765
1143
|
def translate_exception(exception, message:, sql:, binds:)
|
766
1144
|
# override in derived class
|
767
1145
|
case exception
|
768
|
-
when RuntimeError
|
1146
|
+
when RuntimeError, ActiveRecord::ActiveRecordError
|
769
1147
|
exception
|
770
1148
|
else
|
771
|
-
ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds)
|
1149
|
+
ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
772
1150
|
end
|
773
1151
|
end
|
774
1152
|
|
@@ -812,9 +1190,30 @@ module ActiveRecord
|
|
812
1190
|
#
|
813
1191
|
# This is an internal hook to make possible connection adapters to build
|
814
1192
|
# custom result objects with connection-specific data.
|
815
|
-
def build_result(columns:, rows:, column_types:
|
1193
|
+
def build_result(columns:, rows:, column_types: nil)
|
816
1194
|
ActiveRecord::Result.new(columns, rows, column_types)
|
817
1195
|
end
|
1196
|
+
|
1197
|
+
# Perform any necessary initialization upon the newly-established
|
1198
|
+
# @raw_connection -- this is the place to modify the adapter's
|
1199
|
+
# connection settings, run queries to configure any application-global
|
1200
|
+
# "session" variables, etc.
|
1201
|
+
#
|
1202
|
+
# Implementations may assume this method will only be called while
|
1203
|
+
# holding @lock (or from #initialize).
|
1204
|
+
def configure_connection
|
1205
|
+
check_version
|
1206
|
+
end
|
1207
|
+
|
1208
|
+
def default_prepared_statements
|
1209
|
+
true
|
1210
|
+
end
|
1211
|
+
|
1212
|
+
def warning_ignored?(warning)
|
1213
|
+
ActiveRecord.db_warnings_ignore.any? do |warning_matcher|
|
1214
|
+
warning.message.match?(warning_matcher) || warning.code.to_s.match?(warning_matcher)
|
1215
|
+
end
|
1216
|
+
end
|
818
1217
|
end
|
819
1218
|
end
|
820
1219
|
end
|