Skip to content

Commit

Permalink
Problem: pgx::datum::Serializer trait scope
Browse files Browse the repository at this point in the history
It takes care of both serialization and deserialization. However, some
types can potentially be only deserialized, or serialized. Besides, the
naming of the trait itself showed that it is not a perfect match.

Solution: split it into Serializer and Deserializer, respectively
  • Loading branch information
yrashk committed Mar 8, 2023
1 parent 38120f1 commit cd26d68
Show file tree
Hide file tree
Showing 3 changed files with 20 additions and 40 deletions.
3 changes: 2 additions & 1 deletion pgx-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -781,7 +781,8 @@ fn impl_postgres_type(ast: DeriveInput) -> syn::Result<proc_macro2::TokenStream>
de.bounds = bounds;
lt_generics.params.insert(0, GenericParam::Lifetime(de));
stream.extend(quote! {
impl #lt_generics ::pgx::datum::Serializer<'de> for #name #generics { }
impl #generics ::pgx::datum::Serializer for #name #generics { }
impl #lt_generics ::pgx::datum::Deserializer<'de> for #name #generics { }
});
}

Expand Down
38 changes: 6 additions & 32 deletions pgx-tests/src/tests/postgres_type_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Use of this source code is governed by the MIT license that can be found in the
*/
use core::ffi::CStr;
use pgx::prelude::*;
use pgx::{InOutFuncs, PgVarlena, PgVarlenaInOutFuncs, Serializer, StringInfo};
use pgx::{Deserializer, InOutFuncs, PgVarlena, PgVarlenaInOutFuncs, Serializer, StringInfo};
use serde::{Deserialize, Serialize};
use std::io::Write;
use std::str::FromStr;
Expand Down Expand Up @@ -157,11 +157,13 @@ pub enum JsonEnumType {
#[custom_serializer]
pub struct CustomSerialized;

impl<'de> Serializer<'de> for CustomSerialized {
impl Serializer for CustomSerialized {
fn to_writer<W: Write>(&self, mut writer: W) {
writer.write(&[1]).expect("can't write");
}
}

impl<'de> Deserializer<'de> for CustomSerialized {
fn from_slice(slice: &'de [u8]) -> Self {
if slice != &[1] {
panic!("wrong type")
Expand All @@ -171,33 +173,15 @@ impl<'de> Serializer<'de> for CustomSerialized {
}
}

#[derive(Serialize, Deserialize, PostgresType)]
#[custom_serializer]
pub struct AnotherCustomSerialized;

impl<'de> Serializer<'de> for AnotherCustomSerialized {
fn to_writer<W: Write>(&self, mut writer: W) {
writer.write(&[0]).expect("can't write");
}

fn from_slice(slice: &'de [u8]) -> Self {
if slice != &[0] {
panic!("wrong type")
} else {
AnotherCustomSerialized
}
}
}

#[cfg(any(test, feature = "pg_test"))]
#[pgx::pg_schema]
mod tests {
#[allow(unused_imports)]
use crate as pgx_tests;

use crate::tests::postgres_type_tests::{
AnotherCustomSerialized, CustomSerialized, CustomTextFormatSerializedEnumType,
CustomTextFormatSerializedType, JsonEnumType, JsonType, VarlenaEnumType, VarlenaType,
CustomSerialized, CustomTextFormatSerializedEnumType, CustomTextFormatSerializedType,
JsonEnumType, JsonType, VarlenaEnumType, VarlenaType,
};
use pgx::prelude::*;
use pgx::PgVarlena;
Expand Down Expand Up @@ -300,14 +284,4 @@ mod tests {
)
.unwrap();
}

#[pg_test(error = "wrong type")]
fn custom_serializer_wrong_type() {
let s = CustomSerialized;
let _ = Spi::get_one_with_args::<AnotherCustomSerialized>(
r#"SELECT $1"#,
vec![(PgOid::Custom(CustomSerialized::type_oid()), s.into_datum())],
)
.unwrap();
}
}
19 changes: 12 additions & 7 deletions pgx/src/datum/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,10 @@ use pgx_sql_entity_graph::RustSqlMapping;
/// Implemented automatically by `#[derive(PostgresType)]`
pub trait PostgresType {}

/// Serializing to and from datum
/// Serializing to datum
///
/// Default implementation uses CBOR and Varlena
pub trait Serializer<'de>: Serialize + Deserialize<'de> {
pub trait Serializer: Serialize {
/// Serializes the value to Datum
///
/// Default implementation wraps the output of `Self::to_writer` into a Varlena
Expand All @@ -93,7 +93,12 @@ pub trait Serializer<'de>: Serialize + Deserialize<'de> {
fn to_writer<W: std::io::Write>(&self, writer: W) {
serde_cbor::to_writer(writer, &self).expect("failed to encode as CBOR");
}
}

/// Deserializing from datum
///
/// Default implementation uses CBOR and Varlena
pub trait Deserializer<'de>: Deserialize<'de> {
/// Deserializes datum into a value
///
/// Default implementation assumes datum to be a varlena and uses `Self::from_slice`
Expand Down Expand Up @@ -121,7 +126,7 @@ pub trait Serializer<'de>: Serialize + Deserialize<'de> {
let input = datum.cast_mut_ptr();
// this gets the varlena Datum copied into this memory context
let varlena = pg_sys::pg_detoast_datum_copy(input as *mut pg_sys::varlena);
<Self as Serializer<'de>>::deserialize(varlena.into())
<Self as Deserializer<'de>>::deserialize(varlena.into())
})
}
}
Expand All @@ -134,9 +139,9 @@ pub trait Serializer<'de>: Serialize + Deserialize<'de> {
}
}

impl<'de, T> IntoDatum for T
impl<T> IntoDatum for T
where
T: PostgresType + Serializer<'de>,
T: PostgresType + Serializer,
{
fn into_datum(self) -> Option<pg_sys::Datum> {
Some(Serializer::serialize(&self))
Expand All @@ -149,7 +154,7 @@ where

impl<'de, T> FromDatum for T
where
T: PostgresType + Serializer<'de>,
T: PostgresType + Deserializer<'de>,
{
unsafe fn from_polymorphic_datum(
datum: pg_sys::Datum,
Expand All @@ -159,7 +164,7 @@ where
if is_null {
None
} else {
Some(<T as Serializer>::deserialize(datum))
Some(<T as Deserializer>::deserialize(datum))
}
}

Expand Down

0 comments on commit cd26d68

Please sign in to comment.