Skip to main content

lora_database/database/
explain.rs

1//! `Database::explain` — plan-only query inspection.
2//!
3//! `explain` parses, analyzes, and compiles a query, then returns the
4//! resulting [`QueryPlan`]. The executor is *never* invoked, so calling
5//! `explain` on a mutating query (CREATE / MERGE / SET / DELETE / REMOVE)
6//! reports the plan without producing any side effects.
7//!
8//! `params` is accepted for API symmetry with `execute()` and reserved
9//! for a future cost model — v1 does not consult parameter values when
10//! producing the plan tree.
11
12use std::any::Any;
13use std::collections::BTreeMap;
14
15use lora_compiler::plan_tree_from_compiled;
16use lora_executor::{classify_stream, plan_result_columns, LoraValue};
17use lora_store::{GraphStorage, GraphStorageMut};
18
19use crate::database::Database;
20use crate::error::LoraError;
21use crate::explain::{PlanShape, QueryPlan};
22
23impl<S> Database<S>
24where
25    S: GraphStorage + GraphStorageMut + Any + Clone + Send + Sync + 'static,
26{
27    /// Compile `query` and return the plan that *would* run.
28    ///
29    /// The executor is not invoked: `explain` is a pure planning call
30    /// and never produces side effects. Mutating queries return their
31    /// plan without touching the graph.
32    ///
33    /// `params` is accepted for symmetry with `execute()`. The returned
34    /// plan is identical regardless of the parameter values today;
35    /// future cost-model work may use parameter values for selectivity
36    /// estimation, so callers should pass real values when they have
37    /// them.
38    pub fn explain(
39        &self,
40        query: &str,
41        _params: Option<BTreeMap<String, LoraValue>>,
42    ) -> Result<QueryPlan, LoraError> {
43        let store = self.read_store();
44        let compiled = self
45            .compile_query_cached(query, &*store)
46            .map_err(LoraError::from_anyhow)?;
47        let tree = plan_tree_from_compiled(&compiled);
48        let shape: PlanShape = classify_stream(&compiled).into();
49        let result_columns = plan_result_columns(&compiled.physical);
50        Ok(QueryPlan {
51            query: query.to_string(),
52            tree,
53            shape,
54            result_columns,
55        })
56    }
57}