1use std::collections::HashSet;
22use std::ops::{BitAnd, Range};
23use std::sync::Arc;
24
25use api::v1::SemanticType;
26use common_recordbatch::filter::SimpleFilterEvaluator;
27use datatypes::arrow::array::{Array, BinaryArray, BooleanArray, BooleanBufferBuilder};
28use datatypes::arrow::buffer::BooleanBuffer;
29use datatypes::arrow::datatypes::SchemaRef;
30use datatypes::arrow::record_batch::RecordBatch;
31use futures::StreamExt;
32use mito_codec::row_converter::{PrimaryKeyCodec, PrimaryKeyFilter, build_primary_key_codec};
33use parquet::arrow::ProjectionMask;
34use parquet::arrow::arrow_reader::RowSelection;
35use parquet::schema::types::SchemaDescriptor;
36use smallvec::{SmallVec, smallvec};
37use snafu::{OptionExt, ResultExt};
38use store_api::metadata::{RegionMetadata, RegionMetadataRef};
39use store_api::storage::consts::PRIMARY_KEY_COLUMN_NAME;
40use table::predicate::Predicate;
41
42use crate::cache::PrefilterKey;
43use crate::error::{
44 ComputeArrowSnafu, DecodeSnafu, EvalPartitionFilterSnafu, NewRecordBatchSnafu,
45 RecordBatchSnafu, Result, UnexpectedSnafu,
46};
47use crate::sst::parquet::file_range::PreFilterMode;
48use crate::sst::parquet::flat_format::FlatReadFormat;
49use crate::sst::parquet::format::PrimaryKeyArray;
50use crate::sst::parquet::reader::{
51 MaybeFilter, PhysicalFilterContext, RowGroupBuildContext, RowGroupReaderBuilder,
52 SimpleFilterContext,
53};
54
55pub(crate) fn matching_row_ranges_by_primary_key(
56 input: &RecordBatch,
57 pk_column_index: usize,
58 pk_filter: &mut dyn PrimaryKeyFilter,
59) -> Result<Vec<Range<usize>>> {
60 let pk_column = input.column(pk_column_index);
61 if let Some(pk_dict_array) = pk_column.as_any().downcast_ref::<PrimaryKeyArray>() {
62 matching_row_ranges_from_dict(pk_dict_array, input.num_rows(), pk_filter)
63 } else if let Some(pk_binary_array) = pk_column.as_any().downcast_ref::<BinaryArray>() {
64 matching_row_ranges_from_binary(pk_binary_array, input.num_rows(), pk_filter)
65 } else {
66 UnexpectedSnafu {
67 reason: format!(
68 "Primary key column is neither a dictionary nor a binary array, got {:?}",
69 pk_column.data_type()
70 ),
71 }
72 .fail()
73 }
74}
75
76fn push_matched_range(
79 matched_row_ranges: &mut Vec<Range<usize>>,
80 pk_filter: &mut dyn PrimaryKeyFilter,
81 pk: &[u8],
82 start: usize,
83 end: usize,
84) -> Result<()> {
85 if pk_filter.matches(pk).context(DecodeSnafu)? {
86 if let Some(last) = matched_row_ranges.last_mut()
87 && last.end == start
88 {
89 last.end = end;
90 } else {
91 matched_row_ranges.push(start..end);
92 }
93 }
94 Ok(())
95}
96
97fn matching_row_ranges_from_dict(
99 pk_dict_array: &PrimaryKeyArray,
100 num_rows: usize,
101 pk_filter: &mut dyn PrimaryKeyFilter,
102) -> Result<Vec<Range<usize>>> {
103 let pk_values = pk_dict_array
104 .values()
105 .as_any()
106 .downcast_ref::<BinaryArray>()
107 .context(UnexpectedSnafu {
108 reason: "Primary key values are not binary array",
109 })?;
110 let keys = pk_dict_array.keys();
111 let key_values = keys.values();
112
113 if key_values.is_empty() {
114 return Ok(std::iter::once(0..num_rows).collect());
115 }
116
117 let mut matched_row_ranges: Vec<Range<usize>> = Vec::new();
118 let mut start = 0;
119 while start < key_values.len() {
120 let key = key_values[start];
121 let mut end = start + 1;
122 while end < key_values.len() && key_values[end] == key {
123 end += 1;
124 }
125
126 push_matched_range(
127 &mut matched_row_ranges,
128 pk_filter,
129 pk_values.value(key as usize),
130 start,
131 end,
132 )?;
133
134 start = end;
135 }
136
137 Ok(matched_row_ranges)
138}
139
140fn matching_row_ranges_from_binary(
146 pk_array: &BinaryArray,
147 num_rows: usize,
148 pk_filter: &mut dyn PrimaryKeyFilter,
149) -> Result<Vec<Range<usize>>> {
150 if pk_array.is_empty() {
151 return Ok(std::iter::once(0..num_rows).collect());
152 }
153
154 let mut matched_row_ranges: Vec<Range<usize>> = Vec::new();
155 let mut start = 0;
156 while start < pk_array.len() {
157 let value = pk_array.value(start);
158 let mut end = start + 1;
159 while end < pk_array.len() && pk_array.value(end) == value {
160 end += 1;
161 }
162
163 push_matched_range(&mut matched_row_ranges, pk_filter, value, start, end)?;
164
165 start = end;
166 }
167
168 Ok(matched_row_ranges)
169}
170
171pub(crate) fn prefilter_flat_batch_by_primary_key(
174 input: RecordBatch,
175 pk_column_index: usize,
176 pk_filter: &mut dyn PrimaryKeyFilter,
177) -> Result<Option<RecordBatch>> {
178 if input.num_rows() == 0 {
179 return Ok(Some(input));
180 }
181
182 let matched_row_ranges =
183 matching_row_ranges_by_primary_key(&input, pk_column_index, pk_filter)?;
184 if matched_row_ranges.is_empty() {
185 return Ok(None);
186 }
187
188 if matched_row_ranges.len() == 1
189 && matched_row_ranges[0].start == 0
190 && matched_row_ranges[0].end == input.num_rows()
191 {
192 return Ok(Some(input));
193 }
194
195 if matched_row_ranges.len() == 1 {
196 let span = &matched_row_ranges[0];
197 return Ok(Some(input.slice(span.start, span.end - span.start)));
198 }
199
200 let mut builder = BooleanBufferBuilder::new(input.num_rows());
201 builder.append_n(input.num_rows(), false);
202 for span in matched_row_ranges {
203 for i in span {
204 builder.set_bit(i, true);
205 }
206 }
207
208 let filtered = datatypes::arrow::compute::filter_record_batch(
209 &input,
210 &BooleanArray::new(builder.finish(), None),
211 )
212 .context(ComputeArrowSnafu)?;
213 if filtered.num_rows() == 0 {
214 Ok(None)
215 } else {
216 Ok(Some(filtered))
217 }
218}
219
220pub(crate) struct CachedPrimaryKeyFilter {
221 inner: Box<dyn PrimaryKeyFilter>,
222 last_primary_key: Vec<u8>,
223 last_match: Option<bool>,
224}
225
226impl CachedPrimaryKeyFilter {
227 pub(crate) fn new(inner: Box<dyn PrimaryKeyFilter>) -> Self {
228 Self {
229 inner,
230 last_primary_key: Vec::new(),
231 last_match: None,
232 }
233 }
234}
235
236impl PrimaryKeyFilter for CachedPrimaryKeyFilter {
237 fn matches(&mut self, pk: &[u8]) -> mito_codec::error::Result<bool> {
238 if let Some(last_match) = self.last_match
239 && self.last_primary_key == pk
240 {
241 return Ok(last_match);
242 }
243
244 let matched = self.inner.matches(pk)?;
245 self.last_primary_key.clear();
246 self.last_primary_key.extend_from_slice(pk);
247 self.last_match = Some(matched);
248 Ok(matched)
249 }
250}
251
252pub(crate) struct BulkFilterPlan {
258 pub(crate) remaining_simple_filters: Vec<SimpleFilterContext>,
260 pub(crate) pk_filters: Option<Arc<Vec<SimpleFilterEvaluator>>>,
263}
264
265pub(crate) fn build_primary_key_filter(
271 sst_metadata: &RegionMetadataRef,
272 expected_metadata: Option<&RegionMetadata>,
273 predicate: Option<&Predicate>,
274) -> Option<CachedPrimaryKeyFilter> {
275 let filters = predicate
276 .into_iter()
277 .flat_map(|predicate| predicate.exprs())
278 .filter_map(|expr| SimpleFilterContext::new_opt(sst_metadata, expected_metadata, expr))
279 .filter_map(|filter_ctx| {
280 (filter_ctx.semantic_type() == SemanticType::Tag)
281 .then(|| filter_ctx.filter().as_filter().cloned())
282 .flatten()
283 })
284 .collect::<Vec<_>>();
285 if filters.is_empty() {
286 return None;
287 }
288
289 let codec = build_primary_key_codec(sst_metadata.as_ref());
290 let filter = codec.primary_key_filter(sst_metadata, Arc::new(filters));
291 Some(CachedPrimaryKeyFilter::new(filter))
292}
293
294pub(crate) struct ReaderFilterPlan {
302 pub(crate) remaining_simple_filters: Vec<SimpleFilterContext>,
307 pub(crate) prefilter_builder: Option<PrefilterContextBuilder>,
311}
312
313pub(crate) fn build_bulk_filter_plan(
314 read_format: &FlatReadFormat,
315 predicate: Option<&Predicate>,
316) -> BulkFilterPlan {
317 let metadata = read_format.metadata();
318 let simple_filters: Vec<SimpleFilterContext> = predicate
321 .into_iter()
322 .flat_map(|predicate| {
323 predicate
324 .exprs()
325 .iter()
326 .filter_map(|expr| SimpleFilterContext::new_opt(metadata, None, expr))
327 })
328 .collect();
329
330 if read_format.batch_has_raw_pk_columns() || metadata.primary_key.is_empty() {
334 return BulkFilterPlan {
335 remaining_simple_filters: simple_filters,
336 pk_filters: None,
337 };
338 }
339
340 let mut remaining_simple_filters = Vec::new();
341 let mut pk_filters = Vec::new();
342
343 for filter_ctx in simple_filters {
344 let pk_filter = filter_ctx.filter().as_filter().and_then(|filter| {
347 (filter_ctx.semantic_type() == SemanticType::Tag).then(|| filter.clone())
348 });
349
350 if let Some(pk_filter) = pk_filter {
351 pk_filters.push(pk_filter);
352 } else {
353 remaining_simple_filters.push(filter_ctx);
354 }
355 }
356
357 BulkFilterPlan {
358 remaining_simple_filters,
359 pk_filters: (!pk_filters.is_empty()).then_some(Arc::new(pk_filters)),
360 }
361}
362
363pub(crate) fn build_reader_filter_plan(
379 predicate: Option<&Predicate>,
380 expected_metadata: Option<&RegionMetadata>,
381 pre_filter_mode: PreFilterMode,
382 enable_predicate_prefilter: bool,
383 read_format: &FlatReadFormat,
384 codec: &Arc<dyn PrimaryKeyCodec>,
385) -> ReaderFilterPlan {
386 let Some(predicate) = predicate else {
387 return ReaderFilterPlan {
388 remaining_simple_filters: Vec::new(),
389 prefilter_builder: None,
390 };
391 };
392
393 let metadata = read_format.metadata();
394 let mut prefilter_simple_filters = Vec::new();
395 let mut remaining_simple_filters = Vec::new();
396 let mut prefilter_physical_filters = Vec::new();
397 let mut primary_key_filters = Vec::new();
398 let mut pk_filter_contexts = Vec::new();
399
400 let field_prefilter_enabled = pre_filter_mode == PreFilterMode::All;
404 let need_pk_prefilter = !read_format.batch_has_raw_pk_columns();
408
409 let can_direct_prefilter = |semantic_type: SemanticType| -> bool {
412 match semantic_type {
413 SemanticType::Tag => !need_pk_prefilter,
414 SemanticType::Field => field_prefilter_enabled,
415 SemanticType::Timestamp => true,
416 }
417 };
418
419 for expr in predicate.exprs() {
420 if let Some(filter_ctx) = SimpleFilterContext::new_opt(metadata, expected_metadata, expr) {
423 if !enable_predicate_prefilter {
424 remaining_simple_filters.push(filter_ctx);
425 continue;
426 }
427
428 let Some(filter) = filter_ctx.filter().as_filter() else {
432 remaining_simple_filters.push(filter_ctx);
433 continue;
434 };
435
436 let direct_prefilter = can_direct_prefilter(filter_ctx.semantic_type());
439 if direct_prefilter {
440 assert!(
441 read_format
442 .arrow_schema()
443 .column_with_name(filter.column_name())
444 .is_some(),
445 "Column '{}' is not present in the arrow schema {:?}",
446 filter.column_name(),
447 read_format.arrow_schema(),
448 );
449 prefilter_simple_filters.push(filter_ctx);
450 continue;
451 }
452
453 if need_pk_prefilter && filter_ctx.semantic_type() == SemanticType::Tag {
455 primary_key_filters.push(filter.clone());
456 pk_filter_contexts.push(filter_ctx);
457 } else {
458 remaining_simple_filters.push(filter_ctx);
459 }
460 continue;
461 }
462
463 if !enable_predicate_prefilter {
464 continue;
465 }
466
467 if let Some(filter) =
472 PhysicalFilterContext::new_opt(metadata, expected_metadata, read_format, expr)
473 && can_direct_prefilter(filter.semantic_type())
474 {
475 prefilter_physical_filters.push(filter);
476 }
477 }
478
479 if !enable_predicate_prefilter {
480 return ReaderFilterPlan {
481 remaining_simple_filters,
482 prefilter_builder: None,
483 };
484 }
485
486 let pk_filter_expr_strs = (!pk_filter_contexts.is_empty()).then(|| {
487 let mut expr_strs = pk_filter_contexts
488 .iter()
489 .map(|filter_ctx| filter_ctx.expr_str().to_string())
490 .collect::<Vec<_>>();
491 expr_strs.sort();
492 SmallVec::from_vec(expr_strs)
493 });
494 let pk_filter_exprs =
495 (!primary_key_filters.is_empty()).then_some(Arc::new(primary_key_filters));
496 let schema_version = expected_metadata
497 .map(|metadata| metadata.schema_version)
498 .unwrap_or_else(|| read_format.metadata().schema_version);
499 let prefilter_builder = PrefilterContextBuilder::new(
500 read_format,
501 codec,
502 pk_filter_exprs,
503 pk_filter_expr_strs,
504 prefilter_simple_filters.clone(),
505 prefilter_physical_filters,
506 schema_version,
507 );
508
509 if prefilter_builder.is_some() {
510 ReaderFilterPlan {
511 remaining_simple_filters,
512 prefilter_builder,
513 }
514 } else {
515 remaining_simple_filters.extend(prefilter_simple_filters);
518 remaining_simple_filters.extend(pk_filter_contexts);
519 ReaderFilterPlan {
520 remaining_simple_filters,
521 prefilter_builder: None,
522 }
523 }
524}
525
526pub(crate) struct PrefilterContext {
528 pk_filter: Option<Box<dyn PrimaryKeyFilter>>,
530 filters: Vec<SimpleFilterContext>,
532 physical_filters: Vec<PhysicalFilterContext>,
535 schema_version: u64,
537 pk_filter_expr_strs: Option<SmallVec<[String; 1]>>,
539 arrow_schema: SchemaRef,
541}
542
543pub(crate) struct PrefilterContextBuilder {
549 pk_filters: Option<Arc<Vec<SimpleFilterEvaluator>>>,
550 pk_filter_expr_strs: Option<SmallVec<[String; 1]>>,
551 filters: Vec<SimpleFilterContext>,
552 physical_filters: Vec<PhysicalFilterContext>,
553 codec: Arc<dyn PrimaryKeyCodec>,
554 metadata: RegionMetadataRef,
555 schema_version: u64,
556 arrow_schema: SchemaRef,
557}
558
559impl PrefilterContextBuilder {
560 pub(crate) fn new(
567 read_format: &FlatReadFormat,
568 codec: &Arc<dyn PrimaryKeyCodec>,
569 primary_key_filters: Option<Arc<Vec<SimpleFilterEvaluator>>>,
570 primary_key_filter_expr_strs: Option<SmallVec<[String; 1]>>,
571 filters: Vec<SimpleFilterContext>,
572 physical_filters: Vec<PhysicalFilterContext>,
573 schema_version: u64,
574 ) -> Option<Self> {
575 let metadata = read_format.metadata();
576 let use_raw_tag_columns = read_format.batch_has_raw_pk_columns();
577 let pk_filters = (!use_raw_tag_columns)
578 .then_some(primary_key_filters)
579 .flatten()
580 .filter(|filters| !filters.is_empty());
581 let pk_filter_expr_strs = pk_filters
582 .is_some()
583 .then_some(primary_key_filter_expr_strs)
584 .flatten();
585
586 let mut prefilter_column_names = HashSet::new();
587 for filter_ctx in &filters {
588 if let MaybeFilter::Filter(filter) = filter_ctx.filter() {
589 prefilter_column_names.insert(filter.column_name().to_string());
590 }
591 }
592
593 if pk_filters.is_some() {
594 prefilter_column_names.insert(PRIMARY_KEY_COLUMN_NAME.to_string());
595 }
596
597 for filter_ctx in &physical_filters {
598 prefilter_column_names.insert(filter_ctx.column_name().to_string());
599 }
600
601 let prefilter_count =
602 compute_projection_count(&prefilter_column_names, read_format.arrow_schema());
603
604 if prefilter_count == 0 {
605 return None;
606 }
607
608 let total_count = read_format.parquet_read_columns().root_indices().len();
609 let remaining_count = total_count.saturating_sub(prefilter_count);
610 if pk_filters.is_none() && prefilter_count >= total_count {
611 return None;
612 }
613
614 if pk_filters.is_none()
615 && !should_use_prefilter(prefilter_count, remaining_count, total_count)
616 {
617 return None;
618 }
619
620 Some(Self {
621 pk_filters,
622 pk_filter_expr_strs,
623 filters,
624 physical_filters,
625 codec: Arc::clone(codec),
626 metadata: metadata.clone(),
627 schema_version,
628 arrow_schema: read_format.arrow_schema().clone(),
629 })
630 }
631
632 pub(crate) fn build(&self) -> PrefilterContext {
634 let pk_filter = self
635 .build_primary_key_filter()
636 .map(|filter| Box::new(filter) as Box<dyn PrimaryKeyFilter>);
637 PrefilterContext {
638 pk_filter,
639 filters: self.filters.clone(),
640 physical_filters: self.physical_filters.clone(),
641 schema_version: self.schema_version,
642 pk_filter_expr_strs: self.pk_filter_expr_strs.clone(),
643 arrow_schema: self.arrow_schema.clone(),
644 }
645 }
646
647 pub(crate) fn build_primary_key_filter(&self) -> Option<CachedPrimaryKeyFilter> {
649 self.pk_filters.as_ref().map(|pk_filters| {
650 let filter = self
651 .codec
652 .primary_key_filter(&self.metadata, Arc::clone(pk_filters));
653 CachedPrimaryKeyFilter::new(filter)
654 })
655 }
656}
657
658const PREFILTER_COLUMN_RATIO_THRESHOLD: f64 = 0.5;
659const PREFILTER_MIN_REMAINING_COLUMNS: usize = 2;
660
661pub(crate) struct PrefilterResult {
663 pub(crate) refined_selection: RowSelection,
665 pub(crate) filtered_rows: usize,
667}
668
669fn compute_projection_mask(
674 column_names: &HashSet<String>,
675 arrow_schema: &datatypes::arrow::datatypes::SchemaRef,
676 parquet_schema: &SchemaDescriptor,
677) -> ProjectionMask {
678 ProjectionMask::roots(
679 parquet_schema,
680 projection_indices(column_names, arrow_schema),
681 )
682}
683
684fn compute_projection_count(
685 column_names: &HashSet<String>,
686 arrow_schema: &datatypes::arrow::datatypes::SchemaRef,
687) -> usize {
688 projection_indices(column_names, arrow_schema).len()
689}
690
691fn projection_indices(
692 column_names: &HashSet<String>,
693 arrow_schema: &datatypes::arrow::datatypes::SchemaRef,
694) -> Vec<usize> {
695 let mut projection_indices: Vec<usize> = column_names
696 .iter()
697 .filter_map(|name| arrow_schema.column_with_name(name).map(|(index, _)| index))
698 .collect();
699 projection_indices.sort_unstable();
700 projection_indices.dedup();
701 projection_indices
702}
703
704fn should_use_prefilter(
705 prefilter_count: usize,
706 remaining_count: usize,
707 total_count: usize,
708) -> bool {
709 if remaining_count == 0 {
710 return false;
711 }
712
713 if remaining_count < PREFILTER_MIN_REMAINING_COLUMNS {
714 return false;
715 }
716
717 let ratio = prefilter_count as f64 / total_count as f64;
718 ratio <= PREFILTER_COLUMN_RATIO_THRESHOLD
719}
720
721pub(crate) async fn execute_prefilter(
722 prefilter_ctx: &mut PrefilterContext,
723 reader_builder: &RowGroupReaderBuilder,
724 build_ctx: &RowGroupBuildContext<'_>,
725) -> Result<PrefilterResult> {
726 let entries = build_prefilter_cache_entries(prefilter_ctx, reader_builder, build_ctx);
727
728 if entries.is_empty() {
729 return execute_prefilter_by_reading_columns(prefilter_ctx, reader_builder, build_ctx)
730 .await;
731 }
732
733 execute_prefilter_with_result_cache(prefilter_ctx, reader_builder, build_ctx, entries).await
734}
735
736async fn execute_prefilter_with_result_cache(
737 prefilter_ctx: &mut PrefilterContext,
738 reader_builder: &RowGroupReaderBuilder,
739 build_ctx: &RowGroupBuildContext<'_>,
740 entries: Vec<PrefilterEntry>,
741) -> Result<PrefilterResult> {
742 let non_cacheable_physical = non_cacheable_physical_filters(prefilter_ctx);
743 let mut hit_mask: Option<BooleanBuffer> = None;
744 let mut misses = Vec::new();
745 for entry in entries {
746 let Some(key) = &entry.key else {
747 misses.push(entry);
748 continue;
749 };
750
751 if let Some(mask) = reader_builder.cache_strategy().get_prefilter_result(key) {
752 hit_mask = Some(match hit_mask {
753 Some(hit_mask) => hit_mask.bitand(mask.as_ref()),
754 None => mask.as_ref().clone(),
755 });
756 } else {
757 misses.push(entry);
758 }
759 }
760
761 if misses.is_empty() && non_cacheable_physical.is_empty() {
762 let combined_mask = hit_mask.unwrap_or_else(|| BooleanBuffer::new_set(0));
763 let refined_selection =
764 refined_selection_from_mask(&combined_mask, &build_ctx.row_selection);
765 let rows_before_filter = rows_before_filter(reader_builder, build_ctx);
766 let filtered_rows = rows_before_filter.saturating_sub(refined_selection.row_count());
767 return Ok(PrefilterResult {
768 refined_selection,
769 filtered_rows,
770 });
771 }
772
773 let mut uncached_entries = misses;
774 uncached_entries.extend(
775 non_cacheable_physical
776 .iter()
777 .copied()
778 .map(|idx| PrefilterEntry::without_cache(PrefilterEntryKind::Physical(idx))),
779 );
780 let (uncached_mask, read_rows) =
781 build_prefilter_masks(prefilter_ctx, reader_builder, build_ctx, &uncached_entries).await?;
782
783 let final_mask = match (hit_mask, uncached_mask) {
784 (Some(hit_mask), Some(uncached_mask)) => hit_mask.bitand(&uncached_mask),
785 (Some(hit_mask), None) => hit_mask,
786 (None, Some(uncached_mask)) => uncached_mask,
787 (None, None) => BooleanBuffer::new_set(read_rows),
788 };
789 debug_assert_eq!(final_mask.len(), read_rows);
790 let rows_selected = final_mask.count_set_bits();
791 let filtered_rows = read_rows.saturating_sub(rows_selected);
792 let refined_selection = refined_selection_from_mask(&final_mask, &build_ctx.row_selection);
793
794 Ok(PrefilterResult {
795 refined_selection,
796 filtered_rows,
797 })
798}
799
800fn non_cacheable_physical_filters(prefilter_ctx: &PrefilterContext) -> Vec<usize> {
801 prefilter_ctx
802 .physical_filters
803 .iter()
804 .enumerate()
805 .filter_map(|(idx, filter)| (!filter.is_immutable()).then_some(idx))
806 .collect()
807}
808
809async fn build_prefilter_masks(
810 prefilter_ctx: &mut PrefilterContext,
811 reader_builder: &RowGroupReaderBuilder,
812 build_ctx: &RowGroupBuildContext<'_>,
813 entries: &[PrefilterEntry],
814) -> Result<(Option<BooleanBuffer>, usize)> {
815 let prefilter_column_names = prefilter_column_names_for_entries(prefilter_ctx, entries);
816 let parquet_schema = reader_builder
817 .parquet_metadata()
818 .file_metadata()
819 .schema_descr();
820 let projection = compute_projection_mask(
821 &prefilter_column_names,
822 &prefilter_ctx.arrow_schema,
823 parquet_schema,
824 );
825
826 let mut stream = reader_builder
827 .build_with_projection(
828 build_ctx.row_group_idx,
829 build_ctx.row_selection.clone(),
830 projection,
831 build_ctx.fetch_metrics,
832 )
833 .await?;
834
835 let mut cache_builders = entries
836 .iter()
837 .map(|entry| entry.key.is_some().then(|| BooleanBufferBuilder::new(0)))
838 .collect::<Vec<_>>();
839 let mut combined_builder = (!entries.is_empty()).then(|| BooleanBufferBuilder::new(0));
840 let mut rows_before_filter = 0usize;
841
842 while let Some(batch_result) = stream.next().await {
843 let batch = batch_result?;
844 let num_rows = batch.num_rows();
845 if num_rows == 0 {
846 continue;
847 }
848 rows_before_filter += num_rows;
849
850 let mut batch_mask = BooleanBuffer::new_set(num_rows);
851 for (idx, entry) in entries.iter().enumerate() {
852 let mask = eval_entry_mask(
853 &batch,
854 prefilter_ctx,
855 entry.kind,
856 reader_builder.file_path(),
857 )?;
858 batch_mask = batch_mask.bitand(&mask);
859 if let Some(Some(builder)) = cache_builders.get_mut(idx) {
860 builder.append_buffer(&mask);
861 }
862 }
863 if let Some(builder) = &mut combined_builder {
864 builder.append_buffer(&batch_mask);
865 }
866 }
867
868 for (entry, builder) in entries.iter().zip(cache_builders) {
869 if let (Some(key), Some(mut builder)) = (&entry.key, builder) {
870 reader_builder
871 .cache_strategy()
872 .put_prefilter_result(key.clone(), Arc::new(builder.finish()));
873 }
874 }
875
876 Ok((
877 combined_builder.map(|mut builder| builder.finish()),
878 rows_before_filter,
879 ))
880}
881
882fn prefilter_column_names_for_entries(
883 prefilter_ctx: &PrefilterContext,
884 entries: &[PrefilterEntry],
885) -> HashSet<String> {
886 let mut prefilter_column_names = HashSet::new();
887 for entry in entries {
888 match entry.kind {
889 PrefilterEntryKind::Simple(idx) => {
890 if let MaybeFilter::Filter(filter) = prefilter_ctx.filters[idx].filter() {
891 prefilter_column_names.insert(filter.column_name().to_string());
892 }
893 }
894 PrefilterEntryKind::Physical(idx) => {
895 prefilter_column_names.insert(
896 prefilter_ctx.physical_filters[idx]
897 .column_name()
898 .to_string(),
899 );
900 }
901 PrefilterEntryKind::PkGroup => {
902 prefilter_column_names.insert(PRIMARY_KEY_COLUMN_NAME.to_string());
903 }
904 }
905 }
906 prefilter_column_names
907}
908
909async fn execute_prefilter_by_reading_columns(
910 prefilter_ctx: &mut PrefilterContext,
911 reader_builder: &RowGroupReaderBuilder,
912 build_ctx: &RowGroupBuildContext<'_>,
913) -> Result<PrefilterResult> {
914 let entries = all_prefilter_entries(prefilter_ctx);
915 let (mask, rows_before_filter) =
916 build_prefilter_masks(prefilter_ctx, reader_builder, build_ctx, &entries).await?;
917
918 let final_mask = mask.unwrap_or_else(|| BooleanBuffer::new_set(rows_before_filter));
919 let rows_selected = final_mask.count_set_bits();
920 let filtered_rows = rows_before_filter.saturating_sub(rows_selected);
921 let refined_selection = refined_selection_from_mask(&final_mask, &build_ctx.row_selection);
922
923 Ok(PrefilterResult {
924 refined_selection,
925 filtered_rows,
926 })
927}
928
929fn all_prefilter_entries(prefilter_ctx: &PrefilterContext) -> Vec<PrefilterEntry> {
930 let mut entries = Vec::new();
931 if prefilter_ctx.pk_filter.is_some() {
932 entries.push(PrefilterEntry::without_cache(PrefilterEntryKind::PkGroup));
933 }
934 entries.extend(
935 prefilter_ctx
936 .filters
937 .iter()
938 .enumerate()
939 .map(|(idx, _)| PrefilterEntry::without_cache(PrefilterEntryKind::Simple(idx))),
940 );
941 entries.extend(
942 prefilter_ctx
943 .physical_filters
944 .iter()
945 .enumerate()
946 .map(|(idx, _)| PrefilterEntry::without_cache(PrefilterEntryKind::Physical(idx))),
947 );
948 entries
949}
950
951#[derive(Clone, Copy)]
952enum PrefilterEntryKind {
953 Simple(usize),
954 Physical(usize),
955 PkGroup,
956}
957
958struct PrefilterEntry {
959 kind: PrefilterEntryKind,
960 key: Option<PrefilterKey>,
961}
962
963impl PrefilterEntry {
964 fn without_cache(kind: PrefilterEntryKind) -> Self {
965 Self { kind, key: None }
966 }
967}
968
969fn build_prefilter_cache_entries(
970 prefilter_ctx: &PrefilterContext,
971 reader_builder: &RowGroupReaderBuilder,
972 build_ctx: &RowGroupBuildContext<'_>,
973) -> Vec<PrefilterEntry> {
974 let row_selection = PrefilterKey::row_selection_snapshot(build_ctx.row_selection.as_ref());
975 let file_id = reader_builder.file_handle().file_id().file_id();
976 let row_group_idx = build_ctx.row_group_idx as u32;
977 let mut entries = Vec::new();
978
979 for (idx, filter_ctx) in prefilter_ctx.filters.iter().enumerate() {
980 entries.push(PrefilterEntry {
981 kind: PrefilterEntryKind::Simple(idx),
982 key: Some(PrefilterKey::new(
983 file_id,
984 row_group_idx,
985 row_selection.clone(),
986 prefilter_ctx.schema_version,
987 smallvec![filter_ctx.expr_str().to_string()],
988 )),
989 });
990 }
991
992 for (idx, filter_ctx) in prefilter_ctx.physical_filters.iter().enumerate() {
993 if !filter_ctx.is_immutable() {
994 continue;
995 }
996 entries.push(PrefilterEntry {
997 kind: PrefilterEntryKind::Physical(idx),
998 key: Some(PrefilterKey::new(
999 file_id,
1000 row_group_idx,
1001 row_selection.clone(),
1002 prefilter_ctx.schema_version,
1003 smallvec![filter_ctx.expr_str().to_string()],
1004 )),
1005 });
1006 }
1007
1008 if prefilter_ctx.pk_filter.is_some()
1009 && let Some(exprs) = &prefilter_ctx.pk_filter_expr_strs
1010 {
1011 entries.push(PrefilterEntry {
1012 kind: PrefilterEntryKind::PkGroup,
1013 key: Some(PrefilterKey::new(
1014 file_id,
1015 row_group_idx,
1016 row_selection,
1017 prefilter_ctx.schema_version,
1018 exprs.clone(),
1019 )),
1020 });
1021 }
1022
1023 entries
1024}
1025
1026fn rows_before_filter(
1027 reader_builder: &RowGroupReaderBuilder,
1028 build_ctx: &RowGroupBuildContext<'_>,
1029) -> usize {
1030 build_ctx.row_selection.as_ref().map_or_else(
1031 || {
1032 reader_builder
1033 .parquet_metadata()
1034 .row_group(build_ctx.row_group_idx)
1035 .num_rows() as usize
1036 },
1037 RowSelection::row_count,
1038 )
1039}
1040
1041fn refined_selection_from_mask(
1042 mask: &BooleanBuffer,
1043 original_selection: &Option<RowSelection>,
1044) -> RowSelection {
1045 if mask.is_empty() || mask.count_set_bits() == 0 {
1046 return RowSelection::from(vec![]);
1047 }
1048
1049 let prefilter_selection = RowSelection::from_filters(&[BooleanArray::from(mask.clone())]);
1050 match original_selection {
1051 Some(original) => original.and_then(&prefilter_selection),
1052 None => prefilter_selection,
1053 }
1054}
1055
1056fn eval_entry_mask(
1057 batch: &RecordBatch,
1058 prefilter_ctx: &mut PrefilterContext,
1059 kind: PrefilterEntryKind,
1060 file_path: &str,
1061) -> Result<BooleanBuffer> {
1062 match kind {
1063 PrefilterEntryKind::Simple(idx) => {
1064 eval_simple_filter_mask(batch, &prefilter_ctx.filters[idx], file_path)
1065 }
1066 PrefilterEntryKind::Physical(idx) => {
1067 eval_physical_filter_mask(batch, &prefilter_ctx.physical_filters[idx], file_path)
1068 }
1069 PrefilterEntryKind::PkGroup => {
1070 let pk_filter = prefilter_ctx.pk_filter.as_mut().context(UnexpectedSnafu {
1071 reason: "Missing primary key filter for prefilter cache entry",
1072 })?;
1073 primary_key_filter_mask(batch, pk_filter.as_mut())
1074 }
1075 }
1076}
1077
1078pub(crate) fn primary_key_filter_mask(
1080 batch: &RecordBatch,
1081 pk_filter: &mut dyn PrimaryKeyFilter,
1082) -> Result<BooleanBuffer> {
1083 let (pk_column_index, _) = batch
1084 .schema()
1085 .column_with_name(PRIMARY_KEY_COLUMN_NAME)
1086 .context(UnexpectedSnafu {
1087 reason: "Primary key column not found in prefilter batch",
1088 })?;
1089 let matched_row_ranges = matching_row_ranges_by_primary_key(batch, pk_column_index, pk_filter)?;
1090 let mut builder = BooleanBufferBuilder::new(batch.num_rows());
1091 builder.append_n(batch.num_rows(), false);
1092 for range in matched_row_ranges {
1093 for row in range {
1094 builder.set_bit(row, true);
1095 }
1096 }
1097 Ok(builder.finish())
1098}
1099
1100fn eval_simple_filter_mask(
1101 batch: &RecordBatch,
1102 filter_ctx: &SimpleFilterContext,
1103 file_path: &str,
1104) -> Result<BooleanBuffer> {
1105 let filter = match filter_ctx.filter() {
1106 MaybeFilter::Filter(filter) => filter,
1107 MaybeFilter::Matched => return Ok(BooleanBuffer::new_set(batch.num_rows())),
1108 MaybeFilter::Pruned => return Ok(BooleanBuffer::new_unset(batch.num_rows())),
1109 };
1110
1111 let (idx, _) = batch
1112 .schema()
1113 .column_with_name(filter.column_name())
1114 .with_context(|| UnexpectedSnafu {
1115 reason: format!(
1116 "Prefilter column '{}' (id {}) not found in batch for file {}",
1117 filter.column_name(),
1118 filter_ctx.column_id(),
1119 file_path
1120 ),
1121 })?;
1122 let column = batch.column(idx).clone();
1123 filter.evaluate_array(&column).context(RecordBatchSnafu)
1124}
1125
1126fn eval_physical_filter_mask(
1127 batch: &RecordBatch,
1128 filter_ctx: &PhysicalFilterContext,
1129 file_path: &str,
1130) -> Result<BooleanBuffer> {
1131 let filter = filter_ctx.filter();
1132
1133 let (idx, _) = batch
1134 .schema()
1135 .column_with_name(filter_ctx.column_name())
1136 .with_context(|| UnexpectedSnafu {
1137 reason: format!(
1138 "Prefilter physical column '{}' (id {}) not found in batch for file {}",
1139 filter_ctx.column_name(),
1140 filter_ctx.column_id(),
1141 file_path
1142 ),
1143 })?;
1144 let column = batch.column(idx).clone();
1145
1146 let record_batch = RecordBatch::try_new(filter_ctx.schema().clone(), vec![column])
1147 .context(NewRecordBatchSnafu)?;
1148 let evaluated = filter
1149 .evaluate(&record_batch)
1150 .context(EvalPartitionFilterSnafu)?;
1151 let array = evaluated
1152 .into_array(record_batch.num_rows())
1153 .context(EvalPartitionFilterSnafu)?;
1154 let boolean_array = array
1155 .as_any()
1156 .downcast_ref::<BooleanArray>()
1157 .context(UnexpectedSnafu {
1158 reason: "Failed to downcast physical filter result to BooleanArray",
1159 })?;
1160 let mut result = boolean_array.values().clone();
1163 if let Some(nulls) = boolean_array.nulls() {
1164 result = result.bitand(nulls.inner());
1165 }
1166 Ok(result)
1167}
1168
1169#[cfg(test)]
1170mod tests {
1171 use std::sync::Arc;
1172 use std::sync::atomic::{AtomicUsize, Ordering};
1173
1174 use common_recordbatch::filter::SimpleFilterEvaluator;
1175 use datafusion_common::ScalarValue;
1176 use datafusion_expr::{col, lit};
1177 use datatypes::arrow::array::{
1178 ArrayRef, DictionaryArray, TimestampMillisecondArray, UInt8Array, UInt32Array, UInt64Array,
1179 };
1180 use datatypes::arrow::datatypes::{DataType, Field, Schema, UInt32Type};
1181 use mito_codec::row_converter::{PrimaryKeyFilter, build_primary_key_codec};
1182 use store_api::codec::PrimaryKeyEncoding;
1183
1184 use super::*;
1185 use crate::read::read_columns::ReadColumns;
1186 use crate::sst::internal_fields;
1187 use crate::sst::parquet::flat_format::{FlatReadFormat, primary_key_column_index};
1188 use crate::test_util::sst_util::{
1189 new_primary_key, new_record_batch_with_custom_sequence, sst_region_metadata,
1190 sst_region_metadata_with_encoding,
1191 };
1192
1193 struct CountingPrimaryKeyFilter {
1194 hits: Arc<AtomicUsize>,
1195 expected: Vec<u8>,
1196 }
1197
1198 impl PrimaryKeyFilter for CountingPrimaryKeyFilter {
1199 fn matches(&mut self, pk: &[u8]) -> mito_codec::error::Result<bool> {
1200 self.hits.fetch_add(1, Ordering::Relaxed);
1201 Ok(pk == self.expected.as_slice())
1202 }
1203 }
1204
1205 #[test]
1206 fn test_cached_primary_key_filter_reuses_previous_result() {
1207 let expected = new_primary_key(&["a", "x"]);
1208 let hits = Arc::new(AtomicUsize::new(0));
1209 let mut filter = CachedPrimaryKeyFilter::new(Box::new(CountingPrimaryKeyFilter {
1210 hits: Arc::clone(&hits),
1211 expected: expected.clone(),
1212 }));
1213
1214 assert!(filter.matches(expected.as_slice()).unwrap());
1215 assert!(filter.matches(expected.as_slice()).unwrap());
1216 assert!(
1217 !filter
1218 .matches(new_primary_key(&["b", "x"]).as_slice())
1219 .unwrap()
1220 );
1221
1222 assert_eq!(hits.load(Ordering::Relaxed), 2);
1223 }
1224
1225 fn new_test_filters(exprs: &[datafusion_expr::Expr]) -> Vec<SimpleFilterEvaluator> {
1226 exprs
1227 .iter()
1228 .filter_map(SimpleFilterEvaluator::try_new)
1229 .collect()
1230 }
1231
1232 fn new_simple_filter_contexts(
1233 metadata: &RegionMetadataRef,
1234 exprs: &[datafusion_expr::Expr],
1235 ) -> Vec<SimpleFilterContext> {
1236 exprs
1237 .iter()
1238 .filter_map(|expr| SimpleFilterContext::new_opt(metadata, None, expr))
1239 .collect()
1240 }
1241
1242 fn new_physical_filter_contexts(
1243 metadata: &RegionMetadataRef,
1244 read_format: &FlatReadFormat,
1245 exprs: &[datafusion_expr::Expr],
1246 ) -> Vec<PhysicalFilterContext> {
1247 exprs
1248 .iter()
1249 .filter_map(|expr| PhysicalFilterContext::new_opt(metadata, None, read_format, expr))
1250 .collect()
1251 }
1252
1253 fn new_raw_batch(primary_keys: &[&[u8]], field_values: &[u64]) -> RecordBatch {
1254 assert_eq!(primary_keys.len(), field_values.len());
1255
1256 let metadata = Arc::new(sst_region_metadata());
1257 let arrow_schema = metadata.schema.arrow_schema();
1258 let field_column = arrow_schema
1259 .field(arrow_schema.index_of("field_0").unwrap())
1260 .clone();
1261 let time_index_column = arrow_schema
1262 .field(arrow_schema.index_of("ts").unwrap())
1263 .clone();
1264 let mut fields = vec![field_column, time_index_column];
1265 fields.extend(
1266 internal_fields()
1267 .into_iter()
1268 .map(|field| field.as_ref().clone()),
1269 );
1270 let schema = Arc::new(Schema::new(fields));
1271
1272 let mut dict_values = Vec::new();
1273 let mut keys = Vec::with_capacity(primary_keys.len());
1274 for pk in primary_keys {
1275 let key = dict_values
1276 .iter()
1277 .position(|existing: &&[u8]| existing == pk)
1278 .unwrap_or_else(|| {
1279 dict_values.push(*pk);
1280 dict_values.len() - 1
1281 });
1282 keys.push(key as u32);
1283 }
1284 let pk_array: ArrayRef = Arc::new(DictionaryArray::<UInt32Type>::new(
1285 UInt32Array::from(keys),
1286 Arc::new(BinaryArray::from_iter_values(dict_values.iter().copied())),
1287 ));
1288
1289 RecordBatch::try_new(
1290 schema,
1291 vec![
1292 Arc::new(UInt64Array::from(field_values.to_vec())),
1293 Arc::new(TimestampMillisecondArray::from_iter_values(
1294 0..primary_keys.len() as i64,
1295 )),
1296 pk_array,
1297 Arc::new(UInt64Array::from(vec![1; primary_keys.len()])),
1298 Arc::new(UInt8Array::from(vec![1; primary_keys.len()])),
1299 ],
1300 )
1301 .unwrap()
1302 }
1303
1304 fn new_prefilter_batch(primary_keys: &[&[u8]], field_values: &[u64]) -> RecordBatch {
1305 assert_eq!(primary_keys.len(), field_values.len());
1306
1307 let metadata = Arc::new(sst_region_metadata());
1308 let arrow_schema = metadata.schema.arrow_schema();
1309 let field_column = arrow_schema
1310 .field(arrow_schema.index_of("field_0").unwrap())
1311 .clone();
1312 let time_index_column = arrow_schema
1313 .field(arrow_schema.index_of("ts").unwrap())
1314 .clone();
1315 let schema = Arc::new(Schema::new(vec![
1316 field_column,
1317 time_index_column,
1318 internal_fields()[0].as_ref().clone(),
1319 ]));
1320
1321 let mut dict_values = Vec::new();
1322 let mut keys = Vec::with_capacity(primary_keys.len());
1323 for pk in primary_keys {
1324 let key = dict_values
1325 .iter()
1326 .position(|existing: &&[u8]| existing == pk)
1327 .unwrap_or_else(|| {
1328 dict_values.push(*pk);
1329 dict_values.len() - 1
1330 });
1331 keys.push(key as u32);
1332 }
1333 let pk_array: ArrayRef = Arc::new(DictionaryArray::<UInt32Type>::new(
1334 UInt32Array::from(keys),
1335 Arc::new(BinaryArray::from_iter_values(dict_values.iter().copied())),
1336 ));
1337
1338 RecordBatch::try_new(
1339 schema,
1340 vec![
1341 Arc::new(UInt64Array::from(field_values.to_vec())),
1342 Arc::new(TimestampMillisecondArray::from_iter_values(
1343 0..primary_keys.len() as i64,
1344 )),
1345 pk_array,
1346 ],
1347 )
1348 .unwrap()
1349 }
1350
1351 fn new_prefilter_batch_binary_pk(primary_keys: &[&[u8]], field_values: &[u64]) -> RecordBatch {
1352 assert_eq!(primary_keys.len(), field_values.len());
1353
1354 let metadata = Arc::new(sst_region_metadata());
1355 let arrow_schema = metadata.schema.arrow_schema();
1356 let field_column = arrow_schema
1357 .field(arrow_schema.index_of("field_0").unwrap())
1358 .clone();
1359 let time_index_column = arrow_schema
1360 .field(arrow_schema.index_of("ts").unwrap())
1361 .clone();
1362 let schema = Arc::new(Schema::new(vec![
1363 field_column,
1364 time_index_column,
1365 Field::new(PRIMARY_KEY_COLUMN_NAME, DataType::Binary, false),
1366 ]));
1367
1368 let pk_array: ArrayRef =
1369 Arc::new(BinaryArray::from_iter_values(primary_keys.iter().copied()));
1370
1371 RecordBatch::try_new(
1372 schema,
1373 vec![
1374 Arc::new(UInt64Array::from(field_values.to_vec())),
1375 Arc::new(TimestampMillisecondArray::from_iter_values(
1376 0..primary_keys.len() as i64,
1377 )),
1378 pk_array,
1379 ],
1380 )
1381 .unwrap()
1382 }
1383
1384 fn field_values(batch: &RecordBatch) -> Vec<u64> {
1385 batch
1386 .column(0)
1387 .as_any()
1388 .downcast_ref::<UInt64Array>()
1389 .unwrap()
1390 .values()
1391 .to_vec()
1392 }
1393
1394 fn remaining_simple_filter_columns(filters: &[SimpleFilterContext]) -> Vec<&str> {
1395 filters
1396 .iter()
1397 .map(|filter_ctx| filter_ctx.filter().as_filter().unwrap().column_name())
1398 .collect()
1399 }
1400
1401 #[test]
1402 fn test_prefilter_primary_key_drops_single_dictionary_batch() {
1403 let metadata = Arc::new(sst_region_metadata());
1404 let filters = Arc::new(new_test_filters(&[col("tag_0").eq(lit("b"))]));
1405 let mut primary_key_filter =
1406 build_primary_key_codec(metadata.as_ref()).primary_key_filter(&metadata, filters);
1407 let pk_a = new_primary_key(&["a", "x"]);
1408 let batch = new_raw_batch(&[pk_a.as_slice(), pk_a.as_slice()], &[10, 11]);
1409 let pk_col_idx = primary_key_column_index(batch.num_columns());
1410
1411 let filtered =
1412 prefilter_flat_batch_by_primary_key(batch, pk_col_idx, primary_key_filter.as_mut())
1413 .unwrap();
1414
1415 assert!(filtered.is_none());
1416 }
1417
1418 #[test]
1419 fn test_prefilter_primary_key_builds_mask_for_fragmented_matches() {
1420 let metadata = Arc::new(sst_region_metadata());
1421 let filters = Arc::new(new_test_filters(&[col("tag_0")
1422 .eq(lit("a"))
1423 .or(col("tag_0").eq(lit("c")))]));
1424 let mut primary_key_filter =
1425 build_primary_key_codec(metadata.as_ref()).primary_key_filter(&metadata, filters);
1426 let pk_a = new_primary_key(&["a", "x"]);
1427 let pk_b = new_primary_key(&["b", "x"]);
1428 let pk_c = new_primary_key(&["c", "x"]);
1429 let pk_d = new_primary_key(&["d", "x"]);
1430 let batch = new_raw_batch(
1431 &[
1432 pk_a.as_slice(),
1433 pk_a.as_slice(),
1434 pk_b.as_slice(),
1435 pk_b.as_slice(),
1436 pk_c.as_slice(),
1437 pk_c.as_slice(),
1438 pk_d.as_slice(),
1439 pk_d.as_slice(),
1440 ],
1441 &[10, 11, 12, 13, 14, 15, 16, 17],
1442 );
1443 let pk_col_idx = primary_key_column_index(batch.num_columns());
1444
1445 let filtered =
1446 prefilter_flat_batch_by_primary_key(batch, pk_col_idx, primary_key_filter.as_mut())
1447 .unwrap()
1448 .unwrap();
1449
1450 assert_eq!(filtered.num_rows(), 4);
1451 assert_eq!(field_values(&filtered), vec![10, 11, 14, 15]);
1452 }
1453
1454 #[test]
1455 fn test_prefilter_builder_returns_none_without_selected_filters() {
1456 let metadata: RegionMetadataRef =
1457 Arc::new(sst_region_metadata_with_encoding(PrimaryKeyEncoding::Dense));
1458 let read_format = FlatReadFormat::new(
1459 metadata.clone(),
1460 ReadColumns::from_deduped_column_ids(
1461 metadata.column_metadatas.iter().map(|c| c.column_id),
1462 ),
1463 None,
1464 "test",
1465 false,
1466 )
1467 .unwrap();
1468 let codec = build_primary_key_codec(metadata.as_ref());
1469
1470 let builder = PrefilterContextBuilder::new(
1471 &read_format,
1472 &codec,
1473 None,
1474 None,
1475 Vec::new(),
1476 Vec::new(),
1477 metadata.schema_version,
1478 );
1479 assert!(builder.is_none());
1480 }
1481
1482 #[test]
1483 fn test_should_use_prefilter() {
1484 assert!(should_use_prefilter(1, 5, 6));
1485 assert!(!should_use_prefilter(1, 0, 1));
1486 assert!(!should_use_prefilter(1, 1, 2));
1487 assert!(!should_use_prefilter(4, 3, 7));
1488 assert!(should_use_prefilter(3, 3, 6));
1489 }
1490
1491 #[test]
1492 fn test_build_bulk_filter_plan_classifies_filters_across_read_paths() {
1493 let metadata: RegionMetadataRef = Arc::new(sst_region_metadata_with_encoding(
1494 PrimaryKeyEncoding::Sparse,
1495 ));
1496 let legacy_read_format = FlatReadFormat::new(
1497 metadata.clone(),
1498 ReadColumns::from_deduped_column_ids(
1499 metadata.column_metadatas.iter().map(|c| c.column_id),
1500 ),
1501 None,
1502 "memtable",
1503 false,
1504 )
1505 .unwrap();
1506 assert!(!legacy_read_format.batch_has_raw_pk_columns());
1507
1508 let plan = build_bulk_filter_plan(
1509 &legacy_read_format,
1510 Some(&Predicate::new(vec![
1511 col("tag_0").eq(lit("a")),
1512 col("field_0").gt(lit(1_u64)),
1513 ])),
1514 );
1515 assert_eq!(
1516 plan.pk_filters.as_ref().map(|filters| filters.len()),
1517 Some(1)
1518 );
1519 assert_eq!(
1520 remaining_simple_filter_columns(&plan.remaining_simple_filters),
1521 vec!["field_0"]
1522 );
1523
1524 let metadata: RegionMetadataRef = Arc::new(sst_region_metadata());
1525 let raw_pk_read_format = FlatReadFormat::new(
1526 metadata.clone(),
1527 ReadColumns::from_deduped_column_ids(
1528 metadata.column_metadatas.iter().map(|c| c.column_id),
1529 ),
1530 None,
1531 "memtable",
1532 true,
1533 )
1534 .unwrap();
1535 assert!(raw_pk_read_format.batch_has_raw_pk_columns());
1536
1537 let tag_only_plan = build_bulk_filter_plan(
1538 &raw_pk_read_format,
1539 Some(&Predicate::new(vec![col("tag_0").eq(lit("a"))])),
1540 );
1541 assert!(tag_only_plan.pk_filters.is_none());
1542 assert_eq!(
1543 remaining_simple_filter_columns(&tag_only_plan.remaining_simple_filters),
1544 vec!["tag_0"]
1545 );
1546
1547 let field_only_plan = build_bulk_filter_plan(
1548 &raw_pk_read_format,
1549 Some(&Predicate::new(vec![col("field_0").gt(lit(1_u64))])),
1550 );
1551 assert!(field_only_plan.pk_filters.is_none());
1552 assert_eq!(
1553 remaining_simple_filter_columns(&field_only_plan.remaining_simple_filters),
1554 vec!["field_0"]
1555 );
1556 }
1557
1558 #[test]
1559 fn test_build_reader_filter_plan_classifies_filters_for_prefilter_modes() {
1560 let metadata: RegionMetadataRef = Arc::new(sst_region_metadata());
1561 let full_read_format = FlatReadFormat::new(
1562 metadata.clone(),
1563 ReadColumns::from_deduped_column_ids(
1564 metadata.column_metadatas.iter().map(|c| c.column_id),
1565 ),
1566 None,
1567 "test",
1568 true,
1569 )
1570 .unwrap();
1571 let codec = build_primary_key_codec(metadata.as_ref());
1572
1573 let skip_fields_plan = build_reader_filter_plan(
1574 Some(&Predicate::new(vec![
1575 col("tag_0").eq(lit("a")),
1576 col("field_0").gt(lit(1_u64)),
1577 ])),
1578 None,
1579 PreFilterMode::SkipFields,
1580 true,
1581 &full_read_format,
1582 &codec,
1583 );
1584 assert!(skip_fields_plan.prefilter_builder.is_some());
1585 assert_eq!(
1586 remaining_simple_filter_columns(&skip_fields_plan.remaining_simple_filters),
1587 vec!["field_0"]
1588 );
1589
1590 let metric_metadata: RegionMetadataRef = Arc::new(sst_region_metadata_with_encoding(
1591 PrimaryKeyEncoding::Sparse,
1592 ));
1593 let field_0 = metric_metadata.column_by_name("field_0").unwrap().column_id;
1594 let ts = metric_metadata.time_index_column().column_id;
1595 let projected_read_format = FlatReadFormat::new(
1596 metric_metadata.clone(),
1597 ReadColumns::from_deduped_column_ids([field_0, ts]),
1598 None,
1599 "test",
1600 true,
1601 )
1602 .unwrap();
1603 let metric_codec = build_primary_key_codec(metric_metadata.as_ref());
1604 let pk_prefilter_plan = build_reader_filter_plan(
1605 Some(&Predicate::new(vec![col("tag_0").eq(lit("a"))])),
1606 None,
1607 PreFilterMode::All,
1608 true,
1609 &projected_read_format,
1610 &metric_codec,
1611 );
1612 assert!(pk_prefilter_plan.prefilter_builder.is_some());
1613 assert!(
1614 pk_prefilter_plan
1615 .prefilter_builder
1616 .as_ref()
1617 .unwrap()
1618 .build_primary_key_filter()
1619 .is_some()
1620 );
1621 assert!(pk_prefilter_plan.remaining_simple_filters.is_empty());
1622
1623 let disabled_plan = build_reader_filter_plan(
1624 Some(&Predicate::new(vec![
1625 col("tag_0").eq(lit("a")),
1626 col("field_0").gt(lit(1_u64)),
1627 col("ts").gt_eq(lit(ScalarValue::TimestampMillisecond(Some(1), None))),
1628 ])),
1629 None,
1630 PreFilterMode::All,
1631 false,
1632 &projected_read_format,
1633 &metric_codec,
1634 );
1635 assert!(disabled_plan.prefilter_builder.is_none());
1636 assert_eq!(
1637 remaining_simple_filter_columns(&disabled_plan.remaining_simple_filters),
1638 vec!["tag_0", "field_0", "ts"]
1639 );
1640 }
1641
1642 #[test]
1643 fn test_pk_filter_expr_strings_are_stable_under_expr_order() {
1644 let metadata: RegionMetadataRef = Arc::new(sst_region_metadata_with_encoding(
1645 PrimaryKeyEncoding::Sparse,
1646 ));
1647 let read_format = FlatReadFormat::new(
1648 metadata.clone(),
1649 ReadColumns::from_deduped_column_ids(
1650 metadata.column_metadatas.iter().map(|c| c.column_id),
1651 ),
1652 None,
1653 "test",
1654 false,
1655 )
1656 .unwrap();
1657 let codec = build_primary_key_codec(metadata.as_ref());
1658
1659 let expr_a = col("tag_0").eq(lit("a"));
1660 let expr_b = col("tag_1").eq(lit("x"));
1661 let plan_ab = build_reader_filter_plan(
1662 Some(&Predicate::new(vec![expr_a.clone(), expr_b.clone()])),
1663 None,
1664 PreFilterMode::All,
1665 true,
1666 &read_format,
1667 &codec,
1668 );
1669 let plan_b_a = build_reader_filter_plan(
1670 Some(&Predicate::new(vec![expr_b, expr_a])),
1671 None,
1672 PreFilterMode::All,
1673 true,
1674 &read_format,
1675 &codec,
1676 );
1677
1678 let exprs_ab = plan_ab.prefilter_builder.unwrap().pk_filter_expr_strs;
1679 let exprs_b_a = plan_b_a.prefilter_builder.unwrap().pk_filter_expr_strs;
1680 assert!(exprs_ab.is_some());
1681 assert_eq!(exprs_ab, exprs_b_a);
1682 }
1683
1684 #[test]
1685 fn test_simple_and_physical_contexts_preserve_expr_strings() {
1686 let metadata: RegionMetadataRef = Arc::new(sst_region_metadata());
1687 let read_format = FlatReadFormat::new(
1688 metadata.clone(),
1689 ReadColumns::from_deduped_column_ids(
1690 metadata.column_metadatas.iter().map(|c| c.column_id),
1691 ),
1692 None,
1693 "test",
1694 true,
1695 )
1696 .unwrap();
1697
1698 let simple_expr = col("tag_0").eq(lit("a"));
1699 let simple = SimpleFilterContext::new_opt(&metadata, None, &simple_expr).unwrap();
1700 assert_eq!(simple.expr_str(), format!("{simple_expr:?}"));
1701
1702 let physical_expr = col("field_0").in_list(vec![lit(1_u64), lit(2_u64)], false);
1703 let physical =
1704 PhysicalFilterContext::new_opt(&metadata, None, &read_format, &physical_expr).unwrap();
1705 assert_eq!(physical.expr_str(), format!("{physical_expr:?}"));
1706 }
1707
1708 #[test]
1709 fn test_eval_simple_filter_mask_uses_flat_tag_columns_directly() {
1710 let metadata: RegionMetadataRef = Arc::new(sst_region_metadata());
1711 let filters = new_simple_filter_contexts(&metadata, &[col("tag_0").eq(lit("a"))]);
1712 let batch = new_record_batch_with_custom_sequence(&["a", "x"], 0, 4, 1);
1713
1714 let mask = eval_simple_filter_mask(&batch, &filters[0], "test").unwrap();
1715 assert_eq!(mask.count_set_bits(), 4);
1716 }
1717
1718 #[test]
1719 fn test_eval_simple_filter_mask_errors_on_missing_selected_column() {
1720 let metadata: RegionMetadataRef = Arc::new(sst_region_metadata());
1721 let filters = new_simple_filter_contexts(&metadata, &[col("tag_0").eq(lit("a"))]);
1722 let pk = new_primary_key(&["a", "x"]);
1723 let batch = new_raw_batch(&[pk.as_slice()], &[10]);
1724
1725 let err = eval_simple_filter_mask(&batch, &filters[0], "test").unwrap_err();
1726 let err = err.to_string();
1727 assert!(err.contains("Prefilter column"));
1728 assert!(err.contains("tag_0"));
1729 }
1730
1731 #[test]
1732 fn test_eval_physical_filter_mask_evaluates_physical_filters() {
1733 let metadata: RegionMetadataRef =
1734 Arc::new(sst_region_metadata_with_encoding(PrimaryKeyEncoding::Dense));
1735 let read_format = FlatReadFormat::new(
1736 metadata.clone(),
1737 ReadColumns::from_deduped_column_ids(
1738 metadata.column_metadatas.iter().map(|c| c.column_id),
1739 ),
1740 None,
1741 "test",
1742 false,
1743 )
1744 .unwrap();
1745 let expr = col("field_0").in_list(vec![lit(11_u64)], false);
1746 let physical_filters = new_physical_filter_contexts(&metadata, &read_format, &[expr]);
1747 let pk = new_primary_key(&["a", "x"]);
1748 let batch = new_raw_batch(&[pk.as_slice(), pk.as_slice(), pk.as_slice()], &[9, 10, 11]);
1749
1750 let mask = eval_physical_filter_mask(&batch, &physical_filters[0], "test").unwrap();
1751 assert_eq!(mask.count_set_bits(), 1);
1752 }
1753
1754 #[test]
1755 fn test_eval_pk_group_mask_finds_pk_column_by_name() {
1756 let metadata = Arc::new(sst_region_metadata());
1757 let filters = Arc::new(new_test_filters(&[col("tag_0").eq(lit("a"))]));
1758 let mut pk_filter = Some(Box::new(CachedPrimaryKeyFilter::new(
1759 build_primary_key_codec(metadata.as_ref()).primary_key_filter(&metadata, filters),
1760 )) as Box<dyn PrimaryKeyFilter>);
1761 let pk_a = new_primary_key(&["a", "x"]);
1762 let pk_b = new_primary_key(&["b", "x"]);
1763 let batch = new_prefilter_batch(
1764 &[
1765 pk_a.as_slice(),
1766 pk_a.as_slice(),
1767 pk_b.as_slice(),
1768 pk_b.as_slice(),
1769 ],
1770 &[10, 11, 12, 13],
1771 );
1772
1773 let mask = primary_key_filter_mask(&batch, pk_filter.as_mut().unwrap().as_mut()).unwrap();
1774
1775 assert_eq!(mask.count_set_bits(), 2);
1776 }
1777
1778 #[test]
1779 fn test_eval_pk_group_mask_handles_binary_pk_column() {
1780 let metadata = Arc::new(sst_region_metadata());
1781 let filters = Arc::new(new_test_filters(&[col("tag_0").eq(lit("a"))]));
1782 let mut pk_filter = Some(Box::new(CachedPrimaryKeyFilter::new(
1783 build_primary_key_codec(metadata.as_ref()).primary_key_filter(&metadata, filters),
1784 )) as Box<dyn PrimaryKeyFilter>);
1785 let pk_a = new_primary_key(&["a", "x"]);
1786 let pk_b = new_primary_key(&["b", "x"]);
1787 let batch = new_prefilter_batch_binary_pk(
1788 &[
1789 pk_a.as_slice(),
1790 pk_a.as_slice(),
1791 pk_b.as_slice(),
1792 pk_b.as_slice(),
1793 ],
1794 &[10, 11, 12, 13],
1795 );
1796
1797 let mask = primary_key_filter_mask(&batch, pk_filter.as_mut().unwrap().as_mut()).unwrap();
1798
1799 assert_eq!(mask.count_set_bits(), 2);
1800 }
1801}