activerecord 6.1.7 → 7.2.2
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 +616 -1290
- data/MIT-LICENSE +1 -1
- data/README.rdoc +31 -31
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +17 -14
- data/lib/active_record/association_relation.rb +2 -12
- data/lib/active_record/associations/alias_tracker.rb +25 -19
- data/lib/active_record/associations/association.rb +60 -21
- data/lib/active_record/associations/association_scope.rb +17 -12
- data/lib/active_record/associations/belongs_to_association.rb +37 -11
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +13 -4
- data/lib/active_record/associations/builder/association.rb +11 -5
- data/lib/active_record/associations/builder/belongs_to.rb +41 -14
- data/lib/active_record/associations/builder/collection_association.rb +10 -3
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -7
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +4 -4
- data/lib/active_record/associations/builder/singular_association.rb +6 -2
- data/lib/active_record/associations/collection_association.rb +46 -36
- data/lib/active_record/associations/collection_proxy.rb +44 -16
- data/lib/active_record/associations/disable_joins_association_scope.rb +59 -0
- 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 +29 -19
- data/lib/active_record/associations/has_many_through_association.rb +19 -8
- 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/join_association.rb +30 -27
- data/lib/active_record/associations/join_dependency.rb +28 -20
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +212 -53
- data/lib/active_record/associations/preloader/batch.rb +48 -0
- data/lib/active_record/associations/preloader/branch.rb +153 -0
- data/lib/active_record/associations/preloader/through_association.rb +50 -16
- data/lib/active_record/associations/preloader.rb +50 -121
- data/lib/active_record/associations/singular_association.rb +15 -3
- data/lib/active_record/associations/through_association.rb +25 -14
- data/lib/active_record/associations.rb +429 -522
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +1 -5
- data/lib/active_record/attribute_methods/before_type_cast.rb +24 -2
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +73 -22
- data/lib/active_record/attribute_methods/primary_key.rb +47 -27
- data/lib/active_record/attribute_methods/query.rb +31 -19
- data/lib/active_record/attribute_methods/read.rb +14 -11
- data/lib/active_record/attribute_methods/serialization.rb +174 -37
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +15 -9
- data/lib/active_record/attribute_methods/write.rb +12 -15
- data/lib/active_record/attribute_methods.rb +164 -52
- data/lib/active_record/attributes.rb +57 -54
- data/lib/active_record/autosave_association.rb +74 -57
- data/lib/active_record/base.rb +27 -5
- data/lib/active_record/callbacks.rb +19 -35
- 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 +284 -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 +79 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +325 -604
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +5 -17
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -60
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +230 -64
- data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -131
- 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 +378 -143
- data/lib/active_record/connection_adapters/abstract/transaction.rb +361 -76
- data/lib/active_record/connection_adapters/abstract_adapter.rb +624 -163
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +348 -165
- 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 +29 -130
- data/lib/active_record/connection_adapters/mysql/quoting.rb +81 -55
- 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 +107 -68
- data/lib/active_record/connection_adapters/pool_config.rb +26 -16
- data/lib/active_record/connection_adapters/pool_manager.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/column.rb +30 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +114 -54
- 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/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/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 +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/uuid.rb +14 -4
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +137 -104
- 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 +173 -3
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +403 -77
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -10
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +520 -253
- data/lib/active_record/connection_adapters/schema_cache.rb +326 -102
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +78 -55
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +68 -54
- 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 +66 -22
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +372 -130
- 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 +130 -6
- data/lib/active_record/connection_handling.rb +132 -146
- data/lib/active_record/core.rb +310 -253
- data/lib/active_record/counter_cache.rb +68 -34
- data/lib/active_record/database_configurations/connection_url_resolver.rb +10 -4
- data/lib/active_record/database_configurations/database_config.rb +34 -10
- data/lib/active_record/database_configurations/hash_config.rb +107 -31
- data/lib/active_record/database_configurations/url_config.rb +38 -13
- data/lib/active_record/database_configurations.rb +96 -60
- data/lib/active_record/delegated_type.rb +90 -20
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +4 -2
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +3 -3
- 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 +230 -0
- data/lib/active_record/encryption/encrypted_attribute_type.rb +175 -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 +170 -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 +157 -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_pack_message_serializer.rb +76 -0
- data/lib/active_record/encryption/message_serializer.rb +96 -0
- data/lib/active_record/encryption/null_encryptor.rb +25 -0
- data/lib/active_record/encryption/properties.rb +76 -0
- data/lib/active_record/encryption/read_only_null_encryptor.rb +28 -0
- data/lib/active_record/encryption/scheme.rb +100 -0
- data/lib/active_record/encryption.rb +58 -0
- data/lib/active_record/enum.rb +170 -62
- data/lib/active_record/errors.rb +210 -27
- data/lib/active_record/explain.rb +21 -12
- 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 +179 -112
- data/lib/active_record/future_result.rb +178 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +85 -31
- data/lib/active_record/insert_all.rb +148 -32
- data/lib/active_record/integration.rb +14 -10
- data/lib/active_record/internal_metadata.rb +123 -23
- data/lib/active_record/legacy_yaml_adapter.rb +2 -39
- data/lib/active_record/locking/optimistic.rb +43 -27
- data/lib/active_record/locking/pessimistic.rb +15 -6
- data/lib/active_record/log_subscriber.rb +41 -29
- data/lib/active_record/marshalling.rb +59 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +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 +113 -16
- data/lib/active_record/migration/compatibility.rb +235 -46
- 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/join_table.rb +1 -1
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +374 -177
- data/lib/active_record/model_schema.rb +145 -158
- data/lib/active_record/nested_attributes.rb +61 -23
- data/lib/active_record/no_touching.rb +3 -3
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +282 -283
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +18 -25
- data/lib/active_record/query_logs.rb +189 -0
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +44 -9
- data/lib/active_record/railtie.rb +229 -71
- data/lib/active_record/railties/controller_runtime.rb +25 -11
- data/lib/active_record/railties/databases.rake +189 -256
- 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 +332 -103
- data/lib/active_record/relation/batches/batch_enumerator.rb +38 -9
- data/lib/active_record/relation/batches.rb +200 -65
- data/lib/active_record/relation/calculations.rb +301 -112
- data/lib/active_record/relation/delegation.rb +33 -22
- data/lib/active_record/relation/finder_methods.rb +123 -52
- data/lib/active_record/relation/merger.rb +26 -19
- 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 +29 -22
- data/lib/active_record/relation/query_attribute.rb +30 -12
- data/lib/active_record/relation/query_methods.rb +870 -163
- data/lib/active_record/relation/record_fetch_warning.rb +10 -9
- data/lib/active_record/relation/spawn_methods.rb +7 -6
- data/lib/active_record/relation/where_clause.rb +15 -36
- data/lib/active_record/relation.rb +736 -145
- data/lib/active_record/result.rb +67 -54
- data/lib/active_record/runtime_registry.rb +71 -13
- data/lib/active_record/sanitization.rb +84 -34
- data/lib/active_record/schema.rb +39 -23
- data/lib/active_record/schema_dumper.rb +90 -31
- data/lib/active_record/schema_migration.rb +74 -23
- data/lib/active_record/scoping/default.rb +72 -15
- data/lib/active_record/scoping/named.rb +6 -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 +30 -9
- data/lib/active_record/statement_cache.rb +7 -7
- data/lib/active_record/store.rb +10 -10
- data/lib/active_record/suppressor.rb +13 -15
- data/lib/active_record/table_metadata.rb +7 -3
- data/lib/active_record/tasks/database_tasks.rb +288 -149
- 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 +16 -7
- data/lib/active_record/test_databases.rb +1 -1
- data/lib/active_record/test_fixtures.rb +173 -155
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +32 -19
- 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 +118 -41
- data/lib/active_record/translation.rb +3 -5
- 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 -7
- 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/type_caster/connection.rb +4 -4
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +13 -7
- 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 +65 -15
- data/lib/active_record/validations.rb +12 -5
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +444 -32
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/attributes/attribute.rb +0 -8
- 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/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/binary.rb +6 -7
- data/lib/arel/nodes/bound_sql_literal.rb +65 -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 +1 -9
- data/lib/arel/nodes/insert_statement.rb +2 -2
- 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/select_core.rb +2 -2
- data/lib/arel/nodes/select_statement.rb +2 -2
- data/lib/arel/nodes/sql_literal.rb +13 -0
- data/lib/arel/nodes/table_alias.rb +4 -0
- data/lib/arel/nodes/update_statement.rb +8 -3
- data/lib/arel/nodes.rb +7 -2
- data/lib/arel/predications.rb +14 -4
- data/lib/arel/select_manager.rb +11 -5
- data/lib/arel/table.rb +9 -6
- data/lib/arel/tree_manager.rb +8 -15
- data/lib/arel/update_manager.rb +20 -5
- data/lib/arel/visitors/dot.rb +81 -90
- data/lib/arel/visitors/mysql.rb +23 -5
- data/lib/arel/visitors/postgresql.rb +1 -22
- data/lib/arel/visitors/sqlite.rb +25 -0
- data/lib/arel/visitors/to_sql.rb +170 -36
- data/lib/arel/visitors/visitor.rb +2 -2
- data/lib/arel.rb +23 -4
- 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/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
- 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 +103 -17
- 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
|
@@ -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{(
|
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,95 +71,174 @@ 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
|
|
68
87
|
def self.build_read_query_regexp(*parts) # :nodoc:
|
69
88
|
parts += DEFAULT_READ_QUERY
|
70
89
|
parts = parts.map { |part| /#{part}/i }
|
71
|
-
/\A(?:[
|
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
|
)
|
103
|
-
end
|
104
168
|
|
105
|
-
|
106
|
-
|
169
|
+
@default_timezone = self.class.validate_default_timezone(@config[:default_timezone])
|
170
|
+
|
171
|
+
@raw_connection_dirty = false
|
172
|
+
@verified = false
|
107
173
|
end
|
108
174
|
|
109
|
-
def
|
110
|
-
|
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}>"
|
111
180
|
end
|
112
181
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
return ActiveRecord::Base.connection_handler.prevent_writes if ActiveRecord::Base.legacy_connection_handling
|
125
|
-
return false if connection_klass.nil?
|
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
|
192
|
+
end
|
126
193
|
|
127
|
-
|
194
|
+
EXCEPTION_NEVER = { Exception => :never }.freeze # :nodoc:
|
195
|
+
EXCEPTION_IMMEDIATE = { Exception => :immediate }.freeze # :nodoc:
|
196
|
+
private_constant :EXCEPTION_NEVER, :EXCEPTION_IMMEDIATE
|
197
|
+
def with_instrumenter(instrumenter, &block) # :nodoc:
|
198
|
+
Thread.handle_interrupt(EXCEPTION_NEVER) do
|
199
|
+
previous_instrumenter = @instrumenter
|
200
|
+
@instrumenter = instrumenter
|
201
|
+
Thread.handle_interrupt(EXCEPTION_IMMEDIATE, &block)
|
202
|
+
ensure
|
203
|
+
@instrumenter = previous_instrumenter
|
204
|
+
end
|
128
205
|
end
|
129
206
|
|
130
|
-
def
|
131
|
-
|
207
|
+
def check_if_write_query(sql) # :nodoc:
|
208
|
+
if preventing_writes? && write_query?(sql)
|
209
|
+
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
|
210
|
+
end
|
132
211
|
end
|
133
212
|
|
134
|
-
def
|
135
|
-
|
213
|
+
def replica?
|
214
|
+
@config[:replica] || false
|
136
215
|
end
|
137
216
|
|
138
|
-
def
|
139
|
-
@
|
140
|
-
|
141
|
-
spec_name = conn.pool.pool_config.connection_specification_name
|
217
|
+
def connection_retries
|
218
|
+
(@config[:connection_retries] || 1).to_i
|
219
|
+
end
|
142
220
|
|
143
|
-
|
221
|
+
def retry_deadline
|
222
|
+
if @config[:retry_deadline]
|
223
|
+
@config[:retry_deadline].to_f
|
224
|
+
else
|
225
|
+
nil
|
226
|
+
end
|
227
|
+
end
|
144
228
|
|
145
|
-
|
229
|
+
def default_timezone
|
230
|
+
@default_timezone || ActiveRecord.default_timezone
|
231
|
+
end
|
146
232
|
|
147
|
-
|
148
|
-
|
149
|
-
|
233
|
+
# Determines whether writes are currently being prevented.
|
234
|
+
#
|
235
|
+
# Returns true if the connection is a replica or returns
|
236
|
+
# the value of +current_preventing_writes+.
|
237
|
+
def preventing_writes?
|
238
|
+
return true if replica?
|
239
|
+
return false if connection_class.nil?
|
150
240
|
|
151
|
-
|
152
|
-
end
|
153
|
-
end
|
241
|
+
connection_class.current_preventing_writes
|
154
242
|
end
|
155
243
|
|
156
244
|
def prepared_statements?
|
@@ -159,7 +247,7 @@ module ActiveRecord
|
|
159
247
|
alias :prepared_statements :prepared_statements?
|
160
248
|
|
161
249
|
def prepared_statements_disabled_cache # :nodoc:
|
162
|
-
|
250
|
+
ActiveSupport::IsolatedExecutionState[:active_record_prepared_statements_disabled_cache] ||= Set.new
|
163
251
|
end
|
164
252
|
|
165
253
|
class Version
|
@@ -189,41 +277,48 @@ module ActiveRecord
|
|
189
277
|
def lease
|
190
278
|
if in_use?
|
191
279
|
msg = +"Cannot lease connection, "
|
192
|
-
if @owner ==
|
280
|
+
if @owner == ActiveSupport::IsolatedExecutionState.context
|
193
281
|
msg << "it is already leased by the current thread."
|
194
282
|
else
|
195
283
|
msg << "it is already in use by a different thread: #{@owner}. " \
|
196
|
-
"Current thread: #{
|
284
|
+
"Current thread: #{ActiveSupport::IsolatedExecutionState.context}."
|
197
285
|
end
|
198
286
|
raise ActiveRecordError, msg
|
199
287
|
end
|
200
288
|
|
201
|
-
@owner =
|
289
|
+
@owner = ActiveSupport::IsolatedExecutionState.context
|
202
290
|
end
|
203
291
|
|
204
|
-
def
|
205
|
-
@pool.
|
292
|
+
def connection_class # :nodoc:
|
293
|
+
@pool.connection_class
|
206
294
|
end
|
207
295
|
|
208
|
-
|
209
|
-
|
296
|
+
# The role (e.g. +:writing+) for the current connection. In a
|
297
|
+
# non-multi role application, +:writing+ is returned.
|
298
|
+
def role
|
299
|
+
@pool.role
|
210
300
|
end
|
211
301
|
|
212
|
-
|
213
|
-
|
214
|
-
|
302
|
+
# The shard (e.g. +:default+) for the current connection. In
|
303
|
+
# a non-sharded application, +:default+ is returned.
|
304
|
+
def shard
|
305
|
+
@pool.shard
|
306
|
+
end
|
307
|
+
|
308
|
+
def schema_cache
|
309
|
+
@pool.schema_cache || (@schema_cache ||= BoundSchemaReflection.for_lone_connection(@pool.schema_reflection, self))
|
215
310
|
end
|
216
311
|
|
217
312
|
# this method must only be called while holding connection pool's mutex
|
218
313
|
def expire
|
219
314
|
if in_use?
|
220
|
-
if @owner !=
|
315
|
+
if @owner != ActiveSupport::IsolatedExecutionState.context
|
221
316
|
raise ActiveRecordError, "Cannot expire connection, " \
|
222
317
|
"it is owned by a different thread: #{@owner}. " \
|
223
|
-
"Current thread: #{
|
318
|
+
"Current thread: #{ActiveSupport::IsolatedExecutionState.context}."
|
224
319
|
end
|
225
320
|
|
226
|
-
@idle_since =
|
321
|
+
@idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
227
322
|
@owner = nil
|
228
323
|
else
|
229
324
|
raise ActiveRecordError, "Cannot expire connection, it is not currently leased."
|
@@ -233,10 +328,10 @@ module ActiveRecord
|
|
233
328
|
# this method must only be called while holding connection pool's mutex (and a desire for segfaults)
|
234
329
|
def steal! # :nodoc:
|
235
330
|
if in_use?
|
236
|
-
if @owner !=
|
331
|
+
if @owner != ActiveSupport::IsolatedExecutionState.context
|
237
332
|
pool.send :remove_connection_from_thread_cache, self, @owner
|
238
333
|
|
239
|
-
@owner =
|
334
|
+
@owner = ActiveSupport::IsolatedExecutionState.context
|
240
335
|
end
|
241
336
|
else
|
242
337
|
raise ActiveRecordError, "Cannot steal connection, it is not currently leased."
|
@@ -246,7 +341,7 @@ module ActiveRecord
|
|
246
341
|
# Seconds since this connection was returned to the pool
|
247
342
|
def seconds_idle # :nodoc:
|
248
343
|
return 0 if in_use?
|
249
|
-
|
344
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC) - @idle_since
|
250
345
|
end
|
251
346
|
|
252
347
|
def unprepared_statement
|
@@ -264,7 +359,14 @@ module ActiveRecord
|
|
264
359
|
|
265
360
|
# Does the database for this adapter exist?
|
266
361
|
def self.database_exists?(config)
|
267
|
-
|
362
|
+
new(config).database_exists?
|
363
|
+
end
|
364
|
+
|
365
|
+
def database_exists?
|
366
|
+
connect!
|
367
|
+
true
|
368
|
+
rescue ActiveRecord::NoDatabaseError
|
369
|
+
false
|
268
370
|
end
|
269
371
|
|
270
372
|
# Does this adapter support DDL rollbacks in transactions? That is, would
|
@@ -282,6 +384,16 @@ module ActiveRecord
|
|
282
384
|
false
|
283
385
|
end
|
284
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
|
+
|
285
397
|
# Does this adapter support application-enforced advisory locking?
|
286
398
|
def supports_advisory_locks?
|
287
399
|
false
|
@@ -308,6 +420,11 @@ module ActiveRecord
|
|
308
420
|
false
|
309
421
|
end
|
310
422
|
|
423
|
+
# Does this adapter support including non-key columns?
|
424
|
+
def supports_index_include?
|
425
|
+
false
|
426
|
+
end
|
427
|
+
|
311
428
|
# Does this adapter support expression indices?
|
312
429
|
def supports_expression_index?
|
313
430
|
false
|
@@ -344,11 +461,26 @@ module ActiveRecord
|
|
344
461
|
false
|
345
462
|
end
|
346
463
|
|
464
|
+
# Does this adapter support creating deferrable constraints?
|
465
|
+
def supports_deferrable_constraints?
|
466
|
+
false
|
467
|
+
end
|
468
|
+
|
347
469
|
# Does this adapter support creating check constraints?
|
348
470
|
def supports_check_constraints?
|
349
471
|
false
|
350
472
|
end
|
351
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
|
+
|
352
484
|
# Does this adapter support views?
|
353
485
|
def supports_views?
|
354
486
|
false
|
@@ -364,7 +496,7 @@ module ActiveRecord
|
|
364
496
|
false
|
365
497
|
end
|
366
498
|
|
367
|
-
# Does this adapter support
|
499
|
+
# Does this adapter support JSON data type?
|
368
500
|
def supports_json?
|
369
501
|
false
|
370
502
|
end
|
@@ -418,12 +550,49 @@ module ActiveRecord
|
|
418
550
|
false
|
419
551
|
end
|
420
552
|
|
553
|
+
def supports_concurrent_connections?
|
554
|
+
true
|
555
|
+
end
|
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
|
+
|
565
|
+
def async_enabled? # :nodoc:
|
566
|
+
supports_concurrent_connections? &&
|
567
|
+
!ActiveRecord.async_query_executor.nil? && !pool.async_executor.nil?
|
568
|
+
end
|
569
|
+
|
421
570
|
# This is meant to be implemented by the adapters that support extensions
|
422
|
-
def disable_extension(name)
|
571
|
+
def disable_extension(name, **)
|
423
572
|
end
|
424
573
|
|
425
574
|
# This is meant to be implemented by the adapters that support extensions
|
426
|
-
def enable_extension(name)
|
575
|
+
def enable_extension(name, **)
|
576
|
+
end
|
577
|
+
|
578
|
+
# This is meant to be implemented by the adapters that support custom enum types
|
579
|
+
def create_enum(*) # :nodoc:
|
580
|
+
end
|
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:
|
427
596
|
end
|
428
597
|
|
429
598
|
def advisory_locks_enabled? # :nodoc:
|
@@ -461,27 +630,72 @@ module ActiveRecord
|
|
461
630
|
yield
|
462
631
|
end
|
463
632
|
|
633
|
+
# Override to check all foreign key constraints in a database.
|
634
|
+
# The adapter should raise a +ActiveRecord::StatementInvalid+ if foreign key
|
635
|
+
# constraints are not met.
|
636
|
+
def check_all_foreign_keys_valid!
|
637
|
+
end
|
638
|
+
|
464
639
|
# CONNECTION MANAGEMENT ====================================
|
465
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
|
+
|
466
648
|
# Checks whether the connection to the database is still active. This includes
|
467
649
|
# checking whether the database is actually capable of responding, i.e. whether
|
468
650
|
# the connection isn't stale.
|
469
651
|
def active?
|
470
652
|
end
|
471
653
|
|
472
|
-
# Disconnects from the database if already connected, and establishes a
|
473
|
-
#
|
474
|
-
#
|
475
|
-
def reconnect!
|
476
|
-
|
477
|
-
|
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
|
478
689
|
end
|
479
690
|
|
480
691
|
# Disconnects from the database if already connected. Otherwise, this
|
481
692
|
# method does nothing.
|
482
693
|
def disconnect!
|
483
|
-
|
484
|
-
|
694
|
+
@lock.synchronize do
|
695
|
+
clear_cache!(new_connection: true)
|
696
|
+
reset_transaction
|
697
|
+
@raw_connection_dirty = false
|
698
|
+
end
|
485
699
|
end
|
486
700
|
|
487
701
|
# Immediately forget this connection ever existed. Unlike disconnect!,
|
@@ -492,22 +706,20 @@ module ActiveRecord
|
|
492
706
|
# rid of a connection that belonged to its parent.
|
493
707
|
def discard!
|
494
708
|
# 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
709
|
end
|
502
710
|
|
503
711
|
# Reset the state of this connection, directing the DBMS to clear
|
504
712
|
# transactions and other connection-related server-side state. Usually a
|
505
713
|
# database-dependent operation.
|
506
714
|
#
|
507
|
-
#
|
508
|
-
#
|
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).
|
509
719
|
def reset!
|
510
|
-
|
720
|
+
clear_cache!(new_connection: true)
|
721
|
+
reset_transaction
|
722
|
+
configure_connection
|
511
723
|
end
|
512
724
|
|
513
725
|
# Removes the connection from the pool and disconnect it.
|
@@ -517,8 +729,16 @@ module ActiveRecord
|
|
517
729
|
end
|
518
730
|
|
519
731
|
# Clear any caching the database adapter may be doing.
|
520
|
-
def clear_cache!
|
521
|
-
|
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
|
522
742
|
end
|
523
743
|
|
524
744
|
# Returns true if its required to reload the connection between requests for development mode.
|
@@ -530,7 +750,31 @@ module ActiveRecord
|
|
530
750
|
# This is done under the hood by calling #active?. If the connection
|
531
751
|
# is no longer active, then this method will reconnect to the database.
|
532
752
|
def verify!
|
533
|
-
|
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
|
534
778
|
end
|
535
779
|
|
536
780
|
# Provides access to the underlying database driver for this adapter. For
|
@@ -539,9 +783,16 @@ module ActiveRecord
|
|
539
783
|
#
|
540
784
|
# This is useful for when you need to call a proprietary method such as
|
541
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+.
|
542
790
|
def raw_connection
|
543
|
-
|
544
|
-
|
791
|
+
with_raw_connection do |conn|
|
792
|
+
disable_lazy_transactions!
|
793
|
+
@raw_connection_dirty = true
|
794
|
+
conn
|
795
|
+
end
|
545
796
|
end
|
546
797
|
|
547
798
|
def default_uniqueness_comparison(attribute, value) # :nodoc:
|
@@ -593,84 +844,265 @@ module ActiveRecord
|
|
593
844
|
end
|
594
845
|
|
595
846
|
def database_version # :nodoc:
|
596
|
-
|
847
|
+
pool.server_version(self)
|
597
848
|
end
|
598
849
|
|
599
850
|
def check_version # :nodoc:
|
600
851
|
end
|
601
852
|
|
602
|
-
|
603
|
-
|
604
|
-
|
605
|
-
|
853
|
+
# Returns the version identifier of the schema currently available in
|
854
|
+
# the database. This is generally equal to the number of the highest-
|
855
|
+
# numbered migration that has been executed, or 0 if no schema
|
856
|
+
# information is present / the database is empty.
|
857
|
+
def schema_version
|
858
|
+
pool.migration_context.current_version
|
859
|
+
end
|
860
|
+
|
861
|
+
class << self
|
862
|
+
def register_class_with_precision(mapping, key, klass, **kwargs) # :nodoc:
|
863
|
+
mapping.register_type(key) do |*args|
|
864
|
+
precision = extract_precision(args.last)
|
865
|
+
klass.new(precision: precision, **kwargs)
|
866
|
+
end
|
867
|
+
end
|
868
|
+
|
869
|
+
def extended_type_map(default_timezone:) # :nodoc:
|
870
|
+
Type::TypeMap.new(self::TYPE_MAP).tap do |m|
|
871
|
+
register_class_with_precision m, %r(\A[^\(]*time)i, Type::Time, timezone: default_timezone
|
872
|
+
register_class_with_precision m, %r(\A[^\(]*datetime)i, Type::DateTime, timezone: default_timezone
|
873
|
+
m.alias_type %r(\A[^\(]*timestamp)i, "datetime"
|
874
|
+
end
|
875
|
+
end
|
876
|
+
|
877
|
+
private
|
878
|
+
def initialize_type_map(m)
|
879
|
+
register_class_with_limit m, %r(boolean)i, Type::Boolean
|
880
|
+
register_class_with_limit m, %r(char)i, Type::String
|
881
|
+
register_class_with_limit m, %r(binary)i, Type::Binary
|
882
|
+
register_class_with_limit m, %r(text)i, Type::Text
|
883
|
+
register_class_with_precision m, %r(date)i, Type::Date
|
884
|
+
register_class_with_precision m, %r(time)i, Type::Time
|
885
|
+
register_class_with_precision m, %r(datetime)i, Type::DateTime
|
886
|
+
register_class_with_limit m, %r(float)i, Type::Float
|
887
|
+
register_class_with_limit m, %r(int)i, Type::Integer
|
888
|
+
|
889
|
+
m.alias_type %r(blob)i, "binary"
|
890
|
+
m.alias_type %r(clob)i, "text"
|
891
|
+
m.alias_type %r(timestamp)i, "datetime"
|
892
|
+
m.alias_type %r(numeric)i, "decimal"
|
893
|
+
m.alias_type %r(number)i, "decimal"
|
894
|
+
m.alias_type %r(double)i, "float"
|
895
|
+
|
896
|
+
m.register_type %r(^json)i, Type::Json.new
|
897
|
+
|
898
|
+
m.register_type(%r(decimal)i) do |sql_type|
|
899
|
+
scale = extract_scale(sql_type)
|
900
|
+
precision = extract_precision(sql_type)
|
901
|
+
|
902
|
+
if scale == 0
|
903
|
+
# FIXME: Remove this class as well
|
904
|
+
Type::DecimalWithoutScale.new(precision: precision)
|
905
|
+
else
|
906
|
+
Type::Decimal.new(precision: precision, scale: scale)
|
907
|
+
end
|
908
|
+
end
|
909
|
+
end
|
910
|
+
|
911
|
+
def register_class_with_limit(mapping, key, klass)
|
912
|
+
mapping.register_type(key) do |*args|
|
913
|
+
limit = extract_limit(args.last)
|
914
|
+
klass.new(limit: limit)
|
915
|
+
end
|
916
|
+
end
|
917
|
+
|
918
|
+
def extract_scale(sql_type)
|
919
|
+
case sql_type
|
920
|
+
when /\((\d+)\)/ then 0
|
921
|
+
when /\((\d+)(,(\d+))\)/ then $3.to_i
|
922
|
+
end
|
923
|
+
end
|
924
|
+
|
925
|
+
def extract_precision(sql_type)
|
926
|
+
$1.to_i if sql_type =~ /\((\d+)(,\d+)?\)/
|
606
927
|
end
|
928
|
+
|
929
|
+
def extract_limit(sql_type)
|
930
|
+
$1.to_i if sql_type =~ /\((.*)\)/
|
931
|
+
end
|
932
|
+
end
|
933
|
+
|
934
|
+
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
|
935
|
+
EXTENDED_TYPE_MAPS = Concurrent::Map.new
|
936
|
+
|
937
|
+
private
|
938
|
+
def reconnect_can_restore_state?
|
939
|
+
transaction_manager.restorable? && !@raw_connection_dirty
|
607
940
|
end
|
608
941
|
|
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
|
-
|
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
|
636
997
|
else
|
637
|
-
|
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
|
638
1035
|
end
|
639
1036
|
end
|
640
1037
|
end
|
641
1038
|
|
642
|
-
|
643
|
-
|
644
|
-
|
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
|
645
1044
|
end
|
646
1045
|
|
647
|
-
def
|
648
|
-
|
649
|
-
limit = extract_limit(args.last)
|
650
|
-
klass.new(limit: limit)
|
651
|
-
end
|
1046
|
+
def retryable_connection_error?(exception)
|
1047
|
+
exception.is_a?(ConnectionNotEstablished) || exception.is_a?(ConnectionFailed)
|
652
1048
|
end
|
653
1049
|
|
654
|
-
def
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
1050
|
+
def invalidate_transaction(exception)
|
1051
|
+
return unless exception.is_a?(TransactionRollbackError)
|
1052
|
+
return unless savepoint_errors_invalidate_transactions?
|
1053
|
+
|
1054
|
+
current_transaction.invalidate!
|
659
1055
|
end
|
660
1056
|
|
661
|
-
def
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
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)
|
666
1062
|
end
|
667
1063
|
|
668
|
-
def
|
669
|
-
|
1064
|
+
def backoff(counter)
|
1065
|
+
sleep 0.1 * counter
|
670
1066
|
end
|
671
1067
|
|
672
|
-
def
|
673
|
-
|
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
|
+
|
1098
|
+
def type_map
|
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
|
674
1106
|
end
|
675
1107
|
|
676
1108
|
def translate_exception_class(e, sql, binds)
|
@@ -683,7 +1115,7 @@ module ActiveRecord
|
|
683
1115
|
exception
|
684
1116
|
end
|
685
1117
|
|
686
|
-
def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil) # :doc:
|
1118
|
+
def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil, async: false, &block) # :doc:
|
687
1119
|
@instrumenter.instrument(
|
688
1120
|
"sql.active_record",
|
689
1121
|
sql: sql,
|
@@ -691,22 +1123,30 @@ module ActiveRecord
|
|
691
1123
|
binds: binds,
|
692
1124
|
type_casted_binds: type_casted_binds,
|
693
1125
|
statement_name: statement_name,
|
694
|
-
|
695
|
-
|
696
|
-
|
697
|
-
|
698
|
-
|
699
|
-
|
1126
|
+
async: async,
|
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)
|
1134
|
+
end
|
1135
|
+
|
1136
|
+
def transform_query(sql)
|
1137
|
+
ActiveRecord.query_transformers.each do |transformer|
|
1138
|
+
sql = transformer.call(sql, self)
|
700
1139
|
end
|
1140
|
+
sql
|
701
1141
|
end
|
702
1142
|
|
703
1143
|
def translate_exception(exception, message:, sql:, binds:)
|
704
1144
|
# override in derived class
|
705
1145
|
case exception
|
706
|
-
when RuntimeError
|
1146
|
+
when RuntimeError, ActiveRecord::ActiveRecordError
|
707
1147
|
exception
|
708
1148
|
else
|
709
|
-
ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds)
|
1149
|
+
ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
710
1150
|
end
|
711
1151
|
end
|
712
1152
|
|
@@ -750,9 +1190,30 @@ module ActiveRecord
|
|
750
1190
|
#
|
751
1191
|
# This is an internal hook to make possible connection adapters to build
|
752
1192
|
# custom result objects with connection-specific data.
|
753
|
-
def build_result(columns:, rows:, column_types:
|
1193
|
+
def build_result(columns:, rows:, column_types: nil)
|
754
1194
|
ActiveRecord::Result.new(columns, rows, column_types)
|
755
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
|
756
1217
|
end
|
757
1218
|
end
|
758
1219
|
end
|