Skip to content

Commit 45b0414

Browse files
authored
Merge pull request #129 from psqlpy-python/feature/kwargs_as_parameters
Added support for named parameters
2 parents 75e78c1 + c9b9c8c commit 45b0414

15 files changed

+425
-96
lines changed

Cargo.lock

+10-8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,5 @@ pgvector = { git = "https://github.com/chandr-andr/pgvector-rust.git", branch =
5454
] }
5555
futures-channel = "0.3.31"
5656
futures = "0.3.31"
57+
regex = "1.11.1"
58+
once_cell = "1.20.3"

docs/.vuepress/sidebar.ts

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export default sidebar({
3333
prefix: "usage/",
3434
collapsible: true,
3535
children: [
36+
"parameters",
3637
{
3738
text: "Types",
3839
prefix: "types/",

docs/usage/parameters.md

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
---
2+
title: Passing parameters to SQL queries
3+
---
4+
5+
We support two variant of passing parameters to sql queries.
6+
7+
::: tabs
8+
@tab Parameters sequence
9+
10+
You can pass parameters as some python Sequence.
11+
12+
Placeholders in querystring must be marked as `$1`, `$2` and so on,
13+
depending on how many parameters you have.
14+
15+
```python
16+
async def main():
17+
...
18+
19+
await connection.execute(
20+
querystring="SELECT * FROM users WHERE id = $1",
21+
parameters=(101,),
22+
)
23+
```
24+
25+
@tab Parameters mapping
26+
27+
If you prefer use named arguments, we support it too.
28+
Placeholder in querystring must look like `$(parameter)p`.
29+
30+
If you don't pass parameter but have it in querystring, exception will be raised.
31+
32+
```python
33+
async def main():
34+
...
35+
36+
await connection.execute(
37+
querystring="SELECT * FROM users WHERE id = $(user_id)p",
38+
parameters=dict(user_id=101),
39+
)
40+
```
41+
42+
:::

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ warn_unused_ignores = false
6464
[tool.ruff]
6565
fix = true
6666
unsafe-fixes = true
67-
line-length = 120
67+
line-length = 89
6868
exclude = [".venv/", "psqlpy-stress"]
6969

7070
[tool.ruff.format]

python/psqlpy/_internal/__init__.pyi

+23-18
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import types
22
from enum import Enum
33
from io import BytesIO
44
from ipaddress import IPv4Address, IPv6Address
5-
from typing import Any, Awaitable, Callable, Sequence, TypeVar
5+
from typing import Any, Awaitable, Callable, Mapping, Sequence, TypeVar
66

7-
from typing_extensions import Buffer, Self
7+
from typing_extensions import Buffer, Self, TypeAlias
88

99
_CustomClass = TypeVar(
1010
"_CustomClass",
@@ -13,6 +13,8 @@ _RowFactoryRV = TypeVar(
1313
"_RowFactoryRV",
1414
)
1515

16+
ParamsT: TypeAlias = Sequence[Any] | Mapping[str, Any] | None
17+
1618
class QueryResult:
1719
"""Result."""
1820

@@ -150,7 +152,7 @@ class SingleQueryResult:
150152

151153
class SynchronousCommit(Enum):
152154
"""
153-
Class for synchronous_commit option for transactions.
155+
Synchronous_commit option for transactions.
154156
155157
### Variants:
156158
- `On`: The meaning may change based on whether you have
@@ -181,7 +183,7 @@ class SynchronousCommit(Enum):
181183
RemoteApply = 5
182184

183185
class IsolationLevel(Enum):
184-
"""Class for Isolation Level for transactions."""
186+
"""Isolation Level for transactions."""
185187

186188
ReadUncommitted = 1
187189
ReadCommitted = 2
@@ -290,7 +292,7 @@ class Cursor:
290292

291293
cursor_name: str
292294
querystring: str
293-
parameters: Sequence[Any]
295+
parameters: ParamsT = None
294296
prepared: bool | None
295297
conn_dbname: str | None
296298
user: str | None
@@ -464,7 +466,7 @@ class Transaction:
464466
async def execute(
465467
self: Self,
466468
querystring: str,
467-
parameters: Sequence[Any] | None = None,
469+
parameters: ParamsT = None,
468470
prepared: bool = True,
469471
) -> QueryResult:
470472
"""Execute the query.
@@ -554,7 +556,7 @@ class Transaction:
554556
async def fetch(
555557
self: Self,
556558
querystring: str,
557-
parameters: Sequence[Any] | None = None,
559+
parameters: ParamsT = None,
558560
prepared: bool = True,
559561
) -> QueryResult:
560562
"""Fetch the result from database.
@@ -574,7 +576,7 @@ class Transaction:
574576
async def fetch_row(
575577
self: Self,
576578
querystring: str,
577-
parameters: Sequence[Any] | None = None,
579+
parameters: ParamsT = None,
578580
prepared: bool = True,
579581
) -> SingleQueryResult:
580582
"""Fetch exaclty single row from query.
@@ -613,7 +615,7 @@ class Transaction:
613615
async def fetch_val(
614616
self: Self,
615617
querystring: str,
616-
parameters: Sequence[Any] | None = None,
618+
parameters: ParamsT = None,
617619
prepared: bool = True,
618620
) -> Any | None:
619621
"""Execute the query and return first value of the first row.
@@ -814,7 +816,7 @@ class Transaction:
814816
def cursor(
815817
self: Self,
816818
querystring: str,
817-
parameters: Sequence[Any] | None = None,
819+
parameters: ParamsT = None,
818820
fetch_number: int | None = None,
819821
scroll: bool | None = None,
820822
prepared: bool = True,
@@ -906,7 +908,7 @@ class Connection:
906908
async def execute(
907909
self: Self,
908910
querystring: str,
909-
parameters: Sequence[Any] | None = None,
911+
parameters: ParamsT = None,
910912
prepared: bool = True,
911913
) -> QueryResult:
912914
"""Execute the query.
@@ -990,7 +992,7 @@ class Connection:
990992
async def fetch(
991993
self: Self,
992994
querystring: str,
993-
parameters: Sequence[Any] | None = None,
995+
parameters: ParamsT = None,
994996
prepared: bool = True,
995997
) -> QueryResult:
996998
"""Fetch the result from database.
@@ -1010,7 +1012,7 @@ class Connection:
10101012
async def fetch_row(
10111013
self: Self,
10121014
querystring: str,
1013-
parameters: Sequence[Any] | None = None,
1015+
parameters: ParamsT = None,
10141016
prepared: bool = True,
10151017
) -> SingleQueryResult:
10161018
"""Fetch exaclty single row from query.
@@ -1046,7 +1048,7 @@ class Connection:
10461048
async def fetch_val(
10471049
self: Self,
10481050
querystring: str,
1049-
parameters: Sequence[Any] | None = None,
1051+
parameters: ParamsT = None,
10501052
prepared: bool = True,
10511053
) -> Any:
10521054
"""Execute the query and return first value of the first row.
@@ -1100,7 +1102,7 @@ class Connection:
11001102
def cursor(
11011103
self: Self,
11021104
querystring: str,
1103-
parameters: Sequence[Any] | None = None,
1105+
parameters: ParamsT = None,
11041106
fetch_number: int | None = None,
11051107
scroll: bool | None = None,
11061108
prepared: bool = True,
@@ -1708,10 +1710,13 @@ class ConnectionPoolBuilder:
17081710
self: Self,
17091711
keepalives_retries: int,
17101712
) -> Self:
1711-
"""
1712-
Set the maximum number of TCP keepalive probes that will be sent before dropping a connection.
1713+
"""Keepalives Retries.
1714+
1715+
Set the maximum number of TCP keepalive probes
1716+
that will be sent before dropping a connection.
17131717
1714-
This is ignored for Unix domain sockets, or if the `keepalives` option is disabled.
1718+
This is ignored for Unix domain sockets,
1719+
or if the `keepalives` option is disabled.
17151720
17161721
### Parameters:
17171722
- `keepalives_retries`: number of retries.

python/psqlpy/_internal/extra_types.pyi

+14-4
Original file line numberDiff line numberDiff line change
@@ -143,9 +143,13 @@ class MacAddr8:
143143
class CustomType:
144144
def __init__(self, value: bytes) -> None: ...
145145

146-
Coordinates: TypeAlias = list[int | float] | set[int | float] | tuple[int | float, int | float]
146+
Coordinates: TypeAlias = (
147+
list[int | float] | set[int | float] | tuple[int | float, int | float]
148+
)
147149
PairsOfCoordinates: TypeAlias = (
148-
list[Coordinates | int | float] | set[Coordinates | int | float] | tuple[Coordinates | int | float, ...]
150+
list[Coordinates | int | float]
151+
| set[Coordinates | int | float]
152+
| tuple[Coordinates | int | float, ...]
149153
)
150154

151155
class Point:
@@ -227,7 +231,9 @@ class Circle:
227231

228232
def __init__(
229233
self: Self,
230-
value: list[int | float] | set[int | float] | tuple[int | float, int | float, int | float],
234+
value: list[int | float]
235+
| set[int | float]
236+
| tuple[int | float, int | float, int | float],
231237
) -> None:
232238
"""Create new instance of Circle.
233239
@@ -374,7 +380,11 @@ class IpAddressArray:
374380
def __init__(
375381
self: Self,
376382
inner: typing.Sequence[
377-
IPv4Address | IPv6Address | typing.Sequence[IPv4Address] | typing.Sequence[IPv6Address] | typing.Any,
383+
IPv4Address
384+
| IPv6Address
385+
| typing.Sequence[IPv4Address]
386+
| typing.Sequence[IPv6Address]
387+
| typing.Any,
378388
],
379389
) -> None:
380390
"""Create new instance of IpAddressArray.

python/tests/conftest.py

+22
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ def listener_table_name() -> str:
7373
return random_string()
7474

7575

76+
@pytest.fixture
77+
def map_parameters_table_name() -> str:
78+
return random_string()
79+
80+
7681
@pytest.fixture
7782
def number_database_records() -> int:
7883
return random.randint(10, 35)
@@ -161,6 +166,23 @@ async def create_table_for_listener_tests(
161166
)
162167

163168

169+
@pytest.fixture
170+
async def create_table_for_map_parameters_test(
171+
psql_pool: ConnectionPool,
172+
map_parameters_table_name: str,
173+
) -> AsyncGenerator[None, None]:
174+
connection = await psql_pool.connection()
175+
await connection.execute(
176+
f"CREATE TABLE {map_parameters_table_name}"
177+
"(id SERIAL, name VARCHAR(255),surname VARCHAR(255), age INT)",
178+
)
179+
180+
yield
181+
await connection.execute(
182+
f"DROP TABLE {map_parameters_table_name}",
183+
)
184+
185+
164186
@pytest.fixture
165187
async def test_cursor(
166188
psql_pool: ConnectionPool,

python/tests/test_connection.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,10 @@ async def test_execute_batch_method(psql_pool: ConnectionPool) -> None:
183183
connection = await psql_pool.connection()
184184
await connection.execute(querystring="DROP TABLE IF EXISTS execute_batch")
185185
await connection.execute(querystring="DROP TABLE IF EXISTS execute_batch2")
186-
query = "CREATE TABLE execute_batch (name VARCHAR);CREATE TABLE execute_batch2 (name VARCHAR);"
186+
query = (
187+
"CREATE TABLE execute_batch (name VARCHAR);"
188+
"CREATE TABLE execute_batch2 (name VARCHAR);"
189+
)
187190
async with psql_pool.acquire() as conn:
188191
await conn.execute_batch(querystring=query)
189192
await conn.execute(querystring="SELECT * FROM execute_batch")

0 commit comments

Comments
 (0)