Skip to main content

rupg_func/
deparse.rs

1//! The deparse functions `pg_get_expr`, `pg_get_constraintdef`, `pg_get_indexdef`, `pg_get_viewdef`, `pg_get_ruledef`, `pg_get_triggerdef`, the `pg_get_function_*` functions, `pg_get_partkeydef` and `pg_get_partition_constraintdef`: a port of the parts of `ruleutils.c` that show the expressions, the queries and the definitions of the catalog.
2//!
3//! The functions read the stored form that `rupg_analyze::node` writes, or the query that the analyzer makes from the text of a view, and show it as PostgreSQL shows its node trees. The rules for parentheses, casts and constants follow `get_rule_expr`. The functions show the objects of the user and the system views. An index or a constraint of a system catalog gives an error that says that the engine cannot show it yet.
4
5mod 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
37/// `PRETTYINDENT_VAR`: the indent of the lines of a `CASE`.
38const INDENT_VAR: i32 = 4;
39/// `PRETTYINDENT_STD`.
40const INDENT_STD: i32 = 8;
41/// `PRETTYINDENT_LIMIT`: the indent after which each level adds less.
42const INDENT_LIMIT: i32 = 40;
43/// The access method `btree`, the one access method that has an order.
44const BTREE: u32 = 403;
45/// The access method `hash`.
46const HASH: u32 = 405;
47/// `INDOPTION_DESC`.
48const DESC: i16 = 1;
49/// `INDOPTION_NULLS_FIRST`.
50const NULLS_FIRST: i16 = 2;
51/// The first OID that a user object can have.
52const FIRST_USER_OID: u32 = 16384;
53
54/// The names of the relations of one query and of their columns, as `deparse_namespace` with its `deparse_columns`.
55#[derive(Clone, Debug, Default)]
56struct Namespace {
57    /// The name of each relation of the query, by its index, as `rtable_names`. The name is unique in the query and in the queries outside it.
58    names: Vec<String>,
59    /// The names of the columns of each relation, by the column number from 1.
60    columns: Vec<Vec<String>>,
61    /// For each relation, true when `FROM` shows the names of the columns after the name of the relation, as `printaliases`.
62    print_aliases: Vec<bool>,
63    /// The value and the name of each column of `USING` of a join with no alias that is not a column of one side. PostgreSQL shows such a column by its name, as a column of the join.
64    merged: Vec<(Expr, String)>,
65    /// The relations of the query, where a field of a value of type `record` finds its name.
66    relations: Vec<rupg_analyze::Relation>,
67    /// The joins of `FROM`, an outer join before the joins in it, as the `deparse_columns` of the entries of the joins.
68    joins: Vec<JoinNames>,
69    /// The joins, by their index in `joins`, that the deparse shows now. A column of a relation of such a join shows the name of its relation, not the name of the join.
70    inside: Vec<usize>,
71    /// The subqueries of `FROM`, by the index of the relation, whose `Subquery Scan` the plan does not keep. A node above reads the target list of the plan of the subquery.
72    removed: Vec<(usize, Rc<Removed>)>,
73    /// The name of the target table of an `INSERT` and the names of its columns, which a `Var` of [`TARGET`] reads, as the entry of the target table in the range table.
74    target: Option<(String, Vec<String>)>,
75    /// The names of `old` and `new` of `RETURNING` and the names of the columns of the table, which a `Var` of [`RETURNING_OLD`] or [`RETURNING_NEW`] reads.
76    returning: Option<(String, String, Vec<String>)>,
77}
78
79/// The target list of the plan of a subquery whose `Subquery Scan` the plan does not keep, as `trivial_subqueryscan` takes the scan out of the plan.
80#[derive(Debug)]
81struct Removed {
82    /// The names of the subquery and of the queries outside it, the query outside first.
83    namespaces: Vec<Namespace>,
84    /// The targets of the subquery.
85    targets: Vec<Expr>,
86}
87
88/// The names of a join and of its columns, as `deparse_columns` for an entry of a join.
89#[derive(Clone, Debug, Default)]
90struct JoinNames {
91    /// The name of the join with an alias, which no other relation of the query has, or `None` for a join with no alias.
92    name: Option<String>,
93    /// The indexes of the relations in the join.
94    relations: Vec<usize>,
95    /// `colnames`: the name of each column of the join.
96    columns: Vec<String>,
97    /// `usingNames`: the names of the columns of `USING`.
98    using: Vec<String>,
99    /// The value of each column of the join.
100    values: Vec<Expr>,
101    /// `printaliases`: `FROM` shows the names of the columns after the alias.
102    print_aliases: bool,
103}
104
105/// A deparse of an expression or of a query, as `deparse_context`. Each form of the functions sets `PRETTYFLAG_INDENT`, so a `CASE` always takes more than one line. `EXPLAIN` does not set it.
106struct Deparser<'a> {
107    session: &'a dyn Session,
108    /// The names of the queries that have the expression, the outermost query first. A `Var` finds its relation in the namespace of its level.
109    namespaces: Vec<Namespace>,
110    /// `PRETTYFLAG_PAREN`: add parentheses only where the syntax needs them.
111    paren: bool,
112    /// `indentLevel`.
113    indent: i32,
114    /// For `EXPLAIN`, `PRETTYFLAG_INDENT` is not set, as `deparse_expression` makes the context: a keyword does not start a new line.
115    pub(crate) flat: bool,
116    /// `wrapColumn`: the length of a line after which the target list and `FROM` start a new line for the next item, or -1 to keep the items on one line.
117    wrap: i32,
118    /// `varprefix`: show the name of the relation before the name of each column.
119    prefix: bool,
120    /// `colNamesVisible`: the names of the columns of the query show in its result.
121    names_visible: bool,
122    /// `inGroupBy`.
123    in_group_by: bool,
124    /// The name of each column of the result of the query, with the column of `FROM` that the column shows. A column in `ORDER BY` with the name of another column of the result shows the name of its relation, as `get_variable` does.
125    outputs: Vec<(String, Option<Var>)>,
126    /// `windowClause`: the windows of the query, which a window function call names by number.
127    windows: Vec<WindowClause>,
128    /// The expressions of the targets of the query, which the items of the windows name.
129    window_targets: Vec<Expr>,
130    /// For `EXPLAIN`, the subplans of the subqueries in the expressions. A subquery shows the name of its subplan, as `get_rule_expr` shows a `SubPlan`.
131    subplans: Vec<SubName>,
132    /// For `EXPLAIN`, the subplan whose columns the test of `ANY` or `ALL` reads now, as `(SubPlan any_1)`.
133    subplan: Option<String>,
134    /// For `EXPLAIN`, the name of each window of the query by its number, which a call of a window function shows after `OVER`.
135    window_names: Vec<Option<String>>,
136    /// For `EXPLAIN`, a column of a query outside the query always shows the name of its relation, as `get_parameter` shows the value of a parameter.
137    outer_prefix: bool,
138    /// For `EXPLAIN` of a condition of a scan, the relation of the scan. A column of another relation is the value of a parameter of a nested loop, which shows the name of its relation, as `get_parameter`.
139    pub(crate) own: Option<usize>,
140    /// For the body of a function in SQL, the name of the function and the name of each argument, as the `funcname` and `argnames` of the outermost `deparse_namespace`. A parameter shows the name of its argument.
141    function: Option<(String, Vec<String>)>,
142    buf: String,
143}
144
145/// The name of the subplan of a subquery in an expression, for `EXPLAIN`.
146#[derive(Clone, Debug)]
147struct SubName {
148    /// The subquery. The deparse only compares the address.
149    link: *const SubLink,
150    name: String,
151    /// The subplan is an `InitPlan`.
152    init: bool,
153    /// The subplan keeps its rows in a hash table.
154    hashed: bool,
155    /// The subplan is the `ANY` that runs in the place of `EXISTS`, which shows in its place.
156    alternative: bool,
157}
158
159impl<'a> Deparser<'a> {
160    /// A deparse of an expression of one relation, as `deparse_context_for`: a `Var` shows the name of its column with no name of the relation.
161    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    /// A deparse with no query.
184    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    /// `get_parameter` for a parameter `$n` of the extended protocol or of a function. In the body of a function in SQL, an argument with a name shows the name, after the name of the function when a query has relations. Else it shows `$n`.
209    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    /// `appendContextKeyword`: a new line with the indent and `plus` more spaces, then the keyword.
226    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    /// The space that `get_rule_expr` adds before a keyword when the keyword does not start a new line.
248    fn space(&mut self) {
249        if self.flat {
250            self.buf.push(' ');
251        }
252    }
253
254    /// `get_variable`: the column with the name of its relation when the query needs it, and the name of the column. A column of `ORDER BY` also shows the name of its relation when a column of the result has the same name and shows another value.
255    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        // A column of a `Subquery Scan` that the plan does not keep is the target of the plan of the subquery, as `get_variable` finds it for `OUTER_VAR`. A target that is not a column gets parentheses.
282        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(&quote_identifier(&refname));
309            self.buf.push('.');
310        }
311        self.buf.push_str(&quote_identifier(&attname));
312        Ok(attname)
313    }
314
315    /// `get_variable` for a whole-row `Var`: the name of the relation and `*`. At the top level of a target list the text has a cast to the row type.
316    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(&quote_identifier(&refname));
327            self.buf.push('.');
328        }
329        self.buf.push('*');
330        if top {
331            self.buf.push_str("::");
332            self.buf.push_str(&reg::type_text(e.ty, Some(e.typmod), self.session)?);
333        }
334        Ok(())
335    }
336
337    /// The `(` that a node writes around itself when the flags do not have `PRETTYFLAG_PAREN`.
338    fn open(&mut self) {
339        if !self.paren {
340            self.buf.push('(');
341        }
342    }
343
344    /// The `)` that goes with [`Deparser::open`].
345    fn close(&mut self) {
346        if !self.paren {
347            self.buf.push(')');
348        }
349    }
350
351    /// The `(` of `get_rule_expr_paren` for a node that is not simple, when the flags have `PRETTYFLAG_PAREN`.
352    fn open_pretty(&mut self) {
353        if self.paren {
354            self.buf.push('(');
355        }
356    }
357
358    /// The `)` that goes with [`Deparser::open_pretty`].
359    fn close_pretty(&mut self) {
360        if self.paren {
361            self.buf.push(')');
362        }
363    }
364
365    /// `get_rule_expr_paren`: the expression, in parentheses when `PRETTYFLAG_PAREN` is set and the expression is not simple in its parent.
366    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    /// The expressions of a list, with `, ` between them.
379    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    /// `get_rule_expr`. `implicit` is `showimplicit`: true to show the casts that the analyzer added.
390    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(&quote_identifier(&join));
394                self.buf.push('.');
395            }
396            self.buf.push_str(&quote_identifier(&column));
397            return Ok(());
398        }
399        if let Some(name) = self.merged_name(e) {
400            self.buf.push_str(&quote_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                // The argument needs parentheses unless it is a subscript or another field, also when it is a column.
412                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(&quote_identifier(&name));
424            }
425            ExprKind::Func(f) => self.func(e, f, implicit)?,
426            ExprKind::NamedArg(na) => {
427                self.buf.push_str(&quote_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            // An implicit cast to a domain shows its argument with no parentheses.
437            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            // `processIndirection` shows an assignment to a field as the target of `SET`, so a `FieldStore` does not come here.
443            ExprKind::FieldStore(_) => return Err(Error::internal("unexpected FieldStore")),
444            ExprKind::Subscript(sub) => {
445                // The array needs parentheses unless it is a column or a field, also when it is another subscript.
446                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                // An assignment shows its new value after the subscripts.
469                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(&quote_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                // A `NOT` over the test is a `BoolExpr` in PostgreSQL.
548                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            // A `PlaceHolderVar` above the node that computes it reads it from below, as `get_variable` shows a target of the node below that is not a column.
592            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    /// The case of `get_rule_expr` for an `XmlExpr`.
647    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(&quote_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(&quote_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                // The argument is a list, which is never simple.
723                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(&reg::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    /// The case of `get_rule_expr` for a `JsonExpr`, with `get_json_path_spec`, `get_json_returning` and `get_json_expr_options`.
741    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(&quote_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    /// `get_json_table`: a call of `JSON_TABLE` in `FROM`.
798    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(&quote_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    /// The path of a scan and its name.
838    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(&quote_identifier(&scan.name));
843        Ok(())
844    }
845
846    /// `get_json_table_columns`: the columns of a scan, then its `NESTED` parts.
847    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(&quote_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(&reg::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    /// `get_json_table_nested_columns`.
906    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    /// `get_json_behavior`.
930    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    /// `get_json_constructor` and `get_json_constructor_options`.
947    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    /// `get_json_agg_constructor`: the call of the aggregate or of the window function with the name and the options of the constructor.
970    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    /// `get_json_constructor_options`: `ABSENT ON NULL`, `NULL ON NULL`, `WITH UNIQUE KEYS` and `RETURNING`.
986    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    /// `get_const_expr`. `showtype` is -1 to show no type, 0 to show the type when the text alone does not give it, and 1 to always show it.
1014    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                // `simple_quote_literal` with `standard_conforming_strings` on doubles only the quote.
1043                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    /// A `RelabelType` or a `CoerceViaIO`.
1065    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    /// `get_coercion_expr`: `arg::type`. A constant of the same type shows no type of its own, because the cast shows it.
1081    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    /// `get_func_expr` and `get_oper_expr`.
1098    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    /// A `RowCompareExpr`: both rows with `ROW`, and the name of the operator of the first pair of fields.
1130    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    /// A call in the form `name(args)`.
1166    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        // `generate_function_name`: the name has its schema when the name alone does not find the function.
1171        if reg::function_visible(proc, self.session) {
1172            self.buf.push_str(&quote_identifier(proc.name));
1173        } else {
1174            let schema = reg::namespace_name(proc.namespace, self.session);
1175            self.buf.push_str(&reg::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    /// A `CASE`. With an argument, a condition `CaseTestExpr = value` shows only the value.
1193    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
1223/// The name of a system column.
1224fn 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
1236/// The text of a `SQLValueFunction`.
1237fn 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
1257/// The name of an operator with one character, as `get_simple_binary_op_name` gives it for an operator with two arguments.
1258fn 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
1270/// True for a node that shows its arguments between its own parentheses or keywords, the parents that the cases of `isSimpleNode` name for a test and a `BoolExpr`. A call shows a cast with `::`, which binds tighter than a test.
1271fn 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
1292/// The case of `isSimpleNode` for a `BoolExpr` with the operator `op`.
1293fn 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
1306/// `get_json_format`: the `FORMAT` clause, or nothing for the default format.
1307fn 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
1319/// `isSimpleNode`: true when the node needs no parentheses in its parent.
1320fn 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        // The `.` binds the most, but a field of a field needs parentheses.
1328        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                // The same priority: `(a - b) - c` needs no parentheses, `a - (b - c)` does.
1374                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
1394/// True when a `Var` is in the expression.
1395fn has_var(e: &Expr) -> bool {
1396    matches!(e.kind, ExprKind::Var(_)) || e.children().into_iter().any(has_var)
1397}
1398
1399impl Deparser<'_> {
1400    /// `get_name_for_var_field`: the name of the field with the index from 0. A value of type `record` takes the name from the expression that gives it, as the analyzer finds the fields.
1401    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
1415/// `get_name_for_var_field` for a value of a composite type: the name of the field with the index from 0.
1416fn 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
1422/// The names of the fields of a composite type, also through a domain, or `None` when the type is not the row type of a relation. A value of the type has no field for a dropped column.
1423pub(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
1443/// The names of the columns of a relation, by the column number from 1, or `None` when no relation has the OID.
1444fn 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
1460/// `deparse_expression_pretty` for a stored expression or a stored list of expressions, with the columns of the relation.
1461fn 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
1477/// The text of a constant of a stored bound of a partition, as `get_const_expr` with `showtype` -1 shows it.
1478fn 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
1485/// The `PartitionBoundSpec` part of `get_rule_expr`: the bound of a partition in `relpartbound`, as `FOR VALUES` shows it.
1486fn 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
1516/// `get_range_partbound_string`: the datums of a bound of a range partition in parentheses.
1517fn 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
1533/// The text of a stored constant, as `get_const_expr` with `showtype` -1 shows it, for the detail of an error on the bound of a partition.
1534///
1535/// # Errors
1536///
1537/// An internal error for a stored form that is not a constant, and the errors of the output function of the type.
1538pub 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
1545/// `deparse_expression` of a stored expression that has no column, such as the default of a domain in `typdefault`.
1546pub fn deparse_expression(text: &str, session: &dyn Session) -> Result<String> {
1547    deparse(text, "", &[], false, session)
1548}
1549
1550/// `deparse_expression` of a stored expression of the relation `relid`, with the names of its columns, as `pg_get_expr` gives it. The relation 0 gives an expression with no columns, such as the check of a domain.
1551///
1552/// # Errors
1553///
1554/// An internal error for a relation that the catalog does not have, and the errors of the deparse.
1555pub 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
1569/// The expressions of a stored form. A list is `(...)`, and an expression is `{...}`.
1570fn 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
1578/// `generate_relation_name`: the name of the relation, with its schema when the name alone does not find it.
1579fn 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
1589/// `generate_qualified_relation_name`: the name of the relation with its schema.
1590fn 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
1596/// The pretty flags of a call: `PRETTYFLAG_PAREN` and `PRETTYFLAG_SCHEMA` when the argument at `index` is true. A call without the argument has only `PRETTYFLAG_INDENT`.
1597fn 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
1604/// `pg_get_expr(pg_node_tree, oid [, bool])`.
1605fn 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
1628/// The error for an object of a system catalog, which has no stored form that the engine can read yet.
1629fn system_object(what: &str) -> Error {
1630    not_yet(format!("{what} of a system catalog"))
1631}
1632
1633/// The names of the columns, quoted, with `, ` between them, as `decompile_column_index_array`.
1634fn 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
1649/// The text of a referential action, or `None` for `NO ACTION`.
1650fn 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
1660/// `pg_get_constraintdef_worker`.
1661fn 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            // The check of a domain has no relation and no column. It refers to the value with `VALUE`.
1720            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        // The `NOT NULL` constraint of a domain has no column.
1732        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    // The validated status does not matter for a constraint that is `NOT ENFORCED`.
1748    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
1756/// `pg_get_constraintdef(oid [, bool])`.
1757fn 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
1769/// The catalog and the row number of a static row, by its OID, or by `indexrelid` for `pg_index`.
1770fn 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
1779/// The text form of a column of a static row.
1780fn 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
1786/// The quoted names of columns of a built-in relation, with `, ` between them.
1787fn 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
1805/// The numbers in the text form of an `int2vector` or an `int2[]`.
1806fn int2_list(text: &str) -> Vec<i16> {
1807    text.trim_matches(['{', '}']).split([' ', ',']).filter_map(|n| n.parse().ok()).collect()
1808}
1809
1810/// `pg_get_constraintdef` of a constraint of a system catalog: a primary key, a unique constraint or a `NOT NULL` constraint.
1811fn 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
1824/// `pg_get_indexdef` of an index of a system catalog. Each one is a B-tree on columns, with the default operator classes and no options.
1825fn 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
1857/// The collation of an index expression, as `exprCollation` gives it: the collation of the columns in it when they agree, else the default collation for a type that has collations.
1858fn 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
1882/// The OID of the collation `default`.
1883const DEFAULT_COLLATION: u32 = 100;
1884
1885/// `generate_collation_name`: the name of the collation, with its schema when the name alone does not find it.
1886fn 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
1896/// `get_opclass_name`: ` name` of the operator class, or nothing for the default class of the type.
1897fn 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
1910/// `pg_get_indexdef_worker` for an index of the user. `colno` 0 gives the whole statement, and another number gives only the text of that column.
1911fn 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(&quote_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
2020/// `looks_like_function`: true for an index expression that the syntax of an index column takes without parentheses.
2021fn 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
2033/// `pg_get_statisticsobj_worker`: the statement of a statistics object, or with `columns_only` only its columns and expressions. The statement has the schema of the object always, and the kinds only when the object does not have all of them. An expression that is not a call of a function is in parentheses. An OID that is not a statistics object gives `None`.
2034fn 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
2090/// `pg_get_statisticsobjdef`: the statement of a statistics object, or null for an OID that is not one.
2091fn 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
2096/// `pg_get_statisticsobjdef_columns`: the columns and the expressions of a statistics object.
2097fn 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
2102/// `pg_get_statisticsobjdef_expressions`: the expressions of a statistics object as a `text[]`, each with `PRETTYFLAG_INDENT` only, or null for an object with no expressions.
2103fn 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
2122/// `pg_get_partkeydef_worker`: the partition key of a partitioned table, as `PARTITION BY` writes it.
2123fn 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(&quote_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
2180/// `pg_get_partkeydef_columns(oid, true)`: the columns and the expressions of the partition key of a partitioned table, with no strategy, collations or operator classes, as the detail of an error of tuple routing shows them.
2181///
2182/// # Errors
2183///
2184/// The error of the deparse of an expression of the key.
2185pub 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
2192/// `pg_get_indexdef_columns(oid, true)`: the key columns and the expressions of the key of an index, as the detail of an error of a duplicate key shows them.
2193///
2194/// # Errors
2195///
2196/// The error of the deparse of an expression of the key.
2197pub 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
2213/// `pg_get_partkeydef(oid)`: the partition key of a partitioned table, or null for another relation.
2214fn 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
2227/// `pg_get_partition_constraintdef(oid)`: the partition constraint of a partition, as `generate_partition_qual` makes it and `deparse_expression_pretty` shows it with the columns of the partition. A table that is not a partition gives null.
2228fn 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
2249/// `pg_get_indexdef(oid)` and `pg_get_indexdef(oid, int4, bool)`.
2250fn 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
2273/// The kernels of this module, by the `prosrc` of the function.
2274pub(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}