#![allow(clippy::useless_conversion, clippy::unnecessary_cast)]
use std::cell::UnsafeCell;
use std::ffi::c_ulong;
use std::fmt::Debug;
use std::marker::PhantomData;
#[cfg(feature = "logging-debug")]
use std::{any::type_name, mem::size_of};
use udf_sys::UDF_INIT;
#[cfg(feature = "logging-debug")]
use crate::udf_log;
use crate::{Init, UdfState};
#[repr(u32)]
#[non_exhaustive]
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum MaxLenOptions {
IntDefault = 21,
RealBase = 13,
Blob = 1 << 16,
MediumBlob = 1 << 24,
}
#[repr(transparent)]
pub struct UdfCfg<S: UdfState>(pub(crate) UnsafeCell<UDF_INIT>, PhantomData<S>);
impl<S: UdfState> UdfCfg<S> {
#[inline]
pub(crate) unsafe fn from_raw_ptr<'p>(ptr: *const UDF_INIT) -> &'p Self {
&*ptr.cast()
}
pub(crate) fn store_box<T>(&self, b: Box<T>) {
let box_ptr = Box::into_raw(b);
#[cfg(feature = "logging-debug")]
udf_log!(
Debug: "{box_ptr:p} {} bytes udf->server control transfer ({})",
size_of::<T>(),type_name::<T>()
);
unsafe { (*self.0.get()).ptr = box_ptr.cast() };
}
pub(crate) unsafe fn retrieve_box<T>(&self) -> Box<T> {
let box_ptr = (*self.0.get()).ptr.cast::<T>();
#[cfg(feature = "logging-debug")]
udf_log!(
Debug: "{box_ptr:p} {} bytes server->udf control transfer ({})",
size_of::<T>(),type_name::<T>()
);
Box::from_raw(box_ptr)
}
#[inline]
pub fn get_maybe_null(&self) -> bool {
unsafe { (*self.0.get()).maybe_null }
}
#[inline]
pub fn get_decimals(&self) -> u32 {
unsafe { (*self.0.get()).decimals as u32 }
}
#[inline]
pub fn set_decimals(&self, v: u32) {
unsafe { (*self.0.get()).decimals = v.into() }
}
#[inline]
pub fn get_max_len(&self) -> u64 {
unsafe { (*self.0.get()).max_length as u64 }
}
#[inline]
pub fn get_is_const(&self) -> bool {
unsafe { (*self.0.get()).const_item }
}
}
impl UdfCfg<Init> {
#[inline]
pub fn set_maybe_null(&self, v: bool) {
unsafe { (*self.0.get()).maybe_null = v };
}
#[inline]
pub fn set_max_len(&self, v: u64) {
let set: c_ulong = v.try_into().unwrap_or(c_ulong::MAX);
unsafe { (*self.0.get()).max_length = set };
}
#[inline]
pub fn set_is_const(&self, v: bool) {
unsafe { (*self.0.get()).const_item = v };
}
}
impl<T: UdfState> Debug for UdfCfg<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let base = unsafe { &*self.0.get() };
f.debug_struct("UdfCfg")
.field("maybe_null", &base.maybe_null)
.field("decimals", &base.decimals)
.field("max_len", &base.max_length)
.field("is_const", &base.const_item)
.field("ptr", &base.ptr)
.finish()
}
}
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use std::mem::{align_of, size_of};
use super::*;
use crate::mock::MockUdfCfg;
use crate::{Init, Process};
#[test]
fn cfg_init_size() {
assert_eq!(
size_of::<UDF_INIT>(),
size_of::<UdfCfg<Init>>(),
concat!("Size of: ", stringify!(UDF_INIT))
);
assert_eq!(
align_of::<UDF_INIT>(),
align_of::<UdfCfg<Init>>(),
concat!("Alignment of ", stringify!(UDF_INIT))
);
}
#[test]
fn cfg_proc_size() {
assert_eq!(
size_of::<UDF_INIT>(),
size_of::<UdfCfg<Process>>(),
concat!("Size of: ", stringify!(UDF_INIT))
);
assert_eq!(
align_of::<UDF_INIT>(),
align_of::<UdfCfg<Process>>(),
concat!("Alignment of ", stringify!(UDF_INIT))
);
}
#[test]
fn test_box_load_store() {
#[derive(PartialEq, Debug, Clone)]
struct X {
s: String,
map: HashMap<i64, f64>,
}
let mut map = HashMap::new();
map.insert(930_984_098, 4_525_435_435.900_981);
map.insert(12_341_234, -234.090_909_092);
map.insert(-23_412_343_453, 838_383.6);
let stored = X {
s: "This is a string".to_owned(),
map,
};
let mut m = MockUdfCfg::new();
let cfg = m.as_init();
cfg.store_box(Box::new(stored.clone()));
let loaded: X = unsafe { *cfg.retrieve_box() };
assert_eq!(stored, loaded);
}
#[test]
fn maybe_null() {
let mut m = MockUdfCfg::new();
*m.maybe_null() = false;
assert!(!m.as_init().get_maybe_null());
*m.maybe_null() = true;
assert!(m.as_init().get_maybe_null());
}
#[test]
fn decimals() {
let mut m = MockUdfCfg::new();
*m.decimals() = 1234;
assert_eq!(m.as_init().get_decimals(), 1234);
*m.decimals() = 0;
assert_eq!(m.as_init().get_decimals(), 0);
*m.decimals() = 1;
assert_eq!(m.as_init().get_decimals(), 1);
m.as_init().set_decimals(4);
assert_eq!(*m.decimals(), 4);
}
#[test]
fn max_len() {
let mut m = MockUdfCfg::new();
*m.max_len() = 1234;
assert_eq!(m.as_init().get_max_len(), 1234);
*m.max_len() = 0;
assert_eq!(m.as_init().get_max_len(), 0);
*m.max_len() = 1;
assert_eq!(m.as_init().get_max_len(), 1);
}
#[test]
fn test_const() {
let mut m = MockUdfCfg::new();
*m.is_const() = false;
assert!(!m.as_init().get_is_const());
*m.is_const() = true;
assert!(m.as_init().get_is_const());
}
}