use super::*;
pub(super) fn function<'a>(
function: hawdb_core::TemporalFunction,
arguments: &'a [ProjectionExpression],
evaluate: impl Fn(&'a ProjectionExpression) -> Result<ProjectedValue<'a>>,
) -> Result<ProjectedValue<'a>> {
let mut values = Vec::with_capacity(arguments.len());
for argument in arguments {
let value = evaluate(argument)?;
if value.as_ref() == &Value::Null {
return Ok(ProjectedValue::scalar(Value::Null));
}
values.push(value);
}
let owned = values
.into_iter()
.map(ProjectedValue::into_owned)
.collect::<Vec<_>>();
hawdb_core::temporal::evaluate(function, &owned, hawdb_core::temporal::statement_now())
.map(ProjectedValue::scalar)
.map_err(|error| HawDBError::Execution(error.to_string()))
}
pub(super) fn property<'a>(
value: ProjectedValue<'a>,
key: &str,
account: Option<&QueryMemoryAccount>,
) -> Result<ProjectedValue<'a>> {
let ProjectedValue { value, _allocation } = value;
match value {
Cow::Borrowed(Value::Map(values)) => Ok(values
.get(key)
.map(ProjectedValue::borrowed)
.unwrap_or_else(|| ProjectedValue::scalar(Value::Null))),
Cow::Owned(Value::Map(mut values)) => Ok(match values.remove(key) {
Some(value) => ProjectedValue {
value: Cow::Owned(value),
_allocation,
},
None => ProjectedValue::scalar(Value::Null),
}),
value => scalar_property(&value, key, account).map(ProjectedValue::scalar),
}
}
pub(super) fn scalar_property(
value: &Value,
key: &str,
account: Option<&QueryMemoryAccount>,
) -> Result<Value> {
match value {
Value::Null => Ok(Value::Null),
value if hawdb_core::temporal::is_temporal(value) => {
hawdb_core::temporal::component(value, key)
.map_err(|error| HawDBError::Execution(error.to_string()))
}
value => Err(HawDBError::Execution(format!(
"property access '.{key}' requires a map or temporal value, got {:?}",
description(value, account)
))),
}
}
pub(super) fn map_literal<'a>(
entries: &'a [(String, ProjectionExpression)],
evaluate: impl Fn(&'a ProjectionExpression) -> Result<ProjectedValue<'a>>,
account: Option<&QueryMemoryAccount>,
work: &ListWork,
) -> Result<ProjectedValue<'a>> {
let mut map = std::collections::BTreeMap::new();
for (key, value) in entries {
map.insert(key.clone(), evaluate(value)?.into_owned());
}
let bytes = map_memory_bytes(&map).saturating_add(std::mem::size_of::<Value>());
match account {
Some(account) => {
let allocation = account.reserve(bytes)?;
Ok(ProjectedValue {
value: Cow::Owned(Value::Map(map)),
_allocation: Some(allocation),
})
}
None => {
work.charge_unaccounted(bytes)?;
Ok(ProjectedValue::scalar(Value::Map(map)))
}
}
}