Skip to main content

grafeo_engine/database/
query.rs

1//! Query execution methods for GrafeoDB.
2
3use grafeo_common::utils::error::Result;
4
5use super::{FromValue, QueryResult};
6
7impl super::GrafeoDB {
8    /// Executes a closure with a one-shot session, syncing graph context back
9    /// to the database afterward. This ensures `USE GRAPH`, `SESSION SET GRAPH`,
10    /// and `SESSION RESET` persist across one-shot `execute()` calls.
11    fn with_session<F>(&self, func: F) -> Result<QueryResult>
12    where
13        F: FnOnce(&crate::session::Session) -> Result<QueryResult>,
14    {
15        let session = self.session();
16        let result = func(&session);
17        // Sync graph and schema state back, even on error (the session command may
18        // have succeeded before a subsequent query failed in the same session).
19        *self.current_graph.write() = session.current_graph();
20        *self.current_schema.write() = session.current_schema();
21        result
22    }
23
24    /// Runs a query directly on the database.
25    ///
26    /// A convenience method that creates a temporary session behind the
27    /// scenes. If you're running multiple queries, grab a
28    /// [`session()`](Self::session) instead to avoid the overhead.
29    ///
30    /// Graph context commands (`USE GRAPH`, `SESSION SET GRAPH`, `SESSION RESET`)
31    /// persist across calls: running `execute("USE GRAPH analytics")` followed
32    /// by `execute("MATCH (n) RETURN n")` routes the second query to the
33    /// analytics graph.
34    ///
35    /// # Errors
36    ///
37    /// Returns an error if parsing or execution fails.
38    pub fn execute(&self, query: &str) -> Result<QueryResult> {
39        self.with_session(|s| s.execute(query))
40    }
41
42    /// Runs a read-only GQL query and returns a lazy result stream.
43    ///
44    /// The stream pulls chunks from the operator pipeline on demand, so
45    /// memory usage is bounded regardless of result-set size. Rejects
46    /// mutations, EXPLAIN/PROFILE, schema/session commands, and queries
47    /// whose planner emits a push-based pipeline (ORDER BY, aggregate,
48    /// DISTINCT, etc.). Use [`execute`](Self::execute) for those.
49    ///
50    /// # Stability: Experimental
51    ///
52    /// New in 0.5.40. Signature may change before being promoted to Beta.
53    ///
54    /// # Errors
55    ///
56    /// Returns an error if parsing or planning fails, or if the query is a
57    /// kind that cannot be streamed.
58    #[cfg(all(feature = "gql", feature = "lpg"))]
59    pub fn execute_streaming(
60        &self,
61        query: &str,
62    ) -> Result<crate::query::executor::stream::OwnedResultStream> {
63        use crate::query::executor::stream::OwnedResultStream;
64        let session = self.session();
65        let (source, columns, deadline) = session.build_streaming_plan(query)?;
66        // Sync graph/schema state back (parser may have changed the session's
67        // view, although streaming rejects session commands so this is a
68        // defensive no-op today).
69        *self.current_graph.write() = session.current_graph();
70        *self.current_schema.write() = session.current_schema();
71        Ok(OwnedResultStream::new(source, columns, deadline))
72    }
73
74    /// Executes a GQL query with visibility at the specified epoch.
75    ///
76    /// This enables time-travel queries: the query sees the database
77    /// as it existed at the given epoch.
78    ///
79    /// # Errors
80    ///
81    /// Returns an error if parsing or execution fails.
82    #[cfg(feature = "gql")]
83    pub fn execute_at_epoch(
84        &self,
85        query: &str,
86        epoch: grafeo_common::types::EpochId,
87    ) -> Result<QueryResult> {
88        self.with_session(|s| s.execute_at_epoch(query, epoch))
89    }
90
91    /// Executes a query with parameters and returns the result.
92    ///
93    /// # Errors
94    ///
95    /// Returns an error if the query fails.
96    pub fn execute_with_params(
97        &self,
98        query: &str,
99        params: std::collections::HashMap<String, grafeo_common::types::Value>,
100    ) -> Result<QueryResult> {
101        self.with_session(|s| s.execute_with_params(query, params))
102    }
103
104    /// Executes a Cypher query and returns the result.
105    ///
106    /// # Errors
107    ///
108    /// Returns an error if the query fails.
109    #[cfg(feature = "cypher")]
110    pub fn execute_cypher(&self, query: &str) -> Result<QueryResult> {
111        self.with_session(|s| s.execute_cypher(query))
112    }
113
114    /// Executes a Cypher query with parameters and returns the result.
115    ///
116    /// # Errors
117    ///
118    /// Returns an error if the query fails.
119    #[cfg(feature = "cypher")]
120    pub fn execute_cypher_with_params(
121        &self,
122        query: &str,
123        params: std::collections::HashMap<String, grafeo_common::types::Value>,
124    ) -> Result<QueryResult> {
125        self.with_session(|s| s.execute_language(query, "cypher", Some(params)))
126    }
127
128    /// Executes a Gremlin query and returns the result.
129    ///
130    /// # Errors
131    ///
132    /// Returns an error if the query fails.
133    #[cfg(feature = "gremlin")]
134    pub fn execute_gremlin(&self, query: &str) -> Result<QueryResult> {
135        self.with_session(|s| s.execute_gremlin(query))
136    }
137
138    /// Executes a Gremlin query with parameters and returns the result.
139    ///
140    /// # Errors
141    ///
142    /// Returns an error if the query fails.
143    #[cfg(feature = "gremlin")]
144    pub fn execute_gremlin_with_params(
145        &self,
146        query: &str,
147        params: std::collections::HashMap<String, grafeo_common::types::Value>,
148    ) -> Result<QueryResult> {
149        self.with_session(|s| s.execute_gremlin_with_params(query, params))
150    }
151
152    /// Executes a GraphQL query and returns the result.
153    ///
154    /// # Errors
155    ///
156    /// Returns an error if the query fails.
157    #[cfg(feature = "graphql")]
158    pub fn execute_graphql(&self, query: &str) -> Result<QueryResult> {
159        self.with_session(|s| s.execute_graphql(query))
160    }
161
162    /// Executes a GraphQL query with parameters and returns the result.
163    ///
164    /// # Errors
165    ///
166    /// Returns an error if the query fails.
167    #[cfg(feature = "graphql")]
168    pub fn execute_graphql_with_params(
169        &self,
170        query: &str,
171        params: std::collections::HashMap<String, grafeo_common::types::Value>,
172    ) -> Result<QueryResult> {
173        self.with_session(|s| s.execute_graphql_with_params(query, params))
174    }
175
176    /// Executes a SQL/PGQ query (SQL:2023 GRAPH_TABLE) and returns the result.
177    ///
178    /// # Errors
179    ///
180    /// Returns an error if the query fails.
181    #[cfg(feature = "sql-pgq")]
182    pub fn execute_sql(&self, query: &str) -> Result<QueryResult> {
183        self.with_session(|s| s.execute_sql(query))
184    }
185
186    /// Executes a SQL/PGQ query with parameters and returns the result.
187    ///
188    /// # Errors
189    ///
190    /// Returns an error if the query fails.
191    #[cfg(feature = "sql-pgq")]
192    pub fn execute_sql_with_params(
193        &self,
194        query: &str,
195        params: std::collections::HashMap<String, grafeo_common::types::Value>,
196    ) -> Result<QueryResult> {
197        self.with_session(|s| s.execute_sql_with_params(query, params))
198    }
199
200    /// Executes a query in the specified language by name.
201    ///
202    /// Supported language names: `"gql"`, `"cypher"`, `"gremlin"`, `"graphql"`,
203    /// `"sparql"`, `"sql"`. Each requires the corresponding feature flag.
204    ///
205    /// # Errors
206    ///
207    /// Returns an error if the language is unknown/disabled, or if the query
208    /// fails.
209    pub fn execute_language(
210        &self,
211        query: &str,
212        language: &str,
213        params: Option<std::collections::HashMap<String, grafeo_common::types::Value>>,
214    ) -> Result<QueryResult> {
215        self.with_session(|s| s.execute_language(query, language, params))
216    }
217
218    /// Executes a query and returns a single scalar value.
219    ///
220    /// # Errors
221    ///
222    /// Returns an error if the query fails or doesn't return exactly one row.
223    pub fn query_scalar<T: FromValue>(&self, query: &str) -> Result<T> {
224        let result = self.execute(query)?;
225        result.scalar()
226    }
227}