23 个版本

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

#1546过程宏

Download history 64/week @ 2024-05-19 156/week @ 2024-05-26 16/week @ 2024-06-02 5/week @ 2024-06-09 1/week @ 2024-06-16

1,608 每月下载量
2 个 Crates 中使用(通过 scyllax

MIT/Apache

57KB
1K 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_query 属性,定义插入/更新查询很容易。

#[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: "[email protected]".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
~53K SLoC