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.
Files changed (81) hide show
  1. {altcodepro_polydb_python-2.3.4/src/altcodepro_polydb_python.egg-info → altcodepro_polydb_python-2.3.6}/PKG-INFO +1 -1
  2. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/pyproject.toml +1 -1
  3. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6/src/altcodepro_polydb_python.egg-info}/PKG-INFO +1 -1
  4. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/AzureTableStorageAdapter.py +97 -60
  5. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/base/NoSQLKVAdapter.py +3 -2
  6. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/cloudDatabaseFactory.py +0 -2
  7. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/databaseFactory.py +8 -4
  8. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/LICENSE +0 -0
  9. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/MANIFEST.in +0 -0
  10. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/README.md +0 -0
  11. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/example_usage.py +0 -0
  12. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/requirements-aws.txt +0 -0
  13. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/requirements-azure.txt +0 -0
  14. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/requirements-dev.txt +0 -0
  15. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/requirements-gcp.txt +0 -0
  16. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/requirements-generic.txt +0 -0
  17. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/requirements.txt +0 -0
  18. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/setup.cfg +0 -0
  19. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/setup.py +0 -0
  20. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/altcodepro_polydb_python.egg-info/SOURCES.txt +0 -0
  21. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/altcodepro_polydb_python.egg-info/dependency_links.txt +0 -0
  22. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/altcodepro_polydb_python.egg-info/requires.txt +0 -0
  23. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/altcodepro_polydb_python.egg-info/top_level.txt +0 -0
  24. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/PolyDB.py +0 -0
  25. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/__init__.py +0 -0
  26. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/AzureBlobStorageAdapter.py +0 -0
  27. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/AzureFileStorageAdapter.py +0 -0
  28. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/AzureQueueAdapter.py +0 -0
  29. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/BlockchainBlobAdapter.py +0 -0
  30. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/BlockchainKVAdapter.py +0 -0
  31. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/BlockchainQueueAdapter.py +0 -0
  32. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/DynamoDBAdapter.py +0 -0
  33. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/EFSAdapter.py +0 -0
  34. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/FirestoreAdapter.py +0 -0
  35. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/GCPPubSubAdapter.py +0 -0
  36. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/GCPStorageAdapter.py +0 -0
  37. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/MongoDBAdapter.py +0 -0
  38. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/PostgreSQLAdapter.py +0 -0
  39. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/S3Adapter.py +0 -0
  40. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/S3CompatibleAdapter.py +0 -0
  41. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/SQSAdapter.py +0 -0
  42. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/VercelBlobAdapter.py +0 -0
  43. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/VercelKVAdapter.py +0 -0
  44. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/VercelQueueAdapter.py +0 -0
  45. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/adapters/__init__.py +0 -0
  46. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/advanced_query.py +0 -0
  47. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/audit/AuditStorage.py +0 -0
  48. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/audit/__init__.py +0 -0
  49. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/audit/context.py +0 -0
  50. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/audit/manager.py +0 -0
  51. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/audit/models.py +0 -0
  52. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/base/ObjectStorageAdapter.py +0 -0
  53. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/base/QueueAdapter.py +0 -0
  54. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/base/SharedFilesAdapter.py +0 -0
  55. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/base/__init__.py +0 -0
  56. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/batch.py +0 -0
  57. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/cache.py +0 -0
  58. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/decorators.py +0 -0
  59. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/errors.py +0 -0
  60. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/json_safe.py +0 -0
  61. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/models.py +0 -0
  62. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/monitoring.py +0 -0
  63. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/multitenancy.py +0 -0
  64. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/py.typed +0 -0
  65. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/query.py +0 -0
  66. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/registry.py +0 -0
  67. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/retry.py +0 -0
  68. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/schema.py +0 -0
  69. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/security.py +0 -0
  70. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/types.py +0 -0
  71. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/utils.py +0 -0
  72. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/src/polydb/validation.py +0 -0
  73. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/tests/test_aws.py +0 -0
  74. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/tests/test_azure.py +0 -0
  75. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/tests/test_blockchain.py +0 -0
  76. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/tests/test_cloud_factory.py +0 -0
  77. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/tests/test_gcp.py +0 -0
  78. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/tests/test_mongodb.py +0 -0
  79. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/tests/test_multi_engine.py +0 -0
  80. {altcodepro_polydb_python-2.3.4 → altcodepro_polydb_python-2.3.6}/tests/test_postgresql.py +0 -0
  81. {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.4
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.4"
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.4
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
- if not self._table_client:
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 (Azure uses UTF-16 for strings)
388
+ # SIZE ESTIMATION (use ORIGINAL payload, not packed)
341
389
  # ---------------------------------------------------
342
- MAX_PROPERTY_CHARS = 30 * 1024 # ~30K chars safe under 32K limit
343
- MAX_ENTITY_SIZE = 40 * 1024 # conservative total entity threshold
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
- # Check full entity size
362
- entity_json = json.dumps(entity, default=json_safe)
363
- entity_size = len(entity_json.encode("utf-8"))
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 entity_size > MAX_ENTITY_SIZE
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={entity_size//1024} KB, large_key={large_key})"
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
- self._blob_upload(blob_key, full_payload_bytes)
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": len(full_payload_bytes),
436
+ "_size": payload_size,
392
437
  "_checksum": checksum,
393
438
  "__keymap__": entity.get("__keymap__", "{}"),
394
439
  }
395
440
 
396
- # Keep only small searchable fields
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} ({len(full_payload_bytes)//1024} KB)")
458
+ logger.info(f"Stored in blob: {blob_key} ({payload_size // 1024} KB)")
414
459
 
415
- return {
416
- "PartitionKey": safe_pk,
417
- "RowKey": safe_rk,
418
- "_overflow": True,
419
- "_blob_key": blob_key,
420
- "id": safe_rk,
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
- return {
429
- "PartitionKey": safe_pk,
430
- "RowKey": safe_rk,
431
- "id": safe_rk,
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
- if not self._table_client:
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
- if not self._table_client:
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
 
@@ -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", "id")
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
 
@@ -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
 
@@ -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: Any
85
- nosql: Any
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) -> Any:
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) -> Any:
295
+ def _nosql(self) -> NoSQLKVAdapter:
292
296
  for e in self._engines:
293
297
  if e.is_default_nosql:
294
298
  return e.nosql()