use crate::{pg_sys, void_mut_ptr, PgBox, PgRelation};
use pgx_pg_sys::AsPgCStr;
use std::ops::Deref;
pub struct PgTupleDesc<'a> {
tupdesc: Option<PgBox<pg_sys::TupleDescData>>,
parent: Option<&'a PgRelation>,
need_release: bool,
need_pfree: bool,
}
impl<'a> PgTupleDesc<'a> {
pub unsafe fn from_pg<'b>(ptr: pg_sys::TupleDesc) -> PgTupleDesc<'b> {
PgTupleDesc {
tupdesc: Some(PgBox::from_pg(ptr)),
parent: None,
need_release: true,
need_pfree: false,
}
}
pub unsafe fn from_pg_unchecked<'b>(ptr: pg_sys::TupleDesc) -> PgTupleDesc<'b> {
PgTupleDesc {
tupdesc: Some(PgBox::from_pg(ptr)),
parent: None,
need_release: false,
need_pfree: false,
}
}
pub unsafe fn from_pg_copy<'b>(ptr: pg_sys::TupleDesc) -> PgTupleDesc<'b> {
PgTupleDesc {
tupdesc: Some(PgBox::from_pg(pg_sys::CreateTupleDescCopyConstr(ptr))),
parent: None,
need_release: false,
need_pfree: true,
}
}
pub unsafe fn from_pg_is_copy<'b>(ptr: pg_sys::TupleDesc) -> PgTupleDesc<'b> {
PgTupleDesc {
tupdesc: Some(PgBox::from_pg(ptr)),
parent: None,
need_release: false,
need_pfree: true,
}
}
pub fn from_relation(parent: &PgRelation) -> PgTupleDesc {
PgTupleDesc {
tupdesc: Some(unsafe { PgBox::from_pg(parent.rd_att) }),
parent: Some(parent),
need_release: false,
need_pfree: false,
}
}
pub fn for_composite_type(name: &str) -> Option<PgTupleDesc<'a>> {
unsafe {
let mut typoid = pg_sys::Oid::INVALID;
let mut typmod = 0;
pg_sys::parseTypeString(name.as_pg_cstr(), &mut typoid, &mut typmod, true);
if typoid == pg_sys::InvalidOid {
return None;
}
let tuple_desc = pg_sys::lookup_rowtype_tupdesc_copy(typoid, typmod);
Some(PgTupleDesc::from_pg_copy(tuple_desc))
}
}
pub fn parent(&self) -> Option<&PgRelation> {
self.parent
}
pub fn oid(&self) -> pg_sys::Oid {
self.tupdesc.as_ref().unwrap().tdtypeid
}
pub fn typmod(&self) -> i32 {
self.tupdesc.as_ref().unwrap().tdtypmod
}
pub fn len(&self) -> usize {
self.tupdesc.as_ref().unwrap().natts as usize
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn get(&self, i: usize) -> Option<&pg_sys::FormData_pg_attribute> {
if i >= self.len() {
None
} else {
Some(tupdesc_get_attr(self.tupdesc.as_ref().unwrap(), i))
}
}
pub fn iter(&self) -> TupleDescIterator {
TupleDescIterator { tupdesc: self, curr: 0 }
}
pub fn into_pg(mut self) -> *mut pg_sys::TupleDescData {
self.tupdesc.take().unwrap().into_pg()
}
}
impl<'a> Deref for PgTupleDesc<'a> {
type Target = PgBox<pg_sys::TupleDescData>;
fn deref(&self) -> &Self::Target {
self.tupdesc.as_ref().unwrap()
}
}
impl<'a> Clone for PgTupleDesc<'a> {
fn clone(&self) -> Self {
let tupdesc = self.tupdesc.as_ref().expect("PgTupleDesc.tupdesc was None");
let tupdesc =
unsafe { PgBox::from_pg(pg_sys::CreateTupleDescCopyConstr(tupdesc.as_ptr())) };
Self {
tupdesc: Some(tupdesc),
parent: self.parent.clone(),
need_release: false,
need_pfree: true,
}
}
}
impl<'a> Drop for PgTupleDesc<'a> {
fn drop(&mut self) {
if self.tupdesc.is_some() {
let tupdesc = self.tupdesc.take().unwrap();
if self.need_release {
unsafe { release_tupdesc(tupdesc.as_ptr()) }
} else if self.need_pfree {
unsafe { pg_sys::pfree(tupdesc.as_ptr() as void_mut_ptr) }
}
}
}
}
pub unsafe fn release_tupdesc(ptr: pg_sys::TupleDesc) {
if (*ptr).tdrefcount >= 0 {
pg_sys::DecrTupleDescRefCount(ptr)
}
}
#[cfg(any(
feature = "pg11",
feature = "pg12",
feature = "pg13",
feature = "pg14",
feature = "pg15"
))]
#[inline]
fn tupdesc_get_attr(
tupdesc: &PgBox<pg_sys::TupleDescData>,
attno: usize,
) -> &pg_sys::FormData_pg_attribute {
let atts = unsafe { tupdesc.attrs.as_slice(tupdesc.natts as usize) };
&atts[attno]
}
pub struct TupleDescIterator<'a> {
tupdesc: &'a PgTupleDesc<'a>,
curr: usize,
}
impl<'a> Iterator for TupleDescIterator<'a> {
type Item = &'a pg_sys::FormData_pg_attribute;
fn next(&mut self) -> Option<Self::Item> {
let result = self.tupdesc.get(self.curr);
self.curr += 1;
result
}
}
pub struct TupleDescDataIntoIterator<'a> {
tupdesc: PgTupleDesc<'a>,
curr: usize,
}
impl<'a> IntoIterator for PgTupleDesc<'a> {
type Item = pg_sys::FormData_pg_attribute;
type IntoIter = TupleDescDataIntoIterator<'a>;
fn into_iter(self) -> Self::IntoIter {
TupleDescDataIntoIterator { tupdesc: self, curr: 0 }
}
}
impl<'a> Iterator for TupleDescDataIntoIterator<'a> {
type Item = pg_sys::FormData_pg_attribute;
fn next(&mut self) -> Option<Self::Item> {
let result = match self.tupdesc.get(self.curr) {
Some(result) => *result,
None => {
return None;
}
};
self.curr += 1;
Some(result)
}
}