1use std::sync::Arc;
16
17use arc_swap::ArcSwap;
18use common_telemetry::{debug, warn};
19use common_time::Timestamp;
20use common_time::range::TimestampRange;
21use common_time::timestamp::TimeUnit;
22use datafusion::common::ScalarValue;
23use datafusion::physical_optimizer::pruning::PruningPredicate;
24use datafusion_common::ToDFSchema;
25use datafusion_common::pruning::PruningStatistics;
26use datafusion_common::tree_node::TreeNode;
27use datafusion_expr::expr::{Expr, InList};
28use datafusion_expr::{Between, BinaryExpr, Operator};
29use datafusion_physical_expr::execution_props::ExecutionProps;
30use datafusion_physical_expr::expressions::DynamicFilterPhysicalExpr;
31use datafusion_physical_expr::{PhysicalExpr, create_physical_expr};
32use datatypes::arrow;
33use datatypes::value::scalar_value_to_timestamp;
34use snafu::ResultExt;
35
36use crate::error;
37
38#[cfg(test)]
39mod stats;
40
41macro_rules! return_none_if_utf8 {
44 ($lit: ident) => {
45 if is_string_timestamp_literal($lit) {
46 warn!(
47 "Unexpected ScalarValue::Utf8 in time range predicate: {:?}. Maybe it's an implicit bug, please report it to https://github.com/GreptimeTeam/greptimedb/issues",
48 $lit
49 );
50
51 return None;
53 }
54 };
55}
56
57pub fn is_string_timestamp_literal(scalar: &ScalarValue) -> bool {
58 matches!(
59 scalar,
60 ScalarValue::Utf8(_) | ScalarValue::LargeUtf8(_) | ScalarValue::Utf8View(_)
61 )
62}
63
64#[derive(Debug, Clone, Default)]
66pub struct Predicate {
67 exprs: Arc<Vec<Expr>>,
69 dyn_filters: Arc<ArcSwap<Vec<Arc<DynamicFilterPhysicalExpr>>>>,
73}
74
75impl Predicate {
76 pub fn new(exprs: Vec<Expr>) -> Self {
80 Self {
81 exprs: Arc::new(exprs),
82 dyn_filters: Arc::new(ArcSwap::new(Arc::new(vec![]))),
83 }
84 }
85
86 pub fn with_dyn_filters(
87 exprs: Vec<Expr>,
88 dyn_filters: Vec<Arc<DynamicFilterPhysicalExpr>>,
89 ) -> Self {
90 Self {
91 exprs: Arc::new(exprs),
92 dyn_filters: Arc::new(ArcSwap::new(Arc::new(dyn_filters))),
93 }
94 }
95
96 pub fn is_empty(&self) -> bool {
97 self.exprs.is_empty() && self.dyn_filters.load().is_empty()
98 }
99
100 pub fn add_dyn_filters(&self, dyn_filters: Vec<Arc<DynamicFilterPhysicalExpr>>) {
102 self.dyn_filters.rcu(|existing| {
103 let mut new_filters = existing.as_ref().clone();
104 new_filters.extend(dyn_filters.clone());
105 Arc::new(new_filters)
106 });
107 }
108
109 pub fn exprs(&self) -> &[Expr] {
111 &self.exprs
112 }
113
114 pub fn dyn_filters(&self) -> Arc<Vec<Arc<DynamicFilterPhysicalExpr>>> {
117 self.dyn_filters.load_full()
118 }
119
120 pub fn dyn_filter_phy_exprs(&self) -> error::Result<Vec<Arc<dyn PhysicalExpr>>> {
123 self.dyn_filters
124 .load()
125 .iter()
126 .map(|e| e.current())
127 .collect::<Result<Vec<_>, _>>()
128 .context(error::DatafusionSnafu)
129 }
130
131 pub fn to_physical_expr(
133 expr: &Expr,
134 schema: &arrow::datatypes::SchemaRef,
135 ) -> error::Result<Arc<dyn PhysicalExpr>> {
136 let df_schema = schema
137 .clone()
138 .to_dfschema_ref()
139 .context(error::DatafusionSnafu)?;
140
141 let execution_props = &ExecutionProps::new();
145
146 create_physical_expr(expr, df_schema.as_ref(), execution_props)
147 .context(error::DatafusionSnafu)
148 }
149
150 pub fn to_physical_exprs(
152 &self,
153 schema: &arrow::datatypes::SchemaRef,
154 ) -> error::Result<Vec<Arc<dyn PhysicalExpr>>> {
155 let dyn_filters = self.dyn_filter_phy_exprs()?;
156
157 Ok(self
158 .exprs
159 .iter()
160 .filter_map(|expr| Self::to_physical_expr(expr, schema).ok())
161 .chain(dyn_filters)
162 .collect::<Vec<_>>())
163 }
164
165 pub fn prune_with_stats<S: PruningStatistics>(
168 &self,
169 stats: &S,
170 schema: &arrow::datatypes::SchemaRef,
171 ) -> Vec<bool> {
172 let mut res = vec![true; stats.num_containers()];
173 let physical_exprs = match self.to_physical_exprs(schema) {
174 Ok(expr) => expr,
175 Err(e) => {
176 warn!(e; "Failed to build physical expr from predicates: {:?}", &self.exprs);
177 return res;
178 }
179 };
180
181 for expr in &physical_exprs {
182 match PruningPredicate::try_new(expr.clone(), schema.clone()) {
183 Ok(p) => match p.prune(stats) {
184 Ok(r) => {
185 for (curr_val, res) in r.into_iter().zip(res.iter_mut()) {
186 *res &= curr_val
187 }
188 }
189 Err(e) => {
190 warn!(e; "Failed to prune row groups");
191 }
192 },
193 Err(e) => {
194 debug!("Failed to create pruning predicate for expr: {e:?}");
196 }
197 }
198 }
199 res
200 }
201}
202
203pub fn build_time_range_predicate(
208 ts_col_name: &str,
209 ts_col_unit: TimeUnit,
210 filters: &[Expr],
211) -> TimestampRange {
212 let mut res = TimestampRange::min_to_max();
213 for expr in filters {
214 if let Some(range) = extract_time_range_from_expr(ts_col_name, ts_col_unit, expr) {
215 res = res.and(&range);
216 }
217 }
218 res
219}
220
221#[derive(Debug, Clone, PartialEq, Eq)]
228pub enum TimeRangeExtraction {
229 Absent,
231 Extracted(TimestampRange),
234 Unsupported,
237}
238
239pub fn extract_time_range_strict(
242 ts_col_name: &str,
243 ts_col_unit: TimeUnit,
244 filters: &[Expr],
245) -> TimeRangeExtraction {
246 let mut range: Option<TimestampRange> = None;
247 for expr in filters {
248 if !expr
249 .column_refs()
250 .iter()
251 .any(|column| column.name == ts_col_name)
252 {
253 continue;
254 }
255 if contains_disjunction_over_column(expr, ts_col_name) {
260 return TimeRangeExtraction::Unsupported;
261 }
262 match extract_time_range_from_expr(ts_col_name, ts_col_unit, expr) {
267 Some(extracted) => {
268 range = Some(match range {
269 Some(acc) => acc.and(&extracted),
270 None => extracted,
271 });
272 }
273 None => return TimeRangeExtraction::Unsupported,
274 }
275 }
276 match range {
277 Some(range) => TimeRangeExtraction::Extracted(range),
278 None => TimeRangeExtraction::Absent,
279 }
280}
281
282fn contains_disjunction_over_column(expr: &Expr, ts_col_name: &str) -> bool {
283 let is_disjunction = |expr: &Expr| {
284 matches!(
285 expr,
286 Expr::BinaryExpr(BinaryExpr {
287 op: Operator::Or,
288 ..
289 }) | Expr::InList(_)
290 ) && expr
291 .column_refs()
292 .iter()
293 .any(|column| column.name == ts_col_name)
294 };
295 expr.exists(|expr| Ok(is_disjunction(expr))).unwrap_or(true)
296}
297
298pub fn extract_time_range_from_expr(
301 ts_col_name: &str,
302 ts_col_unit: TimeUnit,
303 expr: &Expr,
304) -> Option<TimestampRange> {
305 match expr {
306 Expr::BinaryExpr(BinaryExpr { left, op, right }) => {
307 extract_from_binary_expr(ts_col_name, ts_col_unit, left, op, right)
308 }
309 Expr::Between(Between {
310 expr,
311 negated,
312 low,
313 high,
314 }) => extract_from_between_expr(ts_col_name, ts_col_unit, expr, negated, low, high),
315 Expr::InList(InList {
316 expr,
317 list,
318 negated,
319 }) => extract_from_in_list_expr(ts_col_name, expr, *negated, list),
320 _ => None,
321 }
322}
323
324fn extract_from_binary_expr(
325 ts_col_name: &str,
326 ts_col_unit: TimeUnit,
327 left: &Expr,
328 op: &Operator,
329 right: &Expr,
330) -> Option<TimestampRange> {
331 match op {
332 Operator::Eq => get_timestamp_filter(ts_col_name, left, right)
333 .and_then(|(ts, _)| ts.convert_to(ts_col_unit))
334 .map(TimestampRange::single),
335 Operator::Lt => {
336 let (ts, reverse) = get_timestamp_filter(ts_col_name, left, right)?;
337 if reverse {
338 let ts_val = ts.convert_to(ts_col_unit)?.value();
340 Some(TimestampRange::from_start(Timestamp::new(
341 ts_val + 1,
342 ts_col_unit,
343 )))
344 } else {
345 ts.convert_to_ceil(ts_col_unit)
347 .map(|ts| TimestampRange::until_end(ts, false))
348 }
349 }
350 Operator::LtEq => {
351 let (ts, reverse) = get_timestamp_filter(ts_col_name, left, right)?;
352 if reverse {
353 ts.convert_to_ceil(ts_col_unit)
355 .map(TimestampRange::from_start)
356 } else {
357 ts.convert_to(ts_col_unit)
359 .map(|ts| TimestampRange::until_end(ts, true))
360 }
361 }
362 Operator::Gt => {
363 let (ts, reverse) = get_timestamp_filter(ts_col_name, left, right)?;
364 if reverse {
365 ts.convert_to_ceil(ts_col_unit)
367 .map(|t| TimestampRange::until_end(t, false))
368 } else {
369 let ts_val = ts.convert_to(ts_col_unit)?.value();
371 Some(TimestampRange::from_start(Timestamp::new(
372 ts_val + 1,
373 ts_col_unit,
374 )))
375 }
376 }
377 Operator::GtEq => {
378 let (ts, reverse) = get_timestamp_filter(ts_col_name, left, right)?;
379 if reverse {
380 ts.convert_to(ts_col_unit)
382 .map(|t| TimestampRange::until_end(t, true))
383 } else {
384 ts.convert_to_ceil(ts_col_unit)
386 .map(TimestampRange::from_start)
387 }
388 }
389 Operator::And => {
390 let left = extract_time_range_from_expr(ts_col_name, ts_col_unit, left)
393 .unwrap_or_else(TimestampRange::min_to_max);
394 let right = extract_time_range_from_expr(ts_col_name, ts_col_unit, right)
395 .unwrap_or_else(TimestampRange::min_to_max);
396 Some(left.and(&right))
397 }
398 Operator::Or => {
399 let left = extract_time_range_from_expr(ts_col_name, ts_col_unit, left)?;
400 let right = extract_time_range_from_expr(ts_col_name, ts_col_unit, right)?;
401 Some(left.or(&right))
402 }
403 _ => None,
404 }
405}
406
407fn get_timestamp_filter(ts_col_name: &str, left: &Expr, right: &Expr) -> Option<(Timestamp, bool)> {
408 let (col, lit, reverse) = match (left, right) {
409 (Expr::Column(column), Expr::Literal(scalar, _)) => (column, scalar, false),
410 (Expr::Literal(scalar, _), Expr::Column(column)) => (column, scalar, true),
411 _ => {
412 return None;
413 }
414 };
415 if col.name != ts_col_name {
416 return None;
417 }
418
419 return_none_if_utf8!(lit);
420 scalar_value_to_timestamp(lit, None).map(|t| (t, reverse))
421}
422
423fn extract_from_between_expr(
424 ts_col_name: &str,
425 ts_col_unit: TimeUnit,
426 expr: &Expr,
427 negated: &bool,
428 low: &Expr,
429 high: &Expr,
430) -> Option<TimestampRange> {
431 let Expr::Column(col) = expr else {
432 return None;
433 };
434 if col.name != ts_col_name {
435 return None;
436 }
437
438 if *negated {
439 return None;
440 }
441
442 match (low, high) {
443 (Expr::Literal(low, _), Expr::Literal(high, _)) => {
444 return_none_if_utf8!(low);
445 return_none_if_utf8!(high);
446
447 let low_opt =
448 scalar_value_to_timestamp(low, None).and_then(|ts| ts.convert_to(ts_col_unit));
449 let high_opt = scalar_value_to_timestamp(high, None)
450 .and_then(|ts| ts.convert_to_ceil(ts_col_unit));
451 Some(TimestampRange::new_inclusive(low_opt, high_opt))
452 }
453 _ => None,
454 }
455}
456
457fn extract_from_in_list_expr(
459 ts_col_name: &str,
460 expr: &Expr,
461 negated: bool,
462 list: &[Expr],
463) -> Option<TimestampRange> {
464 if negated {
465 return None;
466 }
467 let Expr::Column(col) = expr else {
468 return None;
469 };
470 if col.name != ts_col_name {
471 return None;
472 }
473
474 if list.is_empty() {
475 return Some(TimestampRange::empty());
476 }
477 let mut init_range = TimestampRange::empty();
478 for expr in list {
479 if let Expr::Literal(scalar, _) = expr {
480 return_none_if_utf8!(scalar);
481 if let Some(timestamp) = scalar_value_to_timestamp(scalar, None) {
482 init_range = init_range.or(&TimestampRange::single(timestamp))
483 } else {
484 return None;
487 }
488 }
489 }
490 Some(init_range)
491}
492
493#[cfg(test)]
494mod tests {
495 use std::sync::Arc;
496
497 use common_test_util::temp_dir::{TempDir, create_temp_dir};
498 use datafusion::parquet::arrow::ArrowWriter;
499 use datafusion_common::{Column, ScalarValue};
500 use datafusion_expr::{BinaryExpr, Literal, Operator, col, lit};
501 use datatypes::arrow::array::Int32Array;
502 use datatypes::arrow::datatypes::{DataType, Field, Schema};
503 use datatypes::arrow::record_batch::RecordBatch;
504 use datatypes::arrow_array::StringArray;
505 use parquet::arrow::ParquetRecordBatchStreamBuilder;
506 use parquet::file::properties::WriterProperties;
507
508 use super::*;
509 use crate::predicate::stats::RowGroupPruningStatistics;
510
511 fn check_build_predicate(expr: Expr, expect: TimestampRange) {
512 assert_eq!(
513 expect,
514 build_time_range_predicate("ts", TimeUnit::Millisecond, &[expr])
515 );
516 }
517
518 #[test]
519 fn test_gt() {
520 check_build_predicate(
522 col("ts").gt(lit(ScalarValue::TimestampMillisecond(Some(1), None))),
523 TimestampRange::from_start(Timestamp::new_millisecond(2)),
524 );
525
526 check_build_predicate(
528 lit(ScalarValue::TimestampMillisecond(Some(1), None)).gt(col("ts")),
529 TimestampRange::until_end(Timestamp::new_millisecond(1), false),
530 );
531
532 check_build_predicate(
534 lit(ScalarValue::TimestampMicrosecond(Some(1001), None)).gt(col("ts")),
535 TimestampRange::until_end(Timestamp::new_millisecond(1), true),
536 );
537
538 check_build_predicate(
540 col("ts").gt(lit(ScalarValue::TimestampMicrosecond(Some(1001), None))),
541 TimestampRange::from_start(Timestamp::new_millisecond(2)),
542 );
543
544 check_build_predicate(
546 lit(ScalarValue::TimestampSecond(Some(1), None)).gt(col("ts")),
547 TimestampRange::until_end(Timestamp::new_millisecond(1000), false),
548 );
549
550 check_build_predicate(
552 col("ts").gt(lit(ScalarValue::TimestampSecond(Some(1), None))),
553 TimestampRange::from_start(Timestamp::new_millisecond(1001)),
554 );
555 }
556
557 #[test]
558 fn test_gt_eq() {
559 check_build_predicate(
561 col("ts").gt_eq(lit(ScalarValue::TimestampMillisecond(Some(1), None))),
562 TimestampRange::from_start(Timestamp::new_millisecond(1)),
563 );
564
565 check_build_predicate(
567 lit(ScalarValue::TimestampMillisecond(Some(1), None)).gt_eq(col("ts")),
568 TimestampRange::until_end(Timestamp::new_millisecond(1), true),
569 );
570
571 check_build_predicate(
573 lit(ScalarValue::TimestampMicrosecond(Some(1001), None)).gt_eq(col("ts")),
574 TimestampRange::until_end(Timestamp::new_millisecond(1), true),
575 );
576
577 check_build_predicate(
579 col("ts").gt_eq(lit(ScalarValue::TimestampMicrosecond(Some(1001), None))),
580 TimestampRange::from_start(Timestamp::new_millisecond(2)),
581 );
582
583 check_build_predicate(
585 lit(ScalarValue::TimestampSecond(Some(1), None)).gt_eq(col("ts")),
586 TimestampRange::until_end(Timestamp::new_millisecond(1000), true),
587 );
588
589 check_build_predicate(
591 col("ts").gt_eq(lit(ScalarValue::TimestampSecond(Some(1), None))),
592 TimestampRange::from_start(Timestamp::new_millisecond(1000)),
593 );
594 }
595
596 #[test]
597 fn test_lt() {
598 check_build_predicate(
600 col("ts").lt(lit(ScalarValue::TimestampMillisecond(Some(1), None))),
601 TimestampRange::until_end(Timestamp::new_millisecond(1), false),
602 );
603
604 check_build_predicate(
606 lit(ScalarValue::TimestampMillisecond(Some(1), None)).lt(col("ts")),
607 TimestampRange::from_start(Timestamp::new_millisecond(2)),
608 );
609
610 check_build_predicate(
612 lit(ScalarValue::TimestampMicrosecond(Some(1001), None)).lt(col("ts")),
613 TimestampRange::from_start(Timestamp::new_millisecond(2)),
614 );
615
616 check_build_predicate(
618 col("ts").lt(lit(ScalarValue::TimestampMicrosecond(Some(1001), None))),
619 TimestampRange::until_end(Timestamp::new_millisecond(1), true),
620 );
621
622 check_build_predicate(
624 lit(ScalarValue::TimestampSecond(Some(1), None)).lt(col("ts")),
625 TimestampRange::from_start(Timestamp::new_millisecond(1001)),
626 );
627
628 check_build_predicate(
630 col("ts").lt(lit(ScalarValue::TimestampSecond(Some(1), None))),
631 TimestampRange::until_end(Timestamp::new_millisecond(1000), false),
632 );
633 }
634
635 #[test]
636 fn test_lt_eq() {
637 check_build_predicate(
639 col("ts").lt_eq(lit(ScalarValue::TimestampMillisecond(Some(1), None))),
640 TimestampRange::until_end(Timestamp::new_millisecond(1), true),
641 );
642
643 check_build_predicate(
645 lit(ScalarValue::TimestampMillisecond(Some(1), None)).lt_eq(col("ts")),
646 TimestampRange::from_start(Timestamp::new_millisecond(1)),
647 );
648
649 check_build_predicate(
651 lit(ScalarValue::TimestampMicrosecond(Some(1001), None)).lt_eq(col("ts")),
652 TimestampRange::from_start(Timestamp::new_millisecond(2)),
653 );
654
655 check_build_predicate(
657 col("ts").lt_eq(lit(ScalarValue::TimestampMicrosecond(Some(1001), None))),
658 TimestampRange::until_end(Timestamp::new_millisecond(1), true),
659 );
660
661 check_build_predicate(
663 lit(ScalarValue::TimestampSecond(Some(1), None)).lt_eq(col("ts")),
664 TimestampRange::from_start(Timestamp::new_millisecond(1000)),
665 );
666
667 check_build_predicate(
669 col("ts").lt_eq(lit(ScalarValue::TimestampSecond(Some(1), None))),
670 TimestampRange::until_end(Timestamp::new_millisecond(1000), true),
671 );
672 }
673
674 #[test]
675 fn test_extract_time_range_strict() {
676 fn ts_lit(ms: i64) -> Expr {
677 lit(ScalarValue::TimestampMillisecond(Some(ms), None))
678 }
679 let extract =
680 |filters: &[Expr]| extract_time_range_strict("ts", TimeUnit::Millisecond, filters);
681 let range = |start: i64, end: i64| {
682 TimestampRange::new(
683 Timestamp::new_millisecond(start),
684 Timestamp::new_millisecond(end),
685 )
686 .unwrap()
687 };
688
689 assert_eq!(extract(&[]), TimeRangeExtraction::Absent);
691 assert_eq!(
692 extract(&[col("host").eq(lit("a"))]),
693 TimeRangeExtraction::Absent
694 );
695
696 assert_eq!(
698 extract(&[
699 col("ts").gt_eq(ts_lit(1000)),
700 col("ts").lt(ts_lit(2000)),
701 col("host").eq(lit("a")),
702 ]),
703 TimeRangeExtraction::Extracted(range(1000, 2000))
704 );
705
706 assert_eq!(
708 extract(&[col("ts").gt_eq(ts_lit(1000))]),
709 TimeRangeExtraction::Extracted(TimestampRange::from_start(Timestamp::new_millisecond(
710 1000
711 )))
712 );
713 assert_eq!(
714 extract(&[col("ts").lt(ts_lit(2000))]),
715 TimeRangeExtraction::Extracted(TimestampRange::until_end(
716 Timestamp::new_millisecond(2000),
717 false
718 ))
719 );
720
721 assert_eq!(
723 extract(&[col("ts").between(ts_lit(1000), ts_lit(2000))]),
724 TimeRangeExtraction::Extracted(range(1000, 2001))
725 );
726
727 assert_eq!(
729 extract(&[col("ts").eq(ts_lit(1500))]),
730 TimeRangeExtraction::Extracted(TimestampRange::single(Timestamp::new_millisecond(
731 1500
732 )))
733 );
734
735 assert_eq!(
737 extract(&[col("ts").gt_eq(ts_lit(1000)).and(col("ts").lt(col("t2")))]),
738 TimeRangeExtraction::Extracted(TimestampRange::from_start(Timestamp::new_millisecond(
739 1000
740 )))
741 );
742
743 let TimeRangeExtraction::Extracted(empty) =
745 extract(&[col("ts").gt_eq(ts_lit(2000)), col("ts").lt(ts_lit(1000))])
746 else {
747 panic!("expected an extraction");
748 };
749 assert!(empty.is_empty());
750
751 assert_eq!(
754 extract(&[col("ts").gt(ts_lit(1000)).or(col("host").eq(lit("a")))]),
755 TimeRangeExtraction::Unsupported
756 );
757 assert_eq!(
758 extract(&[!col("ts").gt(ts_lit(1000))]),
759 TimeRangeExtraction::Unsupported
760 );
761 assert_eq!(
762 extract(&[col("ts").gt_eq(col("t2"))]),
763 TimeRangeExtraction::Unsupported
764 );
765 }
766
767 async fn gen_test_parquet_file(dir: &TempDir, cnt: usize) -> (String, Arc<Schema>) {
768 let path = dir
769 .path()
770 .join("test-prune.parquet")
771 .to_string_lossy()
772 .to_string();
773
774 let name_field = Field::new("name", DataType::Utf8, true);
775 let count_field = Field::new("cnt", DataType::Int32, true);
776 let schema = Arc::new(Schema::new(vec![name_field, count_field]));
777
778 let file = std::fs::OpenOptions::new()
779 .write(true)
780 .create(true)
781 .truncate(true)
782 .open(path.clone())
783 .unwrap();
784
785 let write_props = WriterProperties::builder()
786 .set_max_row_group_row_count(Some(10))
787 .build();
788 let mut writer = ArrowWriter::try_new(file, schema.clone(), Some(write_props)).unwrap();
789
790 for i in (0..cnt).step_by(10) {
791 let name_array = Arc::new(StringArray::from(
792 (i..(i + 10).min(cnt))
793 .map(|i| i.to_string())
794 .collect::<Vec<_>>(),
795 )) as Arc<_>;
796 let count_array = Arc::new(Int32Array::from(
797 (i..(i + 10).min(cnt)).map(|i| i as i32).collect::<Vec<_>>(),
798 )) as Arc<_>;
799 let rb = RecordBatch::try_new(schema.clone(), vec![name_array, count_array]).unwrap();
800 writer.write(&rb).unwrap();
801 }
802 let _ = writer.close().unwrap();
803 (path, schema)
804 }
805
806 async fn assert_prune(array_cnt: usize, filters: Vec<Expr>, expect: Vec<bool>) {
807 let dir = create_temp_dir("prune_parquet");
808 let (path, arrow_schema) = gen_test_parquet_file(&dir, array_cnt).await;
809 let schema = Arc::new(datatypes::schema::Schema::try_from(arrow_schema.clone()).unwrap());
810 let arrow_predicate = Predicate::new(filters);
811 let builder = ParquetRecordBatchStreamBuilder::new(
812 tokio::fs::OpenOptions::new()
813 .read(true)
814 .open(path)
815 .await
816 .unwrap(),
817 )
818 .await
819 .unwrap();
820 let metadata = builder.metadata().clone();
821 let row_groups = metadata.row_groups();
822
823 let stats = RowGroupPruningStatistics::new(row_groups, &schema);
824 let res = arrow_predicate.prune_with_stats(&stats, &arrow_schema);
825 assert_eq!(expect, res);
826 }
827
828 fn gen_predicate(max_val: i32, op: Operator) -> Vec<Expr> {
829 vec![datafusion_expr::Expr::BinaryExpr(BinaryExpr {
830 left: Box::new(datafusion_expr::Expr::Column(Column::from_name("cnt"))),
831 op,
832 right: Box::new(max_val.lit()),
833 })]
834 }
835
836 #[tokio::test]
837 async fn test_prune_empty() {
838 assert_prune(3, vec![], vec![true]).await;
839 }
840
841 #[tokio::test]
842 async fn test_prune_all_match() {
843 let p = gen_predicate(3, Operator::Gt);
844 assert_prune(2, p, vec![false]).await;
845 }
846
847 #[tokio::test]
848 async fn test_prune_gt() {
849 let p = gen_predicate(29, Operator::Gt);
850 assert_prune(
851 100,
852 p,
853 vec![
854 false, false, false, true, true, true, true, true, true, true,
855 ],
856 )
857 .await;
858 }
859
860 #[tokio::test]
861 async fn test_prune_eq_expr() {
862 let p = gen_predicate(30, Operator::Eq);
863 assert_prune(40, p, vec![false, false, false, true]).await;
864 }
865
866 #[tokio::test]
867 async fn test_prune_neq_expr() {
868 let p = gen_predicate(30, Operator::NotEq);
869 assert_prune(40, p, vec![true, true, true, true]).await;
870 }
871
872 #[tokio::test]
873 async fn test_prune_gteq_expr() {
874 let p = gen_predicate(29, Operator::GtEq);
875 assert_prune(40, p, vec![false, false, true, true]).await;
876 }
877
878 #[tokio::test]
879 async fn test_prune_lt_expr() {
880 let p = gen_predicate(30, Operator::Lt);
881 assert_prune(40, p, vec![true, true, true, false]).await;
882 }
883
884 #[tokio::test]
885 async fn test_prune_lteq_expr() {
886 let p = gen_predicate(30, Operator::LtEq);
887 assert_prune(40, p, vec![true, true, true, true]).await;
888 }
889
890 #[tokio::test]
891 async fn test_prune_between_expr() {
892 let p = gen_predicate(30, Operator::LtEq);
893 assert_prune(40, p, vec![true, true, true, true]).await;
894 }
895
896 #[tokio::test]
897 async fn test_or() {
898 let e = datafusion_expr::Expr::Column(Column::from_name("cnt"))
900 .gt(30.lit())
901 .or(datafusion_expr::Expr::Column(Column::from_name("cnt")).lt(20.lit()));
902 assert_prune(40, vec![e], vec![true, true, false, true]).await;
903 }
904
905 #[tokio::test]
906 async fn test_to_physical_expr() {
907 let predicate = Predicate::new(vec![
908 col("host").eq(lit("host_a")),
909 col("ts").gt(lit(ScalarValue::TimestampMicrosecond(Some(123), None))),
910 ]);
911
912 let schema = Arc::new(arrow::datatypes::Schema::new(vec![Field::new(
913 "host",
914 arrow::datatypes::DataType::Utf8,
915 false,
916 )]));
917
918 let predicates = predicate.to_physical_exprs(&schema).unwrap();
919 assert!(!predicates.is_empty());
920
921 let physical_expr = Predicate::to_physical_expr(&col("host").eq(lit("host_a")), &schema);
922 assert!(physical_expr.is_ok());
923 }
924}