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 resolved = self.resolve(name, overloads, infer)?;
89 super::invocation::call_output_schema(
90 types,
91 &resolved.function.def,
92 &resolved.invocation.parameter_types,
93 )
94 }
95
96 pub(crate) fn resolve(
97 &self,
98 name: &str,
99 overloads: &RoutineOverloadContext<'_>,
100 infer: &mut dyn FnMut(&ExpressionPlan) -> Result<Option<ColumnType>, SQLError>,
101 ) -> Result<super::StaticFunctionMatch, SQLError> {
102 let argument_types = infer_call_argument_types(self.arguments, &self.decoded, infer)?;
103 let Some(resolved) = overloads.resolve_static_sql_routine_match(
104 name,
105 None,
106 &self.names,
107 &argument_types,
108 self.explicit_variadic,
109 RoutineCallKind::Procedure,
110 )?
111 else {
112 let signature = argument_types
113 .iter()
114 .map(|argument| {
115 argument
116 .as_ref()
117 .map_or_else(|| "unknown", column_type_name)
118 })
119 .collect::<Vec<_>>()
120 .join(", ");
121 return Err(SQLError::Diagnostic {
122 sqlstate: "42883".into(),
123 message: format!("procedure {name}({signature}) does not exist"),
124 detail: None,
125 hint: Some(
126 "No procedure matches the given name and argument types. You might need to add explicit type casts."
127 .into(),
128 ),
129 });
130 };
131 Ok(resolved)
132 }
133}