use crate::errors::ExecutorError;
pub(super) fn to_number(
args: &[vibesql_types::SqlValue],
) -> Result<vibesql_types::SqlValue, ExecutorError> {
if args.len() != 1 {
return Err(ExecutorError::UnsupportedFeature(format!(
"TO_NUMBER requires exactly 1 argument, got {}",
args.len()
)));
}
match &args[0] {
vibesql_types::SqlValue::Null => Ok(vibesql_types::SqlValue::Null),
vibesql_types::SqlValue::Varchar(s) | vibesql_types::SqlValue::Character(s) => {
let cleaned = s.trim().replace(",", "");
if let Ok(n) = cleaned.parse::<i64>() {
Ok(vibesql_types::SqlValue::Integer(n))
} else if let Ok(n) = cleaned.parse::<f64>() {
Ok(vibesql_types::SqlValue::Double(n))
} else {
Err(ExecutorError::UnsupportedFeature(format!(
"TO_NUMBER: Invalid numeric string '{}'",
s
)))
}
}
val => Err(ExecutorError::UnsupportedFeature(format!(
"TO_NUMBER requires string argument, got {:?}",
val
))),
}
}
pub(super) fn to_date(
args: &[vibesql_types::SqlValue],
) -> Result<vibesql_types::SqlValue, ExecutorError> {
if args.len() != 2 {
return Err(ExecutorError::UnsupportedFeature(format!(
"TO_DATE requires exactly 2 arguments (string, format), got {}",
args.len()
)));
}
match (&args[0], &args[1]) {
(vibesql_types::SqlValue::Null, _) | (_, vibesql_types::SqlValue::Null) => {
Ok(vibesql_types::SqlValue::Null)
}
(
vibesql_types::SqlValue::Varchar(input) | vibesql_types::SqlValue::Character(input),
vibesql_types::SqlValue::Varchar(format) | vibesql_types::SqlValue::Character(format),
) => {
let naive_date = super::super::date_format::parse_date(input, format)?;
use chrono::Datelike;
let date = vibesql_types::Date::new(
naive_date.year(),
naive_date.month() as u8,
naive_date.day() as u8,
)
.map_err(|e| ExecutorError::UnsupportedFeature(format!("Invalid date: {}", e)))?;
Ok(vibesql_types::SqlValue::Date(date))
}
(input, format) => Err(ExecutorError::UnsupportedFeature(format!(
"TO_DATE requires string arguments, got {:?} and {:?}",
input, format
))),
}
}
pub(super) fn to_timestamp(
args: &[vibesql_types::SqlValue],
) -> Result<vibesql_types::SqlValue, ExecutorError> {
if args.len() != 2 {
return Err(ExecutorError::UnsupportedFeature(format!(
"TO_TIMESTAMP requires exactly 2 arguments (string, format), got {}",
args.len()
)));
}
match (&args[0], &args[1]) {
(vibesql_types::SqlValue::Null, _) | (_, vibesql_types::SqlValue::Null) => {
Ok(vibesql_types::SqlValue::Null)
}
(
vibesql_types::SqlValue::Varchar(input) | vibesql_types::SqlValue::Character(input),
vibesql_types::SqlValue::Varchar(format) | vibesql_types::SqlValue::Character(format),
) => {
let naive_timestamp = super::super::date_format::parse_timestamp(input, format)?;
use chrono::{Datelike, Timelike};
let date = vibesql_types::Date::new(
naive_timestamp.year(),
naive_timestamp.month() as u8,
naive_timestamp.day() as u8,
)
.map_err(|e| ExecutorError::UnsupportedFeature(format!("Invalid date: {}", e)))?;
let time = vibesql_types::Time::new(
naive_timestamp.hour() as u8,
naive_timestamp.minute() as u8,
naive_timestamp.second() as u8,
naive_timestamp.nanosecond(),
)
.map_err(|e| ExecutorError::UnsupportedFeature(format!("Invalid time: {}", e)))?;
Ok(vibesql_types::SqlValue::Timestamp(vibesql_types::Timestamp::new(date, time)))
}
(input, format) => Err(ExecutorError::UnsupportedFeature(format!(
"TO_TIMESTAMP requires string arguments, got {:?} and {:?}",
input, format
))),
}
}
pub(super) fn to_char(
args: &[vibesql_types::SqlValue],
) -> Result<vibesql_types::SqlValue, ExecutorError> {
if args.len() != 2 {
return Err(ExecutorError::UnsupportedFeature(format!(
"TO_CHAR requires exactly 2 arguments (value, format), got {}",
args.len()
)));
}
let format_str = match &args[1] {
vibesql_types::SqlValue::Varchar(s) | vibesql_types::SqlValue::Character(s) => s,
vibesql_types::SqlValue::Null => return Ok(vibesql_types::SqlValue::Null),
val => {
return Err(ExecutorError::UnsupportedFeature(format!(
"TO_CHAR format must be string, got {:?}",
val
)))
}
};
match &args[0] {
vibesql_types::SqlValue::Null => Ok(vibesql_types::SqlValue::Null),
vibesql_types::SqlValue::Date(date) => {
use chrono::NaiveDate;
let naive_date = NaiveDate::from_ymd_opt(date.year, date.month as u32, date.day as u32)
.ok_or_else(|| ExecutorError::UnsupportedFeature("Invalid date".to_string()))?;
let formatted = super::super::date_format::format_date(&naive_date, format_str)?;
Ok(vibesql_types::SqlValue::Varchar(arcstr::ArcStr::from(formatted)))
}
vibesql_types::SqlValue::Timestamp(ts) => {
use chrono::{NaiveDate, NaiveDateTime, NaiveTime};
let naive_date =
NaiveDate::from_ymd_opt(ts.date.year, ts.date.month as u32, ts.date.day as u32)
.ok_or_else(|| ExecutorError::UnsupportedFeature("Invalid date".to_string()))?;
let naive_time = NaiveTime::from_hms_nano_opt(
ts.time.hour as u32,
ts.time.minute as u32,
ts.time.second as u32,
ts.time.nanosecond,
)
.ok_or_else(|| ExecutorError::UnsupportedFeature("Invalid time".to_string()))?;
let naive_timestamp = NaiveDateTime::new(naive_date, naive_time);
let formatted =
super::super::date_format::format_timestamp(&naive_timestamp, format_str)?;
Ok(vibesql_types::SqlValue::Varchar(arcstr::ArcStr::from(formatted)))
}
vibesql_types::SqlValue::Time(time) => {
use chrono::NaiveTime;
let naive_time = NaiveTime::from_hms_nano_opt(
time.hour as u32,
time.minute as u32,
time.second as u32,
time.nanosecond,
)
.ok_or_else(|| ExecutorError::UnsupportedFeature("Invalid time".to_string()))?;
let formatted = super::super::date_format::format_time(&naive_time, format_str)?;
Ok(vibesql_types::SqlValue::Varchar(arcstr::ArcStr::from(formatted)))
}
vibesql_types::SqlValue::Integer(n) => {
let formatted = super::super::date_format::format_number(*n as f64, format_str)?;
Ok(vibesql_types::SqlValue::Varchar(arcstr::ArcStr::from(formatted)))
}
vibesql_types::SqlValue::Smallint(n) => {
let formatted = super::super::date_format::format_number(*n as f64, format_str)?;
Ok(vibesql_types::SqlValue::Varchar(arcstr::ArcStr::from(formatted)))
}
vibesql_types::SqlValue::Bigint(n) => {
let formatted = super::super::date_format::format_number(*n as f64, format_str)?;
Ok(vibesql_types::SqlValue::Varchar(arcstr::ArcStr::from(formatted)))
}
vibesql_types::SqlValue::Float(n) => {
let formatted = super::super::date_format::format_number(*n as f64, format_str)?;
Ok(vibesql_types::SqlValue::Varchar(arcstr::ArcStr::from(formatted)))
}
vibesql_types::SqlValue::Double(n) => {
let formatted = super::super::date_format::format_number(*n, format_str)?;
Ok(vibesql_types::SqlValue::Varchar(arcstr::ArcStr::from(formatted)))
}
vibesql_types::SqlValue::Real(n) => {
let formatted = super::super::date_format::format_number(*n as f64, format_str)?;
Ok(vibesql_types::SqlValue::Varchar(arcstr::ArcStr::from(formatted)))
}
val => {
Err(ExecutorError::UnsupportedFeature(format!("TO_CHAR cannot format type {:?}", val)))
}
}
}
pub(super) fn cast(
args: &[vibesql_types::SqlValue],
sql_mode: &vibesql_types::SqlMode,
) -> Result<vibesql_types::SqlValue, ExecutorError> {
if args.len() != 2 {
return Err(ExecutorError::UnsupportedFeature(format!(
"CAST requires exactly 2 arguments (value, target_type), got {}",
args.len()
)));
}
let target_type_str = match &args[1] {
vibesql_types::SqlValue::Varchar(s) | vibesql_types::SqlValue::Character(s) => {
s.to_uppercase()
}
val => {
return Err(ExecutorError::UnsupportedFeature(format!(
"CAST target type must be string, got {:?}",
val
)))
}
};
let target_type = match target_type_str.as_str() {
"INTEGER" | "INT" => vibesql_types::DataType::Integer,
"SMALLINT" => vibesql_types::DataType::Smallint,
"BIGINT" => vibesql_types::DataType::Bigint,
"FLOAT" => vibesql_types::DataType::Float { precision: 53 },
"DOUBLE PRECISION" | "DOUBLE" => vibesql_types::DataType::DoublePrecision,
"VARCHAR" => vibesql_types::DataType::Varchar { max_length: Some(255) },
"DATE" => vibesql_types::DataType::Date,
"TIME" => vibesql_types::DataType::Time { with_timezone: false },
"TIMESTAMP" => vibesql_types::DataType::Timestamp { with_timezone: false },
_ => {
return Err(ExecutorError::UnsupportedFeature(format!(
"CAST to type '{}' not supported",
target_type_str
)))
}
};
super::super::casting::cast_value(&args[0], &target_type, sql_mode)
}