sql-athame 0.4.0a10__py3-none-any.whl → 0.4.0a11__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.
- sql_athame/dataclasses.py +85 -19
- {sql_athame-0.4.0a10.dist-info → sql_athame-0.4.0a11.dist-info}/METADATA +9 -16
- {sql_athame-0.4.0a10.dist-info → sql_athame-0.4.0a11.dist-info}/RECORD +5 -5
- {sql_athame-0.4.0a10.dist-info → sql_athame-0.4.0a11.dist-info}/WHEEL +1 -1
- {sql_athame-0.4.0a10.dist-info → sql_athame-0.4.0a11.dist-info/licenses}/LICENSE +0 -0
sql_athame/dataclasses.py
CHANGED
@@ -157,7 +157,7 @@ class ModelBase:
|
|
157
157
|
_cache: dict[tuple, Any]
|
158
158
|
table_name: str
|
159
159
|
primary_key_names: tuple[str, ...]
|
160
|
-
|
160
|
+
insert_multiple_mode: str
|
161
161
|
|
162
162
|
def __init_subclass__(
|
163
163
|
cls,
|
@@ -169,12 +169,9 @@ class ModelBase:
|
|
169
169
|
):
|
170
170
|
cls._cache = {}
|
171
171
|
cls.table_name = table_name
|
172
|
-
if insert_multiple_mode
|
173
|
-
cls.array_safe_insert = True
|
174
|
-
elif insert_multiple_mode == "unnest":
|
175
|
-
cls.array_safe_insert = False
|
176
|
-
else:
|
172
|
+
if insert_multiple_mode not in ("array_safe", "unnest", "executemany"):
|
177
173
|
raise ValueError("Unknown `insert_multiple_mode`")
|
174
|
+
cls.insert_multiple_mode = insert_multiple_mode
|
178
175
|
if isinstance(primary_key, str):
|
179
176
|
cls.primary_key_names = (primary_key,)
|
180
177
|
else:
|
@@ -357,7 +354,17 @@ class ModelBase:
|
|
357
354
|
return query
|
358
355
|
|
359
356
|
@classmethod
|
360
|
-
async def
|
357
|
+
async def cursor_from(
|
358
|
+
cls: type[T],
|
359
|
+
connection: Connection,
|
360
|
+
query: Fragment,
|
361
|
+
prefetch: int = 1000,
|
362
|
+
) -> AsyncGenerator[T, None]:
|
363
|
+
async for row in connection.cursor(*query, prefetch=prefetch):
|
364
|
+
yield cls.from_mapping(row)
|
365
|
+
|
366
|
+
@classmethod
|
367
|
+
def select_cursor(
|
361
368
|
cls: type[T],
|
362
369
|
connection: Connection,
|
363
370
|
order_by: Union[FieldNames, str] = (),
|
@@ -365,11 +372,19 @@ class ModelBase:
|
|
365
372
|
where: Where = (),
|
366
373
|
prefetch: int = 1000,
|
367
374
|
) -> AsyncGenerator[T, None]:
|
368
|
-
|
369
|
-
|
375
|
+
return cls.cursor_from(
|
376
|
+
connection,
|
377
|
+
cls.select_sql(order_by=order_by, for_update=for_update, where=where),
|
370
378
|
prefetch=prefetch,
|
371
|
-
)
|
372
|
-
|
379
|
+
)
|
380
|
+
|
381
|
+
@classmethod
|
382
|
+
async def fetch_from(
|
383
|
+
cls: type[T],
|
384
|
+
connection_or_pool: Union[Connection, Pool],
|
385
|
+
query: Fragment,
|
386
|
+
) -> list[T]:
|
387
|
+
return [cls.from_mapping(row) for row in await connection_or_pool.fetch(*query)]
|
373
388
|
|
374
389
|
@classmethod
|
375
390
|
async def select(
|
@@ -379,12 +394,10 @@ class ModelBase:
|
|
379
394
|
for_update: bool = False,
|
380
395
|
where: Where = (),
|
381
396
|
) -> list[T]:
|
382
|
-
return
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
)
|
387
|
-
]
|
397
|
+
return await cls.fetch_from(
|
398
|
+
connection_or_pool,
|
399
|
+
cls.select_sql(order_by=order_by, for_update=for_update, where=where),
|
400
|
+
)
|
388
401
|
|
389
402
|
@classmethod
|
390
403
|
def create_sql(cls: type[T], **kwargs: Any) -> Fragment:
|
@@ -506,6 +519,37 @@ class ModelBase:
|
|
506
519
|
),
|
507
520
|
)
|
508
521
|
|
522
|
+
@classmethod
|
523
|
+
def insert_multiple_executemany_chunk_sql(
|
524
|
+
cls: type[T], chunk_size: int
|
525
|
+
) -> Fragment:
|
526
|
+
def generate() -> Fragment:
|
527
|
+
columns = len(cls.column_info())
|
528
|
+
values = ", ".join(
|
529
|
+
f"({', '.join(f'${i}' for i in chunk)})"
|
530
|
+
for chunk in chunked(range(1, columns * chunk_size + 1), columns)
|
531
|
+
)
|
532
|
+
return sql(
|
533
|
+
"INSERT INTO {table} ({fields}) VALUES {values}",
|
534
|
+
table=cls.table_name_sql(),
|
535
|
+
fields=sql.list(cls.field_names_sql()),
|
536
|
+
values=sql.literal(values),
|
537
|
+
).flatten()
|
538
|
+
|
539
|
+
return cls._cached(
|
540
|
+
("insert_multiple_executemany_chunk", chunk_size),
|
541
|
+
generate,
|
542
|
+
)
|
543
|
+
|
544
|
+
@classmethod
|
545
|
+
async def insert_multiple_executemany(
|
546
|
+
cls: type[T], connection_or_pool: Union[Connection, Pool], rows: Iterable[T]
|
547
|
+
) -> None:
|
548
|
+
args = [r.field_values() for r in rows]
|
549
|
+
query = cls.insert_multiple_executemany_chunk_sql(1).query()[0]
|
550
|
+
if args:
|
551
|
+
await connection_or_pool.executemany(query, args)
|
552
|
+
|
509
553
|
@classmethod
|
510
554
|
async def insert_multiple_unnest(
|
511
555
|
cls: type[T], connection_or_pool: Union[Connection, Pool], rows: Iterable[T]
|
@@ -527,11 +571,28 @@ class ModelBase:
|
|
527
571
|
async def insert_multiple(
|
528
572
|
cls: type[T], connection_or_pool: Union[Connection, Pool], rows: Iterable[T]
|
529
573
|
) -> str:
|
530
|
-
if cls.
|
574
|
+
if cls.insert_multiple_mode == "executemany":
|
575
|
+
await cls.insert_multiple_executemany(connection_or_pool, rows)
|
576
|
+
return "INSERT"
|
577
|
+
elif cls.insert_multiple_mode == "array_safe":
|
531
578
|
return await cls.insert_multiple_array_safe(connection_or_pool, rows)
|
532
579
|
else:
|
533
580
|
return await cls.insert_multiple_unnest(connection_or_pool, rows)
|
534
581
|
|
582
|
+
@classmethod
|
583
|
+
async def upsert_multiple_executemany(
|
584
|
+
cls: type[T],
|
585
|
+
connection_or_pool: Union[Connection, Pool],
|
586
|
+
rows: Iterable[T],
|
587
|
+
insert_only: FieldNamesSet = (),
|
588
|
+
) -> None:
|
589
|
+
args = [r.field_values() for r in rows]
|
590
|
+
query = cls.upsert_sql(
|
591
|
+
cls.insert_multiple_executemany_chunk_sql(1), exclude=insert_only
|
592
|
+
).query()[0]
|
593
|
+
if args:
|
594
|
+
await connection_or_pool.executemany(query, args)
|
595
|
+
|
535
596
|
@classmethod
|
536
597
|
async def upsert_multiple_unnest(
|
537
598
|
cls: type[T],
|
@@ -566,7 +627,12 @@ class ModelBase:
|
|
566
627
|
rows: Iterable[T],
|
567
628
|
insert_only: FieldNamesSet = (),
|
568
629
|
) -> str:
|
569
|
-
if cls.
|
630
|
+
if cls.insert_multiple_mode == "executemany":
|
631
|
+
await cls.upsert_multiple_executemany(
|
632
|
+
connection_or_pool, rows, insert_only=insert_only
|
633
|
+
)
|
634
|
+
return "INSERT"
|
635
|
+
elif cls.insert_multiple_mode == "array_safe":
|
570
636
|
return await cls.upsert_multiple_array_safe(
|
571
637
|
connection_or_pool, rows, insert_only=insert_only
|
572
638
|
)
|
@@ -1,22 +1,16 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: sql-athame
|
3
|
-
Version: 0.4.
|
3
|
+
Version: 0.4.0a11
|
4
4
|
Summary: Python tool for slicing and dicing SQL
|
5
|
-
|
5
|
+
Project-URL: homepage, https://github.com/bdowning/sql-athame
|
6
|
+
Project-URL: repository, https://github.com/bdowning/sql-athame
|
7
|
+
Author-email: Brian Downing <bdowning@lavos.net>
|
6
8
|
License: MIT
|
7
|
-
|
8
|
-
|
9
|
-
Requires-Python: >=3.9,<4.0
|
10
|
-
Classifier: License :: OSI Approved :: MIT License
|
11
|
-
Classifier: Programming Language :: Python :: 3
|
12
|
-
Classifier: Programming Language :: Python :: 3.9
|
13
|
-
Classifier: Programming Language :: Python :: 3.10
|
14
|
-
Classifier: Programming Language :: Python :: 3.11
|
15
|
-
Classifier: Programming Language :: Python :: 3.12
|
16
|
-
Provides-Extra: asyncpg
|
17
|
-
Requires-Dist: asyncpg ; extra == "asyncpg"
|
9
|
+
License-File: LICENSE
|
10
|
+
Requires-Python: <4.0,>=3.9
|
18
11
|
Requires-Dist: typing-extensions
|
19
|
-
|
12
|
+
Provides-Extra: asyncpg
|
13
|
+
Requires-Dist: asyncpg; extra == 'asyncpg'
|
20
14
|
Description-Content-Type: text/markdown
|
21
15
|
|
22
16
|
# sql-athame
|
@@ -405,4 +399,3 @@ MIT.
|
|
405
399
|
|
406
400
|
---
|
407
401
|
Copyright (c) 2019, 2020 Brian Downing
|
408
|
-
|
@@ -1,11 +1,11 @@
|
|
1
1
|
sql_athame/__init__.py,sha256=0GA9-xfnZ_gw7Vysx6t4iInQ2kGXos1EYwBI6Eb7auU,100
|
2
2
|
sql_athame/base.py,sha256=FR7EmC0VkX1VRgvAutSEfYSWhlEYpoqS1Kqxp1jHp6Y,10293
|
3
|
-
sql_athame/dataclasses.py,sha256=
|
3
|
+
sql_athame/dataclasses.py,sha256=0XjLXpSBgdtWqJkTdaluLs8_RXPlfJp5ERKGOe-bXBM,27442
|
4
4
|
sql_athame/escape.py,sha256=kK101xXeFitlvuG-L_hvhdpgGJCtmRTprsn1yEfZKws,758
|
5
5
|
sql_athame/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
6
6
|
sql_athame/sqlalchemy.py,sha256=aWopfPh3j71XwKmcN_VcHRNlhscI0Sckd4AiyGf8Tpw,1293
|
7
7
|
sql_athame/types.py,sha256=FQ06l9Uc-vo57UrAarvnukILdV2gN1IaYUnHJ_bNYic,475
|
8
|
-
sql_athame-0.4.
|
9
|
-
sql_athame-0.4.
|
10
|
-
sql_athame-0.4.
|
11
|
-
sql_athame-0.4.
|
8
|
+
sql_athame-0.4.0a11.dist-info/METADATA,sha256=DXasc1ZenA7rqqTPu0G0Y1dfFEGmzngZGhAnUxGJAcs,12570
|
9
|
+
sql_athame-0.4.0a11.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
10
|
+
sql_athame-0.4.0a11.dist-info/licenses/LICENSE,sha256=xqV29vPFqITcKifYrGPgVIBjq4fdmLSwY3gRUtDKafg,1076
|
11
|
+
sql_athame-0.4.0a11.dist-info/RECORD,,
|
File without changes
|