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
79#[must_use]
81pub fn wrap_variadic_argument(mut argument: Expr) -> Expr {
82 if variadic_argument_value(&argument).is_some() {
83 return argument;
84 }
85 if let Expr::Func { binding, args, .. } = &mut argument {
86 if binding_dispatch(binding.as_ref()) == Some(FunctionDispatch::NamedArgument)
87 && args.len() == 2
88 {
89 let value = args.remove(1);
90 args.push(variadic_argument_marker(value));
91 return argument;
92 }
93 }
94 variadic_argument_marker(argument)
95}
96
97fn variadic_argument_marker(value: Expr) -> Expr {
98 let binding = FunctionBinding::dispatched(FunctionDispatch::VariadicArgument);
99 Expr::Func {
100 name: binding.name.clone(),
101 binding: Some(binding),
102 args: vec![value],
103 distinct: false,
104 order_by: Vec::new(),
105 filter: None,
106 }
107}
108
109#[must_use]
111pub fn variadic_argument_value(argument: &Expr) -> Option<&Expr> {
112 let value = named_argument_value(argument).unwrap_or(argument);
113 direct_variadic_argument_value(value)
114}
115
116#[must_use]
118pub fn call_argument_value(argument: &Expr) -> &Expr {
119 let value = named_argument_value(argument).unwrap_or(argument);
120 direct_variadic_argument_value(value).unwrap_or(value)
121}
122
123pub fn validate_named_argument_order<'a>(
127 argument_names: impl IntoIterator<Item = Option<&'a str>>,
128) -> Result<()> {
129 validate_named_argument_order_with_control(argument_names, &ProductionControl::uncontrolled())
130}
131
132pub fn validate_named_argument_order_with_control<'a>(
133 argument_names: impl IntoIterator<Item = Option<&'a str>>,
134 control: &ProductionControl<'_>,
135) -> Result<()> {
136 control.check()?;
137 let mut saw_named = false;
138 let mut named = ProductionVec::new(*control);
139 for argument_name in argument_names {
140 control.check()?;
141 let Some(argument_name) = argument_name else {
142 if saw_named {
143 return Err(SQLError::Routine {
144 sqlstate: "42601".into(),
145 message: "positional argument cannot follow named argument".into(),
146 });
147 }
148 continue;
149 };
150 saw_named = true;
151 if named.contains(&argument_name) {
152 return Err(SQLError::Routine {
153 sqlstate: "42601".into(),
154 message: format!("argument name \"{argument_name}\" used more than once"),
155 });
156 }
157 named.push_copy(argument_name)?;
158 }
159 Ok(())
160}
161
162pub fn evaluate_call_args(
164 args: &[Expr],
165 ctx: &EvalContext<'_>,
166) -> Result<Vec<(Option<String>, Value)>> {
167 args.iter()
168 .map(|arg| match arg {
169 Expr::Func {
170 binding,
171 args: inner,
172 ..
173 } if binding_dispatch(binding.as_ref()) == Some(FunctionDispatch::NamedArgument) => {
174 let Some(Expr::Literal(Value::Str(arg_name))) = inner.first() else {
175 return Err(SQLError::Internal("named argument without a name".into()));
176 };
177 let value_expr = inner
178 .get(1)
179 .ok_or_else(|| SQLError::Internal("named argument without a value".into()))?;
180 Ok((
181 Some(arg_name.clone()),
182 evaluate_call_argument_value(value_expr, ctx)?,
183 ))
184 }
185 other => Ok((None, evaluate_call_argument_value(other, ctx)?)),
186 })
187 .collect()
188}
189
190fn evaluate_call_argument_value(argument: &Expr, ctx: &EvalContext<'_>) -> Result<Value> {
191 if let Expr::Func { binding, args, .. } = argument {
192 if binding_dispatch(binding.as_ref()) == Some(FunctionDispatch::VariadicArgument) {
193 let [value] = args.as_slice() else {
194 return Err(SQLError::Internal(
195 "VARIADIC argument marker must contain one value".into(),
196 ));
197 };
198 return eval(value, ctx);
199 }
200 }
201 eval(argument, ctx)
202}
203
204pub(super) fn evaluated_argument_names_with_control<'a>(
206 args: &'a [(Option<String>, Value)],
207 control: &ProductionControl<'_>,
208) -> Result<Produced<Vec<Option<&'a str>>>> {
209 let mut names = ProductionVec::new(*control);
210 names.reserve(args.len())?;
211 for (name, _) in args {
212 names.push_copy(name.as_deref())?;
213 }
214 Ok(names.finish()?)
215}