#sqlite #sql-query #postgresql #sql-database #database-driver #mysql

sqlx-oldapi

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

24次发布

0.6.27 2024年8月16日
0.6.23 2024年6月22日
0.6.19 2024年1月28日
0.6.18 2023年11月18日
0.6.8 2023年7月31日

#332 in 数据库接口

Download history 448/week @ 2024-05-01 137/week @ 2024-05-08 143/week @ 2024-05-15 93/week @ 2024-05-22 106/week @ 2024-05-29 94/week @ 2024-06-05 52/week @ 2024-06-12 270/week @ 2024-06-19 48/week @ 2024-06-26 48/week @ 2024-07-03 70/week @ 2024-07-10 27/week @ 2024-07-17 157/week @ 2024-07-24 112/week @ 2024-07-31 318/week @ 2024-08-07 458/week @ 2024-08-14

1,048 每月下载
3 个Crates中(2个直接)使用

MIT/Apache

1.5MB
34K SLoC

这是由@launchbadge的原始sqlx库的分支。sqlx v0.7在向后兼容v0.6方面存在问题,使得升级变得困难。sqlx v0.7还取消了Microsoft SQL Server的支持(以作为单独的商业产品出售)。此分支旨在作为sqlx v0.6的替代品,以下是一些更改

  • 更新为使用依赖项的最新版本,包括
    • 所有缺失的安全更新
    • 最新的SQLite版本
  • 改进了对Microsoft SQL Server的支持,包括
    • 支持读取和写入binaryvarbinary数据
    • 使用chrono功能支持读取和写入datedatetimedatetimeoffset数据
    • 支持读取和写入numericdecimal
    • 多个关于字符串处理的错误修复,包括对长字符串的更好支持
    • 支持数据包分块,这解决了大型绑定参数或大型查询会失败的问题

SQLx

🧰 Rust SQL 工具集


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

SQLx是一个异步、纯Rust SQL库,具有编译时检查的查询,无需DSL。

  • 真正的异步。从头开始使用async/await进行最大并发性。

  • 编译时检查的查询(如果您想的话)。请参阅SQLx不是ORM

  • 数据库无关。支持PostgreSQLMySQLSQLiteMSSQL

  • 纯Rust。Postgres和MySQL/MariaDB驱动程序使用纯Rust编写,不安全代码。

  • 运行时无关。在不同的运行时(async-std / tokio / actix)和TLS后端(native-tlsrustls)上运行。

† SQLite驱动程序使用libsqlite3 C库,因为SQLite是嵌入式数据库(要使SQLite完全使用Rust编写,唯一的方法是将SQLite的所有内容都移植到Rust)。

†† SQLx使用#![forbid(unsafe_code)],除非启用了sqlite功能。由于SQLite驱动程序与C交互,这些交互是unsafe的。


  • 跨平台。作为本地Rust,SQLx可以在任何支持Rust的环境中编译。

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

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

  • 自动语句准备和缓存。当使用高级查询API(sqlx::query)时,语句按连接准备和缓存。

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

  • 在支持的数据库中启用传输层安全性(TLS)(MySQLPostgreSQL)。

  • 使用PostgreSQLLISTENNOTIFY进行异步通知。

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

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

安装

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

# Cargo.toml
[dependencies]
# tokio + rustls
sqlx = { package = "sqlx-oldapi",  version = "0.6", features = [ "runtime-tokio-rustls" ] }
# async-std + native-tls
sqlx = { package = "sqlx-oldapi",  version = "0.6", features = [ "runtime-async-std-native-tls" ] }

运行时和TLS后端不是分开的功能集,是为了解决Cargo问题的解决方案。

Cargo功能标志

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

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

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

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

  • runtime-actix-native-tls:使用actix运行时和native-tls TLS后端。

  • runtime-actix-rustls:使用actix运行时和rustls TLS后端。

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

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

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

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

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

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

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

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

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

  • time:添加对来自time crate(替代chrono,由query!宏优先选择)的日期和时间类型的支持。

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

  • git2:添加对git2::Oid的支持。

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

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

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

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

  • tls:添加对TLS连接的支持。

  • offline:在无法访问实时数据库(如CI)时,启用离线模式构建宏。

SQLx不是一个ORM!

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

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

如果您正在寻找一个(异步)ORM,您可以检查ormxSeaORM,后者建立在SQLx之上。

用法

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

快速入门

[dependencies]
# PICK ONE:
# Async-std:
sqlx = { package = "sqlx-oldapi",  version = "0.6", features = [  "runtime-async-std-native-tls", "postgres" ] }
async-std = { version = "1", features = [ "attributes" ] }

# Tokio:
sqlx = { package = "sqlx-oldapi",  version = "0.6", features = [ "runtime-tokio-native-tls" , "postgres" ] }
tokio = { version = "1", features = ["full"] }

# Actix-web:
sqlx = { package = "sqlx-oldapi",  version = "0.6", features = [ "runtime-actix-native-tls" , "postgres" ] }
actix-web = "4"
use sqlx::postgres::PgPoolOptions;
// use sqlx::mysql::MySqlPoolOptions;
// etc.

#[async_std::main]
// or #[tokio::main]
// or #[actix_web::main]
async fn main() -> Result<(), sqlx::Error> {
    // Create a connection pool
    //  for MySQL, 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)
    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.toml 中可以使用优化构建来显著加快 cargo checkcargo build 的增量操作(更多信息请参阅《Cargo Book》中的配置文件部分

[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 许可证定义的,您有意提交的工作中的任何贡献,都应如上所述进行双重许可,不附加任何其他条款或条件。

依赖项

~6–28MB
~464K SLoC