1use parking_lot::Mutex;
6use serde::{Deserialize, Serialize};
7use std::collections::{HashMap, VecDeque};
8use std::sync::atomic::{AtomicU64, Ordering};
9
10const ERROR_TYPE_COUNT: usize = 11;
12const QUERY_PHASE_COUNT: usize = 5;
14const MAX_RECENT_ERRORS: usize = 100;
16
17pub fn error_type_to_index(error_type: ErrorType) -> usize {
19 match error_type {
20 ErrorType::ParseError => 0,
21 ErrorType::ValidationError => 1,
22 ErrorType::PlanningError => 2,
23 ErrorType::OptimizationError => 3,
24 ErrorType::ExecutionError => 4,
25 ErrorType::StorageError => 5,
26 ErrorType::TimeoutError => 6,
27 ErrorType::MemoryLimitError => 7,
28 ErrorType::PermissionError => 8,
29 ErrorType::SessionError => 9,
30 ErrorType::OtherError => 10,
31 }
32}
33
34pub fn index_to_error_type(index: usize) -> Option<ErrorType> {
36 match index {
37 0 => Some(ErrorType::ParseError),
38 1 => Some(ErrorType::ValidationError),
39 2 => Some(ErrorType::PlanningError),
40 3 => Some(ErrorType::OptimizationError),
41 4 => Some(ErrorType::ExecutionError),
42 5 => Some(ErrorType::StorageError),
43 6 => Some(ErrorType::TimeoutError),
44 7 => Some(ErrorType::MemoryLimitError),
45 8 => Some(ErrorType::PermissionError),
46 9 => Some(ErrorType::SessionError),
47 10 => Some(ErrorType::OtherError),
48 _ => None,
49 }
50}
51
52pub fn query_phase_to_index(phase: QueryPhase) -> usize {
54 match phase {
55 QueryPhase::Parse => 0,
56 QueryPhase::Validate => 1,
57 QueryPhase::Plan => 2,
58 QueryPhase::Optimize => 3,
59 QueryPhase::Execute => 4,
60 }
61}
62
63pub fn index_to_query_phase(index: usize) -> Option<QueryPhase> {
65 match index {
66 0 => Some(QueryPhase::Parse),
67 1 => Some(QueryPhase::Validate),
68 2 => Some(QueryPhase::Plan),
69 3 => Some(QueryPhase::Optimize),
70 4 => Some(QueryPhase::Execute),
71 _ => None,
72 }
73}
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
77pub enum QueryPhase {
78 Parse,
79 Validate,
80 Plan,
81 Optimize,
82 Execute,
83}
84
85impl std::fmt::Display for QueryPhase {
86 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87 match self {
88 QueryPhase::Parse => write!(f, "parse"),
89 QueryPhase::Validate => write!(f, "validate"),
90 QueryPhase::Plan => write!(f, "plan"),
91 QueryPhase::Optimize => write!(f, "optimize"),
92 QueryPhase::Execute => write!(f, "execute"),
93 }
94 }
95}
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
99pub enum ErrorType {
100 ParseError,
101 ValidationError,
102 PlanningError,
103 OptimizationError,
104 ExecutionError,
105 StorageError,
106 TimeoutError,
107 MemoryLimitError,
108 PermissionError,
109 SessionError,
110 OtherError,
111}
112
113impl std::fmt::Display for ErrorType {
114 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115 match self {
116 ErrorType::ParseError => write!(f, "parse_error"),
117 ErrorType::ValidationError => write!(f, "validation_error"),
118 ErrorType::PlanningError => write!(f, "planning_error"),
119 ErrorType::OptimizationError => write!(f, "optimization_error"),
120 ErrorType::ExecutionError => write!(f, "execution_error"),
121 ErrorType::StorageError => write!(f, "storage_error"),
122 ErrorType::TimeoutError => write!(f, "timeout_error"),
123 ErrorType::MemoryLimitError => write!(f, "memory_limit_error"),
124 ErrorType::PermissionError => write!(f, "permission_error"),
125 ErrorType::SessionError => write!(f, "session_error"),
126 ErrorType::OtherError => write!(f, "other_error"),
127 }
128 }
129}
130
131#[derive(Debug, Clone)]
133pub struct ErrorInfo {
134 pub error_type: ErrorType,
135 pub error_phase: QueryPhase,
136 pub error_message: String,
137 pub error_details: Option<String>,
138}
139
140#[derive(Debug, Clone)]
142pub struct RecentError {
143 pub timestamp: u64,
144 pub error_type: ErrorType,
145 pub error_phase: QueryPhase,
146 pub message: String,
147 pub query_text: Option<String>,
148}
149
150impl RecentError {
151 pub fn new(error_info: &ErrorInfo, query_text: Option<String>) -> Self {
152 Self {
153 timestamp: std::time::SystemTime::now()
154 .duration_since(std::time::UNIX_EPOCH)
155 .unwrap_or_default()
156 .as_secs(),
157 error_type: error_info.error_type,
158 error_phase: error_info.error_phase,
159 message: error_info.error_message.clone(),
160 query_text,
161 }
162 }
163}
164
165impl ErrorInfo {
166 pub fn new(
167 error_type: ErrorType,
168 error_phase: QueryPhase,
169 error_message: impl Into<String>,
170 ) -> Self {
171 Self {
172 error_type,
173 error_phase,
174 error_message: error_message.into(),
175 error_details: None,
176 }
177 }
178
179 pub fn with_details(mut self, details: impl Into<String>) -> Self {
180 self.error_details = Some(details.into());
181 self
182 }
183}
184
185#[derive(Debug, Clone)]
187pub struct ErrorSummary {
188 pub total_errors: u64,
189 pub errors_by_type: HashMap<ErrorType, u64>,
190 pub errors_by_phase: HashMap<QueryPhase, u64>,
191}
192
193#[derive(Debug)]
195pub struct ErrorStatsManager {
196 error_counts: [AtomicU64; ERROR_TYPE_COUNT],
197 error_by_phase: [AtomicU64; QUERY_PHASE_COUNT],
198 recent_errors: Mutex<VecDeque<RecentError>>,
199}
200
201impl ErrorStatsManager {
202 pub fn new() -> Self {
203 Self {
204 error_counts: std::array::from_fn(|_| AtomicU64::new(0)),
205 error_by_phase: std::array::from_fn(|_| AtomicU64::new(0)),
206 recent_errors: Mutex::new(VecDeque::with_capacity(MAX_RECENT_ERRORS)),
207 }
208 }
209
210 pub fn record_error(&self, error_type: ErrorType, phase: QueryPhase) {
211 let index = error_type_to_index(error_type);
212 self.error_counts[index].fetch_add(1, Ordering::Relaxed);
213
214 let phase_index = query_phase_to_index(phase);
215 self.error_by_phase[phase_index].fetch_add(1, Ordering::Relaxed);
216
217 log::warn!("Query error: type={}, phase={}", error_type, phase);
218 }
219
220 pub fn record_error_with_context(&self, error_info: &ErrorInfo, query_text: Option<String>) {
221 self.record_error(error_info.error_type, error_info.error_phase);
222
223 let mut recent = self.recent_errors.lock();
224 let error = RecentError::new(error_info, query_text);
225 recent.push_back(error);
226
227 while recent.len() > MAX_RECENT_ERRORS {
229 recent.pop_front();
230 }
231 }
232
233 pub fn get_error_count(&self, error_type: ErrorType) -> u64 {
234 let index = error_type_to_index(error_type);
235 self.error_counts[index].load(Ordering::Relaxed)
236 }
237
238 pub fn get_error_count_by_phase(&self, phase: QueryPhase) -> u64 {
239 let index = query_phase_to_index(phase);
240 self.error_by_phase[index].load(Ordering::Relaxed)
241 }
242
243 pub fn get_all_error_counts(&self) -> HashMap<ErrorType, u64> {
244 let mut result = HashMap::new();
245 for i in 0..ERROR_TYPE_COUNT {
246 if let Some(error_type) = index_to_error_type(i) {
247 let count = self.error_counts[i].load(Ordering::Relaxed);
248 if count > 0 {
249 result.insert(error_type, count);
250 }
251 }
252 }
253 result
254 }
255
256 pub fn get_all_error_counts_by_phase(&self) -> HashMap<QueryPhase, u64> {
257 let mut result = HashMap::new();
258 for i in 0..QUERY_PHASE_COUNT {
259 if let Some(phase) = index_to_query_phase(i) {
260 let count = self.error_by_phase[i].load(Ordering::Relaxed);
261 if count > 0 {
262 result.insert(phase, count);
263 }
264 }
265 }
266 result
267 }
268
269 pub fn reset_error_counts(&self) {
270 for counter in &self.error_counts {
271 counter.store(0, Ordering::Relaxed);
272 }
273 for counter in &self.error_by_phase {
274 counter.store(0, Ordering::Relaxed);
275 }
276 }
277
278 pub fn get_error_summary(&self) -> ErrorSummary {
279 ErrorSummary {
280 total_errors: self
281 .error_counts
282 .iter()
283 .map(|c| c.load(Ordering::Relaxed))
284 .sum(),
285 errors_by_type: self.get_all_error_counts(),
286 errors_by_phase: self.get_all_error_counts_by_phase(),
287 }
288 }
289
290 pub fn get_recent_errors(&self, limit: usize) -> Vec<RecentError> {
291 let recent = self.recent_errors.lock();
292 recent.iter().rev().take(limit).cloned().collect()
293 }
294}
295
296impl Default for ErrorStatsManager {
297 fn default() -> Self {
298 Self::new()
299 }
300}
301
302#[cfg(test)]
303mod tests {
304 use super::*;
305
306 #[test]
307 fn test_error_stats_recording() {
308 let stats = ErrorStatsManager::new();
309
310 stats.record_error(ErrorType::ParseError, QueryPhase::Parse);
311 stats.record_error(ErrorType::ParseError, QueryPhase::Parse);
312 stats.record_error(ErrorType::ExecutionError, QueryPhase::Execute);
313
314 assert_eq!(stats.get_error_count(ErrorType::ParseError), 2);
315 assert_eq!(stats.get_error_count(ErrorType::ExecutionError), 1);
316 assert_eq!(stats.get_error_count(ErrorType::StorageError), 0);
317
318 assert_eq!(stats.get_error_count_by_phase(QueryPhase::Parse), 2);
319 assert_eq!(stats.get_error_count_by_phase(QueryPhase::Execute), 1);
320 assert_eq!(stats.get_error_count_by_phase(QueryPhase::Validate), 0);
321 }
322
323 #[test]
324 fn test_error_stats_summary() {
325 let stats = ErrorStatsManager::new();
326
327 stats.record_error(ErrorType::ParseError, QueryPhase::Parse);
328 stats.record_error(ErrorType::ValidationError, QueryPhase::Validate);
329 stats.record_error(ErrorType::ExecutionError, QueryPhase::Execute);
330
331 let summary = stats.get_error_summary();
332 assert_eq!(summary.total_errors, 3);
333 assert_eq!(summary.errors_by_type.get(&ErrorType::ParseError), Some(&1));
334 assert_eq!(summary.errors_by_phase.get(&QueryPhase::Parse), Some(&1));
335 }
336
337 #[test]
338 fn test_error_stats_reset() {
339 let stats = ErrorStatsManager::new();
340
341 stats.record_error(ErrorType::ParseError, QueryPhase::Parse);
342 assert_eq!(stats.get_error_count(ErrorType::ParseError), 1);
343
344 stats.reset_error_counts();
345 assert_eq!(stats.get_error_count(ErrorType::ParseError), 0);
346 }
347
348 #[test]
349 fn test_recent_errors() {
350 let stats = ErrorStatsManager::new();
351
352 let error1 = ErrorInfo::new(ErrorType::ParseError, QueryPhase::Parse, "Error 1");
353 let error2 = ErrorInfo::new(ErrorType::ExecutionError, QueryPhase::Execute, "Error 2");
354
355 stats.record_error_with_context(&error1, Some("SELECT * FROM t1".to_string()));
356 stats.record_error_with_context(&error2, Some("INSERT INTO t2 VALUES (1)".to_string()));
357
358 let recent = stats.get_recent_errors(10);
359 assert_eq!(recent.len(), 2);
360 assert_eq!(recent[0].error_type, ErrorType::ExecutionError);
361 assert_eq!(recent[1].error_type, ErrorType::ParseError);
362 assert!(recent[0].query_text.is_some());
363 }
364}