velocity-python 0.1.39__tar.gz → 0.1.40__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 (190) hide show
  1. {velocity_python-0.1.39/src/velocity_python.egg-info → velocity_python-0.1.40}/PKG-INFO +1 -1
  2. {velocity_python-0.1.39 → velocity_python-0.1.40}/pyproject.toml +1 -1
  3. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/__init__.py +1 -1
  4. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/core/table.py +488 -3
  5. {velocity_python-0.1.39 → velocity_python-0.1.40/src/velocity_python.egg-info}/PKG-INFO +1 -1
  6. velocity_python-0.1.40/tests/test_table_alter.py +546 -0
  7. velocity_python-0.1.39/tests/test_table_alter.py +0 -175
  8. {velocity_python-0.1.39 → velocity_python-0.1.40}/LICENSE +0 -0
  9. {velocity_python-0.1.39 → velocity_python-0.1.40}/README.md +0 -0
  10. {velocity_python-0.1.39 → velocity_python-0.1.40}/setup.cfg +0 -0
  11. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/aws/__init__.py +0 -0
  12. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/aws/amplify.py +0 -0
  13. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/aws/amplify_build.py +0 -0
  14. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/aws/assets/__init__.py +0 -0
  15. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/aws/assets/backfill.py +0 -0
  16. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/aws/assets/indexing.py +0 -0
  17. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/aws/assets/references.py +0 -0
  18. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/aws/assets/service.py +0 -0
  19. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/aws/assets/usage_index.py +0 -0
  20. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/aws/dirty_pipeline.py +0 -0
  21. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/aws/handlers/__init__.py +0 -0
  22. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/aws/handlers/base_handler.py +0 -0
  23. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/aws/handlers/context.py +0 -0
  24. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/aws/handlers/context_factory.py +0 -0
  25. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/aws/handlers/exceptions.py +0 -0
  26. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/aws/handlers/lambda_handler.py +0 -0
  27. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/aws/handlers/mixins/__init__.py +0 -0
  28. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/aws/handlers/mixins/data_service.py +0 -0
  29. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/aws/handlers/mixins/web_handler.py +0 -0
  30. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/aws/handlers/perf.py +0 -0
  31. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/aws/handlers/response.py +0 -0
  32. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/aws/handlers/sqs_handler.py +0 -0
  33. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/aws/s3.py +0 -0
  34. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/aws/ssm_config.py +0 -0
  35. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/aws/tests/__init__.py +0 -0
  36. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/aws/tests/test_base_handler_error_response.py +0 -0
  37. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/aws/tests/test_lambda_handler_json_serialization.py +0 -0
  38. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/aws/tests/test_response.py +0 -0
  39. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/__init__.py +0 -0
  40. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/core/__init__.py +0 -0
  41. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/core/async_support.py +0 -0
  42. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/core/column.py +0 -0
  43. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/core/database.py +0 -0
  44. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/core/decorators.py +0 -0
  45. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/core/engine.py +0 -0
  46. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/core/result.py +0 -0
  47. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/core/row.py +0 -0
  48. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/core/sequence.py +0 -0
  49. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/core/transaction.py +0 -0
  50. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/core/view.py +0 -0
  51. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/exceptions.py +0 -0
  52. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/migrations.py +0 -0
  53. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/servers/__init__.py +0 -0
  54. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/servers/base/__init__.py +0 -0
  55. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/servers/base/initializer.py +0 -0
  56. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/servers/base/operators.py +0 -0
  57. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/servers/base/sql.py +0 -0
  58. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/servers/base/types.py +0 -0
  59. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/servers/mysql/__init__.py +0 -0
  60. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/servers/mysql/operators.py +0 -0
  61. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/servers/mysql/reserved.py +0 -0
  62. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/servers/mysql/sql.py +0 -0
  63. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/servers/mysql/types.py +0 -0
  64. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/servers/postgres/__init__.py +0 -0
  65. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/servers/postgres/operators.py +0 -0
  66. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/servers/postgres/reserved.py +0 -0
  67. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/servers/postgres/sql.py +0 -0
  68. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/servers/postgres/types.py +0 -0
  69. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/servers/sqlite/__init__.py +0 -0
  70. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/servers/sqlite/operators.py +0 -0
  71. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/servers/sqlite/reserved.py +0 -0
  72. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/servers/sqlite/sql.py +0 -0
  73. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/servers/sqlite/types.py +0 -0
  74. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/servers/sqlserver/__init__.py +0 -0
  75. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/servers/sqlserver/operators.py +0 -0
  76. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/servers/sqlserver/reserved.py +0 -0
  77. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/servers/sqlserver/sql.py +0 -0
  78. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/servers/sqlserver/types.py +0 -0
  79. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/servers/tablehelper.py +0 -0
  80. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/__init__.py +0 -0
  81. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/common_db_test.py +0 -0
  82. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/postgres/__init__.py +0 -0
  83. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/postgres/common.py +0 -0
  84. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/postgres/test_column.py +0 -0
  85. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/postgres/test_connections.py +0 -0
  86. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/postgres/test_database.py +0 -0
  87. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/postgres/test_engine.py +0 -0
  88. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/postgres/test_general_usage.py +0 -0
  89. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/postgres/test_imports.py +0 -0
  90. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/postgres/test_result.py +0 -0
  91. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/postgres/test_row.py +0 -0
  92. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/postgres/test_row_comprehensive.py +0 -0
  93. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/postgres/test_schema_locking.py +0 -0
  94. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/postgres/test_schema_locking_unit.py +0 -0
  95. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/postgres/test_sequence.py +0 -0
  96. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/postgres/test_sql_comprehensive.py +0 -0
  97. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/postgres/test_table.py +0 -0
  98. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/postgres/test_table_comprehensive.py +0 -0
  99. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/postgres/test_transaction.py +0 -0
  100. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/sql/__init__.py +0 -0
  101. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/sql/common.py +0 -0
  102. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/sql/test_postgres_select_advanced.py +0 -0
  103. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/sql/test_postgres_select_variances.py +0 -0
  104. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/test_cursor_rowcount_fix.py +0 -0
  105. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/test_db_utils.py +0 -0
  106. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/test_postgres.py +0 -0
  107. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/test_postgres_unchanged.py +0 -0
  108. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/test_process_error_robustness.py +0 -0
  109. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/test_result_caching.py +0 -0
  110. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/test_result_sql_aware.py +0 -0
  111. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/test_row_get_missing_column.py +0 -0
  112. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/test_schema_locking_initializers.py +0 -0
  113. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/test_schema_locking_simple.py +0 -0
  114. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/test_sql_builder.py +0 -0
  115. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/test_tablehelper.py +0 -0
  116. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/tests/test_view_helper.py +0 -0
  117. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/db/utils.py +0 -0
  118. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/logging.py +0 -0
  119. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/misc/__init__.py +0 -0
  120. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/misc/conv/__init__.py +0 -0
  121. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/misc/conv/iconv.py +0 -0
  122. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/misc/conv/oconv.py +0 -0
  123. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/misc/db.py +0 -0
  124. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/misc/export.py +0 -0
  125. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/misc/format.py +0 -0
  126. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/misc/mail.py +0 -0
  127. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/misc/merge.py +0 -0
  128. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/misc/pdf.py +0 -0
  129. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/misc/tests/__init__.py +0 -0
  130. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/misc/tests/test_db.py +0 -0
  131. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/misc/tests/test_fix.py +0 -0
  132. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/misc/tests/test_format.py +0 -0
  133. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/misc/tests/test_iconv.py +0 -0
  134. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/misc/tests/test_merge.py +0 -0
  135. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/misc/tests/test_oconv.py +0 -0
  136. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/misc/tests/test_original_error.py +0 -0
  137. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/misc/tests/test_timer.py +0 -0
  138. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/misc/timer.py +0 -0
  139. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/misc/tools.py +0 -0
  140. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/payment/__init__.py +0 -0
  141. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/payment/authorizenet_adapter.py +0 -0
  142. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/payment/authorizenet_mirror.py +0 -0
  143. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/payment/base_adapter.py +0 -0
  144. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/payment/braintree_adapter.py +0 -0
  145. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/payment/braintree_mirror.py +0 -0
  146. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/payment/charge_rules.py +0 -0
  147. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/payment/stripe_adapter.py +0 -0
  148. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity/payment/stripe_mirror.py +0 -0
  149. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity_python.egg-info/SOURCES.txt +0 -0
  150. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity_python.egg-info/dependency_links.txt +0 -0
  151. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity_python.egg-info/entry_points.txt +0 -0
  152. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity_python.egg-info/requires.txt +0 -0
  153. {velocity_python-0.1.39 → velocity_python-0.1.40}/src/velocity_python.egg-info/top_level.txt +0 -0
  154. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_amplify_build.py +0 -0
  155. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_asset_indexing.py +0 -0
  156. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_asset_references.py +0 -0
  157. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_assets_service.py +0 -0
  158. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_async_support.py +0 -0
  159. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_batch_operations.py +0 -0
  160. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_concurrency_safety.py +0 -0
  161. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_connection_pool.py +0 -0
  162. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_connection_resilience.py +0 -0
  163. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_decorators.py +0 -0
  164. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_dirty_pipeline_fast_path.py +0 -0
  165. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_email_processing.py +0 -0
  166. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_iconv_money_to_cents.py +0 -0
  167. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_lambda_handler.py +0 -0
  168. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_lambda_handler_auth.py +0 -0
  169. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_mixins_import.py +0 -0
  170. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_n_plus_one.py +0 -0
  171. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_observability.py +0 -0
  172. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_payment_authorizenet_adapter.py +0 -0
  173. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_payment_braintree_adapter.py +0 -0
  174. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_payment_braintree_mirror.py +0 -0
  175. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_payment_profile_sorting.py +0 -0
  176. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_payment_stripe_adapter.py +0 -0
  177. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_pdf.py +0 -0
  178. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_prepared_statements.py +0 -0
  179. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_psycopg3_upgrade.py +0 -0
  180. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_query_cache.py +0 -0
  181. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_row_batch_update.py +0 -0
  182. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_row_cache_staleness.py +0 -0
  183. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_row_dirty_tracking.py +0 -0
  184. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_schema_migrations.py +0 -0
  185. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_security_hardening.py +0 -0
  186. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_server_cursor.py +0 -0
  187. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_spreadsheet_functions.py +0 -0
  188. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_sqs_per_record_transactions.py +0 -0
  189. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_sys_modified_count_postgres_demo.py +0 -0
  190. {velocity_python-0.1.39 → velocity_python-0.1.40}/tests/test_where_clause_validation.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: velocity-python
3
- Version: 0.1.39
3
+ Version: 0.1.40
4
4
  Summary: A rapid application development library for interfacing with data storage
5
5
  Author-email: Velocity Team <info@codeclubs.org>
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "velocity-python"
7
- version = "0.1.39"
7
+ version = "0.1.40"
8
8
  authors = [
9
9
  { name="Velocity Team", email="info@codeclubs.org" },
10
10
  ]
@@ -1,4 +1,4 @@
1
- __version__ = version = "0.1.39"
1
+ __version__ = version = "0.1.40"
2
2
 
3
3
  import importlib as _importlib
4
4
 
@@ -358,6 +358,99 @@ def _normalize_foreign_key_specs(foreign_keys):
358
358
  return normalized
359
359
 
360
360
 
361
+ def _normalize_drop_index_specs(indexes):
362
+ if indexes is None:
363
+ return []
364
+
365
+ if isinstance(indexes, (str, bytes, Mapping)):
366
+ indexes = [indexes]
367
+
368
+ normalized = []
369
+ for definition in indexes:
370
+ if isinstance(definition, Mapping):
371
+ name = definition.get("name")
372
+ columns = definition.get("columns")
373
+ else:
374
+ name = definition
375
+ columns = None
376
+
377
+ if isinstance(columns, str):
378
+ columns = [column.strip() for column in columns.split(",") if column.strip()]
379
+ elif isinstance(columns, Sequence):
380
+ columns = list(columns)
381
+ elif columns is not None:
382
+ columns = [columns]
383
+
384
+ if not name and not columns:
385
+ raise ValueError("drop_indexes entries require a name or columns")
386
+
387
+ normalized.append(
388
+ {
389
+ "name": _normalize_identifier(name) if name else None,
390
+ "columns": tuple(_normalize_identifier(column) for column in columns)
391
+ if columns
392
+ else None,
393
+ }
394
+ )
395
+
396
+ return normalized
397
+
398
+
399
+ def _normalize_drop_foreign_key_specs(foreign_keys):
400
+ if foreign_keys is None:
401
+ return []
402
+
403
+ if isinstance(foreign_keys, (str, bytes, Mapping)):
404
+ foreign_keys = [foreign_keys]
405
+
406
+ normalized = []
407
+ for definition in foreign_keys:
408
+ if isinstance(definition, Mapping):
409
+ name = definition.get("name")
410
+ columns = definition.get("columns") or definition.get("column")
411
+ ref_table = definition.get("ref_table") or definition.get("key_to_table")
412
+ ref_columns = definition.get("ref_columns") or definition.get("key_to_columns")
413
+ else:
414
+ name = definition
415
+ columns = None
416
+ ref_table = None
417
+ ref_columns = None
418
+
419
+ if isinstance(columns, str):
420
+ columns = [column.strip() for column in columns.split(",") if column.strip()]
421
+ elif isinstance(columns, Sequence):
422
+ columns = list(columns)
423
+ elif columns is not None:
424
+ columns = [columns]
425
+
426
+ if isinstance(ref_columns, str):
427
+ ref_columns = [column.strip() for column in ref_columns.split(",") if column.strip()]
428
+ elif isinstance(ref_columns, Sequence):
429
+ ref_columns = list(ref_columns)
430
+ elif ref_columns is not None:
431
+ ref_columns = [ref_columns]
432
+
433
+ if not name and not columns:
434
+ raise ValueError("drop_foreign_keys entries require a name or columns")
435
+
436
+ normalized.append(
437
+ {
438
+ "name": _normalize_identifier(name) if name else None,
439
+ "columns": tuple(_normalize_identifier(column) for column in columns)
440
+ if columns
441
+ else None,
442
+ "ref_table": _normalize_identifier(ref_table) if ref_table else None,
443
+ "ref_columns": tuple(
444
+ _normalize_identifier(column) for column in ref_columns
445
+ )
446
+ if ref_columns
447
+ else None,
448
+ }
449
+ )
450
+
451
+ return normalized
452
+
453
+
361
454
  class Table:
362
455
  SYSTEM_COLUMNS = SYSTEM_COLUMN_NAMES
363
456
 
@@ -532,14 +625,16 @@ class Table:
532
625
  return statements if sql_only else None
533
626
 
534
627
  @return_default(None)
535
- def drop_index(self, columns, **kwds):
628
+ def drop_index(self, columns=None, name=None, **kwds):
536
629
  """
537
630
  Drops an index for the specified columns.
538
631
  """
539
- sql, vals = self.sql.drop_index(self.name, columns)
632
+ sql, vals = self.sql.drop_index(self.name, columns, name=name)
540
633
  if kwds.get("sql_only", False):
541
634
  return sql, vals
542
- _ddl_logger.warning("DDL DROP INDEX on %s columns=%s", self.name, columns)
635
+ _ddl_logger.warning(
636
+ "DDL DROP INDEX on %s columns=%s name=%s", self.name, columns, name
637
+ )
543
638
  self.tx.execute(sql, vals, cursor=self.cursor())
544
639
 
545
640
  @return_default(None)
@@ -887,6 +982,396 @@ class Table:
887
982
 
888
983
  return summary
889
984
 
985
+ def migrate_schema(
986
+ self,
987
+ columns=None,
988
+ rename_columns=None,
989
+ drop_columns=None,
990
+ unique_indexes=None,
991
+ indexes=None,
992
+ foreign_keys=None,
993
+ drop_indexes=None,
994
+ drop_foreign_keys=None,
995
+ replace_indexes=False,
996
+ replace_foreign_keys=False,
997
+ create_missing=True,
998
+ ensure_system_columns=False,
999
+ ):
1000
+ """Apply explicit schema migrations needed to evolve this table.
1001
+
1002
+ Unlike ``ensure_schema()``, this helper may rename columns, alter
1003
+ existing column definitions, drop obsolete columns, and explicitly
1004
+ replace indexes or foreign keys. It is intended for deliberate
1005
+ deploy-time migration code rather than passive schema alignment.
1006
+
1007
+ Operations run in this order:
1008
+
1009
+ 1. Create the table when missing and ``create_missing=True``.
1010
+ 2. Rename columns.
1011
+ 3. Drop explicit foreign keys and indexes.
1012
+ 4. Add or alter columns using :meth:`alter` in ``smart`` mode.
1013
+ 5. Ensure system columns when requested.
1014
+ 6. Create or replace declared indexes and foreign keys.
1015
+ 7. Drop columns.
1016
+ """
1017
+
1018
+ if columns is not None and not isinstance(columns, Mapping):
1019
+ raise TypeError("columns must be a mapping when provided.")
1020
+ if rename_columns is not None and not isinstance(rename_columns, Mapping):
1021
+ raise TypeError("rename_columns must be a mapping when provided.")
1022
+
1023
+ normalized_columns = self.lower_keys(columns or {})
1024
+ normalized_renames = {}
1025
+ for orig, new in (rename_columns or {}).items():
1026
+ orig_name = _normalize_identifier(orig)
1027
+ new_name = _normalize_identifier(new)
1028
+ if not orig_name or not new_name:
1029
+ raise ValueError("rename_columns entries require non-empty source and target names")
1030
+ normalized_renames[orig_name] = new_name
1031
+
1032
+ if drop_columns is None:
1033
+ normalized_drops = []
1034
+ elif isinstance(drop_columns, str):
1035
+ normalized_drops = [
1036
+ _normalize_identifier(column)
1037
+ for column in drop_columns.split(",")
1038
+ if _normalize_identifier(column)
1039
+ ]
1040
+ elif isinstance(drop_columns, Sequence):
1041
+ normalized_drops = [
1042
+ _normalize_identifier(column)
1043
+ for column in drop_columns
1044
+ if _normalize_identifier(column)
1045
+ ]
1046
+ else:
1047
+ normalized_drops = [_normalize_identifier(drop_columns)]
1048
+
1049
+ conflicting_columns = set(normalized_columns).intersection(normalized_drops)
1050
+ if conflicting_columns:
1051
+ raise ValueError(
1052
+ "columns and drop_columns cannot reference the same column(s): "
1053
+ + ", ".join(sorted(conflicting_columns))
1054
+ )
1055
+
1056
+ normalized_indexes = _normalize_index_specs(indexes)
1057
+ normalized_unique_indexes = _normalize_index_specs(
1058
+ unique_indexes, unique_default=True
1059
+ )
1060
+ normalized_foreign_keys = _normalize_foreign_key_specs(foreign_keys)
1061
+ normalized_drop_indexes = _normalize_drop_index_specs(drop_indexes)
1062
+ normalized_drop_foreign_keys = _normalize_drop_foreign_key_specs(
1063
+ drop_foreign_keys
1064
+ )
1065
+
1066
+ summary = {
1067
+ "created_table": False,
1068
+ "renamed_columns": [],
1069
+ "altered_columns": [],
1070
+ "dropped_columns": [],
1071
+ "created_indexes": [],
1072
+ "dropped_indexes": [],
1073
+ "created_foreign_keys": [],
1074
+ "dropped_foreign_keys": [],
1075
+ "ensured_system_columns": False,
1076
+ }
1077
+
1078
+ table_exists = self.exists()
1079
+ if not table_exists:
1080
+ if not create_missing:
1081
+ raise exceptions.DbTableMissingError(
1082
+ f"Table '{self.name}' does not exist and create_missing=False."
1083
+ )
1084
+ self._ensure_schema_unlocked("create missing table")
1085
+ self.create(normalized_columns)
1086
+ summary["created_table"] = True
1087
+ table_exists = True
1088
+
1089
+ existing_columns = {column.lower() for column in self.sys_columns()}
1090
+
1091
+ if not summary["created_table"]:
1092
+ for orig_name, new_name in normalized_renames.items():
1093
+ if orig_name == new_name:
1094
+ continue
1095
+
1096
+ orig_exists = orig_name in existing_columns
1097
+ new_exists = new_name in existing_columns
1098
+
1099
+ if orig_exists and new_exists:
1100
+ raise exceptions.DbSchemaConflictError(
1101
+ f"Cannot rename column '{orig_name}' to '{new_name}' on table '{self.name}': "
1102
+ "both columns already exist."
1103
+ )
1104
+ if not orig_exists and new_exists:
1105
+ continue
1106
+ if not orig_exists:
1107
+ raise exceptions.DbSchemaConflictError(
1108
+ f"Cannot rename column '{orig_name}' to '{new_name}' on table '{self.name}': "
1109
+ f"column '{orig_name}' does not exist."
1110
+ )
1111
+
1112
+ self._ensure_schema_unlocked("rename columns")
1113
+ self.rename_column(orig_name, new_name)
1114
+ summary["renamed_columns"].append(
1115
+ {"from": orig_name, "to": new_name}
1116
+ )
1117
+ existing_columns.remove(orig_name)
1118
+ existing_columns.add(new_name)
1119
+
1120
+ existing_foreign_keys = self._existing_foreign_key_signatures()
1121
+ for spec in normalized_drop_foreign_keys:
1122
+ match = next(
1123
+ (
1124
+ existing
1125
+ for existing in existing_foreign_keys
1126
+ if (
1127
+ spec["name"]
1128
+ and existing["constraint_name"] == spec["name"]
1129
+ )
1130
+ or (
1131
+ spec["columns"]
1132
+ and existing["columns"] == spec["columns"]
1133
+ and (
1134
+ spec["ref_table"] is None
1135
+ or existing["ref_table"] == spec["ref_table"]
1136
+ )
1137
+ and (
1138
+ spec["ref_columns"] is None
1139
+ or existing["ref_columns"] == spec["ref_columns"]
1140
+ )
1141
+ )
1142
+ ),
1143
+ None,
1144
+ )
1145
+ if not match:
1146
+ continue
1147
+
1148
+ self._ensure_schema_unlocked("drop foreign keys")
1149
+ self.drop_foreign_key(
1150
+ list(match["columns"]),
1151
+ match["ref_table"],
1152
+ list(match["ref_columns"]),
1153
+ name=match["constraint_name"],
1154
+ )
1155
+ summary["dropped_foreign_keys"].append(
1156
+ {
1157
+ "name": match["constraint_name"],
1158
+ "columns": list(match["columns"]),
1159
+ }
1160
+ )
1161
+ existing_foreign_keys = self._existing_foreign_key_signatures()
1162
+
1163
+ existing_indexes = self._existing_index_signatures()
1164
+ for spec in normalized_drop_indexes:
1165
+ match = next(
1166
+ (
1167
+ existing
1168
+ for existing in existing_indexes
1169
+ if (
1170
+ spec["name"] and existing["name"] == spec["name"]
1171
+ )
1172
+ or (
1173
+ spec["columns"]
1174
+ and existing["columns"] == spec["columns"]
1175
+ )
1176
+ ),
1177
+ None,
1178
+ )
1179
+ if not match:
1180
+ continue
1181
+
1182
+ self._ensure_schema_unlocked("drop indexes")
1183
+ self.drop_index(list(match["columns"]), name=match["name"])
1184
+ summary["dropped_indexes"].append(
1185
+ {
1186
+ "name": match["name"],
1187
+ "columns": list(match["columns"]),
1188
+ }
1189
+ )
1190
+ existing_indexes = self._existing_index_signatures()
1191
+
1192
+ if normalized_columns and not summary["created_table"]:
1193
+ planned = self.alter(normalized_columns, sql_only=True)
1194
+ if planned:
1195
+ self._ensure_schema_unlocked("alter columns")
1196
+ self.alter(normalized_columns)
1197
+ summary["altered_columns"] = list(normalized_columns.keys())
1198
+ existing_columns.update(normalized_columns.keys())
1199
+
1200
+ if ensure_system_columns:
1201
+ required_system_columns = {name.lower() for name in self.SYSTEM_COLUMNS}
1202
+ if not required_system_columns.issubset(existing_columns):
1203
+ self._ensure_schema_unlocked("ensure system columns")
1204
+ self.ensure_system_columns()
1205
+ summary["ensured_system_columns"] = True
1206
+ existing_columns.update(required_system_columns)
1207
+
1208
+ existing_indexes = self._existing_index_signatures()
1209
+ for spec in normalized_unique_indexes + normalized_indexes:
1210
+ desired_signature = self._desired_index_signature(spec)
1211
+ exact_match = next(
1212
+ (
1213
+ existing
1214
+ for existing in existing_indexes
1215
+ if existing["unique"] == desired_signature["unique"]
1216
+ and existing["columns"] == desired_signature["columns"]
1217
+ and existing["where"] == desired_signature["where"]
1218
+ ),
1219
+ None,
1220
+ )
1221
+ if exact_match and (
1222
+ not spec["name"] or exact_match["name"] == desired_signature["name"]
1223
+ ):
1224
+ continue
1225
+
1226
+ conflict = None
1227
+ if exact_match and spec["name"]:
1228
+ conflict = exact_match
1229
+ if conflict is None and spec["name"]:
1230
+ conflict = next(
1231
+ (
1232
+ existing
1233
+ for existing in existing_indexes
1234
+ if existing["name"] == desired_signature["name"]
1235
+ ),
1236
+ None,
1237
+ )
1238
+ if conflict is None:
1239
+ conflict = next(
1240
+ (
1241
+ existing
1242
+ for existing in existing_indexes
1243
+ if existing["columns"] == desired_signature["columns"]
1244
+ and existing["where"] == desired_signature["where"]
1245
+ ),
1246
+ None,
1247
+ )
1248
+ if conflict is not None:
1249
+ if not replace_indexes:
1250
+ raise exceptions.DbSchemaConflictError(
1251
+ f"Index migration on table '{self.name}' conflicts with existing index '{conflict['name']}'. "
1252
+ "Pass replace_indexes=True to replace it explicitly."
1253
+ )
1254
+ self._ensure_schema_unlocked("replace indexes")
1255
+ self.drop_index(list(conflict["columns"]), name=conflict["name"])
1256
+ summary["dropped_indexes"].append(
1257
+ {
1258
+ "name": conflict["name"],
1259
+ "columns": list(conflict["columns"]),
1260
+ }
1261
+ )
1262
+ existing_indexes = self._existing_index_signatures()
1263
+
1264
+ self._ensure_schema_unlocked("create indexes")
1265
+ self.create_index(
1266
+ spec["columns"],
1267
+ unique=spec["unique"],
1268
+ direction=spec["direction"],
1269
+ where=spec["where"],
1270
+ lower=spec["lower"],
1271
+ name=spec["name"],
1272
+ )
1273
+ summary["created_indexes"].append(
1274
+ {
1275
+ "name": spec["name"] or desired_signature["name"],
1276
+ "columns": list(spec["columns"]),
1277
+ "unique": spec["unique"],
1278
+ }
1279
+ )
1280
+ existing_indexes = self._existing_index_signatures()
1281
+
1282
+ existing_foreign_keys = self._existing_foreign_key_signatures()
1283
+ for spec in normalized_foreign_keys:
1284
+ exact_match = next(
1285
+ (
1286
+ existing
1287
+ for existing in existing_foreign_keys
1288
+ if existing["columns"] == spec["columns"]
1289
+ and existing["ref_table"] == spec["ref_table"]
1290
+ and existing["ref_columns"] == spec["ref_columns"]
1291
+ and existing["on_delete"] == spec["on_delete"]
1292
+ and existing["on_update"] == spec["on_update"]
1293
+ ),
1294
+ None,
1295
+ )
1296
+ if exact_match and (
1297
+ not spec["name"] or exact_match["constraint_name"] == spec["name"]
1298
+ ):
1299
+ continue
1300
+
1301
+ conflict = None
1302
+ if exact_match and spec["name"]:
1303
+ conflict = exact_match
1304
+ if conflict is None and spec["name"]:
1305
+ conflict = next(
1306
+ (
1307
+ existing
1308
+ for existing in existing_foreign_keys
1309
+ if existing["constraint_name"] == spec["name"]
1310
+ ),
1311
+ None,
1312
+ )
1313
+ if conflict is None:
1314
+ conflict = next(
1315
+ (
1316
+ existing
1317
+ for existing in existing_foreign_keys
1318
+ if existing["columns"] == spec["columns"]
1319
+ ),
1320
+ None,
1321
+ )
1322
+ if conflict is not None:
1323
+ if not replace_foreign_keys:
1324
+ raise exceptions.DbSchemaConflictError(
1325
+ f"Foreign key migration on table '{self.name}' conflicts with existing constraint '{conflict['constraint_name']}'. "
1326
+ "Pass replace_foreign_keys=True to replace it explicitly."
1327
+ )
1328
+ self._ensure_schema_unlocked("replace foreign keys")
1329
+ self.drop_foreign_key(
1330
+ list(conflict["columns"]),
1331
+ conflict["ref_table"],
1332
+ list(conflict["ref_columns"]),
1333
+ name=conflict["constraint_name"],
1334
+ )
1335
+ summary["dropped_foreign_keys"].append(
1336
+ {
1337
+ "name": conflict["constraint_name"],
1338
+ "columns": list(conflict["columns"]),
1339
+ }
1340
+ )
1341
+ existing_foreign_keys = self._existing_foreign_key_signatures()
1342
+
1343
+ self._ensure_schema_unlocked("create foreign keys")
1344
+ self.create_foreign_key(
1345
+ list(spec["columns"]),
1346
+ spec["ref_table"],
1347
+ list(spec["ref_columns"]),
1348
+ name=spec["name"],
1349
+ on_delete=spec["on_delete"],
1350
+ on_update=spec["on_update"],
1351
+ )
1352
+ summary["created_foreign_keys"].append(
1353
+ {
1354
+ "name": spec["name"],
1355
+ "columns": list(spec["columns"]),
1356
+ "ref_table": spec["ref_table"],
1357
+ "ref_columns": list(spec["ref_columns"]),
1358
+ "on_delete": spec["on_delete"],
1359
+ "on_update": spec["on_update"],
1360
+ }
1361
+ )
1362
+ existing_foreign_keys = self._existing_foreign_key_signatures()
1363
+
1364
+ if not summary["created_table"]:
1365
+ for column_name in normalized_drops:
1366
+ if column_name not in existing_columns:
1367
+ continue
1368
+ self._ensure_schema_unlocked("drop columns")
1369
+ self.drop_column(column_name)
1370
+ summary["dropped_columns"].append(column_name)
1371
+ existing_columns.remove(column_name)
1372
+
1373
+ return summary
1374
+
890
1375
  def drop(self):
891
1376
  """
892
1377
  Drops this table if it exists.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: velocity-python
3
- Version: 0.1.39
3
+ Version: 0.1.40
4
4
  Summary: A rapid application development library for interfacing with data storage
5
5
  Author-email: Velocity Team <info@codeclubs.org>
6
6
  License-Expression: MIT