tracktolib 0.49.1__py3-none-any.whl → 0.51.0__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.
- tracktolib/pg/__init__.py +1 -0
- tracktolib/pg/query.py +36 -4
- tracktolib/pg_utils.py +16 -2
- {tracktolib-0.49.1.dist-info → tracktolib-0.51.0.dist-info}/METADATA +1 -1
- {tracktolib-0.49.1.dist-info → tracktolib-0.51.0.dist-info}/RECORD +7 -7
- {tracktolib-0.49.1.dist-info → tracktolib-0.51.0.dist-info}/LICENSE +0 -0
- {tracktolib-0.49.1.dist-info → tracktolib-0.51.0.dist-info}/WHEEL +0 -0
tracktolib/pg/__init__.py
CHANGED
tracktolib/pg/query.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import typing
|
|
2
2
|
from dataclasses import dataclass, field
|
|
3
3
|
from typing import TypeVar, Iterable, Callable, Generic, Iterator, TypeAlias, overload, Any, Literal
|
|
4
|
+
|
|
4
5
|
from ..pg_utils import get_conflict_query
|
|
5
6
|
|
|
6
7
|
try:
|
|
@@ -32,6 +33,7 @@ def _get_on_conflict_query(
|
|
|
32
33
|
constraint: K | None,
|
|
33
34
|
on_conflict: K | None,
|
|
34
35
|
where: K | None,
|
|
36
|
+
merge_columns: Iterable[K] | None,
|
|
35
37
|
) -> str:
|
|
36
38
|
_on_conflict = get_conflict_query(
|
|
37
39
|
columns=columns,
|
|
@@ -40,6 +42,7 @@ def _get_on_conflict_query(
|
|
|
40
42
|
constraint=constraint,
|
|
41
43
|
on_conflict=on_conflict,
|
|
42
44
|
where=where,
|
|
45
|
+
merge_columns=merge_columns,
|
|
43
46
|
)
|
|
44
47
|
return f"{query} {_on_conflict}"
|
|
45
48
|
|
|
@@ -72,6 +75,8 @@ class PGConflictQuery(Generic[K]):
|
|
|
72
75
|
query: str | None = None
|
|
73
76
|
constraint: str | None = None
|
|
74
77
|
where: str | None = None
|
|
78
|
+
"""JSONB keys to merge (like jsonb1 || newjsonb2)"""
|
|
79
|
+
merge_keys: Iterable[K] | None = None
|
|
75
80
|
|
|
76
81
|
def __post_init__(self):
|
|
77
82
|
_has_keys = 1 if (self.keys or self.ignore_keys) else 0
|
|
@@ -172,6 +177,7 @@ class PGInsertQuery(PGQuery):
|
|
|
172
177
|
self.on_conflict.constraint,
|
|
173
178
|
self.on_conflict.query,
|
|
174
179
|
self.on_conflict.where,
|
|
180
|
+
self.on_conflict.merge_keys,
|
|
175
181
|
)
|
|
176
182
|
|
|
177
183
|
# Returning
|
|
@@ -193,17 +199,30 @@ def get_update_fields(
|
|
|
193
199
|
start_from: int = 0,
|
|
194
200
|
ignore_keys: list[str] | None = None,
|
|
195
201
|
quote_columns: bool = False,
|
|
202
|
+
merge_keys: list[str] | None = None,
|
|
196
203
|
) -> tuple[str, list]:
|
|
197
204
|
values, fields, where_values = [], [], []
|
|
198
205
|
counter = 0
|
|
206
|
+
_merge_keys = set(merge_keys or [])
|
|
207
|
+
_ignore_keys = ignore_keys or []
|
|
208
|
+
|
|
209
|
+
_invalid_merge_keys = _merge_keys - set(keys)
|
|
210
|
+
if _invalid_merge_keys:
|
|
211
|
+
raise ValueError(f"Merge keys not in keys found: {_invalid_merge_keys}")
|
|
212
|
+
|
|
199
213
|
for k in keys:
|
|
200
214
|
v = item[k]
|
|
201
|
-
if
|
|
215
|
+
if k in _ignore_keys:
|
|
202
216
|
where_values.append(v)
|
|
203
217
|
continue
|
|
204
218
|
values.append(v)
|
|
205
219
|
_col = f'"{k}"' if quote_columns else k
|
|
206
|
-
|
|
220
|
+
_counter = counter + start_from + 1
|
|
221
|
+
fields.append(
|
|
222
|
+
f"{_col} = ${_counter}"
|
|
223
|
+
if k not in _merge_keys
|
|
224
|
+
else f"{_col} = COALESCE(t.{_col}, jsonb_build_object()) || " f"${_counter}"
|
|
225
|
+
)
|
|
207
226
|
counter += 1
|
|
208
227
|
return ",\n".join(fields), values + where_values
|
|
209
228
|
|
|
@@ -222,6 +241,8 @@ class PGUpdateQuery(PGQuery):
|
|
|
222
241
|
returning: str | list[str] | None = None
|
|
223
242
|
"""If True, the query will return all the updated fields"""
|
|
224
243
|
return_keys: bool = False
|
|
244
|
+
"""Values to update using merge (like {}::jsonb || {}::jsonb)"""
|
|
245
|
+
merge_keys: list[str] | None = None
|
|
225
246
|
|
|
226
247
|
_update_fields: str | None = field(init=False, default=None)
|
|
227
248
|
_values: list | None = field(init=False, default=None)
|
|
@@ -238,6 +259,7 @@ class PGUpdateQuery(PGQuery):
|
|
|
238
259
|
start_from=self.start_from or 0,
|
|
239
260
|
ignore_keys=self.where_keys,
|
|
240
261
|
quote_columns=self.quote_columns,
|
|
262
|
+
merge_keys=self.merge_keys,
|
|
241
263
|
)
|
|
242
264
|
if self.returning and self.return_keys:
|
|
243
265
|
raise ValueError("Please choose either returning or return_keys")
|
|
@@ -263,7 +285,7 @@ class PGUpdateQuery(PGQuery):
|
|
|
263
285
|
raise ValueError("No update fields found")
|
|
264
286
|
|
|
265
287
|
query = f"""
|
|
266
|
-
UPDATE {self.table}
|
|
288
|
+
UPDATE {self.table} t
|
|
267
289
|
SET {self._update_fields}
|
|
268
290
|
{self._get_where_query()}
|
|
269
291
|
"""
|
|
@@ -370,8 +392,13 @@ async def update_one(
|
|
|
370
392
|
keys: list[str] | None = None,
|
|
371
393
|
start_from: int | None = None,
|
|
372
394
|
where: str | None = None,
|
|
395
|
+
merge_keys: list[str] | None = None,
|
|
373
396
|
):
|
|
374
|
-
query = PGUpdateQuery(
|
|
397
|
+
query = PGUpdateQuery(
|
|
398
|
+
table=table, items=[item], start_from=start_from, where_keys=keys, where=where, merge_keys=merge_keys
|
|
399
|
+
)
|
|
400
|
+
print(query.query)
|
|
401
|
+
print(query.values)
|
|
375
402
|
await conn.execute(query.query, *args, *query.values)
|
|
376
403
|
|
|
377
404
|
|
|
@@ -386,6 +413,7 @@ async def update_returning(
|
|
|
386
413
|
where: str | None = None,
|
|
387
414
|
keys: list[str] | None = None,
|
|
388
415
|
start_from: int | None = None,
|
|
416
|
+
merge_keys: list[str] | None = None,
|
|
389
417
|
) -> Any | None: ...
|
|
390
418
|
|
|
391
419
|
|
|
@@ -400,6 +428,7 @@ async def update_returning(
|
|
|
400
428
|
where: str | None = None,
|
|
401
429
|
keys: list[str] | None = None,
|
|
402
430
|
start_from: int | None = None,
|
|
431
|
+
merge_keys: list[str] | None = None,
|
|
403
432
|
) -> asyncpg.Record | None: ...
|
|
404
433
|
|
|
405
434
|
|
|
@@ -414,6 +443,7 @@ async def update_returning(
|
|
|
414
443
|
where: str | None = None,
|
|
415
444
|
keys: list[str] | None = None,
|
|
416
445
|
start_from: int | None = None,
|
|
446
|
+
merge_keys: list[str] | None = None,
|
|
417
447
|
) -> asyncpg.Record | None: ...
|
|
418
448
|
|
|
419
449
|
|
|
@@ -427,6 +457,7 @@ async def update_returning(
|
|
|
427
457
|
where: str | None = None,
|
|
428
458
|
keys: list[str] | None = None,
|
|
429
459
|
start_from: int | None = None,
|
|
460
|
+
merge_keys: list[str] | None = None,
|
|
430
461
|
) -> Any | asyncpg.Record | None:
|
|
431
462
|
if returning is not None:
|
|
432
463
|
returning_values = [returning] if isinstance(returning, str) else returning
|
|
@@ -440,6 +471,7 @@ async def update_returning(
|
|
|
440
471
|
where_keys=keys,
|
|
441
472
|
return_keys=return_keys,
|
|
442
473
|
returning=returning_values,
|
|
474
|
+
merge_keys=merge_keys,
|
|
443
475
|
)
|
|
444
476
|
fn = conn.fetchval if len(returning_values or []) == 1 else conn.fetchrow
|
|
445
477
|
return await fn(query.query, *args, *query.values)
|
tracktolib/pg_utils.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
from typing_extensions import LiteralString
|
|
2
1
|
from typing import Iterable
|
|
3
2
|
from typing import cast
|
|
4
3
|
|
|
4
|
+
from typing_extensions import LiteralString
|
|
5
|
+
|
|
5
6
|
|
|
6
7
|
def get_tmp_table_query(
|
|
7
8
|
schema: LiteralString,
|
|
@@ -41,6 +42,7 @@ def get_conflict_query(
|
|
|
41
42
|
constraint: str | None = None,
|
|
42
43
|
on_conflict: str | None = None,
|
|
43
44
|
where: str | None = None,
|
|
45
|
+
merge_columns: Iterable[str] | None = None,
|
|
44
46
|
) -> LiteralString:
|
|
45
47
|
if on_conflict:
|
|
46
48
|
return cast(LiteralString, on_conflict)
|
|
@@ -55,8 +57,20 @@ def get_conflict_query(
|
|
|
55
57
|
else:
|
|
56
58
|
raise NotImplementedError("update_keys or constraint must be set")
|
|
57
59
|
|
|
58
|
-
|
|
60
|
+
_update_columns = update_columns or []
|
|
61
|
+
_ignore_columns = ignore_columns or []
|
|
62
|
+
_merge_columns = merge_columns or []
|
|
63
|
+
|
|
64
|
+
if set(_merge_columns) & set(_update_columns):
|
|
65
|
+
raise ValueError("Duplicate keys found between merge and update")
|
|
66
|
+
if set(_merge_columns) & set(_ignore_columns):
|
|
67
|
+
raise ValueError("Merge column cannot be ignored")
|
|
68
|
+
|
|
69
|
+
_ignore_columns = [*_update_columns, *_ignore_columns, *_merge_columns]
|
|
59
70
|
fields = ", ".join(f"{x} = COALESCE(EXCLUDED.{x}, t.{x})" for x in columns if x not in _ignore_columns)
|
|
71
|
+
if merge_columns:
|
|
72
|
+
fields = fields + ", " if fields else fields
|
|
73
|
+
fields += ", ".join(f"{x} = COALESCE(t.{x}, jsonb_build_object()) || EXCLUDED.{x}" for x in merge_columns)
|
|
60
74
|
if not fields:
|
|
61
75
|
raise ValueError("No fields set")
|
|
62
76
|
|
|
@@ -3,17 +3,17 @@ tracktolib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
3
3
|
tracktolib/api.py,sha256=xArVgRj_g7bw2tEDbjC9qs9f55b9X0-kJoc8-s1rsYo,9616
|
|
4
4
|
tracktolib/http_utils.py,sha256=c10JGmHaBw3VSDMYhz2dvVw2lo4PUAq1xMub74I7xDc,2625
|
|
5
5
|
tracktolib/logs.py,sha256=M5RZ8OYKgLEBJeC1AwUCSRbFAH09hwdTjjs-AQ9QGg8,2204
|
|
6
|
-
tracktolib/pg/__init__.py,sha256=
|
|
7
|
-
tracktolib/pg/query.py,sha256=
|
|
6
|
+
tracktolib/pg/__init__.py,sha256=j67e3B3gBbCHLD20QBybptmNdbbVMzNhZE6XjIPuKVo,349
|
|
7
|
+
tracktolib/pg/query.py,sha256=XX5eD02TRWRIiBVj0ENooQ893aycan2fJ55_1J207Rw,14711
|
|
8
8
|
tracktolib/pg/utils.py,sha256=cL24KEt4SWJQ7LJPzaO3c8Xg0ZLmjhn22DtTWg86nwc,6324
|
|
9
9
|
tracktolib/pg_sync.py,sha256=z6EB0hELq6t129gnVnX0rNXv2McZXxI8khy-TEG65Ag,5165
|
|
10
|
-
tracktolib/pg_utils.py,sha256=
|
|
10
|
+
tracktolib/pg_utils.py,sha256=VXPpy1jGq6aCgTlfFJDIrq6JDujR83JN5ZRiCi8Lx4E,2582
|
|
11
11
|
tracktolib/s3/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
12
|
tracktolib/s3/minio.py,sha256=wMEjkSes9Fp39fD17IctALpD6zB2xwDRQEmO7Vzan3g,1387
|
|
13
13
|
tracktolib/s3/s3.py,sha256=d0Q63Zb62ef4jAt05zQwpgYvAoxHC9kSQuHzzK90VvE,4825
|
|
14
14
|
tracktolib/tests.py,sha256=Pbc4yGQrIWFLBEgX-kDBxhMCqH-bfOJtkH2agvaM6ZQ,382
|
|
15
15
|
tracktolib/utils.py,sha256=jwLww8bqDu8zEip9uN4yW0lE5_YMWfrAHYtagr8sYOA,5295
|
|
16
|
-
tracktolib-0.
|
|
17
|
-
tracktolib-0.
|
|
18
|
-
tracktolib-0.
|
|
19
|
-
tracktolib-0.
|
|
16
|
+
tracktolib-0.51.0.dist-info/LICENSE,sha256=uUanH0X7SeZEPdsRTHegMSMTiIHMurt9H0jSwEwKE1Y,1081
|
|
17
|
+
tracktolib-0.51.0.dist-info/METADATA,sha256=v2MsoN7EIuclo58jhPp40YJespn9Oy4rYKM8kg7VRsQ,3641
|
|
18
|
+
tracktolib-0.51.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
19
|
+
tracktolib-0.51.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|