SQLx Knowledge Patch
Baseline: SQLx through 0.7.x. Covered range: 0.8.0 through the 0.9.0-changelog batch.
Use this skill before changing SQLx dependencies, features, queries, macros, migrations, connection options, or encoded types. Check the detailed references when a task touches backend-specific behavior.
Reference index
| Reference | Topics |
| --- | --- |
| installation-and-configuration.md | Rust and CLI requirements, runtime/TLS features, sqlx.toml, offline builds, SQLite linking and opt-in APIs |
| queries-macros-and-types.md | SqlSafeStr, dynamic queries, macro inference, argument/API signature changes, derives, encoded types |
| database-specific-behavior.md | PostgreSQL ipnet and connection escaping, MySQL RSA/text/collations, SQLite value and extension safety |
| migrations-and-observability.md | Migration capabilities and API changes, tracing field correction |
Breaking changes and removals
Toolchain and CLI installation
SQLx 0.9 requires Rust 1.94.0. The repository no longer tracks Cargo.lock, so this old installation form no longer works:
cargo install --locked sqlx-cli
Install without --locked, or maintain a lockfile yourself when reproducibility is required.
Runtime and TLS features
The combined runtime-and-TLS features were soft-deprecated in 0.8 and removed in 0.9. Select a runtime and TLS implementation independently.
[dependencies]
sqlx = { version = "0.9", features = [
"runtime-tokio",
"postgres",
"tls-rustls-ring-native-roots",
] }
Do not carry forward features such as:
features = ["runtime-tokio-native-tls"]
The Rustls choices include:
tls-rustls-ring-native-rootstls-rustls-ring-webpkitls-rustls-aws-lc-rs
For runtimes, runtime-smol and runtime-async-global-executor replace deprecated async-std usage.
Dynamic query strings require an assertion
All query*() functions accept impl SqlSafeStr. String literals and other static strings work directly. Wrap non-static or dynamically assembled SQL explicitly in AssertSqlSafe:
let query = sqlx::query(sqlx::AssertSqlSafe(dynamic_sql));
This API also permits an owned query to be returned as Query<'static, DB>.
Treat the wrapper as an explicit trust boundary: parameterize values and assert only the SQL structure.
Public type signatures changed
Update annotations, bounds, and manual trait implementations for these changes:
RawSqlmethods now have aDBtype parameter.Arguments,SqliteArguments, andAnyArgumentsno longer have their former lifetime parameters.PgAdvisoryLockGuardno longer has a lifetime parameter.- The
Migratetrait changed significantly. resolve_blocking()is hidden and SemVer-exempt; do not depend on it.
SQLite values have tighter thread boundaries
SqliteValue is now !Sync, and SqliteValueRef is now !Send. Before crossing the respective thread boundary, clone the value, convert it to an owned representation, or put owned state behind appropriate synchronization.
After decoding one SQLite value as borrowed &[u8] or &str, decoding that same value as another type now returns an error.
PostgreSQL newtype array derivation
#[derive(sqlx::Type)] now generates PgHasArrayType for newtype structs. Remove a conflicting manual implementation or disable generation:
#[derive(sqlx::Type)]
#[sqlx(no_pg_array)]
struct UserId(i64);
Escaping and telemetry corrections
PgConnectOptions::options() escapes option values itself. Remove caller-side manual escaping to prevent double escaping.
Backslash escapes in .pgpass passwords are decoded, which can change the password sent compared with earlier behavior.
Rename tracing consumers that expect aquired_after_secs; the corrected field is acquired_after_secs.
Configuration quick reference
Per-crate sqlx.toml
SQLx 0.9 adds per-crate sqlx.toml configuration. It can:
- rename the crate's
DATABASE_URLvariable; - define global macro type overrides;
- rename or relocate
_sqlx_migrations; - exclude selected characters, including whitespace, from migration hashes.
sqlx-cli enables this support by default. Library users must enable it:
[dependencies]
sqlx = { version = "0.9", features = ["sqlx-toml"] }
An explicitly set SQLX_OFFLINE_DIR environment variable takes precedence when macros search for offline data.
The offline feature is optional, allowing configurations that do not need it to build without serde.
SQLite linking choice
Since 0.8, sqlite bundles and statically links SQLite. Select sqlite-unbundled when linking a system or custom library:
[dependencies]
sqlx = { version = "0.9", features = ["runtime-tokio", "sqlite-unbundled"] }
The unbundled path requires SQLite at build time, recommends SQLite 3.20.0 or newer, and may invoke bindgen and increase build time.
SQLite opt-in features
Enable only the APIs the application needs:
| Feature | Capability / caveat |
| --- | --- |
| sqlite-preupdate-hook | Enables SQLite's normally disabled preupdate-hook API; with sqlite-unbundled, linking fails if the system library omits it. |
| sqlite-deserialize | Gates SQLite serialize/deserialize APIs. |
| sqlite-load-extension | Gates extension-loading APIs; loading is unsafe, including configuration through sqlx.toml. |
| sqlite-unlock-notify | Gates SQLx's internal SQLite unlock notification support. |
The compatible maximum libsqlite3-sys version may increase in a non-breaking SQLx release.
Query, macro, and type quick reference
PostgreSQL query!() now forces a generic plan for nullability inference. Recheck generated output types when upgrading queries whose inferred nullability depends on planning.
Encode, Decode, and Type support Box, Arc, Rc, and Cow. Decoding a Cow always yields Cow::Owned.
PostgreSQL also supports nested domains. Transparent derives work for single-field named structs.
Database-specific quick reference
PostgreSQL
Enable ipnet to map PostgreSQL INET and CIDR to types from the ipnet crate, as an alternative to ipnetwork:
sqlx = { version = "0.9", features = ["runtime-tokio", "postgres", "ipnet"] }
MySQL
Plaintext connections that need RSA password encryption fail unless mysql-rsa is enabled. TLS connections do not use this path.
sqlx = { version = "0.9", features = ["runtime-smol", "mysql", "mysql-rsa"] }
Non-binary text-like columns that previously inferred as Vec<u8> may infer as String. SQLx sends SET NAMES utf8mb4 without forcing a collation. Calling MySqlConnectOptions::charset() or .collation() automatically enables set_names.
Migration quick reference
The migration layer now supports:
no_txmigrations;- skipping migrations;
- constructing a migrator with
Migrator::with_migrations().
set_ignore_missing() and set_locking() now return &mut Self, so update code that expected ownership or a different builder return type.
Upgrade checklist
- Move to Rust 1.94.0 before adopting SQLx 0.9.
- Remove
--lockedfrom directsqlx-cliinstallation unless supplying your own lockfile workflow. - Replace combined runtime/TLS and deprecated async-std features.
- Decide whether library crates should enable
sqlx-tomlandoffline. - Wrap dynamic SQL with
AssertSqlSafeand keep values parameterized. - Update removed lifetimes, the
RawSqldatabase parameter, and migration trait usage. - Audit derived PostgreSQL newtypes for duplicate
PgHasArrayTypeimplementations. - Recheck
query!()output nullability and MySQL text inference. - Audit SQLite thread crossings, repeated borrowed decoding, and extension-loading safety.
- Remove manual PostgreSQL option escaping and update the corrected tracing field name.