activerecord 7.0.0 → 7.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 +515 -1268
- data/MIT-LICENSE +1 -1
- data/README.rdoc +31 -31
- 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 +28 -17
- data/lib/active_record/associations/collection_proxy.rb +36 -13
- 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 +28 -18
- 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/join_association.rb +27 -25
- data/lib/active_record/associations/join_dependency.rb +18 -14
- 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 +2 -4
- 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 +378 -491
- data/lib/active_record/attribute_assignment.rb +1 -13
- 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 +153 -70
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -6
- data/lib/active_record/attribute_methods/write.rb +6 -6
- data/lib/active_record/attribute_methods.rb +153 -40
- data/lib/active_record/attributes.rb +63 -48
- data/lib/active_record/autosave_association.rb +70 -38
- data/lib/active_record/base.rb +12 -8
- 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 +124 -132
- 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 +297 -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 +215 -63
- data/lib/active_record/connection_adapters/abstract/quoting.rb +83 -65
- 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 +319 -135
- data/lib/active_record/connection_adapters/abstract/transaction.rb +367 -75
- data/lib/active_record/connection_adapters/abstract_adapter.rb +512 -126
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +282 -119
- 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 +27 -140
- data/lib/active_record/connection_adapters/mysql/quoting.rb +64 -52
- 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 +45 -14
- 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 +16 -3
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +101 -48
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -2
- 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 +4 -2
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +94 -61
- 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 +151 -2
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +379 -66
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +370 -203
- data/lib/active_record/connection_adapters/schema_cache.rb +302 -79
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +60 -43
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +61 -46
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +20 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +16 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +64 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +321 -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 +98 -106
- data/lib/active_record/core.rb +220 -177
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +8 -2
- 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 +88 -35
- data/lib/active_record/delegated_type.rb +40 -11
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +3 -1
- data/lib/active_record/disable_joins_association_relation.rb +1 -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 +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 +47 -25
- data/lib/active_record/encryption/encrypted_attribute_type.rb +49 -14
- data/lib/active_record/encryption/encryptor.rb +25 -10
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +3 -3
- data/lib/active_record/encryption/extended_deterministic_queries.rb +83 -86
- 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_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 +4 -4
- data/lib/active_record/encryption/read_only_null_encryptor.rb +4 -0
- data/lib/active_record/encryption/scheme.rb +23 -22
- data/lib/active_record/encryption.rb +1 -0
- data/lib/active_record/enum.rb +131 -27
- data/lib/active_record/errors.rb +151 -31
- data/lib/active_record/explain.rb +21 -12
- 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 +169 -99
- data/lib/active_record/future_result.rb +47 -8
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +34 -18
- data/lib/active_record/insert_all.rb +72 -22
- data/lib/active_record/integration.rb +13 -10
- data/lib/active_record/internal_metadata.rb +124 -20
- data/lib/active_record/locking/optimistic.rb +39 -24
- data/lib/active_record/locking/pessimistic.rb +8 -5
- data/lib/active_record/log_subscriber.rb +28 -27
- 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 +110 -13
- data/lib/active_record/migration/compatibility.rb +174 -64
- 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 +292 -125
- data/lib/active_record/model_schema.rb +113 -112
- data/lib/active_record/nested_attributes.rb +35 -9
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +177 -345
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +19 -25
- data/lib/active_record/query_logs.rb +102 -51
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +34 -9
- data/lib/active_record/railtie.rb +153 -100
- data/lib/active_record/railties/controller_runtime.rb +24 -10
- data/lib/active_record/railties/databases.rake +148 -152
- 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 +278 -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 +293 -108
- data/lib/active_record/relation/delegation.rb +31 -20
- 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 +38 -4
- 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 +25 -1
- data/lib/active_record/relation/query_methods.rb +625 -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 +602 -96
- data/lib/active_record/result.rb +55 -52
- data/lib/active_record/runtime_registry.rb +63 -1
- data/lib/active_record/sanitization.rb +76 -30
- data/lib/active_record/schema.rb +39 -23
- data/lib/active_record/schema_dumper.rb +82 -30
- data/lib/active_record/schema_migration.rb +75 -24
- data/lib/active_record/scoping/default.rb +20 -12
- 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/serialization.rb +5 -0
- data/lib/active_record/signed_id.rb +29 -8
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/store.rb +16 -11
- data/lib/active_record/suppressor.rb +3 -1
- data/lib/active_record/table_metadata.rb +7 -3
- data/lib/active_record/tasks/database_tasks.rb +191 -121
- 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 +16 -7
- data/lib/active_record/test_fixtures.rb +174 -152
- 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 +109 -27
- data/lib/active_record/translation.rb +1 -3
- 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 +9 -7
- 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 +12 -6
- 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 +63 -14
- data/lib/active_record/validations.rb +12 -5
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +266 -30
- 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/filter_predications.rb +1 -1
- 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/filter.rb +1 -1
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/homogeneous_in.rb +1 -9
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/{and.rb → nary.rb} +9 -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/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 +59 -17
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -63
|
@@ -4,6 +4,7 @@ require "set"
|
|
|
4
4
|
require "active_record/connection_adapters/sql_type_metadata"
|
|
5
5
|
require "active_record/connection_adapters/abstract/schema_dumper"
|
|
6
6
|
require "active_record/connection_adapters/abstract/schema_creation"
|
|
7
|
+
require "active_support/concurrency/null_lock"
|
|
7
8
|
require "active_support/concurrency/load_interlock_aware_monitor"
|
|
8
9
|
require "arel/collectors/bind"
|
|
9
10
|
require "arel/collectors/composite"
|
|
@@ -12,6 +13,8 @@ require "arel/collectors/substitute_binds"
|
|
|
12
13
|
|
|
13
14
|
module ActiveRecord
|
|
14
15
|
module ConnectionAdapters # :nodoc:
|
|
16
|
+
# = Active Record Abstract Adapter
|
|
17
|
+
#
|
|
15
18
|
# Active Record supports multiple database systems. AbstractAdapter and
|
|
16
19
|
# related classes form the abstraction layer which makes this possible.
|
|
17
20
|
# An AbstractAdapter represents a connection to a database, and provides an
|
|
@@ -20,7 +23,7 @@ module ActiveRecord
|
|
|
20
23
|
# and +:limit+ options, etc.
|
|
21
24
|
#
|
|
22
25
|
# All the concrete database adapters follow the interface laid down in this class.
|
|
23
|
-
# {ActiveRecord::Base.
|
|
26
|
+
# {ActiveRecord::Base.lease_connection}[rdoc-ref:ConnectionHandling#lease_connection] returns an AbstractAdapter object, which
|
|
24
27
|
# you can use.
|
|
25
28
|
#
|
|
26
29
|
# Most of the methods in the adapter are useful during migrations. Most
|
|
@@ -36,12 +39,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,35 +90,105 @@ module ActiveRecord
|
|
|
71
90
|
/\A(?:[(\s]|#{COMMENT_REGEX})*#{Regexp.union(*parts)}/
|
|
72
91
|
end
|
|
73
92
|
|
|
74
|
-
def self.
|
|
75
|
-
|
|
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
|
|
76
119
|
end
|
|
77
120
|
|
|
78
|
-
|
|
79
|
-
|
|
121
|
+
# Opens a database console session.
|
|
122
|
+
def self.dbconsole(config, options = {})
|
|
123
|
+
raise NotImplementedError
|
|
80
124
|
end
|
|
81
125
|
|
|
82
|
-
def initialize(
|
|
126
|
+
def initialize(config_or_deprecated_connection, deprecated_logger = nil, deprecated_connection_options = nil, deprecated_config = nil) # :nodoc:
|
|
83
127
|
super()
|
|
84
128
|
|
|
85
|
-
@
|
|
86
|
-
@
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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)
|
|
92
157
|
@visitor = arel_visitor
|
|
93
158
|
@statements = build_statement_pool
|
|
94
|
-
|
|
159
|
+
self.lock_thread = nil
|
|
95
160
|
|
|
96
|
-
@prepared_statements = self.class.type_cast_config_to_boolean(
|
|
97
|
-
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 }
|
|
98
163
|
)
|
|
99
164
|
|
|
100
165
|
@advisory_locks_enabled = self.class.type_cast_config_to_boolean(
|
|
101
|
-
config.fetch(:advisory_locks, true)
|
|
166
|
+
@config.fetch(:advisory_locks, true)
|
|
102
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
|
|
103
192
|
end
|
|
104
193
|
|
|
105
194
|
EXCEPTION_NEVER = { Exception => :never }.freeze # :nodoc:
|
|
@@ -125,53 +214,33 @@ module ActiveRecord
|
|
|
125
214
|
@config[:replica] || false
|
|
126
215
|
end
|
|
127
216
|
|
|
128
|
-
def
|
|
129
|
-
@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
|
|
130
231
|
end
|
|
131
232
|
|
|
132
233
|
# Determines whether writes are currently being prevented.
|
|
133
234
|
#
|
|
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+.
|
|
235
|
+
# Returns true if the connection is a replica or returns
|
|
236
|
+
# the value of +current_preventing_writes+.
|
|
141
237
|
def preventing_writes?
|
|
142
238
|
return true if replica?
|
|
143
|
-
return ActiveRecord::Base.connection_handler.prevent_writes if ActiveRecord.legacy_connection_handling
|
|
144
239
|
return false if connection_class.nil?
|
|
145
240
|
|
|
146
241
|
connection_class.current_preventing_writes
|
|
147
242
|
end
|
|
148
243
|
|
|
149
|
-
def migrations_paths # :nodoc:
|
|
150
|
-
@config[:migrations_paths] || Migrator.migrations_paths
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
def migration_context # :nodoc:
|
|
154
|
-
MigrationContext.new(migrations_paths, schema_migration)
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
def schema_migration # :nodoc:
|
|
158
|
-
@schema_migration ||= begin
|
|
159
|
-
conn = self
|
|
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 }
|
|
169
|
-
|
|
170
|
-
self.connection_specification_name = spec_name
|
|
171
|
-
end
|
|
172
|
-
end
|
|
173
|
-
end
|
|
174
|
-
|
|
175
244
|
def prepared_statements?
|
|
176
245
|
@prepared_statements && !prepared_statements_disabled_cache.include?(object_id)
|
|
177
246
|
end
|
|
@@ -208,50 +277,45 @@ module ActiveRecord
|
|
|
208
277
|
def lease
|
|
209
278
|
if in_use?
|
|
210
279
|
msg = +"Cannot lease connection, "
|
|
211
|
-
if @owner ==
|
|
280
|
+
if @owner == ActiveSupport::IsolatedExecutionState.context
|
|
212
281
|
msg << "it is already leased by the current thread."
|
|
213
282
|
else
|
|
214
283
|
msg << "it is already in use by a different thread: #{@owner}. " \
|
|
215
|
-
"Current thread: #{
|
|
284
|
+
"Current thread: #{ActiveSupport::IsolatedExecutionState.context}."
|
|
216
285
|
end
|
|
217
286
|
raise ActiveRecordError, msg
|
|
218
287
|
end
|
|
219
288
|
|
|
220
|
-
@owner =
|
|
289
|
+
@owner = ActiveSupport::IsolatedExecutionState.context
|
|
221
290
|
end
|
|
222
291
|
|
|
223
292
|
def connection_class # :nodoc:
|
|
224
293
|
@pool.connection_class
|
|
225
294
|
end
|
|
226
295
|
|
|
227
|
-
# The role (
|
|
228
|
-
# non-multi role application,
|
|
296
|
+
# The role (e.g. +:writing+) for the current connection. In a
|
|
297
|
+
# non-multi role application, +:writing+ is returned.
|
|
229
298
|
def role
|
|
230
299
|
@pool.role
|
|
231
300
|
end
|
|
232
301
|
|
|
233
|
-
# The shard (
|
|
234
|
-
# a non-sharded application,
|
|
302
|
+
# The shard (e.g. +:default+) for the current connection. In
|
|
303
|
+
# a non-sharded application, +:default+ is returned.
|
|
235
304
|
def shard
|
|
236
305
|
@pool.shard
|
|
237
306
|
end
|
|
238
307
|
|
|
239
308
|
def schema_cache
|
|
240
|
-
@pool.
|
|
241
|
-
end
|
|
242
|
-
|
|
243
|
-
def schema_cache=(cache)
|
|
244
|
-
cache.connection = self
|
|
245
|
-
@pool.set_schema_cache(cache)
|
|
309
|
+
@pool.schema_cache || (@schema_cache ||= BoundSchemaReflection.for_lone_connection(@pool.schema_reflection, self))
|
|
246
310
|
end
|
|
247
311
|
|
|
248
312
|
# this method must only be called while holding connection pool's mutex
|
|
249
313
|
def expire
|
|
250
314
|
if in_use?
|
|
251
|
-
if @owner !=
|
|
315
|
+
if @owner != ActiveSupport::IsolatedExecutionState.context
|
|
252
316
|
raise ActiveRecordError, "Cannot expire connection, " \
|
|
253
317
|
"it is owned by a different thread: #{@owner}. " \
|
|
254
|
-
"Current thread: #{
|
|
318
|
+
"Current thread: #{ActiveSupport::IsolatedExecutionState.context}."
|
|
255
319
|
end
|
|
256
320
|
|
|
257
321
|
@idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
@@ -264,10 +328,10 @@ module ActiveRecord
|
|
|
264
328
|
# this method must only be called while holding connection pool's mutex (and a desire for segfaults)
|
|
265
329
|
def steal! # :nodoc:
|
|
266
330
|
if in_use?
|
|
267
|
-
if @owner !=
|
|
331
|
+
if @owner != ActiveSupport::IsolatedExecutionState.context
|
|
268
332
|
pool.send :remove_connection_from_thread_cache, self, @owner
|
|
269
333
|
|
|
270
|
-
@owner =
|
|
334
|
+
@owner = ActiveSupport::IsolatedExecutionState.context
|
|
271
335
|
end
|
|
272
336
|
else
|
|
273
337
|
raise ActiveRecordError, "Cannot steal connection, it is not currently leased."
|
|
@@ -295,7 +359,14 @@ module ActiveRecord
|
|
|
295
359
|
|
|
296
360
|
# Does the database for this adapter exist?
|
|
297
361
|
def self.database_exists?(config)
|
|
298
|
-
|
|
362
|
+
new(config).database_exists?
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
def database_exists?
|
|
366
|
+
connect!
|
|
367
|
+
true
|
|
368
|
+
rescue ActiveRecord::NoDatabaseError
|
|
369
|
+
false
|
|
299
370
|
end
|
|
300
371
|
|
|
301
372
|
# Does this adapter support DDL rollbacks in transactions? That is, would
|
|
@@ -313,6 +384,16 @@ module ActiveRecord
|
|
|
313
384
|
false
|
|
314
385
|
end
|
|
315
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
|
+
|
|
316
397
|
# Does this adapter support application-enforced advisory locking?
|
|
317
398
|
def supports_advisory_locks?
|
|
318
399
|
false
|
|
@@ -339,6 +420,11 @@ module ActiveRecord
|
|
|
339
420
|
false
|
|
340
421
|
end
|
|
341
422
|
|
|
423
|
+
# Does this adapter support including non-key columns?
|
|
424
|
+
def supports_index_include?
|
|
425
|
+
false
|
|
426
|
+
end
|
|
427
|
+
|
|
342
428
|
# Does this adapter support expression indices?
|
|
343
429
|
def supports_expression_index?
|
|
344
430
|
false
|
|
@@ -385,6 +471,16 @@ module ActiveRecord
|
|
|
385
471
|
false
|
|
386
472
|
end
|
|
387
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
|
+
|
|
388
484
|
# Does this adapter support views?
|
|
389
485
|
def supports_views?
|
|
390
486
|
false
|
|
@@ -400,7 +496,7 @@ module ActiveRecord
|
|
|
400
496
|
false
|
|
401
497
|
end
|
|
402
498
|
|
|
403
|
-
# Does this adapter support
|
|
499
|
+
# Does this adapter support JSON data type?
|
|
404
500
|
def supports_json?
|
|
405
501
|
false
|
|
406
502
|
end
|
|
@@ -458,23 +554,47 @@ module ActiveRecord
|
|
|
458
554
|
true
|
|
459
555
|
end
|
|
460
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
|
+
|
|
461
565
|
def async_enabled? # :nodoc:
|
|
462
566
|
supports_concurrent_connections? &&
|
|
463
567
|
!ActiveRecord.async_query_executor.nil? && !pool.async_executor.nil?
|
|
464
568
|
end
|
|
465
569
|
|
|
466
570
|
# This is meant to be implemented by the adapters that support extensions
|
|
467
|
-
def disable_extension(name)
|
|
571
|
+
def disable_extension(name, **)
|
|
468
572
|
end
|
|
469
573
|
|
|
470
574
|
# This is meant to be implemented by the adapters that support extensions
|
|
471
|
-
def enable_extension(name)
|
|
575
|
+
def enable_extension(name, **)
|
|
472
576
|
end
|
|
473
577
|
|
|
474
578
|
# This is meant to be implemented by the adapters that support custom enum types
|
|
475
579
|
def create_enum(*) # :nodoc:
|
|
476
580
|
end
|
|
477
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
|
+
|
|
478
598
|
def advisory_locks_enabled? # :nodoc:
|
|
479
599
|
supports_advisory_locks? && @advisory_locks_enabled
|
|
480
600
|
end
|
|
@@ -511,31 +631,71 @@ module ActiveRecord
|
|
|
511
631
|
end
|
|
512
632
|
|
|
513
633
|
# Override to check all foreign key constraints in a database.
|
|
514
|
-
|
|
515
|
-
|
|
634
|
+
# The adapter should raise a +ActiveRecord::StatementInvalid+ if foreign key
|
|
635
|
+
# constraints are not met.
|
|
636
|
+
def check_all_foreign_keys_valid!
|
|
516
637
|
end
|
|
517
638
|
|
|
518
639
|
# CONNECTION MANAGEMENT ====================================
|
|
519
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
|
+
|
|
520
648
|
# Checks whether the connection to the database is still active. This includes
|
|
521
649
|
# checking whether the database is actually capable of responding, i.e. whether
|
|
522
650
|
# the connection isn't stale.
|
|
523
651
|
def active?
|
|
524
652
|
end
|
|
525
653
|
|
|
526
|
-
# Disconnects from the database if already connected, and establishes a
|
|
527
|
-
#
|
|
528
|
-
#
|
|
529
|
-
def reconnect!
|
|
530
|
-
|
|
531
|
-
|
|
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
|
|
532
689
|
end
|
|
533
690
|
|
|
534
691
|
# Disconnects from the database if already connected. Otherwise, this
|
|
535
692
|
# method does nothing.
|
|
536
693
|
def disconnect!
|
|
537
|
-
|
|
538
|
-
|
|
694
|
+
@lock.synchronize do
|
|
695
|
+
clear_cache!(new_connection: true)
|
|
696
|
+
reset_transaction
|
|
697
|
+
@raw_connection_dirty = false
|
|
698
|
+
end
|
|
539
699
|
end
|
|
540
700
|
|
|
541
701
|
# Immediately forget this connection ever existed. Unlike disconnect!,
|
|
@@ -546,22 +706,20 @@ module ActiveRecord
|
|
|
546
706
|
# rid of a connection that belonged to its parent.
|
|
547
707
|
def discard!
|
|
548
708
|
# 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
709
|
end
|
|
556
710
|
|
|
557
711
|
# Reset the state of this connection, directing the DBMS to clear
|
|
558
712
|
# transactions and other connection-related server-side state. Usually a
|
|
559
713
|
# database-dependent operation.
|
|
560
714
|
#
|
|
561
|
-
#
|
|
562
|
-
#
|
|
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).
|
|
563
719
|
def reset!
|
|
564
|
-
|
|
720
|
+
clear_cache!(new_connection: true)
|
|
721
|
+
reset_transaction
|
|
722
|
+
configure_connection
|
|
565
723
|
end
|
|
566
724
|
|
|
567
725
|
# Removes the connection from the pool and disconnect it.
|
|
@@ -571,8 +729,16 @@ module ActiveRecord
|
|
|
571
729
|
end
|
|
572
730
|
|
|
573
731
|
# Clear any caching the database adapter may be doing.
|
|
574
|
-
def clear_cache!
|
|
575
|
-
|
|
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
|
|
576
742
|
end
|
|
577
743
|
|
|
578
744
|
# Returns true if its required to reload the connection between requests for development mode.
|
|
@@ -584,7 +750,31 @@ module ActiveRecord
|
|
|
584
750
|
# This is done under the hood by calling #active?. If the connection
|
|
585
751
|
# is no longer active, then this method will reconnect to the database.
|
|
586
752
|
def verify!
|
|
587
|
-
|
|
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
|
|
588
778
|
end
|
|
589
779
|
|
|
590
780
|
# Provides access to the underlying database driver for this adapter. For
|
|
@@ -593,9 +783,16 @@ module ActiveRecord
|
|
|
593
783
|
#
|
|
594
784
|
# This is useful for when you need to call a proprietary method such as
|
|
595
785
|
# PostgreSQL's lo_* methods.
|
|
786
|
+
#
|
|
787
|
+
# Active Record cannot track if the database is getting modified using
|
|
788
|
+
# this client. If that is the case, generally you'll want to invalidate
|
|
789
|
+
# the query cache using +ActiveRecord::Base.clear_query_cache+.
|
|
596
790
|
def raw_connection
|
|
597
|
-
|
|
598
|
-
|
|
791
|
+
with_raw_connection do |conn|
|
|
792
|
+
disable_lazy_transactions!
|
|
793
|
+
@raw_connection_dirty = true
|
|
794
|
+
conn
|
|
795
|
+
end
|
|
599
796
|
end
|
|
600
797
|
|
|
601
798
|
def default_uniqueness_comparison(attribute, value) # :nodoc:
|
|
@@ -647,7 +844,7 @@ module ActiveRecord
|
|
|
647
844
|
end
|
|
648
845
|
|
|
649
846
|
def database_version # :nodoc:
|
|
650
|
-
|
|
847
|
+
pool.server_version(self)
|
|
651
848
|
end
|
|
652
849
|
|
|
653
850
|
def check_version # :nodoc:
|
|
@@ -658,19 +855,25 @@ module ActiveRecord
|
|
|
658
855
|
# numbered migration that has been executed, or 0 if no schema
|
|
659
856
|
# information is present / the database is empty.
|
|
660
857
|
def schema_version
|
|
661
|
-
migration_context.current_version
|
|
858
|
+
pool.migration_context.current_version
|
|
662
859
|
end
|
|
663
860
|
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
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
|
|
668
867
|
end
|
|
669
868
|
|
|
670
|
-
|
|
671
|
-
|
|
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
|
|
672
876
|
|
|
673
|
-
class << self
|
|
674
877
|
private
|
|
675
878
|
def initialize_type_map(m)
|
|
676
879
|
register_class_with_limit m, %r(boolean)i, Type::Boolean
|
|
@@ -712,13 +915,6 @@ module ActiveRecord
|
|
|
712
915
|
end
|
|
713
916
|
end
|
|
714
917
|
|
|
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
918
|
def extract_scale(sql_type)
|
|
723
919
|
case sql_type
|
|
724
920
|
when /\((\d+)\)/ then 0
|
|
@@ -736,10 +932,177 @@ module ActiveRecord
|
|
|
736
932
|
end
|
|
737
933
|
|
|
738
934
|
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
|
|
935
|
+
EXTENDED_TYPE_MAPS = Concurrent::Map.new
|
|
739
936
|
|
|
740
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
|
+
|
|
741
1098
|
def type_map
|
|
742
|
-
|
|
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
|
|
743
1106
|
end
|
|
744
1107
|
|
|
745
1108
|
def translate_exception_class(e, sql, binds)
|
|
@@ -761,16 +1124,18 @@ module ActiveRecord
|
|
|
761
1124
|
type_casted_binds: type_casted_binds,
|
|
762
1125
|
statement_name: statement_name,
|
|
763
1126
|
async: async,
|
|
764
|
-
connection: self
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
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)
|
|
769
1134
|
end
|
|
770
1135
|
|
|
771
1136
|
def transform_query(sql)
|
|
772
1137
|
ActiveRecord.query_transformers.each do |transformer|
|
|
773
|
-
sql = transformer.call(sql)
|
|
1138
|
+
sql = transformer.call(sql, self)
|
|
774
1139
|
end
|
|
775
1140
|
sql
|
|
776
1141
|
end
|
|
@@ -778,10 +1143,10 @@ module ActiveRecord
|
|
|
778
1143
|
def translate_exception(exception, message:, sql:, binds:)
|
|
779
1144
|
# override in derived class
|
|
780
1145
|
case exception
|
|
781
|
-
when RuntimeError
|
|
1146
|
+
when RuntimeError, ActiveRecord::ActiveRecordError
|
|
782
1147
|
exception
|
|
783
1148
|
else
|
|
784
|
-
ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds)
|
|
1149
|
+
ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
785
1150
|
end
|
|
786
1151
|
end
|
|
787
1152
|
|
|
@@ -825,9 +1190,30 @@ module ActiveRecord
|
|
|
825
1190
|
#
|
|
826
1191
|
# This is an internal hook to make possible connection adapters to build
|
|
827
1192
|
# custom result objects with connection-specific data.
|
|
828
|
-
def build_result(columns:, rows:, column_types:
|
|
1193
|
+
def build_result(columns:, rows:, column_types: nil)
|
|
829
1194
|
ActiveRecord::Result.new(columns, rows, column_types)
|
|
830
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
|
|
831
1217
|
end
|
|
832
1218
|
end
|
|
833
1219
|
end
|