use crate::{FromDatum, PgBox, PgMemoryContexts, pg_sys, void_mut_ptr};
use core::{ptr, slice};
use pgrx_pg_sys::ffi::pg_guard_ffi_boundary;
#[macro_export]
macro_rules! default {
($ty:ty, $val:tt) => {
$ty
};
($ty:ty, $val:path) => {
$ty
};
($ty:ty, $val:expr) => {
$ty
};
}
pub struct NULL;
#[macro_export]
macro_rules! name {
($name:tt, $ty:ty) => {
$ty
};
}
#[inline]
pub unsafe fn pg_getarg<T: FromDatum>(fcinfo: pg_sys::FunctionCallInfo, num: usize) -> Option<T> {
let datum = pg_get_nullable_datum(fcinfo, num);
unsafe {
if T::GET_TYPOID {
T::from_polymorphic_datum(datum.value, datum.isnull, super::pg_getarg_type(fcinfo, num))
} else {
T::from_datum(datum.value, datum.isnull)
}
}
}
#[inline]
pub unsafe fn pg_arg_is_null(fcinfo: pg_sys::FunctionCallInfo, num: usize) -> bool {
pg_get_nullable_datum(fcinfo, num).isnull
}
#[inline]
pub unsafe fn pg_getarg_datum(
fcinfo: pg_sys::FunctionCallInfo,
num: usize,
) -> Option<pg_sys::Datum> {
if pg_arg_is_null(fcinfo, num) { None } else { Some(pg_get_nullable_datum(fcinfo, num).value) }
}
#[inline]
pub unsafe fn pg_getarg_datum_raw(fcinfo: pg_sys::FunctionCallInfo, num: usize) -> pg_sys::Datum {
pg_get_nullable_datum(fcinfo, num).value
}
#[inline]
pub unsafe fn pg_get_nullable_datum(
fcinfo: pg_sys::FunctionCallInfo,
num: usize,
) -> pg_sys::NullableDatum {
let _nullptr_check = ptr::NonNull::new(fcinfo).expect("fcinfo pointer must be non-null");
unsafe {
let nargs = (*fcinfo).nargs;
let args_ptr: *const pg_sys::NullableDatum = ptr::addr_of!((*fcinfo).args).cast();
let args = slice::from_raw_parts(args_ptr, nargs as _);
args[num]
}
}
#[inline]
pub unsafe fn pg_return_null(fcinfo: pg_sys::FunctionCallInfo) -> pg_sys::Datum {
let fcinfo = unsafe { fcinfo.as_mut() }.unwrap();
fcinfo.isnull = true;
pg_sys::Datum::from(0)
}
pub unsafe fn pg_get_collation(fcinfo: pg_sys::FunctionCallInfo) -> pg_sys::Oid {
let fcinfo = unsafe { fcinfo.as_mut() }.unwrap();
fcinfo.fncollation
}
#[inline]
pub unsafe fn pg_getarg_type(fcinfo: pg_sys::FunctionCallInfo, num: usize) -> pg_sys::Oid {
pg_sys::get_fn_expr_argtype(fcinfo.as_ref().unwrap().flinfo, num as std::os::raw::c_int)
}
#[inline]
pub fn pg_return_void() -> pg_sys::Datum {
pg_sys::Datum::from(0)
}
pub unsafe fn pg_func_extra<ReturnType, DefaultValue: FnOnce() -> ReturnType>(
fcinfo: pg_sys::FunctionCallInfo,
default: DefaultValue,
) -> PgBox<ReturnType> {
let fcinfo = PgBox::from_pg(fcinfo);
let mut flinfo = PgBox::from_pg(fcinfo.flinfo);
if flinfo.fn_extra.is_null() {
flinfo.fn_extra = PgMemoryContexts::For(flinfo.fn_mcxt).leak_and_drop_on_delete(default())
as void_mut_ptr;
}
PgBox::from_pg(flinfo.fn_extra as *mut ReturnType)
}
pub unsafe fn direct_function_call<R: FromDatum>(
func: unsafe fn(pg_sys::FunctionCallInfo) -> pg_sys::Datum,
args: &[Option<pg_sys::Datum>],
) -> Option<R> {
direct_function_call_as_datum(func, args).and_then(|d| R::from_datum(d, false))
}
pub unsafe fn direct_pg_extern_function_call<R: FromDatum>(
func: unsafe extern "C-unwind" fn(pg_sys::FunctionCallInfo) -> pg_sys::Datum,
args: &[Option<pg_sys::Datum>],
) -> Option<R> {
direct_pg_extern_function_call_as_datum(func, args).and_then(|d| R::from_datum(d, false))
}
pub unsafe fn direct_function_call_as_datum(
func: unsafe fn(pg_sys::FunctionCallInfo) -> pg_sys::Datum,
args: &[Option<pg_sys::Datum>],
) -> Option<pg_sys::Datum> {
direct_function_call_as_datum_internal(|fcinfo| func(fcinfo), args)
}
unsafe fn direct_function_call_as_datum_internal(
func: impl FnOnce(pg_sys::FunctionCallInfo) -> pg_sys::Datum,
args: &[Option<pg_sys::Datum>],
) -> Option<pg_sys::Datum> {
let nargs: i16 = args.len().try_into().expect("too many args passed to function");
let fcinfo = pg_sys::palloc0(
std::mem::size_of::<pg_sys::FunctionCallInfoBaseData>()
+ std::mem::size_of::<pg_sys::NullableDatum>() * args.len(),
)
.cast::<pg_sys::FunctionCallInfoBaseData>();
(*fcinfo).flinfo = std::ptr::null_mut();
(*fcinfo).context = std::ptr::null_mut();
(*fcinfo).resultinfo = std::ptr::null_mut();
(*fcinfo).fncollation = pg_sys::InvalidOid;
(*fcinfo).isnull = false;
(*fcinfo).nargs = nargs;
let args_ptr: *mut pg_sys::NullableDatum = ptr::addr_of_mut!((*fcinfo).args).cast();
{
let arg_slice = slice::from_raw_parts_mut(args_ptr, args.len());
for (n_datum, arg) in arg_slice.iter_mut().zip(args) {
n_datum.isnull = arg.is_none();
n_datum.value = arg.unwrap_or(pg_sys::Datum::from(0));
}
}
let result = func(fcinfo);
let result = if (*fcinfo).isnull { None } else { Some(result) };
pg_sys::pfree(fcinfo.cast());
result
}
pub unsafe fn direct_pg_extern_function_call_as_datum(
func: unsafe extern "C-unwind" fn(pg_sys::FunctionCallInfo) -> pg_sys::Datum,
args: &[Option<pg_sys::Datum>],
) -> Option<pg_sys::Datum> {
direct_function_call_as_datum_internal(|fcinfo| pg_guard_ffi_boundary(|| func(fcinfo)), args)
}
#[inline]
pub unsafe fn srf_is_first_call(fcinfo: pg_sys::FunctionCallInfo) -> bool {
(*(*fcinfo).flinfo).fn_extra.is_null()
}
#[inline]
#[deprecated(since = "0.12.0", note = "you want pg_sys::init_MultiFuncCall")]
pub unsafe fn srf_first_call_init(
fcinfo: pg_sys::FunctionCallInfo,
) -> *mut pg_sys::FuncCallContext {
pg_sys::init_MultiFuncCall(fcinfo)
}
#[inline]
#[deprecated(since = "0.12.0", note = "you want pg_sys::per_MultiFuncCall")]
pub unsafe fn srf_per_call_setup(fcinfo: pg_sys::FunctionCallInfo) -> *mut pg_sys::FuncCallContext {
pg_sys::per_MultiFuncCall(fcinfo)
}
#[inline]
pub unsafe fn srf_return_next(
fcinfo: pg_sys::FunctionCallInfo,
funcctx: *mut pg_sys::FuncCallContext,
) {
(*funcctx).call_cntr += 1;
(*((*fcinfo).resultinfo as *mut pg_sys::ReturnSetInfo)).isDone =
pg_sys::ExprDoneCond::ExprMultipleResult;
}
#[inline]
pub unsafe fn srf_return_done(
fcinfo: pg_sys::FunctionCallInfo,
funcctx: *mut pg_sys::FuncCallContext,
) {
pg_sys::end_MultiFuncCall(fcinfo, funcctx);
(*((*fcinfo).resultinfo as *mut pg_sys::ReturnSetInfo)).isDone =
pg_sys::ExprDoneCond::ExprEndResult;
}