activerecord 7.0.0 → 7.1.0
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 +1607 -1040
- data/MIT-LICENSE +1 -1
- data/README.rdoc +17 -18
- data/lib/active_record/aggregations.rb +16 -13
- data/lib/active_record/association_relation.rb +1 -1
- data/lib/active_record/associations/association.rb +18 -3
- data/lib/active_record/associations/association_scope.rb +16 -9
- data/lib/active_record/associations/belongs_to_association.rb +14 -6
- data/lib/active_record/associations/builder/association.rb +3 -3
- data/lib/active_record/associations/builder/belongs_to.rb +21 -8
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
- data/lib/active_record/associations/builder/singular_association.rb +4 -0
- data/lib/active_record/associations/collection_association.rb +17 -12
- data/lib/active_record/associations/collection_proxy.rb +22 -12
- data/lib/active_record/associations/foreign_association.rb +10 -3
- data/lib/active_record/associations/has_many_association.rb +27 -17
- data/lib/active_record/associations/has_many_through_association.rb +10 -6
- data/lib/active_record/associations/has_one_association.rb +10 -3
- data/lib/active_record/associations/join_dependency.rb +20 -14
- data/lib/active_record/associations/preloader/association.rb +27 -6
- data/lib/active_record/associations/preloader/through_association.rb +1 -1
- data/lib/active_record/associations/preloader.rb +13 -10
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations/through_association.rb +22 -11
- data/lib/active_record/associations.rb +345 -219
- data/lib/active_record/attribute_assignment.rb +0 -2
- data/lib/active_record/attribute_methods/before_type_cast.rb +17 -0
- data/lib/active_record/attribute_methods/dirty.rb +40 -26
- data/lib/active_record/attribute_methods/primary_key.rb +76 -24
- data/lib/active_record/attribute_methods/query.rb +28 -16
- data/lib/active_record/attribute_methods/read.rb +18 -5
- data/lib/active_record/attribute_methods/serialization.rb +172 -69
- data/lib/active_record/attribute_methods/write.rb +3 -3
- data/lib/active_record/attribute_methods.rb +110 -28
- data/lib/active_record/attributes.rb +3 -3
- data/lib/active_record/autosave_association.rb +56 -10
- data/lib/active_record/base.rb +10 -5
- data/lib/active_record/callbacks.rb +16 -32
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +70 -34
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +164 -89
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +3 -1
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +63 -43
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +128 -32
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +60 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +52 -8
- 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 +163 -29
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +302 -129
- data/lib/active_record/connection_adapters/abstract/transaction.rb +287 -58
- data/lib/active_record/connection_adapters/abstract_adapter.rb +504 -106
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +217 -104
- data/lib/active_record/connection_adapters/column.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +23 -144
- data/lib/active_record/connection_adapters/mysql/quoting.rb +29 -12
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +9 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +10 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +8 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +38 -14
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +148 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +98 -53
- data/lib/active_record/connection_adapters/pool_config.rb +14 -5
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +72 -45
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -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 +3 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +41 -8
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +6 -10
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +76 -6
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +131 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +358 -57
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +343 -181
- data/lib/active_record/connection_adapters/schema_cache.rb +287 -59
- data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +45 -39
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +22 -5
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +41 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +242 -81
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +98 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +254 -0
- data/lib/active_record/connection_adapters.rb +3 -1
- data/lib/active_record/connection_handling.rb +73 -96
- data/lib/active_record/core.rb +136 -148
- data/lib/active_record/counter_cache.rb +46 -25
- data/lib/active_record/database_configurations/connection_url_resolver.rb +1 -0
- data/lib/active_record/database_configurations/database_config.rb +9 -3
- data/lib/active_record/database_configurations/hash_config.rb +22 -12
- data/lib/active_record/database_configurations/url_config.rb +17 -11
- data/lib/active_record/database_configurations.rb +87 -34
- data/lib/active_record/delegated_type.rb +9 -4
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +2 -0
- data/lib/active_record/disable_joins_association_relation.rb +1 -1
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +4 -1
- data/lib/active_record/encryption/config.rb +25 -1
- data/lib/active_record/encryption/configurable.rb +13 -14
- data/lib/active_record/encryption/context.rb +10 -3
- data/lib/active_record/encryption/contexts.rb +8 -4
- data/lib/active_record/encryption/derived_secret_key_provider.rb +9 -3
- data/lib/active_record/encryption/deterministic_key_provider.rb +1 -1
- data/lib/active_record/encryption/encryptable_record.rb +38 -22
- data/lib/active_record/encryption/encrypted_attribute_type.rb +19 -8
- data/lib/active_record/encryption/encryptor.rb +7 -7
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +3 -3
- data/lib/active_record/encryption/extended_deterministic_queries.rb +83 -71
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +3 -3
- data/lib/active_record/encryption/key_generator.rb +12 -1
- data/lib/active_record/encryption/message.rb +1 -1
- data/lib/active_record/encryption/message_serializer.rb +2 -0
- data/lib/active_record/encryption/properties.rb +4 -4
- data/lib/active_record/encryption/scheme.rb +20 -23
- data/lib/active_record/encryption.rb +1 -0
- data/lib/active_record/enum.rb +114 -27
- data/lib/active_record/errors.rb +108 -15
- data/lib/active_record/explain.rb +23 -3
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/model_metadata.rb +14 -4
- data/lib/active_record/fixture_set/render_context.rb +2 -0
- data/lib/active_record/fixture_set/table_row.rb +29 -8
- data/lib/active_record/fixtures.rb +121 -73
- data/lib/active_record/future_result.rb +30 -5
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +30 -16
- data/lib/active_record/insert_all.rb +55 -8
- data/lib/active_record/integration.rb +10 -10
- data/lib/active_record/internal_metadata.rb +118 -30
- data/lib/active_record/locking/optimistic.rb +32 -18
- data/lib/active_record/locking/pessimistic.rb +8 -5
- data/lib/active_record/log_subscriber.rb +39 -17
- data/lib/active_record/marshalling.rb +56 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +4 -0
- data/lib/active_record/middleware/database_selector.rb +18 -13
- data/lib/active_record/middleware/shard_selector.rb +7 -5
- data/lib/active_record/migration/command_recorder.rb +104 -9
- data/lib/active_record/migration/compatibility.rb +158 -64
- data/lib/active_record/migration/default_strategy.rb +23 -0
- data/lib/active_record/migration/execution_strategy.rb +19 -0
- data/lib/active_record/migration.rb +271 -117
- data/lib/active_record/model_schema.rb +82 -50
- data/lib/active_record/nested_attributes.rb +23 -3
- data/lib/active_record/normalization.rb +159 -0
- data/lib/active_record/persistence.rb +200 -47
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +3 -21
- data/lib/active_record/query_logs.rb +87 -51
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +16 -3
- data/lib/active_record/railtie.rb +127 -61
- data/lib/active_record/railties/controller_runtime.rb +12 -8
- data/lib/active_record/railties/databases.rake +142 -143
- 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 +177 -45
- data/lib/active_record/relation/batches/batch_enumerator.rb +5 -3
- data/lib/active_record/relation/batches.rb +190 -61
- data/lib/active_record/relation/calculations.rb +200 -83
- data/lib/active_record/relation/delegation.rb +23 -9
- data/lib/active_record/relation/finder_methods.rb +77 -16
- data/lib/active_record/relation/merger.rb +2 -0
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +31 -3
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +4 -6
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +26 -14
- data/lib/active_record/relation/query_attribute.rb +25 -1
- data/lib/active_record/relation/query_methods.rb +429 -76
- data/lib/active_record/relation/spawn_methods.rb +18 -1
- data/lib/active_record/relation.rb +98 -41
- data/lib/active_record/result.rb +25 -9
- data/lib/active_record/runtime_registry.rb +10 -1
- data/lib/active_record/sanitization.rb +57 -16
- data/lib/active_record/schema.rb +36 -22
- data/lib/active_record/schema_dumper.rb +65 -23
- data/lib/active_record/schema_migration.rb +68 -33
- data/lib/active_record/scoping/default.rb +20 -12
- data/lib/active_record/scoping/named.rb +2 -2
- data/lib/active_record/scoping.rb +2 -1
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/serialization.rb +5 -0
- data/lib/active_record/signed_id.rb +9 -7
- data/lib/active_record/store.rb +16 -11
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +16 -3
- data/lib/active_record/tasks/database_tasks.rb +138 -107
- data/lib/active_record/tasks/mysql_database_tasks.rb +15 -6
- data/lib/active_record/tasks/postgresql_database_tasks.rb +17 -15
- data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
- data/lib/active_record/test_fixtures.rb +123 -99
- data/lib/active_record/timestamp.rb +26 -14
- data/lib/active_record/token_for.rb +113 -0
- data/lib/active_record/touch_later.rb +11 -6
- data/lib/active_record/transactions.rb +39 -13
- data/lib/active_record/translation.rb +1 -1
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +8 -4
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +3 -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 +50 -5
- data/lib/active_record/validations.rb +8 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +143 -16
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +1 -1
- data/lib/arel/nodes/and.rb +4 -0
- data/lib/arel/nodes/binary.rb +6 -1
- data/lib/arel/nodes/bound_sql_literal.rb +61 -0
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/filter.rb +1 -1
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/homogeneous_in.rb +0 -8
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/node.rb +111 -2
- data/lib/arel/nodes/sql_literal.rb +6 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes.rb +4 -0
- data/lib/arel/predications.rb +2 -0
- data/lib/arel/table.rb +9 -5
- data/lib/arel/visitors/mysql.rb +8 -1
- data/lib/arel/visitors/to_sql.rb +81 -17
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +16 -2
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/migration.rb +3 -1
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +15 -6
- metadata +50 -15
- 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
|
@@ -36,12 +39,20 @@ 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
|
+
|
53
|
+
@pool.schema_reflection.load!(self) if ActiveRecord.lazily_load_schema_cache
|
54
|
+
end
|
55
|
+
|
45
56
|
set_callback :checkin, :after, :enable_lazy_transactions!
|
46
57
|
|
47
58
|
def self.type_cast_config_to_integer(config)
|
@@ -62,6 +73,16 @@ module ActiveRecord
|
|
62
73
|
end
|
63
74
|
end
|
64
75
|
|
76
|
+
def self.validate_default_timezone(config)
|
77
|
+
case config
|
78
|
+
when nil
|
79
|
+
when "utc", "local"
|
80
|
+
config.to_sym
|
81
|
+
else
|
82
|
+
raise ArgumentError, "default_timezone must be either 'utc' or 'local'"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
65
86
|
DEFAULT_READ_QUERY = [:begin, :commit, :explain, :release, :rollback, :savepoint, :select, :with] # :nodoc:
|
66
87
|
private_constant :DEFAULT_READ_QUERY
|
67
88
|
|
@@ -71,35 +92,104 @@ module ActiveRecord
|
|
71
92
|
/\A(?:[(\s]|#{COMMENT_REGEX})*#{Regexp.union(*parts)}/
|
72
93
|
end
|
73
94
|
|
74
|
-
def self.
|
75
|
-
|
95
|
+
def self.find_cmd_and_exec(commands, *args) # :doc:
|
96
|
+
commands = Array(commands)
|
97
|
+
|
98
|
+
dirs_on_path = ENV["PATH"].to_s.split(File::PATH_SEPARATOR)
|
99
|
+
unless (ext = RbConfig::CONFIG["EXEEXT"]).empty?
|
100
|
+
commands = commands.map { |cmd| "#{cmd}#{ext}" }
|
101
|
+
end
|
102
|
+
|
103
|
+
full_path_command = nil
|
104
|
+
found = commands.detect do |cmd|
|
105
|
+
dirs_on_path.detect do |path|
|
106
|
+
full_path_command = File.join(path, cmd)
|
107
|
+
begin
|
108
|
+
stat = File.stat(full_path_command)
|
109
|
+
rescue SystemCallError
|
110
|
+
else
|
111
|
+
stat.file? && stat.executable?
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
if found
|
117
|
+
exec full_path_command, *args
|
118
|
+
else
|
119
|
+
abort("Couldn't find database client: #{commands.join(', ')}. Check your $PATH and try again.")
|
120
|
+
end
|
76
121
|
end
|
77
122
|
|
78
|
-
|
79
|
-
|
123
|
+
# Opens a database console session.
|
124
|
+
def self.dbconsole(config, options = {})
|
125
|
+
raise NotImplementedError
|
80
126
|
end
|
81
127
|
|
82
|
-
def initialize(
|
128
|
+
def initialize(config_or_deprecated_connection, deprecated_logger = nil, deprecated_connection_options = nil, deprecated_config = nil) # :nodoc:
|
83
129
|
super()
|
84
130
|
|
85
|
-
@
|
86
|
-
@
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
131
|
+
@raw_connection = nil
|
132
|
+
@unconfigured_connection = nil
|
133
|
+
|
134
|
+
if config_or_deprecated_connection.is_a?(Hash)
|
135
|
+
@config = config_or_deprecated_connection.symbolize_keys
|
136
|
+
@logger = ActiveRecord::Base.logger
|
137
|
+
|
138
|
+
if deprecated_logger || deprecated_connection_options || deprecated_config
|
139
|
+
raise ArgumentError, "when initializing an ActiveRecord adapter with a config hash, that should be the only argument"
|
140
|
+
end
|
141
|
+
else
|
142
|
+
# Soft-deprecated for now; we'll probably warn in future.
|
143
|
+
|
144
|
+
@unconfigured_connection = config_or_deprecated_connection
|
145
|
+
@logger = deprecated_logger || ActiveRecord::Base.logger
|
146
|
+
if deprecated_config
|
147
|
+
@config = (deprecated_config || {}).symbolize_keys
|
148
|
+
@connection_parameters = deprecated_connection_options
|
149
|
+
else
|
150
|
+
@config = (deprecated_connection_options || {}).symbolize_keys
|
151
|
+
@connection_parameters = nil
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
@owner = nil
|
156
|
+
@instrumenter = ActiveSupport::Notifications.instrumenter
|
157
|
+
@pool = ActiveRecord::ConnectionAdapters::NullPool.new
|
158
|
+
@idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
92
159
|
@visitor = arel_visitor
|
93
160
|
@statements = build_statement_pool
|
94
|
-
|
161
|
+
self.lock_thread = nil
|
95
162
|
|
96
|
-
@prepared_statements = self.class.type_cast_config_to_boolean(
|
97
|
-
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 }
|
98
165
|
)
|
99
166
|
|
100
167
|
@advisory_locks_enabled = self.class.type_cast_config_to_boolean(
|
101
|
-
config.fetch(:advisory_locks, true)
|
168
|
+
@config.fetch(:advisory_locks, true)
|
102
169
|
)
|
170
|
+
|
171
|
+
@default_timezone = self.class.validate_default_timezone(@config[:default_timezone])
|
172
|
+
|
173
|
+
@raw_connection_dirty = false
|
174
|
+
@verified = false
|
175
|
+
end
|
176
|
+
|
177
|
+
THREAD_LOCK = ActiveSupport::Concurrency::ThreadLoadInterlockAwareMonitor.new
|
178
|
+
private_constant :THREAD_LOCK
|
179
|
+
|
180
|
+
FIBER_LOCK = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
|
181
|
+
private_constant :FIBER_LOCK
|
182
|
+
|
183
|
+
def lock_thread=(lock_thread) # :nodoc:
|
184
|
+
@lock =
|
185
|
+
case lock_thread
|
186
|
+
when Thread
|
187
|
+
THREAD_LOCK
|
188
|
+
when Fiber
|
189
|
+
FIBER_LOCK
|
190
|
+
else
|
191
|
+
ActiveSupport::Concurrency::NullLock
|
192
|
+
end
|
103
193
|
end
|
104
194
|
|
105
195
|
EXCEPTION_NEVER = { Exception => :never }.freeze # :nodoc:
|
@@ -129,18 +219,28 @@ module ActiveRecord
|
|
129
219
|
@config.fetch(:use_metadata_table, true)
|
130
220
|
end
|
131
221
|
|
222
|
+
def connection_retries
|
223
|
+
(@config[:connection_retries] || 1).to_i
|
224
|
+
end
|
225
|
+
|
226
|
+
def retry_deadline
|
227
|
+
if @config[:retry_deadline]
|
228
|
+
@config[:retry_deadline].to_f
|
229
|
+
else
|
230
|
+
nil
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
def default_timezone
|
235
|
+
@default_timezone || ActiveRecord.default_timezone
|
236
|
+
end
|
237
|
+
|
132
238
|
# Determines whether writes are currently being prevented.
|
133
239
|
#
|
134
|
-
# Returns true if the connection is a replica
|
135
|
-
#
|
136
|
-
# If the application is using legacy handling, returns
|
137
|
-
# true if +connection_handler.prevent_writes+ is set.
|
138
|
-
#
|
139
|
-
# If the application is using the new connection handling
|
140
|
-
# will return true based on +current_preventing_writes+.
|
240
|
+
# Returns true if the connection is a replica or returns
|
241
|
+
# the value of +current_preventing_writes+.
|
141
242
|
def preventing_writes?
|
142
243
|
return true if replica?
|
143
|
-
return ActiveRecord::Base.connection_handler.prevent_writes if ActiveRecord.legacy_connection_handling
|
144
244
|
return false if connection_class.nil?
|
145
245
|
|
146
246
|
connection_class.current_preventing_writes
|
@@ -151,25 +251,15 @@ module ActiveRecord
|
|
151
251
|
end
|
152
252
|
|
153
253
|
def migration_context # :nodoc:
|
154
|
-
MigrationContext.new(migrations_paths, schema_migration)
|
254
|
+
MigrationContext.new(migrations_paths, schema_migration, internal_metadata)
|
155
255
|
end
|
156
256
|
|
157
257
|
def schema_migration # :nodoc:
|
158
|
-
|
159
|
-
|
160
|
-
spec_name = conn.pool.pool_config.connection_specification_name
|
161
|
-
|
162
|
-
return ActiveRecord::SchemaMigration if spec_name == "ActiveRecord::Base"
|
163
|
-
|
164
|
-
schema_migration_name = "#{spec_name}::SchemaMigration"
|
165
|
-
|
166
|
-
Class.new(ActiveRecord::SchemaMigration) do
|
167
|
-
define_singleton_method(:name) { schema_migration_name }
|
168
|
-
define_singleton_method(:to_s) { schema_migration_name }
|
258
|
+
SchemaMigration.new(self)
|
259
|
+
end
|
169
260
|
|
170
|
-
|
171
|
-
|
172
|
-
end
|
261
|
+
def internal_metadata # :nodoc:
|
262
|
+
InternalMetadata.new(self)
|
173
263
|
end
|
174
264
|
|
175
265
|
def prepared_statements?
|
@@ -208,50 +298,45 @@ module ActiveRecord
|
|
208
298
|
def lease
|
209
299
|
if in_use?
|
210
300
|
msg = +"Cannot lease connection, "
|
211
|
-
if @owner ==
|
301
|
+
if @owner == ActiveSupport::IsolatedExecutionState.context
|
212
302
|
msg << "it is already leased by the current thread."
|
213
303
|
else
|
214
304
|
msg << "it is already in use by a different thread: #{@owner}. " \
|
215
|
-
"Current thread: #{
|
305
|
+
"Current thread: #{ActiveSupport::IsolatedExecutionState.context}."
|
216
306
|
end
|
217
307
|
raise ActiveRecordError, msg
|
218
308
|
end
|
219
309
|
|
220
|
-
@owner =
|
310
|
+
@owner = ActiveSupport::IsolatedExecutionState.context
|
221
311
|
end
|
222
312
|
|
223
313
|
def connection_class # :nodoc:
|
224
314
|
@pool.connection_class
|
225
315
|
end
|
226
316
|
|
227
|
-
# The role (
|
228
|
-
# non-multi role application,
|
317
|
+
# The role (e.g. +:writing+) for the current connection. In a
|
318
|
+
# non-multi role application, +:writing+ is returned.
|
229
319
|
def role
|
230
320
|
@pool.role
|
231
321
|
end
|
232
322
|
|
233
|
-
# The shard (
|
234
|
-
# a non-sharded application,
|
323
|
+
# The shard (e.g. +:default+) for the current connection. In
|
324
|
+
# a non-sharded application, +:default+ is returned.
|
235
325
|
def shard
|
236
326
|
@pool.shard
|
237
327
|
end
|
238
328
|
|
239
329
|
def schema_cache
|
240
|
-
@pool.
|
241
|
-
end
|
242
|
-
|
243
|
-
def schema_cache=(cache)
|
244
|
-
cache.connection = self
|
245
|
-
@pool.set_schema_cache(cache)
|
330
|
+
@schema_cache ||= BoundSchemaReflection.new(@pool.schema_reflection, self)
|
246
331
|
end
|
247
332
|
|
248
333
|
# this method must only be called while holding connection pool's mutex
|
249
334
|
def expire
|
250
335
|
if in_use?
|
251
|
-
if @owner !=
|
336
|
+
if @owner != ActiveSupport::IsolatedExecutionState.context
|
252
337
|
raise ActiveRecordError, "Cannot expire connection, " \
|
253
338
|
"it is owned by a different thread: #{@owner}. " \
|
254
|
-
"Current thread: #{
|
339
|
+
"Current thread: #{ActiveSupport::IsolatedExecutionState.context}."
|
255
340
|
end
|
256
341
|
|
257
342
|
@idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
@@ -264,10 +349,10 @@ module ActiveRecord
|
|
264
349
|
# this method must only be called while holding connection pool's mutex (and a desire for segfaults)
|
265
350
|
def steal! # :nodoc:
|
266
351
|
if in_use?
|
267
|
-
if @owner !=
|
352
|
+
if @owner != ActiveSupport::IsolatedExecutionState.context
|
268
353
|
pool.send :remove_connection_from_thread_cache, self, @owner
|
269
354
|
|
270
|
-
@owner =
|
355
|
+
@owner = ActiveSupport::IsolatedExecutionState.context
|
271
356
|
end
|
272
357
|
else
|
273
358
|
raise ActiveRecordError, "Cannot steal connection, it is not currently leased."
|
@@ -295,7 +380,14 @@ module ActiveRecord
|
|
295
380
|
|
296
381
|
# Does the database for this adapter exist?
|
297
382
|
def self.database_exists?(config)
|
298
|
-
|
383
|
+
new(config).database_exists?
|
384
|
+
end
|
385
|
+
|
386
|
+
def database_exists?
|
387
|
+
connect!
|
388
|
+
true
|
389
|
+
rescue ActiveRecord::NoDatabaseError
|
390
|
+
false
|
299
391
|
end
|
300
392
|
|
301
393
|
# Does this adapter support DDL rollbacks in transactions? That is, would
|
@@ -313,6 +405,16 @@ module ActiveRecord
|
|
313
405
|
false
|
314
406
|
end
|
315
407
|
|
408
|
+
# Do TransactionRollbackErrors on savepoints affect the parent
|
409
|
+
# transaction?
|
410
|
+
def savepoint_errors_invalidate_transactions?
|
411
|
+
false
|
412
|
+
end
|
413
|
+
|
414
|
+
def supports_restart_db_transaction?
|
415
|
+
false
|
416
|
+
end
|
417
|
+
|
316
418
|
# Does this adapter support application-enforced advisory locking?
|
317
419
|
def supports_advisory_locks?
|
318
420
|
false
|
@@ -339,6 +441,11 @@ module ActiveRecord
|
|
339
441
|
false
|
340
442
|
end
|
341
443
|
|
444
|
+
# Does this adapter support including non-key columns?
|
445
|
+
def supports_index_include?
|
446
|
+
false
|
447
|
+
end
|
448
|
+
|
342
449
|
# Does this adapter support expression indices?
|
343
450
|
def supports_expression_index?
|
344
451
|
false
|
@@ -385,6 +492,16 @@ module ActiveRecord
|
|
385
492
|
false
|
386
493
|
end
|
387
494
|
|
495
|
+
# Does this adapter support creating exclusion constraints?
|
496
|
+
def supports_exclusion_constraints?
|
497
|
+
false
|
498
|
+
end
|
499
|
+
|
500
|
+
# Does this adapter support creating unique constraints?
|
501
|
+
def supports_unique_constraints?
|
502
|
+
false
|
503
|
+
end
|
504
|
+
|
388
505
|
# Does this adapter support views?
|
389
506
|
def supports_views?
|
390
507
|
false
|
@@ -400,7 +517,7 @@ module ActiveRecord
|
|
400
517
|
false
|
401
518
|
end
|
402
519
|
|
403
|
-
# Does this adapter support
|
520
|
+
# Does this adapter support JSON data type?
|
404
521
|
def supports_json?
|
405
522
|
false
|
406
523
|
end
|
@@ -458,23 +575,47 @@ module ActiveRecord
|
|
458
575
|
true
|
459
576
|
end
|
460
577
|
|
578
|
+
def supports_nulls_not_distinct?
|
579
|
+
false
|
580
|
+
end
|
581
|
+
|
582
|
+
def return_value_after_insert?(column) # :nodoc:
|
583
|
+
column.auto_incremented_by_db?
|
584
|
+
end
|
585
|
+
|
461
586
|
def async_enabled? # :nodoc:
|
462
587
|
supports_concurrent_connections? &&
|
463
588
|
!ActiveRecord.async_query_executor.nil? && !pool.async_executor.nil?
|
464
589
|
end
|
465
590
|
|
466
591
|
# This is meant to be implemented by the adapters that support extensions
|
467
|
-
def disable_extension(name)
|
592
|
+
def disable_extension(name, **)
|
468
593
|
end
|
469
594
|
|
470
595
|
# This is meant to be implemented by the adapters that support extensions
|
471
|
-
def enable_extension(name)
|
596
|
+
def enable_extension(name, **)
|
472
597
|
end
|
473
598
|
|
474
599
|
# This is meant to be implemented by the adapters that support custom enum types
|
475
600
|
def create_enum(*) # :nodoc:
|
476
601
|
end
|
477
602
|
|
603
|
+
# This is meant to be implemented by the adapters that support custom enum types
|
604
|
+
def drop_enum(*) # :nodoc:
|
605
|
+
end
|
606
|
+
|
607
|
+
# This is meant to be implemented by the adapters that support custom enum types
|
608
|
+
def rename_enum(*) # :nodoc:
|
609
|
+
end
|
610
|
+
|
611
|
+
# This is meant to be implemented by the adapters that support custom enum types
|
612
|
+
def add_enum_value(*) # :nodoc:
|
613
|
+
end
|
614
|
+
|
615
|
+
# This is meant to be implemented by the adapters that support custom enum types
|
616
|
+
def rename_enum_value(*) # :nodoc:
|
617
|
+
end
|
618
|
+
|
478
619
|
def advisory_locks_enabled? # :nodoc:
|
479
620
|
supports_advisory_locks? && @advisory_locks_enabled
|
480
621
|
end
|
@@ -512,7 +653,17 @@ module ActiveRecord
|
|
512
653
|
|
513
654
|
# Override to check all foreign key constraints in a database.
|
514
655
|
def all_foreign_keys_valid?
|
656
|
+
check_all_foreign_keys_valid!
|
515
657
|
true
|
658
|
+
rescue ActiveRecord::StatementInvalid
|
659
|
+
false
|
660
|
+
end
|
661
|
+
deprecate :all_foreign_keys_valid?, deprecator: ActiveRecord.deprecator
|
662
|
+
|
663
|
+
# Override to check all foreign key constraints in a database.
|
664
|
+
# The adapter should raise a +ActiveRecord::StatementInvalid+ if foreign key
|
665
|
+
# constraints are not met.
|
666
|
+
def check_all_foreign_keys_valid!
|
516
667
|
end
|
517
668
|
|
518
669
|
# CONNECTION MANAGEMENT ====================================
|
@@ -523,19 +674,50 @@ module ActiveRecord
|
|
523
674
|
def active?
|
524
675
|
end
|
525
676
|
|
526
|
-
# Disconnects from the database if already connected, and establishes a
|
527
|
-
#
|
528
|
-
#
|
529
|
-
def reconnect!
|
530
|
-
|
531
|
-
|
677
|
+
# Disconnects from the database if already connected, and establishes a new
|
678
|
+
# connection with the database. Implementors should define private #reconnect
|
679
|
+
# instead.
|
680
|
+
def reconnect!(restore_transactions: false)
|
681
|
+
retries_available = connection_retries
|
682
|
+
deadline = retry_deadline && Process.clock_gettime(Process::CLOCK_MONOTONIC) + retry_deadline
|
683
|
+
|
684
|
+
@lock.synchronize do
|
685
|
+
reconnect
|
686
|
+
|
687
|
+
enable_lazy_transactions!
|
688
|
+
@raw_connection_dirty = false
|
689
|
+
@verified = true
|
690
|
+
|
691
|
+
reset_transaction(restore: restore_transactions) do
|
692
|
+
clear_cache!(new_connection: true)
|
693
|
+
configure_connection
|
694
|
+
end
|
695
|
+
rescue => original_exception
|
696
|
+
translated_exception = translate_exception_class(original_exception, nil, nil)
|
697
|
+
retry_deadline_exceeded = deadline && deadline < Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
698
|
+
|
699
|
+
if !retry_deadline_exceeded && retries_available > 0
|
700
|
+
retries_available -= 1
|
701
|
+
|
702
|
+
if retryable_connection_error?(translated_exception)
|
703
|
+
backoff(connection_retries - retries_available)
|
704
|
+
retry
|
705
|
+
end
|
706
|
+
end
|
707
|
+
|
708
|
+
@verified = false
|
709
|
+
|
710
|
+
raise translated_exception
|
711
|
+
end
|
532
712
|
end
|
533
713
|
|
714
|
+
|
534
715
|
# Disconnects from the database if already connected. Otherwise, this
|
535
716
|
# method does nothing.
|
536
717
|
def disconnect!
|
537
|
-
clear_cache!
|
718
|
+
clear_cache!(new_connection: true)
|
538
719
|
reset_transaction
|
720
|
+
@raw_connection_dirty = false
|
539
721
|
end
|
540
722
|
|
541
723
|
# Immediately forget this connection ever existed. Unlike disconnect!,
|
@@ -546,22 +728,20 @@ module ActiveRecord
|
|
546
728
|
# rid of a connection that belonged to its parent.
|
547
729
|
def discard!
|
548
730
|
# This should be overridden by concrete adapters.
|
549
|
-
#
|
550
|
-
# Prevent @connection's finalizer from touching the socket, or
|
551
|
-
# otherwise communicating with its server, when it is collected.
|
552
|
-
if schema_cache.connection == self
|
553
|
-
schema_cache.connection = nil
|
554
|
-
end
|
555
731
|
end
|
556
732
|
|
557
733
|
# Reset the state of this connection, directing the DBMS to clear
|
558
734
|
# transactions and other connection-related server-side state. Usually a
|
559
735
|
# database-dependent operation.
|
560
736
|
#
|
561
|
-
#
|
562
|
-
#
|
737
|
+
# If a database driver or protocol does not support such a feature,
|
738
|
+
# implementors may alias this to #reconnect!. Otherwise, implementors
|
739
|
+
# should call super immediately after resetting the connection (and while
|
740
|
+
# still holding @lock).
|
563
741
|
def reset!
|
564
|
-
|
742
|
+
clear_cache!(new_connection: true)
|
743
|
+
reset_transaction
|
744
|
+
configure_connection
|
565
745
|
end
|
566
746
|
|
567
747
|
# Removes the connection from the pool and disconnect it.
|
@@ -571,8 +751,16 @@ module ActiveRecord
|
|
571
751
|
end
|
572
752
|
|
573
753
|
# Clear any caching the database adapter may be doing.
|
574
|
-
def clear_cache!
|
575
|
-
|
754
|
+
def clear_cache!(new_connection: false)
|
755
|
+
if @statements
|
756
|
+
@lock.synchronize do
|
757
|
+
if new_connection
|
758
|
+
@statements.reset
|
759
|
+
else
|
760
|
+
@statements.clear
|
761
|
+
end
|
762
|
+
end
|
763
|
+
end
|
576
764
|
end
|
577
765
|
|
578
766
|
# Returns true if its required to reload the connection between requests for development mode.
|
@@ -584,7 +772,33 @@ module ActiveRecord
|
|
584
772
|
# This is done under the hood by calling #active?. If the connection
|
585
773
|
# is no longer active, then this method will reconnect to the database.
|
586
774
|
def verify!
|
587
|
-
|
775
|
+
unless active?
|
776
|
+
if @unconfigured_connection
|
777
|
+
@lock.synchronize do
|
778
|
+
if @unconfigured_connection
|
779
|
+
@raw_connection = @unconfigured_connection
|
780
|
+
@unconfigured_connection = nil
|
781
|
+
configure_connection
|
782
|
+
@verified = true
|
783
|
+
return
|
784
|
+
end
|
785
|
+
end
|
786
|
+
end
|
787
|
+
|
788
|
+
reconnect!(restore_transactions: true)
|
789
|
+
end
|
790
|
+
|
791
|
+
@verified = true
|
792
|
+
end
|
793
|
+
|
794
|
+
def connect!
|
795
|
+
verify!
|
796
|
+
self
|
797
|
+
end
|
798
|
+
|
799
|
+
def clean! # :nodoc:
|
800
|
+
@raw_connection_dirty = false
|
801
|
+
@verified = nil
|
588
802
|
end
|
589
803
|
|
590
804
|
# Provides access to the underlying database driver for this adapter. For
|
@@ -593,9 +807,16 @@ module ActiveRecord
|
|
593
807
|
#
|
594
808
|
# This is useful for when you need to call a proprietary method such as
|
595
809
|
# PostgreSQL's lo_* methods.
|
810
|
+
#
|
811
|
+
# Active Record cannot track if the database is getting modified using
|
812
|
+
# this client. If that is the case, generally you'll want to invalidate
|
813
|
+
# the query cache using +ActiveRecord::Base.clear_query_cache+.
|
596
814
|
def raw_connection
|
597
|
-
|
598
|
-
|
815
|
+
with_raw_connection do |conn|
|
816
|
+
disable_lazy_transactions!
|
817
|
+
@raw_connection_dirty = true
|
818
|
+
conn
|
819
|
+
end
|
599
820
|
end
|
600
821
|
|
601
822
|
def default_uniqueness_comparison(attribute, value) # :nodoc:
|
@@ -661,16 +882,22 @@ module ActiveRecord
|
|
661
882
|
migration_context.current_version
|
662
883
|
end
|
663
884
|
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
885
|
+
class << self
|
886
|
+
def register_class_with_precision(mapping, key, klass, **kwargs) # :nodoc:
|
887
|
+
mapping.register_type(key) do |*args|
|
888
|
+
precision = extract_precision(args.last)
|
889
|
+
klass.new(precision: precision, **kwargs)
|
890
|
+
end
|
668
891
|
end
|
669
892
|
|
670
|
-
|
671
|
-
|
893
|
+
def extended_type_map(default_timezone:) # :nodoc:
|
894
|
+
Type::TypeMap.new(self::TYPE_MAP).tap do |m|
|
895
|
+
register_class_with_precision m, %r(\A[^\(]*time)i, Type::Time, timezone: default_timezone
|
896
|
+
register_class_with_precision m, %r(\A[^\(]*datetime)i, Type::DateTime, timezone: default_timezone
|
897
|
+
m.alias_type %r(\A[^\(]*timestamp)i, "datetime"
|
898
|
+
end
|
899
|
+
end
|
672
900
|
|
673
|
-
class << self
|
674
901
|
private
|
675
902
|
def initialize_type_map(m)
|
676
903
|
register_class_with_limit m, %r(boolean)i, Type::Boolean
|
@@ -712,13 +939,6 @@ module ActiveRecord
|
|
712
939
|
end
|
713
940
|
end
|
714
941
|
|
715
|
-
def register_class_with_precision(mapping, key, klass)
|
716
|
-
mapping.register_type(key) do |*args|
|
717
|
-
precision = extract_precision(args.last)
|
718
|
-
klass.new(precision: precision)
|
719
|
-
end
|
720
|
-
end
|
721
|
-
|
722
942
|
def extract_scale(sql_type)
|
723
943
|
case sql_type
|
724
944
|
when /\((\d+)\)/ then 0
|
@@ -736,10 +956,168 @@ module ActiveRecord
|
|
736
956
|
end
|
737
957
|
|
738
958
|
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
|
959
|
+
EXTENDED_TYPE_MAPS = Concurrent::Map.new
|
739
960
|
|
740
961
|
private
|
962
|
+
def reconnect_can_restore_state?
|
963
|
+
transaction_manager.restorable? && !@raw_connection_dirty
|
964
|
+
end
|
965
|
+
|
966
|
+
# Lock the monitor, ensure we're properly connected and
|
967
|
+
# transactions are materialized, and then yield the underlying
|
968
|
+
# raw connection object.
|
969
|
+
#
|
970
|
+
# If +allow_retry+ is true, a connection-related exception will
|
971
|
+
# cause an automatic reconnect and re-run of the block, up to
|
972
|
+
# the connection's configured +connection_retries+ setting
|
973
|
+
# and the configured +retry_deadline+ limit.
|
974
|
+
#
|
975
|
+
# If +materialize_transactions+ is false, the block will be run without
|
976
|
+
# ensuring virtual transactions have been materialized in the DB
|
977
|
+
# server's state. The active transaction will also remain clean
|
978
|
+
# (if it is not already dirty), meaning it's able to be restored
|
979
|
+
# by reconnecting and opening an equivalent-depth set of new
|
980
|
+
# transactions. This should only be used by transaction control
|
981
|
+
# methods, and internal transaction-agnostic queries.
|
982
|
+
#
|
983
|
+
###
|
984
|
+
#
|
985
|
+
# It's not the primary use case, so not something to optimize
|
986
|
+
# for, but note that this method does need to be re-entrant:
|
987
|
+
# +materialize_transactions+ will re-enter if it has work to do,
|
988
|
+
# and the yield block can also do so under some circumstances.
|
989
|
+
#
|
990
|
+
# In the latter case, we really ought to guarantee the inner
|
991
|
+
# call will not reconnect (which would interfere with the
|
992
|
+
# still-yielded connection in the outer block), but we currently
|
993
|
+
# provide no special enforcement there.
|
994
|
+
#
|
995
|
+
def with_raw_connection(allow_retry: false, materialize_transactions: true)
|
996
|
+
@lock.synchronize do
|
997
|
+
connect! if @raw_connection.nil? && reconnect_can_restore_state?
|
998
|
+
|
999
|
+
self.materialize_transactions if materialize_transactions
|
1000
|
+
|
1001
|
+
retries_available = allow_retry ? connection_retries : 0
|
1002
|
+
deadline = retry_deadline && Process.clock_gettime(Process::CLOCK_MONOTONIC) + retry_deadline
|
1003
|
+
reconnectable = reconnect_can_restore_state?
|
1004
|
+
|
1005
|
+
if @verified
|
1006
|
+
# Cool, we're confident the connection's ready to use. (Note this might have
|
1007
|
+
# become true during the above #materialize_transactions.)
|
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
|
+
result = yield @raw_connection
|
1025
|
+
@verified = true
|
1026
|
+
result
|
1027
|
+
rescue => original_exception
|
1028
|
+
translated_exception = translate_exception_class(original_exception, nil, nil)
|
1029
|
+
invalidate_transaction(translated_exception)
|
1030
|
+
retry_deadline_exceeded = deadline && deadline < Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
1031
|
+
|
1032
|
+
if !retry_deadline_exceeded && retries_available > 0
|
1033
|
+
retries_available -= 1
|
1034
|
+
|
1035
|
+
if retryable_query_error?(translated_exception)
|
1036
|
+
backoff(connection_retries - retries_available)
|
1037
|
+
retry
|
1038
|
+
elsif reconnectable && retryable_connection_error?(translated_exception)
|
1039
|
+
reconnect!(restore_transactions: true)
|
1040
|
+
# Only allowed to reconnect once, because reconnect! has its own retry
|
1041
|
+
# loop
|
1042
|
+
reconnectable = false
|
1043
|
+
retry
|
1044
|
+
end
|
1045
|
+
end
|
1046
|
+
|
1047
|
+
unless retryable_query_error?(translated_exception)
|
1048
|
+
# Barring a known-retryable error inside the query (regardless of
|
1049
|
+
# whether we were in a _position_ to retry it), we should infer that
|
1050
|
+
# there's likely a real problem with the connection.
|
1051
|
+
@verified = false
|
1052
|
+
end
|
1053
|
+
|
1054
|
+
raise translated_exception
|
1055
|
+
ensure
|
1056
|
+
dirty_current_transaction if materialize_transactions
|
1057
|
+
end
|
1058
|
+
end
|
1059
|
+
end
|
1060
|
+
|
1061
|
+
def retryable_connection_error?(exception)
|
1062
|
+
exception.is_a?(ConnectionNotEstablished) || exception.is_a?(ConnectionFailed)
|
1063
|
+
end
|
1064
|
+
|
1065
|
+
def invalidate_transaction(exception)
|
1066
|
+
return unless exception.is_a?(TransactionRollbackError)
|
1067
|
+
return unless savepoint_errors_invalidate_transactions?
|
1068
|
+
|
1069
|
+
current_transaction.invalidate!
|
1070
|
+
end
|
1071
|
+
|
1072
|
+
def retryable_query_error?(exception)
|
1073
|
+
# We definitely can't retry if we were inside an invalidated transaction.
|
1074
|
+
return false if current_transaction.invalidated?
|
1075
|
+
|
1076
|
+
exception.is_a?(Deadlocked) || exception.is_a?(LockWaitTimeout)
|
1077
|
+
end
|
1078
|
+
|
1079
|
+
def backoff(counter)
|
1080
|
+
sleep 0.1 * counter
|
1081
|
+
end
|
1082
|
+
|
1083
|
+
def reconnect
|
1084
|
+
raise NotImplementedError
|
1085
|
+
end
|
1086
|
+
|
1087
|
+
# Returns a raw connection for internal use with methods that are known
|
1088
|
+
# to both be thread-safe and not rely upon actual server communication.
|
1089
|
+
# This is useful for e.g. string escaping methods.
|
1090
|
+
def any_raw_connection
|
1091
|
+
@raw_connection || valid_raw_connection
|
1092
|
+
end
|
1093
|
+
|
1094
|
+
# Similar to any_raw_connection, but ensures it is validated and
|
1095
|
+
# connected. Any method called on this result still needs to be
|
1096
|
+
# independently thread-safe, so it probably shouldn't talk to the
|
1097
|
+
# server... but some drivers fail if they know the connection has gone
|
1098
|
+
# away.
|
1099
|
+
def valid_raw_connection
|
1100
|
+
(@verified && @raw_connection) ||
|
1101
|
+
# `allow_retry: false`, to force verification: the block won't
|
1102
|
+
# raise, so a retry wouldn't help us get the valid connection we
|
1103
|
+
# need.
|
1104
|
+
with_raw_connection(allow_retry: false, materialize_transactions: false) { |conn| conn }
|
1105
|
+
end
|
1106
|
+
|
1107
|
+
def extended_type_map_key
|
1108
|
+
if @default_timezone
|
1109
|
+
{ default_timezone: @default_timezone }
|
1110
|
+
end
|
1111
|
+
end
|
1112
|
+
|
741
1113
|
def type_map
|
742
|
-
|
1114
|
+
if key = extended_type_map_key
|
1115
|
+
self.class::EXTENDED_TYPE_MAPS.compute_if_absent(key) do
|
1116
|
+
self.class.extended_type_map(**key)
|
1117
|
+
end
|
1118
|
+
else
|
1119
|
+
self.class::TYPE_MAP
|
1120
|
+
end
|
743
1121
|
end
|
744
1122
|
|
745
1123
|
def translate_exception_class(e, sql, binds)
|
@@ -761,16 +1139,16 @@ module ActiveRecord
|
|
761
1139
|
type_casted_binds: type_casted_binds,
|
762
1140
|
statement_name: statement_name,
|
763
1141
|
async: async,
|
764
|
-
connection: self
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
|
1142
|
+
connection: self,
|
1143
|
+
&block
|
1144
|
+
)
|
1145
|
+
rescue ActiveRecord::StatementInvalid => ex
|
1146
|
+
raise ex.set_query(sql, binds)
|
769
1147
|
end
|
770
1148
|
|
771
1149
|
def transform_query(sql)
|
772
1150
|
ActiveRecord.query_transformers.each do |transformer|
|
773
|
-
sql = transformer.call(sql)
|
1151
|
+
sql = transformer.call(sql, self)
|
774
1152
|
end
|
775
1153
|
sql
|
776
1154
|
end
|
@@ -778,10 +1156,10 @@ module ActiveRecord
|
|
778
1156
|
def translate_exception(exception, message:, sql:, binds:)
|
779
1157
|
# override in derived class
|
780
1158
|
case exception
|
781
|
-
when RuntimeError
|
1159
|
+
when RuntimeError, ActiveRecord::ActiveRecordError
|
782
1160
|
exception
|
783
1161
|
else
|
784
|
-
ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds)
|
1162
|
+
ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
785
1163
|
end
|
786
1164
|
end
|
787
1165
|
|
@@ -828,6 +1206,26 @@ module ActiveRecord
|
|
828
1206
|
def build_result(columns:, rows:, column_types: {})
|
829
1207
|
ActiveRecord::Result.new(columns, rows, column_types)
|
830
1208
|
end
|
1209
|
+
|
1210
|
+
# Perform any necessary initialization upon the newly-established
|
1211
|
+
# @raw_connection -- this is the place to modify the adapter's
|
1212
|
+
# connection settings, run queries to configure any application-global
|
1213
|
+
# "session" variables, etc.
|
1214
|
+
#
|
1215
|
+
# Implementations may assume this method will only be called while
|
1216
|
+
# holding @lock (or from #initialize).
|
1217
|
+
def configure_connection
|
1218
|
+
end
|
1219
|
+
|
1220
|
+
def default_prepared_statements
|
1221
|
+
true
|
1222
|
+
end
|
1223
|
+
|
1224
|
+
def warning_ignored?(warning)
|
1225
|
+
ActiveRecord.db_warnings_ignore.any? do |warning_matcher|
|
1226
|
+
warning.message.match?(warning_matcher) || warning.code.to_s.match?(warning_matcher)
|
1227
|
+
end
|
1228
|
+
end
|
831
1229
|
end
|
832
1230
|
end
|
833
1231
|
end
|