use super::{CompositeCol, MAX_STR_COMPONENT, encode_component};
use crate::catalog::ValType;
use crate::table_error::WhereError;
use kevy_text::SortOrder;
#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct WhereClause {
pub eqs: Vec<(Vec<u8>, Vec<u8>)>,
pub range: Option<(Vec<u8>, Vec<u8>, Vec<u8>)>,
}
pub fn parse_where(
argv: &[Vec<u8>],
at: usize,
stop: impl Fn(&[u8]) -> bool,
) -> Option<(WhereClause, usize)> {
let mut w = WhereClause::default();
let mut i = at;
while i < argv.len() && !stop(&argv[i]) {
if argv[i].eq_ignore_ascii_case(b"RANGE") {
let col = argv.get(i + 1)?.clone();
let min = argv.get(i + 2)?.clone();
let max = argv.get(i + 3)?.clone();
w.range = Some((col, min, max));
i += 4;
break;
}
if !argv.get(i + 1)?.eq_ignore_ascii_case(b"EQ") {
return None;
}
w.eqs.push((argv[i].clone(), argv.get(i + 2)?.clone()));
i += 3;
}
if w.eqs.is_empty() && w.range.is_none() {
return None;
}
Some((w, i))
}
fn declared_list(cols: &[CompositeCol]) -> Vec<Vec<u8>> {
cols.iter().map(|c| c.name.clone()).collect()
}
fn bound_component(col: &CompositeCol, raw: &[u8], now: i64) -> Result<Vec<u8>, WhereError> {
let resolved;
let raw = if col.ty == ValType::I64 && raw.first() == Some(&b'@') {
match kevy_time::eval(raw, now) {
Some(i) => {
resolved = i.to_string().into_bytes();
resolved.as_slice()
}
None => {
return Err(WhereError::TimeExpression {
bound: raw.to_vec(),
column: col.name.clone(),
});
}
}
} else {
raw
};
encode_component(col, raw).ok_or_else(|| WhereError::Value {
bound: raw.to_vec(),
ty: col.ty,
column: col.name.clone(),
})
}
fn component_max(col: &CompositeCol) -> Vec<u8> {
match (col.ty, col.order) {
(ValType::I64 | ValType::F64, _) => vec![0xFF; 8],
(ValType::Str, SortOrder::Desc) => vec![0xFF, 0xFF],
(ValType::Str, SortOrder::Asc) => vec![0xFF; MAX_STR_COMPONENT * 2 + 3],
(ValType::Vector, _) => Vec::new(),
}
}
pub fn composite_bounds(
cols: &[CompositeCol],
w: &WhereClause,
now: i64,
) -> Result<(Vec<u8>, Vec<u8>), WhereError> {
let mut lo = Vec::new();
let mut hi = Vec::new();
let mut at = 0usize;
for (name, value) in &w.eqs {
let col = resolve_col(cols, at, name)?;
let enc = bound_component(col, value, now)?;
lo.extend_from_slice(&enc);
hi.extend_from_slice(&enc);
at += 1;
}
if let Some((name, min, max)) = &w.range {
let col = resolve_col(cols, at, name)?;
let a = bound_component(col, min, now)?;
let b = bound_component(col, max, now)?;
let (emin, emax) = if a <= b { (a, b) } else { (b, a) };
lo.extend_from_slice(&emin);
hi.extend_from_slice(&emax);
at += 1;
}
for col in &cols[at..] {
let m = component_max(col);
let dominates = col.ty == ValType::Str && col.order == SortOrder::Asc;
hi.extend_from_slice(&m);
if dominates {
break;
}
}
Ok((lo, hi))
}
fn resolve_col<'c>(
cols: &'c [CompositeCol],
at: usize,
name: &[u8],
) -> Result<&'c CompositeCol, WhereError> {
if !cols.iter().any(|c| c.name == name) {
return Err(WhereError::UnknownColumn {
column: name.to_vec(),
declared: declared_list(cols),
});
}
match cols.get(at) {
Some(c) if c.name == name => Ok(c),
_ => Err(WhereError::NotLeadingPrefix { declared: declared_list(cols) }),
}
}