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