uqa_sql/expr/
call_arguments.rs1use std::borrow::Cow;
10
11use uqa_core::{
12 memory::{Produced, ProductionControl, ProductionString, ProductionVec},
13 Value,
14};
15
16use crate::ast::{Expr, FunctionBinding, FunctionDispatch};
17use crate::error::{Result, SQLError};
18
19use super::context::EvalContext;
20use super::evaluator::eval;
21
22pub(super) fn normalized_function_name(name: &str) -> Cow<'_, str> {
23 normalized_function_name_with_control(name, &ProductionControl::uncontrolled())
24 .expect("ordinary function name normalization")
25 .into_uncontrolled()
26 .expect("ordinary function name has no retained owner")
27}
28
29pub(super) fn normalized_function_name_with_control<'a>(
30 name: &'a str,
31 control: &ProductionControl<'_>,
32) -> Result<Produced<Cow<'a, str>>> {
33 control.check()?;
34 let stripped = name.strip_prefix("pg_catalog.").unwrap_or(name);
35 if !stripped.bytes().any(|byte| byte.is_ascii_uppercase()) {
36 return control
37 .finish(Cow::Borrowed(stripped), control.empty_reservation())
38 .map_err(Into::into);
39 }
40 let mut output = ProductionString::new(*control);
41 output.reserve(stripped.len())?;
42 for character in stripped.chars() {
43 output.push(character.to_ascii_lowercase())?;
44 }
45 let (output, memory) = output.finish()?.into_parts();
46 control
47 .finish(Cow::Owned(output), memory)
48 .map_err(Into::into)
49}
50
51fn binding_dispatch(binding: Option<&FunctionBinding>) -> Option<FunctionDispatch> {
52 binding.and_then(|binding| binding.dispatch)
53}
54
55fn direct_variadic_argument_value(argument: &Expr) -> Option<&Expr> {
56 let Expr::Func { binding, args, .. } = argument else {
57 return None;
58 };
59 if binding_dispatch(binding.as_ref()) != Some(FunctionDispatch::VariadicArgument) {
60 return None;
61 }
62 let [value] = args.as_slice() else {
63 return None;
64 };
65 Some(value)
66}
67
68fn named_argument_value(argument: &Expr) -> Option<&Expr> {
69 let Expr::Func { binding, args, .. } = argument else {
70 return None;
71 };
72 if binding_dispatch(binding.as_ref()) == Some(FunctionDispatch::NamedArgument) {
73 args.get(1)
74 } else {
75 None
76 }
77}
78
79pub fn call_argument_name(argument: &Expr) -> Result<Option<&str>> {
81 let Expr::Func { binding, args, .. } = argument else {
82 return Ok(None);
83 };
84 if binding_dispatch(binding.as_ref()) != Some(FunctionDispatch::NamedArgument) {
85 return Ok(None);
86 }
87 let [Expr::Literal(Value::Str(name)), _] = args.as_slice() else {
88 return Err(SQLError::Internal("invalid named argument marker".into()));
89 };
90 Ok(Some(name))
91}
92
93#[must_use]
95pub fn wrap_variadic_argument(mut argument: Expr) -> Expr {
96 if variadic_argument_value(&argument).is_some() {
97 return argument;
98 }
99 if let Expr::Func { binding, args, .. } = &mut argument {
100 if binding_dispatch(binding.as_ref()) == Some(FunctionDispatch::NamedArgument)
101 && args.len() == 2
102 {
103 let value = args.remove(1);
104 args.push(variadic_argument_marker(value));
105 return argument;
106 }
107 }
108 variadic_argument_marker(argument)
109}
110
111fn variadic_argument_marker(value: Expr) -> Expr {
112 let binding = FunctionBinding::dispatched(FunctionDispatch::VariadicArgument);
113 Expr::Func {
114 order_syntax: crate::ast::FunctionOrderSyntax::Ordinary,
115 name: binding.name.clone(),
116 binding: Some(binding),
117 args: vec![value],
118 distinct: false,
119 order_by: Vec::new(),
120 filter: None,
121 }
122}
123
124#[must_use]
126pub fn variadic_argument_value(argument: &Expr) -> Option<&Expr> {
127 let value = named_argument_value(argument).unwrap_or(argument);
128 direct_variadic_argument_value(value)
129}
130
131#[must_use]
133pub fn call_argument_value(argument: &Expr) -> &Expr {
134 let value = named_argument_value(argument).unwrap_or(argument);
135 direct_variadic_argument_value(value).unwrap_or(value)
136}
137
138pub fn validate_named_argument_order<'a>(
142 argument_names: impl IntoIterator<Item = Option<&'a str>>,
143) -> Result<()> {
144 validate_named_argument_order_with_control(argument_names, &ProductionControl::uncontrolled())
145}
146
147pub fn validate_named_argument_order_with_control<'a>(
148 argument_names: impl IntoIterator<Item = Option<&'a str>>,
149 control: &ProductionControl<'_>,
150) -> Result<()> {
151 control.check()?;
152 let mut saw_named = false;
153 let mut named = ProductionVec::new(*control);
154 for argument_name in argument_names {
155 control.check()?;
156 let Some(argument_name) = argument_name else {
157 if saw_named {
158 return Err(SQLError::Routine {
159 sqlstate: "42601".into(),
160 message: "positional argument cannot follow named argument".into(),
161 });
162 }
163 continue;
164 };
165 saw_named = true;
166 if named.contains(&argument_name) {
167 return Err(SQLError::Routine {
168 sqlstate: "42601".into(),
169 message: format!("argument name \"{argument_name}\" used more than once"),
170 });
171 }
172 named.push_copy(argument_name)?;
173 }
174 Ok(())
175}
176
177pub fn evaluate_call_args(
179 args: &[Expr],
180 ctx: &EvalContext<'_>,
181) -> Result<Vec<(Option<String>, Value)>> {
182 args.iter()
183 .map(|arg| match arg {
184 Expr::Func {
185 binding,
186 args: inner,
187 ..
188 } if binding_dispatch(binding.as_ref()) == Some(FunctionDispatch::NamedArgument) => {
189 let Some(Expr::Literal(Value::Str(arg_name))) = inner.first() else {
190 return Err(SQLError::Internal("named argument without a name".into()));
191 };
192 let value_expr = inner
193 .get(1)
194 .ok_or_else(|| SQLError::Internal("named argument without a value".into()))?;
195 Ok((
196 Some(arg_name.clone()),
197 evaluate_call_argument_value(value_expr, ctx)?,
198 ))
199 }
200 other => Ok((None, evaluate_call_argument_value(other, ctx)?)),
201 })
202 .collect()
203}
204
205fn evaluate_call_argument_value(argument: &Expr, ctx: &EvalContext<'_>) -> Result<Value> {
206 if let Expr::Func { binding, args, .. } = argument {
207 if binding_dispatch(binding.as_ref()) == Some(FunctionDispatch::VariadicArgument) {
208 let [value] = args.as_slice() else {
209 return Err(SQLError::Internal(
210 "VARIADIC argument marker must contain one value".into(),
211 ));
212 };
213 return eval(value, ctx);
214 }
215 }
216 eval(argument, ctx)
217}
218
219pub(super) fn evaluated_argument_names_with_control<'a>(
221 args: &'a [(Option<String>, Value)],
222 control: &ProductionControl<'_>,
223) -> Result<Produced<Vec<Option<&'a str>>>> {
224 let mut names = ProductionVec::new(*control);
225 names.reserve(args.len())?;
226 for (name, _) in args {
227 names.push_copy(name.as_deref())?;
228 }
229 Ok(names.finish()?)
230}