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