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}