use crate::EngineError;
use crate::eval;
use alloc::string::String;
use alloc::vec::Vec;
use spg_storage::{Table, Value};
pub(crate) struct ExprKeyPlan {
exprs: Vec<Option<spg_sql::ast::Expr>>,
}
impl ExprKeyPlan {
pub(crate) fn for_table(table: &Table) -> Result<Option<Self>, EngineError> {
if !table.indices().iter().any(|i| i.expression.is_some()) {
return Ok(None);
}
let mut exprs = Vec::with_capacity(table.indices().len());
for idx in table.indices() {
let parsed = match &idx.expression {
Some(src) => Some(spg_sql::parser::parse_expression(src).map_err(|e| {
EngineError::Unsupported(alloc::format!(
"index {:?} expression {src:?} failed to re-parse: {e:?}",
idx.name
))
})?),
None => None,
};
exprs.push(parsed);
}
Ok(Some(Self { exprs }))
}
pub(crate) fn keys_for(
&self,
values: &[Value<'static>],
ctx: &eval::EvalContext<'_>,
) -> Result<Vec<Option<Value<'static>>>, EngineError> {
let row = spg_storage::Row {
values: values.to_vec(),
};
let mut out = Vec::with_capacity(self.exprs.len());
for expr in &self.exprs {
out.push(match expr {
Some(e) => {
let v = eval::eval_expr(e, &row, ctx)
.map_err(|e| EngineError::Unsupported(alloc::format!("{e:?}")))?;
(!v.is_null()).then_some(v)
}
None => None,
});
}
Ok(out)
}
}
pub(crate) fn refresh(table: &mut Table) -> Result<(), EngineError> {
let stale = table.stale_expression_indices();
if stale.is_empty() {
return Ok(());
}
let schema = table.schema().clone();
let ctx = eval::EvalContext::new(&schema.columns, None);
for (name, src) in stale {
let expr = spg_sql::parser::parse_expression(&src).map_err(|e| {
EngineError::Unsupported(alloc::format!(
"index {name:?} expression {src:?} failed to re-parse: {e:?}"
))
})?;
let mut keys: Vec<Option<Value<'static>>> = Vec::with_capacity(table.stored_row_count());
for i in 0..table.stored_row_count() {
let Some(values) = table.row_values_at(i) else {
return Ok(());
};
let row = spg_storage::Row {
values: values.to_vec(),
};
let v = eval::eval_expr(&expr, &row, &ctx)
.map_err(|e| EngineError::Unsupported(alloc::format!("{e:?}")))?;
keys.push((!v.is_null()).then_some(v));
}
table
.rebuild_expression_index(&name, &keys)
.map_err(EngineError::Storage)?;
}
Ok(())
}
pub(crate) fn index_for_expression(table: &Table, expr: &spg_sql::ast::Expr) -> Option<String> {
let wanted = alloc::format!("{expr}");
table
.indices()
.iter()
.find(|i| {
i.expression.as_deref() == Some(wanted.as_str())
&& table.expr_index_is_complete(&i.name)
})
.map(|i| i.name.clone())
}
pub(crate) fn refresh_named(
engine_catalog: &mut spg_storage::Catalog,
name: &str,
) -> Result<(), EngineError> {
let Some(table) = engine_catalog.get_mut(name) else {
return Ok(());
};
if table.stale_expression_indices().is_empty() {
return Ok(());
}
refresh(table)
}
pub(crate) fn rebuild_all(cat: &mut spg_storage::Catalog) {
let names: Vec<alloc::string::String> = cat
.table_names()
.into_iter()
.filter(|n| {
cat.get(n)
.is_some_and(|t| !t.stale_expression_indices().is_empty())
})
.collect();
for name in names {
if let Some(table) = cat.get_mut(&name) {
let _ = refresh(table);
}
}
}