use crate::{
catalog::index_policy::ducklake_base_type,
store::index_encoding::{IndexKeyValue, IntWidth},
};
#[derive(Debug, Clone, PartialEq)]
pub enum LookupInput {
Int(i64),
UInt(u64),
Float(f64),
Bool(bool),
Str(String),
Bytes(Vec<u8>),
}
#[allow(clippy::match_same_arms, clippy::cast_possible_truncation)]
pub fn coerce_lookup_value(
input: &LookupInput,
ducklake_type: &str,
) -> std::result::Result<IndexKeyValue, String> {
let want = |what: &str| -> String {
format!("index lookup: expected {what} value for a `{ducklake_type}` column")
};
let signed_input = || -> std::result::Result<i128, String> {
match input {
LookupInput::Int(value) => Ok(i128::from(*value)),
LookupInput::UInt(value) => Ok(i128::from(*value)),
_ => Err(want("an integer")),
}
};
let unsigned_input = || -> std::result::Result<u128, String> {
match input {
LookupInput::UInt(value) => Ok(u128::from(*value)),
LookupInput::Int(value) if *value >= 0 => Ok(value.unsigned_abs().into()),
_ => Err(want("an unsigned integer")),
}
};
let signed = |value, width| IndexKeyValue::Int { value, width };
let unsigned = |value, width| IndexKeyValue::UInt { value, width };
let value = match ducklake_base_type(ducklake_type).as_str() {
"TINYINT" | "INT8" => signed(signed_input()?, IntWidth::I8),
"SMALLINT" | "INT16" => signed(signed_input()?, IntWidth::I16),
"INTEGER" | "INT32" => signed(signed_input()?, IntWidth::I32),
"BIGINT" | "INT64" => signed(signed_input()?, IntWidth::I64),
"UTINYINT" | "UINT8" => unsigned(unsigned_input()?, IntWidth::I8),
"USMALLINT" | "UINT16" => unsigned(unsigned_input()?, IntWidth::I16),
"UINTEGER" | "UINT32" => unsigned(unsigned_input()?, IntWidth::I32),
"UBIGINT" | "UINT64" => unsigned(unsigned_input()?, IntWidth::I64),
"DATE" => signed(signed_input()?, IntWidth::I32),
"TIME"
| "TIME_NS"
| "TIMETZ"
| "TIME WITH TIME ZONE"
| "TIMESTAMP"
| "TIMESTAMP_S"
| "TIMESTAMP_MS"
| "TIMESTAMP_NS"
| "TIMESTAMP_US"
| "TIMESTAMPTZ"
| "TIMESTAMP WITH TIME ZONE" => signed(signed_input()?, IntWidth::I64),
"BOOLEAN" => {
let LookupInput::Bool(value) = input else {
return Err(want("a boolean"));
};
IndexKeyValue::Bool(*value)
}
"FLOAT" | "FLOAT32" | "REAL" => {
let LookupInput::Float(value) = input else {
return Err(want("a float"));
};
IndexKeyValue::F32(*value as f32)
}
"DOUBLE" | "FLOAT64" => {
let LookupInput::Float(value) = input else {
return Err(want("a float"));
};
IndexKeyValue::F64(*value)
}
"VARCHAR" | "TEXT" | "JSON" => {
let LookupInput::Str(value) = input else {
return Err(want("a string"));
};
IndexKeyValue::Str(value.clone())
}
"UUID" | "BLOB" => {
let LookupInput::Bytes(value) = input else {
return Err(want("a UUID or blob"));
};
IndexKeyValue::Bytes(value.clone())
}
other => {
return Err(format!(
"index lookup: column type `{other}` is not supported"
));
}
};
Ok(value)
}