Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/launchbadge/sqlx/llms.txt

Use this file to discover all available pages before exploring further.

SQLx’s PostgreSQL driver is written in pure Rust using zero unsafe code. It speaks the PostgreSQL wire protocol directly, supporting TLS, asynchronous notifications via LISTEN/NOTIFY, advisory locks, and the full range of PostgreSQL-native types including arrays, JSONB, UUID, ranges, and custom enums. The driver is runtime-agnostic and works with both tokio and async-std.

Feature flag

Enable the driver by adding the postgres feature to your Cargo.toml. You must also choose a runtime and, if needed, a TLS backend.
[dependencies]
# tokio + rustls (recommended)
sqlx = { version = "0.8", features = ["runtime-tokio", "tls-rustls-ring-webpki", "postgres"] }

# tokio + native-tls
sqlx = { version = "0.8", features = ["runtime-tokio", "tls-native-tls", "postgres"] }

# async-std, no TLS
sqlx = { version = "0.8", features = ["runtime-async-std", "postgres"] }

Connecting

Connection URL

The simplest way to connect is with a postgres:// URL:
postgres://[user[:password]@][host][:port]/[database][?param=value]
use sqlx::postgres::PgPoolOptions;

let pool = PgPoolOptions::new()
    .max_connections(5)
    .connect("postgres://postgres:password@localhost/mydb")
    .await?;

PgConnectOptions

For programmatic configuration, use PgConnectOptions. It reads standard PG* environment variables (PGHOST, PGPORT, PGUSER, PGPASSWORD, PGDATABASE) when constructed with PgConnectOptions::new().
use sqlx::postgres::{PgConnectOptions, PgSslMode, PgPoolOptions};

let opts = PgConnectOptions::new()
    .host("localhost")
    .port(5432)
    .username("postgres")
    .password("secret")
    .database("mydb")
    .ssl_mode(PgSslMode::Require)
    .application_name("my-service");

let pool = PgPoolOptions::new()
    .max_connections(10)
    .connect_with(opts)
    .await?;
You can also parse a URL string and then override individual fields:
use sqlx::postgres::PgConnectOptions;

let opts: PgConnectOptions = "postgres://postgres@localhost/mydb".parse()?;
let opts = opts.application_name("my-service");

Key options

OptionDefaultDescription
hostlocalhost (or Unix socket if available)Hostname or path to a Unix domain socket directory
port5432TCP port
usernameOS usernameLogin role
passwordNonePassword (also read from ~/.pgpass)
databaseSame as usernameTarget database
ssl_modePreferTLS negotiation mode (see below)
application_nameNoneShown in pg_stat_activity
statement_cache_capacity100LRU prepared-statement cache size per connection

TLS and PgSslMode

PgSslMode controls TLS negotiation. Pass it to .ssl_mode() on PgConnectOptions or set sslmode= in the URL.
VariantBehaviour
DisableNever use TLS
AllowTry plain first; fall back to TLS
PreferTry TLS first; fall back to plain (default)
RequireRequire TLS; verify CA if a root cert is present
VerifyCaRequire TLS; verify the server certificate is signed by a trusted CA
VerifyFullLike VerifyCa, plus hostname verification
use sqlx::postgres::{PgConnectOptions, PgSslMode};

let opts = PgConnectOptions::new()
    .ssl_mode(PgSslMode::VerifyFull)
    .ssl_root_cert("./ca.crt")
    .ssl_client_cert("./client.crt")
    .ssl_client_key("./client.key");
Using rustls requires TLS 1.2 or later. If you see HandshakeFailure when connecting to an older server, switch to tls-native-tls which uses the system TLS stack with broader version support.

Connection pooling

PgPool (an alias for Pool<Postgres>) manages a pool of connections. Prefer PgPool over individual connections in application code.
use sqlx::PgPool;

let pool = PgPool::connect("postgres://postgres:password@localhost/mydb").await?;

LISTEN / NOTIFY

PgListener provides an async stream of server-sent notifications. It automatically reconnects on connection loss and re-subscribes to all channels.
use sqlx::postgres::PgListener;

let mut listener = PgListener::connect("postgres://postgres:password@localhost/mydb").await?;
listener.listen("events").await?;
listener.listen_all(["orders", "shipments"]).await?;

loop {
    let notification = listener.recv().await?;
    println!(
        "channel={} payload={}",
        notification.channel(),
        notification.payload()
    );
}
To send a notification from another connection:
SELECT pg_notify('events', 'hello from SQL');
sqlx::query("SELECT pg_notify($1, $2)")
    .bind("events")
    .bind("hello from Rust")
    .execute(&pool)
    .await?;
PgListener::connect_with lets you share an existing PgPool rather than creating a dedicated internal pool.

Advisory locks

PostgreSQL advisory locks are application-level cooperative locks keyed by a 64-bit integer. They are useful for distributed mutual exclusion.
// Acquire a session-level advisory lock (released when the connection is returned to the pool)
sqlx::query("SELECT pg_advisory_lock($1)")
    .bind(12345_i64)
    .execute(&pool)
    .await?;

// Try a non-blocking acquire; returns true if the lock was obtained
let (locked,): (bool,) = sqlx::query_as("SELECT pg_try_advisory_lock($1)")
    .bind(12345_i64)
    .fetch_one(&pool)
    .await?;

// Release the lock explicitly
sqlx::query("SELECT pg_advisory_unlock($1)")
    .bind(12345_i64)
    .execute(&pool)
    .await?;

PostgreSQL-specific types

Enable additional Cargo features to unlock type mappings:

uuid

Maps UUIDuuid::Uuid. Enable with features = ["uuid"].

json

Maps JSON / JSONBserde_json::Value. Enable with features = ["json"].

chrono

Maps TIMESTAMP, DATE, TIMEchrono types. Enable with features = ["chrono"].

bigdecimal

Maps NUMERICbigdecimal::BigDecimal. Enable with features = ["bigdecimal"].

Built-in type mapping

PostgreSQL typeRust type
BOOLbool
SMALLINT / INT2i16
INTEGER / INT4i32
BIGINT / INT8i64
REAL / FLOAT4f32
DOUBLE PRECISION / FLOAT8f64
TEXT / VARCHAR / CHARString / &str
BYTEAVec<u8> / &[u8]
UUIDuuid::Uuid (feature: uuid)
JSON / JSONBserde_json::Value (feature: json)
INET / CIDRipnetwork::IpNetwork (feature: ipnetwork) or ipnet::IpNet (feature: ipnet)
TIMESTAMPchrono::NaiveDateTime (feature: chrono)
TIMESTAMPTZchrono::DateTime<Utc> (feature: chrono)
DATEchrono::NaiveDate (feature: chrono)
NUMERICbigdecimal::BigDecimal (feature: bigdecimal)
T[] (array)Vec<T>

Custom / enum types

Use #[derive(sqlx::Type)] to map a Rust enum to a PostgreSQL enum type:
#[derive(sqlx::Type)]
#[sqlx(type_name = "mood", rename_all = "lowercase")]
enum Mood {
    Happy,
    Sad,
    Neutral,
}

WHERE IN with arrays

PostgreSQL supports binding a Vec directly using = ANY($1), which avoids generating a different query plan for every possible array length:
let ids: Vec<i64> = vec![1, 2, 3];

let rows = sqlx::query!(
    "SELECT * FROM orders WHERE id = ANY($1)",
    &ids[..]
)
.fetch_all(&pool)
.await?;
!= ANY($1) is not equivalent to NOT IN (...). Use != ALL($1) for that behaviour.

Bulk inserts with UNNEST

Pass multiple arrays to UNNEST() to perform efficient bulk inserts without generating combinatorially different query plans:
let texts: Vec<String> = vec!["alpha".into(), "beta".into()];
let counts: Vec<i64> = vec![10, 20];

sqlx::query!(
    "INSERT INTO items(name, count)
     SELECT * FROM UNNEST($1::text[], $2::int8[])",
    &texts[..],
    &counts[..]
)
.execute(&pool)
.await?;
The explicit casts (e.g. $1::text[]) tell Postgres the array element type at prepare time, which is required for the query! macro to typecheck successfully.

Build docs developers (and LLMs) love