use crate::SqlValue;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ValueType {
Integer,
Numeric,
Float,
Text,
Blob,
Null,
}
pub trait TypeBehavior {
fn has_decimal_type(&self) -> bool;
fn uses_dynamic_typing(&self) -> bool;
fn division_result_type(&self, left: &SqlValue, right: &SqlValue) -> ValueType;
fn permissive_type_coercion(&self) -> bool;
}
fn is_float_value(value: &SqlValue) -> bool {
matches!(
value,
SqlValue::Float(_) | SqlValue::Real(_) | SqlValue::Double(_) | SqlValue::Numeric(_)
)
}
impl TypeBehavior for super::SqlMode {
fn has_decimal_type(&self) -> bool {
match self {
super::SqlMode::MySQL { .. } => true,
super::SqlMode::SQLite => false,
}
}
fn uses_dynamic_typing(&self) -> bool {
match self {
super::SqlMode::MySQL { .. } => false,
super::SqlMode::SQLite => true,
}
}
fn division_result_type(&self, left: &SqlValue, right: &SqlValue) -> ValueType {
if left.is_null() || right.is_null() {
return ValueType::Null;
}
match self {
super::SqlMode::MySQL { flags } => {
if flags.sqlite_division_semantics {
if is_float_value(left) || is_float_value(right) {
ValueType::Float
} else {
ValueType::Integer
}
} else {
ValueType::Numeric
}
}
super::SqlMode::SQLite => {
if is_float_value(left) || is_float_value(right) {
ValueType::Float
} else {
ValueType::Integer
}
}
}
}
fn permissive_type_coercion(&self) -> bool {
match self {
super::SqlMode::MySQL { .. } => true,
super::SqlMode::SQLite => true,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::SqlMode;
#[test]
fn test_has_decimal_type() {
assert!(SqlMode::MySQL { flags: Default::default() }.has_decimal_type());
assert!(!SqlMode::SQLite.has_decimal_type());
assert!(SqlMode::default().has_decimal_type());
}
#[test]
fn test_uses_dynamic_typing() {
assert!(!SqlMode::MySQL { flags: Default::default() }.uses_dynamic_typing());
assert!(SqlMode::SQLite.uses_dynamic_typing());
assert!(!SqlMode::default().uses_dynamic_typing());
}
#[test]
fn test_permissive_type_coercion() {
assert!(SqlMode::MySQL { flags: Default::default() }.permissive_type_coercion());
assert!(SqlMode::SQLite.permissive_type_coercion());
}
#[test]
fn test_mysql_division_result_type() {
let mode = SqlMode::MySQL { flags: Default::default() };
assert_eq!(
mode.division_result_type(&SqlValue::Integer(5), &SqlValue::Integer(2)),
ValueType::Numeric
);
assert_eq!(
mode.division_result_type(&SqlValue::Bigint(10), &SqlValue::Bigint(3)),
ValueType::Numeric
);
assert_eq!(
mode.division_result_type(&SqlValue::Float(5.5), &SqlValue::Integer(2)),
ValueType::Numeric
);
assert_eq!(
mode.division_result_type(&SqlValue::Integer(100), &SqlValue::Float(2.5)),
ValueType::Numeric
);
assert_eq!(
mode.division_result_type(&SqlValue::Numeric(7.5), &SqlValue::Numeric(2.5)),
ValueType::Numeric
);
assert_eq!(
mode.division_result_type(&SqlValue::Null, &SqlValue::Integer(2)),
ValueType::Null
);
assert_eq!(
mode.division_result_type(&SqlValue::Integer(5), &SqlValue::Null),
ValueType::Null
);
}
#[test]
fn test_mysql_with_sqlite_division_semantics() {
use crate::MySqlModeFlags;
let flags = MySqlModeFlags { sqlite_division_semantics: true, ..Default::default() };
let mode = SqlMode::MySQL { flags };
assert_eq!(
mode.division_result_type(&SqlValue::Integer(5), &SqlValue::Integer(2)),
ValueType::Integer
);
assert_eq!(
mode.division_result_type(&SqlValue::Bigint(10), &SqlValue::Bigint(3)),
ValueType::Integer
);
assert_eq!(
mode.division_result_type(&SqlValue::Float(5.0), &SqlValue::Integer(2)),
ValueType::Float
);
assert_eq!(
mode.division_result_type(&SqlValue::Integer(10), &SqlValue::Float(2.0)),
ValueType::Float
);
assert_eq!(
mode.division_result_type(&SqlValue::Null, &SqlValue::Integer(2)),
ValueType::Null
);
}
#[test]
fn test_sqlite_division_result_type() {
let mode = SqlMode::SQLite;
assert_eq!(
mode.division_result_type(&SqlValue::Integer(5), &SqlValue::Integer(2)),
ValueType::Integer
);
assert_eq!(
mode.division_result_type(&SqlValue::Bigint(10), &SqlValue::Bigint(3)),
ValueType::Integer
);
assert_eq!(
mode.division_result_type(&SqlValue::Float(5.0), &SqlValue::Integer(2)),
ValueType::Float
);
assert_eq!(
mode.division_result_type(&SqlValue::Integer(10), &SqlValue::Float(2.0)),
ValueType::Float
);
assert_eq!(
mode.division_result_type(&SqlValue::Real(7.5), &SqlValue::Real(2.5)),
ValueType::Float
);
assert_eq!(
mode.division_result_type(&SqlValue::Double(5.0), &SqlValue::Integer(2)),
ValueType::Float
);
assert_eq!(
mode.division_result_type(&SqlValue::Numeric(5.0), &SqlValue::Integer(2)),
ValueType::Float
);
assert_eq!(
mode.division_result_type(&SqlValue::Null, &SqlValue::Integer(2)),
ValueType::Null
);
assert_eq!(
mode.division_result_type(&SqlValue::Integer(5), &SqlValue::Null),
ValueType::Null
);
}
#[test]
fn test_value_type_enum() {
assert_ne!(ValueType::Integer, ValueType::Numeric);
assert_ne!(ValueType::Numeric, ValueType::Float);
assert_ne!(ValueType::Integer, ValueType::Float);
assert_ne!(ValueType::Integer, ValueType::Text);
assert_ne!(ValueType::Integer, ValueType::Blob);
assert_ne!(ValueType::Integer, ValueType::Null);
}
}