hawdb_optimizer/
context.rs1use crate::OptimizerConfig;
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum QueryFamily {
19 GraphRead,
20 GraphWrite,
21 VectorSearch,
22 Schema,
23 System,
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum StatementClass {
28 Read,
29 Write,
30}
31
32#[derive(Debug, Clone, Copy, PartialEq, Eq)]
33pub enum ExplainMode {
34 None,
35 Explain,
36 Analyze,
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub enum TraceSink {
41 Disabled,
42 Collect,
43}
44
45#[derive(Debug, Clone, PartialEq, Eq)]
46pub struct ResourceHints {
47 pub priority: u8,
48 pub max_memory_bytes: Option<u64>,
49 pub max_parallelism: usize,
50}
51
52impl Default for ResourceHints {
53 fn default() -> Self {
54 Self {
55 priority: 128,
56 max_memory_bytes: None,
57 max_parallelism: 1,
58 }
59 }
60}
61
62#[derive(Debug, Clone, PartialEq, Eq)]
63pub struct OptimizerContext {
64 normalized_query: Option<String>,
65 query_digest: Option<String>,
66 query_family: QueryFamily,
67 statement_class: StatementClass,
68 resource_hints: ResourceHints,
69 explain_mode: ExplainMode,
70 trace_sink: TraceSink,
71 optimizer_config: OptimizerConfig,
72}
73
74impl Default for OptimizerContext {
75 fn default() -> Self {
76 Self {
77 normalized_query: None,
78 query_digest: None,
79 query_family: QueryFamily::GraphRead,
80 statement_class: StatementClass::Read,
81 resource_hints: ResourceHints::default(),
82 explain_mode: ExplainMode::None,
83 trace_sink: TraceSink::Collect,
84 optimizer_config: OptimizerConfig::default(),
85 }
86 }
87}
88
89impl OptimizerContext {
90 pub fn from_config(optimizer_config: OptimizerConfig) -> Self {
91 Self {
92 optimizer_config,
93 ..Self::default()
94 }
95 }
96
97 pub fn with_normalized_query(mut self, normalized_query: impl Into<String>) -> Self {
98 self.normalized_query = Some(normalized_query.into());
99 self
100 }
101
102 pub fn with_query_identity(
103 mut self,
104 normalized_query: impl Into<String>,
105 query_digest: impl Into<String>,
106 ) -> Self {
107 self.normalized_query = Some(normalized_query.into());
108 self.query_digest = Some(query_digest.into());
109 self
110 }
111
112 pub fn with_query_family(mut self, query_family: QueryFamily) -> Self {
113 self.query_family = query_family;
114 self.statement_class = match query_family {
115 QueryFamily::GraphWrite | QueryFamily::Schema => StatementClass::Write,
116 QueryFamily::GraphRead | QueryFamily::VectorSearch | QueryFamily::System => {
117 StatementClass::Read
118 }
119 };
120 self
121 }
122
123 pub fn with_resource_hints(mut self, resource_hints: ResourceHints) -> Self {
124 self.resource_hints = resource_hints;
125 self
126 }
127
128 pub fn with_explain_mode(mut self, explain_mode: ExplainMode) -> Self {
129 self.explain_mode = explain_mode;
130 self
131 }
132
133 pub fn with_trace_sink(mut self, trace_sink: TraceSink) -> Self {
134 self.trace_sink = trace_sink;
135 self
136 }
137
138 pub fn normalized_query(&self) -> Option<&str> {
139 self.normalized_query.as_deref()
140 }
141
142 pub fn query_digest(&self) -> Option<&str> {
143 self.query_digest.as_deref()
144 }
145
146 pub fn query_family(&self) -> QueryFamily {
147 self.query_family
148 }
149
150 pub fn statement_class(&self) -> StatementClass {
151 self.statement_class
152 }
153
154 pub fn resource_hints(&self) -> &ResourceHints {
155 &self.resource_hints
156 }
157
158 pub fn explain_mode(&self) -> ExplainMode {
159 self.explain_mode
160 }
161
162 pub fn trace_sink(&self) -> TraceSink {
163 self.trace_sink
164 }
165
166 pub fn optimizer_config(&self) -> &OptimizerConfig {
167 &self.optimizer_config
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174
175 #[test]
176 fn query_family_derives_statement_class_with_query_identity() {
177 let context = OptimizerContext::default()
178 .with_query_family(QueryFamily::VectorSearch)
179 .with_query_identity("match (m) return m", "q1:test");
180
181 assert_eq!(context.statement_class(), StatementClass::Read);
182 assert_eq!(context.query_digest(), Some("q1:test"));
183 assert_eq!(context.normalized_query(), Some("match (m) return m"));
184 }
185
186 #[test]
187 fn resource_hints_are_bounded_by_explicit_parallelism() {
188 let context = OptimizerContext::default().with_resource_hints(ResourceHints {
189 priority: 200,
190 max_memory_bytes: Some(16 * 1024 * 1024),
191 max_parallelism: 2,
192 });
193
194 assert_eq!(context.resource_hints().max_parallelism, 2);
195 assert_eq!(
196 context.resource_hints().max_memory_bytes,
197 Some(16 * 1024 * 1024)
198 );
199 }
200}