| Crates.io | pgrx-named-columns |
| lib.rs | pgrx-named-columns |
| version | 0.2.0 |
| created_at | 2025-03-31 20:12:46.553877+00 |
| updated_at | 2025-03-31 20:12:46.553877+00 |
| description | Declare reusable pgrx rows using a structure instead of a tuple with the name!() decl-macro |
| homepage | |
| repository | https://github.com/edgarogh/pgrx-named-columns/ |
| max_upload_size | |
| id | 1613904 |
| size | 62,662 |
pgrx-named-columnsThe pgrx Rust crate (github · crates · docs) is a really nice library to develop PostgreSQL extensions in Rust. Given the following Rust code:
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))
}
...you can use the alphabet function inside your database.
select alphabet(8);
Note how the column names are defined in Rust, line 3 and 4, using an inert declarative macro. There is currently no other way to define them. This is a problem for 2 reasons :
alphabet_reverse function that returns the exact same columns without copy-pasting code. This become a big problem when you don't have 2, but 50 columns.These problems could easily be solved by using a struct as the impl Iterator's item, but due to the way procedural macros work, they cannot access type-level information when they run. The only proper way to solve this would be a complete redesign of pgrx, which I cannot do. I opened pgrx/issues#451.
Hence, the creation of this library : using filthy procedural macro hacks, including having the macro open a Rust file twice to read data outside the item it is applied to, it makes it possible to use a structure as returned rows. There are millions of way this can fail due to how badly implemented it is, but it should work for the general use-case. Here's how it looks :
const ALPHABET: &str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
struct IndexedLetter {
idx: i8,
letter: char,
}
#[pg_extern_columns("path/to/current/file.rs")]
fn alphabet(length: i8) -> ::pgrx::iter::TableIterator<'static, IndexedLetter> {
ALPHABET
.chars()
.take(length.clamp(0, 25) as usize)
.enumerate()
.map(|(idx, letter)| IndexedLetter {
idx: idx as _,
letter,
})
}
The path in the attribute parameters is probably the ugliest aspect of the macro, it is used to find the definition of IndexedLetter.