use crate::{PgBox, PgRelation, pg_sys, regtypein, void_mut_ptr};
use pgrx_pg_sys::PgTryBuilder;
use pgrx_pg_sys::errcodes::PgSqlErrorCode;
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>> {
let typoid = PgTryBuilder::new(|| Some(regtypein(name)))
.catch_when(PgSqlErrorCode::ERRCODE_UNDEFINED_OBJECT, |_| None)
.execute()?;
Self::for_composite_type_by_oid(typoid)
}
pub fn for_composite_type_by_oid(typoid: pg_sys::Oid) -> Option<PgTupleDesc<'a>> {
unsafe {
if typoid == pg_sys::InvalidOid {
return None;
}
let tuple_desc = pg_sys::lookup_rowtype_tupdesc_copy(typoid, -1);
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 Deref for PgTupleDesc<'_> {
type Target = PgBox<pg_sys::TupleDescData>;
fn deref(&self) -> &Self::Target {
self.tupdesc.as_ref().unwrap()
}
}
impl Clone for PgTupleDesc<'_> {
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, need_release: false, need_pfree: true }
}
}
impl Drop for PgTupleDesc<'_> {
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 = "pg13",
feature = "pg14",
feature = "pg15",
feature = "pg16",
feature = "pg17"
))]
#[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]
}
#[cfg(feature = "pg18")]
#[inline]
fn tupdesc_get_attr(
tupdesc: &PgBox<pg_sys::TupleDescData>,
attno: usize,
) -> &pg_sys::FormData_pg_attribute {
let att_pointer =
unsafe { tupdesc.compact_attrs.as_ptr().add(tupdesc.natts.try_into().unwrap()).cast() };
let atts = unsafe { std::slice::from_raw_parts(att_pointer, 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 Iterator for TupleDescDataIntoIterator<'_> {
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)
}
}