use crate::{PgBox, PgOid, WhoAllocated, pg_sys, rust_regtypein, set_varsize_4b};
use core::fmt::Display;
use pgrx_pg_sys::panic::ErrorReportable;
use std::{
any::Any,
ffi::{CStr, CString},
ptr::addr_of_mut,
str,
};
pub trait IntoDatum {
fn into_datum(self) -> Option<pg_sys::Datum>;
fn type_oid() -> pg_sys::Oid;
fn composite_type_oid(&self) -> Option<pg_sys::Oid> {
None
}
#[inline]
fn is_compatible_with(other: pg_sys::Oid) -> bool {
Self::type_oid() == other
}
}
impl<T> IntoDatum for Option<T>
where
T: IntoDatum,
{
fn into_datum(self) -> Option<pg_sys::Datum> {
match self {
Some(t) => t.into_datum(),
None => None,
}
}
fn type_oid() -> pg_sys::Oid {
T::type_oid()
}
}
impl<T, E> IntoDatum for Result<T, E>
where
T: IntoDatum,
E: Any + Display,
{
#[inline]
fn into_datum(self) -> Option<pg_sys::Datum> {
self.unwrap_or_report().into_datum()
}
#[inline]
fn type_oid() -> pg_sys::Oid {
T::type_oid()
}
}
impl IntoDatum for bool {
#[inline]
fn into_datum(self) -> Option<pg_sys::Datum> {
Some(pg_sys::Datum::from(if self { 1 } else { 0 }))
}
fn type_oid() -> pg_sys::Oid {
pg_sys::BOOLOID
}
}
impl IntoDatum for i8 {
fn into_datum(self) -> Option<pg_sys::Datum> {
Some(pg_sys::Datum::from(self))
}
fn type_oid() -> pg_sys::Oid {
pg_sys::CHAROID
}
}
impl IntoDatum for i16 {
#[inline]
fn into_datum(self) -> Option<pg_sys::Datum> {
Some(pg_sys::Datum::from(self))
}
fn type_oid() -> pg_sys::Oid {
pg_sys::INT2OID
}
fn is_compatible_with(other: pg_sys::Oid) -> bool {
Self::type_oid() == other || i8::type_oid() == other
}
}
impl IntoDatum for i32 {
#[inline]
fn into_datum(self) -> Option<pg_sys::Datum> {
Some(pg_sys::Datum::from(self))
}
fn type_oid() -> pg_sys::Oid {
pg_sys::INT4OID
}
fn is_compatible_with(other: pg_sys::Oid) -> bool {
Self::type_oid() == other || i8::type_oid() == other || i16::type_oid() == other
}
}
impl IntoDatum for i64 {
#[inline]
fn into_datum(self) -> Option<pg_sys::Datum> {
Some(pg_sys::Datum::from(self))
}
fn type_oid() -> pg_sys::Oid {
pg_sys::INT8OID
}
fn is_compatible_with(other: pg_sys::Oid) -> bool {
Self::type_oid() == other
|| i8::type_oid() == other
|| i16::type_oid() == other
|| i32::type_oid() == other
|| i64::type_oid() == other
}
}
impl IntoDatum for f32 {
#[inline]
fn into_datum(self) -> Option<pg_sys::Datum> {
Some(self.to_bits().into())
}
fn type_oid() -> pg_sys::Oid {
pg_sys::FLOAT4OID
}
}
impl IntoDatum for f64 {
#[inline]
fn into_datum(self) -> Option<pg_sys::Datum> {
Some(self.to_bits().into())
}
fn type_oid() -> pg_sys::Oid {
pg_sys::FLOAT8OID
}
}
impl IntoDatum for pg_sys::Oid {
#[inline]
fn into_datum(self) -> Option<pg_sys::Datum> {
if self == pg_sys::Oid::INVALID { None } else { Some(pg_sys::Datum::from(self.to_u32())) }
}
#[inline]
fn type_oid() -> pg_sys::Oid {
pg_sys::OIDOID
}
}
impl IntoDatum for pg_sys::TransactionId {
#[inline]
fn into_datum(self) -> Option<pg_sys::Datum> {
if self == Self::INVALID { None } else { Some(self.into()) }
}
#[inline]
fn type_oid() -> pg_sys::Oid {
pg_sys::XIDOID
}
}
impl IntoDatum for PgOid {
#[inline]
fn into_datum(self) -> Option<pg_sys::Datum> {
match self {
PgOid::Invalid => None,
oid => Some(oid.value().into()),
}
}
fn type_oid() -> pg_sys::Oid {
pg_sys::OIDOID
}
}
macro_rules! impl_into_datum_str {
($t:ty) => {
impl IntoDatum for $t {
#[inline]
fn into_datum(self) -> Option<$crate::pg_sys::Datum> {
self.as_bytes().into_datum()
}
#[inline]
fn type_oid() -> pg_sys::Oid {
pg_sys::TEXTOID
}
#[inline]
fn is_compatible_with(other: pg_sys::Oid) -> bool {
Self::type_oid() == other || other == pg_sys::VARCHAROID
}
}
};
}
impl_into_datum_str!(String);
impl_into_datum_str!(&String);
impl_into_datum_str!(&str);
impl IntoDatum for char {
#[inline]
fn into_datum(self) -> Option<pg_sys::Datum> {
let mut buf = [0; 4];
self.encode_utf8(&mut buf);
unsafe {
let len = self.len_utf8();
str::from_utf8_unchecked(&buf[..len]).into_datum()
}
}
fn type_oid() -> pg_sys::Oid {
pg_sys::VARCHAROID
}
#[inline]
fn is_compatible_with(other: pg_sys::Oid) -> bool {
Self::type_oid() == other || other == pg_sys::VARCHAROID
}
}
macro_rules! impl_into_datum_c_str {
($t:ty) => {
impl IntoDatum for $t {
#[inline]
fn into_datum(self) -> Option<$crate::pg_sys::Datum> {
unsafe {
let src = self.as_ref().to_bytes_with_nul();
let dst = $crate::pg_sys::palloc(src.len());
dst.copy_from(src.as_ptr().cast(), src.len());
Some(dst.into())
}
}
#[inline]
fn type_oid() -> pg_sys::Oid {
pg_sys::CSTRINGOID
}
}
};
}
impl_into_datum_c_str!(CString);
impl_into_datum_c_str!(&CString);
impl_into_datum_c_str!(&CStr);
impl<'a> IntoDatum for &'a [u8] {
#[inline]
fn into_datum(self) -> Option<pg_sys::Datum> {
let len = self.len().saturating_add(pg_sys::VARHDRSZ);
assert!(len < (u32::MAX as usize >> 2));
unsafe {
let varlena = pg_sys::palloc(len) as *mut pg_sys::varlena;
let varattrib_4b: *mut _ =
&mut varlena.cast::<pg_sys::varattrib_4b>().as_mut().unwrap_unchecked().va_4byte;
set_varsize_4b(varlena, len as i32);
std::ptr::copy_nonoverlapping(
self.as_ptr(),
addr_of_mut!((&mut *varattrib_4b).va_data).cast::<u8>(),
self.len(),
);
Some(pg_sys::Datum::from(varlena))
}
}
#[inline]
fn type_oid() -> pg_sys::Oid {
pg_sys::BYTEAOID
}
}
impl IntoDatum for Vec<u8> {
#[inline]
fn into_datum(self) -> Option<pg_sys::Datum> {
(&self[..]).into_datum()
}
#[inline]
fn type_oid() -> pg_sys::Oid {
pg_sys::BYTEAOID
}
}
impl IntoDatum for () {
#[inline]
fn into_datum(self) -> Option<pg_sys::Datum> {
Some(pg_sys::Datum::from(0))
}
fn type_oid() -> pg_sys::Oid {
pg_sys::VOIDOID
}
}
impl<T, AllocatedBy: WhoAllocated> IntoDatum for PgBox<T, AllocatedBy> {
#[inline]
fn into_datum(self) -> Option<pg_sys::Datum> {
if self.is_null() { None } else { Some(self.into_pg().into()) }
}
fn type_oid() -> pg_sys::Oid {
rust_regtypein::<T>()
}
}
impl IntoDatum for pg_sys::Datum {
fn into_datum(self) -> Option<pg_sys::Datum> {
Some(self)
}
fn type_oid() -> pg_sys::Oid {
pg_sys::INT8OID
}
}