Skip to content

Commit

Permalink
a few tests around the various types pgx can generate.
Browse files Browse the repository at this point in the history
also fix the from_datum_in_memory_context() implementation for `FromDatum for PgVarlena<T> where T: Copy + Sized`;
  • Loading branch information
eeeebbbbrrrr committed Aug 15, 2020
1 parent 5c6b3d5 commit a599758
Show file tree
Hide file tree
Showing 4 changed files with 127 additions and 0 deletions.
1 change: 1 addition & 0 deletions pgx-tests/sql/load-order.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ tests_node_tests.generated.sql
tests_pg_try_tests.generated.sql
tests_xact_callback_tests.generated.sql
tests_xid64_tests.generated.sql
tests_postgres_type_tests.generated.sql
1 change: 1 addition & 0 deletions pgx-tests/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ mod node_tests;
mod numeric_tests;
mod pg_extern_args_tests;
mod pg_try_tests;
mod postgres_type_tests;
mod schema_tests;
mod spi_tests;
mod srf_tests;
Expand Down
101 changes: 101 additions & 0 deletions pgx-tests/src/tests/postgres_type_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
use pgx::*;
use serde::{Deserialize, Serialize};
use std::ffi::CStr;
use std::str::FromStr;

#[derive(Copy, Clone, PostgresType)]
#[pgvarlena_inoutfuncs]
pub struct VarlenaType {
a: f32,
b: f32,
c: i64,
}

impl PgVarlenaInOutFuncs for VarlenaType {
fn input(input: &CStr) -> PgVarlena<Self> where {
let mut iter = input.to_str().unwrap().split(',');
let (a, b, c) = (iter.next(), iter.next(), iter.next());

let mut result = PgVarlena::<VarlenaType>::new();
result.a = f32::from_str(a.unwrap()).expect("a is not a valid f32");
result.b = f32::from_str(b.unwrap()).expect("b is not a valid f32");
result.c = i64::from_str(c.unwrap()).expect("c is not a valid i64");
result
}

fn output(&self, buffer: &mut StringInfo) {
buffer.push_str(&format!("{},{},{}", self.a, self.b, self.c))
}
}

#[derive(Serialize, Deserialize, PostgresType)]
#[inoutfuncs]
pub struct CustomTextFormatSerializedType {
a: f32,
b: f32,
c: i64,
}

impl InOutFuncs for CustomTextFormatSerializedType {
fn input(input: &CStr) -> Self {
let mut iter = input.to_str().unwrap().split(',');
let (a, b, c) = (iter.next(), iter.next(), iter.next());

CustomTextFormatSerializedType {
a: f32::from_str(a.unwrap()).expect("a is not a valid f32"),
b: f32::from_str(b.unwrap()).expect("b is not a valid f32"),
c: i64::from_str(c.unwrap()).expect("c is not a valid i64"),
}
}

fn output(&self, buffer: &mut StringInfo) {
buffer.push_str(&format!("{},{},{}", self.a, self.b, self.c))
}
}

#[derive(Serialize, Deserialize, PostgresType)]
pub struct JsonType {
a: f32,
b: f32,
c: i64,
}

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

use crate::tests::postgres_type_tests::{
CustomTextFormatSerializedType, JsonType, VarlenaType,
};
use pgx::*;

#[pg_test]
fn test_mytype() {
let result = Spi::get_one::<PgVarlena<VarlenaType>>("SELECT '1.0,2.0,3'::VarlenaType")
.expect("SPI returned NULL");
assert_eq!(result.a, 1.0);
assert_eq!(result.b, 2.0);
assert_eq!(result.c, 3);
}

#[pg_test]
fn test_serializedtype() {
let result = Spi::get_one::<CustomTextFormatSerializedType>(
"SELECT '1.0,2.0,3'::CustomTextFormatSerializedType",
)
.expect("SPI returned NULL");
assert_eq!(result.a, 1.0);
assert_eq!(result.b, 2.0);
assert_eq!(result.c, 3);
}

#[pg_test]
fn test_jsontype() {
let result = Spi::get_one::<JsonType>(r#"SELECT '{"a": 1.0, "b": 2.0, "c": 3}'::JsonType"#)
.expect("SPI returned NULL");
assert_eq!(result.a, 1.0);
assert_eq!(result.b, 2.0);
assert_eq!(result.c, 3);
}
}
24 changes: 24 additions & 0 deletions pgx/src/datum/varlena.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,30 @@ where
Some(PgVarlena::<T>::from_datum(datum))
}
}

unsafe fn from_datum_in_memory_context(
mut memory_context: PgMemoryContexts,
datum: usize,
is_null: bool,
_typoid: u32,
) -> Option<Self> {
if is_null {
None
} else if datum == 0 {
panic!("a varlena Datum was flagged as non-null but the datum is zero");
} else {
memory_context.switch_to(|_| {
// this gets the varlena Datum copied into this memory context
let detoasted = pg_sys::pg_detoast_datum_copy(datum as *mut pg_sys::varlena);

// and we need to unpack it (if necessary), which will decompress it too
let varlena = pg_sys::pg_detoast_datum_packed(detoasted);

// and now we return it as a &str
Some(PgVarlena::<T>::from_datum(varlena as pg_sys::Datum))
})
}
}
}

impl<T> IntoDatum for T
Expand Down

0 comments on commit a599758

Please sign in to comment.