1#[cfg(test)]
8mod tests;
9
10use crate::serialize::{SerializeError, SerializeErrorKind};
11use std::fmt;
12use thiserror::Error as ThisError;
13
14#[derive(Debug, ThisError)]
115#[error("{message}")]
116pub struct InternalError {
117 pub(crate) class: ErrorClass,
118 pub(crate) origin: ErrorOrigin,
119 pub(crate) message: String,
120
121 pub(crate) detail: Option<ErrorDetail>,
124}
125
126impl InternalError {
127 pub fn new(class: ErrorClass, origin: ErrorOrigin, message: impl Into<String>) -> Self {
131 let message = message.into();
132
133 let detail = match (class, origin) {
134 (ErrorClass::Corruption, ErrorOrigin::Store) => {
135 Some(ErrorDetail::Store(StoreError::Corrupt {
136 message: message.clone(),
137 }))
138 }
139 (ErrorClass::InvariantViolation, ErrorOrigin::Store) => {
140 Some(ErrorDetail::Store(StoreError::InvariantViolation {
141 message: message.clone(),
142 }))
143 }
144 _ => None,
145 };
146
147 Self {
148 class,
149 origin,
150 message,
151 detail,
152 }
153 }
154
155 #[must_use]
157 pub const fn class(&self) -> ErrorClass {
158 self.class
159 }
160
161 #[must_use]
163 pub const fn origin(&self) -> ErrorOrigin {
164 self.origin
165 }
166
167 #[must_use]
169 pub fn message(&self) -> &str {
170 &self.message
171 }
172
173 #[must_use]
175 pub const fn detail(&self) -> Option<&ErrorDetail> {
176 self.detail.as_ref()
177 }
178
179 #[must_use]
181 pub fn into_message(self) -> String {
182 self.message
183 }
184
185 pub(crate) fn classified(
187 class: ErrorClass,
188 origin: ErrorOrigin,
189 message: impl Into<String>,
190 ) -> Self {
191 Self::new(class, origin, message)
192 }
193
194 pub(crate) fn with_message(self, message: impl Into<String>) -> Self {
196 Self::classified(self.class, self.origin, message)
197 }
198
199 pub(crate) fn with_origin(self, origin: ErrorOrigin) -> Self {
203 Self::classified(self.class, origin, self.message)
204 }
205
206 pub(crate) fn index_invariant(message: impl Into<String>) -> Self {
208 Self::new(
209 ErrorClass::InvariantViolation,
210 ErrorOrigin::Index,
211 message.into(),
212 )
213 }
214
215 pub(crate) fn planner_executor_invariant(reason: impl Into<String>) -> Self {
218 Self::new(
219 ErrorClass::InvariantViolation,
220 ErrorOrigin::Planner,
221 Self::executor_invariant_message(reason),
222 )
223 }
224
225 pub(crate) fn query_executor_invariant(reason: impl Into<String>) -> Self {
228 Self::new(
229 ErrorClass::InvariantViolation,
230 ErrorOrigin::Query,
231 Self::executor_invariant_message(reason),
232 )
233 }
234
235 pub(crate) fn executor_invariant(message: impl Into<String>) -> Self {
237 Self::new(
238 ErrorClass::InvariantViolation,
239 ErrorOrigin::Executor,
240 message.into(),
241 )
242 }
243
244 pub(crate) fn executor_internal(message: impl Into<String>) -> Self {
246 Self::new(ErrorClass::Internal, ErrorOrigin::Executor, message.into())
247 }
248
249 pub(crate) fn executor_unsupported(message: impl Into<String>) -> Self {
251 Self::new(
252 ErrorClass::Unsupported,
253 ErrorOrigin::Executor,
254 message.into(),
255 )
256 }
257
258 #[must_use]
260 pub(crate) fn executor_invariant_message(reason: impl Into<String>) -> String {
261 format!("executor invariant violated: {}", reason.into())
262 }
263
264 pub(crate) fn planner_invariant(message: impl Into<String>) -> Self {
266 Self::new(
267 ErrorClass::InvariantViolation,
268 ErrorOrigin::Planner,
269 message.into(),
270 )
271 }
272
273 #[must_use]
275 pub(crate) fn invalid_logical_plan_message(reason: impl Into<String>) -> String {
276 format!("invalid logical plan: {}", reason.into())
277 }
278
279 pub(crate) fn query_invalid_logical_plan(reason: impl Into<String>) -> Self {
281 Self::planner_invariant(Self::invalid_logical_plan_message(reason))
282 }
283
284 pub(crate) fn query_invariant(message: impl Into<String>) -> Self {
286 Self::new(
287 ErrorClass::InvariantViolation,
288 ErrorOrigin::Query,
289 message.into(),
290 )
291 }
292
293 pub(crate) fn cursor_invariant(message: impl Into<String>) -> Self {
295 Self::new(
296 ErrorClass::InvariantViolation,
297 ErrorOrigin::Cursor,
298 message.into(),
299 )
300 }
301
302 pub(crate) fn store_invariant(message: impl Into<String>) -> Self {
304 Self::new(
305 ErrorClass::InvariantViolation,
306 ErrorOrigin::Store,
307 message.into(),
308 )
309 }
310
311 pub(crate) fn store_internal(message: impl Into<String>) -> Self {
313 Self::new(ErrorClass::Internal, ErrorOrigin::Store, message.into())
314 }
315
316 pub(crate) fn index_internal(message: impl Into<String>) -> Self {
318 Self::new(ErrorClass::Internal, ErrorOrigin::Index, message.into())
319 }
320
321 #[cfg(test)]
323 pub(crate) fn query_internal(message: impl Into<String>) -> Self {
324 Self::new(ErrorClass::Internal, ErrorOrigin::Query, message.into())
325 }
326
327 pub(crate) fn query_unsupported(message: impl Into<String>) -> Self {
329 Self::new(ErrorClass::Unsupported, ErrorOrigin::Query, message.into())
330 }
331
332 pub(crate) fn serialize_internal(message: impl Into<String>) -> Self {
334 Self::new(ErrorClass::Internal, ErrorOrigin::Serialize, message.into())
335 }
336
337 pub(crate) fn store_corruption(message: impl Into<String>) -> Self {
339 Self::new(ErrorClass::Corruption, ErrorOrigin::Store, message.into())
340 }
341
342 pub(crate) fn commit_corruption(detail: impl fmt::Display) -> Self {
344 Self::store_corruption(format!("commit marker corrupted: {detail}"))
345 }
346
347 pub(crate) fn commit_component_corruption(component: &str, detail: impl fmt::Display) -> Self {
349 Self::store_corruption(format!("commit marker {component} corrupted: {detail}"))
350 }
351
352 pub(crate) fn index_corruption(message: impl Into<String>) -> Self {
354 Self::new(ErrorClass::Corruption, ErrorOrigin::Index, message.into())
355 }
356
357 pub(crate) fn serialize_corruption(message: impl Into<String>) -> Self {
359 Self::new(
360 ErrorClass::Corruption,
361 ErrorOrigin::Serialize,
362 message.into(),
363 )
364 }
365
366 #[must_use]
368 pub fn missing_persisted_slot(field_name: &'static str) -> Self {
369 Self::serialize_corruption(format!(
370 "row decode failed: missing required field '{field_name}'",
371 ))
372 }
373
374 pub(crate) fn identity_corruption(message: impl Into<String>) -> Self {
376 Self::new(
377 ErrorClass::Corruption,
378 ErrorOrigin::Identity,
379 message.into(),
380 )
381 }
382
383 pub(crate) fn store_unsupported(message: impl Into<String>) -> Self {
385 Self::new(ErrorClass::Unsupported, ErrorOrigin::Store, message.into())
386 }
387
388 pub(crate) fn index_unsupported(message: impl Into<String>) -> Self {
390 Self::new(ErrorClass::Unsupported, ErrorOrigin::Index, message.into())
391 }
392
393 pub(crate) fn serialize_unsupported(message: impl Into<String>) -> Self {
395 Self::new(
396 ErrorClass::Unsupported,
397 ErrorOrigin::Serialize,
398 message.into(),
399 )
400 }
401
402 pub(crate) fn cursor_unsupported(message: impl Into<String>) -> Self {
404 Self::new(ErrorClass::Unsupported, ErrorOrigin::Cursor, message.into())
405 }
406
407 pub(crate) fn serialize_incompatible_persisted_format(message: impl Into<String>) -> Self {
409 Self::new(
410 ErrorClass::IncompatiblePersistedFormat,
411 ErrorOrigin::Serialize,
412 message.into(),
413 )
414 }
415
416 pub(crate) fn serialize_payload_decode_failed(
419 source: SerializeError,
420 payload_label: &'static str,
421 ) -> Self {
422 match source {
423 SerializeError::DeserializeSizeLimitExceeded { len, max_bytes } => {
426 Self::serialize_corruption(format!(
427 "{payload_label} decode failed: payload size {len} exceeds limit {max_bytes}"
428 ))
429 }
430 SerializeError::Deserialize(_) => Self::serialize_corruption(format!(
431 "{payload_label} decode failed: {}",
432 SerializeErrorKind::Deserialize
433 )),
434 SerializeError::Serialize(_) => Self::serialize_corruption(format!(
435 "{payload_label} decode failed: {}",
436 SerializeErrorKind::Serialize
437 )),
438 }
439 }
440
441 #[cfg(feature = "sql")]
444 pub(crate) fn query_unsupported_sql_feature(feature: &'static str) -> Self {
445 let message = format!(
446 "SQL query is not executable in this release: unsupported SQL feature: {feature}"
447 );
448
449 Self {
450 class: ErrorClass::Unsupported,
451 origin: ErrorOrigin::Query,
452 message,
453 detail: Some(ErrorDetail::Query(
454 QueryErrorDetail::UnsupportedSqlFeature { feature },
455 )),
456 }
457 }
458
459 pub fn store_not_found(key: impl Into<String>) -> Self {
460 let key = key.into();
461
462 Self {
463 class: ErrorClass::NotFound,
464 origin: ErrorOrigin::Store,
465 message: format!("data key not found: {key}"),
466 detail: Some(ErrorDetail::Store(StoreError::NotFound { key })),
467 }
468 }
469
470 pub fn unsupported_entity_path(path: impl Into<String>) -> Self {
472 let path = path.into();
473
474 Self::new(
475 ErrorClass::Unsupported,
476 ErrorOrigin::Store,
477 format!("unsupported entity path: '{path}'"),
478 )
479 }
480
481 #[must_use]
482 pub const fn is_not_found(&self) -> bool {
483 matches!(
484 self.detail,
485 Some(ErrorDetail::Store(StoreError::NotFound { .. }))
486 )
487 }
488
489 #[must_use]
490 pub fn display_with_class(&self) -> String {
491 format!("{}:{}: {}", self.origin, self.class, self.message)
492 }
493
494 pub(crate) fn index_plan_corruption(origin: ErrorOrigin, message: impl Into<String>) -> Self {
496 let message = message.into();
497 Self::new(
498 ErrorClass::Corruption,
499 origin,
500 format!("corruption detected ({origin}): {message}"),
501 )
502 }
503
504 pub(crate) fn index_plan_index_corruption(message: impl Into<String>) -> Self {
506 Self::index_plan_corruption(ErrorOrigin::Index, message)
507 }
508
509 pub(crate) fn index_plan_store_corruption(message: impl Into<String>) -> Self {
511 Self::index_plan_corruption(ErrorOrigin::Store, message)
512 }
513
514 pub(crate) fn index_plan_serialize_corruption(message: impl Into<String>) -> Self {
516 Self::index_plan_corruption(ErrorOrigin::Serialize, message)
517 }
518
519 pub(crate) fn index_plan_invariant(origin: ErrorOrigin, message: impl Into<String>) -> Self {
521 let message = message.into();
522 Self::new(
523 ErrorClass::InvariantViolation,
524 origin,
525 format!("invariant violation detected ({origin}): {message}"),
526 )
527 }
528
529 pub(crate) fn index_plan_store_invariant(message: impl Into<String>) -> Self {
531 Self::index_plan_invariant(ErrorOrigin::Store, message)
532 }
533
534 pub(crate) fn index_violation(path: &str, index_fields: &[&str]) -> Self {
536 Self::new(
537 ErrorClass::Conflict,
538 ErrorOrigin::Index,
539 format!(
540 "index constraint violation: {path} ({})",
541 index_fields.join(", ")
542 ),
543 )
544 }
545}
546
547#[derive(Debug, ThisError)]
555pub enum ErrorDetail {
556 #[error("{0}")]
557 Store(StoreError),
558 #[error("{0}")]
559 Query(QueryErrorDetail),
560 }
567
568#[derive(Debug, ThisError)]
576pub enum StoreError {
577 #[error("key not found: {key}")]
578 NotFound { key: String },
579
580 #[error("store corruption: {message}")]
581 Corrupt { message: String },
582
583 #[error("store invariant violation: {message}")]
584 InvariantViolation { message: String },
585}
586
587#[derive(Debug, ThisError)]
594pub enum QueryErrorDetail {
595 #[error("unsupported SQL feature: {feature}")]
596 UnsupportedSqlFeature { feature: &'static str },
597}
598
599#[derive(Clone, Copy, Debug, Eq, PartialEq)]
606pub enum ErrorClass {
607 Corruption,
608 IncompatiblePersistedFormat,
609 NotFound,
610 Internal,
611 Conflict,
612 Unsupported,
613 InvariantViolation,
614}
615
616impl fmt::Display for ErrorClass {
617 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
618 let label = match self {
619 Self::Corruption => "corruption",
620 Self::IncompatiblePersistedFormat => "incompatible_persisted_format",
621 Self::NotFound => "not_found",
622 Self::Internal => "internal",
623 Self::Conflict => "conflict",
624 Self::Unsupported => "unsupported",
625 Self::InvariantViolation => "invariant_violation",
626 };
627 write!(f, "{label}")
628 }
629}
630
631#[derive(Clone, Copy, Debug, Eq, PartialEq)]
638pub enum ErrorOrigin {
639 Serialize,
640 Store,
641 Index,
642 Identity,
643 Query,
644 Planner,
645 Cursor,
646 Recovery,
647 Response,
648 Executor,
649 Interface,
650}
651
652impl fmt::Display for ErrorOrigin {
653 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
654 let label = match self {
655 Self::Serialize => "serialize",
656 Self::Store => "store",
657 Self::Index => "index",
658 Self::Identity => "identity",
659 Self::Query => "query",
660 Self::Planner => "planner",
661 Self::Cursor => "cursor",
662 Self::Recovery => "recovery",
663 Self::Response => "response",
664 Self::Executor => "executor",
665 Self::Interface => "interface",
666 };
667 write!(f, "{label}")
668 }
669}