1use crate::tree_store::{FILE_FORMAT_VERSION3, MAX_VALUE_LENGTH, PageNumber};
2use crate::{ReadTransaction, TypeName};
3use alloc::boxed::Box;
4use alloc::format;
5use alloc::string::String;
6use core::fmt::{Display, Formatter};
7use core::panic;
8
9#[cfg(feature = "std")]
10use crate::group_commit::GroupCommitError;
11#[cfg(feature = "std")]
12use std::sync::PoisonError;
13
14#[derive(Debug)]
19pub enum BackendError {
20 #[cfg(feature = "std")]
21 Io(std::io::Error),
22 #[cfg(not(feature = "std"))]
23 Message(String),
24}
25
26impl Display for BackendError {
27 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
28 match self {
29 #[cfg(feature = "std")]
30 BackendError::Io(err) => write!(f, "{err}"),
31 #[cfg(not(feature = "std"))]
32 BackendError::Message(msg) => write!(f, "{msg}"),
33 }
34 }
35}
36
37#[cfg(feature = "std")]
38impl std::error::Error for BackendError {}
39
40#[cfg(feature = "std")]
41impl From<std::io::Error> for BackendError {
42 fn from(err: std::io::Error) -> Self {
43 BackendError::Io(err)
44 }
45}
46
47impl BackendError {
48 #[cfg(feature = "std")]
50 pub fn kind(&self) -> std::io::ErrorKind {
51 match self {
52 BackendError::Io(e) => e.kind(),
53 }
54 }
55}
56
57#[derive(Debug)]
59#[non_exhaustive]
60pub enum StorageError {
61 Corrupted(String),
63 Internal(String),
67 ValueTooLarge(usize),
69 BlobNotFound(u64),
71 BlobChecksumMismatch {
73 sequence: u64,
75 expected: u128,
77 actual: u128,
79 },
80 BlobWriterActive,
82 BlobWriterFinished,
84 BlobRangeOutOfBounds {
86 blob_length: u64,
88 requested_offset: u64,
90 requested_length: u64,
92 },
93 MemoryBudgetExceeded {
95 budget: usize,
97 used: usize,
99 },
100 HistorySnapshotNotFound(u64),
102 InvalidPageType {
104 page_region: u32,
106 page_index: u32,
108 page_order: u8,
110 found: u8,
112 },
113 InvalidChildRef {
115 page_region: u32,
117 page_index: u32,
119 page_order: u8,
121 child_index: usize,
123 is_checksum: bool,
125 },
126 InvalidEntryIndex {
128 page_region: u32,
130 page_index: u32,
132 page_order: u8,
134 entry_index: usize,
136 },
137 PageCorrupted {
139 page_region: u32,
141 page_index: u32,
143 page_order: u8,
145 detail: &'static str,
147 },
148 CdcCursorBehindRetention {
151 cursor_txn_id: u64,
153 oldest_retained_txn_id: u64,
155 },
156 InvalidConfiguration {
158 message: String,
160 },
161 IndexNotTrained {
163 index_name: String,
165 },
166 DimensionMismatch {
168 index_name: String,
170 expected: usize,
172 actual: usize,
174 },
175 InvalidIndexConfig {
177 detail: String,
179 },
180 FormatError {
182 detail: String,
184 },
185 RecoveryRequired,
187 OutOfSpace,
189 LockTimeout(String),
192 Io(BackendError),
193 PreviousIo,
194 DatabaseClosed,
195 LockPoisoned(&'static panic::Location<'static>),
196}
197
198#[cfg(feature = "std")]
199impl<T> From<PoisonError<T>> for StorageError {
200 fn from(_: PoisonError<T>) -> StorageError {
201 StorageError::LockPoisoned(panic::Location::caller())
202 }
203}
204
205impl From<BackendError> for StorageError {
206 fn from(err: BackendError) -> StorageError {
207 StorageError::Io(err)
208 }
209}
210
211#[cfg(feature = "std")]
212impl From<std::io::Error> for StorageError {
213 fn from(err: std::io::Error) -> StorageError {
214 StorageError::Io(BackendError::Io(err))
215 }
216}
217
218impl From<StorageError> for Error {
219 fn from(err: StorageError) -> Error {
220 match err {
221 StorageError::Corrupted(msg) => Error::Corrupted(msg),
222 StorageError::Internal(msg) => Error::Internal(msg),
223 StorageError::ValueTooLarge(x) => Error::ValueTooLarge(x),
224 StorageError::BlobNotFound(seq) => Error::BlobNotFound(seq),
225 StorageError::BlobChecksumMismatch {
226 sequence,
227 expected,
228 actual,
229 } => Error::BlobChecksumMismatch {
230 sequence,
231 expected,
232 actual,
233 },
234 StorageError::BlobWriterActive => Error::BlobWriterActive,
235 StorageError::BlobWriterFinished => Error::BlobWriterFinished,
236 StorageError::BlobRangeOutOfBounds {
237 blob_length,
238 requested_offset,
239 requested_length,
240 } => Error::BlobRangeOutOfBounds {
241 blob_length,
242 requested_offset,
243 requested_length,
244 },
245 StorageError::MemoryBudgetExceeded { budget, used } => {
246 Error::MemoryBudgetExceeded { budget, used }
247 }
248 StorageError::HistorySnapshotNotFound(id) => Error::HistorySnapshotNotFound(id),
249 StorageError::InvalidPageType {
250 page_region,
251 page_index,
252 page_order,
253 found,
254 } => Error::InvalidPageType {
255 page_region,
256 page_index,
257 page_order,
258 found,
259 },
260 StorageError::InvalidChildRef {
261 page_region,
262 page_index,
263 page_order,
264 child_index,
265 is_checksum,
266 } => Error::InvalidChildRef {
267 page_region,
268 page_index,
269 page_order,
270 child_index,
271 is_checksum,
272 },
273 StorageError::InvalidEntryIndex {
274 page_region,
275 page_index,
276 page_order,
277 entry_index,
278 } => Error::InvalidEntryIndex {
279 page_region,
280 page_index,
281 page_order,
282 entry_index,
283 },
284 StorageError::PageCorrupted {
285 page_region,
286 page_index,
287 page_order,
288 detail,
289 } => Error::PageCorrupted {
290 page_region,
291 page_index,
292 page_order,
293 detail,
294 },
295 StorageError::CdcCursorBehindRetention {
296 cursor_txn_id,
297 oldest_retained_txn_id,
298 } => Error::CdcCursorBehindRetention {
299 cursor_txn_id,
300 oldest_retained_txn_id,
301 },
302 StorageError::InvalidConfiguration { message } => {
303 Error::InvalidConfiguration { message }
304 }
305 StorageError::IndexNotTrained { index_name } => Error::IndexNotTrained { index_name },
306 StorageError::DimensionMismatch {
307 index_name,
308 expected,
309 actual,
310 } => Error::DimensionMismatch {
311 index_name,
312 expected,
313 actual,
314 },
315 StorageError::InvalidIndexConfig { detail } => Error::InvalidIndexConfig { detail },
316 StorageError::FormatError { detail } => Error::FormatError { detail },
317 StorageError::RecoveryRequired => Error::RecoveryRequired,
318 StorageError::OutOfSpace => Error::OutOfSpace,
319 StorageError::LockTimeout(msg) => Error::LockTimeout(msg),
320 StorageError::Io(x) => Error::Io(x),
321 StorageError::PreviousIo => Error::PreviousIo,
322 StorageError::DatabaseClosed => Error::DatabaseClosed,
323 StorageError::LockPoisoned(location) => Error::LockPoisoned(location),
324 }
325 }
326}
327
328impl Display for StorageError {
329 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
330 match self {
331 StorageError::Corrupted(msg) => {
332 write!(f, "DB corrupted: {msg}")
333 }
334 StorageError::Internal(msg) => {
335 write!(f, "Internal error (this is a bug): {msg}")
336 }
337 StorageError::ValueTooLarge(len) => {
338 write!(
339 f,
340 "The value (length={len}) being inserted exceeds the maximum of {}GiB",
341 MAX_VALUE_LENGTH / 1024 / 1024 / 1024
342 )
343 }
344 StorageError::BlobNotFound(seq) => {
345 write!(f, "Blob not found: sequence={seq}")
346 }
347 StorageError::BlobChecksumMismatch {
348 sequence,
349 expected,
350 actual,
351 } => {
352 write!(
353 f,
354 "Blob checksum mismatch: sequence={sequence}, expected={expected:#034x}, actual={actual:#034x}"
355 )
356 }
357 StorageError::BlobWriterActive => {
358 write!(
359 f,
360 "Cannot create blob writer or store blob while another writer is active"
361 )
362 }
363 StorageError::BlobWriterFinished => {
364 write!(f, "Blob writer has already been finished")
365 }
366 StorageError::BlobRangeOutOfBounds {
367 blob_length,
368 requested_offset,
369 requested_length,
370 } => {
371 write!(
372 f,
373 "Blob range out of bounds: blob_length={blob_length}, requested offset={requested_offset}, length={requested_length}"
374 )
375 }
376 StorageError::MemoryBudgetExceeded { budget, used } => {
377 write!(
378 f,
379 "Memory budget exceeded: budget={budget} bytes, used={used} bytes"
380 )
381 }
382 StorageError::HistorySnapshotNotFound(id) => {
383 write!(f, "History snapshot not found for transaction id={id}")
384 }
385 StorageError::InvalidPageType {
386 page_region,
387 page_index,
388 page_order,
389 found,
390 } => {
391 write!(
392 f,
393 "Invalid page type byte {found} on page ({page_region}, {page_index}, order={page_order}), expected LEAF (1) or BRANCH (2)"
394 )
395 }
396 StorageError::InvalidChildRef {
397 page_region,
398 page_index,
399 page_order,
400 child_index,
401 is_checksum,
402 } => {
403 let kind = if *is_checksum { "checksum" } else { "pointer" };
404 write!(
405 f,
406 "Invalid child {kind} at index {child_index} on page ({page_region}, {page_index}, order={page_order})"
407 )
408 }
409 StorageError::InvalidEntryIndex {
410 page_region,
411 page_index,
412 page_order,
413 entry_index,
414 } => {
415 write!(
416 f,
417 "Invalid entry index {entry_index} on page ({page_region}, {page_index}, order={page_order})"
418 )
419 }
420 StorageError::PageCorrupted {
421 page_region,
422 page_index,
423 page_order,
424 detail,
425 } => {
426 write!(
427 f,
428 "Page ({page_region}, {page_index}, order={page_order}) corrupted: {detail}"
429 )
430 }
431 StorageError::CdcCursorBehindRetention {
432 cursor_txn_id,
433 oldest_retained_txn_id,
434 } => {
435 write!(
436 f,
437 "CDC cursor (txn_id={cursor_txn_id}) is behind the retention window (oldest retained txn_id={oldest_retained_txn_id})"
438 )
439 }
440 StorageError::InvalidConfiguration { message } => {
441 write!(f, "Invalid configuration: {message}")
442 }
443 StorageError::IndexNotTrained { index_name } => {
444 write!(f, "IVF-PQ index '{index_name}' has not been trained")
445 }
446 StorageError::DimensionMismatch {
447 index_name,
448 expected,
449 actual,
450 } => {
451 write!(
452 f,
453 "Dimension mismatch on index '{index_name}': expected {expected}, got {actual}"
454 )
455 }
456 StorageError::InvalidIndexConfig { detail } => {
457 write!(f, "Invalid index configuration: {detail}")
458 }
459 StorageError::FormatError { detail } => {
460 write!(f, "Format error: {detail}")
461 }
462 StorageError::RecoveryRequired => {
463 write!(
464 f,
465 "Database recovery required. Close and re-open the database."
466 )
467 }
468 StorageError::OutOfSpace => {
469 write!(f, "Storage space exhausted: allocator cannot grow further")
470 }
471 StorageError::LockTimeout(msg) => {
472 write!(f, "Lock timeout: {msg}")
473 }
474 StorageError::Io(err) => {
475 write!(f, "I/O error: {err}")
476 }
477 StorageError::DatabaseClosed => {
478 write!(f, "Database has been closed")
479 }
480 StorageError::PreviousIo => {
481 write!(
482 f,
483 "Previous I/O error occurred. Please close and re-open the database."
484 )
485 }
486 StorageError::LockPoisoned(location) => {
487 write!(f, "Poisoned internal lock: {location}")
488 }
489 }
490 }
491}
492
493#[cfg(feature = "std")]
494impl std::error::Error for StorageError {}
495
496impl StorageError {
497 pub(crate) fn invalid_page_type(page: PageNumber, found: u8) -> Self {
498 StorageError::InvalidPageType {
499 page_region: page.region,
500 page_index: page.page_index,
501 page_order: page.page_order,
502 found,
503 }
504 }
505
506 pub(crate) fn invalid_child_pointer(page: PageNumber, child_index: usize) -> Self {
507 StorageError::InvalidChildRef {
508 page_region: page.region,
509 page_index: page.page_index,
510 page_order: page.page_order,
511 child_index,
512 is_checksum: false,
513 }
514 }
515
516 pub(crate) fn invalid_child_checksum(page: PageNumber, child_index: usize) -> Self {
517 StorageError::InvalidChildRef {
518 page_region: page.region,
519 page_index: page.page_index,
520 page_order: page.page_order,
521 child_index,
522 is_checksum: true,
523 }
524 }
525
526 pub(crate) fn invalid_entry_index(page: PageNumber, entry_index: usize) -> Self {
527 StorageError::InvalidEntryIndex {
528 page_region: page.region,
529 page_index: page.page_index,
530 page_order: page.page_order,
531 entry_index,
532 }
533 }
534
535 pub(crate) fn page_corrupted(page: PageNumber, detail: &'static str) -> Self {
536 StorageError::PageCorrupted {
537 page_region: page.region,
538 page_index: page.page_index,
539 page_order: page.page_order,
540 detail,
541 }
542 }
543
544 pub(crate) fn invalid_config(message: impl Into<String>) -> Self {
545 StorageError::InvalidConfiguration {
546 message: message.into(),
547 }
548 }
549
550 pub(crate) fn index_not_trained(index_name: impl Into<String>) -> Self {
551 StorageError::IndexNotTrained {
552 index_name: index_name.into(),
553 }
554 }
555
556 pub(crate) fn dimension_mismatch(
557 index_name: impl Into<String>,
558 expected: usize,
559 actual: usize,
560 ) -> Self {
561 StorageError::DimensionMismatch {
562 index_name: index_name.into(),
563 expected,
564 actual,
565 }
566 }
567
568 pub(crate) fn invalid_index_config(detail: impl Into<String>) -> Self {
569 StorageError::InvalidIndexConfig {
570 detail: detail.into(),
571 }
572 }
573
574 pub(crate) fn format_error(detail: impl Into<String>) -> Self {
575 StorageError::FormatError {
576 detail: detail.into(),
577 }
578 }
579
580 pub(crate) fn corrupted(detail: impl Into<String>) -> Self {
581 StorageError::Corrupted(detail.into())
582 }
583}
584
585#[derive(Debug)]
587#[non_exhaustive]
588pub enum TableError {
589 TableTypeMismatch {
591 table: String,
592 key: TypeName,
593 value: TypeName,
594 },
595 TableIsMultimap(String),
597 TableIsNotMultimap(String),
599 TypeDefinitionChanged {
600 name: TypeName,
601 alignment: usize,
602 width: Option<usize>,
603 },
604 TableDoesNotExist(String),
606 TableExists(String),
608 TableAlreadyOpen(String, &'static panic::Location<'static>),
611 Storage(StorageError),
613}
614
615impl TableError {
616 pub(crate) fn into_storage_error_or_internal(self, msg: &str) -> StorageError {
617 match self {
618 TableError::TableTypeMismatch { .. }
619 | TableError::TableIsMultimap(_)
620 | TableError::TableIsNotMultimap(_)
621 | TableError::TypeDefinitionChanged { .. }
622 | TableError::TableDoesNotExist(_)
623 | TableError::TableExists(_)
624 | TableError::TableAlreadyOpen(_, _) => {
625 StorageError::Internal(format!("{msg}: {self}"))
626 }
627 TableError::Storage(storage) => storage,
628 }
629 }
630}
631
632impl From<TableError> for Error {
633 fn from(err: TableError) -> Error {
634 match err {
635 TableError::TypeDefinitionChanged {
636 name,
637 alignment,
638 width,
639 } => Error::TypeDefinitionChanged {
640 name,
641 alignment,
642 width,
643 },
644 TableError::TableTypeMismatch { table, key, value } => {
645 Error::TableTypeMismatch { table, key, value }
646 }
647 TableError::TableIsMultimap(table) => Error::TableIsMultimap(table),
648 TableError::TableIsNotMultimap(table) => Error::TableIsNotMultimap(table),
649 TableError::TableDoesNotExist(table) => Error::TableDoesNotExist(table),
650 TableError::TableExists(table) => Error::TableExists(table),
651 TableError::TableAlreadyOpen(name, location) => Error::TableAlreadyOpen(name, location),
652 TableError::Storage(storage) => storage.into(),
653 }
654 }
655}
656
657impl From<StorageError> for TableError {
658 fn from(err: StorageError) -> TableError {
659 TableError::Storage(err)
660 }
661}
662
663impl Display for TableError {
664 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
665 match self {
666 TableError::TypeDefinitionChanged {
667 name,
668 alignment,
669 width,
670 } => {
671 write!(
672 f,
673 "Current definition of {} does not match stored definition (width={:?}, alignment={})",
674 name.name(),
675 width,
676 alignment,
677 )
678 }
679 TableError::TableTypeMismatch { table, key, value } => {
680 write!(
681 f,
682 "{table} is of type Table<{}, {}>",
683 key.name(),
684 value.name(),
685 )
686 }
687 TableError::TableIsMultimap(table) => {
688 write!(f, "{table} is a multimap table")
689 }
690 TableError::TableIsNotMultimap(table) => {
691 write!(f, "{table} is not a multimap table")
692 }
693 TableError::TableDoesNotExist(table) => {
694 write!(f, "Table '{table}' does not exist")
695 }
696 TableError::TableExists(table) => {
697 write!(f, "Table '{table}' already exists")
698 }
699 TableError::TableAlreadyOpen(name, location) => {
700 write!(f, "Table '{name}' already opened at: {location}")
701 }
702 TableError::Storage(storage) => storage.fmt(f),
703 }
704 }
705}
706
707#[cfg(feature = "std")]
708impl std::error::Error for TableError {}
709
710#[derive(Debug)]
712#[non_exhaustive]
713pub enum DatabaseError {
714 DatabaseAlreadyOpen,
716 RepairAborted,
718 UpgradeRequired(u8),
720 Storage(StorageError),
722}
723
724impl From<DatabaseError> for Error {
725 fn from(err: DatabaseError) -> Error {
726 match err {
727 DatabaseError::DatabaseAlreadyOpen => Error::DatabaseAlreadyOpen,
728 DatabaseError::RepairAborted => Error::RepairAborted,
729 DatabaseError::UpgradeRequired(x) => Error::UpgradeRequired(x),
730 DatabaseError::Storage(storage) => storage.into(),
731 }
732 }
733}
734
735impl From<BackendError> for DatabaseError {
736 fn from(err: BackendError) -> DatabaseError {
737 DatabaseError::Storage(StorageError::Io(err))
738 }
739}
740
741#[cfg(feature = "std")]
742impl From<std::io::Error> for DatabaseError {
743 fn from(err: std::io::Error) -> DatabaseError {
744 DatabaseError::Storage(StorageError::Io(BackendError::Io(err)))
745 }
746}
747
748impl From<StorageError> for DatabaseError {
749 fn from(err: StorageError) -> DatabaseError {
750 DatabaseError::Storage(err)
751 }
752}
753
754impl Display for DatabaseError {
755 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
756 match self {
757 DatabaseError::UpgradeRequired(actual) => {
758 write!(
759 f,
760 "Manual upgrade required. Expected file format version {FILE_FORMAT_VERSION3}, but file is version {actual}"
761 )
762 }
763 DatabaseError::RepairAborted => {
764 write!(f, "Database repair aborted.")
765 }
766 DatabaseError::DatabaseAlreadyOpen => {
767 write!(f, "Database already open. Cannot acquire lock.")
768 }
769 DatabaseError::Storage(storage) => storage.fmt(f),
770 }
771 }
772}
773
774#[cfg(feature = "std")]
775impl std::error::Error for DatabaseError {}
776
777#[derive(Debug)]
779#[non_exhaustive]
780pub enum SavepointError {
781 InvalidSavepoint,
786 Storage(StorageError),
788}
789
790impl From<SavepointError> for Error {
791 fn from(err: SavepointError) -> Error {
792 match err {
793 SavepointError::InvalidSavepoint => Error::InvalidSavepoint,
794 SavepointError::Storage(storage) => storage.into(),
795 }
796 }
797}
798
799impl From<StorageError> for SavepointError {
800 fn from(err: StorageError) -> SavepointError {
801 SavepointError::Storage(err)
802 }
803}
804
805impl Display for SavepointError {
806 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
807 match self {
808 SavepointError::InvalidSavepoint => {
809 write!(f, "Savepoint is invalid or cannot be created.")
810 }
811 SavepointError::Storage(storage) => storage.fmt(f),
812 }
813 }
814}
815
816#[cfg(feature = "std")]
817impl std::error::Error for SavepointError {}
818
819#[derive(Debug)]
821#[non_exhaustive]
822pub enum CompactionError {
823 PersistentSavepointExists,
825 EphemeralSavepointExists,
827 TransactionInProgress,
829 Cancelled,
831 Storage(StorageError),
833}
834
835impl From<CompactionError> for Error {
836 fn from(err: CompactionError) -> Error {
837 match err {
838 CompactionError::PersistentSavepointExists => Error::PersistentSavepointExists,
839 CompactionError::EphemeralSavepointExists => Error::EphemeralSavepointExists,
840 CompactionError::TransactionInProgress => Error::TransactionInProgress,
841 CompactionError::Cancelled => Error::CompactionCancelled,
842 CompactionError::Storage(storage) => storage.into(),
843 }
844 }
845}
846
847impl From<StorageError> for CompactionError {
848 fn from(err: StorageError) -> CompactionError {
849 CompactionError::Storage(err)
850 }
851}
852
853impl Display for CompactionError {
854 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
855 match self {
856 CompactionError::PersistentSavepointExists => {
857 write!(
858 f,
859 "Persistent savepoint exists. Operation cannot be performed."
860 )
861 }
862 CompactionError::EphemeralSavepointExists => {
863 write!(
864 f,
865 "Ephemeral savepoint exists. Operation cannot be performed."
866 )
867 }
868 CompactionError::TransactionInProgress => {
869 write!(
870 f,
871 "A transaction is still in progress. Operation cannot be performed."
872 )
873 }
874 CompactionError::Cancelled => {
875 write!(f, "Compaction cancelled by progress callback")
876 }
877 CompactionError::Storage(storage) => storage.fmt(f),
878 }
879 }
880}
881
882#[cfg(feature = "std")]
883impl std::error::Error for CompactionError {}
884
885#[derive(Debug)]
887#[non_exhaustive]
888pub enum SetDurabilityError {
889 PersistentSavepointModified,
891}
892
893impl From<SetDurabilityError> for Error {
894 fn from(err: SetDurabilityError) -> Error {
895 match err {
896 SetDurabilityError::PersistentSavepointModified => Error::PersistentSavepointModified,
897 }
898 }
899}
900
901impl Display for SetDurabilityError {
902 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
903 match self {
904 SetDurabilityError::PersistentSavepointModified => {
905 write!(
906 f,
907 "Persistent savepoint modified. Cannot reduce transaction durability"
908 )
909 }
910 }
911 }
912}
913
914#[cfg(feature = "std")]
915impl std::error::Error for SetDurabilityError {}
916
917#[derive(Debug)]
919#[non_exhaustive]
920pub enum TransactionError {
921 Storage(StorageError),
923 ReadTransactionStillInUse(Box<ReadTransaction>),
925}
926
927impl TransactionError {
928 pub(crate) fn into_storage_error(self) -> StorageError {
929 match self {
930 TransactionError::Storage(storage) => storage,
931 _ => StorageError::Internal(String::from("unexpected non-storage transaction error")),
932 }
933 }
934}
935
936impl From<TransactionError> for Error {
937 fn from(err: TransactionError) -> Error {
938 match err {
939 TransactionError::Storage(storage) => storage.into(),
940 TransactionError::ReadTransactionStillInUse(txn) => {
941 Error::ReadTransactionStillInUse(txn)
942 }
943 }
944 }
945}
946
947impl From<StorageError> for TransactionError {
948 fn from(err: StorageError) -> TransactionError {
949 TransactionError::Storage(err)
950 }
951}
952
953impl Display for TransactionError {
954 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
955 match self {
956 TransactionError::Storage(storage) => storage.fmt(f),
957 TransactionError::ReadTransactionStillInUse(_) => {
958 write!(f, "Transaction still in use")
959 }
960 }
961 }
962}
963
964#[cfg(feature = "std")]
965impl std::error::Error for TransactionError {}
966
967#[derive(Debug)]
969#[non_exhaustive]
970pub enum CommitError {
971 Storage(StorageError),
973}
974
975impl CommitError {
976 pub(crate) fn into_storage_error(self) -> StorageError {
977 match self {
978 CommitError::Storage(storage) => storage,
979 }
980 }
981}
982
983impl From<CommitError> for Error {
984 fn from(err: CommitError) -> Error {
985 match err {
986 CommitError::Storage(storage) => storage.into(),
987 }
988 }
989}
990
991impl From<StorageError> for CommitError {
992 fn from(err: StorageError) -> CommitError {
993 CommitError::Storage(err)
994 }
995}
996
997impl Display for CommitError {
998 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
999 match self {
1000 CommitError::Storage(storage) => storage.fmt(f),
1001 }
1002 }
1003}
1004
1005#[cfg(feature = "std")]
1006impl std::error::Error for CommitError {}
1007
1008#[derive(Debug)]
1010#[non_exhaustive]
1011pub enum Error {
1012 DatabaseAlreadyOpen,
1014 InvalidSavepoint,
1019 RepairAborted,
1021 PersistentSavepointModified,
1023 PersistentSavepointExists,
1025 EphemeralSavepointExists,
1027 TransactionInProgress,
1029 Corrupted(String),
1031 Internal(String),
1035 UpgradeRequired(u8),
1037 ValueTooLarge(usize),
1039 TableTypeMismatch {
1041 table: String,
1042 key: TypeName,
1043 value: TypeName,
1044 },
1045 TableIsMultimap(String),
1047 TableIsNotMultimap(String),
1049 TypeDefinitionChanged {
1050 name: TypeName,
1051 alignment: usize,
1052 width: Option<usize>,
1053 },
1054 TableDoesNotExist(String),
1056 TableExists(String),
1058 TableAlreadyOpen(String, &'static panic::Location<'static>),
1061 BlobNotFound(u64),
1063 BlobChecksumMismatch {
1065 sequence: u64,
1067 expected: u128,
1069 actual: u128,
1071 },
1072 BlobWriterActive,
1074 BlobWriterFinished,
1076 BlobRangeOutOfBounds {
1078 blob_length: u64,
1080 requested_offset: u64,
1082 requested_length: u64,
1084 },
1085 MemoryBudgetExceeded {
1087 budget: usize,
1089 used: usize,
1091 },
1092 HistorySnapshotNotFound(u64),
1094 InvalidPageType {
1096 page_region: u32,
1097 page_index: u32,
1098 page_order: u8,
1099 found: u8,
1100 },
1101 InvalidChildRef {
1103 page_region: u32,
1104 page_index: u32,
1105 page_order: u8,
1106 child_index: usize,
1107 is_checksum: bool,
1108 },
1109 InvalidEntryIndex {
1111 page_region: u32,
1112 page_index: u32,
1113 page_order: u8,
1114 entry_index: usize,
1115 },
1116 PageCorrupted {
1118 page_region: u32,
1119 page_index: u32,
1120 page_order: u8,
1121 detail: &'static str,
1122 },
1123 CdcCursorBehindRetention {
1125 cursor_txn_id: u64,
1127 oldest_retained_txn_id: u64,
1129 },
1130 InvalidConfiguration {
1132 message: String,
1134 },
1135 IndexNotTrained {
1137 index_name: String,
1139 },
1140 DimensionMismatch {
1142 index_name: String,
1144 expected: usize,
1146 actual: usize,
1148 },
1149 InvalidIndexConfig {
1151 detail: String,
1153 },
1154 FormatError {
1156 detail: String,
1158 },
1159 RecoveryRequired,
1161 OutOfSpace,
1163 LockTimeout(String),
1165 Io(BackendError),
1166 DatabaseClosed,
1167 PreviousIo,
1169 LockPoisoned(&'static panic::Location<'static>),
1170 ReadTransactionStillInUse(Box<ReadTransaction>),
1172 #[cfg(feature = "std")]
1174 GroupCommitPeerFailed,
1175 #[cfg(feature = "std")]
1177 GroupCommitShutdown,
1178 CompactionCancelled,
1180}
1181
1182#[cfg(feature = "std")]
1183impl From<GroupCommitError> for Error {
1184 fn from(err: GroupCommitError) -> Error {
1185 match err {
1186 GroupCommitError::BatchFailed(e) => e,
1187 GroupCommitError::PeerFailed => Error::GroupCommitPeerFailed,
1188 GroupCommitError::TransactionFailed(e) | GroupCommitError::CommitFailed(e) => e.into(),
1189 GroupCommitError::Shutdown => Error::GroupCommitShutdown,
1190 GroupCommitError::LockPoisoned => Error::LockPoisoned(panic::Location::caller()),
1191 }
1192 }
1193}
1194
1195#[cfg(feature = "std")]
1196impl<T> From<PoisonError<T>> for Error {
1197 fn from(_: PoisonError<T>) -> Error {
1198 Error::LockPoisoned(panic::Location::caller())
1199 }
1200}
1201
1202#[cfg(feature = "std")]
1203impl From<std::io::Error> for Error {
1204 fn from(err: std::io::Error) -> Error {
1205 Error::Io(BackendError::Io(err))
1206 }
1207}
1208
1209impl From<BackendError> for Error {
1210 fn from(err: BackendError) -> Error {
1211 Error::Io(err)
1212 }
1213}
1214
1215impl Display for Error {
1216 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
1217 match self {
1218 Error::Corrupted(msg) => {
1219 write!(f, "DB corrupted: {msg}")
1220 }
1221 Error::Internal(msg) => {
1222 write!(f, "Internal error (this is a bug): {msg}")
1223 }
1224 Error::UpgradeRequired(actual) => {
1225 write!(
1226 f,
1227 "Manual upgrade required. Expected file format version {FILE_FORMAT_VERSION3}, but file is version {actual}"
1228 )
1229 }
1230 Error::ValueTooLarge(len) => {
1231 write!(
1232 f,
1233 "The value (length={len}) being inserted exceeds the maximum of {}GiB",
1234 MAX_VALUE_LENGTH / 1024 / 1024 / 1024
1235 )
1236 }
1237 Error::TypeDefinitionChanged {
1238 name,
1239 alignment,
1240 width,
1241 } => {
1242 write!(
1243 f,
1244 "Current definition of {} does not match stored definition (width={:?}, alignment={})",
1245 name.name(),
1246 width,
1247 alignment,
1248 )
1249 }
1250 Error::TableTypeMismatch { table, key, value } => {
1251 write!(
1252 f,
1253 "{table} is of type Table<{}, {}>",
1254 key.name(),
1255 value.name(),
1256 )
1257 }
1258 Error::TableIsMultimap(table) => {
1259 write!(f, "{table} is a multimap table")
1260 }
1261 Error::TableIsNotMultimap(table) => {
1262 write!(f, "{table} is not a multimap table")
1263 }
1264 Error::TableDoesNotExist(table) => {
1265 write!(f, "Table '{table}' does not exist")
1266 }
1267 Error::TableExists(table) => {
1268 write!(f, "Table '{table}' already exists")
1269 }
1270 Error::TableAlreadyOpen(name, location) => {
1271 write!(f, "Table '{name}' already opened at: {location}")
1272 }
1273 Error::BlobNotFound(seq) => {
1274 write!(f, "Blob not found: sequence={seq}")
1275 }
1276 Error::BlobChecksumMismatch {
1277 sequence,
1278 expected,
1279 actual,
1280 } => {
1281 write!(
1282 f,
1283 "Blob checksum mismatch: sequence={sequence}, expected={expected:#034x}, actual={actual:#034x}"
1284 )
1285 }
1286 Error::BlobWriterActive => {
1287 write!(
1288 f,
1289 "Cannot create blob writer or store blob while another writer is active"
1290 )
1291 }
1292 Error::BlobWriterFinished => {
1293 write!(f, "Blob writer has already been finished")
1294 }
1295 Error::BlobRangeOutOfBounds {
1296 blob_length,
1297 requested_offset,
1298 requested_length,
1299 } => {
1300 write!(
1301 f,
1302 "Blob range out of bounds: blob_length={blob_length}, requested offset={requested_offset}, length={requested_length}"
1303 )
1304 }
1305 Error::MemoryBudgetExceeded { budget, used } => {
1306 write!(
1307 f,
1308 "Memory budget exceeded: budget={budget} bytes, used={used} bytes"
1309 )
1310 }
1311 Error::HistorySnapshotNotFound(id) => {
1312 write!(f, "History snapshot not found for transaction id={id}")
1313 }
1314 Error::InvalidPageType {
1315 page_region,
1316 page_index,
1317 page_order,
1318 found,
1319 } => {
1320 write!(
1321 f,
1322 "Invalid page type byte {found} on page ({page_region}, {page_index}, order={page_order}), expected LEAF (1) or BRANCH (2)"
1323 )
1324 }
1325 Error::InvalidChildRef {
1326 page_region,
1327 page_index,
1328 page_order,
1329 child_index,
1330 is_checksum,
1331 } => {
1332 let kind = if *is_checksum { "checksum" } else { "pointer" };
1333 write!(
1334 f,
1335 "Invalid child {kind} at index {child_index} on page ({page_region}, {page_index}, order={page_order})"
1336 )
1337 }
1338 Error::InvalidEntryIndex {
1339 page_region,
1340 page_index,
1341 page_order,
1342 entry_index,
1343 } => {
1344 write!(
1345 f,
1346 "Invalid entry index {entry_index} on page ({page_region}, {page_index}, order={page_order})"
1347 )
1348 }
1349 Error::PageCorrupted {
1350 page_region,
1351 page_index,
1352 page_order,
1353 detail,
1354 } => {
1355 write!(
1356 f,
1357 "Page ({page_region}, {page_index}, order={page_order}) corrupted: {detail}"
1358 )
1359 }
1360 Error::CdcCursorBehindRetention {
1361 cursor_txn_id,
1362 oldest_retained_txn_id,
1363 } => {
1364 write!(
1365 f,
1366 "CDC cursor (txn_id={cursor_txn_id}) is behind the retention window (oldest retained txn_id={oldest_retained_txn_id})"
1367 )
1368 }
1369 Error::InvalidConfiguration { message } => {
1370 write!(f, "Invalid configuration: {message}")
1371 }
1372 Error::IndexNotTrained { index_name } => {
1373 write!(f, "IVF-PQ index '{index_name}' has not been trained")
1374 }
1375 Error::DimensionMismatch {
1376 index_name,
1377 expected,
1378 actual,
1379 } => {
1380 write!(
1381 f,
1382 "Dimension mismatch on index '{index_name}': expected {expected}, got {actual}"
1383 )
1384 }
1385 Error::InvalidIndexConfig { detail } => {
1386 write!(f, "Invalid index configuration: {detail}")
1387 }
1388 Error::FormatError { detail } => {
1389 write!(f, "Format error: {detail}")
1390 }
1391 Error::RecoveryRequired => {
1392 write!(
1393 f,
1394 "Database recovery required. Close and re-open the database."
1395 )
1396 }
1397 Error::OutOfSpace => {
1398 write!(f, "Storage space exhausted: allocator cannot grow further")
1399 }
1400 Error::LockTimeout(msg) => {
1401 write!(f, "Lock timeout: {msg}")
1402 }
1403 Error::Io(err) => {
1404 write!(f, "I/O error: {err}")
1405 }
1406 Error::DatabaseClosed => {
1407 write!(f, "Database has been closed")
1408 }
1409 Error::PreviousIo => {
1410 write!(
1411 f,
1412 "Previous I/O error occurred. Please close and re-open the database."
1413 )
1414 }
1415 Error::LockPoisoned(location) => {
1416 write!(f, "Poisoned internal lock: {location}")
1417 }
1418 Error::DatabaseAlreadyOpen => {
1419 write!(f, "Database already open. Cannot acquire lock.")
1420 }
1421 Error::RepairAborted => {
1422 write!(f, "Database repair aborted.")
1423 }
1424 Error::PersistentSavepointModified => {
1425 write!(
1426 f,
1427 "Persistent savepoint modified. Cannot reduce transaction durability"
1428 )
1429 }
1430 Error::PersistentSavepointExists => {
1431 write!(
1432 f,
1433 "Persistent savepoint exists. Operation cannot be performed."
1434 )
1435 }
1436 Error::EphemeralSavepointExists => {
1437 write!(
1438 f,
1439 "Ephemeral savepoint exists. Operation cannot be performed."
1440 )
1441 }
1442 Error::TransactionInProgress => {
1443 write!(
1444 f,
1445 "A transaction is still in progress. Operation cannot be performed."
1446 )
1447 }
1448 Error::InvalidSavepoint => {
1449 write!(f, "Savepoint is invalid or cannot be created.")
1450 }
1451 Error::ReadTransactionStillInUse(_) => {
1452 write!(f, "Transaction still in use")
1453 }
1454 #[cfg(feature = "std")]
1455 Error::GroupCommitPeerFailed => {
1456 write!(
1457 f,
1458 "Group commit batch rolled back: another batch in the group failed"
1459 )
1460 }
1461 #[cfg(feature = "std")]
1462 Error::GroupCommitShutdown => {
1463 write!(f, "Group commit failed: database is shutting down")
1464 }
1465 Error::CompactionCancelled => {
1466 write!(f, "Compaction cancelled by progress callback")
1467 }
1468 }
1469 }
1470}
1471
1472#[cfg(feature = "std")]
1473impl std::error::Error for Error {}