pgrx
pgrx copied to clipboard
How to implement record type?
pgx implements lots of concrete types and a few pseudo-types. How difficult would it be to implement a "record" type?
https://www.postgresql.org/docs/current/datatype-pseudo.html
Technically, it's a "composite" type. https://www.postgresql.org/docs/current/rowtypes.html
It would be really useful for one of my functions to have access to an entire row.
Here's how it gets implemented in C: https://www.postgresql.org/docs/current/xfunc-c.html#id-1.8.3.13.10
HeapTupleHeader t = PG_GETARG_HEAPTUPLEHEADER(0);
A little more:
use crate::pg_sys::HeapTuple;
#[allow(unused)]
#[pg_extern]
fn rowtest(myrow: HeapTuple) {}
produces:
error[E0277]: the trait bound `*mut HeapTupleData: pgx::PostgresType` is not satisfied
--> src/query/query.rs:86:19
|
86 | fn rowtest(myrow: HeapTuple) {}
| ^^^^^^^^^ the trait `pgx::PostgresType` is not implemented for `*mut HeapTupleData`
|
= note: required because of the requirements on the impl of `FromDatum` for `*mut HeapTupleData`
note: required by a bound in `pg_getarg`
--> /Users/ccleve/.cargo/registry/src/github.com-1ecc6299db9ec823/pgx-0.2.4/src/fcinfo.rs:132:25
|
132 | pub fn pg_getarg<T: FromDatum>(fcinfo: pg_sys::FunctionCallInfo, num: usize) -> Option<T> {
| ^^^^^^^^^ required by this bound in `pg_getarg`
error[E0277]: the trait bound `*mut HeapTupleData: Deserialize<'_>` is not satisfied
--> src/query/query.rs:86:19
|
86 | fn rowtest(myrow: HeapTuple) {}
| ^^^^^^^^^ the trait `Deserialize<'_>` is not implemented for `*mut HeapTupleData`
|
= note: required because of the requirements on the impl of `FromDatum` for `*mut HeapTupleData`
note: required by a bound in `pg_getarg`
--> /Users/ccleve/.cargo/registry/src/github.com-1ecc6299db9ec823/pgx-0.2.4/src/fcinfo.rs:132:25
|
132 | pub fn pg_getarg<T: FromDatum>(fcinfo: pg_sys::FunctionCallInfo, num: usize) -> Option<T> {
| ^^^^^^^^^ required by this bound in `pg_getarg`
It's not clear to me whether to use HeapTuple or HeapTupleHeader, but they yield similar compile errors.
We've got plans to add support for composite and record types come the new year. I've put a lot of thought into it, so now it's a matter of sitting down to do it.
The tricky part for record types is going to be mapping it to a well-defined Rust type and/or some kind of dynamically defined structure (akin to a HashMap) as your code wouldn't necessarily know the shape of the record type being passed in.
In the mean time, you might could do some work to implement FromDatum for a wrapper around HeapTuple, but the schema generator won't know how to write record for the argument type in the CREATE FUNCTION definition.
Will this work?
use crate::pg_sys::Datum;
#[pg_extern]
pub fn foo(rec: Datum) {
// extract info from rec here
}
This compiles, but chokes during schema generation. Is there a way to suppress scheme gen for this function?
I could then manually add:
CREATE FUNCTION foo (anyelement)
RETURNS void
STRICT
LANGUAGE c /* Rust */
AS 'MODULE_PATHNAME', 'foo_wrapper';
I think you'd want to do something more like:
#[pg_extern]
fn foo (fcinfo: pg_sys::FunctionCallInfo) -> pg_sys::Datum {
let record = pg_getarg::<pg_sys::Datum>(fcinfo, 0); // get the first arg to the function, which would be your "record" type
// do the Postgres internals magic to unpack the record Datum into something you can use
}
And, this function will need the pgxsql doccomment block with the CREATE FUNCTION statement. Without it the schema generator will fail as it'll have no idea what the SQL-side arguments are supposed to be.