use std::cmp::min;
use Type::*;
use crate::value::r#type::Type;
impl Type {
pub fn promote(left: Type, right: Type) -> Type {
if matches!(left, Any) || matches!(right, Any) {
return Any;
}
if matches!(left, Option(_)) || matches!(right, Option(_)) {
return left;
}
if left == Utf8 || right == Utf8 {
return Utf8;
}
if left == Boolean || right == Boolean {
return Boolean;
}
if left == Float8 || right == Float8 {
return Float8;
}
if left == Float4 || right == Float4 {
return Float8;
}
let signed_order = [Int1, Int2, Int4, Int8, Int16];
let unsigned_order = [Uint1, Uint2, Uint4, Uint8, Uint16];
let is_signed = |k: &Type| signed_order.contains(k);
let is_unsigned = |k: &Type| unsigned_order.contains(k);
let rank = |k: &Type| match k {
Int1 | Uint1 => 0,
Int2 | Uint2 => 1,
Int4 | Uint4 => 2,
Int8 | Uint8 => 3,
Int16 | Uint16 => 4,
_ => usize::MAX,
};
if is_signed(&left) && is_signed(&right) {
return signed_order[min(rank(&left).max(rank(&right)), 3) + 1].clone();
}
if is_unsigned(&left) && is_unsigned(&right) {
return unsigned_order[min(rank(&left).max(rank(&right)), 3) + 1].clone();
}
if (is_signed(&left) && is_unsigned(&right)) || (is_unsigned(&left) && is_signed(&right)) {
return match rank(&left).max(rank(&right)) + 1 {
0 => Int1,
1 => Int2,
2 => Int4,
3 => Int8,
4 => Int16,
_ => Int16,
};
}
left
}
}
#[cfg(test)]
pub mod tests {
use Type::*;
use crate::value::r#type::Type;
#[test]
fn test_promote_bool() {
let cases = [
(Boolean, Boolean, Boolean),
(Boolean, Float4, Boolean),
(Boolean, Float8, Boolean),
(Boolean, Int1, Boolean),
(Boolean, Int2, Boolean),
(Boolean, Int4, Boolean),
(Boolean, Int8, Boolean),
(Boolean, Int16, Boolean),
(Boolean, Utf8, Utf8),
(Boolean, Uint1, Boolean),
(Boolean, Uint2, Boolean),
(Boolean, Uint4, Boolean),
(Boolean, Uint8, Boolean),
(Boolean, Uint16, Boolean),
];
for (left, right, expected) in cases {
assert_eq!(Type::promote(left, right), expected);
}
}
#[test]
fn test_promote_float4() {
let cases = [
(Float4, Boolean, Boolean),
(Float4, Float4, Float8),
(Float4, Float8, Float8),
(Float4, Int1, Float8),
(Float4, Int2, Float8),
(Float4, Int4, Float8),
(Float4, Int8, Float8),
(Float4, Int16, Float8),
(Float4, Utf8, Utf8),
(Float4, Uint1, Float8),
(Float4, Uint2, Float8),
(Float4, Uint4, Float8),
(Float4, Uint8, Float8),
(Float4, Uint16, Float8),
];
for (left, right, expected) in cases {
assert_eq!(Type::promote(left, right), expected);
}
}
#[test]
fn test_promote_float8() {
let cases = [
(Float8, Boolean, Boolean),
(Float8, Float4, Float8),
(Float8, Float8, Float8),
(Float8, Int1, Float8),
(Float8, Int2, Float8),
(Float8, Int4, Float8),
(Float8, Int8, Float8),
(Float8, Int16, Float8),
(Float8, Utf8, Utf8),
(Float8, Uint1, Float8),
(Float8, Uint2, Float8),
(Float8, Uint4, Float8),
(Float8, Uint8, Float8),
(Float8, Uint16, Float8),
];
for (left, right, expected) in cases {
assert_eq!(Type::promote(left, right), expected);
}
}
#[test]
fn test_promote_int1() {
let cases = [
(Int1, Boolean, Boolean),
(Int1, Float4, Float8),
(Int1, Float8, Float8),
(Int1, Int1, Int2),
(Int1, Int2, Int4),
(Int1, Int4, Int8),
(Int1, Int8, Int16),
(Int1, Int16, Int16),
(Int1, Utf8, Utf8),
(Int1, Uint1, Int2),
(Int1, Uint2, Int4),
(Int1, Uint4, Int8),
(Int1, Uint8, Int16),
(Int1, Uint16, Int16),
];
for (left, right, expected) in cases {
assert_eq!(Type::promote(left, right), expected);
}
}
#[test]
fn test_promote_int2() {
let cases = [
(Int2, Boolean, Boolean),
(Int2, Float4, Float8),
(Int2, Float8, Float8),
(Int2, Int1, Int4),
(Int2, Int2, Int4),
(Int2, Int4, Int8),
(Int2, Int8, Int16),
(Int2, Int16, Int16),
(Int2, Utf8, Utf8),
(Int2, Uint1, Int4),
(Int2, Uint2, Int4),
(Int2, Uint4, Int8),
(Int2, Uint8, Int16),
(Int2, Uint16, Int16),
];
for (left, right, expected) in cases {
assert_eq!(Type::promote(left, right), expected);
}
}
#[test]
fn test_promote_int4() {
let cases = [
(Int4, Boolean, Boolean),
(Int4, Float4, Float8),
(Int4, Float8, Float8),
(Int4, Int1, Int8),
(Int4, Int2, Int8),
(Int4, Int4, Int8),
(Int4, Int8, Int16),
(Int4, Int16, Int16),
(Int4, Utf8, Utf8),
(Int4, Uint1, Int8),
(Int4, Uint2, Int8),
(Int4, Uint4, Int8),
(Int4, Uint8, Int16),
(Int4, Uint16, Int16),
];
for (left, right, expected) in cases {
assert_eq!(Type::promote(left, right), expected);
}
}
#[test]
fn test_promote_int8() {
let cases = [
(Int8, Boolean, Boolean),
(Int8, Float4, Float8),
(Int8, Float8, Float8),
(Int8, Int1, Int16),
(Int8, Int2, Int16),
(Int8, Int4, Int16),
(Int8, Int8, Int16),
(Int8, Int16, Int16),
(Int8, Utf8, Utf8),
(Int8, Uint1, Int16),
(Int8, Uint2, Int16),
(Int8, Uint4, Int16),
(Int8, Uint8, Int16),
(Int8, Uint16, Int16),
];
for (left, right, expected) in cases {
assert_eq!(Type::promote(left, right), expected);
}
}
#[test]
fn test_promote_int16() {
let cases = [
(Int16, Boolean, Boolean),
(Int16, Float4, Float8),
(Int16, Float8, Float8),
(Int16, Int1, Int16),
(Int16, Int2, Int16),
(Int16, Int4, Int16),
(Int16, Int8, Int16),
(Int16, Int16, Int16),
(Int16, Utf8, Utf8),
(Int16, Uint1, Int16),
(Int16, Uint2, Int16),
(Int16, Uint4, Int16),
(Int16, Uint8, Int16),
(Int16, Uint16, Int16),
];
for (left, right, expected) in cases {
assert_eq!(Type::promote(left, right), expected);
}
}
#[test]
fn test_promote_string() {
let kinds = [
Boolean, Float4, Float8, Int1, Int2, Int4, Int8, Int16, Utf8, Uint1, Uint2, Uint4, Uint8,
Uint16,
];
for ty in kinds {
assert_eq!(Type::promote(Utf8, ty), Utf8);
}
}
#[test]
fn test_promote_uint1() {
let cases = [
(Uint1, Boolean, Boolean),
(Uint1, Float4, Float8),
(Uint1, Float8, Float8),
(Uint1, Int1, Int2),
(Uint1, Int2, Int4),
(Uint1, Int4, Int8),
(Uint1, Int8, Int16),
(Uint1, Int16, Int16),
(Uint1, Utf8, Utf8),
(Uint1, Uint1, Uint2),
(Uint1, Uint2, Uint4),
(Uint1, Uint4, Uint8),
(Uint1, Uint8, Uint16),
(Uint1, Uint16, Uint16),
];
for (left, right, expected) in cases {
assert_eq!(Type::promote(left, right), expected);
}
}
#[test]
fn test_promote_uint2() {
let cases = [
(Uint2, Boolean, Boolean),
(Uint2, Float4, Float8),
(Uint2, Float8, Float8),
(Uint2, Int1, Int4),
(Uint2, Int2, Int4),
(Uint2, Int4, Int8),
(Uint2, Int8, Int16),
(Uint2, Int16, Int16),
(Uint2, Utf8, Utf8),
(Uint2, Uint1, Uint4),
(Uint2, Uint2, Uint4),
(Uint2, Uint4, Uint8),
(Uint2, Uint8, Uint16),
(Uint2, Uint16, Uint16),
];
for (left, right, expected) in cases {
assert_eq!(Type::promote(left, right), expected);
}
}
#[test]
fn test_promote_uint4() {
let cases = [
(Uint4, Boolean, Boolean),
(Uint4, Float4, Float8),
(Uint4, Float8, Float8),
(Uint4, Int1, Int8),
(Uint4, Int2, Int8),
(Uint4, Int4, Int8),
(Uint4, Int8, Int16),
(Uint4, Int16, Int16),
(Uint4, Utf8, Utf8),
(Uint4, Uint1, Uint8),
(Uint4, Uint2, Uint8),
(Uint4, Uint4, Uint8),
(Uint4, Uint8, Uint16),
(Uint4, Uint16, Uint16),
];
for (left, right, expected) in cases {
assert_eq!(Type::promote(left, right), expected);
}
}
#[test]
fn test_promote_uint8() {
let cases = [
(Uint8, Boolean, Boolean),
(Uint8, Float4, Float8),
(Uint8, Float8, Float8),
(Uint8, Int1, Int16),
(Uint8, Int2, Int16),
(Uint8, Int4, Int16),
(Uint8, Int8, Int16),
(Uint8, Int16, Int16),
(Uint8, Utf8, Utf8),
(Uint8, Uint1, Uint16),
(Uint8, Uint2, Uint16),
(Uint8, Uint4, Uint16),
(Uint8, Uint8, Uint16),
(Uint8, Uint16, Uint16),
];
for (left, right, expected) in cases {
assert_eq!(Type::promote(left, right), expected);
}
}
#[test]
fn test_promote_any_is_absorbing() {
let kinds = [
Boolean,
Float4,
Float8,
Int1,
Int2,
Int4,
Int8,
Int16,
Uint1,
Uint2,
Uint4,
Uint8,
Uint16,
Utf8,
Date,
DateTime,
Time,
Duration,
Uuid4,
Uuid7,
Blob,
IdentityId,
DictionaryId,
Int,
Uint,
Decimal,
Any,
];
for ty in kinds {
assert_eq!(Type::promote(Any, ty.clone()), Any, "Any on left with {:?}", ty);
assert_eq!(Type::promote(ty.clone(), Any), Any, "Any on right with {:?}", ty);
}
assert_eq!(Type::promote(Any, Option(Box::new(Int4))), Any);
assert_eq!(Type::promote(Option(Box::new(Int4)), Any), Any);
}
#[test]
fn test_promote_uint16() {
let cases = [
(Uint16, Boolean, Boolean),
(Uint16, Float4, Float8),
(Uint16, Float8, Float8),
(Uint16, Int1, Int16),
(Uint16, Int2, Int16),
(Uint16, Int4, Int16),
(Uint16, Int8, Int16),
(Uint16, Int16, Int16),
(Uint16, Utf8, Utf8),
(Uint16, Uint1, Uint16),
(Uint16, Uint2, Uint16),
(Uint16, Uint4, Uint16),
(Uint16, Uint8, Uint16),
(Uint16, Uint16, Uint16),
];
for (left, right, expected) in cases {
assert_eq!(Type::promote(left, right), expected);
}
}
}