From 477c52da330aa51236dbfec547432015ff8daa87 Mon Sep 17 00:00:00 2001 From: "Renan A. Fonseca" Date: Thu, 19 Feb 2026 23:13:03 +0100 Subject: [PATCH] sqlx-postgres: add support to abstract sockets PostgreSQL treats hosts starting by @ as abstract sockets (linux only). This patch modifies parse to correctly read host parameter as socket in these cases. It also modifies fetch_socket to provide the proper argument to net::connect_uds. --- sqlx-postgres/src/options/mod.rs | 5 ++++- sqlx-postgres/src/options/parse.rs | 14 ++++++++++++-- tests/postgres/postgres.rs | 17 +++++++++++++++++ 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/sqlx-postgres/src/options/mod.rs b/sqlx-postgres/src/options/mod.rs index 21e6628cae..7b8d05dcda 100644 --- a/sqlx-postgres/src/options/mod.rs +++ b/sqlx-postgres/src/options/mod.rs @@ -457,7 +457,10 @@ impl PgConnectOptions { pub(crate) fn fetch_socket(&self) -> Option { match self.socket { Some(ref socket) => { - let full_path = format!("{}/.s.PGSQL.{}", socket.display(), self.port); + let mut full_path = format!("{}/.s.PGSQL.{}", socket.display(), self.port); + if full_path.starts_with("@") { + full_path.replace_range(0..1, "\0") + } Some(full_path) } None if self.host.starts_with('/') => { diff --git a/sqlx-postgres/src/options/parse.rs b/sqlx-postgres/src/options/parse.rs index e911305698..02dc27e677 100644 --- a/sqlx-postgres/src/options/parse.rs +++ b/sqlx-postgres/src/options/parse.rs @@ -12,7 +12,9 @@ impl PgConnectOptions { if let Some(host) = url.host_str() { let host_decoded = percent_decode_str(host); options = match host_decoded.clone().next() { - Some(b'/') => options.socket(&*host_decoded.decode_utf8().map_err(Error::config)?), + Some(b'/') | Some(b'@') => { + options.socket(&*host_decoded.decode_utf8().map_err(Error::config)?) + } _ => options.host(host), } } @@ -67,7 +69,7 @@ impl PgConnectOptions { } "host" => { - if value.starts_with('/') { + if value.starts_with('/') || value.starts_with('@') { options = options.socket(&*value); } else { options = options.host(&value); @@ -188,6 +190,14 @@ fn it_parses_socket_correctly_from_parameter() { assert_eq!(Some("/var/run/postgres/".into()), opts.socket); } +#[test] +fn it_parses_abstract_socket_correctly_from_parameter() { + let url = "postgres:///?host=@pgdata"; + let opts = PgConnectOptions::from_str(url).unwrap(); + + assert_eq!(Some("@pgdata".into()), opts.socket); +} + #[test] fn it_parses_host_correctly_from_parameter() { let url = "postgres:///?host=google.database.com"; diff --git a/tests/postgres/postgres.rs b/tests/postgres/postgres.rs index 06adf0ca7f..0feef9eaa1 100644 --- a/tests/postgres/postgres.rs +++ b/tests/postgres/postgres.rs @@ -28,6 +28,23 @@ async fn it_connects() -> anyhow::Result<()> { Ok(()) } +#[cfg(any(target_os = "linux", target_os = "android"))] +#[ignore = "requires a PostgreSQL instance listening on the abstract Unix socket @pg_data"] +#[sqlx_macros::test] +async fn it_connects_via_abstract_socket() -> anyhow::Result<()> { + setup_if_needed(); + + let opts: PgConnectOptions = env::var("DATABASE_URL")?.parse().unwrap(); + let opts = opts.socket("@pg_data"); + + let mut conn = PgConnection::connect_with(&opts).await?; + + let value: i32 = sqlx::query_scalar("SELECT 1").fetch_one(&mut conn).await?; + assert_eq!(1, value); + + Ok(()) +} + #[sqlx_macros::test] async fn it_can_select_void() -> anyhow::Result<()> { let mut conn = new::().await?;