sqlx README

The project's README from github.com/transact-rs/sqlx. As one markdown file: README.md.

SQLx

🧰 The Rust SQL Toolkit

actions status Crates.io version chat docs.rs docs Download

Install | Usage | Docs | Ecosystem | Discord


Built with ❤️ by The LaunchBadge team

Have a question? Be sure to check the FAQ first!

SQLx is an async, pure Rust† SQL crate featuring compile-time checked queries without a DSL.

† The SQLite driver uses the libsqlite3 C library as SQLite is an embedded database (the only way we could be pure Rust for SQLite is by porting all of SQLite to Rust).

†† SQLx uses #![forbid(unsafe_code)] unless the sqlite feature is enabled. The SQLite driver directly invokes the SQLite3 API via libsqlite3-sys, which requires unsafe.


Install

SQLx is compatible with the async-std, tokio, and actix runtimes; and, the native-tls and rustls TLS backends. When adding the dependency, you must choose a runtime feature that is runtime + tls.

# Cargo.toml
[dependencies]
# PICK ONE OF THE FOLLOWING:

# tokio (no TLS)
sqlx = { version = "0.9", features = [ "runtime-tokio" ] }
# tokio + native-tls
sqlx = { version = "0.9", features = [ "runtime-tokio", "tls-native-tls" ] }
# tokio + rustls with ring and WebPKI CA certificates
sqlx = { version = "0.9", features = [ "runtime-tokio", "tls-rustls-ring-webpki" ] }
# tokio + rustls with ring and platform's native CA certificates
sqlx = { version = "0.9", features = [ "runtime-tokio", "tls-rustls-ring-native-roots" ] }
# tokio + rustls with aws-lc-rs
sqlx = { version = "0.9", features = [ "runtime-tokio", "tls-rustls-aws-lc-rs" ] }

# async-std (no TLS)
sqlx = { version = "0.9", features = [ "runtime-async-std" ] }
# async-std + native-tls
sqlx = { version = "0.9", features = [ "runtime-async-std", "tls-native-tls" ] }
# async-std + rustls with ring and WebPKI CA certificates
sqlx = { version = "0.9", features = [ "runtime-async-std", "tls-rustls-ring-webpki" ] }
# async-std + rustls with ring and platform's native CA certificates
sqlx = { version = "0.9", features = [ "runtime-async-std", "tls-rustls-ring-native-roots" ] }
# async-std + rustls with aws-lc-rs
sqlx = { version = "0.9", features = [ "runtime-async-std", "tls-rustls-aws-lc-rs" ] }

Cargo Feature Flags

For backward-compatibility reasons, the runtime and TLS features can either be chosen together as a single feature, or separately.

For forward compatibility, you should use the separate runtime and TLS features as the combination features may be removed in the future.

SQLx is not an ORM!

SQLx supports compile-time checked queries. It does not, however, do this by providing a Rust API or DSL (domain-specific language) for building queries. Instead, it provides macros that take regular SQL as input and ensure that it is valid for your database. The way this works is that SQLx connects to your development DB at compile time to have the database itself verify (and return some info on) your SQL queries. This has some potentially surprising implications:

If you are looking for an (asynchronous) ORM, you can check out our new Ecosystem wiki page!

Usage

See the examples/ folder for more in-depth usage.

Quickstart

use sqlx::postgres::PgPoolOptions;
// use sqlx::mysql::MySqlPoolOptions;
// etc.

#[async_std::main] // Requires the `attributes` feature of `async-std`
// or #[tokio::main]
// or #[actix_web::main]
async fn main() -> Result<(), sqlx::Error> {
    // Create a connection pool
    //  for MySQL/MariaDB, use MySqlPoolOptions::new()
    //  for SQLite, use SqlitePoolOptions::new()
    //  etc.
    let pool = PgPoolOptions::new()
        .max_connections(5)
        .connect("postgres://postgres:password@localhost/test").await?;

    // Make a simple query to return the given parameter (use a question mark `?` instead of `$1` for MySQL/MariaDB)
    let row: (i64,) = sqlx::query_as("SELECT $1")
        .bind(150_i64)
        .fetch_one(&pool).await?;

    assert_eq!(row.0, 150);

    Ok(())
}

Connecting

A single connection can be established using any of the database connection types and calling connect().

use sqlx::Connection;

let conn = SqliteConnection::connect("sqlite::memory:").await?;

Generally, you will want to instead create a connection pool (sqlx::Pool) for the application to regulate how many server-side connections it's using.

let pool = MySqlPool::connect("mysql://user:pass@host/database").await?;

Querying

In SQL, queries can be separated into prepared (parameterized) or unprepared (simple). Prepared queries have their query plan cached, use a binary mode of communication (lower bandwidth and faster decoding), and utilize parameters to avoid SQL injection. Unprepared queries are simple and intended only for use where a prepared statement will not work, such as various database commands (e.g., PRAGMA or SET or BEGIN).

SQLx supports all operations with both types of queries. In SQLx, a &str is treated as an unprepared query, and a Query or QueryAs struct is treated as a prepared query.

// low-level, Executor trait
conn.execute("BEGIN").await?; // unprepared, simple query
conn.execute(sqlx::query("DELETE FROM table")).await?; // prepared, cached query

We should prefer to use the high-level query interface whenever possible. To make this easier, there are finalizers on the type to avoid the need to wrap with an executor.

sqlx::query("DELETE FROM table").execute(&mut conn).await?;
sqlx::query("DELETE FROM table").execute(&pool).await?;

The execute query finalizer returns the number of affected rows, if any, and drops all received results. In addition, there are fetch, fetch_one, fetch_optional, and fetch_all to receive results.

The Query type returned from sqlx::query will return Row<'conn> from the database. Column values can be accessed by ordinal or by name with row.get(). As the Row retains an immutable borrow on the connection, only one Row may exist at a time.

The fetch query finalizer returns a stream-like type that iterates through the rows in the result sets.

// provides `try_next`
use futures_util::TryStreamExt;
// provides `try_get`
use sqlx::Row;

let mut rows = sqlx::query("SELECT * FROM users WHERE email = ?")
    .bind("[email protected]")
    .fetch(&mut conn);

while let Some(row) = rows.try_next().await? {
    // map the row into a user-defined domain type
    let email: &str = row.try_get("email")?;
}

To assist with mapping the row into a domain type, one of two idioms may be used:

let mut stream = sqlx::query("SELECT * FROM users")
    .map(|row: PgRow| {
        // map the row into a user-defined domain type
    })
    .fetch(&mut conn);
#[derive(sqlx::FromRow)]
struct User { name: String, id: i64 }

let mut stream = sqlx::query_as::<_, User>("SELECT * FROM users WHERE email = ? OR name = ?")
    .bind("[email protected]")
    .bind("example_username")
    .fetch(&mut conn);

Instead of a stream of results, we can use fetch_one or fetch_optional to request one required or optional result from the database.

Compile-time verification

We can use the macro, sqlx::query! to achieve compile-time syntactic and semantic verification of the SQL, with an output to an anonymous record type where each SQL column is a Rust field (using raw identifiers where needed).

let countries = sqlx::query!(
        "
SELECT country, COUNT(*) as count
FROM users
GROUP BY country
WHERE organization = ?
        ",
        organization
    )
    .fetch_all(&pool) // -> Vec<{ country: String, count: i64 }>
    .await?;

// countries[0].country
// countries[0].count

Differences from query():

The biggest downside to query!() is that the output type cannot be named (due to Rust not officially supporting anonymous records). To address that, there is a query_as!() macro that is mostly identical except that you can name the output type.

// no traits are needed
struct Country { country: String, count: i64 }

let countries = sqlx::query_as!(Country,
        "
SELECT country, COUNT(*) as count
FROM users
GROUP BY country
WHERE organization = ?
        ",
        organization
    )
    .fetch_all(&pool) // -> Vec<Country>
    .await?;

// countries[0].country
// countries[0].count

To avoid the need of having a development database around to compile the project even when no modifications (to the database-accessing parts of the code) are done, you can enable "offline mode" to cache the results of the SQL query analysis using the sqlx command-line tool. See sqlx-cli/README.md.

Compile-time verified queries do quite a bit of work at compile time. Incremental actions like cargo check and cargo build can be significantly faster when using an optimized build by putting the following in your Cargo.toml (More information in the Profiles section of The Cargo Book)

[profile.dev.package.sqlx-macros]
opt-level = 3

1 The dotenv crate itself appears abandoned as of December 2021 so we now use the dotenvy crate instead. The file format is the same.

Safety

This crate uses #![forbid(unsafe_code)] to ensure everything is implemented in 100% Safe Rust.

If the sqlite feature is enabled, this is downgraded to #![deny(unsafe_code)] with #![allow(unsafe_code)] on the sqlx::sqlite module. There are several places where we interact with the C SQLite API. We try to document each call for the invariants we're assuming. We absolutely welcome auditing of, and feedback on, our unsafe code usage.

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any Contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

sqlx (transact-rs/sqlx)

Searching Database…

17.6K ·GitHub
这是什么?SQLx 是一个异步的纯 Rust SQL 工具包,用于连接数据库,它支持编译期校验查询语句,而且没有任何 DSL。它支持 PostgreSQL、MySQL、MariaDB 和 SQLite。
也就是说它不是 ORM——那它有什么不一样?它明确表示自己不是 ORM。你直接在 query! 这样的宏里写普通的 SQL,SQLx 会让你开发用的数据库在编译期校验这些 SQL——中间不需要任何查询构造器 API 或 DSL。
我怎么把它加到项目里?把 sqlx 作为 Cargo 依赖添加,然后选择 feature 标志:一个运行时(runtime-tokio、runtime-async-std),一个 TLS 后端(tls-rustls、tls-native-tls),以及你的数据库(postgres、mysql 或 sqlite)。
它会把我绑死在某个特定的异步运行时或 TLS 库上吗?不会——它能配合 async-std、tokio 和 actix 这些运行时,TLS 方面可以用 native-tls 或 rustls。Actix-web 与 Tokio 完全兼容,所以不需要单独的运行时 feature。
编译期校验查询有什么坑?query! 宏在构建时需要设置 DATABASE_URL,指向一个类型和表结构相同的开发数据库(不需要有数据)。离线模式可以通过 sqlx CLI 缓存分析结果,query_as! 还能给你一个具名的输出类型。
在我用的任何操作系统上都能编译和运行吗?可以——SQLx 是原生 Rust 写的,只要 Rust 支持的平台都能编译。
它自带连接池吗?有——sqlx::Pool 是内置的。你还能得到行流式读取、每条连接自动准备并缓存语句、带保存点的嵌套事务,以及 PostgreSQL 的 LISTEN/NOTIFY 通知。
它真的是 100% 安全的 Rust 吗?基本是:这个 crate 使用了 #![forbid(unsafe_code)],Postgres 和 MySQL/MariaDB 驱动是纯 Rust 实现,完全不涉及 unsafe。SQLite 驱动是例外——它调用 C 的 libsqlite3 API,这需要用到 unsafe。
我可以在运行时而不是编译时选择数据库驱动吗?可以——启用 any feature 后,AnyPool 会根据连接 URL 的协议前缀自动连接对应的驱动。
What are similar projects?usql, flyway, dbx, tabularis, migrate, orm, goose, node-postgres
Size
9.9 MB
Stars
17.6K
Updated
CommitOct 16
Age
6y
Commits · 90 Days