Files
synapse/rust/src/database/postgres/mod.rs
T
Erik JohnstonandClaude Opus 4.8 780beaedb9 Add Postgres value mapping for the Rust database backend
Introduce the Python<->Postgres value-mapping layer that the forthcoming
Rust DBAPI2 backend will build on, plus the module scaffolding to hang it
off (`synapse_rust.database.postgres`).

`PgValue` is an owned representation of a bound parameter that implements
`tokio_postgres::types::ToSql`, encoding into Postgres' binary wire format
according to the column type from the prepared statement (so a single
Python `int` becomes INT2/INT4/INT8 as appropriate, with range checks).
`PythonPgFromSql` is the decode counterpart, turning a column's wire bytes
back into the natural Python object (and SQL NULL into `None`). The two
`accepts` lists are kept in sync via tests.

The `value` submodule is exposed as `pub` for now so its
not-yet-consumed public items don't trip clippy's `dead_code` lint; later
changes that wire it into the cursor/connection code tighten that back up.

Covers int / float / bool / str / bytes / None; lists and richer types
(json, decimal, timestamps) are left to a follow-up.

The unit tests exercise the whole mapping without a live Postgres server,
including the float4 lossy narrowing, integer width boundaries, the
WrongType and out-of-range paths, and the unsupported/non-UTF-8 decode
errors.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-10-12 18:20:43 +00:00

38 lines
1.6 KiB
Rust

//! [`tokio_postgres`]-backed Postgres backend for the Rust `database` module.
//!
//! This module will grow the Python-facing `Connection` / `Cursor` classes and
//! the `connect` factory; for now it hosts the value-mapping layer ([`value`])
//! that converts between Python objects and Postgres' binary wire format.
//!
//! The driver itself is async; the eventual `Connection` / `Cursor` types will
//! drive it from sync Python methods via a shared multi-thread tokio runtime.
use pyo3::prelude::*;
use pyo3::types::PyModule;
// `pub` (rather than private) so the value-mapping types are reachable from the
// crate root as public API while nothing inside the crate consumes them yet.
// This is what stops clippy's `dead_code` lint from firing on them before the
// cursor/connection code (added in later changes) wires them up; the visibility
// is tightened back to private once that happens.
pub mod value;
/// Register the `postgres` submodule under the parent `database` module.
///
/// The `Connection` / `Cursor` classes and the `connect` factory are added in
/// later changes; for now this just creates the (otherwise empty) submodule so
/// the module tree — and the `value` mapping layer hanging off it — exists.
pub fn register_module(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
let child = PyModule::new(py, "postgres")?;
m.add_submodule(&child)?;
// We need to manually add the module to sys.modules to make `from
// synapse.synapse_rust.database import postgres` work.
py.import("sys")?
.getattr("modules")?
.set_item("synapse.synapse_rust.database.postgres", child)?;
Ok(())
}