use std::borrow::Cow;
use uqa_core::Value;
use crate::ast::{Expr, FunctionBinding, FunctionDispatch};
use crate::error::{Result, SQLError};
use super::context::EvalContext;
use super::evaluator::eval;
pub(super) fn normalized_function_name(name: &str) -> Cow<'_, str> {
let stripped = name.strip_prefix("pg_catalog.").unwrap_or(name);
if stripped.bytes().any(|byte| byte.is_ascii_uppercase()) {
Cow::Owned(stripped.to_ascii_lowercase())
} else {
Cow::Borrowed(stripped)
}
}
fn binding_dispatch(binding: Option<&FunctionBinding>) -> Option<FunctionDispatch> {
binding.and_then(|binding| binding.dispatch)
}
fn direct_variadic_argument_value(argument: &Expr) -> Option<&Expr> {
let Expr::Func { binding, args, .. } = argument else {
return None;
};
if binding_dispatch(binding.as_ref()) != Some(FunctionDispatch::VariadicArgument) {
return None;
}
let [value] = args.as_slice() else {
return None;
};
Some(value)
}
fn named_argument_value(argument: &Expr) -> Option<&Expr> {
let Expr::Func { binding, args, .. } = argument else {
return None;
};
if binding_dispatch(binding.as_ref()) == Some(FunctionDispatch::NamedArgument) {
args.get(1)
} else {
None
}
}
#[must_use]
pub fn wrap_variadic_argument(mut argument: Expr) -> Expr {
if variadic_argument_value(&argument).is_some() {
return argument;
}
if let Expr::Func { binding, args, .. } = &mut argument {
if binding_dispatch(binding.as_ref()) == Some(FunctionDispatch::NamedArgument)
&& args.len() == 2
{
let value = args.remove(1);
args.push(variadic_argument_marker(value));
return argument;
}
}
variadic_argument_marker(argument)
}
fn variadic_argument_marker(value: Expr) -> Expr {
let binding = FunctionBinding::dispatched(FunctionDispatch::VariadicArgument);
Expr::Func {
name: binding.name.clone(),
binding: Some(binding),
args: vec![value],
distinct: false,
order_by: Vec::new(),
filter: None,
}
}
#[must_use]
pub fn variadic_argument_value(argument: &Expr) -> Option<&Expr> {
let value = named_argument_value(argument).unwrap_or(argument);
direct_variadic_argument_value(value)
}
#[must_use]
pub fn call_argument_value(argument: &Expr) -> &Expr {
let value = named_argument_value(argument).unwrap_or(argument);
direct_variadic_argument_value(value).unwrap_or(value)
}
pub fn validate_named_argument_order<'a>(
argument_names: impl IntoIterator<Item = Option<&'a str>>,
) -> Result<()> {
let mut saw_named = false;
let mut named = Vec::new();
for argument_name in argument_names {
let Some(argument_name) = argument_name else {
if saw_named {
return Err(SQLError::Routine {
sqlstate: "42601".into(),
message: "positional argument cannot follow named argument".into(),
});
}
continue;
};
saw_named = true;
if named.contains(&argument_name) {
return Err(SQLError::Routine {
sqlstate: "42601".into(),
message: format!("argument name \"{argument_name}\" used more than once"),
});
}
named.push(argument_name);
}
Ok(())
}
pub fn evaluate_call_args(
args: &[Expr],
ctx: &EvalContext<'_>,
) -> Result<Vec<(Option<String>, Value)>> {
args.iter()
.map(|arg| match arg {
Expr::Func {
binding,
args: inner,
..
} if binding_dispatch(binding.as_ref()) == Some(FunctionDispatch::NamedArgument) => {
let Some(Expr::Literal(Value::Str(arg_name))) = inner.first() else {
return Err(SQLError::Internal("named argument without a name".into()));
};
let value_expr = inner
.get(1)
.ok_or_else(|| SQLError::Internal("named argument without a value".into()))?;
Ok((
Some(arg_name.clone()),
evaluate_call_argument_value(value_expr, ctx)?,
))
}
other => Ok((None, evaluate_call_argument_value(other, ctx)?)),
})
.collect()
}
fn evaluate_call_argument_value(argument: &Expr, ctx: &EvalContext<'_>) -> Result<Value> {
if let Expr::Func { binding, args, .. } = argument {
if binding_dispatch(binding.as_ref()) == Some(FunctionDispatch::VariadicArgument) {
let [value] = args.as_slice() else {
return Err(SQLError::Internal(
"VARIADIC argument marker must contain one value".into(),
));
};
return eval(value, ctx);
}
}
eval(argument, ctx)
}