use std::{slice, str};
use udf_sys::Item_result;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
#[repr(i8)]
pub enum SqlType {
Int = Item_result::INT_RESULT as i8,
Real = Item_result::REAL_RESULT as i8,
String = Item_result::STRING_RESULT as i8,
Decimal = Item_result::DECIMAL_RESULT as i8,
}
impl SqlType {
#[inline]
pub fn to_item_result(&self) -> Item_result {
match *self {
Self::Int => Item_result::INT_RESULT,
Self::Real => Item_result::REAL_RESULT,
Self::String => Item_result::STRING_RESULT,
Self::Decimal => Item_result::DECIMAL_RESULT,
}
}
#[inline]
pub fn display_name(&self) -> &'static str {
match *self {
Self::String => "string",
Self::Real => "real",
Self::Int => "int",
Self::Decimal => "decimal",
}
}
}
impl TryFrom<i8> for SqlType {
type Error = String;
#[inline]
fn try_from(tag: i8) -> Result<Self, Self::Error> {
let val = match tag {
x if x == Self::String as i8 => Self::String,
x if x == Self::Real as i8 => Self::Real,
x if x == Self::Int as i8 => Self::Int,
x if x == Self::Decimal as i8 => Self::Decimal,
_ => return Err(format!("invalid arg type {tag} received")),
};
Ok(val)
}
}
impl TryFrom<Item_result> for SqlType {
type Error = String;
#[inline]
fn try_from(tag: Item_result) -> Result<Self, Self::Error> {
let val = match tag {
Item_result::STRING_RESULT => Self::String,
Item_result::REAL_RESULT => Self::Real,
Item_result::INT_RESULT => Self::Int,
Item_result::DECIMAL_RESULT => Self::Decimal,
_ => return Err(format!("invalid arg type {tag:?} received")),
};
Ok(val)
}
}
impl TryFrom<&SqlResult<'_>> for SqlType {
type Error = String;
#[inline]
fn try_from(tag: &SqlResult) -> Result<Self, Self::Error> {
let val = match *tag {
SqlResult::String(_) => Self::String,
SqlResult::Real(_) => Self::Real,
SqlResult::Int(_) => Self::Int,
SqlResult::Decimal(_) => Self::Decimal,
};
Ok(val)
}
}
#[derive(Debug, PartialEq, Clone)]
#[non_exhaustive]
pub enum SqlResult<'a> {
String(Option<&'a [u8]>),
Real(Option<f64>),
Int(Option<i64>),
Decimal(Option<&'a str>),
}
impl<'a> SqlResult<'a> {
pub(crate) unsafe fn from_ptr(
ptr: *const u8,
tag: Item_result,
len: usize,
) -> Result<Self, String> {
let marker =
SqlType::try_from(tag).map_err(|_| format!("invalid arg type {tag:?} received"))?;
let arg = if ptr.is_null() {
match marker {
SqlType::Int => SqlResult::Int(None),
SqlType::Real => SqlResult::Real(None),
SqlType::String => SqlResult::String(None),
SqlType::Decimal => SqlResult::Decimal(None),
}
} else {
unsafe {
#[allow(clippy::cast_ptr_alignment)]
match marker {
SqlType::Int => SqlResult::Int(Some(*(ptr.cast::<i64>()))),
SqlType::Real => SqlResult::Real(Some(*(ptr.cast::<f64>()))),
SqlType::String => SqlResult::String(Some(slice::from_raw_parts(ptr, len))),
SqlType::Decimal => SqlResult::Decimal(Some(str::from_utf8_unchecked(
slice::from_raw_parts(ptr, len),
))),
}
}
};
Ok(arg)
}
#[inline]
pub fn display_name(&self) -> &'static str {
SqlType::try_from(self).map_or("unknown", |v| v.display_name())
}
#[inline]
pub fn is_int(&self) -> bool {
matches!(*self, Self::Int(_))
}
#[inline]
pub fn is_real(&self) -> bool {
matches!(*self, Self::Real(_))
}
#[inline]
pub fn is_string(&self) -> bool {
matches!(*self, Self::String(_))
}
#[inline]
pub fn is_decimal(&self) -> bool {
matches!(*self, Self::Decimal(_))
}
#[inline]
pub fn as_int(&self) -> Option<i64> {
match *self {
Self::Int(v) => v,
_ => None,
}
}
#[inline]
pub fn as_real(&'a self) -> Option<f64> {
match *self {
Self::Real(v) => v,
_ => None,
}
}
#[inline]
pub fn as_string(&'a self) -> Option<&'a str> {
match *self {
Self::String(Some(v)) => Some(str::from_utf8(v).ok()?),
Self::Decimal(Some(v)) => Some(v),
_ => None,
}
}
#[inline]
pub fn as_bytes(&'a self) -> Option<&'a [u8]> {
match *self {
Self::String(Some(v)) => Some(v),
Self::Decimal(Some(v)) => Some(v.as_bytes()),
_ => None,
}
}
}