velocity-python 0.1.37__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.37/src/velocity_python.egg-info → velocity_python-0.1.39}/PKG-INFO +1 -1
- {velocity_python-0.1.37 → velocity_python-0.1.39}/pyproject.toml +1 -1
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/__init__.py +1 -1
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/core/decorators.py +103 -17
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/core/table.py +579 -7
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/exceptions.py +7 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/servers/base/sql.py +4 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/servers/mysql/sql.py +17 -2
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/servers/postgres/sql.py +32 -10
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/servers/sqlite/sql.py +11 -1
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/servers/sqlserver/sql.py +17 -2
- velocity_python-0.1.39/src/velocity/db/tests/postgres/test_schema_locking.py +631 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39/src/velocity_python.egg-info}/PKG-INFO +1 -1
- velocity_python-0.1.37/src/velocity/db/tests/postgres/test_schema_locking.py +0 -348
- {velocity_python-0.1.37 → velocity_python-0.1.39}/LICENSE +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/README.md +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/setup.cfg +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/aws/__init__.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/aws/amplify.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/aws/amplify_build.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/aws/assets/__init__.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/aws/assets/backfill.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/aws/assets/indexing.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/aws/assets/references.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/aws/assets/service.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/aws/assets/usage_index.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/aws/dirty_pipeline.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/aws/handlers/__init__.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/aws/handlers/base_handler.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/aws/handlers/context.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/aws/handlers/context_factory.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/aws/handlers/exceptions.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/aws/handlers/lambda_handler.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/aws/handlers/mixins/__init__.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/aws/handlers/mixins/data_service.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/aws/handlers/mixins/web_handler.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/aws/handlers/perf.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/aws/handlers/response.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/aws/handlers/sqs_handler.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/aws/s3.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/aws/ssm_config.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/aws/tests/__init__.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/aws/tests/test_base_handler_error_response.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/aws/tests/test_lambda_handler_json_serialization.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/aws/tests/test_response.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/__init__.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/core/__init__.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/core/async_support.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/core/column.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/core/database.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/core/engine.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/core/result.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/core/row.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/core/sequence.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/core/transaction.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/core/view.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/migrations.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/servers/__init__.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/servers/base/__init__.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/servers/base/initializer.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/servers/base/operators.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/servers/base/types.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/servers/mysql/__init__.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/servers/mysql/operators.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/servers/mysql/reserved.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/servers/mysql/types.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/servers/postgres/__init__.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/servers/postgres/operators.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/servers/postgres/reserved.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/servers/postgres/types.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/servers/sqlite/__init__.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/servers/sqlite/operators.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/servers/sqlite/reserved.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/servers/sqlite/types.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/servers/sqlserver/__init__.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/servers/sqlserver/operators.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/servers/sqlserver/reserved.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/servers/sqlserver/types.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/servers/tablehelper.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/__init__.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/common_db_test.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/postgres/__init__.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/postgres/common.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/postgres/test_column.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/postgres/test_connections.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/postgres/test_database.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/postgres/test_engine.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/postgres/test_general_usage.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/postgres/test_imports.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/postgres/test_result.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/postgres/test_row.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/postgres/test_row_comprehensive.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/postgres/test_schema_locking_unit.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/postgres/test_sequence.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/postgres/test_sql_comprehensive.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/postgres/test_table.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/postgres/test_table_comprehensive.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/postgres/test_transaction.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/sql/__init__.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/sql/common.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/sql/test_postgres_select_advanced.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/sql/test_postgres_select_variances.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/test_cursor_rowcount_fix.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/test_db_utils.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/test_postgres.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/test_postgres_unchanged.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/test_process_error_robustness.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/test_result_caching.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/test_result_sql_aware.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/test_row_get_missing_column.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/test_schema_locking_initializers.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/test_schema_locking_simple.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/test_sql_builder.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/test_tablehelper.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/tests/test_view_helper.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/db/utils.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/logging.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/misc/__init__.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/misc/conv/__init__.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/misc/conv/iconv.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/misc/conv/oconv.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/misc/db.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/misc/export.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/misc/format.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/misc/mail.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/misc/merge.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/misc/pdf.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/misc/tests/__init__.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/misc/tests/test_db.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/misc/tests/test_fix.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/misc/tests/test_format.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/misc/tests/test_iconv.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/misc/tests/test_merge.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/misc/tests/test_oconv.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/misc/tests/test_original_error.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/misc/tests/test_timer.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/misc/timer.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/misc/tools.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/payment/__init__.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/payment/authorizenet_adapter.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/payment/authorizenet_mirror.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/payment/base_adapter.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/payment/braintree_adapter.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/payment/braintree_mirror.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/payment/charge_rules.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/payment/stripe_adapter.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity/payment/stripe_mirror.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity_python.egg-info/SOURCES.txt +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity_python.egg-info/dependency_links.txt +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity_python.egg-info/entry_points.txt +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity_python.egg-info/requires.txt +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/src/velocity_python.egg-info/top_level.txt +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_amplify_build.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_asset_indexing.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_asset_references.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_assets_service.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_async_support.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_batch_operations.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_concurrency_safety.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_connection_pool.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_connection_resilience.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_decorators.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_dirty_pipeline_fast_path.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_email_processing.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_iconv_money_to_cents.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_lambda_handler.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_lambda_handler_auth.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_mixins_import.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_n_plus_one.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_observability.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_payment_authorizenet_adapter.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_payment_braintree_adapter.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_payment_braintree_mirror.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_payment_profile_sorting.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_payment_stripe_adapter.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_pdf.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_prepared_statements.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_psycopg3_upgrade.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_query_cache.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_row_batch_update.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_row_cache_staleness.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_row_dirty_tracking.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_schema_migrations.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_security_hardening.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_server_cursor.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_spreadsheet_functions.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_sqs_per_record_transactions.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_sys_modified_count_postgres_demo.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_table_alter.py +0 -0
- {velocity_python-0.1.37 → velocity_python-0.1.39}/tests/test_where_clause_validation.py +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import time
|
|
3
3
|
import random
|
|
4
|
+
from collections.abc import Mapping, Sequence
|
|
4
5
|
from functools import wraps
|
|
5
6
|
from velocity.db import exceptions
|
|
6
7
|
|
|
@@ -148,6 +149,73 @@ def return_default(
|
|
|
148
149
|
return decorator
|
|
149
150
|
|
|
150
151
|
|
|
152
|
+
def _merge_schema_seed(target, value):
|
|
153
|
+
"""Collect representative columns from dict payloads or lists of dict rows."""
|
|
154
|
+
|
|
155
|
+
if isinstance(value, Mapping):
|
|
156
|
+
for key, val in value.items():
|
|
157
|
+
if isinstance(key, str) and key not in target:
|
|
158
|
+
target[key] = val
|
|
159
|
+
return
|
|
160
|
+
|
|
161
|
+
if isinstance(value, Sequence) and not isinstance(value, (str, bytes, bytearray)):
|
|
162
|
+
for item in value:
|
|
163
|
+
if isinstance(item, Mapping):
|
|
164
|
+
_merge_schema_seed(target, item)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _collect_schema_seed(args, kwds):
|
|
168
|
+
data = {}
|
|
169
|
+
|
|
170
|
+
for key in ("pk", "data"):
|
|
171
|
+
_merge_schema_seed(data, kwds.get(key))
|
|
172
|
+
|
|
173
|
+
for arg in args:
|
|
174
|
+
_merge_schema_seed(data, arg)
|
|
175
|
+
|
|
176
|
+
return data
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _normalize_conflict_columns(pk):
|
|
180
|
+
if pk is None:
|
|
181
|
+
return []
|
|
182
|
+
if isinstance(pk, str):
|
|
183
|
+
return [pk]
|
|
184
|
+
if isinstance(pk, Mapping):
|
|
185
|
+
return [str(key) for key in pk.keys()]
|
|
186
|
+
if isinstance(pk, Sequence) and not isinstance(pk, (str, bytes, bytearray)):
|
|
187
|
+
return [str(column) for column in pk]
|
|
188
|
+
|
|
189
|
+
try:
|
|
190
|
+
return [str(column) for column in pk]
|
|
191
|
+
except TypeError:
|
|
192
|
+
return [str(pk)]
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _infer_conflict_columns(func, args, kwds):
|
|
196
|
+
"""Infer explicit ON CONFLICT columns for upsert operations."""
|
|
197
|
+
|
|
198
|
+
if func.__name__ not in {"merge", "upsert_many"}:
|
|
199
|
+
return []
|
|
200
|
+
|
|
201
|
+
pk = kwds.get("pk")
|
|
202
|
+
if pk is None and len(args) >= 2:
|
|
203
|
+
pk = args[1]
|
|
204
|
+
|
|
205
|
+
columns = [column.lower() for column in _normalize_conflict_columns(pk) if column]
|
|
206
|
+
if len(columns) == 1 and columns[0] == "sys_id":
|
|
207
|
+
return []
|
|
208
|
+
return columns
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def _missing_conflict_constraint_error(exc):
|
|
212
|
+
message = str(exc or "").lower()
|
|
213
|
+
return (
|
|
214
|
+
"no unique or exclusion constraint matching the on conflict specification"
|
|
215
|
+
in message
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
|
|
151
219
|
def create_missing(func):
|
|
152
220
|
"""
|
|
153
221
|
If the function call fails with DbColumnMissingError or DbTableMissingError,
|
|
@@ -161,6 +229,8 @@ def create_missing(func):
|
|
|
161
229
|
|
|
162
230
|
@wraps(func)
|
|
163
231
|
def wrapper(self, *args, **kwds):
|
|
232
|
+
conflict_columns = _infer_conflict_columns(func, args, kwds)
|
|
233
|
+
|
|
164
234
|
sp = self.tx.create_savepoint(cursor=self.cursor())
|
|
165
235
|
try:
|
|
166
236
|
result = func(self, *args, **kwds)
|
|
@@ -181,14 +251,7 @@ def create_missing(func):
|
|
|
181
251
|
) from e
|
|
182
252
|
|
|
183
253
|
# Existing logic for automatic creation
|
|
184
|
-
data =
|
|
185
|
-
if "pk" in kwds:
|
|
186
|
-
data.update(kwds["pk"])
|
|
187
|
-
if "data" in kwds:
|
|
188
|
-
data.update(kwds["data"])
|
|
189
|
-
for i, arg in enumerate(args):
|
|
190
|
-
if isinstance(arg, dict):
|
|
191
|
-
data.update(arg)
|
|
254
|
+
data = _collect_schema_seed(args, kwds)
|
|
192
255
|
|
|
193
256
|
# ALTER TABLE ADD COLUMN IF NOT EXISTS — acquire an advisory
|
|
194
257
|
# lock to serialize DDL across concurrent Lambda containers,
|
|
@@ -220,15 +283,7 @@ def create_missing(func):
|
|
|
220
283
|
) from e
|
|
221
284
|
|
|
222
285
|
# Existing logic for automatic creation
|
|
223
|
-
data =
|
|
224
|
-
if "pk" in kwds:
|
|
225
|
-
data.update(kwds["pk"])
|
|
226
|
-
if "data" in kwds:
|
|
227
|
-
data.update(kwds["data"])
|
|
228
|
-
for i, arg in enumerate(args):
|
|
229
|
-
if isinstance(arg, dict):
|
|
230
|
-
data.update(arg)
|
|
231
|
-
|
|
286
|
+
data = _collect_schema_seed(args, kwds)
|
|
232
287
|
# CREATE TABLE IF NOT EXISTS — acquire advisory lock then
|
|
233
288
|
# guard against concurrent creation with a savepoint.
|
|
234
289
|
try:
|
|
@@ -242,6 +297,37 @@ def create_missing(func):
|
|
|
242
297
|
except exceptions.DbObjectExistsError:
|
|
243
298
|
self.tx.rollback_savepoint(sp2, cursor=self.cursor())
|
|
244
299
|
|
|
300
|
+
if conflict_columns:
|
|
301
|
+
self.create_index(conflict_columns, unique=True)
|
|
302
|
+
|
|
303
|
+
return func(self, *args, **kwds)
|
|
304
|
+
except exceptions.DbException as e:
|
|
305
|
+
self.tx.rollback_savepoint(sp, cursor=self.cursor())
|
|
306
|
+
|
|
307
|
+
if not conflict_columns or not _missing_conflict_constraint_error(e):
|
|
308
|
+
raise
|
|
309
|
+
|
|
310
|
+
if self.tx.engine.schema_locked:
|
|
311
|
+
logger.warning(
|
|
312
|
+
"@create_missing triggered on locked schema: table=%s error=%s",
|
|
313
|
+
self.name, e,
|
|
314
|
+
extra={
|
|
315
|
+
"table_name": self.name,
|
|
316
|
+
"operation": "create_missing_conflict_index",
|
|
317
|
+
"schema_locked": True,
|
|
318
|
+
},
|
|
319
|
+
)
|
|
320
|
+
raise exceptions.DbSchemaLockedError(
|
|
321
|
+
"Cannot create missing unique index: schema is locked. "
|
|
322
|
+
f"Original error: {e}"
|
|
323
|
+
) from e
|
|
324
|
+
|
|
325
|
+
try:
|
|
326
|
+
self.tx.advisory_lock(f"velocity_ddl_{self.name}")
|
|
327
|
+
except Exception:
|
|
328
|
+
pass
|
|
329
|
+
|
|
330
|
+
self.create_index(conflict_columns, unique=True)
|
|
245
331
|
return func(self, *args, **kwds)
|
|
246
332
|
|
|
247
333
|
return wrapper
|