Skip to main content

rupg_func/deparse/
explain.rs

1//! The texts of a plan for `EXPLAIN`: the names of its relations, as `select_rtable_names_for_explain` chooses them, and its expressions, as `deparse_expression` shows them with the context of `set_deparse_context_plan`.
2
3use std::collections::HashMap;
4use std::ptr;
5use std::rc::Rc;
6
7use rupg_analyze::{Expr, FromItem, SortGroup, SubLink, SubLinkKind};
8use rupg_plan::{Kind, Node, Plan, TARGET, Texts};
9use rupg_sql::nodes::FRAMEOPTION_NONDEFAULT;
10
11use super::{Deparser, Error, Namespace, Removed, Result, SubName, builtin, quote_identifier, reg};
12use crate::Session;
13
14/// The output of `EXPLAIN` for a plan, with the names and the expressions that the session shows.
15///
16/// # Errors
17///
18/// The errors of the deparse of the expressions.
19pub fn explain(
20    plan: &Plan<'_>,
21    options: &rupg_plan::Options,
22    session: &dyn Session,
23) -> Result<String> {
24    let mut texts = PlanTexts::new(plan, session);
25    rupg_plan::explain(plan, options, &mut texts)
26}
27
28/// The names of the relations of each query of a plan and the names of its subplans and of its windows.
29struct PlanTexts<'a, 'q> {
30    session: &'a dyn Session,
31    plan: &'a Plan<'q>,
32    /// The names of each scope of the plan.
33    spaces: Vec<Namespace>,
34    /// The name of the target table of each scope of an `INSERT`.
35    targets: Vec<Option<String>>,
36    /// The name of each window of each scope, as `WindowAgg.winname`.
37    windows: Vec<Vec<Option<String>>>,
38    subplans: Vec<SubName>,
39}
40
41impl<'a, 'q> PlanTexts<'a, 'q> {
42    /// The names of the relations of the plan. The scopes in [`Plan::order`] take their names in that order, and each name is unique in all of them. The target table of an `INSERT` is the first relation of its query.
43    fn new(plan: &'a Plan<'q>, session: &'a dyn Session) -> PlanTexts<'a, 'q> {
44        let d = Deparser::new(session, false, 0);
45        let count = plan.scopes.len();
46        let mut spaces: Vec<Option<Namespace>> = vec![None; count];
47        let mut targets = vec![None; count];
48        let mut used: HashMap<String, u32> = HashMap::new();
49        // `transformOnConflictClause`: the entry `excluded` of the range table comes after the target table of the `INSERT`.
50        let mut conflicts = Vec::new();
51        conflict_scopes(&plan.root, &mut conflicts);
52        let mut excluded = vec![None; count];
53        let mut kept = Vec::new();
54        subquery_scans(&plan.root, &mut kept);
55        for &s in &plan.order {
56            let Some(scope) = plan.scopes.get(s) else { continue };
57            if spaces[s].is_some() {
58                continue;
59            }
60            // `expand_inherited_rtentry`: a member has the alias of its relation, which `set_rtable_names` makes unique.
61            if let Some(leaf) = scope.leaf
62                && let Some(Some(of)) = spaces.get(leaf.of)
63            {
64                let mut space = of.clone();
65                if leaf.renamed
66                    && let Some(rel) = scope.query.relations.get(leaf.relation)
67                {
68                    space.names[leaf.relation] =
69                        super::query::unique_name(d.base_name(rel), &mut used);
70                }
71                spaces[s] = Some(space);
72                continue;
73            }
74            if let Some(write) = scope.target {
75                let base = write.alias.clone().unwrap_or_else(|| {
76                    reg::class_name(write.table, session)
77                        .map_or_else(|| write.name.clone(), |(name, _)| name.to_string())
78                });
79                targets[s] = Some(super::query::unique_name(base, &mut used));
80                if write.on_conflict.as_ref().is_some_and(|c| c.update.is_some()) {
81                    excluded[s] = Some(super::query::unique_name("excluded".into(), &mut used));
82                }
83            }
84            let hidden = unscanned(plan, s, &kept);
85            spaces[s] = Some(d.namespace_in(scope.query, &mut used, true, &hidden));
86            // `ApplyRetrieveRule`: the entry of a view with triggers `INSTEAD OF` that the statement writes comes after the entries of the query.
87            if let Some(write) =
88                scope.query.write.as_ref().filter(|w| w.instead && scope.target.is_none())
89            {
90                // The entry has the name of the subquery of the view: the alias of the statement, or the name of the view.
91                let base = scope
92                    .query
93                    .relations
94                    .first()
95                    .map_or_else(|| write.name.clone(), |relation| relation.name.clone());
96                targets[s] = Some(super::query::unique_name(base, &mut used));
97            }
98        }
99        let mut spaces: Vec<Namespace> = spaces
100            .into_iter()
101            .enumerate()
102            .map(|(s, space)| {
103                space.unwrap_or_else(|| {
104                    d.namespace_in(plan.scopes[s].query, &mut HashMap::new(), true, &[])
105                })
106            })
107            .collect();
108        // The query of `DO UPDATE` reads the target table and `excluded` with the names of their entries.
109        for (update, insert) in conflicts {
110            let Some(space) = spaces.get_mut(update) else { continue };
111            if let Some(Some(name)) = targets.get(insert)
112                && let Some(slot) = space.names.get_mut(0)
113            {
114                slot.clone_from(name);
115            }
116            if let Some(Some(name)) = excluded.get(insert)
117                && let Some(FromItem::Relation(at)) = plan.scopes[update].query.from.get(1)
118                && let Some(slot) = space.names.get_mut(*at)
119            {
120                slot.clone_from(name);
121            }
122        }
123        removed_scans(plan, &mut spaces);
124        // The `Output` of the `ModifyTable` of an `INSERT` with `RETURNING` reads the columns of the target table, and a column of `old` or `new` reads the columns of the table with the name of the row.
125        for (s, space) in spaces.iter_mut().enumerate() {
126            let scope = &plan.scopes[s];
127            let write = scope.target.or(scope.query.write.as_ref());
128            let returning = scope.target.and_then(|w| w.returning.as_deref());
129            if let Some(returning) = write.and_then(|w| w.returning.as_deref())
130                && let (Some(shown), Some(table)) =
131                    (returning.shown.as_deref(), returning.relations.first())
132            {
133                let columns = table.columns.iter().map(|c| c.name.clone()).collect();
134                space.returning = Some((shown.old.clone(), shown.new.clone(), columns));
135            }
136            if let (Some(name), Some(returning)) = (&targets[s], returning)
137                && let Some(table) = returning.relations.first()
138            {
139                let columns = table.columns.iter().map(|c| c.name.clone()).collect();
140                space.target = Some((name.clone(), columns));
141            }
142        }
143        let mut windows: Vec<Vec<Option<String>>> =
144            plan.scopes.iter().map(|s| vec![None; s.query.windows.len()]).collect();
145        window_names(&plan.root, &mut windows);
146        let subplans = plan
147            .sublinks
148            .iter()
149            .map(|l| SubName {
150                link: ptr::from_ref(l.link),
151                name: l.name.clone(),
152                init: l.init,
153                hashed: l.hashed,
154                alternative: l.alternative,
155            })
156            .collect();
157        PlanTexts { session, plan, spaces, targets, windows, subplans }
158    }
159
160    /// A deparse of the expressions of a scope, with the names of the scope and of the scopes outside it.
161    fn deparser(&self, scope: usize, prefix: bool) -> Result<Deparser<'a>> {
162        let mut chain = Vec::new();
163        let mut at = Some(scope);
164        while let Some(s) = at {
165            let Some(found) = self.plan.scopes.get(s) else {
166                return Err(Error::internal(format!("no scope {s} in the plan")));
167            };
168            chain.push(s);
169            at = found.parent;
170        }
171        let mut d = Deparser::new(self.session, false, 0);
172        d.flat = true;
173        d.namespaces = chain.iter().rev().map(|s| self.spaces[*s].clone()).collect();
174        d.prefix = prefix;
175        d.outer_prefix = true;
176        let query = self.plan.scopes[scope].query;
177        d.windows.clone_from(&query.windows);
178        d.window_targets = query.targets.iter().map(|t| t.expr.clone()).collect();
179        d.window_names.clone_from(&self.windows[scope]);
180        d.subplans.clone_from(&self.subplans);
181        Ok(d)
182    }
183}
184
185/// `set_rtable_names` with the `rels_used` of `ExplainPreScanNode`: the subqueries of `FROM` of the scope `s` whose `Subquery Scan` the plan does not keep, as `trivial_subqueryscan` takes it out, take no name, because no node of the plan scans them. `kept` has the scope and the relation of each `Subquery Scan`. A subquery of a set operation, and a subquery that is a set operation, keep their names.
186fn unscanned(plan: &Plan<'_>, s: usize, kept: &[(usize, usize)]) -> Vec<usize> {
187    let outer = plan.scopes[s].query;
188    if outer.set_op.is_some() {
189        return Vec::new();
190    }
191    plan.scopes
192        .iter()
193        .filter(|scope| {
194            scope.parent == Some(s) && scope.leaf.is_none() && scope.query.set_op.is_none()
195        })
196        .filter_map(|scope| {
197            outer
198                .relations
199                .iter()
200                .position(|rel| rel.subquery.as_deref().is_some_and(|q| ptr::eq(q, scope.query)))
201        })
202        .filter(|r| !kept.contains(&(s, *r)))
203        .collect()
204}
205
206/// The subqueries of `FROM` whose `Subquery Scan` the plan does not keep, as `trivial_subqueryscan` takes the scan out of the plan. A column of such a subquery shows the target of the subquery with the names of the subquery, so the inner subqueries come first. A subquery of a set operation does not count.
207fn removed_scans(plan: &Plan<'_>, spaces: &mut [Namespace]) {
208    let mut kept = Vec::new();
209    subquery_scans(&plan.root, &mut kept);
210    let chain = |s: usize| {
211        let mut out = vec![s];
212        while let Some(parent) =
213            out.last().and_then(|&at| plan.scopes.get(at)).and_then(|at| at.parent)
214        {
215            out.push(parent);
216        }
217        out.reverse();
218        out
219    };
220    let mut order: Vec<usize> = (0..plan.scopes.len()).collect();
221    order.sort_by_key(|&s| std::cmp::Reverse(chain(s).len()));
222    for s in order {
223        let scope = &plan.scopes[s];
224        let Some(parent) = scope.parent.filter(|_| scope.leaf.is_none()) else { continue };
225        let outer = plan.scopes[parent].query;
226        if outer.set_op.is_some() {
227            continue;
228        }
229        let of = |rel: &rupg_analyze::Relation| {
230            rel.subquery.as_deref().is_some_and(|q| ptr::eq(q, scope.query))
231        };
232        let Some(r) = outer.relations.iter().position(of) else { continue };
233        if kept.contains(&(parent, r)) {
234            continue;
235        }
236        // `set_deparse_plan`: the columns of a set operation show as the targets of its leftmost `SELECT`, which is the first member of its `Append`.
237        let leftmost = scope.query.set_op.as_ref().and_then(|op| {
238            let leaf = scope.query.relations.get(op.left.leftmost())?.subquery.as_deref()?;
239            let at = plan.scopes.iter().position(|q| ptr::eq(q.query, leaf))?;
240            Some((at, leaf))
241        });
242        let (s, query) = leftmost.unwrap_or((s, scope.query));
243        let namespaces = chain(s).into_iter().map(|at| spaces[at].clone()).collect();
244        let targets = query.targets.iter().map(|t| t.expr.clone()).collect();
245        spaces[parent].removed.push((r, Rc::new(Removed { namespaces, targets })));
246    }
247}
248
249/// The scope and the relation of each `Subquery Scan` of the plan.
250fn subquery_scans(node: &Node<'_>, out: &mut Vec<(usize, usize)>) {
251    if let Kind::SubqueryScan(r) = node.kind {
252        out.push((node.scope, r));
253    }
254    for child in &node.children {
255        subquery_scans(child, out);
256    }
257    for sub in node.init_plans.iter().chain(&node.sub_plans) {
258        subquery_scans(&sub.plan, out);
259    }
260}
261
262/// The names of the windows of the nodes of a plan, by the scope and the number of each window.
263/// The scope of the query of `DO UPDATE` of each `ModifyTable` with `ON CONFLICT`, with the scope of the `ModifyTable`.
264fn conflict_scopes(node: &Node<'_>, out: &mut Vec<(usize, usize)>) {
265    if let Kind::Modify { conflict: Some(conflict), .. } = &node.kind
266        && let Some(update) = conflict.scope
267    {
268        out.push((update, node.scope));
269    }
270    for child in &node.children {
271        conflict_scopes(child, out);
272    }
273    for sub in node.init_plans.iter().chain(&node.sub_plans) {
274        conflict_scopes(&sub.plan, out);
275    }
276}
277
278fn window_names(node: &Node<'_>, out: &mut [Vec<Option<String>>]) {
279    if let Kind::WindowAgg { name, window, .. } = &node.kind
280        && let Some(slot) = out.get_mut(node.scope).and_then(|w| w.get_mut(*window))
281    {
282        *slot = Some(name.clone());
283    }
284    for child in &node.children {
285        window_names(child, out);
286    }
287    for sub in node.init_plans.iter().chain(&node.sub_plans) {
288        window_names(&sub.plan, out);
289    }
290}
291
292impl Texts for PlanTexts<'_, '_> {
293    fn expr(&mut self, scope: usize, expr: &Expr, prefix: bool) -> Result<String> {
294        let mut d = self.deparser(scope, prefix)?;
295        d.expr(expr, false)?;
296        Ok(d.buf)
297    }
298
299    fn below_expr(&mut self, scope: usize, expr: &Expr, prefix: bool) -> Result<String> {
300        let mut d = self.deparser(scope, prefix)?;
301        d.expr(expr, true)?;
302        Ok(d.buf)
303    }
304
305    fn scan_expr(&mut self, scope: usize, expr: &Expr, prefix: bool, own: usize) -> Result<String> {
306        let mut d = self.deparser(scope, prefix)?;
307        d.own = Some(own);
308        d.expr(expr, false)?;
309        Ok(d.buf)
310    }
311
312    fn refname(&mut self, scope: usize, relation: usize) -> String {
313        if relation == TARGET {
314            return self.targets.get(scope).cloned().flatten().unwrap_or_default();
315        }
316        self.spaces.get(scope).and_then(|s| s.names.get(relation)).cloned().unwrap_or_default()
317    }
318
319    fn quote(&self, name: &str) -> String {
320        quote_identifier(name)
321    }
322
323    fn object(&mut self, oid: u32, function: bool) -> (String, String) {
324        let (name, namespace) = if function {
325            match builtin::proc_by_oid(oid) {
326                Some(proc) => (proc.name, proc.namespace),
327                None => return (String::new(), String::new()),
328            }
329        } else {
330            match reg::class_name(oid, self.session) {
331                Some(found) => found,
332                None => return (String::new(), String::new()),
333            }
334        };
335        let schema = reg::namespace_name(namespace, self.session).unwrap_or_default();
336        (name.to_string(), schema.to_string())
337    }
338
339    fn window(
340        &mut self,
341        scope: usize,
342        window: usize,
343        rows_frame: bool,
344        partition: &[usize],
345        prefix: bool,
346    ) -> Result<String> {
347        let query = self.plan.scopes[scope].query;
348        let clause = query
349            .windows
350            .get(window)
351            .ok_or_else(|| Error::internal(format!("no window {window} in the query")))?;
352        let target = |target: usize| {
353            query
354                .targets
355                .get(target)
356                .map(|t| &t.expr)
357                .ok_or_else(|| Error::internal("an item of a window with no expression"))
358        };
359        let mut d = self.deparser(scope, prefix)?;
360        d.buf.push('(');
361        let items: Vec<&SortGroup> =
362            partition.iter().filter_map(|&i| clause.partition.get(i)).collect();
363        if !items.is_empty() {
364            d.buf.push_str("PARTITION BY ");
365            for (i, item) in items.iter().enumerate() {
366                if i > 0 {
367                    d.buf.push_str(", ");
368                }
369                d.expr(target(item.target)?, false)?;
370            }
371        }
372        if !clause.order.is_empty() {
373            if !items.is_empty() {
374                d.buf.push(' ');
375            }
376            d.buf.push_str("ORDER BY ");
377            for (i, item) in clause.order.iter().enumerate() {
378                if i > 0 {
379                    d.buf.push_str(", ");
380                }
381                // `show_window_keys` shows only the expression of each key, with no direction.
382                d.expr(target(item.target)?, false)?;
383            }
384        }
385        let space = !items.is_empty() || !clause.order.is_empty();
386        if rows_frame {
387            if space {
388                d.buf.push(' ');
389            }
390            d.buf.push_str("ROWS UNBOUNDED PRECEDING");
391        } else if clause.frame.options & FRAMEOPTION_NONDEFAULT != 0 {
392            if space {
393                d.buf.push(' ');
394            }
395            d.frame(&clause.frame)?;
396        }
397        d.buf.push(')');
398        Ok(d.buf)
399    }
400}
401
402impl Deparser<'_> {
403    /// `get_rule_expr` for a `SubPlan`: the name of the subplan of a subquery, or of the `ANY` that runs in the place of `EXISTS`, with the test of `ANY`, `ALL` or a comparison of rows, where each column of the subquery shows as `(SubPlan any_1).col1`. A column of `SET (a, b) = (SELECT ...)` shows as `(SubPlan multiexpr_1).col1` or `(InitPlan multiexpr_1).col1`. The value of an `InitPlan` shows as `(InitPlan expr_1).col1`, as `get_parameter` shows the parameter that the `InitPlan` sets.
404    pub(super) fn subplan_link(&mut self, sub: &SubLink, name: &SubName) -> Result<()> {
405        let sub = match &sub.alternative {
406            Some(any) if name.alternative => &**any,
407            _ => sub,
408        };
409        let plan = match (name.init, name.hashed) {
410            (true, _) => format!("InitPlan {}", name.name),
411            (false, true) => format!("hashed SubPlan {}", name.name),
412            (false, false) => format!("SubPlan {}", name.name),
413        };
414        if let SubLinkKind::Multi { column, .. } = sub.kind {
415            self.buf.push_str(&format!("({plan}).col{}", column + 1));
416            return Ok(());
417        }
418        if name.init {
419            match &sub.test {
420                Some(test) => self.subplan_test(test, &plan)?,
421                None => self.buf.push_str(&format!("({plan}).col1")),
422            }
423            return Ok(());
424        }
425        self.buf.push_str(match sub.kind {
426            SubLinkKind::Exists => "EXISTS(",
427            SubLinkKind::All => "(ALL ",
428            SubLinkKind::Any => "(ANY ",
429            SubLinkKind::Array => "ARRAY(",
430            SubLinkKind::RowCompare | SubLinkKind::Expr | SubLinkKind::Multi { .. } => "(",
431        });
432        match &sub.test {
433            Some(test) => self.subplan_test(test, &plan)?,
434            None => self.buf.push_str(&plan),
435        }
436        self.buf.push(')');
437        Ok(())
438    }
439
440    /// The test of a subquery, where a column of the subquery shows as a column of the subplan.
441    fn subplan_test(&mut self, test: &Expr, plan: &str) -> Result<()> {
442        let saved = self.subplan.replace(format!("({plan})"));
443        let done = self.expr(test, false);
444        self.subplan = saved;
445        done
446    }
447}