Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for named parameters #129

Merged
merged 4 commits into from
Feb 22, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,5 @@ pgvector = { git = "https://github.com/chandr-andr/pgvector-rust.git", branch =
] }
futures-channel = "0.3.31"
futures = "0.3.31"
regex = "1.11.1"
once_cell = "1.20.3"
1 change: 1 addition & 0 deletions docs/.vuepress/sidebar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export default sidebar({
prefix: "usage/",
collapsible: true,
children: [
"parameters",
{
text: "Types",
prefix: "types/",
Expand Down
42 changes: 42 additions & 0 deletions docs/usage/parameters.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
---
title: Passing parameters to SQL queries
---

We support two variant of passing parameters to sql queries.

::: tabs
@tab Parameters sequence

You can pass parameters as some python Sequence.

Placeholders in querystring must be marked as `$1`, `$2` and so on,
depending on how many parameters you have.

```python
async def main():
...

await connection.execute(
querystring="SELECT * FROM users WHERE id = $1",
parameters=(101,),
)
```

@tab Parameters mapping

If you prefer use named arguments, we support it too.
Placeholder in querystring must look like `$(parameter)p`.

If you don't pass parameter but have it in querystring, exception will be raised.

```python
async def main():
...

await connection.execute(
querystring="SELECT * FROM users WHERE id = $(user_id)p",
parameters=dict(user_id=101),
)
```

:::
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ warn_unused_ignores = false
[tool.ruff]
fix = true
unsafe-fixes = true
line-length = 120
line-length = 89
exclude = [".venv/", "psqlpy-stress"]

[tool.ruff.format]
Expand Down
41 changes: 23 additions & 18 deletions python/psqlpy/_internal/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import types
from enum import Enum
from io import BytesIO
from ipaddress import IPv4Address, IPv6Address
from typing import Any, Awaitable, Callable, Sequence, TypeVar
from typing import Any, Awaitable, Callable, Mapping, Sequence, TypeVar

from typing_extensions import Buffer, Self
from typing_extensions import Buffer, Self, TypeAlias

_CustomClass = TypeVar(
"_CustomClass",
Expand All @@ -13,6 +13,8 @@ _RowFactoryRV = TypeVar(
"_RowFactoryRV",
)

ParamsT: TypeAlias = Sequence[Any] | Mapping[str, Any] | None

class QueryResult:
"""Result."""

Expand Down Expand Up @@ -150,7 +152,7 @@ class SingleQueryResult:

class SynchronousCommit(Enum):
"""
Class for synchronous_commit option for transactions.
Synchronous_commit option for transactions.

### Variants:
- `On`: The meaning may change based on whether you have
Expand Down Expand Up @@ -181,7 +183,7 @@ class SynchronousCommit(Enum):
RemoteApply = 5

class IsolationLevel(Enum):
"""Class for Isolation Level for transactions."""
"""Isolation Level for transactions."""

ReadUncommitted = 1
ReadCommitted = 2
Expand Down Expand Up @@ -290,7 +292,7 @@ class Cursor:

cursor_name: str
querystring: str
parameters: Sequence[Any]
parameters: ParamsT = None
prepared: bool | None
conn_dbname: str | None
user: str | None
Expand Down Expand Up @@ -464,7 +466,7 @@ class Transaction:
async def execute(
self: Self,
querystring: str,
parameters: Sequence[Any] | None = None,
parameters: ParamsT = None,
prepared: bool = True,
) -> QueryResult:
"""Execute the query.
Expand Down Expand Up @@ -554,7 +556,7 @@ class Transaction:
async def fetch(
self: Self,
querystring: str,
parameters: Sequence[Any] | None = None,
parameters: ParamsT = None,
prepared: bool = True,
) -> QueryResult:
"""Fetch the result from database.
Expand All @@ -574,7 +576,7 @@ class Transaction:
async def fetch_row(
self: Self,
querystring: str,
parameters: Sequence[Any] | None = None,
parameters: ParamsT = None,
prepared: bool = True,
) -> SingleQueryResult:
"""Fetch exaclty single row from query.
Expand Down Expand Up @@ -613,7 +615,7 @@ class Transaction:
async def fetch_val(
self: Self,
querystring: str,
parameters: Sequence[Any] | None = None,
parameters: ParamsT = None,
prepared: bool = True,
) -> Any | None:
"""Execute the query and return first value of the first row.
Expand Down Expand Up @@ -814,7 +816,7 @@ class Transaction:
def cursor(
self: Self,
querystring: str,
parameters: Sequence[Any] | None = None,
parameters: ParamsT = None,
fetch_number: int | None = None,
scroll: bool | None = None,
prepared: bool = True,
Expand Down Expand Up @@ -906,7 +908,7 @@ class Connection:
async def execute(
self: Self,
querystring: str,
parameters: Sequence[Any] | None = None,
parameters: ParamsT = None,
prepared: bool = True,
) -> QueryResult:
"""Execute the query.
Expand Down Expand Up @@ -990,7 +992,7 @@ class Connection:
async def fetch(
self: Self,
querystring: str,
parameters: Sequence[Any] | None = None,
parameters: ParamsT = None,
prepared: bool = True,
) -> QueryResult:
"""Fetch the result from database.
Expand All @@ -1010,7 +1012,7 @@ class Connection:
async def fetch_row(
self: Self,
querystring: str,
parameters: Sequence[Any] | None = None,
parameters: ParamsT = None,
prepared: bool = True,
) -> SingleQueryResult:
"""Fetch exaclty single row from query.
Expand Down Expand Up @@ -1046,7 +1048,7 @@ class Connection:
async def fetch_val(
self: Self,
querystring: str,
parameters: Sequence[Any] | None = None,
parameters: ParamsT = None,
prepared: bool = True,
) -> Any:
"""Execute the query and return first value of the first row.
Expand Down Expand Up @@ -1100,7 +1102,7 @@ class Connection:
def cursor(
self: Self,
querystring: str,
parameters: Sequence[Any] | None = None,
parameters: ParamsT = None,
fetch_number: int | None = None,
scroll: bool | None = None,
prepared: bool = True,
Expand Down Expand Up @@ -1708,10 +1710,13 @@ class ConnectionPoolBuilder:
self: Self,
keepalives_retries: int,
) -> Self:
"""
Set the maximum number of TCP keepalive probes that will be sent before dropping a connection.
"""Keepalives Retries.

Set the maximum number of TCP keepalive probes
that will be sent before dropping a connection.

This is ignored for Unix domain sockets, or if the `keepalives` option is disabled.
This is ignored for Unix domain sockets,
or if the `keepalives` option is disabled.

### Parameters:
- `keepalives_retries`: number of retries.
Expand Down
18 changes: 14 additions & 4 deletions python/psqlpy/_internal/extra_types.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,13 @@ class MacAddr8:
class CustomType:
def __init__(self, value: bytes) -> None: ...

Coordinates: TypeAlias = list[int | float] | set[int | float] | tuple[int | float, int | float]
Coordinates: TypeAlias = (
list[int | float] | set[int | float] | tuple[int | float, int | float]
)
PairsOfCoordinates: TypeAlias = (
list[Coordinates | int | float] | set[Coordinates | int | float] | tuple[Coordinates | int | float, ...]
list[Coordinates | int | float]
| set[Coordinates | int | float]
| tuple[Coordinates | int | float, ...]
)

class Point:
Expand Down Expand Up @@ -227,7 +231,9 @@ class Circle:

def __init__(
self: Self,
value: list[int | float] | set[int | float] | tuple[int | float, int | float, int | float],
value: list[int | float]
| set[int | float]
| tuple[int | float, int | float, int | float],
) -> None:
"""Create new instance of Circle.

Expand Down Expand Up @@ -374,7 +380,11 @@ class IpAddressArray:
def __init__(
self: Self,
inner: typing.Sequence[
IPv4Address | IPv6Address | typing.Sequence[IPv4Address] | typing.Sequence[IPv6Address] | typing.Any,
IPv4Address
| IPv6Address
| typing.Sequence[IPv4Address]
| typing.Sequence[IPv6Address]
| typing.Any,
],
) -> None:
"""Create new instance of IpAddressArray.
Expand Down
22 changes: 22 additions & 0 deletions python/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ def listener_table_name() -> str:
return random_string()


@pytest.fixture
def map_parameters_table_name() -> str:
return random_string()


@pytest.fixture
def number_database_records() -> int:
return random.randint(10, 35)
Expand Down Expand Up @@ -161,6 +166,23 @@ async def create_table_for_listener_tests(
)


@pytest.fixture
async def create_table_for_map_parameters_test(
psql_pool: ConnectionPool,
map_parameters_table_name: str,
) -> AsyncGenerator[None, None]:
connection = await psql_pool.connection()
await connection.execute(
f"CREATE TABLE {map_parameters_table_name}"
"(id SERIAL, name VARCHAR(255),surname VARCHAR(255), age INT)",
)

yield
await connection.execute(
f"DROP TABLE {map_parameters_table_name}",
)


@pytest.fixture
async def test_cursor(
psql_pool: ConnectionPool,
Expand Down
5 changes: 4 additions & 1 deletion python/tests/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,10 @@ async def test_execute_batch_method(psql_pool: ConnectionPool) -> None:
connection = await psql_pool.connection()
await connection.execute(querystring="DROP TABLE IF EXISTS execute_batch")
await connection.execute(querystring="DROP TABLE IF EXISTS execute_batch2")
query = "CREATE TABLE execute_batch (name VARCHAR);CREATE TABLE execute_batch2 (name VARCHAR);"
query = (
"CREATE TABLE execute_batch (name VARCHAR);"
"CREATE TABLE execute_batch2 (name VARCHAR);"
)
async with psql_pool.acquire() as conn:
await conn.execute_batch(querystring=query)
await conn.execute(querystring="SELECT * FROM execute_batch")
Expand Down
Loading
Loading