dblift 3.2.0__tar.gz → 3.3.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 (473) hide show
  1. {dblift-3.2.0 → dblift-3.3.0}/PKG-INFO +1 -1
  2. {dblift-3.2.0 → dblift-3.3.0}/core/introspection/_column_enricher.py +91 -8
  3. {dblift-3.2.0 → dblift-3.3.0}/core/introspection/base_introspector.py +7 -5
  4. {dblift-3.2.0 → dblift-3.3.0}/core/introspection/extractors/index_extractor.py +15 -7
  5. dblift-3.3.0/core/seams/feature_loading.py +109 -0
  6. {dblift-3.2.0 → dblift-3.3.0}/core/seams/sql_generators.py +1 -1
  7. {dblift-3.2.0 → dblift-3.3.0}/core/sql_generator/alter/alter_generator_factory.py +10 -2
  8. {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/view.py +1 -1
  9. {dblift-3.2.0 → dblift-3.3.0}/db/base_quirks.py +19 -9
  10. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/cosmosdb/cosmosdb/history_manager.py +1 -1
  11. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/cosmosdb/cosmosdb/query_executor.py +56 -5
  12. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/cosmosdb/provider.py +22 -0
  13. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/db2/quirks.py +4 -2
  14. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/duckdb/parser/duckdb_regex_parser.py +1 -1
  15. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/redshift/sqlalchemy_url.py +11 -14
  16. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlite/provider.py +0 -11
  17. {dblift-3.2.0 → dblift-3.3.0}/dblift.egg-info/PKG-INFO +1 -1
  18. {dblift-3.2.0 → dblift-3.3.0}/pyproject.toml +1 -1
  19. dblift-3.2.0/core/seams/feature_loading.py +0 -66
  20. {dblift-3.2.0 → dblift-3.3.0}/LICENSE +0 -0
  21. {dblift-3.2.0 → dblift-3.3.0}/README.md +0 -0
  22. {dblift-3.2.0 → dblift-3.3.0}/api/__init__.py +0 -0
  23. {dblift-3.2.0 → dblift-3.3.0}/api/_cli_support.py +0 -0
  24. {dblift-3.2.0 → dblift-3.3.0}/api/_client_factory.py +0 -0
  25. {dblift-3.2.0 → dblift-3.3.0}/api/_client_operations.py +0 -0
  26. {dblift-3.2.0 → dblift-3.3.0}/api/_engine_config.py +0 -0
  27. {dblift-3.2.0 → dblift-3.3.0}/api/async_client.py +0 -0
  28. {dblift-3.2.0 → dblift-3.3.0}/api/client.py +0 -0
  29. {dblift-3.2.0 → dblift-3.3.0}/api/events.py +0 -0
  30. {dblift-3.2.0 → dblift-3.3.0}/api/migrations.py +0 -0
  31. {dblift-3.2.0 → dblift-3.3.0}/api/py.typed +0 -0
  32. {dblift-3.2.0 → dblift-3.3.0}/cli/__init__.py +0 -0
  33. {dblift-3.2.0 → dblift-3.3.0}/cli/_command_handlers.py +0 -0
  34. {dblift-3.2.0 → dblift-3.3.0}/cli/_config_helpers.py +0 -0
  35. {dblift-3.2.0 → dblift-3.3.0}/cli/_constants.py +0 -0
  36. {dblift-3.2.0 → dblift-3.3.0}/cli/_output.py +0 -0
  37. {dblift-3.2.0 → dblift-3.3.0}/cli/_parser_setup.py +0 -0
  38. {dblift-3.2.0 → dblift-3.3.0}/cli/commands/__init__.py +0 -0
  39. {dblift-3.2.0 → dblift-3.3.0}/cli/commands/config_command.py +0 -0
  40. {dblift-3.2.0 → dblift-3.3.0}/cli/db_utils.py +0 -0
  41. {dblift-3.2.0 → dblift-3.3.0}/cli/extensions.py +0 -0
  42. {dblift-3.2.0 → dblift-3.3.0}/cli/handlers/__init__.py +0 -0
  43. {dblift-3.2.0 → dblift-3.3.0}/cli/handlers/_shared.py +0 -0
  44. {dblift-3.2.0 → dblift-3.3.0}/cli/handlers/baseline.py +0 -0
  45. {dblift-3.2.0 → dblift-3.3.0}/cli/handlers/clean.py +0 -0
  46. {dblift-3.2.0 → dblift-3.3.0}/cli/handlers/import_flyway.py +0 -0
  47. {dblift-3.2.0 → dblift-3.3.0}/cli/handlers/info.py +0 -0
  48. {dblift-3.2.0 → dblift-3.3.0}/cli/handlers/migrate.py +0 -0
  49. {dblift-3.2.0 → dblift-3.3.0}/cli/handlers/repair.py +0 -0
  50. {dblift-3.2.0 → dblift-3.3.0}/cli/handlers/undo.py +0 -0
  51. {dblift-3.2.0 → dblift-3.3.0}/cli/handlers/validate.py +0 -0
  52. {dblift-3.2.0 → dblift-3.3.0}/cli/main.py +0 -0
  53. {dblift-3.2.0 → dblift-3.3.0}/cli/premium_manifest.py +0 -0
  54. {dblift-3.2.0 → dblift-3.3.0}/config/__init__.py +0 -0
  55. {dblift-3.2.0 → dblift-3.3.0}/config/_credential_masking.py +0 -0
  56. {dblift-3.2.0 → dblift-3.3.0}/config/_subclasses/__init__.py +0 -0
  57. {dblift-3.2.0 → dblift-3.3.0}/config/_subclasses/dummy_config.py +0 -0
  58. {dblift-3.2.0 → dblift-3.3.0}/config/_url_builder_mixin.py +0 -0
  59. {dblift-3.2.0 → dblift-3.3.0}/config/config_builder.py +0 -0
  60. {dblift-3.2.0 → dblift-3.3.0}/config/database_config.py +0 -0
  61. {dblift-3.2.0 → dblift-3.3.0}/config/dblift_config.py +0 -0
  62. {dblift-3.2.0 → dblift-3.3.0}/config/errors.py +0 -0
  63. {dblift-3.2.0 → dblift-3.3.0}/config/property_registry.py +0 -0
  64. {dblift-3.2.0 → dblift-3.3.0}/config/secrets/__init__.py +0 -0
  65. {dblift-3.2.0 → dblift-3.3.0}/config/secrets/_cache.py +0 -0
  66. {dblift-3.2.0 → dblift-3.3.0}/config/secrets/_provider_base.py +0 -0
  67. {dblift-3.2.0 → dblift-3.3.0}/config/secrets/_registry.py +0 -0
  68. {dblift-3.2.0 → dblift-3.3.0}/config/secrets/_resolver.py +0 -0
  69. {dblift-3.2.0 → dblift-3.3.0}/config/secrets/_secrets_config.py +0 -0
  70. {dblift-3.2.0 → dblift-3.3.0}/core/__init__.py +0 -0
  71. {dblift-3.2.0 → dblift-3.3.0}/core/constants.py +0 -0
  72. {dblift-3.2.0 → dblift-3.3.0}/core/dialect_boundary.py +0 -0
  73. {dblift-3.2.0 → dblift-3.3.0}/core/exceptions.py +0 -0
  74. {dblift-3.2.0 → dblift-3.3.0}/core/introspection/__init__.py +0 -0
  75. {dblift-3.2.0 → dblift-3.3.0}/core/introspection/_partition_enricher.py +0 -0
  76. {dblift-3.2.0 → dblift-3.3.0}/core/introspection/_schema_orchestrator.py +0 -0
  77. {dblift-3.2.0 → dblift-3.3.0}/core/introspection/_utils.py +0 -0
  78. {dblift-3.2.0 → dblift-3.3.0}/core/introspection/_vendor_property_applier.py +0 -0
  79. {dblift-3.2.0 → dblift-3.3.0}/core/introspection/extractors/__init__.py +0 -0
  80. {dblift-3.2.0 → dblift-3.3.0}/core/introspection/extractors/base_extractor.py +0 -0
  81. {dblift-3.2.0 → dblift-3.3.0}/core/introspection/extractors/column_extractor.py +0 -0
  82. {dblift-3.2.0 → dblift-3.3.0}/core/introspection/extractors/constraint_extractor.py +0 -0
  83. {dblift-3.2.0 → dblift-3.3.0}/core/introspection/extractors/misc_extractor.py +0 -0
  84. {dblift-3.2.0 → dblift-3.3.0}/core/introspection/extractors/procedure_extractor.py +0 -0
  85. {dblift-3.2.0 → dblift-3.3.0}/core/introspection/extractors/sequence_extractor.py +0 -0
  86. {dblift-3.2.0 → dblift-3.3.0}/core/introspection/extractors/table_extractor.py +0 -0
  87. {dblift-3.2.0 → dblift-3.3.0}/core/introspection/extractors/trigger_extractor.py +0 -0
  88. {dblift-3.2.0 → dblift-3.3.0}/core/introspection/extractors/view_extractor.py +0 -0
  89. {dblift-3.2.0 → dblift-3.3.0}/core/introspection/introspector_factory.py +0 -0
  90. {dblift-3.2.0 → dblift-3.3.0}/core/introspection/result.py +0 -0
  91. {dblift-3.2.0 → dblift-3.3.0}/core/introspection/schema_introspector.py +0 -0
  92. {dblift-3.2.0 → dblift-3.3.0}/core/introspection/vendor_queries_base.py +0 -0
  93. {dblift-3.2.0 → dblift-3.3.0}/core/introspection/vendor_queries_factory.py +0 -0
  94. {dblift-3.2.0 → dblift-3.3.0}/core/introspection/vendor_queries_protocols.py +0 -0
  95. {dblift-3.2.0 → dblift-3.3.0}/core/introspection/version_detector.py +0 -0
  96. {dblift-3.2.0 → dblift-3.3.0}/core/logger/__init__.py +0 -0
  97. {dblift-3.2.0 → dblift-3.3.0}/core/logger/_base.py +0 -0
  98. {dblift-3.2.0 → dblift-3.3.0}/core/logger/_factory.py +0 -0
  99. {dblift-3.2.0 → dblift-3.3.0}/core/logger/_formatters.py +0 -0
  100. {dblift-3.2.0 → dblift-3.3.0}/core/logger/_levels.py +0 -0
  101. {dblift-3.2.0 → dblift-3.3.0}/core/logger/_multi.py +0 -0
  102. {dblift-3.2.0 → dblift-3.3.0}/core/logger/_null.py +0 -0
  103. {dblift-3.2.0 → dblift-3.3.0}/core/logger/console.py +0 -0
  104. {dblift-3.2.0 → dblift-3.3.0}/core/logger/formatters/__init__.py +0 -0
  105. {dblift-3.2.0 → dblift-3.3.0}/core/logger/formatters/_formatter_impl.py +0 -0
  106. {dblift-3.2.0 → dblift-3.3.0}/core/logger/formatters/factory.py +0 -0
  107. {dblift-3.2.0 → dblift-3.3.0}/core/logger/formatters/formatter.py +0 -0
  108. {dblift-3.2.0 → dblift-3.3.0}/core/logger/formatters/htmlformatter.py +0 -0
  109. {dblift-3.2.0 → dblift-3.3.0}/core/logger/formatters/jsonformatter.py +0 -0
  110. {dblift-3.2.0 → dblift-3.3.0}/core/logger/log.py +0 -0
  111. {dblift-3.2.0 → dblift-3.3.0}/core/logger/results.py +0 -0
  112. {dblift-3.2.0 → dblift-3.3.0}/core/logger/templates/oldreport.html +0 -0
  113. {dblift-3.2.0 → dblift-3.3.0}/core/logger/templates/report.html +0 -0
  114. {dblift-3.2.0 → dblift-3.3.0}/core/migration/__init__.py +0 -0
  115. {dblift-3.2.0 → dblift-3.3.0}/core/migration/_type_match.py +0 -0
  116. {dblift-3.2.0 → dblift-3.3.0}/core/migration/clean_summary.py +0 -0
  117. {dblift-3.2.0 → dblift-3.3.0}/core/migration/commands/__init__.py +0 -0
  118. {dblift-3.2.0 → dblift-3.3.0}/core/migration/commands/_script_events.py +0 -0
  119. {dblift-3.2.0 → dblift-3.3.0}/core/migration/commands/base_command.py +0 -0
  120. {dblift-3.2.0 → dblift-3.3.0}/core/migration/commands/baseline_command.py +0 -0
  121. {dblift-3.2.0 → dblift-3.3.0}/core/migration/commands/clean_command.py +0 -0
  122. {dblift-3.2.0 → dblift-3.3.0}/core/migration/commands/import_flyway_command.py +0 -0
  123. {dblift-3.2.0 → dblift-3.3.0}/core/migration/commands/info_command.py +0 -0
  124. {dblift-3.2.0 → dblift-3.3.0}/core/migration/commands/migrate_command.py +0 -0
  125. {dblift-3.2.0 → dblift-3.3.0}/core/migration/commands/repair_command.py +0 -0
  126. {dblift-3.2.0 → dblift-3.3.0}/core/migration/commands/undo_command.py +0 -0
  127. {dblift-3.2.0 → dblift-3.3.0}/core/migration/commands/validate_command.py +0 -0
  128. {dblift-3.2.0 → dblift-3.3.0}/core/migration/encoding.py +0 -0
  129. {dblift-3.2.0 → dblift-3.3.0}/core/migration/executor/__init__.py +0 -0
  130. {dblift-3.2.0 → dblift-3.3.0}/core/migration/executor/execution_engine.py +0 -0
  131. {dblift-3.2.0 → dblift-3.3.0}/core/migration/executor/migration_executor.py +0 -0
  132. {dblift-3.2.0 → dblift-3.3.0}/core/migration/executor/migration_helpers.py +0 -0
  133. {dblift-3.2.0 → dblift-3.3.0}/core/migration/executor/placeholder_manager.py +0 -0
  134. {dblift-3.2.0 → dblift-3.3.0}/core/migration/executor/transaction_policy.py +0 -0
  135. {dblift-3.2.0 → dblift-3.3.0}/core/migration/executors/__init__.py +0 -0
  136. {dblift-3.2.0 → dblift-3.3.0}/core/migration/executors/base_executor.py +0 -0
  137. {dblift-3.2.0 → dblift-3.3.0}/core/migration/executors/executor_factory.py +0 -0
  138. {dblift-3.2.0 → dblift-3.3.0}/core/migration/executors/python_executor.py +0 -0
  139. {dblift-3.2.0 → dblift-3.3.0}/core/migration/executors/sql_executor.py +0 -0
  140. {dblift-3.2.0 → dblift-3.3.0}/core/migration/formats/__init__.py +0 -0
  141. {dblift-3.2.0 → dblift-3.3.0}/core/migration/formats/format_detector.py +0 -0
  142. {dblift-3.2.0 → dblift-3.3.0}/core/migration/formats/migration_format.py +0 -0
  143. {dblift-3.2.0 → dblift-3.3.0}/core/migration/history/__init__.py +0 -0
  144. {dblift-3.2.0 → dblift-3.3.0}/core/migration/history/migration_history_manager.py +0 -0
  145. {dblift-3.2.0 → dblift-3.3.0}/core/migration/journals/__init__.py +0 -0
  146. {dblift-3.2.0 → dblift-3.3.0}/core/migration/journals/migration_journal.py +0 -0
  147. {dblift-3.2.0 → dblift-3.3.0}/core/migration/migration.py +0 -0
  148. {dblift-3.2.0 → dblift-3.3.0}/core/migration/placeholders/__init__.py +0 -0
  149. {dblift-3.2.0 → dblift-3.3.0}/core/migration/placeholders/placeholder_service.py +0 -0
  150. {dblift-3.2.0 → dblift-3.3.0}/core/migration/rules/__init__.py +0 -0
  151. {dblift-3.2.0 → dblift-3.3.0}/core/migration/rules/migration_rules.py +0 -0
  152. {dblift-3.2.0 → dblift-3.3.0}/core/migration/scripting/__init__.py +0 -0
  153. {dblift-3.2.0 → dblift-3.3.0}/core/migration/scripting/migration_script_manager.py +0 -0
  154. {dblift-3.2.0 → dblift-3.3.0}/core/migration/scripting/undo_script_generator/__init__.py +0 -0
  155. {dblift-3.2.0 → dblift-3.3.0}/core/migration/scripting/undo_script_generator/_ddl_reversers.py +0 -0
  156. {dblift-3.2.0 → dblift-3.3.0}/core/migration/scripting/undo_script_generator/_dml_reversers.py +0 -0
  157. {dblift-3.2.0 → dblift-3.3.0}/core/migration/scripting/undo_script_generator/_extractors.py +0 -0
  158. {dblift-3.2.0 → dblift-3.3.0}/core/migration/scripting/undo_script_generator/_generator.py +0 -0
  159. {dblift-3.2.0 → dblift-3.3.0}/core/migration/scripting/undo_script_generator/_helpers.py +0 -0
  160. {dblift-3.2.0 → dblift-3.3.0}/core/migration/scripting/undo_script_generator/_models.py +0 -0
  161. {dblift-3.2.0 → dblift-3.3.0}/core/migration/scripting/undo_script_generator/_reversers.py +0 -0
  162. {dblift-3.2.0 → dblift-3.3.0}/core/migration/sql/__init__.py +0 -0
  163. {dblift-3.2.0 → dblift-3.3.0}/core/migration/sql/execution_statement.py +0 -0
  164. {dblift-3.2.0 → dblift-3.3.0}/core/migration/sql/sql_analyzer.py +0 -0
  165. {dblift-3.2.0 → dblift-3.3.0}/core/migration/sql/sql_execution_service.py +0 -0
  166. {dblift-3.2.0 → dblift-3.3.0}/core/migration/sql/statement_splitter.py +0 -0
  167. {dblift-3.2.0 → dblift-3.3.0}/core/migration/state/__init__.py +0 -0
  168. {dblift-3.2.0 → dblift-3.3.0}/core/migration/state/migration_data_service.py +0 -0
  169. {dblift-3.2.0 → dblift-3.3.0}/core/migration/state/migration_display_state.py +0 -0
  170. {dblift-3.2.0 → dblift-3.3.0}/core/migration/state/migration_formatter.py +0 -0
  171. {dblift-3.2.0 → dblift-3.3.0}/core/migration/state/migration_state.py +0 -0
  172. {dblift-3.2.0 → dblift-3.3.0}/core/migration/state/migration_state_manager.py +0 -0
  173. {dblift-3.2.0 → dblift-3.3.0}/core/migration/state/migration_state_service.py +0 -0
  174. {dblift-3.2.0 → dblift-3.3.0}/core/migration/ui/__init__.py +0 -0
  175. {dblift-3.2.0 → dblift-3.3.0}/core/migration/ui/data_collector.py +0 -0
  176. {dblift-3.2.0 → dblift-3.3.0}/core/migration/ui/display_formatters.py +0 -0
  177. {dblift-3.2.0 → dblift-3.3.0}/core/migration/ui/migration_analyzer.py +0 -0
  178. {dblift-3.2.0 → dblift-3.3.0}/core/migration/ui/migration_ui.py +0 -0
  179. {dblift-3.2.0 → dblift-3.3.0}/core/migration/ui/table_renderer.py +0 -0
  180. {dblift-3.2.0 → dblift-3.3.0}/core/migration/version_utils.py +0 -0
  181. {dblift-3.2.0 → dblift-3.3.0}/core/normalization/__init__.py +0 -0
  182. {dblift-3.2.0 → dblift-3.3.0}/core/normalization/dependency_resolver.py +0 -0
  183. {dblift-3.2.0 → dblift-3.3.0}/core/normalization/identifier_normalizer.py +0 -0
  184. {dblift-3.2.0 → dblift-3.3.0}/core/normalization/object_orderer.py +0 -0
  185. {dblift-3.2.0 → dblift-3.3.0}/core/normalization/type_constants.py +0 -0
  186. {dblift-3.2.0 → dblift-3.3.0}/core/normalization/type_mapper.py +0 -0
  187. {dblift-3.2.0 → dblift-3.3.0}/core/normalization/type_mappings.py +0 -0
  188. {dblift-3.2.0 → dblift-3.3.0}/core/normalization/type_normalizer.py +0 -0
  189. {dblift-3.2.0 → dblift-3.3.0}/core/seams/__init__.py +0 -0
  190. {dblift-3.2.0 → dblift-3.3.0}/core/seams/capabilities.py +0 -0
  191. {dblift-3.2.0 → dblift-3.3.0}/core/seams/client_factory.py +0 -0
  192. {dblift-3.2.0 → dblift-3.3.0}/core/seams/event_listeners.py +0 -0
  193. {dblift-3.2.0 → dblift-3.3.0}/core/seams/introspection.py +0 -0
  194. {dblift-3.2.0 → dblift-3.3.0}/core/seams/license_info.py +0 -0
  195. {dblift-3.2.0 → dblift-3.3.0}/core/seams/runtime_checks.py +0 -0
  196. {dblift-3.2.0 → dblift-3.3.0}/core/seams/tier_resolver.py +0 -0
  197. {dblift-3.2.0 → dblift-3.3.0}/core/sql_generator/__init__.py +0 -0
  198. {dblift-3.2.0 → dblift-3.3.0}/core/sql_generator/alter/__init__.py +0 -0
  199. {dblift-3.2.0 → dblift-3.3.0}/core/sql_generator/alter/base_alter_generator.py +0 -0
  200. {dblift-3.2.0 → dblift-3.3.0}/core/sql_generator/alter_generator.py +0 -0
  201. {dblift-3.2.0 → dblift-3.3.0}/core/sql_generator/base_generator.py +0 -0
  202. {dblift-3.2.0 → dblift-3.3.0}/core/sql_generator/basic_table_ddl_generator.py +0 -0
  203. {dblift-3.2.0 → dblift-3.3.0}/core/sql_generator/dependency_analyzer.py +0 -0
  204. {dblift-3.2.0 → dblift-3.3.0}/core/sql_generator/diff_converters/__init__.py +0 -0
  205. {dblift-3.2.0 → dblift-3.3.0}/core/sql_generator/diff_converters/base_converter.py +0 -0
  206. {dblift-3.2.0 → dblift-3.3.0}/core/sql_generator/formatter.py +0 -0
  207. {dblift-3.2.0 → dblift-3.3.0}/core/sql_generator/generator_factory.py +0 -0
  208. {dblift-3.2.0 → dblift-3.3.0}/core/sql_generator/options.py +0 -0
  209. {dblift-3.2.0 → dblift-3.3.0}/core/sql_generator/script_organizer.py +0 -0
  210. {dblift-3.2.0 → dblift-3.3.0}/core/sql_generator/sql_generator.py +0 -0
  211. {dblift-3.2.0 → dblift-3.3.0}/core/sql_generator/sql_statement.py +0 -0
  212. {dblift-3.2.0 → dblift-3.3.0}/core/sql_generator/table_ddl_render.py +0 -0
  213. {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/__init__.py +0 -0
  214. {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/_base_parse_result.py +0 -0
  215. {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/_base_sql_column.py +0 -0
  216. {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/_base_sql_constraint.py +0 -0
  217. {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/_base_sql_object.py +0 -0
  218. {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/_base_sql_statement.py +0 -0
  219. {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/base.py +0 -0
  220. {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/constraint_validator.py +0 -0
  221. {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/database_link.py +0 -0
  222. {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/dialect.py +0 -0
  223. {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/event.py +0 -0
  224. {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/extension.py +0 -0
  225. {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/feature_gates.py +0 -0
  226. {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/foreign_data_wrapper.py +0 -0
  227. {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/foreign_server.py +0 -0
  228. {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/index.py +0 -0
  229. {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/linked_server.py +0 -0
  230. {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/module.py +0 -0
  231. {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/package.py +0 -0
  232. {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/partition.py +0 -0
  233. {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/procedure.py +0 -0
  234. {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/sequence.py +0 -0
  235. {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/server_info.py +0 -0
  236. {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/synonym.py +0 -0
  237. {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/table.py +0 -0
  238. {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/table_canonicalizer.py +0 -0
  239. {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/table_options.py +0 -0
  240. {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/trigger.py +0 -0
  241. {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/user_defined_type.py +0 -0
  242. {dblift-3.2.0 → dblift-3.3.0}/core/sql_model/view_options.py +0 -0
  243. {dblift-3.2.0 → dblift-3.3.0}/core/sql_parser/__init__.py +0 -0
  244. {dblift-3.2.0 → dblift-3.3.0}/core/sql_parser/_partition_handler.py +0 -0
  245. {dblift-3.2.0 → dblift-3.3.0}/core/sql_parser/_sqlglot_builders.py +0 -0
  246. {dblift-3.2.0 → dblift-3.3.0}/core/sql_parser/base_statement_parser.py +0 -0
  247. {dblift-3.2.0 → dblift-3.3.0}/core/sql_parser/base_tokenizer.py +0 -0
  248. {dblift-3.2.0 → dblift-3.3.0}/core/sql_parser/common/__init__.py +0 -0
  249. {dblift-3.2.0 → dblift-3.3.0}/core/sql_parser/common/base_parser.py +0 -0
  250. {dblift-3.2.0 → dblift-3.3.0}/core/sql_parser/dialects/__init__.py +0 -0
  251. {dblift-3.2.0 → dblift-3.3.0}/core/sql_parser/dialects/base_config.py +0 -0
  252. {dblift-3.2.0 → dblift-3.3.0}/core/sql_parser/enhanced_regex_parser.py +0 -0
  253. {dblift-3.2.0 → dblift-3.3.0}/core/sql_parser/hybrid_parser.py +0 -0
  254. {dblift-3.2.0 → dblift-3.3.0}/core/sql_parser/parser_context.py +0 -0
  255. {dblift-3.2.0 → dblift-3.3.0}/core/sql_parser/parser_factory.py +0 -0
  256. {dblift-3.2.0 → dblift-3.3.0}/core/sql_parser/parser_interface.py +0 -0
  257. {dblift-3.2.0 → dblift-3.3.0}/core/sql_parser/sqlglot_parser.py +0 -0
  258. {dblift-3.2.0 → dblift-3.3.0}/core/sql_parser/tokens.py +0 -0
  259. {dblift-3.2.0 → dblift-3.3.0}/core/sql_parser/unified_regex_parser.py +0 -0
  260. {dblift-3.2.0 → dblift-3.3.0}/core/sql_validator/__init__.py +0 -0
  261. {dblift-3.2.0 → dblift-3.3.0}/core/sql_validator/_checksum_validator.py +0 -0
  262. {dblift-3.2.0 → dblift-3.3.0}/core/sql_validator/_flyway_compatibility.py +0 -0
  263. {dblift-3.2.0 → dblift-3.3.0}/core/sql_validator/_migration_filter.py +0 -0
  264. {dblift-3.2.0 → dblift-3.3.0}/core/sql_validator/_sql_syntax_validator.py +0 -0
  265. {dblift-3.2.0 → dblift-3.3.0}/core/sql_validator/_strict_mode_validator.py +0 -0
  266. {dblift-3.2.0 → dblift-3.3.0}/core/sql_validator/migration_validator.py +0 -0
  267. {dblift-3.2.0 → dblift-3.3.0}/core/state/sql_script_formatter.py +0 -0
  268. {dblift-3.2.0 → dblift-3.3.0}/core/state/sql_statement.py +0 -0
  269. {dblift-3.2.0 → dblift-3.3.0}/core/utils/__init__.py +0 -0
  270. {dblift-3.2.0 → dblift-3.3.0}/core/utils/database_url_parser.py +0 -0
  271. {dblift-3.2.0 → dblift-3.3.0}/core/utils/metadata_helpers.py +0 -0
  272. {dblift-3.2.0 → dblift-3.3.0}/core/utils/row_access.py +0 -0
  273. {dblift-3.2.0 → dblift-3.3.0}/core/utils/string_utils.py +0 -0
  274. {dblift-3.2.0 → dblift-3.3.0}/core/utils/url_masking.py +0 -0
  275. {dblift-3.2.0 → dblift-3.3.0}/db/__init__.py +0 -0
  276. {dblift-3.2.0 → dblift-3.3.0}/db/base_provider.py +0 -0
  277. {dblift-3.2.0 → dblift-3.3.0}/db/constants.py +0 -0
  278. {dblift-3.2.0 → dblift-3.3.0}/db/dml_analysis.py +0 -0
  279. {dblift-3.2.0 → dblift-3.3.0}/db/error.py +0 -0
  280. {dblift-3.2.0 → dblift-3.3.0}/db/error_handler.py +0 -0
  281. {dblift-3.2.0 → dblift-3.3.0}/db/exceptions.py +0 -0
  282. {dblift-3.2.0 → dblift-3.3.0}/db/feature_gate.py +0 -0
  283. {dblift-3.2.0 → dblift-3.3.0}/db/native_connection_manager.py +0 -0
  284. {dblift-3.2.0 → dblift-3.3.0}/db/object_naming.py +0 -0
  285. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/__init__.py +0 -0
  286. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/_pg_compatible.py +0 -0
  287. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/alloydb/__init__.py +0 -0
  288. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/alloydb/plugin.py +0 -0
  289. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/aurora_postgresql/__init__.py +0 -0
  290. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/aurora_postgresql/plugin.py +0 -0
  291. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/base_history_manager.py +0 -0
  292. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/base_locking_manager.py +0 -0
  293. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/base_query_executor.py +0 -0
  294. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/base_schema_operations.py +0 -0
  295. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/base_snapshot_manager.py +0 -0
  296. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/base_undo_manager.py +0 -0
  297. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/citus/__init__.py +0 -0
  298. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/citus/plugin.py +0 -0
  299. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/cockroachdb/__init__.py +0 -0
  300. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/cockroachdb/plugin.py +0 -0
  301. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/cockroachdb/provider.py +0 -0
  302. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/cockroachdb/quirks.py +0 -0
  303. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/cosmosdb/__init__.py +0 -0
  304. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/cosmosdb/config.py +0 -0
  305. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/cosmosdb/cosmosdb/__init__.py +0 -0
  306. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/cosmosdb/cosmosdb/_sdk.py +0 -0
  307. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/cosmosdb/cosmosdb/connection_manager.py +0 -0
  308. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/cosmosdb/cosmosdb/locking_manager.py +0 -0
  309. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/cosmosdb/cosmosdb/schema_operations.py +0 -0
  310. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/cosmosdb/introspection/__init__.py +0 -0
  311. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/cosmosdb/plugin.py +0 -0
  312. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/cosmosdb/quirks.py +0 -0
  313. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/db2/__init__.py +0 -0
  314. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/db2/config.py +0 -0
  315. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/db2/db2/__init__.py +0 -0
  316. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/db2/db2/history_manager.py +0 -0
  317. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/db2/db2/locking_manager.py +0 -0
  318. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/db2/db2/schema_operations.py +0 -0
  319. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/db2/introspection/__init__.py +0 -0
  320. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/db2/parser/__init__.py +0 -0
  321. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/db2/parser/db2_regex_parser.py +0 -0
  322. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/db2/parser/parser_config.py +0 -0
  323. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/db2/plugin.py +0 -0
  324. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/db2/provider.py +0 -0
  325. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/db2/sqlalchemy_url.py +0 -0
  326. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/duckdb/__init__.py +0 -0
  327. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/duckdb/config.py +0 -0
  328. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/duckdb/parser/__init__.py +0 -0
  329. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/duckdb/parser/parser_config.py +0 -0
  330. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/duckdb/plugin.py +0 -0
  331. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/duckdb/provider.py +0 -0
  332. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/duckdb/quirks.py +0 -0
  333. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/duckdb/sqlalchemy_url.py +0 -0
  334. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mariadb/__init__.py +0 -0
  335. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mariadb/plugin.py +0 -0
  336. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mariadb/provider.py +0 -0
  337. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mariadb/quirks.py +0 -0
  338. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mysql/__init__.py +0 -0
  339. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mysql/config.py +0 -0
  340. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mysql/introspection/__init__.py +0 -0
  341. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mysql/mysql/__init__.py +0 -0
  342. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mysql/mysql/history_manager.py +0 -0
  343. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mysql/mysql/locking_manager.py +0 -0
  344. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mysql/mysql/schema_operations.py +0 -0
  345. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mysql/parser/__init__.py +0 -0
  346. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mysql/parser/mysql_regex_parser.py +0 -0
  347. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mysql/parser/mysql_statement_parser.py +0 -0
  348. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mysql/parser/mysql_tokenizer.py +0 -0
  349. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mysql/parser/parser_config.py +0 -0
  350. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mysql/plugin.py +0 -0
  351. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mysql/provider.py +0 -0
  352. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mysql/quirks.py +0 -0
  353. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/mysql/sqlalchemy_url.py +0 -0
  354. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/neon/__init__.py +0 -0
  355. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/neon/plugin.py +0 -0
  356. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/nosql_base/__init__.py +0 -0
  357. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/nosql_base/history.py +0 -0
  358. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/nosql_base/introspection.py +0 -0
  359. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/nosql_base/locking.py +0 -0
  360. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/__init__.py +0 -0
  361. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/config.py +0 -0
  362. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/introspection/__init__.py +0 -0
  363. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/introspection/oracle_utils.py +0 -0
  364. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/oracle/__init__.py +0 -0
  365. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/oracle/dbms_output.py +0 -0
  366. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/oracle/history_manager.py +0 -0
  367. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/oracle/locking_manager.py +0 -0
  368. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/oracle/schema_operations.py +0 -0
  369. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/parser/__init__.py +0 -0
  370. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/parser/_comments.py +0 -0
  371. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/parser/_object_extractor.py +0 -0
  372. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/parser/_plsql_block.py +0 -0
  373. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/parser/_sqlplus.py +0 -0
  374. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/parser/_statement_splitter.py +0 -0
  375. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/parser/oracle_parser.py +0 -0
  376. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/parser/oracle_statement_parser.py +0 -0
  377. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/parser/oracle_tokenizer.py +0 -0
  378. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/parser/parser_config.py +0 -0
  379. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/parser/sqlplus_context.py +0 -0
  380. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/plugin.py +0 -0
  381. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/provider.py +0 -0
  382. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/quirks.py +0 -0
  383. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/oracle/sqlalchemy_url.py +0 -0
  384. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/postgresql/__init__.py +0 -0
  385. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/postgresql/_provider_query_executor.py +0 -0
  386. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/postgresql/config.py +0 -0
  387. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/postgresql/introspection/__init__.py +0 -0
  388. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/postgresql/parser/__init__.py +0 -0
  389. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/postgresql/parser/parser_config.py +0 -0
  390. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/postgresql/parser/postgresql_regex_parser.py +0 -0
  391. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/postgresql/parser/postgresql_statement_parser.py +0 -0
  392. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/postgresql/parser/postgresql_tokenizer.py +0 -0
  393. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/postgresql/plugin.py +0 -0
  394. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/postgresql/postgresql/__init__.py +0 -0
  395. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/postgresql/postgresql/history_manager.py +0 -0
  396. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/postgresql/postgresql/locking_manager.py +0 -0
  397. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/postgresql/postgresql/schema_operations.py +0 -0
  398. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/postgresql/provider.py +0 -0
  399. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/postgresql/quirks.py +0 -0
  400. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/postgresql/sqlalchemy_url.py +0 -0
  401. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/redshift/__init__.py +0 -0
  402. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/redshift/plugin.py +0 -0
  403. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/redshift/provider.py +0 -0
  404. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/redshift/quirks.py +0 -0
  405. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/snowflake/__init__.py +0 -0
  406. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/snowflake/config.py +0 -0
  407. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/snowflake/plugin.py +0 -0
  408. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/snowflake/provider.py +0 -0
  409. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/snowflake/quirks.py +0 -0
  410. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/snowflake/sqlalchemy_url.py +0 -0
  411. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlite/__init__.py +0 -0
  412. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlite/config.py +0 -0
  413. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlite/introspection/__init__.py +0 -0
  414. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlite/parser/__init__.py +0 -0
  415. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlite/parser/parser_config.py +0 -0
  416. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlite/parser/sqlite_regex_parser.py +0 -0
  417. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlite/plugin.py +0 -0
  418. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlite/quirks.py +0 -0
  419. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlite/sqlalchemy_url.py +0 -0
  420. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlite/sqlite/__init__.py +0 -0
  421. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlite/sqlite/connection_manager.py +0 -0
  422. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlite/sqlite/history_manager.py +0 -0
  423. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlite/sqlite/locking_manager.py +0 -0
  424. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlite/sqlite/query_executor.py +0 -0
  425. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlite/sqlite/schema_operations.py +0 -0
  426. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlserver/__init__.py +0 -0
  427. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlserver/config.py +0 -0
  428. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlserver/introspection/__init__.py +0 -0
  429. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlserver/parser/__init__.py +0 -0
  430. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlserver/parser/parser_config.py +0 -0
  431. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlserver/parser/sqlserver_regex_parser.py +0 -0
  432. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlserver/parser/sqlserver_statement_parser.py +0 -0
  433. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlserver/parser/sqlserver_tokenizer.py +0 -0
  434. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlserver/parser/tsql_batch_separator.py +0 -0
  435. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlserver/plugin.py +0 -0
  436. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlserver/provider.py +0 -0
  437. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlserver/quirks.py +0 -0
  438. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlserver/sqlalchemy_url.py +0 -0
  439. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlserver/sqlserver/__init__.py +0 -0
  440. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlserver/sqlserver/history_manager.py +0 -0
  441. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlserver/sqlserver/locking_manager.py +0 -0
  442. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/sqlserver/sqlserver/schema_operations.py +0 -0
  443. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/supabase/__init__.py +0 -0
  444. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/supabase/plugin.py +0 -0
  445. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/timescaledb/__init__.py +0 -0
  446. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/timescaledb/plugin.py +0 -0
  447. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/yugabytedb/__init__.py +0 -0
  448. {dblift-3.2.0 → dblift-3.3.0}/db/plugins/yugabytedb/plugin.py +0 -0
  449. {dblift-3.2.0 → dblift-3.3.0}/db/provider_capabilities.py +0 -0
  450. {dblift-3.2.0 → dblift-3.3.0}/db/provider_interfaces.py +0 -0
  451. {dblift-3.2.0 → dblift-3.3.0}/db/provider_registry.py +0 -0
  452. {dblift-3.2.0 → dblift-3.3.0}/db/sqlalchemy_provider.py +0 -0
  453. {dblift-3.2.0 → dblift-3.3.0}/db/value_utils.py +0 -0
  454. {dblift-3.2.0 → dblift-3.3.0}/dblift.egg-info/SOURCES.txt +0 -0
  455. {dblift-3.2.0 → dblift-3.3.0}/dblift.egg-info/dependency_links.txt +0 -0
  456. {dblift-3.2.0 → dblift-3.3.0}/dblift.egg-info/entry_points.txt +0 -0
  457. {dblift-3.2.0 → dblift-3.3.0}/dblift.egg-info/requires.txt +0 -0
  458. {dblift-3.2.0 → dblift-3.3.0}/dblift.egg-info/top_level.txt +0 -0
  459. {dblift-3.2.0 → dblift-3.3.0}/integrations/__init__.py +0 -0
  460. {dblift-3.2.0 → dblift-3.3.0}/integrations/django/__init__.py +0 -0
  461. {dblift-3.2.0 → dblift-3.3.0}/integrations/django/_client.py +0 -0
  462. {dblift-3.2.0 → dblift-3.3.0}/integrations/django/_engine.py +0 -0
  463. {dblift-3.2.0 → dblift-3.3.0}/integrations/django/apps.py +0 -0
  464. {dblift-3.2.0 → dblift-3.3.0}/integrations/django/checks.py +0 -0
  465. {dblift-3.2.0 → dblift-3.3.0}/integrations/django/management/__init__.py +0 -0
  466. {dblift-3.2.0 → dblift-3.3.0}/integrations/django/management/commands/__init__.py +0 -0
  467. {dblift-3.2.0 → dblift-3.3.0}/integrations/django/management/commands/dblift_info.py +0 -0
  468. {dblift-3.2.0 → dblift-3.3.0}/integrations/django/management/commands/dblift_migrate.py +0 -0
  469. {dblift-3.2.0 → dblift-3.3.0}/integrations/django/management/commands/dblift_validate.py +0 -0
  470. {dblift-3.2.0 → dblift-3.3.0}/integrations/fastapi.py +0 -0
  471. {dblift-3.2.0 → dblift-3.3.0}/integrations/flask.py +0 -0
  472. {dblift-3.2.0 → dblift-3.3.0}/integrations/opentelemetry.py +0 -0
  473. {dblift-3.2.0 → dblift-3.3.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dblift
3
- Version: 3.2.0
3
+ Version: 3.3.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
@@ -16,7 +16,7 @@ The two helpers take the introspector instance as their first parameter
16
16
  from __future__ import annotations
17
17
 
18
18
  import traceback
19
- from typing import TYPE_CHECKING, List
19
+ from typing import TYPE_CHECKING, Any, List
20
20
 
21
21
  if TYPE_CHECKING:
22
22
  from core.introspection.schema_introspector import SchemaIntrospector
@@ -253,18 +253,101 @@ def enrich_columns_with_identity(
253
253
  ),
254
254
  }
255
255
 
256
+ # sql_variant columns (e.g. SQL Server's
257
+ # sys.identity_columns.seed_value/increment_value/last_value) can
258
+ # arrive via pymssql/FreeTDS as raw on-wire bytes rather than an int
259
+ # (e.g. the int 1 as b'\x01\x00\x00\x00'). Normalize those to a plain
260
+ # int here so every downstream consumer -- regardless of which
261
+ # dialect's driver quirks produced the raw value -- can rely on
262
+ # identity_seed/identity_increment/identity_last_value already being
263
+ # an int. Non-bytes values (str, int, None) pass through unchanged,
264
+ # exactly as before.
265
+ #
266
+ # Bytes are only decoded when their length matches a standard
267
+ # two's-complement integer width (1, 2, 4, or 8 bytes --
268
+ # tinyint/smallint/int/bigint). SQL Server also permits IDENTITY on
269
+ # decimal/numeric columns, whose sql_variant wire shape is a sign
270
+ # byte plus an *unsigned* magnitude -- not a plain two's-complement
271
+ # integer -- so decoding those the same way would silently produce a
272
+ # plausible-looking but wrong value. For any other byte length,
273
+ # refuse to guess and raise instead; the outer try/except around
274
+ # this whole enrichment pass logs a warning for it.
275
+ def _decode_sql_variant_int(column_name: str, field_name: str, value: Any) -> Any:
276
+ if not isinstance(value, bytes):
277
+ return value
278
+ if len(value) not in (1, 2, 4, 8):
279
+ raise ValueError(
280
+ f"Column {column_name!r}: {field_name} arrived as "
281
+ f"{len(value)} raw bytes, which is not a standard "
282
+ f"two's-complement integer width (1, 2, 4, or 8 bytes). "
283
+ f"This looks like a non-integer sql_variant encoding "
284
+ f"(e.g. decimal/numeric IDENTITY) that isn't supported yet."
285
+ )
286
+ return int.from_bytes(value, byteorder="little", signed=True)
287
+
256
288
  # Enrich matching columns with detailed metadata
257
289
  for column in columns:
258
290
  identity_data = identity_map.get(column.name.upper())
259
291
  if identity_data:
260
- # Mark as identity (may already be marked from the column query)
292
+ # Mark as identity (may already be marked from the column
293
+ # query) for now -- the except block below reverts this if
294
+ # decoding seed OR increment fails, since a column whose real
295
+ # identity parameters we can't decode must not still claim
296
+ # is_identity (see that block for why). last_value is decoded
297
+ # separately below and its failure does NOT revert this: it
298
+ # has no downstream DDL consumer, so losing it carries none
299
+ # of the "silently wrong seed/increment" risk the reset here
300
+ # exists to guard against.
261
301
  column.is_identity = True
262
- # Add detailed metadata
263
- column.identity_seed = identity_data["seed_value"]
264
- column.identity_increment = identity_data["increment_value"]
265
- # Note: last_value is not part of SQL Model, store as custom attribute if needed
266
- if identity_data["last_value"] is not None:
267
- column.identity_last_value = identity_data["last_value"] # type: ignore[attr-defined]
302
+ # A decode failure (see _decode_sql_variant_int above) is
303
+ # isolated to THIS column, not the whole table: without this,
304
+ # one decimal/numeric-typed IDENTITY column sitting alongside
305
+ # ordinary int-typed ones would raise past this loop into the
306
+ # table-level except below, silently losing seed/increment/
307
+ # last_value for every column processed after it too -- a
308
+ # wider blast radius than the single bad column the raise is
309
+ # actually about.
310
+ try:
311
+ # Add detailed metadata
312
+ column.identity_seed = _decode_sql_variant_int(
313
+ column.name, "seed_value", identity_data["seed_value"]
314
+ )
315
+ column.identity_increment = _decode_sql_variant_int(
316
+ column.name, "increment_value", identity_data["increment_value"]
317
+ )
318
+ except ValueError as decode_error:
319
+ si.log.warning(
320
+ f"Could not decode identity metadata for column "
321
+ f"{column.name!r} in {schema}.{table}: {decode_error}"
322
+ )
323
+ # A decode failure means we cannot reliably tell "seed
324
+ # decoded fine, increment failed" apart from "failed
325
+ # immediately" -- either way, downstream DDL rendering
326
+ # must not mistake a leftover None for "never asked, use
327
+ # SQL Server's own default". Reset is_identity and the
328
+ # two fields render_identity_clause reads so the column
329
+ # falls out of identity handling entirely rather than
330
+ # rendering a guessed IDENTITY(1,1).
331
+ column.is_identity = False
332
+ column.identity_seed = None
333
+ column.identity_increment = None
334
+ else:
335
+ # seed/increment decoded fine -- only now attempt
336
+ # last_value, in its own try/except so that a
337
+ # last_value-only decode failure (e.g. a non-standard
338
+ # byte width) doesn't discard the good seed/increment we
339
+ # just stored. Note: last_value is not part of SQL Model,
340
+ # store as custom attribute if needed.
341
+ if identity_data["last_value"] is not None:
342
+ try:
343
+ column.identity_last_value = _decode_sql_variant_int( # type: ignore[attr-defined]
344
+ column.name, "last_value", identity_data["last_value"]
345
+ )
346
+ except ValueError as decode_error:
347
+ si.log.warning(
348
+ f"Could not decode identity metadata for column "
349
+ f"{column.name!r} in {schema}.{table}: {decode_error}"
350
+ )
268
351
 
269
352
  if identity_map:
270
353
  si.log.debug(
@@ -387,19 +387,21 @@ class BaseIntrospector:
387
387
  # Delegate to index extractor
388
388
  return self._get_index_extractor().get_indexes(schema, table)
389
389
 
390
- def get_all_indexes(self, schema: str) -> List[Index]:
390
+ def get_all_indexes(self, schema: str) -> Optional[List[Index]]:
391
391
  """
392
392
  Get all indexes for an entire schema in a single bulk query.
393
393
 
394
- Delegates to IndexExtractor.get_all_indexes. Returns an empty list
395
- if the dialect does not support bulk index retrieval.
394
+ Delegates to IndexExtractor.get_all_indexes and propagates its
395
+ three-way answer unchanged.
396
396
 
397
397
  Args:
398
398
  schema: Schema name
399
399
 
400
400
  Returns:
401
- List of Index objects for the entire schema, or [] if the dialect
402
- does not support bulk retrieval.
401
+ ``None`` if the dialect cannot answer in bulk, meaning the caller
402
+ must fall back to per-table retrieval. ``[]`` if the bulk query ran
403
+ and the schema genuinely has no indexes. Otherwise the list of
404
+ Index objects for the entire schema.
403
405
  """
404
406
  return self._get_index_extractor().get_all_indexes(schema)
405
407
 
@@ -1,6 +1,6 @@
1
1
  """Index extraction from plugin-owned vendor metadata queries."""
2
2
 
3
- from typing import Any, Dict, List
3
+ from typing import Any, Dict, List, Optional
4
4
 
5
5
  from core.introspection.extractors.base_extractor import BaseExtractor
6
6
  from core.sql_model.index import Index
@@ -42,7 +42,7 @@ class IndexExtractor(BaseExtractor):
42
42
  # Convert to Index objects
43
43
  return self._build_index_objects(schema, table, indexes_data)
44
44
 
45
- def get_all_indexes(self, schema: str) -> List[Index]:
45
+ def get_all_indexes(self, schema: str) -> Optional[List[Index]]:
46
46
  """
47
47
  Get all indexes for an entire schema.
48
48
 
@@ -52,17 +52,25 @@ class IndexExtractor(BaseExtractor):
52
52
  schema: Schema name
53
53
 
54
54
  Returns:
55
- List of Index objects for the entire schema
55
+ ``None`` if bulk retrieval is unavailable — either no vendor
56
+ queries exist for the dialect, or its bulk index query declines —
57
+ meaning the caller must ask table by table. ``[]`` if the bulk
58
+ query ran and the schema genuinely has no indexes. Otherwise the
59
+ list of Index objects for the entire schema.
56
60
  """
61
+ # Unlike ColumnExtractor and TableExtractor.get_tables, which raise
62
+ # RuntimeError when their query is unavailable, this refuses with
63
+ # None: those two have no fallback, so refusing is the only honest
64
+ # option, whereas a working per-table path exists here and None routes
65
+ # the caller to it. Raising would break callers that legitimately
66
+ # have that fallback.
57
67
  if not self.vendor_queries:
58
- return []
68
+ return None
59
69
 
60
70
  self.ensure_metadata()
61
71
  query_result = self.vendor_queries.get_all_indexes_query(schema)
62
72
  if query_result is None:
63
- return (
64
- []
65
- ) # no bulk query; empty for this test path (per-table fallback would be used by callers)
73
+ return None
66
74
 
67
75
  query, params = query_result
68
76
  rows = self.provider.query_executor.execute_query(self.connection, query, params)
@@ -0,0 +1,109 @@
1
+ """Load ``dblift.features`` entry points (tier feature registration).
2
+
3
+ Each ``dblift.features`` entry point resolves to a no-arg callable that
4
+ performs registrations against the neutral seams
5
+ (:mod:`core.seams.runtime_checks`, :mod:`core.seams.tier_resolver`, ...).
6
+ OSS ships none; installed higher tiers register their runtime checks and
7
+ tier resolver here.
8
+
9
+ Loading is idempotent via a module-level once-flag: entry points do not
10
+ change at runtime, and both CLI startup and API-client construction invoke
11
+ :func:`load_feature_extensions`, so double-loading is expected and cheap. An
12
+ empty entry-point result latches immediately unless a known tier
13
+ distribution (``dblift-pro``/``dblift-enterprise``) is installed but its
14
+ entry point hasn't reached this process's metadata view yet, in which case
15
+ it is retried on a later call instead of latched.
16
+
17
+ ``DBLIFT_DISABLE_CLI_EXTENSIONS=1`` skips feature loading entirely. This is
18
+ an accepted, explicit operator opt-out: with higher-tier packages installed
19
+ it also disables their registered runtime checks and tier resolution for
20
+ API usage in the same process.
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ import logging
26
+ import os
27
+ from collections.abc import Callable
28
+ from importlib.metadata import PackageNotFoundError, entry_points, version
29
+ from typing import Protocol
30
+
31
+ _log = logging.getLogger(__name__)
32
+
33
+ FEATURE_ENTRY_POINT_GROUP = "dblift.features"
34
+
35
+ # Feature registrations load in tier order (pro before enterprise, unknown
36
+ # third parties in between, name as the tie-breaker) — the same ordering
37
+ # contract as cli/extensions.py's command-extension loaders.
38
+ _TIER_LOAD_ORDER = {"pro": 0, "enterprise": 2}
39
+ _DEFAULT_TIER_LOAD_ORDER = 1
40
+
41
+ _features_loaded = False
42
+
43
+ # Must be updated if a tier package's distribution name changes in the
44
+ # monorepo, or a new tier is added under a different name -- an unlisted
45
+ # distribution is indistinguishable here from "not installed", so
46
+ # _any_tier_distribution_installed silently reverts to the pre-fix
47
+ # never-retries behavior for it.
48
+ _KNOWN_TIER_DISTRIBUTIONS = ("dblift-pro", "dblift-enterprise")
49
+
50
+
51
+ def _any_tier_distribution_installed() -> bool:
52
+ """Whether a known paid-tier package is installed as a distribution.
53
+
54
+ Checked independently of whether its ``dblift.features`` entry point has
55
+ become visible yet in this process's ``importlib.metadata`` view -- this
56
+ is what distinguishes "no dblift.features entry point will ever exist"
57
+ (pure OSS, no tier package installed at all -- the ordinary case) from
58
+ "one exists but hasn't reached this process's view of the metadata yet"
59
+ (the documented race an empty ``entry_points()`` result alone can't
60
+ rule out).
61
+ """
62
+ for name in _KNOWN_TIER_DISTRIBUTIONS:
63
+ try:
64
+ version(name)
65
+ except PackageNotFoundError:
66
+ continue
67
+ except Exception as exc: # a broken distribution must not break startup
68
+ _log.warning("Could not check whether %r is installed: %s", name, exc)
69
+ continue
70
+ return True
71
+ return False
72
+
73
+
74
+ class _FeatureEntryPoint(Protocol):
75
+ name: str
76
+
77
+ def load(self) -> Callable[[], object]:
78
+ pass
79
+
80
+
81
+ def load_feature_extensions() -> None:
82
+ """Load every ``dblift.features`` entry point (idempotent, best-effort)."""
83
+ global _features_loaded
84
+ if _features_loaded or os.environ.get("DBLIFT_DISABLE_CLI_EXTENSIONS") == "1":
85
+ return
86
+ discovered_by_name: dict[str, _FeatureEntryPoint] = {
87
+ entry_point.name: entry_point
88
+ for entry_point in entry_points(group=FEATURE_ENTRY_POINT_GROUP)
89
+ }
90
+ ordered = sorted(
91
+ discovered_by_name.values(),
92
+ key=lambda ep: (_TIER_LOAD_ORDER.get(ep.name, _DEFAULT_TIER_LOAD_ORDER), ep.name),
93
+ )
94
+ for entry_point in ordered:
95
+ try:
96
+ register = entry_point.load()
97
+ register()
98
+ except Exception as exc: # a bad plugin must not break startup
99
+ _log.warning("dblift.features '%s' failed to load: %s", entry_point.name, exc)
100
+ # Latch when something was actually found, OR when nothing was found but
101
+ # no tier package is installed at all -- an empty result is only the
102
+ # documented race (a paid package's dblift.features entry point not yet
103
+ # reaching this process's importlib.metadata view, needing a retry on a
104
+ # later call, exactly like the identical latch bug fixed in
105
+ # AlterGeneratorFactory._ensure_populated) when a tier distribution is
106
+ # actually installed. In pure OSS, with no tier package installed,
107
+ # OSS's own pyproject.toml declares the entry-points group empty, so an
108
+ # empty result is permanent and must latch immediately.
109
+ _features_loaded = bool(ordered) or not _any_tier_distribution_installed()
@@ -35,7 +35,7 @@ def attach_registered_sql_generators() -> None:
35
35
  def clear_sql_generator_registrars() -> None:
36
36
  """Test hook."""
37
37
  global _bootstrapped
38
- import core.seams.feature_loading as feature_loading
38
+ from core.seams import feature_loading
39
39
 
40
40
  _registrars.clear()
41
41
  _bootstrapped = False
@@ -46,7 +46,8 @@ class AlterGeneratorFactory:
46
46
  load_feature_extensions()
47
47
  attach_registered_sql_generators()
48
48
  ProviderRegistry.discover_plugins()
49
- for plugin_info in ProviderRegistry.list_plugins():
49
+ plugin_list = ProviderRegistry.list_plugins()
50
+ for plugin_info in plugin_list:
50
51
  # Per-plugin fault isolation: a broken plugin must not
51
52
  # prevent the others from registering. (PR #241 Bugbot.)
52
53
  try:
@@ -62,7 +63,14 @@ class AlterGeneratorFactory:
62
63
  plugin_info.name,
63
64
  exc,
64
65
  )
65
- cls._populated = True
66
+ # Latch only when plugin discovery itself found something. An empty
67
+ # ``plugin_list`` is the documented race (entry points not yet wired
68
+ # up in this process) and must be retried on a later call. A
69
+ # non-empty ``plugin_list`` means discovery genuinely completed --
70
+ # even if every plugin's quirks raised or returned no ALTER
71
+ # generator class, that is a real, stable outcome and should latch
72
+ # rather than re-running full discovery on every subsequent call.
73
+ cls._populated = bool(plugin_list)
66
74
 
67
75
  @classmethod
68
76
  def create_generator(cls, dialect: str) -> BaseAlterGenerator:
@@ -298,7 +298,7 @@ class View(SqlObject):
298
298
  Returns:
299
299
  Dictionary with view attributes
300
300
  """
301
- result = {
301
+ result: Dict[str, Any] = {
302
302
  "name": self.name,
303
303
  "schema": self.schema,
304
304
  "object_type": self.object_type.value,
@@ -435,9 +435,15 @@ class BaseQuirks:
435
435
  #: Oracle requires ``FROM DUAL``.
436
436
  connection_probe_sql: str = "SELECT 1"
437
437
  #: ``SELECT … LIMIT N`` syntax is supported. PostgreSQL, MySQL and
438
- #: SQLite accept ``LIMIT``; Oracle (``FETCH FIRST N ROWS ONLY``)
439
- #: and DB2 (``FETCH FIRST N ROWS ONLY``) do not; SQL Server uses
440
- #: ``TOP N`` instead. When False, post-commit verification queries
438
+ #: SQLite accept ``LIMIT``; Oracle (``FETCH FIRST N ROWS ONLY``) does
439
+ #: not; SQL Server uses ``TOP N`` instead. DB2's *preferred* rendering
440
+ #: is also ``FETCH FIRST N ROWS ONLY`` (see :attr:`row_limit_style`),
441
+ #: but DB2 additionally accepts a bare trailing ``LIMIT`` clause —
442
+ #: verified against a live db2 12.01.0500 server via the
443
+ #: capability-probe integration test
444
+ #: (``tests/integration/capabilities/test_engine_capabilities.py``) —
445
+ #: so it sets this True even though its declared ``row_limit_style``
446
+ #: is not ``"limit"``. When False, post-commit verification queries
441
447
  #: and similar probes omit the ``LIMIT`` clause.
442
448
  select_supports_limit: bool = True
443
449
  #: How a SELECT is bounded to N rows. One of:
@@ -461,12 +467,16 @@ class BaseQuirks:
461
467
  #: Read through :meth:`row_limit_clauses`, which renders the triple
462
468
  #: rather than making each caller re-derive the syntax. Distinct from
463
469
  #: :attr:`select_supports_limit`, which answers the coarser "may I
464
- #: append ``LIMIT`` at all" question for optional probes — but the two
465
- #: overlap in practice: every dialect that sets a non-default
466
- #: ``row_limit_style`` (SQL Server, Oracle, DB2) also sets
467
- #: ``select_supports_limit = False``, because none of them accept a bare
468
- #: trailing ``LIMIT`` either. Nothing in the framework enforces that the
469
- #: two stay consistent, so a new dialect could set one without the other;
470
+ #: append ``LIMIT`` at all" question for optional probes — the two
471
+ #: usually correlate (SQL Server and Oracle, which set a non-default
472
+ #: ``row_limit_style``, also set ``select_supports_limit = False``,
473
+ #: since neither accepts a bare trailing ``LIMIT``) but they are NOT
474
+ #: guaranteed to: DB2 is a documented exception — it declares
475
+ #: ``row_limit_style = "fetch_first"`` as its preferred rendering yet
476
+ #: sets ``select_supports_limit = True`` because the live engine also
477
+ #: accepts a bare trailing ``LIMIT`` (verified via the capability-probe
478
+ #: integration test). Nothing in the framework enforces that the two
479
+ #: stay consistent, so a new dialect could set one without the other;
470
480
  #: see the invariant test in ``tests/unit/db/test_dialect_capability_quirks.py``.
471
481
  row_limit_style: str = "limit"
472
482
  #: How an "insert or update on primary-key conflict" is expressed.
@@ -419,7 +419,7 @@ class CosmosDbHistoryManager(DocumentHistoryManager):
419
419
  # row: a point delete addressed by the wrong partition key returns 404,
420
420
  # which would read as "already deleted" and remove nothing.
421
421
  query = "SELECT c.id, c.script, c.success, c.version FROM c WHERE c.script = @script"
422
- parameters = [{"name": "@script", "value": script_name}]
422
+ parameters: List[Dict[str, object]] = [{"name": "@script", "value": script_name}]
423
423
  try:
424
424
  failed_docs = [
425
425
  doc
@@ -3,12 +3,15 @@ Cosmos DB query execution using the SQL API.
3
3
 
4
4
  The Cosmos DB SQL API is a read-only query language: this module runs
5
5
  ``SELECT`` against containers. Everything that changes state — containers,
6
- documents, throughput, indexing policy, TTL — is an Azure SDK call made
7
- from a Python migration, not SQL routed through a translator.
6
+ documents, throughput, indexing policy, TTL — is an Azure SDK call, not SQL
7
+ routed through a translator. That SDK call is made either from a
8
+ user-authored Python migration (via ``context.db`` / ``context.raw_client``)
9
+ or, for internal callers that are not a migration, through a native escape
10
+ hatch exposed here (e.g. ``upsert_native_item``).
8
11
  """
9
12
 
10
13
  import re
11
- from typing import Any, Dict, List, Optional
14
+ from typing import Any, Dict, List, Optional, cast
12
15
 
13
16
  from core.exceptions import NoSqlWriteNotSupportedError
14
17
  from core.logger import Log, NullLog
@@ -63,8 +66,11 @@ class CosmosDbQueryExecutor(BaseQueryExecutor):
63
66
  The Cosmos DB SQL API reads; it does not write. Containers and
64
67
  documents are changed through the Azure SDK, so a write statement
65
68
  reaching here is a mistake to surface rather than something to
66
- translate. Python migrations get to the SDK through
67
- ``context.db`` / ``context.raw_client``.
69
+ translate. User-authored Python migrations get to the SDK through
70
+ ``context.db`` / ``context.raw_client``; internal, non-migration
71
+ callers that need to write a single document use
72
+ :meth:`upsert_native_item` instead of building SQL for this method
73
+ to reject.
68
74
 
69
75
  Returns the number of rows a read produced (``0`` for the scalar
70
76
  liveness probe).
@@ -168,6 +174,51 @@ class CosmosDbQueryExecutor(BaseQueryExecutor):
168
174
  self.log.error(error_msg)
169
175
  raise
170
176
 
177
+ def upsert_native_item(self, container_name: str, document: Dict[str, Any]) -> Dict[str, Any]:
178
+ """Upsert a document directly through the Azure SDK, bypassing SQL.
179
+
180
+ ``execute_statement`` only accepts ``SELECT`` — the Cosmos DB SQL API
181
+ is read-only, and pretending otherwise (the pseudo-SQL emulator this
182
+ module used to have) was actively wrong, which is why the ADR
183
+ removing pseudo-SQL deleted it. Container *creation* was ported to a
184
+ native SDK call at the time (see
185
+ ``CosmosDbProvider.create_snapshot_table_if_not_exists``, which calls
186
+ ``CosmosDbSchemaOperations.create_container_if_not_exists`` instead of
187
+ rendering ``CREATE TABLE``), but the matching write for a single
188
+ document was missed: an internal caller such as the schema-snapshot
189
+ repository still built a plain SQL ``INSERT`` and routed it through
190
+ ``execute_statement``, which now raises
191
+ ``NoSqlWriteNotSupportedError`` for anything but a ``SELECT``. That
192
+ exception is swallowed by a best-effort event listener, so
193
+ ``migrate`` reports success while the snapshot document silently
194
+ never lands.
195
+
196
+ This method provides the native, non-SQL escape hatch a caller needs
197
+ to close that gap: it calls ``azure.cosmos.ContainerProxy.upsert_item``
198
+ directly instead of rendering anything this executor — or its
199
+ SELECT-only ``execute_statement`` — would have to parse as SQL. It is
200
+ meant for internal dblift callers that hold a provider/query-executor
201
+ reference and are not a user-authored migration; migrations already
202
+ reach the SDK through ``context.db`` / ``context.raw_client`` (see
203
+ ``execute_statement``'s docstring). This change adds the primitive
204
+ only — the schema-snapshot repository still needs to be updated, in
205
+ the sibling monorepo where it lives, to call this instead of
206
+ building an ``INSERT``; until that happens the gap above is not yet
207
+ closed end-to-end.
208
+
209
+ Args:
210
+ container_name: Name of the Cosmos DB container to upsert into.
211
+ document: The full document body to upsert. Must include
212
+ whatever field the container's partition key path points at.
213
+
214
+ Returns:
215
+ The document as returned by the SDK (typically the input body
216
+ plus Cosmos-assigned system properties such as ``_etag``,
217
+ ``_ts``, ``_self``).
218
+ """
219
+ container_client = self.connection_manager.get_container_client(container_name)
220
+ return cast(Dict[str, Any], container_client.upsert_item(body=document))
221
+
171
222
  def _normalize_cosmos_sql(self, sql: str, container_name: str) -> str:
172
223
  """Normalize Cosmos DB SQL query to use proper container alias.
173
224
 
@@ -75,6 +75,28 @@ class CosmosDbProvider(NativeProvider):
75
75
  connection = self._get_connection_or_raise()
76
76
  return self.query_executor.execute_query(connection, sql, params)
77
77
 
78
+ def upsert_native_item(self, container_name: str, document: Dict[str, Any]) -> Dict[str, Any]:
79
+ """Upsert a document straight through the Azure SDK, bypassing SQL.
80
+
81
+ Thin forward to :meth:`CosmosDbQueryExecutor.upsert_native_item` —
82
+ the same shape as :meth:`execute_statement` and :meth:`execute_query`
83
+ above, which forward to the query executor's SQL-shaped methods. This
84
+ one exists for callers (e.g. the schema-snapshot repository) that
85
+ need to write a single document and hold a provider reference rather
86
+ than a query-executor one; see
87
+ :meth:`CosmosDbQueryExecutor.upsert_native_item` for why a native
88
+ write path exists instead of routing an ``INSERT`` through
89
+ ``execute_statement``.
90
+
91
+ No connection guard here (unlike ``execute_statement`` /
92
+ ``execute_query``): the query executor reaches the container through
93
+ ``connection_manager.get_container_client`` rather than the
94
+ ``connection`` object, the same reason
95
+ ``create_snapshot_table_if_not_exists`` below calls into
96
+ ``schema_operations`` directly without fetching one.
97
+ """
98
+ return self.query_executor.upsert_native_item(container_name, document)
99
+
78
100
  def create_schema_if_not_exists(self, schema: str) -> None:
79
101
  """Create schema if it doesn't exist (Cosmos DB doesn't have schemas)."""
80
102
  connection = self._get_connection_or_raise()
@@ -72,8 +72,10 @@ class Db2Quirks(BaseQuirks):
72
72
  return self.is_data_history_table_already_exists_error(error_message)
73
73
 
74
74
  connection_probe_sql = "SELECT 1 FROM SYSIBM.SYSDUMMY1"
75
- select_supports_limit = False
76
- # Trailing ``FETCH FIRST n ROWS ONLY``; no ``LIMIT``.
75
+ select_supports_limit = True
76
+ # Canonical rendering is trailing ``FETCH FIRST n ROWS ONLY``, but DB2
77
+ # also accepts a bare trailing ``LIMIT n`` (verified against a live
78
+ # db2 12.01.0500 server via the capability-probe integration test).
77
79
  row_limit_style = "fetch_first"
78
80
  unquoted_identifier_case = "uppercase"
79
81
  connection_identifier_attrs = ("url", "host", "database")
@@ -5,7 +5,7 @@ so statement splitting only needs to respect string literals and comments
5
5
  around the ``;`` separator.
6
6
  """
7
7
 
8
- from typing import Any, Dict, List, Optional
8
+ from typing import Dict, List, Optional
9
9
 
10
10
  from core.sql_model.base import SqlObject, SqlObjectType
11
11
  from core.sql_parser.enhanced_regex_parser import EnhancedRegexParser
@@ -1,6 +1,6 @@
1
1
  """SQLAlchemy URL construction for the Redshift plugin."""
2
2
 
3
- from typing import Any, Dict, cast
3
+ from typing import Any, Dict
4
4
 
5
5
  from sqlalchemy.engine import URL, make_url
6
6
 
@@ -33,18 +33,15 @@ def build_sqlalchemy_url(database_config: Any) -> str:
33
33
  query = _query_mapping(database_config, url.query)
34
34
  if query != dict(url.query):
35
35
  url = url.set(query=query)
36
- return cast(str, url.render_as_string(hide_password=False))
36
+ return url.render_as_string(hide_password=False)
37
37
  raise ValueError("Redshift native connections require a SQLAlchemy URL")
38
38
 
39
- return cast(
40
- str,
41
- URL.create(
42
- "redshift+redshift_connector",
43
- username=getattr(database_config, "username", None) or None,
44
- password=getattr(database_config, "password", None) or None,
45
- host=getattr(database_config, "host", None) or "localhost",
46
- port=getattr(database_config, "port", None),
47
- database=getattr(database_config, "database", None),
48
- query=_query_mapping(database_config),
49
- ).render_as_string(hide_password=False),
50
- )
39
+ return URL.create(
40
+ "redshift+redshift_connector",
41
+ username=getattr(database_config, "username", None) or None,
42
+ password=getattr(database_config, "password", None) or None,
43
+ host=getattr(database_config, "host", None) or "localhost",
44
+ port=getattr(database_config, "port", None),
45
+ database=getattr(database_config, "database", None),
46
+ query=_query_mapping(database_config),
47
+ ).render_as_string(hide_password=False)
@@ -432,17 +432,6 @@ class SQLiteProvider(NativeProvider):
432
432
  connection, schema, create_schema, table_name
433
433
  )
434
434
 
435
- def supports_snapshots(self) -> bool:
436
- """SQLite does not own schema snapshot persistence."""
437
- return False
438
-
439
- def create_snapshot_table_if_not_exists(
440
- self, schema: str, table_name: str = "dblift_schema_snapshots"
441
- ) -> None:
442
- """Satisfy BaseProvider while leaving snapshot storage unsupported."""
443
- _ = (schema, table_name)
444
- raise NotImplementedError("SQLite provider does not support schema snapshot persistence")
445
-
446
435
  def is_connected(self) -> bool:
447
436
  """Return True when an active sqlite3 connection is held.
448
437
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dblift
3
- Version: 3.2.0
3
+ Version: 3.3.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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "dblift"
7
- version = "3.2.0"
7
+ version = "3.3.0"
8
8
  description = "A database migration tool that supports multiple databases with native Python drivers"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"