use std::any::Any;
use std::collections::BTreeMap;
use std::sync::Arc;
use std::time::Instant;
use lora_compiler::plan_tree_from_compiled;
use lora_executor::{
classify_stream, collect_compiled, plan_result_columns, CollectorGuard, LoraValue,
MetricsCollector, StreamShape,
};
use lora_store::{GraphStorage, GraphStorageMut};
use crate::database::Database;
use crate::error::LoraError;
use crate::explain::{OperatorMetrics, PlanShape, ProfileMetrics, QueryPlan, QueryProfile};
impl<S> Database<S>
where
S: GraphStorage + GraphStorageMut + Any + Clone + Send + Sync + 'static,
{
pub fn profile(
&self,
query: &str,
params: Option<BTreeMap<String, LoraValue>>,
) -> Result<QueryProfile, LoraError> {
let params = params.unwrap_or_default();
let (store, store_epoch) = self.read_store_with_epoch_deadline(None)?;
let compiled = self
.compile_query_cached(query, &*store, store_epoch)
.map_err(LoraError::from_anyhow)?;
let tree = plan_tree_from_compiled(&compiled);
let shape: PlanShape = classify_stream(&compiled).into();
let result_columns = plan_result_columns(&compiled.physical);
drop(store);
let plan = QueryPlan {
query: query.to_string(),
tree,
shape,
result_columns,
};
let collector = Arc::new(MetricsCollector::new());
let _guard = CollectorGuard::install(collector.clone());
let started = Instant::now();
let rows = if matches!(classify_stream(&compiled), StreamShape::ReadOnly) {
let snapshot = self.read_store();
let res = collect_compiled(&*snapshot, params, &compiled)
.map_err(|e| LoraError::from_anyhow(e.into()));
drop(snapshot);
res?
} else {
self.execute_rows_with_params(query, params)
.map_err(|e| LoraError::from_anyhow(e.into()))?
};
let total_elapsed_ns = started.elapsed().as_nanos() as u64;
drop(_guard);
let per_operator = collector
.snapshot()
.into_iter()
.map(|(id, op)| {
(
id,
OperatorMetrics {
rows: op.rows,
elapsed_ns: op.elapsed_ns,
next_calls: op.next_calls,
db_hits: 0,
},
)
})
.collect();
let metrics = ProfileMetrics {
total_elapsed_ns,
total_rows: rows.len() as u64,
mutated: shape.is_mutating(),
per_operator,
};
Ok(QueryProfile { plan, metrics })
}
}