use crate::format::BlockStats;
#[derive(Debug, Clone)]
pub struct ScanPredicate {
pub col_idx: usize,
pub op: PredicateOp,
pub value: f64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PredicateOp {
Gt,
Gte,
Lt,
Lte,
Eq,
Ne,
}
impl ScanPredicate {
pub fn gt(col_idx: usize, value: f64) -> Self {
Self {
col_idx,
op: PredicateOp::Gt,
value,
}
}
pub fn gte(col_idx: usize, value: f64) -> Self {
Self {
col_idx,
op: PredicateOp::Gte,
value,
}
}
pub fn lt(col_idx: usize, value: f64) -> Self {
Self {
col_idx,
op: PredicateOp::Lt,
value,
}
}
pub fn lte(col_idx: usize, value: f64) -> Self {
Self {
col_idx,
op: PredicateOp::Lte,
value,
}
}
pub fn eq(col_idx: usize, value: f64) -> Self {
Self {
col_idx,
op: PredicateOp::Eq,
value,
}
}
pub fn not_eq(col_idx: usize, value: f64) -> Self {
Self {
col_idx,
op: PredicateOp::Ne,
value,
}
}
pub fn can_skip_block(&self, stats: &BlockStats) -> bool {
if stats.min.is_nan() || stats.max.is_nan() {
return false;
}
match self.op {
PredicateOp::Gt => stats.max <= self.value,
PredicateOp::Gte => stats.max < self.value,
PredicateOp::Lt => stats.min >= self.value,
PredicateOp::Lte => stats.min > self.value,
PredicateOp::Eq => self.value < stats.min || self.value > stats.max,
PredicateOp::Ne => stats.min == self.value && stats.max == self.value,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn stats(min: f64, max: f64) -> BlockStats {
BlockStats {
min,
max,
null_count: 0,
row_count: 1024,
}
}
#[test]
fn gt_predicate() {
let pred = ScanPredicate::gt(0, 50.0);
assert!(pred.can_skip_block(&stats(10.0, 40.0)));
assert!(!pred.can_skip_block(&stats(10.0, 60.0)));
assert!(pred.can_skip_block(&stats(10.0, 50.0)));
}
#[test]
fn gte_predicate() {
let pred = ScanPredicate::gte(0, 50.0);
assert!(pred.can_skip_block(&stats(10.0, 49.0)));
assert!(!pred.can_skip_block(&stats(10.0, 50.0)));
}
#[test]
fn lt_predicate() {
let pred = ScanPredicate::lt(0, 50.0);
assert!(pred.can_skip_block(&stats(60.0, 100.0)));
assert!(!pred.can_skip_block(&stats(40.0, 100.0)));
}
#[test]
fn eq_predicate() {
let pred = ScanPredicate::eq(0, 50.0);
assert!(pred.can_skip_block(&stats(10.0, 40.0)));
assert!(pred.can_skip_block(&stats(60.0, 100.0)));
assert!(!pred.can_skip_block(&stats(40.0, 60.0)));
}
#[test]
fn ne_predicate() {
let pred = ScanPredicate::not_eq(0, 50.0);
assert!(pred.can_skip_block(&stats(50.0, 50.0)));
assert!(!pred.can_skip_block(&stats(40.0, 60.0)));
}
#[test]
fn non_numeric_never_skipped() {
let pred = ScanPredicate::gt(0, 50.0);
let nan_stats = BlockStats::non_numeric(0, 1024);
assert!(!pred.can_skip_block(&nan_stats));
}
}