1mod explain;
6mod function;
7mod partqual;
8mod query;
9
10pub use explain::explain;
11pub use query::view_statement;
12
13use std::ptr;
14use std::rc::Rc;
15
16use rupg_analyze::node::{StoredBound, StoredDatum};
17use rupg_analyze::{
18 Aggref, BoolOp, BoolTest, Case, CastForm, Expr, ExprKind, Func, FuncForm, JsonBehavior,
19 JsonBehaviorKind, JsonCtor, JsonCtorKind, JsonEncoding, JsonExpr, JsonExprOp, JsonFormat,
20 JsonFormatKind, JsonItemType, JsonTable, JsonTablePlan, JsonTableScan, JsonWrapper, RowCompare,
21 SortGroup, SqlValue, SubLink, SubLinkKind, Var, WindowClause, WindowFunc, XmlExpr, XmlOp,
22};
23use rupg_analyze::{
24 MERGE_ACTION, RETURNING_NEW, RETURNING_OLD, default_opclass, node, sort_operators,
25};
26use rupg_catalog::{ConKind, Constraint, IndexInfo, PartStrategy, PartitionKey, RelKind, Relation};
27use rupg_common::{Error, Result, SqlState};
28use rupg_pgcatalog::builtin::{self, Named};
29use rupg_plan::TARGET;
30use rupg_types::{Value, oid, sql_name};
31
32use crate::io::to_text;
33use crate::reg;
34use crate::text::quote_identifier;
35use crate::{Call, Kernel, Session, bad_value, not_yet};
36
37const INDENT_VAR: i32 = 4;
39const INDENT_STD: i32 = 8;
41const INDENT_LIMIT: i32 = 40;
43const BTREE: u32 = 403;
45const HASH: u32 = 405;
47const DESC: i16 = 1;
49const NULLS_FIRST: i16 = 2;
51const FIRST_USER_OID: u32 = 16384;
53
54#[derive(Clone, Debug, Default)]
56struct Namespace {
57 names: Vec<String>,
59 columns: Vec<Vec<String>>,
61 print_aliases: Vec<bool>,
63 merged: Vec<(Expr, String)>,
65 relations: Vec<rupg_analyze::Relation>,
67 joins: Vec<JoinNames>,
69 inside: Vec<usize>,
71 removed: Vec<(usize, Rc<Removed>)>,
73 target: Option<(String, Vec<String>)>,
75 returning: Option<(String, String, Vec<String>)>,
77}
78
79#[derive(Debug)]
81struct Removed {
82 namespaces: Vec<Namespace>,
84 targets: Vec<Expr>,
86}
87
88#[derive(Clone, Debug, Default)]
90struct JoinNames {
91 name: Option<String>,
93 relations: Vec<usize>,
95 columns: Vec<String>,
97 using: Vec<String>,
99 values: Vec<Expr>,
101 print_aliases: bool,
103}
104
105struct Deparser<'a> {
107 session: &'a dyn Session,
108 namespaces: Vec<Namespace>,
110 paren: bool,
112 indent: i32,
114 pub(crate) flat: bool,
116 wrap: i32,
118 prefix: bool,
120 names_visible: bool,
122 in_group_by: bool,
124 outputs: Vec<(String, Option<Var>)>,
126 windows: Vec<WindowClause>,
128 window_targets: Vec<Expr>,
130 subplans: Vec<SubName>,
132 subplan: Option<String>,
134 window_names: Vec<Option<String>>,
136 outer_prefix: bool,
138 pub(crate) own: Option<usize>,
140 function: Option<(String, Vec<String>)>,
142 buf: String,
143}
144
145#[derive(Clone, Debug)]
147struct SubName {
148 link: *const SubLink,
150 name: String,
151 init: bool,
153 hashed: bool,
155 alternative: bool,
157}
158
159impl<'a> Deparser<'a> {
160 fn for_columns(
162 session: &'a dyn Session,
163 name: &str,
164 columns: Vec<String>,
165 paren: bool,
166 ) -> Deparser<'a> {
167 let mut d = Deparser::new(session, paren, 0);
168 d.namespaces.push(Namespace {
169 names: vec![name.to_string()],
170 columns: vec![columns],
171 print_aliases: vec![false],
172 merged: Vec::new(),
173 relations: Vec::new(),
174 joins: Vec::new(),
175 inside: Vec::new(),
176 removed: Vec::new(),
177 target: None,
178 returning: None,
179 });
180 d
181 }
182
183 fn new(session: &'a dyn Session, paren: bool, wrap: i32) -> Deparser<'a> {
185 Deparser {
186 session,
187 namespaces: Vec::new(),
188 paren,
189 indent: 0,
190 flat: false,
191 wrap,
192 prefix: false,
193 names_visible: true,
194 in_group_by: false,
195 outputs: Vec::new(),
196 windows: Vec::new(),
197 window_targets: Vec::new(),
198 subplans: Vec::new(),
199 subplan: None,
200 window_names: Vec::new(),
201 outer_prefix: false,
202 own: None,
203 function: None,
204 buf: String::new(),
205 }
206 }
207
208 fn parameter(&mut self, n: usize) {
210 let named = self.function.as_ref().and_then(|(function, names)| {
211 let name = names.get(n.checked_sub(1)?).filter(|name| !name.is_empty())?;
212 Some((quote_identifier(function), quote_identifier(name)))
213 });
214 let Some((function, name)) = named else {
215 self.buf.push_str(&format!("${n}"));
216 return;
217 };
218 if self.namespaces.iter().any(|space| !space.names.is_empty()) {
219 self.buf.push_str(&function);
220 self.buf.push('.');
221 }
222 self.buf.push_str(&name);
223 }
224
225 fn keyword(&mut self, word: &str, before: i32, after: i32, plus: i32) {
227 if self.flat {
228 self.buf.push_str(word);
229 return;
230 }
231 self.indent += before;
232 let kept = self.buf.trim_end_matches(' ').len();
233 self.buf.truncate(kept);
234 self.buf.push('\n');
235 let amount = if self.indent < INDENT_LIMIT {
236 self.indent.max(0)
237 } else {
238 (INDENT_LIMIT + (self.indent - INDENT_LIMIT) / (INDENT_STD / 2)) % INDENT_LIMIT
239 } + plus;
240 for _ in 0..amount {
241 self.buf.push(' ');
242 }
243 self.buf.push_str(word);
244 self.indent = (self.indent + after).max(0);
245 }
246
247 fn space(&mut self) {
249 if self.flat {
250 self.buf.push(' ');
251 }
252 }
253
254 fn variable(&mut self, var: &Var, in_order_by: bool) -> Result<String> {
256 let bogus = || Error::internal(format!("bogus varlevelsup: {}", var.levels_up));
257 let depth = self.namespaces.len().checked_sub(var.levels_up + 1).ok_or_else(bogus)?;
258 if var.relation == MERGE_ACTION {
259 self.buf.push_str("MERGE_ACTION()");
260 return Ok("merge_action".to_string());
261 }
262 let space = &self.namespaces[depth];
263 let target = match (&space.target, &space.returning, var.relation) {
264 (Some((name, columns)), _, TARGET) => Some((name, columns)),
265 (_, Some((old, _, columns)), RETURNING_OLD) => Some((old, columns)),
266 (_, Some((_, new, columns)), RETURNING_NEW) => Some((new, columns)),
267 _ => None,
268 };
269 let refname = match target {
270 Some((name, _)) => name.clone(),
271 None => space.names.get(var.relation).cloned().unwrap_or_default(),
272 };
273 let columns =
274 target.map(|(_, columns)| columns).or_else(|| space.columns.get(var.relation));
275 let attname = match usize::try_from(var.attnum) {
276 Ok(n) if n > 0 => columns.and_then(|c| c.get(n - 1)).cloned(),
277 _ => system_column(var.attnum).map(str::to_string),
278 };
279 let attname =
280 attname.ok_or_else(|| Error::internal(format!("invalid attnum {}", var.attnum)))?;
281 let removed = space.removed.iter().find(|(r, _)| *r == var.relation);
283 if let Some(removed) = removed.map(|(_, removed)| Rc::clone(removed))
284 && let Ok(at @ 1..) = usize::try_from(var.attnum)
285 && let Some(target) = removed.targets.get(at - 1)
286 {
287 let outer = std::mem::replace(&mut self.namespaces, removed.namespaces.clone());
288 let parens = !matches!(target.kind, ExprKind::Var(_));
289 if parens {
290 self.buf.push('(');
291 }
292 let done = self.expr(target, true);
293 if parens {
294 self.buf.push(')');
295 }
296 self.namespaces = outer;
297 done?;
298 return Ok(attname);
299 }
300 let mut prefix = self.prefix
301 || (self.outer_prefix && var.levels_up > 0)
302 || self.own.is_some_and(|o| var.levels_up == 0 && var.relation != o)
303 || [RETURNING_OLD, RETURNING_NEW].contains(&var.relation);
304 if in_order_by && !self.in_group_by && !prefix {
305 prefix = self.outputs.iter().any(|(name, v)| *name == attname && *v != Some(*var));
306 }
307 if prefix && !refname.is_empty() {
308 self.buf.push_str("e_identifier(&refname));
309 self.buf.push('.');
310 }
311 self.buf.push_str("e_identifier(&attname));
312 Ok(attname)
313 }
314
315 fn whole_row(&mut self, var: &Var, e: &Expr, top: bool) -> Result<()> {
317 let bogus = || Error::internal(format!("bogus varlevelsup: {}", var.levels_up));
318 let depth = self.namespaces.len().checked_sub(var.levels_up + 1).ok_or_else(bogus)?;
319 let space = &self.namespaces[depth];
320 let refname = match (&space.returning, var.relation) {
321 (Some((old, ..)), RETURNING_OLD) => old.clone(),
322 (Some((_, new, _)), RETURNING_NEW) => new.clone(),
323 _ => space.names.get(var.relation).cloned().unwrap_or_default(),
324 };
325 if !refname.is_empty() {
326 self.buf.push_str("e_identifier(&refname));
327 self.buf.push('.');
328 }
329 self.buf.push('*');
330 if top {
331 self.buf.push_str("::");
332 self.buf.push_str(®::type_text(e.ty, Some(e.typmod), self.session)?);
333 }
334 Ok(())
335 }
336
337 fn open(&mut self) {
339 if !self.paren {
340 self.buf.push('(');
341 }
342 }
343
344 fn close(&mut self) {
346 if !self.paren {
347 self.buf.push(')');
348 }
349 }
350
351 fn open_pretty(&mut self) {
353 if self.paren {
354 self.buf.push('(');
355 }
356 }
357
358 fn close_pretty(&mut self) {
360 if self.paren {
361 self.buf.push(')');
362 }
363 }
364
365 fn paren(&mut self, e: &Expr, implicit: bool, parent: &Expr) -> Result<()> {
367 let need = self.paren && !is_simple(e, parent, self.paren);
368 if need {
369 self.buf.push('(');
370 }
371 self.expr(e, implicit)?;
372 if need {
373 self.buf.push(')');
374 }
375 Ok(())
376 }
377
378 fn list(&mut self, items: &[Expr], implicit: bool) -> Result<()> {
380 for (i, item) in items.iter().enumerate() {
381 if i > 0 {
382 self.buf.push_str(", ");
383 }
384 self.expr(item, implicit)?;
385 }
386 Ok(())
387 }
388
389 fn expr(&mut self, e: &Expr, implicit: bool) -> Result<()> {
391 if let Some((join, column)) = self.join_column(e) {
392 if self.prefix {
393 self.buf.push_str("e_identifier(&join));
394 self.buf.push('.');
395 }
396 self.buf.push_str("e_identifier(&column));
397 return Ok(());
398 }
399 if let Some(name) = self.merged_name(e) {
400 self.buf.push_str("e_identifier(&name));
401 return Ok(());
402 }
403 match &e.kind {
404 ExprKind::Const(value) => self.constant(e, value, 0)?,
405 ExprKind::Param(n) => self.parameter(*n),
406 ExprKind::Var(var) if var.attnum == 0 => self.whole_row(var, e, false)?,
407 ExprKind::Var(var) => {
408 self.variable(var, false)?;
409 }
410 ExprKind::FieldSelect(arg, field) => {
411 let parens =
413 !matches!(arg.kind, ExprKind::Subscript(_) | ExprKind::FieldSelect(..));
414 if parens {
415 self.buf.push('(');
416 }
417 self.expr(arg, true)?;
418 if parens {
419 self.buf.push(')');
420 }
421 let name = self.field_name(arg, *field)?;
422 self.buf.push('.');
423 self.buf.push_str("e_identifier(&name));
424 }
425 ExprKind::Func(f) => self.func(e, f, implicit)?,
426 ExprKind::NamedArg(na) => {
427 self.buf.push_str("e_identifier(&na.name));
428 self.buf.push_str(" => ");
429 self.expr(&na.arg, implicit)?;
430 }
431 ExprKind::Relabel(arg, form) => self.cast_node(e, arg, *form, e.typmod, implicit)?,
432 ExprKind::CoerceViaIo(arg, form) => self.cast_node(e, arg, *form, -1, implicit)?,
433 ExprKind::ArrayCoerce { arg, form, .. } => {
434 self.cast_node(e, arg, *form, e.typmod, implicit)?;
435 }
436 ExprKind::CoerceToDomain(arg, CastForm::Implicit) if !implicit => {
438 self.expr(arg, false)?;
439 }
440 ExprKind::CoerceToDomain(arg, _) => self.coercion(arg, e.ty, e.typmod, e)?,
441 ExprKind::DomainValue => self.buf.push_str("VALUE"),
442 ExprKind::FieldStore(_) => return Err(Error::internal("unexpected FieldStore")),
444 ExprKind::Subscript(sub) => {
445 let parens =
447 !matches!(sub.container.kind, ExprKind::Var(_) | ExprKind::FieldSelect(..));
448 if parens {
449 self.buf.push('(');
450 }
451 self.expr(&sub.container, implicit)?;
452 if parens {
453 self.buf.push(')');
454 }
455 for (i, upper) in sub.upper.iter().enumerate() {
456 self.buf.push('[');
457 if let Some(lower) = &sub.lower {
458 if let Some(Some(lower)) = lower.get(i) {
459 self.expr(lower, false)?;
460 }
461 self.buf.push(':');
462 }
463 if let Some(upper) = upper {
464 self.expr(upper, false)?;
465 }
466 self.buf.push(']');
467 }
468 if let Some(assign) = &sub.assign {
470 self.buf.push_str(" := ");
471 self.expr(assign, false)?;
472 }
473 }
474 ExprKind::Bool(op, args) => {
475 self.open();
476 if *op == BoolOp::Not {
477 self.buf.push_str("NOT ");
478 }
479 let word = if *op == BoolOp::And { " AND " } else { " OR " };
480 for (i, arg) in args.iter().enumerate() {
481 if i > 0 {
482 self.buf.push_str(word);
483 }
484 self.paren(arg, false, e)?;
485 }
486 self.close();
487 }
488 ExprKind::NullTest(arg, is_null) => {
489 self.open();
490 self.paren(arg, true, e)?;
491 self.buf.push_str(if *is_null { " IS NULL" } else { " IS NOT NULL" });
492 self.close();
493 }
494 ExprKind::BooleanTest(arg, test) => {
495 self.open();
496 self.paren(arg, false, e)?;
497 self.buf.push_str(match test {
498 BoolTest::IsTrue => " IS TRUE",
499 BoolTest::IsNotTrue => " IS NOT TRUE",
500 BoolTest::IsFalse => " IS FALSE",
501 BoolTest::IsNotFalse => " IS NOT FALSE",
502 BoolTest::IsUnknown => " IS UNKNOWN",
503 BoolTest::IsNotUnknown => " IS NOT UNKNOWN",
504 });
505 self.close();
506 }
507 ExprKind::Case(case) => self.case(case)?,
508 ExprKind::CaseTest => self.buf.push_str("CASE_TEST_EXPR"),
509 ExprKind::Coalesce(args) => {
510 self.buf.push_str("COALESCE(");
511 self.list(args, true)?;
512 self.buf.push(')');
513 }
514 ExprKind::Row(args, form) => {
515 self.buf.push_str("ROW(");
516 self.list(args, true)?;
517 self.buf.push(')');
518 if *form == CastForm::Explicit {
519 let ty = reg::type_text(e.ty, Some(-1), self.session)?;
520 self.buf.push_str(&format!("::{ty}"));
521 }
522 }
523 ExprKind::JoinRow(row) => match &row.alias {
524 Some(_) => {
525 let name = self.join_row_name(row);
526 self.buf.push_str("e_identifier(&name));
527 self.buf.push_str(".*");
528 }
529 None => {
530 self.buf.push_str("ROW(");
531 self.list(&row.fields, true)?;
532 self.buf.push(')');
533 }
534 },
535 ExprKind::RowCompare(rc) => self.row_compare(rc)?,
536 ExprKind::MinMax { greatest, args, .. } => {
537 self.buf.push_str(if *greatest { "GREATEST(" } else { "LEAST(" });
538 self.list(args, true)?;
539 self.buf.push(')');
540 }
541 ExprKind::NullIf { args, .. } => {
542 self.buf.push_str("NULLIF(");
543 self.list(args, true)?;
544 self.buf.push(')');
545 }
546 ExprKind::Distinct { not, args, .. } => {
547 if *not {
549 self.open();
550 self.buf.push_str("NOT ");
551 }
552 self.open();
553 self.paren(&args[0], true, e)?;
554 self.buf.push_str(" IS DISTINCT FROM ");
555 self.paren(&args[1], true, e)?;
556 self.close();
557 if *not {
558 self.close();
559 }
560 }
561 ExprKind::ScalarArrayOp { func, any, args } => {
562 self.open();
563 self.paren(&args[0], true, e)?;
564 let element = builtin::type_by_oid(args[1].ty).map_or(0, |t| t.elem);
565 let op = builtin::operators()
566 .iter()
567 .filter(|op| op.code == *func)
568 .max_by_key(|op| (op.left == args[0].ty, op.right == element))
569 .ok_or_else(|| Error::internal(format!("no operator for function {func}")))?;
570 self.buf.push_str(&format!(" {} {} (", op.name, if *any { "ANY" } else { "ALL" }));
571 self.paren(&args[1], true, e)?;
572 self.buf.push(')');
573 self.close();
574 }
575 ExprKind::Array { elements, .. } => {
576 self.buf.push_str("ARRAY[");
577 self.list(elements, true)?;
578 self.buf.push(']');
579 if elements.is_empty() {
580 let ty = reg::type_text(e.ty, Some(-1), self.session)?;
581 self.buf.push_str(&format!("::{ty}"));
582 }
583 }
584 ExprKind::SqlValue(value) => self.buf.push_str(&sql_value(*value)),
585 ExprKind::Agg(agg) => self.aggregate(agg)?,
586 ExprKind::Grouping(g) => {
587 self.buf.push_str("GROUPING(");
588 self.list(&g.args, true)?;
589 self.buf.push(')');
590 }
591 ExprKind::PlaceHolder(p) => {
593 self.buf.push('(');
594 self.expr(&p.expr, true)?;
595 self.buf.push(')');
596 }
597 ExprKind::Window(w) => self.window_func(w)?,
598 ExprKind::SubLink(sub) => {
599 let link = ptr::from_ref(sub.as_ref());
600 match self.subplans.iter().find(|s| s.link == link).cloned() {
601 Some(name) => self.subplan_link(sub, &name)?,
602 None => self.sublink(sub)?,
603 }
604 }
605 ExprKind::SubColumn(column) => {
606 let Some(subplan) = &self.subplan else {
607 return Err(Error::internal("a column of a subquery outside its test"));
608 };
609 self.buf.push_str(&format!("{subplan}.col{}", column + 1));
610 }
611 ExprKind::JsonValue(v) => {
612 self.expr(&v.raw, false)?;
613 json_format(v.format, &mut self.buf);
614 }
615 ExprKind::JsonCtor(c) => self.json_ctor(c)?,
616 ExprKind::Xml(x) => self.xml_expr(x)?,
617 ExprKind::JsonExpr(j) => self.json_expr(j, implicit)?,
618 ExprKind::JsonTable(_) => return Err(Error::internal("JSON_TABLE out of FROM")),
619 ExprKind::CurrentOf(c) => match c.args.first().map(|a| &a.kind) {
620 Some(ExprKind::Param(n)) => self.buf.push_str(&format!("CURRENT OF ${n}")),
621 Some(ExprKind::Const(name)) => {
622 let name = name.as_str().unwrap_or_default();
623 self.buf.push_str(&format!("CURRENT OF {}", quote_identifier(name)));
624 }
625 _ => return Err(Error::internal("CURRENT OF with no cursor")),
626 },
627 ExprKind::IsJson(p) => {
628 self.open();
629 self.paren(&p.expr, true, e)?;
630 self.buf.push_str(" IS JSON");
631 self.buf.push_str(match p.item {
632 JsonItemType::Any => "",
633 JsonItemType::Object => " OBJECT",
634 JsonItemType::Array => " ARRAY",
635 JsonItemType::Scalar => " SCALAR",
636 });
637 if p.unique {
638 self.buf.push_str(" WITH UNIQUE KEYS");
639 }
640 self.close();
641 }
642 }
643 Ok(())
644 }
645
646 fn xml_expr(&mut self, x: &XmlExpr) -> Result<()> {
648 self.buf.push_str(match x.op {
649 XmlOp::Concat => "XMLCONCAT(",
650 XmlOp::Element => "XMLELEMENT(",
651 XmlOp::Forest => "XMLFOREST(",
652 XmlOp::Parse => "XMLPARSE(",
653 XmlOp::Pi => "XMLPI(",
654 XmlOp::Root => "XMLROOT(",
655 XmlOp::Serialize => "XMLSERIALIZE(",
656 XmlOp::Document => "",
657 });
658 if matches!(x.op, XmlOp::Parse | XmlOp::Serialize) {
659 self.buf.push_str(if x.document { "DOCUMENT " } else { "CONTENT " });
660 }
661 let mut comma = false;
662 if let Some(name) = &x.name {
663 self.buf.push_str("NAME ");
664 self.buf.push_str("e_identifier(&sql_name(name)));
665 comma = true;
666 }
667 if !x.named.is_empty() {
668 let forest = x.op == XmlOp::Forest;
669 if !forest {
670 if comma {
671 self.buf.push_str(", ");
672 }
673 self.buf.push_str("XMLATTRIBUTES(");
674 comma = false;
675 }
676 for (name, e) in &x.named {
677 if comma {
678 self.buf.push_str(", ");
679 }
680 self.expr(e, true)?;
681 self.buf.push_str(" AS ");
682 self.buf.push_str("e_identifier(&sql_name(name)));
683 comma = true;
684 }
685 if !forest {
686 self.buf.push(')');
687 }
688 }
689 if !x.args.is_empty() {
690 if comma {
691 self.buf.push_str(", ");
692 }
693 match x.op {
694 XmlOp::Parse => {
695 self.expr(&x.args[0], true)?;
696 let preserve = matches!(
697 x.args.get(1).map(|e| &e.kind),
698 Some(ExprKind::Const(Value::Bool(true)))
699 );
700 self.buf.push_str(if preserve {
701 " PRESERVE WHITESPACE"
702 } else {
703 " STRIP WHITESPACE"
704 });
705 }
706 XmlOp::Root => {
707 self.expr(&x.args[0], true)?;
708 self.buf.push_str(", VERSION ");
709 match x.args.get(1) {
710 Some(Expr { kind: ExprKind::Const(Value::Null), .. }) | None => {
711 self.buf.push_str("NO VALUE");
712 }
713 Some(version) => self.expr(version, false)?,
714 }
715 self.buf.push_str(match x.args.get(2).map(|e| &e.kind) {
716 Some(ExprKind::Const(Value::Int4(0))) => ", STANDALONE YES",
717 Some(ExprKind::Const(Value::Int4(1))) => ", STANDALONE NO",
718 Some(ExprKind::Const(Value::Int4(2))) => ", STANDALONE NO VALUE",
719 _ => "",
720 });
721 }
722 XmlOp::Document => {
724 self.open_pretty();
725 self.list(&x.args, false)?;
726 self.close_pretty();
727 }
728 _ => self.list(&x.args, true)?,
729 }
730 }
731 if x.op == XmlOp::Serialize {
732 self.buf.push_str(" AS ");
733 self.buf.push_str(®::type_text(x.target.0, Some(x.target.1), self.session)?);
734 self.buf.push_str(if x.indent { " INDENT" } else { " NO INDENT" });
735 }
736 self.buf.push_str(if x.op == XmlOp::Document { " IS DOCUMENT" } else { ")" });
737 Ok(())
738 }
739
740 fn json_expr(&mut self, j: &JsonExpr, showimplicit: bool) -> Result<()> {
742 self.buf.push_str(match j.op {
743 JsonExprOp::Exists => "JSON_EXISTS(",
744 JsonExprOp::Query => "JSON_QUERY(",
745 JsonExprOp::Value => "JSON_VALUE(",
746 });
747 self.expr(&j.formatted, showimplicit)?;
748 self.buf.push_str(", ");
749 match &j.path.kind {
750 ExprKind::Const(value) => self.constant(&j.path, value, -1)?,
751 _ => self.expr(&j.path, showimplicit)?,
752 }
753 for (i, (name, value)) in j.passing.iter().enumerate() {
754 self.buf.push_str(if i == 0 { " PASSING " } else { ", " });
755 self.expr(value, showimplicit)?;
756 self.buf.push_str(" AS ");
757 self.buf.push_str("e_identifier(name));
758 }
759 let returning = j.returning;
760 if j.op != JsonExprOp::Exists || returning.ty != oid::BOOL {
761 let ty = reg::type_text(returning.ty, Some(returning.typmod), self.session)?;
762 self.buf.push_str(&format!(" RETURNING {ty}"));
763 let default = if returning.ty == oid::JSONB {
764 JsonFormatKind::Jsonb
765 } else {
766 JsonFormatKind::Json
767 };
768 if j.op != JsonExprOp::Query || returning.format.kind != default {
769 json_format(returning.format, &mut self.buf);
770 }
771 }
772 if j.op == JsonExprOp::Query {
773 self.buf.push_str(match j.wrapper {
774 JsonWrapper::Conditional => " WITH CONDITIONAL WRAPPER",
775 JsonWrapper::Unconditional => " WITH UNCONDITIONAL WRAPPER",
776 JsonWrapper::None | JsonWrapper::Unspec => " WITHOUT WRAPPER",
777 });
778 self.buf.push_str(if j.omit_quotes { " OMIT QUOTES" } else { " KEEP QUOTES" });
779 }
780 let default = if j.op == JsonExprOp::Exists {
781 JsonBehaviorKind::False
782 } else {
783 JsonBehaviorKind::Null
784 };
785 if let Some(on_empty) = &j.on_empty
786 && on_empty.kind != default
787 {
788 self.json_behavior(on_empty, "EMPTY")?;
789 }
790 if j.on_error.kind != default {
791 self.json_behavior(&j.on_error, "ERROR")?;
792 }
793 self.buf.push(')');
794 Ok(())
795 }
796
797 pub(crate) fn json_table(&mut self, t: &JsonTable) -> Result<()> {
799 let JsonTablePlan::Scan(root) = &t.plan else {
800 return Err(Error::internal("a JSON_TABLE plan with no scan at the top"));
801 };
802 self.buf.push_str("JSON_TABLE(");
803 self.indent += INDENT_VAR;
804 self.keyword("", 0, 0, 0);
805 self.expr(&t.context, false)?;
806 self.buf.push_str(", ");
807 self.json_table_path(root)?;
808 if !t.passing.is_empty() {
809 self.buf.push(' ');
810 self.keyword("PASSING ", 0, 0, 0);
811 self.indent += INDENT_VAR;
812 for (i, (name, value)) in t.passing.iter().enumerate() {
813 if i > 0 {
814 self.buf.push_str(", ");
815 }
816 self.keyword("", 0, 0, 0);
817 self.expr(value, false)?;
818 self.buf.push_str(" AS ");
819 self.buf.push_str("e_identifier(name));
820 }
821 self.indent -= INDENT_VAR;
822 }
823 self.json_table_columns(t, root)?;
824 match t.on_error {
825 JsonBehaviorKind::EmptyArray => {}
826 kind => {
827 self.buf.push(' ');
828 self.buf.push_str(kind.name());
829 self.buf.push_str(" ON ERROR");
830 }
831 }
832 self.indent -= INDENT_VAR;
833 self.keyword(")", 0, 0, 0);
834 Ok(())
835 }
836
837 fn json_table_path(&mut self, scan: &JsonTableScan) -> Result<()> {
839 let path = Expr::new(ExprKind::Const(scan.path.clone()), oid::JSONPATH);
840 self.constant(&path, &scan.path, -1)?;
841 self.buf.push_str(" AS ");
842 self.buf.push_str("e_identifier(&scan.name));
843 Ok(())
844 }
845
846 fn json_table_columns(&mut self, t: &JsonTable, scan: &JsonTableScan) -> Result<()> {
848 self.buf.push(' ');
849 self.keyword("COLUMNS (", 0, 0, 0);
850 self.indent += INDENT_VAR;
851 for at in scan.columns.clone() {
852 let column = &t.columns[at];
853 if at > scan.columns.start {
854 self.buf.push_str(", ");
855 }
856 self.keyword("", 0, 0, 0);
857 self.buf.push_str("e_identifier(&column.name));
858 self.buf.push(' ');
859 let Some(ExprKind::JsonExpr(j)) = column.expr.as_ref().map(|e| &e.kind) else {
860 self.buf.push_str("FOR ORDINALITY");
861 continue;
862 };
863 self.buf.push_str(®::type_text(column.ty, Some(column.typmod), self.session)?);
864 let default = if j.op == JsonExprOp::Exists {
865 self.buf.push_str(" EXISTS");
866 JsonBehaviorKind::False
867 } else {
868 if j.op == JsonExprOp::Query
869 && builtin::type_by_oid(column.ty).is_some_and(|row| row.category == b'S')
870 {
871 self.buf.push_str(" FORMAT JSON");
872 }
873 JsonBehaviorKind::Null
874 };
875 self.buf.push_str(" PATH ");
876 match &j.path.kind {
877 ExprKind::Const(value) => self.constant(&j.path, value, -1)?,
878 _ => self.expr(&j.path, false)?,
879 }
880 if j.op == JsonExprOp::Query {
881 self.buf.push_str(match j.wrapper {
882 JsonWrapper::Conditional => " WITH CONDITIONAL WRAPPER",
883 JsonWrapper::Unconditional => " WITH UNCONDITIONAL WRAPPER",
884 JsonWrapper::None | JsonWrapper::Unspec => " WITHOUT WRAPPER",
885 });
886 self.buf.push_str(if j.omit_quotes { " OMIT QUOTES" } else { " KEEP QUOTES" });
887 }
888 if let Some(on_empty) = &j.on_empty
889 && on_empty.kind != default
890 {
891 self.json_behavior(on_empty, "EMPTY")?;
892 }
893 if j.on_error.kind != default {
894 self.json_behavior(&j.on_error, "ERROR")?;
895 }
896 }
897 if let Some(child) = &scan.child {
898 self.json_table_nested(t, child, !scan.columns.is_empty())?;
899 }
900 self.indent -= INDENT_VAR;
901 self.keyword(")", 0, 0, 0);
902 Ok(())
903 }
904
905 fn json_table_nested(
907 &mut self,
908 t: &JsonTable,
909 plan: &JsonTablePlan,
910 comma: bool,
911 ) -> Result<()> {
912 match plan {
913 JsonTablePlan::Scan(scan) => {
914 if comma {
915 self.buf.push(',');
916 }
917 self.buf.push(' ');
918 self.keyword("NESTED PATH ", 0, 0, 0);
919 self.json_table_path(scan)?;
920 self.json_table_columns(t, scan)
921 }
922 JsonTablePlan::Sibling(left, right) => {
923 self.json_table_nested(t, left, comma)?;
924 self.json_table_nested(t, right, true)
925 }
926 }
927 }
928
929 fn json_behavior(&mut self, behavior: &JsonBehavior, on: &str) -> Result<()> {
931 match (&behavior.expr, behavior.kind) {
932 (Some(expr), JsonBehaviorKind::Default) => {
933 self.buf.push_str(" DEFAULT ");
934 self.expr(expr, false)?;
935 }
936 (_, kind) => {
937 self.buf.push(' ');
938 self.buf.push_str(kind.name());
939 }
940 }
941 self.buf.push_str(" ON ");
942 self.buf.push_str(on);
943 Ok(())
944 }
945
946 fn json_ctor(&mut self, c: &JsonCtor) -> Result<()> {
948 self.buf.push_str(match c.kind {
949 JsonCtorKind::Object => "JSON_OBJECT(",
950 JsonCtorKind::Array => "JSON_ARRAY(",
951 JsonCtorKind::Parse => "JSON(",
952 JsonCtorKind::Scalar => "JSON_SCALAR(",
953 JsonCtorKind::Serialize => "JSON_SERIALIZE(",
954 JsonCtorKind::ObjectAgg | JsonCtorKind::ArrayAgg => return self.json_agg_ctor(c),
955 JsonCtorKind::ArrayQuery => return self.json_array_query(c),
956 });
957 for (i, arg) in c.args.iter().enumerate() {
958 if i > 0 {
959 let object = c.kind == JsonCtorKind::Object && i % 2 != 0;
960 self.buf.push_str(if object { " : " } else { ", " });
961 }
962 self.expr(arg, true)?;
963 }
964 self.json_ctor_options(c)?;
965 self.buf.push(')');
966 Ok(())
967 }
968
969 fn json_agg_ctor(&mut self, c: &JsonCtor) -> Result<()> {
971 let saved = std::mem::take(&mut self.buf);
972 let done = self.json_ctor_options(c);
973 let options = std::mem::replace(&mut self.buf, saved);
974 done?;
975 let object = c.kind == JsonCtorKind::ObjectAgg;
976 let json =
977 Some((if object { "JSON_OBJECTAGG" } else { "JSON_ARRAYAGG" }, &*options, object));
978 match c.func.as_ref().map(|f| &f.kind) {
979 Some(ExprKind::Agg(agg)) => self.aggregate_as(agg, json),
980 Some(ExprKind::Window(w)) => self.window_func_as(w, json),
981 _ => Err(Error::internal("invalid JsonConstructorExpr underlying node type")),
982 }
983 }
984
985 fn json_ctor_options(&mut self, c: &JsonCtor) -> Result<()> {
987 if c.absent_on_null {
988 if matches!(c.kind, JsonCtorKind::Object | JsonCtorKind::ObjectAgg) {
989 self.buf.push_str(" ABSENT ON NULL");
990 }
991 } else if matches!(c.kind, JsonCtorKind::Array | JsonCtorKind::ArrayAgg) {
992 self.buf.push_str(" NULL ON NULL");
993 }
994 if c.unique {
995 self.buf.push_str(" WITH UNIQUE KEYS");
996 }
997 if !matches!(c.kind, JsonCtorKind::Parse | JsonCtorKind::Scalar) {
998 let returning = c.returning;
999 let ty = reg::type_text(returning.ty, Some(returning.typmod), self.session)?;
1000 self.buf.push_str(&format!(" RETURNING {ty}"));
1001 let default = if returning.ty == oid::JSONB {
1002 JsonFormatKind::Jsonb
1003 } else {
1004 JsonFormatKind::Json
1005 };
1006 if returning.format.kind != default {
1007 json_format(returning.format, &mut self.buf);
1008 }
1009 }
1010 Ok(())
1011 }
1012
1013 fn constant(&mut self, e: &Expr, value: &Value, showtype: i32) -> Result<()> {
1015 let label = |this: &Self| reg::type_text(e.ty, Some(e.typmod), this.session);
1016 if value.is_null() {
1017 self.buf.push_str("NULL");
1018 if showtype >= 0 {
1019 let ty = label(self)?;
1020 self.buf.push_str(&format!("::{ty}"));
1021 }
1022 return Ok(());
1023 }
1024 let text = to_text(e.ty, value, self.session)?;
1025 let mut need = false;
1026 match e.ty {
1027 oid::INT4 if !text.starts_with('-') => self.buf.push_str(&text),
1028 oid::NUMERIC
1029 if text.starts_with(|c: char| c.is_ascii_digit())
1030 && text.contains(['e', 'E', '.']) =>
1031 {
1032 self.buf.push_str(&text);
1033 }
1034 oid::INT4 | oid::NUMERIC => {
1035 self.buf.push_str(&format!("'{text}'"));
1036 need = true;
1037 }
1038 oid::BOOL => {
1039 self.buf.push_str(if value.as_bool() == Some(true) { "true" } else { "false" })
1040 }
1041 _ => {
1042 self.buf.push('\'');
1044 self.buf.push_str(&text.replace('\'', "''"));
1045 self.buf.push('\'');
1046 }
1047 }
1048 if showtype < 0 {
1049 return Ok(());
1050 }
1051 let need = match e.ty {
1052 oid::BOOL | oid::UNKNOWN => false,
1053 oid::INT4 => need,
1054 oid::NUMERIC => need || e.typmod >= 0,
1055 _ => true,
1056 };
1057 if need || showtype > 0 {
1058 let ty = label(self)?;
1059 self.buf.push_str(&format!("::{ty}"));
1060 }
1061 Ok(())
1062 }
1063
1064 fn cast_node(
1066 &mut self,
1067 e: &Expr,
1068 arg: &Expr,
1069 form: CastForm,
1070 typmod: i32,
1071 implicit: bool,
1072 ) -> Result<()> {
1073 if form == CastForm::Implicit && !implicit {
1074 self.paren(arg, false, e)
1075 } else {
1076 self.coercion(arg, e.ty, typmod, e)
1077 }
1078 }
1079
1080 fn coercion(&mut self, arg: &Expr, ty: u32, typmod: i32, parent: &Expr) -> Result<()> {
1082 match &arg.kind {
1083 ExprKind::Const(value) if arg.ty == ty && arg.typmod == -1 => {
1084 self.constant(arg, value, -1)?;
1085 }
1086 _ => {
1087 self.open();
1088 self.paren(arg, false, parent)?;
1089 self.close();
1090 }
1091 }
1092 let ty = reg::type_text(ty, Some(typmod), self.session)?;
1093 self.buf.push_str(&format!("::{ty}"));
1094 Ok(())
1095 }
1096
1097 fn func(&mut self, e: &Expr, f: &Func, implicit: bool) -> Result<()> {
1099 match f.form {
1100 FuncForm::Operator(opno) => {
1101 let op = builtin::operator_by_oid(opno).ok_or_else(|| {
1102 Error::internal(format!("cache lookup failed for operator {opno}"))
1103 })?;
1104 self.open();
1105 if let [left, right] = f.args.as_slice() {
1106 self.paren(left, true, e)?;
1107 self.buf.push_str(&format!(" {} ", op.name));
1108 self.paren(right, true, e)?;
1109 } else {
1110 self.buf.push_str(&format!("{} ", op.name));
1111 self.paren(&f.args[0], true, e)?;
1112 }
1113 self.close();
1114 }
1115 FuncForm::ImplicitCast if !implicit => self.paren(&f.args[0], false, e)?,
1116 FuncForm::ExplicitCast | FuncForm::ImplicitCast => {
1117 self.coercion(&f.args[0], e.ty, e.typmod, e)?;
1118 }
1119 FuncForm::SqlSyntax => {
1120 if !self.sql_syntax(e, f)? {
1121 self.call(f)?;
1122 }
1123 }
1124 FuncForm::Call => self.call(f)?,
1125 }
1126 Ok(())
1127 }
1128
1129 fn row_compare(&mut self, rc: &RowCompare) -> Result<()> {
1131 let mut pairs = Vec::with_capacity(rc.ops.len());
1132 let mut name = "";
1133 for op in &rc.ops {
1134 let ExprKind::Func(Func { form: FuncForm::Operator(opno), args, .. }) = &op.kind else {
1135 return Err(Error::internal("a row comparison that is not an operator"));
1136 };
1137 let [left, right] = args.as_slice() else {
1138 return Err(Error::internal("a row comparison operator needs two arguments"));
1139 };
1140 if name.is_empty() {
1141 name = builtin::operator_by_oid(*opno).map(|op| op.name).ok_or_else(|| {
1142 Error::internal(format!("cache lookup failed for operator {opno}"))
1143 })?;
1144 }
1145 pairs.push((left, right));
1146 }
1147 self.buf.push_str("(ROW(");
1148 for (i, (left, _)) in pairs.iter().enumerate() {
1149 if i > 0 {
1150 self.buf.push_str(", ");
1151 }
1152 self.expr(left, true)?;
1153 }
1154 self.buf.push_str(&format!(") {name} ROW("));
1155 for (i, (_, right)) in pairs.iter().enumerate() {
1156 if i > 0 {
1157 self.buf.push_str(", ");
1158 }
1159 self.expr(right, true)?;
1160 }
1161 self.buf.push_str("))");
1162 Ok(())
1163 }
1164
1165 fn call(&mut self, f: &Func) -> Result<()> {
1167 let proc = builtin::proc_by_oid(f.oid).ok_or_else(|| {
1168 Error::internal(format!("cache lookup failed for function {}", f.oid))
1169 })?;
1170 if reg::function_visible(proc, self.session) {
1172 self.buf.push_str("e_identifier(proc.name));
1173 } else {
1174 let schema = reg::namespace_name(proc.namespace, self.session);
1175 self.buf.push_str(®::qualified(schema, proc.name));
1176 }
1177 self.buf.push('(');
1178 let variadic = f.variadic && proc.variadic != 0;
1179 for (i, arg) in f.args.iter().enumerate() {
1180 if i > 0 {
1181 self.buf.push_str(", ");
1182 }
1183 if variadic && i + 1 == f.args.len() {
1184 self.buf.push_str("VARIADIC ");
1185 }
1186 self.expr(arg, true)?;
1187 }
1188 self.buf.push(')');
1189 Ok(())
1190 }
1191
1192 fn case(&mut self, case: &Case) -> Result<()> {
1194 self.keyword("CASE", 0, INDENT_VAR, 0);
1195 if let Some(arg) = &case.arg {
1196 self.buf.push(' ');
1197 self.expr(arg, true)?;
1198 }
1199 for (when, result) in &case.whens {
1200 let mut shown = when;
1201 if case.arg.is_some()
1202 && let ExprKind::Func(Func { form: FuncForm::Operator(_), args, .. }) = &when.kind
1203 && let [left, right] = args.as_slice()
1204 && matches!(left.strip_implicit().kind, ExprKind::CaseTest)
1205 {
1206 shown = right;
1207 }
1208 self.space();
1209 self.keyword("WHEN ", 0, 0, 0);
1210 self.expr(shown, false)?;
1211 self.buf.push_str(" THEN ");
1212 self.expr(result, true)?;
1213 }
1214 self.space();
1215 self.keyword("ELSE ", 0, 0, 0);
1216 self.expr(&case.default, true)?;
1217 self.space();
1218 self.keyword("END", -INDENT_VAR, 0, 0);
1219 Ok(())
1220 }
1221}
1222
1223fn system_column(attnum: i16) -> Option<&'static str> {
1225 Some(match attnum {
1226 -1 => "ctid",
1227 -2 => "xmin",
1228 -3 => "cmin",
1229 -4 => "xmax",
1230 -5 => "cmax",
1231 -6 => "tableoid",
1232 _ => return None,
1233 })
1234}
1235
1236fn sql_value(value: SqlValue) -> String {
1238 let with = |name: &str, precision: Option<i32>| match precision {
1239 Some(p) => format!("{name}({p})"),
1240 None => name.to_string(),
1241 };
1242 match value {
1243 SqlValue::CurrentDate => "CURRENT_DATE".to_string(),
1244 SqlValue::CurrentTime(p) => with("CURRENT_TIME", p),
1245 SqlValue::CurrentTimestamp(p) => with("CURRENT_TIMESTAMP", p),
1246 SqlValue::LocalTime(p) => with("LOCALTIME", p),
1247 SqlValue::LocalTimestamp(p) => with("LOCALTIMESTAMP", p),
1248 SqlValue::CurrentRole => "CURRENT_ROLE".to_string(),
1249 SqlValue::CurrentUser => "CURRENT_USER".to_string(),
1250 SqlValue::User => "USER".to_string(),
1251 SqlValue::SessionUser => "SESSION_USER".to_string(),
1252 SqlValue::CurrentCatalog => "CURRENT_CATALOG".to_string(),
1253 SqlValue::CurrentSchema => "CURRENT_SCHEMA".to_string(),
1254 }
1255}
1256
1257fn simple_binary_op(e: &Expr) -> Option<u8> {
1259 match &e.kind {
1260 ExprKind::Func(Func { form: FuncForm::Operator(opno), args, .. }) if args.len() == 2 => {
1261 builtin::operator_by_oid(*opno).and_then(|op| match op.name.as_bytes() {
1262 [c] => Some(*c),
1263 _ => None,
1264 })
1265 }
1266 _ => None,
1267 }
1268}
1269
1270fn own_syntax(parent: &Expr) -> bool {
1272 match &parent.kind {
1273 ExprKind::Func(f) => f.form == FuncForm::Call,
1274 ExprKind::Bool(..)
1275 | ExprKind::Subscript(_)
1276 | ExprKind::Array { .. }
1277 | ExprKind::Row(..)
1278 | ExprKind::JoinRow(_)
1279 | ExprKind::Coalesce(_)
1280 | ExprKind::MinMax { .. }
1281 | ExprKind::NullIf { .. }
1282 | ExprKind::Agg(_)
1283 | ExprKind::Grouping(_)
1284 | ExprKind::PlaceHolder(_)
1285 | ExprKind::Window(_)
1286 | ExprKind::Xml(_)
1287 | ExprKind::Case(_) => true,
1288 _ => false,
1289 }
1290}
1291
1292fn bool_simple(op: BoolOp, parent: &Expr, paren: bool) -> bool {
1294 match &parent.kind {
1295 ExprKind::Bool(parent_op, _) => {
1296 paren
1297 && match op {
1298 BoolOp::Not | BoolOp::And => matches!(parent_op, BoolOp::And | BoolOp::Or),
1299 BoolOp::Or => *parent_op == BoolOp::Or,
1300 }
1301 }
1302 _ => own_syntax(parent),
1303 }
1304}
1305
1306fn json_format(format: JsonFormat, buf: &mut String) {
1308 match format.kind {
1309 JsonFormatKind::Default => return,
1310 JsonFormatKind::Json => buf.push_str(" FORMAT JSON"),
1311 JsonFormatKind::Jsonb => buf.push_str(" FORMAT JSONB"),
1312 }
1313 if format.encoding != JsonEncoding::Default {
1314 buf.push_str(" ENCODING ");
1315 buf.push_str(format.encoding.name());
1316 }
1317}
1318
1319fn is_simple(e: &Expr, parent: &Expr, paren: bool) -> bool {
1321 match &e.kind {
1322 ExprKind::Const(_)
1323 | ExprKind::Param(_)
1324 | ExprKind::Var(_)
1325 | ExprKind::SubColumn(_)
1326 | ExprKind::DomainValue => true,
1327 ExprKind::FieldSelect(..) => !matches!(parent.kind, ExprKind::FieldSelect(..)),
1329 ExprKind::Array { .. }
1330 | ExprKind::Row(..)
1331 | ExprKind::JoinRow(_)
1332 | ExprKind::Subscript(_)
1333 | ExprKind::Coalesce(_)
1334 | ExprKind::MinMax { .. }
1335 | ExprKind::SqlValue(_)
1336 | ExprKind::NullIf { .. }
1337 | ExprKind::Agg(_)
1338 | ExprKind::Grouping(_)
1339 | ExprKind::PlaceHolder(_)
1340 | ExprKind::Window(_)
1341 | ExprKind::JsonCtor(_)
1342 | ExprKind::JsonExpr(_)
1343 | ExprKind::JsonTable(_)
1344 | ExprKind::Xml(_)
1345 | ExprKind::CurrentOf(_)
1346 | ExprKind::Case(_) => true,
1347 ExprKind::Func(f) if !matches!(f.form, FuncForm::Operator(_)) => true,
1348 ExprKind::JsonValue(v) => is_simple(&v.raw, e, paren),
1349 ExprKind::Relabel(arg, _)
1350 | ExprKind::CoerceViaIo(arg, _)
1351 | ExprKind::ArrayCoerce { arg, .. }
1352 | ExprKind::CoerceToDomain(arg, _) => is_simple(arg, e, paren),
1353 ExprKind::Func(f) => {
1354 if paren
1355 && matches!(&parent.kind, ExprKind::Func(p) if matches!(p.form, FuncForm::Operator(_)))
1356 {
1357 let (Some(op), Some(parent_op)) = (simple_binary_op(e), simple_binary_op(parent))
1358 else {
1359 return false;
1360 };
1361 let (low, high) = (b"+-".contains(&op), b"*/%".contains(&op));
1362 let (parent_low, parent_high) =
1363 (b"+-".contains(&parent_op), b"*/%".contains(&parent_op));
1364 if !(low || high) || !(parent_low || parent_high) {
1365 return false;
1366 }
1367 if high && parent_low {
1368 return true;
1369 }
1370 if low && parent_high {
1371 return false;
1372 }
1373 let ExprKind::Func(p) = &parent.kind else { return false };
1375 return p.args.first().is_some_and(|first| ptr::eq(first, e)) && !f.args.is_empty();
1376 }
1377 own_syntax(parent)
1378 }
1379 ExprKind::SubLink(_)
1380 | ExprKind::NullTest(..)
1381 | ExprKind::BooleanTest(..)
1382 | ExprKind::IsJson(_) => own_syntax(parent),
1383 ExprKind::Distinct { not: false, .. } => own_syntax(parent),
1384 ExprKind::Distinct { not: true, .. } => bool_simple(BoolOp::Not, parent, paren),
1385 ExprKind::Bool(op, _) => bool_simple(*op, parent, paren),
1386 ExprKind::ScalarArrayOp { .. }
1387 | ExprKind::RowCompare(_)
1388 | ExprKind::CaseTest
1389 | ExprKind::FieldStore(_)
1390 | ExprKind::NamedArg(_) => false,
1391 }
1392}
1393
1394fn has_var(e: &Expr) -> bool {
1396 matches!(e.kind, ExprKind::Var(_)) || e.children().into_iter().any(has_var)
1397}
1398
1399impl Deparser<'_> {
1400 fn field_name(&self, arg: &Expr, field: usize) -> Result<String> {
1402 if arg.ty == oid::RECORD {
1403 let levels: Vec<&[rupg_analyze::Relation]> =
1404 self.namespaces.iter().rev().map(|n| &n.relations[..]).collect();
1405 if let Some(columns) = rupg_analyze::record_fields(arg, &levels)?
1406 && let Some(column) = columns.get(field)
1407 {
1408 return Ok(column.name.clone());
1409 }
1410 }
1411 row_field_name(arg.ty, field, self.session)
1412 }
1413}
1414
1415fn row_field_name(ty: u32, field: usize, session: &dyn Session) -> Result<String> {
1417 row_names(ty, session).and_then(|names| names.get(field).cloned()).ok_or_else(|| {
1418 Error::internal(format!("could not identify column {} in a record", field + 1))
1419 })
1420}
1421
1422pub(crate) fn row_names(ty: u32, session: &dyn Session) -> Option<Vec<String>> {
1424 let mut ty = ty;
1425 let relid = loop {
1426 match builtin::type_by_oid(ty) {
1427 Some(row) if row.kind == b'd' && row.base != 0 => ty = row.base,
1428 Some(row) => break row.relid,
1429 None => {
1430 break session
1431 .catalog()
1432 .and_then(|c| c.relations().find(|r| r.row_type == ty))
1433 .map_or(0, |r| r.oid);
1434 }
1435 }
1436 };
1437 if let Some(rel) = session.catalog().and_then(|c| c.relation(relid)) {
1438 return Some(rel.columns.iter().filter(|c| !c.dropped).map(|c| c.name.clone()).collect());
1439 }
1440 column_names(relid, session)
1441}
1442
1443fn column_names(relid: u32, session: &dyn Session) -> Option<Vec<String>> {
1445 if let Some(rel) = session.catalog().and_then(|c| c.relation(relid)) {
1446 return Some(rel.columns.iter().map(|c| c.name.clone()).collect());
1447 }
1448 builtin::class_by_oid(relid)?;
1449 let mut names = Vec::new();
1450 for attr in builtin::attributes(relid).iter().filter(|a| a.num > 0) {
1451 let at = usize::try_from(attr.num - 1).unwrap_or(0);
1452 if names.len() <= at {
1453 names.resize(at + 1, String::new());
1454 }
1455 names[at] = attr.name.to_string();
1456 }
1457 Some(names)
1458}
1459
1460fn deparse(
1462 text: &str,
1463 name: &str,
1464 columns: &[String],
1465 paren: bool,
1466 session: &dyn Session,
1467) -> Result<String> {
1468 if node::is_bound(text) {
1469 return partition_bound(text, session);
1470 }
1471 let exprs = stored(text)?;
1472 let mut d = Deparser::for_columns(session, name, columns.to_vec(), paren);
1473 d.list(&exprs, false)?;
1474 Ok(d.buf)
1475}
1476
1477fn bound_constant(d: &mut Deparser<'_>, e: &Expr) -> Result<()> {
1479 match &e.kind {
1480 ExprKind::Const(value) => d.constant(e, value, -1),
1481 _ => Err(Error::internal("a partition bound that is not a constant")),
1482 }
1483}
1484
1485fn partition_bound(text: &str, session: &dyn Session) -> Result<String> {
1487 let mut d = Deparser::for_columns(session, "", Vec::new(), false);
1488 match node::read_bound(text)? {
1489 StoredBound::Default => d.buf.push_str("DEFAULT"),
1490 StoredBound::Hash { modulus, remainder } => {
1491 d.buf.push_str(&format!("FOR VALUES WITH (modulus {modulus}, remainder {remainder})"));
1492 }
1493 StoredBound::List(values) => {
1494 d.buf.push_str("FOR VALUES IN (");
1495 for (i, value) in values.iter().enumerate() {
1496 if i > 0 {
1497 d.buf.push_str(", ");
1498 }
1499 match value {
1500 Some(e) => bound_constant(&mut d, e)?,
1501 None => d.buf.push_str("NULL"),
1502 }
1503 }
1504 d.buf.push(')');
1505 }
1506 StoredBound::Range { lower, upper } => {
1507 d.buf.push_str("FOR VALUES FROM ");
1508 range_datums(&mut d, &lower)?;
1509 d.buf.push_str(" TO ");
1510 range_datums(&mut d, &upper)?;
1511 }
1512 }
1513 Ok(d.buf)
1514}
1515
1516fn range_datums(d: &mut Deparser<'_>, datums: &[StoredDatum]) -> Result<()> {
1518 d.buf.push('(');
1519 for (i, datum) in datums.iter().enumerate() {
1520 if i > 0 {
1521 d.buf.push_str(", ");
1522 }
1523 match datum {
1524 StoredDatum::MinValue => d.buf.push_str("MINVALUE"),
1525 StoredDatum::MaxValue => d.buf.push_str("MAXVALUE"),
1526 StoredDatum::Value(e) => bound_constant(d, e)?,
1527 }
1528 }
1529 d.buf.push(')');
1530 Ok(())
1531}
1532
1533pub fn deparse_constant(text: &str, session: &dyn Session) -> Result<String> {
1539 let e = node::read(text)?;
1540 let mut d = Deparser::for_columns(session, "", Vec::new(), false);
1541 bound_constant(&mut d, &e)?;
1542 Ok(d.buf)
1543}
1544
1545pub fn deparse_expression(text: &str, session: &dyn Session) -> Result<String> {
1547 deparse(text, "", &[], false, session)
1548}
1549
1550pub fn deparse_relation_expression(
1556 text: &str,
1557 relid: u32,
1558 session: &dyn Session,
1559) -> Result<String> {
1560 if relid == 0 {
1561 return deparse_expression(text, session);
1562 }
1563 let columns = column_names(relid, session)
1564 .ok_or_else(|| Error::internal(format!("cache lookup failed for relation {relid}")))?;
1565 let name = reg::class_name(relid, session).map_or("", |(name, _)| name);
1566 deparse(text, name, &columns, false, session)
1567}
1568
1569fn stored(text: &str) -> Result<Vec<Expr>> {
1571 if text.trim_start().starts_with('(') {
1572 node::read_list(text)
1573 } else {
1574 Ok(vec![node::read(text)?])
1575 }
1576}
1577
1578fn relation_name(relid: u32, session: &dyn Session) -> Result<String> {
1580 let (name, namespace) = reg::class_name(relid, session)
1581 .ok_or_else(|| Error::internal(format!("cache lookup failed for relation {relid}")))?;
1582 if reg::class_visible(relid, session) == Some(true) {
1583 Ok(quote_identifier(name))
1584 } else {
1585 Ok(reg::qualified(reg::namespace_name(namespace, session), name))
1586 }
1587}
1588
1589fn qualified_relation_name(relid: u32, session: &dyn Session) -> Result<String> {
1591 let (name, namespace) = reg::class_name(relid, session)
1592 .ok_or_else(|| Error::internal(format!("cache lookup failed for relation {relid}")))?;
1593 Ok(reg::qualified(reg::namespace_name(namespace, session), name))
1594}
1595
1596fn pretty_arg(args: &[Value], index: usize) -> Result<bool> {
1598 match args.get(index) {
1599 Some(value) => value.as_bool().ok_or_else(bad_value),
1600 None => Ok(false),
1601 }
1602}
1603
1604fn get_expr(call: &Call<'_>, args: &[Value]) -> Result<Value> {
1606 let text = args.first().and_then(Value::as_str).ok_or_else(bad_value)?;
1607 let relid = args.get(1).and_then(Value::as_oid).ok_or_else(bad_value)?;
1608 let paren = pretty_arg(args, 2)?;
1609 let session = call.session;
1610 let columns = if relid == 0 {
1611 if stored(text)?.iter().any(has_var) {
1612 return Err(Error::new(
1613 SqlState::INVALID_PARAMETER_VALUE,
1614 "expression contains variables",
1615 ));
1616 }
1617 Vec::new()
1618 } else {
1619 match column_names(relid, session) {
1620 Some(columns) => columns,
1621 None => return Ok(Value::Null),
1622 }
1623 };
1624 let name = reg::class_name(relid, session).map_or("", |(name, _)| name);
1625 Ok(Value::text(deparse(text, name, &columns, paren, session)?))
1626}
1627
1628fn system_object(what: &str) -> Error {
1630 not_yet(format!("{what} of a system catalog"))
1631}
1632
1633fn column_list(rel: &Relation, keys: &[i16]) -> Result<String> {
1635 let names: Result<Vec<String>> = keys
1636 .iter()
1637 .map(|&k| {
1638 rel.column(k).map(|c| quote_identifier(&c.name)).ok_or_else(|| {
1639 Error::internal(format!(
1640 "cache lookup failed for attribute {k} of relation {}",
1641 rel.oid
1642 ))
1643 })
1644 })
1645 .collect();
1646 Ok(names?.join(", "))
1647}
1648
1649fn action(code: char) -> Option<&'static str> {
1651 match code {
1652 'r' => Some("RESTRICT"),
1653 'c' => Some("CASCADE"),
1654 'n' => Some("SET NULL"),
1655 'd' => Some("SET DEFAULT"),
1656 _ => None,
1657 }
1658}
1659
1660fn constraint_def(con: &Constraint, paren: bool, session: &dyn Session) -> Result<String> {
1662 let catalog = session.catalog().ok_or_else(|| Error::internal("no catalog"))?;
1663 let relation = || {
1664 catalog.relation(con.relation).ok_or_else(|| {
1665 Error::internal(format!("cache lookup failed for relation {}", con.relation))
1666 })
1667 };
1668 let mut out = String::new();
1669 match con.kind {
1670 ConKind::Foreign => {
1671 let fk = con
1672 .foreign
1673 .as_ref()
1674 .ok_or_else(|| Error::internal("a foreign key without its facts"))?;
1675 let target = catalog.relation(fk.table).ok_or_else(|| {
1676 Error::internal(format!("cache lookup failed for relation {}", fk.table))
1677 })?;
1678 out.push_str(&format!(
1679 "FOREIGN KEY ({}) REFERENCES {}({})",
1680 column_list(relation()?, &con.keys)?,
1681 relation_name(fk.table, session)?,
1682 column_list(target, &fk.keys)?
1683 ));
1684 out.push_str(match fk.match_type {
1685 'f' => " MATCH FULL",
1686 'p' => " MATCH PARTIAL",
1687 _ => "",
1688 });
1689 if let Some(update) = action(fk.update) {
1690 out.push_str(&format!(" ON UPDATE {update}"));
1691 }
1692 if let Some(delete) = action(fk.delete) {
1693 out.push_str(&format!(" ON DELETE {delete}"));
1694 }
1695 }
1696 ConKind::Primary | ConKind::Unique => {
1697 let index = catalog.relation(con.index).and_then(|r| r.index.as_ref());
1698 let index = index.ok_or_else(|| {
1699 Error::internal(format!("cache lookup failed for index {}", con.index))
1700 })?;
1701 out.push_str(if con.kind == ConKind::Primary { "PRIMARY KEY " } else { "UNIQUE " });
1702 if con.kind == ConKind::Unique && index.nulls_not_distinct {
1703 out.push_str("NULLS NOT DISTINCT ");
1704 }
1705 let rel = relation()?;
1706 out.push_str(&format!("({})", column_list(rel, &con.keys)?));
1707 if index.keys.len() > con.keys.len() {
1708 out.push_str(&format!(
1709 " INCLUDE ({})",
1710 column_list(rel, &index.keys[con.keys.len()..])?
1711 ));
1712 }
1713 }
1714 ConKind::Check => {
1715 let text = con
1716 .expr
1717 .as_deref()
1718 .ok_or_else(|| Error::internal("a check constraint without its expression"))?;
1719 let (name, columns): (&str, Vec<String>) = if con.domain == 0 {
1721 let rel = relation()?;
1722 (&rel.name, rel.columns.iter().map(|c| c.name.clone()).collect())
1723 } else {
1724 ("", Vec::new())
1725 };
1726 out.push_str(&format!("CHECK ({})", deparse(text, name, &columns, paren, session)?));
1727 if con.no_inherit {
1728 out.push_str(" NO INHERIT");
1729 }
1730 }
1731 ConKind::NotNull if con.domain != 0 => out.push_str("NOT NULL"),
1733 ConKind::Trigger => out.push_str("TRIGGER"),
1734 ConKind::NotNull => {
1735 out.push_str(&format!("NOT NULL {}", column_list(relation()?, &con.keys[..1])?));
1736 if con.no_inherit {
1737 out.push_str(" NO INHERIT");
1738 }
1739 }
1740 }
1741 if con.deferrable {
1742 out.push_str(" DEFERRABLE");
1743 }
1744 if con.deferred {
1745 out.push_str(" INITIALLY DEFERRED");
1746 }
1747 if !con.enforced {
1749 out.push_str(" NOT ENFORCED");
1750 } else if !con.validated {
1751 out.push_str(" NOT VALID");
1752 }
1753 Ok(out)
1754}
1755
1756fn get_constraintdef(call: &Call<'_>, args: &[Value]) -> Result<Value> {
1758 let oid = args.first().and_then(Value::as_oid).ok_or_else(bad_value)?;
1759 let paren = pretty_arg(args, 1)?;
1760 match call.session.catalog().and_then(|c| c.constraint(oid)) {
1761 Some(con) => Ok(Value::text(constraint_def(con, paren, call.session)?)),
1762 None if oid < FIRST_USER_OID => {
1763 Ok(builtin_constraint_def(oid)?.map_or(Value::Null, Value::text))
1764 }
1765 None => Ok(Value::Null),
1766 }
1767}
1768
1769fn builtin_position(catalog: &str, oid: u32) -> Option<(&'static rupg_pgcatalog::Catalog, usize)> {
1771 let cat = rupg_pgcatalog::catalog(catalog)?;
1772 let column = if catalog == "pg_index" { "indexrelid" } else { "oid" };
1773 match &cat.rows.get(cat.column(column)?)?.values {
1774 rupg_pgcatalog::Values::Oid(v) => Some((cat, v.iter().position(|&o| o == oid)?)),
1775 _ => None,
1776 }
1777}
1778
1779fn builtin_field(cat: &rupg_pgcatalog::Catalog, row: usize, column: &str) -> Result<String> {
1781 cat.column(column)
1782 .and_then(|at| cat.rows.get(at)?.text(row))
1783 .ok_or_else(|| Error::internal(format!("no column {column} in {}", cat.name)))
1784}
1785
1786fn builtin_columns(relid: u32, keys: &[i16]) -> Result<String> {
1788 let names: Result<Vec<String>> = keys
1789 .iter()
1790 .map(|&k| {
1791 builtin::attributes(relid)
1792 .iter()
1793 .find(|a| a.num == k)
1794 .map(|a| quote_identifier(a.name))
1795 .ok_or_else(|| {
1796 Error::internal(format!(
1797 "cache lookup failed for attribute {k} of relation {relid}"
1798 ))
1799 })
1800 })
1801 .collect();
1802 Ok(names?.join(", "))
1803}
1804
1805fn int2_list(text: &str) -> Vec<i16> {
1807 text.trim_matches(['{', '}']).split([' ', ',']).filter_map(|n| n.parse().ok()).collect()
1808}
1809
1810fn builtin_constraint_def(oid: u32) -> Result<Option<String>> {
1812 let Some((cat, row)) = builtin_position("pg_constraint", oid) else { return Ok(None) };
1813 let relid: u32 = builtin_field(cat, row, "conrelid")?.parse().map_err(|_| bad_value())?;
1814 let keys = int2_list(&builtin_field(cat, row, "conkey")?);
1815 let columns = builtin_columns(relid, &keys)?;
1816 Ok(Some(match builtin_field(cat, row, "contype")?.as_str() {
1817 "p" => format!("PRIMARY KEY ({columns})"),
1818 "u" => format!("UNIQUE ({columns})"),
1819 "n" => format!("NOT NULL {columns}"),
1820 _ => return Err(system_object("pg_get_constraintdef")),
1821 }))
1822}
1823
1824fn builtin_index_def(
1826 oid: u32,
1827 colno: i32,
1828 paren: bool,
1829 session: &dyn Session,
1830) -> Result<Option<String>> {
1831 let Some((cat, row)) = builtin_position("pg_index", oid) else { return Ok(None) };
1832 let relid: u32 = builtin_field(cat, row, "indrelid")?.parse().map_err(|_| bad_value())?;
1833 let keys = int2_list(&builtin_field(cat, row, "indkey")?);
1834 if colno != 0 {
1835 let key = usize::try_from(colno - 1).ok().and_then(|at| keys.get(at));
1836 return Ok(Some(match key {
1837 Some(&key) => builtin_columns(relid, &[key])?,
1838 None => String::new(),
1839 }));
1840 }
1841 let index = builtin::class_by_oid(oid)
1842 .ok_or_else(|| Error::internal(format!("cache lookup failed for relation {oid}")))?;
1843 let table = if paren {
1844 relation_name(relid, session)?
1845 } else {
1846 qualified_relation_name(relid, session)?
1847 };
1848 let unique = builtin_field(cat, row, "indisunique")? == "t";
1849 Ok(Some(format!(
1850 "CREATE {}INDEX {} ON {table} USING btree ({})",
1851 if unique { "UNIQUE " } else { "" },
1852 quote_identifier(index.name),
1853 builtin_columns(relid, &keys)?
1854 )))
1855}
1856
1857fn expr_collation(e: &Expr, rel: &Relation) -> u32 {
1859 fn walk(e: &Expr, rel: &Relation, found: &mut Vec<u32>) {
1860 if let ExprKind::Var(var) = &e.kind
1861 && let Some(column) = rel.column(var.attnum)
1862 && column.collation != 0
1863 {
1864 found.push(column.collation);
1865 }
1866 for child in e.children() {
1867 walk(child, rel, found);
1868 }
1869 }
1870 if !rupg_analyze::types::collatable(e.ty) {
1871 return 0;
1872 }
1873 let mut found = Vec::new();
1874 walk(e, rel, &mut found);
1875 match found.first() {
1876 Some(&first) if found.iter().all(|&c| c == first) => first,
1877 Some(_) => 0,
1878 None => DEFAULT_COLLATION,
1879 }
1880}
1881
1882const DEFAULT_COLLATION: u32 = 100;
1884
1885fn collation_name(collation: u32, session: &dyn Session) -> Result<String> {
1887 let row = builtin::named_by_oid(Named::Collation, collation)
1888 .ok_or_else(|| Error::internal(format!("cache lookup failed for collation {collation}")))?;
1889 if reg::visible(Named::Collation, row, session) {
1890 Ok(quote_identifier(row.name))
1891 } else {
1892 Ok(reg::qualified(reg::namespace_name(row.namespace, session), row.name))
1893 }
1894}
1895
1896fn opclass_name(class: u32, ty: u32, session: &dyn Session) -> Result<String> {
1898 let (name, namespace, method) = reg::method_row(Named::Opclass, class, session)
1899 .ok_or_else(|| Error::internal(format!("cache lookup failed for opclass {class}")))?;
1900 if default_opclass(ty, method).is_some_and(|d| d.oid == class) {
1901 return Ok(String::new());
1902 }
1903 if reg::method_visible(Named::Opclass, class, session) {
1904 Ok(format!(" {}", quote_identifier(&name)))
1905 } else {
1906 Ok(format!(" {}", reg::qualified(reg::namespace_name(namespace, session), &name)))
1907 }
1908}
1909
1910fn index_def(
1912 index: &Relation,
1913 info: &IndexInfo,
1914 colno: i32,
1915 paren: bool,
1916 session: &dyn Session,
1917) -> Result<String> {
1918 let catalog = session.catalog().ok_or_else(|| Error::internal("no catalog"))?;
1919 let rel = catalog.relation(info.table).ok_or_else(|| {
1920 Error::internal(format!("cache lookup failed for relation {}", info.table))
1921 })?;
1922 let columns: Vec<String> = rel.columns.iter().map(|c| c.name.clone()).collect();
1923 let exprs = match &info.exprs {
1924 Some(text) => node::read_list(text)?,
1925 None => Vec::new(),
1926 };
1927 let mut exprs = exprs.iter();
1928 let whole = colno == 0;
1929 let mut out = String::new();
1930 if whole {
1931 let method = match info.method {
1932 BTREE => "btree",
1933 HASH => "hash",
1934 other => {
1935 return Err(Error::internal(format!(
1936 "cache lookup failed for access method {other}"
1937 )));
1938 }
1939 };
1940 let table = if paren {
1941 relation_name(info.table, session)?
1942 } else {
1943 qualified_relation_name(info.table, session)?
1944 };
1945 out.push_str(&format!(
1946 "CREATE {}INDEX {} ON {}{table} USING {method} (",
1947 if info.unique { "UNIQUE " } else { "" },
1948 quote_identifier(&index.name),
1949 if index.kind == RelKind::PartitionedIndex { "ONLY " } else { "" }
1950 ));
1951 }
1952 let key_count = usize::try_from(info.key_count).unwrap_or(0);
1953 for (keyno, &attnum) in info.keys.iter().enumerate() {
1954 let shown = whole || usize::try_from(colno).is_ok_and(|c| c == keyno + 1);
1955 if whole {
1956 if keyno == key_count {
1957 out.push_str(") INCLUDE (");
1958 } else if keyno > 0 {
1959 out.push_str(", ");
1960 }
1961 }
1962 let (ty, collation) = if attnum != 0 {
1963 let column = rel.column(attnum).ok_or_else(|| {
1964 Error::internal(format!("cache lookup failed for attribute {attnum}"))
1965 })?;
1966 if shown {
1967 out.push_str("e_identifier(&column.name));
1968 }
1969 (column.ty, column.collation)
1970 } else {
1971 let e =
1972 exprs.next().ok_or_else(|| Error::internal("too few entries in indexprs list"))?;
1973 if shown {
1974 let mut d = Deparser::for_columns(session, &rel.name, columns.clone(), paren);
1975 d.expr(e, false)?;
1976 if looks_like_function(e) {
1977 out.push_str(&d.buf);
1978 } else {
1979 out.push_str(&format!("({})", d.buf));
1980 }
1981 }
1982 (e.ty, expr_collation(e, rel))
1983 };
1984 if whole && keyno < key_count {
1985 let collation_used = info.collations.get(keyno).copied().unwrap_or(0);
1986 if collation_used != 0 && collation_used != collation {
1987 out.push_str(&format!(" COLLATE {}", collation_name(collation_used, session)?));
1988 }
1989 if let Some(&class) = info.classes.get(keyno) {
1990 out.push_str(&opclass_name(class, ty, session)?);
1991 }
1992 if info.method == BTREE {
1993 let option = info.options.get(keyno).copied().unwrap_or(0);
1994 if option & DESC != 0 {
1995 out.push_str(" DESC");
1996 if option & NULLS_FIRST == 0 {
1997 out.push_str(" NULLS LAST");
1998 }
1999 } else if option & NULLS_FIRST != 0 {
2000 out.push_str(" NULLS FIRST");
2001 }
2002 }
2003 }
2004 }
2005 if whole {
2006 out.push(')');
2007 if info.nulls_not_distinct {
2008 out.push_str(" NULLS NOT DISTINCT");
2009 }
2010 if let Some(text) = &info.predicate {
2011 out.push_str(&format!(
2012 " WHERE {}",
2013 deparse(text, &rel.name, &columns, paren, session)?
2014 ));
2015 }
2016 }
2017 Ok(out)
2018}
2019
2020fn looks_like_function(e: &Expr) -> bool {
2022 match &e.kind {
2023 ExprKind::Func(f) => matches!(f.form, FuncForm::Call | FuncForm::SqlSyntax),
2024 ExprKind::NullIf { .. }
2025 | ExprKind::Coalesce(_)
2026 | ExprKind::MinMax { .. }
2027 | ExprKind::SqlValue(_)
2028 | ExprKind::Xml(_) => true,
2029 _ => false,
2030 }
2031}
2032
2033fn statisticsobj_def(
2035 oid: u32,
2036 columns_only: bool,
2037 session: &dyn Session,
2038) -> Result<Option<String>> {
2039 let catalog = session.catalog().ok_or_else(|| Error::internal("no catalog"))?;
2040 let Some(stat) = catalog.stat_ext(oid) else { return Ok(None) };
2041 let rel = catalog.relation(stat.relation).ok_or_else(|| {
2042 Error::internal(format!("cache lookup failed for relation {}", stat.relation))
2043 })?;
2044 let exprs = match &stat.exprs {
2045 Some(text) => node::read_list(text)?,
2046 None => Vec::new(),
2047 };
2048 let mut out = String::new();
2049 if !columns_only {
2050 let schema = if reg::is_own_temp(stat.namespace, session) {
2051 Some("pg_temp")
2052 } else {
2053 reg::namespace_name(stat.namespace, session)
2054 };
2055 out.push_str(&format!("CREATE STATISTICS {}", reg::qualified(schema, &stat.name)));
2056 let kinds: Vec<&str> = [(b'd', "ndistinct"), (b'f', "dependencies"), (b'm', "mcv")]
2057 .into_iter()
2058 .filter(|(code, _)| stat.kinds.contains(code))
2059 .map(|(_, word)| word)
2060 .collect();
2061 if kinds.len() < 3 && stat.keys.len() + exprs.len() > 1 {
2062 out.push_str(&format!(" ({})", kinds.join(", ")));
2063 }
2064 out.push_str(" ON ");
2065 }
2066 let mut items = Vec::new();
2067 for &attnum in &stat.keys {
2068 let column = rel.column(attnum).ok_or_else(|| {
2069 Error::internal(format!(
2070 "cache lookup failed for attribute {attnum} of relation {}",
2071 stat.relation
2072 ))
2073 })?;
2074 items.push(quote_identifier(&column.name));
2075 }
2076 let columns: Vec<String> = rel.columns.iter().map(|c| c.name.clone()).collect();
2077 for e in &exprs {
2078 let mut d = Deparser::for_columns(session, &rel.name, columns.clone(), true);
2079 d.flat = true;
2080 d.expr(e, false)?;
2081 items.push(if looks_like_function(e) { d.buf } else { format!("({})", d.buf) });
2082 }
2083 out.push_str(&items.join(", "));
2084 if !columns_only {
2085 out.push_str(&format!(" FROM {}", relation_name(stat.relation, session)?));
2086 }
2087 Ok(Some(out))
2088}
2089
2090fn get_statisticsobjdef(call: &Call<'_>, args: &[Value]) -> Result<Value> {
2092 let oid = args.first().and_then(Value::as_oid).ok_or_else(bad_value)?;
2093 Ok(statisticsobj_def(oid, false, call.session)?.map_or(Value::Null, Value::text))
2094}
2095
2096fn get_statisticsobjdef_columns(call: &Call<'_>, args: &[Value]) -> Result<Value> {
2098 let oid = args.first().and_then(Value::as_oid).ok_or_else(bad_value)?;
2099 Ok(statisticsobj_def(oid, true, call.session)?.map_or(Value::Null, Value::text))
2100}
2101
2102fn get_statisticsobjdef_expressions(call: &Call<'_>, args: &[Value]) -> Result<Value> {
2104 let oid = args.first().and_then(Value::as_oid).ok_or_else(bad_value)?;
2105 let catalog = call.session.catalog().ok_or_else(|| Error::internal("no catalog"))?;
2106 let Some((stat, Some(text))) = catalog.stat_ext(oid).map(|s| (s, s.exprs.as_ref())) else {
2107 return Ok(Value::Null);
2108 };
2109 let rel = catalog.relation(stat.relation).ok_or_else(|| {
2110 Error::internal(format!("cache lookup failed for relation {}", stat.relation))
2111 })?;
2112 let columns: Vec<String> = rel.columns.iter().map(|c| c.name.clone()).collect();
2113 let mut items = Vec::new();
2114 for e in node::read_list(text)? {
2115 let mut d = Deparser::for_columns(call.session, &rel.name, columns.clone(), false);
2116 d.expr(&e, false)?;
2117 items.push(Some(Value::text(d.buf)));
2118 }
2119 Ok(Value::Array(Box::new(rupg_types::Array::one(items))))
2120}
2121
2122fn partkey_def(
2124 rel: &Relation,
2125 key: &PartitionKey,
2126 attrs_only: bool,
2127 session: &dyn Session,
2128) -> Result<String> {
2129 let columns: Vec<String> = rel.columns.iter().map(|c| c.name.clone()).collect();
2130 let exprs = match &key.exprs {
2131 Some(text) => node::read_list(text)?,
2132 None => Vec::new(),
2133 };
2134 let mut exprs = exprs.iter();
2135 let strategy = match key.strategy {
2136 PartStrategy::Hash => "HASH",
2137 PartStrategy::List => "LIST",
2138 PartStrategy::Range => "RANGE",
2139 };
2140 let mut out = if attrs_only { String::new() } else { format!("{strategy} (") };
2141 for (keyno, &attnum) in key.attrs.iter().enumerate() {
2142 if keyno > 0 {
2143 out.push_str(", ");
2144 }
2145 let (ty, collation) = if attnum != 0 {
2146 let column = rel.column(attnum).ok_or_else(|| {
2147 Error::internal(format!("cache lookup failed for attribute {attnum}"))
2148 })?;
2149 out.push_str("e_identifier(&column.name));
2150 (column.ty, column.collation)
2151 } else {
2152 let e =
2153 exprs.next().ok_or_else(|| Error::internal("too few entries in partexprs list"))?;
2154 let mut d = Deparser::for_columns(session, &rel.name, columns.clone(), attrs_only);
2155 d.expr(e, false)?;
2156 if looks_like_function(e) {
2157 out.push_str(&d.buf);
2158 } else {
2159 out.push_str(&format!("({})", d.buf));
2160 }
2161 (e.ty, expr_collation(e, rel))
2162 };
2163 if attrs_only {
2164 continue;
2165 }
2166 let collation_used = key.collations.get(keyno).copied().unwrap_or(0);
2167 if collation_used != 0 && collation_used != collation {
2168 out.push_str(&format!(" COLLATE {}", collation_name(collation_used, session)?));
2169 }
2170 if let Some(&class) = key.classes.get(keyno) {
2171 out.push_str(&opclass_name(class, ty, session)?);
2172 }
2173 }
2174 if !attrs_only {
2175 out.push(')');
2176 }
2177 Ok(out)
2178}
2179
2180pub fn partition_key_columns(rel: &Relation, session: &dyn Session) -> Result<String> {
2186 match &rel.partition_key {
2187 Some(key) => partkey_def(rel, key, true, session),
2188 None => Err(Error::internal(format!("relation {} is not partitioned", rel.oid))),
2189 }
2190}
2191
2192pub fn index_key_columns(index: u32, session: &dyn Session) -> Result<String> {
2198 let catalog = session.catalog().ok_or_else(|| Error::internal("no catalog"))?;
2199 let found = catalog.relation(index).and_then(|r| Some((r, r.index.as_ref()?)));
2200 let Some((rel, info)) = found else {
2201 return Err(Error::internal(format!("cache lookup failed for index {index}")));
2202 };
2203 let mut out = String::new();
2204 for colno in 1..=info.key_count {
2205 if colno > 1 {
2206 out.push_str(", ");
2207 }
2208 out.push_str(&index_def(rel, info, i32::from(colno), true, session)?);
2209 }
2210 Ok(out)
2211}
2212
2213fn get_partkeydef(call: &Call<'_>, args: &[Value]) -> Result<Value> {
2215 let oid = args.first().and_then(Value::as_oid).ok_or_else(bad_value)?;
2216 let found = call
2217 .session
2218 .catalog()
2219 .and_then(|c| c.relation(oid))
2220 .and_then(|r| Some((r, r.partition_key.as_ref()?)));
2221 match found {
2222 Some((rel, key)) => Ok(Value::text(partkey_def(rel, key, false, call.session)?)),
2223 None => Ok(Value::Null),
2224 }
2225}
2226
2227fn get_partition_constraintdef(call: &Call<'_>, args: &[Value]) -> Result<Value> {
2229 let oid = args.first().and_then(Value::as_oid).ok_or_else(bad_value)?;
2230 let Some(catalog) = call.session.catalog() else { return Ok(Value::Null) };
2231 let Some(rel) = catalog.relation(oid) else { return Ok(Value::Null) };
2232 let mut qual = partqual::partition_qual(catalog, rel)?;
2233 let expr = match qual.len() {
2234 0 => return Ok(Value::Null),
2235 1 => qual.remove(0),
2236 _ => Expr {
2237 kind: ExprKind::Bool(BoolOp::And, qual),
2238 ty: oid::BOOL,
2239 typmod: -1,
2240 location: None,
2241 },
2242 };
2243 let columns = rel.columns.iter().map(|c| c.name.clone()).collect();
2244 let mut d = Deparser::for_columns(call.session, &rel.name, columns, false);
2245 d.expr(&expr, false)?;
2246 Ok(Value::text(d.buf))
2247}
2248
2249fn get_indexdef(call: &Call<'_>, args: &[Value]) -> Result<Value> {
2251 let oid = args.first().and_then(Value::as_oid).ok_or_else(bad_value)?;
2252 let colno = match args.get(1) {
2253 Some(value) => {
2254 i32::try_from(value.as_i64().ok_or_else(bad_value)?).map_err(|_| bad_value())?
2255 }
2256 None => 0,
2257 };
2258 let paren = pretty_arg(args, 2)?;
2259 let found = call
2260 .session
2261 .catalog()
2262 .and_then(|c| c.relation(oid))
2263 .and_then(|r| Some((r, r.index.as_ref()?)));
2264 match found {
2265 Some((index, info)) => Ok(Value::text(index_def(index, info, colno, paren, call.session)?)),
2266 None if oid < FIRST_USER_OID => {
2267 Ok(builtin_index_def(oid, colno, paren, call.session)?.map_or(Value::Null, Value::text))
2268 }
2269 None => Ok(Value::Null),
2270 }
2271}
2272
2273pub(crate) fn by_src(src: &str) -> Option<Kernel> {
2275 Some(match src {
2276 "pg_get_expr" | "pg_get_expr_ext" => get_expr,
2277 "pg_get_constraintdef" | "pg_get_constraintdef_ext" => get_constraintdef,
2278 "pg_get_indexdef" | "pg_get_indexdef_ext" => get_indexdef,
2279 "pg_get_statisticsobjdef" => get_statisticsobjdef,
2280 "pg_get_statisticsobjdef_columns" => get_statisticsobjdef_columns,
2281 "pg_get_statisticsobjdef_expressions" => get_statisticsobjdef_expressions,
2282 "pg_get_function_arguments" => function::get_function_arguments,
2283 "pg_get_function_identity_arguments" => function::get_function_identity_arguments,
2284 "pg_get_function_result" => function::get_function_result,
2285 "pg_get_function_arg_default" => function::get_function_arg_default,
2286 "pg_get_function_sqlbody" => function::get_function_sqlbody,
2287 "pg_get_functiondef" => function::get_functiondef,
2288 "pg_get_viewdef" | "pg_get_viewdef_ext" | "pg_get_viewdef_wrap" => query::get_viewdef,
2289 "pg_get_viewdef_name" | "pg_get_viewdef_name_ext" => query::get_viewdef_name,
2290 "pg_get_ruledef" | "pg_get_ruledef_ext" => query::get_ruledef,
2291 "pg_get_triggerdef" | "pg_get_triggerdef_ext" => query::get_triggerdef,
2292 "pg_get_partkeydef" => get_partkeydef,
2293 "pg_get_partition_constraintdef" => get_partition_constraintdef,
2294 _ => return None,
2295 })
2296}