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 bridges Rust and SQL through three core traits: Type, Encode, and Decode. Every value you bind as a query parameter or read back from a row passes through this system. Understanding how the traits compose helps you extend SQLx with custom types and choose the right feature flags for your project.

The three core traits

Type

Declares which SQL type corresponds to a Rust type and whether a given SQL type is compatible at runtime.

Encode

Serialises a Rust value into the database’s wire format when binding query parameters.

Decode

Deserialises a raw database value into a Rust type when reading a row column.

The Type trait

Type<DB> tells SQLx what SQL type a Rust type corresponds to and how to check compatibility:
pub trait Type<DB: Database> {
    /// Returns the canonical SQL type for this Rust type.
    fn type_info() -> DB::TypeInfo;

    /// Returns true if this Rust type is compatible with the given SQL type.
    fn compatible(ty: &DB::TypeInfo) -> bool {
        Self::type_info().type_compatible(ty)
    }
}
type_info() is called when binding arguments so the database can plan the query correctly. compatible() is checked when decoding a column value to prevent silent type mismatches at runtime. Option<T> automatically implements Type<DB> for any T: Type<DB>. The SQL type is the same as the inner type; NULL is handled at the Encode/Decode level.

The Encode trait

Encode<'q, DB> converts a Rust value into bytes in the buffer that SQLx sends to the database:
pub trait Encode<'q, DB: Database> {
    fn encode(self, buf: &mut DB::ArgumentBuffer) -> Result<IsNull, BoxDynError>;

    fn encode_by_ref(&self, buf: &mut DB::ArgumentBuffer) -> Result<IsNull, BoxDynError>;
}
encode is preferred because it can move the value and reuse existing allocations. encode_by_ref is the required method; encode has a default implementation that delegates to it. The return type, IsNull, signals whether the encoded value should be treated as SQL NULL:
pub enum IsNull {
    Yes, // value is NULL; no bytes were written
    No,  // value is not NULL
}
Smart pointer types (Arc<T>, Box<T>, Rc<T>, Cow<'_, T>) all forward to the inner Encode implementation automatically, so you can bind owned or borrowed values interchangeably.

The Decode trait

Decode<'r, DB> reads a raw value from the database wire format into a Rust type:
pub trait Decode<'r, DB: Database>: Sized {
    fn decode(value: DB::ValueRef<'r>) -> Result<Self, BoxDynError>;
}
The 'r lifetime ties the decoded value to the lifetime of the row being read. Option<T> is implemented automatically: if the column value is NULL, None is returned without calling T::decode. A common pattern is to delegate decoding to a type you already support, then parse from there:
impl<'r, DB: Database> Decode<'r, DB> for MyType
where
    &'r str: Decode<'r, DB>,
{
    fn decode(value: DB::ValueRef<'r>) -> Result<Self, BoxDynError> {
        let s = <&str as Decode<DB>>::decode(value)?;
        Ok(s.parse()?)
    }
}

Built-in type mappings

SQLx ships with implementations for common Rust primitives. The exact SQL type names vary by database; the table below lists the Rust types and their general SQL counterparts.
Rust typeSQL type
boolBOOLEAN
i8TINYINT / SMALLINT
i16SMALLINT
i32INT / INTEGER
i64BIGINT
u8TINYINT UNSIGNED
u16SMALLINT UNSIGNED
u32INT UNSIGNED
u64BIGINT UNSIGNED
f32FLOAT / REAL
f64DOUBLE / DOUBLE PRECISION
String / &strVARCHAR / TEXT / CHAR
Vec<u8> / &[u8]BLOB / BYTEA / VARBINARY
Option<T>Nullable version of T’s SQL type
Not all integer widths are supported by every database. PostgreSQL does not have unsigned integer types; MySQL has all widths listed. Check your database’s type documentation for details.

Optional type integrations

Additional Rust types are enabled through Cargo feature flags. Add the relevant features to your Cargo.toml [dependencies] entry for sqlx.
Feature flagRust type(s)SQL type
uuiduuid::UuidUUID
chronochrono::NaiveDate, NaiveTime, NaiveDateTime, DateTime<Tz>DATE, TIME, TIMESTAMP, TIMESTAMPTZ
timetime::Date, Time, PrimitiveDateTime, OffsetDateTimeDATE, TIME, TIMESTAMP, TIMESTAMPTZ
jsonserde_json::Value, sqlx::types::Json<T>, sqlx::types::JsonRawValueJSON, JSONB
bigdecimalbigdecimal::BigDecimalNUMERIC / DECIMAL
rust_decimalrust_decimal::DecimalNUMERIC / DECIMAL
ipnetipnet::IpNet, Ipv4Net, Ipv6NetINET, CIDR (PostgreSQL only)
ipnetworkipnetwork::IpNetwork, Ipv4Network, Ipv6NetworkINET, CIDR (PostgreSQL only)
mac_addressmac_address::MacAddressMACADDR (PostgreSQL only)
bit-vecbit_vec::BitVecBIT, VARBIT (PostgreSQL only)
bstrbstr::BStringBLOB / BYTEA
Enable one or more of these in Cargo.toml:
[dependencies]
sqlx = { version = "0.8", features = ["runtime-tokio", "postgres", "uuid", "chrono", "json"] }
chrono and time cover overlapping SQL types. If you enable both, the query! macro prefers time. To control which crate takes precedence, configure preferred_crates in sqlx.toml. Avoid enabling both unless you have a specific need.

Resolving chrono / time ambiguity with sqlx.toml

When both chrono and time are enabled, add an sqlx.toml at your workspace root to tell the macro which crate to map date/time columns to:
# sqlx.toml
[macros.preferred_crates]
date-time = "chrono"   # or "time"
This only affects the query! and query_as! macro output. Runtime encoding and decoding work for both crates regardless.

Smart pointer pass-through

Type, Encode, and Decode are all implemented for Arc<T>, Box<T>, Rc<T>, and Cow<'_, T> when the inner type supports them. You can bind a Box<str> or Arc<String> directly without any conversion.

Build docs developers (and LLMs) love