dblift 2.2.0__tar.gz → 2.2.2__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-2.2.0 → dblift-2.2.2}/PKG-INFO +1 -1
- dblift-2.2.2/api/__init__.py +16 -0
- {dblift-2.2.0 → dblift-2.2.2}/api/_client_operations.py +6 -2
- {dblift-2.2.0 → dblift-2.2.2}/api/client.py +9 -5
- {dblift-2.2.0 → dblift-2.2.2}/cli/_config_helpers.py +40 -11
- {dblift-2.2.0 → dblift-2.2.2}/cli/_output.py +2 -1
- {dblift-2.2.0 → dblift-2.2.2}/cli/_parser_setup.py +23 -5
- {dblift-2.2.0 → dblift-2.2.2}/cli/handlers/_shared.py +20 -10
- {dblift-2.2.0 → dblift-2.2.2}/cli/main.py +59 -12
- {dblift-2.2.0 → dblift-2.2.2}/config/database_config.py +3 -3
- {dblift-2.2.0 → dblift-2.2.2}/config/dblift_config.py +89 -5
- {dblift-2.2.0 → dblift-2.2.2}/config/secrets/_registry.py +3 -2
- {dblift-2.2.0 → dblift-2.2.2}/core/dialect_boundary.py +9 -4
- {dblift-2.2.0 → dblift-2.2.2}/core/logger/_formatters.py +21 -1
- {dblift-2.2.0 → dblift-2.2.2}/core/logger/formatters/_formatter_impl.py +1 -3
- {dblift-2.2.0 → dblift-2.2.2}/core/logger/formatters/htmlformatter.py +26 -14
- dblift-2.2.2/core/logger/templates/oldreport.html +3031 -0
- dblift-2.2.2/core/logger/templates/report.html +1046 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/commands/base_command.py +2 -2
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/commands/migrate_command.py +3 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/executor/execution_engine.py +3 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/executor/migration_executor.py +1 -1
- dblift-2.2.2/core/seams/capabilities.py +20 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/seams/event_listeners.py +3 -2
- dblift-2.2.2/core/seams/feature_loading.py +66 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/seams/introspection.py +1 -1
- dblift-2.2.2/core/seams/license_info.py +42 -0
- dblift-2.2.2/core/seams/runtime_checks.py +35 -0
- dblift-2.2.2/core/seams/sql_generators.py +42 -0
- dblift-2.2.2/core/seams/tier_resolver.py +59 -0
- dblift-2.2.2/core/sql_generator/__init__.py +42 -0
- dblift-2.2.2/core/sql_generator/alter/__init__.py +17 -0
- dblift-2.2.2/core/sql_generator/alter/alter_generator_factory.py +123 -0
- dblift-2.2.2/core/sql_generator/alter/base_alter_generator.py +169 -0
- dblift-2.2.2/core/sql_generator/alter_generator.py +64 -0
- dblift-2.2.2/core/sql_generator/base_generator.py +607 -0
- dblift-2.2.2/core/sql_generator/basic_table_ddl_generator.py +1034 -0
- dblift-2.2.2/core/sql_generator/dependency_analyzer.py +602 -0
- dblift-2.2.2/core/sql_generator/diff_converters/__init__.py +5 -0
- dblift-2.2.2/core/sql_generator/diff_converters/base_converter.py +36 -0
- dblift-2.2.2/core/sql_generator/formatter.py +112 -0
- dblift-2.2.2/core/sql_generator/generator_factory.py +188 -0
- dblift-2.2.2/core/sql_generator/options.py +59 -0
- dblift-2.2.2/core/sql_generator/script_organizer.py +307 -0
- dblift-2.2.2/core/sql_generator/sql_generator.py +50 -0
- dblift-2.2.2/core/sql_generator/sql_statement.py +5 -0
- dblift-2.2.2/core/sql_generator/table_ddl_render.py +61 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_model/event.py +14 -2
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_model/extension.py +14 -2
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_model/foreign_data_wrapper.py +12 -2
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_model/foreign_server.py +14 -2
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_model/index.py +14 -2
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_model/package.py +14 -2
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_model/procedure.py +14 -2
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_model/sequence.py +21 -1
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_model/synonym.py +14 -2
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_model/table.py +32 -61
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_model/trigger.py +12 -2
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_model/user_defined_type.py +12 -2
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_model/view.py +14 -2
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_validator/__init__.py +1 -1
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_validator/migration_validator.py +2 -2
- {dblift-2.2.0 → dblift-2.2.2}/db/base_quirks.py +22 -7
- {dblift-2.2.0 → dblift-2.2.2}/db/dml_analysis.py +1 -1
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/cosmosdb/cosmosdb/query_executor.py +1 -1
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/cosmosdb/quirks.py +13 -6
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/db2/db2/schema_operations.py +10 -8
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/db2/provider.py +14 -8
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/db2/quirks.py +10 -5
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/mysql/quirks.py +11 -6
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/oracle/quirks.py +47 -38
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/postgresql/quirks.py +13 -9
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/sqlite/quirks.py +9 -5
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/sqlserver/quirks.py +11 -7
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/sqlserver/sqlalchemy_url.py +12 -1
- {dblift-2.2.0 → dblift-2.2.2}/dblift.egg-info/PKG-INFO +1 -1
- {dblift-2.2.0 → dblift-2.2.2}/dblift.egg-info/SOURCES.txt +25 -3
- {dblift-2.2.0 → dblift-2.2.2}/pyproject.toml +4 -1
- dblift-2.2.0/api/__init__.py +0 -12
- dblift-2.2.0/core/features.py +0 -123
- dblift-2.2.0/core/logger/formatters/diff_utils.py +0 -414
- dblift-2.2.0/core/normalization/data_type_normalizer.py +0 -5
- {dblift-2.2.0 → dblift-2.2.2}/LICENSE +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/README.md +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/api/_cli_support.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/api/_client_factory.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/api/_engine_config.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/api/async_client.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/api/events.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/api/migrations.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/api/py.typed +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/cli/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/cli/_command_handlers.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/cli/_constants.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/cli/commands/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/cli/commands/config_command.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/cli/db_utils.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/cli/extensions.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/cli/handlers/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/cli/handlers/baseline.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/cli/handlers/clean.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/cli/handlers/import_flyway.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/cli/handlers/info.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/cli/handlers/migrate.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/cli/handlers/repair.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/cli/handlers/undo.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/cli/handlers/validate.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/config/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/config/_credential_masking.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/config/_subclasses/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/config/_subclasses/dummy_config.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/config/_url_builder_mixin.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/config/config_builder.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/config/errors.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/config/property_registry.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/config/secrets/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/config/secrets/_cache.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/config/secrets/_provider_base.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/config/secrets/_resolver.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/config/secrets/_secrets_config.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/constants.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/exceptions.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/introspection/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/introspection/_column_enricher.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/introspection/_partition_enricher.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/introspection/_schema_orchestrator.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/introspection/_utils.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/introspection/_vendor_property_applier.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/introspection/base_introspector.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/introspection/extractors/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/introspection/extractors/base_extractor.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/introspection/extractors/column_extractor.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/introspection/extractors/constraint_extractor.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/introspection/extractors/index_extractor.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/introspection/extractors/misc_extractor.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/introspection/extractors/procedure_extractor.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/introspection/extractors/sequence_extractor.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/introspection/extractors/table_extractor.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/introspection/extractors/trigger_extractor.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/introspection/extractors/view_extractor.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/introspection/introspector_factory.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/introspection/result.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/introspection/schema_introspector.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/introspection/vendor_queries_base.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/introspection/vendor_queries_factory.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/introspection/vendor_queries_protocols.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/introspection/version_detector.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/logger/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/logger/_base.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/logger/_factory.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/logger/_levels.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/logger/_multi.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/logger/_null.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/logger/console.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/logger/formatters/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/logger/formatters/factory.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/logger/formatters/formatter.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/logger/formatters/jsonformatter.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/logger/log.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/logger/results.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/_type_match.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/clean_summary.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/commands/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/commands/_script_events.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/commands/baseline_command.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/commands/clean_command.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/commands/import_flyway_command.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/commands/info_command.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/commands/repair_command.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/commands/undo_command.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/commands/validate_command.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/encoding.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/executor/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/executor/migration_helpers.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/executor/placeholder_manager.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/executor/transaction_policy.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/executors/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/executors/base_executor.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/executors/executor_factory.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/executors/python_executor.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/executors/sql_executor.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/formats/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/formats/format_detector.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/formats/migration_format.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/history/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/history/migration_history_manager.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/journals/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/journals/migration_journal.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/migration.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/placeholders/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/placeholders/placeholder_service.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/rules/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/rules/migration_rules.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/scripting/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/scripting/migration_script_manager.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/scripting/undo_script_generator/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/scripting/undo_script_generator/_ddl_reversers.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/scripting/undo_script_generator/_dml_reversers.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/scripting/undo_script_generator/_extractors.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/scripting/undo_script_generator/_generator.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/scripting/undo_script_generator/_helpers.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/scripting/undo_script_generator/_models.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/scripting/undo_script_generator/_reversers.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/sql/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/sql/execution_statement.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/sql/sql_analyzer.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/sql/sql_execution_service.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/sql/statement_splitter.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/state/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/state/migration_data_service.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/state/migration_display_state.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/state/migration_formatter.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/state/migration_state.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/state/migration_state_manager.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/state/migration_state_service.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/ui/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/ui/data_collector.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/ui/display_formatters.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/ui/migration_analyzer.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/ui/migration_ui.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/ui/table_renderer.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/migration/version_utils.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/normalization/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/normalization/dependency_resolver.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/normalization/identifier_normalizer.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/normalization/object_orderer.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/normalization/type_constants.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/normalization/type_mapper.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/normalization/type_mappings.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/normalization/type_normalizer.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/seams/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_model/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_model/_base_parse_result.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_model/_base_sql_column.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_model/_base_sql_constraint.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_model/_base_sql_object.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_model/_base_sql_statement.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_model/base.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_model/constraint_validator.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_model/database_link.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_model/dialect.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_model/linked_server.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_model/module.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_model/partition.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_model/table_canonicalizer.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_model/table_options.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_model/view_options.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_parser/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_parser/_partition_handler.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_parser/_sqlglot_builders.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_parser/base_statement_parser.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_parser/base_tokenizer.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_parser/common/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_parser/common/base_parser.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_parser/dialects/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_parser/dialects/base_config.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_parser/enhanced_regex_parser.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_parser/hybrid_parser.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_parser/parser_context.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_parser/parser_factory.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_parser/parser_interface.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_parser/sqlglot_parser.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_parser/tokens.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_parser/unified_regex_parser.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_validator/_checksum_validator.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_validator/_flyway_compatibility.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_validator/_migration_filter.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_validator/_sql_syntax_validator.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/sql_validator/_strict_mode_validator.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/state/sql_script_formatter.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/state/sql_statement.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/utils/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/utils/database_url_parser.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/utils/metadata_helpers.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/utils/row_access.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/utils/string_utils.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/core/utils/url_masking.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/base_provider.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/constants.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/error.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/error_handler.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/exceptions.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/native_connection_manager.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/object_naming.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/base_history_manager.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/base_locking_manager.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/base_query_executor.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/base_schema_operations.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/base_snapshot_manager.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/base_undo_manager.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/cosmosdb/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/cosmosdb/config.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/cosmosdb/cosmosdb/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/cosmosdb/cosmosdb/_sdk.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/cosmosdb/cosmosdb/connection_manager.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/cosmosdb/cosmosdb/history_manager.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/cosmosdb/cosmosdb/locking_manager.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/cosmosdb/cosmosdb/schema_operations.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/cosmosdb/introspection/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/cosmosdb/parser/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/cosmosdb/parser/cosmosdb_regex_parser.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/cosmosdb/plugin.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/cosmosdb/provider.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/cosmosdb/sdk_translator/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/cosmosdb/sdk_translator/_executor.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/cosmosdb/sdk_translator/_executors.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/cosmosdb/sdk_translator/_parsing.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/cosmosdb/sdk_translator/_script_generation.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/cosmosdb/sdk_translator/_translator.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/cosmosdb/sdk_translator/_translators.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/db2/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/db2/config.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/db2/db2/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/db2/db2/history_manager.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/db2/db2/locking_manager.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/db2/introspection/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/db2/parser/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/db2/parser/db2_regex_parser.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/db2/parser/parser_config.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/db2/plugin.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/db2/sqlalchemy_url.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/mariadb/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/mariadb/plugin.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/mariadb/provider.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/mariadb/quirks.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/mysql/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/mysql/config.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/mysql/introspection/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/mysql/mysql/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/mysql/mysql/history_manager.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/mysql/mysql/locking_manager.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/mysql/mysql/schema_operations.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/mysql/parser/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/mysql/parser/mysql_regex_parser.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/mysql/parser/mysql_statement_parser.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/mysql/parser/mysql_tokenizer.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/mysql/parser/parser_config.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/mysql/plugin.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/mysql/provider.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/mysql/sqlalchemy_url.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/oracle/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/oracle/config.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/oracle/introspection/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/oracle/introspection/oracle_utils.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/oracle/oracle/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/oracle/oracle/dbms_output.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/oracle/oracle/history_manager.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/oracle/oracle/locking_manager.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/oracle/oracle/schema_operations.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/oracle/parser/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/oracle/parser/_comments.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/oracle/parser/_object_extractor.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/oracle/parser/_plsql_block.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/oracle/parser/_sqlplus.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/oracle/parser/_statement_splitter.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/oracle/parser/oracle_parser.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/oracle/parser/oracle_statement_parser.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/oracle/parser/oracle_tokenizer.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/oracle/parser/parser_config.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/oracle/parser/sqlplus_context.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/oracle/plugin.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/oracle/provider.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/oracle/sqlalchemy_url.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/postgresql/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/postgresql/_provider_query_executor.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/postgresql/config.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/postgresql/introspection/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/postgresql/parser/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/postgresql/parser/parser_config.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/postgresql/parser/postgresql_regex_parser.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/postgresql/parser/postgresql_statement_parser.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/postgresql/parser/postgresql_tokenizer.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/postgresql/plugin.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/postgresql/postgresql/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/postgresql/postgresql/history_manager.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/postgresql/postgresql/locking_manager.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/postgresql/postgresql/schema_operations.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/postgresql/provider.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/postgresql/sqlalchemy_url.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/sqlite/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/sqlite/config.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/sqlite/introspection/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/sqlite/parser/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/sqlite/parser/parser_config.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/sqlite/parser/sqlite_regex_parser.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/sqlite/plugin.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/sqlite/provider.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/sqlite/sqlalchemy_url.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/sqlite/sqlite/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/sqlite/sqlite/connection_manager.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/sqlite/sqlite/history_manager.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/sqlite/sqlite/locking_manager.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/sqlite/sqlite/query_executor.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/sqlite/sqlite/schema_operations.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/sqlserver/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/sqlserver/config.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/sqlserver/introspection/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/sqlserver/parser/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/sqlserver/parser/parser_config.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/sqlserver/parser/sqlserver_regex_parser.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/sqlserver/parser/sqlserver_statement_parser.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/sqlserver/parser/sqlserver_tokenizer.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/sqlserver/parser/tsql_batch_separator.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/sqlserver/plugin.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/sqlserver/provider.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/sqlserver/sqlserver/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/sqlserver/sqlserver/history_manager.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/sqlserver/sqlserver/locking_manager.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/plugins/sqlserver/sqlserver/schema_operations.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/provider_capabilities.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/provider_interfaces.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/provider_registry.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/sqlalchemy_provider.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/db/value_utils.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/dblift.egg-info/dependency_links.txt +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/dblift.egg-info/entry_points.txt +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/dblift.egg-info/requires.txt +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/dblift.egg-info/top_level.txt +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/integrations/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/integrations/django/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/integrations/django/_client.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/integrations/django/_engine.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/integrations/django/apps.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/integrations/django/checks.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/integrations/django/management/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/integrations/django/management/commands/__init__.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/integrations/django/management/commands/dblift_info.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/integrations/django/management/commands/dblift_migrate.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/integrations/django/management/commands/dblift_validate.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/integrations/fastapi.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/integrations/flask.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/integrations/opentelemetry.py +0 -0
- {dblift-2.2.0 → dblift-2.2.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dblift
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.2
|
|
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
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Public API for DBLift library integration.
|
|
2
|
+
|
|
3
|
+
This module provides a clean Python API for using DBLift programmatically,
|
|
4
|
+
enabling integration with IDEs, CI/CD pipelines, and other development tools.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from api.client import DBLiftClient
|
|
8
|
+
from api.events import EventEmitter, EventType
|
|
9
|
+
from api.migrations import MigrationContext
|
|
10
|
+
|
|
11
|
+
__all__ = [ # noqa: F822
|
|
12
|
+
"DBLiftClient",
|
|
13
|
+
"EventEmitter",
|
|
14
|
+
"EventType",
|
|
15
|
+
"MigrationContext",
|
|
16
|
+
]
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
"""Private operation helpers for
|
|
1
|
+
"""Private operation helpers for :mod:`api.client`.
|
|
2
|
+
|
|
3
|
+
Keep large operation bodies out of ``DBLiftClient`` so the public client class
|
|
4
|
+
stays readable while preserving the same public methods and behavior.
|
|
5
|
+
"""
|
|
2
6
|
|
|
3
7
|
from __future__ import annotations
|
|
4
8
|
|
|
@@ -19,7 +23,7 @@ def _heuristic_statement_count_from_sql(sql_text: str) -> int:
|
|
|
19
23
|
|
|
20
24
|
|
|
21
25
|
def _apply_sql_script_warning_scan(
|
|
22
|
-
result: GenerateUndoScriptResult,
|
|
26
|
+
result: Union[Any, GenerateUndoScriptResult],
|
|
23
27
|
sql_text: str,
|
|
24
28
|
) -> None:
|
|
25
29
|
"""Set manual-review flag and collect per-line warnings from generated SQL text."""
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"""Main client for programmatic access to DBLift."""
|
|
2
2
|
|
|
3
3
|
from functools import wraps
|
|
4
|
-
from importlib import import_module
|
|
5
4
|
from pathlib import Path
|
|
6
5
|
from types import TracebackType
|
|
7
6
|
from typing import (
|
|
@@ -25,10 +24,7 @@ from api._client_factory import (
|
|
|
25
24
|
normalize_migrations_dirs,
|
|
26
25
|
resolve_config_or_raise,
|
|
27
26
|
)
|
|
28
|
-
from api._client_operations import
|
|
29
|
-
generate_undo_script_operation,
|
|
30
|
-
generate_undo_scripts_operation,
|
|
31
|
-
)
|
|
27
|
+
from api._client_operations import generate_undo_script_operation, generate_undo_scripts_operation
|
|
32
28
|
from api.events import EventEmitter, EventType, use_client_emitter
|
|
33
29
|
from config import DbliftConfig
|
|
34
30
|
from core.logger.results import (
|
|
@@ -44,6 +40,7 @@ from core.logger.results import (
|
|
|
44
40
|
)
|
|
45
41
|
from core.migration.executor.migration_executor import MigrationExecutor
|
|
46
42
|
from core.seams.event_listeners import attach_registered_listeners
|
|
43
|
+
from core.seams.feature_loading import load_feature_extensions
|
|
47
44
|
from db.base_provider import BaseProvider
|
|
48
45
|
from db.provider_interfaces import ConnectionProvider, TransactionalProvider
|
|
49
46
|
|
|
@@ -119,6 +116,13 @@ class DBLiftClient:
|
|
|
119
116
|
log_file: Optional log file path; omit to keep ``config`` values
|
|
120
117
|
**kwargs: Additional configuration options
|
|
121
118
|
"""
|
|
119
|
+
# Feature registrations from installed add-on packages must load for
|
|
120
|
+
# every client construction path, not just CLI startup — otherwise the
|
|
121
|
+
# runtime-check and tier-resolver seams would silently no-op for
|
|
122
|
+
# API-only usage. Idempotent (module-level once-flag), so double-loading
|
|
123
|
+
# with the CLI's own call is harmless.
|
|
124
|
+
load_feature_extensions()
|
|
125
|
+
|
|
122
126
|
# Resolve config before logger so defaults come from merged config (e.g. from file).
|
|
123
127
|
config = resolve_config_or_raise(provider, config)
|
|
124
128
|
|
|
@@ -35,15 +35,37 @@ _SUBCOMMAND_BOOLEAN_FLAGS = frozenset(
|
|
|
35
35
|
"--clean-disabled",
|
|
36
36
|
"--clean-enabled",
|
|
37
37
|
"--dry-run",
|
|
38
|
+
"--generate-sql",
|
|
39
|
+
"--ignore-unmanaged",
|
|
40
|
+
"--include-drops",
|
|
41
|
+
"--keep-container",
|
|
42
|
+
"--managed-only",
|
|
38
43
|
"--mark-as-executed",
|
|
44
|
+
"--no-performance",
|
|
39
45
|
"--show-sql",
|
|
46
|
+
"--rehearse-rollback",
|
|
47
|
+
"--skip-replay",
|
|
40
48
|
"--skip-validation",
|
|
49
|
+
"--skip-validate-sql",
|
|
50
|
+
"--split-by-type",
|
|
41
51
|
"--strict",
|
|
52
|
+
"--unmanaged-only",
|
|
42
53
|
"--validate-only",
|
|
43
54
|
}
|
|
44
55
|
)
|
|
45
56
|
|
|
46
57
|
|
|
58
|
+
def _command_handler_attr(command: Optional[str], attr_name: str, default: Any = None) -> Any:
|
|
59
|
+
if not command:
|
|
60
|
+
return default
|
|
61
|
+
from cli._command_handlers import _COMMAND_HANDLERS
|
|
62
|
+
|
|
63
|
+
handler = _COMMAND_HANDLERS.get(command)
|
|
64
|
+
if handler is None:
|
|
65
|
+
return default
|
|
66
|
+
return getattr(handler, attr_name, default)
|
|
67
|
+
|
|
68
|
+
|
|
47
69
|
def _extract_commands_from_argv(
|
|
48
70
|
argv_list: List[str],
|
|
49
71
|
available_commands: List[str],
|
|
@@ -257,16 +279,21 @@ def _load_and_merge_config(args: argparse.Namespace, log: Any) -> Any:
|
|
|
257
279
|
db_overrides["schema"] = args.database_schema
|
|
258
280
|
|
|
259
281
|
if db_overrides:
|
|
260
|
-
|
|
261
|
-
|
|
282
|
+
command = getattr(args, "command", None)
|
|
283
|
+
commands_list = getattr(args, "commands_list", None) or ([command] if command else [])
|
|
284
|
+
is_offline = len(commands_list) == 1 and bool(
|
|
285
|
+
_command_handler_attr(command, "_dblift_skip_secret_resolution", False)
|
|
286
|
+
)
|
|
287
|
+
if not is_offline:
|
|
288
|
+
from config.secrets._resolver import resolve_secret_refs
|
|
262
289
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
290
|
+
try:
|
|
291
|
+
db_overrides = resolve_secret_refs({"database": db_overrides}, config.secrets).get(
|
|
292
|
+
"database", db_overrides
|
|
293
|
+
)
|
|
294
|
+
except SecretsResolutionError as e:
|
|
295
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
296
|
+
sys.exit(1)
|
|
270
297
|
config.database = ConfigBuilder.merge_database_overrides(config.database, db_overrides)
|
|
271
298
|
|
|
272
299
|
if log:
|
|
@@ -321,7 +348,7 @@ def _validate_db_config(
|
|
|
321
348
|
if commands and commands[0] == "db":
|
|
322
349
|
return
|
|
323
350
|
|
|
324
|
-
migration_commands =
|
|
351
|
+
migration_commands = {
|
|
325
352
|
"migrate",
|
|
326
353
|
"undo",
|
|
327
354
|
"clean",
|
|
@@ -330,7 +357,9 @@ def _validate_db_config(
|
|
|
330
357
|
"repair",
|
|
331
358
|
"import-flyway",
|
|
332
359
|
"baseline",
|
|
333
|
-
|
|
360
|
+
}
|
|
361
|
+
if _command_handler_attr(args.command, "_dblift_validate_db_config", False):
|
|
362
|
+
migration_commands.add(args.command)
|
|
334
363
|
if args.command not in migration_commands:
|
|
335
364
|
return
|
|
336
365
|
|
|
@@ -13,7 +13,8 @@ dispatch in ``cli/main.py`` re-derive the same predicate:
|
|
|
13
13
|
else:
|
|
14
14
|
ctx.log.info(payload) # routed through ConsoleLog
|
|
15
15
|
|
|
16
|
-
That duplication has already caused
|
|
16
|
+
That duplication has already caused two regressions (PR 158 banner fix
|
|
17
|
+
+ PR-01 machine-format scope). ADR-0005 added the
|
|
17
18
|
`MACHINE_READABLE_FORMATS` set as a single source of truth; this
|
|
18
19
|
module does the same for the *routing* decision itself.
|
|
19
20
|
|
|
@@ -116,6 +116,8 @@ def _add_registry_flags(parser: argparse.ArgumentParser) -> None:
|
|
|
116
116
|
"""
|
|
117
117
|
from config.property_registry import PROPERTY_REGISTRY
|
|
118
118
|
|
|
119
|
+
deferred_specs = {"max_snapshots", "snapshot_table"}
|
|
120
|
+
|
|
119
121
|
# Collect option strings across the WHOLE tree (root + every subparser), not
|
|
120
122
|
# just the root parser. Many registry properties (e.g. --tags,
|
|
121
123
|
# --target-version, --snapshot-table) intentionally live only on specific
|
|
@@ -137,6 +139,8 @@ def _add_registry_flags(parser: argparse.ArgumentParser) -> None:
|
|
|
137
139
|
for spec in PROPERTY_REGISTRY:
|
|
138
140
|
if spec.cli_only or spec.cli_exempt or "." in spec.name:
|
|
139
141
|
continue
|
|
142
|
+
if spec.name in deferred_specs:
|
|
143
|
+
continue
|
|
140
144
|
if spec.cli_aliases:
|
|
141
145
|
continue # legacy flag (e.g. --table, --strict) already provides the surface
|
|
142
146
|
if spec.cli in existing:
|
|
@@ -202,7 +206,7 @@ def _make_filter_parent() -> argparse.ArgumentParser:
|
|
|
202
206
|
|
|
203
207
|
# NOTE: ``--target-version`` is intentionally NOT extracted into a parent
|
|
204
208
|
# parser. Its help text is genuinely command-specific (migrate / undo /
|
|
205
|
-
#
|
|
209
|
+
# validate each describe a different intent) and unifying it would degrade
|
|
206
210
|
# `--help` output. Each subparser adds it explicitly in
|
|
207
211
|
# ``_add_diff_and_target_options`` so the user-facing string stays
|
|
208
212
|
# accurate. See Bugbot review on PR-09.
|
|
@@ -232,13 +236,12 @@ def _add_baseline_options(baseline_parser: argparse.ArgumentParser) -> None:
|
|
|
232
236
|
|
|
233
237
|
|
|
234
238
|
def _add_diff_and_target_options(
|
|
235
|
-
diff_parser: argparse.ArgumentParser | None,
|
|
236
239
|
migrate_parser: argparse.ArgumentParser,
|
|
237
240
|
undo_parser: argparse.ArgumentParser,
|
|
238
241
|
validate_parser: argparse.ArgumentParser,
|
|
239
242
|
clean_parser: argparse.ArgumentParser,
|
|
240
243
|
) -> None:
|
|
241
|
-
"""Configure
|
|
244
|
+
"""Configure migrate/undo/validate/clean subcommand-specific options.
|
|
242
245
|
|
|
243
246
|
``--target-version`` is added per-command (rather than via a shared
|
|
244
247
|
parent parser) because each command has a meaningfully different
|
|
@@ -290,6 +293,21 @@ def _add_diff_and_target_options(
|
|
|
290
293
|
)
|
|
291
294
|
|
|
292
295
|
|
|
296
|
+
def _register_builtin_command_parsers(
|
|
297
|
+
parser: argparse.ArgumentParser,
|
|
298
|
+
) -> list[argparse.ArgumentParser]:
|
|
299
|
+
"""Register first-party command parsers that remain in the OSS CLI."""
|
|
300
|
+
subparser_actions = [
|
|
301
|
+
action for action in parser._actions if isinstance(action, argparse._SubParsersAction)
|
|
302
|
+
]
|
|
303
|
+
if not subparser_actions:
|
|
304
|
+
return []
|
|
305
|
+
|
|
306
|
+
registered: list[argparse.ArgumentParser] = []
|
|
307
|
+
|
|
308
|
+
return registered
|
|
309
|
+
|
|
310
|
+
|
|
293
311
|
def create_parser(
|
|
294
312
|
exit_on_error: bool = True, suppress_errors: bool = False
|
|
295
313
|
) -> argparse.ArgumentParser:
|
|
@@ -441,10 +459,10 @@ def create_parser(
|
|
|
441
459
|
default="flyway_schema_history",
|
|
442
460
|
help="Source Flyway schema history table name (default: flyway_schema_history)",
|
|
443
461
|
)
|
|
444
|
-
builtin_extension_parsers
|
|
462
|
+
builtin_extension_parsers = _register_builtin_command_parsers(parser)
|
|
445
463
|
# Configure arguments via extracted functions
|
|
446
464
|
_add_baseline_options(baseline_parser)
|
|
447
|
-
_add_diff_and_target_options(
|
|
465
|
+
_add_diff_and_target_options(migrate_parser, undo_parser, validate_parser, clean_parser)
|
|
448
466
|
# info --format option (JSON output for scripting)
|
|
449
467
|
info_parser.add_argument(
|
|
450
468
|
"--format",
|
|
@@ -12,11 +12,9 @@ from dataclasses import dataclass, field
|
|
|
12
12
|
from pathlib import Path
|
|
13
13
|
from typing import Any, Dict, List, Optional
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
#
|
|
18
|
-
# SQL files when scanning a directory (e.g. leftover temp files, schema
|
|
19
|
-
# dumps) so only intentional migration scripts are
|
|
15
|
+
# Flyway-compatible migration filename patterns — used by SQL-file validation to
|
|
16
|
+
# skip non-migration SQL files when scanning a directory (e.g. leftover
|
|
17
|
+
# temp files, schema dumps) so only intentional migration scripts are
|
|
20
18
|
# checked. Explicitly-listed files are always validated regardless of name.
|
|
21
19
|
_MIGRATION_FILENAME_RE = re.compile(
|
|
22
20
|
r"^[VRUBvrub][\d_.]*__.*\.sql$",
|
|
@@ -61,9 +59,21 @@ class CliCommandContext:
|
|
|
61
59
|
# Migration configuration
|
|
62
60
|
placeholders: Dict[str, Any] = field(default_factory=dict)
|
|
63
61
|
dir_recursive_map: Dict[Path, bool] = field(default_factory=dict)
|
|
64
|
-
# License tier for feature-level gates.
|
|
65
|
-
#
|
|
66
|
-
|
|
62
|
+
# License tier for feature-level gates. Opaque to OSS (see
|
|
63
|
+
# core.seams.tier_resolver) — execute_single_command supplies the real
|
|
64
|
+
# CLI-resolved tier; paid-tier handler tests must pass their own tier
|
|
65
|
+
# explicitly since OSS no longer has a permissive default to fall back on.
|
|
66
|
+
license_tier: Any = None
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@dataclass
|
|
70
|
+
class ConfigOnlyClient:
|
|
71
|
+
"""Config-only stand-in for commands that do not need a live provider."""
|
|
72
|
+
|
|
73
|
+
config: Any
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
ValidateSqlConfigClient = ConfigOnlyClient
|
|
67
77
|
|
|
68
78
|
|
|
69
79
|
def _set_command_completed(log: Any, result: Any, command_type: str) -> None:
|
|
@@ -92,7 +102,7 @@ def emit_rendered_output(
|
|
|
92
102
|
) -> None:
|
|
93
103
|
"""Dispatch a rendered command output to machine channel, logger, and/or file.
|
|
94
104
|
|
|
95
|
-
|
|
105
|
+
Shared by extension report handlers that follow the same machine/human/file
|
|
96
106
|
dispatch pattern once the command-specific rendering is done.
|
|
97
107
|
"""
|
|
98
108
|
if command_output.is_machine_format:
|
|
@@ -111,7 +121,7 @@ def emit_rendered_output(
|
|
|
111
121
|
|
|
112
122
|
|
|
113
123
|
def _extract_version_filters(args: Any) -> tuple[Any, Any, Any, Any, Any]:
|
|
114
|
-
"""Extract version/tag filter arguments common to
|
|
124
|
+
"""Extract version/tag filter arguments common to filtered migration handlers.
|
|
115
125
|
|
|
116
126
|
Returns:
|
|
117
127
|
Tuple (target_version, versions, exclude_versions, tags, exclude_tags),
|
|
@@ -50,13 +50,10 @@ from cli._config_helpers import ( # noqa: F401
|
|
|
50
50
|
_validate_log_format_for_cli,
|
|
51
51
|
)
|
|
52
52
|
from cli._output import CommandOutput, from_args
|
|
53
|
-
from cli._parser_setup import
|
|
54
|
-
_add_baseline_options,
|
|
55
|
-
_add_diff_and_target_options,
|
|
56
|
-
create_parser,
|
|
57
|
-
parse_with_selective_errors,
|
|
58
|
-
)
|
|
53
|
+
from cli._parser_setup import create_parser, parse_with_selective_errors
|
|
59
54
|
from cli.extensions import load_terminal_commands
|
|
55
|
+
from core.seams.feature_loading import load_feature_extensions
|
|
56
|
+
from core.seams.license_info import get_license_info
|
|
60
57
|
|
|
61
58
|
# Module-level placeholder; main() uses a local 'log' variable (no global declaration)
|
|
62
59
|
log = None
|
|
@@ -102,16 +99,13 @@ _GLOBAL_ONLY_ARGS: List[str] = [
|
|
|
102
99
|
"--quiet",
|
|
103
100
|
"-q",
|
|
104
101
|
"--no-progress",
|
|
105
|
-
# Root-only value flags
|
|
106
|
-
# (installed_by is added directly; max_snapshots is generated by
|
|
107
|
-
# _add_registry_flags). They live only on the root parser, so — like the
|
|
102
|
+
# Root-only value flags. They live only on the root parser, so — like the
|
|
108
103
|
# other root-only value flags above — they must be classified global or the
|
|
109
104
|
# argv preprocessor relocates them past the subcommand token and the
|
|
110
105
|
# subparser rejects them as "unrecognized arguments". Covered by
|
|
111
106
|
# tests/unit/cli/test_global_only_args_completeness.py.
|
|
112
107
|
"--installed-by",
|
|
113
108
|
"--max-snapshots",
|
|
114
|
-
"--snapshot-table",
|
|
115
109
|
]
|
|
116
110
|
|
|
117
111
|
# Tool-level flag aliases for subcommands that take their own version-like
|
|
@@ -145,7 +139,10 @@ class _CliContext:
|
|
|
145
139
|
log: Any # core.logger Log — bootstrap instance (re-assigned by phase 3).
|
|
146
140
|
config: Optional[Any] # config.DbliftConfig | None (None for `db` subcommands)
|
|
147
141
|
|
|
148
|
-
# Optional paid extension metadata.
|
|
142
|
+
# Optional paid extension metadata. Populated from the
|
|
143
|
+
# ``core.seams.license_info`` seam: ``None`` in a pure OSS install (no
|
|
144
|
+
# provider registered), a license-info dict when a higher tier registers one.
|
|
145
|
+
license_info: Optional[Any] = None
|
|
149
146
|
|
|
150
147
|
|
|
151
148
|
def main() -> None:
|
|
@@ -164,6 +161,7 @@ def main() -> None:
|
|
|
164
161
|
from core.logger.console import install_rich_traceback
|
|
165
162
|
|
|
166
163
|
install_rich_traceback()
|
|
164
|
+
load_feature_extensions()
|
|
167
165
|
|
|
168
166
|
ctx = _parse_argv_and_load_config(sys.argv[1:])
|
|
169
167
|
command_output = _setup_logging_and_output(ctx)
|
|
@@ -251,7 +249,8 @@ def _parse_argv_and_load_config(argv: List[str]) -> _CliContext:
|
|
|
251
249
|
# Validate log format before any config load so bogus --log-format fails with argparse
|
|
252
250
|
# UX (and so db/* commands are covered — _validate_db_config skips early for "db").
|
|
253
251
|
_validate_log_format_for_cli(args, parser)
|
|
254
|
-
# Attach the full command list so
|
|
252
|
+
# Attach the full command list so config loading can distinguish
|
|
253
|
+
# single-command config-only flows from chained invocations.
|
|
255
254
|
args.commands_list = commands
|
|
256
255
|
config = None if commands[0] == "db" else _load_and_merge_config(args, log)
|
|
257
256
|
_validate_db_config(args, config, parser, commands)
|
|
@@ -264,6 +263,7 @@ def _parse_argv_and_load_config(argv: List[str]) -> _CliContext:
|
|
|
264
263
|
parser=parser,
|
|
265
264
|
log=log,
|
|
266
265
|
config=config,
|
|
266
|
+
license_info=get_license_info(args),
|
|
267
267
|
)
|
|
268
268
|
|
|
269
269
|
|
|
@@ -277,6 +277,27 @@ def _build_terminal_args(
|
|
|
277
277
|
return args
|
|
278
278
|
|
|
279
279
|
|
|
280
|
+
def _command_handler_attr(command: Optional[str], attr_name: str, default: Any = None) -> Any:
|
|
281
|
+
if not command:
|
|
282
|
+
return default
|
|
283
|
+
handler = _COMMAND_HANDLERS.get(command)
|
|
284
|
+
if handler is None:
|
|
285
|
+
return default
|
|
286
|
+
return getattr(handler, attr_name, default)
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def _propagate_license_banner(log: Any, license_info: Optional[Any]) -> None:
|
|
290
|
+
"""Set ``license_info`` on every sub-logger's formatter so the banner
|
|
291
|
+
renders. No-op when ``license_info`` is falsy (pure OSS: no provider
|
|
292
|
+
registered on the ``core.seams.license_info`` seam)."""
|
|
293
|
+
if not license_info:
|
|
294
|
+
return
|
|
295
|
+
loggers = getattr(log, "logs", [log])
|
|
296
|
+
for sub_log in loggers:
|
|
297
|
+
if hasattr(sub_log, "formatter"):
|
|
298
|
+
sub_log.formatter.license_info = license_info
|
|
299
|
+
|
|
300
|
+
|
|
280
301
|
def _setup_logging_and_output(ctx: _CliContext) -> CommandOutput:
|
|
281
302
|
"""Phase 3: configure logging, build :class:`CommandOutput`.
|
|
282
303
|
|
|
@@ -305,6 +326,12 @@ def _setup_logging_and_output(ctx: _CliContext) -> CommandOutput:
|
|
|
305
326
|
|
|
306
327
|
assert ctx.config is not None
|
|
307
328
|
ctx.log = _configure_logging(ctx.args, ctx.config, ctx.parser)
|
|
329
|
+
# Propagate the license banner (if a higher tier registered a provider on
|
|
330
|
+
# the seam) onto the reconfigured loggers' formatters. No-op in a pure OSS
|
|
331
|
+
# install where ``ctx.license_info`` is ``None``.
|
|
332
|
+
_propagate_license_banner(ctx.log, ctx.license_info)
|
|
333
|
+
|
|
334
|
+
_apply_configured_output_format(ctx)
|
|
308
335
|
|
|
309
336
|
# Banner routing is centralised in :class:`cli._output.CommandOutput`
|
|
310
337
|
# (ADR-0008 supersedes ADR-0005's suppression approach). Machine
|
|
@@ -312,6 +339,18 @@ def _setup_logging_and_output(ctx: _CliContext) -> CommandOutput:
|
|
|
312
339
|
return from_args(ctx.args)
|
|
313
340
|
|
|
314
341
|
|
|
342
|
+
def _apply_configured_output_format(ctx: _CliContext) -> None:
|
|
343
|
+
"""Use handler-provided config defaults before top-level banner routing."""
|
|
344
|
+
if len(ctx.commands) != 1 or getattr(ctx.args, "format", None):
|
|
345
|
+
return
|
|
346
|
+
resolver = _command_handler_attr(ctx.commands[0], "_dblift_configured_output_format")
|
|
347
|
+
if not callable(resolver):
|
|
348
|
+
return
|
|
349
|
+
output_format = resolver(ctx.args, ctx.config)
|
|
350
|
+
if output_format:
|
|
351
|
+
ctx.args.format = output_format
|
|
352
|
+
|
|
353
|
+
|
|
315
354
|
def _dispatch_command(ctx: _CliContext, command_output: CommandOutput) -> int:
|
|
316
355
|
"""Phase 4: build full workflow context and run the command loop.
|
|
317
356
|
|
|
@@ -350,6 +389,7 @@ def _dispatch_command(ctx: _CliContext, command_output: CommandOutput) -> int:
|
|
|
350
389
|
)
|
|
351
390
|
schema_name = getattr(ctx.config.database, "schema", None)
|
|
352
391
|
formatter = TextFormatter()
|
|
392
|
+
formatter.license_info = ctx.license_info
|
|
353
393
|
main_header = formatter.format_header(schema_name, database_name)
|
|
354
394
|
|
|
355
395
|
from core.migration.commands import base_command
|
|
@@ -362,6 +402,13 @@ def _dispatch_command(ctx: _CliContext, command_output: CommandOutput) -> int:
|
|
|
362
402
|
# in base_command does not re-emit it (it also has no format awareness).
|
|
363
403
|
if main_header:
|
|
364
404
|
base_command._console_main_header_printed = True # type: ignore[attr-defined]
|
|
405
|
+
for command in ctx.commands:
|
|
406
|
+
handler = _COMMAND_HANDLERS.get(command)
|
|
407
|
+
if handler is None:
|
|
408
|
+
continue
|
|
409
|
+
command_module = sys.modules.get(handler.__module__)
|
|
410
|
+
if command_module is not None:
|
|
411
|
+
command_module._console_main_header_printed = True # type: ignore[attr-defined]
|
|
365
412
|
|
|
366
413
|
for cmd_index, command in enumerate(ctx.commands):
|
|
367
414
|
if len(ctx.commands) > 1 and cmd_index > 0:
|
|
@@ -199,9 +199,9 @@ def _apply_url_overrides(cls: Type["BaseDatabaseConfig"], data: Dict[str, Any])
|
|
|
199
199
|
if url_lower.startswith("jdbc:"):
|
|
200
200
|
raise ValueError("Legacy database URLs are no longer supported; use a SQLAlchemy URL")
|
|
201
201
|
# _allow_incomplete is set when the URL could not be parsed (e.g. an
|
|
202
|
-
# unresolved secret URI in offline mode). Skip hydration/inference so
|
|
203
|
-
#
|
|
204
|
-
# is opened.
|
|
202
|
+
# unresolved secret URI in offline mode). Skip hydration/inference so that
|
|
203
|
+
# `dblift plan` with `database.url: vault://...` doesn't mutate fields from
|
|
204
|
+
# the secret URI before a connection is opened.
|
|
205
205
|
if data.get("_allow_incomplete"):
|
|
206
206
|
return
|
|
207
207
|
if ProviderRegistry.is_native_dialect(db_type):
|
|
@@ -25,6 +25,56 @@ _CONFIG_LOAD_EXC: Tuple[Type[Exception], ...] = (
|
|
|
25
25
|
AttributeError,
|
|
26
26
|
IndexError,
|
|
27
27
|
)
|
|
28
|
+
_PAID_RAW_CONFIG_KEYS: Tuple[str, ...] = ("data_sets", "datasets", "validation")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# ``validate-sql`` only needs a dialect-typed DbliftConfig; connection is never opened.
|
|
32
|
+
# Placeholder database URLs satisfy :meth:`DbliftConfig.validate_complete_data` and are never
|
|
33
|
+
# used to connect in the validate-sql code path.
|
|
34
|
+
def _validate_sql_lint_filler(dialect: str) -> Dict[str, Any]:
|
|
35
|
+
"""Minimal ``database:`` block for validate-sql (lint-only) when no real DB is configured.
|
|
36
|
+
|
|
37
|
+
Story 26-11: dialect normalisation + placeholder URL come from the
|
|
38
|
+
plugin registry / quirks. The earlier hardcoded
|
|
39
|
+
``_VALIDATE_SQL_DIALECT_TO_DB``, ``_VALIDATE_SQL_PLACEHOLDER_URL``,
|
|
40
|
+
and ``_VALIDATE_SQL_DB_TO_DIALECT`` dicts are gone; adding a new
|
|
41
|
+
dialect with offline-lint support = override
|
|
42
|
+
``lint_placeholder_url`` in its plugin quirks.py.
|
|
43
|
+
"""
|
|
44
|
+
from db.provider_registry import ProviderRegistry
|
|
45
|
+
|
|
46
|
+
db_type = ProviderRegistry.canonical_dialect_name((dialect or "").strip().lower())
|
|
47
|
+
if not db_type:
|
|
48
|
+
return {}
|
|
49
|
+
quirks = ProviderRegistry.get_quirks(db_type)
|
|
50
|
+
placeholder = quirks.lint_placeholder_url
|
|
51
|
+
if not placeholder:
|
|
52
|
+
return {}
|
|
53
|
+
return {
|
|
54
|
+
"database": {
|
|
55
|
+
"type": db_type,
|
|
56
|
+
"url": placeholder,
|
|
57
|
+
"username": "dblift_validate_sql",
|
|
58
|
+
"password": "dblift_validate_sql",
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _validate_sql_effective_dialect(args: Any, config_data: Dict[str, Any]) -> Optional[str]:
|
|
64
|
+
"""Resolve validate-sql dialect from CLI or config without defaulting."""
|
|
65
|
+
cli_dialect = getattr(args, "dialect", None)
|
|
66
|
+
if isinstance(cli_dialect, str) and cli_dialect.strip():
|
|
67
|
+
return cli_dialect.strip().lower()
|
|
68
|
+
|
|
69
|
+
database_config = config_data.get("database")
|
|
70
|
+
if isinstance(database_config, dict):
|
|
71
|
+
from db.provider_registry import ProviderRegistry
|
|
72
|
+
|
|
73
|
+
db_type = str(database_config.get("type") or "").strip().lower()
|
|
74
|
+
if db_type:
|
|
75
|
+
return ProviderRegistry.canonical_dialect_name(db_type) or db_type
|
|
76
|
+
|
|
77
|
+
return None
|
|
28
78
|
|
|
29
79
|
|
|
30
80
|
def _placeholder_tokens(raw_placeholders: Any) -> List[str]:
|
|
@@ -79,6 +129,15 @@ def _resolve_env_placeholders(data: Any) -> Any:
|
|
|
79
129
|
return data
|
|
80
130
|
|
|
81
131
|
|
|
132
|
+
def _extract_paid_raw_config(data: Dict[str, Any]) -> Dict[str, Any]:
|
|
133
|
+
paid_raw: Dict[str, Any] = {}
|
|
134
|
+
for key in _PAID_RAW_CONFIG_KEYS:
|
|
135
|
+
value = data.get(key)
|
|
136
|
+
if value is not None:
|
|
137
|
+
paid_raw[key] = value
|
|
138
|
+
return paid_raw
|
|
139
|
+
|
|
140
|
+
|
|
82
141
|
def load_config(config_file_path: Optional[str], args: Optional[Any] = None) -> "DbliftConfig":
|
|
83
142
|
"""
|
|
84
143
|
Load configuration from a file and override with command line arguments
|
|
@@ -119,13 +178,33 @@ def load_config(config_file_path: Optional[str], args: Optional[Any] = None) ->
|
|
|
119
178
|
if args_dict:
|
|
120
179
|
config_data = DbliftConfig.merge_config_data(config_data, args_dict)
|
|
121
180
|
|
|
181
|
+
# ``validate-sql`` does not need a real database, but static linting does
|
|
182
|
+
# need an explicit SQL dialect or a database type from config.
|
|
183
|
+
if args and getattr(args, "command", None) == "validate-sql":
|
|
184
|
+
dialect = _validate_sql_effective_dialect(args, config_data)
|
|
185
|
+
if not dialect:
|
|
186
|
+
raise ConfigurationError(
|
|
187
|
+
"validate-sql requires --dialect for offline validation when no database type is configured."
|
|
188
|
+
)
|
|
189
|
+
filler = _validate_sql_lint_filler(dialect)
|
|
190
|
+
if filler:
|
|
191
|
+
config_data = DbliftConfig.merge_config_data(filler, config_data)
|
|
192
|
+
|
|
122
193
|
if not config_data:
|
|
123
194
|
raise ConfigurationError(
|
|
124
195
|
"No configuration source provided. Pass --config, --db-url, or set DBLIFT_DB_URL."
|
|
125
196
|
)
|
|
126
197
|
|
|
127
|
-
|
|
198
|
+
command = getattr(args, "command", None) if args else None
|
|
199
|
+
commands_list = getattr(args, "commands_list", None) if args else None
|
|
200
|
+
if commands_list is None and command:
|
|
201
|
+
commands_list = [command]
|
|
202
|
+
is_offline_cmd = bool(
|
|
203
|
+
command in ("validate-sql", "plan") and commands_list and len(commands_list) == 1
|
|
204
|
+
)
|
|
128
205
|
config = DbliftConfig.from_dict(config_data, resolve_secrets=not is_offline_cmd)
|
|
206
|
+
if config_file_path:
|
|
207
|
+
setattr(config, "_config_file_path", config_file_path)
|
|
129
208
|
if args:
|
|
130
209
|
installed_by = getattr(args, "installed_by", None)
|
|
131
210
|
if installed_by:
|
|
@@ -431,13 +510,14 @@ class DbliftConfig:
|
|
|
431
510
|
Args:
|
|
432
511
|
data: Configuration dictionary
|
|
433
512
|
resolve_secrets: When False, skip secret-URI resolution (used for
|
|
434
|
-
offline commands that never open a DB connection
|
|
435
|
-
require secret-manager credentials to be available).
|
|
513
|
+
offline commands such as static SQL lint that never open a DB connection
|
|
514
|
+
and must not require secret-manager credentials to be available).
|
|
436
515
|
|
|
437
516
|
Returns:
|
|
438
517
|
A DbliftConfig instance
|
|
439
518
|
"""
|
|
440
519
|
data = _resolve_env_placeholders(data or {})
|
|
520
|
+
paid_raw_config = _extract_paid_raw_config(data)
|
|
441
521
|
|
|
442
522
|
if resolve_secrets:
|
|
443
523
|
# Two-phase secret resolution:
|
|
@@ -483,7 +563,7 @@ class DbliftConfig:
|
|
|
483
563
|
# Non-secret unparseable URLs are left as-is so _apply_url_overrides
|
|
484
564
|
# raises a focused URL error immediately rather than producing a
|
|
485
565
|
# silently broken config.
|
|
486
|
-
# When secrets are not resolved (offline commands),
|
|
566
|
+
# When secrets are not resolved (offline commands like plan/validate-sql),
|
|
487
567
|
# a secret URI in database.url must not be validated as a database URL — the
|
|
488
568
|
# type may already be known from database.type but the URL is still raw.
|
|
489
569
|
# This covers the case where database.type is set explicitly, which skips
|
|
@@ -614,6 +694,8 @@ class DbliftConfig:
|
|
|
614
694
|
secrets=secrets_config,
|
|
615
695
|
)
|
|
616
696
|
config.log_dir = root_log_dir
|
|
697
|
+
if paid_raw_config:
|
|
698
|
+
setattr(config, "_paid_config_data", paid_raw_config)
|
|
617
699
|
return config
|
|
618
700
|
|
|
619
701
|
@staticmethod
|
|
@@ -922,7 +1004,9 @@ class DbliftConfig:
|
|
|
922
1004
|
config_data = _resolve_env_placeholders(config_data)
|
|
923
1005
|
if not config_data:
|
|
924
1006
|
raise yaml.YAMLError("Empty or invalid YAML file")
|
|
925
|
-
|
|
1007
|
+
config = cls.from_dict(config_data)
|
|
1008
|
+
setattr(config, "_config_file_path", str(config_file))
|
|
1009
|
+
return config
|
|
926
1010
|
except FileNotFoundError:
|
|
927
1011
|
raise FileNotFoundError(f"Config file not found: {config_file}")
|
|
928
1012
|
except yaml.YAMLError as e:
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
"""Provider class registry — maps URI schemes to provider classes."""
|
|
2
2
|
|
|
3
|
-
from importlib import import_module
|
|
4
3
|
from importlib.metadata import entry_points
|
|
5
4
|
from typing import Dict, Optional, Set, Type
|
|
6
5
|
|
|
7
6
|
# Fixed set of known schemes — used for fast URI detection without requiring
|
|
8
7
|
# providers to be registered first (avoids import-order sensitivity).
|
|
9
|
-
KNOWN_SCHEMES: frozenset = frozenset(
|
|
8
|
+
KNOWN_SCHEMES: frozenset = frozenset(
|
|
9
|
+
["vault", "aws-secrets", "aws-ssm", "azure-keyvault", "gcp-secrets"]
|
|
10
|
+
)
|
|
10
11
|
|
|
11
12
|
# Populated by providers/__init__.py at import time.
|
|
12
13
|
_providers: Dict[str, Type] = {}
|