Skip to main content

rupg_analyze/
record.rs

1//! `get_expr_result_tupdesc`, `build_function_result_tupdesc_t` and `expandRecordVariable` of `funcapi.c` and `parse_target.c`: the fields of a value of type `record`, which a field selection such as `(x).a` reads.
2
3use rupg_common::Result;
4use rupg_pgcatalog::builtin;
5
6use crate::expr::{Expr, ExprKind, Func};
7use crate::from::{Column, Relation};
8use crate::{Analyzer, poly, types};
9
10/// `build_function_result_tupdesc_t` and `resolve_polymorphic_tupdesc`: the columns of the rows of a call of a function with two or more `OUT`, `INOUT` or `TABLE` parameters, or of a procedure with one or more. A parameter without a name gives the name `columnN`, and a polymorphic type takes the actual type that the arguments of the call give. `None` for a function with fewer such parameters.
11///
12/// # Errors
13///
14/// The error of a polymorphic type that the arguments do not resolve.
15pub fn out_columns(func: &Func) -> Result<Option<Vec<Column>>> {
16    let Some(proc) = builtin::proc_by_oid(func.oid) else { return Ok(None) };
17    let (Some(all), Some(modes)) = (proc.allargtypes, proc.argmodes) else { return Ok(None) };
18    let mut columns: Vec<Column> = Vec::new();
19    for (i, (&ty, mode)) in all.iter().zip(modes).enumerate() {
20        if !matches!(mode, b'o' | b'b' | b't') {
21            continue;
22        }
23        let name = proc.argnames.and_then(|n| n.get(i)).copied().filter(|n| !n.is_empty());
24        let name = name.map_or_else(|| format!("column{}", columns.len() + 1), str::to_string);
25        let ty = if types::is_polymorphic(ty) {
26            let inputs: Vec<u32> = func.args.iter().map(|a| a.ty).collect();
27            poly::resolve(&inputs, &mut proc.argtypes.to_vec(), ty)?
28        } else {
29            ty
30        };
31        columns.push(Column { name, ty, typmod: -1, not_null: false });
32    }
33    let procedure = proc.kind == b'p' && !columns.is_empty();
34    Ok((columns.len() > 1 || procedure).then_some(columns))
35}
36
37/// `get_expr_result_tupdesc` and `expandRecordVariable`: the fields of an expression of type `record`. A call of a function gives its `OUT` parameters. The whole row of a join gives the columns of the join. A column of a subquery in `FROM` gives the fields of its expression in the subquery, also through more subqueries. `levels` has the relations of the query of the expression, then the relations of each query outside it. `None` when the fields are not known before the query runs.
38///
39/// # Errors
40///
41/// The error of a polymorphic type that the arguments of a call do not resolve.
42pub fn record_fields(expr: &Expr, levels: &[&[Relation]]) -> Result<Option<Vec<Column>>> {
43    match &expr.kind {
44        ExprKind::Func(f) => out_columns(f),
45        ExprKind::JoinRow(row) => Ok(Some(
46            row.names
47                .iter()
48                .zip(&row.fields)
49                .map(|(name, field)| Column {
50                    name: name.clone(),
51                    ty: field.ty,
52                    typmod: field.typmod,
53                    not_null: false,
54                })
55                .collect(),
56        )),
57        ExprKind::Var(var) => {
58            let Some(relation) = levels.get(var.levels_up).and_then(|r| r.get(var.relation)) else {
59                return Ok(None);
60            };
61            if var.attnum == 0 {
62                let mut columns = relation.columns.clone();
63                if relation.oid != 0 {
64                    columns.retain(|c| !c.is_dropped());
65                }
66                if let Some(names) = &relation.alias {
67                    for (column, name) in columns.iter_mut().zip(names) {
68                        column.name.clone_from(name);
69                    }
70                }
71                return Ok(Some(columns));
72            }
73            // A view is a relation of the query, so a column of type `record` of a view has no known fields, as in PostgreSQL.
74            let (0, Some(query)) = (relation.oid, &relation.subquery) else { return Ok(None) };
75            let target = usize::try_from(var.attnum - 1).ok().and_then(|i| query.targets.get(i));
76            let Some(target) = target else { return Ok(None) };
77            let mut inner = vec![&query.relations[..]];
78            inner.extend_from_slice(&levels[var.levels_up..]);
79            record_fields(&target.expr, &inner)
80        }
81        _ => Ok(None),
82    }
83}
84
85impl Analyzer<'_> {
86    /// The fields of an expression of type `record` in the query that the analyzer reads now.
87    pub(crate) fn record_columns(&self, expr: &Expr) -> Result<Option<Vec<Column>>> {
88        let levels: Vec<&[Relation]> = self.levels().map(|(_, s)| &s.relations[..]).collect();
89        record_fields(expr, &levels)
90    }
91}