use vibesql_types::SqlValue;
use crate::errors::ExecutorError;
pub fn numeric_to_f64(val: &SqlValue) -> Result<f64, ExecutorError> {
match val {
SqlValue::Integer(n) => Ok(*n as f64),
SqlValue::Bigint(n) => Ok(*n as f64),
SqlValue::Smallint(n) => Ok(*n as f64),
SqlValue::Unsigned(n) => Ok(*n as f64),
SqlValue::Numeric(f) => Ok(*f),
SqlValue::Float(f) => Ok(*f as f64),
SqlValue::Double(f) => Ok(*f),
SqlValue::Real(f) => Ok(*f as f64),
SqlValue::Varchar(s) | SqlValue::Character(s) => s.as_str().parse::<f64>().map_err(|_| {
ExecutorError::UnsupportedFeature(format!("Cannot convert '{}' to numeric", s))
}),
_ => Err(ExecutorError::UnsupportedFeature(format!("Cannot convert {:?} to numeric", val))),
}
}
pub fn power(args: &[SqlValue]) -> Result<SqlValue, ExecutorError> {
if args.len() != 2 {
return Err(ExecutorError::UnsupportedFeature(format!(
"POWER requires exactly 2 arguments, got {}",
args.len()
)));
}
match (&args[0], &args[1]) {
(SqlValue::Null, _) | (_, SqlValue::Null) => Ok(SqlValue::Null),
(SqlValue::Integer(base), SqlValue::Integer(exp)) => {
if *exp >= 0 && *exp <= i32::MAX as i64 {
Ok(SqlValue::Double((*base as f64).powi(*exp as i32)))
} else {
Ok(SqlValue::Double((*base as f64).powf(*exp as f64)))
}
}
(SqlValue::Float(base), SqlValue::Float(exp)) => Ok(SqlValue::Float(base.powf(*exp))),
(SqlValue::Double(base), SqlValue::Double(exp)) => Ok(SqlValue::Double(base.powf(*exp))),
(base, exp) => {
let base_f64 = numeric_to_f64(base)?;
let exp_f64 = numeric_to_f64(exp)?;
Ok(SqlValue::Double(base_f64.powf(exp_f64)))
}
}
}
pub fn sqrt(args: &[SqlValue]) -> Result<SqlValue, ExecutorError> {
if args.len() != 1 {
return Err(ExecutorError::UnsupportedFeature(format!(
"SQRT requires exactly 1 argument, got {}",
args.len()
)));
}
match &args[0] {
SqlValue::Null => Ok(SqlValue::Null),
SqlValue::Integer(n) => {
if *n < 0 {
Err(ExecutorError::UnsupportedFeature("SQRT of negative number".to_string()))
} else {
Ok(SqlValue::Double((*n as f64).sqrt()))
}
}
SqlValue::Float(f) => {
if *f < 0.0 {
Err(ExecutorError::UnsupportedFeature("SQRT of negative number".to_string()))
} else {
Ok(SqlValue::Float(f.sqrt()))
}
}
SqlValue::Double(f) => {
if *f < 0.0 {
Err(ExecutorError::UnsupportedFeature("SQRT of negative number".to_string()))
} else {
Ok(SqlValue::Double(f.sqrt()))
}
}
val => {
let f = numeric_to_f64(val)?;
if f < 0.0 {
Err(ExecutorError::UnsupportedFeature("SQRT of negative number".to_string()))
} else {
Ok(SqlValue::Double(f.sqrt()))
}
}
}
}
pub fn exp(args: &[SqlValue]) -> Result<SqlValue, ExecutorError> {
if args.len() != 1 {
return Err(ExecutorError::UnsupportedFeature(format!(
"EXP requires exactly 1 argument, got {}",
args.len()
)));
}
match &args[0] {
SqlValue::Null => Ok(SqlValue::Null),
val => {
let x = numeric_to_f64(val)?;
Ok(SqlValue::Double(x.exp()))
}
}
}
pub fn ln(args: &[SqlValue]) -> Result<SqlValue, ExecutorError> {
if args.len() != 1 {
return Err(ExecutorError::UnsupportedFeature(format!(
"LN requires exactly 1 argument, got {}",
args.len()
)));
}
match &args[0] {
SqlValue::Null => Ok(SqlValue::Null),
val => {
let x = numeric_to_f64(val)?;
if x <= 0.0 {
Err(ExecutorError::UnsupportedFeature("LN of non-positive number".to_string()))
} else {
Ok(SqlValue::Double(x.ln()))
}
}
}
}
pub fn log(args: &[SqlValue]) -> Result<SqlValue, ExecutorError> {
match args.len() {
1 => {
log10(args)
}
2 => {
match (&args[0], &args[1]) {
(SqlValue::Null, _) | (_, SqlValue::Null) => Ok(SqlValue::Null),
(base_val, x_val) => {
let base = numeric_to_f64(base_val)?;
let x = numeric_to_f64(x_val)?;
if base <= 0.0 || base == 1.0 {
Ok(SqlValue::Null)
} else if x <= 0.0 {
Ok(SqlValue::Null)
} else {
Ok(SqlValue::Double(x.ln() / base.ln()))
}
}
}
}
_ => Err(ExecutorError::UnsupportedFeature(format!(
"LOG requires 1 or 2 arguments, got {}",
args.len()
))),
}
}
pub fn log10(args: &[SqlValue]) -> Result<SqlValue, ExecutorError> {
if args.len() != 1 {
return Err(ExecutorError::UnsupportedFeature(format!(
"LOG10 requires exactly 1 argument, got {}",
args.len()
)));
}
match &args[0] {
SqlValue::Null => Ok(SqlValue::Null),
val => {
let x = numeric_to_f64(val)?;
if x <= 0.0 {
Err(ExecutorError::UnsupportedFeature("LOG10 of non-positive number".to_string()))
} else {
Ok(SqlValue::Double(x.log10()))
}
}
}
}