mz_transform/fusion/
map.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 a sequence of `Map` operators in to one `Map` operator.
11//!
12//! This transform introduces the complexity that max expressions can
13//! refer to the results of prior map expressions. This is an important
14//! detail that is often overlooked and leads to bugs. However, it is
15//! important to coalesce these operators so that we can more easily
16//! move them around other operators together.
17//!
18//! Also removes empty `Map` operators.
19
20use std::mem;
21
22use mz_expr::MirRelationExpr;
23
24use crate::TransformCtx;
25
26/// Fuses a sequence of `Map` operators in to one `Map` operator.
27#[derive(Debug)]
28pub struct Map;
29
30impl crate::Transform for Map {
31    fn name(&self) -> &'static str {
32        "MapFusion"
33    }
34
35    #[mz_ore::instrument(
36        target = "optimizer",
37        level = "debug",
38        fields(path.segment = "map_fusion")
39    )]
40    fn actually_perform_transform(
41        &self,
42        relation: &mut MirRelationExpr,
43        _: &mut TransformCtx,
44    ) -> Result<(), crate::TransformError> {
45        relation.visit_pre_mut(Self::action);
46        mz_repr::explain::trace_plan(&*relation);
47        Ok(())
48    }
49}
50
51impl Map {
52    /// Fuses a sequence of `Map` operators into one `Map` operator.
53    /// Remove the map operator if it is empty.
54    pub fn action(relation: &mut MirRelationExpr) {
55        if let MirRelationExpr::Map { input, scalars } = relation {
56            while let MirRelationExpr::Map {
57                input: inner_input,
58                scalars: inner_scalars,
59            } = &mut **input
60            {
61                inner_scalars.append(scalars);
62                mem::swap(scalars, inner_scalars);
63                **input = inner_input.take_dangerous();
64            }
65
66            if scalars.is_empty() {
67                *relation = input.take_dangerous();
68            }
69        }
70    }
71}