Skip to main content

grafeo_engine/database/
query.rs

1//! Query execution methods for GrafeoDB.
2
3use grafeo_common::utils::error::Result;
4
5#[cfg(feature = "rdf")]
6use grafeo_core::graph::rdf::RdfStore;
7#[cfg(feature = "rdf")]
8use std::sync::Arc;
9
10use super::{FromValue, QueryResult};
11
12impl super::GrafeoDB {
13    /// Executes a closure with a one-shot session, syncing graph context back
14    /// to the database afterward. This ensures `USE GRAPH`, `SESSION SET GRAPH`,
15    /// and `SESSION RESET` persist across one-shot `execute()` calls.
16    fn with_session<F>(&self, func: F) -> Result<QueryResult>
17    where
18        F: FnOnce(&crate::session::Session) -> Result<QueryResult>,
19    {
20        let session = self.session();
21        let result = func(&session);
22        // Sync graph state back, even on error (USE GRAPH may have succeeded
23        // before a subsequent query failed in the same session).
24        *self.current_graph.write() = session.current_graph();
25        result
26    }
27
28    /// Runs a query directly on the database.
29    ///
30    /// A convenience method that creates a temporary session behind the
31    /// scenes. If you're running multiple queries, grab a
32    /// [`session()`](Self::session) instead to avoid the overhead.
33    ///
34    /// Graph context commands (`USE GRAPH`, `SESSION SET GRAPH`, `SESSION RESET`)
35    /// persist across calls: running `execute("USE GRAPH analytics")` followed
36    /// by `execute("MATCH (n) RETURN n")` routes the second query to the
37    /// analytics graph.
38    ///
39    /// # Errors
40    ///
41    /// Returns an error if parsing or execution fails.
42    pub fn execute(&self, query: &str) -> Result<QueryResult> {
43        self.with_session(|s| s.execute(query))
44    }
45
46    /// Executes a GQL query with visibility at the specified epoch.
47    ///
48    /// This enables time-travel queries: the query sees the database
49    /// as it existed at the given epoch.
50    ///
51    /// # Errors
52    ///
53    /// Returns an error if parsing or execution fails.
54    pub fn execute_at_epoch(
55        &self,
56        query: &str,
57        epoch: grafeo_common::types::EpochId,
58    ) -> Result<QueryResult> {
59        self.with_session(|s| s.execute_at_epoch(query, epoch))
60    }
61
62    /// Executes a query with parameters and returns the result.
63    ///
64    /// # Errors
65    ///
66    /// Returns an error if the query fails.
67    pub fn execute_with_params(
68        &self,
69        query: &str,
70        params: std::collections::HashMap<String, grafeo_common::types::Value>,
71    ) -> Result<QueryResult> {
72        self.with_session(|s| s.execute_with_params(query, params))
73    }
74
75    /// Executes a Cypher query and returns the result.
76    ///
77    /// # Errors
78    ///
79    /// Returns an error if the query fails.
80    #[cfg(feature = "cypher")]
81    pub fn execute_cypher(&self, query: &str) -> Result<QueryResult> {
82        self.with_session(|s| s.execute_cypher(query))
83    }
84
85    /// Executes a Cypher query with parameters and returns the result.
86    ///
87    /// # Errors
88    ///
89    /// Returns an error if the query fails.
90    #[cfg(feature = "cypher")]
91    pub fn execute_cypher_with_params(
92        &self,
93        query: &str,
94        params: std::collections::HashMap<String, grafeo_common::types::Value>,
95    ) -> Result<QueryResult> {
96        self.with_session(|s| s.execute_language(query, "cypher", Some(params)))
97    }
98
99    /// Executes a Gremlin query and returns the result.
100    ///
101    /// # Errors
102    ///
103    /// Returns an error if the query fails.
104    #[cfg(feature = "gremlin")]
105    pub fn execute_gremlin(&self, query: &str) -> Result<QueryResult> {
106        self.with_session(|s| s.execute_gremlin(query))
107    }
108
109    /// Executes a Gremlin query with parameters and returns the result.
110    ///
111    /// # Errors
112    ///
113    /// Returns an error if the query fails.
114    #[cfg(feature = "gremlin")]
115    pub fn execute_gremlin_with_params(
116        &self,
117        query: &str,
118        params: std::collections::HashMap<String, grafeo_common::types::Value>,
119    ) -> Result<QueryResult> {
120        self.with_session(|s| s.execute_gremlin_with_params(query, params))
121    }
122
123    /// Executes a GraphQL query and returns the result.
124    ///
125    /// # Errors
126    ///
127    /// Returns an error if the query fails.
128    #[cfg(feature = "graphql")]
129    pub fn execute_graphql(&self, query: &str) -> Result<QueryResult> {
130        self.with_session(|s| s.execute_graphql(query))
131    }
132
133    /// Executes a GraphQL query with parameters and returns the result.
134    ///
135    /// # Errors
136    ///
137    /// Returns an error if the query fails.
138    #[cfg(feature = "graphql")]
139    pub fn execute_graphql_with_params(
140        &self,
141        query: &str,
142        params: std::collections::HashMap<String, grafeo_common::types::Value>,
143    ) -> Result<QueryResult> {
144        self.with_session(|s| s.execute_graphql_with_params(query, params))
145    }
146
147    /// Executes a SQL/PGQ query (SQL:2023 GRAPH_TABLE) and returns the result.
148    ///
149    /// # Errors
150    ///
151    /// Returns an error if the query fails.
152    #[cfg(feature = "sql-pgq")]
153    pub fn execute_sql(&self, query: &str) -> Result<QueryResult> {
154        self.with_session(|s| s.execute_sql(query))
155    }
156
157    /// Executes a SQL/PGQ query with parameters and returns the result.
158    ///
159    /// # Errors
160    ///
161    /// Returns an error if the query fails.
162    #[cfg(feature = "sql-pgq")]
163    pub fn execute_sql_with_params(
164        &self,
165        query: &str,
166        params: std::collections::HashMap<String, grafeo_common::types::Value>,
167    ) -> Result<QueryResult> {
168        self.with_session(|s| s.execute_sql_with_params(query, params))
169    }
170
171    /// Executes a SPARQL query and returns the result.
172    ///
173    /// SPARQL queries operate on the RDF triple store.
174    ///
175    /// # Errors
176    ///
177    /// Returns an error if the query fails.
178    ///
179    /// # Examples
180    ///
181    /// ```no_run
182    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
183    /// use grafeo_engine::GrafeoDB;
184    ///
185    /// let db = GrafeoDB::new_in_memory();
186    /// let result = db.execute_sparql("SELECT ?s ?p ?o WHERE { ?s ?p ?o }")?;
187    /// # Ok(())
188    /// # }
189    /// ```
190    #[cfg(all(feature = "sparql", feature = "rdf"))]
191    pub fn execute_sparql(&self, query: &str) -> Result<QueryResult> {
192        use crate::query::{
193            Executor, optimizer::Optimizer, planner::rdf::RdfPlanner, translators::sparql,
194        };
195
196        // Parse and translate the SPARQL query to a logical plan
197        let logical_plan = sparql::translate(query)?;
198
199        // Optimize the plan
200        let optimizer = Optimizer::from_store(&self.store);
201        let optimized_plan = optimizer.optimize(logical_plan)?;
202
203        // Convert to physical plan using RDF planner
204        let planner = RdfPlanner::new(Arc::clone(&self.rdf_store));
205        #[cfg(feature = "wal")]
206        let planner = planner.with_wal(self.wal.as_ref().map(Arc::clone));
207        let mut physical_plan = planner.plan(&optimized_plan)?;
208
209        // Execute the plan
210        let executor = Executor::with_columns(physical_plan.columns.clone());
211        executor.execute(physical_plan.operator.as_mut())
212    }
213
214    /// Executes a query in the specified language by name.
215    ///
216    /// Supported language names: `"gql"`, `"cypher"`, `"gremlin"`, `"graphql"`,
217    /// `"sparql"`, `"sql"`. Each requires the corresponding feature flag.
218    ///
219    /// # Errors
220    ///
221    /// Returns an error if the language is unknown/disabled, or if the query
222    /// fails.
223    pub fn execute_language(
224        &self,
225        query: &str,
226        language: &str,
227        params: Option<std::collections::HashMap<String, grafeo_common::types::Value>>,
228    ) -> Result<QueryResult> {
229        self.with_session(|s| s.execute_language(query, language, params))
230    }
231
232    /// Returns the RDF store.
233    ///
234    /// This provides direct access to the RDF store for triple operations.
235    #[cfg(feature = "rdf")]
236    #[must_use]
237    pub fn rdf_store(&self) -> &Arc<RdfStore> {
238        &self.rdf_store
239    }
240
241    /// Executes a query and returns a single scalar value.
242    ///
243    /// # Errors
244    ///
245    /// Returns an error if the query fails or doesn't return exactly one row.
246    pub fn query_scalar<T: FromValue>(&self, query: &str) -> Result<T> {
247        let result = self.execute(query)?;
248        result.scalar()
249    }
250}