#postgresql #pgx #name #tuple #rows #structure #declare

pgx-named-columns

使用结构体而非元组,通过 name!() 声明宏声明可重用的 pgx 行

1 个不稳定版本

0.1.0 2022 年 2 月 22 日

#1581数据库接口

MIT/Apache

15KB
241

pgx-named-columns

pgx Rust 包 (github · crates · docs) 是一个用于在 Rust 中开发 PostgreSQL 扩展的非常好的库。考虑到以下 Rust 代码

const ALPHABET: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

#[pg_extern]
fn alphabet(length: i8) -> impl Iterator<Item = (
    name!(idx, i8),
    name!(letter, char),
)> {
    ALPHABET
        .chars()
        .take(length.clamp(0, 25) as usize)
        .enumerate()
        .map(|(i, l)| (i as i8, l))
}

...你可以在数据库中使用 alphabet 函数。

select alphabet(8);

注意列名是在 Rust 中如何定义的,第 3 行和第 4 行,使用了一个惰性声明宏。目前还没有其他定义它们的方法。这有两个原因

  • 列名不能轻松地在两个预期返回相同值的函数之间重用。例如,你无法轻松创建一个返回相同列的 alphabet_reverse 函数,而不需要复制粘贴代码。当你不是有两个列,而是有 50 个列时,这将成为一个大问题。
  • 乍一看,返回元组的哪个值对应于哪个列名并不清楚。如果你的元组包含很多相同类型的列,很容易混淆它们。

这些问题可以通过使用结构体作为迭代器的项来轻松解决,但是因为过程宏的工作方式,它们在运行时无法访问类型级别信息。唯一的正确解决方法是完全重新设计 pgx,但我无法做到。我已经打开了 pgx/issues#451

因此,创建了此库:使用肮脏的过程宏黑客,包括宏打开 Rust 文件两次以读取它所应用的项目之外的数据,它使得可以使用结构体作为返回的行。由于实现方式非常糟糕,因此存在数百万种失败的方式,但对于一般用例应该可以工作。下面是如何实现的

const ALPHABET: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

struct IndexedLetter {
    idx: i8,
    letter: char,
}

#[pg_extern_columns("path/to/current/file.rs")]
fn alphabet(length: i8) -> impl Iterator<Item = IndexedLetter> {
    ALPHABET
        .chars()
        .take(length.clamp(0, 25) as usize)
        .enumerate()
        .map(|(idx, letter)| IndexedLetter {
            idx: idx as _,
            letter,
        })
}

在属性参数中的路径可能是宏最丑陋的部分,它用于找到IndexedLetter的定义。

依赖项

~1.5MB
~37K SLoC