altcodepro-polydb-python 2.5.11__py3-none-any.whl → 2.5.18__py3-none-any.whl

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: altcodepro-polydb-python
3
- Version: 2.5.11
3
+ Version: 2.5.18
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
@@ -1,6 +1,6 @@
1
- altcodepro_polydb_python-2.5.11.dist-info/licenses/LICENSE,sha256=9X8GLocsBwy-5aR5JGOt2SAMDDPs9Qv-YnqmHBHOXrw,1067
1
+ altcodepro_polydb_python-2.5.18.dist-info/licenses/LICENSE,sha256=9X8GLocsBwy-5aR5JGOt2SAMDDPs9Qv-YnqmHBHOXrw,1067
2
2
  polydb/PolyDB.py,sha256=_6YaeX7-9G9fK6hj4I-JbsfdVIwAotx7E_4IJqdGcjQ,23536
3
- polydb/__init__.py,sha256=pQrfRlbXkMQyaRZuHlNrJEhVArOQADemoXqFQYxqq1c,1876
3
+ polydb/__init__.py,sha256=p9dAWJ3-JNCeHgb1WzkTMkuURFQy-RUNI4QeD9zBVf0,1876
4
4
  polydb/advanced_query.py,sha256=M9sKmdF2PsLZWSc-zKpXRcwaDumbYYTsibmL_IAjKP4,6931
5
5
  polydb/aio.py,sha256=0EQ6hXrf6N4If6pcht6L9B7qz0PU2yGahyGzEss2eQg,8340
6
6
  polydb/batch.py,sha256=ulvSdtVeofsOiVNVAQwrE9tXIIaP8fjiW1Sbpz3dcv0,4093
@@ -17,7 +17,7 @@ polydb/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
17
  polydb/query.py,sha256=17V-e70df9NaiaUYPN5_MQz7O0vRdBaCR4xlZU3F2YE,8511
18
18
  polydb/registry.py,sha256=RD_elvFXcmhTdCyZDm2f3ej0elxqhArnSJ2aO9k5VCU,2352
19
19
  polydb/retry.py,sha256=0rILN3-auuzomOXPveIAvgnhBbtXXGNKvxg6qcRgN4g,4926
20
- polydb/schema.py,sha256=7Nz1EHcl4FWpdfsTonoFpMrkb0HcWKOiBpW_TTpPlrE,6447
20
+ polydb/schema.py,sha256=jDjUuOaz2DANddhMTSxMwZ1MB-EOVDgDrCHbxkp-iM4,7712
21
21
  polydb/security.py,sha256=IF8Oh6Y6YU9xSjz_pi6lotfJuhIUJgP7cCmpGc8bz6w,20158
22
22
  polydb/types.py,sha256=Z1R8z06-WYoh7cnn0oE_2N10v6hdx7VJD7TNL5ynWhA,2667
23
23
  polydb/utils.py,sha256=YS-BhBpQfQsOf-z_i1zw_kvMYwJ05Zn1S2Xt6K4_2ak,3171
@@ -27,7 +27,7 @@ polydb/adapters/AzureBlobStorageAdapter.py,sha256=ZTovSC_CVduGnIoIQUUeNxDQlf-IbF
27
27
  polydb/adapters/AzureFileStorageAdapter.py,sha256=VZNprqlBXCuWUgtqClNT-NrQmRf-XFYEiRA2BLbf-Sc,7046
28
28
  polydb/adapters/AzureKeyVaultAdapter.py,sha256=7Nizs9qGh6cytbZNgV78f-odOO4Z6sUlwm_HQYqt-pc,2339
29
29
  polydb/adapters/AzureQueueAdapter.py,sha256=1e2HiFvkH1BI_JKS9fgays2J41kOfH6pUPbZDe1wmWA,13658
30
- polydb/adapters/AzureTableStorageAdapter.py,sha256=Ff4xbpqJKMN6EdMcsR9WMMFkMaceXmzb32v-nIiqVRo,33340
30
+ polydb/adapters/AzureTableStorageAdapter.py,sha256=WsaivYIPYv0ScuuaJtCLDaBle9zTrwMsQ2ml_02m0dk,34043
31
31
  polydb/adapters/BlockchainBlobAdapter.py,sha256=85H3PxiL9rGDtK-sTsW26jOuUQ_SGipLSPu3s-juZRA,3298
32
32
  polydb/adapters/BlockchainFileAdapter.py,sha256=lZbRyC18LghZcVh5QyIRP7tCftNF_Ep1FKG0KRkvMmk,8335
33
33
  polydb/adapters/BlockchainKVAdapter.py,sha256=pvpiPshDE5Z_IRcSBvBwgAXT--86lgEyIsEUeqcmoE8,5422
@@ -41,7 +41,7 @@ polydb/adapters/GCPSecretManagerAdapter.py,sha256=ZFL2FGh7uTaBfpUo-HKXZAxS9s-w0w
41
41
  polydb/adapters/GCPStorageAdapter.py,sha256=ce7vCOW46XbdjiupAgMgkgopy3IV7nM5YvIwEhLIKvQ,7133
42
42
  polydb/adapters/KafkaQueueAdapter.py,sha256=_FrcI6JLIoJHPOtSIL2q1uXHeao9ZkGMyUnT68p36HQ,14329
43
43
  polydb/adapters/MongoDBAdapter.py,sha256=Rs1L9gbGj2hHiJuz62Ms7uRK6sppHc1aIO4MNrtZ3Zo,11750
44
- polydb/adapters/PostgreSQLAdapter.py,sha256=NPmf-s4UMGUFtQsqlAufrGtWI0N1vDvBYNSGAFEnBW4,45040
44
+ polydb/adapters/PostgreSQLAdapter.py,sha256=xbRLYMt6w0eAwPOagb5lt6i3gJX9orzNOU8dX9JiBl0,47976
45
45
  polydb/adapters/RabbitMQAdapter.py,sha256=rMeqWXQmnfRX_pakaZX6q1bHkZ8E1boQ_Pkj3byl4E4,19903
46
46
  polydb/adapters/S3Adapter.py,sha256=ZkMR91u1e4SnrXSUbV3siKFYdw0ykYHJfyP2cUJUVso,7564
47
47
  polydb/adapters/S3CompatibleAdapter.py,sha256=Qj9SUtCnOFNL9c05wNVjICxZacFCnee-Wn_-hh0lwwE,7006
@@ -68,7 +68,7 @@ polydb/observability/logging.py,sha256=FWYky-Mg1IhLpwuaKuLIsTROBZc9bI_-AO-y53nvE
68
68
  polydb/services/__init__.py,sha256=Zs3PZbHutgPdPjJatygvtYvKPZMBt1qKSqESbFRPHJs,241
69
69
  polydb/services/compliance_service.py,sha256=UMjCbQ4z13-w6pT4RLQpMi_u1zbShEpiz8kawA2WsSU,5075
70
70
  polydb/services/security_service.py,sha256=gGo_j9Ca_rgvxS7s6C5JUktugQBj1PV1DXpMnOYAGbI,4992
71
- altcodepro_polydb_python-2.5.11.dist-info/METADATA,sha256=lCtZ5IOmpMi28p20Tifh50NbHIVHjFrOz8OC-7LthrY,15599
72
- altcodepro_polydb_python-2.5.11.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
73
- altcodepro_polydb_python-2.5.11.dist-info/top_level.txt,sha256=WgLFWJoYjUhwvyPxJFl6jYLrVFuBJDX3OABf4ocwk_E,7
74
- altcodepro_polydb_python-2.5.11.dist-info/RECORD,,
71
+ altcodepro_polydb_python-2.5.18.dist-info/METADATA,sha256=Lp7sTTTNxyekuXaPMLtDyslq8Mv-hA65LAxy_WeGKoI,15599
72
+ altcodepro_polydb_python-2.5.18.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
73
+ altcodepro_polydb_python-2.5.18.dist-info/top_level.txt,sha256=WgLFWJoYjUhwvyPxJFl6jYLrVFuBJDX3OABf4ocwk_E,7
74
+ altcodepro_polydb_python-2.5.18.dist-info/RECORD,,
polydb/__init__.py CHANGED
@@ -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.14"
7
+ __version__ = "2.5.18"
8
8
 
9
9
  from .aio import AsyncDatabaseFactory, AsyncPolyDB
10
10
  from .audit.context import AuditContext
@@ -125,6 +125,19 @@ class AzureTableStorageAdapter(NoSQLKVAdapter):
125
125
  s = re.sub(r"[\\/#\?\x00-\x1f\x7f:+ ]", "_", s)
126
126
  return s[:1024] if len(s) > 1024 else s
127
127
 
128
+ def _check_overflow(self, data: JsonDict): # type: ignore[override]
129
+ """Azure overflows per PROPERTY inside `_put_raw`, never per record.
130
+
131
+ The base class' whole-record overflow replaces the record with a stub
132
+ of underscore keys (`_overflow`, `_blob_key`, ...), but `_put_raw`
133
+ drops every key starting with "_" when it builds the entity -- so the
134
+ stub's pointer was never stored and the real fields of the write were
135
+ silently lost (only an orphaned blob remained). Returning the data
136
+ untouched lets `_put_raw` spill each >30KB property on its own, which
137
+ `_get_raw`/`_query_raw` already rehydrate.
138
+ """
139
+ return data, None
140
+
128
141
  def _sanitize_prop_name(self, name: Any) -> str:
129
142
  s = str(name)
130
143
  s = re.sub(r"[^A-Za-z0-9_]", "_", s)
@@ -365,6 +365,50 @@ class PostgreSQLAdapter:
365
365
  return [self._serialize_param(p) for p in params]
366
366
 
367
367
  def _deserialize_row(self, row: JsonDict) -> JsonDict:
368
+ """Best-effort convenience decode: any string column value that
369
+ LOOKS like JSON (starts/ends with `{}`/`[]`) is `json.loads`'d,
370
+ regardless of the column's own declared type. This is a real,
371
+ deliberate, LOAD-BEARING convenience a real caller depends on --
372
+ e.g. universal-interprter's `observability.py`'s `Observability.
373
+ query()` writes a `logs.emit` payload as `json.dumps(...)` into a
374
+ plain TEXT column (`payload`, never JSONB -- see that module's
375
+ own comment) and its own docstring explicitly promises "logs.
376
+ query's own `payload` field gets a real dict back"; removing this
377
+ heuristic (briefly tried, then reverted -- see this repo's own
378
+ 2.5.16/2.5.17 CLAUDE.md history) silently broke that real,
379
+ already-shipped contract. `query_log.py`'s own `fields` column is
380
+ the identical shape, just defended a second way: it independently
381
+ `json.loads`'s its own value with an `isinstance(..., str)` guard
382
+ rather than relying on this method at all -- proof this repo has
383
+ real callers on both sides (some lean on this adapter-level
384
+ decode, some defend for themselves).
385
+
386
+ **The real, honest trade-off, unavoidable at this layer**: a
387
+ genuinely non-JSON TEXT/VARCHAR string whose own real content
388
+ merely LOOKS JSON-shaped (e.g. a prompt template's own `content`
389
+ column holding a literal JSON-encoded email template -- see
390
+ universal-interprter's `prompt_template_store.py`) gets coerced
391
+ from `str` to `dict`/`list` here too, since this method has no
392
+ way to distinguish "real app data serialized as JSON text" from
393
+ "a legitimately JSON-shaped string that must stay a string" --
394
+ both are simply "a string that starts with `{` and ends with
395
+ `}`." This adapter deliberately does NOT try to solve that
396
+ ambiguity generically (declaring the column JSONB instead sidesteps
397
+ it entirely -- psycopg2 registers global JSON/JSONB typecasters
398
+ for OIDs 114/3802 automatically at import time, so a genuine
399
+ `json`/`jsonb` column never even reaches this heuristic, `isinstance
400
+ (v, str)` already `False` by the time this method sees it); a
401
+ caller that must keep a TEXT column's string content literal
402
+ despite it being JSON-shaped owns that coercion at ITS OWN layer
403
+ instead (see `prompt_template_store.py`'s own `_coerce_content_to_str`
404
+ for the concrete pattern: re-`json.dumps()` a `content` value that
405
+ came back non-`str`, restoring the real string every caller of
406
+ that store is promised). This adapter stays a "dumb storage
407
+ layer" in the sense that it never MODELS what a column's real
408
+ semantic type should be -- it does still apply this one generic,
409
+ best-effort convenience decode uniformly, exactly as it always
410
+ has.
411
+ """
368
412
  for k, v in list(row.items()):
369
413
  if isinstance(v, str):
370
414
  s = v.strip()
polydb/schema.py CHANGED
@@ -61,6 +61,17 @@ class Index:
61
61
  name: str
62
62
  columns: List[str]
63
63
  unique: bool = False
64
+ # Postgres index access method -- "btree" (the default, and the only
65
+ # one Postgres actually permits combined with UNIQUE) plus the real
66
+ # non-default methods a caller may need for a specific column shape:
67
+ # "gin"/"gist" (full-text search, JSONB containment, array overlap),
68
+ # "hash" (equality-only, smaller than btree for that one case), "brin"
69
+ # (large, naturally-ordered, append-mostly columns -- cheap to
70
+ # maintain relative to btree at real scale). Never validated against
71
+ # Postgres's own real per-type support here -- that's a live DDL
72
+ # concern the caller's own database will enforce; this dataclass only
73
+ # carries the caller's choice through to the generated statement.
74
+ using: str = "btree"
64
75
 
65
76
 
66
77
  class SchemaBuilder:
@@ -128,10 +139,17 @@ class SchemaBuilder:
128
139
 
129
140
  for idx in self.indexes:
130
141
  unique = "UNIQUE " if idx.unique else ""
142
+ # "btree" omitted outright rather than spelled out as
143
+ # "USING btree" -- it's Postgres's own implicit default, and
144
+ # every index this codebase generated before `using` existed
145
+ # rendered without a USING clause at all; emitting it only for
146
+ # a non-default method keeps every pre-existing caller's own
147
+ # generated SQL text byte-for-byte unchanged.
148
+ using = f"USING {idx.using} " if idx.using != "btree" else ""
131
149
  cols = ", ".join(validate_column_name(c) for c in idx.columns)
132
150
  sql = (
133
151
  f"CREATE {unique}INDEX IF NOT EXISTS {validate_table_name(idx.name)} "
134
- f"ON {table_name}({cols});"
152
+ f"ON {table_name}{' ' if using else ''}{using}({cols});"
135
153
  )
136
154
  statements.append(sql)
137
155