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.
Files changed (414) hide show
  1. {dblift-2.0.4 → dblift-2.1.0}/PKG-INFO +29 -8
  2. {dblift-2.0.4 → dblift-2.1.0}/README.md +28 -7
  3. {dblift-2.0.4 → dblift-2.1.0}/api/_client_factory.py +7 -7
  4. {dblift-2.0.4 → dblift-2.1.0}/api/_engine_config.py +4 -1
  5. {dblift-2.0.4 → dblift-2.1.0}/api/client.py +8 -6
  6. {dblift-2.0.4 → dblift-2.1.0}/cli/_config_helpers.py +1 -1
  7. {dblift-2.0.4 → dblift-2.1.0}/cli/_parser_setup.py +18 -0
  8. {dblift-2.0.4 → dblift-2.1.0}/cli/handlers/_shared.py +5 -0
  9. {dblift-2.0.4 → dblift-2.1.0}/cli/main.py +1 -0
  10. dblift-2.1.0/config/_subclasses/__init__.py +15 -0
  11. {dblift-2.0.4 → dblift-2.1.0}/config/config_builder.py +44 -8
  12. {dblift-2.0.4 → dblift-2.1.0}/config/database_config.py +26 -40
  13. {dblift-2.0.4 → dblift-2.1.0}/config/dblift_config.py +16 -25
  14. {dblift-2.0.4 → dblift-2.1.0}/core/constants.py +9 -0
  15. {dblift-2.0.4 → dblift-2.1.0}/core/dialect_boundary.py +28 -0
  16. dblift-2.1.0/core/features.py +123 -0
  17. {dblift-2.0.4 → dblift-2.1.0}/core/introspection/extractors/constraint_extractor.py +4 -13
  18. dblift-2.1.0/core/introspection/version_detector.py +41 -0
  19. {dblift-2.0.4 → dblift-2.1.0}/core/logger/console.py +72 -1
  20. {dblift-2.0.4 → dblift-2.1.0}/core/logger/formatters/diff_utils.py +24 -6
  21. {dblift-2.0.4 → dblift-2.1.0}/core/logger/formatters/htmlformatter.py +5 -0
  22. {dblift-2.0.4 → dblift-2.1.0}/core/logger/log.py +21 -3
  23. {dblift-2.0.4 → dblift-2.1.0}/core/migration/commands/import_flyway_command.py +3 -2
  24. {dblift-2.0.4 → dblift-2.1.0}/core/migration/executor/execution_engine.py +10 -12
  25. {dblift-2.0.4 → dblift-2.1.0}/core/migration/executors/sql_executor.py +7 -4
  26. {dblift-2.0.4 → dblift-2.1.0}/core/migration/migration.py +26 -2
  27. {dblift-2.0.4 → dblift-2.1.0}/core/migration/scripting/undo_script_generator/_dml_reversers.py +4 -4
  28. {dblift-2.0.4 → dblift-2.1.0}/core/migration/scripting/undo_script_generator/_extractors.py +7 -8
  29. {dblift-2.0.4 → dblift-2.1.0}/core/migration/scripting/undo_script_generator/_generator.py +1 -1
  30. {dblift-2.0.4 → dblift-2.1.0}/core/migration/scripting/undo_script_generator/_helpers.py +31 -8
  31. {dblift-2.0.4 → dblift-2.1.0}/core/migration/scripting/undo_script_generator/_reversers.py +4 -4
  32. {dblift-2.0.4 → dblift-2.1.0}/core/migration/sql/sql_analyzer.py +3 -2
  33. {dblift-2.0.4 → dblift-2.1.0}/core/migration/sql/statement_splitter.py +1 -3
  34. {dblift-2.0.4 → dblift-2.1.0}/core/migration/ui/table_renderer.py +21 -28
  35. {dblift-2.0.4 → dblift-2.1.0}/core/normalization/dependency_resolver.py +12 -3
  36. {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/__init__.py +3 -2
  37. {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/constraint_validator.py +3 -4
  38. {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/database_link.py +1 -6
  39. {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/dialect.py +83 -140
  40. {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/event.py +4 -20
  41. {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/extension.py +1 -6
  42. {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/foreign_data_wrapper.py +1 -6
  43. {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/foreign_server.py +1 -6
  44. {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/index.py +5 -51
  45. {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/linked_server.py +1 -6
  46. {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/module.py +1 -6
  47. {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/package.py +4 -4
  48. {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/procedure.py +4 -40
  49. {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/sequence.py +4 -39
  50. {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/table.py +145 -349
  51. dblift-2.1.0/core/sql_model/table_options.py +162 -0
  52. {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/trigger.py +5 -54
  53. {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/view.py +11 -92
  54. {dblift-2.0.4 → dblift-2.1.0}/core/sql_parser/sqlglot_parser.py +1 -1
  55. {dblift-2.0.4 → dblift-2.1.0}/core/state/sql_statement.py +5 -2
  56. {dblift-2.0.4 → dblift-2.1.0}/core/utils/database_url_parser.py +10 -10
  57. {dblift-2.0.4 → dblift-2.1.0}/db/base_provider.py +64 -1
  58. {dblift-2.0.4 → dblift-2.1.0}/db/base_quirks.py +249 -1
  59. dblift-2.1.0/db/dml_analysis.py +656 -0
  60. {dblift-2.0.4 → dblift-2.1.0}/db/error.py +21 -110
  61. {dblift-2.0.4 → dblift-2.1.0}/db/native_connection_manager.py +2 -3
  62. dblift-2.1.0/db/object_naming.py +58 -0
  63. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/base_snapshot_manager.py +15 -40
  64. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/cosmosdb/connection_manager.py +1 -1
  65. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/cosmosdb/query_executor.py +2 -2
  66. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/plugin.py +1 -1
  67. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/quirks.py +4 -0
  68. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/db2/plugin.py +1 -1
  69. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/db2/quirks.py +58 -3
  70. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mariadb/quirks.py +13 -0
  71. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mysql/plugin.py +1 -1
  72. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mysql/quirks.py +123 -6
  73. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/plugin.py +1 -1
  74. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/quirks.py +87 -2
  75. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/postgresql/plugin.py +1 -1
  76. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/postgresql/quirks.py +40 -5
  77. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlite/plugin.py +1 -1
  78. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlserver/plugin.py +1 -1
  79. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlserver/quirks.py +22 -7
  80. {dblift-2.0.4 → dblift-2.1.0}/db/provider_interfaces.py +22 -0
  81. {dblift-2.0.4 → dblift-2.1.0}/db/provider_registry.py +59 -0
  82. {dblift-2.0.4 → dblift-2.1.0}/db/sqlalchemy_provider.py +8 -6
  83. {dblift-2.0.4 → dblift-2.1.0}/dblift.egg-info/PKG-INFO +29 -8
  84. {dblift-2.0.4 → dblift-2.1.0}/dblift.egg-info/SOURCES.txt +8 -8
  85. {dblift-2.0.4 → dblift-2.1.0}/integrations/django/_engine.py +1 -1
  86. {dblift-2.0.4 → dblift-2.1.0}/integrations/flask.py +1 -0
  87. {dblift-2.0.4 → dblift-2.1.0}/pyproject.toml +12 -10
  88. dblift-2.0.4/config/_subclasses/__init__.py +0 -12
  89. dblift-2.0.4/core/features.py +0 -28
  90. dblift-2.0.4/core/introspection/capability_matrix.py +0 -276
  91. dblift-2.0.4/core/introspection/version_detector.py +0 -279
  92. dblift-2.0.4/core/sql_model/table_options.py +0 -73
  93. dblift-2.0.4/db/object_naming.py +0 -39
  94. {dblift-2.0.4 → dblift-2.1.0}/LICENSE +0 -0
  95. {dblift-2.0.4 → dblift-2.1.0}/api/__init__.py +0 -0
  96. {dblift-2.0.4 → dblift-2.1.0}/api/_cli_support.py +0 -0
  97. {dblift-2.0.4 → dblift-2.1.0}/api/_client_operations.py +0 -0
  98. {dblift-2.0.4 → dblift-2.1.0}/api/async_client.py +0 -0
  99. {dblift-2.0.4 → dblift-2.1.0}/api/events.py +0 -0
  100. {dblift-2.0.4 → dblift-2.1.0}/api/migrations.py +0 -0
  101. {dblift-2.0.4 → dblift-2.1.0}/api/py.typed +0 -0
  102. {dblift-2.0.4 → dblift-2.1.0}/cli/__init__.py +0 -0
  103. {dblift-2.0.4 → dblift-2.1.0}/cli/_command_handlers.py +0 -0
  104. {dblift-2.0.4 → dblift-2.1.0}/cli/_constants.py +0 -0
  105. {dblift-2.0.4 → dblift-2.1.0}/cli/_output.py +0 -0
  106. {dblift-2.0.4 → dblift-2.1.0}/cli/db_utils.py +0 -0
  107. {dblift-2.0.4 → dblift-2.1.0}/cli/extensions.py +0 -0
  108. {dblift-2.0.4 → dblift-2.1.0}/cli/handlers/__init__.py +0 -0
  109. {dblift-2.0.4 → dblift-2.1.0}/cli/handlers/baseline.py +0 -0
  110. {dblift-2.0.4 → dblift-2.1.0}/cli/handlers/clean.py +0 -0
  111. {dblift-2.0.4 → dblift-2.1.0}/cli/handlers/import_flyway.py +0 -0
  112. {dblift-2.0.4 → dblift-2.1.0}/cli/handlers/info.py +0 -0
  113. {dblift-2.0.4 → dblift-2.1.0}/cli/handlers/migrate.py +0 -0
  114. {dblift-2.0.4 → dblift-2.1.0}/cli/handlers/repair.py +0 -0
  115. {dblift-2.0.4 → dblift-2.1.0}/cli/handlers/undo.py +0 -0
  116. {dblift-2.0.4 → dblift-2.1.0}/cli/handlers/validate.py +0 -0
  117. {dblift-2.0.4 → dblift-2.1.0}/config/__init__.py +0 -0
  118. {dblift-2.0.4 → dblift-2.1.0}/config/_credential_masking.py +0 -0
  119. {dblift-2.0.4 → dblift-2.1.0}/config/_subclasses/dummy_config.py +0 -0
  120. {dblift-2.0.4 → dblift-2.1.0}/config/_url_builder_mixin.py +0 -0
  121. {dblift-2.0.4 → dblift-2.1.0}/config/errors.py +0 -0
  122. {dblift-2.0.4 → dblift-2.1.0}/config/secrets/__init__.py +0 -0
  123. {dblift-2.0.4 → dblift-2.1.0}/config/secrets/_cache.py +0 -0
  124. {dblift-2.0.4 → dblift-2.1.0}/config/secrets/_provider_base.py +0 -0
  125. {dblift-2.0.4 → dblift-2.1.0}/config/secrets/_registry.py +0 -0
  126. {dblift-2.0.4 → dblift-2.1.0}/config/secrets/_resolver.py +0 -0
  127. {dblift-2.0.4 → dblift-2.1.0}/config/secrets/_secrets_config.py +0 -0
  128. {dblift-2.0.4 → dblift-2.1.0}/core/__init__.py +0 -0
  129. {dblift-2.0.4 → dblift-2.1.0}/core/exceptions.py +0 -0
  130. {dblift-2.0.4 → dblift-2.1.0}/core/introspection/__init__.py +0 -0
  131. {dblift-2.0.4 → dblift-2.1.0}/core/introspection/_column_enricher.py +0 -0
  132. {dblift-2.0.4 → dblift-2.1.0}/core/introspection/_partition_enricher.py +0 -0
  133. {dblift-2.0.4 → dblift-2.1.0}/core/introspection/_schema_orchestrator.py +0 -0
  134. {dblift-2.0.4 → dblift-2.1.0}/core/introspection/_utils.py +0 -0
  135. {dblift-2.0.4 → dblift-2.1.0}/core/introspection/_vendor_property_applier.py +0 -0
  136. {dblift-2.0.4 → dblift-2.1.0}/core/introspection/base_introspector.py +0 -0
  137. {dblift-2.0.4 → dblift-2.1.0}/core/introspection/extractors/__init__.py +0 -0
  138. {dblift-2.0.4 → dblift-2.1.0}/core/introspection/extractors/base_extractor.py +0 -0
  139. {dblift-2.0.4 → dblift-2.1.0}/core/introspection/extractors/column_extractor.py +0 -0
  140. {dblift-2.0.4 → dblift-2.1.0}/core/introspection/extractors/index_extractor.py +0 -0
  141. {dblift-2.0.4 → dblift-2.1.0}/core/introspection/extractors/misc_extractor.py +0 -0
  142. {dblift-2.0.4 → dblift-2.1.0}/core/introspection/extractors/procedure_extractor.py +0 -0
  143. {dblift-2.0.4 → dblift-2.1.0}/core/introspection/extractors/sequence_extractor.py +0 -0
  144. {dblift-2.0.4 → dblift-2.1.0}/core/introspection/extractors/table_extractor.py +0 -0
  145. {dblift-2.0.4 → dblift-2.1.0}/core/introspection/extractors/trigger_extractor.py +0 -0
  146. {dblift-2.0.4 → dblift-2.1.0}/core/introspection/extractors/view_extractor.py +0 -0
  147. {dblift-2.0.4 → dblift-2.1.0}/core/introspection/introspector_factory.py +0 -0
  148. {dblift-2.0.4 → dblift-2.1.0}/core/introspection/result.py +0 -0
  149. {dblift-2.0.4 → dblift-2.1.0}/core/introspection/schema_introspector.py +0 -0
  150. {dblift-2.0.4 → dblift-2.1.0}/core/introspection/vendor_queries_base.py +0 -0
  151. {dblift-2.0.4 → dblift-2.1.0}/core/introspection/vendor_queries_factory.py +0 -0
  152. {dblift-2.0.4 → dblift-2.1.0}/core/introspection/vendor_queries_protocols.py +0 -0
  153. {dblift-2.0.4 → dblift-2.1.0}/core/logger/__init__.py +0 -0
  154. {dblift-2.0.4 → dblift-2.1.0}/core/logger/_base.py +0 -0
  155. {dblift-2.0.4 → dblift-2.1.0}/core/logger/_factory.py +0 -0
  156. {dblift-2.0.4 → dblift-2.1.0}/core/logger/_formatters.py +0 -0
  157. {dblift-2.0.4 → dblift-2.1.0}/core/logger/_levels.py +0 -0
  158. {dblift-2.0.4 → dblift-2.1.0}/core/logger/_multi.py +0 -0
  159. {dblift-2.0.4 → dblift-2.1.0}/core/logger/_null.py +0 -0
  160. {dblift-2.0.4 → dblift-2.1.0}/core/logger/formatters/__init__.py +0 -0
  161. {dblift-2.0.4 → dblift-2.1.0}/core/logger/formatters/_formatter_impl.py +0 -0
  162. {dblift-2.0.4 → dblift-2.1.0}/core/logger/formatters/factory.py +0 -0
  163. {dblift-2.0.4 → dblift-2.1.0}/core/logger/formatters/formatter.py +0 -0
  164. {dblift-2.0.4 → dblift-2.1.0}/core/logger/formatters/jsonformatter.py +0 -0
  165. {dblift-2.0.4 → dblift-2.1.0}/core/logger/results.py +0 -0
  166. {dblift-2.0.4 → dblift-2.1.0}/core/migration/__init__.py +0 -0
  167. {dblift-2.0.4 → dblift-2.1.0}/core/migration/_type_match.py +0 -0
  168. {dblift-2.0.4 → dblift-2.1.0}/core/migration/clean_summary.py +0 -0
  169. {dblift-2.0.4 → dblift-2.1.0}/core/migration/commands/__init__.py +0 -0
  170. {dblift-2.0.4 → dblift-2.1.0}/core/migration/commands/_script_events.py +0 -0
  171. {dblift-2.0.4 → dblift-2.1.0}/core/migration/commands/base_command.py +0 -0
  172. {dblift-2.0.4 → dblift-2.1.0}/core/migration/commands/baseline_command.py +0 -0
  173. {dblift-2.0.4 → dblift-2.1.0}/core/migration/commands/clean_command.py +0 -0
  174. {dblift-2.0.4 → dblift-2.1.0}/core/migration/commands/info_command.py +0 -0
  175. {dblift-2.0.4 → dblift-2.1.0}/core/migration/commands/migrate_command.py +0 -0
  176. {dblift-2.0.4 → dblift-2.1.0}/core/migration/commands/repair_command.py +0 -0
  177. {dblift-2.0.4 → dblift-2.1.0}/core/migration/commands/undo_command.py +0 -0
  178. {dblift-2.0.4 → dblift-2.1.0}/core/migration/commands/validate_command.py +0 -0
  179. {dblift-2.0.4 → dblift-2.1.0}/core/migration/encoding.py +0 -0
  180. {dblift-2.0.4 → dblift-2.1.0}/core/migration/executor/__init__.py +0 -0
  181. {dblift-2.0.4 → dblift-2.1.0}/core/migration/executor/migration_executor.py +0 -0
  182. {dblift-2.0.4 → dblift-2.1.0}/core/migration/executor/migration_helpers.py +0 -0
  183. {dblift-2.0.4 → dblift-2.1.0}/core/migration/executor/placeholder_manager.py +0 -0
  184. {dblift-2.0.4 → dblift-2.1.0}/core/migration/executor/transaction_policy.py +0 -0
  185. {dblift-2.0.4 → dblift-2.1.0}/core/migration/executors/__init__.py +0 -0
  186. {dblift-2.0.4 → dblift-2.1.0}/core/migration/executors/base_executor.py +0 -0
  187. {dblift-2.0.4 → dblift-2.1.0}/core/migration/executors/executor_factory.py +0 -0
  188. {dblift-2.0.4 → dblift-2.1.0}/core/migration/executors/python_executor.py +0 -0
  189. {dblift-2.0.4 → dblift-2.1.0}/core/migration/formats/__init__.py +0 -0
  190. {dblift-2.0.4 → dblift-2.1.0}/core/migration/formats/format_detector.py +0 -0
  191. {dblift-2.0.4 → dblift-2.1.0}/core/migration/formats/migration_format.py +0 -0
  192. {dblift-2.0.4 → dblift-2.1.0}/core/migration/history/__init__.py +0 -0
  193. {dblift-2.0.4 → dblift-2.1.0}/core/migration/history/migration_history_manager.py +0 -0
  194. {dblift-2.0.4 → dblift-2.1.0}/core/migration/journals/__init__.py +0 -0
  195. {dblift-2.0.4 → dblift-2.1.0}/core/migration/journals/migration_journal.py +0 -0
  196. {dblift-2.0.4 → dblift-2.1.0}/core/migration/placeholders/__init__.py +0 -0
  197. {dblift-2.0.4 → dblift-2.1.0}/core/migration/placeholders/placeholder_service.py +0 -0
  198. {dblift-2.0.4 → dblift-2.1.0}/core/migration/rules/__init__.py +0 -0
  199. {dblift-2.0.4 → dblift-2.1.0}/core/migration/rules/migration_rules.py +0 -0
  200. {dblift-2.0.4 → dblift-2.1.0}/core/migration/scripting/__init__.py +0 -0
  201. {dblift-2.0.4 → dblift-2.1.0}/core/migration/scripting/migration_script_manager.py +0 -0
  202. {dblift-2.0.4 → dblift-2.1.0}/core/migration/scripting/undo_script_generator/__init__.py +0 -0
  203. {dblift-2.0.4 → dblift-2.1.0}/core/migration/scripting/undo_script_generator/_ddl_reversers.py +0 -0
  204. {dblift-2.0.4 → dblift-2.1.0}/core/migration/scripting/undo_script_generator/_models.py +0 -0
  205. {dblift-2.0.4 → dblift-2.1.0}/core/migration/sql/__init__.py +0 -0
  206. {dblift-2.0.4 → dblift-2.1.0}/core/migration/sql/execution_statement.py +0 -0
  207. {dblift-2.0.4 → dblift-2.1.0}/core/migration/sql/sql_execution_service.py +0 -0
  208. {dblift-2.0.4 → dblift-2.1.0}/core/migration/state/__init__.py +0 -0
  209. {dblift-2.0.4 → dblift-2.1.0}/core/migration/state/migration_data_service.py +0 -0
  210. {dblift-2.0.4 → dblift-2.1.0}/core/migration/state/migration_display_state.py +0 -0
  211. {dblift-2.0.4 → dblift-2.1.0}/core/migration/state/migration_formatter.py +0 -0
  212. {dblift-2.0.4 → dblift-2.1.0}/core/migration/state/migration_state.py +0 -0
  213. {dblift-2.0.4 → dblift-2.1.0}/core/migration/state/migration_state_manager.py +0 -0
  214. {dblift-2.0.4 → dblift-2.1.0}/core/migration/state/migration_state_service.py +0 -0
  215. {dblift-2.0.4 → dblift-2.1.0}/core/migration/ui/__init__.py +0 -0
  216. {dblift-2.0.4 → dblift-2.1.0}/core/migration/ui/data_collector.py +0 -0
  217. {dblift-2.0.4 → dblift-2.1.0}/core/migration/ui/display_formatters.py +0 -0
  218. {dblift-2.0.4 → dblift-2.1.0}/core/migration/ui/migration_analyzer.py +0 -0
  219. {dblift-2.0.4 → dblift-2.1.0}/core/migration/ui/migration_ui.py +0 -0
  220. {dblift-2.0.4 → dblift-2.1.0}/core/migration/version_utils.py +0 -0
  221. {dblift-2.0.4 → dblift-2.1.0}/core/normalization/__init__.py +0 -0
  222. {dblift-2.0.4 → dblift-2.1.0}/core/normalization/data_type_normalizer.py +0 -0
  223. {dblift-2.0.4 → dblift-2.1.0}/core/normalization/identifier_normalizer.py +0 -0
  224. {dblift-2.0.4 → dblift-2.1.0}/core/normalization/object_orderer.py +0 -0
  225. {dblift-2.0.4 → dblift-2.1.0}/core/normalization/type_constants.py +0 -0
  226. {dblift-2.0.4 → dblift-2.1.0}/core/normalization/type_mapper.py +0 -0
  227. {dblift-2.0.4 → dblift-2.1.0}/core/normalization/type_mappings.py +0 -0
  228. {dblift-2.0.4 → dblift-2.1.0}/core/normalization/type_normalizer.py +0 -0
  229. {dblift-2.0.4 → dblift-2.1.0}/core/seams/__init__.py +0 -0
  230. {dblift-2.0.4 → dblift-2.1.0}/core/seams/event_listeners.py +0 -0
  231. {dblift-2.0.4 → dblift-2.1.0}/core/seams/introspection.py +0 -0
  232. {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/_base_parse_result.py +0 -0
  233. {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/_base_sql_column.py +0 -0
  234. {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/_base_sql_constraint.py +0 -0
  235. {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/_base_sql_object.py +0 -0
  236. {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/_base_sql_statement.py +0 -0
  237. {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/base.py +0 -0
  238. {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/partition.py +0 -0
  239. {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/synonym.py +0 -0
  240. {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/table_canonicalizer.py +0 -0
  241. {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/user_defined_type.py +0 -0
  242. {dblift-2.0.4 → dblift-2.1.0}/core/sql_model/view_options.py +0 -0
  243. {dblift-2.0.4 → dblift-2.1.0}/core/sql_parser/__init__.py +0 -0
  244. {dblift-2.0.4 → dblift-2.1.0}/core/sql_parser/_partition_handler.py +0 -0
  245. {dblift-2.0.4 → dblift-2.1.0}/core/sql_parser/_sqlglot_builders.py +0 -0
  246. {dblift-2.0.4 → dblift-2.1.0}/core/sql_parser/base_statement_parser.py +0 -0
  247. {dblift-2.0.4 → dblift-2.1.0}/core/sql_parser/base_tokenizer.py +0 -0
  248. {dblift-2.0.4 → dblift-2.1.0}/core/sql_parser/common/__init__.py +0 -0
  249. {dblift-2.0.4 → dblift-2.1.0}/core/sql_parser/common/base_parser.py +0 -0
  250. {dblift-2.0.4 → dblift-2.1.0}/core/sql_parser/dialects/__init__.py +0 -0
  251. {dblift-2.0.4 → dblift-2.1.0}/core/sql_parser/dialects/base_config.py +0 -0
  252. {dblift-2.0.4 → dblift-2.1.0}/core/sql_parser/enhanced_regex_parser.py +0 -0
  253. {dblift-2.0.4 → dblift-2.1.0}/core/sql_parser/hybrid_parser.py +0 -0
  254. {dblift-2.0.4 → dblift-2.1.0}/core/sql_parser/parser_context.py +0 -0
  255. {dblift-2.0.4 → dblift-2.1.0}/core/sql_parser/parser_factory.py +0 -0
  256. {dblift-2.0.4 → dblift-2.1.0}/core/sql_parser/parser_interface.py +0 -0
  257. {dblift-2.0.4 → dblift-2.1.0}/core/sql_parser/tokens.py +0 -0
  258. {dblift-2.0.4 → dblift-2.1.0}/core/sql_parser/unified_regex_parser.py +0 -0
  259. {dblift-2.0.4 → dblift-2.1.0}/core/sql_validator/__init__.py +0 -0
  260. {dblift-2.0.4 → dblift-2.1.0}/core/sql_validator/_checksum_validator.py +0 -0
  261. {dblift-2.0.4 → dblift-2.1.0}/core/sql_validator/_flyway_compatibility.py +0 -0
  262. {dblift-2.0.4 → dblift-2.1.0}/core/sql_validator/_migration_filter.py +0 -0
  263. {dblift-2.0.4 → dblift-2.1.0}/core/sql_validator/_sql_syntax_validator.py +0 -0
  264. {dblift-2.0.4 → dblift-2.1.0}/core/sql_validator/_strict_mode_validator.py +0 -0
  265. {dblift-2.0.4 → dblift-2.1.0}/core/sql_validator/migration_validator.py +0 -0
  266. {dblift-2.0.4 → dblift-2.1.0}/core/state/sql_script_formatter.py +0 -0
  267. {dblift-2.0.4 → dblift-2.1.0}/core/utils/__init__.py +0 -0
  268. {dblift-2.0.4 → dblift-2.1.0}/core/utils/metadata_helpers.py +0 -0
  269. {dblift-2.0.4 → dblift-2.1.0}/core/utils/row_access.py +0 -0
  270. {dblift-2.0.4 → dblift-2.1.0}/core/utils/string_utils.py +0 -0
  271. {dblift-2.0.4 → dblift-2.1.0}/core/utils/url_masking.py +0 -0
  272. {dblift-2.0.4 → dblift-2.1.0}/db/__init__.py +0 -0
  273. {dblift-2.0.4 → dblift-2.1.0}/db/constants.py +0 -0
  274. {dblift-2.0.4 → dblift-2.1.0}/db/error_handler.py +0 -0
  275. {dblift-2.0.4 → dblift-2.1.0}/db/exceptions.py +0 -0
  276. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/__init__.py +0 -0
  277. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/base_history_manager.py +0 -0
  278. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/base_locking_manager.py +0 -0
  279. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/base_query_executor.py +0 -0
  280. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/base_schema_operations.py +0 -0
  281. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/base_undo_manager.py +0 -0
  282. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/__init__.py +0 -0
  283. /dblift-2.0.4/config/_subclasses/cosmosdb_config.py → /dblift-2.1.0/db/plugins/cosmosdb/config.py +0 -0
  284. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/cosmosdb/__init__.py +0 -0
  285. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/cosmosdb/_sdk.py +0 -0
  286. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/cosmosdb/history_manager.py +0 -0
  287. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/cosmosdb/locking_manager.py +0 -0
  288. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/cosmosdb/schema_operations.py +0 -0
  289. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/introspection/__init__.py +0 -0
  290. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/parser/__init__.py +0 -0
  291. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/parser/cosmosdb_regex_parser.py +0 -0
  292. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/provider.py +0 -0
  293. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/sdk_translator/__init__.py +0 -0
  294. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/sdk_translator/_executor.py +0 -0
  295. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/sdk_translator/_executors.py +0 -0
  296. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/sdk_translator/_parsing.py +0 -0
  297. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/sdk_translator/_script_generation.py +0 -0
  298. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/sdk_translator/_translator.py +0 -0
  299. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/cosmosdb/sdk_translator/_translators.py +0 -0
  300. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/db2/__init__.py +0 -0
  301. /dblift-2.0.4/config/_subclasses/db2_config.py → /dblift-2.1.0/db/plugins/db2/config.py +0 -0
  302. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/db2/db2/__init__.py +0 -0
  303. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/db2/db2/history_manager.py +0 -0
  304. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/db2/db2/locking_manager.py +0 -0
  305. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/db2/db2/schema_operations.py +0 -0
  306. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/db2/introspection/__init__.py +0 -0
  307. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/db2/parser/__init__.py +0 -0
  308. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/db2/parser/db2_regex_parser.py +0 -0
  309. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/db2/parser/parser_config.py +0 -0
  310. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/db2/provider.py +0 -0
  311. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/db2/sqlalchemy_url.py +0 -0
  312. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mariadb/__init__.py +0 -0
  313. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mariadb/plugin.py +0 -0
  314. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mariadb/provider.py +0 -0
  315. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mysql/__init__.py +0 -0
  316. /dblift-2.0.4/config/_subclasses/mysql_config.py → /dblift-2.1.0/db/plugins/mysql/config.py +0 -0
  317. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mysql/introspection/__init__.py +0 -0
  318. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mysql/mysql/__init__.py +0 -0
  319. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mysql/mysql/history_manager.py +0 -0
  320. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mysql/mysql/locking_manager.py +0 -0
  321. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mysql/mysql/schema_operations.py +0 -0
  322. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mysql/parser/__init__.py +0 -0
  323. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mysql/parser/mysql_regex_parser.py +0 -0
  324. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mysql/parser/mysql_statement_parser.py +0 -0
  325. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mysql/parser/mysql_tokenizer.py +0 -0
  326. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mysql/parser/parser_config.py +0 -0
  327. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mysql/provider.py +0 -0
  328. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/mysql/sqlalchemy_url.py +0 -0
  329. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/__init__.py +0 -0
  330. /dblift-2.0.4/config/_subclasses/oracle_config.py → /dblift-2.1.0/db/plugins/oracle/config.py +0 -0
  331. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/introspection/__init__.py +0 -0
  332. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/introspection/oracle_utils.py +0 -0
  333. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/oracle/__init__.py +0 -0
  334. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/oracle/dbms_output.py +0 -0
  335. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/oracle/history_manager.py +0 -0
  336. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/oracle/locking_manager.py +0 -0
  337. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/oracle/schema_operations.py +0 -0
  338. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/parser/__init__.py +0 -0
  339. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/parser/_comments.py +0 -0
  340. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/parser/_object_extractor.py +0 -0
  341. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/parser/_plsql_block.py +0 -0
  342. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/parser/_sqlplus.py +0 -0
  343. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/parser/_statement_splitter.py +0 -0
  344. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/parser/oracle_parser.py +0 -0
  345. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/parser/oracle_statement_parser.py +0 -0
  346. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/parser/oracle_tokenizer.py +0 -0
  347. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/parser/parser_config.py +0 -0
  348. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/parser/sqlplus_context.py +0 -0
  349. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/provider.py +0 -0
  350. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/oracle/sqlalchemy_url.py +0 -0
  351. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/postgresql/__init__.py +0 -0
  352. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/postgresql/_provider_query_executor.py +0 -0
  353. /dblift-2.0.4/config/_subclasses/postgresql_config.py → /dblift-2.1.0/db/plugins/postgresql/config.py +0 -0
  354. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/postgresql/introspection/__init__.py +0 -0
  355. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/postgresql/parser/__init__.py +0 -0
  356. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/postgresql/parser/parser_config.py +0 -0
  357. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/postgresql/parser/postgresql_regex_parser.py +0 -0
  358. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/postgresql/parser/postgresql_statement_parser.py +0 -0
  359. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/postgresql/parser/postgresql_tokenizer.py +0 -0
  360. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/postgresql/postgresql/__init__.py +0 -0
  361. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/postgresql/postgresql/history_manager.py +0 -0
  362. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/postgresql/postgresql/locking_manager.py +0 -0
  363. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/postgresql/postgresql/schema_operations.py +0 -0
  364. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/postgresql/provider.py +0 -0
  365. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/postgresql/sqlalchemy_url.py +0 -0
  366. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlite/__init__.py +0 -0
  367. /dblift-2.0.4/config/_subclasses/sqlite_config.py → /dblift-2.1.0/db/plugins/sqlite/config.py +0 -0
  368. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlite/introspection/__init__.py +0 -0
  369. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlite/parser/__init__.py +0 -0
  370. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlite/parser/parser_config.py +0 -0
  371. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlite/parser/sqlite_regex_parser.py +0 -0
  372. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlite/provider.py +0 -0
  373. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlite/quirks.py +0 -0
  374. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlite/sqlalchemy_url.py +0 -0
  375. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlite/sqlite/__init__.py +0 -0
  376. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlite/sqlite/connection_manager.py +0 -0
  377. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlite/sqlite/history_manager.py +0 -0
  378. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlite/sqlite/locking_manager.py +0 -0
  379. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlite/sqlite/query_executor.py +0 -0
  380. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlite/sqlite/schema_operations.py +0 -0
  381. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlserver/__init__.py +0 -0
  382. /dblift-2.0.4/config/_subclasses/sqlserver_config.py → /dblift-2.1.0/db/plugins/sqlserver/config.py +0 -0
  383. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlserver/introspection/__init__.py +0 -0
  384. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlserver/parser/__init__.py +0 -0
  385. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlserver/parser/parser_config.py +0 -0
  386. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlserver/parser/sqlserver_regex_parser.py +0 -0
  387. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlserver/parser/sqlserver_statement_parser.py +0 -0
  388. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlserver/parser/sqlserver_tokenizer.py +0 -0
  389. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlserver/parser/tsql_batch_separator.py +0 -0
  390. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlserver/provider.py +0 -0
  391. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlserver/sqlalchemy_url.py +0 -0
  392. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlserver/sqlserver/__init__.py +0 -0
  393. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlserver/sqlserver/history_manager.py +0 -0
  394. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlserver/sqlserver/locking_manager.py +0 -0
  395. {dblift-2.0.4 → dblift-2.1.0}/db/plugins/sqlserver/sqlserver/schema_operations.py +0 -0
  396. {dblift-2.0.4 → dblift-2.1.0}/db/provider_capabilities.py +0 -0
  397. {dblift-2.0.4 → dblift-2.1.0}/db/value_utils.py +0 -0
  398. {dblift-2.0.4 → dblift-2.1.0}/dblift.egg-info/dependency_links.txt +0 -0
  399. {dblift-2.0.4 → dblift-2.1.0}/dblift.egg-info/entry_points.txt +0 -0
  400. {dblift-2.0.4 → dblift-2.1.0}/dblift.egg-info/requires.txt +0 -0
  401. {dblift-2.0.4 → dblift-2.1.0}/dblift.egg-info/top_level.txt +0 -0
  402. {dblift-2.0.4 → dblift-2.1.0}/integrations/__init__.py +0 -0
  403. {dblift-2.0.4 → dblift-2.1.0}/integrations/django/__init__.py +0 -0
  404. {dblift-2.0.4 → dblift-2.1.0}/integrations/django/_client.py +0 -0
  405. {dblift-2.0.4 → dblift-2.1.0}/integrations/django/apps.py +0 -0
  406. {dblift-2.0.4 → dblift-2.1.0}/integrations/django/checks.py +0 -0
  407. {dblift-2.0.4 → dblift-2.1.0}/integrations/django/management/__init__.py +0 -0
  408. {dblift-2.0.4 → dblift-2.1.0}/integrations/django/management/commands/__init__.py +0 -0
  409. {dblift-2.0.4 → dblift-2.1.0}/integrations/django/management/commands/dblift_info.py +0 -0
  410. {dblift-2.0.4 → dblift-2.1.0}/integrations/django/management/commands/dblift_migrate.py +0 -0
  411. {dblift-2.0.4 → dblift-2.1.0}/integrations/django/management/commands/dblift_validate.py +0 -0
  412. {dblift-2.0.4 → dblift-2.1.0}/integrations/fastapi.py +0 -0
  413. {dblift-2.0.4 → dblift-2.1.0}/integrations/opentelemetry.py +0 -0
  414. {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.4
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 User Guide
81
+ # DBLift Database release safety for Python teams
82
82
 
83
83
  <p align="center">
84
- <img src="logo/dblift_logo.png" width="600" alt="Dblift Logo">
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
  ![Python](https://img.shields.io/badge/Python-3.11%2B-blue)
92
88
  ![License](https://img.shields.io/badge/License-Apache--2.0-blue)
93
-
94
89
  [![Unit Tests](https://github.com/cmodiano/dblift-oss/actions/workflows/unit-tests.yml/badge.svg)](https://github.com/cmodiano/dblift-oss/actions/workflows/unit-tests.yml)
90
+ [![codecov](https://codecov.io/gh/cmodiano/dblift-oss/graph/badge.svg)](https://codecov.io/gh/cmodiano/dblift-oss)
95
91
  [![Security Policy](https://img.shields.io/badge/Security-Policy-green)](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 User Guide
1
+ # DBLift Database release safety for Python teams
2
2
 
3
3
  <p align="center">
4
- <img src="logo/dblift_logo.png" width="600" alt="Dblift Logo">
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
  ![Python](https://img.shields.io/badge/Python-3.11%2B-blue)
12
8
  ![License](https://img.shields.io/badge/License-Apache--2.0-blue)
13
-
14
9
  [![Unit Tests](https://github.com/cmodiano/dblift-oss/actions/workflows/unit-tests.yml/badge.svg)](https://github.com/cmodiano/dblift-oss/actions/workflows/unit-tests.yml)
10
+ [![codecov](https://codecov.io/gh/cmodiano/dblift-oss/graph/badge.svg)](https://codecov.io/gh/cmodiano/dblift-oss)
15
11
  [![Security Policy](https://img.shields.io/badge/Security-Policy-green)](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 dialect_name == "sqlite": # lint: allow-dialect-string: stdlib sqlite3 provider path
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. Without this, the provider would open its *own* ``sqlite3``
364
- # connection and migrate a different database than the caller's engine
365
- # fatal for ``sqlite:///:memory:`` where every connection is a separate
366
- # in-memory DB. Reach through the SQLAlchemy engine/connection to its
367
- # underlying DBAPI ``sqlite3.Connection`` and hand that to the provider.
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
- db_type = "postgresql" # lint: allow-dialect-string: safe fallback only; real type comes from URL scheme + ProviderRegistry.canonical_dialect_name above # noqa: E501
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
- return (
207
- # lint: allow-dialect-string: dialect dispatch
208
- config_dialect
209
- # lint: allow-dialect-string: dialect dispatch
210
- or "postgresql"
211
- ).lower() # lint: allow-dialect-string: dialect dispatch
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 BaseDatabaseConfig, _detect_dialect_from_url
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
- _url_lower = overrides["url"].lower()
151
- if _url_lower.startswith(("sqlite:", "sqlite3:")):
152
- result.type = "sqlite" # lint: allow-dialect-string: dialect dispatch
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
- _url_lower = overrides["url"].lower()
170
- if not _url_lower.startswith(("sqlite:", "sqlite3:")):
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": "sqlite", # lint: allow-dialect-string: dialect dispatch
209
+ "type": _file_dialect,
174
210
  "url": overrides["url"],
175
- "schema": overrides.get("schema", base_config.schema) or "main",
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
- """Handle bare ``sqlite:`` / ``sqlite3:`` URIs (PEP-249 / RFC 8085 style).
102
+ """Infer ``type`` for native URIs (e.g. ``sqlite:`` / ``sqlite3:``) via the registry.
106
103
 
107
- Unknown schemes are left for normal validation so secret/offline URLs can
108
- still flow through the incomplete-config path when requested.
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
- if url_l.startswith("sqlite3:"):
114
- data["type"] = "sqlite" # lint: allow-dialect-string: URL-scheme inference
115
- elif url_l.startswith("sqlite:"):
116
- data["type"] = "sqlite" # lint: allow-dialect-string: URL-scheme inference
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: per-dialect subclasses register themselves at the bottom of this
446
- # module via eager imports of ``config._subclasses.*_config``; the
447
- # ``BaseDatabaseConfig._registry`` lookup below depends on those
448
- # registrations being in place by the time ``create()`` runs.
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
- # Re-export per-dialect subclasses so the legacy
593
- # ``from config.database_config import XxxConfig`` import path keeps working
594
- # (used by ~25 test modules and a couple of production sites).
595
- #
596
- # These imports are intentionally at the bottom of the module: each subclass
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
- # Roadmap action #11: third-party plugins do **not** need to add to this list
602
- # they declare ``config_class=MyConfig`` on their ``PluginInfo`` and
603
- # ``_resolve_config_class`` (above) picks it up via the plugin registry. The
604
- # eager imports below stay only because removing them would break the legacy
605
- # import path; they aren't the modern entry point for new dialects.
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
- url_lower = url.lower()
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
- scheme = url_lower.split(":", 1)[0].split("+", 1)[0]
481
- db_type = ProviderRegistry.canonical_dialect_name(scheme) or ""
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
- if url_lower.startswith(("sqlite:", "sqlite3:")):
646
- db_type = "sqlite" # lint: allow-dialect-string: URL-scheme inference (sqlite:// URL maps to sqlite type)
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
- # CosmosDB-specific auth validation: account_endpoint + account_key (or
658
- # use_managed_identity). is_nosql is too generic — match the canonical
659
- # plugin name so future NoSQL dialects don't inherit Azure-specific rules.
660
- # lint: allow-dialect-string: CosmosDB Azure auth validation
661
- if ProviderRegistry.canonical_dialect_name(db_type) == "cosmosdb":
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
- inferred_type = ""
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
- scheme = url_lower.split(":", 1)[0].split("+", 1)[0]
853
- inferred_type = ProviderRegistry.canonical_dialect_name(scheme) or ""
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
  ]