2 个版本
0.2.7 | 2022 年 3 月 18 日 |
---|---|
0.2.6 | 2022 年 3 月 18 日 |
#26 在 #rusqlite
在 2 个包中使用 (通过 fnsql)
39KB
685 行
fnsql
包提供围绕 SQL 查询的简单类型安全的可选包装器。您不需要调用无类型的 .query()
和 .execute()
,而是调用自动生成的唯一包装器,这些包装器具有强类型,例如 .query_<name>()
和 .execute_<name>()
。但是,您需要手动指定输入和输出类型,只需在查询中指定一次,并在使用查询的代码中分离。
这是一个非常简单的实现,不会强迫您使用任何模式或 ORM,因此如果您已经在使用 rusqlite
或 postgres
包,您可以逐步用类型安全的包装器替换无类型的查询,或者从有意见的 ORM 迁移。
生成这些包装器的方法是为每个查询指定输入和输出类型。例如,考虑以下使用 fnsql
指定的定义,基于 rusqlite
的示例
fnsql::fnsql! {
#[rusqlite, test]
create_table_pet() {
"CREATE TABLE pet (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
data BLOB
)"
}
#[rusqlite, test(with=[create_table_pet])]
insert_new_pet(name: String, data: Option<Vec<u8>>) {
"INSERT INTO pet (name, data) VALUES (:name, :data)"
}
#[rusqlite, test(with=[create_table_pet])]
get_pet_id_data(name: Option<String>) -> [(i32, Option<Vec<u8>>, String)] {
"SELECT id, data, name FROM pet WHERE pet.name = :name"
}
}
这些定义可以这样使用(注释的是之前无类型接口的使用方式)
let mut conn = rusqlite::Connection::open_in_memory()?;
conn.execute_create_table_pet()?;
// conn.execute(
// "CREATE TABLE pet (
// id INTEGER PRIMARY KEY,
// name TEXT NOT NULL,
// data BLOB
// )",
// [],
// )?;
conn.execute_insert_new_pet(&me.name, &me.data)?;
// conn.execute(
// "INSERT INTO pet (name, data) VALUES (?1, ?2)",
// params![me.name, me.data],
// )?;
let mut stmt = conn.prepare_get_pet_id_data()?;
// let mut stmt = conn.prepare("SELECT id, data, name FROM pet WHERE pet.name = :name")?;
let pet_iter = stmt.query_map(&Some("Max".to_string()), |id, data, name| {
Ok::<_, rusqlite::Error>(Pet {
id,
data,
name,
})
})?;
// let pet_iter = stmt.query_map([(":name", "Max".to_string())], |row| {
// Ok(Pet {
// id: row.get(0)?,
// name: row.get(1)?,
// data: row.get(2)?,
// })
// })?;
技术讨论
这个包的想法是允许直接使用 SQL,但从不使用内联查询或在调用站点进行类型推断。相反,我们在顶层声明每个查询,为每个查询提供一个名称和从名称派生的访问器方法。
- 命名变量的类型以 Rust 类似语法给出。
- 返回行的类型也提供。
fnsql
不保证确保类型与查询匹配,您将使用cargo test
和没有其他代码来发现这一点。fnsql
为每个查询编写测试。使用Arbitrary
生成参数值。- 如果测试一个查询依赖于另一个,可以使用
test(with=[..])
来指定。
running 3 tests
test auto_create_table_pet ... ok
test auto_insert_new_pet ... ok
test auto_get_pet_id_data ... ok
以下内容是允许生成的查询测试编译
[dev-dependencies]
arbitrary = { version = "1", features = ["derive"] }
局限性
- 虽然它确实为
cargo test
提供了自动生成的测试来验证查询,但它不会基于 SQL 查询字符串进行任何编译时验证。 - 目前仅支持
rusqlite
和postgres
。
依赖项
~3–4.5MB
~87K SLoC