dblift 4.0.2__tar.gz → 4.2.0__tar.gz

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