Added pg_binary_protocol attribute to derive send and receive functions for PostgresType#2068
Conversation
pg_binary_protocol attribute to derive send and receive functions for PostgresType
d323e24 to
2f3d5bf
Compare
|
@LucaCappelletti94 this is on my radar -- I just don't have time to dedicate to a proper review at the moment. I hope to get to it this week. |
|
No worries, let me know if there is anything I can do to facilitate the review process |
|
FWIW, this overall looks ok to me, in terms of what it does. I tested it on one of my projects that has custom types. |
|
I will likely be doing an extended version afterwards, first trying to tackle the fixed size types as described in my issue #2076 |
|
Please do let me know whether there is anything I can do to facilitate the review of this PR - I want to build the fixed-size type derive on top of this PR, and I would like to make sure that first this one is considered a-okay |
eeeebbbbrrrr
left a comment
There was a problem hiding this comment.
This looks great and well thought out and implemented.
Thanks for your patience and most especially thanks for your work.
Welcome to pgrx v0.15.0. This begins a new series for pgrx that includes support for Postgres 18. As of this release, that means Postgres 18beta1. This release does contain a few breaking API changes but they're largely mechanical. Don't worry, the compiler will let you know! As always, please install our CI tool with `cargo install cargo-pgrx --version 0.15.0 --locked` and then run `cargo pgrx upgrade` in all of your extension crates. If you want to start working with Postgres 18beta1, you'll also need to re-init your pgrx environment with `cargo pgrx init`. That will automatically detect all the latest Postgres versions, including 18beta1. At the top here, I'd like to thank @silver-ymz for the 18beta1 support. It was a pleasant surprise to see that work come from the community -- it's no easy task to add a new Postgres version to pgrx! That said, as Postgres 18 is currently beta, you should consider pgrx' support for it as beta too. Please report any problems with 18beta1 (or discrepancies with other versions) as GitHub issues. Also, this release requires rust v1.88.0 or greater. `if-let` chains are now a thing and we're not afraid to use them. # What's Changed ## Postgres 18beta1 Support * Support Postgres 18beta1 by @silver-ymz in #2056 * pg18 support: add header and implement `#define` by @eeeebbbbrrrr in #2094 * improve pg_magic_func by @usamoi in #2088 ## More Headers * Added `catalog/heap.h` binding by @ccleve in #2072 * include `utils/pg_status.h` by @eeeebbbbrrrr in #2091 ## `cargo-pgrx` improvements * Pass `LLVM_*` variables to `--runas` command by @theory in #2083 * `does_db_exist()`: fix `psql` argument order by @eeeebbbbrrrr in #2093 * `cargo pgrx regress` output is no longer fully buffered by @eeeebbbbrrrr in #2095 * Detect `pgrx_embed` name from lib name by @YohDeadfall in #2035 * Fixed error message if no artifact found by @YohDeadfall in #2034 * `cargo-pgrx`: use system certificate store for HTTPS validation by @charmitro in #2074 * Decoding command output in Windows by @if0ne in #2084 ## Breaking Changes * fix GUC by @usamoi in #2064 * refactor GUC by @usamoi in #2066 ## New Stuff * Added `pg_binary_protocol` attribute to derive send and receive functions for `PostgresType` by @LucaCappelletti94 in #2068 * Expose guc hooks by @thesuhas in #2075 * Allows to create multiple aggregates for the same Rust type by @if0ne in #2078 ## General Code Cleanup * `cargo clippy --fix` by @eeeebbbbrrrr in #2092 * Use `if-let` to unpack Options by @stuhood in #2089 * docs: fix typo in `rust_byte_slice_to_bytea()` docs by @burmecia in #2071 * Added a missing `#[doc(hidden)]` by @LucaCappelletti94 in #2079 ## Administrative * Updated Fedora to latest in CI by @YohDeadfall in #2085 * fix ci on beta rust (1.89) by @usamoi in #2087 ## New Contributors Much thanks to our new contributors! Your work is sincerely appreciated! * @charmitro made their first contribution in #2074 * @thesuhas made their first contribution in #2075 * @if0ne made their first contribution in #2084 * @stuhood made their first contribution in #2089 **Full Changelog**: v0.14.3...v0.15.0
…ions for `PostgresType` (pgcentralfoundation#2068) This PR generally addresses issues such as pgcentralfoundation#2060, pgcentralfoundation#1446, pgcentralfoundation#1364, and pksunkara/pgx_ulid#27, and supersedes PR pgcentralfoundation#887. - [x] Introduces the generation of the `SEND` and `RECEIVE` bindings. - [x] Introduces the generation of the `*_recv` and `*_send` functions in rust, based on the existing `serde_cbor`-based methods [`cbor_encode`](https://github.com/pgcentralfoundation/pgrx/blob/231acce2448ce7df2b9f01cb0962077122a0cf83/pgrx/src/datum/varlena.rs#L380-L397) and [`cbor_decode`](https://github.com/pgcentralfoundation/pgrx/blob/231acce2448ce7df2b9f01cb0962077122a0cf83/pgrx/src/datum/varlena.rs#L399-L409) - [x] Introduces the `#[pg_binary_protocol]` decorator to be used alongside the `PostgresType` derive This PR is not API breaking, as even if people have created functions with naming clashes they would still need to add the attribute on the interested structs. Many thanks to @YohDeadfall and @zommiommy for their help in understanding better pgrx internals and postgres's binary protocol. ## The generated Rust & SQL Given a custom rust type such as: ```rust #[derive(serde::Serialize, serde::Deserialize, pgrx::PostgresType)] #[pg_binary_protocol] pub struct PositiveU32 { pub field: i32, } ``` The resulting generated rust bindings look like: ```rust #[doc(hidden)] #[::pgrx::pgrx_macros::pg_extern(immutable, strict, parallel_safe)] pub fn positiveu32_recv( internal: ::pgrx::datum::Internal, ) -> PositiveU32 { let buf = unsafe { internal.get_mut::<::pgrx::pg_sys::StringInfoData>().unwrap() }; let mut serialized = ::pgrx::StringInfo::new(); serialized.push_bytes(&[0u8; ::pgrx::pg_sys::VARHDRSZ]); serialized.push_bytes(unsafe { core::slice::from_raw_parts( buf.data as *const u8, buf.len as usize ) }); let size = serialized.len(); let varlena = serialized.into_char_ptr(); unsafe{ ::pgrx::set_varsize_4b(varlena as *mut ::pgrx::pg_sys::varlena, size as i32); buf.cursor = buf.len; ::pgrx::datum::cbor_decode(varlena as *mut ::pgrx::pg_sys::varlena) } } #[doc(hidden)] #[::pgrx::pgrx_macros::pg_extern(immutable, strict, parallel_safe)] pub fn positiveu32_send(input: PositiveU32) -> Vec<u8> { use ::pgrx::datum::{FromDatum, IntoDatum}; let Some(datum): Option<::pgrx::pg_sys::Datum> = input.into_datum() else { ::pgrx::error!("Datum of type `{}` is unexpectedly NULL.", stringify!(#name)); }; unsafe { let Some(serialized): Option<Vec<u8>> = FromDatum::from_datum(datum, false) else { ::pgrx::error!("Failed to CBOR-serialize Datum to type `{}`.", stringify!(#name)); }; serialized } } ``` And the associated generated SQL looks like: ```sql /* <begin connected objects> */ /* This file is auto generated by pgrx. The ordering of items is not stable, it is driven by a dependency graph. */ /* </end connected objects> */ /* <begin connected objects> */ -- utils/diesel-pgrx/example_extension/src/lib.rs:16 -- example_extension::PositiveU32 CREATE TYPE PositiveU32; -- utils/diesel-pgrx/example_extension/src/lib.rs:16 -- example_extension::positiveu32_in CREATE FUNCTION "positiveu32_in"( "input" cstring /* core::option::Option<&core::ffi::c_str::CStr> */ ) RETURNS PositiveU32 /* core::option::Option<example_extension::PositiveU32> */ IMMUTABLE PARALLEL SAFE LANGUAGE c /* Rust */ AS 'MODULE_PATHNAME', 'positiveu32_in_wrapper'; -- utils/diesel-pgrx/example_extension/src/lib.rs:16 -- example_extension::positiveu32_out CREATE FUNCTION "positiveu32_out"( "input" PositiveU32 /* example_extension::PositiveU32 */ ) RETURNS cstring /* alloc::ffi::c_str::CString */ IMMUTABLE STRICT PARALLEL SAFE LANGUAGE c /* Rust */ AS 'MODULE_PATHNAME', 'positiveu32_out_wrapper'; -- utils/diesel-pgrx/example_extension/src/lib.rs:16 -- example_extension::positiveu32_recv CREATE FUNCTION "positiveu32_recv"( "internal" internal /* pgrx::datum::internal::Internal */ ) RETURNS PositiveU32 /* example_extension::PositiveU32 */ IMMUTABLE STRICT PARALLEL SAFE LANGUAGE c /* Rust */ AS 'MODULE_PATHNAME', 'positiveu32_recv_wrapper'; -- utils/diesel-pgrx/example_extension/src/lib.rs:16 -- example_extension::positiveu32_send CREATE FUNCTION "positiveu32_send"( "input" PositiveU32 /* example_extension::PositiveU32 */ ) RETURNS bytea /* alloc::vec::Vec<u8> */ IMMUTABLE STRICT PARALLEL SAFE LANGUAGE c /* Rust */ AS 'MODULE_PATHNAME', 'positiveu32_send_wrapper'; -- utils/diesel-pgrx/example_extension/src/lib.rs:16 -- example_extension::PositiveU32 CREATE TYPE PositiveU32 ( INTERNALLENGTH = variable, INPUT = positiveu32_in, /* example_extension::positiveu32_in */ OUTPUT = positiveu32_out, /* example_extension::positiveu32_out */ RECEIVE = positiveu32_recv, /* example_extension::positiveu32_recv */ SEND = positiveu32_send, /* example_extension::positiveu32_send */ STORAGE = extended ); /* </end connected objects> */ ``` ## On Diesel traits I use [`diesel`](https://docs.rs/diesel/latest/diesel/) extensively in my projects, and it uses the binary protocol to send data to PostgreSQL. This PR makes it possible to use Postgres's binary protocol with `pgrx` type, so I can now implement diesel's [`ToSql`](https://docs.rs/diesel/latest/diesel/serialize/trait.ToSql.html) and [`FromSql`](https://docs.rs/diesel/latest/diesel/deserialize/trait.FromSql.html) traits as follows: ```rust #[derive( Debug, diesel::FromSqlRow, diesel::AsExpression, serde::Serialize, serde::Deserialize, pgrx::PostgresType )] #[pg_binary_protocol] #[diesel(sql_type = MyCustomTypeDiesel)] pub struct MyCustomType { pub field1: String, pub field2: i32, } #[derive( Debug, Clone, Copy, Default, diesel::query_builder::QueryId, diesel::sql_types::SqlType, )] #[diesel(postgres_type(name = "my_custom_type"))] pub struct MyCustomTypeDiesel; impl diesel::serialize::ToSql<MyCustomTypeDiesel, diesel::pg::Pg> for MyCustomType { fn to_sql<'b>(&'b self, out: &mut diesel::serialize::Output<'b, '_, diesel::pg::Pg>) -> diesel::serialize::Result { use std::io::Write; serde_cbor::to_writer(out, self)?; Ok(diesel::serialize::IsNull::No) } } impl diesel::deserialize::FromSql<MyCustomTypeDiesel, diesel::pg::Pg> for MyCustomType { fn from_sql(raw: diesel::pg::PgValue<'_>) -> diesel::deserialize::Result<Self> { Ok(serde_cbor::from_slice(raw.as_bytes())?) } } ``` *P.S. Sorry for the very small commits, but I could only figure out how to compile pgrx inside a Docker and therefore to test it in there I had to push every single small test edit.* *P.P.S I have also tried to add support for fixed-size types, but there is too much varlena-specific methods at this time and the amount of code it would be needed for that makes it necessarily a separate PR*
Welcome to pgrx v0.15.0. This begins a new series for pgrx that includes support for Postgres 18. As of this release, that means Postgres 18beta1. This release does contain a few breaking API changes but they're largely mechanical. Don't worry, the compiler will let you know! As always, please install our CI tool with `cargo install cargo-pgrx --version 0.15.0 --locked` and then run `cargo pgrx upgrade` in all of your extension crates. If you want to start working with Postgres 18beta1, you'll also need to re-init your pgrx environment with `cargo pgrx init`. That will automatically detect all the latest Postgres versions, including 18beta1. At the top here, I'd like to thank @silver-ymz for the 18beta1 support. It was a pleasant surprise to see that work come from the community -- it's no easy task to add a new Postgres version to pgrx! That said, as Postgres 18 is currently beta, you should consider pgrx' support for it as beta too. Please report any problems with 18beta1 (or discrepancies with other versions) as GitHub issues. Also, this release requires rust v1.88.0 or greater. `if-let` chains are now a thing and we're not afraid to use them. # What's Changed ## Postgres 18beta1 Support * Support Postgres 18beta1 by @silver-ymz in pgcentralfoundation#2056 * pg18 support: add header and implement `#define` by @eeeebbbbrrrr in pgcentralfoundation#2094 * improve pg_magic_func by @usamoi in pgcentralfoundation#2088 ## More Headers * Added `catalog/heap.h` binding by @ccleve in pgcentralfoundation#2072 * include `utils/pg_status.h` by @eeeebbbbrrrr in pgcentralfoundation#2091 ## `cargo-pgrx` improvements * Pass `LLVM_*` variables to `--runas` command by @theory in pgcentralfoundation#2083 * `does_db_exist()`: fix `psql` argument order by @eeeebbbbrrrr in pgcentralfoundation#2093 * `cargo pgrx regress` output is no longer fully buffered by @eeeebbbbrrrr in pgcentralfoundation#2095 * Detect `pgrx_embed` name from lib name by @YohDeadfall in pgcentralfoundation#2035 * Fixed error message if no artifact found by @YohDeadfall in pgcentralfoundation#2034 * `cargo-pgrx`: use system certificate store for HTTPS validation by @charmitro in pgcentralfoundation#2074 * Decoding command output in Windows by @if0ne in pgcentralfoundation#2084 ## Breaking Changes * fix GUC by @usamoi in pgcentralfoundation#2064 * refactor GUC by @usamoi in pgcentralfoundation#2066 ## New Stuff * Added `pg_binary_protocol` attribute to derive send and receive functions for `PostgresType` by @LucaCappelletti94 in pgcentralfoundation#2068 * Expose guc hooks by @thesuhas in pgcentralfoundation#2075 * Allows to create multiple aggregates for the same Rust type by @if0ne in pgcentralfoundation#2078 ## General Code Cleanup * `cargo clippy --fix` by @eeeebbbbrrrr in pgcentralfoundation#2092 * Use `if-let` to unpack Options by @stuhood in pgcentralfoundation#2089 * docs: fix typo in `rust_byte_slice_to_bytea()` docs by @burmecia in pgcentralfoundation#2071 * Added a missing `#[doc(hidden)]` by @LucaCappelletti94 in pgcentralfoundation#2079 ## Administrative * Updated Fedora to latest in CI by @YohDeadfall in pgcentralfoundation#2085 * fix ci on beta rust (1.89) by @usamoi in pgcentralfoundation#2087 ## New Contributors Much thanks to our new contributors! Your work is sincerely appreciated! * @charmitro made their first contribution in pgcentralfoundation#2074 * @thesuhas made their first contribution in pgcentralfoundation#2075 * @if0ne made their first contribution in pgcentralfoundation#2084 * @stuhood made their first contribution in pgcentralfoundation#2089 **Full Changelog**: pgcentralfoundation/pgrx@v0.14.3...v0.15.0
This PR generally addresses issues such as #2060, #1446, #1364, and pksunkara/pgx_ulid#27, and supersedes PR #887.
SENDandRECEIVEbindings.*_recvand*_sendfunctions in rust, based on the existingserde_cbor-based methodscbor_encodeandcbor_decode#[pg_binary_protocol]decorator to be used alongside thePostgresTypederiveThis PR is not API breaking, as even if people have created functions with naming clashes they would still need to add the attribute on the interested structs.
Many thanks to @YohDeadfall and @zommiommy for their help in understanding better pgrx internals and postgres's binary protocol.
The generated Rust & SQL
Given a custom rust type such as:
The resulting generated rust bindings look like:
And the associated generated SQL looks like:
On Diesel traits
I use
dieselextensively in my projects, and it uses the binary protocol to send data to PostgreSQL. This PR makes it possible to use Postgres's binary protocol withpgrxtype, so I can now implement diesel'sToSqlandFromSqltraits as follows:P.S. Sorry for the very small commits, but I could only figure out how to compile pgrx inside a Docker and therefore to test it in there I had to push every single small test edit.
P.P.S I have also tried to add support for fixed-size types, but there is too much varlena-specific methods at this time and the amount of code it would be needed for that makes it necessarily a separate PR