Skip to main content

linkrs_query/context/
query_request_context.rs

1//! Query Request Context – A Simplified Version Dedicated to the Query Layer
2//!
3//! This module provides the minimum amount of contextual information required to execute the query, thereby avoiding the need for the query layer to rely on the API layer.
4
5use crate::storage::QueryStorage;
6use crate::storage::StorageOperationContext;
7use linkrs_core::Value;
8use parking_lot::RwLock;
9use std::collections::HashMap;
10use std::sync::Arc;
11
12/// Query request context – Simplified version
13///
14/// Contains:
15/// - Session ID
16/// - User name
17/// - Graph Space Name
18/// - Query string
19/// - Query parameters
20/// - Optional caller-assigned query ID (threaded to the runtime)
21#[derive(Debug, Clone, Default)]
22pub struct QueryRequestContext {
23    /// Session ID
24    pub session_id: Option<i64>,
25    /// Username
26    pub user_name: Option<String>,
27    /// Name of the graph space
28    pub space_name: Option<String>,
29    /// Query string
30    pub query: String,
31    /// Query parameters
32    pub parameters: HashMap<String, Value>,
33    /// Session variable snapshot (`$name` references), captured once per
34    /// statement at the API layer. Distinct from `parameters` (`@name`).
35    pub session_variables: HashMap<String, Value>,
36    /// Optional caller/server-assigned query ID.
37    ///
38    /// When set, the execution runtime is registered and identified under
39    /// this ID; when absent, the [`QueryRegistry`] allocates a unique id.
40    /// [`QueryRegistry`]: crate::executor::streaming::query_registry::QueryRegistry
41    pub query_id: Option<u64>,
42    /// Transaction identity and storage binding for this execution.
43    pub transaction_id: Option<linkrs_core::types::TransactionId>,
44    pub auto_commit: bool,
45    pub read_only: bool,
46    /// Transaction isolation level injected by the API layer for queries
47    /// running inside an explicit transaction. `None` = auto-commit
48    /// statement-level snapshot semantics.
49    pub isolation_level: Option<linkrs_core::types::TransactionIsolationLevel>,
50    pub operation_context: Option<StorageOperationContext>,
51    pub operation_storage: Option<Arc<RwLock<dyn QueryStorage>>>,
52    /// Pre-parsed statement AST supplied by the API layer.
53    ///
54    /// When present, [`prepare_request`] skips the internal parse of `query`
55    /// and uses this AST directly (the classification pass already produced
56    /// it). The AST carries its own expression analysis context, so all
57    /// expression ids remain consistent with the generated plan.
58    ///
59    /// [`prepare_request`]: crate::pipeline::prepared::PreparedRequest
60    pub parsed_statement: Option<Arc<crate::parser::ast::stmt::Ast>>,
61    /// Consistency requirement for secondary-index reads (vector/fulltext).
62    /// `None` = eventual; `Some(cfg)` = read-your-writes with timeout and optional LSN.
63    pub ryw_config: Option<linkrs_core::types::ReadYourWritesConfig>,
64}
65
66impl QueryRequestContext {
67    /// Create a new query request context.
68    pub fn new(query: String) -> Self {
69        Self {
70            session_id: None,
71            user_name: None,
72            space_name: None,
73            query,
74            parameters: HashMap::new(),
75            session_variables: HashMap::new(),
76            query_id: None,
77            transaction_id: None,
78            auto_commit: true,
79            read_only: false,
80            isolation_level: None,
81            operation_context: None,
82            operation_storage: None,
83            parsed_statement: None,
84            ryw_config: None,
85        }
86    }
87
88    /// Create a query request context with parameters
89    pub fn with_parameters(mut self, parameters: HashMap<String, Value>) -> Self {
90        self.parameters = parameters;
91        self
92    }
93
94    /// Set the session variable snapshot.
95    pub fn with_session_variables(mut self, variables: HashMap<String, Value>) -> Self {
96        self.session_variables = variables;
97        self
98    }
99
100    /// Setting the session ID
101    pub fn with_session_id(mut self, session_id: i64) -> Self {
102        self.session_id = Some(session_id);
103        self
104    }
105
106    /// Set the username
107    pub fn with_user_name(mut self, user_name: String) -> Self {
108        self.user_name = Some(user_name);
109        self
110    }
111
112    /// Set the name of the graph space.
113    pub fn with_space_name(mut self, space_name: String) -> Self {
114        self.space_name = Some(space_name);
115        self
116    }
117
118    /// Set the caller/server-assigned query ID.
119    pub fn with_query_id(mut self, query_id: u64) -> Self {
120        self.query_id = Some(query_id);
121        self
122    }
123
124    /// Obtain parameters
125    pub fn get_parameter(&self, param: &str) -> Option<Value> {
126        self.parameters.get(param).cloned()
127    }
128
129    /// Check whether the parameters exist.
130    pub fn has_parameter(&self, param: &str) -> bool {
131        self.parameters.contains_key(param)
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use super::*;
138
139    #[test]
140    fn test_query_request_context_new() {
141        let ctx = QueryRequestContext::new("MATCH (n) RETURN n".to_string());
142        assert_eq!(ctx.query, "MATCH (n) RETURN n");
143        assert!(ctx.session_id.is_none());
144        assert!(ctx.space_name.is_none());
145    }
146
147    #[test]
148    fn test_query_request_context_with_params() {
149        let mut params = HashMap::new();
150        params.insert("name".into(), Value::from("test"));
151
152        let ctx = QueryRequestContext::new("QUERY".to_string())
153            .with_parameters(params)
154            .with_session_id(123)
155            .with_space_name("test_space".to_string());
156
157        assert_eq!(ctx.session_id, Some(123));
158        assert_eq!(ctx.space_name, Some("test_space".to_string()));
159        assert!(ctx.has_parameter("name"));
160    }
161}