Skip to main content

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}