altcodepro-polydb-python 2.3.4__tar.gz → 2.3.6__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.
- {altcodepro_polydb_python-2.3.4/src/altcodepro_polydb_python.egg-info → altcodepro_polydb_python-2.3.6}/PKG-INFO +1 -1
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/pyproject.toml +1 -1
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6/src/altcodepro_polydb_python.egg-info}/PKG-INFO +1 -1
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/AzureTableStorageAdapter.py +97 -60
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/base/NoSQLKVAdapter.py +3 -2
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/cloudDatabaseFactory.py +0 -2
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/databaseFactory.py +8 -4
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/LICENSE +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/MANIFEST.in +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/README.md +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/example_usage.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/requirements-aws.txt +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/requirements-azure.txt +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/requirements-dev.txt +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/requirements-gcp.txt +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/requirements-generic.txt +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/requirements.txt +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/setup.cfg +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/setup.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/altcodepro_polydb_python.egg-info/SOURCES.txt +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/altcodepro_polydb_python.egg-info/dependency_links.txt +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/altcodepro_polydb_python.egg-info/requires.txt +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/altcodepro_polydb_python.egg-info/top_level.txt +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/PolyDB.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/__init__.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/AzureBlobStorageAdapter.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/AzureFileStorageAdapter.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/AzureQueueAdapter.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/BlockchainBlobAdapter.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/BlockchainKVAdapter.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/BlockchainQueueAdapter.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/DynamoDBAdapter.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/EFSAdapter.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/FirestoreAdapter.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/GCPPubSubAdapter.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/GCPStorageAdapter.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/MongoDBAdapter.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/PostgreSQLAdapter.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/S3Adapter.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/S3CompatibleAdapter.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/SQSAdapter.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/VercelBlobAdapter.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/VercelKVAdapter.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/VercelQueueAdapter.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/__init__.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/advanced_query.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/audit/AuditStorage.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/audit/__init__.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/audit/context.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/audit/manager.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/audit/models.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/base/ObjectStorageAdapter.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/base/QueueAdapter.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/base/SharedFilesAdapter.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/base/__init__.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/batch.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/cache.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/decorators.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/errors.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/json_safe.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/models.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/monitoring.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/multitenancy.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/py.typed +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/query.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/registry.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/retry.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/schema.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/security.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/types.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/utils.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/validation.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/tests/test_aws.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/tests/test_azure.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/tests/test_blockchain.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/tests/test_cloud_factory.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/tests/test_gcp.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/tests/test_mongodb.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/tests/test_multi_engine.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/tests/test_postgresql.py +0 -0
- {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/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.3.
|
|
3
|
+
Version: 2.3.6
|
|
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.3.
|
|
7
|
+
version = "2.3.6"
|
|
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.3.
|
|
3
|
+
Version: 2.3.6
|
|
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
|
|
@@ -49,7 +49,6 @@ class AzureTableStorageAdapter(NoSQLKVAdapter):
|
|
|
49
49
|
self,
|
|
50
50
|
partition_config: Optional[PartitionConfig] = None,
|
|
51
51
|
connection_string: str = "",
|
|
52
|
-
table_name="",
|
|
53
52
|
container_name="",
|
|
54
53
|
):
|
|
55
54
|
super().__init__(partition_config)
|
|
@@ -57,14 +56,12 @@ class AzureTableStorageAdapter(NoSQLKVAdapter):
|
|
|
57
56
|
self.connection_string = (
|
|
58
57
|
connection_string or os.getenv("AZURE_STORAGE_CONNECTION_STRING") or ""
|
|
59
58
|
)
|
|
60
|
-
self.table_name = table_name or os.getenv("AZURE_TABLE_NAME", "defaulttable") or ""
|
|
61
59
|
self.container_name = container_name or os.getenv("AZURE_CONTAINER_NAME", "overflow") or ""
|
|
62
60
|
|
|
63
61
|
if not self.connection_string:
|
|
64
62
|
raise ConnectionError("AZURE_STORAGE_CONNECTION_STRING must be set")
|
|
65
63
|
|
|
66
|
-
self._client = None
|
|
67
|
-
self._table_client = None
|
|
64
|
+
self._client: Any = None
|
|
68
65
|
self._blob_service = None
|
|
69
66
|
self._client_lock = threading.Lock()
|
|
70
67
|
self._initialize_client()
|
|
@@ -77,13 +74,6 @@ class AzureTableStorageAdapter(NoSQLKVAdapter):
|
|
|
77
74
|
with self._client_lock:
|
|
78
75
|
if not self._client:
|
|
79
76
|
self._client = TableServiceClient.from_connection_string(self.connection_string)
|
|
80
|
-
self._table_client = self._client.get_table_client(self.table_name)
|
|
81
|
-
|
|
82
|
-
try:
|
|
83
|
-
self._client.create_table_if_not_exists(self.table_name)
|
|
84
|
-
except Exception:
|
|
85
|
-
pass
|
|
86
|
-
|
|
87
77
|
self._blob_service = BlobServiceClient.from_connection_string(
|
|
88
78
|
self.connection_string
|
|
89
79
|
)
|
|
@@ -213,7 +203,6 @@ class AzureTableStorageAdapter(NoSQLKVAdapter):
|
|
|
213
203
|
def _pack_entity(self, model: type, pk: str, rk: str, data: JsonDict) -> JsonDict:
|
|
214
204
|
entity: JsonDict = {"PartitionKey": pk, "RowKey": rk}
|
|
215
205
|
|
|
216
|
-
# ✅ model isolation
|
|
217
206
|
entity[_MODEL_FIELD] = model.__qualname__
|
|
218
207
|
|
|
219
208
|
keymap: Dict[str, str] = {}
|
|
@@ -324,28 +313,86 @@ class AzureTableStorageAdapter(NoSQLKVAdapter):
|
|
|
324
313
|
# -----------------------------
|
|
325
314
|
# Required NoSQLKVAdapter hooks
|
|
326
315
|
# -----------------------------
|
|
316
|
+
def _sanitize_table_name(self, name: str) -> str:
|
|
317
|
+
"""Convert collection_name to valid Azure Table name (alphanumeric only)."""
|
|
318
|
+
if not name:
|
|
319
|
+
return "defaulttable"
|
|
320
|
+
|
|
321
|
+
# Remove all invalid characters, keep only letters and numbers
|
|
322
|
+
sanitized = re.sub(r"[^a-zA-Z0-9]", "", name)
|
|
323
|
+
|
|
324
|
+
# Must start with a letter
|
|
325
|
+
if sanitized and sanitized[0].isdigit():
|
|
326
|
+
sanitized = "t" + sanitized
|
|
327
|
+
|
|
328
|
+
# Must be 3-63 characters
|
|
329
|
+
if len(sanitized) < 3:
|
|
330
|
+
sanitized = sanitized + "table"
|
|
331
|
+
if len(sanitized) > 63:
|
|
332
|
+
sanitized = sanitized[:63]
|
|
333
|
+
|
|
334
|
+
return sanitized.lower()
|
|
335
|
+
|
|
336
|
+
def _get_table_name(self, model: type) -> str:
|
|
337
|
+
"""
|
|
338
|
+
Extract collection_name from UDL model definition.
|
|
339
|
+
"""
|
|
340
|
+
# Primary source: UDL definition
|
|
341
|
+
definition = getattr(model, "__udl_definition__", None)
|
|
342
|
+
if definition:
|
|
343
|
+
metadata = getattr(definition, "x_metadata", {}) or {}
|
|
344
|
+
collection_name = metadata.get("collection_name")
|
|
345
|
+
if collection_name:
|
|
346
|
+
return self._sanitize_table_name(collection_name)
|
|
347
|
+
|
|
348
|
+
# Fallback: __polydb__ metadata
|
|
349
|
+
polydb_meta = getattr(model, "__polydb__", None)
|
|
350
|
+
if isinstance(polydb_meta, dict):
|
|
351
|
+
collection = polydb_meta.get("collection") or polydb_meta.get("collection_name")
|
|
352
|
+
if collection:
|
|
353
|
+
return self._sanitize_table_name(str(collection))
|
|
354
|
+
|
|
355
|
+
# Last resort
|
|
356
|
+
return os.getenv("AZURE_TABLE_NAME", "defaulttable") or "defaulttable"
|
|
357
|
+
|
|
358
|
+
def _get_table_client(self, model: type):
|
|
359
|
+
"""
|
|
360
|
+
Get table client + automatically create the table if it doesn't exist.
|
|
361
|
+
This is the recommended pattern for Azure Table Storage.
|
|
362
|
+
"""
|
|
363
|
+
table_name = self._get_table_name(model)
|
|
364
|
+
|
|
365
|
+
try:
|
|
366
|
+
# This is the key call - creates the table if missing
|
|
367
|
+
self._client.create_table_if_not_exists(table_name)
|
|
368
|
+
logger.info(f"✅ Azure Table ensured/created: {table_name}")
|
|
369
|
+
except Exception as e:
|
|
370
|
+
# TableAlreadyExists is normal and safe to ignore
|
|
371
|
+
if "TableAlreadyExists" not in str(e) and "already exists" not in str(e).lower():
|
|
372
|
+
logger.warning(f"Could not create table {table_name}: {e}")
|
|
373
|
+
|
|
374
|
+
# Now return the client
|
|
375
|
+
return self._client.get_table_client(table_name=table_name)
|
|
327
376
|
|
|
328
377
|
@retry(max_attempts=3, delay=1.0, exceptions=(NoSQLError,))
|
|
329
378
|
def _put_raw(self, model: type, pk: str, rk: str, data: JsonDict) -> JsonDict:
|
|
330
379
|
try:
|
|
331
|
-
|
|
332
|
-
raise NoSQLError("Azure Table client not initialized")
|
|
333
|
-
|
|
380
|
+
self._table_client = self._get_table_client(model)
|
|
334
381
|
safe_pk = self._sanitize_pk_rk(pk)
|
|
335
382
|
safe_rk = self._sanitize_pk_rk(rk)
|
|
336
383
|
|
|
384
|
+
# Pack entity (encoded for Azure Table)
|
|
337
385
|
entity = self._pack_entity(model, safe_pk, safe_rk, data)
|
|
338
386
|
|
|
339
387
|
# ---------------------------------------------------
|
|
340
|
-
# SIZE ESTIMATION (
|
|
388
|
+
# SIZE ESTIMATION (use ORIGINAL payload, not packed)
|
|
341
389
|
# ---------------------------------------------------
|
|
342
|
-
MAX_PROPERTY_CHARS = 30 * 1024 # ~30K
|
|
343
|
-
MAX_ENTITY_SIZE = 40 * 1024 # conservative total
|
|
390
|
+
MAX_PROPERTY_CHARS = 30 * 1024 # ~30K safe under UTF-16 32K limit
|
|
391
|
+
MAX_ENTITY_SIZE = 40 * 1024 # conservative total threshold
|
|
344
392
|
|
|
345
393
|
def _is_large_string(val: Any) -> bool:
|
|
346
394
|
return isinstance(val, str) and len(val) > MAX_PROPERTY_CHARS
|
|
347
395
|
|
|
348
|
-
# Check if any property exceeds safe char limit
|
|
349
396
|
has_large_property = False
|
|
350
397
|
large_key = None
|
|
351
398
|
|
|
@@ -358,42 +405,40 @@ class AzureTableStorageAdapter(NoSQLKVAdapter):
|
|
|
358
405
|
)
|
|
359
406
|
break
|
|
360
407
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
408
|
+
payload_json = json.dumps(data, default=json_safe)
|
|
409
|
+
payload_bytes = payload_json.encode("utf-8")
|
|
410
|
+
payload_size = len(payload_bytes)
|
|
364
411
|
|
|
365
|
-
force_blob = has_large_property or
|
|
412
|
+
force_blob = has_large_property or payload_size > MAX_ENTITY_SIZE
|
|
366
413
|
|
|
414
|
+
# ---------------------------------------------------
|
|
415
|
+
# BLOB OVERFLOW PATH
|
|
416
|
+
# ---------------------------------------------------
|
|
367
417
|
if force_blob:
|
|
368
418
|
logger.info(
|
|
369
419
|
f"Entity exceeds safe limits → using blob storage "
|
|
370
|
-
f"(size={
|
|
420
|
+
f"(size={payload_size // 1024} KB, large_key={large_key})"
|
|
371
421
|
)
|
|
372
422
|
|
|
373
|
-
|
|
374
|
-
# STORE FULL ENTITY IN BLOB
|
|
375
|
-
# ---------------------------------------------------
|
|
376
|
-
full_payload_bytes = entity_json.encode("utf-8")
|
|
377
|
-
checksum = hashlib.md5(full_payload_bytes).hexdigest()
|
|
423
|
+
checksum = hashlib.md5(payload_bytes).hexdigest()
|
|
378
424
|
blob_key = self._blob_key(safe_pk, safe_rk, checksum)
|
|
379
425
|
|
|
380
|
-
|
|
426
|
+
# Upload ORIGINAL payload (not packed entity)
|
|
427
|
+
self._blob_upload(blob_key, payload_bytes)
|
|
381
428
|
|
|
382
|
-
#
|
|
383
|
-
# STORE REFERENCE IN TABLE
|
|
384
|
-
# ---------------------------------------------------
|
|
429
|
+
# Build reference entity
|
|
385
430
|
reference_entity = {
|
|
386
431
|
"PartitionKey": safe_pk,
|
|
387
432
|
"RowKey": safe_rk,
|
|
388
433
|
_MODEL_FIELD: model.__qualname__,
|
|
389
434
|
"_overflow": True,
|
|
390
435
|
"_blob_key": blob_key,
|
|
391
|
-
"_size":
|
|
436
|
+
"_size": payload_size,
|
|
392
437
|
"_checksum": checksum,
|
|
393
438
|
"__keymap__": entity.get("__keymap__", "{}"),
|
|
394
439
|
}
|
|
395
440
|
|
|
396
|
-
# Keep only small
|
|
441
|
+
# Keep only small queryable fields
|
|
397
442
|
for k, v in entity.items():
|
|
398
443
|
if k in ("PartitionKey", "RowKey", "__keymap__", _MODEL_FIELD):
|
|
399
444
|
continue
|
|
@@ -407,32 +452,31 @@ class AzureTableStorageAdapter(NoSQLKVAdapter):
|
|
|
407
452
|
|
|
408
453
|
elif isinstance(v, datetime):
|
|
409
454
|
reference_entity[k] = v
|
|
410
|
-
|
|
455
|
+
self._table_client = self._get_table_client(model)
|
|
411
456
|
self._table_client.upsert_entity(reference_entity)
|
|
412
457
|
|
|
413
|
-
logger.info(f"Stored in blob: {blob_key} ({
|
|
458
|
+
logger.info(f"Stored in blob: {blob_key} ({payload_size // 1024} KB)")
|
|
414
459
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
460
|
+
restored = self._unpack_entity(entity)
|
|
461
|
+
restored["_overflow"] = True
|
|
462
|
+
restored["_blob_key"] = blob_key
|
|
463
|
+
restored["_checksum"] = checksum
|
|
464
|
+
restored["_size"] = payload_size
|
|
465
|
+
restored["id"] = safe_rk
|
|
466
|
+
|
|
467
|
+
return restored
|
|
422
468
|
|
|
423
469
|
# ---------------------------------------------------
|
|
424
470
|
# NORMAL TABLE STORAGE
|
|
425
471
|
# ---------------------------------------------------
|
|
426
472
|
self._table_client.upsert_entity(entity)
|
|
427
473
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
}
|
|
474
|
+
restored = self._unpack_entity(entity)
|
|
475
|
+
restored["id"] = safe_rk
|
|
476
|
+
|
|
477
|
+
return restored
|
|
433
478
|
|
|
434
479
|
except Exception as e:
|
|
435
|
-
# Do NOT retry deterministic size errors
|
|
436
480
|
if "PropertyValueTooLarge" in str(e):
|
|
437
481
|
raise NoSQLError("Azure Table limit exceeded → must use blob overflow") from e
|
|
438
482
|
|
|
@@ -441,12 +485,9 @@ class AzureTableStorageAdapter(NoSQLKVAdapter):
|
|
|
441
485
|
@retry(max_attempts=3, delay=1.0, exceptions=(NoSQLError,))
|
|
442
486
|
def _get_raw(self, model: type, pk: str, rk: str) -> Optional[JsonDict]:
|
|
443
487
|
try:
|
|
444
|
-
if not self._table_client:
|
|
445
|
-
return None
|
|
446
|
-
|
|
447
488
|
safe_pk = self._sanitize_pk_rk(pk)
|
|
448
489
|
safe_rk = self._sanitize_pk_rk(rk)
|
|
449
|
-
|
|
490
|
+
self._table_client = self._get_table_client(model)
|
|
450
491
|
entity = self._table_client.get_entity(safe_pk, safe_rk)
|
|
451
492
|
entity_dict = dict(entity)
|
|
452
493
|
|
|
@@ -491,9 +532,7 @@ class AzureTableStorageAdapter(NoSQLKVAdapter):
|
|
|
491
532
|
self, model: type, filters: Dict[str, Any], limit: Optional[int]
|
|
492
533
|
) -> List[JsonDict]:
|
|
493
534
|
try:
|
|
494
|
-
|
|
495
|
-
return []
|
|
496
|
-
|
|
535
|
+
self._table_client = self._get_table_client(model)
|
|
497
536
|
# always enforce model filter
|
|
498
537
|
eff_filters = dict(filters or {})
|
|
499
538
|
eff_filters[_MODEL_FIELD] = model.__qualname__
|
|
@@ -580,9 +619,7 @@ class AzureTableStorageAdapter(NoSQLKVAdapter):
|
|
|
580
619
|
@retry(max_attempts=3, delay=1.0, exceptions=(NoSQLError,))
|
|
581
620
|
def _delete_raw(self, model: type, pk: str, rk: str, etag: Optional[str]) -> JsonDict:
|
|
582
621
|
try:
|
|
583
|
-
|
|
584
|
-
return {"deleted": False}
|
|
585
|
-
|
|
622
|
+
self._table_client = self._get_table_client(model)
|
|
586
623
|
safe_pk = self._sanitize_pk_rk(pk)
|
|
587
624
|
safe_rk = self._sanitize_pk_rk(rk)
|
|
588
625
|
|
{altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/base/NoSQLKVAdapter.py
RENAMED
|
@@ -36,8 +36,8 @@ class NoSQLKVAdapter:
|
|
|
36
36
|
def _get_pk_rk(self, model: type, data: JsonDict) -> Tuple[str, str]:
|
|
37
37
|
"""Extract PK/RK from model metadata"""
|
|
38
38
|
meta = getattr(model, "__polydb__", {})
|
|
39
|
-
pk_field = meta.get("pk_field", "
|
|
40
|
-
rk_field = meta.get("rk_field")
|
|
39
|
+
pk_field = meta.get("pk_field") or meta.get("partition_key", "tenant_id")
|
|
40
|
+
rk_field = meta.get("rk_field") or meta.get("sort_key", "id")
|
|
41
41
|
|
|
42
42
|
if self.partition_config:
|
|
43
43
|
try:
|
|
@@ -257,6 +257,7 @@ class NoSQLKVAdapter:
|
|
|
257
257
|
if isinstance(entity_id, dict):
|
|
258
258
|
pk = entity_id.get("partition_key") or entity_id.get("pk")
|
|
259
259
|
rk = entity_id.get("row_key") or entity_id.get("rk") or entity_id.get("id")
|
|
260
|
+
|
|
260
261
|
else:
|
|
261
262
|
pk, rk = self._get_pk_rk(model, {"id": entity_id})
|
|
262
263
|
|
{altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/cloudDatabaseFactory.py
RENAMED
|
@@ -234,7 +234,6 @@ class CloudDatabaseFactory:
|
|
|
234
234
|
from .adapters.AzureTableStorageAdapter import AzureTableStorageAdapter
|
|
235
235
|
|
|
236
236
|
connection_string = ""
|
|
237
|
-
table_name = ""
|
|
238
237
|
container_name = ""
|
|
239
238
|
|
|
240
239
|
if isinstance(cfg, AzureTableConfig):
|
|
@@ -245,7 +244,6 @@ class CloudDatabaseFactory:
|
|
|
245
244
|
instance = AzureTableStorageAdapter(
|
|
246
245
|
partition_config=partition_config,
|
|
247
246
|
connection_string=connection_string,
|
|
248
|
-
table_name=table_name,
|
|
249
247
|
container_name=container_name,
|
|
250
248
|
)
|
|
251
249
|
|
{altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/databaseFactory.py
RENAMED
|
@@ -20,6 +20,10 @@ from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Union
|
|
|
20
20
|
|
|
21
21
|
from tenacity import retry, stop_after_attempt, wait_exponential
|
|
22
22
|
|
|
23
|
+
from .adapters.PostgreSQLAdapter import PostgreSQLAdapter
|
|
24
|
+
|
|
25
|
+
from .base.NoSQLKVAdapter import NoSQLKVAdapter
|
|
26
|
+
|
|
23
27
|
from .batch import BatchOperations
|
|
24
28
|
from .cache import CacheWarmer, RedisCacheEngine
|
|
25
29
|
from .monitoring import HealthCheck, MetricsCollector, PerformanceMonitor
|
|
@@ -81,8 +85,8 @@ class EngineOverride:
|
|
|
81
85
|
|
|
82
86
|
@dataclass
|
|
83
87
|
class _ResolvedAdapters:
|
|
84
|
-
sql:
|
|
85
|
-
nosql:
|
|
88
|
+
sql: PostgreSQLAdapter
|
|
89
|
+
nosql: NoSQLKVAdapter
|
|
86
90
|
engine_name: str
|
|
87
91
|
|
|
88
92
|
|
|
@@ -281,14 +285,14 @@ class DatabaseFactory:
|
|
|
281
285
|
return self._engine_by_name[name]
|
|
282
286
|
|
|
283
287
|
@property
|
|
284
|
-
def _sql(self) ->
|
|
288
|
+
def _sql(self) -> PostgreSQLAdapter:
|
|
285
289
|
for e in self._engines:
|
|
286
290
|
if e.is_default_sql:
|
|
287
291
|
return e.sql()
|
|
288
292
|
return self._engines[0].sql()
|
|
289
293
|
|
|
290
294
|
@property
|
|
291
|
-
def _nosql(self) ->
|
|
295
|
+
def _nosql(self) -> NoSQLKVAdapter:
|
|
292
296
|
for e in self._engines:
|
|
293
297
|
if e.is_default_nosql:
|
|
294
298
|
return e.nosql()
|
|
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
|
|
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
|
{altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/EFSAdapter.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/S3Adapter.py
RENAMED
|
File without changes
|
|
File without changes
|
{altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/SQSAdapter.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/__init__.py
RENAMED
|
File without changes
|
{altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/advanced_query.py
RENAMED
|
File without changes
|
{altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/audit/AuditStorage.py
RENAMED
|
File without changes
|
{altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/audit/__init__.py
RENAMED
|
File without changes
|
{altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/audit/context.py
RENAMED
|
File without changes
|
{altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/audit/manager.py
RENAMED
|
File without changes
|
{altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/audit/models.py
RENAMED
|
File without changes
|
|
File without changes
|
{altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/base/QueueAdapter.py
RENAMED
|
File without changes
|
|
File without changes
|
{altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/base/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/multitenancy.py
RENAMED
|
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
|
{altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/tests/test_cloud_factory.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/tests/test_multi_engine.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|