12个版本
0.2.1 | 2024年5月25日 |
---|---|
0.2.0 | 2024年2月16日 |
0.1.11-alpha | 2023年10月14日 |
0.1.8-alpha |
|
0.1.0 | 2023年11月27日 |
#382 in 数据库接口
1,631 每月下载量
用于 scyllax-cli
97KB
2K SLoC
scyllax (sɪl-æks)
Scylla的基于SQLx和Discord的查询系统。
示例
1. 模型定义
在编写任何查询之前,您必须定义一个模型。
#[entity]
pub struct PersonEntity {
#[entity(primary_key)]
pub id: uuid::Uuid,
pub email: String,
pub created_at: i64,
}
2. 读取查询
使用 read_query
属性,定义选择查询很容易。
#[read_query(
query = "select * from person where id = :id limit 1",
return_type = "PersonEntity"
)]
pub struct GetPersonById {
pub id: Uuid,
}
3. Upsert查询
使用 upsert_query
属性,定义upsert查询很容易。
#[entity]
#[upsert_query(table = "person", name = UpsertPerson)]
pub struct PersonEntity {
#[entity(primary_key)]
pub id: uuid::Uuid,
pub email: String,
pub created_at: i64,
}
4. 查询集合
Scylla要求查询在执行之前必须准备。为了在启动时准备(并检查)所有查询,创建一个查询集合并将其传递给一个执行器。
create_query_collection!(
PersonQueries,
[
GetPersonById,
GetPersonByEmail
],
[
DeletePersonById,
UpsertPerson
]
);
let executor = Executor::<PersonQueries>::new(Arc::new(session)).await;
let user = executor.execute_read(GetPersonByEmail {
email: "[email protected]".to_string(),
}).await?;
println!("{user:#?}");
特性
- 读取查询
- 写入查询 (https://github.com/trufflehq/scyllax/pull/1)
- 请求合并
- 编译时选择查询验证
- 确保结构中存在where约束
- 确保where约束与结构类型相同
- 运行时查询验证(结构与模式匹配)
待办事项
- 删除
anyhow
,更精细的错误
使用方法
请参阅 示例 以获取更多详细信息。
参考资料
#[read_request(
query = "select * from foo where id = ? limit 1",
entity_type = "Foo"
)]
struct GetFooById {
#[shard_key]
id: i64
}
handle.execute_read(GetFooById { ... }).await
Jake的消息
尽管如此,与Scylla Rust包装器不同,我们不需要字段以正确的顺序排列才能使我们的东西正常工作。我们做了两件聪明的事情
SELECT *
实际上是一个谎言。永远不要在预编译语句中使用SELECT *
,绝对不要。CQL 存在一个协议级别的漏洞,当在客户端和服务器之间存在架构不同步条件时,进行SELECT *
操作可能导致架构更改时的数据损坏。因此,我们采取的做法是查看实体类型结构,并将SELECT *
转换为SELECT col_a, col_b, col_c
。这意味着如果某个列存在于架构中,但不在我们要反序列化的结构中,我们实际上不会查询它。这个漏洞的实质是,当向表中添加新列时,数据库可能开始返回该列的数据,而客户端却对此一无所知。在极端情况下,这可能导致数据反序列化错位。请参阅 https://docs.datastax.com/en/developer/java-driver/3.0/manual/statements/prepared/#avoid-preparing-select-queries - 尽管这看起来最终在原生协议 v5 中得到了修复,但我不确定 Scylla 是否还在使用它。
- 对于查询参数的绑定,我们本质上解析 SQL 语句,找出所有的绑定位置,然后生成代码以按正确顺序(因为它们需要在查询中定义的顺序指定)绑定字段。我们通过在 proc 宏中编译时生成执行查询序列化的代码来实现这一点,因此我们不需要承担重新排序的运行时开销。
在启动时,我们准备一切,并使用代码中对数据库中的结构进行类型检查。手动注册一切都是可以的。如果你尝试使用未注册的查询,它可以在编译时失败。
依赖项
~12–23MB
~313K SLoC