Skip to content

Commit 2cbeedd

Browse files
committed
Merge branch 'main' of github.com:qaspen-python/psqlpy
2 parents e97df07 + d3bbaf3 commit 2cbeedd

File tree

11 files changed

+587
-155
lines changed

11 files changed

+587
-155
lines changed

Cargo.lock

Lines changed: 115 additions & 102 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,63 @@ async def main() -> None:
264264
await transaction.rollback()
265265
```
266266

267+
### Transaction execute many
268+
269+
You can execute one statement with multiple pararmeters.
270+
The query will be executed with all parameters or will not be executed at all.
271+
272+
```python
273+
from typing import Any
274+
275+
from psqlpy import PSQLPool, IsolationLevel
276+
277+
278+
db_pool = PSQLPool()
279+
280+
async def main() -> None:
281+
await db_pool.startup()
282+
283+
connection = await db_pool.connection()
284+
transaction = connection.transaction(
285+
isolation_level=IsolationLevel.Serializable,
286+
)
287+
288+
await transaction.begin()
289+
await transaction.execute_many(
290+
"INSERT INTO users VALUES ($1)",
291+
[["first-data"], ["second-data"], ["third-data"]],
292+
)
293+
await transaction.commit()
294+
```
295+
296+
### Transaction fetch row
297+
298+
You can fetch first row.
299+
300+
```python
301+
from typing import Any
302+
303+
from psqlpy import PSQLPool, IsolationLevel
304+
305+
306+
db_pool = PSQLPool()
307+
308+
async def main() -> None:
309+
await db_pool.startup()
310+
311+
connection = await db_pool.connection()
312+
transaction = connection.transaction(
313+
isolation_level=IsolationLevel.Serializable,
314+
)
315+
316+
await transaction.begin()
317+
first_row = await transaction.fetch_row(
318+
"SELECT * FROM users WHERE user_id = $1",
319+
["user-id"],
320+
)
321+
first_row_result = first_row.result() # This will be a dict.
322+
```
323+
267324
### Transaction ROLLBACK TO SAVEPOINT
268325

269326
You can rollback your transaction to the specified savepoint, but before it you must create it.

python/psqlpy/_internal/__init__.pyi

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ class QueryResult:
1010
def result(self: Self) -> list[dict[Any, Any]]:
1111
"""Return result from database as a list of dicts."""
1212

13+
class SingleQueryResult:
14+
"""Single result."""
15+
16+
def result(self: Self) -> dict[Any, Any]:
17+
"""Return result from database as a dict."""
18+
1319
class IsolationLevel(Enum):
1420
"""Class for Isolation Level for transactions."""
1521

@@ -269,6 +275,108 @@ class Transaction:
269275
# This way transaction begins and commits by itself.
270276
```
271277
"""
278+
async def execute_many(
279+
self: Self,
280+
querystring: str,
281+
parameters: list[list[Any]] | None = None,
282+
) -> None: ...
283+
"""Execute query multiple times with different parameters.
284+
285+
Querystring can contain `$<number>` parameters
286+
for converting them in the driver side.
287+
288+
### Parameters:
289+
- `querystring`: querystring to execute.
290+
- `parameters`: list of list of parameters to pass in the query.
291+
292+
### Example:
293+
```python
294+
import asyncio
295+
296+
from psqlpy import PSQLPool, QueryResult
297+
298+
299+
async def main() -> None:
300+
db_pool = PSQLPool()
301+
await db_pool.startup()
302+
303+
transaction = await db_pool.transaction()
304+
await transaction.begin()
305+
query_result: QueryResult = await transaction.execute_many(
306+
"INSERT INTO users (name, age) VALUES ($1, $2)",
307+
[["boba", 10], ["boba", 20]],
308+
)
309+
dict_result: List[Dict[Any, Any]] = query_result.result()
310+
# You must call commit manually
311+
await transaction.commit()
312+
313+
# Or you can transaction as a async context manager
314+
315+
async def main() -> None:
316+
db_pool = PSQLPool()
317+
await psqlpy.startup()
318+
319+
transaction = await db_pool.transaction()
320+
async with transaction:
321+
query_result: QueryResult = await transaction.execute(
322+
"SELECT username FROM users WHERE id = $1",
323+
[100],
324+
)
325+
dict_result: List[Dict[Any, Any]] = query_result.result()
326+
# This way transaction begins and commits by itself.
327+
```
328+
"""
329+
async def fetch_row(
330+
self: Self,
331+
querystring: str,
332+
parameters: list[Any] | None = None,
333+
) -> SingleQueryResult:
334+
"""Execute the query and return first row.
335+
336+
Querystring can contain `$<number>` parameters
337+
for converting them in the driver side.
338+
339+
### Parameters:
340+
- `querystring`: querystring to execute.
341+
- `parameters`: list of parameters to pass in the query.
342+
343+
### Example:
344+
```python
345+
import asyncio
346+
347+
from psqlpy import PSQLPool, QueryResult
348+
349+
350+
async def main() -> None:
351+
db_pool = PSQLPool()
352+
await db_pool.startup()
353+
354+
transaction = await db_pool.transaction()
355+
await transaction.begin()
356+
query_result: SingleQueryResult = await transaction.execute(
357+
"SELECT username FROM users WHERE id = $1",
358+
[100],
359+
)
360+
dict_result: Dict[Any, Any] = query_result.result()
361+
# You must call commit manually
362+
await transaction.commit()
363+
364+
# Or you can transaction as a async context manager
365+
366+
async def main() -> None:
367+
db_pool = PSQLPool()
368+
await psqlpy.startup()
369+
370+
transaction = await db_pool.transaction()
371+
async with transaction:
372+
query_result: SingleQueryResult = await transaction.execute(
373+
"SELECT username FROM users WHERE id = $1",
374+
[100],
375+
)
376+
dict_result: Dict[Any, Any] = query_result.result()
377+
# This way transaction begins and commits by itself.
378+
```
379+
"""
272380
async def savepoint(self: Self, savepoint_name: str) -> None:
273381
"""Create new savepoint.
274382

python/tests/helpers.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import typing
2+
3+
from psqlpy import Transaction
4+
5+
6+
async def count_rows_in_test_table(
7+
table_name: str,
8+
transaction: Transaction,
9+
) -> int:
10+
query_result: typing.Final = await transaction.execute(
11+
f"SELECT COUNT(*) FROM {table_name}",
12+
)
13+
return query_result.result()[0]["count"]

python/tests/test_connection.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33
from psqlpy import PSQLPool, QueryResult, Transaction
44

5+
pytestmark = pytest.mark.anyio
6+
57

6-
@pytest.mark.anyio()
78
async def test_connection_execute(
89
psql_pool: PSQLPool,
910
table_name: str,
@@ -19,7 +20,6 @@ async def test_connection_execute(
1920
assert len(conn_result.result()) == number_database_records
2021

2122

22-
@pytest.mark.anyio()
2323
async def test_connection_transaction(
2424
psql_pool: PSQLPool,
2525
) -> None:

python/tests/test_connection_pool.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33
from psqlpy import Connection, ConnRecyclingMethod, PSQLPool, QueryResult
44

5+
pytestmark = pytest.mark.anyio
6+
57

6-
@pytest.mark.anyio()
78
async def test_pool_dsn_startup() -> None:
89
"""Test that connection pool can startup with dsn."""
910
pg_pool = PSQLPool(
@@ -14,7 +15,6 @@ async def test_pool_dsn_startup() -> None:
1415
await pg_pool.execute("SELECT 1")
1516

1617

17-
@pytest.mark.anyio()
1818
async def test_pool_execute(
1919
psql_pool: PSQLPool,
2020
table_name: str,
@@ -32,7 +32,6 @@ async def test_pool_execute(
3232
assert len(inner_result) == number_database_records
3333

3434

35-
@pytest.mark.anyio()
3635
async def test_pool_connection(
3736
psql_pool: PSQLPool,
3837
) -> None:
@@ -41,7 +40,6 @@ async def test_pool_connection(
4140
assert isinstance(connection, Connection)
4241

4342

44-
@pytest.mark.anyio()
4543
@pytest.mark.parametrize(
4644
"conn_recycling_method",
4745
[

python/tests/test_cursor.py

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@
22

33
from psqlpy import Cursor
44

5+
pytestmark = pytest.mark.anyio
6+
57

6-
@pytest.mark.anyio()
78
async def test_cursor_fetch(
89
number_database_records: int,
910
test_cursor: Cursor,
@@ -13,7 +14,6 @@ async def test_cursor_fetch(
1314
assert len(result.result()) == number_database_records // 2
1415

1516

16-
@pytest.mark.anyio()
1717
async def test_cursor_fetch_next(
1818
test_cursor: Cursor,
1919
) -> None:
@@ -22,7 +22,6 @@ async def test_cursor_fetch_next(
2222
assert len(result.result()) == 1
2323

2424

25-
@pytest.mark.anyio()
2625
async def test_cursor_fetch_prior(
2726
test_cursor: Cursor,
2827
) -> None:
@@ -35,7 +34,6 @@ async def test_cursor_fetch_prior(
3534
assert len(result.result()) == 1
3635

3736

38-
@pytest.mark.anyio()
3937
async def test_cursor_fetch_first(
4038
test_cursor: Cursor,
4139
) -> None:
@@ -49,7 +47,6 @@ async def test_cursor_fetch_first(
4947
assert fetch_first.result() == first.result()
5048

5149

52-
@pytest.mark.anyio()
5350
async def test_cursor_fetch_last(
5451
test_cursor: Cursor,
5552
number_database_records: int,
@@ -64,7 +61,6 @@ async def test_cursor_fetch_last(
6461
assert all_res.result()[-1] == last_res.result()[0]
6562

6663

67-
@pytest.mark.anyio()
6864
async def test_cursor_fetch_absolute(
6965
test_cursor: Cursor,
7066
number_database_records: int,
@@ -85,7 +81,6 @@ async def test_cursor_fetch_absolute(
8581
assert all_res.result()[-1] == last_record.result()[0]
8682

8783

88-
@pytest.mark.anyio()
8984
async def test_cursor_fetch_relative(
9085
test_cursor: Cursor,
9186
number_database_records: int,
@@ -107,7 +102,6 @@ async def test_cursor_fetch_relative(
107102
assert not (records.result())
108103

109104

110-
@pytest.mark.anyio()
111105
async def test_cursor_fetch_forward_all(
112106
test_cursor: Cursor,
113107
number_database_records: int,
@@ -124,7 +118,6 @@ async def test_cursor_fetch_forward_all(
124118
)
125119

126120

127-
@pytest.mark.anyio()
128121
async def test_cursor_fetch_backward(
129122
test_cursor: Cursor,
130123
) -> None:
@@ -142,7 +135,6 @@ async def test_cursor_fetch_backward(
142135
assert len(must_not_be_empty.result()) == expected_number_of_results
143136

144137

145-
@pytest.mark.anyio()
146138
async def test_cursor_fetch_backward_all(
147139
test_cursor: Cursor,
148140
) -> None:

0 commit comments

Comments
 (0)