use cratestack_core::{Query, scan_sql_placeholders};
use crate::diagnostics::{SchemaError, span_error};
pub(super) fn validate_query_placeholders(query: &Query) -> Result<(), SchemaError> {
let Some(sql) = query.sql() else {
return Ok(());
};
let referenced = scan_sql_placeholders(&sql);
let declared = u32::try_from(query.args.len()).unwrap_or(u32::MAX);
if let Some(&out_of_range) = referenced
.iter()
.find(|&&index| index == 0 || index > declared)
{
return Err(span_error(
format!(
"query `{}` references parameter `${out_of_range}` in its SQL body, but {}",
query.name,
declared_parameters(query)
),
query.span,
));
}
for (position, arg) in query.args.iter().enumerate() {
let index = position as u32 + 1;
if !referenced.contains(&index) {
return Err(span_error(
format!(
"query `{}` declares parameter `{}` (`${index}`) but it is never referenced \
in the SQL body",
query.name, arg.name
),
arg.span,
));
}
}
Ok(())
}
fn declared_parameters(query: &Query) -> String {
if query.args.is_empty() {
return "no parameters are declared".to_owned();
}
let names = query
.args
.iter()
.map(|arg| format!("`{}`", arg.name))
.collect::<Vec<_>>()
.join(", ");
format!(
"only {} parameter(s) are declared ({names})",
query.args.len()
)
}