Files
synapse/synapse/synapse_rust/database/postgres.pyi
T
Erik JohnstonandClaude Opus 4.8 2e6fdbbfee Add Postgres Connection/Cursor types and connect()
Add the Python-facing `Connection` / `Cursor` pair and the `connect`
factory, implementing enough of the PEP-249 (DBAPI2) shape for Synapse's
needs.

A `Connection` owns a single `tokio_postgres::Client`. The client *moves*
between the connection and an in-flight cursor rather than being shared:
it lives in the connection between interactions, is taken out for the
duration of a cursor, and is handed back when the cursor finishes. That
single-owner baton (an `Option<Client>` slot on each side) makes it
structurally impossible to use the connection mid-transaction or to run
two overlapping transactions on one socket — both become a clean "already
closed" error.

The transaction lifecycle (`cursor` opens with `BEGIN`; `finish`
COMMIT/ROLLBACKs and hands the client back; `CursorGuard` rolls back an
abandoned transaction on drop) is included here. On any
transaction-control error the client is dropped rather than returned,
closing the socket — safer than handing a possibly-broken connection back
to what will become a connection pool.

`connect()` parses a libpq-style DSN, blocks until connected, and spawns
the long-lived connection task onto the shared runtime (the libpq
default-host fixup is a follow-up). The cursor query methods
(`execute` / `fetch_one` / `fetch_all` / `fetch_next_batch` / `rowcount`)
delegate to the `CursorQueryState` machine.

`run_interaction` — the high-level glue that opens a cursor, runs the
callback, and commits/rolls back — follows in the next change, so
`cursor`/`finish`/`CursorGuard` carry a transitional `#[allow(dead_code)]`
until then. Now that the value/helpers/cursor_state modules are consumed
internally, their visibility is tightened from `pub` back to private.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 09:04:57 +00:00

90 lines
3.2 KiB
Python

# This file is licensed under the Affero General Public License (AGPL) version 3.
#
# Copyright (C) 2026 Element Creations Ltd
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# See the GNU Affero General Public License for more details:
# <https://www.gnu.org/licenses/agpl-3.0.html>.
from types import TracebackType
from typing import Any, Optional, Sequence
class ConnectionPool:
"""A pool of native `tokio_postgres` connections.
Built once from a libpq-style DSN; connections are opened lazily. See
:mod:`synapse.storage.rust_dbapi` / :mod:`synapse.storage.rust_pool` for the
DBAPI2 / Twisted adapters that wrap it.
"""
def __init__(
self,
dsn: str,
max_size: int = 10,
*,
synchronous_commit: bool = True,
statement_timeout_ms: Optional[int] = None,
) -> None: ...
def connect(self) -> Connection:
"""Check a connection out of the pool, blocking until one is available.
Raises an ``OperationalError`` if the configured ``checkout_timeout_ms``
elapses first.
"""
def close(self) -> None: ...
class Connection:
"""A connection checked out of a :class:`ConnectionPool`."""
@property
def autocommit(self) -> bool: ...
def cursor(self) -> Cursor: ...
def commit(self) -> None: ...
def rollback(self) -> None: ...
def close(self) -> None: ...
def is_closed(self) -> bool: ...
def in_transaction(self) -> bool: ...
def set_autocommit(self, autocommit: bool) -> None: ...
def __enter__(self) -> Connection: ...
def __exit__(
self,
exc_type: Optional[type[BaseException]],
exc: Optional[BaseException],
tb: Optional[TracebackType],
) -> bool: ...
class Cursor:
"""A PEP-249-style cursor over a connection's current transaction.
Result-fetching uses ``fetch_one`` / ``fetch_all`` / ``fetch_next_batch`` and
``rowcount`` / ``description`` as *methods* (not DBAPI2 properties); the
:class:`synapse.storage.rust_dbapi.Cursor` adapter presents the DBAPI2 shape.
"""
def execute(self, query: str, params: Optional[Sequence[Any]] = None) -> None: ...
def executemany(self, query: str, params_seq: Sequence[Sequence[Any]]) -> None: ...
def executescript(self, script: str) -> None: ...
def fetch_one(self) -> Optional[tuple[Any, ...]]: ...
def fetch_all(self) -> list[tuple[Any, ...]]: ...
def fetch_next_batch(self, capacity: int = 100) -> list[tuple[Any, ...]]: ...
def rowcount(self) -> int: ...
def description(self) -> Optional[list[tuple[Any, ...]]]: ...
def close(self) -> None: ...
# The PEP-249 exception hierarchy raised by the backend. `DatabaseError` and its
# subclasses carry the SQLSTATE as `pgcode` (a 5-character string, or `None`),
# matching psycopg2 so engine code such as `is_deadlock` can read `error.pgcode`.
class Error(Exception): ...
class DatabaseError(Error):
pgcode: Optional[str]
class OperationalError(DatabaseError): ...
class IntegrityError(DatabaseError): ...
class ProgrammingError(DatabaseError): ...