dblift 3.2.0__tar.gz → 3.3.0__tar.gz
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.
- {dblift-3.2.0 → dblift-3.3.0}/PKG-INFO +1 -1
- {dblift-3.2.0 → dblift-3.3.0}/core/introspection/_column_enricher.py +91 -8
- {dblift-3.2.0 → dblift-3.3.0}/core/introspection/base_introspector.py +7 -5
- {dblift-3.2.0 → dblift-3.3.0}/core/introspection/extractors/index_extractor.py +15 -7
- dblift-3.3.0/core/seams/feature_loading.py +109 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/seams/sql_generators.py +1 -1
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_generator/alter/alter_generator_factory.py +10 -2
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/view.py +1 -1
- {dblift-3.2.0 → dblift-3.3.0}/db/base_quirks.py +19 -9
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/cosmosdb/cosmosdb/history_manager.py +1 -1
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/cosmosdb/cosmosdb/query_executor.py +56 -5
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/cosmosdb/provider.py +22 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/db2/quirks.py +4 -2
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/duckdb/parser/duckdb_regex_parser.py +1 -1
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/redshift/sqlalchemy_url.py +11 -14
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlite/provider.py +0 -11
- {dblift-3.2.0 → dblift-3.3.0}/dblift.egg-info/PKG-INFO +1 -1
- {dblift-3.2.0 → dblift-3.3.0}/pyproject.toml +1 -1
- dblift-3.2.0/core/seams/feature_loading.py +0 -66
- {dblift-3.2.0 → dblift-3.3.0}/LICENSE +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/README.md +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/api/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/api/_cli_support.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/api/_client_factory.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/api/_client_operations.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/api/_engine_config.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/api/async_client.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/api/client.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/api/events.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/api/migrations.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/api/py.typed +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/cli/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/cli/_command_handlers.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/cli/_config_helpers.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/cli/_constants.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/cli/_output.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/cli/_parser_setup.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/cli/commands/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/cli/commands/config_command.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/cli/db_utils.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/cli/extensions.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/cli/handlers/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/cli/handlers/_shared.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/cli/handlers/baseline.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/cli/handlers/clean.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/cli/handlers/import_flyway.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/cli/handlers/info.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/cli/handlers/migrate.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/cli/handlers/repair.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/cli/handlers/undo.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/cli/handlers/validate.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/cli/main.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/cli/premium_manifest.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/config/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/config/_credential_masking.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/config/_subclasses/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/config/_subclasses/dummy_config.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/config/_url_builder_mixin.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/config/config_builder.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/config/database_config.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/config/dblift_config.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/config/errors.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/config/property_registry.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/config/secrets/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/config/secrets/_cache.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/config/secrets/_provider_base.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/config/secrets/_registry.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/config/secrets/_resolver.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/config/secrets/_secrets_config.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/constants.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/dialect_boundary.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/exceptions.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/introspection/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/introspection/_partition_enricher.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/introspection/_schema_orchestrator.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/introspection/_utils.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/introspection/_vendor_property_applier.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/introspection/extractors/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/introspection/extractors/base_extractor.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/introspection/extractors/column_extractor.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/introspection/extractors/constraint_extractor.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/introspection/extractors/misc_extractor.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/introspection/extractors/procedure_extractor.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/introspection/extractors/sequence_extractor.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/introspection/extractors/table_extractor.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/introspection/extractors/trigger_extractor.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/introspection/extractors/view_extractor.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/introspection/introspector_factory.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/introspection/result.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/introspection/schema_introspector.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/introspection/vendor_queries_base.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/introspection/vendor_queries_factory.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/introspection/vendor_queries_protocols.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/introspection/version_detector.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/logger/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/logger/_base.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/logger/_factory.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/logger/_formatters.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/logger/_levels.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/logger/_multi.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/logger/_null.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/logger/console.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/logger/formatters/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/logger/formatters/_formatter_impl.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/logger/formatters/factory.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/logger/formatters/formatter.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/logger/formatters/htmlformatter.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/logger/formatters/jsonformatter.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/logger/log.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/logger/results.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/logger/templates/oldreport.html +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/logger/templates/report.html +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/_type_match.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/clean_summary.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/commands/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/commands/_script_events.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/commands/base_command.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/commands/baseline_command.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/commands/clean_command.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/commands/import_flyway_command.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/commands/info_command.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/commands/migrate_command.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/commands/repair_command.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/commands/undo_command.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/commands/validate_command.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/encoding.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/executor/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/executor/execution_engine.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/executor/migration_executor.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/executor/migration_helpers.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/executor/placeholder_manager.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/executor/transaction_policy.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/executors/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/executors/base_executor.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/executors/executor_factory.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/executors/python_executor.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/executors/sql_executor.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/formats/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/formats/format_detector.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/formats/migration_format.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/history/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/history/migration_history_manager.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/journals/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/journals/migration_journal.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/migration.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/placeholders/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/placeholders/placeholder_service.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/rules/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/rules/migration_rules.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/scripting/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/scripting/migration_script_manager.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/scripting/undo_script_generator/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/scripting/undo_script_generator/_ddl_reversers.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/scripting/undo_script_generator/_dml_reversers.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/scripting/undo_script_generator/_extractors.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/scripting/undo_script_generator/_generator.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/scripting/undo_script_generator/_helpers.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/scripting/undo_script_generator/_models.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/scripting/undo_script_generator/_reversers.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/sql/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/sql/execution_statement.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/sql/sql_analyzer.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/sql/sql_execution_service.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/sql/statement_splitter.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/state/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/state/migration_data_service.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/state/migration_display_state.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/state/migration_formatter.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/state/migration_state.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/state/migration_state_manager.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/state/migration_state_service.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/ui/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/ui/data_collector.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/ui/display_formatters.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/ui/migration_analyzer.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/ui/migration_ui.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/ui/table_renderer.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/migration/version_utils.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/normalization/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/normalization/dependency_resolver.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/normalization/identifier_normalizer.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/normalization/object_orderer.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/normalization/type_constants.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/normalization/type_mapper.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/normalization/type_mappings.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/normalization/type_normalizer.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/seams/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/seams/capabilities.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/seams/client_factory.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/seams/event_listeners.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/seams/introspection.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/seams/license_info.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/seams/runtime_checks.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/seams/tier_resolver.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_generator/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_generator/alter/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_generator/alter/base_alter_generator.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_generator/alter_generator.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_generator/base_generator.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_generator/basic_table_ddl_generator.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_generator/dependency_analyzer.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_generator/diff_converters/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_generator/diff_converters/base_converter.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_generator/formatter.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_generator/generator_factory.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_generator/options.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_generator/script_organizer.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_generator/sql_generator.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_generator/sql_statement.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_generator/table_ddl_render.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/_base_parse_result.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/_base_sql_column.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/_base_sql_constraint.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/_base_sql_object.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/_base_sql_statement.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/base.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/constraint_validator.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/database_link.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/dialect.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/event.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/extension.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/feature_gates.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/foreign_data_wrapper.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/foreign_server.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/index.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/linked_server.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/module.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/package.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/partition.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/procedure.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/sequence.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/server_info.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/synonym.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/table.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/table_canonicalizer.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/table_options.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/trigger.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/user_defined_type.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/view_options.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_parser/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_parser/_partition_handler.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_parser/_sqlglot_builders.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_parser/base_statement_parser.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_parser/base_tokenizer.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_parser/common/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_parser/common/base_parser.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_parser/dialects/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_parser/dialects/base_config.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_parser/enhanced_regex_parser.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_parser/hybrid_parser.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_parser/parser_context.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_parser/parser_factory.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_parser/parser_interface.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_parser/sqlglot_parser.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_parser/tokens.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_parser/unified_regex_parser.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_validator/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_validator/_checksum_validator.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_validator/_flyway_compatibility.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_validator/_migration_filter.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_validator/_sql_syntax_validator.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_validator/_strict_mode_validator.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/sql_validator/migration_validator.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/state/sql_script_formatter.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/state/sql_statement.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/utils/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/utils/database_url_parser.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/utils/metadata_helpers.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/utils/row_access.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/utils/string_utils.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/core/utils/url_masking.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/base_provider.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/constants.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/dml_analysis.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/error.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/error_handler.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/exceptions.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/feature_gate.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/native_connection_manager.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/object_naming.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/_pg_compatible.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/alloydb/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/alloydb/plugin.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/aurora_postgresql/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/aurora_postgresql/plugin.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/base_history_manager.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/base_locking_manager.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/base_query_executor.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/base_schema_operations.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/base_snapshot_manager.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/base_undo_manager.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/citus/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/citus/plugin.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/cockroachdb/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/cockroachdb/plugin.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/cockroachdb/provider.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/cockroachdb/quirks.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/cosmosdb/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/cosmosdb/config.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/cosmosdb/cosmosdb/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/cosmosdb/cosmosdb/_sdk.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/cosmosdb/cosmosdb/connection_manager.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/cosmosdb/cosmosdb/locking_manager.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/cosmosdb/cosmosdb/schema_operations.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/cosmosdb/introspection/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/cosmosdb/plugin.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/cosmosdb/quirks.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/db2/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/db2/config.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/db2/db2/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/db2/db2/history_manager.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/db2/db2/locking_manager.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/db2/db2/schema_operations.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/db2/introspection/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/db2/parser/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/db2/parser/db2_regex_parser.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/db2/parser/parser_config.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/db2/plugin.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/db2/provider.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/db2/sqlalchemy_url.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/duckdb/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/duckdb/config.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/duckdb/parser/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/duckdb/parser/parser_config.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/duckdb/plugin.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/duckdb/provider.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/duckdb/quirks.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/duckdb/sqlalchemy_url.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mariadb/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mariadb/plugin.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mariadb/provider.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mariadb/quirks.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mysql/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mysql/config.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mysql/introspection/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mysql/mysql/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mysql/mysql/history_manager.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mysql/mysql/locking_manager.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mysql/mysql/schema_operations.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mysql/parser/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mysql/parser/mysql_regex_parser.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mysql/parser/mysql_statement_parser.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mysql/parser/mysql_tokenizer.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mysql/parser/parser_config.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mysql/plugin.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mysql/provider.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mysql/quirks.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mysql/sqlalchemy_url.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/neon/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/neon/plugin.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/nosql_base/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/nosql_base/history.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/nosql_base/introspection.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/nosql_base/locking.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/config.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/introspection/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/introspection/oracle_utils.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/oracle/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/oracle/dbms_output.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/oracle/history_manager.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/oracle/locking_manager.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/oracle/schema_operations.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/parser/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/parser/_comments.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/parser/_object_extractor.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/parser/_plsql_block.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/parser/_sqlplus.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/parser/_statement_splitter.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/parser/oracle_parser.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/parser/oracle_statement_parser.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/parser/oracle_tokenizer.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/parser/parser_config.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/parser/sqlplus_context.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/plugin.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/provider.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/quirks.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/sqlalchemy_url.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/postgresql/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/postgresql/_provider_query_executor.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/postgresql/config.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/postgresql/introspection/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/postgresql/parser/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/postgresql/parser/parser_config.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/postgresql/parser/postgresql_regex_parser.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/postgresql/parser/postgresql_statement_parser.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/postgresql/parser/postgresql_tokenizer.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/postgresql/plugin.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/postgresql/postgresql/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/postgresql/postgresql/history_manager.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/postgresql/postgresql/locking_manager.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/postgresql/postgresql/schema_operations.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/postgresql/provider.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/postgresql/quirks.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/postgresql/sqlalchemy_url.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/redshift/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/redshift/plugin.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/redshift/provider.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/redshift/quirks.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/snowflake/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/snowflake/config.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/snowflake/plugin.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/snowflake/provider.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/snowflake/quirks.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/snowflake/sqlalchemy_url.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlite/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlite/config.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlite/introspection/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlite/parser/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlite/parser/parser_config.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlite/parser/sqlite_regex_parser.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlite/plugin.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlite/quirks.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlite/sqlalchemy_url.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlite/sqlite/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlite/sqlite/connection_manager.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlite/sqlite/history_manager.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlite/sqlite/locking_manager.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlite/sqlite/query_executor.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlite/sqlite/schema_operations.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlserver/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlserver/config.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlserver/introspection/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlserver/parser/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlserver/parser/parser_config.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlserver/parser/sqlserver_regex_parser.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlserver/parser/sqlserver_statement_parser.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlserver/parser/sqlserver_tokenizer.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlserver/parser/tsql_batch_separator.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlserver/plugin.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlserver/provider.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlserver/quirks.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlserver/sqlalchemy_url.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlserver/sqlserver/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlserver/sqlserver/history_manager.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlserver/sqlserver/locking_manager.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlserver/sqlserver/schema_operations.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/supabase/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/supabase/plugin.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/timescaledb/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/timescaledb/plugin.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/yugabytedb/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/plugins/yugabytedb/plugin.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/provider_capabilities.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/provider_interfaces.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/provider_registry.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/sqlalchemy_provider.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/db/value_utils.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/dblift.egg-info/SOURCES.txt +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/dblift.egg-info/dependency_links.txt +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/dblift.egg-info/entry_points.txt +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/dblift.egg-info/requires.txt +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/dblift.egg-info/top_level.txt +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/integrations/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/integrations/django/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/integrations/django/_client.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/integrations/django/_engine.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/integrations/django/apps.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/integrations/django/checks.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/integrations/django/management/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/integrations/django/management/commands/__init__.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/integrations/django/management/commands/dblift_info.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/integrations/django/management/commands/dblift_migrate.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/integrations/django/management/commands/dblift_validate.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/integrations/fastapi.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/integrations/flask.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/integrations/opentelemetry.py +0 -0
- {dblift-3.2.0 → dblift-3.3.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dblift
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.3.0
|
|
4
4
|
Summary: A database migration tool that supports multiple databases with native Python drivers
|
|
5
5
|
Classifier: Development Status :: 5 - Production/Stable
|
|
6
6
|
Classifier: Intended Audience :: Developers
|
|
@@ -16,7 +16,7 @@ The two helpers take the introspector instance as their first parameter
|
|
|
16
16
|
from __future__ import annotations
|
|
17
17
|
|
|
18
18
|
import traceback
|
|
19
|
-
from typing import TYPE_CHECKING, List
|
|
19
|
+
from typing import TYPE_CHECKING, Any, List
|
|
20
20
|
|
|
21
21
|
if TYPE_CHECKING:
|
|
22
22
|
from core.introspection.schema_introspector import SchemaIntrospector
|
|
@@ -253,18 +253,101 @@ def enrich_columns_with_identity(
|
|
|
253
253
|
),
|
|
254
254
|
}
|
|
255
255
|
|
|
256
|
+
# sql_variant columns (e.g. SQL Server's
|
|
257
|
+
# sys.identity_columns.seed_value/increment_value/last_value) can
|
|
258
|
+
# arrive via pymssql/FreeTDS as raw on-wire bytes rather than an int
|
|
259
|
+
# (e.g. the int 1 as b'\x01\x00\x00\x00'). Normalize those to a plain
|
|
260
|
+
# int here so every downstream consumer -- regardless of which
|
|
261
|
+
# dialect's driver quirks produced the raw value -- can rely on
|
|
262
|
+
# identity_seed/identity_increment/identity_last_value already being
|
|
263
|
+
# an int. Non-bytes values (str, int, None) pass through unchanged,
|
|
264
|
+
# exactly as before.
|
|
265
|
+
#
|
|
266
|
+
# Bytes are only decoded when their length matches a standard
|
|
267
|
+
# two's-complement integer width (1, 2, 4, or 8 bytes --
|
|
268
|
+
# tinyint/smallint/int/bigint). SQL Server also permits IDENTITY on
|
|
269
|
+
# decimal/numeric columns, whose sql_variant wire shape is a sign
|
|
270
|
+
# byte plus an *unsigned* magnitude -- not a plain two's-complement
|
|
271
|
+
# integer -- so decoding those the same way would silently produce a
|
|
272
|
+
# plausible-looking but wrong value. For any other byte length,
|
|
273
|
+
# refuse to guess and raise instead; the outer try/except around
|
|
274
|
+
# this whole enrichment pass logs a warning for it.
|
|
275
|
+
def _decode_sql_variant_int(column_name: str, field_name: str, value: Any) -> Any:
|
|
276
|
+
if not isinstance(value, bytes):
|
|
277
|
+
return value
|
|
278
|
+
if len(value) not in (1, 2, 4, 8):
|
|
279
|
+
raise ValueError(
|
|
280
|
+
f"Column {column_name!r}: {field_name} arrived as "
|
|
281
|
+
f"{len(value)} raw bytes, which is not a standard "
|
|
282
|
+
f"two's-complement integer width (1, 2, 4, or 8 bytes). "
|
|
283
|
+
f"This looks like a non-integer sql_variant encoding "
|
|
284
|
+
f"(e.g. decimal/numeric IDENTITY) that isn't supported yet."
|
|
285
|
+
)
|
|
286
|
+
return int.from_bytes(value, byteorder="little", signed=True)
|
|
287
|
+
|
|
256
288
|
# Enrich matching columns with detailed metadata
|
|
257
289
|
for column in columns:
|
|
258
290
|
identity_data = identity_map.get(column.name.upper())
|
|
259
291
|
if identity_data:
|
|
260
|
-
# Mark as identity (may already be marked from the column
|
|
292
|
+
# Mark as identity (may already be marked from the column
|
|
293
|
+
# query) for now -- the except block below reverts this if
|
|
294
|
+
# decoding seed OR increment fails, since a column whose real
|
|
295
|
+
# identity parameters we can't decode must not still claim
|
|
296
|
+
# is_identity (see that block for why). last_value is decoded
|
|
297
|
+
# separately below and its failure does NOT revert this: it
|
|
298
|
+
# has no downstream DDL consumer, so losing it carries none
|
|
299
|
+
# of the "silently wrong seed/increment" risk the reset here
|
|
300
|
+
# exists to guard against.
|
|
261
301
|
column.is_identity = True
|
|
262
|
-
#
|
|
263
|
-
column
|
|
264
|
-
column
|
|
265
|
-
#
|
|
266
|
-
|
|
267
|
-
|
|
302
|
+
# A decode failure (see _decode_sql_variant_int above) is
|
|
303
|
+
# isolated to THIS column, not the whole table: without this,
|
|
304
|
+
# one decimal/numeric-typed IDENTITY column sitting alongside
|
|
305
|
+
# ordinary int-typed ones would raise past this loop into the
|
|
306
|
+
# table-level except below, silently losing seed/increment/
|
|
307
|
+
# last_value for every column processed after it too -- a
|
|
308
|
+
# wider blast radius than the single bad column the raise is
|
|
309
|
+
# actually about.
|
|
310
|
+
try:
|
|
311
|
+
# Add detailed metadata
|
|
312
|
+
column.identity_seed = _decode_sql_variant_int(
|
|
313
|
+
column.name, "seed_value", identity_data["seed_value"]
|
|
314
|
+
)
|
|
315
|
+
column.identity_increment = _decode_sql_variant_int(
|
|
316
|
+
column.name, "increment_value", identity_data["increment_value"]
|
|
317
|
+
)
|
|
318
|
+
except ValueError as decode_error:
|
|
319
|
+
si.log.warning(
|
|
320
|
+
f"Could not decode identity metadata for column "
|
|
321
|
+
f"{column.name!r} in {schema}.{table}: {decode_error}"
|
|
322
|
+
)
|
|
323
|
+
# A decode failure means we cannot reliably tell "seed
|
|
324
|
+
# decoded fine, increment failed" apart from "failed
|
|
325
|
+
# immediately" -- either way, downstream DDL rendering
|
|
326
|
+
# must not mistake a leftover None for "never asked, use
|
|
327
|
+
# SQL Server's own default". Reset is_identity and the
|
|
328
|
+
# two fields render_identity_clause reads so the column
|
|
329
|
+
# falls out of identity handling entirely rather than
|
|
330
|
+
# rendering a guessed IDENTITY(1,1).
|
|
331
|
+
column.is_identity = False
|
|
332
|
+
column.identity_seed = None
|
|
333
|
+
column.identity_increment = None
|
|
334
|
+
else:
|
|
335
|
+
# seed/increment decoded fine -- only now attempt
|
|
336
|
+
# last_value, in its own try/except so that a
|
|
337
|
+
# last_value-only decode failure (e.g. a non-standard
|
|
338
|
+
# byte width) doesn't discard the good seed/increment we
|
|
339
|
+
# just stored. Note: last_value is not part of SQL Model,
|
|
340
|
+
# store as custom attribute if needed.
|
|
341
|
+
if identity_data["last_value"] is not None:
|
|
342
|
+
try:
|
|
343
|
+
column.identity_last_value = _decode_sql_variant_int( # type: ignore[attr-defined]
|
|
344
|
+
column.name, "last_value", identity_data["last_value"]
|
|
345
|
+
)
|
|
346
|
+
except ValueError as decode_error:
|
|
347
|
+
si.log.warning(
|
|
348
|
+
f"Could not decode identity metadata for column "
|
|
349
|
+
f"{column.name!r} in {schema}.{table}: {decode_error}"
|
|
350
|
+
)
|
|
268
351
|
|
|
269
352
|
if identity_map:
|
|
270
353
|
si.log.debug(
|
|
@@ -387,19 +387,21 @@ class BaseIntrospector:
|
|
|
387
387
|
# Delegate to index extractor
|
|
388
388
|
return self._get_index_extractor().get_indexes(schema, table)
|
|
389
389
|
|
|
390
|
-
def get_all_indexes(self, schema: str) -> List[Index]:
|
|
390
|
+
def get_all_indexes(self, schema: str) -> Optional[List[Index]]:
|
|
391
391
|
"""
|
|
392
392
|
Get all indexes for an entire schema in a single bulk query.
|
|
393
393
|
|
|
394
|
-
Delegates to IndexExtractor.get_all_indexes
|
|
395
|
-
|
|
394
|
+
Delegates to IndexExtractor.get_all_indexes and propagates its
|
|
395
|
+
three-way answer unchanged.
|
|
396
396
|
|
|
397
397
|
Args:
|
|
398
398
|
schema: Schema name
|
|
399
399
|
|
|
400
400
|
Returns:
|
|
401
|
-
|
|
402
|
-
|
|
401
|
+
``None`` if the dialect cannot answer in bulk, meaning the caller
|
|
402
|
+
must fall back to per-table retrieval. ``[]`` if the bulk query ran
|
|
403
|
+
and the schema genuinely has no indexes. Otherwise the list of
|
|
404
|
+
Index objects for the entire schema.
|
|
403
405
|
"""
|
|
404
406
|
return self._get_index_extractor().get_all_indexes(schema)
|
|
405
407
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Index extraction from plugin-owned vendor metadata queries."""
|
|
2
2
|
|
|
3
|
-
from typing import Any, Dict, List
|
|
3
|
+
from typing import Any, Dict, List, Optional
|
|
4
4
|
|
|
5
5
|
from core.introspection.extractors.base_extractor import BaseExtractor
|
|
6
6
|
from core.sql_model.index import Index
|
|
@@ -42,7 +42,7 @@ class IndexExtractor(BaseExtractor):
|
|
|
42
42
|
# Convert to Index objects
|
|
43
43
|
return self._build_index_objects(schema, table, indexes_data)
|
|
44
44
|
|
|
45
|
-
def get_all_indexes(self, schema: str) -> List[Index]:
|
|
45
|
+
def get_all_indexes(self, schema: str) -> Optional[List[Index]]:
|
|
46
46
|
"""
|
|
47
47
|
Get all indexes for an entire schema.
|
|
48
48
|
|
|
@@ -52,17 +52,25 @@ class IndexExtractor(BaseExtractor):
|
|
|
52
52
|
schema: Schema name
|
|
53
53
|
|
|
54
54
|
Returns:
|
|
55
|
-
|
|
55
|
+
``None`` if bulk retrieval is unavailable — either no vendor
|
|
56
|
+
queries exist for the dialect, or its bulk index query declines —
|
|
57
|
+
meaning the caller must ask table by table. ``[]`` if the bulk
|
|
58
|
+
query ran and the schema genuinely has no indexes. Otherwise the
|
|
59
|
+
list of Index objects for the entire schema.
|
|
56
60
|
"""
|
|
61
|
+
# Unlike ColumnExtractor and TableExtractor.get_tables, which raise
|
|
62
|
+
# RuntimeError when their query is unavailable, this refuses with
|
|
63
|
+
# None: those two have no fallback, so refusing is the only honest
|
|
64
|
+
# option, whereas a working per-table path exists here and None routes
|
|
65
|
+
# the caller to it. Raising would break callers that legitimately
|
|
66
|
+
# have that fallback.
|
|
57
67
|
if not self.vendor_queries:
|
|
58
|
-
return
|
|
68
|
+
return None
|
|
59
69
|
|
|
60
70
|
self.ensure_metadata()
|
|
61
71
|
query_result = self.vendor_queries.get_all_indexes_query(schema)
|
|
62
72
|
if query_result is None:
|
|
63
|
-
return
|
|
64
|
-
[]
|
|
65
|
-
) # no bulk query; empty for this test path (per-table fallback would be used by callers)
|
|
73
|
+
return None
|
|
66
74
|
|
|
67
75
|
query, params = query_result
|
|
68
76
|
rows = self.provider.query_executor.execute_query(self.connection, query, params)
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"""Load ``dblift.features`` entry points (tier feature registration).
|
|
2
|
+
|
|
3
|
+
Each ``dblift.features`` entry point resolves to a no-arg callable that
|
|
4
|
+
performs registrations against the neutral seams
|
|
5
|
+
(:mod:`core.seams.runtime_checks`, :mod:`core.seams.tier_resolver`, ...).
|
|
6
|
+
OSS ships none; installed higher tiers register their runtime checks and
|
|
7
|
+
tier resolver here.
|
|
8
|
+
|
|
9
|
+
Loading is idempotent via a module-level once-flag: entry points do not
|
|
10
|
+
change at runtime, and both CLI startup and API-client construction invoke
|
|
11
|
+
:func:`load_feature_extensions`, so double-loading is expected and cheap. An
|
|
12
|
+
empty entry-point result latches immediately unless a known tier
|
|
13
|
+
distribution (``dblift-pro``/``dblift-enterprise``) is installed but its
|
|
14
|
+
entry point hasn't reached this process's metadata view yet, in which case
|
|
15
|
+
it is retried on a later call instead of latched.
|
|
16
|
+
|
|
17
|
+
``DBLIFT_DISABLE_CLI_EXTENSIONS=1`` skips feature loading entirely. This is
|
|
18
|
+
an accepted, explicit operator opt-out: with higher-tier packages installed
|
|
19
|
+
it also disables their registered runtime checks and tier resolution for
|
|
20
|
+
API usage in the same process.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import logging
|
|
26
|
+
import os
|
|
27
|
+
from collections.abc import Callable
|
|
28
|
+
from importlib.metadata import PackageNotFoundError, entry_points, version
|
|
29
|
+
from typing import Protocol
|
|
30
|
+
|
|
31
|
+
_log = logging.getLogger(__name__)
|
|
32
|
+
|
|
33
|
+
FEATURE_ENTRY_POINT_GROUP = "dblift.features"
|
|
34
|
+
|
|
35
|
+
# Feature registrations load in tier order (pro before enterprise, unknown
|
|
36
|
+
# third parties in between, name as the tie-breaker) — the same ordering
|
|
37
|
+
# contract as cli/extensions.py's command-extension loaders.
|
|
38
|
+
_TIER_LOAD_ORDER = {"pro": 0, "enterprise": 2}
|
|
39
|
+
_DEFAULT_TIER_LOAD_ORDER = 1
|
|
40
|
+
|
|
41
|
+
_features_loaded = False
|
|
42
|
+
|
|
43
|
+
# Must be updated if a tier package's distribution name changes in the
|
|
44
|
+
# monorepo, or a new tier is added under a different name -- an unlisted
|
|
45
|
+
# distribution is indistinguishable here from "not installed", so
|
|
46
|
+
# _any_tier_distribution_installed silently reverts to the pre-fix
|
|
47
|
+
# never-retries behavior for it.
|
|
48
|
+
_KNOWN_TIER_DISTRIBUTIONS = ("dblift-pro", "dblift-enterprise")
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _any_tier_distribution_installed() -> bool:
|
|
52
|
+
"""Whether a known paid-tier package is installed as a distribution.
|
|
53
|
+
|
|
54
|
+
Checked independently of whether its ``dblift.features`` entry point has
|
|
55
|
+
become visible yet in this process's ``importlib.metadata`` view -- this
|
|
56
|
+
is what distinguishes "no dblift.features entry point will ever exist"
|
|
57
|
+
(pure OSS, no tier package installed at all -- the ordinary case) from
|
|
58
|
+
"one exists but hasn't reached this process's view of the metadata yet"
|
|
59
|
+
(the documented race an empty ``entry_points()`` result alone can't
|
|
60
|
+
rule out).
|
|
61
|
+
"""
|
|
62
|
+
for name in _KNOWN_TIER_DISTRIBUTIONS:
|
|
63
|
+
try:
|
|
64
|
+
version(name)
|
|
65
|
+
except PackageNotFoundError:
|
|
66
|
+
continue
|
|
67
|
+
except Exception as exc: # a broken distribution must not break startup
|
|
68
|
+
_log.warning("Could not check whether %r is installed: %s", name, exc)
|
|
69
|
+
continue
|
|
70
|
+
return True
|
|
71
|
+
return False
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class _FeatureEntryPoint(Protocol):
|
|
75
|
+
name: str
|
|
76
|
+
|
|
77
|
+
def load(self) -> Callable[[], object]:
|
|
78
|
+
pass
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def load_feature_extensions() -> None:
|
|
82
|
+
"""Load every ``dblift.features`` entry point (idempotent, best-effort)."""
|
|
83
|
+
global _features_loaded
|
|
84
|
+
if _features_loaded or os.environ.get("DBLIFT_DISABLE_CLI_EXTENSIONS") == "1":
|
|
85
|
+
return
|
|
86
|
+
discovered_by_name: dict[str, _FeatureEntryPoint] = {
|
|
87
|
+
entry_point.name: entry_point
|
|
88
|
+
for entry_point in entry_points(group=FEATURE_ENTRY_POINT_GROUP)
|
|
89
|
+
}
|
|
90
|
+
ordered = sorted(
|
|
91
|
+
discovered_by_name.values(),
|
|
92
|
+
key=lambda ep: (_TIER_LOAD_ORDER.get(ep.name, _DEFAULT_TIER_LOAD_ORDER), ep.name),
|
|
93
|
+
)
|
|
94
|
+
for entry_point in ordered:
|
|
95
|
+
try:
|
|
96
|
+
register = entry_point.load()
|
|
97
|
+
register()
|
|
98
|
+
except Exception as exc: # a bad plugin must not break startup
|
|
99
|
+
_log.warning("dblift.features '%s' failed to load: %s", entry_point.name, exc)
|
|
100
|
+
# Latch when something was actually found, OR when nothing was found but
|
|
101
|
+
# no tier package is installed at all -- an empty result is only the
|
|
102
|
+
# documented race (a paid package's dblift.features entry point not yet
|
|
103
|
+
# reaching this process's importlib.metadata view, needing a retry on a
|
|
104
|
+
# later call, exactly like the identical latch bug fixed in
|
|
105
|
+
# AlterGeneratorFactory._ensure_populated) when a tier distribution is
|
|
106
|
+
# actually installed. In pure OSS, with no tier package installed,
|
|
107
|
+
# OSS's own pyproject.toml declares the entry-points group empty, so an
|
|
108
|
+
# empty result is permanent and must latch immediately.
|
|
109
|
+
_features_loaded = bool(ordered) or not _any_tier_distribution_installed()
|
|
@@ -35,7 +35,7 @@ def attach_registered_sql_generators() -> None:
|
|
|
35
35
|
def clear_sql_generator_registrars() -> None:
|
|
36
36
|
"""Test hook."""
|
|
37
37
|
global _bootstrapped
|
|
38
|
-
|
|
38
|
+
from core.seams import feature_loading
|
|
39
39
|
|
|
40
40
|
_registrars.clear()
|
|
41
41
|
_bootstrapped = False
|
|
@@ -46,7 +46,8 @@ class AlterGeneratorFactory:
|
|
|
46
46
|
load_feature_extensions()
|
|
47
47
|
attach_registered_sql_generators()
|
|
48
48
|
ProviderRegistry.discover_plugins()
|
|
49
|
-
|
|
49
|
+
plugin_list = ProviderRegistry.list_plugins()
|
|
50
|
+
for plugin_info in plugin_list:
|
|
50
51
|
# Per-plugin fault isolation: a broken plugin must not
|
|
51
52
|
# prevent the others from registering. (PR #241 Bugbot.)
|
|
52
53
|
try:
|
|
@@ -62,7 +63,14 @@ class AlterGeneratorFactory:
|
|
|
62
63
|
plugin_info.name,
|
|
63
64
|
exc,
|
|
64
65
|
)
|
|
65
|
-
|
|
66
|
+
# Latch only when plugin discovery itself found something. An empty
|
|
67
|
+
# ``plugin_list`` is the documented race (entry points not yet wired
|
|
68
|
+
# up in this process) and must be retried on a later call. A
|
|
69
|
+
# non-empty ``plugin_list`` means discovery genuinely completed --
|
|
70
|
+
# even if every plugin's quirks raised or returned no ALTER
|
|
71
|
+
# generator class, that is a real, stable outcome and should latch
|
|
72
|
+
# rather than re-running full discovery on every subsequent call.
|
|
73
|
+
cls._populated = bool(plugin_list)
|
|
66
74
|
|
|
67
75
|
@classmethod
|
|
68
76
|
def create_generator(cls, dialect: str) -> BaseAlterGenerator:
|
|
@@ -435,9 +435,15 @@ class BaseQuirks:
|
|
|
435
435
|
#: Oracle requires ``FROM DUAL``.
|
|
436
436
|
connection_probe_sql: str = "SELECT 1"
|
|
437
437
|
#: ``SELECT … LIMIT N`` syntax is supported. PostgreSQL, MySQL and
|
|
438
|
-
#: SQLite accept ``LIMIT``; Oracle (``FETCH FIRST N ROWS ONLY``)
|
|
439
|
-
#:
|
|
440
|
-
#:
|
|
438
|
+
#: SQLite accept ``LIMIT``; Oracle (``FETCH FIRST N ROWS ONLY``) does
|
|
439
|
+
#: not; SQL Server uses ``TOP N`` instead. DB2's *preferred* rendering
|
|
440
|
+
#: is also ``FETCH FIRST N ROWS ONLY`` (see :attr:`row_limit_style`),
|
|
441
|
+
#: but DB2 additionally accepts a bare trailing ``LIMIT`` clause —
|
|
442
|
+
#: verified against a live db2 12.01.0500 server via the
|
|
443
|
+
#: capability-probe integration test
|
|
444
|
+
#: (``tests/integration/capabilities/test_engine_capabilities.py``) —
|
|
445
|
+
#: so it sets this True even though its declared ``row_limit_style``
|
|
446
|
+
#: is not ``"limit"``. When False, post-commit verification queries
|
|
441
447
|
#: and similar probes omit the ``LIMIT`` clause.
|
|
442
448
|
select_supports_limit: bool = True
|
|
443
449
|
#: How a SELECT is bounded to N rows. One of:
|
|
@@ -461,12 +467,16 @@ class BaseQuirks:
|
|
|
461
467
|
#: Read through :meth:`row_limit_clauses`, which renders the triple
|
|
462
468
|
#: rather than making each caller re-derive the syntax. Distinct from
|
|
463
469
|
#: :attr:`select_supports_limit`, which answers the coarser "may I
|
|
464
|
-
#: append ``LIMIT`` at all" question for optional probes —
|
|
465
|
-
#:
|
|
466
|
-
#: ``row_limit_style
|
|
467
|
-
#:
|
|
468
|
-
#:
|
|
469
|
-
#:
|
|
470
|
+
#: append ``LIMIT`` at all" question for optional probes — the two
|
|
471
|
+
#: usually correlate (SQL Server and Oracle, which set a non-default
|
|
472
|
+
#: ``row_limit_style``, also set ``select_supports_limit = False``,
|
|
473
|
+
#: since neither accepts a bare trailing ``LIMIT``) but they are NOT
|
|
474
|
+
#: guaranteed to: DB2 is a documented exception — it declares
|
|
475
|
+
#: ``row_limit_style = "fetch_first"`` as its preferred rendering yet
|
|
476
|
+
#: sets ``select_supports_limit = True`` because the live engine also
|
|
477
|
+
#: accepts a bare trailing ``LIMIT`` (verified via the capability-probe
|
|
478
|
+
#: integration test). Nothing in the framework enforces that the two
|
|
479
|
+
#: stay consistent, so a new dialect could set one without the other;
|
|
470
480
|
#: see the invariant test in ``tests/unit/db/test_dialect_capability_quirks.py``.
|
|
471
481
|
row_limit_style: str = "limit"
|
|
472
482
|
#: How an "insert or update on primary-key conflict" is expressed.
|
|
@@ -419,7 +419,7 @@ class CosmosDbHistoryManager(DocumentHistoryManager):
|
|
|
419
419
|
# row: a point delete addressed by the wrong partition key returns 404,
|
|
420
420
|
# which would read as "already deleted" and remove nothing.
|
|
421
421
|
query = "SELECT c.id, c.script, c.success, c.version FROM c WHERE c.script = @script"
|
|
422
|
-
parameters = [{"name": "@script", "value": script_name}]
|
|
422
|
+
parameters: List[Dict[str, object]] = [{"name": "@script", "value": script_name}]
|
|
423
423
|
try:
|
|
424
424
|
failed_docs = [
|
|
425
425
|
doc
|
|
@@ -3,12 +3,15 @@ Cosmos DB query execution using the SQL API.
|
|
|
3
3
|
|
|
4
4
|
The Cosmos DB SQL API is a read-only query language: this module runs
|
|
5
5
|
``SELECT`` against containers. Everything that changes state — containers,
|
|
6
|
-
documents, throughput, indexing policy, TTL — is an Azure SDK call
|
|
7
|
-
|
|
6
|
+
documents, throughput, indexing policy, TTL — is an Azure SDK call, not SQL
|
|
7
|
+
routed through a translator. That SDK call is made either from a
|
|
8
|
+
user-authored Python migration (via ``context.db`` / ``context.raw_client``)
|
|
9
|
+
or, for internal callers that are not a migration, through a native escape
|
|
10
|
+
hatch exposed here (e.g. ``upsert_native_item``).
|
|
8
11
|
"""
|
|
9
12
|
|
|
10
13
|
import re
|
|
11
|
-
from typing import Any, Dict, List, Optional
|
|
14
|
+
from typing import Any, Dict, List, Optional, cast
|
|
12
15
|
|
|
13
16
|
from core.exceptions import NoSqlWriteNotSupportedError
|
|
14
17
|
from core.logger import Log, NullLog
|
|
@@ -63,8 +66,11 @@ class CosmosDbQueryExecutor(BaseQueryExecutor):
|
|
|
63
66
|
The Cosmos DB SQL API reads; it does not write. Containers and
|
|
64
67
|
documents are changed through the Azure SDK, so a write statement
|
|
65
68
|
reaching here is a mistake to surface rather than something to
|
|
66
|
-
translate. Python migrations get to the SDK through
|
|
67
|
-
``context.db`` / ``context.raw_client
|
|
69
|
+
translate. User-authored Python migrations get to the SDK through
|
|
70
|
+
``context.db`` / ``context.raw_client``; internal, non-migration
|
|
71
|
+
callers that need to write a single document use
|
|
72
|
+
:meth:`upsert_native_item` instead of building SQL for this method
|
|
73
|
+
to reject.
|
|
68
74
|
|
|
69
75
|
Returns the number of rows a read produced (``0`` for the scalar
|
|
70
76
|
liveness probe).
|
|
@@ -168,6 +174,51 @@ class CosmosDbQueryExecutor(BaseQueryExecutor):
|
|
|
168
174
|
self.log.error(error_msg)
|
|
169
175
|
raise
|
|
170
176
|
|
|
177
|
+
def upsert_native_item(self, container_name: str, document: Dict[str, Any]) -> Dict[str, Any]:
|
|
178
|
+
"""Upsert a document directly through the Azure SDK, bypassing SQL.
|
|
179
|
+
|
|
180
|
+
``execute_statement`` only accepts ``SELECT`` — the Cosmos DB SQL API
|
|
181
|
+
is read-only, and pretending otherwise (the pseudo-SQL emulator this
|
|
182
|
+
module used to have) was actively wrong, which is why the ADR
|
|
183
|
+
removing pseudo-SQL deleted it. Container *creation* was ported to a
|
|
184
|
+
native SDK call at the time (see
|
|
185
|
+
``CosmosDbProvider.create_snapshot_table_if_not_exists``, which calls
|
|
186
|
+
``CosmosDbSchemaOperations.create_container_if_not_exists`` instead of
|
|
187
|
+
rendering ``CREATE TABLE``), but the matching write for a single
|
|
188
|
+
document was missed: an internal caller such as the schema-snapshot
|
|
189
|
+
repository still built a plain SQL ``INSERT`` and routed it through
|
|
190
|
+
``execute_statement``, which now raises
|
|
191
|
+
``NoSqlWriteNotSupportedError`` for anything but a ``SELECT``. That
|
|
192
|
+
exception is swallowed by a best-effort event listener, so
|
|
193
|
+
``migrate`` reports success while the snapshot document silently
|
|
194
|
+
never lands.
|
|
195
|
+
|
|
196
|
+
This method provides the native, non-SQL escape hatch a caller needs
|
|
197
|
+
to close that gap: it calls ``azure.cosmos.ContainerProxy.upsert_item``
|
|
198
|
+
directly instead of rendering anything this executor — or its
|
|
199
|
+
SELECT-only ``execute_statement`` — would have to parse as SQL. It is
|
|
200
|
+
meant for internal dblift callers that hold a provider/query-executor
|
|
201
|
+
reference and are not a user-authored migration; migrations already
|
|
202
|
+
reach the SDK through ``context.db`` / ``context.raw_client`` (see
|
|
203
|
+
``execute_statement``'s docstring). This change adds the primitive
|
|
204
|
+
only — the schema-snapshot repository still needs to be updated, in
|
|
205
|
+
the sibling monorepo where it lives, to call this instead of
|
|
206
|
+
building an ``INSERT``; until that happens the gap above is not yet
|
|
207
|
+
closed end-to-end.
|
|
208
|
+
|
|
209
|
+
Args:
|
|
210
|
+
container_name: Name of the Cosmos DB container to upsert into.
|
|
211
|
+
document: The full document body to upsert. Must include
|
|
212
|
+
whatever field the container's partition key path points at.
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
The document as returned by the SDK (typically the input body
|
|
216
|
+
plus Cosmos-assigned system properties such as ``_etag``,
|
|
217
|
+
``_ts``, ``_self``).
|
|
218
|
+
"""
|
|
219
|
+
container_client = self.connection_manager.get_container_client(container_name)
|
|
220
|
+
return cast(Dict[str, Any], container_client.upsert_item(body=document))
|
|
221
|
+
|
|
171
222
|
def _normalize_cosmos_sql(self, sql: str, container_name: str) -> str:
|
|
172
223
|
"""Normalize Cosmos DB SQL query to use proper container alias.
|
|
173
224
|
|
|
@@ -75,6 +75,28 @@ class CosmosDbProvider(NativeProvider):
|
|
|
75
75
|
connection = self._get_connection_or_raise()
|
|
76
76
|
return self.query_executor.execute_query(connection, sql, params)
|
|
77
77
|
|
|
78
|
+
def upsert_native_item(self, container_name: str, document: Dict[str, Any]) -> Dict[str, Any]:
|
|
79
|
+
"""Upsert a document straight through the Azure SDK, bypassing SQL.
|
|
80
|
+
|
|
81
|
+
Thin forward to :meth:`CosmosDbQueryExecutor.upsert_native_item` —
|
|
82
|
+
the same shape as :meth:`execute_statement` and :meth:`execute_query`
|
|
83
|
+
above, which forward to the query executor's SQL-shaped methods. This
|
|
84
|
+
one exists for callers (e.g. the schema-snapshot repository) that
|
|
85
|
+
need to write a single document and hold a provider reference rather
|
|
86
|
+
than a query-executor one; see
|
|
87
|
+
:meth:`CosmosDbQueryExecutor.upsert_native_item` for why a native
|
|
88
|
+
write path exists instead of routing an ``INSERT`` through
|
|
89
|
+
``execute_statement``.
|
|
90
|
+
|
|
91
|
+
No connection guard here (unlike ``execute_statement`` /
|
|
92
|
+
``execute_query``): the query executor reaches the container through
|
|
93
|
+
``connection_manager.get_container_client`` rather than the
|
|
94
|
+
``connection`` object, the same reason
|
|
95
|
+
``create_snapshot_table_if_not_exists`` below calls into
|
|
96
|
+
``schema_operations`` directly without fetching one.
|
|
97
|
+
"""
|
|
98
|
+
return self.query_executor.upsert_native_item(container_name, document)
|
|
99
|
+
|
|
78
100
|
def create_schema_if_not_exists(self, schema: str) -> None:
|
|
79
101
|
"""Create schema if it doesn't exist (Cosmos DB doesn't have schemas)."""
|
|
80
102
|
connection = self._get_connection_or_raise()
|
|
@@ -72,8 +72,10 @@ class Db2Quirks(BaseQuirks):
|
|
|
72
72
|
return self.is_data_history_table_already_exists_error(error_message)
|
|
73
73
|
|
|
74
74
|
connection_probe_sql = "SELECT 1 FROM SYSIBM.SYSDUMMY1"
|
|
75
|
-
select_supports_limit =
|
|
76
|
-
#
|
|
75
|
+
select_supports_limit = True
|
|
76
|
+
# Canonical rendering is trailing ``FETCH FIRST n ROWS ONLY``, but DB2
|
|
77
|
+
# also accepts a bare trailing ``LIMIT n`` (verified against a live
|
|
78
|
+
# db2 12.01.0500 server via the capability-probe integration test).
|
|
77
79
|
row_limit_style = "fetch_first"
|
|
78
80
|
unquoted_identifier_case = "uppercase"
|
|
79
81
|
connection_identifier_attrs = ("url", "host", "database")
|
|
@@ -5,7 +5,7 @@ so statement splitting only needs to respect string literals and comments
|
|
|
5
5
|
around the ``;`` separator.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
from typing import
|
|
8
|
+
from typing import Dict, List, Optional
|
|
9
9
|
|
|
10
10
|
from core.sql_model.base import SqlObject, SqlObjectType
|
|
11
11
|
from core.sql_parser.enhanced_regex_parser import EnhancedRegexParser
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""SQLAlchemy URL construction for the Redshift plugin."""
|
|
2
2
|
|
|
3
|
-
from typing import Any, Dict
|
|
3
|
+
from typing import Any, Dict
|
|
4
4
|
|
|
5
5
|
from sqlalchemy.engine import URL, make_url
|
|
6
6
|
|
|
@@ -33,18 +33,15 @@ def build_sqlalchemy_url(database_config: Any) -> str:
|
|
|
33
33
|
query = _query_mapping(database_config, url.query)
|
|
34
34
|
if query != dict(url.query):
|
|
35
35
|
url = url.set(query=query)
|
|
36
|
-
return
|
|
36
|
+
return url.render_as_string(hide_password=False)
|
|
37
37
|
raise ValueError("Redshift native connections require a SQLAlchemy URL")
|
|
38
38
|
|
|
39
|
-
return
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
query=_query_mapping(database_config),
|
|
49
|
-
).render_as_string(hide_password=False),
|
|
50
|
-
)
|
|
39
|
+
return URL.create(
|
|
40
|
+
"redshift+redshift_connector",
|
|
41
|
+
username=getattr(database_config, "username", None) or None,
|
|
42
|
+
password=getattr(database_config, "password", None) or None,
|
|
43
|
+
host=getattr(database_config, "host", None) or "localhost",
|
|
44
|
+
port=getattr(database_config, "port", None),
|
|
45
|
+
database=getattr(database_config, "database", None),
|
|
46
|
+
query=_query_mapping(database_config),
|
|
47
|
+
).render_as_string(hide_password=False)
|
|
@@ -432,17 +432,6 @@ class SQLiteProvider(NativeProvider):
|
|
|
432
432
|
connection, schema, create_schema, table_name
|
|
433
433
|
)
|
|
434
434
|
|
|
435
|
-
def supports_snapshots(self) -> bool:
|
|
436
|
-
"""SQLite does not own schema snapshot persistence."""
|
|
437
|
-
return False
|
|
438
|
-
|
|
439
|
-
def create_snapshot_table_if_not_exists(
|
|
440
|
-
self, schema: str, table_name: str = "dblift_schema_snapshots"
|
|
441
|
-
) -> None:
|
|
442
|
-
"""Satisfy BaseProvider while leaving snapshot storage unsupported."""
|
|
443
|
-
_ = (schema, table_name)
|
|
444
|
-
raise NotImplementedError("SQLite provider does not support schema snapshot persistence")
|
|
445
|
-
|
|
446
435
|
def is_connected(self) -> bool:
|
|
447
436
|
"""Return True when an active sqlite3 connection is held.
|
|
448
437
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dblift
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.3.0
|
|
4
4
|
Summary: A database migration tool that supports multiple databases with native Python drivers
|
|
5
5
|
Classifier: Development Status :: 5 - Production/Stable
|
|
6
6
|
Classifier: Intended Audience :: Developers
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "dblift"
|
|
7
|
-
version = "3.
|
|
7
|
+
version = "3.3.0"
|
|
8
8
|
description = "A database migration tool that supports multiple databases with native Python drivers"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|