use vibesql_types::SqlValue;
use super::compare;
use crate::errors::ExecutorError;
#[inline]
pub fn equal(left: &SqlValue, right: &SqlValue) -> Result<SqlValue, ExecutorError> {
compare(left, right, |cmp| cmp == std::cmp::Ordering::Equal, "=")
}
#[inline]
pub fn not_equal(left: &SqlValue, right: &SqlValue) -> Result<SqlValue, ExecutorError> {
compare(left, right, |cmp| cmp != std::cmp::Ordering::Equal, "<>")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_integer_equality() {
let result = equal(&SqlValue::Integer(5), &SqlValue::Integer(5)).unwrap();
assert_eq!(result, SqlValue::Boolean(true));
let result = equal(&SqlValue::Integer(5), &SqlValue::Integer(3)).unwrap();
assert_eq!(result, SqlValue::Boolean(false));
}
#[test]
fn test_string_equality() {
let result = equal(
&SqlValue::Varchar(arcstr::ArcStr::from("hello")),
&SqlValue::Varchar(arcstr::ArcStr::from("hello")),
)
.unwrap();
assert_eq!(result, SqlValue::Boolean(true));
}
#[test]
fn test_cross_type_string() {
let result = equal(
&SqlValue::Character(arcstr::ArcStr::from("hello")),
&SqlValue::Varchar(arcstr::ArcStr::from("hello")),
)
.unwrap();
assert_eq!(result, SqlValue::Boolean(true));
}
#[test]
fn test_boolean_equality() {
let result = equal(&SqlValue::Boolean(true), &SqlValue::Boolean(true)).unwrap();
assert_eq!(result, SqlValue::Boolean(true));
}
#[test]
fn test_mixed_exact_numeric() {
let result = equal(&SqlValue::Smallint(5), &SqlValue::Bigint(5)).unwrap();
assert_eq!(result, SqlValue::Boolean(true));
}
#[test]
fn test_mixed_float_integer() {
let result = equal(&SqlValue::Float(5.0), &SqlValue::Integer(5)).unwrap();
assert_eq!(result, SqlValue::Boolean(true));
}
#[test]
fn test_integer_vs_numeric() {
let result =
equal(&SqlValue::Integer(200), &SqlValue::Numeric(174.36666666666667)).unwrap();
assert_eq!(result, SqlValue::Boolean(false));
}
#[test]
fn test_boolean_equals_integer() {
assert_eq!(
equal(&SqlValue::Boolean(true), &SqlValue::Integer(1)).unwrap(),
SqlValue::Boolean(true)
);
assert_eq!(
equal(&SqlValue::Boolean(false), &SqlValue::Integer(0)).unwrap(),
SqlValue::Boolean(true)
);
assert_eq!(
equal(&SqlValue::Boolean(true), &SqlValue::Integer(0)).unwrap(),
SqlValue::Boolean(false)
);
}
#[test]
fn test_boolean_comparison_with_float() {
assert_eq!(
equal(&SqlValue::Boolean(true), &SqlValue::Float(1.0)).unwrap(),
SqlValue::Boolean(true)
);
}
#[test]
fn test_boolean_comparison_with_numeric() {
assert_eq!(
equal(&SqlValue::Boolean(true), &SqlValue::Numeric(1.0)).unwrap(),
SqlValue::Boolean(true)
);
}
#[test]
fn test_boolean_equality_symmetric() {
assert_eq!(
equal(&SqlValue::Boolean(true), &SqlValue::Integer(1)).unwrap(),
equal(&SqlValue::Integer(1), &SqlValue::Boolean(true)).unwrap()
);
assert_eq!(
equal(&SqlValue::Boolean(false), &SqlValue::Integer(0)).unwrap(),
equal(&SqlValue::Integer(0), &SqlValue::Boolean(false)).unwrap()
);
}
#[test]
fn test_boolean_comparison_all_numeric_types() {
let true_val = SqlValue::Boolean(true);
assert_eq!(equal(&true_val, &SqlValue::Smallint(1)).unwrap(), SqlValue::Boolean(true));
assert_eq!(equal(&true_val, &SqlValue::Bigint(1)).unwrap(), SqlValue::Boolean(true));
assert_eq!(equal(&true_val, &SqlValue::Real(1.0)).unwrap(), SqlValue::Boolean(true));
assert_eq!(equal(&true_val, &SqlValue::Double(1.0)).unwrap(), SqlValue::Boolean(true));
assert_eq!(equal(&true_val, &SqlValue::Numeric(1.0)).unwrap(), SqlValue::Boolean(true));
}
#[test]
fn test_boolean_not_equal() {
assert_eq!(
not_equal(&SqlValue::Boolean(true), &SqlValue::Integer(0)).unwrap(),
SqlValue::Boolean(true)
);
assert_eq!(
not_equal(&SqlValue::Boolean(false), &SqlValue::Integer(1)).unwrap(),
SqlValue::Boolean(true)
);
assert_eq!(
not_equal(&SqlValue::Boolean(true), &SqlValue::Integer(1)).unwrap(),
SqlValue::Boolean(false)
);
}
#[test]
fn test_text_integer_type_ordering() {
assert_eq!(
equal(&SqlValue::Varchar(arcstr::ArcStr::from("10")), &SqlValue::Integer(10)).unwrap(),
SqlValue::Boolean(false)
);
assert_eq!(
equal(&SqlValue::Integer(10), &SqlValue::Varchar(arcstr::ArcStr::from("10"))).unwrap(),
SqlValue::Boolean(false)
);
}
#[test]
fn test_text_real_type_ordering() {
assert_eq!(
equal(&SqlValue::Varchar(arcstr::ArcStr::from("10")), &SqlValue::Double(10.0)).unwrap(),
SqlValue::Boolean(false)
);
assert_eq!(
equal(&SqlValue::Double(10.0), &SqlValue::Varchar(arcstr::ArcStr::from("10"))).unwrap(),
SqlValue::Boolean(false)
);
}
#[test]
fn test_text_numeric_type_ordering_all_types() {
let text_10 = SqlValue::Varchar(arcstr::ArcStr::from("10"));
assert_eq!(equal(&text_10, &SqlValue::Integer(10)).unwrap(), SqlValue::Boolean(false));
assert_eq!(equal(&text_10, &SqlValue::Smallint(10)).unwrap(), SqlValue::Boolean(false));
assert_eq!(equal(&text_10, &SqlValue::Bigint(10)).unwrap(), SqlValue::Boolean(false));
assert_eq!(equal(&text_10, &SqlValue::Float(10.0)).unwrap(), SqlValue::Boolean(false));
assert_eq!(equal(&text_10, &SqlValue::Real(10.0)).unwrap(), SqlValue::Boolean(false));
assert_eq!(equal(&text_10, &SqlValue::Double(10.0)).unwrap(), SqlValue::Boolean(false));
assert_eq!(equal(&text_10, &SqlValue::Numeric(10.0)).unwrap(), SqlValue::Boolean(false));
}
#[test]
fn test_text_numeric_type_ordering_character_type() {
assert_eq!(
equal(&SqlValue::Character(arcstr::ArcStr::from("10")), &SqlValue::Integer(10))
.unwrap(),
SqlValue::Boolean(false)
);
assert_eq!(
equal(&SqlValue::Integer(10), &SqlValue::Character(arcstr::ArcStr::from("10")))
.unwrap(),
SqlValue::Boolean(false)
);
}
#[test]
fn test_non_numeric_text_not_equal() {
assert_eq!(
equal(&SqlValue::Varchar(arcstr::ArcStr::from("hello")), &SqlValue::Integer(10))
.unwrap(),
SqlValue::Boolean(false)
);
assert_eq!(
equal(&SqlValue::Integer(10), &SqlValue::Varchar(arcstr::ArcStr::from("hello")))
.unwrap(),
SqlValue::Boolean(false)
);
}
#[test]
fn test_blob_vs_text_type_ordering() {
let text = SqlValue::Varchar(arcstr::ArcStr::from("abc"));
let blob = SqlValue::Blob(vec![0x61, 0x62, 0x63]);
assert_eq!(equal(&text, &blob).unwrap(), SqlValue::Boolean(false));
assert_eq!(equal(&blob, &text).unwrap(), SqlValue::Boolean(false));
assert_eq!(not_equal(&text, &blob).unwrap(), SqlValue::Boolean(true));
assert_eq!(not_equal(&blob, &text).unwrap(), SqlValue::Boolean(true));
}
#[test]
fn test_blob_vs_numeric_type_ordering() {
let blob = SqlValue::Blob(vec![0x01]);
assert_eq!(equal(&SqlValue::Integer(1), &blob).unwrap(), SqlValue::Boolean(false));
assert_eq!(equal(&blob, &SqlValue::Integer(1)).unwrap(), SqlValue::Boolean(false));
assert_eq!(equal(&SqlValue::Real(1.0), &blob).unwrap(), SqlValue::Boolean(false));
assert_eq!(equal(&blob, &SqlValue::Real(1.0)).unwrap(), SqlValue::Boolean(false));
}
#[test]
fn test_blob_vs_blob_bytewise() {
let a = SqlValue::Blob(vec![0x61, 0x62, 0x63]);
let b = SqlValue::Blob(vec![0x61, 0x62, 0x63]);
assert_eq!(equal(&a, &b).unwrap(), SqlValue::Boolean(true));
let c = SqlValue::Blob(vec![0x61, 0x62]);
assert_eq!(equal(&a, &c).unwrap(), SqlValue::Boolean(false));
assert_eq!(not_equal(&a, &c).unwrap(), SqlValue::Boolean(true));
}
fn ts(s: &str) -> SqlValue {
use std::str::FromStr;
SqlValue::Timestamp(vibesql_types::Timestamp::from_str(s).unwrap())
}
#[test]
fn test_timestamp_vs_string_equality() {
let timestamp = ts("2022-01-27 13:15:44");
let matching = SqlValue::Varchar(arcstr::ArcStr::from("2022-01-27 13:15:44"));
let differing = SqlValue::Varchar(arcstr::ArcStr::from("2022-01-27 13:15:45"));
assert_eq!(equal(×tamp, &matching).unwrap(), SqlValue::Boolean(true));
assert_eq!(equal(&matching, ×tamp).unwrap(), SqlValue::Boolean(true));
assert_eq!(equal(×tamp, &differing).unwrap(), SqlValue::Boolean(false));
assert_eq!(not_equal(×tamp, &differing).unwrap(), SqlValue::Boolean(true));
let matching_char = SqlValue::Character(arcstr::ArcStr::from("2022-01-27 13:15:44"));
assert_eq!(equal(×tamp, &matching_char).unwrap(), SqlValue::Boolean(true));
}
#[test]
fn test_timestamp_vs_unparseable_string_is_false_not_error() {
let timestamp = ts("2022-01-27 00:00:00");
let junk = SqlValue::Varchar(arcstr::ArcStr::from("hello"));
assert_eq!(equal(×tamp, &junk).unwrap(), SqlValue::Boolean(false));
assert_eq!(equal(&junk, ×tamp).unwrap(), SqlValue::Boolean(false));
assert_eq!(not_equal(×tamp, &junk).unwrap(), SqlValue::Boolean(true));
}
#[test]
fn test_timestamp_vs_date_only_string_is_not_equal() {
let midnight = ts("2017-07-08 00:00:00");
let date_only = SqlValue::Varchar(arcstr::ArcStr::from("2017-07-08"));
assert_eq!(equal(&midnight, &date_only).unwrap(), SqlValue::Boolean(false));
assert_eq!(equal(&date_only, &midnight).unwrap(), SqlValue::Boolean(false));
assert_eq!(not_equal(&midnight, &date_only).unwrap(), SqlValue::Boolean(true));
}
#[test]
fn test_time_vs_string_equality() {
let time = SqlValue::Time(vibesql_types::Time::new(13, 15, 44, 0).unwrap());
let matching = SqlValue::Varchar(arcstr::ArcStr::from("13:15:44"));
let differing = SqlValue::Varchar(arcstr::ArcStr::from("13:15:45"));
assert_eq!(equal(&time, &matching).unwrap(), SqlValue::Boolean(true));
assert_eq!(equal(&matching, &time).unwrap(), SqlValue::Boolean(true));
assert_eq!(equal(&time, &differing).unwrap(), SqlValue::Boolean(false));
}
}