use std::any::type_name;
use std::ffi::{c_char, c_uchar};
use std::num::NonZeroU8;
use std::panic::{self, AssertUnwindSafe};
use udf_sys::{UDF_ARGS, UDF_INIT};
#[cfg(feature = "logging-debug")]
use crate::wrapper::debug;
use crate::wrapper::write_msg_to_buf;
use crate::{udf_log, AggregateUdf, ArgList, BasicUdf, Process, UdfCfg, MYSQL_ERRMSG_SIZE};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BufConverter<U, B>
where
U: BasicUdf,
B: Default,
{
udf: U,
buf: B,
}
impl<U, B> BufConverter<U, B>
where
U: BasicUdf,
B: Default,
{
#[allow(dead_code)]
fn set_retval(&mut self, val: B) {
self.buf = val;
}
}
pub trait UdfConverter<U> {
fn as_mut_ref(&mut self) -> &mut U;
fn into_storable(source: U) -> Self;
}
impl<U, B> UdfConverter<U> for BufConverter<U, B>
where
U: BasicUdf,
B: Default,
{
fn as_mut_ref(&mut self) -> &mut U {
&mut self.udf
}
fn into_storable(source: U) -> Self {
Self {
udf: source,
buf: B::default(),
}
}
}
impl<U: BasicUdf> UdfConverter<U> for U {
fn as_mut_ref(&mut self) -> &mut U {
self
}
fn into_storable(source: U) -> Self {
source
}
}
#[inline]
pub unsafe fn wrap_init<W: UdfConverter<U>, U: BasicUdf>(
initid: *mut UDF_INIT,
args: *mut UDF_ARGS,
message: *mut c_char,
) -> bool {
#[cfg(feature = "logging-debug")]
debug::pre_init_call::<U>(initid, args, message);
let mut ret = false;
let mut ret_wrap = AssertUnwindSafe(&mut ret);
panic::catch_unwind(move || {
let cfg = UdfCfg::from_raw_ptr(initid);
let arglist = ArgList::from_raw_ptr(args);
let boxed_struct: Box<W> = match U::init(cfg, arglist) {
Ok(v) => Box::new(W::into_storable(v)),
Err(e) => {
write_msg_to_buf::<MYSQL_ERRMSG_SIZE>(e.as_bytes(), message);
**ret_wrap = true;
return;
}
};
arglist.flush_all_coercions();
cfg.store_box(boxed_struct);
})
.unwrap_or_else(|_| {
write_msg_to_buf::<MYSQL_ERRMSG_SIZE>(b"(critical) init function panicked", message);
udf_log!(Critical: "init function panicked for `{}`", type_name::<U>());
ret = true;
});
#[cfg(feature = "logging-debug")]
debug::post_init_call::<U>(initid, args, message, ret);
ret
}
#[inline]
pub unsafe fn wrap_deinit<W: UdfConverter<U>, U: BasicUdf>(initid: *const UDF_INIT) {
#[cfg(feature = "logging-debug")]
debug::pre_deinit_call::<U>(initid);
panic::catch_unwind(|| {
let cfg: &UdfCfg<Process> = UdfCfg::from_raw_ptr(initid);
cfg.retrieve_box::<W>();
})
.unwrap_or_else(|_| udf_log!(Critical: "deinit function panicked for `{}`", type_name::<U>()));
}
#[inline]
pub unsafe fn wrap_add<W: UdfConverter<U>, U: AggregateUdf>(
initid: *mut UDF_INIT,
args: *mut UDF_ARGS,
_is_null: *mut c_uchar,
error: *mut c_uchar,
) {
#[cfg(feature = "logging-debug")]
debug::pre_add_call::<U>(initid, args, error);
panic::catch_unwind(|| {
let cfg = UdfCfg::from_raw_ptr(initid);
let arglist = ArgList::from_raw_ptr(args);
let err = *(error as *const Option<NonZeroU8>);
let mut b = cfg.retrieve_box::<W>();
let res = U::add(b.as_mut_ref(), cfg, arglist, err);
cfg.store_box(b);
if let Err(e) = res {
*error = e.into();
}
})
.unwrap_or_else(|_| udf_log!(Critical: "add function panicked for `{}`", type_name::<U>()));
}
#[inline]
pub unsafe fn wrap_clear<W: UdfConverter<U>, U: AggregateUdf>(
initid: *mut UDF_INIT,
_is_null: *mut c_uchar,
error: *mut c_uchar,
) {
#[cfg(feature = "logging-debug")]
debug::pre_clear_call::<U>(initid, error);
panic::catch_unwind(|| {
let cfg = UdfCfg::from_raw_ptr(initid);
let err = *(error as *const Option<NonZeroU8>);
let mut b = cfg.retrieve_box::<W>();
let res = U::clear(b.as_mut_ref(), cfg, err);
cfg.store_box(b);
if let Err(e) = res {
*error = e.into();
}
})
.unwrap_or_else(|_| udf_log!(Critical: "clear function panicked for `{}`", type_name::<U>()));
}
#[inline]
pub unsafe fn wrap_remove<W: UdfConverter<U>, U: AggregateUdf>(
initid: *mut UDF_INIT,
args: *mut UDF_ARGS,
_is_null: *mut c_uchar,
error: *mut c_uchar,
) {
#[cfg(feature = "logging-debug")]
debug::pre_remove_call::<U>(initid, args, error);
panic::catch_unwind(|| {
let cfg = UdfCfg::from_raw_ptr(initid);
let arglist = ArgList::from_raw_ptr(args);
let err = *(error as *const Option<NonZeroU8>);
let mut b = cfg.retrieve_box::<W>();
let res = U::remove(b.as_mut_ref(), cfg, arglist, err);
cfg.store_box(b);
if let Err(e) = res {
*error = e.into();
}
})
.unwrap_or_else(|_| udf_log!(Critical: "remove function panicked for `{}`", type_name::<U>()));
}