velocity-python 0.1.38__tar.gz → 0.1.39__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.
- {velocity_python-0.1.38/src/velocity_python.egg-info → velocity_python-0.1.39}/PKG-INFO +1 -1
- {velocity_python-0.1.38 → velocity_python-0.1.39}/pyproject.toml +1 -1
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/__init__.py +1 -1
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/core/table.py +104 -7
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/servers/base/sql.py +4 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/servers/mysql/sql.py +17 -2
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/servers/postgres/sql.py +32 -10
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/servers/sqlite/sql.py +11 -1
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/servers/sqlserver/sql.py +17 -2
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/postgres/test_schema_locking.py +65 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39/src/velocity_python.egg-info}/PKG-INFO +1 -1
- {velocity_python-0.1.38 → velocity_python-0.1.39}/LICENSE +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/README.md +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/setup.cfg +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/aws/__init__.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/aws/amplify.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/aws/amplify_build.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/aws/assets/__init__.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/aws/assets/backfill.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/aws/assets/indexing.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/aws/assets/references.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/aws/assets/service.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/aws/assets/usage_index.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/aws/dirty_pipeline.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/aws/handlers/__init__.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/aws/handlers/base_handler.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/aws/handlers/context.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/aws/handlers/context_factory.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/aws/handlers/exceptions.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/aws/handlers/lambda_handler.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/aws/handlers/mixins/__init__.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/aws/handlers/mixins/data_service.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/aws/handlers/mixins/web_handler.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/aws/handlers/perf.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/aws/handlers/response.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/aws/handlers/sqs_handler.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/aws/s3.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/aws/ssm_config.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/aws/tests/__init__.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/aws/tests/test_base_handler_error_response.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/aws/tests/test_lambda_handler_json_serialization.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/aws/tests/test_response.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/__init__.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/core/__init__.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/core/async_support.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/core/column.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/core/database.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/core/decorators.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/core/engine.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/core/result.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/core/row.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/core/sequence.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/core/transaction.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/core/view.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/exceptions.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/migrations.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/servers/__init__.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/servers/base/__init__.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/servers/base/initializer.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/servers/base/operators.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/servers/base/types.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/servers/mysql/__init__.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/servers/mysql/operators.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/servers/mysql/reserved.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/servers/mysql/types.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/servers/postgres/__init__.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/servers/postgres/operators.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/servers/postgres/reserved.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/servers/postgres/types.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/servers/sqlite/__init__.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/servers/sqlite/operators.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/servers/sqlite/reserved.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/servers/sqlite/types.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/servers/sqlserver/__init__.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/servers/sqlserver/operators.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/servers/sqlserver/reserved.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/servers/sqlserver/types.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/servers/tablehelper.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/__init__.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/common_db_test.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/postgres/__init__.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/postgres/common.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/postgres/test_column.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/postgres/test_connections.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/postgres/test_database.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/postgres/test_engine.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/postgres/test_general_usage.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/postgres/test_imports.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/postgres/test_result.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/postgres/test_row.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/postgres/test_row_comprehensive.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/postgres/test_schema_locking_unit.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/postgres/test_sequence.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/postgres/test_sql_comprehensive.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/postgres/test_table.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/postgres/test_table_comprehensive.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/postgres/test_transaction.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/sql/__init__.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/sql/common.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/sql/test_postgres_select_advanced.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/sql/test_postgres_select_variances.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/test_cursor_rowcount_fix.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/test_db_utils.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/test_postgres.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/test_postgres_unchanged.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/test_process_error_robustness.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/test_result_caching.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/test_result_sql_aware.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/test_row_get_missing_column.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/test_schema_locking_initializers.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/test_schema_locking_simple.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/test_sql_builder.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/test_tablehelper.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/tests/test_view_helper.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/db/utils.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/logging.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/misc/__init__.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/misc/conv/__init__.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/misc/conv/iconv.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/misc/conv/oconv.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/misc/db.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/misc/export.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/misc/format.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/misc/mail.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/misc/merge.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/misc/pdf.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/misc/tests/__init__.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/misc/tests/test_db.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/misc/tests/test_fix.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/misc/tests/test_format.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/misc/tests/test_iconv.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/misc/tests/test_merge.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/misc/tests/test_oconv.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/misc/tests/test_original_error.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/misc/tests/test_timer.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/misc/timer.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/misc/tools.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/payment/__init__.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/payment/authorizenet_adapter.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/payment/authorizenet_mirror.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/payment/base_adapter.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/payment/braintree_adapter.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/payment/braintree_mirror.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/payment/charge_rules.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/payment/stripe_adapter.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/payment/stripe_mirror.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity_python.egg-info/SOURCES.txt +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity_python.egg-info/dependency_links.txt +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity_python.egg-info/entry_points.txt +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity_python.egg-info/requires.txt +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity_python.egg-info/top_level.txt +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_amplify_build.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_asset_indexing.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_asset_references.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_assets_service.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_async_support.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_batch_operations.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_concurrency_safety.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_connection_pool.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_connection_resilience.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_decorators.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_dirty_pipeline_fast_path.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_email_processing.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_iconv_money_to_cents.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_lambda_handler.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_lambda_handler_auth.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_mixins_import.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_n_plus_one.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_observability.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_payment_authorizenet_adapter.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_payment_braintree_adapter.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_payment_braintree_mirror.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_payment_profile_sorting.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_payment_stripe_adapter.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_pdf.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_prepared_statements.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_psycopg3_upgrade.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_query_cache.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_row_batch_update.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_row_cache_staleness.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_row_dirty_tracking.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_schema_migrations.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_security_hardening.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_server_cursor.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_spreadsheet_functions.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_sqs_per_record_transactions.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_sys_modified_count_postgres_demo.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_table_alter.py +0 -0
- {velocity_python-0.1.38 → velocity_python-0.1.39}/tests/test_where_clause_validation.py +0 -0
|
@@ -155,6 +155,12 @@ def _normalize_identifier(value):
|
|
|
155
155
|
return str(value or "").strip().strip('"').lower()
|
|
156
156
|
|
|
157
157
|
|
|
158
|
+
def _normalize_referential_action(value):
|
|
159
|
+
if not value:
|
|
160
|
+
return "NO ACTION"
|
|
161
|
+
return " ".join(str(value).strip().upper().split())
|
|
162
|
+
|
|
163
|
+
|
|
158
164
|
def _split_sql_expressions(text):
|
|
159
165
|
expressions = []
|
|
160
166
|
current = []
|
|
@@ -267,12 +273,14 @@ def _normalize_index_specs(indexes, *, unique_default=False):
|
|
|
267
273
|
direction = definition.get("direction")
|
|
268
274
|
where = definition.get("where")
|
|
269
275
|
lower = definition.get("lower")
|
|
276
|
+
name = definition.get("name")
|
|
270
277
|
else:
|
|
271
278
|
columns = definition
|
|
272
279
|
unique = unique_default
|
|
273
280
|
direction = None
|
|
274
281
|
where = None
|
|
275
282
|
lower = None
|
|
283
|
+
name = None
|
|
276
284
|
|
|
277
285
|
if isinstance(columns, str):
|
|
278
286
|
columns = [column.strip() for column in columns.split(",") if column.strip()]
|
|
@@ -288,6 +296,7 @@ def _normalize_index_specs(indexes, *, unique_default=False):
|
|
|
288
296
|
"direction": direction,
|
|
289
297
|
"where": where,
|
|
290
298
|
"lower": lower,
|
|
299
|
+
"name": _normalize_identifier(name) if name else None,
|
|
291
300
|
}
|
|
292
301
|
)
|
|
293
302
|
|
|
@@ -315,6 +324,9 @@ def _normalize_foreign_key_specs(foreign_keys):
|
|
|
315
324
|
raise ValueError("Foreign key definition requires ref_table")
|
|
316
325
|
|
|
317
326
|
ref_columns = definition.get("ref_columns") or definition.get("key_to_columns") or "sys_id"
|
|
327
|
+
name = definition.get("name")
|
|
328
|
+
on_delete = _normalize_referential_action(definition.get("on_delete"))
|
|
329
|
+
on_update = _normalize_referential_action(definition.get("on_update"))
|
|
318
330
|
|
|
319
331
|
if isinstance(columns, str):
|
|
320
332
|
columns = [column.strip() for column in columns.split(",") if column.strip()]
|
|
@@ -337,6 +349,9 @@ def _normalize_foreign_key_specs(foreign_keys):
|
|
|
337
349
|
"ref_columns": tuple(
|
|
338
350
|
_normalize_identifier(column) for column in ref_columns
|
|
339
351
|
),
|
|
352
|
+
"name": _normalize_identifier(name) if name else None,
|
|
353
|
+
"on_delete": on_delete,
|
|
354
|
+
"on_update": on_update,
|
|
340
355
|
}
|
|
341
356
|
)
|
|
342
357
|
|
|
@@ -430,7 +445,14 @@ class Table:
|
|
|
430
445
|
|
|
431
446
|
@return_default(None, (exceptions.DbObjectExistsError,))
|
|
432
447
|
def create_index(
|
|
433
|
-
self,
|
|
448
|
+
self,
|
|
449
|
+
columns,
|
|
450
|
+
unique=False,
|
|
451
|
+
direction=None,
|
|
452
|
+
where=None,
|
|
453
|
+
lower=None,
|
|
454
|
+
name=None,
|
|
455
|
+
**kwds,
|
|
434
456
|
):
|
|
435
457
|
"""
|
|
436
458
|
Creates an index on the given columns. Returns None on success, or `None` if the index already exists.
|
|
@@ -445,6 +467,7 @@ class Table:
|
|
|
445
467
|
direction=direction,
|
|
446
468
|
where=where,
|
|
447
469
|
lower=lower,
|
|
470
|
+
name=name,
|
|
448
471
|
)
|
|
449
472
|
if kwds.get("sql_only", False):
|
|
450
473
|
return sql, vals
|
|
@@ -484,6 +507,7 @@ class Table:
|
|
|
484
507
|
"direction": definition.get("direction"),
|
|
485
508
|
"where": definition.get("where"),
|
|
486
509
|
"lower": definition.get("lower"),
|
|
510
|
+
"name": definition.get("name"),
|
|
487
511
|
}
|
|
488
512
|
else:
|
|
489
513
|
columns = definition
|
|
@@ -492,6 +516,7 @@ class Table:
|
|
|
492
516
|
"direction": None,
|
|
493
517
|
"where": None,
|
|
494
518
|
"lower": None,
|
|
519
|
+
"name": None,
|
|
495
520
|
}
|
|
496
521
|
|
|
497
522
|
if isinstance(columns, str):
|
|
@@ -558,6 +583,7 @@ class Table:
|
|
|
558
583
|
direction=spec["direction"],
|
|
559
584
|
where=spec["where"],
|
|
560
585
|
lower=spec["lower"],
|
|
586
|
+
name=spec["name"],
|
|
561
587
|
sql_only=True,
|
|
562
588
|
)
|
|
563
589
|
return _parse_index_signature(sql)
|
|
@@ -594,6 +620,8 @@ class Table:
|
|
|
594
620
|
_normalize_identifier(item["referenced_column_name"])
|
|
595
621
|
for item in ordered
|
|
596
622
|
),
|
|
623
|
+
"on_delete": _normalize_referential_action(first.get("delete_rule")),
|
|
624
|
+
"on_update": _normalize_referential_action(first.get("update_rule")),
|
|
597
625
|
}
|
|
598
626
|
)
|
|
599
627
|
return signatures
|
|
@@ -710,6 +738,7 @@ class Table:
|
|
|
710
738
|
summary["skipped_indexes"].append(
|
|
711
739
|
{
|
|
712
740
|
"columns": list(spec["columns"]),
|
|
741
|
+
"name": spec["name"],
|
|
713
742
|
"reason": message,
|
|
714
743
|
}
|
|
715
744
|
)
|
|
@@ -719,6 +748,7 @@ class Table:
|
|
|
719
748
|
summary["skipped_indexes"].append(
|
|
720
749
|
{
|
|
721
750
|
"columns": list(spec["columns"]),
|
|
751
|
+
"name": spec["name"],
|
|
722
752
|
"reason": "create_missing_indexes is disabled",
|
|
723
753
|
}
|
|
724
754
|
)
|
|
@@ -732,11 +762,13 @@ class Table:
|
|
|
732
762
|
direction=spec["direction"],
|
|
733
763
|
where=spec["where"],
|
|
734
764
|
lower=spec["lower"],
|
|
765
|
+
name=spec["name"],
|
|
735
766
|
)
|
|
736
767
|
summary["created_indexes"].append(
|
|
737
768
|
{
|
|
738
769
|
"columns": list(spec["columns"]),
|
|
739
770
|
"unique": spec["unique"],
|
|
771
|
+
"name": spec["name"],
|
|
740
772
|
}
|
|
741
773
|
)
|
|
742
774
|
existing_indexes = self._existing_index_signatures()
|
|
@@ -750,6 +782,7 @@ class Table:
|
|
|
750
782
|
summary["skipped_indexes"].append(
|
|
751
783
|
{
|
|
752
784
|
"columns": list(spec["columns"]),
|
|
785
|
+
"name": spec["name"],
|
|
753
786
|
"reason": message,
|
|
754
787
|
}
|
|
755
788
|
)
|
|
@@ -763,12 +796,40 @@ class Table:
|
|
|
763
796
|
if existing["columns"] == spec["columns"]
|
|
764
797
|
and existing["ref_table"] == spec["ref_table"]
|
|
765
798
|
and existing["ref_columns"] == spec["ref_columns"]
|
|
799
|
+
and existing["on_delete"] == spec["on_delete"]
|
|
800
|
+
and existing["on_update"] == spec["on_update"]
|
|
766
801
|
),
|
|
767
802
|
None,
|
|
768
803
|
)
|
|
769
804
|
if exact_match:
|
|
770
805
|
continue
|
|
771
806
|
|
|
807
|
+
name_conflict = None
|
|
808
|
+
if spec["name"]:
|
|
809
|
+
name_conflict = next(
|
|
810
|
+
(
|
|
811
|
+
existing
|
|
812
|
+
for existing in existing_foreign_keys
|
|
813
|
+
if existing["constraint_name"] == spec["name"]
|
|
814
|
+
),
|
|
815
|
+
None,
|
|
816
|
+
)
|
|
817
|
+
if name_conflict:
|
|
818
|
+
message = (
|
|
819
|
+
f"Foreign key '{spec['name']}' on table '{self.name}' exists "
|
|
820
|
+
"with a different definition."
|
|
821
|
+
)
|
|
822
|
+
if on_existing_conflicts == "raise":
|
|
823
|
+
raise exceptions.DbSchemaConflictError(message)
|
|
824
|
+
summary["skipped_foreign_keys"].append(
|
|
825
|
+
{
|
|
826
|
+
"columns": list(spec["columns"]),
|
|
827
|
+
"name": spec["name"],
|
|
828
|
+
"reason": message,
|
|
829
|
+
}
|
|
830
|
+
)
|
|
831
|
+
continue
|
|
832
|
+
|
|
772
833
|
column_conflict = next(
|
|
773
834
|
(
|
|
774
835
|
existing
|
|
@@ -780,13 +841,14 @@ class Table:
|
|
|
780
841
|
if column_conflict:
|
|
781
842
|
message = (
|
|
782
843
|
f"Foreign key on table '{self.name}' for columns {spec['columns']} "
|
|
783
|
-
"exists with a different target."
|
|
844
|
+
"exists with a different target or referential action."
|
|
784
845
|
)
|
|
785
846
|
if on_existing_conflicts == "raise":
|
|
786
847
|
raise exceptions.DbSchemaConflictError(message)
|
|
787
848
|
summary["skipped_foreign_keys"].append(
|
|
788
849
|
{
|
|
789
850
|
"columns": list(spec["columns"]),
|
|
851
|
+
"name": spec["name"],
|
|
790
852
|
"reason": message,
|
|
791
853
|
}
|
|
792
854
|
)
|
|
@@ -796,6 +858,7 @@ class Table:
|
|
|
796
858
|
summary["skipped_foreign_keys"].append(
|
|
797
859
|
{
|
|
798
860
|
"columns": list(spec["columns"]),
|
|
861
|
+
"name": spec["name"],
|
|
799
862
|
"reason": "create_missing_foreign_keys is disabled",
|
|
800
863
|
}
|
|
801
864
|
)
|
|
@@ -806,12 +869,18 @@ class Table:
|
|
|
806
869
|
list(spec["columns"]),
|
|
807
870
|
spec["ref_table"],
|
|
808
871
|
list(spec["ref_columns"]),
|
|
872
|
+
name=spec["name"],
|
|
873
|
+
on_delete=spec["on_delete"],
|
|
874
|
+
on_update=spec["on_update"],
|
|
809
875
|
)
|
|
810
876
|
summary["created_foreign_keys"].append(
|
|
811
877
|
{
|
|
812
878
|
"columns": list(spec["columns"]),
|
|
813
879
|
"ref_table": spec["ref_table"],
|
|
814
880
|
"ref_columns": list(spec["ref_columns"]),
|
|
881
|
+
"name": spec["name"],
|
|
882
|
+
"on_delete": spec["on_delete"],
|
|
883
|
+
"on_update": spec["on_update"],
|
|
815
884
|
}
|
|
816
885
|
)
|
|
817
886
|
existing_foreign_keys = self._existing_foreign_key_signatures()
|
|
@@ -1124,25 +1193,53 @@ class Table:
|
|
|
1124
1193
|
|
|
1125
1194
|
@return_default()
|
|
1126
1195
|
def create_foreign_key(
|
|
1127
|
-
self,
|
|
1196
|
+
self,
|
|
1197
|
+
columns,
|
|
1198
|
+
key_to_table,
|
|
1199
|
+
key_to_columns="sys_id",
|
|
1200
|
+
name=None,
|
|
1201
|
+
on_delete=None,
|
|
1202
|
+
on_update=None,
|
|
1203
|
+
**kwds,
|
|
1128
1204
|
):
|
|
1129
1205
|
"""
|
|
1130
1206
|
Creates a foreign key referencing `key_to_table(key_to_columns)`.
|
|
1131
1207
|
"""
|
|
1132
1208
|
sql, vals = self.sql.create_foreign_key(
|
|
1133
|
-
self.name,
|
|
1209
|
+
self.name,
|
|
1210
|
+
columns,
|
|
1211
|
+
key_to_table,
|
|
1212
|
+
key_to_columns,
|
|
1213
|
+
name=name,
|
|
1214
|
+
on_delete=on_delete,
|
|
1215
|
+
on_update=on_update,
|
|
1134
1216
|
)
|
|
1135
1217
|
if kwds.get("sql_only", False):
|
|
1136
1218
|
return sql, vals
|
|
1137
1219
|
_ddl_logger.warning("DDL CREATE FOREIGN KEY on %s columns=%s ref=%s(%s)", self.name, columns, key_to_table, key_to_columns)
|
|
1138
1220
|
return self.tx.execute(sql, vals, cursor=self.cursor())
|
|
1139
1221
|
|
|
1140
|
-
def drop_foreign_key(
|
|
1222
|
+
def drop_foreign_key(
|
|
1223
|
+
self,
|
|
1224
|
+
columns,
|
|
1225
|
+
key_to_table,
|
|
1226
|
+
key_to_columns="sys_id",
|
|
1227
|
+
name=None,
|
|
1228
|
+
on_delete=None,
|
|
1229
|
+
on_update=None,
|
|
1230
|
+
**kwds,
|
|
1231
|
+
):
|
|
1141
1232
|
"""
|
|
1142
1233
|
Drops the specified foreign key constraint.
|
|
1143
1234
|
"""
|
|
1144
|
-
sql, vals = self.sql.
|
|
1145
|
-
self.name,
|
|
1235
|
+
sql, vals = self.sql.drop_foreign_key(
|
|
1236
|
+
self.name,
|
|
1237
|
+
columns,
|
|
1238
|
+
key_to_table,
|
|
1239
|
+
key_to_columns,
|
|
1240
|
+
name=name,
|
|
1241
|
+
on_delete=on_delete,
|
|
1242
|
+
on_update=on_update,
|
|
1146
1243
|
)
|
|
1147
1244
|
if kwds.get("sql_only", False):
|
|
1148
1245
|
return sql, vals
|
|
@@ -407,6 +407,8 @@ class BaseSQLDialect(ABC):
|
|
|
407
407
|
key_to_columns: List[str],
|
|
408
408
|
name: Optional[str] = None,
|
|
409
409
|
schema: Optional[str] = None,
|
|
410
|
+
on_delete: Optional[str] = None,
|
|
411
|
+
on_update: Optional[str] = None,
|
|
410
412
|
) -> str:
|
|
411
413
|
"""Generate CREATE FOREIGN KEY statement."""
|
|
412
414
|
pass
|
|
@@ -421,6 +423,8 @@ class BaseSQLDialect(ABC):
|
|
|
421
423
|
key_to_columns: Optional[List[str]] = None,
|
|
422
424
|
name: Optional[str] = None,
|
|
423
425
|
schema: Optional[str] = None,
|
|
426
|
+
on_delete: Optional[str] = None,
|
|
427
|
+
on_update: Optional[str] = None,
|
|
424
428
|
) -> str:
|
|
425
429
|
"""Generate DROP FOREIGN KEY statement."""
|
|
426
430
|
pass
|
|
@@ -654,7 +654,15 @@ END;
|
|
|
654
654
|
|
|
655
655
|
@classmethod
|
|
656
656
|
def create_foreign_key(
|
|
657
|
-
cls,
|
|
657
|
+
cls,
|
|
658
|
+
table,
|
|
659
|
+
columns,
|
|
660
|
+
key_to_table,
|
|
661
|
+
key_to_columns,
|
|
662
|
+
name=None,
|
|
663
|
+
schema=None,
|
|
664
|
+
on_delete=None,
|
|
665
|
+
on_update=None,
|
|
658
666
|
):
|
|
659
667
|
if name is None:
|
|
660
668
|
name = f"fk_{table}_{'_'.join(columns)}"
|
|
@@ -662,12 +670,17 @@ END;
|
|
|
662
670
|
col_list = ", ".join(quote(col) for col in columns)
|
|
663
671
|
ref_col_list = ", ".join(quote(col) for col in key_to_columns)
|
|
664
672
|
|
|
665
|
-
|
|
673
|
+
sql = f"""
|
|
666
674
|
ALTER TABLE {quote(table)}
|
|
667
675
|
ADD CONSTRAINT {quote(name)}
|
|
668
676
|
FOREIGN KEY ({col_list})
|
|
669
677
|
REFERENCES {quote(key_to_table)} ({ref_col_list})
|
|
670
678
|
"""
|
|
679
|
+
if on_delete:
|
|
680
|
+
sql += f" ON DELETE {str(on_delete).upper()}"
|
|
681
|
+
if on_update:
|
|
682
|
+
sql += f" ON UPDATE {str(on_update).upper()}"
|
|
683
|
+
return sql
|
|
671
684
|
|
|
672
685
|
@classmethod
|
|
673
686
|
def drop_foreign_key(
|
|
@@ -678,6 +691,8 @@ END;
|
|
|
678
691
|
key_to_columns=None,
|
|
679
692
|
name=None,
|
|
680
693
|
schema=None,
|
|
694
|
+
on_delete=None,
|
|
695
|
+
on_update=None,
|
|
681
696
|
):
|
|
682
697
|
if name is None:
|
|
683
698
|
name = f"fk_{table}_{'_'.join(columns)}"
|
|
@@ -1461,6 +1461,8 @@ class SQL(BaseSQLDialect):
|
|
|
1461
1461
|
, KCU2.CONSTRAINT_SCHEMA AS "REFERENCED_TABLE_SCHEMA"
|
|
1462
1462
|
, KCU2.TABLE_NAME AS "REFERENCED_TABLE_NAME"
|
|
1463
1463
|
, KCU2.COLUMN_NAME AS "REFERENCED_COLUMN_NAME"
|
|
1464
|
+
, RC.UPDATE_RULE AS "UPDATE_RULE"
|
|
1465
|
+
, RC.DELETE_RULE AS "DELETE_RULE"
|
|
1464
1466
|
FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS RC
|
|
1465
1467
|
JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE KCU1
|
|
1466
1468
|
ON KCU1.CONSTRAINT_CATALOG = RC.CONSTRAINT_CATALOG
|
|
@@ -1495,7 +1497,15 @@ class SQL(BaseSQLDialect):
|
|
|
1495
1497
|
|
|
1496
1498
|
@classmethod
|
|
1497
1499
|
def create_foreign_key(
|
|
1498
|
-
cls,
|
|
1500
|
+
cls,
|
|
1501
|
+
table,
|
|
1502
|
+
columns,
|
|
1503
|
+
key_to_table,
|
|
1504
|
+
key_to_columns,
|
|
1505
|
+
name=None,
|
|
1506
|
+
schema=None,
|
|
1507
|
+
on_delete=None,
|
|
1508
|
+
on_update=None,
|
|
1499
1509
|
):
|
|
1500
1510
|
if "." not in table and schema:
|
|
1501
1511
|
table = f"{schema}.{table}"
|
|
@@ -1510,7 +1520,15 @@ class SQL(BaseSQLDialect):
|
|
|
1510
1520
|
m.update(key_to_table.encode("utf-8"))
|
|
1511
1521
|
m.update(" ".join(key_to_columns).encode("utf-8"))
|
|
1512
1522
|
name = f"FK_{m.hexdigest()}"
|
|
1513
|
-
sql =
|
|
1523
|
+
sql = (
|
|
1524
|
+
f"ALTER TABLE {table} ADD CONSTRAINT {name} FOREIGN KEY ({','.join(columns)}) "
|
|
1525
|
+
f"REFERENCES {key_to_table} ({','.join(key_to_columns)})"
|
|
1526
|
+
)
|
|
1527
|
+
if on_delete:
|
|
1528
|
+
sql += f" ON DELETE {str(on_delete).upper()}"
|
|
1529
|
+
if on_update:
|
|
1530
|
+
sql += f" ON UPDATE {str(on_update).upper()}"
|
|
1531
|
+
sql += ";"
|
|
1514
1532
|
sql = sqlparse.format(sql, reindent=True, keyword_case="upper")
|
|
1515
1533
|
return sql, tuple()
|
|
1516
1534
|
|
|
@@ -1523,6 +1541,8 @@ class SQL(BaseSQLDialect):
|
|
|
1523
1541
|
key_to_columns=None,
|
|
1524
1542
|
name=None,
|
|
1525
1543
|
schema=None,
|
|
1544
|
+
on_delete=None,
|
|
1545
|
+
on_update=None,
|
|
1526
1546
|
):
|
|
1527
1547
|
if "." not in table and schema:
|
|
1528
1548
|
table = f"{schema}.{table}"
|
|
@@ -1576,10 +1596,11 @@ class SQL(BaseSQLDialect):
|
|
|
1576
1596
|
"",
|
|
1577
1597
|
columns.replace(" ", "").replace(",", "_").replace('"', ""),
|
|
1578
1598
|
)
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1599
|
+
if trigram:
|
|
1600
|
+
name = f"IDX__TRGM_{table.replace('.', '_')}_{trigram}__{name}".upper()
|
|
1601
|
+
else:
|
|
1602
|
+
name = f"IDX__{table.replace('.', '_')}__{name}".upper()
|
|
1603
|
+
sql.append(name)
|
|
1583
1604
|
sql.append("ON")
|
|
1584
1605
|
sql.append(TableHelper.quote(tablename))
|
|
1585
1606
|
|
|
@@ -1633,10 +1654,11 @@ class SQL(BaseSQLDialect):
|
|
|
1633
1654
|
"",
|
|
1634
1655
|
columns.replace(" ", "").replace(",", "_").replace('"', ""),
|
|
1635
1656
|
)
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1657
|
+
if trigram:
|
|
1658
|
+
name = f"IDX__TRGM_{table.replace('.', '_')}_{trigram.upper()}__{name}"
|
|
1659
|
+
else:
|
|
1660
|
+
name = f"IDX__{table.replace('.', '_')}__{name}"
|
|
1661
|
+
sql.append(name)
|
|
1640
1662
|
|
|
1641
1663
|
sql = sqlparse.format(" ".join(sql), reindent=True, keyword_case="upper")
|
|
1642
1664
|
return sql, tuple()
|
|
@@ -593,7 +593,15 @@ END;
|
|
|
593
593
|
|
|
594
594
|
@classmethod
|
|
595
595
|
def create_foreign_key(
|
|
596
|
-
cls,
|
|
596
|
+
cls,
|
|
597
|
+
table,
|
|
598
|
+
columns,
|
|
599
|
+
key_to_table,
|
|
600
|
+
key_to_columns,
|
|
601
|
+
name=None,
|
|
602
|
+
schema=None,
|
|
603
|
+
on_delete=None,
|
|
604
|
+
on_update=None,
|
|
597
605
|
):
|
|
598
606
|
# SQLite foreign keys must be defined at table creation time
|
|
599
607
|
return "-- SQLite foreign keys must be defined at table creation"
|
|
@@ -607,6 +615,8 @@ END;
|
|
|
607
615
|
key_to_columns=None,
|
|
608
616
|
name=None,
|
|
609
617
|
schema=None,
|
|
618
|
+
on_delete=None,
|
|
619
|
+
on_update=None,
|
|
610
620
|
):
|
|
611
621
|
return "-- SQLite foreign keys must be dropped by recreating table"
|
|
612
622
|
|
|
@@ -733,7 +733,15 @@ END;
|
|
|
733
733
|
|
|
734
734
|
@classmethod
|
|
735
735
|
def create_foreign_key(
|
|
736
|
-
cls,
|
|
736
|
+
cls,
|
|
737
|
+
table,
|
|
738
|
+
columns,
|
|
739
|
+
key_to_table,
|
|
740
|
+
key_to_columns,
|
|
741
|
+
name=None,
|
|
742
|
+
schema=None,
|
|
743
|
+
on_delete=None,
|
|
744
|
+
on_update=None,
|
|
737
745
|
):
|
|
738
746
|
if name is None:
|
|
739
747
|
name = f"FK_{table}_{'_'.join(columns)}"
|
|
@@ -741,12 +749,17 @@ END;
|
|
|
741
749
|
col_list = ", ".join(quote(col) for col in columns)
|
|
742
750
|
ref_col_list = ", ".join(quote(col) for col in key_to_columns)
|
|
743
751
|
|
|
744
|
-
|
|
752
|
+
sql = f"""
|
|
745
753
|
ALTER TABLE {quote(table)}
|
|
746
754
|
ADD CONSTRAINT {quote(name)}
|
|
747
755
|
FOREIGN KEY ({col_list})
|
|
748
756
|
REFERENCES {quote(key_to_table)} ({ref_col_list})
|
|
749
757
|
"""
|
|
758
|
+
if on_delete:
|
|
759
|
+
sql += f" ON DELETE {str(on_delete).upper()}"
|
|
760
|
+
if on_update:
|
|
761
|
+
sql += f" ON UPDATE {str(on_update).upper()}"
|
|
762
|
+
return sql
|
|
750
763
|
|
|
751
764
|
@classmethod
|
|
752
765
|
def drop_foreign_key(
|
|
@@ -757,6 +770,8 @@ END;
|
|
|
757
770
|
key_to_columns=None,
|
|
758
771
|
name=None,
|
|
759
772
|
schema=None,
|
|
773
|
+
on_delete=None,
|
|
774
|
+
on_update=None,
|
|
760
775
|
):
|
|
761
776
|
if name is None:
|
|
762
777
|
name = f"FK_{table}_{'_'.join(columns)}"
|
|
@@ -253,6 +253,71 @@ class TestSchemaLocking(unittest.TestCase):
|
|
|
253
253
|
|
|
254
254
|
run()
|
|
255
255
|
|
|
256
|
+
def test_ensure_schema_supports_named_indexes_and_foreign_keys(self):
|
|
257
|
+
"""ensure_schema should preserve explicit names and referential actions."""
|
|
258
|
+
unlocked_engine = postgres.initialize(
|
|
259
|
+
database=test_db, schema_locked=False, pool_enabled=False
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
@unlocked_engine.transaction
|
|
263
|
+
def run(tx):
|
|
264
|
+
tx.table("schema_departments").ensure_schema(columns={"name": str})
|
|
265
|
+
|
|
266
|
+
summary = tx.table("schema_memberships").ensure_schema(
|
|
267
|
+
columns={
|
|
268
|
+
"email": str,
|
|
269
|
+
"status": str,
|
|
270
|
+
"department_id": int,
|
|
271
|
+
},
|
|
272
|
+
unique_indexes=[
|
|
273
|
+
{"columns": ["email"], "name": "schema_memberships_email_uq"}
|
|
274
|
+
],
|
|
275
|
+
indexes=[
|
|
276
|
+
{"columns": ["status"], "name": "schema_memberships_status_idx"}
|
|
277
|
+
],
|
|
278
|
+
foreign_keys=[
|
|
279
|
+
{
|
|
280
|
+
"columns": ["department_id"],
|
|
281
|
+
"ref_table": "schema_departments",
|
|
282
|
+
"ref_columns": ["sys_id"],
|
|
283
|
+
"name": "schema_memberships_department_fk",
|
|
284
|
+
"on_delete": "cascade",
|
|
285
|
+
}
|
|
286
|
+
],
|
|
287
|
+
create_missing_foreign_keys=True,
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
self.assertEqual(summary["created_indexes"][0]["name"], "schema_memberships_email_uq")
|
|
291
|
+
self.assertEqual(summary["created_foreign_keys"][0]["name"], "schema_memberships_department_fk")
|
|
292
|
+
self.assertEqual(summary["created_foreign_keys"][0]["on_delete"], "CASCADE")
|
|
293
|
+
|
|
294
|
+
index_names = {
|
|
295
|
+
row["indexname"].lower()
|
|
296
|
+
for row in tx.execute(
|
|
297
|
+
"""
|
|
298
|
+
SELECT indexname
|
|
299
|
+
FROM pg_indexes
|
|
300
|
+
WHERE schemaname = 'public' AND tablename = %s
|
|
301
|
+
""",
|
|
302
|
+
["schema_memberships"],
|
|
303
|
+
).as_dict().all()
|
|
304
|
+
}
|
|
305
|
+
self.assertIn("schema_memberships_email_uq", index_names)
|
|
306
|
+
self.assertIn("schema_memberships_status_idx", index_names)
|
|
307
|
+
|
|
308
|
+
fk_rules = tx.execute(
|
|
309
|
+
"""
|
|
310
|
+
SELECT constraint_name, delete_rule
|
|
311
|
+
FROM information_schema.referential_constraints
|
|
312
|
+
WHERE constraint_name = %s
|
|
313
|
+
""",
|
|
314
|
+
["schema_memberships_department_fk"],
|
|
315
|
+
).as_dict().all()
|
|
316
|
+
self.assertEqual(len(fk_rules), 1)
|
|
317
|
+
self.assertEqual(fk_rules[0]["delete_rule"], "CASCADE")
|
|
318
|
+
|
|
319
|
+
run()
|
|
320
|
+
|
|
256
321
|
def test_ensure_schema_raises_schema_conflict_for_duplicate_unique_rows(self):
|
|
257
322
|
"""ensure_schema should raise a schema conflict when unique index backfill finds duplicates."""
|
|
258
323
|
unlocked_engine = postgres.initialize(
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/aws/handlers/context_factory.py
RENAMED
|
File without changes
|
|
File without changes
|
{velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/aws/handlers/lambda_handler.py
RENAMED
|
File without changes
|
{velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/aws/handlers/mixins/__init__.py
RENAMED
|
File without changes
|
{velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/aws/handlers/mixins/data_service.py
RENAMED
|
File without changes
|
{velocity_python-0.1.38 → velocity_python-0.1.39}/src/velocity/aws/handlers/mixins/web_handler.py
RENAMED
|
File without changes
|
|
File without changes
|