pgrx icon indicating copy to clipboard operation
pgrx copied to clipboard

How to implement record type?

Open ccleve opened this issue 3 years ago • 4 comments

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);

ccleve avatar Dec 10 '21 19:12 ccleve

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.

ccleve avatar Dec 10 '21 20:12 ccleve

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.

eeeebbbbrrrr avatar Dec 10 '21 20:12 eeeebbbbrrrr

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';

ccleve avatar Dec 10 '21 22:12 ccleve

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.

eeeebbbbrrrr avatar Dec 13 '21 18:12 eeeebbbbrrrr