mirror of
https://github.com/element-hq/synapse.git
synced 2026-08-22 07:59:54 +00:00
Use libpq's default host when the DSN omits one
`tokio_postgres` and libpq disagree on the default host when a DSN gives no `host=`: tokio-postgres falls back to localhost, whereas libpq (which `psql` and the rest of Synapse use) applies its configurable compiled-in default — typically the Unix socket directory — and honours `PGHOST`. To keep Synapse's existing connection behaviour, `connect()` now runs the DSN through `fixup_default_host`: if the parsed config has neither a host nor a hostaddr, it asks libpq what its default would be (via `PQconnectStart` on an empty conninfo, which applies libpq's defaults/env without opening a socket) and sets that on the tokio-postgres config. The libpq call lives in a small hand-written safe wrapper (`database::postgres::libpq`) over the `pq-sys` crate. pq-sys ships pre-generated bindings and links the system libpq itself, so — unlike a bindgen-based binding crate — this needs no libclang to build. The behaviour is exercised by the Python integration tests. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
5b734670a3
commit
d7ae4dc645
@@ -1,8 +1,11 @@
|
||||
//! [`tokio_postgres`]-backed `Connection` / `Cursor` types exposed to Python.
|
||||
//!
|
||||
//! The driver itself is async; we drive it from sync Python methods via a
|
||||
//! shared multi-thread tokio runtime (see `super::runtime`).
|
||||
//! The driver itself is async; we drive it from sync Python methods via the
|
||||
//! extension's shared multi-thread tokio runtime (see [`crate::tokio_runtime`]).
|
||||
//! [`connect`] takes the runtime's handle from the reactor once and hands it to
|
||||
//! the [`Connection`], which carries it for the life of the connection.
|
||||
|
||||
use anyhow::Error;
|
||||
use log::warn;
|
||||
use pyo3::exceptions::PyRuntimeError;
|
||||
use pyo3::prelude::*;
|
||||
@@ -14,6 +17,7 @@ use crate::tokio_runtime::runtime_handle;
|
||||
mod connection;
|
||||
mod cursor_state;
|
||||
mod helpers;
|
||||
mod libpq;
|
||||
mod value;
|
||||
|
||||
/// Register the `postgres` submodule (the `Connection` / `Cursor` classes and
|
||||
@@ -59,9 +63,8 @@ fn connect<'py>(
|
||||
) -> PyResult<Bound<'py, connection::Connection>> {
|
||||
let handle = runtime_handle(reactor)?;
|
||||
|
||||
let config = dsn
|
||||
.parse::<tokio_postgres::Config>()
|
||||
.map_err(|e| PyRuntimeError::new_err(format!("Failed to parse DSN: {e}")))?;
|
||||
let config = fixup_default_host(dsn)
|
||||
.map_err(|e| PyRuntimeError::new_err(format!("Failed to prepare DSN: {e}")))?;
|
||||
|
||||
// TLS is not yet supported: unlike libpq (whose default is
|
||||
// `sslmode=prefer`), we never negotiate TLS regardless of the DSN's
|
||||
@@ -81,3 +84,29 @@ fn connect<'py>(
|
||||
|
||||
Bound::new(py, conn)
|
||||
}
|
||||
|
||||
/// Fix up a DSN to ensure it has a host, using libpq's default host if
|
||||
/// necessary.
|
||||
///
|
||||
/// [`tokio_postgres`] has a different default host than libpq, which is what
|
||||
/// Synapse previously used (and is what e.g. `psql` uses). libpq's default host
|
||||
/// is configurable, so when the DSN omits a host we ask libpq what its default
|
||||
/// would be and use that instead (see [`libpq::default_host`]).
|
||||
fn fixup_default_host(dsn: &str) -> Result<tokio_postgres::Config, Error> {
|
||||
let mut config = dsn.parse::<tokio_postgres::Config>()?;
|
||||
|
||||
// `tokio_postgres` parses only the DSN string (it does not consult `PGHOST`
|
||||
// or the compiled-in default), so an empty host list means the DSN really
|
||||
// omitted the host. A DSN that gives a `hostaddr` instead of a `host` is
|
||||
// still connectable as-is, so leave it alone too — injecting a default host
|
||||
// there would just confuse TLS/SNI.
|
||||
if !config.get_hosts().is_empty() || !config.get_hostaddrs().is_empty() {
|
||||
return Ok(config);
|
||||
}
|
||||
|
||||
// Resolve libpq's default host without connecting (see `libpq::default_host`).
|
||||
let host = libpq::default_host()?;
|
||||
config.host(&host);
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user