omg-activerecord 8.0.0.alpha1
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 +7 -0
- data/CHANGELOG.md +355 -0
- data/MIT-LICENSE +22 -0
- data/README.rdoc +219 -0
- data/examples/performance.rb +185 -0
- data/examples/simple.rb +15 -0
- data/lib/active_record/aggregations.rb +287 -0
- data/lib/active_record/association_relation.rb +50 -0
- data/lib/active_record/associations/alias_tracker.rb +90 -0
- data/lib/active_record/associations/association.rb +417 -0
- data/lib/active_record/associations/association_scope.rb +175 -0
- data/lib/active_record/associations/belongs_to_association.rb +163 -0
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +50 -0
- data/lib/active_record/associations/builder/association.rb +170 -0
- data/lib/active_record/associations/builder/belongs_to.rb +160 -0
- data/lib/active_record/associations/builder/collection_association.rb +80 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +107 -0
- data/lib/active_record/associations/builder/has_many.rb +23 -0
- data/lib/active_record/associations/builder/has_one.rb +61 -0
- data/lib/active_record/associations/builder/singular_association.rb +48 -0
- data/lib/active_record/associations/collection_association.rb +535 -0
- data/lib/active_record/associations/collection_proxy.rb +1163 -0
- 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 +40 -0
- data/lib/active_record/associations/has_many_association.rb +167 -0
- data/lib/active_record/associations/has_many_through_association.rb +232 -0
- data/lib/active_record/associations/has_one_association.rb +142 -0
- data/lib/active_record/associations/has_one_through_association.rb +45 -0
- data/lib/active_record/associations/join_dependency/join_association.rb +106 -0
- data/lib/active_record/associations/join_dependency/join_base.rb +23 -0
- data/lib/active_record/associations/join_dependency/join_part.rb +71 -0
- data/lib/active_record/associations/join_dependency.rb +301 -0
- data/lib/active_record/associations/nested_error.rb +47 -0
- data/lib/active_record/associations/preloader/association.rb +316 -0
- 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 +150 -0
- data/lib/active_record/associations/preloader.rb +135 -0
- data/lib/active_record/associations/singular_association.rb +76 -0
- data/lib/active_record/associations/through_association.rb +132 -0
- data/lib/active_record/associations.rb +1897 -0
- data/lib/active_record/asynchronous_queries_tracker.rb +64 -0
- data/lib/active_record/attribute_assignment.rb +82 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +106 -0
- data/lib/active_record/attribute_methods/composite_primary_key.rb +84 -0
- data/lib/active_record/attribute_methods/dirty.rb +262 -0
- data/lib/active_record/attribute_methods/primary_key.rb +158 -0
- data/lib/active_record/attribute_methods/query.rb +50 -0
- data/lib/active_record/attribute_methods/read.rb +46 -0
- data/lib/active_record/attribute_methods/serialization.rb +232 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +94 -0
- data/lib/active_record/attribute_methods/write.rb +49 -0
- data/lib/active_record/attribute_methods.rb +542 -0
- data/lib/active_record/attributes.rb +307 -0
- data/lib/active_record/autosave_association.rb +586 -0
- data/lib/active_record/base.rb +338 -0
- data/lib/active_record/callbacks.rb +452 -0
- data/lib/active_record/coders/column_serializer.rb +61 -0
- data/lib/active_record/coders/json.rb +15 -0
- data/lib/active_record/coders/yaml_column.rb +95 -0
- data/lib/active_record/connection_adapters/abstract/connection_handler.rb +290 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/queue.rb +210 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool/reaper.rb +78 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +923 -0
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +31 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +747 -0
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +319 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +239 -0
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +24 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +190 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +961 -0
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +106 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1883 -0
- data/lib/active_record/connection_adapters/abstract/transaction.rb +676 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +1218 -0
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +1016 -0
- data/lib/active_record/connection_adapters/column.rb +122 -0
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +28 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +95 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +71 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +114 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +106 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +106 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +97 -0
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +300 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +40 -0
- data/lib/active_record/connection_adapters/mysql2/database_statements.rb +96 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +196 -0
- 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 +82 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +231 -0
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +91 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +53 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +54 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +31 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +109 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +74 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +124 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
- 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 +125 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +45 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +38 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +238 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +71 -0
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +169 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +392 -0
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +127 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +1162 -0
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +44 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +79 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +1182 -0
- data/lib/active_record/connection_adapters/schema_cache.rb +478 -0
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +45 -0
- data/lib/active_record/connection_adapters/sqlite3/column.rb +62 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +145 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +116 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +37 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +39 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +47 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +221 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +843 -0
- data/lib/active_record/connection_adapters/statement_pool.rb +67 -0
- data/lib/active_record/connection_adapters/trilogy/database_statements.rb +69 -0
- data/lib/active_record/connection_adapters/trilogy_adapter.rb +212 -0
- data/lib/active_record/connection_adapters.rb +176 -0
- data/lib/active_record/connection_handling.rb +413 -0
- data/lib/active_record/core.rb +836 -0
- data/lib/active_record/counter_cache.rb +230 -0
- data/lib/active_record/database_configurations/connection_url_resolver.rb +105 -0
- data/lib/active_record/database_configurations/database_config.rb +104 -0
- data/lib/active_record/database_configurations/hash_config.rb +172 -0
- data/lib/active_record/database_configurations/url_config.rb +78 -0
- data/lib/active_record/database_configurations.rb +309 -0
- data/lib/active_record/delegated_type.rb +289 -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 +121 -0
- 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 +70 -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 +184 -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 +177 -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 +159 -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 +107 -0
- data/lib/active_record/encryption.rb +58 -0
- data/lib/active_record/enum.rb +424 -0
- data/lib/active_record/errors.rb +614 -0
- data/lib/active_record/explain.rb +63 -0
- data/lib/active_record/explain_registry.rb +37 -0
- data/lib/active_record/explain_subscriber.rb +34 -0
- data/lib/active_record/fixture_set/file.rb +89 -0
- data/lib/active_record/fixture_set/model_metadata.rb +42 -0
- data/lib/active_record/fixture_set/render_context.rb +19 -0
- data/lib/active_record/fixture_set/table_row.rb +208 -0
- data/lib/active_record/fixture_set/table_rows.rb +46 -0
- data/lib/active_record/fixtures.rb +850 -0
- data/lib/active_record/future_result.rb +182 -0
- data/lib/active_record/gem_version.rb +17 -0
- data/lib/active_record/inheritance.rb +366 -0
- data/lib/active_record/insert_all.rb +328 -0
- data/lib/active_record/integration.rb +209 -0
- data/lib/active_record/internal_metadata.rb +164 -0
- data/lib/active_record/legacy_yaml_adapter.rb +15 -0
- data/lib/active_record/locale/en.yml +48 -0
- data/lib/active_record/locking/optimistic.rb +228 -0
- data/lib/active_record/locking/pessimistic.rb +102 -0
- data/lib/active_record/log_subscriber.rb +149 -0
- data/lib/active_record/marshalling.rb +56 -0
- data/lib/active_record/message_pack.rb +124 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +48 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
- data/lib/active_record/middleware/database_selector.rb +87 -0
- data/lib/active_record/middleware/shard_selector.rb +62 -0
- data/lib/active_record/migration/command_recorder.rb +406 -0
- data/lib/active_record/migration/compatibility.rb +490 -0
- 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 +16 -0
- data/lib/active_record/migration/pending_migration_connection.rb +21 -0
- data/lib/active_record/migration.rb +1626 -0
- data/lib/active_record/model_schema.rb +635 -0
- data/lib/active_record/nested_attributes.rb +633 -0
- data/lib/active_record/no_touching.rb +65 -0
- data/lib/active_record/normalization.rb +163 -0
- data/lib/active_record/persistence.rb +968 -0
- data/lib/active_record/promise.rb +84 -0
- data/lib/active_record/query_cache.rb +56 -0
- data/lib/active_record/query_logs.rb +247 -0
- data/lib/active_record/query_logs_formatter.rb +30 -0
- data/lib/active_record/querying.rb +122 -0
- data/lib/active_record/railtie.rb +440 -0
- data/lib/active_record/railties/console_sandbox.rb +5 -0
- data/lib/active_record/railties/controller_runtime.rb +65 -0
- data/lib/active_record/railties/databases.rake +641 -0
- data/lib/active_record/railties/job_runtime.rb +23 -0
- data/lib/active_record/readonly_attributes.rb +66 -0
- data/lib/active_record/reflection.rb +1287 -0
- data/lib/active_record/relation/batches/batch_enumerator.rb +115 -0
- data/lib/active_record/relation/batches.rb +491 -0
- data/lib/active_record/relation/calculations.rb +679 -0
- data/lib/active_record/relation/delegation.rb +154 -0
- data/lib/active_record/relation/finder_methods.rb +661 -0
- data/lib/active_record/relation/from_clause.rb +30 -0
- data/lib/active_record/relation/merger.rb +192 -0
- data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +76 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +19 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +60 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +22 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +24 -0
- data/lib/active_record/relation/predicate_builder.rb +181 -0
- data/lib/active_record/relation/query_attribute.rb +68 -0
- data/lib/active_record/relation/query_methods.rb +2235 -0
- data/lib/active_record/relation/record_fetch_warning.rb +52 -0
- data/lib/active_record/relation/spawn_methods.rb +78 -0
- data/lib/active_record/relation/where_clause.rb +218 -0
- data/lib/active_record/relation.rb +1495 -0
- data/lib/active_record/result.rb +249 -0
- data/lib/active_record/runtime_registry.rb +82 -0
- data/lib/active_record/sanitization.rb +254 -0
- data/lib/active_record/schema.rb +77 -0
- data/lib/active_record/schema_dumper.rb +364 -0
- data/lib/active_record/schema_migration.rb +106 -0
- data/lib/active_record/scoping/default.rb +205 -0
- data/lib/active_record/scoping/named.rb +202 -0
- data/lib/active_record/scoping.rb +136 -0
- data/lib/active_record/secure_password.rb +60 -0
- data/lib/active_record/secure_token.rb +66 -0
- data/lib/active_record/serialization.rb +29 -0
- data/lib/active_record/signed_id.rb +137 -0
- data/lib/active_record/statement_cache.rb +164 -0
- data/lib/active_record/store.rb +299 -0
- data/lib/active_record/suppressor.rb +59 -0
- data/lib/active_record/table_metadata.rb +85 -0
- data/lib/active_record/tasks/database_tasks.rb +681 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +120 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +147 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +89 -0
- data/lib/active_record/test_databases.rb +24 -0
- data/lib/active_record/test_fixtures.rb +321 -0
- data/lib/active_record/testing/query_assertions.rb +121 -0
- data/lib/active_record/timestamp.rb +177 -0
- data/lib/active_record/token_for.rb +123 -0
- data/lib/active_record/touch_later.rb +70 -0
- data/lib/active_record/transaction.rb +132 -0
- data/lib/active_record/transactions.rb +523 -0
- data/lib/active_record/translation.rb +22 -0
- data/lib/active_record/type/adapter_specific_registry.rb +144 -0
- data/lib/active_record/type/date.rb +9 -0
- data/lib/active_record/type/date_time.rb +9 -0
- data/lib/active_record/type/decimal_without_scale.rb +15 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +57 -0
- data/lib/active_record/type/internal/timezone.rb +22 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +76 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +35 -0
- data/lib/active_record/type/type_map.rb +58 -0
- data/lib/active_record/type/unsigned_integer.rb +16 -0
- data/lib/active_record/type.rb +83 -0
- data/lib/active_record/type_caster/connection.rb +33 -0
- data/lib/active_record/type_caster/map.rb +23 -0
- data/lib/active_record/type_caster.rb +9 -0
- data/lib/active_record/validations/absence.rb +25 -0
- data/lib/active_record/validations/associated.rb +65 -0
- data/lib/active_record/validations/length.rb +26 -0
- data/lib/active_record/validations/numericality.rb +36 -0
- data/lib/active_record/validations/presence.rb +45 -0
- data/lib/active_record/validations/uniqueness.rb +295 -0
- data/lib/active_record/validations.rb +101 -0
- data/lib/active_record/version.rb +10 -0
- data/lib/active_record.rb +616 -0
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes/attribute.rb +33 -0
- data/lib/arel/collectors/bind.rb +31 -0
- data/lib/arel/collectors/composite.rb +46 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +27 -0
- data/lib/arel/collectors/substitute_binds.rb +35 -0
- data/lib/arel/crud.rb +48 -0
- data/lib/arel/delete_manager.rb +32 -0
- data/lib/arel/errors.rb +19 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +53 -0
- data/lib/arel/filter_predications.rb +9 -0
- data/lib/arel/insert_manager.rb +48 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +125 -0
- data/lib/arel/nodes/bind_param.rb +44 -0
- data/lib/arel/nodes/bound_sql_literal.rb +65 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +62 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/cte.rb +36 -0
- data/lib/arel/nodes/delete_statement.rb +44 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +15 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/filter.rb +10 -0
- data/lib/arel/nodes/fragments.rb +35 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +45 -0
- data/lib/arel/nodes/grouping.rb +11 -0
- data/lib/arel/nodes/homogeneous_in.rb +68 -0
- data/lib/arel/nodes/in.rb +15 -0
- data/lib/arel/nodes/infix_operation.rb +92 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/leading_join.rb +8 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/nary.rb +39 -0
- data/lib/arel/nodes/node.rb +161 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +32 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +35 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +44 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +46 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/nodes.rb +75 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +260 -0
- data/lib/arel/select_manager.rb +276 -0
- data/lib/arel/table.rb +121 -0
- data/lib/arel/tree_manager.rb +65 -0
- data/lib/arel/update_manager.rb +49 -0
- data/lib/arel/visitors/dot.rb +299 -0
- data/lib/arel/visitors/mysql.rb +111 -0
- data/lib/arel/visitors/postgresql.rb +99 -0
- data/lib/arel/visitors/sqlite.rb +38 -0
- data/lib/arel/visitors/to_sql.rb +1033 -0
- data/lib/arel/visitors/visitor.rb +45 -0
- data/lib/arel/visitors.rb +13 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/arel.rb +73 -0
- data/lib/rails/generators/active_record/application_record/USAGE +8 -0
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +26 -0
- data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +76 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +29 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +48 -0
- data/lib/rails/generators/active_record/migration.rb +54 -0
- data/lib/rails/generators/active_record/model/USAGE +113 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +94 -0
- 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 +22 -0
- data/lib/rails/generators/active_record/model/templates/module.rb.tt +7 -0
- 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
- data/lib/rails/generators/active_record.rb +19 -0
- metadata +505 -0
|
@@ -0,0 +1,1218 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "set"
|
|
4
|
+
require "active_record/connection_adapters/sql_type_metadata"
|
|
5
|
+
require "active_record/connection_adapters/abstract/schema_dumper"
|
|
6
|
+
require "active_record/connection_adapters/abstract/schema_creation"
|
|
7
|
+
require "active_support/concurrency/null_lock"
|
|
8
|
+
require "active_support/concurrency/load_interlock_aware_monitor"
|
|
9
|
+
require "arel/collectors/bind"
|
|
10
|
+
require "arel/collectors/composite"
|
|
11
|
+
require "arel/collectors/sql_string"
|
|
12
|
+
require "arel/collectors/substitute_binds"
|
|
13
|
+
|
|
14
|
+
module ActiveRecord
|
|
15
|
+
module ConnectionAdapters # :nodoc:
|
|
16
|
+
# = Active Record Abstract Adapter
|
|
17
|
+
#
|
|
18
|
+
# Active Record supports multiple database systems. AbstractAdapter and
|
|
19
|
+
# related classes form the abstraction layer which makes this possible.
|
|
20
|
+
# An AbstractAdapter represents a connection to a database, and provides an
|
|
21
|
+
# abstract interface for database-specific functionality such as establishing
|
|
22
|
+
# a connection, escaping values, building the right SQL fragments for +:offset+
|
|
23
|
+
# and +:limit+ options, etc.
|
|
24
|
+
#
|
|
25
|
+
# All the concrete database adapters follow the interface laid down in this class.
|
|
26
|
+
# {ActiveRecord::Base.lease_connection}[rdoc-ref:ConnectionHandling#lease_connection] returns an AbstractAdapter object, which
|
|
27
|
+
# you can use.
|
|
28
|
+
#
|
|
29
|
+
# Most of the methods in the adapter are useful during migrations. Most
|
|
30
|
+
# notably, the instance methods provided by SchemaStatements are very useful.
|
|
31
|
+
class AbstractAdapter
|
|
32
|
+
ADAPTER_NAME = "Abstract"
|
|
33
|
+
include ActiveSupport::Callbacks
|
|
34
|
+
define_callbacks :checkout, :checkin
|
|
35
|
+
|
|
36
|
+
include Quoting, DatabaseStatements, SchemaStatements
|
|
37
|
+
include DatabaseLimits
|
|
38
|
+
include QueryCache
|
|
39
|
+
include Savepoints
|
|
40
|
+
|
|
41
|
+
SIMPLE_INT = /\A\d+\z/
|
|
42
|
+
COMMENT_REGEX = %r{(?:--.*\n)|/\*(?:[^*]|\*[^/])*\*/}
|
|
43
|
+
|
|
44
|
+
attr_reader :pool
|
|
45
|
+
attr_reader :visitor, :owner, :logger, :lock
|
|
46
|
+
alias :in_use? :owner
|
|
47
|
+
|
|
48
|
+
def pool=(value)
|
|
49
|
+
return if value.eql?(@pool)
|
|
50
|
+
@schema_cache = nil
|
|
51
|
+
@pool = value
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
set_callback :checkin, :after, :enable_lazy_transactions!
|
|
55
|
+
|
|
56
|
+
def self.type_cast_config_to_integer(config)
|
|
57
|
+
if config.is_a?(Integer)
|
|
58
|
+
config
|
|
59
|
+
elsif SIMPLE_INT.match?(config)
|
|
60
|
+
config.to_i
|
|
61
|
+
else
|
|
62
|
+
config
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def self.type_cast_config_to_boolean(config)
|
|
67
|
+
if config == "false"
|
|
68
|
+
false
|
|
69
|
+
else
|
|
70
|
+
config
|
|
71
|
+
end
|
|
72
|
+
end
|
|
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
|
+
|
|
84
|
+
DEFAULT_READ_QUERY = [:begin, :commit, :explain, :release, :rollback, :savepoint, :select, :with] # :nodoc:
|
|
85
|
+
private_constant :DEFAULT_READ_QUERY
|
|
86
|
+
|
|
87
|
+
def self.build_read_query_regexp(*parts) # :nodoc:
|
|
88
|
+
parts += DEFAULT_READ_QUERY
|
|
89
|
+
parts = parts.map { |part| /#{part}/i }
|
|
90
|
+
/\A(?:[(\s]|#{COMMENT_REGEX})*#{Regexp.union(*parts)}/
|
|
91
|
+
end
|
|
92
|
+
|
|
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
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Opens a database console session.
|
|
122
|
+
def self.dbconsole(config, options = {})
|
|
123
|
+
raise NotImplementedError
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def initialize(config_or_deprecated_connection, deprecated_logger = nil, deprecated_connection_options = nil, deprecated_config = nil) # :nodoc:
|
|
127
|
+
super()
|
|
128
|
+
|
|
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)
|
|
157
|
+
@visitor = arel_visitor
|
|
158
|
+
@statements = build_statement_pool
|
|
159
|
+
self.lock_thread = nil
|
|
160
|
+
|
|
161
|
+
@prepared_statements = !ActiveRecord.disable_prepared_statements && self.class.type_cast_config_to_boolean(
|
|
162
|
+
@config.fetch(:prepared_statements) { default_prepared_statements }
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
@advisory_locks_enabled = self.class.type_cast_config_to_boolean(
|
|
166
|
+
@config.fetch(:advisory_locks, true)
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
@default_timezone = self.class.validate_default_timezone(@config[:default_timezone])
|
|
170
|
+
|
|
171
|
+
@raw_connection_dirty = false
|
|
172
|
+
@verified = false
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def inspect # :nodoc:
|
|
176
|
+
name_field = " name=#{pool.db_config.name.inspect}" unless pool.db_config.name == "primary"
|
|
177
|
+
shard_field = " shard=#{shard.inspect}" unless shard == :default
|
|
178
|
+
|
|
179
|
+
"#<#{self.class.name}:#{'%#016x' % (object_id << 1)} env_name=#{pool.db_config.env_name.inspect}#{name_field} role=#{role.inspect}#{shard_field}>"
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def lock_thread=(lock_thread) # :nodoc:
|
|
183
|
+
@lock =
|
|
184
|
+
case lock_thread
|
|
185
|
+
when Thread
|
|
186
|
+
ActiveSupport::Concurrency::ThreadLoadInterlockAwareMonitor.new
|
|
187
|
+
when Fiber
|
|
188
|
+
ActiveSupport::Concurrency::LoadInterlockAwareMonitor.new
|
|
189
|
+
else
|
|
190
|
+
ActiveSupport::Concurrency::NullLock
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
|
|
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
|
|
205
|
+
end
|
|
206
|
+
|
|
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
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def replica?
|
|
214
|
+
@config[:replica] || false
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def connection_retries
|
|
218
|
+
(@config[:connection_retries] || 1).to_i
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
def retry_deadline
|
|
222
|
+
if @config[:retry_deadline]
|
|
223
|
+
@config[:retry_deadline].to_f
|
|
224
|
+
else
|
|
225
|
+
nil
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def default_timezone
|
|
230
|
+
@default_timezone || ActiveRecord.default_timezone
|
|
231
|
+
end
|
|
232
|
+
|
|
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?
|
|
240
|
+
|
|
241
|
+
connection_class.current_preventing_writes
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
def prepared_statements?
|
|
245
|
+
@prepared_statements && !prepared_statements_disabled_cache.include?(object_id)
|
|
246
|
+
end
|
|
247
|
+
alias :prepared_statements :prepared_statements?
|
|
248
|
+
|
|
249
|
+
def prepared_statements_disabled_cache # :nodoc:
|
|
250
|
+
ActiveSupport::IsolatedExecutionState[:active_record_prepared_statements_disabled_cache] ||= Set.new
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
class Version
|
|
254
|
+
include Comparable
|
|
255
|
+
|
|
256
|
+
attr_reader :full_version_string
|
|
257
|
+
|
|
258
|
+
def initialize(version_string, full_version_string = nil)
|
|
259
|
+
@version = version_string.split(".").map(&:to_i)
|
|
260
|
+
@full_version_string = full_version_string
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def <=>(version_string)
|
|
264
|
+
@version <=> version_string.split(".").map(&:to_i)
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def to_s
|
|
268
|
+
@version.join(".")
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def valid_type?(type) # :nodoc:
|
|
273
|
+
!native_database_types[type].nil?
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
# this method must only be called while holding connection pool's mutex
|
|
277
|
+
def lease
|
|
278
|
+
if in_use?
|
|
279
|
+
msg = +"Cannot lease connection, "
|
|
280
|
+
if @owner == ActiveSupport::IsolatedExecutionState.context
|
|
281
|
+
msg << "it is already leased by the current thread."
|
|
282
|
+
else
|
|
283
|
+
msg << "it is already in use by a different thread: #{@owner}. " \
|
|
284
|
+
"Current thread: #{ActiveSupport::IsolatedExecutionState.context}."
|
|
285
|
+
end
|
|
286
|
+
raise ActiveRecordError, msg
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
@owner = ActiveSupport::IsolatedExecutionState.context
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def connection_class # :nodoc:
|
|
293
|
+
@pool.connection_class
|
|
294
|
+
end
|
|
295
|
+
|
|
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
|
|
300
|
+
end
|
|
301
|
+
|
|
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))
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
# this method must only be called while holding connection pool's mutex
|
|
313
|
+
def expire
|
|
314
|
+
if in_use?
|
|
315
|
+
if @owner != ActiveSupport::IsolatedExecutionState.context
|
|
316
|
+
raise ActiveRecordError, "Cannot expire connection, " \
|
|
317
|
+
"it is owned by a different thread: #{@owner}. " \
|
|
318
|
+
"Current thread: #{ActiveSupport::IsolatedExecutionState.context}."
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
@idle_since = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
322
|
+
@owner = nil
|
|
323
|
+
else
|
|
324
|
+
raise ActiveRecordError, "Cannot expire connection, it is not currently leased."
|
|
325
|
+
end
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
# this method must only be called while holding connection pool's mutex (and a desire for segfaults)
|
|
329
|
+
def steal! # :nodoc:
|
|
330
|
+
if in_use?
|
|
331
|
+
if @owner != ActiveSupport::IsolatedExecutionState.context
|
|
332
|
+
pool.send :remove_connection_from_thread_cache, self, @owner
|
|
333
|
+
|
|
334
|
+
@owner = ActiveSupport::IsolatedExecutionState.context
|
|
335
|
+
end
|
|
336
|
+
else
|
|
337
|
+
raise ActiveRecordError, "Cannot steal connection, it is not currently leased."
|
|
338
|
+
end
|
|
339
|
+
end
|
|
340
|
+
|
|
341
|
+
# Seconds since this connection was returned to the pool
|
|
342
|
+
def seconds_idle # :nodoc:
|
|
343
|
+
return 0 if in_use?
|
|
344
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC) - @idle_since
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
def unprepared_statement
|
|
348
|
+
cache = prepared_statements_disabled_cache.add?(object_id) if @prepared_statements
|
|
349
|
+
yield
|
|
350
|
+
ensure
|
|
351
|
+
cache&.delete(object_id)
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
# Returns the human-readable name of the adapter. Use mixed case - one
|
|
355
|
+
# can always use downcase if needed.
|
|
356
|
+
def adapter_name
|
|
357
|
+
self.class::ADAPTER_NAME
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
# Does the database for this adapter exist?
|
|
361
|
+
def self.database_exists?(config)
|
|
362
|
+
new(config).database_exists?
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
def database_exists?
|
|
366
|
+
connect!
|
|
367
|
+
true
|
|
368
|
+
rescue ActiveRecord::NoDatabaseError
|
|
369
|
+
false
|
|
370
|
+
end
|
|
371
|
+
|
|
372
|
+
# Does this adapter support DDL rollbacks in transactions? That is, would
|
|
373
|
+
# CREATE TABLE or ALTER TABLE get rolled back by a transaction?
|
|
374
|
+
def supports_ddl_transactions?
|
|
375
|
+
false
|
|
376
|
+
end
|
|
377
|
+
|
|
378
|
+
def supports_bulk_alter?
|
|
379
|
+
false
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
# Does this adapter support savepoints?
|
|
383
|
+
def supports_savepoints?
|
|
384
|
+
false
|
|
385
|
+
end
|
|
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
|
+
|
|
397
|
+
# Does this adapter support application-enforced advisory locking?
|
|
398
|
+
def supports_advisory_locks?
|
|
399
|
+
false
|
|
400
|
+
end
|
|
401
|
+
|
|
402
|
+
# Should primary key values be selected from their corresponding
|
|
403
|
+
# sequence before the insert statement? If true, next_sequence_value
|
|
404
|
+
# is called before each insert to set the record's primary key.
|
|
405
|
+
def prefetch_primary_key?(table_name = nil)
|
|
406
|
+
false
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
def supports_partitioned_indexes?
|
|
410
|
+
false
|
|
411
|
+
end
|
|
412
|
+
|
|
413
|
+
# Does this adapter support index sort order?
|
|
414
|
+
def supports_index_sort_order?
|
|
415
|
+
false
|
|
416
|
+
end
|
|
417
|
+
|
|
418
|
+
# Does this adapter support partial indices?
|
|
419
|
+
def supports_partial_index?
|
|
420
|
+
false
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
# Does this adapter support including non-key columns?
|
|
424
|
+
def supports_index_include?
|
|
425
|
+
false
|
|
426
|
+
end
|
|
427
|
+
|
|
428
|
+
# Does this adapter support expression indices?
|
|
429
|
+
def supports_expression_index?
|
|
430
|
+
false
|
|
431
|
+
end
|
|
432
|
+
|
|
433
|
+
# Does this adapter support explain?
|
|
434
|
+
def supports_explain?
|
|
435
|
+
false
|
|
436
|
+
end
|
|
437
|
+
|
|
438
|
+
# Does this adapter support setting the isolation level for a transaction?
|
|
439
|
+
def supports_transaction_isolation?
|
|
440
|
+
false
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
# Does this adapter support database extensions?
|
|
444
|
+
def supports_extensions?
|
|
445
|
+
false
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
# Does this adapter support creating indexes in the same statement as
|
|
449
|
+
# creating the table?
|
|
450
|
+
def supports_indexes_in_create?
|
|
451
|
+
false
|
|
452
|
+
end
|
|
453
|
+
|
|
454
|
+
# Does this adapter support creating foreign key constraints?
|
|
455
|
+
def supports_foreign_keys?
|
|
456
|
+
false
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
# Does this adapter support creating invalid constraints?
|
|
460
|
+
def supports_validate_constraints?
|
|
461
|
+
false
|
|
462
|
+
end
|
|
463
|
+
|
|
464
|
+
# Does this adapter support creating deferrable constraints?
|
|
465
|
+
def supports_deferrable_constraints?
|
|
466
|
+
false
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
# Does this adapter support creating check constraints?
|
|
470
|
+
def supports_check_constraints?
|
|
471
|
+
false
|
|
472
|
+
end
|
|
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
|
+
|
|
484
|
+
# Does this adapter support views?
|
|
485
|
+
def supports_views?
|
|
486
|
+
false
|
|
487
|
+
end
|
|
488
|
+
|
|
489
|
+
# Does this adapter support materialized views?
|
|
490
|
+
def supports_materialized_views?
|
|
491
|
+
false
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
# Does this adapter support datetime with precision?
|
|
495
|
+
def supports_datetime_with_precision?
|
|
496
|
+
false
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
# Does this adapter support JSON data type?
|
|
500
|
+
def supports_json?
|
|
501
|
+
false
|
|
502
|
+
end
|
|
503
|
+
|
|
504
|
+
# Does this adapter support metadata comments on database objects (tables, columns, indexes)?
|
|
505
|
+
def supports_comments?
|
|
506
|
+
false
|
|
507
|
+
end
|
|
508
|
+
|
|
509
|
+
# Can comments for tables, columns, and indexes be specified in create/alter table statements?
|
|
510
|
+
def supports_comments_in_create?
|
|
511
|
+
false
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
# Does this adapter support virtual columns?
|
|
515
|
+
def supports_virtual_columns?
|
|
516
|
+
false
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
# Does this adapter support foreign/external tables?
|
|
520
|
+
def supports_foreign_tables?
|
|
521
|
+
false
|
|
522
|
+
end
|
|
523
|
+
|
|
524
|
+
# Does this adapter support optimizer hints?
|
|
525
|
+
def supports_optimizer_hints?
|
|
526
|
+
false
|
|
527
|
+
end
|
|
528
|
+
|
|
529
|
+
def supports_common_table_expressions?
|
|
530
|
+
false
|
|
531
|
+
end
|
|
532
|
+
|
|
533
|
+
def supports_lazy_transactions?
|
|
534
|
+
false
|
|
535
|
+
end
|
|
536
|
+
|
|
537
|
+
def supports_insert_returning?
|
|
538
|
+
false
|
|
539
|
+
end
|
|
540
|
+
|
|
541
|
+
def supports_insert_on_duplicate_skip?
|
|
542
|
+
false
|
|
543
|
+
end
|
|
544
|
+
|
|
545
|
+
def supports_insert_on_duplicate_update?
|
|
546
|
+
false
|
|
547
|
+
end
|
|
548
|
+
|
|
549
|
+
def supports_insert_conflict_target?
|
|
550
|
+
false
|
|
551
|
+
end
|
|
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
|
+
|
|
570
|
+
# This is meant to be implemented by the adapters that support extensions
|
|
571
|
+
def disable_extension(name, **)
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
# This is meant to be implemented by the adapters that support extensions
|
|
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:
|
|
596
|
+
end
|
|
597
|
+
|
|
598
|
+
# This is meant to be implemented by the adapters that support virtual tables
|
|
599
|
+
def create_virtual_table(*) # :nodoc:
|
|
600
|
+
end
|
|
601
|
+
|
|
602
|
+
# This is meant to be implemented by the adapters that support virtual tables
|
|
603
|
+
def drop_virtual_table(*) # :nodoc:
|
|
604
|
+
end
|
|
605
|
+
|
|
606
|
+
def advisory_locks_enabled? # :nodoc:
|
|
607
|
+
supports_advisory_locks? && @advisory_locks_enabled
|
|
608
|
+
end
|
|
609
|
+
|
|
610
|
+
# This is meant to be implemented by the adapters that support advisory
|
|
611
|
+
# locks
|
|
612
|
+
#
|
|
613
|
+
# Return true if we got the lock, otherwise false
|
|
614
|
+
def get_advisory_lock(lock_id) # :nodoc:
|
|
615
|
+
end
|
|
616
|
+
|
|
617
|
+
# This is meant to be implemented by the adapters that support advisory
|
|
618
|
+
# locks.
|
|
619
|
+
#
|
|
620
|
+
# Return true if we released the lock, otherwise false
|
|
621
|
+
def release_advisory_lock(lock_id) # :nodoc:
|
|
622
|
+
end
|
|
623
|
+
|
|
624
|
+
# A list of extensions, to be filled in by adapters that support them.
|
|
625
|
+
def extensions
|
|
626
|
+
[]
|
|
627
|
+
end
|
|
628
|
+
|
|
629
|
+
# A list of index algorithms, to be filled by adapters that support them.
|
|
630
|
+
def index_algorithms
|
|
631
|
+
{}
|
|
632
|
+
end
|
|
633
|
+
|
|
634
|
+
# REFERENTIAL INTEGRITY ====================================
|
|
635
|
+
|
|
636
|
+
# Override to turn off referential integrity while executing <tt>&block</tt>.
|
|
637
|
+
def disable_referential_integrity
|
|
638
|
+
yield
|
|
639
|
+
end
|
|
640
|
+
|
|
641
|
+
# Override to check all foreign key constraints in a database.
|
|
642
|
+
# The adapter should raise a +ActiveRecord::StatementInvalid+ if foreign key
|
|
643
|
+
# constraints are not met.
|
|
644
|
+
def check_all_foreign_keys_valid!
|
|
645
|
+
end
|
|
646
|
+
|
|
647
|
+
# CONNECTION MANAGEMENT ====================================
|
|
648
|
+
|
|
649
|
+
# Checks whether the connection to the database was established. This doesn't
|
|
650
|
+
# include checking whether the database is actually capable of responding, i.e.
|
|
651
|
+
# whether the connection is stale.
|
|
652
|
+
def connected?
|
|
653
|
+
!@raw_connection.nil?
|
|
654
|
+
end
|
|
655
|
+
|
|
656
|
+
# Checks whether the connection to the database is still active. This includes
|
|
657
|
+
# checking whether the database is actually capable of responding, i.e. whether
|
|
658
|
+
# the connection isn't stale.
|
|
659
|
+
def active?
|
|
660
|
+
end
|
|
661
|
+
|
|
662
|
+
# Disconnects from the database if already connected, and establishes a new
|
|
663
|
+
# connection with the database. Implementors should define private #reconnect
|
|
664
|
+
# instead.
|
|
665
|
+
def reconnect!(restore_transactions: false)
|
|
666
|
+
retries_available = connection_retries
|
|
667
|
+
deadline = retry_deadline && Process.clock_gettime(Process::CLOCK_MONOTONIC) + retry_deadline
|
|
668
|
+
|
|
669
|
+
@lock.synchronize do
|
|
670
|
+
reconnect
|
|
671
|
+
|
|
672
|
+
enable_lazy_transactions!
|
|
673
|
+
@raw_connection_dirty = false
|
|
674
|
+
@verified = true
|
|
675
|
+
|
|
676
|
+
reset_transaction(restore: restore_transactions) do
|
|
677
|
+
clear_cache!(new_connection: true)
|
|
678
|
+
configure_connection
|
|
679
|
+
end
|
|
680
|
+
rescue => original_exception
|
|
681
|
+
translated_exception = translate_exception_class(original_exception, nil, nil)
|
|
682
|
+
retry_deadline_exceeded = deadline && deadline < Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
683
|
+
|
|
684
|
+
if !retry_deadline_exceeded && retries_available > 0
|
|
685
|
+
retries_available -= 1
|
|
686
|
+
|
|
687
|
+
if retryable_connection_error?(translated_exception)
|
|
688
|
+
backoff(connection_retries - retries_available)
|
|
689
|
+
retry
|
|
690
|
+
end
|
|
691
|
+
end
|
|
692
|
+
|
|
693
|
+
@verified = false
|
|
694
|
+
|
|
695
|
+
raise translated_exception
|
|
696
|
+
end
|
|
697
|
+
end
|
|
698
|
+
|
|
699
|
+
# Disconnects from the database if already connected. Otherwise, this
|
|
700
|
+
# method does nothing.
|
|
701
|
+
def disconnect!
|
|
702
|
+
@lock.synchronize do
|
|
703
|
+
clear_cache!(new_connection: true)
|
|
704
|
+
reset_transaction
|
|
705
|
+
@raw_connection_dirty = false
|
|
706
|
+
end
|
|
707
|
+
end
|
|
708
|
+
|
|
709
|
+
# Immediately forget this connection ever existed. Unlike disconnect!,
|
|
710
|
+
# this will not communicate with the server.
|
|
711
|
+
#
|
|
712
|
+
# After calling this method, the behavior of all other methods becomes
|
|
713
|
+
# undefined. This is called internally just before a forked process gets
|
|
714
|
+
# rid of a connection that belonged to its parent.
|
|
715
|
+
def discard!
|
|
716
|
+
# This should be overridden by concrete adapters.
|
|
717
|
+
end
|
|
718
|
+
|
|
719
|
+
# Reset the state of this connection, directing the DBMS to clear
|
|
720
|
+
# transactions and other connection-related server-side state. Usually a
|
|
721
|
+
# database-dependent operation.
|
|
722
|
+
#
|
|
723
|
+
# If a database driver or protocol does not support such a feature,
|
|
724
|
+
# implementors may alias this to #reconnect!. Otherwise, implementors
|
|
725
|
+
# should call super immediately after resetting the connection (and while
|
|
726
|
+
# still holding @lock).
|
|
727
|
+
def reset!
|
|
728
|
+
clear_cache!(new_connection: true)
|
|
729
|
+
reset_transaction
|
|
730
|
+
configure_connection
|
|
731
|
+
end
|
|
732
|
+
|
|
733
|
+
# Removes the connection from the pool and disconnect it.
|
|
734
|
+
def throw_away!
|
|
735
|
+
pool.remove self
|
|
736
|
+
disconnect!
|
|
737
|
+
end
|
|
738
|
+
|
|
739
|
+
# Clear any caching the database adapter may be doing.
|
|
740
|
+
def clear_cache!(new_connection: false)
|
|
741
|
+
if @statements
|
|
742
|
+
@lock.synchronize do
|
|
743
|
+
if new_connection
|
|
744
|
+
@statements.reset
|
|
745
|
+
else
|
|
746
|
+
@statements.clear
|
|
747
|
+
end
|
|
748
|
+
end
|
|
749
|
+
end
|
|
750
|
+
end
|
|
751
|
+
|
|
752
|
+
# Returns true if its required to reload the connection between requests for development mode.
|
|
753
|
+
def requires_reloading?
|
|
754
|
+
false
|
|
755
|
+
end
|
|
756
|
+
|
|
757
|
+
# Checks whether the connection to the database is still active (i.e. not stale).
|
|
758
|
+
# This is done under the hood by calling #active?. If the connection
|
|
759
|
+
# is no longer active, then this method will reconnect to the database.
|
|
760
|
+
def verify!
|
|
761
|
+
unless active?
|
|
762
|
+
@lock.synchronize do
|
|
763
|
+
if @unconfigured_connection
|
|
764
|
+
@raw_connection = @unconfigured_connection
|
|
765
|
+
@unconfigured_connection = nil
|
|
766
|
+
configure_connection
|
|
767
|
+
@verified = true
|
|
768
|
+
return
|
|
769
|
+
end
|
|
770
|
+
|
|
771
|
+
reconnect!(restore_transactions: true)
|
|
772
|
+
end
|
|
773
|
+
end
|
|
774
|
+
|
|
775
|
+
@verified = true
|
|
776
|
+
end
|
|
777
|
+
|
|
778
|
+
def connect!
|
|
779
|
+
verify!
|
|
780
|
+
self
|
|
781
|
+
end
|
|
782
|
+
|
|
783
|
+
def clean! # :nodoc:
|
|
784
|
+
@raw_connection_dirty = false
|
|
785
|
+
@verified = nil
|
|
786
|
+
end
|
|
787
|
+
|
|
788
|
+
# Provides access to the underlying database driver for this adapter. For
|
|
789
|
+
# example, this method returns a Mysql2::Client object in case of Mysql2Adapter,
|
|
790
|
+
# and a PG::Connection object in case of PostgreSQLAdapter.
|
|
791
|
+
#
|
|
792
|
+
# This is useful for when you need to call a proprietary method such as
|
|
793
|
+
# PostgreSQL's lo_* methods.
|
|
794
|
+
#
|
|
795
|
+
# Active Record cannot track if the database is getting modified using
|
|
796
|
+
# this client. If that is the case, generally you'll want to invalidate
|
|
797
|
+
# the query cache using +ActiveRecord::Base.clear_query_cache+.
|
|
798
|
+
def raw_connection
|
|
799
|
+
with_raw_connection do |conn|
|
|
800
|
+
disable_lazy_transactions!
|
|
801
|
+
@raw_connection_dirty = true
|
|
802
|
+
conn
|
|
803
|
+
end
|
|
804
|
+
end
|
|
805
|
+
|
|
806
|
+
def default_uniqueness_comparison(attribute, value) # :nodoc:
|
|
807
|
+
attribute.eq(value)
|
|
808
|
+
end
|
|
809
|
+
|
|
810
|
+
def case_sensitive_comparison(attribute, value) # :nodoc:
|
|
811
|
+
attribute.eq(value)
|
|
812
|
+
end
|
|
813
|
+
|
|
814
|
+
def case_insensitive_comparison(attribute, value) # :nodoc:
|
|
815
|
+
column = column_for_attribute(attribute)
|
|
816
|
+
|
|
817
|
+
if can_perform_case_insensitive_comparison_for?(column)
|
|
818
|
+
attribute.lower.eq(attribute.relation.lower(value))
|
|
819
|
+
else
|
|
820
|
+
attribute.eq(value)
|
|
821
|
+
end
|
|
822
|
+
end
|
|
823
|
+
|
|
824
|
+
def can_perform_case_insensitive_comparison_for?(column)
|
|
825
|
+
true
|
|
826
|
+
end
|
|
827
|
+
private :can_perform_case_insensitive_comparison_for?
|
|
828
|
+
|
|
829
|
+
# Check the connection back in to the connection pool
|
|
830
|
+
def close
|
|
831
|
+
pool.checkin self
|
|
832
|
+
end
|
|
833
|
+
|
|
834
|
+
def default_index_type?(index) # :nodoc:
|
|
835
|
+
index.using.nil?
|
|
836
|
+
end
|
|
837
|
+
|
|
838
|
+
# Called by ActiveRecord::InsertAll,
|
|
839
|
+
# Passed an instance of ActiveRecord::InsertAll::Builder,
|
|
840
|
+
# This method implements standard bulk inserts for all databases, but
|
|
841
|
+
# should be overridden by adapters to implement common features with
|
|
842
|
+
# non-standard syntax like handling duplicates or returning values.
|
|
843
|
+
def build_insert_sql(insert) # :nodoc:
|
|
844
|
+
if insert.skip_duplicates? || insert.update_duplicates?
|
|
845
|
+
raise NotImplementedError, "#{self.class} should define `build_insert_sql` to implement adapter-specific logic for handling duplicates during INSERT"
|
|
846
|
+
end
|
|
847
|
+
|
|
848
|
+
"INSERT #{insert.into} #{insert.values_list}"
|
|
849
|
+
end
|
|
850
|
+
|
|
851
|
+
def get_database_version # :nodoc:
|
|
852
|
+
end
|
|
853
|
+
|
|
854
|
+
def database_version # :nodoc:
|
|
855
|
+
pool.server_version(self)
|
|
856
|
+
end
|
|
857
|
+
|
|
858
|
+
def check_version # :nodoc:
|
|
859
|
+
end
|
|
860
|
+
|
|
861
|
+
# Returns the version identifier of the schema currently available in
|
|
862
|
+
# the database. This is generally equal to the number of the highest-
|
|
863
|
+
# numbered migration that has been executed, or 0 if no schema
|
|
864
|
+
# information is present / the database is empty.
|
|
865
|
+
def schema_version
|
|
866
|
+
pool.migration_context.current_version
|
|
867
|
+
end
|
|
868
|
+
|
|
869
|
+
class << self
|
|
870
|
+
def register_class_with_precision(mapping, key, klass, **kwargs) # :nodoc:
|
|
871
|
+
mapping.register_type(key) do |*args|
|
|
872
|
+
precision = extract_precision(args.last)
|
|
873
|
+
klass.new(precision: precision, **kwargs)
|
|
874
|
+
end
|
|
875
|
+
end
|
|
876
|
+
|
|
877
|
+
def extended_type_map(default_timezone:) # :nodoc:
|
|
878
|
+
Type::TypeMap.new(self::TYPE_MAP).tap do |m|
|
|
879
|
+
register_class_with_precision m, %r(\A[^\(]*time)i, Type::Time, timezone: default_timezone
|
|
880
|
+
register_class_with_precision m, %r(\A[^\(]*datetime)i, Type::DateTime, timezone: default_timezone
|
|
881
|
+
m.alias_type %r(\A[^\(]*timestamp)i, "datetime"
|
|
882
|
+
end
|
|
883
|
+
end
|
|
884
|
+
|
|
885
|
+
private
|
|
886
|
+
def initialize_type_map(m)
|
|
887
|
+
register_class_with_limit m, %r(boolean)i, Type::Boolean
|
|
888
|
+
register_class_with_limit m, %r(char)i, Type::String
|
|
889
|
+
register_class_with_limit m, %r(binary)i, Type::Binary
|
|
890
|
+
register_class_with_limit m, %r(text)i, Type::Text
|
|
891
|
+
register_class_with_precision m, %r(date)i, Type::Date
|
|
892
|
+
register_class_with_precision m, %r(time)i, Type::Time
|
|
893
|
+
register_class_with_precision m, %r(datetime)i, Type::DateTime
|
|
894
|
+
register_class_with_limit m, %r(float)i, Type::Float
|
|
895
|
+
register_class_with_limit m, %r(int)i, Type::Integer
|
|
896
|
+
|
|
897
|
+
m.alias_type %r(blob)i, "binary"
|
|
898
|
+
m.alias_type %r(clob)i, "text"
|
|
899
|
+
m.alias_type %r(timestamp)i, "datetime"
|
|
900
|
+
m.alias_type %r(numeric)i, "decimal"
|
|
901
|
+
m.alias_type %r(number)i, "decimal"
|
|
902
|
+
m.alias_type %r(double)i, "float"
|
|
903
|
+
|
|
904
|
+
m.register_type %r(^json)i, Type::Json.new
|
|
905
|
+
|
|
906
|
+
m.register_type(%r(decimal)i) do |sql_type|
|
|
907
|
+
scale = extract_scale(sql_type)
|
|
908
|
+
precision = extract_precision(sql_type)
|
|
909
|
+
|
|
910
|
+
if scale == 0
|
|
911
|
+
# FIXME: Remove this class as well
|
|
912
|
+
Type::DecimalWithoutScale.new(precision: precision)
|
|
913
|
+
else
|
|
914
|
+
Type::Decimal.new(precision: precision, scale: scale)
|
|
915
|
+
end
|
|
916
|
+
end
|
|
917
|
+
end
|
|
918
|
+
|
|
919
|
+
def register_class_with_limit(mapping, key, klass)
|
|
920
|
+
mapping.register_type(key) do |*args|
|
|
921
|
+
limit = extract_limit(args.last)
|
|
922
|
+
klass.new(limit: limit)
|
|
923
|
+
end
|
|
924
|
+
end
|
|
925
|
+
|
|
926
|
+
def extract_scale(sql_type)
|
|
927
|
+
case sql_type
|
|
928
|
+
when /\((\d+)\)/ then 0
|
|
929
|
+
when /\((\d+)(,(\d+))\)/ then $3.to_i
|
|
930
|
+
end
|
|
931
|
+
end
|
|
932
|
+
|
|
933
|
+
def extract_precision(sql_type)
|
|
934
|
+
$1.to_i if sql_type =~ /\((\d+)(,\d+)?\)/
|
|
935
|
+
end
|
|
936
|
+
|
|
937
|
+
def extract_limit(sql_type)
|
|
938
|
+
$1.to_i if sql_type =~ /\((.*)\)/
|
|
939
|
+
end
|
|
940
|
+
end
|
|
941
|
+
|
|
942
|
+
TYPE_MAP = Type::TypeMap.new.tap { |m| initialize_type_map(m) }
|
|
943
|
+
EXTENDED_TYPE_MAPS = Concurrent::Map.new
|
|
944
|
+
|
|
945
|
+
private
|
|
946
|
+
def reconnect_can_restore_state?
|
|
947
|
+
transaction_manager.restorable? && !@raw_connection_dirty
|
|
948
|
+
end
|
|
949
|
+
|
|
950
|
+
# Lock the monitor, ensure we're properly connected and
|
|
951
|
+
# transactions are materialized, and then yield the underlying
|
|
952
|
+
# raw connection object.
|
|
953
|
+
#
|
|
954
|
+
# If +allow_retry+ is true, a connection-related exception will
|
|
955
|
+
# cause an automatic reconnect and re-run of the block, up to
|
|
956
|
+
# the connection's configured +connection_retries+ setting
|
|
957
|
+
# and the configured +retry_deadline+ limit. (Note that when
|
|
958
|
+
# +allow_retry+ is true, it's possible to return without having marked
|
|
959
|
+
# the connection as verified. If the block is guaranteed to exercise the
|
|
960
|
+
# connection, consider calling `verified!` to avoid needless
|
|
961
|
+
# verification queries in subsequent calls.)
|
|
962
|
+
#
|
|
963
|
+
# If +materialize_transactions+ is false, the block will be run without
|
|
964
|
+
# ensuring virtual transactions have been materialized in the DB
|
|
965
|
+
# server's state. The active transaction will also remain clean
|
|
966
|
+
# (if it is not already dirty), meaning it's able to be restored
|
|
967
|
+
# by reconnecting and opening an equivalent-depth set of new
|
|
968
|
+
# transactions. This should only be used by transaction control
|
|
969
|
+
# methods, and internal transaction-agnostic queries.
|
|
970
|
+
#
|
|
971
|
+
###
|
|
972
|
+
#
|
|
973
|
+
# It's not the primary use case, so not something to optimize
|
|
974
|
+
# for, but note that this method does need to be re-entrant:
|
|
975
|
+
# +materialize_transactions+ will re-enter if it has work to do,
|
|
976
|
+
# and the yield block can also do so under some circumstances.
|
|
977
|
+
#
|
|
978
|
+
# In the latter case, we really ought to guarantee the inner
|
|
979
|
+
# call will not reconnect (which would interfere with the
|
|
980
|
+
# still-yielded connection in the outer block), but we currently
|
|
981
|
+
# provide no special enforcement there.
|
|
982
|
+
#
|
|
983
|
+
def with_raw_connection(allow_retry: false, materialize_transactions: true)
|
|
984
|
+
@lock.synchronize do
|
|
985
|
+
connect! if @raw_connection.nil? && reconnect_can_restore_state?
|
|
986
|
+
|
|
987
|
+
self.materialize_transactions if materialize_transactions
|
|
988
|
+
|
|
989
|
+
retries_available = allow_retry ? connection_retries : 0
|
|
990
|
+
deadline = retry_deadline && Process.clock_gettime(Process::CLOCK_MONOTONIC) + retry_deadline
|
|
991
|
+
reconnectable = reconnect_can_restore_state?
|
|
992
|
+
|
|
993
|
+
if @verified
|
|
994
|
+
# Cool, we're confident the connection's ready to use. (Note this might have
|
|
995
|
+
# become true during the above #materialize_transactions.)
|
|
996
|
+
elsif reconnectable
|
|
997
|
+
if allow_retry
|
|
998
|
+
# Not sure about the connection yet, but if anything goes wrong we can
|
|
999
|
+
# just reconnect and re-run our query
|
|
1000
|
+
else
|
|
1001
|
+
# We can reconnect if needed, but we don't trust the upcoming query to be
|
|
1002
|
+
# safely re-runnable: let's verify the connection to be sure
|
|
1003
|
+
verify!
|
|
1004
|
+
end
|
|
1005
|
+
else
|
|
1006
|
+
# We don't know whether the connection is okay, but it also doesn't matter:
|
|
1007
|
+
# we wouldn't be able to reconnect anyway. We're just going to run our query
|
|
1008
|
+
# and hope for the best.
|
|
1009
|
+
end
|
|
1010
|
+
|
|
1011
|
+
begin
|
|
1012
|
+
yield @raw_connection
|
|
1013
|
+
rescue => original_exception
|
|
1014
|
+
translated_exception = translate_exception_class(original_exception, nil, nil)
|
|
1015
|
+
invalidate_transaction(translated_exception)
|
|
1016
|
+
retry_deadline_exceeded = deadline && deadline < Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
1017
|
+
|
|
1018
|
+
if !retry_deadline_exceeded && retries_available > 0
|
|
1019
|
+
retries_available -= 1
|
|
1020
|
+
|
|
1021
|
+
if retryable_query_error?(translated_exception)
|
|
1022
|
+
backoff(connection_retries - retries_available)
|
|
1023
|
+
retry
|
|
1024
|
+
elsif reconnectable && retryable_connection_error?(translated_exception)
|
|
1025
|
+
reconnect!(restore_transactions: true)
|
|
1026
|
+
# Only allowed to reconnect once, because reconnect! has its own retry
|
|
1027
|
+
# loop
|
|
1028
|
+
reconnectable = false
|
|
1029
|
+
retry
|
|
1030
|
+
end
|
|
1031
|
+
end
|
|
1032
|
+
|
|
1033
|
+
unless retryable_query_error?(translated_exception)
|
|
1034
|
+
# Barring a known-retryable error inside the query (regardless of
|
|
1035
|
+
# whether we were in a _position_ to retry it), we should infer that
|
|
1036
|
+
# there's likely a real problem with the connection.
|
|
1037
|
+
@verified = false
|
|
1038
|
+
end
|
|
1039
|
+
|
|
1040
|
+
raise translated_exception
|
|
1041
|
+
ensure
|
|
1042
|
+
dirty_current_transaction if materialize_transactions
|
|
1043
|
+
end
|
|
1044
|
+
end
|
|
1045
|
+
end
|
|
1046
|
+
|
|
1047
|
+
# Mark the connection as verified. Call this inside a
|
|
1048
|
+
# `with_raw_connection` block only when the block is guaranteed to
|
|
1049
|
+
# exercise the raw connection.
|
|
1050
|
+
def verified!
|
|
1051
|
+
@verified = true
|
|
1052
|
+
end
|
|
1053
|
+
|
|
1054
|
+
def retryable_connection_error?(exception)
|
|
1055
|
+
(exception.is_a?(ConnectionNotEstablished) && !exception.is_a?(ConnectionNotDefined)) ||
|
|
1056
|
+
exception.is_a?(ConnectionFailed)
|
|
1057
|
+
end
|
|
1058
|
+
|
|
1059
|
+
def invalidate_transaction(exception)
|
|
1060
|
+
return unless exception.is_a?(TransactionRollbackError)
|
|
1061
|
+
return unless savepoint_errors_invalidate_transactions?
|
|
1062
|
+
|
|
1063
|
+
current_transaction.invalidate!
|
|
1064
|
+
end
|
|
1065
|
+
|
|
1066
|
+
def retryable_query_error?(exception)
|
|
1067
|
+
# We definitely can't retry if we were inside an invalidated transaction.
|
|
1068
|
+
return false if current_transaction.invalidated?
|
|
1069
|
+
|
|
1070
|
+
exception.is_a?(Deadlocked) || exception.is_a?(LockWaitTimeout)
|
|
1071
|
+
end
|
|
1072
|
+
|
|
1073
|
+
def backoff(counter)
|
|
1074
|
+
sleep 0.1 * counter
|
|
1075
|
+
end
|
|
1076
|
+
|
|
1077
|
+
def reconnect
|
|
1078
|
+
raise NotImplementedError
|
|
1079
|
+
end
|
|
1080
|
+
|
|
1081
|
+
# Returns a raw connection for internal use with methods that are known
|
|
1082
|
+
# to both be thread-safe and not rely upon actual server communication.
|
|
1083
|
+
# This is useful for e.g. string escaping methods.
|
|
1084
|
+
def any_raw_connection
|
|
1085
|
+
@raw_connection || valid_raw_connection
|
|
1086
|
+
end
|
|
1087
|
+
|
|
1088
|
+
# Similar to any_raw_connection, but ensures it is validated and
|
|
1089
|
+
# connected. Any method called on this result still needs to be
|
|
1090
|
+
# independently thread-safe, so it probably shouldn't talk to the
|
|
1091
|
+
# server... but some drivers fail if they know the connection has gone
|
|
1092
|
+
# away.
|
|
1093
|
+
def valid_raw_connection
|
|
1094
|
+
(@verified && @raw_connection) ||
|
|
1095
|
+
# `allow_retry: false`, to force verification: the block won't
|
|
1096
|
+
# raise, so a retry wouldn't help us get the valid connection we
|
|
1097
|
+
# need.
|
|
1098
|
+
with_raw_connection(allow_retry: false, materialize_transactions: false) { |conn| conn }
|
|
1099
|
+
end
|
|
1100
|
+
|
|
1101
|
+
def extended_type_map_key
|
|
1102
|
+
if @default_timezone
|
|
1103
|
+
{ default_timezone: @default_timezone }
|
|
1104
|
+
end
|
|
1105
|
+
end
|
|
1106
|
+
|
|
1107
|
+
def type_map
|
|
1108
|
+
if key = extended_type_map_key
|
|
1109
|
+
self.class::EXTENDED_TYPE_MAPS.compute_if_absent(key) do
|
|
1110
|
+
self.class.extended_type_map(**key)
|
|
1111
|
+
end
|
|
1112
|
+
else
|
|
1113
|
+
self.class::TYPE_MAP
|
|
1114
|
+
end
|
|
1115
|
+
end
|
|
1116
|
+
|
|
1117
|
+
def translate_exception_class(native_error, sql, binds)
|
|
1118
|
+
return native_error if native_error.is_a?(ActiveRecordError)
|
|
1119
|
+
|
|
1120
|
+
message = "#{native_error.class.name}: #{native_error.message}"
|
|
1121
|
+
|
|
1122
|
+
active_record_error = translate_exception(
|
|
1123
|
+
native_error, message: message, sql: sql, binds: binds
|
|
1124
|
+
)
|
|
1125
|
+
active_record_error.set_backtrace(native_error.backtrace)
|
|
1126
|
+
active_record_error
|
|
1127
|
+
end
|
|
1128
|
+
|
|
1129
|
+
def log(sql, name = "SQL", binds = [], type_casted_binds = [], async: false, &block) # :doc:
|
|
1130
|
+
@instrumenter.instrument(
|
|
1131
|
+
"sql.active_record",
|
|
1132
|
+
sql: sql,
|
|
1133
|
+
name: name,
|
|
1134
|
+
binds: binds,
|
|
1135
|
+
type_casted_binds: type_casted_binds,
|
|
1136
|
+
async: async,
|
|
1137
|
+
connection: self,
|
|
1138
|
+
transaction: current_transaction.user_transaction.presence,
|
|
1139
|
+
row_count: 0,
|
|
1140
|
+
&block
|
|
1141
|
+
)
|
|
1142
|
+
rescue ActiveRecord::StatementInvalid => ex
|
|
1143
|
+
raise ex.set_query(sql, binds)
|
|
1144
|
+
end
|
|
1145
|
+
|
|
1146
|
+
def translate_exception(exception, message:, sql:, binds:)
|
|
1147
|
+
# override in derived class
|
|
1148
|
+
case exception
|
|
1149
|
+
when RuntimeError, ActiveRecord::ActiveRecordError
|
|
1150
|
+
exception
|
|
1151
|
+
else
|
|
1152
|
+
ActiveRecord::StatementInvalid.new(message, sql: sql, binds: binds, connection_pool: @pool)
|
|
1153
|
+
end
|
|
1154
|
+
end
|
|
1155
|
+
|
|
1156
|
+
def column_for(table_name, column_name)
|
|
1157
|
+
column_name = column_name.to_s
|
|
1158
|
+
columns(table_name).detect { |c| c.name == column_name } ||
|
|
1159
|
+
raise(ActiveRecordError, "No such column: #{table_name}.#{column_name}")
|
|
1160
|
+
end
|
|
1161
|
+
|
|
1162
|
+
def column_for_attribute(attribute)
|
|
1163
|
+
table_name = attribute.relation.name
|
|
1164
|
+
schema_cache.columns_hash(table_name)[attribute.name.to_s]
|
|
1165
|
+
end
|
|
1166
|
+
|
|
1167
|
+
def collector
|
|
1168
|
+
if prepared_statements
|
|
1169
|
+
Arel::Collectors::Composite.new(
|
|
1170
|
+
Arel::Collectors::SQLString.new,
|
|
1171
|
+
Arel::Collectors::Bind.new,
|
|
1172
|
+
)
|
|
1173
|
+
else
|
|
1174
|
+
Arel::Collectors::SubstituteBinds.new(
|
|
1175
|
+
self,
|
|
1176
|
+
Arel::Collectors::SQLString.new,
|
|
1177
|
+
)
|
|
1178
|
+
end
|
|
1179
|
+
end
|
|
1180
|
+
|
|
1181
|
+
def arel_visitor
|
|
1182
|
+
Arel::Visitors::ToSql.new(self)
|
|
1183
|
+
end
|
|
1184
|
+
|
|
1185
|
+
def build_statement_pool
|
|
1186
|
+
end
|
|
1187
|
+
|
|
1188
|
+
# Builds the result object.
|
|
1189
|
+
#
|
|
1190
|
+
# This is an internal hook to make possible connection adapters to build
|
|
1191
|
+
# custom result objects with connection-specific data.
|
|
1192
|
+
def build_result(columns:, rows:, column_types: nil)
|
|
1193
|
+
ActiveRecord::Result.new(columns, rows, column_types)
|
|
1194
|
+
end
|
|
1195
|
+
|
|
1196
|
+
# Perform any necessary initialization upon the newly-established
|
|
1197
|
+
# @raw_connection -- this is the place to modify the adapter's
|
|
1198
|
+
# connection settings, run queries to configure any application-global
|
|
1199
|
+
# "session" variables, etc.
|
|
1200
|
+
#
|
|
1201
|
+
# Implementations may assume this method will only be called while
|
|
1202
|
+
# holding @lock (or from #initialize).
|
|
1203
|
+
def configure_connection
|
|
1204
|
+
check_version
|
|
1205
|
+
end
|
|
1206
|
+
|
|
1207
|
+
def default_prepared_statements
|
|
1208
|
+
true
|
|
1209
|
+
end
|
|
1210
|
+
|
|
1211
|
+
def warning_ignored?(warning)
|
|
1212
|
+
ActiveRecord.db_warnings_ignore.any? do |warning_matcher|
|
|
1213
|
+
warning.message.match?(warning_matcher) || warning.code.to_s.match?(warning_matcher)
|
|
1214
|
+
end
|
|
1215
|
+
end
|
|
1216
|
+
end
|
|
1217
|
+
end
|
|
1218
|
+
end
|