15个版本

0.2.1 2024年5月25日
0.2.0 2024年2月16日
0.1.11-alpha2023年10月14日
0.1.1 2023年12月14日
0.1.0 2023年11月27日

#1497 in 过程宏

Download history 1/week @ 2024-04-08 187/week @ 2024-05-20 43/week @ 2024-05-27 13/week @ 2024-06-03 8/week @ 2024-06-10

1,078每月下载量
用于 3 个crate(直接使用2个)

MIT/Apache

98KB
2.5K SLoC

scyllax (sɪl-æks)

Scylla的SQLx和Discord启发式查询系统。

discord codecov CI

示例

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要求查询在执行前必须准备。为了在启动时准备(并检查)所有查询,创建一个查询集合并将其传递给Executor。

create_query_collection!(
    PersonQueries,
    [
        GetPersonById,
        GetPersonByEmail
    ],
    [
        DeletePersonById,
        UpsertPerson
    ]
);

let executor = Executor::<PersonQueries>::new(Arc::new(session)).await;

let user = executor.execute_read(GetPersonByEmail {
    email: "user@truffle.vip".to_string(),
}).await?;

println!("{user:#?}");

功能

  • 读取查询
  • 写入查询 (https://github.com/trufflehq/scyllax/pull/1)
  • 请求合并
  • 编译时选择查询验证
    • 确保where约束存在于结构体中
    • 确保where约束与结构体类型相同
  • 运行时查询验证(结构体与模式匹配)

待办事项

  • 移除anyhow,更精细的错误

用法

请参阅示例获取更多详情。

参考资料

  1. https://www.reddit.com/r/rust/comments/11ki2n7/a_look_at_how_discord_uses_rust_for_their_data/jb8dmrx/
#[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包装器不同,我们不需要字段以正确的顺序排列以使我们的东西工作。我们做了两件聪明的事情

  1. 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 是否已经开始使用它。
  1. 对于查询参数的绑定,我们本质上解析 SQL 语句并找出所有的绑定位置,然后生成将字段按正确顺序绑定的代码(因为在线路上,它们需要按照在查询中定义的顺序指定。)我们通过在 proc 宏中编译时生成查询序列化的代码来实现这一点,因此我们不会产生任何运行时开销,不需要重新排序。

在启动时,我们准备一切并也在代码中对结构体进行类型检查,以与数据库中的内容进行比较。手动注册一切是可以的,你可以让它在编译时失败,如果你尝试使用未注册的查询。

依赖关系

~2–2.8MB
~52K SLoC