dblift 2.0.4__tar.gz → 2.1.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-2.0.4 → dblift-2.1.0}/PKG-INFO +29 -8
- {dblift-2.0.4 → dblift-2.1.0}/README.md +28 -7
- {dblift-2.0.4 → dblift-2.1.0}/api/_client_factory.py +7 -7
- {dblift-2.0.4 → dblift-2.1.0}/api/_engine_config.py +4 -1
- {dblift-2.0.4 → dblift-2.1.0}/api/client.py +8 -6
- {dblift-2.0.4 → dblift-2.1.0}/cli/_config_helpers.py +1 -1
- {dblift-2.0.4 → dblift-2.1.0}/cli/_parser_setup.py +18 -0
- {dblift-2.0.4 → dblift-2.1.0}/cli/handlers/_shared.py +5 -0
- {dblift-2.0.4 → dblift-2.1.0}/cli/main.py +1 -0
- dblift-2.1.0/config/_subclasses/__init__.py +15 -0
- {dblift-2.0.4 → dblift-2.1.0}/config/config_builder.py +44 -8
- {dblift-2.0.4 → dblift-2.1.0}/config/database_config.py +26 -40
- {dblift-2.0.4 → dblift-2.1.0}/config/dblift_config.py +16 -25
- {dblift-2.0.4 → dblift-2.1.0}/core/constants.py +9 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/dialect_boundary.py +28 -0
- dblift-2.1.0/core/features.py +123 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/introspection/extractors/constraint_extractor.py +4 -13
- dblift-2.1.0/core/introspection/version_detector.py +41 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/logger/console.py +72 -1
- {dblift-2.0.4 → dblift-2.1.0}/core/logger/formatters/diff_utils.py +24 -6
- {dblift-2.0.4 → dblift-2.1.0}/core/logger/formatters/htmlformatter.py +5 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/logger/log.py +21 -3
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/commands/import_flyway_command.py +3 -2
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/executor/execution_engine.py +10 -12
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/executors/sql_executor.py +7 -4
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/migration.py +26 -2
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/scripting/undo_script_generator/_dml_reversers.py +4 -4
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/scripting/undo_script_generator/_extractors.py +7 -8
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/scripting/undo_script_generator/_generator.py +1 -1
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/scripting/undo_script_generator/_helpers.py +31 -8
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/scripting/undo_script_generator/_reversers.py +4 -4
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/sql/sql_analyzer.py +3 -2
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/sql/statement_splitter.py +1 -3
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/ui/table_renderer.py +21 -28
- {dblift-2.0.4 → dblift-2.1.0}/core/normalization/dependency_resolver.py +12 -3
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/__init__.py +3 -2
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/constraint_validator.py +3 -4
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/database_link.py +1 -6
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/dialect.py +83 -140
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/event.py +4 -20
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/extension.py +1 -6
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/foreign_data_wrapper.py +1 -6
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/foreign_server.py +1 -6
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/index.py +5 -51
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/linked_server.py +1 -6
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/module.py +1 -6
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/package.py +4 -4
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/procedure.py +4 -40
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/sequence.py +4 -39
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/table.py +145 -349
- dblift-2.1.0/core/sql_model/table_options.py +162 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/trigger.py +5 -54
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/view.py +11 -92
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_parser/sqlglot_parser.py +1 -1
- {dblift-2.0.4 → dblift-2.1.0}/core/state/sql_statement.py +5 -2
- {dblift-2.0.4 → dblift-2.1.0}/core/utils/database_url_parser.py +10 -10
- {dblift-2.0.4 → dblift-2.1.0}/db/base_provider.py +64 -1
- {dblift-2.0.4 → dblift-2.1.0}/db/base_quirks.py +249 -1
- dblift-2.1.0/db/dml_analysis.py +656 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/error.py +21 -110
- {dblift-2.0.4 → dblift-2.1.0}/db/native_connection_manager.py +2 -3
- dblift-2.1.0/db/object_naming.py +58 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/base_snapshot_manager.py +15 -40
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/cosmosdb/connection_manager.py +1 -1
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/cosmosdb/query_executor.py +2 -2
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/plugin.py +1 -1
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/quirks.py +4 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/db2/plugin.py +1 -1
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/db2/quirks.py +58 -3
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mariadb/quirks.py +13 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mysql/plugin.py +1 -1
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mysql/quirks.py +123 -6
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/plugin.py +1 -1
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/quirks.py +87 -2
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/postgresql/plugin.py +1 -1
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/postgresql/quirks.py +40 -5
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlite/plugin.py +1 -1
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlserver/plugin.py +1 -1
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlserver/quirks.py +22 -7
- {dblift-2.0.4 → dblift-2.1.0}/db/provider_interfaces.py +22 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/provider_registry.py +59 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/sqlalchemy_provider.py +8 -6
- {dblift-2.0.4 → dblift-2.1.0}/dblift.egg-info/PKG-INFO +29 -8
- {dblift-2.0.4 → dblift-2.1.0}/dblift.egg-info/SOURCES.txt +8 -8
- {dblift-2.0.4 → dblift-2.1.0}/integrations/django/_engine.py +1 -1
- {dblift-2.0.4 → dblift-2.1.0}/integrations/flask.py +1 -0
- {dblift-2.0.4 → dblift-2.1.0}/pyproject.toml +12 -10
- dblift-2.0.4/config/_subclasses/__init__.py +0 -12
- dblift-2.0.4/core/features.py +0 -28
- dblift-2.0.4/core/introspection/capability_matrix.py +0 -276
- dblift-2.0.4/core/introspection/version_detector.py +0 -279
- dblift-2.0.4/core/sql_model/table_options.py +0 -73
- dblift-2.0.4/db/object_naming.py +0 -39
- {dblift-2.0.4 → dblift-2.1.0}/LICENSE +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/api/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/api/_cli_support.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/api/_client_operations.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/api/async_client.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/api/events.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/api/migrations.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/api/py.typed +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/cli/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/cli/_command_handlers.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/cli/_constants.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/cli/_output.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/cli/db_utils.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/cli/extensions.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/cli/handlers/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/cli/handlers/baseline.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/cli/handlers/clean.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/cli/handlers/import_flyway.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/cli/handlers/info.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/cli/handlers/migrate.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/cli/handlers/repair.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/cli/handlers/undo.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/cli/handlers/validate.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/config/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/config/_credential_masking.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/config/_subclasses/dummy_config.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/config/_url_builder_mixin.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/config/errors.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/config/secrets/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/config/secrets/_cache.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/config/secrets/_provider_base.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/config/secrets/_registry.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/config/secrets/_resolver.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/config/secrets/_secrets_config.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/exceptions.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/introspection/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/introspection/_column_enricher.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/introspection/_partition_enricher.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/introspection/_schema_orchestrator.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/introspection/_utils.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/introspection/_vendor_property_applier.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/introspection/base_introspector.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/introspection/extractors/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/introspection/extractors/base_extractor.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/introspection/extractors/column_extractor.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/introspection/extractors/index_extractor.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/introspection/extractors/misc_extractor.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/introspection/extractors/procedure_extractor.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/introspection/extractors/sequence_extractor.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/introspection/extractors/table_extractor.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/introspection/extractors/trigger_extractor.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/introspection/extractors/view_extractor.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/introspection/introspector_factory.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/introspection/result.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/introspection/schema_introspector.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/introspection/vendor_queries_base.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/introspection/vendor_queries_factory.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/introspection/vendor_queries_protocols.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/logger/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/logger/_base.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/logger/_factory.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/logger/_formatters.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/logger/_levels.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/logger/_multi.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/logger/_null.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/logger/formatters/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/logger/formatters/_formatter_impl.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/logger/formatters/factory.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/logger/formatters/formatter.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/logger/formatters/jsonformatter.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/logger/results.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/_type_match.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/clean_summary.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/commands/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/commands/_script_events.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/commands/base_command.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/commands/baseline_command.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/commands/clean_command.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/commands/info_command.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/commands/migrate_command.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/commands/repair_command.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/commands/undo_command.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/commands/validate_command.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/encoding.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/executor/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/executor/migration_executor.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/executor/migration_helpers.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/executor/placeholder_manager.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/executor/transaction_policy.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/executors/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/executors/base_executor.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/executors/executor_factory.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/executors/python_executor.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/formats/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/formats/format_detector.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/formats/migration_format.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/history/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/history/migration_history_manager.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/journals/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/journals/migration_journal.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/placeholders/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/placeholders/placeholder_service.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/rules/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/rules/migration_rules.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/scripting/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/scripting/migration_script_manager.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/scripting/undo_script_generator/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/scripting/undo_script_generator/_ddl_reversers.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/scripting/undo_script_generator/_models.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/sql/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/sql/execution_statement.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/sql/sql_execution_service.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/state/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/state/migration_data_service.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/state/migration_display_state.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/state/migration_formatter.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/state/migration_state.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/state/migration_state_manager.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/state/migration_state_service.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/ui/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/ui/data_collector.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/ui/display_formatters.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/ui/migration_analyzer.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/ui/migration_ui.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/migration/version_utils.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/normalization/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/normalization/data_type_normalizer.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/normalization/identifier_normalizer.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/normalization/object_orderer.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/normalization/type_constants.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/normalization/type_mapper.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/normalization/type_mappings.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/normalization/type_normalizer.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/seams/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/seams/event_listeners.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/seams/introspection.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/_base_parse_result.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/_base_sql_column.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/_base_sql_constraint.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/_base_sql_object.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/_base_sql_statement.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/base.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/partition.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/synonym.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/table_canonicalizer.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/user_defined_type.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/view_options.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_parser/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_parser/_partition_handler.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_parser/_sqlglot_builders.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_parser/base_statement_parser.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_parser/base_tokenizer.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_parser/common/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_parser/common/base_parser.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_parser/dialects/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_parser/dialects/base_config.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_parser/enhanced_regex_parser.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_parser/hybrid_parser.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_parser/parser_context.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_parser/parser_factory.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_parser/parser_interface.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_parser/tokens.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_parser/unified_regex_parser.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_validator/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_validator/_checksum_validator.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_validator/_flyway_compatibility.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_validator/_migration_filter.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_validator/_sql_syntax_validator.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_validator/_strict_mode_validator.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/sql_validator/migration_validator.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/state/sql_script_formatter.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/utils/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/utils/metadata_helpers.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/utils/row_access.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/utils/string_utils.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/core/utils/url_masking.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/constants.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/error_handler.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/exceptions.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/base_history_manager.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/base_locking_manager.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/base_query_executor.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/base_schema_operations.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/base_undo_manager.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/__init__.py +0 -0
- /dblift-2.0.4/config/_subclasses/cosmosdb_config.py → /dblift-2.1.0/db/plugins/cosmosdb/config.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/cosmosdb/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/cosmosdb/_sdk.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/cosmosdb/history_manager.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/cosmosdb/locking_manager.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/cosmosdb/schema_operations.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/introspection/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/parser/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/parser/cosmosdb_regex_parser.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/provider.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/sdk_translator/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/sdk_translator/_executor.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/sdk_translator/_executors.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/sdk_translator/_parsing.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/sdk_translator/_script_generation.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/sdk_translator/_translator.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/sdk_translator/_translators.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/db2/__init__.py +0 -0
- /dblift-2.0.4/config/_subclasses/db2_config.py → /dblift-2.1.0/db/plugins/db2/config.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/db2/db2/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/db2/db2/history_manager.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/db2/db2/locking_manager.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/db2/db2/schema_operations.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/db2/introspection/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/db2/parser/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/db2/parser/db2_regex_parser.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/db2/parser/parser_config.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/db2/provider.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/db2/sqlalchemy_url.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mariadb/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mariadb/plugin.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mariadb/provider.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mysql/__init__.py +0 -0
- /dblift-2.0.4/config/_subclasses/mysql_config.py → /dblift-2.1.0/db/plugins/mysql/config.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mysql/introspection/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mysql/mysql/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mysql/mysql/history_manager.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mysql/mysql/locking_manager.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mysql/mysql/schema_operations.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mysql/parser/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mysql/parser/mysql_regex_parser.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mysql/parser/mysql_statement_parser.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mysql/parser/mysql_tokenizer.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mysql/parser/parser_config.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mysql/provider.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mysql/sqlalchemy_url.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/__init__.py +0 -0
- /dblift-2.0.4/config/_subclasses/oracle_config.py → /dblift-2.1.0/db/plugins/oracle/config.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/introspection/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/introspection/oracle_utils.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/oracle/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/oracle/dbms_output.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/oracle/history_manager.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/oracle/locking_manager.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/oracle/schema_operations.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/parser/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/parser/_comments.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/parser/_object_extractor.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/parser/_plsql_block.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/parser/_sqlplus.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/parser/_statement_splitter.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/parser/oracle_parser.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/parser/oracle_statement_parser.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/parser/oracle_tokenizer.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/parser/parser_config.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/parser/sqlplus_context.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/provider.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/sqlalchemy_url.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/postgresql/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/postgresql/_provider_query_executor.py +0 -0
- /dblift-2.0.4/config/_subclasses/postgresql_config.py → /dblift-2.1.0/db/plugins/postgresql/config.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/postgresql/introspection/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/postgresql/parser/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/postgresql/parser/parser_config.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/postgresql/parser/postgresql_regex_parser.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/postgresql/parser/postgresql_statement_parser.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/postgresql/parser/postgresql_tokenizer.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/postgresql/postgresql/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/postgresql/postgresql/history_manager.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/postgresql/postgresql/locking_manager.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/postgresql/postgresql/schema_operations.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/postgresql/provider.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/postgresql/sqlalchemy_url.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlite/__init__.py +0 -0
- /dblift-2.0.4/config/_subclasses/sqlite_config.py → /dblift-2.1.0/db/plugins/sqlite/config.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlite/introspection/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlite/parser/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlite/parser/parser_config.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlite/parser/sqlite_regex_parser.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlite/provider.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlite/quirks.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlite/sqlalchemy_url.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlite/sqlite/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlite/sqlite/connection_manager.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlite/sqlite/history_manager.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlite/sqlite/locking_manager.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlite/sqlite/query_executor.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlite/sqlite/schema_operations.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlserver/__init__.py +0 -0
- /dblift-2.0.4/config/_subclasses/sqlserver_config.py → /dblift-2.1.0/db/plugins/sqlserver/config.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlserver/introspection/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlserver/parser/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlserver/parser/parser_config.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlserver/parser/sqlserver_regex_parser.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlserver/parser/sqlserver_statement_parser.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlserver/parser/sqlserver_tokenizer.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlserver/parser/tsql_batch_separator.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlserver/provider.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlserver/sqlalchemy_url.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlserver/sqlserver/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlserver/sqlserver/history_manager.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlserver/sqlserver/locking_manager.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlserver/sqlserver/schema_operations.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/provider_capabilities.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/db/value_utils.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/dblift.egg-info/dependency_links.txt +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/dblift.egg-info/entry_points.txt +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/dblift.egg-info/requires.txt +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/dblift.egg-info/top_level.txt +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/integrations/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/integrations/django/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/integrations/django/_client.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/integrations/django/apps.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/integrations/django/checks.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/integrations/django/management/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/integrations/django/management/commands/__init__.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/integrations/django/management/commands/dblift_info.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/integrations/django/management/commands/dblift_migrate.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/integrations/django/management/commands/dblift_validate.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/integrations/fastapi.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/integrations/opentelemetry.py +0 -0
- {dblift-2.0.4 → dblift-2.1.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dblift
|
|
3
|
-
Version: 2.0
|
|
3
|
+
Version: 2.1.0
|
|
4
4
|
Summary: A database migration tool that supports multiple databases with native Python drivers
|
|
5
5
|
Classifier: Development Status :: 5 - Production/Stable
|
|
6
6
|
Classifier: Intended Audience :: Developers
|
|
@@ -78,22 +78,30 @@ Provides-Extra: all
|
|
|
78
78
|
Requires-Dist: dblift[db2,mariadb,mysql,oracle,postgresql,sqlserver]; extra == "all"
|
|
79
79
|
Dynamic: license-file
|
|
80
80
|
|
|
81
|
-
# DBLift
|
|
81
|
+
# DBLift — Database release safety for Python teams
|
|
82
82
|
|
|
83
83
|
<p align="center">
|
|
84
|
-
<img src="logo/dblift_logo.png" width="
|
|
84
|
+
<img src="logo/dblift_logo.png" width="280" alt="DBLift Logo">
|
|
85
85
|
</p>
|
|
86
86
|
|
|
87
|
-
**Manage your database changes with confidence**
|
|
88
|
-
|
|
89
|
-
DBLift helps you track and apply database changes systematically. Think of it as version control for your database schema - every change is tracked, can be rolled back, and works consistently across different environments.
|
|
90
|
-
|
|
91
87
|

|
|
92
88
|

|
|
93
|
-
|
|
94
89
|
[](https://github.com/cmodiano/dblift-oss/actions/workflows/unit-tests.yml)
|
|
90
|
+
[](https://codecov.io/gh/cmodiano/dblift-oss)
|
|
95
91
|
[](SECURITY.md)
|
|
96
92
|
|
|
93
|
+
Application code usually has tests, CI pipelines, and pull request review. Database changes often skip one or more of those stages. DBLift is a Python-native migration toolkit that shows you exactly what SQL will run before it runs — and keeps the full migration lifecycle inside your Python workflow. No JVM required.
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
pip install "dblift[postgresql]"
|
|
97
|
+
|
|
98
|
+
dblift validate # check migration state
|
|
99
|
+
dblift migrate --dry-run --show-sql # preview exact SQL before it runs
|
|
100
|
+
dblift migrate # apply when ready
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
> **OSS tier:** The commands above are free and open source (Apache 2.0). Static SQL analysis (`dblift validate-sql`) and schema drift detection (`dblift diff`) are [Pro features](https://dblift.com/pricing).
|
|
104
|
+
|
|
97
105
|
---
|
|
98
106
|
|
|
99
107
|
## Table of Contents
|
|
@@ -998,6 +1006,19 @@ dblift is a pip package with no system dependencies — CI is `pip install` plus
|
|
|
998
1006
|
command. See [CI/CD recipes](docs/user-guide/ci-cd.md) for copy-paste GitHub
|
|
999
1007
|
Actions, GitLab CI, and pre-commit configurations (OSS `validate` / `info`).
|
|
1000
1008
|
|
|
1009
|
+
## What Pro adds
|
|
1010
|
+
|
|
1011
|
+
The OSS tier covers the full migration lifecycle: apply, preview, validate state,
|
|
1012
|
+
roll back, and import from Flyway. When review risk grows, the Pro tier adds:
|
|
1013
|
+
|
|
1014
|
+
| Feature | Command | What it does |
|
|
1015
|
+
|---|---|---|
|
|
1016
|
+
| Static SQL analysis | `dblift validate-sql` | Lints migration files with rule-based checks — catches issues before they reach the database. Built-in rule profiles (core, enterprise, strict). CI-friendly output formats (GitHub Actions, SARIF, GitLab). |
|
|
1017
|
+
| Schema drift detection | `dblift diff` | Compares live database state with what your migrations define. Surfaces objects that have drifted. |
|
|
1018
|
+
| Schema export | `dblift export-schema` | Exports the current schema to SQL migration files. Useful for brownfield onboarding. |
|
|
1019
|
+
|
|
1020
|
+
[See pricing and Pro features →](https://dblift.com/pricing)
|
|
1021
|
+
|
|
1001
1022
|
## API Reference
|
|
1002
1023
|
|
|
1003
1024
|
The Python SDK (`DBLiftClient`, `AsyncDBLiftClient`, `MigrationContext`), CLI,
|
|
@@ -1,19 +1,27 @@
|
|
|
1
|
-
# DBLift
|
|
1
|
+
# DBLift — Database release safety for Python teams
|
|
2
2
|
|
|
3
3
|
<p align="center">
|
|
4
|
-
<img src="logo/dblift_logo.png" width="
|
|
4
|
+
<img src="logo/dblift_logo.png" width="280" alt="DBLift Logo">
|
|
5
5
|
</p>
|
|
6
6
|
|
|
7
|
-
**Manage your database changes with confidence**
|
|
8
|
-
|
|
9
|
-
DBLift helps you track and apply database changes systematically. Think of it as version control for your database schema - every change is tracked, can be rolled back, and works consistently across different environments.
|
|
10
|
-
|
|
11
7
|

|
|
12
8
|

|
|
13
|
-
|
|
14
9
|
[](https://github.com/cmodiano/dblift-oss/actions/workflows/unit-tests.yml)
|
|
10
|
+
[](https://codecov.io/gh/cmodiano/dblift-oss)
|
|
15
11
|
[](SECURITY.md)
|
|
16
12
|
|
|
13
|
+
Application code usually has tests, CI pipelines, and pull request review. Database changes often skip one or more of those stages. DBLift is a Python-native migration toolkit that shows you exactly what SQL will run before it runs — and keeps the full migration lifecycle inside your Python workflow. No JVM required.
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install "dblift[postgresql]"
|
|
17
|
+
|
|
18
|
+
dblift validate # check migration state
|
|
19
|
+
dblift migrate --dry-run --show-sql # preview exact SQL before it runs
|
|
20
|
+
dblift migrate # apply when ready
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
> **OSS tier:** The commands above are free and open source (Apache 2.0). Static SQL analysis (`dblift validate-sql`) and schema drift detection (`dblift diff`) are [Pro features](https://dblift.com/pricing).
|
|
24
|
+
|
|
17
25
|
---
|
|
18
26
|
|
|
19
27
|
## Table of Contents
|
|
@@ -918,6 +926,19 @@ dblift is a pip package with no system dependencies — CI is `pip install` plus
|
|
|
918
926
|
command. See [CI/CD recipes](docs/user-guide/ci-cd.md) for copy-paste GitHub
|
|
919
927
|
Actions, GitLab CI, and pre-commit configurations (OSS `validate` / `info`).
|
|
920
928
|
|
|
929
|
+
## What Pro adds
|
|
930
|
+
|
|
931
|
+
The OSS tier covers the full migration lifecycle: apply, preview, validate state,
|
|
932
|
+
roll back, and import from Flyway. When review risk grows, the Pro tier adds:
|
|
933
|
+
|
|
934
|
+
| Feature | Command | What it does |
|
|
935
|
+
|---|---|---|
|
|
936
|
+
| Static SQL analysis | `dblift validate-sql` | Lints migration files with rule-based checks — catches issues before they reach the database. Built-in rule profiles (core, enterprise, strict). CI-friendly output formats (GitHub Actions, SARIF, GitLab). |
|
|
937
|
+
| Schema drift detection | `dblift diff` | Compares live database state with what your migrations define. Surfaces objects that have drifted. |
|
|
938
|
+
| Schema export | `dblift export-schema` | Exports the current schema to SQL migration files. Useful for brownfield onboarding. |
|
|
939
|
+
|
|
940
|
+
[See pricing and Pro features →](https://dblift.com/pricing)
|
|
941
|
+
|
|
921
942
|
## API Reference
|
|
922
943
|
|
|
923
944
|
The Python SDK (`DBLiftClient`, `AsyncDBLiftClient`, `MigrationContext`), CLI,
|
|
@@ -349,7 +349,6 @@ def client_from_sqlalchemy(
|
|
|
349
349
|
logger = build_default_logger(derived, log_level, log_format, log_file)
|
|
350
350
|
|
|
351
351
|
provider = ProviderRegistry.create_provider(derived, logger)
|
|
352
|
-
dialect_name = str(getattr(engine.dialect, "name", ""))
|
|
353
352
|
|
|
354
353
|
# Inject external engine so provider re-uses caller's Engine/Connection
|
|
355
354
|
# (ownership=False prevents dispose on client/provider close).
|
|
@@ -357,14 +356,15 @@ def client_from_sqlalchemy(
|
|
|
357
356
|
provider._conn_mgr = NativeConnectionManager(
|
|
358
357
|
derived, logger, engine=engine, owns_engine=False
|
|
359
358
|
) # noqa: E501
|
|
360
|
-
elif
|
|
359
|
+
elif hasattr(provider, "attach_external_sqlalchemy"):
|
|
361
360
|
# The native SQLite provider talks to ``sqlite3`` directly instead of
|
|
362
361
|
# through a NativeConnectionManager, so the branch above never fires for
|
|
363
|
-
# it.
|
|
364
|
-
#
|
|
365
|
-
#
|
|
366
|
-
#
|
|
367
|
-
#
|
|
362
|
+
# it. It is the only provider exposing ``attach_external_sqlalchemy`` —
|
|
363
|
+
# the capability of reaching through a caller's SQLAlchemy engine to its
|
|
364
|
+
# underlying DBAPI connection. Without this, the provider would open its
|
|
365
|
+
# *own* ``sqlite3`` connection and migrate a different database than the
|
|
366
|
+
# caller's engine — fatal for ``sqlite:///:memory:`` where every
|
|
367
|
+
# connection is a separate in-memory DB.
|
|
368
368
|
_attach_external_sqlite_connection(provider, engine, connection)
|
|
369
369
|
|
|
370
370
|
# When a specific Connection was passed, bind it directly so that
|
|
@@ -47,7 +47,10 @@ def config_from_engine(
|
|
|
47
47
|
scheme = engine.url.drivername.split("+", 1)[0]
|
|
48
48
|
db_type = ProviderRegistry.canonical_dialect_name(scheme) or scheme
|
|
49
49
|
except Exception:
|
|
50
|
-
|
|
50
|
+
# The provider was already matched above, so it carries its own
|
|
51
|
+
# canonical dialect name — derive the fallback from it instead of
|
|
52
|
+
# naming a dialect here.
|
|
53
|
+
db_type = getattr(provider_cls, "canonical_dialect_key", "")
|
|
51
54
|
|
|
52
55
|
db_dict: dict[str, Any] = {"url": url, "type": db_type}
|
|
53
56
|
if schema is not None:
|
|
@@ -203,12 +203,14 @@ class DBLiftClient:
|
|
|
203
203
|
raise ValueError(
|
|
204
204
|
"Database type is configured but empty. Please set config.database.type to a valid dialect."
|
|
205
205
|
)
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
206
|
+
if config_dialect:
|
|
207
|
+
return str(config_dialect).lower()
|
|
208
|
+
# Neither provider nor config supplied a dialect: fall back to the
|
|
209
|
+
# registry-derived generic dialect rather than a hardcoded literal
|
|
210
|
+
# (ADR-26 E5).
|
|
211
|
+
from core.migration.migration import _default_splitter_dialect
|
|
212
|
+
|
|
213
|
+
return _default_splitter_dialect().lower()
|
|
212
214
|
|
|
213
215
|
@_with_client_emitter
|
|
214
216
|
def migrate(
|
|
@@ -75,7 +75,7 @@ def _extract_commands_from_argv(
|
|
|
75
75
|
i += 1
|
|
76
76
|
# Commands with subcommands: stop extracting further commands,
|
|
77
77
|
# but continue loop so global args are still extracted
|
|
78
|
-
if arg in ("db", "license"):
|
|
78
|
+
if arg in ("db", "license", "data"):
|
|
79
79
|
stop_command_extraction = True
|
|
80
80
|
continue
|
|
81
81
|
|
|
@@ -13,6 +13,24 @@ from typing import Any, Dict, List, Optional, Tuple
|
|
|
13
13
|
from cli.db_utils import setup_db_utils_parser
|
|
14
14
|
|
|
15
15
|
|
|
16
|
+
def _native_dialect_choices() -> List[str]:
|
|
17
|
+
"""Sorted native dialect names from the plugin registry (ADR-26 E5).
|
|
18
|
+
|
|
19
|
+
Replaces the hardcoded ``--dialect`` choices list so the CLI surface
|
|
20
|
+
tracks the registered plugins automatically — dropping a plugin folder
|
|
21
|
+
adds its dialect here with no string literal in framework code.
|
|
22
|
+
"""
|
|
23
|
+
# Route through ``api.*`` (not ``db.provider_registry`` directly): cli/ is
|
|
24
|
+
# banned from importing the registry by .flake8 banned-modules (I251).
|
|
25
|
+
from api._cli_support import ProviderRegistry
|
|
26
|
+
|
|
27
|
+
return sorted(
|
|
28
|
+
p.name
|
|
29
|
+
for p in ProviderRegistry.list_plugins()
|
|
30
|
+
if ProviderRegistry.is_native_dialect(p.name)
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
|
|
16
34
|
def parse_with_selective_errors(
|
|
17
35
|
parser: argparse.ArgumentParser,
|
|
18
36
|
) -> Tuple[Optional[argparse.Namespace], List[str], bool]:
|
|
@@ -12,6 +12,8 @@ from dataclasses import dataclass, field
|
|
|
12
12
|
from pathlib import Path
|
|
13
13
|
from typing import Any, Dict, List, Optional
|
|
14
14
|
|
|
15
|
+
from core.features import FeatureTier
|
|
16
|
+
|
|
15
17
|
# Flyway-compatible migration filename patterns used to skip non-migration
|
|
16
18
|
# SQL files when scanning a directory (e.g. leftover temp files, schema
|
|
17
19
|
# dumps) so only intentional migration scripts are
|
|
@@ -59,6 +61,9 @@ class CliCommandContext:
|
|
|
59
61
|
# Migration configuration
|
|
60
62
|
placeholders: Dict[str, Any] = field(default_factory=dict)
|
|
61
63
|
dir_recursive_map: Dict[Path, bool] = field(default_factory=dict)
|
|
64
|
+
# License tier for feature-level gates. execute_single_command supplies
|
|
65
|
+
# the real CLI-resolved tier; the default keeps direct handler tests terse.
|
|
66
|
+
license_tier: FeatureTier = FeatureTier.ENTERPRISE
|
|
62
67
|
|
|
63
68
|
|
|
64
69
|
def _set_command_completed(log: Any, result: Any, command_type: str) -> None:
|
|
@@ -382,6 +382,7 @@ def _dispatch_command(ctx: _CliContext, command_output: CommandOutput) -> int:
|
|
|
382
382
|
recursive=recursive,
|
|
383
383
|
placeholders=placeholders,
|
|
384
384
|
dir_recursive_map=dir_recursive_map,
|
|
385
|
+
license_tier=_resolve_license_tier(ctx.args),
|
|
385
386
|
)
|
|
386
387
|
if not success:
|
|
387
388
|
any_command_failed = True
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Non-dialect ``BaseDatabaseConfig`` subclass modules.
|
|
2
|
+
|
|
3
|
+
The per-dialect configuration classes that used to live here have moved into
|
|
4
|
+
their plugin packages (``db/plugins/<dialect>/config.py``, ADR-26 D /
|
|
5
|
+
story 26-11): each plugin declares ``config_class=XxxConfig`` on its
|
|
6
|
+
``PluginInfo`` and registers via plugin discovery, so adding a dialect no
|
|
7
|
+
longer requires editing ``config/``.
|
|
8
|
+
|
|
9
|
+
What remains is :mod:`config._subclasses.dummy_config` — a generic/test config
|
|
10
|
+
that is **not** a plugin and therefore has no plugin to register through. It
|
|
11
|
+
imports :class:`config.database_config.BaseDatabaseConfig` and uses
|
|
12
|
+
``@register_database_type`` so its class is wired into the
|
|
13
|
+
``BaseDatabaseConfig._registry`` at import time. The :mod:`config.database_config`
|
|
14
|
+
facade eager-imports it to keep that registration eager.
|
|
15
|
+
"""
|
|
@@ -10,11 +10,40 @@ from dataclasses import replace
|
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
from typing import Any, Dict, Optional, Union
|
|
12
12
|
|
|
13
|
-
from config.database_config import
|
|
13
|
+
from config.database_config import (
|
|
14
|
+
BaseDatabaseConfig,
|
|
15
|
+
_detect_dialect_from_url,
|
|
16
|
+
_native_canonical_from_scheme,
|
|
17
|
+
)
|
|
14
18
|
from config.dblift_config import DbliftConfig
|
|
15
19
|
from config.errors import ConfigurationError
|
|
16
20
|
|
|
17
21
|
|
|
22
|
+
def _file_url_dialect_from_scheme(scheme: str) -> Optional[str]:
|
|
23
|
+
"""Resolve a URL scheme to its canonical dialect *iff* that dialect uses
|
|
24
|
+
file-URL semantics (ADR-26 E5).
|
|
25
|
+
|
|
26
|
+
A "file-URL" dialect is one whose quirks advertise
|
|
27
|
+
``url_optional_when_file_path_given`` — the file path *is* the database, so
|
|
28
|
+
a stale ``database`` attribute on a copied config (e.g. SQL Server's
|
|
29
|
+
``master``) must not win over the URL. SQLite is the only such native
|
|
30
|
+
dialect today; any future embedded/file-based plugin is handled the same
|
|
31
|
+
way without naming a dialect here.
|
|
32
|
+
|
|
33
|
+
Returns the canonical dialect name, or ``None`` when the scheme is unknown
|
|
34
|
+
or its dialect is not file-URL based.
|
|
35
|
+
"""
|
|
36
|
+
canonical = _native_canonical_from_scheme(scheme)
|
|
37
|
+
if not canonical:
|
|
38
|
+
return None
|
|
39
|
+
from db.provider_registry import ProviderRegistry
|
|
40
|
+
|
|
41
|
+
quirks = ProviderRegistry.get_quirks(canonical)
|
|
42
|
+
if getattr(quirks, "url_optional_when_file_path_given", False):
|
|
43
|
+
return canonical
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
|
|
18
47
|
class ConfigBuilder:
|
|
19
48
|
"""Helper class for building and merging configurations."""
|
|
20
49
|
|
|
@@ -147,9 +176,10 @@ class ConfigBuilder:
|
|
|
147
176
|
if hasattr(result, key):
|
|
148
177
|
setattr(result, key, value)
|
|
149
178
|
if overrides.get("url"):
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
179
|
+
_scheme = overrides["url"].strip().lower().split(":", 1)[0].split("+", 1)[0]
|
|
180
|
+
_file_dialect = _file_url_dialect_from_scheme(_scheme)
|
|
181
|
+
if _file_dialect:
|
|
182
|
+
result.type = _file_dialect
|
|
153
183
|
return result
|
|
154
184
|
|
|
155
185
|
@staticmethod
|
|
@@ -166,13 +196,19 @@ class ConfigBuilder:
|
|
|
166
196
|
"""
|
|
167
197
|
if not overrides.get("url"):
|
|
168
198
|
return None
|
|
169
|
-
|
|
170
|
-
|
|
199
|
+
_scheme = overrides["url"].strip().lower().split(":", 1)[0].split("+", 1)[0]
|
|
200
|
+
_file_dialect = _file_url_dialect_from_scheme(_scheme)
|
|
201
|
+
if not _file_dialect:
|
|
171
202
|
return None
|
|
203
|
+
from db.provider_registry import ProviderRegistry
|
|
204
|
+
|
|
205
|
+
_default_schema = getattr(
|
|
206
|
+
ProviderRegistry.get_quirks(_file_dialect), "default_schema_name", None
|
|
207
|
+
)
|
|
172
208
|
sqlite_data: Dict[str, Any] = {
|
|
173
|
-
"type":
|
|
209
|
+
"type": _file_dialect,
|
|
174
210
|
"url": overrides["url"],
|
|
175
|
-
"schema": overrides.get("schema", base_config.schema) or
|
|
211
|
+
"schema": overrides.get("schema", base_config.schema) or _default_schema,
|
|
176
212
|
}
|
|
177
213
|
try:
|
|
178
214
|
return BaseDatabaseConfig.create(sqlite_data)
|
|
@@ -72,9 +72,6 @@ def _infer_type_from_url_scheme(data: Dict[str, Any]) -> None:
|
|
|
72
72
|
_normalize_sqlite_url_alias(data)
|
|
73
73
|
url = str(data.get("url") or "")
|
|
74
74
|
url_lower = url.lower()
|
|
75
|
-
if url_lower.startswith(("sqlite:", "sqlite3:")):
|
|
76
|
-
data["type"] = "sqlite" # lint: allow-dialect-string: URL-scheme inference
|
|
77
|
-
return
|
|
78
75
|
scheme = url_lower.split(":", 1)[0].split("+", 1)[0]
|
|
79
76
|
if scheme:
|
|
80
77
|
canonical = _native_canonical_from_scheme(scheme)
|
|
@@ -102,24 +99,20 @@ def _normalize_sqlite_url_alias(data: Dict[str, Any]) -> None:
|
|
|
102
99
|
|
|
103
100
|
|
|
104
101
|
def _infer_type_from_uri(data: Dict[str, Any], url: str) -> None:
|
|
105
|
-
"""
|
|
102
|
+
"""Infer ``type`` for native URIs (e.g. ``sqlite:`` / ``sqlite3:``) via the registry.
|
|
106
103
|
|
|
107
|
-
|
|
108
|
-
|
|
104
|
+
The scheme is resolved through ``_native_canonical_from_scheme`` after the
|
|
105
|
+
``sqlite3:`` alias is normalised. Unknown schemes are left for normal
|
|
106
|
+
validation so secret/offline URLs can still flow through the
|
|
107
|
+
incomplete-config path when requested.
|
|
109
108
|
"""
|
|
110
109
|
_normalize_sqlite_url_alias(data)
|
|
111
110
|
url = str(data.get("url") or url)
|
|
112
111
|
url_l = (url or "").strip().lower()
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
data["type"] =
|
|
117
|
-
else:
|
|
118
|
-
scheme = url_l.split(":", 1)[0].split("+", 1)[0]
|
|
119
|
-
canonical = _native_canonical_from_scheme(scheme)
|
|
120
|
-
if canonical:
|
|
121
|
-
data["type"] = canonical
|
|
122
|
-
return
|
|
112
|
+
scheme = url_l.split(":", 1)[0].split("+", 1)[0]
|
|
113
|
+
canonical = _native_canonical_from_scheme(scheme)
|
|
114
|
+
if canonical:
|
|
115
|
+
data["type"] = canonical
|
|
123
116
|
|
|
124
117
|
|
|
125
118
|
def _hydrate_from_native_url(data: Dict[str, Any], url: str) -> None:
|
|
@@ -442,10 +435,12 @@ class BaseDatabaseConfig(UrlBuilderMixin, ABC):
|
|
|
442
435
|
|
|
443
436
|
@classmethod
|
|
444
437
|
def create(cls, data: Dict[str, Any]) -> "BaseDatabaseConfig":
|
|
445
|
-
# NOTE:
|
|
446
|
-
#
|
|
447
|
-
# ``
|
|
448
|
-
#
|
|
438
|
+
# NOTE: the config subclass for a dialect is resolved in
|
|
439
|
+
# ``_resolve_config_class`` — first via the eager ``_registry`` (only the
|
|
440
|
+
# non-dialect ``dummy`` config eager-imports here now), then via plugin
|
|
441
|
+
# discovery (``PluginInfo.config_class``). Per-dialect configs live in
|
|
442
|
+
# ``db/plugins/<X>/config.py`` (ADR-26 D); see the block at the bottom
|
|
443
|
+
# of this module.
|
|
449
444
|
# Phase 1: infer a missing ``type`` from well-known URL schemes.
|
|
450
445
|
_infer_type_from_url_scheme(data)
|
|
451
446
|
|
|
@@ -589,27 +584,18 @@ class DatabaseConfig:
|
|
|
589
584
|
|
|
590
585
|
|
|
591
586
|
# ---------------------------------------------------------------------------
|
|
592
|
-
#
|
|
593
|
-
# ``
|
|
594
|
-
# (
|
|
595
|
-
#
|
|
596
|
-
#
|
|
597
|
-
# module imports ``BaseDatabaseConfig`` (and ``register_database_type``) from
|
|
598
|
-
# this file, so the symbols above must be fully defined before we load them.
|
|
599
|
-
# Importing the modules registers their classes via ``@register_database_type``.
|
|
587
|
+
# Per-dialect config subclasses now live in their plugin packages
|
|
588
|
+
# (``db/plugins/<dialect>/config.py``) and register via plugin discovery
|
|
589
|
+
# (story 26-11 / ADR-26 D). Each plugin declares ``config_class=XxxConfig`` on
|
|
590
|
+
# its ``PluginInfo`` and ``_resolve_config_class`` (above) picks it up through
|
|
591
|
+
# the plugin registry — adding a dialect no longer requires editing ``config/``.
|
|
600
592
|
#
|
|
601
|
-
#
|
|
602
|
-
#
|
|
603
|
-
#
|
|
604
|
-
#
|
|
605
|
-
#
|
|
593
|
+
# Only the non-dialect ``dummy`` config stays eager-imported here: it is a
|
|
594
|
+
# generic/test config (not a plugin), so it has no plugin to register through.
|
|
595
|
+
# The import is at the bottom because ``dummy_config`` imports
|
|
596
|
+
# ``BaseDatabaseConfig`` (and ``register_database_type``) from this file, so the
|
|
597
|
+
# symbols above must be fully defined first; importing it registers its class
|
|
598
|
+
# via ``@register_database_type``.
|
|
606
599
|
# ---------------------------------------------------------------------------
|
|
607
600
|
|
|
608
|
-
from config._subclasses.cosmosdb_config import CosmosDbConfig # noqa: E402, F401
|
|
609
|
-
from config._subclasses.db2_config import Db2Config # noqa: E402, F401
|
|
610
601
|
from config._subclasses.dummy_config import DummyDatabaseConfig # noqa: E402, F401
|
|
611
|
-
from config._subclasses.mysql_config import MySqlConfig # noqa: E402, F401
|
|
612
|
-
from config._subclasses.oracle_config import OracleConfig # noqa: E402, F401
|
|
613
|
-
from config._subclasses.postgresql_config import PostgreSqlConfig # noqa: E402, F401
|
|
614
|
-
from config._subclasses.sqlite_config import SQLiteConfig # noqa: E402, F401
|
|
615
|
-
from config._subclasses.sqlserver_config import SqlServerConfig # noqa: E402, F401
|
|
@@ -471,14 +471,11 @@ class DbliftConfig:
|
|
|
471
471
|
db_type = str(database_data.get("type") or "").strip().lower()
|
|
472
472
|
url = str(database_data.get("url") or "").strip()
|
|
473
473
|
if url and not db_type:
|
|
474
|
-
|
|
475
|
-
if url_lower.startswith(("sqlite:", "sqlite3:")):
|
|
476
|
-
db_type = "sqlite" # lint: allow-dialect-string: URL-scheme inference (sqlite:// URL maps to sqlite type)
|
|
477
|
-
else:
|
|
478
|
-
from db.provider_registry import ProviderRegistry
|
|
474
|
+
from db.provider_registry import ProviderRegistry
|
|
479
475
|
|
|
480
|
-
|
|
481
|
-
|
|
476
|
+
url_lower = url.lower()
|
|
477
|
+
scheme = url_lower.split(":", 1)[0].split("+", 1)[0]
|
|
478
|
+
db_type = ProviderRegistry.canonical_dialect_name(scheme) or ""
|
|
482
479
|
if not db_type:
|
|
483
480
|
from config.secrets._registry import is_secret_uri as _is_secret_uri_inline
|
|
484
481
|
|
|
@@ -642,11 +639,8 @@ class DbliftConfig:
|
|
|
642
639
|
url = str(database.get("url") or "").strip()
|
|
643
640
|
if not db_type and url:
|
|
644
641
|
url_lower = url.lower()
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
else:
|
|
648
|
-
scheme = url_lower.split(":", 1)[0].split("+", 1)[0]
|
|
649
|
-
db_type = ProviderRegistry.canonical_dialect_name(scheme) or ""
|
|
642
|
+
scheme = url_lower.split(":", 1)[0].split("+", 1)[0]
|
|
643
|
+
db_type = ProviderRegistry.canonical_dialect_name(scheme) or ""
|
|
650
644
|
|
|
651
645
|
if not db_type:
|
|
652
646
|
if url:
|
|
@@ -654,11 +648,12 @@ class DbliftConfig:
|
|
|
654
648
|
raise ConfigurationError("Database type is required in configuration")
|
|
655
649
|
|
|
656
650
|
_quirks = ProviderRegistry.get_quirks(db_type)
|
|
657
|
-
#
|
|
658
|
-
# use_managed_identity).
|
|
659
|
-
#
|
|
660
|
-
#
|
|
661
|
-
|
|
651
|
+
# Cloud-account auth validation: account_endpoint + account_key (or
|
|
652
|
+
# use_managed_identity). Gated on the ``requires_cloud_account_auth``
|
|
653
|
+
# quirks capability (CosmosDB sets it True) rather than a hardcoded
|
|
654
|
+
# dialect name. ``is_nosql`` is deliberately not reused — it is too
|
|
655
|
+
# generic, so future NoSQL dialects don't inherit Azure-specific rules.
|
|
656
|
+
if _quirks.requires_cloud_account_auth:
|
|
662
657
|
has_endpoint = bool(database.get("account_endpoint") or database.get("url"))
|
|
663
658
|
if not has_endpoint:
|
|
664
659
|
raise ConfigurationError("Cosmos DB configuration requires account_endpoint or url")
|
|
@@ -842,15 +837,11 @@ class DbliftConfig:
|
|
|
842
837
|
from config.secrets._registry import is_secret_uri as _is_secret_uri_args
|
|
843
838
|
|
|
844
839
|
if not _is_secret_uri_args(url_val):
|
|
845
|
-
|
|
846
|
-
url_lower = url_val.strip().lower()
|
|
847
|
-
if url_lower.startswith(("sqlite:", "sqlite3:")):
|
|
848
|
-
inferred_type = "sqlite" # lint: allow-dialect-string: URL-scheme inference (sqlite:// URL maps to sqlite type)
|
|
849
|
-
else:
|
|
850
|
-
from db.provider_registry import ProviderRegistry
|
|
840
|
+
from db.provider_registry import ProviderRegistry
|
|
851
841
|
|
|
852
|
-
|
|
853
|
-
|
|
842
|
+
url_lower = url_val.strip().lower()
|
|
843
|
+
scheme = url_lower.split(":", 1)[0].split("+", 1)[0]
|
|
844
|
+
inferred_type = ProviderRegistry.canonical_dialect_name(scheme) or ""
|
|
854
845
|
if inferred_type:
|
|
855
846
|
db_cfg["type"] = inferred_type
|
|
856
847
|
|
|
@@ -25,6 +25,15 @@ DB_DEPLOYMENT_ID_MAX_LENGTH = 100
|
|
|
25
25
|
# package has to import the other.
|
|
26
26
|
DBLIFT_SCHEMA_SNAPSHOTS_TABLE = "dblift_schema_snapshots"
|
|
27
27
|
|
|
28
|
+
# Data sets / Lane B (audited corrections) managed tables.
|
|
29
|
+
# The change-set table stores before/after row images using the snapshot codec.
|
|
30
|
+
# Per-dataset history tables are named via config (e.g. dblift_data_history_corrections).
|
|
31
|
+
DBLIFT_DATA_CHANGE_SET_TABLE = "dblift_data_change_set"
|
|
32
|
+
|
|
33
|
+
# The audit table is an append-only, hash-chained log of apply/undo events,
|
|
34
|
+
# shared across datasets (chained per-dataset). Tamper-evidence for the ledger.
|
|
35
|
+
DBLIFT_DATA_AUDIT_TABLE = "dblift_data_audit"
|
|
36
|
+
|
|
28
37
|
# Default timeout values
|
|
29
38
|
DEFAULT_CONNECTION_TIMEOUT_SECONDS = 30
|
|
30
39
|
DEFAULT_MIGRATION_LOCK_TIMEOUT_SECONDS = 60
|
|
@@ -55,6 +55,7 @@ override only the deltas.
|
|
|
55
55
|
|
|
56
56
|
from __future__ import annotations
|
|
57
57
|
|
|
58
|
+
import re
|
|
58
59
|
from typing import Any, Optional, Protocol, runtime_checkable
|
|
59
60
|
|
|
60
61
|
|
|
@@ -237,6 +238,29 @@ class TypeMapQuirks(Protocol):
|
|
|
237
238
|
"""Return dialect canonical→preferred type mapping."""
|
|
238
239
|
|
|
239
240
|
|
|
241
|
+
@runtime_checkable
|
|
242
|
+
class ErrorQuirks(Protocol):
|
|
243
|
+
"""Error-classification hooks. Populated by ADR-26 T0."""
|
|
244
|
+
|
|
245
|
+
def error_patterns(self) -> "list[tuple[re.Pattern[str], Any]]":
|
|
246
|
+
"""Return this dialect's ordered (compiled-regex, ErrorCategory) pairs
|
|
247
|
+
for connection/SQL error classification, or [] for none.
|
|
248
|
+
|
|
249
|
+
Typed loosely (second element is the db-layer ``ErrorCategory`` enum)
|
|
250
|
+
because this module is in ``core/`` and MUST NOT import from ``db/``
|
|
251
|
+
at module load — the core→db layering rule. Plugins return the
|
|
252
|
+
precise type."""
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
@runtime_checkable
|
|
256
|
+
class ConnectionQuirks(Protocol):
|
|
257
|
+
"""Connection / engine-pool hooks. Populated by ADR-26 T0."""
|
|
258
|
+
|
|
259
|
+
def engine_pool_options(self) -> "dict[str, Any]":
|
|
260
|
+
"""Return dialect-specific SQLAlchemy engine/pool kwargs merged into
|
|
261
|
+
``create_engine(...)``. Default: {} (no overrides)."""
|
|
262
|
+
|
|
263
|
+
|
|
240
264
|
@runtime_checkable
|
|
241
265
|
class DialectQuirks(
|
|
242
266
|
DdlQuirks,
|
|
@@ -245,6 +269,8 @@ class DialectQuirks(
|
|
|
245
269
|
ComparatorQuirks,
|
|
246
270
|
ValidatorQuirks,
|
|
247
271
|
TypeMapQuirks,
|
|
272
|
+
ErrorQuirks,
|
|
273
|
+
ConnectionQuirks,
|
|
248
274
|
Protocol,
|
|
249
275
|
):
|
|
250
276
|
"""Single contract for dialect-specific behaviour.
|
|
@@ -272,4 +298,6 @@ __all__ = [
|
|
|
272
298
|
"ComparatorQuirks",
|
|
273
299
|
"ValidatorQuirks",
|
|
274
300
|
"TypeMapQuirks",
|
|
301
|
+
"ErrorQuirks",
|
|
302
|
+
"ConnectionQuirks",
|
|
275
303
|
]
|