use crate::{ast::FunctionBinding, ColumnType, SQLError, ScalarExpr};
use uqa_core::memory::{MemoryReservation, Produced, ProductionControl};
#[derive(Debug)]
pub(super) struct BindingCall {
pub(super) name: String,
pub(super) binding: Option<FunctionBinding>,
pub(super) arguments: Vec<ScalarExpr>,
pub(super) distinct: bool,
pub(super) order_by: Vec<crate::ScalarOrder>,
pub(super) filter: Option<Box<ScalarExpr>>,
}
pub(super) type InferType<'a> =
dyn FnMut(&ScalarExpr) -> Result<Option<Produced<ColumnType>>, SQLError> + 'a;
pub(super) struct CallOwner {
pub(super) call: BindingCall,
pub(super) memory: Option<MemoryReservation>,
}
impl CallOwner {
pub(super) fn new(
call: Produced<BindingCall>,
control: &ProductionControl<'_>,
) -> Result<Self, SQLError> {
let call = check_owner(call, control)?;
let (call, memory) = call.into_parts();
Ok(Self { call, memory })
}
pub(super) fn finish(
self,
control: &ProductionControl<'_>,
) -> Result<Produced<BindingCall>, SQLError> {
Ok(control.finish(self.call, self.memory)?)
}
}
pub(super) fn check_memory(
memory: Option<&MemoryReservation>,
control: &ProductionControl<'_>,
) -> Result<(), SQLError> {
match (control.budget(), memory) {
(Some(budget), Some(memory)) => assert!(
budget.shares_allowance(memory.budget()),
"binding uses a foreign allowance"
),
(None, None) => {}
_ => panic!("binding ownership mode differs from its control"),
}
control.check()?;
Ok(())
}
pub(super) fn check_owner<T>(
value: Produced<T>,
control: &ProductionControl<'_>,
) -> Result<Produced<T>, SQLError> {
let (value, memory) = value.into_parts();
Ok(control.finish(value, memory)?)
}
pub(super) fn infer_with_control(
expression: &ScalarExpr,
infer: &mut InferType<'_>,
control: &ProductionControl<'_>,
) -> Result<Option<Produced<ColumnType>>, SQLError> {
infer(expression)?
.map(|ty| check_owner(ty, control))
.transpose()
}
#[cfg(test)]
mod tests;