mod expr;
mod predicate;
mod text;
use crate::{
db::{
QueryError, executor::budget::HardExecutionContext,
query::construction::ConstructionBudget, session::RequestExecutionScope,
},
error::InternalError,
value::Value,
};
use icydb_diagnostic_code::{
DiagnosticExecutionBudgetResource, DiagnosticExecutionBudgetScope, DiagnosticExecutionLane,
};
use std::cell::Cell;
pub(in crate::db) struct PreparationWork<'a> {
scope: &'a RequestExecutionScope,
context: HardExecutionContext,
last_instruction_counter: Cell<u64>,
charges_since_watermark: Cell<u8>,
}
impl PreparationWork<'_> {
pub(in crate::db) fn copy_text(&self, text: &str) -> Result<String, QueryError> {
(self as &dyn ConstructionBudget)
.copy_text(text)
.map_err(QueryError::execute)
}
pub(in crate::db) fn copy_value(&self, value: &Value) -> Result<Value, QueryError> {
(self as &dyn ConstructionBudget)
.copy_value(value)
.map_err(QueryError::execute)
}
pub(in crate::db) fn copy_slice<T, U>(
&self,
values: &[T],
mut copy: impl FnMut(&T) -> Result<U, QueryError>,
) -> Result<Vec<U>, QueryError> {
let mut copied = self.vec_with_capacity(values.len())?;
for value in values {
copied.push(copy(value)?);
}
Ok(copied)
}
pub(in crate::db) fn vec_with_capacity<T>(&self, len: usize) -> Result<Vec<T>, QueryError> {
(self as &dyn ConstructionBudget)
.vec_with_capacity(len)
.map_err(QueryError::execute)
}
pub(in crate::db) fn run<T>(
scope: &RequestExecutionScope,
lane: DiagnosticExecutionLane,
run: impl FnOnce(&PreparationWork<'_>) -> Result<T, QueryError>,
) -> Result<T, QueryError> {
let work = PreparationWork {
scope,
context: HardExecutionContext::new(DiagnosticExecutionBudgetScope::Request, lane, 0),
last_instruction_counter: Cell::new(crate::runtime::local_instruction_counter()),
charges_since_watermark: Cell::new(0),
};
let result = run(&work);
work.check_instruction_watermark()
.map_err(QueryError::execute)?;
result
}
pub(in crate::db) fn charge(
&self,
resource: DiagnosticExecutionBudgetResource,
amount: u64,
) -> Result<(), QueryError> {
ConstructionBudget::charge(self, resource, amount).map_err(QueryError::execute)
}
pub(in crate::db) fn reserve_vec<T>(
&self,
values: &mut Vec<T>,
additional: usize,
) -> Result<(), QueryError> {
(self as &dyn ConstructionBudget)
.reserve_vec(values, additional)
.map_err(QueryError::execute)
}
pub(in crate::db) fn reserve_string(
&self,
text: &mut String,
additional: usize,
) -> Result<(), QueryError> {
(self as &dyn ConstructionBudget)
.reserve_string(text, additional)
.map_err(QueryError::execute)
}
fn check_instruction_watermark(&self) -> Result<(), InternalError> {
let current = crate::runtime::local_instruction_counter();
let previous = self.last_instruction_counter.replace(current);
self.charges_since_watermark.set(0);
self.scope
.charge(
self.context,
DiagnosticExecutionBudgetResource::InstructionUnits,
current.saturating_sub(previous),
)
.map_err(InternalError::from)
}
}
impl ConstructionBudget for PreparationWork<'_> {
fn charge(
&self,
resource: DiagnosticExecutionBudgetResource,
amount: u64,
) -> Result<(), InternalError> {
self.scope
.charge(self.context, resource, amount)
.map_err(InternalError::from)?;
let charges = self.charges_since_watermark.get() + 1;
self.charges_since_watermark.set(charges);
if charges == 64 {
self.check_instruction_watermark()?;
}
Ok(())
}
}
#[cfg(test)]
pub(in crate::db) fn with_preparation_work<T>(run: impl FnOnce(&PreparationWork<'_>) -> T) -> T {
let root = crate::db::RequestExecutionRoot::__new_runtime_root();
PreparationWork::run(&root.scope(), DiagnosticExecutionLane::PublicRead, |work| {
Ok(run(work))
})
.expect("fixture preparation fits a production request")
}