#sql-query #sqlite #postgresql #database-driver #compile-time #mysql #connection-pool

sqlx

🧰 Rust SQL 工具箱。一个支持编译时检查查询的异步、纯Rust SQL库,没有使用DSL。支持PostgreSQL、MySQL和SQLite。

47个版本

0.8.0 2024年7月23日
0.7.4 2024年3月12日
0.7.3 2023年11月23日
0.7.1 2023年7月15日
0.1.0 2019年6月6日

⚠️ 已报告问题

#3 in 数据库接口

Download history 237517/week @ 2024-05-04 259948/week @ 2024-05-11 262936/week @ 2024-05-18 246640/week @ 2024-05-25 276226/week @ 2024-06-01 278468/week @ 2024-06-08 265681/week @ 2024-06-15 265280/week @ 2024-06-22 254853/week @ 2024-06-29 275404/week @ 2024-07-06 268045/week @ 2024-07-13 296330/week @ 2024-07-20 295915/week @ 2024-07-27 290327/week @ 2024-08-03 319060/week @ 2024-08-10 278870/week @ 2024-08-17

1,239,533 每月下载量
用于 1,077 个crate (直接使用769个)

MIT/Apache

1MB
15K SLoC

SQLx

🧰 Rust SQL 工具箱


LaunchBadge 团队 用 ❤️ 构建

有问题?请先查看 常见问题解答

SQLx是一个异步、纯Rust的SQL库,具有编译时检查查询(如果需要的话),不使用DSL。

  • 真正异步。从头开始使用async/await构建,以实现最大并发。

  • 编译时检查查询(如果需要的话)。参见 SQLx不是ORM

  • 数据库无关。支持PostgreSQLMySQLMariaDBSQLite

    • 版本0.7之前支持MSSQL,但已移除,等待作为我们SQLx Pro计划的一部分进行完整重写。
  • 纯Rust。Postgres和MySQL/MariaDB驱动程序使用纯Rust编写,使用不安全代码。

  • 运行时无关。支持不同的运行时环境(async-std / tokio / actix)和TLS后端(native-tls, rustls)。

† SQLite 驱动使用 libsqlite3 C 库作为 SQLite 是一个嵌入式数据库(我们能够使 SQLite 完全使用 Rust 的唯一方法是将 SQLite 的所有内容移植到 Rust)。

†† SQLx 在启用 sqlite 功能的情况下使用 #![forbid(unsafe_code)],否则直接通过 libsqlite3-sys 调用 SQLite3 API,这需要 unsafe


  • 跨平台。作为原生 Rust,SQLx 可以在支持 Rust 的任何地方编译。

  • 内置连接池,使用 sqlx::Pool

  • 行流式读取。数据异步从数据库读取,按需解码。

  • 自动语句准备和缓存。在高级查询 API(sqlx::query)中使用时,每个连接都准备和缓存语句。

  • 简单的(未准备的)查询执行,包括将结果检索到与高级 API 使用的相同 Row 类型。支持批量执行,并从所有语句返回结果。

  • 支持传输层安全性(TLS)的数据库(MySQLMariaDBPostgreSQL)。

  • 使用 LISTENNOTIFY 进行异步通知,适用于 PostgreSQL

  • 支持带保存点的嵌套事务。

  • Any 数据库驱动,可以在运行时更改数据库驱动。一个 AnyPool 通过 URL 方案连接到指定的驱动程序。

安装

SQLx 与 async-stdtokioactix 运行时以及 native-tlsrustls TLS 后端兼容。添加依赖项时,必须选择一个包含 runtime + tls 的运行时功能。

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

# tokio (no TLS)
sqlx = { version = "0.7", features = [ "runtime-tokio" ] }
# tokio + native-tls
sqlx = { version = "0.7", features = [ "runtime-tokio", "tls-native-tls" ] }
# tokio + rustls
sqlx = { version = "0.7", features = [ "runtime-tokio", "tls-rustls" ] }

# async-std (no TLS)
sqlx = { version = "0.7", features = [ "runtime-async-std" ] }
# async-std + native-tls
sqlx = { version = "0.7", features = [ "runtime-async-std", "tls-native-tls" ] }
# async-std + rustls
sqlx = { version = "0.7", features = [ "runtime-async-std", "tls-rustls" ] }

Cargo 功能标志

出于向后兼容性的原因,运行时和 TLS 功能可以一起作为一个单独的功能选择,或者单独选择。

为了向前兼容,应使用单独的运行时和 TLS 功能,因为组合功能可能会在未来被删除。

  • runtime-async-std:使用 async-std 运行时,不启用 TLS 后端。

  • runtime-async-std-native-tls:使用 async-std 运行时和 native-tls TLS 后端(软弃用)。

  • runtime-async-std-rustls:使用 async-std 运行时和 rustls TLS 后端(软弃用)。

  • runtime-tokio:使用 tokio 运行时,不启用 TLS 后端。

  • runtime-tokio-native-tls:使用tokio运行时和native-tls TLS后端(软弃用)。

  • runtime-tokio-rustls:使用tokio运行时和rustls TLS后端(软弃用)。

    • Actix-web完全兼容Tokio,因此不再需要单独的运行时功能。
  • tls-native-tls:使用native-tls TLS后端(在*nix上为OpenSSL,在Windows上为SChannel,在macOS上为Secure Transport)。

  • tls-rustls:使用rustls TLS后端(跨平台后端,仅支持TLS 1.2和1.3)。

  • postgres:添加对Postgres数据库服务器的支持。

  • mysql:添加对MySQL/MariaDB数据库服务器的支持。

  • mssql:添加对MSSQL数据库服务器的支持。

  • sqlite:添加对自包含的SQLite数据库引擎的支持。

  • any:添加对Any数据库驱动程序的支持,该驱动程序可以在运行时代理到数据库驱动程序。

  • derive:添加对derive家族宏的支持,包括FromRowTypeEncodeDecode

  • macros:添加对query*!宏的支持,它允许编译时检查查询。

  • migrate:添加对迁移管理和migrate!宏的支持,允许编译时嵌入迁移。

  • uuid:添加对UUID(在Postgres中)的支持。

  • chrono:添加对来自chrono的日期和时间类型的支持。

  • time:添加对来自time crate的日期和时间类型的支持(作为chrono的替代方案,如果两者都启用,则query!宏更倾向于使用)。

  • bstr:添加对bstr::BString的支持。

  • bigdecimal:使用bigdecimal crate添加对NUMERIC的支持。

  • rust_decimal:使用rust_decimal crate添加对NUMERIC的支持。

  • ipnetwork:使用ipnetwork crate添加对INETCIDR(在postgres中)的支持。

  • json:使用serde_json crate添加对JSONJSONB(在postgres中)的支持。

  • 离线模式现在始终启用。请参阅sqlx-cli/README.md

SQLx不是ORM!

SQLx支持编译时检查查询。然而,它并不是通过提供Rust API或DSL(领域特定语言)来构建查询来实现的。相反,它提供宏,这些宏接受常规SQL作为输入并确保它在您的数据库中是有效的。这种工作的方式是,SQLx在编译时连接到您的开发数据库,以便数据库本身验证(并返回有关您的SQL查询的一些信息)。这有一些潜在的意外影响

  • 由于SQLx永远不需要解析SQL字符串本身,因此可以(包括由数据库扩展添加的东西)使用开发数据库接受的任何语法
  • 由于信息数据库允许检索查询信息量的不同,从查询宏中获得的SQL验证程度取决于数据库。

如果您正在寻找(异步)ORM,可以查看ormxSeaORM,它建立在SQLx之上。

用法

请参阅examples/文件夹以获取更深入的用法。

快速入门

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(())
}

连接

可以使用任何数据库连接类型通过调用connect()建立一个单一连接。

use sqlx::Connection;

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

通常,您可能希望创建一个连接池(sqlx::Pool)来调节应用程序使用的服务器端连接数。

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

查询

在SQL中,查询可以分为预编译(参数化)或未预编译(简单)。预编译查询具有缓存的查询计划,使用二进制通信模式(带宽更低,解码更快),并使用参数来避免SQL注入。未预编译查询简单,仅适用于预编译语句无法使用的地方,例如各种数据库命令(例如,PRAGMASETBEGIN)。

SQLx支持两种类型查询的所有操作。在SQLx中,一个&str被视为未预编译查询,而一个QueryQueryAs结构被视为预编译查询。

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

我们应该尽可能使用高级的query接口。为了使这更容易,类型上有finalizers来避免需要用执行器包装。

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

execute查询finalizer返回受影响的行数(如果有的话),并丢弃所有接收到的结果。此外,还有fetchfetch_onefetch_optionalfetch_all来接收结果。

sqlx::query返回的Query类型将返回数据库中的Row<'conn>。列值可以通过序号或名称使用row.get()来访问。由于Row保留了对连接的不可变借用,因此一次只能存在一个Row

fetch查询finalizer返回一个类似于流的数据类型,该数据类型遍历结果集中的行。

// provides `try_next`
use futures::TryStreamExt;

let mut rows = sqlx::query("SELECT * FROM users WHERE email = ?")
    .bind(email)
    .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")?;
}

为了帮助将行映射到域类型,可以使用两种惯例之一

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(user_email)
    .bind(user_name)
    .fetch(&mut conn);

而不是结果流,我们可以使用fetch_onefetch_optional来从数据库请求一个必需的或可选的结果。

编译时验证

我们可以使用宏sqlx::query!来实现SQL的编译时语法和语义验证,输出到一个匿名记录类型,其中每个SQL列都是一个Rust字段(需要时使用原始标识符)。

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

query()的差异

  • 输入(或绑定)参数必须一次性给出(并且它们在编译时被验证为正确数量和正确类型)。

  • 输出类型是一个匿名记录。在上面的示例中,类型可能类似于

    { country: String, count: i64 }
    
  • DATABASE_URL环境变量必须在构建时设置为一个可以对其执行查询的数据库;数据库不必包含任何数据,但必须是相同类型(MySQL、Postgres等)并且具有与运行时连接的数据库相同的模式。

    为了方便,您可以使用一个.env文件1来设置DATABASE_URL,这样您就不必每次都传递它。

    DATABASE_URL=mysql://localhost/my_database
    

query!()的最大缺点是输出类型不能命名(因为Rust没有官方支持匿名记录)。为了解决这个问题,有一个query_as!()宏,它与它非常相似,只是您可以命名输出类型。

// 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

为了避免在修改数据库访问代码时,即使没有修改也需要一个开发数据库来编译项目,您可以通过使用sqlx命令行工具启用“离线模式”来缓存SQL查询分析的结果。请参阅sqlx-cli/README.md

编译时验证的查询在编译时做了很多工作。像cargo checkcargo build这样的增量操作,当使用优化构建并在您的Cargo.toml中包含以下内容时可以显著更快(更多信息请参阅《Cargo手册》中的配置文件部分

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

1自2021年12月起,dotenv包似乎已被废弃,因此我们现在使用dotenvy包。文件格式相同。

安全性

此包使用#![forbid(unsafe_code)]来确保所有内容都是用100%安全的Rust实现。

如果启用了sqlite功能,这会降低到#![deny(unsafe_code)],并在#![allow(unsafe_code)]上对sqlx::sqlite模块。我们在几个地方与C SQLite API交互。我们试图为每个调用记录我们假设的不变条件。我们绝对欢迎对我们的不安全代码使用进行审计和反馈。

许可证

许可证为以下之一

任选其一。

贡献

除非您明确声明,否则根据Apache-2.0许可证定义的任何贡献,有意提交以包含在作品中的,应按上述方式双许可,不附加任何额外条款或条件。

依赖项

约7-28MB
约468K SLoC