activerecord 6.1.7 → 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 +1516 -1019
- data/MIT-LICENSE +1 -1
- data/README.rdoc +17 -18
- data/lib/active_record/aggregations.rb +17 -14
- data/lib/active_record/association_relation.rb +1 -11
- data/lib/active_record/associations/association.rb +50 -19
- data/lib/active_record/associations/association_scope.rb +17 -12
- data/lib/active_record/associations/belongs_to_association.rb +28 -9
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +10 -2
- data/lib/active_record/associations/builder/association.rb +11 -5
- data/lib/active_record/associations/builder/belongs_to.rb +40 -14
- data/lib/active_record/associations/builder/collection_association.rb +10 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +1 -5
- data/lib/active_record/associations/builder/has_many.rb +3 -2
- data/lib/active_record/associations/builder/has_one.rb +2 -1
- data/lib/active_record/associations/builder/singular_association.rb +6 -2
- data/lib/active_record/associations/collection_association.rb +35 -31
- data/lib/active_record/associations/collection_proxy.rb +30 -15
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -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 +12 -7
- data/lib/active_record/associations/has_one_association.rb +20 -10
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +26 -16
- data/lib/active_record/associations/preloader/association.rb +207 -52
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +147 -0
- data/lib/active_record/associations/preloader/through_association.rb +50 -14
- data/lib/active_record/associations/preloader.rb +50 -121
- data/lib/active_record/associations/singular_association.rb +9 -3
- data/lib/active_record/associations/through_association.rb +25 -14
- data/lib/active_record/associations.rb +423 -289
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +1 -3
- data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
- data/lib/active_record/attribute_methods/dirty.rb +61 -14
- data/lib/active_record/attribute_methods/primary_key.rb +78 -26
- data/lib/active_record/attribute_methods/query.rb +31 -19
- data/lib/active_record/attribute_methods/read.rb +25 -10
- data/lib/active_record/attribute_methods/serialization.rb +194 -37
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +4 -3
- data/lib/active_record/attribute_methods/write.rb +10 -13
- data/lib/active_record/attribute_methods.rb +121 -40
- data/lib/active_record/attributes.rb +27 -38
- data/lib/active_record/autosave_association.rb +61 -30
- data/lib/active_record/base.rb +25 -2
- data/lib/active_record/callbacks.rb +18 -34
- 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 -46
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +367 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +211 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +96 -590
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +171 -51
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +77 -27
- data/lib/active_record/connection_adapters/abstract/quoting.rb +87 -73
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +21 -20
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +186 -31
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +360 -136
- data/lib/active_record/connection_adapters/abstract/transaction.rb +281 -59
- data/lib/active_record/connection_adapters/abstract_adapter.rb +622 -149
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +285 -156
- data/lib/active_record/connection_adapters/column.rb +13 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +1 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +25 -134
- data/lib/active_record/connection_adapters/mysql/quoting.rb +56 -25
- 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 +104 -53
- data/lib/active_record/connection_adapters/pool_config.rb +20 -11
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +18 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +86 -52
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +5 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -14
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +12 -3
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/timestamp_with_time_zone.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +18 -6
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +89 -56
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +92 -2
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +153 -3
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +381 -69
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +492 -230
- data/lib/active_record/connection_adapters/schema_cache.rb +319 -90
- data/lib/active_record/connection_adapters/sqlite3/column.rb +49 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +65 -53
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +37 -21
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +7 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +294 -102
- 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 +9 -6
- data/lib/active_record/connection_handling.rb +107 -136
- data/lib/active_record/core.rb +194 -224
- data/lib/active_record/counter_cache.rb +46 -25
- data/lib/active_record/database_configurations/connection_url_resolver.rb +2 -1
- data/lib/active_record/database_configurations/database_config.rb +21 -12
- data/lib/active_record/database_configurations/hash_config.rb +84 -16
- data/lib/active_record/database_configurations/url_config.rb +18 -12
- data/lib/active_record/database_configurations.rb +95 -59
- data/lib/active_record/delegated_type.rb +61 -15
- 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 +39 -0
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/encryption/auto_filtered_parameters.rb +66 -0
- data/lib/active_record/encryption/cipher/aes256_gcm.rb +101 -0
- data/lib/active_record/encryption/cipher.rb +53 -0
- data/lib/active_record/encryption/config.rb +68 -0
- data/lib/active_record/encryption/configurable.rb +60 -0
- data/lib/active_record/encryption/context.rb +42 -0
- data/lib/active_record/encryption/contexts.rb +76 -0
- data/lib/active_record/encryption/derived_secret_key_provider.rb +18 -0
- data/lib/active_record/encryption/deterministic_key_provider.rb +14 -0
- data/lib/active_record/encryption/encryptable_record.rb +224 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +151 -0
- data/lib/active_record/encryption/encrypted_fixtures.rb +38 -0
- data/lib/active_record/encryption/encrypting_only_encryptor.rb +12 -0
- data/lib/active_record/encryption/encryptor.rb +155 -0
- data/lib/active_record/encryption/envelope_encryption_key_provider.rb +55 -0
- data/lib/active_record/encryption/errors.rb +15 -0
- data/lib/active_record/encryption/extended_deterministic_queries.rb +172 -0
- data/lib/active_record/encryption/extended_deterministic_uniqueness_validator.rb +28 -0
- data/lib/active_record/encryption/key.rb +28 -0
- data/lib/active_record/encryption/key_generator.rb +53 -0
- data/lib/active_record/encryption/key_provider.rb +46 -0
- data/lib/active_record/encryption/message.rb +33 -0
- data/lib/active_record/encryption/message_serializer.rb +92 -0
- data/lib/active_record/encryption/null_encryptor.rb +21 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +24 -0
- data/lib/active_record/encryption/scheme.rb +96 -0
- data/lib/active_record/encryption.rb +56 -0
- data/lib/active_record/enum.rb +156 -62
- data/lib/active_record/errors.rb +171 -15
- data/lib/active_record/explain.rb +23 -3
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +15 -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 +70 -14
- data/lib/active_record/fixture_set/table_rows.rb +4 -4
- data/lib/active_record/fixtures.rb +131 -86
- data/lib/active_record/future_result.rb +164 -0
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +81 -29
- data/lib/active_record/insert_all.rb +133 -20
- data/lib/active_record/integration.rb +11 -10
- data/lib/active_record/internal_metadata.rb +117 -33
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +36 -21
- data/lib/active_record/locking/pessimistic.rb +15 -6
- data/lib/active_record/log_subscriber.rb +52 -19
- 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 +10 -10
- data/lib/active_record/middleware/database_selector.rb +23 -13
- data/lib/active_record/middleware/shard_selector.rb +62 -0
- data/lib/active_record/migration/command_recorder.rb +108 -13
- data/lib/active_record/migration/compatibility.rb +221 -48
- 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/join_table.rb +1 -1
- data/lib/active_record/migration.rb +355 -171
- data/lib/active_record/model_schema.rb +116 -97
- data/lib/active_record/nested_attributes.rb +36 -15
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/normalization.rb +159 -0
- data/lib/active_record/persistence.rb +405 -85
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +3 -21
- data/lib/active_record/query_logs.rb +174 -0
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +29 -6
- data/lib/active_record/railtie.rb +219 -43
- data/lib/active_record/railties/controller_runtime.rb +13 -9
- data/lib/active_record/railties/databases.rake +185 -249
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +41 -3
- data/lib/active_record/reflection.rb +229 -80
- data/lib/active_record/relation/batches/batch_enumerator.rb +23 -7
- data/lib/active_record/relation/batches.rb +192 -63
- data/lib/active_record/relation/calculations.rb +211 -90
- data/lib/active_record/relation/delegation.rb +27 -13
- data/lib/active_record/relation/finder_methods.rb +108 -51
- data/lib/active_record/relation/merger.rb +22 -13
- 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 +27 -20
- data/lib/active_record/relation/query_attribute.rb +30 -12
- data/lib/active_record/relation/query_methods.rb +654 -127
- data/lib/active_record/relation/record_fetch_warning.rb +7 -9
- data/lib/active_record/relation/spawn_methods.rb +20 -3
- data/lib/active_record/relation/where_clause.rb +10 -19
- data/lib/active_record/relation.rb +262 -120
- data/lib/active_record/result.rb +37 -11
- data/lib/active_record/runtime_registry.rb +18 -13
- data/lib/active_record/sanitization.rb +65 -20
- data/lib/active_record/schema.rb +36 -22
- data/lib/active_record/schema_dumper.rb +73 -24
- data/lib/active_record/schema_migration.rb +68 -33
- data/lib/active_record/scoping/default.rb +72 -15
- data/lib/active_record/scoping/named.rb +5 -13
- data/lib/active_record/scoping.rb +65 -34
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +21 -3
- data/lib/active_record/serialization.rb +6 -1
- data/lib/active_record/signed_id.rb +10 -8
- data/lib/active_record/store.rb +10 -10
- data/lib/active_record/suppressor.rb +13 -15
- data/lib/active_record/table_metadata.rb +16 -3
- data/lib/active_record/tasks/database_tasks.rb +225 -136
- data/lib/active_record/tasks/mysql_database_tasks.rb +16 -7
- data/lib/active_record/tasks/postgresql_database_tasks.rb +35 -26
- data/lib/active_record/tasks/sqlite_database_tasks.rb +15 -7
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +116 -96
- data/lib/active_record/timestamp.rb +28 -17
- data/lib/active_record/token_for.rb +113 -0
- data/lib/active_record/touch_later.rb +11 -6
- data/lib/active_record/transactions.rb +48 -27
- data/lib/active_record/translation.rb +3 -3
- data/lib/active_record/type/adapter_specific_registry.rb +32 -14
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -1
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +9 -5
- data/lib/active_record/type/time.rb +4 -0
- data/lib/active_record/type/type_map.rb +17 -20
- data/lib/active_record/type.rb +1 -2
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +4 -4
- 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 +51 -6
- data/lib/active_record/validations.rb +8 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +335 -32
- data/lib/arel/attributes/attribute.rb +0 -8
- data/lib/arel/crud.rb +28 -22
- data/lib/arel/delete_manager.rb +18 -4
- data/lib/arel/errors.rb +10 -0
- data/lib/arel/factory_methods.rb +4 -0
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +2 -3
- 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/casted.rb +1 -1
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/delete_statement.rb +12 -13
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/function.rb +1 -0
- data/lib/arel/nodes/homogeneous_in.rb +0 -8
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/node.rb +111 -2
- data/lib/arel/nodes/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- data/lib/arel/nodes/sql_literal.rb +6 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes/update_statement.rb +8 -3
- data/lib/arel/nodes.rb +5 -0
- data/lib/arel/predications.rb +13 -3
- data/lib/arel/select_manager.rb +10 -4
- data/lib/arel/table.rb +9 -6
- data/lib/arel/tree_manager.rb +0 -12
- data/lib/arel/update_manager.rb +18 -4
- data/lib/arel/visitors/dot.rb +80 -90
- data/lib/arel/visitors/mysql.rb +16 -3
- data/lib/arel/visitors/postgresql.rb +0 -10
- data/lib/arel/visitors/to_sql.rb +139 -19
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +18 -3
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -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
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +1 -1
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +2 -2
- data/lib/rails/generators/active_record/multi_db/multi_db_generator.rb +16 -0
- data/lib/rails/generators/active_record/multi_db/templates/multi_db.rb.tt +44 -0
- metadata +92 -13
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +0 -35
- data/lib/active_record/null_relation.rb +0 -67
@@ -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{(
|
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,44 +73,142 @@ 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
|
|
68
89
|
def self.build_read_query_regexp(*parts) # :nodoc:
|
69
90
|
parts += DEFAULT_READ_QUERY
|
70
91
|
parts = parts.map { |part| /#{part}/i }
|
71
|
-
/\A(?:[
|
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
|
193
|
+
end
|
194
|
+
|
195
|
+
EXCEPTION_NEVER = { Exception => :never }.freeze # :nodoc:
|
196
|
+
EXCEPTION_IMMEDIATE = { Exception => :immediate }.freeze # :nodoc:
|
197
|
+
private_constant :EXCEPTION_NEVER, :EXCEPTION_IMMEDIATE
|
198
|
+
def with_instrumenter(instrumenter, &block) # :nodoc:
|
199
|
+
Thread.handle_interrupt(EXCEPTION_NEVER) do
|
200
|
+
previous_instrumenter = @instrumenter
|
201
|
+
@instrumenter = instrumenter
|
202
|
+
Thread.handle_interrupt(EXCEPTION_IMMEDIATE, &block)
|
203
|
+
ensure
|
204
|
+
@instrumenter = previous_instrumenter
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def check_if_write_query(sql) # :nodoc:
|
209
|
+
if preventing_writes? && write_query?(sql)
|
210
|
+
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
|
211
|
+
end
|
103
212
|
end
|
104
213
|
|
105
214
|
def replica?
|
@@ -110,21 +219,31 @@ module ActiveRecord
|
|
110
219
|
@config.fetch(:use_metadata_table, true)
|
111
220
|
end
|
112
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
|
+
|
113
238
|
# Determines whether writes are currently being prevented.
|
114
239
|
#
|
115
|
-
# Returns true if the connection is a replica
|
116
|
-
#
|
117
|
-
# If the application is using legacy handling, returns
|
118
|
-
# true if +connection_handler.prevent_writes+ is set.
|
119
|
-
#
|
120
|
-
# If the application is using the new connection handling
|
121
|
-
# 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+.
|
122
242
|
def preventing_writes?
|
123
243
|
return true if replica?
|
124
|
-
return
|
125
|
-
return false if connection_klass.nil?
|
244
|
+
return false if connection_class.nil?
|
126
245
|
|
127
|
-
|
246
|
+
connection_class.current_preventing_writes
|
128
247
|
end
|
129
248
|
|
130
249
|
def migrations_paths # :nodoc:
|
@@ -132,25 +251,15 @@ module ActiveRecord
|
|
132
251
|
end
|
133
252
|
|
134
253
|
def migration_context # :nodoc:
|
135
|
-
MigrationContext.new(migrations_paths, schema_migration)
|
254
|
+
MigrationContext.new(migrations_paths, schema_migration, internal_metadata)
|
136
255
|
end
|
137
256
|
|
138
257
|
def schema_migration # :nodoc:
|
139
|
-
|
140
|
-
|
141
|
-
spec_name = conn.pool.pool_config.connection_specification_name
|
142
|
-
|
143
|
-
return ActiveRecord::SchemaMigration if spec_name == "ActiveRecord::Base"
|
144
|
-
|
145
|
-
schema_migration_name = "#{spec_name}::SchemaMigration"
|
146
|
-
|
147
|
-
Class.new(ActiveRecord::SchemaMigration) do
|
148
|
-
define_singleton_method(:name) { schema_migration_name }
|
149
|
-
define_singleton_method(:to_s) { schema_migration_name }
|
258
|
+
SchemaMigration.new(self)
|
259
|
+
end
|
150
260
|
|
151
|
-
|
152
|
-
|
153
|
-
end
|
261
|
+
def internal_metadata # :nodoc:
|
262
|
+
InternalMetadata.new(self)
|
154
263
|
end
|
155
264
|
|
156
265
|
def prepared_statements?
|
@@ -159,7 +268,7 @@ module ActiveRecord
|
|
159
268
|
alias :prepared_statements :prepared_statements?
|
160
269
|
|
161
270
|
def prepared_statements_disabled_cache # :nodoc:
|
162
|
-
|
271
|
+
ActiveSupport::IsolatedExecutionState[:active_record_prepared_statements_disabled_cache] ||= Set.new
|
163
272
|
end
|
164
273
|
|
165
274
|
class Version
|
@@ -189,41 +298,48 @@ module ActiveRecord
|
|
189
298
|
def lease
|
190
299
|
if in_use?
|
191
300
|
msg = +"Cannot lease connection, "
|
192
|
-
if @owner ==
|
301
|
+
if @owner == ActiveSupport::IsolatedExecutionState.context
|
193
302
|
msg << "it is already leased by the current thread."
|
194
303
|
else
|
195
304
|
msg << "it is already in use by a different thread: #{@owner}. " \
|
196
|
-
"Current thread: #{
|
305
|
+
"Current thread: #{ActiveSupport::IsolatedExecutionState.context}."
|
197
306
|
end
|
198
307
|
raise ActiveRecordError, msg
|
199
308
|
end
|
200
309
|
|
201
|
-
@owner =
|
310
|
+
@owner = ActiveSupport::IsolatedExecutionState.context
|
202
311
|
end
|
203
312
|
|
204
|
-
def
|
205
|
-
@pool.
|
313
|
+
def connection_class # :nodoc:
|
314
|
+
@pool.connection_class
|
206
315
|
end
|
207
316
|
|
208
|
-
|
209
|
-
|
317
|
+
# The role (e.g. +:writing+) for the current connection. In a
|
318
|
+
# non-multi role application, +:writing+ is returned.
|
319
|
+
def role
|
320
|
+
@pool.role
|
210
321
|
end
|
211
322
|
|
212
|
-
|
213
|
-
|
214
|
-
|
323
|
+
# The shard (e.g. +:default+) for the current connection. In
|
324
|
+
# a non-sharded application, +:default+ is returned.
|
325
|
+
def shard
|
326
|
+
@pool.shard
|
327
|
+
end
|
328
|
+
|
329
|
+
def schema_cache
|
330
|
+
@schema_cache ||= BoundSchemaReflection.new(@pool.schema_reflection, self)
|
215
331
|
end
|
216
332
|
|
217
333
|
# this method must only be called while holding connection pool's mutex
|
218
334
|
def expire
|
219
335
|
if in_use?
|
220
|
-
if @owner !=
|
336
|
+
if @owner != ActiveSupport::IsolatedExecutionState.context
|
221
337
|
raise ActiveRecordError, "Cannot expire connection, " \
|
222
338
|
"it is owned by a different thread: #{@owner}. " \
|
223
|
-
"Current thread: #{
|
339
|
+
"Current thread: #{ActiveSupport::IsolatedExecutionState.context}."
|
224
340
|
end
|
225
341
|
|
226
|
-
@idle_since =
|
342
|
+
@idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
227
343
|
@owner = nil
|
228
344
|
else
|
229
345
|
raise ActiveRecordError, "Cannot expire connection, it is not currently leased."
|
@@ -233,10 +349,10 @@ module ActiveRecord
|
|
233
349
|
# this method must only be called while holding connection pool's mutex (and a desire for segfaults)
|
234
350
|
def steal! # :nodoc:
|
235
351
|
if in_use?
|
236
|
-
if @owner !=
|
352
|
+
if @owner != ActiveSupport::IsolatedExecutionState.context
|
237
353
|
pool.send :remove_connection_from_thread_cache, self, @owner
|
238
354
|
|
239
|
-
@owner =
|
355
|
+
@owner = ActiveSupport::IsolatedExecutionState.context
|
240
356
|
end
|
241
357
|
else
|
242
358
|
raise ActiveRecordError, "Cannot steal connection, it is not currently leased."
|
@@ -246,7 +362,7 @@ module ActiveRecord
|
|
246
362
|
# Seconds since this connection was returned to the pool
|
247
363
|
def seconds_idle # :nodoc:
|
248
364
|
return 0 if in_use?
|
249
|
-
|
365
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC) - @idle_since
|
250
366
|
end
|
251
367
|
|
252
368
|
def unprepared_statement
|
@@ -264,7 +380,14 @@ module ActiveRecord
|
|
264
380
|
|
265
381
|
# Does the database for this adapter exist?
|
266
382
|
def self.database_exists?(config)
|
267
|
-
|
383
|
+
new(config).database_exists?
|
384
|
+
end
|
385
|
+
|
386
|
+
def database_exists?
|
387
|
+
connect!
|
388
|
+
true
|
389
|
+
rescue ActiveRecord::NoDatabaseError
|
390
|
+
false
|
268
391
|
end
|
269
392
|
|
270
393
|
# Does this adapter support DDL rollbacks in transactions? That is, would
|
@@ -282,6 +405,16 @@ module ActiveRecord
|
|
282
405
|
false
|
283
406
|
end
|
284
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
|
+
|
285
418
|
# Does this adapter support application-enforced advisory locking?
|
286
419
|
def supports_advisory_locks?
|
287
420
|
false
|
@@ -308,6 +441,11 @@ module ActiveRecord
|
|
308
441
|
false
|
309
442
|
end
|
310
443
|
|
444
|
+
# Does this adapter support including non-key columns?
|
445
|
+
def supports_index_include?
|
446
|
+
false
|
447
|
+
end
|
448
|
+
|
311
449
|
# Does this adapter support expression indices?
|
312
450
|
def supports_expression_index?
|
313
451
|
false
|
@@ -344,11 +482,26 @@ module ActiveRecord
|
|
344
482
|
false
|
345
483
|
end
|
346
484
|
|
485
|
+
# Does this adapter support creating deferrable constraints?
|
486
|
+
def supports_deferrable_constraints?
|
487
|
+
false
|
488
|
+
end
|
489
|
+
|
347
490
|
# Does this adapter support creating check constraints?
|
348
491
|
def supports_check_constraints?
|
349
492
|
false
|
350
493
|
end
|
351
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
|
+
|
352
505
|
# Does this adapter support views?
|
353
506
|
def supports_views?
|
354
507
|
false
|
@@ -364,7 +517,7 @@ module ActiveRecord
|
|
364
517
|
false
|
365
518
|
end
|
366
519
|
|
367
|
-
# Does this adapter support
|
520
|
+
# Does this adapter support JSON data type?
|
368
521
|
def supports_json?
|
369
522
|
false
|
370
523
|
end
|
@@ -418,12 +571,49 @@ module ActiveRecord
|
|
418
571
|
false
|
419
572
|
end
|
420
573
|
|
574
|
+
def supports_concurrent_connections?
|
575
|
+
true
|
576
|
+
end
|
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
|
+
|
586
|
+
def async_enabled? # :nodoc:
|
587
|
+
supports_concurrent_connections? &&
|
588
|
+
!ActiveRecord.async_query_executor.nil? && !pool.async_executor.nil?
|
589
|
+
end
|
590
|
+
|
421
591
|
# This is meant to be implemented by the adapters that support extensions
|
422
|
-
def disable_extension(name)
|
592
|
+
def disable_extension(name, **)
|
423
593
|
end
|
424
594
|
|
425
595
|
# This is meant to be implemented by the adapters that support extensions
|
426
|
-
def enable_extension(name)
|
596
|
+
def enable_extension(name, **)
|
597
|
+
end
|
598
|
+
|
599
|
+
# This is meant to be implemented by the adapters that support custom enum types
|
600
|
+
def create_enum(*) # :nodoc:
|
601
|
+
end
|
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:
|
427
617
|
end
|
428
618
|
|
429
619
|
def advisory_locks_enabled? # :nodoc:
|
@@ -461,6 +651,21 @@ module ActiveRecord
|
|
461
651
|
yield
|
462
652
|
end
|
463
653
|
|
654
|
+
# Override to check all foreign key constraints in a database.
|
655
|
+
def all_foreign_keys_valid?
|
656
|
+
check_all_foreign_keys_valid!
|
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!
|
667
|
+
end
|
668
|
+
|
464
669
|
# CONNECTION MANAGEMENT ====================================
|
465
670
|
|
466
671
|
# Checks whether the connection to the database is still active. This includes
|
@@ -469,19 +674,50 @@ module ActiveRecord
|
|
469
674
|
def active?
|
470
675
|
end
|
471
676
|
|
472
|
-
# Disconnects from the database if already connected, and establishes a
|
473
|
-
#
|
474
|
-
#
|
475
|
-
def reconnect!
|
476
|
-
|
477
|
-
|
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
|
478
712
|
end
|
479
713
|
|
714
|
+
|
480
715
|
# Disconnects from the database if already connected. Otherwise, this
|
481
716
|
# method does nothing.
|
482
717
|
def disconnect!
|
483
|
-
clear_cache!
|
718
|
+
clear_cache!(new_connection: true)
|
484
719
|
reset_transaction
|
720
|
+
@raw_connection_dirty = false
|
485
721
|
end
|
486
722
|
|
487
723
|
# Immediately forget this connection ever existed. Unlike disconnect!,
|
@@ -492,22 +728,20 @@ module ActiveRecord
|
|
492
728
|
# rid of a connection that belonged to its parent.
|
493
729
|
def discard!
|
494
730
|
# This should be overridden by concrete adapters.
|
495
|
-
#
|
496
|
-
# Prevent @connection's finalizer from touching the socket, or
|
497
|
-
# otherwise communicating with its server, when it is collected.
|
498
|
-
if schema_cache.connection == self
|
499
|
-
schema_cache.connection = nil
|
500
|
-
end
|
501
731
|
end
|
502
732
|
|
503
733
|
# Reset the state of this connection, directing the DBMS to clear
|
504
734
|
# transactions and other connection-related server-side state. Usually a
|
505
735
|
# database-dependent operation.
|
506
736
|
#
|
507
|
-
#
|
508
|
-
#
|
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).
|
509
741
|
def reset!
|
510
|
-
|
742
|
+
clear_cache!(new_connection: true)
|
743
|
+
reset_transaction
|
744
|
+
configure_connection
|
511
745
|
end
|
512
746
|
|
513
747
|
# Removes the connection from the pool and disconnect it.
|
@@ -517,8 +751,16 @@ module ActiveRecord
|
|
517
751
|
end
|
518
752
|
|
519
753
|
# Clear any caching the database adapter may be doing.
|
520
|
-
def clear_cache!
|
521
|
-
|
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
|
522
764
|
end
|
523
765
|
|
524
766
|
# Returns true if its required to reload the connection between requests for development mode.
|
@@ -530,7 +772,33 @@ module ActiveRecord
|
|
530
772
|
# This is done under the hood by calling #active?. If the connection
|
531
773
|
# is no longer active, then this method will reconnect to the database.
|
532
774
|
def verify!
|
533
|
-
|
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
|
534
802
|
end
|
535
803
|
|
536
804
|
# Provides access to the underlying database driver for this adapter. For
|
@@ -539,9 +807,16 @@ module ActiveRecord
|
|
539
807
|
#
|
540
808
|
# This is useful for when you need to call a proprietary method such as
|
541
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+.
|
542
814
|
def raw_connection
|
543
|
-
|
544
|
-
|
815
|
+
with_raw_connection do |conn|
|
816
|
+
disable_lazy_transactions!
|
817
|
+
@raw_connection_dirty = true
|
818
|
+
conn
|
819
|
+
end
|
545
820
|
end
|
546
821
|
|
547
822
|
def default_uniqueness_comparison(attribute, value) # :nodoc:
|
@@ -599,78 +874,250 @@ module ActiveRecord
|
|
599
874
|
def check_version # :nodoc:
|
600
875
|
end
|
601
876
|
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
877
|
+
# Returns the version identifier of the schema currently available in
|
878
|
+
# the database. This is generally equal to the number of the highest-
|
879
|
+
# numbered migration that has been executed, or 0 if no schema
|
880
|
+
# information is present / the database is empty.
|
881
|
+
def schema_version
|
882
|
+
migration_context.current_version
|
883
|
+
end
|
884
|
+
|
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
|
891
|
+
end
|
892
|
+
|
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
|
900
|
+
|
901
|
+
private
|
902
|
+
def initialize_type_map(m)
|
903
|
+
register_class_with_limit m, %r(boolean)i, Type::Boolean
|
904
|
+
register_class_with_limit m, %r(char)i, Type::String
|
905
|
+
register_class_with_limit m, %r(binary)i, Type::Binary
|
906
|
+
register_class_with_limit m, %r(text)i, Type::Text
|
907
|
+
register_class_with_precision m, %r(date)i, Type::Date
|
908
|
+
register_class_with_precision m, %r(time)i, Type::Time
|
909
|
+
register_class_with_precision m, %r(datetime)i, Type::DateTime
|
910
|
+
register_class_with_limit m, %r(float)i, Type::Float
|
911
|
+
register_class_with_limit m, %r(int)i, Type::Integer
|
912
|
+
|
913
|
+
m.alias_type %r(blob)i, "binary"
|
914
|
+
m.alias_type %r(clob)i, "text"
|
915
|
+
m.alias_type %r(timestamp)i, "datetime"
|
916
|
+
m.alias_type %r(numeric)i, "decimal"
|
917
|
+
m.alias_type %r(number)i, "decimal"
|
918
|
+
m.alias_type %r(double)i, "float"
|
919
|
+
|
920
|
+
m.register_type %r(^json)i, Type::Json.new
|
921
|
+
|
922
|
+
m.register_type(%r(decimal)i) do |sql_type|
|
923
|
+
scale = extract_scale(sql_type)
|
924
|
+
precision = extract_precision(sql_type)
|
925
|
+
|
926
|
+
if scale == 0
|
927
|
+
# FIXME: Remove this class as well
|
928
|
+
Type::DecimalWithoutScale.new(precision: precision)
|
929
|
+
else
|
930
|
+
Type::Decimal.new(precision: precision, scale: scale)
|
931
|
+
end
|
932
|
+
end
|
933
|
+
end
|
934
|
+
|
935
|
+
def register_class_with_limit(mapping, key, klass)
|
936
|
+
mapping.register_type(key) do |*args|
|
937
|
+
limit = extract_limit(args.last)
|
938
|
+
klass.new(limit: limit)
|
939
|
+
end
|
940
|
+
end
|
941
|
+
|
942
|
+
def extract_scale(sql_type)
|
943
|
+
case sql_type
|
944
|
+
when /\((\d+)\)/ then 0
|
945
|
+
when /\((\d+)(,(\d+))\)/ then $3.to_i
|
946
|
+
end
|
606
947
|
end
|
948
|
+
|
949
|
+
def extract_precision(sql_type)
|
950
|
+
$1.to_i if sql_type =~ /\((\d+)(,\d+)?\)/
|
951
|
+
end
|
952
|
+
|
953
|
+
def extract_limit(sql_type)
|
954
|
+
$1.to_i if sql_type =~ /\((.*)\)/
|
955
|
+
end
|
956
|
+
end
|
957
|
+
|
958
|
+
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
|
959
|
+
EXTENDED_TYPE_MAPS = Concurrent::Map.new
|
960
|
+
|
961
|
+
private
|
962
|
+
def reconnect_can_restore_state?
|
963
|
+
transaction_manager.restorable? && !@raw_connection_dirty
|
607
964
|
end
|
608
965
|
|
609
|
-
|
610
|
-
|
611
|
-
|
612
|
-
|
613
|
-
|
614
|
-
|
615
|
-
|
616
|
-
|
617
|
-
|
618
|
-
|
619
|
-
|
620
|
-
|
621
|
-
|
622
|
-
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
|
634
|
-
|
635
|
-
|
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
|
636
1017
|
else
|
637
|
-
|
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
|
638
1057
|
end
|
639
1058
|
end
|
640
1059
|
end
|
641
1060
|
|
642
|
-
def
|
643
|
-
|
644
|
-
initialize_type_map
|
1061
|
+
def retryable_connection_error?(exception)
|
1062
|
+
exception.is_a?(ConnectionNotEstablished) || exception.is_a?(ConnectionFailed)
|
645
1063
|
end
|
646
1064
|
|
647
|
-
def
|
648
|
-
|
649
|
-
|
650
|
-
|
651
|
-
|
1065
|
+
def invalidate_transaction(exception)
|
1066
|
+
return unless exception.is_a?(TransactionRollbackError)
|
1067
|
+
return unless savepoint_errors_invalidate_transactions?
|
1068
|
+
|
1069
|
+
current_transaction.invalidate!
|
652
1070
|
end
|
653
1071
|
|
654
|
-
def
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
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)
|
659
1077
|
end
|
660
1078
|
|
661
|
-
def
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
1079
|
+
def backoff(counter)
|
1080
|
+
sleep 0.1 * counter
|
1081
|
+
end
|
1082
|
+
|
1083
|
+
def reconnect
|
1084
|
+
raise NotImplementedError
|
666
1085
|
end
|
667
1086
|
|
668
|
-
|
669
|
-
|
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
|
670
1092
|
end
|
671
1093
|
|
672
|
-
|
673
|
-
|
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
|
+
|
1113
|
+
def type_map
|
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
|
674
1121
|
end
|
675
1122
|
|
676
1123
|
def translate_exception_class(e, sql, binds)
|
@@ -683,7 +1130,7 @@ module ActiveRecord
|
|
683
1130
|
exception
|
684
1131
|
end
|
685
1132
|
|
686
|
-
def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil) # :doc:
|
1133
|
+
def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil, async: false, &block) # :doc:
|
687
1134
|
@instrumenter.instrument(
|
688
1135
|
"sql.active_record",
|
689
1136
|
sql: sql,
|
@@ -691,22 +1138,28 @@ module ActiveRecord
|
|
691
1138
|
binds: binds,
|
692
1139
|
type_casted_binds: type_casted_binds,
|
693
1140
|
statement_name: statement_name,
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
1141
|
+
async: async,
|
1142
|
+
connection: self,
|
1143
|
+
&block
|
1144
|
+
)
|
1145
|
+
rescue ActiveRecord::StatementInvalid => ex
|
1146
|
+
raise ex.set_query(sql, binds)
|
1147
|
+
end
|
1148
|
+
|
1149
|
+
def transform_query(sql)
|
1150
|
+
ActiveRecord.query_transformers.each do |transformer|
|
1151
|
+
sql = transformer.call(sql, self)
|
700
1152
|
end
|
1153
|
+
sql
|
701
1154
|
end
|
702
1155
|
|
703
1156
|
def translate_exception(exception, message:, sql:, binds:)
|
704
1157
|
# override in derived class
|
705
1158
|
case exception
|
706
|
-
when RuntimeError
|
1159
|
+
when RuntimeError, ActiveRecord::ActiveRecordError
|
707
1160
|
exception
|
708
1161
|
else
|
709
|
-
ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds)
|
1162
|
+
ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
710
1163
|
end
|
711
1164
|
end
|
712
1165
|
|
@@ -753,6 +1206,26 @@ module ActiveRecord
|
|
753
1206
|
def build_result(columns:, rows:, column_types: {})
|
754
1207
|
ActiveRecord::Result.new(columns, rows, column_types)
|
755
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
|
756
1229
|
end
|
757
1230
|
end
|
758
1231
|
end
|