nodedb_columnar/
predicate.rs1use crate::format::BlockStats;
7
8#[derive(Debug, Clone)]
10pub struct ScanPredicate {
11 pub col_idx: usize,
13 pub op: PredicateOp,
15 pub value: f64,
17}
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21pub enum PredicateOp {
22 Gt,
24 Gte,
26 Lt,
28 Lte,
30 Eq,
32 Ne,
34}
35
36impl ScanPredicate {
37 pub fn gt(col_idx: usize, value: f64) -> Self {
39 Self {
40 col_idx,
41 op: PredicateOp::Gt,
42 value,
43 }
44 }
45
46 pub fn gte(col_idx: usize, value: f64) -> Self {
48 Self {
49 col_idx,
50 op: PredicateOp::Gte,
51 value,
52 }
53 }
54
55 pub fn lt(col_idx: usize, value: f64) -> Self {
57 Self {
58 col_idx,
59 op: PredicateOp::Lt,
60 value,
61 }
62 }
63
64 pub fn lte(col_idx: usize, value: f64) -> Self {
66 Self {
67 col_idx,
68 op: PredicateOp::Lte,
69 value,
70 }
71 }
72
73 pub fn eq(col_idx: usize, value: f64) -> Self {
75 Self {
76 col_idx,
77 op: PredicateOp::Eq,
78 value,
79 }
80 }
81
82 pub fn not_eq(col_idx: usize, value: f64) -> Self {
85 Self {
86 col_idx,
87 op: PredicateOp::Ne,
88 value,
89 }
90 }
91
92 pub fn can_skip_block(&self, stats: &BlockStats) -> bool {
97 if stats.min.is_nan() || stats.max.is_nan() {
99 return false;
100 }
101
102 match self.op {
103 PredicateOp::Gt => stats.max <= self.value,
105 PredicateOp::Gte => stats.max < self.value,
107 PredicateOp::Lt => stats.min >= self.value,
109 PredicateOp::Lte => stats.min > self.value,
111 PredicateOp::Eq => self.value < stats.min || self.value > stats.max,
113 PredicateOp::Ne => stats.min == self.value && stats.max == self.value,
115 }
116 }
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122
123 fn stats(min: f64, max: f64) -> BlockStats {
124 BlockStats {
125 min,
126 max,
127 null_count: 0,
128 row_count: 1024,
129 }
130 }
131
132 #[test]
133 fn gt_predicate() {
134 let pred = ScanPredicate::gt(0, 50.0);
135 assert!(pred.can_skip_block(&stats(10.0, 40.0)));
137 assert!(!pred.can_skip_block(&stats(10.0, 60.0)));
139 assert!(pred.can_skip_block(&stats(10.0, 50.0)));
141 }
142
143 #[test]
144 fn gte_predicate() {
145 let pred = ScanPredicate::gte(0, 50.0);
146 assert!(pred.can_skip_block(&stats(10.0, 49.0)));
148 assert!(!pred.can_skip_block(&stats(10.0, 50.0)));
150 }
151
152 #[test]
153 fn lt_predicate() {
154 let pred = ScanPredicate::lt(0, 50.0);
155 assert!(pred.can_skip_block(&stats(60.0, 100.0)));
157 assert!(!pred.can_skip_block(&stats(40.0, 100.0)));
159 }
160
161 #[test]
162 fn eq_predicate() {
163 let pred = ScanPredicate::eq(0, 50.0);
164 assert!(pred.can_skip_block(&stats(10.0, 40.0)));
166 assert!(pred.can_skip_block(&stats(60.0, 100.0)));
168 assert!(!pred.can_skip_block(&stats(40.0, 60.0)));
170 }
171
172 #[test]
173 fn ne_predicate() {
174 let pred = ScanPredicate::not_eq(0, 50.0);
175 assert!(pred.can_skip_block(&stats(50.0, 50.0)));
177 assert!(!pred.can_skip_block(&stats(40.0, 60.0)));
179 }
180
181 #[test]
182 fn non_numeric_never_skipped() {
183 let pred = ScanPredicate::gt(0, 50.0);
184 let nan_stats = BlockStats::non_numeric(0, 1024);
185 assert!(!pred.can_skip_block(&nan_stats));
186 }
187}