mz_transform/fusion/filter.rs
1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10//! Fuses multiple `Filter` operators into one and canonicalizes predicates.
11//!
12//! If the `Filter` operator is empty, removes it.
13//!
14//! ```rust
15//! use mz_expr::{MirRelationExpr, MirScalarExpr};
16//! use mz_repr::{ReprColumnType, ReprRelationType, ReprScalarType};
17//! use mz_repr::optimize::OptimizerFeatures;
18//! use mz_transform::{reprtypecheck, Transform, TransformCtx};
19//! use mz_transform::dataflow::DataflowMetainfo;
20//!
21//! use mz_transform::fusion::filter::Filter;
22//!
23//! let input = MirRelationExpr::Constant {
24//! rows: Ok(vec![]),
25//! typ: ReprRelationType::new(vec![
26//! ReprColumnType { scalar_type: ReprScalarType::Bool, nullable: false },
27//! ]),
28//! };
29//!
30//! let predicate0 = MirScalarExpr::column(0);
31//! let predicate1 = MirScalarExpr::column(0);
32//! let predicate2 = MirScalarExpr::column(0);
33//!
34//! let mut expr = input
35//! .clone()
36//! .filter(vec![predicate0.clone()])
37//! .filter(vec![predicate1.clone()])
38//! .filter(vec![predicate2.clone()]);
39//!
40//! let features = OptimizerFeatures::default();
41//! let typecheck_ctx = reprtypecheck::empty_context();
42//! let mut df_meta = DataflowMetainfo::default();
43//! let mut transform_ctx = TransformCtx::local(&features, &typecheck_ctx, &mut df_meta, None, None);
44//!
45//! // Filter.transform() will deduplicate any predicates
46//! Filter.transform(&mut expr, &mut transform_ctx);
47//!
48//! let correct = input.filter(vec![predicate0]);
49//!
50//! assert_eq!(expr, correct);
51//! ```
52
53use mz_expr::MirRelationExpr;
54
55use crate::TransformCtx;
56
57/// Fuses multiple `Filter` operators into one and deduplicates predicates.
58#[derive(Debug)]
59pub struct Filter;
60
61impl crate::Transform for Filter {
62 fn name(&self) -> &'static str {
63 "FilterFusion"
64 }
65
66 #[mz_ore::instrument(
67 target = "optimizer",
68 level = "debug",
69 fields(path.segment = "filter_fusion")
70 )]
71 fn actually_perform_transform(
72 &self,
73 relation: &mut MirRelationExpr,
74 _: &mut TransformCtx,
75 ) -> Result<(), crate::TransformError> {
76 relation.visit_pre_mut(Self::action);
77 mz_repr::explain::trace_plan(&*relation);
78 Ok(())
79 }
80}
81
82impl Filter {
83 /// Fuses multiple `Filter` operators into one and canonicalizes predicates.
84 pub fn action(relation: &mut MirRelationExpr) {
85 if let MirRelationExpr::Filter { input, predicates } = relation {
86 // consolidate nested filters.
87 while let MirRelationExpr::Filter {
88 input: inner,
89 predicates: p2,
90 } = &mut **input
91 {
92 predicates.append(p2);
93 **input = inner.take_dangerous();
94 }
95
96 mz_expr::canonicalize::canonicalize_predicates(predicates, &input.typ().column_types);
97
98 // remove the Filter stage if empty.
99 if predicates.is_empty() {
100 *relation = input.take_dangerous();
101 }
102 }
103 }
104}