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 数据库接口
1,048 每月下载
在 3 个Crates中(2个直接)使用
1.5MB
34K SLoC
这是由@launchbadge的原始sqlx库的分支。sqlx v0.7在向后兼容v0.6方面存在问题,使得升级变得困难。sqlx v0.7还取消了Microsoft SQL Server的支持(以作为单独的商业产品出售)。此分支旨在作为sqlx v0.6的替代品,以下是一些更改
- 更新为使用依赖项的最新版本,包括
- 所有缺失的安全更新
- 最新的SQLite版本
- 改进了对Microsoft SQL Server的支持,包括
- 支持读取和写入
binary
和varbinary
数据- 使用
chrono
功能支持读取和写入date
、datetime
和datetimeoffset
数据- 支持读取和写入
numeric
和decimal
- 多个关于字符串处理的错误修复,包括对长字符串的更好支持
- 支持数据包分块,这解决了大型绑定参数或大型查询会失败的问题
SQLx
有疑问?请先查看常见问题解答!
SQLx是一个异步、纯Rust SQL库,具有编译时检查的查询,无需DSL。
-
真正的异步。从头开始使用async/await进行最大并发性。
-
编译时检查的查询(如果您想的话)。请参阅SQLx不是ORM。
-
数据库无关。支持PostgreSQL、MySQL、SQLite和MSSQL。
-
纯Rust。Postgres和MySQL/MariaDB驱动程序使用纯Rust编写,零不安全代码。
-
运行时无关。在不同的运行时(
async-std
/tokio
/actix
)和TLS后端(native-tls
,rustls
)上运行。
† 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)(MySQL和PostgreSQL)。
-
使用PostgreSQL的
LISTEN
和NOTIFY
进行异步通知。 -
支持保存点的嵌套事务。
-
提供
Any
数据库驱动程序,可以在运行时更改数据库驱动程序。一个AnyPool
连接到由URL方案指示的驱动程序。
安装
SQLx与async-std
、tokio
和actix
运行时以及native-tls
和rustls
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添加对INET
和CIDR
(在Postgres中)的支持。 -
json
:使用serde_json
crate添加对JSON
和JSONB
(在Postgres中)的支持。 -
tls
:添加对TLS连接的支持。 -
offline
:在无法访问实时数据库(如CI)时,启用离线模式构建宏。- 使用需要安装
sqlx-cli
。请参阅sqlx-cli/README.md。
- 使用需要安装
SQLx不是一个ORM!
SQLx支持编译时检查查询。然而,它并不是通过提供一个Rust API或DSL(领域特定语言)来构建查询来实现的。相反,它提供了宏,这些宏接受常规SQL作为输入并确保它在您的数据库中有效。这种方式的工作原理是,SQLx在编译时连接到您的开发数据库,以便数据库本身验证(并返回有关您的SQL查询的一些信息)。这有一些潜在的意外影响
- 由于SQLx永远不需要解析SQL字符串本身,因此可以使用开发数据库接受的任何语法(包括由数据库扩展添加的内容)
- 由于数据库允许您检索查询信息的不同量,因此从查询宏中获得的SQL验证程度取决于数据库
如果您正在寻找一个(异步)ORM,您可以检查ormx
或SeaORM
,后者建立在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注入。未准备查询简单,仅适用于预准备语句无法使用的情况,例如各种数据库命令(例如,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.toml
中可以使用优化构建来显著加快 cargo check
和 cargo 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 License,版本 2.0(LICENSE-APACHE 或 http://www.apache.org/licenses/LICENSE-2.0)
- MIT 许可证(LICENSE-MIT 或 http://opensource.org/licenses/MIT)
进行。
贡献
除非您明确表示,否则根据 Apache-2.0 许可证定义的,您有意提交的工作中的任何贡献,都应如上所述进行双重许可,不附加任何其他条款或条件。
依赖项
~6–28MB
~464K SLoC