1use std::collections::HashSet;
22use std::ops::{BitAnd, Range};
23use std::sync::Arc;
24
25use api::v1::SemanticType;
26use common_recordbatch::filter::SimpleFilterEvaluator;
27use datafusion_common::ScalarValue;
28use datafusion_expr::Expr;
29use datatypes::arrow::array::{Array, BinaryArray, BooleanArray, BooleanBufferBuilder};
30use datatypes::arrow::buffer::BooleanBuffer;
31use datatypes::arrow::datatypes::SchemaRef;
32use datatypes::arrow::record_batch::RecordBatch;
33use datatypes::prelude::ConcreteDataType;
34use datatypes::value::Value;
35use futures::StreamExt;
36use mito_codec::row_converter::{PrimaryKeyCodec, PrimaryKeyFilter, build_primary_key_codec};
37use parquet::arrow::ProjectionMask;
38use parquet::arrow::arrow_reader::{RowSelection, RowSelector};
39use parquet::file::metadata::ParquetMetaData;
40use parquet::schema::types::SchemaDescriptor;
41use smallvec::{SmallVec, smallvec};
42use snafu::{OptionExt, ResultExt};
43use store_api::metadata::{RegionMetadata, RegionMetadataRef};
44use store_api::storage::consts::PRIMARY_KEY_COLUMN_NAME;
45use table::predicate::Predicate;
46
47use crate::cache::PrefilterKey;
48use crate::error::{
49 ComputeArrowSnafu, DecodeSnafu, EvalPartitionFilterSnafu, NewRecordBatchSnafu,
50 RecordBatchSnafu, Result, UnexpectedSnafu,
51};
52use crate::sst::parquet::file_range::PreFilterMode;
53use crate::sst::parquet::flat_format::FlatReadFormat;
54use crate::sst::parquet::format::{PrimaryKeyArray, StatValues};
55use crate::sst::parquet::reader::{
56 MaybeFilter, PhysicalFilterContext, RowGroupBuildContext, RowGroupReaderBuilder,
57 SimpleFilterContext,
58};
59
60pub(crate) fn matching_row_ranges_by_primary_key(
61 input: &RecordBatch,
62 pk_column_index: usize,
63 pk_filter: &mut dyn PrimaryKeyFilter,
64) -> Result<Vec<Range<usize>>> {
65 let pk_column = input.column(pk_column_index);
66 if let Some(pk_dict_array) = pk_column.as_any().downcast_ref::<PrimaryKeyArray>() {
67 matching_row_ranges_from_dict(pk_dict_array, input.num_rows(), pk_filter)
68 } else if let Some(pk_binary_array) = pk_column.as_any().downcast_ref::<BinaryArray>() {
69 matching_row_ranges_from_binary(pk_binary_array, input.num_rows(), pk_filter)
70 } else {
71 UnexpectedSnafu {
72 reason: format!(
73 "Primary key column is neither a dictionary nor a binary array, got {:?}",
74 pk_column.data_type()
75 ),
76 }
77 .fail()
78 }
79}
80
81fn push_matched_range(
84 matched_row_ranges: &mut Vec<Range<usize>>,
85 pk_filter: &mut dyn PrimaryKeyFilter,
86 pk: &[u8],
87 start: usize,
88 end: usize,
89) -> Result<()> {
90 if pk_filter.matches(pk).context(DecodeSnafu)? {
91 if let Some(last) = matched_row_ranges.last_mut()
92 && last.end == start
93 {
94 last.end = end;
95 } else {
96 matched_row_ranges.push(start..end);
97 }
98 }
99 Ok(())
100}
101
102fn matching_row_ranges_from_dict(
104 pk_dict_array: &PrimaryKeyArray,
105 num_rows: usize,
106 pk_filter: &mut dyn PrimaryKeyFilter,
107) -> Result<Vec<Range<usize>>> {
108 let pk_values = pk_dict_array
109 .values()
110 .as_any()
111 .downcast_ref::<BinaryArray>()
112 .context(UnexpectedSnafu {
113 reason: "Primary key values are not binary array",
114 })?;
115 let keys = pk_dict_array.keys();
116 let key_values = keys.values();
117
118 if key_values.is_empty() {
119 return Ok(std::iter::once(0..num_rows).collect());
120 }
121
122 let mut matched_row_ranges: Vec<Range<usize>> = Vec::new();
123 let mut start = 0;
124 while start < key_values.len() {
125 let key = key_values[start];
126 let mut end = start + 1;
127 while end < key_values.len() && key_values[end] == key {
128 end += 1;
129 }
130
131 push_matched_range(
132 &mut matched_row_ranges,
133 pk_filter,
134 pk_values.value(key as usize),
135 start,
136 end,
137 )?;
138
139 start = end;
140 }
141
142 Ok(matched_row_ranges)
143}
144
145fn matching_row_ranges_from_binary(
151 pk_array: &BinaryArray,
152 num_rows: usize,
153 pk_filter: &mut dyn PrimaryKeyFilter,
154) -> Result<Vec<Range<usize>>> {
155 if pk_array.is_empty() {
156 return Ok(std::iter::once(0..num_rows).collect());
157 }
158
159 let mut matched_row_ranges: Vec<Range<usize>> = Vec::new();
160 let mut start = 0;
161 while start < pk_array.len() {
162 let value = pk_array.value(start);
163 let mut end = start + 1;
164 while end < pk_array.len() && pk_array.value(end) == value {
165 end += 1;
166 }
167
168 push_matched_range(&mut matched_row_ranges, pk_filter, value, start, end)?;
169
170 start = end;
171 }
172
173 Ok(matched_row_ranges)
174}
175
176pub(crate) fn prefilter_flat_batch_by_primary_key(
179 input: RecordBatch,
180 pk_column_index: usize,
181 pk_filter: &mut dyn PrimaryKeyFilter,
182) -> Result<Option<RecordBatch>> {
183 if input.num_rows() == 0 {
184 return Ok(Some(input));
185 }
186
187 let matched_row_ranges =
188 matching_row_ranges_by_primary_key(&input, pk_column_index, pk_filter)?;
189 if matched_row_ranges.is_empty() {
190 return Ok(None);
191 }
192
193 if matched_row_ranges.len() == 1
194 && matched_row_ranges[0].start == 0
195 && matched_row_ranges[0].end == input.num_rows()
196 {
197 return Ok(Some(input));
198 }
199
200 if matched_row_ranges.len() == 1 {
201 let span = &matched_row_ranges[0];
202 return Ok(Some(input.slice(span.start, span.end - span.start)));
203 }
204
205 let mut builder = BooleanBufferBuilder::new(input.num_rows());
206 builder.append_n(input.num_rows(), false);
207 for span in matched_row_ranges {
208 for i in span {
209 builder.set_bit(i, true);
210 }
211 }
212
213 let filtered = datatypes::arrow::compute::filter_record_batch(
214 &input,
215 &BooleanArray::new(builder.finish(), None),
216 )
217 .context(ComputeArrowSnafu)?;
218 if filtered.num_rows() == 0 {
219 Ok(None)
220 } else {
221 Ok(Some(filtered))
222 }
223}
224
225pub(crate) struct CachedPrimaryKeyFilter {
226 inner: Box<dyn PrimaryKeyFilter>,
227 last_primary_key: Vec<u8>,
228 last_match: Option<bool>,
229}
230
231impl CachedPrimaryKeyFilter {
232 pub(crate) fn new(inner: Box<dyn PrimaryKeyFilter>) -> Self {
233 Self {
234 inner,
235 last_primary_key: Vec::new(),
236 last_match: None,
237 }
238 }
239}
240
241impl PrimaryKeyFilter for CachedPrimaryKeyFilter {
242 fn matches(&mut self, pk: &[u8]) -> mito_codec::error::Result<bool> {
243 if let Some(last_match) = self.last_match
244 && self.last_primary_key == pk
245 {
246 return Ok(last_match);
247 }
248
249 let matched = self.inner.matches(pk)?;
250 self.last_primary_key.clear();
251 self.last_primary_key.extend_from_slice(pk);
252 self.last_match = Some(matched);
253 Ok(matched)
254 }
255}
256
257pub(crate) struct BulkFilterPlan {
263 pub(crate) remaining_simple_filters: Vec<SimpleFilterContext>,
265 pub(crate) pk_filters: Option<Arc<Vec<SimpleFilterEvaluator>>>,
268}
269
270pub(crate) fn build_primary_key_filter(
276 sst_metadata: &RegionMetadataRef,
277 expected_metadata: Option<&RegionMetadata>,
278 predicate: Option<&Predicate>,
279) -> Option<CachedPrimaryKeyFilter> {
280 let filters = simple_tag_filters(sst_metadata, expected_metadata, predicate)
281 .into_iter()
282 .map(|(_, filter)| filter)
283 .collect::<Vec<_>>();
284 if filters.is_empty() {
285 return None;
286 }
287
288 let codec = build_primary_key_codec(sst_metadata.as_ref());
289 let filter = codec.primary_key_filter(sst_metadata, Arc::new(filters));
290 Some(CachedPrimaryKeyFilter::new(filter))
291}
292
293pub(crate) fn simple_tag_filters(
295 sst_metadata: &RegionMetadataRef,
296 expected_metadata: Option<&RegionMetadata>,
297 predicate: Option<&Predicate>,
298) -> Vec<(Expr, SimpleFilterEvaluator)> {
299 predicate
300 .into_iter()
301 .flat_map(|predicate| predicate.exprs())
302 .filter_map(|expr| {
303 SimpleFilterContext::new_opt(sst_metadata, expected_metadata, expr)
304 .map(|filter_ctx| (expr, filter_ctx))
305 })
306 .filter_map(|(expr, filter_ctx)| {
307 (filter_ctx.semantic_type() == SemanticType::Tag)
308 .then(|| {
309 filter_ctx
310 .filter()
311 .as_filter()
312 .cloned()
313 .map(|filter| (expr.clone(), filter))
314 })
315 .flatten()
316 })
317 .collect()
318}
319
320pub(crate) struct ReaderFilterPlan {
328 pub(crate) remaining_simple_filters: Vec<SimpleFilterContext>,
333 pub(crate) prefilter_builder: Option<PrefilterContextBuilder>,
337}
338
339pub(crate) fn build_bulk_filter_plan(
340 read_format: &FlatReadFormat,
341 predicate: Option<&Predicate>,
342) -> BulkFilterPlan {
343 let metadata = read_format.metadata();
344 let simple_filters: Vec<SimpleFilterContext> = predicate
346 .into_iter()
347 .flat_map(|predicate| {
348 predicate
349 .exprs()
350 .iter()
351 .filter_map(|expr| SimpleFilterContext::new_opt(metadata, None, expr))
352 })
353 .collect();
354
355 if read_format.batch_has_raw_pk_columns() || metadata.primary_key.is_empty() {
359 return BulkFilterPlan {
360 remaining_simple_filters: simple_filters,
361 pk_filters: None,
362 };
363 }
364
365 let mut remaining_simple_filters = Vec::new();
366 let mut pk_filters = Vec::new();
367
368 for filter_ctx in simple_filters {
369 let pk_filter = filter_ctx.filter().as_filter().and_then(|filter| {
372 (filter_ctx.semantic_type() == SemanticType::Tag).then(|| filter.clone())
373 });
374
375 if let Some(pk_filter) = pk_filter {
376 pk_filters.push(pk_filter);
377 } else {
378 remaining_simple_filters.push(filter_ctx);
379 }
380 }
381
382 BulkFilterPlan {
383 remaining_simple_filters,
384 pk_filters: (!pk_filters.is_empty()).then_some(Arc::new(pk_filters)),
385 }
386}
387
388#[allow(clippy::too_many_arguments)]
405pub(crate) fn build_reader_filter_plan(
406 predicate: Option<&Predicate>,
407 expected_metadata: Option<&RegionMetadata>,
408 pre_filter_mode: PreFilterMode,
409 enable_predicate_prefilter: bool,
410 postpone_time_index_filter: bool,
411 read_format: &FlatReadFormat,
412 codec: &Arc<dyn PrimaryKeyCodec>,
413 parquet_metadata: &ParquetMetaData,
414) -> ReaderFilterPlan {
415 let Some(predicate) = predicate else {
416 return ReaderFilterPlan {
417 remaining_simple_filters: Vec::new(),
418 prefilter_builder: None,
419 };
420 };
421
422 let metadata = read_format.metadata();
423 let mut prefilter_simple_filters = Vec::new();
424 let mut remaining_simple_filters = Vec::new();
425 let mut prefilter_physical_filters = Vec::new();
426 let mut primary_key_filters = Vec::new();
427 let mut pk_filter_contexts = Vec::new();
428
429 let field_prefilter_enabled = pre_filter_mode == PreFilterMode::All;
433 let need_pk_prefilter = !read_format.batch_has_raw_pk_columns();
437
438 let can_direct_prefilter = |semantic_type: SemanticType| -> bool {
441 match semantic_type {
442 SemanticType::Tag => !need_pk_prefilter,
443 SemanticType::Field => field_prefilter_enabled,
444 SemanticType::Timestamp => true,
445 }
446 };
447
448 for expr in predicate.exprs() {
449 if let Some(filter_ctx) = SimpleFilterContext::new_opt(metadata, expected_metadata, expr) {
452 if !enable_predicate_prefilter {
453 remaining_simple_filters.push(filter_ctx);
454 continue;
455 }
456
457 let Some(filter) = filter_ctx.filter().as_filter() else {
461 remaining_simple_filters.push(filter_ctx);
462 continue;
463 };
464
465 if postpone_time_index_filter && filter_ctx.semantic_type() == SemanticType::Timestamp {
466 remaining_simple_filters.push(filter_ctx);
467 continue;
468 }
469
470 let direct_prefilter = can_direct_prefilter(filter_ctx.semantic_type());
473 if direct_prefilter {
474 assert!(
475 read_format
476 .arrow_schema()
477 .column_with_name(filter.column_name())
478 .is_some(),
479 "Column '{}' is not present in the arrow schema {:?}",
480 filter.column_name(),
481 read_format.arrow_schema(),
482 );
483 prefilter_simple_filters.push(filter_ctx);
484 continue;
485 }
486
487 if need_pk_prefilter && filter_ctx.semantic_type() == SemanticType::Tag {
489 primary_key_filters.push(filter.clone());
490 pk_filter_contexts.push(filter_ctx);
491 } else {
492 remaining_simple_filters.push(filter_ctx);
493 }
494 continue;
495 }
496
497 if !enable_predicate_prefilter {
498 continue;
499 }
500
501 if let Some(filter) =
506 PhysicalFilterContext::new_opt(metadata, expected_metadata, read_format, expr)
507 && can_direct_prefilter(filter.semantic_type())
508 {
509 prefilter_physical_filters.push(filter);
510 }
511 }
512
513 if !enable_predicate_prefilter {
514 return ReaderFilterPlan {
515 remaining_simple_filters,
516 prefilter_builder: None,
517 };
518 }
519
520 let pk_filter_expr_strs = (!pk_filter_contexts.is_empty()).then(|| {
521 let mut expr_strs = pk_filter_contexts
522 .iter()
523 .map(|filter_ctx| filter_ctx.expr_str().to_string())
524 .collect::<Vec<_>>();
525 expr_strs.sort();
526 SmallVec::from_vec(expr_strs)
527 });
528 let pk_filter_exprs =
529 (!primary_key_filters.is_empty()).then_some(Arc::new(primary_key_filters));
530 let schema_version = expected_metadata
531 .map(|metadata| metadata.schema_version)
532 .unwrap_or_else(|| read_format.metadata().schema_version);
533 let prefilter_builder = PrefilterContextBuilder::new(
534 read_format,
535 codec,
536 pk_filter_exprs,
537 pk_filter_expr_strs,
538 prefilter_simple_filters.clone(),
539 prefilter_physical_filters,
540 schema_version,
541 parquet_metadata,
542 );
543
544 if prefilter_builder.is_some() {
545 ReaderFilterPlan {
546 remaining_simple_filters,
547 prefilter_builder,
548 }
549 } else {
550 remaining_simple_filters.extend(prefilter_simple_filters);
553 remaining_simple_filters.extend(pk_filter_contexts);
554 ReaderFilterPlan {
555 remaining_simple_filters,
556 prefilter_builder: None,
557 }
558 }
559}
560
561pub(crate) struct PrefilterContext {
563 pk_filter: Option<Box<dyn PrimaryKeyFilter>>,
565 filters: Vec<SimpleFilterContext>,
567 physical_filters: Vec<PhysicalFilterContext>,
570 schema_version: u64,
572 pk_filter_expr_strs: Option<SmallVec<[String; 1]>>,
574 arrow_schema: SchemaRef,
576 proven_simple_filters: Vec<bool>,
578}
579
580pub(crate) struct PrefilterContextBuilder {
586 pk_filters: Option<Arc<Vec<SimpleFilterEvaluator>>>,
587 pk_filter_expr_strs: Option<SmallVec<[String; 1]>>,
588 filters: Vec<SimpleFilterContext>,
589 physical_filters: Vec<PhysicalFilterContext>,
590 codec: Arc<dyn PrimaryKeyCodec>,
591 metadata: RegionMetadataRef,
592 schema_version: u64,
593 arrow_schema: SchemaRef,
594 proven_simple_filters: Vec<Vec<bool>>,
596}
597
598impl PrefilterContextBuilder {
599 #[allow(clippy::too_many_arguments)]
606 pub(crate) fn new(
607 read_format: &FlatReadFormat,
608 codec: &Arc<dyn PrimaryKeyCodec>,
609 primary_key_filters: Option<Arc<Vec<SimpleFilterEvaluator>>>,
610 primary_key_filter_expr_strs: Option<SmallVec<[String; 1]>>,
611 filters: Vec<SimpleFilterContext>,
612 physical_filters: Vec<PhysicalFilterContext>,
613 schema_version: u64,
614 parquet_metadata: &ParquetMetaData,
615 ) -> Option<Self> {
616 let metadata = read_format.metadata();
617 let use_raw_tag_columns = read_format.batch_has_raw_pk_columns();
618 let pk_filters = (!use_raw_tag_columns)
619 .then_some(primary_key_filters)
620 .flatten()
621 .filter(|filters| !filters.is_empty());
622 let pk_filter_expr_strs = pk_filters
623 .is_some()
624 .then_some(primary_key_filter_expr_strs)
625 .flatten();
626
627 let mut prefilter_column_names = HashSet::new();
628 for filter_ctx in &filters {
629 if let MaybeFilter::Filter(filter) = filter_ctx.filter() {
630 prefilter_column_names.insert(filter.column_name().to_string());
631 }
632 }
633
634 if pk_filters.is_some() {
635 prefilter_column_names.insert(PRIMARY_KEY_COLUMN_NAME.to_string());
636 }
637
638 for filter_ctx in &physical_filters {
639 prefilter_column_names.insert(filter_ctx.column_name().to_string());
640 }
641
642 let prefilter_count =
643 compute_projection_count(&prefilter_column_names, read_format.arrow_schema());
644
645 if prefilter_count == 0 {
646 return None;
647 }
648
649 let total_count = read_format.parquet_read_columns().root_indices().len();
650 let remaining_count = total_count.saturating_sub(prefilter_count);
651 if pk_filters.is_none() && prefilter_count >= total_count {
652 return None;
653 }
654
655 if pk_filters.is_none()
656 && !should_use_prefilter(prefilter_count, remaining_count, total_count)
657 {
658 return None;
659 }
660
661 let proven_simple_filters =
662 simple_filter_stats_proofs(read_format, parquet_metadata.row_groups(), &filters);
663
664 Some(Self {
665 pk_filters,
666 pk_filter_expr_strs,
667 filters,
668 physical_filters,
669 codec: Arc::clone(codec),
670 metadata: metadata.clone(),
671 schema_version,
672 arrow_schema: read_format.arrow_schema().clone(),
673 proven_simple_filters,
674 })
675 }
676
677 pub(crate) fn build(&self, row_group_idx: usize) -> PrefilterContext {
679 let pk_filter = self
680 .build_primary_key_filter()
681 .map(|filter| Box::new(filter) as Box<dyn PrimaryKeyFilter>);
682 PrefilterContext {
683 pk_filter,
684 filters: self.filters.clone(),
685 physical_filters: self.physical_filters.clone(),
686 schema_version: self.schema_version,
687 pk_filter_expr_strs: self.pk_filter_expr_strs.clone(),
688 arrow_schema: self.arrow_schema.clone(),
689 proven_simple_filters: self
690 .proven_simple_filters
691 .get(row_group_idx)
692 .cloned()
693 .unwrap_or_else(|| vec![false; self.filters.len()]),
694 }
695 }
696
697 pub(crate) fn build_primary_key_filter(&self) -> Option<CachedPrimaryKeyFilter> {
699 self.pk_filters.as_ref().map(|pk_filters| {
700 let filter = self
701 .codec
702 .primary_key_filter(&self.metadata, Arc::clone(pk_filters));
703 CachedPrimaryKeyFilter::new(filter)
704 })
705 }
706}
707
708const PREFILTER_COLUMN_RATIO_THRESHOLD: f64 = 0.5;
709const PREFILTER_MIN_REMAINING_COLUMNS: usize = 2;
710
711fn simple_filter_stats_proofs(
714 read_format: &FlatReadFormat,
715 row_groups: &[parquet::file::metadata::RowGroupMetaData],
716 filters: &[SimpleFilterContext],
717) -> Vec<Vec<bool>> {
718 let mut proofs = vec![vec![false; filters.len()]; row_groups.len()];
719 for (filter_idx, filter_ctx) in filters.iter().enumerate() {
720 let Some((filter, literal)) = eligible_simple_filter(read_format, filter_ctx) else {
721 continue;
722 };
723 let (StatValues::Values(mins), StatValues::Values(maxs), StatValues::Values(null_counts)) = (
724 read_format.min_values(row_groups, filter_ctx.column_id()),
725 read_format.max_values(row_groups, filter_ctx.column_id()),
726 read_format.null_counts(row_groups, filter_ctx.column_id()),
727 ) else {
728 continue;
729 };
730 for (row_group_idx, proof) in proofs.iter_mut().enumerate() {
731 proof[filter_idx] = simple_filter_is_true_by_values(
732 filter,
733 &literal,
734 stat_value_at(&mins, row_group_idx),
735 stat_value_at(&maxs, row_group_idx),
736 stat_value_at(&null_counts, row_group_idx),
737 );
738 }
739 }
740 proofs
741}
742
743fn eligible_simple_filter<'a>(
744 read_format: &FlatReadFormat,
745 filter_ctx: &'a SimpleFilterContext,
746) -> Option<(&'a SimpleFilterEvaluator, Value)> {
747 if filter_ctx.semantic_type() != SemanticType::Field {
748 return None;
749 }
750 let filter = filter_ctx.filter().as_filter()?;
751 let literal = filter.literal_value()?;
752 let column = read_format
753 .metadata()
754 .column_by_id(filter_ctx.column_id())?;
755 column_type_matches_literal(&column.column_schema.data_type, &literal)
756 .then_some((filter, literal))
757}
758
759fn simple_filter_is_true_by_values(
760 filter: &SimpleFilterEvaluator,
761 literal: &Value,
762 min: Option<Value>,
763 max: Option<Value>,
764 null_count: Option<Value>,
765) -> bool {
766 let (Some(min), Some(max), Some(null_count)) = (min, max, null_count) else {
767 return false;
768 };
769 if null_count != Value::UInt64(0)
770 || !same_supported_value_type(&min, literal)
771 || !same_supported_value_type(&max, literal)
772 || min > max
773 {
774 return false;
775 }
776
777 if filter.is_gt() {
778 min > *literal
779 } else if filter.is_gt_eq() {
780 min >= *literal
781 } else if filter.is_lt() {
782 max < *literal
783 } else if filter.is_lt_eq() {
784 max <= *literal
785 } else if filter.is_eq() {
786 min == *literal && max == *literal
787 } else if filter.is_not_eq() {
788 max < *literal || min > *literal
789 } else {
790 false
791 }
792}
793
794fn column_type_matches_literal(data_type: &ConcreteDataType, literal: &Value) -> bool {
795 matches!(
796 (data_type, literal),
797 (ConcreteDataType::Int32(_), Value::Int32(_))
798 | (ConcreteDataType::UInt32(_), Value::UInt32(_))
799 | (ConcreteDataType::Int64(_), Value::Int64(_))
800 | (ConcreteDataType::UInt64(_), Value::UInt64(_))
801 )
802}
803
804fn same_supported_value_type(left: &Value, right: &Value) -> bool {
805 matches!(
806 (left, right),
807 (Value::Int32(_), Value::Int32(_))
808 | (Value::UInt32(_), Value::UInt32(_))
809 | (Value::Int64(_), Value::Int64(_))
810 | (Value::UInt64(_), Value::UInt64(_))
811 )
812}
813
814fn stat_value_at(values: &datatypes::arrow::array::ArrayRef, index: usize) -> Option<Value> {
815 let scalar = ScalarValue::try_from_array(values, index).ok()?;
816 Value::try_from(scalar).ok()
817}
818
819pub(crate) struct PrefilterResult {
821 pub(crate) refined_selection: RowSelection,
823 pub(crate) filtered_rows: usize,
825}
826
827fn compute_projection_mask(
832 column_names: &HashSet<String>,
833 arrow_schema: &datatypes::arrow::datatypes::SchemaRef,
834 parquet_schema: &SchemaDescriptor,
835) -> ProjectionMask {
836 ProjectionMask::roots(
837 parquet_schema,
838 projection_indices(column_names, arrow_schema),
839 )
840}
841
842fn compute_projection_count(
843 column_names: &HashSet<String>,
844 arrow_schema: &datatypes::arrow::datatypes::SchemaRef,
845) -> usize {
846 projection_indices(column_names, arrow_schema).len()
847}
848
849fn projection_indices(
850 column_names: &HashSet<String>,
851 arrow_schema: &datatypes::arrow::datatypes::SchemaRef,
852) -> Vec<usize> {
853 let mut projection_indices: Vec<usize> = column_names
854 .iter()
855 .filter_map(|name| arrow_schema.column_with_name(name).map(|(index, _)| index))
856 .collect();
857 projection_indices.sort_unstable();
858 projection_indices.dedup();
859 projection_indices
860}
861
862fn should_use_prefilter(
863 prefilter_count: usize,
864 remaining_count: usize,
865 total_count: usize,
866) -> bool {
867 if remaining_count == 0 {
868 return false;
869 }
870
871 if remaining_count < PREFILTER_MIN_REMAINING_COLUMNS {
872 return false;
873 }
874
875 let ratio = prefilter_count as f64 / total_count as f64;
876 ratio <= PREFILTER_COLUMN_RATIO_THRESHOLD
877}
878
879pub(crate) async fn execute_prefilter(
880 prefilter_ctx: &mut PrefilterContext,
881 reader_builder: &RowGroupReaderBuilder,
882 build_ctx: &RowGroupBuildContext<'_>,
883) -> Result<PrefilterResult> {
884 let entries = build_prefilter_cache_entries(prefilter_ctx, reader_builder, build_ctx);
885
886 if entries.is_empty() {
887 return execute_prefilter_by_reading_columns(prefilter_ctx, reader_builder, build_ctx)
888 .await;
889 }
890
891 execute_prefilter_with_result_cache(prefilter_ctx, reader_builder, build_ctx, entries).await
892}
893
894async fn execute_prefilter_with_result_cache(
895 prefilter_ctx: &mut PrefilterContext,
896 reader_builder: &RowGroupReaderBuilder,
897 build_ctx: &RowGroupBuildContext<'_>,
898 entries: Vec<PrefilterEntry>,
899) -> Result<PrefilterResult> {
900 let non_cacheable_physical = non_cacheable_physical_filters(prefilter_ctx);
901 let mut hit_mask: Option<BooleanBuffer> = None;
902 let mut misses = Vec::new();
903 for entry in entries {
904 let Some(key) = &entry.key else {
905 misses.push(entry);
906 continue;
907 };
908
909 if let Some(mask) = reader_builder.cache_strategy().get_prefilter_result(key) {
910 hit_mask = Some(match hit_mask {
911 Some(hit_mask) => hit_mask.bitand(mask.as_ref()),
912 None => mask.as_ref().clone(),
913 });
914 } else {
915 misses.push(entry);
916 }
917 }
918
919 if misses.is_empty() && non_cacheable_physical.is_empty() {
920 let combined_mask = hit_mask.unwrap_or_else(|| BooleanBuffer::new_set(0));
921 let refined_selection =
922 refined_selection_from_mask(&combined_mask, &build_ctx.row_selection);
923 let rows_before_filter = rows_before_filter(reader_builder, build_ctx);
924 let filtered_rows = rows_before_filter.saturating_sub(refined_selection.row_count());
925 return Ok(PrefilterResult {
926 refined_selection,
927 filtered_rows,
928 });
929 }
930
931 let mut uncached_entries = misses;
932 uncached_entries.extend(
933 non_cacheable_physical
934 .iter()
935 .copied()
936 .map(|idx| PrefilterEntry::without_cache(PrefilterEntryKind::Physical(idx))),
937 );
938 let (uncached_mask, read_rows) =
939 build_prefilter_masks(prefilter_ctx, reader_builder, build_ctx, &uncached_entries).await?;
940
941 let final_mask = match (hit_mask, uncached_mask) {
942 (Some(hit_mask), Some(uncached_mask)) => hit_mask.bitand(&uncached_mask),
943 (Some(hit_mask), None) => hit_mask,
944 (None, Some(uncached_mask)) => uncached_mask,
945 (None, None) => BooleanBuffer::new_set(read_rows),
946 };
947 debug_assert_eq!(final_mask.len(), read_rows);
948 let rows_selected = final_mask.count_set_bits();
949 let filtered_rows = read_rows.saturating_sub(rows_selected);
950 let refined_selection = refined_selection_from_mask(&final_mask, &build_ctx.row_selection);
951
952 Ok(PrefilterResult {
953 refined_selection,
954 filtered_rows,
955 })
956}
957
958fn non_cacheable_physical_filters(prefilter_ctx: &PrefilterContext) -> Vec<usize> {
959 prefilter_ctx
960 .physical_filters
961 .iter()
962 .enumerate()
963 .filter_map(|(idx, filter)| (!filter.is_immutable()).then_some(idx))
964 .collect()
965}
966
967async fn build_prefilter_masks(
968 prefilter_ctx: &mut PrefilterContext,
969 reader_builder: &RowGroupReaderBuilder,
970 build_ctx: &RowGroupBuildContext<'_>,
971 entries: &[PrefilterEntry],
972) -> Result<(Option<BooleanBuffer>, usize)> {
973 let prefilter_column_names = prefilter_column_names_for_entries(prefilter_ctx, entries);
974 let parquet_schema = reader_builder
975 .parquet_metadata()
976 .file_metadata()
977 .schema_descr();
978 let projection = compute_projection_mask(
979 &prefilter_column_names,
980 &prefilter_ctx.arrow_schema,
981 parquet_schema,
982 );
983
984 let mut stream = reader_builder
985 .build_with_projection(
986 build_ctx.row_group_idx,
987 build_ctx.row_selection.clone(),
988 projection,
989 build_ctx.fetch_metrics,
990 )
991 .await?;
992
993 let mut cache_builders = entries
994 .iter()
995 .map(|entry| entry.key.is_some().then(|| BooleanBufferBuilder::new(0)))
996 .collect::<Vec<_>>();
997 let mut combined_builder = (!entries.is_empty()).then(|| BooleanBufferBuilder::new(0));
998 let mut rows_before_filter = 0usize;
999
1000 while let Some(batch_result) = stream.next().await {
1001 let batch = batch_result?;
1002 let num_rows = batch.num_rows();
1003 if num_rows == 0 {
1004 continue;
1005 }
1006 rows_before_filter += num_rows;
1007
1008 let mut batch_mask = BooleanBuffer::new_set(num_rows);
1009 for (idx, entry) in entries.iter().enumerate() {
1010 let mask = eval_entry_mask(
1011 &batch,
1012 prefilter_ctx,
1013 entry.kind,
1014 reader_builder.file_path(),
1015 )?;
1016 batch_mask = batch_mask.bitand(&mask);
1017 if let Some(Some(builder)) = cache_builders.get_mut(idx) {
1018 builder.append_buffer(&mask);
1019 }
1020 }
1021 if let Some(builder) = &mut combined_builder {
1022 builder.append_buffer(&batch_mask);
1023 }
1024 }
1025
1026 for (entry, builder) in entries.iter().zip(cache_builders) {
1027 if let (Some(key), Some(mut builder)) = (&entry.key, builder) {
1028 reader_builder
1029 .cache_strategy()
1030 .put_prefilter_result(key.clone(), Arc::new(builder.finish()));
1031 }
1032 }
1033
1034 Ok((
1035 combined_builder.map(|mut builder| builder.finish()),
1036 rows_before_filter,
1037 ))
1038}
1039
1040fn prefilter_column_names_for_entries(
1041 prefilter_ctx: &PrefilterContext,
1042 entries: &[PrefilterEntry],
1043) -> HashSet<String> {
1044 let mut prefilter_column_names = HashSet::new();
1045 for entry in entries {
1046 match entry.kind {
1047 PrefilterEntryKind::Simple(idx) => {
1048 if let MaybeFilter::Filter(filter) = prefilter_ctx.filters[idx].filter() {
1049 prefilter_column_names.insert(filter.column_name().to_string());
1050 }
1051 }
1052 PrefilterEntryKind::Physical(idx) => {
1053 prefilter_column_names.insert(
1054 prefilter_ctx.physical_filters[idx]
1055 .column_name()
1056 .to_string(),
1057 );
1058 }
1059 PrefilterEntryKind::PkGroup => {
1060 prefilter_column_names.insert(PRIMARY_KEY_COLUMN_NAME.to_string());
1061 }
1062 }
1063 }
1064 prefilter_column_names
1065}
1066
1067async fn execute_prefilter_by_reading_columns(
1068 prefilter_ctx: &mut PrefilterContext,
1069 reader_builder: &RowGroupReaderBuilder,
1070 build_ctx: &RowGroupBuildContext<'_>,
1071) -> Result<PrefilterResult> {
1072 let entries = all_prefilter_entries(prefilter_ctx);
1073 if entries.is_empty() {
1074 return Ok(identity_prefilter_result(reader_builder, build_ctx));
1075 }
1076 let (mask, rows_before_filter) =
1077 build_prefilter_masks(prefilter_ctx, reader_builder, build_ctx, &entries).await?;
1078
1079 let final_mask = mask.unwrap_or_else(|| BooleanBuffer::new_set(rows_before_filter));
1080 let rows_selected = final_mask.count_set_bits();
1081 let filtered_rows = rows_before_filter.saturating_sub(rows_selected);
1082 let refined_selection = refined_selection_from_mask(&final_mask, &build_ctx.row_selection);
1083
1084 Ok(PrefilterResult {
1085 refined_selection,
1086 filtered_rows,
1087 })
1088}
1089
1090fn all_prefilter_entries(prefilter_ctx: &PrefilterContext) -> Vec<PrefilterEntry> {
1091 let mut entries = Vec::new();
1092 if prefilter_ctx.pk_filter.is_some() {
1093 entries.push(PrefilterEntry::without_cache(PrefilterEntryKind::PkGroup));
1094 }
1095 entries.extend(
1096 prefilter_ctx
1097 .filters
1098 .iter()
1099 .enumerate()
1100 .filter(|(idx, _)| {
1101 !prefilter_ctx
1102 .proven_simple_filters
1103 .get(*idx)
1104 .copied()
1105 .unwrap_or(false)
1106 })
1107 .map(|(idx, _)| PrefilterEntry::without_cache(PrefilterEntryKind::Simple(idx))),
1108 );
1109 entries.extend(
1110 prefilter_ctx
1111 .physical_filters
1112 .iter()
1113 .enumerate()
1114 .map(|(idx, _)| PrefilterEntry::without_cache(PrefilterEntryKind::Physical(idx))),
1115 );
1116 entries
1117}
1118
1119#[derive(Clone, Copy)]
1120enum PrefilterEntryKind {
1121 Simple(usize),
1122 Physical(usize),
1123 PkGroup,
1124}
1125
1126struct PrefilterEntry {
1127 kind: PrefilterEntryKind,
1128 key: Option<PrefilterKey>,
1129}
1130
1131impl PrefilterEntry {
1132 fn without_cache(kind: PrefilterEntryKind) -> Self {
1133 Self { kind, key: None }
1134 }
1135}
1136
1137fn build_prefilter_cache_entries(
1138 prefilter_ctx: &PrefilterContext,
1139 reader_builder: &RowGroupReaderBuilder,
1140 build_ctx: &RowGroupBuildContext<'_>,
1141) -> Vec<PrefilterEntry> {
1142 let row_selection = PrefilterKey::row_selection_snapshot(build_ctx.row_selection.as_ref());
1143 let file_id = reader_builder.file_handle().file_id().file_id();
1144 let row_group_idx = build_ctx.row_group_idx as u32;
1145 let mut entries = Vec::new();
1146
1147 for (idx, filter_ctx) in prefilter_ctx.filters.iter().enumerate() {
1148 if prefilter_ctx
1149 .proven_simple_filters
1150 .get(idx)
1151 .copied()
1152 .unwrap_or(false)
1153 {
1154 continue;
1155 }
1156 entries.push(PrefilterEntry {
1157 kind: PrefilterEntryKind::Simple(idx),
1158 key: Some(PrefilterKey::new(
1159 file_id,
1160 row_group_idx,
1161 row_selection.clone(),
1162 prefilter_ctx.schema_version,
1163 smallvec![filter_ctx.expr_str().to_string()],
1164 )),
1165 });
1166 }
1167
1168 for (idx, filter_ctx) in prefilter_ctx.physical_filters.iter().enumerate() {
1169 if !filter_ctx.is_immutable() {
1170 continue;
1171 }
1172 entries.push(PrefilterEntry {
1173 kind: PrefilterEntryKind::Physical(idx),
1174 key: Some(PrefilterKey::new(
1175 file_id,
1176 row_group_idx,
1177 row_selection.clone(),
1178 prefilter_ctx.schema_version,
1179 smallvec![filter_ctx.expr_str().to_string()],
1180 )),
1181 });
1182 }
1183
1184 if prefilter_ctx.pk_filter.is_some()
1185 && let Some(exprs) = &prefilter_ctx.pk_filter_expr_strs
1186 {
1187 entries.push(PrefilterEntry {
1188 kind: PrefilterEntryKind::PkGroup,
1189 key: Some(PrefilterKey::new(
1190 file_id,
1191 row_group_idx,
1192 row_selection,
1193 prefilter_ctx.schema_version,
1194 exprs.clone(),
1195 )),
1196 });
1197 }
1198
1199 entries
1200}
1201
1202fn identity_prefilter_result(
1203 reader_builder: &RowGroupReaderBuilder,
1204 build_ctx: &RowGroupBuildContext<'_>,
1205) -> PrefilterResult {
1206 let row_count = reader_builder
1207 .parquet_metadata()
1208 .row_group(build_ctx.row_group_idx)
1209 .num_rows() as usize;
1210 PrefilterResult {
1211 refined_selection: identity_row_selection(&build_ctx.row_selection, row_count),
1212 filtered_rows: 0,
1213 }
1214}
1215
1216fn identity_row_selection(
1217 original_selection: &Option<RowSelection>,
1218 row_count: usize,
1219) -> RowSelection {
1220 original_selection
1221 .clone()
1222 .unwrap_or_else(|| RowSelection::from(vec![RowSelector::select(row_count)]))
1223}
1224
1225fn rows_before_filter(
1226 reader_builder: &RowGroupReaderBuilder,
1227 build_ctx: &RowGroupBuildContext<'_>,
1228) -> usize {
1229 build_ctx.row_selection.as_ref().map_or_else(
1230 || {
1231 reader_builder
1232 .parquet_metadata()
1233 .row_group(build_ctx.row_group_idx)
1234 .num_rows() as usize
1235 },
1236 RowSelection::row_count,
1237 )
1238}
1239
1240fn refined_selection_from_mask(
1241 mask: &BooleanBuffer,
1242 original_selection: &Option<RowSelection>,
1243) -> RowSelection {
1244 if mask.is_empty() || mask.count_set_bits() == 0 {
1245 return RowSelection::from(vec![]);
1246 }
1247
1248 let prefilter_selection = RowSelection::from_filters(&[BooleanArray::from(mask.clone())]);
1249 match original_selection {
1250 Some(original) => original.and_then(&prefilter_selection),
1251 None => prefilter_selection,
1252 }
1253}
1254
1255fn eval_entry_mask(
1256 batch: &RecordBatch,
1257 prefilter_ctx: &mut PrefilterContext,
1258 kind: PrefilterEntryKind,
1259 file_path: &str,
1260) -> Result<BooleanBuffer> {
1261 match kind {
1262 PrefilterEntryKind::Simple(idx) => {
1263 eval_simple_filter_mask(batch, &prefilter_ctx.filters[idx], file_path)
1264 }
1265 PrefilterEntryKind::Physical(idx) => {
1266 eval_physical_filter_mask(batch, &prefilter_ctx.physical_filters[idx], file_path)
1267 }
1268 PrefilterEntryKind::PkGroup => {
1269 let pk_filter = prefilter_ctx.pk_filter.as_mut().context(UnexpectedSnafu {
1270 reason: "Missing primary key filter for prefilter cache entry",
1271 })?;
1272 primary_key_filter_mask(batch, pk_filter.as_mut())
1273 }
1274 }
1275}
1276
1277pub(crate) fn primary_key_filter_mask(
1279 batch: &RecordBatch,
1280 pk_filter: &mut dyn PrimaryKeyFilter,
1281) -> Result<BooleanBuffer> {
1282 let (pk_column_index, _) = batch
1283 .schema()
1284 .column_with_name(PRIMARY_KEY_COLUMN_NAME)
1285 .context(UnexpectedSnafu {
1286 reason: "Primary key column not found in prefilter batch",
1287 })?;
1288 let matched_row_ranges = matching_row_ranges_by_primary_key(batch, pk_column_index, pk_filter)?;
1289 let mut builder = BooleanBufferBuilder::new(batch.num_rows());
1290 builder.append_n(batch.num_rows(), false);
1291 for range in matched_row_ranges {
1292 for row in range {
1293 builder.set_bit(row, true);
1294 }
1295 }
1296 Ok(builder.finish())
1297}
1298
1299fn eval_simple_filter_mask(
1300 batch: &RecordBatch,
1301 filter_ctx: &SimpleFilterContext,
1302 file_path: &str,
1303) -> Result<BooleanBuffer> {
1304 let filter = match filter_ctx.filter() {
1305 MaybeFilter::Filter(filter) => filter,
1306 MaybeFilter::Matched => return Ok(BooleanBuffer::new_set(batch.num_rows())),
1307 MaybeFilter::Pruned => return Ok(BooleanBuffer::new_unset(batch.num_rows())),
1308 };
1309
1310 let (idx, _) = batch
1311 .schema()
1312 .column_with_name(filter.column_name())
1313 .with_context(|| UnexpectedSnafu {
1314 reason: format!(
1315 "Prefilter column '{}' (id {}) not found in batch for file {}",
1316 filter.column_name(),
1317 filter_ctx.column_id(),
1318 file_path
1319 ),
1320 })?;
1321 let column = batch.column(idx).clone();
1322 filter.evaluate_array(&column).context(RecordBatchSnafu)
1323}
1324
1325fn eval_physical_filter_mask(
1326 batch: &RecordBatch,
1327 filter_ctx: &PhysicalFilterContext,
1328 file_path: &str,
1329) -> Result<BooleanBuffer> {
1330 let filter = filter_ctx.filter();
1331
1332 let (idx, _) = batch
1333 .schema()
1334 .column_with_name(filter_ctx.column_name())
1335 .with_context(|| UnexpectedSnafu {
1336 reason: format!(
1337 "Prefilter physical column '{}' (id {}) not found in batch for file {}",
1338 filter_ctx.column_name(),
1339 filter_ctx.column_id(),
1340 file_path
1341 ),
1342 })?;
1343 let column = batch.column(idx).clone();
1344
1345 let record_batch = RecordBatch::try_new(filter_ctx.schema().clone(), vec![column])
1346 .context(NewRecordBatchSnafu)?;
1347 let evaluated = filter
1348 .evaluate(&record_batch)
1349 .context(EvalPartitionFilterSnafu)?;
1350 let array = evaluated
1351 .into_array(record_batch.num_rows())
1352 .context(EvalPartitionFilterSnafu)?;
1353 let boolean_array = array
1354 .as_any()
1355 .downcast_ref::<BooleanArray>()
1356 .context(UnexpectedSnafu {
1357 reason: "Failed to downcast physical filter result to BooleanArray",
1358 })?;
1359 let mut result = boolean_array.values().clone();
1362 if let Some(nulls) = boolean_array.nulls() {
1363 result = result.bitand(nulls.inner());
1364 }
1365 Ok(result)
1366}
1367
1368#[cfg(test)]
1369mod tests {
1370 use std::sync::Arc;
1371 use std::sync::atomic::{AtomicUsize, Ordering};
1372
1373 use bytes::Bytes;
1374 use common_recordbatch::filter::SimpleFilterEvaluator;
1375 use datafusion_common::ScalarValue;
1376 use datafusion_expr::{col, lit};
1377 use datatypes::arrow::array::{
1378 ArrayRef, DictionaryArray, Int32Array, TimestampMillisecondArray, UInt8Array, UInt32Array,
1379 UInt64Array,
1380 };
1381 use datatypes::arrow::datatypes::{DataType, Field, Schema, UInt32Type};
1382 use datatypes::arrow::record_batch::RecordBatch;
1383 use datatypes::prelude::ConcreteDataType;
1384 use datatypes::value::Value;
1385 use mito_codec::row_converter::{PrimaryKeyFilter, build_primary_key_codec};
1386 use parquet::arrow::ArrowWriter;
1387 use parquet::arrow::arrow_reader::{ParquetRecordBatchReaderBuilder, RowSelector};
1388 use store_api::codec::PrimaryKeyEncoding;
1389 use store_api::metadata::RegionMetadataBuilder;
1390 use store_api::region_request::{AlterKind, ModifyColumnType};
1391
1392 use super::*;
1393 use crate::read::read_columns::ReadColumns;
1394 use crate::sst::internal_fields;
1395 use crate::sst::parquet::flat_format::{FlatReadFormat, primary_key_column_index};
1396 use crate::test_util::sst_util::{
1397 new_primary_key, new_record_batch_with_custom_sequence, sst_region_metadata,
1398 sst_region_metadata_with_encoding,
1399 };
1400
1401 struct CountingPrimaryKeyFilter {
1402 hits: Arc<AtomicUsize>,
1403 expected: Vec<u8>,
1404 }
1405
1406 impl PrimaryKeyFilter for CountingPrimaryKeyFilter {
1407 fn matches(&mut self, pk: &[u8]) -> mito_codec::error::Result<bool> {
1408 self.hits.fetch_add(1, Ordering::Relaxed);
1409 Ok(pk == self.expected.as_slice())
1410 }
1411 }
1412
1413 #[test]
1414 fn test_cached_primary_key_filter_reuses_previous_result() {
1415 let expected = new_primary_key(&["a", "x"]);
1416 let hits = Arc::new(AtomicUsize::new(0));
1417 let mut filter = CachedPrimaryKeyFilter::new(Box::new(CountingPrimaryKeyFilter {
1418 hits: Arc::clone(&hits),
1419 expected: expected.clone(),
1420 }));
1421
1422 assert!(filter.matches(expected.as_slice()).unwrap());
1423 assert!(filter.matches(expected.as_slice()).unwrap());
1424 assert!(
1425 !filter
1426 .matches(new_primary_key(&["b", "x"]).as_slice())
1427 .unwrap()
1428 );
1429
1430 assert_eq!(hits.load(Ordering::Relaxed), 2);
1431 }
1432
1433 fn new_test_filters(exprs: &[datafusion_expr::Expr]) -> Vec<SimpleFilterEvaluator> {
1434 exprs
1435 .iter()
1436 .filter_map(SimpleFilterEvaluator::try_new)
1437 .collect()
1438 }
1439
1440 fn new_simple_filter_contexts(
1441 metadata: &RegionMetadataRef,
1442 exprs: &[datafusion_expr::Expr],
1443 ) -> Vec<SimpleFilterContext> {
1444 exprs
1445 .iter()
1446 .filter_map(|expr| SimpleFilterContext::new_opt(metadata, None, expr))
1447 .collect()
1448 }
1449
1450 fn stats_metadata_with_options(
1451 values: &[Vec<u64>],
1452 nulls: Option<&[bool]>,
1453 writer_options: Option<parquet::file::properties::WriterProperties>,
1454 ) -> Arc<ParquetMetaData> {
1455 let first = new_record_batch_with_custom_sequence(&["a", "x"], 0, values[0].len(), 1);
1456 let mut bytes = Vec::new();
1457 let mut writer = ArrowWriter::try_new(&mut bytes, first.schema(), writer_options).unwrap();
1458 for (idx, values) in values.iter().enumerate() {
1459 let has_null = nulls.and_then(|nulls| nulls.get(idx)).copied() == Some(true);
1460 let batch = new_record_batch_with_custom_sequence(
1461 &["a", "x"],
1462 0,
1463 if has_null {
1464 values.len().max(2)
1465 } else {
1466 values.len()
1467 },
1468 1,
1469 );
1470 let mut columns = batch.columns().to_vec();
1471 columns[2] = Arc::new(if has_null {
1472 UInt64Array::from(vec![Some(values[0]), None])
1473 } else {
1474 UInt64Array::from_iter_values(values.iter().copied())
1475 });
1476 writer
1477 .write(&RecordBatch::try_new(batch.schema(), columns).unwrap())
1478 .unwrap();
1479 writer.flush().unwrap();
1480 }
1481 writer.close().unwrap();
1482 ParquetRecordBatchReaderBuilder::try_new(Bytes::from(bytes))
1483 .unwrap()
1484 .metadata()
1485 .clone()
1486 }
1487
1488 fn stats_metadata(values: &[Vec<u64>], nulls: Option<&[bool]>) -> Arc<ParquetMetaData> {
1489 stats_metadata_with_options(values, nulls, None)
1490 }
1491
1492 fn int32_stats_metadata(values: &[i32]) -> Arc<ParquetMetaData> {
1493 let batch = new_record_batch_with_custom_sequence(&["a", "x"], 0, values.len(), 1);
1494 let mut fields = batch
1495 .schema()
1496 .fields()
1497 .iter()
1498 .map(|field| field.as_ref().clone())
1499 .collect::<Vec<_>>();
1500 fields[2].set_data_type(DataType::Int32);
1501 let mut columns = batch.columns().to_vec();
1502 columns[2] = Arc::new(Int32Array::from(values.to_vec()));
1503 let batch = RecordBatch::try_new(Arc::new(Schema::new(fields)), columns).unwrap();
1504 let mut bytes = Vec::new();
1505 let mut writer = ArrowWriter::try_new(&mut bytes, batch.schema(), None).unwrap();
1506 writer.write(&batch).unwrap();
1507 writer.close().unwrap();
1508 ParquetRecordBatchReaderBuilder::try_new(Bytes::from(bytes))
1509 .unwrap()
1510 .metadata()
1511 .clone()
1512 }
1513
1514 fn metadata_with_field_type(data_type: ConcreteDataType) -> RegionMetadataRef {
1515 let mut builder = RegionMetadataBuilder::from_existing(sst_region_metadata());
1516 builder
1517 .alter(AlterKind::ModifyColumnTypes {
1518 columns: vec![ModifyColumnType {
1519 column_name: "field_0".to_string(),
1520 target_type: data_type,
1521 }],
1522 })
1523 .unwrap();
1524 Arc::new(builder.build().unwrap())
1525 }
1526
1527 fn new_physical_filter_contexts(
1528 metadata: &RegionMetadataRef,
1529 read_format: &FlatReadFormat,
1530 exprs: &[datafusion_expr::Expr],
1531 ) -> Vec<PhysicalFilterContext> {
1532 exprs
1533 .iter()
1534 .filter_map(|expr| PhysicalFilterContext::new_opt(metadata, None, read_format, expr))
1535 .collect()
1536 }
1537
1538 fn new_raw_batch(primary_keys: &[&[u8]], field_values: &[u64]) -> RecordBatch {
1539 assert_eq!(primary_keys.len(), field_values.len());
1540
1541 let metadata = Arc::new(sst_region_metadata());
1542 let arrow_schema = metadata.schema.arrow_schema();
1543 let field_column = arrow_schema
1544 .field(arrow_schema.index_of("field_0").unwrap())
1545 .clone();
1546 let time_index_column = arrow_schema
1547 .field(arrow_schema.index_of("ts").unwrap())
1548 .clone();
1549 let mut fields = vec![field_column, time_index_column];
1550 fields.extend(
1551 internal_fields()
1552 .into_iter()
1553 .map(|field| field.as_ref().clone()),
1554 );
1555 let schema = Arc::new(Schema::new(fields));
1556
1557 let mut dict_values = Vec::new();
1558 let mut keys = Vec::with_capacity(primary_keys.len());
1559 for pk in primary_keys {
1560 let key = dict_values
1561 .iter()
1562 .position(|existing: &&[u8]| existing == pk)
1563 .unwrap_or_else(|| {
1564 dict_values.push(*pk);
1565 dict_values.len() - 1
1566 });
1567 keys.push(key as u32);
1568 }
1569 let pk_array: ArrayRef = Arc::new(DictionaryArray::<UInt32Type>::new(
1570 UInt32Array::from(keys),
1571 Arc::new(BinaryArray::from_iter_values(dict_values.iter().copied())),
1572 ));
1573
1574 RecordBatch::try_new(
1575 schema,
1576 vec![
1577 Arc::new(UInt64Array::from(field_values.to_vec())),
1578 Arc::new(TimestampMillisecondArray::from_iter_values(
1579 0..primary_keys.len() as i64,
1580 )),
1581 pk_array,
1582 Arc::new(UInt64Array::from(vec![1; primary_keys.len()])),
1583 Arc::new(UInt8Array::from(vec![1; primary_keys.len()])),
1584 ],
1585 )
1586 .unwrap()
1587 }
1588
1589 fn new_prefilter_batch(primary_keys: &[&[u8]], field_values: &[u64]) -> RecordBatch {
1590 assert_eq!(primary_keys.len(), field_values.len());
1591
1592 let metadata = Arc::new(sst_region_metadata());
1593 let arrow_schema = metadata.schema.arrow_schema();
1594 let field_column = arrow_schema
1595 .field(arrow_schema.index_of("field_0").unwrap())
1596 .clone();
1597 let time_index_column = arrow_schema
1598 .field(arrow_schema.index_of("ts").unwrap())
1599 .clone();
1600 let schema = Arc::new(Schema::new(vec![
1601 field_column,
1602 time_index_column,
1603 internal_fields()[0].as_ref().clone(),
1604 ]));
1605
1606 let mut dict_values = Vec::new();
1607 let mut keys = Vec::with_capacity(primary_keys.len());
1608 for pk in primary_keys {
1609 let key = dict_values
1610 .iter()
1611 .position(|existing: &&[u8]| existing == pk)
1612 .unwrap_or_else(|| {
1613 dict_values.push(*pk);
1614 dict_values.len() - 1
1615 });
1616 keys.push(key as u32);
1617 }
1618 let pk_array: ArrayRef = Arc::new(DictionaryArray::<UInt32Type>::new(
1619 UInt32Array::from(keys),
1620 Arc::new(BinaryArray::from_iter_values(dict_values.iter().copied())),
1621 ));
1622
1623 RecordBatch::try_new(
1624 schema,
1625 vec![
1626 Arc::new(UInt64Array::from(field_values.to_vec())),
1627 Arc::new(TimestampMillisecondArray::from_iter_values(
1628 0..primary_keys.len() as i64,
1629 )),
1630 pk_array,
1631 ],
1632 )
1633 .unwrap()
1634 }
1635
1636 fn new_prefilter_batch_binary_pk(primary_keys: &[&[u8]], field_values: &[u64]) -> RecordBatch {
1637 assert_eq!(primary_keys.len(), field_values.len());
1638
1639 let metadata = Arc::new(sst_region_metadata());
1640 let arrow_schema = metadata.schema.arrow_schema();
1641 let field_column = arrow_schema
1642 .field(arrow_schema.index_of("field_0").unwrap())
1643 .clone();
1644 let time_index_column = arrow_schema
1645 .field(arrow_schema.index_of("ts").unwrap())
1646 .clone();
1647 let schema = Arc::new(Schema::new(vec![
1648 field_column,
1649 time_index_column,
1650 Field::new(PRIMARY_KEY_COLUMN_NAME, DataType::Binary, false),
1651 ]));
1652
1653 let pk_array: ArrayRef =
1654 Arc::new(BinaryArray::from_iter_values(primary_keys.iter().copied()));
1655
1656 RecordBatch::try_new(
1657 schema,
1658 vec![
1659 Arc::new(UInt64Array::from(field_values.to_vec())),
1660 Arc::new(TimestampMillisecondArray::from_iter_values(
1661 0..primary_keys.len() as i64,
1662 )),
1663 pk_array,
1664 ],
1665 )
1666 .unwrap()
1667 }
1668
1669 fn field_values(batch: &RecordBatch) -> Vec<u64> {
1670 batch
1671 .column(0)
1672 .as_any()
1673 .downcast_ref::<UInt64Array>()
1674 .unwrap()
1675 .values()
1676 .to_vec()
1677 }
1678
1679 fn remaining_simple_filter_columns(filters: &[SimpleFilterContext]) -> Vec<&str> {
1680 filters
1681 .iter()
1682 .map(|filter_ctx| filter_ctx.filter().as_filter().unwrap().column_name())
1683 .collect()
1684 }
1685
1686 #[test]
1687 fn test_prefilter_primary_key_drops_single_dictionary_batch() {
1688 let metadata = Arc::new(sst_region_metadata());
1689 let filters = Arc::new(new_test_filters(&[col("tag_0").eq(lit("b"))]));
1690 let mut primary_key_filter =
1691 build_primary_key_codec(metadata.as_ref()).primary_key_filter(&metadata, filters);
1692 let pk_a = new_primary_key(&["a", "x"]);
1693 let batch = new_raw_batch(&[pk_a.as_slice(), pk_a.as_slice()], &[10, 11]);
1694 let pk_col_idx = primary_key_column_index(batch.num_columns());
1695
1696 let filtered =
1697 prefilter_flat_batch_by_primary_key(batch, pk_col_idx, primary_key_filter.as_mut())
1698 .unwrap();
1699
1700 assert!(filtered.is_none());
1701 }
1702
1703 #[test]
1704 fn test_prefilter_primary_key_builds_mask_for_fragmented_matches() {
1705 let metadata = Arc::new(sst_region_metadata());
1706 let filters = Arc::new(new_test_filters(&[col("tag_0")
1707 .eq(lit("a"))
1708 .or(col("tag_0").eq(lit("c")))]));
1709 let mut primary_key_filter =
1710 build_primary_key_codec(metadata.as_ref()).primary_key_filter(&metadata, filters);
1711 let pk_a = new_primary_key(&["a", "x"]);
1712 let pk_b = new_primary_key(&["b", "x"]);
1713 let pk_c = new_primary_key(&["c", "x"]);
1714 let pk_d = new_primary_key(&["d", "x"]);
1715 let batch = new_raw_batch(
1716 &[
1717 pk_a.as_slice(),
1718 pk_a.as_slice(),
1719 pk_b.as_slice(),
1720 pk_b.as_slice(),
1721 pk_c.as_slice(),
1722 pk_c.as_slice(),
1723 pk_d.as_slice(),
1724 pk_d.as_slice(),
1725 ],
1726 &[10, 11, 12, 13, 14, 15, 16, 17],
1727 );
1728 let pk_col_idx = primary_key_column_index(batch.num_columns());
1729
1730 let filtered =
1731 prefilter_flat_batch_by_primary_key(batch, pk_col_idx, primary_key_filter.as_mut())
1732 .unwrap()
1733 .unwrap();
1734
1735 assert_eq!(filtered.num_rows(), 4);
1736 assert_eq!(field_values(&filtered), vec![10, 11, 14, 15]);
1737 }
1738
1739 #[test]
1740 fn test_prefilter_builder_returns_none_without_selected_filters() {
1741 let metadata: RegionMetadataRef =
1742 Arc::new(sst_region_metadata_with_encoding(PrimaryKeyEncoding::Dense));
1743 let read_format = FlatReadFormat::new(
1744 metadata.clone(),
1745 ReadColumns::new(metadata.column_metadatas.iter().map(|c| c.column_id)),
1746 None,
1747 "test",
1748 false,
1749 )
1750 .unwrap();
1751 let codec = build_primary_key_codec(metadata.as_ref());
1752
1753 let builder = PrefilterContextBuilder::new(
1754 &read_format,
1755 &codec,
1756 None,
1757 None,
1758 Vec::new(),
1759 Vec::new(),
1760 metadata.schema_version,
1761 &stats_metadata(&[vec![1]], None),
1762 );
1763 assert!(builder.is_none());
1764 }
1765
1766 #[test]
1767 fn test_simple_filter_stats_uses_real_metadata() {
1768 let metadata: RegionMetadataRef = Arc::new(sst_region_metadata());
1769 let read_format = FlatReadFormat::new(
1770 metadata.clone(),
1771 ReadColumns::new(
1772 metadata
1773 .column_metadatas
1774 .iter()
1775 .map(|column| column.column_id),
1776 ),
1777 None,
1778 "test",
1779 true,
1780 )
1781 .unwrap();
1782 let parquet_metadata = stats_metadata(&[vec![i64::MAX as u64 + 2]], None);
1783 let filters = new_simple_filter_contexts(
1784 &metadata,
1785 &[
1786 col("field_0").gt(lit(i64::MAX as u64 + 1)),
1787 col("field_0").gt(lit(i64::MAX as u64 + 2)),
1788 lit(i64::MAX as u64 + 1).lt(col("field_0")),
1789 ],
1790 );
1791
1792 let proofs =
1793 simple_filter_stats_proofs(&read_format, parquet_metadata.row_groups(), &filters);
1794 assert_eq!(proofs, vec![vec![true, false, true]]);
1795 }
1796
1797 #[test]
1798 fn test_simple_filter_stats_retain_narrow_or_incomplete_metadata() {
1799 let parquet_metadata = stats_metadata(&[vec![2]], Some(&[true]));
1800 let metadata: RegionMetadataRef = Arc::new(sst_region_metadata());
1801 let read_format = FlatReadFormat::new(
1802 metadata.clone(),
1803 ReadColumns::new(
1804 metadata
1805 .column_metadatas
1806 .iter()
1807 .map(|column| column.column_id),
1808 ),
1809 None,
1810 "test",
1811 true,
1812 )
1813 .unwrap();
1814 let filter = new_simple_filter_contexts(&metadata, &[col("field_0").gt(lit(1_u64))]);
1815 assert_eq!(
1816 simple_filter_stats_proofs(&read_format, parquet_metadata.row_groups(), &filter),
1817 vec![vec![false]],
1818 );
1819 let missing_stats = stats_metadata_with_options(
1820 &[vec![2]],
1821 None,
1822 Some(
1823 parquet::file::properties::WriterProperties::builder()
1824 .set_statistics_enabled(parquet::file::properties::EnabledStatistics::None)
1825 .build(),
1826 ),
1827 );
1828 assert_eq!(
1829 simple_filter_stats_proofs(&read_format, missing_stats.row_groups(), &filter),
1830 vec![vec![false]],
1831 );
1832
1833 let narrow_metadata = metadata_with_field_type(ConcreteDataType::int8_datatype());
1836 let narrow_read_format = FlatReadFormat::new(
1837 narrow_metadata.clone(),
1838 ReadColumns::new(
1839 narrow_metadata
1840 .column_metadatas
1841 .iter()
1842 .map(|column| column.column_id),
1843 ),
1844 None,
1845 "test",
1846 true,
1847 )
1848 .unwrap();
1849 let narrow_filter =
1850 new_simple_filter_contexts(&narrow_metadata, &[col("field_0").gt(lit(1_i32))]);
1851 let narrow_stats = int32_stats_metadata(&[2]);
1852 assert_eq!(
1853 simple_filter_stats_proofs(
1854 &narrow_read_format,
1855 narrow_stats.row_groups(),
1856 &narrow_filter,
1857 ),
1858 vec![vec![false]],
1859 );
1860 }
1861
1862 #[test]
1863 fn test_prefilter_builder_uses_row_group_proofs_by_index() {
1864 let metadata: RegionMetadataRef = Arc::new(sst_region_metadata());
1865 let read_format = FlatReadFormat::new(
1866 metadata.clone(),
1867 ReadColumns::new(
1868 metadata
1869 .column_metadatas
1870 .iter()
1871 .map(|column| column.column_id),
1872 ),
1873 None,
1874 "test",
1875 true,
1876 )
1877 .unwrap();
1878 let codec = build_primary_key_codec(metadata.as_ref());
1879 let builder = PrefilterContextBuilder::new(
1880 &read_format,
1881 &codec,
1882 None,
1883 None,
1884 new_simple_filter_contexts(&metadata, &[col("field_0").gt(lit(1_u64))]),
1885 Vec::new(),
1886 metadata.schema_version,
1887 &stats_metadata(&[vec![2], vec![1]], None),
1888 )
1889 .unwrap();
1890
1891 assert!(builder.build(0).proven_simple_filters[0]);
1892 assert!(!builder.build(1).proven_simple_filters[0]);
1893 }
1894
1895 #[test]
1896 fn test_simple_filter_stats_prove_integer_predicates() {
1897 macro_rules! assert_all_operators {
1898 ($literal:expr, $base:expr, $below:expr, $above:expr) => {{
1899 for (expr, min, max) in [
1900 (col("x").gt(lit($literal)), $above.clone(), $above.clone()),
1901 (col("x").gt_eq(lit($literal)), $base.clone(), $above.clone()),
1902 (col("x").lt(lit($literal)), $below.clone(), $below.clone()),
1903 (col("x").lt_eq(lit($literal)), $below.clone(), $base.clone()),
1904 (col("x").eq(lit($literal)), $base.clone(), $base.clone()),
1905 (
1906 col("x").not_eq(lit($literal)),
1907 $below.clone(),
1908 $below.clone(),
1909 ),
1910 ] {
1911 let filter = SimpleFilterEvaluator::try_new(&expr).unwrap();
1912 assert!(simple_filter_is_true_by_values(
1913 &filter,
1914 &$base,
1915 Some(min),
1916 Some(max),
1917 Some(Value::UInt64(0)),
1918 ));
1919 }
1920 }};
1921 }
1922
1923 assert_all_operators!(5_i32, Value::Int32(5), Value::Int32(-1), Value::Int32(6));
1924 assert_all_operators!(5_u32, Value::UInt32(5), Value::UInt32(4), Value::UInt32(6));
1925 assert_all_operators!(5_i64, Value::Int64(5), Value::Int64(-1), Value::Int64(6));
1926 assert_all_operators!(
1927 i64::MAX as u64 + 2,
1928 Value::UInt64(i64::MAX as u64 + 2),
1929 Value::UInt64(i64::MAX as u64 + 1),
1930 Value::UInt64(i64::MAX as u64 + 3)
1931 );
1932
1933 for (expr, min, max) in [
1936 (col("x").lt(lit(5_i64)), 1, 5),
1937 (col("x").gt(lit(5_i64)), 5, 9),
1938 (col("x").lt_eq(lit(5_i64)), 1, 6),
1939 (col("x").gt_eq(lit(5_i64)), 4, 9),
1940 (col("x").eq(lit(5_i64)), 5, 6),
1941 (col("x").not_eq(lit(5_i64)), 5, 9),
1942 (col("x").not_eq(lit(5_i64)), 1, 5),
1943 (col("x").not_eq(lit(5_i64)), 1, 9),
1944 ] {
1945 let filter = SimpleFilterEvaluator::try_new(&expr).unwrap();
1946 assert!(
1947 !simple_filter_is_true_by_values(
1948 &filter,
1949 &Value::Int64(5),
1950 Some(Value::Int64(min)),
1951 Some(Value::Int64(max)),
1952 Some(Value::UInt64(0)),
1953 ),
1954 "{expr:?} must not be proven by stats {min}..={max}",
1955 );
1956 }
1957 }
1958
1959 #[test]
1960 fn test_simple_filter_stats_retain_unknown_or_unsupported_values() {
1961 let filter = SimpleFilterEvaluator::try_new(&col("x").gt(lit(1_i32))).unwrap();
1962 let literal = Value::Int32(1);
1963 for (min, max, null_count) in [
1964 (
1965 Some(Value::Int32(2)),
1966 Some(Value::Int32(3)),
1967 Some(Value::UInt64(1)),
1968 ),
1969 (Some(Value::Int32(2)), Some(Value::Int32(3)), None),
1970 (
1971 Some(Value::Null),
1972 Some(Value::Int32(3)),
1973 Some(Value::UInt64(0)),
1974 ),
1975 (None, Some(Value::Int32(3)), Some(Value::UInt64(0))),
1976 (Some(Value::Int32(2)), None, Some(Value::UInt64(0))),
1977 (
1978 Some(Value::Int64(2)),
1979 Some(Value::Int64(3)),
1980 Some(Value::UInt64(0)),
1981 ),
1982 (
1983 Some(Value::Int32(3)),
1984 Some(Value::Int32(2)),
1985 Some(Value::UInt64(0)),
1986 ),
1987 ] {
1988 assert!(!simple_filter_is_true_by_values(
1989 &filter, &literal, min, max, null_count
1990 ));
1991 }
1992
1993 let float_filter = SimpleFilterEvaluator::try_new(&col("x").gt(lit(1.0_f64))).unwrap();
1994 assert!(!simple_filter_is_true_by_values(
1995 &float_filter,
1996 &Value::Float64(1.0.into()),
1997 Some(Value::Float64(2.0.into())),
1998 Some(Value::Float64(3.0.into())),
1999 Some(Value::UInt64(0)),
2000 ));
2001 }
2002
2003 #[test]
2004 fn test_prefilter_entries_keep_only_unproven_simple_filter_indices() {
2005 let metadata: RegionMetadataRef = Arc::new(sst_region_metadata());
2006 let filters = new_simple_filter_contexts(
2007 &metadata,
2008 &[col("field_0").gt(lit(1_u64)), col("field_0").lt(lit(9_u64))],
2009 );
2010 let context = PrefilterContext {
2011 pk_filter: None,
2012 filters,
2013 physical_filters: Vec::new(),
2014 schema_version: metadata.schema_version,
2015 pk_filter_expr_strs: None,
2016 arrow_schema: metadata.schema.arrow_schema().clone(),
2017 proven_simple_filters: vec![true, false],
2018 };
2019
2020 let entries = all_prefilter_entries(&context);
2021 assert!(matches!(
2022 entries.as_slice(),
2023 [PrefilterEntry {
2024 kind: PrefilterEntryKind::Simple(1),
2025 ..
2026 }]
2027 ));
2028 }
2029
2030 #[test]
2031 fn test_identity_row_selection_preserves_input() {
2032 let sparse = RowSelection::from(vec![
2033 RowSelector::skip(2),
2034 RowSelector::select(3),
2035 RowSelector::skip(1),
2036 ]);
2037 assert_eq!(identity_row_selection(&Some(sparse.clone()), 6), sparse);
2038
2039 let empty = RowSelection::from(vec![]);
2040 assert_eq!(identity_row_selection(&Some(empty.clone()), 6), empty);
2041 assert_eq!(
2042 identity_row_selection(&None, 6),
2043 RowSelection::from(vec![RowSelector::select(6)])
2044 );
2045 }
2046
2047 #[test]
2048 fn test_should_use_prefilter() {
2049 assert!(should_use_prefilter(1, 5, 6));
2050 assert!(!should_use_prefilter(1, 0, 1));
2051 assert!(!should_use_prefilter(1, 1, 2));
2052 assert!(!should_use_prefilter(4, 3, 7));
2053 assert!(should_use_prefilter(3, 3, 6));
2054 }
2055
2056 #[test]
2057 fn test_build_bulk_filter_plan_classifies_filters_across_read_paths() {
2058 let metadata: RegionMetadataRef = Arc::new(sst_region_metadata_with_encoding(
2059 PrimaryKeyEncoding::Sparse,
2060 ));
2061 let legacy_read_format = FlatReadFormat::new(
2062 metadata.clone(),
2063 ReadColumns::new(metadata.column_metadatas.iter().map(|c| c.column_id)),
2064 None,
2065 "memtable",
2066 false,
2067 )
2068 .unwrap();
2069 assert!(!legacy_read_format.batch_has_raw_pk_columns());
2070
2071 let plan = build_bulk_filter_plan(
2072 &legacy_read_format,
2073 Some(&Predicate::new(vec![
2074 col("tag_0").eq(lit("a")),
2075 col("field_0").gt(lit(1_u64)),
2076 ])),
2077 );
2078 assert_eq!(
2079 plan.pk_filters.as_ref().map(|filters| filters.len()),
2080 Some(1)
2081 );
2082 assert_eq!(
2083 remaining_simple_filter_columns(&plan.remaining_simple_filters),
2084 vec!["field_0"]
2085 );
2086
2087 let metadata: RegionMetadataRef = Arc::new(sst_region_metadata());
2088 let raw_pk_read_format = FlatReadFormat::new(
2089 metadata.clone(),
2090 ReadColumns::new(metadata.column_metadatas.iter().map(|c| c.column_id)),
2091 None,
2092 "memtable",
2093 true,
2094 )
2095 .unwrap();
2096 assert!(raw_pk_read_format.batch_has_raw_pk_columns());
2097
2098 let tag_only_plan = build_bulk_filter_plan(
2099 &raw_pk_read_format,
2100 Some(&Predicate::new(vec![col("tag_0").eq(lit("a"))])),
2101 );
2102 assert!(tag_only_plan.pk_filters.is_none());
2103 assert_eq!(
2104 remaining_simple_filter_columns(&tag_only_plan.remaining_simple_filters),
2105 vec!["tag_0"]
2106 );
2107
2108 let field_only_plan = build_bulk_filter_plan(
2109 &raw_pk_read_format,
2110 Some(&Predicate::new(vec![col("field_0").gt(lit(1_u64))])),
2111 );
2112 assert!(field_only_plan.pk_filters.is_none());
2113 assert_eq!(
2114 remaining_simple_filter_columns(&field_only_plan.remaining_simple_filters),
2115 vec!["field_0"]
2116 );
2117 }
2118
2119 #[test]
2120 fn test_build_reader_filter_plan_classifies_filters_for_prefilter_modes() {
2121 let metadata: RegionMetadataRef = Arc::new(sst_region_metadata());
2122 let full_read_format = FlatReadFormat::new(
2123 metadata.clone(),
2124 ReadColumns::new(metadata.column_metadatas.iter().map(|c| c.column_id)),
2125 None,
2126 "test",
2127 true,
2128 )
2129 .unwrap();
2130 let codec = build_primary_key_codec(metadata.as_ref());
2131
2132 let skip_fields_plan = build_reader_filter_plan(
2133 Some(&Predicate::new(vec![
2134 col("tag_0").eq(lit("a")),
2135 col("field_0").gt(lit(1_u64)),
2136 ])),
2137 None,
2138 PreFilterMode::SkipFields,
2139 true,
2140 false,
2141 &full_read_format,
2142 &codec,
2143 &stats_metadata(&[vec![1]], None),
2144 );
2145 assert!(skip_fields_plan.prefilter_builder.is_some());
2146 assert_eq!(
2147 remaining_simple_filter_columns(&skip_fields_plan.remaining_simple_filters),
2148 vec!["field_0"]
2149 );
2150
2151 let postponed_time_plan = build_reader_filter_plan(
2152 Some(&Predicate::new(vec![
2153 col("tag_0").eq(lit("a")),
2154 col("field_0").gt(lit(1_u64)),
2155 col("ts").gt_eq(lit(ScalarValue::TimestampMillisecond(Some(1), None))),
2156 ])),
2157 None,
2158 PreFilterMode::SkipFields,
2159 true,
2160 true,
2161 &full_read_format,
2162 &codec,
2163 &stats_metadata(&[vec![1]], None),
2164 );
2165 assert!(postponed_time_plan.prefilter_builder.is_some());
2166 assert_eq!(
2167 remaining_simple_filter_columns(&postponed_time_plan.remaining_simple_filters),
2168 vec!["field_0", "ts"]
2169 );
2170
2171 let postponed_time_only_plan = build_reader_filter_plan(
2172 Some(&Predicate::new(vec![col("ts").gt_eq(lit(
2173 ScalarValue::TimestampMillisecond(Some(1), None),
2174 ))])),
2175 None,
2176 PreFilterMode::All,
2177 true,
2178 true,
2179 &full_read_format,
2180 &codec,
2181 &stats_metadata(&[vec![1]], None),
2182 );
2183 assert!(postponed_time_only_plan.prefilter_builder.is_none());
2184 assert_eq!(
2185 remaining_simple_filter_columns(&postponed_time_only_plan.remaining_simple_filters),
2186 vec!["ts"]
2187 );
2188
2189 let metric_metadata: RegionMetadataRef = Arc::new(sst_region_metadata_with_encoding(
2190 PrimaryKeyEncoding::Sparse,
2191 ));
2192 let field_0 = metric_metadata.column_by_name("field_0").unwrap().column_id;
2193 let ts = metric_metadata.time_index_column().column_id;
2194 let projected_read_format = FlatReadFormat::new(
2195 metric_metadata.clone(),
2196 ReadColumns::new([field_0, ts]),
2197 None,
2198 "test",
2199 true,
2200 )
2201 .unwrap();
2202 let metric_codec = build_primary_key_codec(metric_metadata.as_ref());
2203 let pk_prefilter_plan = build_reader_filter_plan(
2204 Some(&Predicate::new(vec![col("tag_0").eq(lit("a"))])),
2205 None,
2206 PreFilterMode::All,
2207 true,
2208 false,
2209 &projected_read_format,
2210 &metric_codec,
2211 &stats_metadata(&[vec![1]], None),
2212 );
2213 assert!(pk_prefilter_plan.prefilter_builder.is_some());
2214 assert!(
2215 pk_prefilter_plan
2216 .prefilter_builder
2217 .as_ref()
2218 .unwrap()
2219 .build_primary_key_filter()
2220 .is_some()
2221 );
2222 assert!(pk_prefilter_plan.remaining_simple_filters.is_empty());
2223
2224 let disabled_plan = build_reader_filter_plan(
2225 Some(&Predicate::new(vec![
2226 col("tag_0").eq(lit("a")),
2227 col("field_0").gt(lit(1_u64)),
2228 col("ts").gt_eq(lit(ScalarValue::TimestampMillisecond(Some(1), None))),
2229 ])),
2230 None,
2231 PreFilterMode::All,
2232 false,
2233 true,
2234 &projected_read_format,
2235 &metric_codec,
2236 &stats_metadata(&[vec![1]], None),
2237 );
2238 assert!(disabled_plan.prefilter_builder.is_none());
2239 assert_eq!(
2240 remaining_simple_filter_columns(&disabled_plan.remaining_simple_filters),
2241 vec!["tag_0", "field_0", "ts"]
2242 );
2243 }
2244
2245 #[test]
2246 fn test_pk_filter_expr_strings_are_stable_under_expr_order() {
2247 let metadata: RegionMetadataRef = Arc::new(sst_region_metadata_with_encoding(
2248 PrimaryKeyEncoding::Sparse,
2249 ));
2250 let read_format = FlatReadFormat::new(
2251 metadata.clone(),
2252 ReadColumns::new(metadata.column_metadatas.iter().map(|c| c.column_id)),
2253 None,
2254 "test",
2255 false,
2256 )
2257 .unwrap();
2258 let codec = build_primary_key_codec(metadata.as_ref());
2259
2260 let expr_a = col("tag_0").eq(lit("a"));
2261 let expr_b = col("tag_1").eq(lit("x"));
2262 let plan_ab = build_reader_filter_plan(
2263 Some(&Predicate::new(vec![expr_a.clone(), expr_b.clone()])),
2264 None,
2265 PreFilterMode::All,
2266 true,
2267 false,
2268 &read_format,
2269 &codec,
2270 &stats_metadata(&[vec![1]], None),
2271 );
2272 let plan_b_a = build_reader_filter_plan(
2273 Some(&Predicate::new(vec![expr_b, expr_a])),
2274 None,
2275 PreFilterMode::All,
2276 true,
2277 false,
2278 &read_format,
2279 &codec,
2280 &stats_metadata(&[vec![1]], None),
2281 );
2282
2283 let exprs_ab = plan_ab.prefilter_builder.unwrap().pk_filter_expr_strs;
2284 let exprs_b_a = plan_b_a.prefilter_builder.unwrap().pk_filter_expr_strs;
2285 assert!(exprs_ab.is_some());
2286 assert_eq!(exprs_ab, exprs_b_a);
2287 }
2288
2289 #[test]
2290 fn test_simple_and_physical_contexts_preserve_expr_strings() {
2291 let metadata: RegionMetadataRef = Arc::new(sst_region_metadata());
2292 let read_format = FlatReadFormat::new(
2293 metadata.clone(),
2294 ReadColumns::new(metadata.column_metadatas.iter().map(|c| c.column_id)),
2295 None,
2296 "test",
2297 true,
2298 )
2299 .unwrap();
2300
2301 let simple_expr = col("tag_0").eq(lit("a"));
2302 let simple = SimpleFilterContext::new_opt(&metadata, None, &simple_expr).unwrap();
2303 assert_eq!(simple.expr_str(), format!("{simple_expr:?}"));
2304
2305 let physical_expr = col("field_0").in_list(vec![lit(1_u64), lit(2_u64)], false);
2306 let physical =
2307 PhysicalFilterContext::new_opt(&metadata, None, &read_format, &physical_expr).unwrap();
2308 assert_eq!(physical.expr_str(), format!("{physical_expr:?}"));
2309 }
2310
2311 #[test]
2312 fn test_eval_simple_filter_mask_uses_flat_tag_columns_directly() {
2313 let metadata: RegionMetadataRef = Arc::new(sst_region_metadata());
2314 let filters = new_simple_filter_contexts(&metadata, &[col("tag_0").eq(lit("a"))]);
2315 let batch = new_record_batch_with_custom_sequence(&["a", "x"], 0, 4, 1);
2316
2317 let mask = eval_simple_filter_mask(&batch, &filters[0], "test").unwrap();
2318 assert_eq!(mask.count_set_bits(), 4);
2319 }
2320
2321 #[test]
2322 fn test_eval_simple_filter_mask_errors_on_missing_selected_column() {
2323 let metadata: RegionMetadataRef = Arc::new(sst_region_metadata());
2324 let filters = new_simple_filter_contexts(&metadata, &[col("tag_0").eq(lit("a"))]);
2325 let pk = new_primary_key(&["a", "x"]);
2326 let batch = new_raw_batch(&[pk.as_slice()], &[10]);
2327
2328 let err = eval_simple_filter_mask(&batch, &filters[0], "test").unwrap_err();
2329 let err = err.to_string();
2330 assert!(err.contains("Prefilter column"));
2331 assert!(err.contains("tag_0"));
2332 }
2333
2334 #[test]
2335 fn test_eval_physical_filter_mask_evaluates_physical_filters() {
2336 let metadata: RegionMetadataRef =
2337 Arc::new(sst_region_metadata_with_encoding(PrimaryKeyEncoding::Dense));
2338 let read_format = FlatReadFormat::new(
2339 metadata.clone(),
2340 ReadColumns::new(metadata.column_metadatas.iter().map(|c| c.column_id)),
2341 None,
2342 "test",
2343 false,
2344 )
2345 .unwrap();
2346 let expr = col("field_0").in_list(vec![lit(11_u64)], false);
2347 let physical_filters = new_physical_filter_contexts(&metadata, &read_format, &[expr]);
2348 let pk = new_primary_key(&["a", "x"]);
2349 let batch = new_raw_batch(&[pk.as_slice(), pk.as_slice(), pk.as_slice()], &[9, 10, 11]);
2350
2351 let mask = eval_physical_filter_mask(&batch, &physical_filters[0], "test").unwrap();
2352 assert_eq!(mask.count_set_bits(), 1);
2353 }
2354
2355 #[test]
2356 fn test_eval_pk_group_mask_finds_pk_column_by_name() {
2357 let metadata = Arc::new(sst_region_metadata());
2358 let filters = Arc::new(new_test_filters(&[col("tag_0").eq(lit("a"))]));
2359 let mut pk_filter = Some(Box::new(CachedPrimaryKeyFilter::new(
2360 build_primary_key_codec(metadata.as_ref()).primary_key_filter(&metadata, filters),
2361 )) as Box<dyn PrimaryKeyFilter>);
2362 let pk_a = new_primary_key(&["a", "x"]);
2363 let pk_b = new_primary_key(&["b", "x"]);
2364 let batch = new_prefilter_batch(
2365 &[
2366 pk_a.as_slice(),
2367 pk_a.as_slice(),
2368 pk_b.as_slice(),
2369 pk_b.as_slice(),
2370 ],
2371 &[10, 11, 12, 13],
2372 );
2373
2374 let mask = primary_key_filter_mask(&batch, pk_filter.as_mut().unwrap().as_mut()).unwrap();
2375
2376 assert_eq!(mask.count_set_bits(), 2);
2377 }
2378
2379 #[test]
2380 fn test_eval_pk_group_mask_handles_binary_pk_column() {
2381 let metadata = Arc::new(sst_region_metadata());
2382 let filters = Arc::new(new_test_filters(&[col("tag_0").eq(lit("a"))]));
2383 let mut pk_filter = Some(Box::new(CachedPrimaryKeyFilter::new(
2384 build_primary_key_codec(metadata.as_ref()).primary_key_filter(&metadata, filters),
2385 )) as Box<dyn PrimaryKeyFilter>);
2386 let pk_a = new_primary_key(&["a", "x"]);
2387 let pk_b = new_primary_key(&["b", "x"]);
2388 let batch = new_prefilter_batch_binary_pk(
2389 &[
2390 pk_a.as_slice(),
2391 pk_a.as_slice(),
2392 pk_b.as_slice(),
2393 pk_b.as_slice(),
2394 ],
2395 &[10, 11, 12, 13],
2396 );
2397
2398 let mask = primary_key_filter_mask(&batch, pk_filter.as_mut().unwrap().as_mut()).unwrap();
2399
2400 assert_eq!(mask.count_set_bits(), 2);
2401 }
2402}