use vibesql_types::SqlValue;
use crate::{
errors::ExecutorError,
evaluator::casting::{string_to_number, to_i64},
};
pub(crate) struct BitwiseOps;
fn to_i64_bitwise(value: &SqlValue) -> Result<i64, ExecutorError> {
match value {
SqlValue::Varchar(s) | SqlValue::Character(s) => Ok(string_to_number(s).0),
SqlValue::Blob(bytes) => {
let text = std::str::from_utf8(bytes).unwrap_or("");
Ok(string_to_number(text).0)
}
_ => to_i64(value),
}
}
impl BitwiseOps {
#[inline]
pub fn or(left: &SqlValue, right: &SqlValue) -> Result<SqlValue, ExecutorError> {
let left_i64 = to_i64_bitwise(left)?;
let right_i64 = to_i64_bitwise(right)?;
Ok(SqlValue::Integer(left_i64 | right_i64))
}
#[inline]
pub fn and(left: &SqlValue, right: &SqlValue) -> Result<SqlValue, ExecutorError> {
let left_i64 = to_i64_bitwise(left)?;
let right_i64 = to_i64_bitwise(right)?;
Ok(SqlValue::Integer(left_i64 & right_i64))
}
#[inline]
#[allow(dead_code)] pub fn not(value: &SqlValue) -> Result<SqlValue, ExecutorError> {
let val_i64 = to_i64_bitwise(value)?;
Ok(SqlValue::Integer(!val_i64))
}
#[inline]
pub fn left_shift(left: &SqlValue, right: &SqlValue) -> Result<SqlValue, ExecutorError> {
let left_i64 = to_i64_bitwise(left)?;
let right_i64 = to_i64_bitwise(right)?;
if right_i64 == i64::MIN {
if left_i64 < 0 {
return Ok(SqlValue::Integer(-1));
} else {
return Ok(SqlValue::Integer(0));
}
}
let result = if right_i64 < 0 {
let shift = (-right_i64) as u32;
if shift >= 64 {
if left_i64 < 0 {
-1 } else {
0
}
} else {
left_i64 >> shift
}
} else {
let shift = right_i64 as u32;
if shift >= 64 {
0
} else {
left_i64 << shift
}
};
Ok(SqlValue::Integer(result))
}
#[inline]
pub fn right_shift(left: &SqlValue, right: &SqlValue) -> Result<SqlValue, ExecutorError> {
let left_i64 = to_i64_bitwise(left)?;
let right_i64 = to_i64_bitwise(right)?;
if right_i64 == i64::MIN {
return Ok(SqlValue::Integer(0));
}
let result = if right_i64 < 0 {
let shift = (-right_i64) as u32;
if shift >= 64 {
0
} else {
left_i64 << shift
}
} else {
let shift = right_i64 as u32;
if shift >= 64 {
if left_i64 < 0 {
-1 } else {
0
}
} else {
left_i64 >> shift
}
};
Ok(SqlValue::Integer(result))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_bitwise_or() {
let result = BitwiseOps::or(&SqlValue::Integer(5), &SqlValue::Integer(3)).unwrap();
assert_eq!(result, SqlValue::Integer(7));
}
#[test]
fn test_bitwise_and() {
let result = BitwiseOps::and(&SqlValue::Integer(5), &SqlValue::Integer(3)).unwrap();
assert_eq!(result, SqlValue::Integer(1));
}
#[test]
fn test_bitwise_not() {
let result = BitwiseOps::not(&SqlValue::Integer(0)).unwrap();
assert_eq!(result, SqlValue::Integer(-1));
let result = BitwiseOps::not(&SqlValue::Integer(-1)).unwrap();
assert_eq!(result, SqlValue::Integer(0));
let result = BitwiseOps::not(&SqlValue::Integer(1)).unwrap();
assert_eq!(result, SqlValue::Integer(-2));
}
#[test]
fn test_left_shift() {
let result = BitwiseOps::left_shift(&SqlValue::Integer(1), &SqlValue::Integer(4)).unwrap();
assert_eq!(result, SqlValue::Integer(16));
let result = BitwiseOps::left_shift(&SqlValue::Integer(5), &SqlValue::Integer(2)).unwrap();
assert_eq!(result, SqlValue::Integer(20));
}
#[test]
fn test_right_shift() {
let result =
BitwiseOps::right_shift(&SqlValue::Integer(16), &SqlValue::Integer(4)).unwrap();
assert_eq!(result, SqlValue::Integer(1));
let result =
BitwiseOps::right_shift(&SqlValue::Integer(20), &SqlValue::Integer(2)).unwrap();
assert_eq!(result, SqlValue::Integer(5));
}
#[test]
fn test_negative_shift() {
let result =
BitwiseOps::left_shift(&SqlValue::Integer(16), &SqlValue::Integer(-2)).unwrap();
assert_eq!(result, SqlValue::Integer(4));
let result =
BitwiseOps::right_shift(&SqlValue::Integer(4), &SqlValue::Integer(-2)).unwrap();
assert_eq!(result, SqlValue::Integer(16));
}
#[test]
fn test_large_shift() {
let result = BitwiseOps::left_shift(&SqlValue::Integer(1), &SqlValue::Integer(64)).unwrap();
assert_eq!(result, SqlValue::Integer(0));
let result =
BitwiseOps::right_shift(&SqlValue::Integer(1), &SqlValue::Integer(64)).unwrap();
assert_eq!(result, SqlValue::Integer(0));
let result =
BitwiseOps::right_shift(&SqlValue::Integer(-1), &SqlValue::Integer(64)).unwrap();
assert_eq!(result, SqlValue::Integer(-1));
}
#[test]
fn test_mixed_types() {
let result = BitwiseOps::or(&SqlValue::Smallint(5), &SqlValue::Integer(3)).unwrap();
assert_eq!(result, SqlValue::Integer(7));
let result = BitwiseOps::and(&SqlValue::Bigint(5), &SqlValue::Integer(3)).unwrap();
assert_eq!(result, SqlValue::Integer(1));
}
}