dblift 4.0.2__tar.gz → 4.2.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-4.0.2 → dblift-4.2.0}/PKG-INFO +3 -1
- {dblift-4.0.2 → dblift-4.2.0}/dblift/__init__.py +1 -1
- {dblift-4.0.2 → dblift-4.2.0}/dblift/cli/_command_handlers.py +2 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/cli/_parser_setup.py +14 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/cli/handlers/_shared.py +87 -1
- dblift-4.2.0/dblift/cli/handlers/info.py +56 -0
- dblift-4.2.0/dblift/cli/handlers/mcp.py +36 -0
- dblift-4.2.0/dblift/cli/handlers/migrate.py +72 -0
- dblift-4.2.0/dblift/cli/handlers/validate.py +47 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/cli/main.py +2 -0
- dblift-4.2.0/dblift/cli/mcp/__init__.py +1 -0
- dblift-4.2.0/dblift/cli/mcp/registry.py +34 -0
- dblift-4.2.0/dblift/cli/mcp/runner.py +214 -0
- dblift-4.2.0/dblift/cli/mcp/server.py +257 -0
- dblift-4.2.0/dblift/cli/mcp/tools.py +105 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/logger/results.py +5 -2
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/commands/migrate_command.py +13 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift.egg-info/PKG-INFO +3 -1
- {dblift-4.0.2 → dblift-4.2.0}/dblift.egg-info/SOURCES.txt +6 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift.egg-info/requires.txt +3 -0
- {dblift-4.0.2 → dblift-4.2.0}/pyproject.toml +25 -1
- dblift-4.0.2/dblift/cli/handlers/info.py +0 -151
- dblift-4.0.2/dblift/cli/handlers/migrate.py +0 -47
- dblift-4.0.2/dblift/cli/handlers/validate.py +0 -29
- {dblift-4.0.2 → dblift-4.2.0}/LICENSE +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/README.md +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/api/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/api/_cli_support.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/api/_client_factory.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/api/_client_operations.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/api/_engine_config.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/api/async_client.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/api/client.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/api/events.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/api/migrations.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/api/py.typed +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/cli/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/cli/_config_helpers.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/cli/_constants.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/cli/_output.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/cli/commands/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/cli/commands/config_command.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/cli/db_utils.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/cli/extensions.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/cli/handlers/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/cli/handlers/baseline.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/cli/handlers/clean.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/cli/handlers/import_flyway.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/cli/handlers/repair.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/cli/handlers/undo.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/cli/premium_manifest.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/config/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/config/_credential_masking.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/config/_subclasses/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/config/_subclasses/dummy_config.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/config/_url_builder_mixin.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/config/config_builder.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/config/database_config.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/config/dblift_config.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/config/errors.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/config/property_registry.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/config/secrets/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/config/secrets/_cache.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/config/secrets/_provider_base.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/config/secrets/_registry.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/config/secrets/_resolver.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/config/secrets/_secrets_config.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/constants.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/dialect_boundary.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/exceptions.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/introspection/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/introspection/_column_enricher.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/introspection/_partition_enricher.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/introspection/_schema_orchestrator.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/introspection/_utils.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/introspection/_vendor_property_applier.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/introspection/base_introspector.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/introspection/extractors/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/introspection/extractors/base_extractor.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/introspection/extractors/column_extractor.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/introspection/extractors/constraint_extractor.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/introspection/extractors/index_extractor.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/introspection/extractors/misc_extractor.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/introspection/extractors/procedure_extractor.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/introspection/extractors/sequence_extractor.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/introspection/extractors/table_extractor.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/introspection/extractors/trigger_extractor.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/introspection/extractors/view_extractor.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/introspection/introspector_factory.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/introspection/result.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/introspection/schema_introspector.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/introspection/vendor_queries_base.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/introspection/vendor_queries_factory.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/introspection/vendor_queries_protocols.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/introspection/version_detector.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/logger/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/logger/_base.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/logger/_factory.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/logger/_formatters.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/logger/_levels.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/logger/_multi.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/logger/_null.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/logger/console.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/logger/formatters/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/logger/formatters/_formatter_impl.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/logger/formatters/factory.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/logger/formatters/formatter.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/logger/formatters/htmlformatter.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/logger/formatters/jsonformatter.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/logger/log.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/logger/templates/oldreport.html +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/logger/templates/report.html +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/_type_match.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/clean_summary.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/commands/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/commands/_script_events.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/commands/base_command.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/commands/baseline_command.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/commands/clean_command.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/commands/import_flyway_command.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/commands/info_command.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/commands/repair_command.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/commands/undo_command.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/commands/validate_command.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/encoding.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/executor/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/executor/execution_engine.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/executor/migration_executor.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/executor/migration_helpers.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/executor/placeholder_manager.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/executor/transaction_policy.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/executors/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/executors/base_executor.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/executors/executor_factory.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/executors/python_executor.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/formats/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/formats/format_detector.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/formats/migration_format.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/history/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/history/migration_history_manager.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/journals/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/journals/migration_journal.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/migration.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/placeholders/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/placeholders/placeholder_service.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/rules/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/rules/migration_rules.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/scripting/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/scripting/migration_script_manager.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/scripting/undo_script_generator/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/scripting/undo_script_generator/_extractors.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/scripting/undo_script_generator/_generator.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/scripting/undo_script_generator/_helpers.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/scripting/undo_script_generator/_models.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/scripting/undo_script_generator/_reversers.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/sql/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/sql/execution_statement.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/sql/sql_analyzer.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/sql/sql_execution_service.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/sql/statement_splitter.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/state/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/state/migration_data_service.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/state/migration_display_state.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/state/migration_state.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/state/migration_state_manager.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/state/migration_state_service.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/state/rank_wins.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/ui/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/ui/data_collector.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/ui/migration_ui.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/ui/table_renderer.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/migration/version_utils.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/normalization/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/normalization/type_constants.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/normalization/type_normalizer.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/premium_manifest.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/seams/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/seams/capabilities.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/seams/client_factory.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/seams/event_listeners.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/seams/feature_loading.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/seams/introspection.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/seams/license_info.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/seams/quirks.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/seams/runtime_checks.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/seams/sql_generators.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/seams/tier_resolver.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_generator/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_generator/alter/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_generator/alter/alter_generator_factory.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_generator/alter/base_alter_generator.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_generator/base_generator.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_generator/basic_table_ddl_generator.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_generator/dependency_analyzer.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_generator/diff_converters/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_generator/diff_converters/base_converter.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_generator/formatter.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_generator/generator_factory.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_generator/options.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_generator/script_organizer.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_generator/sql_generator.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_generator/sql_statement.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_generator/table_ddl_render.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_model/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_model/_base_parse_result.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_model/_base_sql_column.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_model/_base_sql_constraint.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_model/_base_sql_object.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_model/_base_sql_statement.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_model/base.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_model/constraint_validator.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_model/database_link.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_model/dialect.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_model/event.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_model/extension.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_model/feature_gates.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_model/foreign_data_wrapper.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_model/foreign_server.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_model/index.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_model/linked_server.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_model/module.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_model/package.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_model/partition.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_model/procedure.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_model/sequence.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_model/server_info.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_model/synonym.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_model/table.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_model/table_canonicalizer.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_model/table_options.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_model/trigger.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_model/user_defined_type.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_model/view.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_model/view_options.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_parser/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_parser/_partition_handler.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_parser/_sqlglot_builders.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_parser/base_statement_parser.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_parser/base_tokenizer.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_parser/common/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_parser/common/base_parser.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_parser/dialects/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_parser/dialects/base_config.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_parser/enhanced_regex_parser.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_parser/hybrid_parser.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_parser/parser_context.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_parser/parser_factory.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_parser/parser_interface.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_parser/sqlglot_parser.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_parser/tokens.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_parser/unified_regex_parser.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_validator/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_validator/_checksum_validator.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_validator/_flyway_compatibility.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_validator/_migration_filter.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_validator/_sql_syntax_validator.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_validator/_strict_mode_validator.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/sql_validator/migration_validator.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/state/sql_statement.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/utils/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/utils/database_url_parser.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/utils/metadata_helpers.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/utils/row_access.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/utils/string_utils.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/core/utils/url_masking.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/base_provider.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/base_quirks.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/constants.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/dml_analysis.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/error.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/exceptions.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/feature_gate.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/native_connection_manager.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/object_naming.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/_pg_compatible.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/alloydb/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/alloydb/plugin.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/aurora_postgresql/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/aurora_postgresql/plugin.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/base_history_manager.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/base_locking_manager.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/base_query_executor.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/base_schema_operations.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/base_snapshot_manager.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/citus/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/citus/plugin.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/cockroachdb/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/cockroachdb/plugin.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/cockroachdb/provider.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/cockroachdb/quirks.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/cockroachdb/sqlalchemy_dialect.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/cockroachdb/sqlalchemy_url.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/cosmosdb/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/cosmosdb/config.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/cosmosdb/cosmosdb/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/cosmosdb/cosmosdb/_sdk.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/cosmosdb/cosmosdb/connection_manager.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/cosmosdb/cosmosdb/history_manager.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/cosmosdb/cosmosdb/locking_manager.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/cosmosdb/cosmosdb/query_executor.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/cosmosdb/cosmosdb/schema_operations.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/cosmosdb/cosmosdb/snapshot_manager.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/cosmosdb/introspection/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/cosmosdb/plugin.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/cosmosdb/provider.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/cosmosdb/quirks.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/db2/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/db2/config.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/db2/db2/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/db2/db2/schema_operations.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/db2/introspection/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/db2/parser/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/db2/parser/db2_regex_parser.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/db2/parser/parser_config.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/db2/plugin.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/db2/provider.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/db2/quirks.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/db2/sqlalchemy_url.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/duckdb/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/duckdb/config.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/duckdb/parser/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/duckdb/parser/duckdb_regex_parser.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/duckdb/parser/parser_config.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/duckdb/plugin.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/duckdb/provider.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/duckdb/quirks.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/duckdb/sqlalchemy_url.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/mariadb/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/mariadb/plugin.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/mariadb/provider.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/mariadb/quirks.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/mongodb/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/mongodb/config.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/mongodb/introspection/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/mongodb/mongodb/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/mongodb/mongodb/_sdk.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/mongodb/mongodb/connection_manager.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/mongodb/mongodb/history_manager.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/mongodb/mongodb/locking_manager.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/mongodb/mongodb/query_executor.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/mongodb/mongodb/schema_operations.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/mongodb/mongodb/snapshot_manager.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/mongodb/plugin.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/mongodb/provider.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/mongodb/quirks.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/mysql/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/mysql/config.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/mysql/introspection/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/mysql/mysql/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/mysql/mysql/schema_operations.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/mysql/parser/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/mysql/parser/mysql_regex_parser.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/mysql/parser/mysql_statement_parser.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/mysql/parser/mysql_tokenizer.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/mysql/parser/parser_config.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/mysql/plugin.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/mysql/provider.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/mysql/quirks.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/mysql/sqlalchemy_url.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/neon/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/neon/plugin.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/nosql_base/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/nosql_base/history.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/nosql_base/introspection.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/nosql_base/locking.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/nosql_base/provider.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/nosql_base/snapshot.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/oracle/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/oracle/config.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/oracle/introspection/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/oracle/introspection/oracle_utils.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/oracle/oracle/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/oracle/oracle/dbms_output.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/oracle/oracle/schema_operations.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/oracle/parser/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/oracle/parser/_comments.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/oracle/parser/_object_extractor.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/oracle/parser/_plsql_block.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/oracle/parser/_sqlplus.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/oracle/parser/_statement_splitter.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/oracle/parser/oracle_parser.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/oracle/parser/oracle_statement_parser.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/oracle/parser/oracle_tokenizer.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/oracle/parser/sqlplus_context.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/oracle/plugin.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/oracle/provider.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/oracle/quirks.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/oracle/sqlalchemy_url.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/postgresql/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/postgresql/_provider_query_executor.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/postgresql/config.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/postgresql/introspection/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/postgresql/parser/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/postgresql/parser/parser_config.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/postgresql/parser/postgresql_regex_parser.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/postgresql/parser/postgresql_statement_parser.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/postgresql/parser/postgresql_tokenizer.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/postgresql/plugin.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/postgresql/postgresql/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/postgresql/postgresql/_lock_key.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/postgresql/postgresql/schema_operations.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/postgresql/provider.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/postgresql/quirks.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/postgresql/search_path.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/postgresql/sqlalchemy_url.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/redshift/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/redshift/plugin.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/redshift/provider.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/redshift/quirks.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/redshift/sqlalchemy_url.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/snowflake/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/snowflake/config.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/snowflake/plugin.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/snowflake/provider.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/snowflake/quirks.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/snowflake/sqlalchemy_url.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/sqlite/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/sqlite/config.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/sqlite/introspection/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/sqlite/parser/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/sqlite/parser/parser_config.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/sqlite/parser/sqlite_regex_parser.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/sqlite/plugin.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/sqlite/provider.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/sqlite/quirks.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/sqlite/sqlalchemy_url.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/sqlite/sqlite/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/sqlite/sqlite/connection_manager.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/sqlite/sqlite/history_manager.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/sqlite/sqlite/locking_manager.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/sqlite/sqlite/query_executor.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/sqlite/sqlite/schema_operations.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/sqlserver/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/sqlserver/config.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/sqlserver/introspection/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/sqlserver/parser/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/sqlserver/parser/parser_config.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/sqlserver/parser/sqlserver_regex_parser.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/sqlserver/parser/sqlserver_statement_parser.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/sqlserver/parser/sqlserver_tokenizer.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/sqlserver/parser/tsql_batch_separator.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/sqlserver/plugin.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/sqlserver/provider.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/sqlserver/quirks.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/sqlserver/sqlalchemy_url.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/sqlserver/sqlserver/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/sqlserver/sqlserver/schema_operations.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/supabase/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/supabase/plugin.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/timescaledb/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/timescaledb/plugin.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/yugabytedb/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/plugins/yugabytedb/plugin.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/provider_capabilities.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/provider_interfaces.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/provider_registry.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/sqlalchemy_provider.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/db/value_utils.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/integrations/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/integrations/django/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/integrations/django/_client.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/integrations/django/_engine.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/integrations/django/apps.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/integrations/django/checks.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/integrations/django/management/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/integrations/django/management/commands/__init__.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/integrations/django/management/commands/dblift_info.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/integrations/django/management/commands/dblift_migrate.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/integrations/django/management/commands/dblift_validate.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/integrations/fastapi.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/integrations/flask.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift/integrations/opentelemetry.py +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift.egg-info/dependency_links.txt +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift.egg-info/entry_points.txt +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/dblift.egg-info/top_level.txt +0 -0
- {dblift-4.0.2 → dblift-4.2.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dblift
|
|
3
|
-
Version: 4.0
|
|
3
|
+
Version: 4.2.0
|
|
4
4
|
Summary: Flyway-style raw-SQL migrations for Python teams. No JVM, and you see the exact SQL before it runs.
|
|
5
5
|
Keywords: flyway,migrations,python,sql,postgresql,mysql
|
|
6
6
|
Classifier: Development Status :: 5 - Production/Stable
|
|
@@ -103,6 +103,8 @@ Provides-Extra: django
|
|
|
103
103
|
Requires-Dist: Django>=4.2; extra == "django"
|
|
104
104
|
Provides-Extra: otel
|
|
105
105
|
Requires-Dist: opentelemetry-api>=1.20; extra == "otel"
|
|
106
|
+
Provides-Extra: mcp
|
|
107
|
+
Requires-Dist: mcp>=2.2; extra == "mcp"
|
|
106
108
|
Provides-Extra: all
|
|
107
109
|
Requires-Dist: dblift[cosmosdb,db2,duckdb,mariadb,mongodb,mysql,oracle,postgresql,sqlserver]; extra == "all"
|
|
108
110
|
Requires-Dist: dblift[redshift,snowflake]; extra == "all"
|
|
@@ -19,6 +19,7 @@ from dblift.cli.handlers.baseline import _handle_baseline
|
|
|
19
19
|
from dblift.cli.handlers.clean import _handle_clean
|
|
20
20
|
from dblift.cli.handlers.import_flyway import _handle_import_flyway
|
|
21
21
|
from dblift.cli.handlers.info import _handle_info, _info_result_to_dict # noqa: F401
|
|
22
|
+
from dblift.cli.handlers.mcp import _handle_mcp
|
|
22
23
|
from dblift.cli.handlers.migrate import _handle_migrate
|
|
23
24
|
from dblift.cli.handlers.repair import _handle_repair
|
|
24
25
|
from dblift.cli.handlers.undo import _handle_undo
|
|
@@ -36,6 +37,7 @@ _COMMAND_HANDLERS: Dict[str, Callable[[CliCommandContext], Tuple[bool, Any]]] =
|
|
|
36
37
|
"baseline": _handle_baseline,
|
|
37
38
|
"repair": _handle_repair,
|
|
38
39
|
"import-flyway": _handle_import_flyway,
|
|
40
|
+
"mcp": _handle_mcp,
|
|
39
41
|
}
|
|
40
42
|
|
|
41
43
|
_extension_handlers = load_command_handlers()
|
|
@@ -560,6 +560,14 @@ def create_parser(
|
|
|
560
560
|
default="table",
|
|
561
561
|
help="Output format (default: table)",
|
|
562
562
|
)
|
|
563
|
+
# validate / migrate --format option (JSON output for scripting and agents)
|
|
564
|
+
for _machine_parser in (validate_parser, migrate_parser):
|
|
565
|
+
_machine_parser.add_argument(
|
|
566
|
+
"--format",
|
|
567
|
+
choices=["console", "json"],
|
|
568
|
+
default="console",
|
|
569
|
+
help="Output format (default: console)",
|
|
570
|
+
)
|
|
563
571
|
# DB utility commands
|
|
564
572
|
db_parser = subparsers.add_parser("db", help="Database utility commands")
|
|
565
573
|
db_subparsers = db_parser.add_subparsers(dest="db_command", required=True)
|
|
@@ -574,6 +582,12 @@ def create_parser(
|
|
|
574
582
|
action="store_true",
|
|
575
583
|
help="List all properties and how to set them (config key / env var / CLI flag)",
|
|
576
584
|
)
|
|
585
|
+
# `dblift mcp` serves the read-only commands as MCP tools over stdio. It is a
|
|
586
|
+
# zero-config command: the server loads the project config per tool call.
|
|
587
|
+
subparsers.add_parser(
|
|
588
|
+
"mcp",
|
|
589
|
+
help="Serve dblift commands as MCP tools over stdio (requires dblift[mcp])",
|
|
590
|
+
)
|
|
577
591
|
import_module("dblift.cli.extensions").load_command_extensions(parser)
|
|
578
592
|
# Stubs fill whatever gaps the extensions left — never the reverse.
|
|
579
593
|
_register_premium_stub_parsers(parser)
|
|
@@ -7,10 +7,12 @@ per-command files stay focused on their own behaviour.
|
|
|
7
7
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
+
import contextlib
|
|
11
|
+
import io
|
|
10
12
|
import re
|
|
11
13
|
from dataclasses import dataclass, field
|
|
12
14
|
from pathlib import Path
|
|
13
|
-
from typing import Any, Dict, List, Optional
|
|
15
|
+
from typing import Any, Callable, Dict, List, Optional, Tuple
|
|
14
16
|
|
|
15
17
|
# Flyway-compatible migration filename patterns — used by SQL-file validation to
|
|
16
18
|
# skip non-migration SQL files when scanning a directory (e.g. leftover
|
|
@@ -131,3 +133,87 @@ def _extract_version_filters(args: Any) -> tuple[Any, Any, Any, Any, Any]:
|
|
|
131
133
|
getattr(args, "tags", None),
|
|
132
134
|
getattr(args, "exclude_tags", None),
|
|
133
135
|
)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _enum_name(value: Any) -> Any:
|
|
139
|
+
"""Return an enum member's ``name`` (``MigrationType.SQL`` → ``"SQL"``), else *value*.
|
|
140
|
+
|
|
141
|
+
``MigrationInfo.type`` / ``.status`` can be enum members; ``json.dumps``
|
|
142
|
+
cannot serialise those, so the machine payload carries the name.
|
|
143
|
+
"""
|
|
144
|
+
if value is None or isinstance(value, str):
|
|
145
|
+
return value
|
|
146
|
+
name = getattr(value, "name", None)
|
|
147
|
+
if isinstance(name, str):
|
|
148
|
+
return name
|
|
149
|
+
return str(value)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _migration_info_to_dict(m: Any) -> Dict[str, Any]:
|
|
153
|
+
"""Serialise one ``MigrationInfo`` row for ``--format json`` payloads.
|
|
154
|
+
|
|
155
|
+
Shared by ``info``, ``validate`` and ``migrate`` so the row shape is one
|
|
156
|
+
contract, not three.
|
|
157
|
+
"""
|
|
158
|
+
installed_on = getattr(m, "installed_on", None)
|
|
159
|
+
return {
|
|
160
|
+
"script": m.script,
|
|
161
|
+
"version": str(m.version) if m.version else None,
|
|
162
|
+
"description": m.description,
|
|
163
|
+
"type": _enum_name(m.type),
|
|
164
|
+
"status": _enum_name(m.status),
|
|
165
|
+
"checksum": m.checksum,
|
|
166
|
+
"installed_on": (
|
|
167
|
+
(installed_on.isoformat() if hasattr(installed_on, "isoformat") else installed_on)
|
|
168
|
+
if installed_on
|
|
169
|
+
else None
|
|
170
|
+
),
|
|
171
|
+
"installed_by": m.installed_by,
|
|
172
|
+
"execution_time": m.execution_time,
|
|
173
|
+
"error": getattr(m, "error", None),
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def run_json_guarded(
|
|
178
|
+
ctx: "CliCommandContext",
|
|
179
|
+
command_type: str,
|
|
180
|
+
call: Callable[[], Any],
|
|
181
|
+
serialize: Callable[[Any], Dict[str, Any]],
|
|
182
|
+
) -> Tuple[bool, Any]:
|
|
183
|
+
"""Run *call* honouring the ``--format`` contract of ``ctx.args``.
|
|
184
|
+
|
|
185
|
+
Human mode: run, report completion, return ``(result.success, result)``;
|
|
186
|
+
exceptions propagate so the outer runner formats them. Machine mode:
|
|
187
|
+
stdout is redirected while *call* runs (banners and headers printed by
|
|
188
|
+
the command must not contaminate the JSON payload), an exception becomes
|
|
189
|
+
``{"success": false, "error": "<Type>: <msg>"}`` on stdout, and the
|
|
190
|
+
serialised result is the only thing written. ``SystemExit`` and
|
|
191
|
+
``KeyboardInterrupt`` are not ``Exception`` and always propagate.
|
|
192
|
+
"""
|
|
193
|
+
from dblift.cli._output import from_args
|
|
194
|
+
|
|
195
|
+
command_output = from_args(ctx.args)
|
|
196
|
+
result: Any = None
|
|
197
|
+
if not command_output.is_machine_format:
|
|
198
|
+
result = call()
|
|
199
|
+
if result is None:
|
|
200
|
+
return (False, None)
|
|
201
|
+
_set_command_completed(ctx.log, result, command_type)
|
|
202
|
+
return (result.success, result)
|
|
203
|
+
|
|
204
|
+
error: Optional[Exception] = None
|
|
205
|
+
try:
|
|
206
|
+
with contextlib.redirect_stdout(io.StringIO()):
|
|
207
|
+
result = call()
|
|
208
|
+
except Exception as exc:
|
|
209
|
+
error = exc
|
|
210
|
+
if error is not None or result is None:
|
|
211
|
+
text = (
|
|
212
|
+
f"{type(error).__name__}: {error}"
|
|
213
|
+
if error is not None
|
|
214
|
+
else f"{command_type.lower()}() returned no result"
|
|
215
|
+
)
|
|
216
|
+
command_output.machine({"success": False, "error": text})
|
|
217
|
+
return (False, None)
|
|
218
|
+
command_output.machine(serialize(result))
|
|
219
|
+
return (result.success, result)
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""Handler for the ``info`` command + InfoResult JSON serialization."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Dict, Tuple
|
|
6
|
+
|
|
7
|
+
from dblift.cli.handlers._shared import (
|
|
8
|
+
CliCommandContext,
|
|
9
|
+
_extract_version_filters,
|
|
10
|
+
_migration_info_to_dict,
|
|
11
|
+
run_json_guarded,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _info_result_to_dict(result: Any) -> Dict[str, Any]:
|
|
16
|
+
"""Serialize an InfoResult to a JSON-compatible dict."""
|
|
17
|
+
migrations = [_migration_info_to_dict(m) for m in getattr(result, "migrations", [])]
|
|
18
|
+
return {
|
|
19
|
+
# ``success`` mirrors the error-path payload (``{"success": False, ...}``)
|
|
20
|
+
# so downstream consumers can do ``result["success"]`` on both happy and
|
|
21
|
+
# error paths without a KeyError on the happy path.
|
|
22
|
+
"success": bool(getattr(result, "success", True)),
|
|
23
|
+
# Same reason ``OutputFormatter.format_info`` prints as ``Error: ...``.
|
|
24
|
+
# Without it a JSON consumer saw ``success: false`` and nothing else,
|
|
25
|
+
# while the key was already populated on the handler's exception payload.
|
|
26
|
+
"error": getattr(result, "error_message", None),
|
|
27
|
+
"current_schema_version": getattr(result, "current_schema_version", None),
|
|
28
|
+
"target_schema": getattr(result, "target_schema", ""),
|
|
29
|
+
"db_version": getattr(result, "db_version", None),
|
|
30
|
+
"database_url_masked": getattr(result, "database_url_masked", None),
|
|
31
|
+
"native_driver": getattr(result, "native_driver", None),
|
|
32
|
+
"migrations": migrations,
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _handle_info(ctx: CliCommandContext) -> Tuple[bool, Any]:
|
|
37
|
+
target_version, versions, exclude_versions, tags, exclude_tags = _extract_version_filters(
|
|
38
|
+
ctx.args
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
from dblift.cli._output import from_args as _output_from_args
|
|
42
|
+
|
|
43
|
+
use_json = _output_from_args(ctx.args).is_machine_format
|
|
44
|
+
info_kwargs = {
|
|
45
|
+
"target_version": target_version,
|
|
46
|
+
"tags": tags,
|
|
47
|
+
"exclude_tags": exclude_tags,
|
|
48
|
+
"versions": versions,
|
|
49
|
+
"exclude_versions": exclude_versions,
|
|
50
|
+
"recursive": ctx.recursive,
|
|
51
|
+
"additional_dirs": ctx.additional_scripts_dirs if ctx.additional_scripts_dirs else None,
|
|
52
|
+
"display_human": not use_json,
|
|
53
|
+
}
|
|
54
|
+
return run_json_guarded(
|
|
55
|
+
ctx, "INFO", lambda: ctx.client.info(**info_kwargs), _info_result_to_dict
|
|
56
|
+
)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Handler for the ``mcp`` command: serve dblift tools over stdio."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, List, Tuple
|
|
6
|
+
|
|
7
|
+
from dblift.cli._output import CommandOutput
|
|
8
|
+
from dblift.cli.handlers._shared import CliCommandContext
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _handle_mcp(ctx: CliCommandContext) -> Tuple[bool, Any]:
|
|
12
|
+
"""Build the MCP server and block on stdin/stdout until the client disconnects.
|
|
13
|
+
|
|
14
|
+
Zero-config: no project config or database is touched at start. Each tool
|
|
15
|
+
call loads the config the way the CLI would, prefixed with the root flags
|
|
16
|
+
this process was started with (``ctx.args.global_arguments``).
|
|
17
|
+
"""
|
|
18
|
+
from dblift.cli.mcp.server import MissingMcpSdkError, build_server
|
|
19
|
+
|
|
20
|
+
global_argv: List[str] = list(getattr(ctx.args, "global_arguments", None) or [])
|
|
21
|
+
try:
|
|
22
|
+
server = build_server(global_argv)
|
|
23
|
+
except MissingMcpSdkError as exc:
|
|
24
|
+
CommandOutput("console").error(str(exc))
|
|
25
|
+
return (False, None)
|
|
26
|
+
except Exception as exc:
|
|
27
|
+
# A registrar contributed by an installed add-on package can fail the
|
|
28
|
+
# build — a duplicate tool name, a signature the SDK rejects. Report
|
|
29
|
+
# it as a CLI error; a traceback is not something the user can act on.
|
|
30
|
+
CommandOutput("console").error(f"dblift mcp: could not start the server: {exc}")
|
|
31
|
+
return (False, None)
|
|
32
|
+
server.run_stdio()
|
|
33
|
+
return (True, None)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
_handle_mcp._dblift_zero_config_command = True # type: ignore[attr-defined]
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""Handler for the ``migrate`` command + MigrateResult JSON serialization."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Dict, Tuple
|
|
6
|
+
|
|
7
|
+
from dblift.cli.handlers._shared import (
|
|
8
|
+
CliCommandContext,
|
|
9
|
+
_extract_version_filters,
|
|
10
|
+
_migration_info_to_dict,
|
|
11
|
+
run_json_guarded,
|
|
12
|
+
)
|
|
13
|
+
from dblift.cli.handlers.validate import _validate_result_to_dict
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _migrate_result_to_dict(result: Any, dry_run: bool) -> Dict[str, Any]:
|
|
17
|
+
"""Serialize a MigrateResult to a JSON-compatible dict."""
|
|
18
|
+
return {
|
|
19
|
+
"success": bool(getattr(result, "success", True)),
|
|
20
|
+
"error": getattr(result, "error_message", None),
|
|
21
|
+
"dry_run": bool(dry_run),
|
|
22
|
+
"target_schema": getattr(result, "target_schema", ""),
|
|
23
|
+
"current_schema_version": getattr(result, "current_schema_version", None),
|
|
24
|
+
"dry_run_count": getattr(result, "dry_run_count", 0),
|
|
25
|
+
"migrations": [_migration_info_to_dict(m) for m in getattr(result, "migrations", [])],
|
|
26
|
+
"migrations_applied": list(getattr(result, "migrations_applied", [])),
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _handle_migrate(ctx: CliCommandContext) -> Tuple[bool, Any]:
|
|
31
|
+
target_version, versions, exclude_versions, tags, exclude_tags = _extract_version_filters(
|
|
32
|
+
ctx.args
|
|
33
|
+
)
|
|
34
|
+
additional_dirs = ctx.additional_scripts_dirs if ctx.additional_scripts_dirs else None
|
|
35
|
+
|
|
36
|
+
# Handle --validate-only by calling validate instead of migrate
|
|
37
|
+
if getattr(ctx.args, "validate_only", False):
|
|
38
|
+
|
|
39
|
+
def _validate_call() -> Any:
|
|
40
|
+
return ctx.client.validate(
|
|
41
|
+
target_version=target_version,
|
|
42
|
+
tags=tags,
|
|
43
|
+
exclude_tags=exclude_tags,
|
|
44
|
+
versions=versions,
|
|
45
|
+
exclude_versions=exclude_versions,
|
|
46
|
+
recursive=ctx.recursive,
|
|
47
|
+
additional_dirs=additional_dirs,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
return run_json_guarded(ctx, "VALIDATE", _validate_call, _validate_result_to_dict)
|
|
51
|
+
|
|
52
|
+
dry_run = bool(getattr(ctx.args, "dry_run", False))
|
|
53
|
+
|
|
54
|
+
def migrate_call() -> Any:
|
|
55
|
+
return ctx.client.migrate(
|
|
56
|
+
target_version=target_version,
|
|
57
|
+
dry_run=dry_run,
|
|
58
|
+
tags=tags,
|
|
59
|
+
exclude_tags=exclude_tags,
|
|
60
|
+
versions=versions,
|
|
61
|
+
exclude_versions=exclude_versions,
|
|
62
|
+
mark_as_executed=getattr(ctx.args, "mark_as_executed", False),
|
|
63
|
+
show_sql=getattr(ctx.args, "show_sql", False),
|
|
64
|
+
show_query_results=getattr(ctx.args, "show_query_results", False),
|
|
65
|
+
placeholders=ctx.placeholders,
|
|
66
|
+
recursive=ctx.recursive,
|
|
67
|
+
additional_dirs=additional_dirs,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
return run_json_guarded(
|
|
71
|
+
ctx, "MIGRATE", migrate_call, lambda r: _migrate_result_to_dict(r, dry_run)
|
|
72
|
+
)
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""Handler for the ``validate`` command + ValidateResult JSON serialization."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Dict, Tuple
|
|
6
|
+
|
|
7
|
+
from dblift.cli.handlers._shared import (
|
|
8
|
+
CliCommandContext,
|
|
9
|
+
_extract_version_filters,
|
|
10
|
+
_migration_info_to_dict,
|
|
11
|
+
run_json_guarded,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _validate_result_to_dict(result: Any) -> Dict[str, Any]:
|
|
16
|
+
"""Serialize a ValidateResult to a JSON-compatible dict."""
|
|
17
|
+
return {
|
|
18
|
+
"success": bool(getattr(result, "success", True)),
|
|
19
|
+
"error": getattr(result, "error_message", None),
|
|
20
|
+
"target_schema": getattr(result, "target_schema", ""),
|
|
21
|
+
"error_count": getattr(result, "error_count", 0),
|
|
22
|
+
"validated_migrations": [
|
|
23
|
+
_migration_info_to_dict(m) for m in getattr(result, "validated_migrations", [])
|
|
24
|
+
],
|
|
25
|
+
"failed_migrations": [
|
|
26
|
+
_migration_info_to_dict(m) for m in getattr(result, "failed_migrations", [])
|
|
27
|
+
],
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _handle_validate(ctx: CliCommandContext) -> Tuple[bool, Any]:
|
|
32
|
+
target_version, versions, exclude_versions, tags, exclude_tags = _extract_version_filters(
|
|
33
|
+
ctx.args
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
def call() -> Any:
|
|
37
|
+
return ctx.client.validate(
|
|
38
|
+
target_version=target_version,
|
|
39
|
+
tags=tags,
|
|
40
|
+
exclude_tags=exclude_tags,
|
|
41
|
+
versions=versions,
|
|
42
|
+
exclude_versions=exclude_versions,
|
|
43
|
+
recursive=ctx.recursive,
|
|
44
|
+
additional_dirs=ctx.additional_scripts_dirs if ctx.additional_scripts_dirs else None,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
return run_json_guarded(ctx, "VALIDATE", call, _validate_result_to_dict)
|
|
@@ -473,6 +473,8 @@ def _parse_argv_and_load_config(argv: List[str]) -> _CliContext:
|
|
|
473
473
|
commands[0], "_dblift_zero_config_command", False
|
|
474
474
|
):
|
|
475
475
|
handler = _COMMAND_HANDLERS[commands[0]]
|
|
476
|
+
# Terminal-style commands (e.g. `mcp`) replay these root flags per call.
|
|
477
|
+
args.global_arguments = global_arguments
|
|
476
478
|
success, _payload = handler(CliCommandContext(args=args))
|
|
477
479
|
sys.exit(0 if success else 1)
|
|
478
480
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""``dblift mcp`` — a stdio MCP server exposing read-only dblift commands as tools."""
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"""Discover MCP tool registrars contributed by installed add-on packages.
|
|
2
|
+
|
|
3
|
+
Each ``dblift.mcp_tools`` entry point resolves to ``register(server) -> None``,
|
|
4
|
+
which receives the :class:`dblift.cli.mcp.server.DbliftMcpServer` and calls
|
|
5
|
+
``server.command_tool(...)`` for every tool it contributes. OSS declares the
|
|
6
|
+
group empty; the built-in tools are registered by :mod:`dblift.cli.mcp.tools`.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import os
|
|
12
|
+
from importlib.metadata import entry_points
|
|
13
|
+
from typing import Any, Callable, Dict, List
|
|
14
|
+
|
|
15
|
+
MCP_TOOLS_ENTRY_POINT_GROUP = "dblift.mcp_tools"
|
|
16
|
+
|
|
17
|
+
ToolRegistrar = Callable[[Any], None]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def load_mcp_tool_registrars() -> List[ToolRegistrar]:
|
|
21
|
+
"""Load every ``dblift.mcp_tools`` registrar, sorted by entry-point name.
|
|
22
|
+
|
|
23
|
+
Honours ``DBLIFT_DISABLE_CLI_EXTENSIONS=1`` like the other CLI seams and
|
|
24
|
+
rejects two entry points sharing a name — silently keeping one would make
|
|
25
|
+
the tool list depend on install order.
|
|
26
|
+
"""
|
|
27
|
+
if os.environ.get("DBLIFT_DISABLE_CLI_EXTENSIONS") == "1":
|
|
28
|
+
return []
|
|
29
|
+
by_name: Dict[str, ToolRegistrar] = {}
|
|
30
|
+
for entry_point in entry_points(group=MCP_TOOLS_ENTRY_POINT_GROUP):
|
|
31
|
+
if entry_point.name in by_name:
|
|
32
|
+
raise ValueError(f"Duplicate MCP tool registrar: {entry_point.name}")
|
|
33
|
+
by_name[entry_point.name] = entry_point.load()
|
|
34
|
+
return [by_name[name] for name in sorted(by_name)]
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"""Run one CLI command in-process and hand back its ``--format json`` payload.
|
|
2
|
+
|
|
3
|
+
This is the seam every MCP tool goes through. It replays the four phases of
|
|
4
|
+
:func:`dblift.cli.main.main` for a single command — argv split, config load,
|
|
5
|
+
logging, client, handler — but never :func:`execute_single_command`, whose
|
|
6
|
+
``SystemExit`` on a capability denial would terminate the stdio server on the
|
|
7
|
+
first unlicensed call. Everything that would exit the CLI process becomes a
|
|
8
|
+
:class:`CommandInvocationError` carrying the exit code, and stdout is
|
|
9
|
+
redirected for the whole call: the JSON-RPC transport owns the real stream.
|
|
10
|
+
|
|
11
|
+
Stderr is captured too: a command's content may render through the stdout
|
|
12
|
+
console or through the console logger, which writes to stderr, and a
|
|
13
|
+
text-mode call joins both so the logger's lines are not lost. Capture also
|
|
14
|
+
lets a failed command's detail be quoted in the raised error. It is
|
|
15
|
+
*mirrored* rather than swallowed: the command's log lines still reach the
|
|
16
|
+
real stderr, which is where a stdio MCP server's own diagnostics belong —
|
|
17
|
+
only stdout is reserved outright for the JSON-RPC channel.
|
|
18
|
+
|
|
19
|
+
Any exception the handler raises — not just :class:`SystemExit` and
|
|
20
|
+
:class:`~dblift.core.seams.capabilities.CapabilityDeniedError` — becomes a
|
|
21
|
+
:class:`CommandInvocationError`; only :class:`KeyboardInterrupt` and other
|
|
22
|
+
non-:class:`Exception` signals still propagate. The client built for the
|
|
23
|
+
call is closed afterward when it exposes a callable ``close`` (some
|
|
24
|
+
handlers get a config-only stand-in with none), so a long-lived server
|
|
25
|
+
making many calls does not leak one connection per call.
|
|
26
|
+
|
|
27
|
+
Calls are serialised by a module-level lock. A client may issue several
|
|
28
|
+
tool calls at once and the SDK runs synchronous tool bodies in worker
|
|
29
|
+
threads, but everything a call reaches for is process-global: ``sys.argv``
|
|
30
|
+
(rewritten while the argv is parsed), ``sys.stdout`` and ``sys.stderr``
|
|
31
|
+
(redirected here), the ``LogFactory`` class attributes and the console
|
|
32
|
+
header flag. Concurrent calls therefore dismantle each other's redirects
|
|
33
|
+
and namespaces — one command's payload can be returned for another, and
|
|
34
|
+
bytes meant for a tool result escape to the real stdout, which is the
|
|
35
|
+
JSON-RPC channel. One command at a time is the only safe reading of that
|
|
36
|
+
shared state.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
from __future__ import annotations
|
|
40
|
+
|
|
41
|
+
import contextlib
|
|
42
|
+
import io
|
|
43
|
+
import json
|
|
44
|
+
import sys
|
|
45
|
+
import threading
|
|
46
|
+
from typing import Any, Dict, Optional, Sequence
|
|
47
|
+
|
|
48
|
+
from dblift.cli._constants import EXIT_LICENSE_REQUIRED
|
|
49
|
+
from dblift.cli.handlers._shared import CliCommandContext
|
|
50
|
+
from dblift.core.seams.capabilities import CapabilityDeniedError
|
|
51
|
+
from dblift.core.seams.tier_resolver import resolve_tier
|
|
52
|
+
|
|
53
|
+
JSON_FORMAT_ARGV: tuple[str, ...] = ("--format", "json")
|
|
54
|
+
_LICENSE_FALLBACK = "This command requires a license that is not available."
|
|
55
|
+
_TAIL = 2000
|
|
56
|
+
_CALL_LOCK = threading.Lock()
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class CommandInvocationError(Exception):
|
|
60
|
+
"""A command did not produce a payload; ``exit_code`` is what the CLI would have returned."""
|
|
61
|
+
|
|
62
|
+
def __init__(self, message: str, exit_code: int) -> None:
|
|
63
|
+
"""Store *message* as the exception text and *exit_code* for the caller."""
|
|
64
|
+
super().__init__(message)
|
|
65
|
+
self.exit_code = exit_code
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class _TeeStream(io.TextIOBase):
|
|
69
|
+
"""A write target that both records text and mirrors it to another stream.
|
|
70
|
+
|
|
71
|
+
Used for stderr: :func:`run_command` needs the text to build a detailed
|
|
72
|
+
:class:`CommandInvocationError`, but the lines themselves must still
|
|
73
|
+
reach the real stderr rather than vanish, unlike stdout.
|
|
74
|
+
|
|
75
|
+
Subclasses :class:`io.TextIOBase` so a command reaching past ``write`` —
|
|
76
|
+
``writelines``, ``isatty``, ``fileno`` — meets a real text stream rather
|
|
77
|
+
than an :class:`AttributeError`.
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
def __init__(self, buffer: io.StringIO, mirror: Optional[Any]) -> None:
|
|
81
|
+
"""Record writes into *buffer* and, when *mirror* is set, forward them to it too."""
|
|
82
|
+
self._buffer = buffer
|
|
83
|
+
self._mirror = mirror
|
|
84
|
+
|
|
85
|
+
def write(self, text: str) -> int:
|
|
86
|
+
"""Append *text* to the buffer and forward it to the mirror stream."""
|
|
87
|
+
self._buffer.write(text)
|
|
88
|
+
if self._mirror is not None:
|
|
89
|
+
self._mirror.write(text)
|
|
90
|
+
return len(text)
|
|
91
|
+
|
|
92
|
+
def writable(self) -> bool:
|
|
93
|
+
"""Report the stream as writable — that is the only thing it is for."""
|
|
94
|
+
return True
|
|
95
|
+
|
|
96
|
+
def flush(self) -> None:
|
|
97
|
+
"""Flush the mirror stream, if any."""
|
|
98
|
+
if self._mirror is not None:
|
|
99
|
+
self._mirror.flush()
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def run_command(
|
|
103
|
+
global_argv: Sequence[str],
|
|
104
|
+
command: str,
|
|
105
|
+
argv: Sequence[str],
|
|
106
|
+
*,
|
|
107
|
+
json_argv: Optional[Sequence[str]] = JSON_FORMAT_ARGV,
|
|
108
|
+
) -> Dict[str, Any]:
|
|
109
|
+
"""Run ``dblift <global_argv> <command> <argv> [json_argv]`` and return its payload.
|
|
110
|
+
|
|
111
|
+
With *json_argv* (default ``--format json``) the return value is the parsed
|
|
112
|
+
stdout document. With ``json_argv=None`` the command has no machine format;
|
|
113
|
+
the return value is ``{"success": <handler result>, "output": <text>}``,
|
|
114
|
+
where *text* is the captured stdout followed by the captured stderr (each
|
|
115
|
+
stripped, joined with a newline, empty parts omitted). A command's content
|
|
116
|
+
may land on either console — some commands render through the stdout
|
|
117
|
+
console, others through the console logger, which writes to stderr — so
|
|
118
|
+
both are joined rather than risk losing whichever one carried it.
|
|
119
|
+
|
|
120
|
+
One call at a time: the module docstring lists the process-global state a
|
|
121
|
+
call rewrites. A second caller waits rather than corrupting the first.
|
|
122
|
+
"""
|
|
123
|
+
with _CALL_LOCK:
|
|
124
|
+
return _run_command_locked(global_argv, command, argv, json_argv=json_argv)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _run_command_locked(
|
|
128
|
+
global_argv: Sequence[str],
|
|
129
|
+
command: str,
|
|
130
|
+
argv: Sequence[str],
|
|
131
|
+
*,
|
|
132
|
+
json_argv: Optional[Sequence[str]],
|
|
133
|
+
) -> Dict[str, Any]:
|
|
134
|
+
"""Body of :func:`run_command`; the caller must hold :data:`_CALL_LOCK`."""
|
|
135
|
+
from dblift.cli import main as cli_main
|
|
136
|
+
from dblift.cli._command_handlers import _COMMAND_HANDLERS, _validate_migrate_options
|
|
137
|
+
|
|
138
|
+
full_argv = [*global_argv, command, *argv, *(json_argv or ())]
|
|
139
|
+
stdout_buf, stderr_buf = io.StringIO(), io.StringIO()
|
|
140
|
+
stderr_tee = _TeeStream(stderr_buf, sys.stderr)
|
|
141
|
+
log: Any = None
|
|
142
|
+
client: Any = None
|
|
143
|
+
success = False
|
|
144
|
+
try:
|
|
145
|
+
with contextlib.redirect_stdout(stdout_buf), contextlib.redirect_stderr(stderr_tee):
|
|
146
|
+
ctx = cli_main._parse_argv_and_load_config(full_argv)
|
|
147
|
+
cli_main._setup_logging_and_output(ctx)
|
|
148
|
+
log = ctx.log
|
|
149
|
+
scripts_dir, additional_dirs, recursive, dir_map = (
|
|
150
|
+
cli_main._resolve_scripts_directories(
|
|
151
|
+
ctx.args, ctx.config, ctx.parser, ctx.commands
|
|
152
|
+
)
|
|
153
|
+
)
|
|
154
|
+
client = cli_main._build_command_client(ctx)
|
|
155
|
+
placeholders = cli_main._collect_placeholders(ctx.args, ctx.config)
|
|
156
|
+
if command == "migrate":
|
|
157
|
+
_validate_migrate_options(ctx.args, ctx.parser)
|
|
158
|
+
cli_main._ensure_connection(client, ctx.log, command)
|
|
159
|
+
handler = _COMMAND_HANDLERS[command]
|
|
160
|
+
success, _result = handler(
|
|
161
|
+
CliCommandContext(
|
|
162
|
+
client=client,
|
|
163
|
+
args=ctx.args,
|
|
164
|
+
log=ctx.log,
|
|
165
|
+
scripts_dir=scripts_dir,
|
|
166
|
+
additional_scripts_dirs=additional_dirs,
|
|
167
|
+
recursive=recursive,
|
|
168
|
+
placeholders=placeholders,
|
|
169
|
+
dir_recursive_map=dir_map,
|
|
170
|
+
license_tier=resolve_tier(ctx.args),
|
|
171
|
+
)
|
|
172
|
+
)
|
|
173
|
+
except CapabilityDeniedError as exc:
|
|
174
|
+
message = str(exc).strip() or _LICENSE_FALLBACK
|
|
175
|
+
raise CommandInvocationError(
|
|
176
|
+
f"{message} (exit code {EXIT_LICENSE_REQUIRED})", EXIT_LICENSE_REQUIRED
|
|
177
|
+
) from exc
|
|
178
|
+
except SystemExit as exc:
|
|
179
|
+
code = exc.code if isinstance(exc.code, int) else 1
|
|
180
|
+
detail = stderr_buf.getvalue().strip()[-_TAIL:]
|
|
181
|
+
raise CommandInvocationError(
|
|
182
|
+
f"dblift {command} exited with code {code}: {detail}", code
|
|
183
|
+
) from exc
|
|
184
|
+
except Exception as exc:
|
|
185
|
+
raise CommandInvocationError(f"{type(exc).__name__}: {exc}", 1) from exc
|
|
186
|
+
finally:
|
|
187
|
+
close = getattr(client, "close", None)
|
|
188
|
+
if callable(close):
|
|
189
|
+
try:
|
|
190
|
+
close()
|
|
191
|
+
except Exception as exc: # a failed close must not mask the result
|
|
192
|
+
if log is not None:
|
|
193
|
+
log.debug(f"Closing the client for '{command}' failed: {exc}")
|
|
194
|
+
if log is not None:
|
|
195
|
+
cli_main._close_logs(log)
|
|
196
|
+
|
|
197
|
+
stdout = stdout_buf.getvalue()
|
|
198
|
+
if json_argv is None:
|
|
199
|
+
# A text-mode command's content may land on either console: some
|
|
200
|
+
# commands render through the stdout console, others through the
|
|
201
|
+
# console logger, which writes to stderr. Join both, stdout first,
|
|
202
|
+
# so the caller gets the actual output regardless of which stream
|
|
203
|
+
# carried it.
|
|
204
|
+
parts = [part.strip() for part in (stdout, stderr_buf.getvalue()) if part.strip()]
|
|
205
|
+
return {"success": bool(success), "output": "\n".join(parts)}
|
|
206
|
+
try:
|
|
207
|
+
payload = json.loads(stdout)
|
|
208
|
+
except json.JSONDecodeError as exc:
|
|
209
|
+
raise CommandInvocationError(
|
|
210
|
+
f"dblift {command} produced no JSON on stdout: {stdout.strip()[-_TAIL:]}", 1
|
|
211
|
+
) from exc
|
|
212
|
+
if not isinstance(payload, dict):
|
|
213
|
+
return {"success": True, "result": payload}
|
|
214
|
+
return payload
|