use crate::errors::ExecutorError;
pub(crate) fn is_exact_numeric(value: &vibesql_types::SqlValue) -> bool {
matches!(
value,
vibesql_types::SqlValue::Smallint(_)
| vibesql_types::SqlValue::Integer(_)
| vibesql_types::SqlValue::Bigint(_)
| vibesql_types::SqlValue::Unsigned(_)
)
}
pub(crate) fn is_approximate_numeric(value: &vibesql_types::SqlValue) -> bool {
matches!(
value,
vibesql_types::SqlValue::Float(_)
| vibesql_types::SqlValue::Real(_)
| vibesql_types::SqlValue::Double(_)
)
}
pub(crate) fn to_i64(value: &vibesql_types::SqlValue) -> Result<i64, ExecutorError> {
match value {
vibesql_types::SqlValue::Smallint(n) => Ok(*n as i64),
vibesql_types::SqlValue::Integer(n) => Ok(*n),
vibesql_types::SqlValue::Bigint(n) => Ok(*n),
vibesql_types::SqlValue::Unsigned(n) => Ok(*n as i64),
vibesql_types::SqlValue::Numeric(f) => Ok(*f as i64),
vibesql_types::SqlValue::Float(f) => Ok(*f as i64),
vibesql_types::SqlValue::Real(f) => Ok(*f as i64),
vibesql_types::SqlValue::Double(f) => Ok(*f as i64),
vibesql_types::SqlValue::Boolean(b) => Ok(if *b { 1 } else { 0 }),
_ => Err(ExecutorError::TypeMismatch {
left: value.clone(),
op: "numeric_conversion".to_string(),
right: vibesql_types::SqlValue::Null,
}),
}
}
pub(crate) fn to_f64(value: &vibesql_types::SqlValue) -> Result<f64, ExecutorError> {
match value {
vibesql_types::SqlValue::Float(n) => Ok(*n as f64),
vibesql_types::SqlValue::Real(n) => Ok(*n as f64),
vibesql_types::SqlValue::Double(n) => Ok(*n),
vibesql_types::SqlValue::Integer(n) => Ok(*n as f64),
vibesql_types::SqlValue::Smallint(n) => Ok(*n as f64),
vibesql_types::SqlValue::Bigint(n) => Ok(*n as f64),
vibesql_types::SqlValue::Unsigned(n) => Ok(*n as f64),
vibesql_types::SqlValue::Numeric(f) => Ok(*f),
vibesql_types::SqlValue::Boolean(b) => Ok(if *b { 1.0 } else { 0.0 }),
_ => Err(ExecutorError::TypeMismatch {
left: value.clone(),
op: "numeric_conversion".to_string(),
right: vibesql_types::SqlValue::Null,
}),
}
}
pub(crate) fn boolean_to_i64(value: &vibesql_types::SqlValue) -> Option<i64> {
match value {
vibesql_types::SqlValue::Boolean(true) => Some(1),
vibesql_types::SqlValue::Boolean(false) => Some(0),
_ => None,
}
}
pub(crate) fn string_to_number(s: &str) -> (i64, f64, bool) {
let trimmed = s.trim_start();
if trimmed.is_empty() {
return (0, 0.0, false);
}
let (sign, rest) = match trimmed.chars().next() {
Some('-') => (-1i64, &trimmed[1..]),
Some('+') => (1i64, &trimmed[1..]),
_ => (1i64, trimmed),
};
let mut has_decimal = false;
let mut has_exponent = false;
let mut end_idx = 0;
let chars: Vec<char> = rest.chars().collect();
let len = chars.len();
while end_idx < len {
let c = chars[end_idx];
if c.is_ascii_digit() {
end_idx += 1;
} else if c == '.' && !has_decimal && !has_exponent {
has_decimal = true;
end_idx += 1;
} else if (c == 'e' || c == 'E') && !has_exponent && end_idx > 0 {
has_exponent = true;
end_idx += 1;
if end_idx < len && (chars[end_idx] == '+' || chars[end_idx] == '-') {
end_idx += 1;
}
if end_idx < len && chars[end_idx].is_ascii_digit() {
while end_idx < len && chars[end_idx].is_ascii_digit() {
end_idx += 1;
}
} else {
has_exponent = false;
end_idx -=
if end_idx > 0 && (chars[end_idx - 1] == '+' || chars[end_idx - 1] == '-') {
2
} else {
1
};
break;
}
} else {
break;
}
}
if end_idx == 0 {
return (0, 0.0, false);
}
let numeric_str: String = chars[..end_idx].iter().collect();
if has_decimal || has_exponent {
match numeric_str.parse::<f64>() {
Ok(f) => {
let result = f * sign as f64;
(result as i64, result, true)
}
Err(_) => (0, 0.0, false),
}
} else {
match numeric_str.parse::<i64>() {
Ok(n) => {
let result = n * sign;
(result, result as f64, false)
}
Err(_) => {
match numeric_str.parse::<f64>() {
Ok(f) => {
let result = f * sign as f64;
(result as i64, result, true)
}
Err(_) => (0, 0.0, false),
}
}
}
}
}
pub(crate) fn string_to_integer(s: &str) -> i64 {
let trimmed = s.trim_start();
if trimmed.is_empty() {
return 0;
}
let (is_negative, rest) = match trimmed.chars().next() {
Some('-') => (true, &trimmed[1..]),
Some('+') => (false, &trimmed[1..]),
_ => (false, trimmed),
};
let mut end_idx = 0;
let chars: Vec<char> = rest.chars().collect();
let len = chars.len();
while end_idx < len && chars[end_idx].is_ascii_digit() {
end_idx += 1;
}
if end_idx == 0 {
return 0;
}
let int_str: String = chars[..end_idx].iter().collect();
match int_str.parse::<i64>() {
Ok(n) => {
if is_negative {
n.checked_neg().unwrap_or(i64::MIN)
} else {
n
}
}
Err(_) => {
if is_negative {
i64::MIN
} else {
i64::MAX
}
}
}
}
#[inline]
fn float_to_int(f: f64, sql_mode: &vibesql_types::SqlMode) -> i64 {
match sql_mode {
vibesql_types::SqlMode::MySQL { .. } => f.round() as i64,
vibesql_types::SqlMode::SQLite => f.trunc() as i64,
}
}
#[inline]
fn float_to_unsigned(f: f64, sql_mode: &vibesql_types::SqlMode) -> u64 {
match sql_mode {
vibesql_types::SqlMode::MySQL { .. } => f.round() as u64,
vibesql_types::SqlMode::SQLite => f.trunc() as u64,
}
}
pub(crate) fn cast_value(
value: &vibesql_types::SqlValue,
target_type: &vibesql_types::DataType,
sql_mode: &vibesql_types::SqlMode,
) -> Result<vibesql_types::SqlValue, ExecutorError> {
use vibesql_types::{DataType::*, SqlValue};
if matches!(value, SqlValue::Null) {
return Ok(SqlValue::Null);
}
match target_type {
Integer => match value {
SqlValue::Integer(n) => Ok(SqlValue::Integer(*n)),
SqlValue::Smallint(n) => Ok(SqlValue::Integer(*n as i64)),
SqlValue::Bigint(n) => Ok(SqlValue::Integer(*n)),
SqlValue::Unsigned(n) => Ok(SqlValue::Integer(*n as i64)),
SqlValue::Numeric(f) => Ok(SqlValue::Integer(float_to_int(*f, sql_mode))),
SqlValue::Float(f) => Ok(SqlValue::Integer(float_to_int(*f as f64, sql_mode))),
SqlValue::Real(f) => Ok(SqlValue::Integer(float_to_int(*f as f64, sql_mode))),
SqlValue::Double(f) => Ok(SqlValue::Integer(float_to_int(*f, sql_mode))),
SqlValue::Boolean(b) => Ok(SqlValue::Integer(if *b { 1 } else { 0 })),
SqlValue::Varchar(s) | SqlValue::Character(s) => {
Ok(SqlValue::Integer(string_to_integer(s)))
}
SqlValue::Blob(bytes) => {
let text = std::str::from_utf8(bytes).unwrap_or("");
Ok(SqlValue::Integer(string_to_integer(text)))
}
_ => Ok(SqlValue::Integer(0)), },
Smallint => match value {
SqlValue::Smallint(n) => Ok(SqlValue::Smallint(*n)),
SqlValue::Integer(n) => Ok(SqlValue::Smallint(*n as i16)),
SqlValue::Bigint(n) => Ok(SqlValue::Smallint(*n as i16)),
SqlValue::Unsigned(n) => Ok(SqlValue::Smallint(*n as i16)),
SqlValue::Numeric(f) => Ok(SqlValue::Smallint(float_to_int(*f, sql_mode) as i16)),
SqlValue::Float(f) => Ok(SqlValue::Smallint(float_to_int(*f as f64, sql_mode) as i16)),
SqlValue::Real(f) => Ok(SqlValue::Smallint(float_to_int(*f as f64, sql_mode) as i16)),
SqlValue::Double(f) => Ok(SqlValue::Smallint(float_to_int(*f, sql_mode) as i16)),
SqlValue::Boolean(b) => Ok(SqlValue::Smallint(if *b { 1 } else { 0 })),
SqlValue::Varchar(s) | SqlValue::Character(s) => {
Ok(SqlValue::Smallint(string_to_integer(s) as i16))
}
SqlValue::Blob(bytes) => {
let text = std::str::from_utf8(bytes).unwrap_or("");
Ok(SqlValue::Smallint(string_to_integer(text) as i16))
}
_ => Ok(SqlValue::Smallint(0)), },
Bigint => match value {
SqlValue::Bigint(n) => Ok(SqlValue::Bigint(*n)),
SqlValue::Integer(n) => Ok(SqlValue::Bigint(*n)),
SqlValue::Smallint(n) => Ok(SqlValue::Bigint(*n as i64)),
SqlValue::Unsigned(n) => Ok(SqlValue::Bigint(*n as i64)),
SqlValue::Numeric(f) => Ok(SqlValue::Bigint(float_to_int(*f, sql_mode))),
SqlValue::Float(f) => Ok(SqlValue::Bigint(float_to_int(*f as f64, sql_mode))),
SqlValue::Real(f) => Ok(SqlValue::Bigint(float_to_int(*f as f64, sql_mode))),
SqlValue::Double(f) => Ok(SqlValue::Bigint(float_to_int(*f, sql_mode))),
SqlValue::Boolean(b) => Ok(SqlValue::Bigint(if *b { 1 } else { 0 })),
SqlValue::Varchar(s) | SqlValue::Character(s) => {
Ok(SqlValue::Bigint(string_to_integer(s)))
}
SqlValue::Blob(bytes) => {
let text = std::str::from_utf8(bytes).unwrap_or("");
Ok(SqlValue::Bigint(string_to_integer(text)))
}
_ => Ok(SqlValue::Bigint(0)), },
Unsigned => match value {
SqlValue::Unsigned(n) => Ok(SqlValue::Unsigned(*n)),
SqlValue::Integer(n) => {
Ok(SqlValue::Unsigned(*n as u64))
}
SqlValue::Smallint(n) => {
Ok(SqlValue::Unsigned(*n as u64))
}
SqlValue::Bigint(n) => {
Ok(SqlValue::Unsigned(*n as u64))
}
SqlValue::Numeric(f) => {
Ok(SqlValue::Unsigned(float_to_unsigned(*f, sql_mode)))
}
SqlValue::Float(f) => {
Ok(SqlValue::Unsigned(float_to_unsigned(*f as f64, sql_mode)))
}
SqlValue::Real(f) => {
Ok(SqlValue::Unsigned(float_to_unsigned(*f as f64, sql_mode)))
}
SqlValue::Double(f) => {
Ok(SqlValue::Unsigned(float_to_unsigned(*f, sql_mode)))
}
SqlValue::Boolean(b) => {
Ok(SqlValue::Unsigned(if *b { 1 } else { 0 }))
}
SqlValue::Varchar(s) | SqlValue::Character(s) => {
Ok(SqlValue::Unsigned(string_to_integer(s) as u64))
}
SqlValue::Blob(bytes) => {
let text = std::str::from_utf8(bytes).unwrap_or("");
Ok(SqlValue::Unsigned(string_to_integer(text) as u64))
}
_ => Ok(SqlValue::Unsigned(0)), },
Numeric { .. } => {
match value {
SqlValue::Integer(n) => Ok(SqlValue::Integer(*n)),
SqlValue::Smallint(n) => Ok(SqlValue::Integer(*n as i64)),
SqlValue::Bigint(n) => Ok(SqlValue::Integer(*n)),
SqlValue::Unsigned(n) => Ok(SqlValue::Integer(*n as i64)),
SqlValue::Boolean(b) => Ok(SqlValue::Integer(if *b { 1 } else { 0 })),
SqlValue::Numeric(f) => Ok(SqlValue::Numeric(*f)),
SqlValue::Float(n) => Ok(SqlValue::Numeric(*n as f64)),
SqlValue::Real(n) => Ok(SqlValue::Numeric(*n)),
SqlValue::Double(n) => Ok(SqlValue::Numeric(*n)),
SqlValue::Varchar(s) | SqlValue::Character(s) => {
let (int_val, float_val, _) = string_to_number(s);
if float_val.fract() == 0.0
&& float_val >= i64::MIN as f64
&& float_val <= i64::MAX as f64
{
Ok(SqlValue::Integer(int_val))
} else {
Ok(SqlValue::Numeric(float_val))
}
}
SqlValue::Blob(bytes) => {
let text = std::str::from_utf8(bytes).unwrap_or("");
let (int_val, float_val, _) = string_to_number(text);
if float_val.fract() == 0.0
&& float_val >= i64::MIN as f64
&& float_val <= i64::MAX as f64
{
Ok(SqlValue::Integer(int_val))
} else {
Ok(SqlValue::Numeric(float_val))
}
}
_ => Ok(SqlValue::Integer(0)), }
}
Float { .. } => match value {
SqlValue::Float(n) => Ok(SqlValue::Float(*n)),
SqlValue::Real(n) => Ok(SqlValue::Float(*n as f32)), SqlValue::Double(n) => Ok(SqlValue::Float(*n as f32)),
SqlValue::Integer(n) => Ok(SqlValue::Float(*n as f32)),
SqlValue::Smallint(n) => Ok(SqlValue::Float(*n as f32)),
SqlValue::Bigint(n) => Ok(SqlValue::Float(*n as f32)),
SqlValue::Unsigned(n) => Ok(SqlValue::Float(*n as f32)),
SqlValue::Numeric(f) => Ok(SqlValue::Float(*f as f32)),
SqlValue::Boolean(b) => Ok(SqlValue::Float(if *b { 1.0 } else { 0.0 })),
SqlValue::Varchar(s) | SqlValue::Character(s) => {
let (_, float_val, _) = string_to_number(s);
Ok(SqlValue::Float(float_val as f32))
}
SqlValue::Blob(bytes) => {
let text = std::str::from_utf8(bytes).unwrap_or("");
let (_, float_val, _) = string_to_number(text);
Ok(SqlValue::Float(float_val as f32))
}
_ => Ok(SqlValue::Float(0.0)), },
Real => match value {
SqlValue::Real(n) => Ok(SqlValue::Real(*n)),
SqlValue::Float(n) => Ok(SqlValue::Real(*n as f64)),
SqlValue::Double(n) => Ok(SqlValue::Real(*n)),
SqlValue::Integer(n) => Ok(SqlValue::Real(*n as f64)),
SqlValue::Smallint(n) => Ok(SqlValue::Real(*n as f64)),
SqlValue::Bigint(n) => Ok(SqlValue::Real(*n as f64)),
SqlValue::Unsigned(n) => Ok(SqlValue::Real(*n as f64)),
SqlValue::Numeric(f) => Ok(SqlValue::Real(*f)),
SqlValue::Boolean(b) => Ok(SqlValue::Real(if *b { 1.0 } else { 0.0 })),
SqlValue::Varchar(s) | SqlValue::Character(s) => {
let (_, float_val, _) = string_to_number(s);
Ok(SqlValue::Real(float_val))
}
SqlValue::Blob(bytes) => {
let text = std::str::from_utf8(bytes).unwrap_or("");
let (_, float_val, _) = string_to_number(text);
Ok(SqlValue::Real(float_val))
}
_ => Ok(SqlValue::Real(0.0)), },
DoublePrecision => match value {
SqlValue::Double(n) => Ok(SqlValue::Double(*n)),
SqlValue::Float(n) => Ok(SqlValue::Double(*n as f64)),
SqlValue::Real(n) => Ok(SqlValue::Double(*n as f64)),
SqlValue::Integer(n) => Ok(SqlValue::Double(*n as f64)),
SqlValue::Smallint(n) => Ok(SqlValue::Double(*n as f64)),
SqlValue::Bigint(n) => Ok(SqlValue::Double(*n as f64)),
SqlValue::Unsigned(n) => Ok(SqlValue::Double(*n as f64)),
SqlValue::Numeric(f) => Ok(SqlValue::Double(*f)),
SqlValue::Boolean(b) => Ok(SqlValue::Double(if *b { 1.0 } else { 0.0 })),
SqlValue::Varchar(s) | SqlValue::Character(s) => {
let (_, float_val, _) = string_to_number(s);
Ok(SqlValue::Double(float_val))
}
SqlValue::Blob(bytes) => {
let text = std::str::from_utf8(bytes).unwrap_or("");
let (_, float_val, _) = string_to_number(text);
Ok(SqlValue::Double(float_val))
}
_ => Ok(SqlValue::Double(0.0)), },
Varchar { max_length } => {
let string_val: arcstr::ArcStr = match value {
SqlValue::Varchar(s) => s.clone(),
SqlValue::Integer(n) => arcstr::ArcStr::from(n.to_string().as_str()),
SqlValue::Smallint(n) => arcstr::ArcStr::from(n.to_string().as_str()),
SqlValue::Bigint(n) => arcstr::ArcStr::from(n.to_string().as_str()),
SqlValue::Unsigned(n) => arcstr::ArcStr::from(n.to_string().as_str()),
SqlValue::Float(n) => arcstr::ArcStr::from(n.to_string().as_str()),
SqlValue::Real(n) => arcstr::ArcStr::from(n.to_string().as_str()),
SqlValue::Double(n) => arcstr::ArcStr::from(n.to_string().as_str()),
SqlValue::Numeric(n) => arcstr::ArcStr::from(n.to_string().as_str()),
SqlValue::Boolean(b) => arcstr::ArcStr::from(if *b { "TRUE" } else { "FALSE" }),
SqlValue::Date(s) => arcstr::ArcStr::from(s.to_string().as_str()),
SqlValue::Time(s) => arcstr::ArcStr::from(s.to_string().as_str()),
SqlValue::Timestamp(s) => arcstr::ArcStr::from(s.to_string().as_str()),
SqlValue::Character(s) => s.clone(),
SqlValue::Blob(bytes) => {
match std::str::from_utf8(bytes) {
Ok(s) => arcstr::ArcStr::from(s),
Err(_) => arcstr::ArcStr::from(String::from_utf8_lossy(bytes).as_ref()),
}
}
_ => arcstr::ArcStr::from(""), };
match max_length {
Some(len) if string_val.len() > *len => {
Ok(SqlValue::Varchar(arcstr::ArcStr::from(&string_val[..*len])))
}
_ => Ok(SqlValue::Varchar(string_val)),
}
}
Date => match value {
SqlValue::Date(s) => Ok(SqlValue::Date(*s)),
SqlValue::Timestamp(s) => {
Ok(SqlValue::Date(s.date))
}
SqlValue::Varchar(s) => {
match s.parse::<vibesql_types::Date>() {
Ok(date) => Ok(SqlValue::Date(date)),
Err(_) => Err(ExecutorError::CastError {
from_type: format!("VARCHAR '{}'", s),
to_type: "DATE".to_string(),
}),
}
}
_ => Err(ExecutorError::CastError {
from_type: format!("{:?}", value),
to_type: "DATE".to_string(),
}),
},
Time { .. } => match value {
SqlValue::Time(s) => Ok(SqlValue::Time(*s)),
SqlValue::Timestamp(s) => {
Ok(SqlValue::Time(s.time))
}
SqlValue::Varchar(s) => {
match s.parse::<vibesql_types::Time>() {
Ok(time) => Ok(SqlValue::Time(time)),
Err(_) => Err(ExecutorError::CastError {
from_type: format!("VARCHAR '{}'", s),
to_type: "TIME".to_string(),
}),
}
}
_ => Err(ExecutorError::CastError {
from_type: format!("{:?}", value),
to_type: "TIME".to_string(),
}),
},
Timestamp { .. } => match value {
SqlValue::Timestamp(s) => Ok(SqlValue::Timestamp(*s)),
SqlValue::Date(date) => {
let midnight = vibesql_types::Time::new(0, 0, 0, 0).unwrap();
Ok(SqlValue::Timestamp(vibesql_types::Timestamp::new(*date, midnight)))
}
SqlValue::Time(time) => {
use chrono::Datelike;
let now = chrono::Local::now();
let current_date =
vibesql_types::Date::new(now.year(), now.month() as u8, now.day() as u8)
.map_err(|e| ExecutorError::CastError {
from_type: format!("TIME '{}'", time),
to_type: format!("TIMESTAMP (date construction failed: {})", e),
})?;
Ok(SqlValue::Timestamp(vibesql_types::Timestamp::new(current_date, *time)))
}
SqlValue::Varchar(s) => {
match s.parse::<vibesql_types::Timestamp>() {
Ok(timestamp) => Ok(SqlValue::Timestamp(timestamp)),
Err(_) => Err(ExecutorError::CastError {
from_type: format!("VARCHAR '{}'", s),
to_type: "TIMESTAMP".to_string(),
}),
}
}
_ => Err(ExecutorError::CastError {
from_type: format!("{:?}", value),
to_type: "TIMESTAMP".to_string(),
}),
},
Character { length } => {
let string_val: arcstr::ArcStr = match value {
SqlValue::Character(s) => s.clone(),
SqlValue::Varchar(s) => s.clone(),
SqlValue::Integer(n) => arcstr::ArcStr::from(n.to_string().as_str()),
SqlValue::Smallint(n) => arcstr::ArcStr::from(n.to_string().as_str()),
SqlValue::Bigint(n) => arcstr::ArcStr::from(n.to_string().as_str()),
SqlValue::Unsigned(n) => arcstr::ArcStr::from(n.to_string().as_str()),
SqlValue::Float(n) => arcstr::ArcStr::from(n.to_string().as_str()),
SqlValue::Real(n) => arcstr::ArcStr::from(n.to_string().as_str()),
SqlValue::Double(n) => arcstr::ArcStr::from(n.to_string().as_str()),
SqlValue::Numeric(n) => arcstr::ArcStr::from(n.to_string().as_str()),
SqlValue::Boolean(b) => arcstr::ArcStr::from(if *b { "TRUE" } else { "FALSE" }),
SqlValue::Blob(bytes) => {
match std::str::from_utf8(bytes) {
Ok(s) => arcstr::ArcStr::from(s),
Err(_) => arcstr::ArcStr::from(String::from_utf8_lossy(bytes).as_ref()),
}
}
_ => arcstr::ArcStr::from(""),
};
if string_val.len() > *length {
Ok(SqlValue::Character(arcstr::ArcStr::from(&string_val[..*length])))
} else if string_val.len() < *length {
let padded = format!("{:<width$}", string_val, width = *length);
Ok(SqlValue::Character(arcstr::ArcStr::from(padded.as_str())))
} else {
Ok(SqlValue::Character(string_val))
}
}
BinaryLargeObject => match value {
SqlValue::Blob(b) => Ok(SqlValue::Blob(b.clone())),
SqlValue::Varchar(s) | SqlValue::Character(s) => {
Ok(SqlValue::Blob(s.as_bytes().to_vec()))
}
SqlValue::Integer(n) => {
Ok(SqlValue::Blob(n.to_string().into_bytes()))
}
SqlValue::Smallint(n) => Ok(SqlValue::Blob(n.to_string().into_bytes())),
SqlValue::Bigint(n) => Ok(SqlValue::Blob(n.to_string().into_bytes())),
SqlValue::Unsigned(n) => Ok(SqlValue::Blob(n.to_string().into_bytes())),
SqlValue::Float(n) => Ok(SqlValue::Blob(n.to_string().into_bytes())),
SqlValue::Real(n) => Ok(SqlValue::Blob(n.to_string().into_bytes())),
SqlValue::Double(n) => Ok(SqlValue::Blob(n.to_string().into_bytes())),
SqlValue::Numeric(n) => Ok(SqlValue::Blob(n.to_string().into_bytes())),
SqlValue::Boolean(b) => {
Ok(SqlValue::Blob(if *b { b"1".to_vec() } else { b"0".to_vec() }))
}
_ => Ok(SqlValue::Blob(vec![])), },
UserDefined { .. } => match target_type.sqlite_affinity() {
vibesql_types::TypeAffinity::Integer => cast_value(value, &Integer, sql_mode),
vibesql_types::TypeAffinity::Text => {
cast_value(value, &Varchar { max_length: None }, sql_mode)
}
vibesql_types::TypeAffinity::None => cast_value(value, &BinaryLargeObject, sql_mode),
vibesql_types::TypeAffinity::Real => cast_value(value, &DoublePrecision, sql_mode),
vibesql_types::TypeAffinity::Numeric => {
let numeric_from_text = |s: &str| -> vibesql_types::SqlValue {
let (i, f, is_float) = string_to_number(s);
if is_float {
if f == f.trunc() && f >= i64::MIN as f64 && f <= i64::MAX as f64 {
SqlValue::Integer(f as i64)
} else {
SqlValue::Double(f)
}
} else {
SqlValue::Integer(i)
}
};
match value {
SqlValue::Varchar(s) | SqlValue::Character(s) => Ok(numeric_from_text(s)),
SqlValue::Blob(bytes) => {
let text = std::str::from_utf8(bytes).unwrap_or("");
Ok(numeric_from_text(text))
}
SqlValue::Boolean(b) => Ok(SqlValue::Integer(if *b { 1 } else { 0 })),
other => Ok(other.clone()),
}
}
},
_ => Err(ExecutorError::UnsupportedFeature(format!(
"CAST to {:?} not yet implemented",
target_type
))),
}
}