use std::fmt::{Display, Write};
use std::marker::PhantomData;
use glaredb_error::{DbError, Result};
use super::null::NullToAnything;
use crate::arrays::array::Array;
use crate::arrays::array::physical_type::{
PhysicalBinary,
PhysicalF16,
PhysicalF32,
PhysicalF64,
PhysicalI8,
PhysicalI16,
PhysicalI32,
PhysicalI64,
PhysicalI128,
PhysicalInterval,
PhysicalU8,
PhysicalU16,
PhysicalU32,
PhysicalU64,
PhysicalU128,
PhysicalUtf8,
ScalarStorage,
};
use crate::arrays::datatype::{DataType, DataTypeId, TimeUnit};
use crate::arrays::executor::OutBuffer;
use crate::arrays::executor::scalar::UnaryExecutor;
use crate::arrays::scalar::decimal::{
Decimal64Type,
Decimal128Type,
DecimalPrimitive,
DecimalType,
};
use crate::functions::cast::behavior::CastErrorState;
use crate::functions::cast::format::{
DecimalFormatter,
Formatter,
TimestampMicrosecondsFormatter,
TimestampMillisecondsFormatter,
TimestampNanosecondsFormatter,
TimestampSecondsFormatter,
};
use crate::functions::cast::{
CastFlatten,
CastFunction,
CastFunctionSet,
RawCastFunction,
TO_STRING_CAST_RULE,
};
use crate::util::iter::IntoExactSizeIterator;
pub const FUNCTION_SET_TO_STRING: CastFunctionSet = CastFunctionSet {
name: "to_string",
target: DataTypeId::Utf8,
#[rustfmt::skip]
functions: &[
RawCastFunction::new(DataTypeId::Null, &NullToAnything, TO_STRING_CAST_RULE, CastFlatten::Safe),
RawCastFunction::new(DataTypeId::Int8, &PrimToString::<PhysicalI8>::new(), TO_STRING_CAST_RULE, CastFlatten::Unsafe),
RawCastFunction::new(DataTypeId::Int16, &PrimToString::<PhysicalI16>::new(), TO_STRING_CAST_RULE, CastFlatten::Unsafe),
RawCastFunction::new(DataTypeId::Int32, &PrimToString::<PhysicalI32>::new(), TO_STRING_CAST_RULE, CastFlatten::Unsafe),
RawCastFunction::new(DataTypeId::Int64, &PrimToString::<PhysicalI64>::new(), TO_STRING_CAST_RULE, CastFlatten::Unsafe),
RawCastFunction::new(DataTypeId::Int128, &PrimToString::<PhysicalI128>::new(), TO_STRING_CAST_RULE, CastFlatten::Unsafe),
RawCastFunction::new(DataTypeId::UInt8, &PrimToString::<PhysicalU8>::new(), TO_STRING_CAST_RULE, CastFlatten::Unsafe),
RawCastFunction::new(DataTypeId::UInt16, &PrimToString::<PhysicalU16>::new(), TO_STRING_CAST_RULE, CastFlatten::Unsafe),
RawCastFunction::new(DataTypeId::UInt32, &PrimToString::<PhysicalU32>::new(), TO_STRING_CAST_RULE, CastFlatten::Unsafe),
RawCastFunction::new(DataTypeId::UInt64, &PrimToString::<PhysicalU64>::new(), TO_STRING_CAST_RULE, CastFlatten::Unsafe),
RawCastFunction::new(DataTypeId::UInt128, &PrimToString::<PhysicalU128>::new(), TO_STRING_CAST_RULE, CastFlatten::Unsafe),
RawCastFunction::new(DataTypeId::Float16, &PrimToString::<PhysicalF16>::new(), TO_STRING_CAST_RULE, CastFlatten::Unsafe),
RawCastFunction::new(DataTypeId::Float32, &PrimToString::<PhysicalF32>::new(), TO_STRING_CAST_RULE, CastFlatten::Unsafe),
RawCastFunction::new(DataTypeId::Float64, &PrimToString::<PhysicalF64>::new(), TO_STRING_CAST_RULE, CastFlatten::Unsafe),
RawCastFunction::new(DataTypeId::Interval, &PrimToString::<PhysicalInterval>::new(), TO_STRING_CAST_RULE, CastFlatten::Unsafe),
RawCastFunction::new(DataTypeId::Decimal64, &DecimalToString::<Decimal64Type>::new(), TO_STRING_CAST_RULE, CastFlatten::Unsafe),
RawCastFunction::new(DataTypeId::Decimal128, &DecimalToString::<Decimal128Type>::new(), TO_STRING_CAST_RULE, CastFlatten::Unsafe),
RawCastFunction::new(DataTypeId::Timestamp, &TimestampToString, TO_STRING_CAST_RULE, CastFlatten::Unsafe),
RawCastFunction::new(DataTypeId::Binary, &BinaryToString, TO_STRING_CAST_RULE, CastFlatten::Unsafe),
],
};
#[derive(Debug, Clone, Copy)]
pub struct PrimToString<S> {
_s: PhantomData<S>,
}
impl<S> PrimToString<S> {
pub const fn new() -> Self {
PrimToString { _s: PhantomData }
}
}
impl<S> CastFunction for PrimToString<S>
where
S: ScalarStorage,
S::StorageType: Display,
{
type State = ();
fn bind(&self, _src: &DataType, _target: &DataType) -> Result<Self::State> {
Ok(())
}
fn cast(
_state: &Self::State,
mut error_state: CastErrorState,
src: &Array,
sel: impl IntoExactSizeIterator<Item = usize>,
out: &mut Array,
) -> Result<()> {
let mut s_buf = String::new();
UnaryExecutor::execute::<S, PhysicalUtf8, _>(
src,
sel,
OutBuffer::from_array(out)?,
|v, buf| {
s_buf.clear();
match write!(s_buf, "{}", v) {
Ok(_) => buf.put(s_buf.as_str()),
Err(_) => {
error_state.set_error(|| DbError::new("Failed to cast to utf8"));
buf.put_null();
}
}
},
)?;
error_state.into_result()
}
}
#[derive(Debug, Clone, Copy)]
pub struct DecimalToString<D> {
_d: PhantomData<D>,
}
#[derive(Debug)]
pub struct DecimalToStringState<P: DecimalPrimitive> {
formatter: DecimalFormatter<P>,
}
impl<D> DecimalToString<D>
where
D: DecimalType,
{
pub const fn new() -> Self {
DecimalToString { _d: PhantomData }
}
}
impl<D> CastFunction for DecimalToString<D>
where
D: DecimalType,
{
type State = DecimalToStringState<D::Primitive>;
fn bind(&self, src: &DataType, _target: &DataType) -> Result<Self::State> {
let meta = src.try_get_decimal_type_meta()?;
let formatter = DecimalFormatter::new(meta.precision, meta.scale);
Ok(DecimalToStringState { formatter })
}
fn cast(
state: &Self::State,
error_state: CastErrorState,
src: &Array,
sel: impl IntoExactSizeIterator<Item = usize>,
out: &mut Array,
) -> Result<()> {
cast_with_formatter::<_, D::Storage>(error_state, &state.formatter, src, sel, out)
}
}
#[derive(Debug, Clone, Copy)]
pub struct TimestampToString;
#[derive(Debug)]
pub struct TimestampToStringState {
unit: TimeUnit,
}
impl CastFunction for TimestampToString {
type State = TimestampToStringState;
fn bind(&self, src: &DataType, _target: &DataType) -> Result<Self::State> {
let m = src.try_get_timestamp_type_meta()?;
Ok(TimestampToStringState { unit: m.unit })
}
fn cast(
state: &Self::State,
error_state: CastErrorState,
src: &Array,
sel: impl IntoExactSizeIterator<Item = usize>,
out: &mut Array,
) -> Result<()> {
match state.unit {
TimeUnit::Second => cast_with_formatter::<_, PhysicalI64>(
error_state,
&TimestampSecondsFormatter::default(),
src,
sel,
out,
),
TimeUnit::Millisecond => cast_with_formatter::<_, PhysicalI64>(
error_state,
&TimestampMillisecondsFormatter::default(),
src,
sel,
out,
),
TimeUnit::Microsecond => cast_with_formatter::<_, PhysicalI64>(
error_state,
&TimestampMicrosecondsFormatter::default(),
src,
sel,
out,
),
TimeUnit::Nanosecond => cast_with_formatter::<_, PhysicalI64>(
error_state,
&TimestampNanosecondsFormatter::default(),
src,
sel,
out,
),
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct BinaryToString;
impl CastFunction for BinaryToString {
type State = ();
fn bind(&self, _src: &DataType, _target: &DataType) -> Result<Self::State> {
Ok(())
}
fn cast(
_state: &Self::State,
mut error_state: CastErrorState,
src: &Array,
sel: impl IntoExactSizeIterator<Item = usize>,
out: &mut Array,
) -> Result<()> {
UnaryExecutor::execute::<PhysicalBinary, PhysicalUtf8, _>(
src,
sel,
OutBuffer::from_array(out)?,
|v, buf| match std::str::from_utf8(v) {
Ok(s) => buf.put(s),
Err(e) => {
error_state.set_error(move || {
DbError::with_source("Failed to cast binary to utf8", Box::new(e))
});
}
},
)
}
}
fn cast_with_formatter<F, S>(
mut error_state: CastErrorState,
formatter: &F,
src: &Array,
sel: impl IntoExactSizeIterator<Item = usize>,
out: &mut Array,
) -> Result<()>
where
F: Formatter,
S: ScalarStorage<StorageType = F::Type>,
{
let mut s_buf = String::new();
UnaryExecutor::execute::<S, PhysicalUtf8, _>(
src,
sel,
OutBuffer::from_array(out)?,
|v, buf| {
s_buf.clear();
match formatter.write(v, &mut s_buf) {
Ok(_) => buf.put(s_buf.as_str()),
Err(_) => {
error_state.set_error(|| DbError::new("Failed to cast to utf8"));
buf.put_null();
}
}
},
)?;
error_state.into_result()
}