mz_adapter/optimize/
view.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//! An Optimizer that
11//! 1. Optimistically calls `optimize_mir_constant`.
12//! 2. Then, if we haven't arrived at a constant, it calls `optimize_mir_local`, i.e., the
13//!    logical optimizer.
14//!
15//! This is used for `CREATE VIEW` statements and in various other situations where no physical
16//! optimization is needed, such as for `INSERT` statements.
17
18use std::time::Instant;
19
20use mz_expr::OptimizedMirRelationExpr;
21use mz_sql::optimizer_metrics::OptimizerMetrics;
22use mz_sql::plan::HirRelationExpr;
23use mz_transform::TransformCtx;
24use mz_transform::dataflow::DataflowMetainfo;
25use mz_transform::typecheck::{SharedContext as TypecheckContext, empty_context};
26
27use crate::optimize::{
28    Optimize, OptimizerConfig, OptimizerError, optimize_mir_constant, optimize_mir_local,
29    trace_plan,
30};
31
32pub struct Optimizer {
33    /// A typechecking context to use throughout the optimizer pipeline.
34    typecheck_ctx: TypecheckContext,
35    /// Optimizer config.
36    config: OptimizerConfig,
37    /// Optimizer metrics.
38    ///
39    /// Allowed to be `None` for cases where view optimization is invoked outside of the
40    /// coordinator context and the metrics are not available.
41    metrics: Option<OptimizerMetrics>,
42}
43
44impl Optimizer {
45    pub fn new(config: OptimizerConfig, metrics: Option<OptimizerMetrics>) -> Self {
46        Self {
47            typecheck_ctx: empty_context(),
48            config,
49            metrics,
50        }
51    }
52}
53
54impl Optimize<HirRelationExpr> for Optimizer {
55    type To = OptimizedMirRelationExpr;
56
57    fn optimize(&mut self, expr: HirRelationExpr) -> Result<Self::To, OptimizerError> {
58        let time = Instant::now();
59
60        // Trace the pipeline input under `optimize/raw`.
61        trace_plan!(at: "raw", &expr);
62
63        // HIR ⇒ MIR lowering and decorrelation
64        let mut expr = expr.lower(&self.config, self.metrics.as_ref())?;
65
66        let mut df_meta = DataflowMetainfo::default();
67        let mut transform_ctx = TransformCtx::local(
68            &self.config.features,
69            &self.typecheck_ctx,
70            &mut df_meta,
71            self.metrics.as_ref(),
72        );
73
74        // First, we run a very simple optimizer pipeline, which only folds constants. This takes
75        // care of constant INSERTs. (This optimizer is also used for INSERTs, not just VIEWs.)
76        expr = optimize_mir_constant(expr, &mut transform_ctx)?;
77
78        // MIR ⇒ MIR optimization (local)
79        let expr = if expr.as_const().is_some() {
80            // No need to optimize further, because we already have a constant.
81            // But trace this at "local", so that `EXPLAIN LOCALLY OPTIMIZED PLAN` can pick it up.
82            trace_plan!(at: "local", &expr);
83            OptimizedMirRelationExpr(expr)
84        } else {
85            // Call the real optimization.
86            optimize_mir_local(expr, &mut transform_ctx)?
87        };
88
89        if let Some(metrics) = &self.metrics {
90            metrics.observe_e2e_optimization_time("view", time.elapsed());
91        }
92
93        // Return the resulting OptimizedMirRelationExpr.
94        Ok(expr)
95    }
96}