1use crate::{
10 assignment::conversion::column_type_name,
11 ast::ColumnType,
12 ir::{analyze_expression_call_arguments, ScalarCallArgument},
13 plan::ExpressionPlan,
14 routines::{
15 declaration::RoutineTypeCatalog,
16 resolution::{RoutineCallKind, RoutineOverloadContext},
17 },
18 RowSchema, SQLError, ScalarExpr,
19};
20use uqa_core::Value;
21
22pub fn validate_call_arguments(arguments: &[ExpressionPlan]) -> Result<(), SQLError> {
24 if arguments
25 .iter()
26 .any(|argument| !argument.subqueries.is_empty())
27 {
28 return Err(SQLError::Unsupported(
29 "cannot use subquery in CALL argument".into(),
30 ));
31 }
32 Ok(())
33}
34
35pub fn infer_call_argument_types(
37 arguments: &[ExpressionPlan],
38 decoded: &[ScalarCallArgument<'_>],
39 infer: &mut dyn FnMut(&ExpressionPlan) -> Result<Option<ColumnType>, SQLError>,
40) -> Result<Vec<Option<ColumnType>>, SQLError> {
41 arguments
42 .iter()
43 .zip(decoded)
44 .map(|(argument, call_argument)| {
45 if matches!(
46 call_argument.value,
47 ScalarExpr::Literal(Value::Str(_) | Value::Null)
48 ) {
49 Ok(None)
50 } else {
51 infer(argument)
52 }
53 })
54 .collect()
55}
56
57pub struct ProcedureCallAnalysis<'a> {
59 arguments: &'a [ExpressionPlan],
60 decoded: Vec<ScalarCallArgument<'a>>,
61 names: Vec<Option<String>>,
62 explicit_variadic: bool,
63}
64
65impl<'a> ProcedureCallAnalysis<'a> {
66 pub fn new(arguments: &'a [ExpressionPlan]) -> Result<Self, SQLError> {
67 validate_call_arguments(arguments)?;
68 let (decoded, explicit_variadic) = analyze_expression_call_arguments(arguments)?;
69 let names = decoded
70 .iter()
71 .map(|argument| argument.name.map(str::to_string))
72 .collect();
73 Ok(Self {
74 arguments,
75 decoded,
76 names,
77 explicit_variadic,
78 })
79 }
80
81 pub fn result_schema(
82 &self,
83 name: &str,
84 overloads: &RoutineOverloadContext<'_>,
85 types: &dyn RoutineTypeCatalog,
86 infer: &mut dyn FnMut(&ExpressionPlan) -> Result<Option<ColumnType>, SQLError>,
87 ) -> Result<Option<RowSchema>, SQLError> {
88 let argument_types = infer_call_argument_types(self.arguments, &self.decoded, infer)?;
89 let Some(resolved) = overloads.resolve_static_sql_routine_match(
90 name,
91 None,
92 &self.names,
93 &argument_types,
94 self.explicit_variadic,
95 RoutineCallKind::Procedure,
96 )?
97 else {
98 let signature = argument_types
99 .iter()
100 .map(|argument| {
101 argument
102 .as_ref()
103 .map_or_else(|| "unknown", column_type_name)
104 })
105 .collect::<Vec<_>>()
106 .join(", ");
107 return Err(SQLError::Routine {
108 sqlstate: "42883".into(),
109 message: format!("procedure {name}({signature}) does not exist"),
110 });
111 };
112 super::invocation::call_output_schema(
113 types,
114 &resolved.function.def,
115 &resolved.invocation.parameter_types,
116 )
117 }
118}