1use 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
14pub 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
28struct PlanTexts<'a, 'q> {
30 session: &'a dyn Session,
31 plan: &'a Plan<'q>,
32 spaces: Vec<Namespace>,
34 targets: Vec<Option<String>>,
36 windows: Vec<Vec<Option<String>>>,
38 subplans: Vec<SubName>,
39}
40
41impl<'a, 'q> PlanTexts<'a, 'q> {
42 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 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 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 if let Some(write) =
88 scope.query.write.as_ref().filter(|w| w.instead && scope.target.is_none())
89 {
90 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 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 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 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
185fn 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
206fn 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 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
249fn 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
262fn 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 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 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 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}