altcodepro-polydb-python 2.5.22__tar.gz → 2.5.23__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 (132) hide show
  1. {altcodepro_polydb_python-2.5.22/src/altcodepro_polydb_python.egg-info → altcodepro_polydb_python-2.5.23}/PKG-INFO +1 -1
  2. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/pyproject.toml +1 -1
  3. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23/src/altcodepro_polydb_python.egg-info}/PKG-INFO +1 -1
  4. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/altcodepro_polydb_python.egg-info/SOURCES.txt +1 -0
  5. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/__init__.py +1 -1
  6. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/databaseFactory.py +7 -0
  7. altcodepro_polydb_python-2.5.23/tests/test_create_unique_violation.py +48 -0
  8. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/LICENSE +0 -0
  9. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/MANIFEST.in +0 -0
  10. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/README.md +0 -0
  11. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/requirements-aws.txt +0 -0
  12. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/requirements-azure.txt +0 -0
  13. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/requirements-dev.txt +0 -0
  14. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/requirements-gcp.txt +0 -0
  15. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/requirements-generic.txt +0 -0
  16. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/requirements.txt +0 -0
  17. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/setup.cfg +0 -0
  18. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/setup.py +0 -0
  19. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/altcodepro_polydb_python.egg-info/dependency_links.txt +0 -0
  20. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/altcodepro_polydb_python.egg-info/requires.txt +0 -0
  21. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/altcodepro_polydb_python.egg-info/top_level.txt +0 -0
  22. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/PolyDB.py +0 -0
  23. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/adapters/AWSSecretsManagerAdapter.py +0 -0
  24. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/adapters/AzureBlobStorageAdapter.py +0 -0
  25. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/adapters/AzureFileStorageAdapter.py +0 -0
  26. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/adapters/AzureKeyVaultAdapter.py +0 -0
  27. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/adapters/AzureQueueAdapter.py +0 -0
  28. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/adapters/AzureTableStorageAdapter.py +0 -0
  29. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/adapters/BlockchainBlobAdapter.py +0 -0
  30. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/adapters/BlockchainFileAdapter.py +0 -0
  31. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/adapters/BlockchainKVAdapter.py +0 -0
  32. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/adapters/BlockchainQueueAdapter.py +0 -0
  33. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/adapters/DynamoDBAdapter.py +0 -0
  34. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/adapters/EFSAdapter.py +0 -0
  35. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/adapters/FirestoreAdapter.py +0 -0
  36. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/adapters/GCPFilestoreAdapter.py +0 -0
  37. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/adapters/GCPPubSubAdapter.py +0 -0
  38. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/adapters/GCPSecretManagerAdapter.py +0 -0
  39. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/adapters/GCPStorageAdapter.py +0 -0
  40. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/adapters/KafkaQueueAdapter.py +0 -0
  41. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/adapters/MongoDBAdapter.py +0 -0
  42. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/adapters/PostgreSQLAdapter.py +0 -0
  43. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/adapters/RabbitMQAdapter.py +0 -0
  44. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/adapters/S3Adapter.py +0 -0
  45. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/adapters/S3CompatibleAdapter.py +0 -0
  46. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/adapters/SQSAdapter.py +0 -0
  47. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/adapters/VaultAdapter.py +0 -0
  48. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/adapters/VercelBlobAdapter.py +0 -0
  49. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/adapters/VercelFileAdapter.py +0 -0
  50. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/adapters/VercelKVAdapter.py +0 -0
  51. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/adapters/VercelQueueAdapter.py +0 -0
  52. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/adapters/__init__.py +0 -0
  53. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/advanced_query.py +0 -0
  54. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/aio.py +0 -0
  55. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/audit/AuditStorage.py +0 -0
  56. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/audit/__init__.py +0 -0
  57. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/audit/context.py +0 -0
  58. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/audit/manager.py +0 -0
  59. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/audit/models.py +0 -0
  60. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/base/NoSQLKVAdapter.py +0 -0
  61. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/base/ObjectStorageAdapter.py +0 -0
  62. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/base/QueueAdapter.py +0 -0
  63. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/base/SecretsAdapter.py +0 -0
  64. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/base/SharedFilesAdapter.py +0 -0
  65. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/base/__init__.py +0 -0
  66. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/base/constraints.py +0 -0
  67. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/batch.py +0 -0
  68. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/cache.py +0 -0
  69. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/cloudDatabaseFactory.py +0 -0
  70. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/errors.py +0 -0
  71. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/json_safe.py +0 -0
  72. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/models.py +0 -0
  73. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/monitoring.py +0 -0
  74. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/multitenancy.py +0 -0
  75. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/observability/__init__.py +0 -0
  76. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/observability/logging.py +0 -0
  77. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/overflow_gc.py +0 -0
  78. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/py.typed +0 -0
  79. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/query.py +0 -0
  80. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/registry.py +0 -0
  81. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/retry.py +0 -0
  82. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/schema.py +0 -0
  83. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/security.py +0 -0
  84. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/services/__init__.py +0 -0
  85. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/services/compliance_service.py +0 -0
  86. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/services/security_service.py +0 -0
  87. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/types.py +0 -0
  88. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/utils.py +0 -0
  89. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/src/polydb/validation.py +0 -0
  90. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_advanced_query_injection.py +0 -0
  91. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_async_api.py +0 -0
  92. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_atomic_decrement.py +0 -0
  93. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_audit_chain_cutover.py +0 -0
  94. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_audit_hmac.py +0 -0
  95. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_aws.py +0 -0
  96. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_azure.py +0 -0
  97. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_azure_keyvault_credential.py +0 -0
  98. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_azure_queue_receipt_handle.py +0 -0
  99. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_azure_table_empty_filter.py +0 -0
  100. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_azure_table_id_property_persisted.py +0 -0
  101. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_azure_table_id_return_value_sanitization_mismatch.py +0 -0
  102. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_azure_table_large_record_patch_roundtrip.py +0 -0
  103. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_azure_table_self_heal.py +0 -0
  104. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_azure_table_unpack_entity.py +0 -0
  105. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_blockchain.py +0 -0
  106. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_blockchain_overflow_guard.py +0 -0
  107. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_cloud_factory.py +0 -0
  108. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_factory_wiring.py +0 -0
  109. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_field_encryption.py +0 -0
  110. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_gcp.py +0 -0
  111. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_kafka.py +0 -0
  112. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_like_pattern_escaping.py +0 -0
  113. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_mongodb.py +0 -0
  114. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_monitoring_health.py +0 -0
  115. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_multi_engine.py +0 -0
  116. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_multitenancy_ddl_injection.py +0 -0
  117. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_nosql_constraints.py +0 -0
  118. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_nosql_kv_overflow_write_path.py +0 -0
  119. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_nosql_operator_injection.py +0 -0
  120. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_observability.py +0 -0
  121. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_overflow_gc.py +0 -0
  122. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_postgresql.py +0 -0
  123. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_postgresql_deserialize_row.py +0 -0
  124. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_queue_extend_delay_cancel.py +0 -0
  125. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_rabbitmq.py +0 -0
  126. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_read_projection.py +0 -0
  127. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_redis_kv.py +0 -0
  128. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_schema_ddl_injection.py +0 -0
  129. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_secrets_aws.py +0 -0
  130. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_secrets_vault.py +0 -0
  131. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_session_vars.py +0 -0
  132. {altcodepro_polydb_python-2.5.22 → altcodepro_polydb_python-2.5.23}/tests/test_vercel.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: altcodepro-polydb-python
3
- Version: 2.5.22
3
+ Version: 2.5.23
4
4
  Summary: Production-ready multi-cloud database abstraction layer with connection pooling, retry logic, and thread safety
5
5
  Author: AltCodePro
6
6
  Project-URL: Homepage, https://github.com/altcodepro/polydb-python
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "altcodepro-polydb-python"
7
- version = "2.5.22"
7
+ version = "2.5.23"
8
8
  description = "Production-ready multi-cloud database abstraction layer with connection pooling, retry logic, and thread safety"
9
9
  readme = { file = "README.md", content-type = "text/markdown" }
10
10
  requires-python = ">=3.11"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: altcodepro-polydb-python
3
- Version: 2.5.22
3
+ Version: 2.5.23
4
4
  Summary: Production-ready multi-cloud database abstraction layer with connection pooling, retry logic, and thread safety
5
5
  Author: AltCodePro
6
6
  Project-URL: Homepage, https://github.com/altcodepro/polydb-python
@@ -102,6 +102,7 @@ tests/test_azure_table_unpack_entity.py
102
102
  tests/test_blockchain.py
103
103
  tests/test_blockchain_overflow_guard.py
104
104
  tests/test_cloud_factory.py
105
+ tests/test_create_unique_violation.py
105
106
  tests/test_factory_wiring.py
106
107
  tests/test_field_encryption.py
107
108
  tests/test_gcp.py
@@ -4,7 +4,7 @@ PolyDB - Enterprise Cloud-Independent Database Abstraction
4
4
  Full LINQ support, field-level audit, cache, soft delete, overflow storage
5
5
  """
6
6
 
7
- __version__ = "2.5.22"
7
+ __version__ = "2.5.23"
8
8
 
9
9
  from .aio import AsyncDatabaseFactory, AsyncPolyDB
10
10
  from .audit.context import AuditContext
@@ -457,6 +457,13 @@ class DatabaseFactory:
457
457
  conflict_cols = _parse_unique_violation_columns(exc)
458
458
  if not conflict_cols or not all(c in data for c in conflict_cols):
459
459
  raise
460
+ if not set(conflict_cols) <= {"id", "tenant_id"}:
461
+ # A declared-unique NON-key column: a second row with the same value is
462
+ # a violation, never a silent overwrite of the existing row.
463
+ from .base.constraints import UniqueConstraintError
464
+
465
+ col = conflict_cols[0]
466
+ raise UniqueConstraintError(col, data.get(col)) from exc
460
467
  where = {c: data[c] for c in conflict_cols}
461
468
  logger.warning(
462
469
  "insert %s hit unique violation on %s — falling through to update",
@@ -0,0 +1,48 @@
1
+ import pytest
2
+
3
+ from polydb.base.constraints import UniqueConstraintError
4
+ from polydb.databaseFactory import DatabaseFactory
5
+
6
+
7
+ class _Sql:
8
+ def __init__(self, msg):
9
+ self.msg, self.updated = msg, False
10
+
11
+ def insert(self, table, data, session_vars=None):
12
+ raise Exception(self.msg)
13
+
14
+ def update(self, *a, **k):
15
+ self.updated = True
16
+ return {"id": "1"}
17
+
18
+
19
+ class M:
20
+ __polydb__ = {"storage": "sql", "table": "users"}
21
+
22
+
23
+ def _factory(msg):
24
+ f = DatabaseFactory.__new__(DatabaseFactory)
25
+ sql = _Sql(msg)
26
+ f._inject_audit_fields = lambda d, is_create: d
27
+ f.encryption = None
28
+ f._enable_cache = False
29
+ f._cache = None
30
+ f.metrics = None
31
+ f._audit_write = lambda *a, **k: None
32
+ f._run = lambda fn: fn()
33
+ f._is_sql = lambda *a, **k: True
34
+ f._adapters_for = lambda *a, **k: type("A", (), {"sql": sql})()
35
+ return f, sql
36
+
37
+
38
+ def test_non_key_unique_violation_raises():
39
+ f, sql = _factory("duplicate key value violates unique constraint\nKey (email)=(a@b.c) already exists")
40
+ with pytest.raises(UniqueConstraintError):
41
+ f.create(M, {"email": "a@b.c", "id": "2"})
42
+ assert not sql.updated
43
+
44
+
45
+ def test_primary_key_conflict_still_overwrites():
46
+ f, sql = _factory("duplicate key value violates unique constraint\nKey (id, tenant_id)=(1, t) already exists")
47
+ f.create(M, {"id": "1", "tenant_id": "t", "x": 1})
48
+ assert sql.updated