mod arithmetic;
mod bitwise;
mod comparison;
mod logical;
mod string;
mod vector;
use arithmetic::ArithmeticOps;
use bitwise::BitwiseOps;
use comparison::ComparisonOps;
use logical::LogicalOps;
use string::StringOps;
use vector::VectorOps;
pub use logical::{is_truthy, is_truthy_string};
use vibesql_types::SqlValue;
use crate::errors::ExecutorError;
fn json_extract(
left: &SqlValue,
right: &SqlValue,
as_text: bool,
) -> Result<SqlValue, ExecutorError> {
let json_str = match left {
SqlValue::Varchar(s) | SqlValue::Character(s) => s.as_str(),
SqlValue::Null => return Ok(SqlValue::Null),
_ => {
return Err(ExecutorError::TypeError(format!(
"JSON operator requires string, got {:?}",
left
)));
}
};
let key = match right {
SqlValue::Varchar(s) | SqlValue::Character(s) => s.as_str().to_string(),
SqlValue::Integer(i) => i.to_string(), SqlValue::Null => return Ok(SqlValue::Null),
_ => {
return Err(ExecutorError::TypeError(format!(
"JSON key must be string or integer, got {:?}",
right
)));
}
};
let json_value: serde_json::Value = match serde_json::from_str(json_str) {
Ok(v) => v,
Err(_) => return Ok(SqlValue::Null), };
let extracted = if let Ok(index) = key.parse::<usize>() {
json_value.get(index)
} else {
json_value.get(&key)
};
match extracted {
Some(value) => {
if as_text {
match value {
serde_json::Value::Null => Ok(SqlValue::Null),
serde_json::Value::String(s) => {
Ok(SqlValue::Varchar(arcstr::ArcStr::from(s.as_str())))
}
serde_json::Value::Number(n) => {
Ok(SqlValue::Varchar(arcstr::ArcStr::from(n.to_string())))
}
serde_json::Value::Bool(b) => {
Ok(SqlValue::Varchar(arcstr::ArcStr::from(if *b {
"true"
} else {
"false"
})))
}
_ => Ok(SqlValue::Varchar(arcstr::ArcStr::from(value.to_string()))),
}
} else {
Ok(SqlValue::Varchar(arcstr::ArcStr::from(value.to_string())))
}
}
None => Ok(SqlValue::Null), }
}
#[allow(dead_code)]
pub(crate) trait BinaryOperator {
fn evaluate(&self, left: &SqlValue, right: &SqlValue) -> Result<SqlValue, ExecutorError>;
}
pub(crate) struct OperatorRegistry;
impl OperatorRegistry {
#[inline]
pub fn eval_binary_op(
left: &SqlValue,
op: &vibesql_ast::BinaryOperator,
right: &SqlValue,
sql_mode: vibesql_types::SqlMode,
) -> Result<SqlValue, ExecutorError> {
use vibesql_ast::BinaryOperator::*;
if matches!(left, SqlValue::Null) || matches!(right, SqlValue::Null) {
if !matches!(op, And | Or) {
return Ok(SqlValue::Null);
}
}
match op {
Plus => ArithmeticOps::add(left, right),
Minus => ArithmeticOps::subtract(left, right),
Multiply => ArithmeticOps::multiply(left, right),
Divide => ArithmeticOps::divide(left, right, sql_mode),
IntegerDivide => ArithmeticOps::integer_divide(left, right),
Modulo => ArithmeticOps::modulo(left, right),
Equal => ComparisonOps::equal(left, right),
NotEqual => ComparisonOps::not_equal(left, right),
LessThan => ComparisonOps::less_than(left, right),
LessThanOrEqual => ComparisonOps::less_than_or_equal(left, right),
GreaterThan => ComparisonOps::greater_than(left, right),
GreaterThanOrEqual => ComparisonOps::greater_than_or_equal(left, right),
And => LogicalOps::and(left, right),
Or => LogicalOps::or(left, right),
Concat => StringOps::concat(left, right),
BitwiseAnd => BitwiseOps::and(left, right),
BitwiseOr => BitwiseOps::or(left, right),
LeftShift => BitwiseOps::left_shift(left, right),
RightShift => BitwiseOps::right_shift(left, right),
CosineDistance => VectorOps::cosine_distance(left, right),
NegativeInnerProduct => VectorOps::negative_inner_product(left, right),
L2Distance => VectorOps::l2_distance(left, right),
JsonExtract => json_extract(left, right, false),
JsonExtractText => json_extract(left, right, true),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_null_handling() {
use vibesql_ast::BinaryOperator::*;
assert!(matches!(
OperatorRegistry::eval_binary_op(
&SqlValue::Null,
&Plus,
&SqlValue::Integer(1),
vibesql_types::SqlMode::default()
)
.unwrap(),
SqlValue::Null
));
assert!(matches!(
OperatorRegistry::eval_binary_op(
&SqlValue::Integer(1),
&Plus,
&SqlValue::Null,
vibesql_types::SqlMode::default()
)
.unwrap(),
SqlValue::Null
));
assert!(matches!(
OperatorRegistry::eval_binary_op(
&SqlValue::Null,
&Equal,
&SqlValue::Null,
vibesql_types::SqlMode::default()
)
.unwrap(),
SqlValue::Null
));
}
}