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::{SqlColumnType, Datum, SqlRelationType, SqlScalarType};
17//! use mz_repr::optimize::OptimizerFeatures;
18//! use mz_transform::{reprtypecheck, typecheck, Transform, TransformCtx};
19//! use mz_transform::dataflow::DataflowMetainfo;
20//!
21//! use mz_transform::fusion::filter::Filter;
22//!
23//! let input = MirRelationExpr::constant(vec![], SqlRelationType::new(vec![
24//!     SqlScalarType::Bool.nullable(false),
25//! ]));
26//!
27//! let predicate0 = MirScalarExpr::column(0);
28//! let predicate1 = MirScalarExpr::column(0);
29//! let predicate2 = MirScalarExpr::column(0);
30//!
31//! let mut expr = input
32//!     .clone()
33//!     .filter(vec![predicate0.clone()])
34//!     .filter(vec![predicate1.clone()])
35//!     .filter(vec![predicate2.clone()]);
36//!
37//! let features = OptimizerFeatures::default();
38//! let typecheck_ctx = typecheck::empty_context();
39//! let repr_typecheck_ctx = reprtypecheck::empty_context();
40//! let mut df_meta = DataflowMetainfo::default();
41//! let mut transform_ctx = TransformCtx::local(&features, &typecheck_ctx, &repr_typecheck_ctx, &mut df_meta, None, None);
42//!
43//! // Filter.transform() will deduplicate any predicates
44//! Filter.transform(&mut expr, &mut transform_ctx);
45//!
46//! let correct = input.filter(vec![predicate0]);
47//!
48//! assert_eq!(expr, correct);
49//! ```
50
51use mz_expr::MirRelationExpr;
52
53use crate::TransformCtx;
54
55/// Fuses multiple `Filter` operators into one and deduplicates predicates.
56#[derive(Debug)]
57pub struct Filter;
58
59impl crate::Transform for Filter {
60    fn name(&self) -> &'static str {
61        "FilterFusion"
62    }
63
64    #[mz_ore::instrument(
65        target = "optimizer",
66        level = "debug",
67        fields(path.segment = "filter_fusion")
68    )]
69    fn actually_perform_transform(
70        &self,
71        relation: &mut MirRelationExpr,
72        _: &mut TransformCtx,
73    ) -> Result<(), crate::TransformError> {
74        relation.visit_pre_mut(Self::action);
75        mz_repr::explain::trace_plan(&*relation);
76        Ok(())
77    }
78}
79
80impl Filter {
81    /// Fuses multiple `Filter` operators into one and canonicalizes predicates.
82    pub fn action(relation: &mut MirRelationExpr) {
83        if let MirRelationExpr::Filter { input, predicates } = relation {
84            // consolidate nested filters.
85            while let MirRelationExpr::Filter {
86                input: inner,
87                predicates: p2,
88            } = &mut **input
89            {
90                predicates.append(p2);
91                *input = Box::new(inner.take_dangerous());
92            }
93
94            mz_expr::canonicalize::canonicalize_predicates(predicates, &input.typ().column_types);
95
96            // remove the Filter stage if empty.
97            if predicates.is_empty() {
98                *relation = input.take_dangerous();
99            }
100        }
101    }
102}