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
70
71
72
73
74
75
76
77
78
79
80
81
82
// 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.

//! Normalize the structure of `Let` and `LetRec` operators in expressions.
//!
//! Normalization happens in the context of "scopes", corresponding to
//! 1. the expression's root and 2. each instance of a `LetRec` AST node.
//!
//! Within each scope,
//! 1. Each expression is normalized to have all `Let` nodes at the root
//! of the expression, in order of identifier.
//! 2. Each expression assigns a contiguous block of identifiers.
//!
//! The transform may remove some `Let` and `Get` operators, and does not
//! introduce any new operators.
//!
//! The module also publishes the function `renumber_bindings` which can
//! be used to renumber bindings in an expression starting from a provided
//! `IdGen`, which is used to prepare distinct expressions for inlining.

use mz_expr::{visit::Visit, MirRelationExpr};

use crate::TransformArgs;

/// Install replace certain `Get` operators with their `Let` value.
#[derive(Debug)]
pub struct Normalize {
    normalize_lets: Box<crate::normalize_lets::NormalizeLets>,
}

impl Normalize {
    /// Construct a new [`Normalize`] instance.
    pub fn new() -> Normalize {
        Normalize {
            normalize_lets: Box::new(crate::normalize_lets::NormalizeLets::new(false)),
        }
    }
}

impl crate::Transform for Normalize {
    fn recursion_safe(&self) -> bool {
        false
    }

    #[tracing::instrument(
        target = "optimizer"
        level = "trace",
        skip_all,
        fields(path.segment = "normalize")
    )]
    fn transform(
        &self,
        relation: &mut MirRelationExpr,
        _args: TransformArgs,
    ) -> Result<(), crate::TransformError> {
        // (1) Normalize lets first. This might enable transforms in (2).
        self.normalize_lets.transform_without_trace(relation)?;
        // (2) Canonicalize and fuse various operators as a bottom-up transform.
        relation.try_visit_mut_post::<_, crate::TransformError>(
            &mut |expr: &mut MirRelationExpr| {
                // (a) Might enable Map fusion in the next step.
                crate::fusion::flatmap_to_map::FlatMapToMap::action(expr);
                // (b) Fuse various like-kinded operators.
                crate::fusion::Fusion::action(expr);
                // (c) Fuse join trees (might lift in-between Filters).
                crate::fusion::join::Join::action(expr)?;
                // (d) Extract column references in Map as Project.
                crate::projection_extraction::ProjectionExtraction::action(expr)?;
                // Done!
                Ok(())
            },
        )?;
        mz_repr::explain::trace_plan(&*relation);
        Ok(())
    }
}