use inillucent_value::Collation;
use super::collation::explicit_argument_collation;
use super::{refused, unsupported, Binder, BoundAggregate, BoundExpr};
use crate::ast::ExprId;
use crate::diagnostic::ParseError;
use crate::function::{self, AggregateFunc};
use crate::lexer::Span;
impl Binder<'_> {
pub(super) fn bind_external_call(
&mut self,
folded: &[u8],
arguments: &[ExprId],
distinct: bool,
span: Span,
) -> Result<Option<BoundExpr>, ParseError> {
let Some(found) = function::lookup_external(self.externals, folded, arguments.len()) else {
return Ok(None);
};
if let Some(why) =
function::schema_refusal(found.flags, self.call_site, self.trusted_schema)
{
return Err(refused(
format!("{} {why}", String::from_utf8_lossy(folded)),
span,
));
}
let aggregate = found.aggregate;
if !aggregate {
if distinct {
return Err(unsupported("DISTINCT on a scalar function", span));
}
let mut bound = Vec::with_capacity(arguments.len());
for argument in arguments {
bound.push(self.bind_expr(*argument)?);
}
return Ok(Some(BoundExpr::External {
name: folded.to_vec(),
arguments: bound,
}));
}
if !self.allow_aggregates || self.inside_aggregate {
return Err(unsupported("misuse of aggregate function", span));
}
self.inside_aggregate = true;
let mut bound = Vec::with_capacity(arguments.len());
for argument in arguments {
bound.push(self.bind_expr(*argument)?);
}
self.inside_aggregate = false;
let collation = bound
.first()
.and_then(BoundExpr::collation)
.unwrap_or(Collation::Binary);
let candidate = BoundAggregate {
func: AggregateFunc::External,
external: Some(folded.to_vec()),
distinct,
arguments: bound,
star: false,
collation,
filter: None,
order_by: Vec::new(),
};
Ok(Some(self.aggregate_slot(candidate)))
}
pub(super) fn aggregate_slot(&mut self, candidate: BoundAggregate) -> BoundExpr {
let collation = explicit_argument_collation(&candidate.arguments);
let slot = match self
.aggregates
.iter()
.position(|existing| existing == &candidate)
{
Some(slot) => slot,
None => {
self.aggregates.push(candidate);
self.aggregates.len().saturating_sub(1)
}
};
BoundExpr::Aggregate { slot, collation }
}
}