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.
- {altcodepro_polydb_python-2.5.11.dist-info → altcodepro_polydb_python-2.5.18.dist-info}/METADATA +1 -1
- {altcodepro_polydb_python-2.5.11.dist-info → altcodepro_polydb_python-2.5.18.dist-info}/RECORD +9 -9
- polydb/__init__.py +1 -1
- polydb/adapters/AzureTableStorageAdapter.py +13 -0
- polydb/adapters/PostgreSQLAdapter.py +44 -0
- polydb/schema.py +19 -1
- {altcodepro_polydb_python-2.5.11.dist-info → altcodepro_polydb_python-2.5.18.dist-info}/WHEEL +0 -0
- {altcodepro_polydb_python-2.5.11.dist-info → altcodepro_polydb_python-2.5.18.dist-info}/licenses/LICENSE +0 -0
- {altcodepro_polydb_python-2.5.11.dist-info → altcodepro_polydb_python-2.5.18.dist-info}/top_level.txt +0 -0
{altcodepro_polydb_python-2.5.11.dist-info → altcodepro_polydb_python-2.5.18.dist-info}/METADATA
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: altcodepro-polydb-python
|
|
3
|
-
Version: 2.5.
|
|
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
|
{altcodepro_polydb_python-2.5.11.dist-info → altcodepro_polydb_python-2.5.18.dist-info}/RECORD
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
altcodepro_polydb_python-2.5.
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
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.
|
|
72
|
-
altcodepro_polydb_python-2.5.
|
|
73
|
-
altcodepro_polydb_python-2.5.
|
|
74
|
-
altcodepro_polydb_python-2.5.
|
|
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.
|
|
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
|
|
{altcodepro_polydb_python-2.5.11.dist-info → altcodepro_polydb_python-2.5.18.dist-info}/WHEEL
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|