uqa_sql/routines/
invocation.rs1use crate::{
9 assignment::routines::RoutineValueContext,
10 ast::{
11 ColumnType, CreateFunction, FunctionParamMode, FunctionReturns, RoutineInvocationBinding,
12 },
13 expr::value_type_name,
14 routines::declaration::RoutineTypeCatalog,
15 SQLError,
16};
17use uqa_core::Value;
18pub fn output_column_names(def: &CreateFunction) -> Vec<String> {
19 def.output_params()
20 .iter()
21 .enumerate()
22 .map(|(idx, p)| {
23 if p.name.is_empty() {
24 format!("column{}", idx + 1)
25 } else {
26 p.name.clone()
27 }
28 })
29 .collect()
30}
31
32pub fn call_signature(name: &str, args: &[(Option<String>, Value)]) -> String {
33 let types = args
34 .iter()
35 .map(|(arg_name, value)| match arg_name {
36 Some(arg_name) => format!("{arg_name} => {}", value_type_name(value)),
37 None => value_type_name(value).to_string(),
38 })
39 .collect::<Vec<_>>()
40 .join(", ");
41 format!("{name}({types})")
42}
43
44pub fn routine_resolution_error(
45 kind: &str,
46 name: &str,
47 args: &[(Option<String>, Value)],
48 suffix: &str,
49) -> SQLError {
50 SQLError::Routine {
51 sqlstate: if suffix == "is not unique" {
52 "42725".into()
53 } else {
54 "42883".into()
55 },
56 message: format!("{kind} {} {suffix}", call_signature(name, args)),
57 }
58}
59
60pub fn runtime_argument_types(
61 args: &[(Option<String>, Value)],
62) -> Result<Vec<Option<ColumnType>>, SQLError> {
63 args.iter()
64 .map(|(_, value)| {
65 if matches!(value, Value::Null) {
66 Ok(None)
67 } else {
68 ColumnType::from_sql_name(value_type_name(value)).map(Some)
69 }
70 })
71 .collect()
72}
73
74pub fn specialized_definition(
75 definition: &CreateFunction,
76 invocation: &RoutineInvocationBinding,
77) -> Result<Option<CreateFunction>, SQLError> {
78 if invocation.parameter_types.len() != definition.params.len() {
79 return Err(SQLError::Internal(format!(
80 "routine `{}` has {} concrete parameter types for {} parameters",
81 definition.name,
82 invocation.parameter_types.len(),
83 definition.params.len()
84 )));
85 }
86 let parameters_match = definition
87 .params
88 .iter()
89 .zip(&invocation.parameter_types)
90 .all(|(parameter, type_name)| parameter.type_name == *type_name);
91 let return_type_matches = match (&invocation.return_type, &definition.returns) {
92 (Some(concrete), FunctionReturns::Scalar { type_name })
93 | (Some(concrete), FunctionReturns::SetOf { type_name }) => concrete == type_name,
94 (None, _) | (Some(_), FunctionReturns::None | FunctionReturns::Table) => true,
95 };
96 if parameters_match && return_type_matches {
97 return Ok(None);
98 }
99 let mut specialized = definition.clone();
100 for (parameter, type_name) in specialized
101 .params
102 .iter_mut()
103 .zip(&invocation.parameter_types)
104 {
105 parameter.type_name.clone_from(type_name);
106 }
107 if let Some(return_type) = &invocation.return_type {
108 match &mut specialized.returns {
109 FunctionReturns::Scalar { type_name } | FunctionReturns::SetOf { type_name } => {
110 type_name.clone_from(return_type);
111 }
112 FunctionReturns::None | FunctionReturns::Table => {}
113 }
114 }
115 Ok(Some(specialized))
116}
117
118pub fn runtime_record_column_type(value: &Value) -> Option<crate::ast::ColumnType> {
119 if matches!(value, Value::Null) {
120 return None;
121 }
122 crate::ast::ColumnType::from_sql_name(crate::expr::value_type_name(value)).ok()
123}
124
125pub fn coerce_anonymous_record_value(
126 context: &dyn RoutineValueContext,
127 value: &Value,
128 type_name: &str,
129) -> Result<Value, SQLError> {
130 let target = context
131 .catalog_column_type(type_name)
132 .or_else(|| crate::ast::ColumnType::from_sql_name(type_name).ok());
133 let Some(target) = target else {
134 return crate::assignment::routines::coerce_routine_value(context, value, type_name);
135 };
136 crate::assignment::conversion::convert_value_to_column_type_with_context(
137 context,
138 value.clone(),
139 &target,
140 )
141 .map_err(|error| match error {
142 SQLError::TypeMismatch(message) if message.starts_with("value too long for type ") => {
143 SQLError::Routine {
144 sqlstate: "22001".into(),
145 message,
146 }
147 }
148 other => other,
149 })
150}
151
152pub fn anonymous_record_shape_error() -> SQLError {
153 SQLError::Routine {
154 sqlstate: "42P13".into(),
155 message: "return type mismatch in function declared to return record".into(),
156 }
157}
158pub fn call_output_schema(
159 catalog: &dyn RoutineTypeCatalog,
160 definition: &crate::ast::CreateFunction,
161 parameter_types: &[String],
162) -> Result<Option<crate::RowSchema>, SQLError> {
163 let output_indices = definition
164 .params
165 .iter()
166 .enumerate()
167 .filter_map(|(index, parameter)| {
168 matches!(
169 parameter.mode,
170 FunctionParamMode::Out | FunctionParamMode::InOut | FunctionParamMode::Table
171 )
172 .then_some(index)
173 })
174 .collect::<Vec<_>>();
175 if output_indices.is_empty() {
176 return Ok(None);
177 }
178 let columns = output_column_names(definition);
179 let column_types = output_indices
180 .into_iter()
181 .map(|index| {
182 catalog
183 .resolve_catalog_column_type(¶meter_types[index])
184 .or_else(|| crate::ast::ColumnType::from_sql_name(¶meter_types[index]).ok())
185 .map(Some)
186 .ok_or_else(|| {
187 SQLError::TypeMismatch(format!("unknown type `{}`", parameter_types[index]))
188 })
189 })
190 .collect::<Result<Vec<_>, SQLError>>()?;
191 Ok(Some(crate::RowSchema::with_types(columns, column_types)))
192}