-
-
Notifications
You must be signed in to change notification settings - Fork 253
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reusable columns #451
Comments
As a temporary fix, I made edgarogh/pgx-named-columns. |
My initial thought, based on the example from your repo was to instead use #[allow(non_snake_case)]
macro_rules! IndexedLetter {
() => { (name!(index, i32), name!(letter, &'static str)) };
(idx : $idx:expr, letter : $letter:expr) => { (i$dx, $letter) };
}
#[pg_extern]
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,
})
} Unfortunately, pgx fails to compile that with:
Which I suppose makes sense. Perhaps we could teach Maybe we could also invent our own I agree that managing an anonymous tuple with lots of fields is unwieldy, but I'm not sure that faking it with a struct is really the right answer. I think it starts to get confused with trying to actually return that type instead. |
edgarogh/pgx-named-columns is clearly a hack, and nothing more. It isn't even capable of forwarding tokens to
I'm pretty sure that macros always execute from the outside to the inside. That's why the It is not AFAIK, possible to expand a macro manually from within another macro. There are exceptions inside the compiler (i.e. At the same time, I'm not sure that macros were ever supposed to be used like expressions (regarding the But I completely understand that it's a complex problem. I'm guessing To me, the only solution, and I'm not implying that it is simple nor realistically feasible, or that contributors would like to waste time on this, would be to cut down on macro usage and rely on more traditional Rust features, most importantly traits. Here's an example of how it could work with
Anyways, it's not an easy problem, and I won't mind if this issue is closed since my personal problem is "solved" with my crate. I haven't dived in your codebase yet, but I'd happily try to contribute code if a solution is decided. And I'm still available if someone wants to continue discussing about this issue. |
I haven't considered using the trait system for what are now If pgx had its own "expand_struct_to_tuple!()" macro, then I'm sure we could do the expansion ourselves -- probably not super smart tho. Maybe we invent a // in `pgx` itself
pub trait PostgresRowType;
#[derive(PostgresRowType)]
struct IndexedLetter {
index: i32,
letter: &'static str,
}
// generates this...
impl PostgresRowType for IndexedLetter;
impl From<IndexedLetter> for (i32, &'static str) {
fn from(v: IndexedLetter) -> Self {
(v.index, v.letter)
}
} Then I think we'd have enough type information to know that... #[pg_extern]
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,
})
} ... is returning something we then need to call Need to noodle it more. Alternatively, pgx could support Postgres composite types that are mapped to Rust structs, and then you can deal with the named attributes from the SQL. |
That's a great idea, and probably the simplest ! However, how do you find about columns and their types to generate the
My bad, I was almost certain that a |
You can use #[derive(Default)]
struct MyTypeWithManyFields {
col_a: i32,
col_b: i32,
col_c: i32,
col_d: i32,
col_e: i32,
col_f: i32,
col_g: i32,
}
impl Into<(i32,i32,i32,i32,i32,i32,i32)> for MyTypeWithManyFields {
fn into(self) -> (i32,i32,i32,i32,i32,i32,i32) {
(
self.col_a,
self.col_b,
self.col_c,
self.col_d,
self.col_e,
self.col_f,
self.col_g,
)
}
}
#[pg_extern]
fn many_columns() -> impl Iterator<Item = (
name!(col_a, i32),
name!(col_b, i32),
name!(col_c, i32),
name!(col_d, i32),
name!(col_e, i32),
name!(col_f, i32),
name!(col_g, i32),
)> {
(0..=10).map(|_| MyTypeWithManyFields::default()).map(Into::into)
} Ideally we'd support something like: type MyManyColumns = (
name!(col_a, i32),
name!(col_b, i32),
name!(col_c, i32),
name!(col_d, i32),
name!(col_e, i32),
name!(col_f, i32),
name!(col_g, i32),
);
#[derive(Default)]
struct MyTypeWithManyFields {
col_a: i32,
col_b: i32,
col_c: i32,
col_d: i32,
col_e: i32,
col_f: i32,
col_g: i32,
}
impl Into<MyManyColumns> for MyTypeWithManyFields {
fn into(self) -> MyManyColumns {
(
self.col_a,
self.col_b,
self.col_c,
self.col_d,
self.col_e,
self.col_f,
self.col_g,
)
}
}
#[pg_extern]
fn many_columns() -> impl Iterator<Item = MyManyColumns> {
(0..=10).map(|_| MyTypeWithManyFields::default()).map(Into::into)
} However our proc macros aren't really sophisticated enough to handle two parts of this:
I think we could solve the second issue, but |
hmm. We shouldn't be doing that. That would be kinda mean! |
After the redesign in #615, we are now capable of using traits to generate SQL. We should start thinking about approaches to this or in general reassessing the design here. |
#1701 has taken a step towards making this real. We need to take a few more in order to actually address this. |
I have a
#[pg_export]
function with a lot of columns. Currently 30, but it might come close to 50 because I'm representing relatively complex types with a lot of fields.The problem is that I have also 30 lines of :
And 30 lines of :
at the end of my function, with no easy way to know which value corresponds to which column at first glance.
Also, I'm thinking of creating a variant of my current
#[pg_export]
function, that has different parameters but returns the exact same values in the end... which means I'd have to copy-paste everything and always keep the structure in sync.I'm looking for a way to define a structure that represents all of these columns as named fields, so all of my problems are solved at once: the function signatures are kept simple and the returned value is much easier to read.
I looked a bit in the documentation and found
#[derive(PostgresType)]
but it doesn't look like it's what I'm looking for as it requires derivingserde
's traits, which a lot ofpgx
-provided PostgreSQL types don't derive.The text was updated successfully, but these errors were encountered: