mz_transform/fusion/
union.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 `Union` operators into one.
11
12use mz_expr::MirRelationExpr;
13use mz_expr::visit::Visit;
14
15/// Fuses multiple `Union` operators into one.
16#[derive(Debug)]
17pub struct Union;
18
19impl crate::Transform for Union {
20    fn name(&self) -> &'static str {
21        "UnionFusion"
22    }
23
24    #[mz_ore::instrument(
25        target = "optimizer",
26        level = "debug",
27        fields(path.segment = "union")
28    )]
29    fn actually_perform_transform(
30        &self,
31        relation: &mut MirRelationExpr,
32        _: &mut crate::TransformCtx,
33    ) -> Result<(), crate::TransformError> {
34        relation.visit_mut_post(&mut Self::action)?;
35        mz_repr::explain::trace_plan(&*relation);
36        Ok(())
37    }
38}
39
40impl Union {
41    /// Fuses multiple `Union` operators into one.
42    ///
43    /// The order among children is maintained, and the action should be idempotent.
44    /// This action works best if other operators such as `Negate` and other linear
45    /// operators are pushed down through other `Union` operators.
46    pub fn action(relation: &mut MirRelationExpr) {
47        if let MirRelationExpr::Union { inputs, .. } = relation {
48            let mut list: Vec<MirRelationExpr> = Vec::with_capacity(1 + inputs.len());
49            Self::unfold_unions_into(relation.take_dangerous(), &mut list);
50            *relation = MirRelationExpr::Union {
51                base: Box::new(list.remove(0)),
52                inputs: list,
53            }
54        }
55    }
56
57    /// Unfolds `self` and children into a list of expressions that can be unioned.
58    fn unfold_unions_into(expr: MirRelationExpr, list: &mut Vec<MirRelationExpr>) {
59        let mut stack = vec![expr];
60        while let Some(expr) = stack.pop() {
61            if let MirRelationExpr::Union { base, inputs } = expr {
62                stack.extend(inputs.into_iter().rev());
63                stack.push(*base);
64            } else {
65                list.push(expr);
66            }
67        }
68    }
69}