12 个不稳定版本 (3 个破坏性更新)
0.7.1 | 2023 年 10 月 25 日 |
---|---|
0.7.0 | 2023 年 10 月 16 日 |
0.6.3 | 2023 年 10 月 16 日 |
0.5.7 | 2023 年 10 月 12 日 |
0.0.0 | 2023 年 10 月 9 日 |
#1270 in 过程宏
每月 36 次下载
用于 exemplar
17KB
433 行
Exemplar
rusqlite 的样板代码消除器。
入门指南
体验一下您可以做什么
#[derive(Debug, PartialEq, Model)]
#[table("users")]
#[check("../tests/schema.sql")]
struct User {
username: String,
#[bind(bind_path)]
#[extr(extr_path)]
home_dir: PathBuf,
#[column("pwd")]
password: Vec<u8>,
}
fn main() -> Result<()> {
let conn = Connection::open_in_memory()?;
conn.execute_batch(
include_str!("../tests/schema.sql")
)?;
let alice = User {
username: "Alice".to_owned(),
home_dir: "/var/home/alice".into(),
password: b"hunter2".to_vec()
};
let bob = User {
username: "Bob".to_owned(),
home_dir: "/var/home/robert".into(),
password: b"password".to_vec()
};
alice.insert(&conn)?;
bob.insert(&conn)?;
let mut stmt = conn.prepare("
SELECT * FROM users ORDER BY username ASC
")?;
let mut iter = stmt.query_and_then([], User::from_row)?;
assert_eq!(alice, iter.next().unwrap()?);
assert_eq!(bob, iter.next().unwrap()?);
Ok(())
}
Exemplar 基于具有自己的 Model
trait,该 trait 有自己的 derive 宏。
特性
- 与原始 SQL 一起工作,而不是与之对抗。
- 轻量级、零成本 API。
- Exemplar 的大部分功能都围绕
Model
trait 展开,该 trait 在运行前被内联和单态化。生成的代码大致相当于您使用纯rusqlite
编写的代码。 - 设计为即插即用;尽可能重用
rusqlite
的现有类型,包括其Result
类型别名。 - 支持任何可以
Deref
到rusqlite::Connection
的类型,例如事务或连接池。
- Exemplar 的大部分功能都围绕
- 可选的测试推导,以防止数据库模式与 Rust 模型类型之间的漂移。
- 用于处理与 SQL 兼容的
enum
和映射到临时查询的 "匿名" 记录类型的宏。 - 在运行时对
dyn Model
进行反射/操作的一些能力。
如果您只需要使用 sqlite
对 Rust 数据进行 CRUD 操作,而不需要完整的 ORM 或企业级 DBMS,那么 Exemplar 就是您所需要的!
常见问题解答
"Exemplar 都不做些什么?"
一些关键特性
- 模式生成和管理。Exemplar 明确不是一个 ORM,并且很难在不落入 ORM 领域的情况下表示外键和迁移等概念。
- 如果您认为这是“必需”的,可以查看
diesel
或sqlx
/seaorm
,它们都支持 SQLite。
- 如果您认为这是“必需”的,可以查看
- 查询生成(不包括
INSERT
。) - 接口兼容性。仅支持
rusqlite
。
"它是不是非常快?"
是的。在我的机器上(根据 这些基准测试),Exemplar 可以
- 在 ~600 纳秒内插入一个非平凡的模式类型(1.6百万行/秒)
- 查询并重建同一类型在 ~9 微秒内(111,000 行/秒,使用
SELECT * LIMIT 1
)
显然,这种速度的功劳要归功于 SQLite 和 rusqlite
的开发者,但我可以自信地说,我没有拖慢速度!
"它与 serde-rusqlite
有何不同?"
serde_rusqlite
是一种巧妙的黑客手段,但它仍然涉及太多扭曲和样板代码,这不符合我的口味——这就是我创建 Exemplar 的原因。
我试图解决的问题点包括
- 需要分配和操作一个
String
列表名称切片,以便有效地反序列化行——这可能是由于serde
的限制?- Exemplar 静态知道期望哪些列,因此
from_row
不需要额外的输入,也不进行多余的分配。
- Exemplar 静态知道期望哪些列,因此
- 无字段
enum
的设计选择很奇怪——它们被低效地序列化为TEXT
而不是INTEGER
。这对于调试来说很好,但我认为更快的选项应该是 Exemplar 的默认设置。 to_params_named(&row1).unwrap().to_slice().as_slice()
有点难以发音(尽管这可能是serde
的怪异之处再次显现。)- 相当于 Exemplar 中的
row1.insert(&conn)
或row1.insert_with(&stmt)
。
- 相当于 Exemplar 中的
- 在编译和运行时都会出现一般
serde
的开销。- 基准测试表明,与 Exemplar 相比,
serde_rusqlite
在插入操作上慢约 25%。 - 检索操作同样快速,这可能是由于最终转换步骤与查询计算和 I/O 相比微不足道。
- 基准测试表明,与 Exemplar 相比,
致谢
rusqlite
,因为它为构建这个库提供了基础。- David Tolnay,感谢他提供的各种 proc macro
咒语包。
依赖项
~310–770KB
~18K SLoC