use std::ffi::{c_char, c_uchar};
use std::num::NonZeroU8;
use udf_sys::{UDF_ARGS, UDF_INIT};
use crate::wrapper::write_msg_to_buf;
use crate::{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 {
log_call!(enter: "init", U, args, message);
let cfg = UdfCfg::from_raw_ptr(initid);
let arglist = ArgList::from_raw_ptr(args);
let init_res = U::init(cfg, arglist);
arglist.flush_all_coercions();
let ret = match init_res {
Ok(v) => {
let boxed_struct: Box<W> = Box::new(W::into_storable(v));
cfg.store_box(boxed_struct);
false
}
Err(e) => {
write_msg_to_buf::<MYSQL_ERRMSG_SIZE>(e.as_bytes(), message);
true
}
};
log_call!(exit: "init", U, &*args, &*message, ret);
ret
}
#[inline]
pub unsafe fn wrap_deinit<W: UdfConverter<U>, U: BasicUdf>(initid: *const UDF_INIT) {
log_call!(enter: "deinit", U, &*initid);
let cfg: &UdfCfg<Process> = UdfCfg::from_raw_ptr(initid);
cfg.retrieve_box::<W>();
}
#[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,
) {
log_call!(enter: "add", U, &*initid, &*args, &*error);
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();
}
}
#[inline]
pub unsafe fn wrap_clear<W: UdfConverter<U>, U: AggregateUdf>(
initid: *mut UDF_INIT,
_is_null: *mut c_uchar,
error: *mut c_uchar,
) {
log_call!(enter: "clear", U, &*initid, &*error);
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();
}
}
#[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,
) {
log_call!(enter: "remove", U, &*initid, &*args, &*error);
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();
}
}