activerecord 6.0.0 → 7.2.3
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 +996 -594
- data/MIT-LICENSE +1 -1
- data/README.rdoc +34 -34
- data/examples/performance.rb +2 -2
- data/lib/active_record/aggregations.rb +22 -20
- data/lib/active_record/association_relation.rb +22 -12
- data/lib/active_record/associations/alias_tracker.rb +41 -30
- data/lib/active_record/associations/association.rb +106 -41
- data/lib/active_record/associations/association_scope.rb +30 -21
- data/lib/active_record/associations/belongs_to_association.rb +69 -14
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +20 -6
- data/lib/active_record/associations/builder/association.rb +39 -6
- data/lib/active_record/associations/builder/belongs_to.rb +47 -17
- data/lib/active_record/associations/builder/collection_association.rb +14 -6
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -10
- data/lib/active_record/associations/builder/has_many.rb +7 -3
- data/lib/active_record/associations/builder/has_one.rb +13 -16
- data/lib/active_record/associations/builder/singular_association.rb +7 -3
- data/lib/active_record/associations/collection_association.rb +90 -53
- data/lib/active_record/associations/collection_proxy.rb +54 -19
- 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 +21 -1
- data/lib/active_record/associations/has_many_association.rb +41 -10
- data/lib/active_record/associations/has_many_through_association.rb +29 -12
- data/lib/active_record/associations/has_one_association.rb +33 -9
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +41 -17
- data/lib/active_record/associations/join_dependency/join_part.rb +3 -3
- data/lib/active_record/associations/join_dependency.rb +97 -54
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +237 -54
- 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 +51 -17
- data/lib/active_record/associations/preloader.rb +55 -121
- data/lib/active_record/associations/singular_association.rb +16 -4
- data/lib/active_record/associations/through_association.rb +26 -15
- data/lib/active_record/associations.rb +454 -440
- data/lib/active_record/asynchronous_queries_tracker.rb +60 -0
- data/lib/active_record/attribute_assignment.rb +11 -14
- data/lib/active_record/attribute_methods/before_type_cast.rb +36 -11
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +75 -34
- data/lib/active_record/attribute_methods/primary_key.rb +53 -31
- data/lib/active_record/attribute_methods/query.rb +31 -22
- data/lib/active_record/attribute_methods/read.rb +16 -17
- data/lib/active_record/attribute_methods/serialization.rb +177 -35
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +18 -15
- data/lib/active_record/attribute_methods/write.rb +16 -28
- data/lib/active_record/attribute_methods.rb +227 -100
- data/lib/active_record/attributes.rb +94 -56
- data/lib/active_record/autosave_association.rb +119 -73
- data/lib/active_record/base.rb +31 -21
- data/lib/active_record/callbacks.rb +168 -55
- 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 -25
- 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 +367 -565
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -57
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +277 -89
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +241 -69
- data/lib/active_record/connection_adapters/abstract/quoting.rb +122 -134
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +324 -72
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +17 -4
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +611 -211
- data/lib/active_record/connection_adapters/abstract/transaction.rb +425 -82
- data/lib/active_record/connection_adapters/abstract_adapter.rb +698 -211
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +464 -239
- data/lib/active_record/connection_adapters/column.rb +28 -1
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +2 -1
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +32 -137
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -2
- data/lib/active_record/connection_adapters/mysql/quoting.rb +90 -43
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +41 -7
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +18 -1
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +13 -4
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +53 -15
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +152 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +127 -63
- data/lib/active_record/connection_adapters/pool_config.rb +83 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +57 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +54 -2
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +127 -100
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +9 -5
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +10 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +15 -2
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +53 -15
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -3
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +5 -4
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -3
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +35 -8
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
- 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 +23 -4
- data/lib/active_record/connection_adapters/postgresql/oid.rb +4 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +139 -106
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -2
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +98 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +176 -4
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +78 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +462 -118
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -11
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +585 -295
- data/lib/active_record/connection_adapters/schema_cache.rb +399 -60
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +99 -48
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +80 -54
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +27 -1
- 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 +102 -24
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +425 -174
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -1
- 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 +176 -0
- data/lib/active_record/connection_handling.rb +243 -115
- data/lib/active_record/core.rb +481 -199
- data/lib/active_record/counter_cache.rb +69 -32
- data/lib/active_record/database_configurations/connection_url_resolver.rb +107 -0
- data/lib/active_record/database_configurations/database_config.rb +77 -10
- data/lib/active_record/database_configurations/hash_config.rb +148 -26
- data/lib/active_record/database_configurations/url_config.rb +44 -45
- data/lib/active_record/database_configurations.rb +190 -114
- data/lib/active_record/delegated_type.rb +279 -0
- data/lib/active_record/deprecator.rb +7 -0
- data/lib/active_record/destroy_association_async_job.rb +38 -0
- data/lib/active_record/disable_joins_association_relation.rb +39 -0
- data/lib/active_record/dynamic_matchers.rb +5 -6
- 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 +171 -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 +224 -73
- data/lib/active_record/errors.rb +254 -36
- data/lib/active_record/explain.rb +30 -17
- data/lib/active_record/explain_registry.rb +11 -6
- data/lib/active_record/explain_subscriber.rb +2 -2
- data/lib/active_record/fixture_set/file.rb +22 -15
- data/lib/active_record/fixture_set/model_metadata.rb +15 -6
- data/lib/active_record/fixture_set/render_context.rb +3 -1
- data/lib/active_record/fixture_set/table_row.rb +88 -16
- data/lib/active_record/fixture_set/table_rows.rb +4 -5
- data/lib/active_record/fixtures.rb +229 -116
- data/lib/active_record/future_result.rb +178 -0
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +121 -48
- data/lib/active_record/insert_all.rb +178 -29
- data/lib/active_record/integration.rb +16 -14
- data/lib/active_record/internal_metadata.rb +132 -21
- data/lib/active_record/legacy_yaml_adapter.rb +3 -36
- data/lib/active_record/locking/optimistic.rb +64 -33
- data/lib/active_record/locking/pessimistic.rb +21 -8
- data/lib/active_record/log_subscriber.rb +61 -30
- 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/session.rb +3 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +19 -19
- data/lib/active_record/middleware/database_selector.rb +25 -13
- data/lib/active_record/middleware/shard_selector.rb +62 -0
- data/lib/active_record/migration/command_recorder.rb +160 -55
- data/lib/active_record/migration/compatibility.rb +286 -43
- 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 -2
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +421 -193
- data/lib/active_record/model_schema.rb +217 -125
- data/lib/active_record/nested_attributes.rb +62 -27
- data/lib/active_record/no_touching.rb +4 -4
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +322 -319
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +18 -15
- data/lib/active_record/query_logs.rb +193 -0
- data/lib/active_record/query_logs_formatter.rb +41 -0
- data/lib/active_record/querying.rb +54 -14
- data/lib/active_record/railtie.rb +250 -72
- data/lib/active_record/railties/console_sandbox.rb +2 -4
- data/lib/active_record/railties/controller_runtime.rb +25 -11
- data/lib/active_record/railties/databases.rake +312 -197
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +45 -3
- data/lib/active_record/reflection.rb +389 -146
- data/lib/active_record/relation/batches/batch_enumerator.rb +61 -16
- data/lib/active_record/relation/batches.rb +214 -73
- data/lib/active_record/relation/calculations.rb +379 -124
- data/lib/active_record/relation/delegation.rb +36 -23
- data/lib/active_record/relation/finder_methods.rb +159 -49
- data/lib/active_record/relation/from_clause.rb +5 -1
- data/lib/active_record/relation/merger.rb +41 -33
- data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -11
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +42 -7
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +20 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +5 -1
- data/lib/active_record/relation/predicate_builder.rb +79 -53
- data/lib/active_record/relation/query_attribute.rb +30 -12
- data/lib/active_record/relation/query_methods.rb +1156 -279
- data/lib/active_record/relation/record_fetch_warning.rb +12 -11
- data/lib/active_record/relation/spawn_methods.rb +10 -9
- data/lib/active_record/relation/where_clause.rb +100 -66
- data/lib/active_record/relation.rb +829 -194
- data/lib/active_record/result.rb +76 -56
- data/lib/active_record/runtime_registry.rb +71 -13
- data/lib/active_record/sanitization.rb +86 -47
- data/lib/active_record/schema.rb +39 -23
- data/lib/active_record/schema_dumper.rb +140 -33
- data/lib/active_record/schema_migration.rb +74 -29
- data/lib/active_record/scoping/default.rb +73 -19
- data/lib/active_record/scoping/named.rb +10 -28
- data/lib/active_record/scoping.rb +65 -35
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +34 -8
- data/lib/active_record/serialization.rb +11 -4
- data/lib/active_record/signed_id.rb +138 -0
- data/lib/active_record/statement_cache.rb +26 -10
- data/lib/active_record/store.rb +19 -14
- data/lib/active_record/suppressor.rb +15 -17
- data/lib/active_record/table_metadata.rb +46 -36
- data/lib/active_record/tasks/database_tasks.rb +371 -205
- data/lib/active_record/tasks/mysql_database_tasks.rb +43 -36
- data/lib/active_record/tasks/postgresql_database_tasks.rb +54 -41
- data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -13
- data/lib/active_record/test_databases.rb +5 -4
- data/lib/active_record/test_fixtures.rb +189 -104
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +35 -25
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +31 -27
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +131 -99
- data/lib/active_record/translation.rb +3 -5
- data/lib/active_record/type/adapter_specific_registry.rb +33 -18
- data/lib/active_record/type/hash_lookup_type_map.rb +34 -2
- data/lib/active_record/type/internal/timezone.rb +7 -2
- data/lib/active_record/type/serialized.rb +11 -6
- data/lib/active_record/type/time.rb +14 -0
- data/lib/active_record/type/type_map.rb +17 -21
- data/lib/active_record/type/unsigned_integer.rb +0 -1
- data/lib/active_record/type.rb +7 -2
- data/lib/active_record/type_caster/connection.rb +4 -5
- data/lib/active_record/type_caster/map.rb +8 -5
- data/lib/active_record/validations/absence.rb +1 -1
- data/lib/active_record/validations/associated.rb +13 -8
- data/lib/active_record/validations/numericality.rb +36 -0
- data/lib/active_record/validations/presence.rb +5 -28
- data/lib/active_record/validations/uniqueness.rb +88 -18
- data/lib/active_record/validations.rb +15 -8
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +446 -40
- data/lib/arel/alias_predication.rb +1 -1
- data/lib/arel/attributes/attribute.rb +4 -8
- data/lib/arel/collectors/bind.rb +8 -1
- data/lib/arel/collectors/composite.rb +15 -0
- data/lib/arel/collectors/sql_string.rb +7 -0
- data/lib/arel/collectors/substitute_binds.rb +7 -0
- data/lib/arel/crud.rb +30 -22
- data/lib/arel/delete_manager.rb +23 -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 +82 -9
- data/lib/arel/nodes/bind_param.rb +8 -0
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/casted.rb +22 -10
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/delete_statement.rb +14 -13
- data/lib/arel/nodes/equality.rb +6 -9
- 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/grouping.rb +3 -0
- data/lib/arel/nodes/homogeneous_in.rb +68 -0
- data/lib/arel/nodes/in.rb +8 -1
- data/lib/arel/nodes/infix_operation.rb +13 -1
- data/lib/arel/nodes/insert_statement.rb +2 -2
- data/lib/arel/nodes/join_source.rb +1 -1
- 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 +122 -11
- data/lib/arel/nodes/ordering.rb +27 -0
- 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 +16 -0
- data/lib/arel/nodes/table_alias.rb +11 -3
- data/lib/arel/nodes/unary.rb +0 -1
- data/lib/arel/nodes/update_statement.rb +11 -4
- data/lib/arel/nodes.rb +10 -3
- data/lib/arel/predications.rb +31 -28
- data/lib/arel/select_manager.rb +18 -9
- data/lib/arel/table.rb +21 -10
- data/lib/arel/tree_manager.rb +8 -15
- data/lib/arel/update_manager.rb +25 -5
- data/lib/arel/visitors/dot.rb +94 -90
- data/lib/arel/visitors/mysql.rb +34 -6
- data/lib/arel/visitors/postgresql.rb +5 -16
- data/lib/arel/visitors/sqlite.rb +25 -1
- data/lib/arel/visitors/to_sql.rb +227 -81
- data/lib/arel/visitors/visitor.rb +2 -3
- data/lib/arel/visitors.rb +0 -7
- data/lib/arel.rb +37 -15
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +6 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -4
- data/lib/rails/generators/active_record/migration.rb +9 -3
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +49 -4
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- 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 +117 -30
- data/lib/active_record/attribute_decorators.rb +0 -90
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -297
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/null_relation.rb +0 -68
- data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
- data/lib/active_record/relation/where_clause_factory.rb +0 -33
- data/lib/arel/attributes.rb +0 -22
- data/lib/arel/visitors/depth_first.rb +0 -204
- data/lib/arel/visitors/ibm_db.rb +0 -34
- data/lib/arel/visitors/informix.rb +0 -62
- data/lib/arel/visitors/mssql.rb +0 -157
- data/lib/arel/visitors/oracle.rb +0 -159
- data/lib/arel/visitors/oracle12.rb +0 -66
- data/lib/arel/visitors/where_sql.rb +0 -23
|
@@ -1,58 +1,20 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require "
|
|
4
|
-
require "active_record/connection_adapters/schema_cache"
|
|
3
|
+
require "set"
|
|
5
4
|
require "active_record/connection_adapters/sql_type_metadata"
|
|
6
5
|
require "active_record/connection_adapters/abstract/schema_dumper"
|
|
7
6
|
require "active_record/connection_adapters/abstract/schema_creation"
|
|
7
|
+
require "active_support/concurrency/null_lock"
|
|
8
8
|
require "active_support/concurrency/load_interlock_aware_monitor"
|
|
9
|
-
require "active_support/deprecation"
|
|
10
9
|
require "arel/collectors/bind"
|
|
11
10
|
require "arel/collectors/composite"
|
|
12
11
|
require "arel/collectors/sql_string"
|
|
13
12
|
require "arel/collectors/substitute_binds"
|
|
14
|
-
require "concurrent/atomic/thread_local_var"
|
|
15
13
|
|
|
16
14
|
module ActiveRecord
|
|
17
15
|
module ConnectionAdapters # :nodoc:
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
autoload :Column
|
|
21
|
-
autoload :ConnectionSpecification
|
|
22
|
-
|
|
23
|
-
autoload_at "active_record/connection_adapters/abstract/schema_definitions" do
|
|
24
|
-
autoload :IndexDefinition
|
|
25
|
-
autoload :ColumnDefinition
|
|
26
|
-
autoload :ChangeColumnDefinition
|
|
27
|
-
autoload :ForeignKeyDefinition
|
|
28
|
-
autoload :TableDefinition
|
|
29
|
-
autoload :Table
|
|
30
|
-
autoload :AlterTable
|
|
31
|
-
autoload :ReferenceDefinition
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
autoload_at "active_record/connection_adapters/abstract/connection_pool" do
|
|
35
|
-
autoload :ConnectionHandler
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
autoload_under "abstract" do
|
|
39
|
-
autoload :SchemaStatements
|
|
40
|
-
autoload :DatabaseStatements
|
|
41
|
-
autoload :DatabaseLimits
|
|
42
|
-
autoload :Quoting
|
|
43
|
-
autoload :ConnectionPool
|
|
44
|
-
autoload :QueryCache
|
|
45
|
-
autoload :Savepoints
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
autoload_at "active_record/connection_adapters/abstract/transaction" do
|
|
49
|
-
autoload :TransactionManager
|
|
50
|
-
autoload :NullTransaction
|
|
51
|
-
autoload :RealTransaction
|
|
52
|
-
autoload :SavepointTransaction
|
|
53
|
-
autoload :TransactionState
|
|
54
|
-
end
|
|
55
|
-
|
|
16
|
+
# = Active Record Abstract Adapter
|
|
17
|
+
#
|
|
56
18
|
# Active Record supports multiple database systems. AbstractAdapter and
|
|
57
19
|
# related classes form the abstraction layer which makes this possible.
|
|
58
20
|
# An AbstractAdapter represents a connection to a database, and provides an
|
|
@@ -61,7 +23,7 @@ module ActiveRecord
|
|
|
61
23
|
# and +:limit+ options, etc.
|
|
62
24
|
#
|
|
63
25
|
# All the concrete database adapters follow the interface laid down in this class.
|
|
64
|
-
# {ActiveRecord::Base.
|
|
26
|
+
# {ActiveRecord::Base.lease_connection}[rdoc-ref:ConnectionHandling#lease_connection] returns an AbstractAdapter object, which
|
|
65
27
|
# you can use.
|
|
66
28
|
#
|
|
67
29
|
# Most of the methods in the adapter are useful during migrations. Most
|
|
@@ -77,11 +39,19 @@ module ActiveRecord
|
|
|
77
39
|
include Savepoints
|
|
78
40
|
|
|
79
41
|
SIMPLE_INT = /\A\d+\z/
|
|
42
|
+
COMMENT_REGEX = %r{(?:--.*\n)|/\*(?:[^*]|\*[^/])*\*/}
|
|
80
43
|
|
|
81
|
-
|
|
44
|
+
attr_reader :pool
|
|
82
45
|
attr_reader :visitor, :owner, :logger, :lock
|
|
46
|
+
attr_accessor :pinned # :nodoc:
|
|
83
47
|
alias :in_use? :owner
|
|
84
48
|
|
|
49
|
+
def pool=(value)
|
|
50
|
+
return if value.eql?(@pool)
|
|
51
|
+
@schema_cache = nil
|
|
52
|
+
@pool = value
|
|
53
|
+
end
|
|
54
|
+
|
|
85
55
|
set_callback :checkin, :after, :enable_lazy_transactions!
|
|
86
56
|
|
|
87
57
|
def self.type_cast_config_to_integer(config)
|
|
@@ -102,82 +72,189 @@ module ActiveRecord
|
|
|
102
72
|
end
|
|
103
73
|
end
|
|
104
74
|
|
|
75
|
+
def self.validate_default_timezone(config)
|
|
76
|
+
case config
|
|
77
|
+
when nil
|
|
78
|
+
when "utc", "local"
|
|
79
|
+
config.to_sym
|
|
80
|
+
else
|
|
81
|
+
raise ArgumentError, "default_timezone must be either 'utc' or 'local'"
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
DEFAULT_READ_QUERY = [:begin, :commit, :explain, :release, :rollback, :savepoint, :select, :with] # :nodoc:
|
|
86
|
+
private_constant :DEFAULT_READ_QUERY
|
|
87
|
+
|
|
105
88
|
def self.build_read_query_regexp(*parts) # :nodoc:
|
|
106
|
-
parts
|
|
107
|
-
|
|
89
|
+
parts += DEFAULT_READ_QUERY
|
|
90
|
+
parts = parts.map { |part| /#{part}/i }
|
|
91
|
+
/\A(?:[(\s]|#{COMMENT_REGEX})*#{Regexp.union(*parts)}/
|
|
108
92
|
end
|
|
109
93
|
|
|
110
|
-
def self.
|
|
111
|
-
|
|
94
|
+
def self.find_cmd_and_exec(commands, *args) # :doc:
|
|
95
|
+
commands = Array(commands)
|
|
96
|
+
|
|
97
|
+
dirs_on_path = ENV["PATH"].to_s.split(File::PATH_SEPARATOR)
|
|
98
|
+
unless (ext = RbConfig::CONFIG["EXEEXT"]).empty?
|
|
99
|
+
commands = commands.map { |cmd| "#{cmd}#{ext}" }
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
full_path_command = nil
|
|
103
|
+
found = commands.detect do |cmd|
|
|
104
|
+
dirs_on_path.detect do |path|
|
|
105
|
+
full_path_command = File.join(path, cmd)
|
|
106
|
+
begin
|
|
107
|
+
stat = File.stat(full_path_command)
|
|
108
|
+
rescue SystemCallError
|
|
109
|
+
else
|
|
110
|
+
stat.file? && stat.executable?
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
if found
|
|
116
|
+
exec full_path_command, *args
|
|
117
|
+
else
|
|
118
|
+
abort("Couldn't find database client: #{commands.join(', ')}. Check your $PATH and try again.")
|
|
119
|
+
end
|
|
112
120
|
end
|
|
113
121
|
|
|
114
|
-
|
|
115
|
-
|
|
122
|
+
# Opens a database console session.
|
|
123
|
+
def self.dbconsole(config, options = {})
|
|
124
|
+
raise NotImplementedError
|
|
116
125
|
end
|
|
117
126
|
|
|
118
|
-
def initialize(
|
|
127
|
+
def initialize(config_or_deprecated_connection, deprecated_logger = nil, deprecated_connection_options = nil, deprecated_config = nil) # :nodoc:
|
|
119
128
|
super()
|
|
120
129
|
|
|
121
|
-
@
|
|
122
|
-
@
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
@idle_since = Concurrent.monotonic_time
|
|
128
|
-
@visitor = arel_visitor
|
|
129
|
-
@statements = build_statement_pool
|
|
130
|
-
@lock = ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
|
|
130
|
+
@raw_connection = nil
|
|
131
|
+
@unconfigured_connection = nil
|
|
132
|
+
|
|
133
|
+
if config_or_deprecated_connection.is_a?(Hash)
|
|
134
|
+
@config = config_or_deprecated_connection.symbolize_keys
|
|
135
|
+
@logger = ActiveRecord::Base.logger
|
|
131
136
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
137
|
+
if deprecated_logger || deprecated_connection_options || deprecated_config
|
|
138
|
+
raise ArgumentError, "when initializing an Active Record adapter with a config hash, that should be the only argument"
|
|
139
|
+
end
|
|
135
140
|
else
|
|
136
|
-
|
|
141
|
+
# Soft-deprecated for now; we'll probably warn in future.
|
|
142
|
+
|
|
143
|
+
@unconfigured_connection = config_or_deprecated_connection
|
|
144
|
+
@logger = deprecated_logger || ActiveRecord::Base.logger
|
|
145
|
+
if deprecated_config
|
|
146
|
+
@config = (deprecated_config || {}).symbolize_keys
|
|
147
|
+
@connection_parameters = deprecated_connection_options
|
|
148
|
+
else
|
|
149
|
+
@config = (deprecated_connection_options || {}).symbolize_keys
|
|
150
|
+
@connection_parameters = nil
|
|
151
|
+
end
|
|
137
152
|
end
|
|
138
153
|
|
|
154
|
+
@owner = nil
|
|
155
|
+
@pinned = false
|
|
156
|
+
@instrumenter = ActiveSupport::Notifications.instrumenter
|
|
157
|
+
@pool = ActiveRecord::ConnectionAdapters::NullPool.new
|
|
158
|
+
@idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
159
|
+
@visitor = arel_visitor
|
|
160
|
+
@statements = build_statement_pool
|
|
161
|
+
self.lock_thread = nil
|
|
162
|
+
|
|
163
|
+
@prepared_statements = !ActiveRecord.disable_prepared_statements && self.class.type_cast_config_to_boolean(
|
|
164
|
+
@config.fetch(:prepared_statements) { default_prepared_statements }
|
|
165
|
+
)
|
|
166
|
+
|
|
139
167
|
@advisory_locks_enabled = self.class.type_cast_config_to_boolean(
|
|
140
|
-
config.fetch(:advisory_locks, true)
|
|
168
|
+
@config.fetch(:advisory_locks, true)
|
|
141
169
|
)
|
|
170
|
+
|
|
171
|
+
@default_timezone = self.class.validate_default_timezone(@config[:default_timezone])
|
|
172
|
+
|
|
173
|
+
@raw_connection_dirty = false
|
|
174
|
+
@last_activity = nil
|
|
175
|
+
@verified = false
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def inspect # :nodoc:
|
|
179
|
+
name_field = " name=#{pool.db_config.name.inspect}" unless pool.db_config.name == "primary"
|
|
180
|
+
shard_field = " shard=#{shard.inspect}" unless shard == :default
|
|
181
|
+
|
|
182
|
+
"#<#{self.class.name}:#{'%#016x' % (object_id << 1)} env_name=#{pool.db_config.env_name.inspect}#{name_field} role=#{role.inspect}#{shard_field}>"
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
def lock_thread=(lock_thread) # :nodoc:
|
|
186
|
+
@lock =
|
|
187
|
+
case lock_thread
|
|
188
|
+
when Thread
|
|
189
|
+
ActiveSupport::Concurrency::ThreadLoadInterlockAwareMonitor.new
|
|
190
|
+
when Fiber
|
|
191
|
+
ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
|
|
192
|
+
else
|
|
193
|
+
ActiveSupport::Concurrency::NullLock
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
EXCEPTION_NEVER = { Exception => :never }.freeze # :nodoc:
|
|
198
|
+
EXCEPTION_IMMEDIATE = { Exception => :immediate }.freeze # :nodoc:
|
|
199
|
+
private_constant :EXCEPTION_NEVER, :EXCEPTION_IMMEDIATE
|
|
200
|
+
def with_instrumenter(instrumenter, &block) # :nodoc:
|
|
201
|
+
Thread.handle_interrupt(EXCEPTION_NEVER) do
|
|
202
|
+
previous_instrumenter = @instrumenter
|
|
203
|
+
@instrumenter = instrumenter
|
|
204
|
+
Thread.handle_interrupt(EXCEPTION_IMMEDIATE, &block)
|
|
205
|
+
ensure
|
|
206
|
+
@instrumenter = previous_instrumenter
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def check_if_write_query(sql) # :nodoc:
|
|
211
|
+
if preventing_writes? && write_query?(sql)
|
|
212
|
+
raise ActiveRecord::ReadOnlyError, "Write query attempted while in readonly mode: #{sql}"
|
|
213
|
+
end
|
|
142
214
|
end
|
|
143
215
|
|
|
144
216
|
def replica?
|
|
145
217
|
@config[:replica] || false
|
|
146
218
|
end
|
|
147
219
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
# Returns true if the connection is a replica, or if +prevent_writes+
|
|
151
|
-
# is set to true.
|
|
152
|
-
def preventing_writes?
|
|
153
|
-
replica? || ActiveRecord::Base.connection_handler.prevent_writes
|
|
220
|
+
def connection_retries
|
|
221
|
+
(@config[:connection_retries] || 1).to_i
|
|
154
222
|
end
|
|
155
223
|
|
|
156
|
-
def
|
|
157
|
-
@config[:
|
|
224
|
+
def verify_timeout
|
|
225
|
+
(@config[:verify_timeout] || 2).to_i
|
|
158
226
|
end
|
|
159
227
|
|
|
160
|
-
def
|
|
161
|
-
|
|
228
|
+
def retry_deadline
|
|
229
|
+
if @config[:retry_deadline]
|
|
230
|
+
@config[:retry_deadline].to_f
|
|
231
|
+
else
|
|
232
|
+
nil
|
|
233
|
+
end
|
|
162
234
|
end
|
|
163
235
|
|
|
164
|
-
def
|
|
165
|
-
@
|
|
166
|
-
|
|
167
|
-
spec_name = conn.pool.spec.name
|
|
168
|
-
name = "#{spec_name}::SchemaMigration"
|
|
236
|
+
def default_timezone
|
|
237
|
+
@default_timezone || ActiveRecord.default_timezone
|
|
238
|
+
end
|
|
169
239
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
240
|
+
# Determines whether writes are currently being prevented.
|
|
241
|
+
#
|
|
242
|
+
# Returns true if the connection is a replica or returns
|
|
243
|
+
# the value of +current_preventing_writes+.
|
|
244
|
+
def preventing_writes?
|
|
245
|
+
return true if replica?
|
|
246
|
+
return false if connection_class.nil?
|
|
247
|
+
|
|
248
|
+
connection_class.current_preventing_writes
|
|
249
|
+
end
|
|
173
250
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
end
|
|
251
|
+
def prepared_statements?
|
|
252
|
+
@prepared_statements && !prepared_statements_disabled_cache.include?(object_id)
|
|
177
253
|
end
|
|
254
|
+
alias :prepared_statements :prepared_statements?
|
|
178
255
|
|
|
179
|
-
def
|
|
180
|
-
|
|
256
|
+
def prepared_statements_disabled_cache # :nodoc:
|
|
257
|
+
ActiveSupport::IsolatedExecutionState[:active_record_prepared_statements_disabled_cache] ||= Set.new
|
|
181
258
|
end
|
|
182
259
|
|
|
183
260
|
class Version
|
|
@@ -207,37 +284,48 @@ module ActiveRecord
|
|
|
207
284
|
def lease
|
|
208
285
|
if in_use?
|
|
209
286
|
msg = +"Cannot lease connection, "
|
|
210
|
-
if @owner ==
|
|
287
|
+
if @owner == ActiveSupport::IsolatedExecutionState.context
|
|
211
288
|
msg << "it is already leased by the current thread."
|
|
212
289
|
else
|
|
213
290
|
msg << "it is already in use by a different thread: #{@owner}. " \
|
|
214
|
-
"Current thread: #{
|
|
291
|
+
"Current thread: #{ActiveSupport::IsolatedExecutionState.context}."
|
|
215
292
|
end
|
|
216
293
|
raise ActiveRecordError, msg
|
|
217
294
|
end
|
|
218
295
|
|
|
219
|
-
@owner =
|
|
296
|
+
@owner = ActiveSupport::IsolatedExecutionState.context
|
|
220
297
|
end
|
|
221
298
|
|
|
222
|
-
def
|
|
223
|
-
@pool.
|
|
299
|
+
def connection_class # :nodoc:
|
|
300
|
+
@pool.connection_class
|
|
224
301
|
end
|
|
225
302
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
303
|
+
# The role (e.g. +:writing+) for the current connection. In a
|
|
304
|
+
# non-multi role application, +:writing+ is returned.
|
|
305
|
+
def role
|
|
306
|
+
@pool.role
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
# The shard (e.g. +:default+) for the current connection. In
|
|
310
|
+
# a non-sharded application, +:default+ is returned.
|
|
311
|
+
def shard
|
|
312
|
+
@pool.shard
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def schema_cache
|
|
316
|
+
@pool.schema_cache || (@schema_cache ||= BoundSchemaReflection.for_lone_connection(@pool.schema_reflection, self))
|
|
229
317
|
end
|
|
230
318
|
|
|
231
319
|
# this method must only be called while holding connection pool's mutex
|
|
232
320
|
def expire
|
|
233
321
|
if in_use?
|
|
234
|
-
if @owner !=
|
|
322
|
+
if @owner != ActiveSupport::IsolatedExecutionState.context
|
|
235
323
|
raise ActiveRecordError, "Cannot expire connection, " \
|
|
236
324
|
"it is owned by a different thread: #{@owner}. " \
|
|
237
|
-
"Current thread: #{
|
|
325
|
+
"Current thread: #{ActiveSupport::IsolatedExecutionState.context}."
|
|
238
326
|
end
|
|
239
327
|
|
|
240
|
-
@idle_since =
|
|
328
|
+
@idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
241
329
|
@owner = nil
|
|
242
330
|
else
|
|
243
331
|
raise ActiveRecordError, "Cannot expire connection, it is not currently leased."
|
|
@@ -247,10 +335,10 @@ module ActiveRecord
|
|
|
247
335
|
# this method must only be called while holding connection pool's mutex (and a desire for segfaults)
|
|
248
336
|
def steal! # :nodoc:
|
|
249
337
|
if in_use?
|
|
250
|
-
if @owner !=
|
|
338
|
+
if @owner != ActiveSupport::IsolatedExecutionState.context
|
|
251
339
|
pool.send :remove_connection_from_thread_cache, self, @owner
|
|
252
340
|
|
|
253
|
-
@owner =
|
|
341
|
+
@owner = ActiveSupport::IsolatedExecutionState.context
|
|
254
342
|
end
|
|
255
343
|
else
|
|
256
344
|
raise ActiveRecordError, "Cannot steal connection, it is not currently leased."
|
|
@@ -260,11 +348,21 @@ module ActiveRecord
|
|
|
260
348
|
# Seconds since this connection was returned to the pool
|
|
261
349
|
def seconds_idle # :nodoc:
|
|
262
350
|
return 0 if in_use?
|
|
263
|
-
|
|
351
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC) - @idle_since
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
# Seconds since this connection last communicated with the server
|
|
355
|
+
def seconds_since_last_activity # :nodoc:
|
|
356
|
+
if @raw_connection && @last_activity
|
|
357
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC) - @last_activity
|
|
358
|
+
end
|
|
264
359
|
end
|
|
265
360
|
|
|
266
361
|
def unprepared_statement
|
|
267
|
-
|
|
362
|
+
cache = prepared_statements_disabled_cache.add?(object_id) if @prepared_statements
|
|
363
|
+
yield
|
|
364
|
+
ensure
|
|
365
|
+
cache&.delete(object_id)
|
|
268
366
|
end
|
|
269
367
|
|
|
270
368
|
# Returns the human-readable name of the adapter. Use mixed case - one
|
|
@@ -275,7 +373,14 @@ module ActiveRecord
|
|
|
275
373
|
|
|
276
374
|
# Does the database for this adapter exist?
|
|
277
375
|
def self.database_exists?(config)
|
|
278
|
-
|
|
376
|
+
new(config).database_exists?
|
|
377
|
+
end
|
|
378
|
+
|
|
379
|
+
def database_exists?
|
|
380
|
+
connect!
|
|
381
|
+
true
|
|
382
|
+
rescue ActiveRecord::NoDatabaseError
|
|
383
|
+
false
|
|
279
384
|
end
|
|
280
385
|
|
|
281
386
|
# Does this adapter support DDL rollbacks in transactions? That is, would
|
|
@@ -293,6 +398,16 @@ module ActiveRecord
|
|
|
293
398
|
false
|
|
294
399
|
end
|
|
295
400
|
|
|
401
|
+
# Do TransactionRollbackErrors on savepoints affect the parent
|
|
402
|
+
# transaction?
|
|
403
|
+
def savepoint_errors_invalidate_transactions?
|
|
404
|
+
false
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
def supports_restart_db_transaction?
|
|
408
|
+
false
|
|
409
|
+
end
|
|
410
|
+
|
|
296
411
|
# Does this adapter support application-enforced advisory locking?
|
|
297
412
|
def supports_advisory_locks?
|
|
298
413
|
false
|
|
@@ -305,6 +420,10 @@ module ActiveRecord
|
|
|
305
420
|
false
|
|
306
421
|
end
|
|
307
422
|
|
|
423
|
+
def supports_partitioned_indexes?
|
|
424
|
+
false
|
|
425
|
+
end
|
|
426
|
+
|
|
308
427
|
# Does this adapter support index sort order?
|
|
309
428
|
def supports_index_sort_order?
|
|
310
429
|
false
|
|
@@ -315,6 +434,11 @@ module ActiveRecord
|
|
|
315
434
|
false
|
|
316
435
|
end
|
|
317
436
|
|
|
437
|
+
# Does this adapter support including non-key columns?
|
|
438
|
+
def supports_index_include?
|
|
439
|
+
false
|
|
440
|
+
end
|
|
441
|
+
|
|
318
442
|
# Does this adapter support expression indices?
|
|
319
443
|
def supports_expression_index?
|
|
320
444
|
false
|
|
@@ -351,12 +475,25 @@ module ActiveRecord
|
|
|
351
475
|
false
|
|
352
476
|
end
|
|
353
477
|
|
|
354
|
-
# Does this adapter support creating
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
478
|
+
# Does this adapter support creating deferrable constraints?
|
|
479
|
+
def supports_deferrable_constraints?
|
|
480
|
+
false
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
# Does this adapter support creating check constraints?
|
|
484
|
+
def supports_check_constraints?
|
|
485
|
+
false
|
|
486
|
+
end
|
|
487
|
+
|
|
488
|
+
# Does this adapter support creating exclusion constraints?
|
|
489
|
+
def supports_exclusion_constraints?
|
|
490
|
+
false
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
# Does this adapter support creating unique constraints?
|
|
494
|
+
def supports_unique_constraints?
|
|
495
|
+
false
|
|
358
496
|
end
|
|
359
|
-
deprecate :supports_foreign_keys_in_create?
|
|
360
497
|
|
|
361
498
|
# Does this adapter support views?
|
|
362
499
|
def supports_views?
|
|
@@ -373,7 +510,7 @@ module ActiveRecord
|
|
|
373
510
|
false
|
|
374
511
|
end
|
|
375
512
|
|
|
376
|
-
# Does this adapter support
|
|
513
|
+
# Does this adapter support JSON data type?
|
|
377
514
|
def supports_json?
|
|
378
515
|
false
|
|
379
516
|
end
|
|
@@ -388,12 +525,6 @@ module ActiveRecord
|
|
|
388
525
|
false
|
|
389
526
|
end
|
|
390
527
|
|
|
391
|
-
# Does this adapter support multi-value insert?
|
|
392
|
-
def supports_multi_insert?
|
|
393
|
-
true
|
|
394
|
-
end
|
|
395
|
-
deprecate :supports_multi_insert?
|
|
396
|
-
|
|
397
528
|
# Does this adapter support virtual columns?
|
|
398
529
|
def supports_virtual_columns?
|
|
399
530
|
false
|
|
@@ -409,6 +540,10 @@ module ActiveRecord
|
|
|
409
540
|
false
|
|
410
541
|
end
|
|
411
542
|
|
|
543
|
+
def supports_common_table_expressions?
|
|
544
|
+
false
|
|
545
|
+
end
|
|
546
|
+
|
|
412
547
|
def supports_lazy_transactions?
|
|
413
548
|
false
|
|
414
549
|
end
|
|
@@ -429,12 +564,49 @@ module ActiveRecord
|
|
|
429
564
|
false
|
|
430
565
|
end
|
|
431
566
|
|
|
567
|
+
def supports_concurrent_connections?
|
|
568
|
+
true
|
|
569
|
+
end
|
|
570
|
+
|
|
571
|
+
def supports_nulls_not_distinct?
|
|
572
|
+
false
|
|
573
|
+
end
|
|
574
|
+
|
|
575
|
+
def return_value_after_insert?(column) # :nodoc:
|
|
576
|
+
column.auto_populated?
|
|
577
|
+
end
|
|
578
|
+
|
|
579
|
+
def async_enabled? # :nodoc:
|
|
580
|
+
supports_concurrent_connections? &&
|
|
581
|
+
!ActiveRecord.async_query_executor.nil? && !pool.async_executor.nil?
|
|
582
|
+
end
|
|
583
|
+
|
|
432
584
|
# This is meant to be implemented by the adapters that support extensions
|
|
433
|
-
def disable_extension(name)
|
|
585
|
+
def disable_extension(name, **)
|
|
434
586
|
end
|
|
435
587
|
|
|
436
588
|
# This is meant to be implemented by the adapters that support extensions
|
|
437
|
-
def enable_extension(name)
|
|
589
|
+
def enable_extension(name, **)
|
|
590
|
+
end
|
|
591
|
+
|
|
592
|
+
# This is meant to be implemented by the adapters that support custom enum types
|
|
593
|
+
def create_enum(*) # :nodoc:
|
|
594
|
+
end
|
|
595
|
+
|
|
596
|
+
# This is meant to be implemented by the adapters that support custom enum types
|
|
597
|
+
def drop_enum(*) # :nodoc:
|
|
598
|
+
end
|
|
599
|
+
|
|
600
|
+
# This is meant to be implemented by the adapters that support custom enum types
|
|
601
|
+
def rename_enum(*) # :nodoc:
|
|
602
|
+
end
|
|
603
|
+
|
|
604
|
+
# This is meant to be implemented by the adapters that support custom enum types
|
|
605
|
+
def add_enum_value(*) # :nodoc:
|
|
606
|
+
end
|
|
607
|
+
|
|
608
|
+
# This is meant to be implemented by the adapters that support custom enum types
|
|
609
|
+
def rename_enum_value(*) # :nodoc:
|
|
438
610
|
end
|
|
439
611
|
|
|
440
612
|
def advisory_locks_enabled? # :nodoc:
|
|
@@ -472,27 +644,74 @@ module ActiveRecord
|
|
|
472
644
|
yield
|
|
473
645
|
end
|
|
474
646
|
|
|
647
|
+
# Override to check all foreign key constraints in a database.
|
|
648
|
+
# The adapter should raise a +ActiveRecord::StatementInvalid+ if foreign key
|
|
649
|
+
# constraints are not met.
|
|
650
|
+
def check_all_foreign_keys_valid!
|
|
651
|
+
end
|
|
652
|
+
|
|
475
653
|
# CONNECTION MANAGEMENT ====================================
|
|
476
654
|
|
|
655
|
+
# Checks whether the connection to the database was established. This doesn't
|
|
656
|
+
# include checking whether the database is actually capable of responding, i.e.
|
|
657
|
+
# whether the connection is stale.
|
|
658
|
+
def connected?
|
|
659
|
+
!@raw_connection.nil?
|
|
660
|
+
end
|
|
661
|
+
|
|
477
662
|
# Checks whether the connection to the database is still active. This includes
|
|
478
663
|
# checking whether the database is actually capable of responding, i.e. whether
|
|
479
664
|
# the connection isn't stale.
|
|
480
665
|
def active?
|
|
481
666
|
end
|
|
482
667
|
|
|
483
|
-
# Disconnects from the database if already connected, and establishes a
|
|
484
|
-
#
|
|
485
|
-
#
|
|
486
|
-
def reconnect!
|
|
487
|
-
|
|
488
|
-
|
|
668
|
+
# Disconnects from the database if already connected, and establishes a new
|
|
669
|
+
# connection with the database. Implementors should define private #reconnect
|
|
670
|
+
# instead.
|
|
671
|
+
def reconnect!(restore_transactions: false)
|
|
672
|
+
retries_available = connection_retries
|
|
673
|
+
deadline = retry_deadline && Process.clock_gettime(Process::CLOCK_MONOTONIC) + retry_deadline
|
|
674
|
+
|
|
675
|
+
@lock.synchronize do
|
|
676
|
+
reconnect
|
|
677
|
+
|
|
678
|
+
enable_lazy_transactions!
|
|
679
|
+
@raw_connection_dirty = false
|
|
680
|
+
@last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
681
|
+
@verified = true
|
|
682
|
+
|
|
683
|
+
reset_transaction(restore: restore_transactions) do
|
|
684
|
+
clear_cache!(new_connection: true)
|
|
685
|
+
attempt_configure_connection
|
|
686
|
+
end
|
|
687
|
+
rescue => original_exception
|
|
688
|
+
translated_exception = translate_exception_class(original_exception, nil, nil)
|
|
689
|
+
retry_deadline_exceeded = deadline && deadline < Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
690
|
+
|
|
691
|
+
if !retry_deadline_exceeded && retries_available > 0
|
|
692
|
+
retries_available -= 1
|
|
693
|
+
|
|
694
|
+
if retryable_connection_error?(translated_exception)
|
|
695
|
+
backoff(connection_retries - retries_available)
|
|
696
|
+
retry
|
|
697
|
+
end
|
|
698
|
+
end
|
|
699
|
+
|
|
700
|
+
@last_activity = nil
|
|
701
|
+
@verified = false
|
|
702
|
+
|
|
703
|
+
raise translated_exception
|
|
704
|
+
end
|
|
489
705
|
end
|
|
490
706
|
|
|
491
707
|
# Disconnects from the database if already connected. Otherwise, this
|
|
492
708
|
# method does nothing.
|
|
493
709
|
def disconnect!
|
|
494
|
-
|
|
495
|
-
|
|
710
|
+
@lock.synchronize do
|
|
711
|
+
clear_cache!(new_connection: true)
|
|
712
|
+
reset_transaction
|
|
713
|
+
@raw_connection_dirty = false
|
|
714
|
+
end
|
|
496
715
|
end
|
|
497
716
|
|
|
498
717
|
# Immediately forget this connection ever existed. Unlike disconnect!,
|
|
@@ -503,27 +722,39 @@ module ActiveRecord
|
|
|
503
722
|
# rid of a connection that belonged to its parent.
|
|
504
723
|
def discard!
|
|
505
724
|
# This should be overridden by concrete adapters.
|
|
506
|
-
#
|
|
507
|
-
# Prevent @connection's finalizer from touching the socket, or
|
|
508
|
-
# otherwise communicating with its server, when it is collected.
|
|
509
|
-
if schema_cache.connection == self
|
|
510
|
-
schema_cache.connection = nil
|
|
511
|
-
end
|
|
512
725
|
end
|
|
513
726
|
|
|
514
727
|
# Reset the state of this connection, directing the DBMS to clear
|
|
515
728
|
# transactions and other connection-related server-side state. Usually a
|
|
516
729
|
# database-dependent operation.
|
|
517
730
|
#
|
|
518
|
-
#
|
|
519
|
-
#
|
|
731
|
+
# If a database driver or protocol does not support such a feature,
|
|
732
|
+
# implementors may alias this to #reconnect!. Otherwise, implementors
|
|
733
|
+
# should call super immediately after resetting the connection (and while
|
|
734
|
+
# still holding @lock).
|
|
520
735
|
def reset!
|
|
521
|
-
|
|
736
|
+
clear_cache!(new_connection: true)
|
|
737
|
+
reset_transaction
|
|
738
|
+
attempt_configure_connection
|
|
739
|
+
end
|
|
740
|
+
|
|
741
|
+
# Removes the connection from the pool and disconnect it.
|
|
742
|
+
def throw_away!
|
|
743
|
+
pool.remove self
|
|
744
|
+
disconnect!
|
|
522
745
|
end
|
|
523
746
|
|
|
524
747
|
# Clear any caching the database adapter may be doing.
|
|
525
|
-
def clear_cache!
|
|
526
|
-
|
|
748
|
+
def clear_cache!(new_connection: false)
|
|
749
|
+
if @statements
|
|
750
|
+
@lock.synchronize do
|
|
751
|
+
if new_connection
|
|
752
|
+
@statements.reset
|
|
753
|
+
else
|
|
754
|
+
@statements.clear
|
|
755
|
+
end
|
|
756
|
+
end
|
|
757
|
+
end
|
|
527
758
|
end
|
|
528
759
|
|
|
529
760
|
# Returns true if its required to reload the connection between requests for development mode.
|
|
@@ -535,7 +766,32 @@ module ActiveRecord
|
|
|
535
766
|
# This is done under the hood by calling #active?. If the connection
|
|
536
767
|
# is no longer active, then this method will reconnect to the database.
|
|
537
768
|
def verify!
|
|
538
|
-
|
|
769
|
+
unless active?
|
|
770
|
+
@lock.synchronize do
|
|
771
|
+
if @unconfigured_connection
|
|
772
|
+
@raw_connection = @unconfigured_connection
|
|
773
|
+
@unconfigured_connection = nil
|
|
774
|
+
attempt_configure_connection
|
|
775
|
+
@last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
776
|
+
@verified = true
|
|
777
|
+
return
|
|
778
|
+
end
|
|
779
|
+
|
|
780
|
+
reconnect!(restore_transactions: true)
|
|
781
|
+
end
|
|
782
|
+
end
|
|
783
|
+
|
|
784
|
+
@verified = true
|
|
785
|
+
end
|
|
786
|
+
|
|
787
|
+
def connect!
|
|
788
|
+
verify!
|
|
789
|
+
self
|
|
790
|
+
end
|
|
791
|
+
|
|
792
|
+
def clean! # :nodoc:
|
|
793
|
+
@raw_connection_dirty = false
|
|
794
|
+
@verified = nil
|
|
539
795
|
end
|
|
540
796
|
|
|
541
797
|
# Provides access to the underlying database driver for this adapter. For
|
|
@@ -544,12 +800,19 @@ module ActiveRecord
|
|
|
544
800
|
#
|
|
545
801
|
# This is useful for when you need to call a proprietary method such as
|
|
546
802
|
# PostgreSQL's lo_* methods.
|
|
803
|
+
#
|
|
804
|
+
# Active Record cannot track if the database is getting modified using
|
|
805
|
+
# this client. If that is the case, generally you'll want to invalidate
|
|
806
|
+
# the query cache using +ActiveRecord::Base.clear_query_cache+.
|
|
547
807
|
def raw_connection
|
|
548
|
-
|
|
549
|
-
|
|
808
|
+
with_raw_connection do |conn|
|
|
809
|
+
disable_lazy_transactions!
|
|
810
|
+
@raw_connection_dirty = true
|
|
811
|
+
conn
|
|
812
|
+
end
|
|
550
813
|
end
|
|
551
814
|
|
|
552
|
-
def default_uniqueness_comparison(attribute, value
|
|
815
|
+
def default_uniqueness_comparison(attribute, value) # :nodoc:
|
|
553
816
|
attribute.eq(value)
|
|
554
817
|
end
|
|
555
818
|
|
|
@@ -577,10 +840,6 @@ module ActiveRecord
|
|
|
577
840
|
pool.checkin self
|
|
578
841
|
end
|
|
579
842
|
|
|
580
|
-
def column_name_for_operation(operation, node) # :nodoc:
|
|
581
|
-
visitor.compile(node)
|
|
582
|
-
end
|
|
583
|
-
|
|
584
843
|
def default_index_type?(index) # :nodoc:
|
|
585
844
|
index.using.nil?
|
|
586
845
|
end
|
|
@@ -602,85 +861,270 @@ module ActiveRecord
|
|
|
602
861
|
end
|
|
603
862
|
|
|
604
863
|
def database_version # :nodoc:
|
|
605
|
-
|
|
864
|
+
pool.server_version(self)
|
|
606
865
|
end
|
|
607
866
|
|
|
608
867
|
def check_version # :nodoc:
|
|
609
868
|
end
|
|
610
869
|
|
|
611
|
-
|
|
870
|
+
# Returns the version identifier of the schema currently available in
|
|
871
|
+
# the database. This is generally equal to the number of the highest-
|
|
872
|
+
# numbered migration that has been executed, or 0 if no schema
|
|
873
|
+
# information is present / the database is empty.
|
|
874
|
+
def schema_version
|
|
875
|
+
pool.migration_context.current_version
|
|
876
|
+
end
|
|
612
877
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
878
|
+
class << self
|
|
879
|
+
def register_class_with_precision(mapping, key, klass, **kwargs) # :nodoc:
|
|
880
|
+
mapping.register_type(key) do |*args|
|
|
881
|
+
precision = extract_precision(args.last)
|
|
882
|
+
klass.new(precision: precision, **kwargs)
|
|
616
883
|
end
|
|
617
884
|
end
|
|
618
885
|
|
|
619
|
-
def
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
886
|
+
def extended_type_map(default_timezone:) # :nodoc:
|
|
887
|
+
Type::TypeMap.new(self::TYPE_MAP).tap do |m|
|
|
888
|
+
register_class_with_precision m, %r(\A[^\(]*time)i, Type::Time, timezone: default_timezone
|
|
889
|
+
register_class_with_precision m, %r(\A[^\(]*datetime)i, Type::DateTime, timezone: default_timezone
|
|
890
|
+
m.alias_type %r(\A[^\(]*timestamp)i, "datetime"
|
|
891
|
+
end
|
|
892
|
+
end
|
|
893
|
+
|
|
894
|
+
private
|
|
895
|
+
def initialize_type_map(m)
|
|
896
|
+
register_class_with_limit m, %r(boolean)i, Type::Boolean
|
|
897
|
+
register_class_with_limit m, %r(char)i, Type::String
|
|
898
|
+
register_class_with_limit m, %r(binary)i, Type::Binary
|
|
899
|
+
register_class_with_limit m, %r(text)i, Type::Text
|
|
900
|
+
register_class_with_precision m, %r(date)i, Type::Date
|
|
901
|
+
register_class_with_precision m, %r(time)i, Type::Time
|
|
902
|
+
register_class_with_precision m, %r(datetime)i, Type::DateTime
|
|
903
|
+
register_class_with_limit m, %r(float)i, Type::Float
|
|
904
|
+
register_class_with_limit m, %r(int)i, Type::Integer
|
|
905
|
+
|
|
906
|
+
m.alias_type %r(blob)i, "binary"
|
|
907
|
+
m.alias_type %r(clob)i, "text"
|
|
908
|
+
m.alias_type %r(timestamp)i, "datetime"
|
|
909
|
+
m.alias_type %r(numeric)i, "decimal"
|
|
910
|
+
m.alias_type %r(number)i, "decimal"
|
|
911
|
+
m.alias_type %r(double)i, "float"
|
|
912
|
+
|
|
913
|
+
m.register_type %r(^json)i, Type::Json.new
|
|
914
|
+
|
|
915
|
+
m.register_type(%r(decimal)i) do |sql_type|
|
|
916
|
+
scale = extract_scale(sql_type)
|
|
917
|
+
precision = extract_precision(sql_type)
|
|
918
|
+
|
|
919
|
+
if scale == 0
|
|
920
|
+
# FIXME: Remove this class as well
|
|
921
|
+
Type::DecimalWithoutScale.new(precision: precision)
|
|
922
|
+
else
|
|
923
|
+
Type::Decimal.new(precision: precision, scale: scale)
|
|
924
|
+
end
|
|
925
|
+
end
|
|
926
|
+
end
|
|
927
|
+
|
|
928
|
+
def register_class_with_limit(mapping, key, klass)
|
|
929
|
+
mapping.register_type(key) do |*args|
|
|
930
|
+
limit = extract_limit(args.last)
|
|
931
|
+
klass.new(limit: limit)
|
|
932
|
+
end
|
|
933
|
+
end
|
|
934
|
+
|
|
935
|
+
def extract_scale(sql_type)
|
|
936
|
+
case sql_type
|
|
937
|
+
when /\((\d+)\)/ then 0
|
|
938
|
+
when /\((\d+)(,(\d+))\)/ then $3.to_i
|
|
939
|
+
end
|
|
940
|
+
end
|
|
941
|
+
|
|
942
|
+
def extract_precision(sql_type)
|
|
943
|
+
$1.to_i if sql_type =~ /\((\d+)(,\d+)?\)/
|
|
944
|
+
end
|
|
945
|
+
|
|
946
|
+
def extract_limit(sql_type)
|
|
947
|
+
$1.to_i if sql_type =~ /\((.*)\)/
|
|
948
|
+
end
|
|
949
|
+
end
|
|
950
|
+
|
|
951
|
+
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
|
|
952
|
+
EXTENDED_TYPE_MAPS = Concurrent::Map.new
|
|
953
|
+
|
|
954
|
+
private
|
|
955
|
+
def reconnect_can_restore_state?
|
|
956
|
+
transaction_manager.restorable? && !@raw_connection_dirty
|
|
957
|
+
end
|
|
958
|
+
|
|
959
|
+
# Lock the monitor, ensure we're properly connected and
|
|
960
|
+
# transactions are materialized, and then yield the underlying
|
|
961
|
+
# raw connection object.
|
|
962
|
+
#
|
|
963
|
+
# If +allow_retry+ is true, a connection-related exception will
|
|
964
|
+
# cause an automatic reconnect and re-run of the block, up to
|
|
965
|
+
# the connection's configured +connection_retries+ setting
|
|
966
|
+
# and the configured +retry_deadline+ limit. (Note that when
|
|
967
|
+
# +allow_retry+ is true, it's possible to return without having marked
|
|
968
|
+
# the connection as verified. If the block is guaranteed to exercise the
|
|
969
|
+
# connection, consider calling `verified!` to avoid needless
|
|
970
|
+
# verification queries in subsequent calls.)
|
|
971
|
+
#
|
|
972
|
+
# If +materialize_transactions+ is false, the block will be run without
|
|
973
|
+
# ensuring virtual transactions have been materialized in the DB
|
|
974
|
+
# server's state. The active transaction will also remain clean
|
|
975
|
+
# (if it is not already dirty), meaning it's able to be restored
|
|
976
|
+
# by reconnecting and opening an equivalent-depth set of new
|
|
977
|
+
# transactions. This should only be used by transaction control
|
|
978
|
+
# methods, and internal transaction-agnostic queries.
|
|
979
|
+
#
|
|
980
|
+
###
|
|
981
|
+
#
|
|
982
|
+
# It's not the primary use case, so not something to optimize
|
|
983
|
+
# for, but note that this method does need to be re-entrant:
|
|
984
|
+
# +materialize_transactions+ will re-enter if it has work to do,
|
|
985
|
+
# and the yield block can also do so under some circumstances.
|
|
986
|
+
#
|
|
987
|
+
# In the latter case, we really ought to guarantee the inner
|
|
988
|
+
# call will not reconnect (which would interfere with the
|
|
989
|
+
# still-yielded connection in the outer block), but we currently
|
|
990
|
+
# provide no special enforcement there.
|
|
991
|
+
#
|
|
992
|
+
def with_raw_connection(allow_retry: false, materialize_transactions: true)
|
|
993
|
+
@lock.synchronize do
|
|
994
|
+
connect! if @raw_connection.nil? && reconnect_can_restore_state?
|
|
995
|
+
|
|
996
|
+
self.materialize_transactions if materialize_transactions
|
|
997
|
+
|
|
998
|
+
retries_available = allow_retry ? connection_retries : 0
|
|
999
|
+
deadline = retry_deadline && Process.clock_gettime(Process::CLOCK_MONOTONIC) + retry_deadline
|
|
1000
|
+
reconnectable = reconnect_can_restore_state?
|
|
1001
|
+
|
|
1002
|
+
if @verified
|
|
1003
|
+
# Cool, we're confident the connection's ready to use. (Note this might have
|
|
1004
|
+
# become true during the above #materialize_transactions.)
|
|
1005
|
+
elsif (last_activity = seconds_since_last_activity) && last_activity < verify_timeout
|
|
1006
|
+
# We haven't actually verified the connection since we acquired it, but it
|
|
1007
|
+
# has been used very recently. We're going to assume it's still okay.
|
|
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
|
|
646
1017
|
else
|
|
647
|
-
|
|
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
|
+
yield @raw_connection
|
|
1025
|
+
rescue => original_exception
|
|
1026
|
+
translated_exception = translate_exception_class(original_exception, nil, nil)
|
|
1027
|
+
invalidate_transaction(translated_exception)
|
|
1028
|
+
retry_deadline_exceeded = deadline && deadline < Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
1029
|
+
|
|
1030
|
+
if !retry_deadline_exceeded && retries_available > 0
|
|
1031
|
+
retries_available -= 1
|
|
1032
|
+
|
|
1033
|
+
if retryable_query_error?(translated_exception)
|
|
1034
|
+
backoff(connection_retries - retries_available)
|
|
1035
|
+
retry
|
|
1036
|
+
elsif reconnectable && retryable_connection_error?(translated_exception)
|
|
1037
|
+
reconnect!(restore_transactions: true)
|
|
1038
|
+
# Only allowed to reconnect once, because reconnect! has its own retry
|
|
1039
|
+
# loop
|
|
1040
|
+
reconnectable = false
|
|
1041
|
+
retry
|
|
1042
|
+
end
|
|
1043
|
+
end
|
|
1044
|
+
|
|
1045
|
+
unless retryable_query_error?(translated_exception)
|
|
1046
|
+
# Barring a known-retryable error inside the query (regardless of
|
|
1047
|
+
# whether we were in a _position_ to retry it), we should infer that
|
|
1048
|
+
# there's likely a real problem with the connection.
|
|
1049
|
+
@last_activity = nil
|
|
1050
|
+
@verified = false
|
|
1051
|
+
end
|
|
1052
|
+
|
|
1053
|
+
raise translated_exception
|
|
1054
|
+
ensure
|
|
1055
|
+
dirty_current_transaction if materialize_transactions
|
|
648
1056
|
end
|
|
649
1057
|
end
|
|
650
1058
|
end
|
|
651
1059
|
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
1060
|
+
# Mark the connection as verified. Call this inside a
|
|
1061
|
+
# `with_raw_connection` block only when the block is guaranteed to
|
|
1062
|
+
# exercise the raw connection.
|
|
1063
|
+
def verified!
|
|
1064
|
+
@last_activity = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
1065
|
+
@verified = true
|
|
655
1066
|
end
|
|
656
1067
|
|
|
657
|
-
def
|
|
658
|
-
|
|
659
|
-
limit = extract_limit(args.last)
|
|
660
|
-
klass.new(limit: limit)
|
|
661
|
-
end
|
|
1068
|
+
def retryable_connection_error?(exception)
|
|
1069
|
+
exception.is_a?(ConnectionNotEstablished) || exception.is_a?(ConnectionFailed)
|
|
662
1070
|
end
|
|
663
1071
|
|
|
664
|
-
def
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
1072
|
+
def invalidate_transaction(exception)
|
|
1073
|
+
return unless exception.is_a?(TransactionRollbackError)
|
|
1074
|
+
return unless savepoint_errors_invalidate_transactions?
|
|
1075
|
+
|
|
1076
|
+
current_transaction.invalidate!
|
|
669
1077
|
end
|
|
670
1078
|
|
|
671
|
-
def
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
1079
|
+
def retryable_query_error?(exception)
|
|
1080
|
+
# We definitely can't retry if we were inside an invalidated transaction.
|
|
1081
|
+
return false if current_transaction.invalidated?
|
|
1082
|
+
|
|
1083
|
+
exception.is_a?(Deadlocked) || exception.is_a?(LockWaitTimeout)
|
|
676
1084
|
end
|
|
677
1085
|
|
|
678
|
-
def
|
|
679
|
-
|
|
1086
|
+
def backoff(counter)
|
|
1087
|
+
sleep 0.1 * counter
|
|
680
1088
|
end
|
|
681
1089
|
|
|
682
|
-
def
|
|
683
|
-
|
|
1090
|
+
def reconnect
|
|
1091
|
+
raise NotImplementedError
|
|
1092
|
+
end
|
|
1093
|
+
|
|
1094
|
+
# Returns a raw connection for internal use with methods that are known
|
|
1095
|
+
# to both be thread-safe and not rely upon actual server communication.
|
|
1096
|
+
# This is useful for e.g. string escaping methods.
|
|
1097
|
+
def any_raw_connection
|
|
1098
|
+
@raw_connection || valid_raw_connection
|
|
1099
|
+
end
|
|
1100
|
+
|
|
1101
|
+
# Similar to any_raw_connection, but ensures it is validated and
|
|
1102
|
+
# connected. Any method called on this result still needs to be
|
|
1103
|
+
# independently thread-safe, so it probably shouldn't talk to the
|
|
1104
|
+
# server... but some drivers fail if they know the connection has gone
|
|
1105
|
+
# away.
|
|
1106
|
+
def valid_raw_connection
|
|
1107
|
+
(@verified && @raw_connection) ||
|
|
1108
|
+
# `allow_retry: false`, to force verification: the block won't
|
|
1109
|
+
# raise, so a retry wouldn't help us get the valid connection we
|
|
1110
|
+
# need.
|
|
1111
|
+
with_raw_connection(allow_retry: false, materialize_transactions: false) { |conn| conn }
|
|
1112
|
+
end
|
|
1113
|
+
|
|
1114
|
+
def extended_type_map_key
|
|
1115
|
+
if @default_timezone
|
|
1116
|
+
{ default_timezone: @default_timezone }
|
|
1117
|
+
end
|
|
1118
|
+
end
|
|
1119
|
+
|
|
1120
|
+
def type_map
|
|
1121
|
+
if key = extended_type_map_key
|
|
1122
|
+
self.class::EXTENDED_TYPE_MAPS.compute_if_absent(key) do
|
|
1123
|
+
self.class.extended_type_map(**key)
|
|
1124
|
+
end
|
|
1125
|
+
else
|
|
1126
|
+
self.class::TYPE_MAP
|
|
1127
|
+
end
|
|
684
1128
|
end
|
|
685
1129
|
|
|
686
1130
|
def translate_exception_class(e, sql, binds)
|
|
@@ -693,7 +1137,7 @@ module ActiveRecord
|
|
|
693
1137
|
exception
|
|
694
1138
|
end
|
|
695
1139
|
|
|
696
|
-
def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil) # :doc:
|
|
1140
|
+
def log(sql, name = "SQL", binds = [], type_casted_binds = [], statement_name = nil, async: false, &block) # :doc:
|
|
697
1141
|
@instrumenter.instrument(
|
|
698
1142
|
"sql.active_record",
|
|
699
1143
|
sql: sql,
|
|
@@ -701,23 +1145,30 @@ module ActiveRecord
|
|
|
701
1145
|
binds: binds,
|
|
702
1146
|
type_casted_binds: type_casted_binds,
|
|
703
1147
|
statement_name: statement_name,
|
|
704
|
-
|
|
705
|
-
connection: self
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
1148
|
+
async: async,
|
|
1149
|
+
connection: self,
|
|
1150
|
+
transaction: current_transaction.user_transaction.presence,
|
|
1151
|
+
row_count: 0,
|
|
1152
|
+
&block
|
|
1153
|
+
)
|
|
1154
|
+
rescue ActiveRecord::StatementInvalid => ex
|
|
1155
|
+
raise ex.set_query(sql, binds)
|
|
1156
|
+
end
|
|
1157
|
+
|
|
1158
|
+
def transform_query(sql)
|
|
1159
|
+
ActiveRecord.query_transformers.each do |transformer|
|
|
1160
|
+
sql = transformer.call(sql, self)
|
|
711
1161
|
end
|
|
1162
|
+
sql
|
|
712
1163
|
end
|
|
713
1164
|
|
|
714
1165
|
def translate_exception(exception, message:, sql:, binds:)
|
|
715
1166
|
# override in derived class
|
|
716
1167
|
case exception
|
|
717
|
-
when RuntimeError
|
|
1168
|
+
when RuntimeError, ActiveRecord::ActiveRecordError
|
|
718
1169
|
exception
|
|
719
1170
|
else
|
|
720
|
-
ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds)
|
|
1171
|
+
ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
721
1172
|
end
|
|
722
1173
|
end
|
|
723
1174
|
|
|
@@ -756,6 +1207,42 @@ module ActiveRecord
|
|
|
756
1207
|
|
|
757
1208
|
def build_statement_pool
|
|
758
1209
|
end
|
|
1210
|
+
|
|
1211
|
+
# Builds the result object.
|
|
1212
|
+
#
|
|
1213
|
+
# This is an internal hook to make possible connection adapters to build
|
|
1214
|
+
# custom result objects with connection-specific data.
|
|
1215
|
+
def build_result(columns:, rows:, column_types: nil)
|
|
1216
|
+
ActiveRecord::Result.new(columns, rows, column_types)
|
|
1217
|
+
end
|
|
1218
|
+
|
|
1219
|
+
# Perform any necessary initialization upon the newly-established
|
|
1220
|
+
# @raw_connection -- this is the place to modify the adapter's
|
|
1221
|
+
# connection settings, run queries to configure any application-global
|
|
1222
|
+
# "session" variables, etc.
|
|
1223
|
+
#
|
|
1224
|
+
# Implementations may assume this method will only be called while
|
|
1225
|
+
# holding @lock (or from #initialize).
|
|
1226
|
+
def configure_connection
|
|
1227
|
+
check_version
|
|
1228
|
+
end
|
|
1229
|
+
|
|
1230
|
+
def attempt_configure_connection
|
|
1231
|
+
configure_connection
|
|
1232
|
+
rescue Exception # Need to handle things such as Timeout::ExitException
|
|
1233
|
+
disconnect!
|
|
1234
|
+
raise
|
|
1235
|
+
end
|
|
1236
|
+
|
|
1237
|
+
def default_prepared_statements
|
|
1238
|
+
true
|
|
1239
|
+
end
|
|
1240
|
+
|
|
1241
|
+
def warning_ignored?(warning)
|
|
1242
|
+
ActiveRecord.db_warnings_ignore.any? do |warning_matcher|
|
|
1243
|
+
warning.message.match?(warning_matcher) || warning.code.to_s.match?(warning_matcher)
|
|
1244
|
+
end
|
|
1245
|
+
end
|
|
759
1246
|
end
|
|
760
1247
|
end
|
|
761
1248
|
end
|