use vibesql_types::SqlValue;
pub fn calculate_next_value(value: &SqlValue) -> Option<SqlValue> {
match value {
SqlValue::Double(d) => {
Some(SqlValue::Double(d + 1.0))
}
SqlValue::Integer(i) => Some(SqlValue::Integer(i + 1)),
SqlValue::Smallint(i) => Some(SqlValue::Smallint(i + 1)),
SqlValue::Bigint(i) => Some(SqlValue::Bigint(i + 1)),
SqlValue::Unsigned(u) => Some(SqlValue::Unsigned(u + 1)),
SqlValue::Float(f) => Some(SqlValue::Float(f + 1.0)),
SqlValue::Real(r) => Some(SqlValue::Real(r + 1.0)),
SqlValue::Numeric(n) => Some(SqlValue::Numeric(n + 1.0)),
_ => None,
}
}
pub fn try_increment_sqlvalue(value: &SqlValue) -> Option<SqlValue> {
match value {
SqlValue::Integer(i) if *i < i64::MAX => Some(SqlValue::Integer(i + 1)),
SqlValue::Smallint(i) if *i < i16::MAX => Some(SqlValue::Smallint(i + 1)),
SqlValue::Bigint(i) if *i < i64::MAX => Some(SqlValue::Bigint(i + 1)),
SqlValue::Unsigned(u) if *u < u64::MAX => Some(SqlValue::Unsigned(u + 1)),
SqlValue::Float(f) if f.is_finite() => {
let increment = (f.abs() * f32::EPSILON).max(f32::MIN_POSITIVE);
let next = f + increment;
if next > *f && next.is_finite() {
Some(SqlValue::Float(next))
} else {
None
}
}
SqlValue::Real(f) if f.is_finite() => {
let increment = (f.abs() * f64::EPSILON).max(f64::MIN_POSITIVE);
let next = f + increment;
if next > *f && next.is_finite() {
Some(SqlValue::Real(next))
} else {
None
}
}
SqlValue::Double(f) if f.is_finite() => {
let increment = (f.abs() * f64::EPSILON).max(f64::MIN_POSITIVE);
let next = f + increment;
if next > *f && next.is_finite() {
Some(SqlValue::Double(next))
} else {
None
}
}
SqlValue::Numeric(f) if f.is_finite() => {
let increment = (f.abs() * f64::EPSILON).max(f64::MIN_POSITIVE);
let next = f + increment;
if next > *f && next.is_finite() {
Some(SqlValue::Numeric(next))
} else {
None
}
}
SqlValue::Varchar(s) => {
Some(SqlValue::Varchar(arcstr::ArcStr::from(format!("{}\0", s).as_str())))
}
SqlValue::Character(s) => {
Some(SqlValue::Character(arcstr::ArcStr::from(format!("{}\0", s).as_str())))
}
SqlValue::Boolean(false) => Some(SqlValue::Boolean(true)),
SqlValue::Boolean(true) => None,
SqlValue::Null => None,
SqlValue::Date(_) | SqlValue::Time(_) | SqlValue::Timestamp(_) | SqlValue::Interval(_) => {
None
}
_ => None,
}
}
pub fn try_increment_sqlvalue_prefix(prefix: &[SqlValue]) -> Option<Vec<SqlValue>> {
if prefix.is_empty() {
return None;
}
let mut result = prefix.to_vec();
let last_idx = result.len() - 1;
match try_increment_sqlvalue(&result[last_idx]) {
Some(next_val) => {
result[last_idx] = next_val;
Some(result)
}
None => None,
}
}
pub fn smart_increment_value(value: &SqlValue) -> Option<SqlValue> {
match value {
SqlValue::Double(_) | SqlValue::Numeric(_) | SqlValue::Float(_) | SqlValue::Real(_) => {
try_increment_sqlvalue(value)
}
SqlValue::Integer(_)
| SqlValue::Smallint(_)
| SqlValue::Bigint(_)
| SqlValue::Unsigned(_) => calculate_next_value(value),
_ => calculate_next_value(value),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_calculate_next_value_double() {
assert_eq!(calculate_next_value(&SqlValue::Double(10.0)), Some(SqlValue::Double(11.0)));
assert_eq!(calculate_next_value(&SqlValue::Double(-5.5)), Some(SqlValue::Double(-4.5)));
assert_eq!(calculate_next_value(&SqlValue::Double(0.0)), Some(SqlValue::Double(1.0)));
}
#[test]
fn test_calculate_next_value_integer() {
assert_eq!(calculate_next_value(&SqlValue::Integer(42)), Some(SqlValue::Integer(43)));
assert_eq!(calculate_next_value(&SqlValue::Integer(-10)), Some(SqlValue::Integer(-9)));
assert_eq!(calculate_next_value(&SqlValue::Integer(0)), Some(SqlValue::Integer(1)));
}
#[test]
fn test_calculate_next_value_smallint() {
assert_eq!(calculate_next_value(&SqlValue::Smallint(100)), Some(SqlValue::Smallint(101)));
assert_eq!(calculate_next_value(&SqlValue::Smallint(-1)), Some(SqlValue::Smallint(0)));
}
#[test]
fn test_calculate_next_value_bigint() {
assert_eq!(
calculate_next_value(&SqlValue::Bigint(1_000_000_000)),
Some(SqlValue::Bigint(1_000_000_001))
);
assert_eq!(calculate_next_value(&SqlValue::Bigint(-999)), Some(SqlValue::Bigint(-998)));
}
#[test]
fn test_calculate_next_value_unsigned() {
assert_eq!(calculate_next_value(&SqlValue::Unsigned(0)), Some(SqlValue::Unsigned(1)));
assert_eq!(calculate_next_value(&SqlValue::Unsigned(999)), Some(SqlValue::Unsigned(1000)));
}
#[test]
fn test_calculate_next_value_float() {
assert_eq!(calculate_next_value(&SqlValue::Float(3.0)), Some(SqlValue::Float(4.0)));
assert_eq!(calculate_next_value(&SqlValue::Float(-2.5)), Some(SqlValue::Float(-1.5)));
}
#[test]
fn test_calculate_next_value_real() {
assert_eq!(calculate_next_value(&SqlValue::Real(7.5)), Some(SqlValue::Real(8.5)));
assert_eq!(calculate_next_value(&SqlValue::Real(0.0)), Some(SqlValue::Real(1.0)));
}
#[test]
fn test_calculate_next_value_numeric() {
assert_eq!(
calculate_next_value(&SqlValue::Numeric(99.99)),
Some(SqlValue::Numeric(100.99))
);
assert_eq!(calculate_next_value(&SqlValue::Numeric(-10.5)), Some(SqlValue::Numeric(-9.5)));
}
#[test]
fn test_calculate_next_value_non_numeric() {
assert_eq!(calculate_next_value(&SqlValue::Varchar(arcstr::ArcStr::from("abc"))), None);
assert_eq!(calculate_next_value(&SqlValue::Character(arcstr::ArcStr::from("hello"))), None);
assert_eq!(calculate_next_value(&SqlValue::Null), None);
assert_eq!(calculate_next_value(&SqlValue::Boolean(true)), None);
assert_eq!(calculate_next_value(&SqlValue::Boolean(false)), None);
}
#[test]
fn test_try_increment_sqlvalue_zero() {
let result = try_increment_sqlvalue(&SqlValue::Double(0.0));
assert!(result.is_some(), "Double(0.0) should increment");
if let Some(SqlValue::Double(d)) = result {
assert!(d > 0.0, "Incremented value should be positive");
}
let result = try_increment_sqlvalue(&SqlValue::Float(0.0));
assert!(result.is_some(), "Float(0.0) should increment");
if let Some(SqlValue::Float(f)) = result {
assert!(f > 0.0, "Incremented value should be positive");
}
let result = try_increment_sqlvalue(&SqlValue::Real(0.0));
assert!(result.is_some(), "Real(0.0) should increment");
if let Some(SqlValue::Real(r)) = result {
assert!(r > 0.0, "Incremented value should be positive");
}
let result = try_increment_sqlvalue(&SqlValue::Numeric(0.0));
assert!(result.is_some(), "Numeric(0.0) should increment");
if let Some(SqlValue::Numeric(n)) = result {
assert!(n > 0.0, "Incremented value should be positive");
}
assert_eq!(try_increment_sqlvalue(&SqlValue::Integer(0)), Some(SqlValue::Integer(1)));
}
}