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 |
|
#3 in 数据库接口
1,239,533 每月下载量
用于 1,077 个crate (直接使用769个)
1MB
15K SLoC
SQLx
有问题?请先查看 常见问题解答!
SQLx是一个异步、纯Rust的SQL库,具有编译时检查查询(如果需要的话),不使用DSL。
-
真正异步。从头开始使用async/await构建,以实现最大并发。
-
编译时检查查询(如果需要的话)。参见 SQLx不是ORM。
-
数据库无关。支持PostgreSQL、MySQL、MariaDB、SQLite。
- 版本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)的数据库(MySQL、MariaDB 和 PostgreSQL)。
-
使用
LISTEN
和NOTIFY
进行异步通知,适用于 PostgreSQL。 -
支持带保存点的嵌套事务。
-
Any
数据库驱动,可以在运行时更改数据库驱动。一个AnyPool
通过 URL 方案连接到指定的驱动程序。
安装
SQLx 与 async-std
、tokio
和 actix
运行时以及 native-tls
和 rustls
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家族宏的支持,包括FromRow
、Type
、Encode
和Decode
。 -
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添加对INET
和CIDR
(在postgres中)的支持。 -
json
:使用serde_json
crate添加对JSON
和JSONB
(在postgres中)的支持。 -
离线模式现在始终启用。请参阅sqlx-cli/README.md。
SQLx不是ORM!
SQLx支持编译时检查查询
。然而,它并不是通过提供Rust API或DSL(领域特定语言)来构建查询来实现的。相反,它提供宏,这些宏接受常规SQL作为输入并确保它在您的数据库中是有效的。这种工作的方式是,SQLx在编译时连接到您的开发数据库,以便数据库本身验证(并返回有关您的SQL查询的一些信息)。这有一些潜在的意外影响
- 由于SQLx永远不需要解析SQL字符串本身,因此可以(包括由数据库扩展添加的东西)使用开发数据库接受的任何语法
- 由于信息数据库允许检索查询信息量的不同,从查询宏中获得的SQL验证程度取决于数据库。
如果您正在寻找(异步)ORM,可以查看ormx
或SeaORM
,它建立在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注入。未预编译查询简单,仅适用于预编译语句无法使用的地方,例如各种数据库命令(例如,PRAGMA
或SET
或BEGIN
)。
SQLx支持两种类型查询的所有操作。在SQLx中,一个&str
被视为未预编译查询,而一个Query
或QueryAs
结构被视为预编译查询。
// 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返回受影响的行数(如果有的话),并丢弃所有接收到的结果。此外,还有fetch
、fetch_one
、fetch_optional
和fetch_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_one
或fetch_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 check
和cargo 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 License,版本2.0(LICENSE-APACHE 或 http://www.apache.org/licenses/LICENSE-2.0)
- MIT许可证(LICENSE-MIT 或 http://opensource.org/licenses/MIT)
任选其一。
贡献
除非您明确声明,否则根据Apache-2.0许可证定义的任何贡献,有意提交以包含在作品中的,应按上述方式双许可,不附加任何额外条款或条件。
依赖项
约7-28MB
约468K SLoC