diff --git a/README.md b/README.md index f24dac07dc..56baf7cf36 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,7 @@ - **Easy Custom Types** + `#[derive(PostgresType)]` to use a Rust struct as a Postgres type - By default, represented as a CBOR-encoded object in-memory/on-disk, and JSON as human-readable + - Supports `#[pg_binary_protocol]` to generate binary protocol send/recv functions - Provide custom in-memory/on-disk/human-readable representations + `#[derive(PostgresEnum)]` to use a Rust enum as a Postgres enum + Composite types supported with the `pgrx::composite_type!("Sample")` macro diff --git a/articles/forging-sql-from-rust.md b/articles/forging-sql-from-rust.md index 6ece22fecb..1c178d6a71 100644 --- a/articles/forging-sql-from-rust.md +++ b/articles/forging-sql-from-rust.md @@ -97,6 +97,7 @@ These `sql` files must be generated from data within the Rust code. The SQL gene - Create 'Shell types', in & out functions, and 'Base types' for each `#[derive(PostgresType)]` marked type. ```rust #[derive(PostgresType, Serialize, Deserialize, Debug, Eq, PartialEq)] + #[pg_binary_protocol] pub struct Animals { names: Vec, age_lookup: HashMap, @@ -140,6 +141,7 @@ These `sql` files must be generated from data within the Rust code. The SQL gene Eq, PartialEq, Ord, Hash, PartialOrd, PostgresType, Serialize, Deserialize )] + #[pg_binary_protocol] pub struct Thing(String); ``` ```sql diff --git a/pgrx-macros/src/lib.rs b/pgrx-macros/src/lib.rs index 8b9cbd8c7e..4c3780b8dd 100644 --- a/pgrx-macros/src/lib.rs +++ b/pgrx-macros/src/lib.rs @@ -698,7 +698,7 @@ fn impl_postgres_enum(ast: DeriveInput) -> syn::Result stream.extend(quote! { impl ::pgrx::datum::FromDatum for #enum_ident { #[inline] - unsafe fn from_polymorphic_datum(datum: ::pgrx::pg_sys::Datum, is_null: bool, typeoid: ::pgrx::pg_sys::Oid) -> Option<#enum_ident> { + unsafe fn from_polymorphic_datum(datum: ::pgrx::pg_sys::Datum, is_null: bool, _typeoid: ::pgrx::pg_sys::Oid) -> Option<#enum_ident> { if is_null { None } else { @@ -781,6 +781,7 @@ Optionally accepts the following attributes: * `inoutfuncs(some_in_fn, some_out_fn)`: Define custom in/out functions for the type. * `pgvarlena_inoutfuncs(some_in_fn, some_out_fn)`: Define custom in/out functions for the `PgVarlena` of this type. +* `pg_binary_protocol`: Use the binary protocol for this type. * `pgrx(alignment = "")`: Derive Postgres alignment from Rust type. One of `"on"`, or `"off"`. * `sql`: Same arguments as [`#[pgrx(sql = ..)]`](macro@pgrx). */ @@ -789,6 +790,7 @@ Optionally accepts the following attributes: attributes( inoutfuncs, pgvarlena_inoutfuncs, + pg_binary_protocol, bikeshed_postgres_type_manually_impl_from_into_datum, requires, pgrx @@ -806,6 +808,9 @@ fn impl_postgres_type(ast: DeriveInput) -> syn::Result let has_lifetimes = generics.lifetimes().next(); let funcname_in = Ident::new(&format!("{name}_in").to_lowercase(), name.span()); let funcname_out = Ident::new(&format!("{name}_out").to_lowercase(), name.span()); + let funcname_recv = Ident::new(&format!("{name}_recv").to_lowercase(), name.span()); + let funcname_send = Ident::new(&format!("{name}_send").to_lowercase(), name.span()); + let mut args = parse_postgres_type_args(&ast.attrs); let mut stream = proc_macro2::TokenStream::new(); @@ -825,7 +830,9 @@ fn impl_postgres_type(ast: DeriveInput) -> syn::Result } } - if args.is_empty() { + if !args.contains(&PostgresTypeAttribute::InOutFuncs) + && !args.contains(&PostgresTypeAttribute::PgVarlenaInOutFuncs) + { // assume the user wants us to implement the InOutFuncs args.insert(PostgresTypeAttribute::Default); } @@ -937,14 +944,14 @@ fn impl_postgres_type(ast: DeriveInput) -> syn::Result if args.contains(&PostgresTypeAttribute::Default) { stream.extend(quote! { #[doc(hidden)] - #[::pgrx::pgrx_macros::pg_extern(immutable,parallel_safe)] + #[::pgrx::pgrx_macros::pg_extern(immutable, parallel_safe)] pub fn #funcname_in #generics(input: Option<&#lifetime ::core::ffi::CStr>) -> Option<#name #generics> { use ::pgrx::inoutfuncs::json_from_slice; input.map(|cstr| json_from_slice(cstr.to_bytes()).ok()).flatten() } #[doc(hidden)] - #[::pgrx::pgrx_macros::pg_extern (immutable,parallel_safe)] + #[::pgrx::pgrx_macros::pg_extern (immutable, parallel_safe)] pub fn #funcname_out #generics(input: #name #generics) -> ::pgrx::ffi::CString { use ::pgrx::inoutfuncs::json_to_vec; let mut bytes = json_to_vec(&input).unwrap(); @@ -1000,7 +1007,57 @@ fn impl_postgres_type(ast: DeriveInput) -> syn::Result }); } - let sql_graph_entity_item = sql_gen::PostgresTypeDerive::from_derive_input(ast)?; + if args.contains(&PostgresTypeAttribute::PgBinaryProtocol) { + // At this time, the `PostgresTypeAttribute` does not impact the way we generate + // the `recv` and `send` functions. + stream.extend(quote! { + #[doc(hidden)] + #[::pgrx::pgrx_macros::pg_extern(immutable, strict, parallel_safe)] + pub fn #funcname_recv #generics( + internal: ::pgrx::datum::Internal, + ) -> #name #generics { + 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]); // reserve space for the header + 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 #funcname_send #generics(input: #name #generics) -> Vec { + 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> = FromDatum::from_datum(datum, false) else { + ::pgrx::error!("Failed to CBOR-serialize Datum to type `{}`.", stringify!(#name)); + }; + serialized + } + } + }); + } + + let sql_graph_entity_item = sql_gen::PostgresTypeDerive::from_derive_input( + ast, + args.contains(&PostgresTypeAttribute::PgBinaryProtocol), + )?; sql_graph_entity_item.to_tokens(&mut stream); Ok(stream) @@ -1097,6 +1154,7 @@ fn impl_guc_enum(ast: DeriveInput) -> syn::Result { #[derive(Debug, Hash, Ord, PartialOrd, Eq, PartialEq)] enum PostgresTypeAttribute { InOutFuncs, + PgBinaryProtocol, PgVarlenaInOutFuncs, Default, ManualFromIntoDatum, @@ -1112,6 +1170,9 @@ fn parse_postgres_type_args(attributes: &[Attribute]) -> HashSet { categorized_attributes.insert(PostgresTypeAttribute::InOutFuncs); } + "pg_binary_protocol" => { + categorized_attributes.insert(PostgresTypeAttribute::PgBinaryProtocol); + } "pgvarlena_inoutfuncs" => { categorized_attributes.insert(PostgresTypeAttribute::PgVarlenaInOutFuncs); } diff --git a/pgrx-sql-entity-graph/src/lib.rs b/pgrx-sql-entity-graph/src/lib.rs index 8cb9cf4253..d2aa56ee7e 100644 --- a/pgrx-sql-entity-graph/src/lib.rs +++ b/pgrx-sql-entity-graph/src/lib.rs @@ -241,6 +241,10 @@ impl ToSql for SqlGraphEntity { in_fn_module_path, out_fn, out_fn_module_path, + receive_fn, + receive_fn_module_path, + send_fn, + send_fn_module_path, .. }) = &context.graph[neighbor] else { @@ -251,7 +255,16 @@ impl ToSql for SqlGraphEntity { && item.full_path.ends_with(in_fn); let is_out_fn = item.full_path.starts_with(out_fn_module_path) && item.full_path.ends_with(out_fn); - is_in_fn || is_out_fn + let is_receive_fn = + receive_fn_module_path.as_ref().is_some_and(|receive_fn_module_path| { + item.full_path.starts_with(receive_fn_module_path) + }) && receive_fn + .is_some_and(|receive_fn| item.full_path.ends_with(receive_fn)); + let is_send_fn = + send_fn_module_path.as_ref().is_some_and(|send_fn_module_path| { + item.full_path.starts_with(send_fn_module_path) + }) && send_fn.is_some_and(|send_fn| item.full_path.ends_with(send_fn)); + is_in_fn || is_out_fn || is_receive_fn || is_send_fn }) { Ok(String::default()) diff --git a/pgrx-sql-entity-graph/src/postgres_type/entity.rs b/pgrx-sql-entity-graph/src/postgres_type/entity.rs index 5f6f8ee148..6c87f45e83 100644 --- a/pgrx-sql-entity-graph/src/postgres_type/entity.rs +++ b/pgrx-sql-entity-graph/src/postgres_type/entity.rs @@ -105,6 +105,10 @@ pub struct PostgresTypeEntity { pub in_fn_module_path: String, pub out_fn: &'static str, pub out_fn_module_path: String, + pub receive_fn: Option<&'static str>, + pub receive_fn_module_path: Option, + pub send_fn: Option<&'static str>, + pub send_fn_module_path: Option, pub to_sql_config: ToSqlConfigEntity, pub alignment: Option, } @@ -152,6 +156,10 @@ impl ToSql for PostgresTypeEntity { out_fn, out_fn_module_path, in_fn, + receive_fn, + receive_fn_module_path, + send_fn, + send_fn_module_path, alignment, .. }) = item_node @@ -217,6 +225,80 @@ impl ToSql for PostgresTypeEntity { .ok_or_else(|| eyre!("Could not find out_fn graph entity."))?; let out_fn_sql = out_fn_entity.to_sql(context)?; + let receive_fn_graph_index_and_receive_fn_sql = receive_fn_module_path + .as_ref() + .zip(*receive_fn) + .map(|(receive_fn_module_path, receive_fn)| { + let receive_fn_module_path = if !receive_fn_module_path.is_empty() { + receive_fn_module_path.clone() + } else { + module_path.to_string() // Presume a local + }; + let receive_fn_path = format!( + "{receive_fn_module_path}{maybe_colons}{receive_fn}", + maybe_colons = if !receive_fn_module_path.is_empty() { "::" } else { "" } + ); + + // Find the receive function in the context + let (_, _index) = context + .externs + .iter() + .find(|(k, _v)| k.full_path == receive_fn_path) + .ok_or_else(|| eyre::eyre!("Did not find `receive_fn`: {receive_fn_path}."))?; + + let (receive_fn_graph_index, receive_fn_entity) = context + .graph + .neighbors_undirected(self_index) + .find_map(|neighbor| match &context.graph[neighbor] { + SqlGraphEntity::Function(func) if func.full_path == receive_fn_path => { + Some((neighbor, func)) + } + _ => None, + }) + .ok_or_else(|| eyre!("Could not find receive_fn graph entity."))?; + let receive_fn_sql = receive_fn_entity.to_sql(context)?; + + Ok::<_, eyre::Report>((receive_fn_graph_index, receive_fn_sql, receive_fn_path)) + }) + .transpose()?; + + let send_fn_graph_index_and_send_fn_sql = send_fn_module_path + .as_ref() + .zip(*send_fn) + .map(|(send_fn_module_path, send_fn)| { + let send_fn_module_path = if !send_fn_module_path.is_empty() { + send_fn_module_path.clone() + } else { + module_path.to_string() // Presume a local + }; + let send_fn_path = format!( + "{send_fn_module_path}{maybe_colons}{send_fn}", + maybe_colons = if !send_fn_module_path.is_empty() { "::" } else { "" } + ); + + // Find the send function in the context + let (_, _index) = context + .externs + .iter() + .find(|(k, _v)| k.full_path == send_fn_path) + .ok_or_else(|| eyre::eyre!("Did not find `send_fn: {}`.", send_fn_path))?; + + let (send_fn_graph_index, send_fn_entity) = context + .graph + .neighbors_undirected(self_index) + .find_map(|neighbor| match &context.graph[neighbor] { + SqlGraphEntity::Function(func) if func.full_path == send_fn_path => { + Some((neighbor, func)) + } + _ => None, + }) + .ok_or_else(|| eyre!("Could not find send_fn graph entity."))?; + let send_fn_sql = send_fn_entity.to_sql(context)?; + + Ok::<_, eyre::Report>((send_fn_graph_index, send_fn_sql, send_fn_path)) + }) + .transpose()?; + let shell_type = format!( "\n\ -- {file}:{line}\n\ @@ -244,6 +326,29 @@ impl ToSql for PostgresTypeEntity { }) .unwrap_or_default(); + let (receive_send_attributes, receive_send_sql) = receive_fn_graph_index_and_receive_fn_sql + .zip(send_fn_graph_index_and_send_fn_sql) + .map(|((receive_fn_graph_index, receive_fn_sql, receive_fn_path), (send_fn_graph_index, send_fn_sql, send_fn_path))| { + let receive_fn = receive_fn.unwrap(); + let send_fn = send_fn.unwrap(); + ( + format! { + "\ + \tRECEIVE = {schema_prefix_receive_fn}{receive_fn}, /* {receive_fn_path} */\n\ + \tSEND = {schema_prefix_send_fn}{send_fn}, /* {send_fn_path} */\n\ + ", + schema_prefix_receive_fn = context.schema_prefix_for(&receive_fn_graph_index), + schema_prefix_send_fn = context.schema_prefix_for(&send_fn_graph_index), + }, + format! { + "\n\ + {receive_fn_sql}\n\ + {send_fn_sql}\n\ + " + } + ) + }).unwrap_or_default(); + let materialized_type = format! { "\n\ -- {file}:{line}\n\ @@ -252,14 +357,24 @@ impl ToSql for PostgresTypeEntity { \tINTERNALLENGTH = variable,\n\ \tINPUT = {schema_prefix_in_fn}{in_fn}, /* {in_fn_path} */\n\ \tOUTPUT = {schema_prefix_out_fn}{out_fn}, /* {out_fn_path} */\n\ + {receive_send_attributes}\ \tSTORAGE = extended{alignment}\n\ );\ ", schema = context.schema_prefix_for(&self_index), schema_prefix_in_fn = context.schema_prefix_for(&in_fn_graph_index), - schema_prefix_out_fn = context.schema_prefix_for(&out_fn_graph_index), + schema_prefix_out_fn = context.schema_prefix_for(&out_fn_graph_index) }; - Ok(shell_type + "\n" + &in_fn_sql + "\n" + &out_fn_sql + "\n" + &materialized_type) + let result = shell_type + + "\n" + + &in_fn_sql + + "\n" + + &out_fn_sql + + &receive_send_sql + + "\n" + + &materialized_type; + + Ok(result) } } diff --git a/pgrx-sql-entity-graph/src/postgres_type/mod.rs b/pgrx-sql-entity-graph/src/postgres_type/mod.rs index ee6b8bc1f6..a4e6ad0b81 100644 --- a/pgrx-sql-entity-graph/src/postgres_type/mod.rs +++ b/pgrx-sql-entity-graph/src/postgres_type/mod.rs @@ -55,6 +55,8 @@ pub struct PostgresTypeDerive { generics: Generics, in_fn: Ident, out_fn: Ident, + receive_fn: Option, + send_fn: Option, to_sql_config: ToSqlConfig, alignment: Alignment, } @@ -65,17 +67,29 @@ impl PostgresTypeDerive { generics: Generics, in_fn: Ident, out_fn: Ident, + receive_fn: Option, + send_fn: Option, to_sql_config: ToSqlConfig, alignment: Alignment, ) -> Result, syn::Error> { if !to_sql_config.overrides_default() { crate::ident_is_acceptable_to_postgres(&name)?; } - Ok(CodeEnrichment(Self { generics, name, in_fn, out_fn, to_sql_config, alignment })) + Ok(CodeEnrichment(Self { + generics, + name, + in_fn, + out_fn, + receive_fn, + send_fn, + to_sql_config, + alignment, + })) } pub fn from_derive_input( derive_input: DeriveInput, + pg_binary_protocol: bool, ) -> Result, syn::Error> { match derive_input.data { syn::Data::Struct(_) | syn::Data::Enum(_) => {} @@ -93,12 +107,26 @@ impl PostgresTypeDerive { &format!("{}_out", derive_input.ident).to_lowercase(), derive_input.ident.span(), ); + let funcname_receive = (pg_binary_protocol).then(|| { + Ident::new( + &format!("{}_recv", derive_input.ident).to_lowercase(), + derive_input.ident.span(), + ) + }); + let funcname_send = (pg_binary_protocol).then(|| { + Ident::new( + &format!("{}_send", derive_input.ident).to_lowercase(), + derive_input.ident.span(), + ) + }); let alignment = Alignment::from_attributes(derive_input.attrs.as_slice())?; Self::new( derive_input.ident, derive_input.generics, funcname_in, funcname_out, + funcname_receive, + funcname_send, to_sql_config, alignment, ) @@ -129,6 +157,40 @@ impl ToEntityGraphTokens for PostgresTypeDerive { let in_fn = &self.in_fn; let out_fn = &self.out_fn; + let stringify_receive_fn = self + .receive_fn + .as_ref() + .map(|f| quote! { Some(stringify!(#f)) }) + .unwrap_or_else(|| quote! { None }); + let stringify_send_fn = self + .send_fn + .as_ref() + .map(|f| quote! { Some(stringify!(#f)) }) + .unwrap_or_else(|| quote! { None }); + let receive_fn_module_path = self + .receive_fn + .as_ref() + .map(|f| { + quote! {Some({ + let in_fn = stringify!(#f); + let mut path_items: Vec<_> = in_fn.split("::").collect(); + let _ = path_items.pop(); // Drop the one we don't want. + path_items.join("::") + })} + }) + .unwrap_or_else(|| quote! { None }); + let send_fn_module_path = self + .send_fn + .as_ref() + .map(|f| { + quote! {Some({ + let out_fn = stringify!(#f); + let mut path_items: Vec<_> = out_fn.split("::").collect(); + let _ = path_items.pop(); // Drop the one we don't want. + path_items.join("::") + })} + }) + .unwrap_or_else(|| quote! { None }); let sql_graph_entity_fn_name = format_ident!("__pgrx_internals_type_{}", self.name); @@ -199,6 +261,10 @@ impl ToEntityGraphTokens for PostgresTypeDerive { let _ = path_items.pop(); // Drop the one we don't want. path_items.join("::") }, + receive_fn: #stringify_receive_fn, + receive_fn_module_path: #receive_fn_module_path, + send_fn: #stringify_send_fn, + send_fn_module_path: #send_fn_module_path, to_sql_config: #to_sql_config, alignment: #alignment, }; @@ -213,10 +279,26 @@ impl ToRustCodeTokens for PostgresTypeDerive {} impl Parse for CodeEnrichment { fn parse(input: ParseStream) -> Result { let ItemStruct { attrs, ident, generics, .. } = input.parse()?; + + let pg_binary_protocol = attrs.iter().any(|a| a.path().is_ident("pg_binary_protocol")); + let to_sql_config = ToSqlConfig::from_attributes(attrs.as_slice())?.unwrap_or_default(); let in_fn = Ident::new(&format!("{}_in", ident).to_lowercase(), ident.span()); let out_fn = Ident::new(&format!("{}_out", ident).to_lowercase(), ident.span()); + let receive_fn = (pg_binary_protocol) + .then(|| Ident::new(&format!("{}_recv", ident).to_lowercase(), ident.span())); + let send_fn = (pg_binary_protocol) + .then(|| Ident::new(&format!("{}_send", ident).to_lowercase(), ident.span())); let alignment = Alignment::from_attributes(attrs.as_slice())?; - PostgresTypeDerive::new(ident, generics, in_fn, out_fn, to_sql_config, alignment) + PostgresTypeDerive::new( + ident, + generics, + in_fn, + out_fn, + receive_fn, + send_fn, + to_sql_config, + alignment, + ) } } diff --git a/pgrx-tests/src/tests/aggregate_tests.rs b/pgrx-tests/src/tests/aggregate_tests.rs index 8d2b7542c9..46bfacfde5 100644 --- a/pgrx-tests/src/tests/aggregate_tests.rs +++ b/pgrx-tests/src/tests/aggregate_tests.rs @@ -12,6 +12,7 @@ use pgrx::prelude::*; use serde::{Deserialize, Serialize}; #[derive(Copy, Clone, Default, Debug, PostgresType, Serialize, Deserialize)] +#[pg_binary_protocol] pub struct DemoSum { count: i32, } @@ -64,6 +65,7 @@ impl Aggregate for DemoSum { } #[derive(Copy, Clone, Default, Debug, PostgresType, Serialize, Deserialize)] +#[pg_binary_protocol] pub struct DemoPercentileDisc; #[pg_aggregate] @@ -105,11 +107,13 @@ mod demo_schema { use serde::{Deserialize, Serialize}; #[derive(Copy, Clone, PostgresType, Serialize, Deserialize)] + #[pg_binary_protocol] pub struct DemoState { pub sum: i32, } } #[derive(Copy, Clone, Default, Debug, PostgresType, Serialize, Deserialize)] +#[pg_binary_protocol] pub struct DemoCustomState; // demonstrate we can properly support an STYPE with a pg_schema diff --git a/pgrx-tests/src/tests/fcinfo_tests.rs b/pgrx-tests/src/tests/fcinfo_tests.rs index d76672ba2d..2ab88502ae 100644 --- a/pgrx-tests/src/tests/fcinfo_tests.rs +++ b/pgrx-tests/src/tests/fcinfo_tests.rs @@ -137,6 +137,7 @@ fn fcinfo_not_named_no_arg(fcinfo: pg_sys::FunctionCallInfo) -> i32 { } #[derive(PostgresType, Serialize, Deserialize, Debug, PartialEq)] +#[pg_binary_protocol] #[inoutfuncs] pub struct NullStrict {} @@ -153,6 +154,7 @@ impl InOutFuncs for NullStrict { } #[derive(PostgresType, Serialize, Deserialize, Debug, PartialEq)] +#[pg_binary_protocol] #[inoutfuncs] pub struct NullError {} diff --git a/pgrx-tests/src/tests/pg_cast_tests.rs b/pgrx-tests/src/tests/pg_cast_tests.rs index 3a8ed1f722..860fae6a7f 100644 --- a/pgrx-tests/src/tests/pg_cast_tests.rs +++ b/pgrx-tests/src/tests/pg_cast_tests.rs @@ -30,6 +30,7 @@ mod pg_catalog { } #[derive(PostgresType, Serialize, Deserialize)] + #[pg_binary_protocol] struct TestCastType; #[pg_cast(implicit, immutable)] diff --git a/pgrx-tests/src/tests/postgres_type_tests.rs b/pgrx-tests/src/tests/postgres_type_tests.rs index 2018681364..33d3a0f471 100644 --- a/pgrx-tests/src/tests/postgres_type_tests.rs +++ b/pgrx-tests/src/tests/postgres_type_tests.rs @@ -14,6 +14,7 @@ use serde::{Deserialize, Serialize}; use std::str::FromStr; #[derive(Copy, Clone, PostgresType, Serialize, Deserialize)] +#[pg_binary_protocol] #[pgvarlena_inoutfuncs] pub struct VarlenaType { a: f32, @@ -39,6 +40,7 @@ impl PgVarlenaInOutFuncs for VarlenaType { } #[derive(Copy, Clone, PostgresType, Serialize, Deserialize)] +#[pg_binary_protocol] #[pgvarlena_inoutfuncs] pub enum VarlenaEnumType { A, @@ -71,6 +73,7 @@ impl PgVarlenaInOutFuncs for VarlenaEnumType { } #[derive(Serialize, Deserialize, PostgresType)] +#[pg_binary_protocol] #[inoutfuncs] pub struct CustomTextFormatSerializedType { a: f32, @@ -105,6 +108,7 @@ fn fn_takes_option(input: Option) -> String { } #[derive(Serialize, Deserialize, PostgresType)] +#[pg_binary_protocol] #[inoutfuncs] pub enum CustomTextFormatSerializedEnumType { A, @@ -140,6 +144,7 @@ fn fn_takes_option_enum(input: Option) -> St } #[derive(Serialize, Deserialize, PostgresType)] +#[pg_binary_protocol] pub struct JsonType { a: f32, b: f32, @@ -147,6 +152,7 @@ pub struct JsonType { } #[derive(Serialize, Deserialize, PostgresType)] +#[pg_binary_protocol] #[serde(tag = "type")] pub enum JsonEnumType { E1 { a: f32 }, diff --git a/pgrx-tests/src/tests/result_tests.rs b/pgrx-tests/src/tests/result_tests.rs index 4246cb93a1..9b8b8f5096 100644 --- a/pgrx-tests/src/tests/result_tests.rs +++ b/pgrx-tests/src/tests/result_tests.rs @@ -19,6 +19,7 @@ use serde::*; // #[derive(Debug, Serialize, Deserialize, PostgresType)] +#[pg_binary_protocol] pub struct ResultTestsA; #[pg_extern] diff --git a/pgrx-tests/src/tests/roundtrip_tests.rs b/pgrx-tests/src/tests/roundtrip_tests.rs index 8dce10018e..7451d2897b 100644 --- a/pgrx-tests/src/tests/roundtrip_tests.rs +++ b/pgrx-tests/src/tests/roundtrip_tests.rs @@ -4,6 +4,7 @@ use rand::distr::{Alphanumeric, StandardUniform}; use rand::Rng; #[derive(pgrx::PostgresType, Clone, PartialEq, Eq, Debug, serde::Serialize, serde::Deserialize)] +#[pg_binary_protocol] pub struct RandomData { i: u64, s: String, diff --git a/pgrx-tests/src/tests/schema_tests.rs b/pgrx-tests/src/tests/schema_tests.rs index 78ab46796b..ad70e8ff3b 100644 --- a/pgrx-tests/src/tests/schema_tests.rs +++ b/pgrx-tests/src/tests/schema_tests.rs @@ -25,17 +25,21 @@ mod test_schema { fn func_generated_with_custom_sql() {} #[derive(Debug, PostgresType, Serialize, Deserialize)] + #[pg_binary_protocol] pub struct TestType(pub u64); #[derive(Debug, PostgresType, Serialize, Deserialize)] + #[pg_binary_protocol] #[pgrx(sql = false)] pub struct ElidedType(pub u64); #[derive(Debug, PostgresType, Serialize, Deserialize)] + #[pg_binary_protocol] #[pgrx(sql = generate_type)] pub struct OtherType(pub u64); #[derive(Debug, PostgresType, Serialize, Deserialize)] + #[pg_binary_protocol] #[pgrx(sql = "CREATE TYPE test_schema.ManuallyRenderedType;")] pub struct OverriddenType(pub u64); diff --git a/pgrx/src/aggregate.rs b/pgrx/src/aggregate.rs index c46c221e67..31d5a455bf 100644 --- a/pgrx/src/aggregate.rs +++ b/pgrx/src/aggregate.rs @@ -29,6 +29,7 @@ use serde::{Serialize, Deserialize}; // pg_module_magic!(); // Uncomment this outside of docs! #[derive(Copy, Clone, Default, PostgresType, Serialize, Deserialize)] +#[pg_binary_protocol] pub struct DemoSum { count: i32, } @@ -221,6 +222,7 @@ Sometimes it's useful to have aggregates share state, or use some other type for # use serde::{Serialize, Deserialize}; # #[derive(Copy, Clone, Default, PostgresType, Serialize, Deserialize)] +#[pg_binary_protocol] pub struct DemoSumState { count: i32, } diff --git a/pgrx/src/lib.rs b/pgrx/src/lib.rs index 78e7bec821..7e4fe40907 100644 --- a/pgrx/src/lib.rs +++ b/pgrx/src/lib.rs @@ -131,6 +131,7 @@ pub use pg_sys::{ check_for_interrupts, debug1, debug2, debug3, debug4, debug5, ereport, error, function_name, info, log, notice, warning, FATAL, PANIC, }; + #[doc(hidden)] pub use pgrx_sql_entity_graph;