mz_compute_types/
explain.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//! `EXPLAIN` support for structures defined in this crate.
11
12pub(crate) mod text;
13
14use std::collections::BTreeMap;
15
16use mz_expr::explain::{ExplainContext, ExplainMultiPlan, ExplainSource, enforce_linear_chains};
17use mz_expr::{MirRelationExpr, OptimizedMirRelationExpr};
18use mz_repr::GlobalId;
19use mz_repr::explain::{AnnotatedPlan, Explain, ExplainError, UnsupportedFormat};
20
21use crate::dataflows::DataflowDescription;
22use crate::plan::Plan;
23
24impl<'a> Explain<'a> for DataflowDescription<Plan> {
25    type Context = ExplainContext<'a>;
26
27    type Text = ExplainMultiPlan<'a, Plan>;
28
29    type VerboseText = ExplainMultiPlan<'a, Plan>;
30
31    type Json = ExplainMultiPlan<'a, Plan>;
32
33    type Dot = UnsupportedFormat;
34
35    fn explain_text(&'a mut self, context: &'a Self::Context) -> Result<Self::Text, ExplainError> {
36        self.as_explain_multi_plan(context)
37    }
38
39    fn explain_verbose_text(
40        &'a mut self,
41        context: &'a Self::Context,
42    ) -> Result<Self::VerboseText, ExplainError> {
43        self.as_explain_multi_plan(context)
44    }
45
46    fn explain_json(&'a mut self, context: &'a Self::Context) -> Result<Self::Text, ExplainError> {
47        self.as_explain_multi_plan(context)
48    }
49}
50
51impl<'a> DataflowDescription<Plan> {
52    fn as_explain_multi_plan(
53        &'a mut self,
54        context: &'a ExplainContext<'a>,
55    ) -> Result<ExplainMultiPlan<'a, Plan>, ExplainError> {
56        let export_ids = export_ids_for(self);
57        let plans = self
58            .objects_to_build
59            .iter_mut()
60            .rev()
61            .map(|build_desc| {
62                let public_id = export_ids
63                    .get(&build_desc.id)
64                    .unwrap_or(&build_desc.id)
65                    .clone();
66                let id = context
67                    .humanizer
68                    .humanize_id(public_id)
69                    .unwrap_or_else(|| public_id.to_string());
70                let plan = AnnotatedPlan {
71                    plan: &build_desc.plan,
72                    annotations: BTreeMap::default(),
73                };
74                (id, plan)
75            })
76            .collect::<Vec<_>>();
77
78        let sources = self
79            .source_imports
80            .iter_mut()
81            .map(|(id, (source_desc, _, _upper))| {
82                let op = source_desc.arguments.operators.as_ref();
83                ExplainSource::new(*id, op, context.config.filter_pushdown)
84            })
85            .collect::<Vec<_>>();
86
87        Ok(ExplainMultiPlan {
88            context,
89            sources,
90            plans,
91        })
92    }
93}
94
95impl<'a> Explain<'a> for DataflowDescription<OptimizedMirRelationExpr> {
96    type Context = ExplainContext<'a>;
97
98    type Text = ExplainMultiPlan<'a, MirRelationExpr>;
99
100    type VerboseText = ExplainMultiPlan<'a, MirRelationExpr>;
101
102    type Json = ExplainMultiPlan<'a, MirRelationExpr>;
103
104    type Dot = UnsupportedFormat;
105
106    fn explain_text(&'a mut self, context: &'a Self::Context) -> Result<Self::Text, ExplainError> {
107        self.as_explain_multi_plan(context)
108    }
109
110    fn explain_verbose_text(
111        &'a mut self,
112        context: &'a Self::Context,
113    ) -> Result<Self::VerboseText, ExplainError> {
114        self.as_explain_multi_plan(context)
115    }
116
117    fn explain_json(&'a mut self, context: &'a Self::Context) -> Result<Self::Text, ExplainError> {
118        self.as_explain_multi_plan(context)
119    }
120}
121
122impl<'a> DataflowDescription<OptimizedMirRelationExpr> {
123    fn as_explain_multi_plan(
124        &'a mut self,
125        context: &'a ExplainContext<'a>,
126    ) -> Result<ExplainMultiPlan<'a, MirRelationExpr>, ExplainError> {
127        let export_ids = export_ids_for(self);
128        let plans = self
129            .objects_to_build
130            .iter_mut()
131            .rev()
132            .map(|build_desc| {
133                // normalize the representation as linear chains
134                // (this implies !context.config.raw_plans by construction)
135                if context.config.linear_chains {
136                    enforce_linear_chains(build_desc.plan.as_inner_mut())?;
137                };
138
139                let id = export_ids
140                    .get(&build_desc.id)
141                    .unwrap_or(&build_desc.id)
142                    .clone();
143                let id = context
144                    .humanizer
145                    .humanize_id(id)
146                    .unwrap_or_else(|| id.to_string());
147                let plan = AnnotatedPlan {
148                    plan: build_desc.plan.as_inner(),
149                    annotations: BTreeMap::default(),
150                };
151                Ok((id, plan))
152            })
153            .collect::<Result<Vec<_>, ExplainError>>()?;
154
155        let sources = self
156            .source_imports
157            .iter_mut()
158            .map(|(id, (source_desc, _, _upper))| {
159                let op = source_desc.arguments.operators.as_ref();
160                ExplainSource::new(*id, op, context.config.filter_pushdown)
161            })
162            .collect::<Vec<_>>();
163
164        Ok(ExplainMultiPlan {
165            context,
166            sources,
167            plans,
168        })
169    }
170}
171
172/// TODO(database-issues#7533): Add documentation.
173pub fn export_ids_for<P, S, T>(dd: &DataflowDescription<P, S, T>) -> BTreeMap<GlobalId, GlobalId> {
174    let mut map = BTreeMap::<GlobalId, GlobalId>::default();
175
176    // Dataflows created from a `CREATE MATERIALIZED VIEW` have:
177    //
178    // 1. Exactly one entry in `objects_to_build` representing the dataflow to
179    //    be installed. This entry has a transient ID that changes whenever the
180    //    dataflow is re-installed.
181    // 2. Exactly one entry in `sink_exports` referencing the `objects_to_build`
182    //    entry.
183    // 3. No enties in index_exports.
184    //
185    // Because the user-facing ID is for the `sink_exports` entry in (2), we
186    // create a mapping.
187    if dd.sink_exports.len() == 1 && dd.objects_to_build.len() == 1 && dd.index_exports.is_empty() {
188        for (public_id, export) in dd.sink_exports.iter() {
189            map.insert(export.from, *public_id);
190        }
191    }
192
193    // Dataflows created from a `CREATE INDEX` adhere to the following
194    // constraints.
195    //
196    // 1. One or more entries in `objects_to_build`. The last entry arranges a
197    //    `Get $id` where $id might be:
198    //    1. The previous `objects_to_build` entry that corresponds to the
199    //       dataflow of the indexed view (if we index a VIEW).
200    //    2. A `source_imports` entry (if we index a SOURCE, TABLE, or
201    //       MATERIALIZED VIEW).
202    //    3. An `index_imports` entry (if we index a SOURCE, TABLE, or
203    //       MATERIALIZED VIEW that is already indexed).
204    // 2. Exactly one entry in `index_exports` identified by the same ID as the
205    //    last `objects_to_build` entry.
206    //
207    // Because there are no transient IDs involved in the above configurations,
208    // we don't need to add further entries to the `map` to account for these
209    // cases.
210
211    map
212}