use rupg_common::{Error, Result};
use rupg_pgcatalog::builtin;
use rupg_sql::nodes::CallStmt;
use rupg_types::oid;
use crate::Analyzer;
use crate::agg::Kind;
use crate::expr::{Expr, ExprKind, Var};
use crate::from::{FromFunction, FromItem, Relation};
use crate::funcargs::{all_arguments, expand};
use crate::select::{Query, Target};
use crate::typename::{names, place};
#[derive(Clone, Debug, PartialEq)]
pub struct OutArg {
pub expr: Expr,
pub name: Option<String>,
pub position: usize,
}
impl Analyzer<'_> {
pub(crate) fn call(&mut self, stmt: &CallStmt) -> Result<Query> {
let Some(fc) = &stmt.funccall else { return Err(Error::internal("CALL with no call")) };
let at = place(fc.location);
let func_names = names(&fc.funcname);
let args = self.with_kind(Kind::CallArgument, |a| a.transform_list(&fc.args))?;
let mut call = self.make_func(&func_names, args, fc.func_variadic, None, true, at)?;
let ExprKind::Func(func) = &mut call.kind else {
return Err(Error::internal("CALL of a procedure that is not a function call"));
};
let proc = builtin::proc_by_oid(func.oid).expect("procedure in pg_proc");
let args = expand(&func.args, &all_arguments(proc))?.into_owned();
let out_args = args
.iter()
.zip(proc.argmodes.unwrap_or_default())
.zip(1..)
.filter(|((_, mode), _)| matches!(**mode, b'b' | b'o'))
.map(|((arg, _), position)| OutArg {
expr: arg.clone(),
name: proc
.argnames
.and_then(|names| names.get(position - 1))
.filter(|name| !name.is_empty())
.map(|name| String::from(*name)),
position,
})
.collect();
func.args = match proc.argmodes {
Some(modes) => args
.into_iter()
.zip(modes)
.filter(|(_, mode)| **mode != b'o')
.map(|(arg, _)| arg)
.collect(),
None => args,
};
let columns = crate::record::out_columns(func)?;
let name = proc.name.to_string();
let Some(columns) = columns.filter(|_| call.ty != oid::VOID) else {
let target = Target { name, expr: call, origin: None, junk: false };
return Ok(Query { targets: vec![target], out_args, ..Query::default() });
};
let targets = columns
.iter()
.zip(1..)
.map(|(column, attnum)| {
let var = Var { relation: 0, attnum, levels_up: 0 };
let mut expr = Expr::new(ExprKind::Var(var), column.ty);
expr.typmod = column.typmod;
Target { name: column.name.clone(), expr, origin: None, junk: false }
})
.collect();
let function = FromFunction {
calls: vec![(call, columns.len())],
ordinality: false,
coldefs: vec![false],
};
let mut relation = Relation::new(0, columns, None, Some(function), None);
relation.name = name;
Ok(Query {
relations: vec![relation],
from: vec![FromItem::Relation(0)],
targets,
out_args,
..Query::default()
})
}
}