1use rupg_common::{Error, Result};
4use rupg_pgcatalog::builtin;
5use rupg_sql::nodes::CallStmt;
6use rupg_types::oid;
7
8use crate::Analyzer;
9use crate::agg::Kind;
10use crate::expr::{Expr, ExprKind, Var};
11use crate::from::{FromFunction, FromItem, Relation};
12use crate::funcargs::{all_arguments, expand};
13use crate::select::{Query, Target};
14use crate::typename::{names, place};
15
16#[derive(Clone, Debug, PartialEq)]
18pub struct OutArg {
19 pub expr: Expr,
21 pub name: Option<String>,
23 pub position: usize,
25}
26
27impl Analyzer<'_> {
28 pub(crate) fn call(&mut self, stmt: &CallStmt) -> Result<Query> {
32 let Some(fc) = &stmt.funccall else { return Err(Error::internal("CALL with no call")) };
33 let at = place(fc.location);
34 let func_names = names(&fc.funcname);
35 let args = self.with_kind(Kind::CallArgument, |a| a.transform_list(&fc.args))?;
36 let mut call = self.make_func(&func_names, args, fc.func_variadic, None, true, at)?;
37 let ExprKind::Func(func) = &mut call.kind else {
38 return Err(Error::internal("CALL of a procedure that is not a function call"));
39 };
40 let proc = builtin::proc_by_oid(func.oid).expect("procedure in pg_proc");
41 let args = expand(&func.args, &all_arguments(proc))?.into_owned();
42 let out_args = args
43 .iter()
44 .zip(proc.argmodes.unwrap_or_default())
45 .zip(1..)
46 .filter(|((_, mode), _)| matches!(**mode, b'b' | b'o'))
47 .map(|((arg, _), position)| OutArg {
48 expr: arg.clone(),
49 name: proc
50 .argnames
51 .and_then(|names| names.get(position - 1))
52 .filter(|name| !name.is_empty())
53 .map(|name| String::from(*name)),
54 position,
55 })
56 .collect();
57 func.args = match proc.argmodes {
58 Some(modes) => args
59 .into_iter()
60 .zip(modes)
61 .filter(|(_, mode)| **mode != b'o')
62 .map(|(arg, _)| arg)
63 .collect(),
64 None => args,
65 };
66 let columns = crate::record::out_columns(func)?;
67 let name = proc.name.to_string();
68 let Some(columns) = columns.filter(|_| call.ty != oid::VOID) else {
69 let target = Target { name, expr: call, origin: None, junk: false };
70 return Ok(Query { targets: vec![target], out_args, ..Query::default() });
71 };
72 let targets = columns
73 .iter()
74 .zip(1..)
75 .map(|(column, attnum)| {
76 let var = Var { relation: 0, attnum, levels_up: 0 };
77 let mut expr = Expr::new(ExprKind::Var(var), column.ty);
78 expr.typmod = column.typmod;
79 Target { name: column.name.clone(), expr, origin: None, junk: false }
80 })
81 .collect();
82 let function = FromFunction {
83 calls: vec![(call, columns.len())],
84 ordinality: false,
85 coldefs: vec![false],
86 };
87 let mut relation = Relation::new(0, columns, None, Some(function), None);
88 relation.name = name;
89 Ok(Query {
90 relations: vec![relation],
91 from: vec![FromItem::Relation(0)],
92 targets,
93 out_args,
94 ..Query::default()
95 })
96 }
97}