#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Lsn(pub u64);
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PageId(pub u64);
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct TxnId(pub u64);
#[derive(
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
)]
pub struct NodeId(pub u64);
#[derive(
Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
)]
pub struct EdgeId(pub u64);
#[derive(Debug)]
pub enum Error {
Io(std::io::Error),
InvalidMagic,
ChecksumMismatch,
VersionMismatch,
NotFound,
AlreadyExists,
InvalidArgument(String),
Corruption(String),
OutOfMemory,
Unimplemented,
DecryptionFailed,
WriterBusy,
EncryptionAuthFailed,
WriteWriteConflict {
node_id: u64,
},
NodeHasEdges {
node_id: u64,
},
QueryTimeout,
ReadOnly,
QueryMemoryExceeded,
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::Io(e) => write!(f, "I/O error: {e}"),
Error::InvalidMagic => write!(f, "invalid magic bytes"),
Error::ChecksumMismatch => write!(f, "checksum mismatch"),
Error::VersionMismatch => write!(f, "version mismatch"),
Error::NotFound => write!(f, "not found"),
Error::AlreadyExists => write!(f, "already exists"),
Error::InvalidArgument(s) => write!(f, "invalid argument: {s}"),
Error::Corruption(s) => write!(f, "corruption: {s}"),
Error::OutOfMemory => write!(f, "out of memory"),
Error::Unimplemented => write!(f, "not yet implemented"),
Error::DecryptionFailed => write!(f, "decryption failed: wrong key or corrupted data"),
Error::WriterBusy => write!(f, "writer busy: a write transaction is already active"),
Error::EncryptionAuthFailed => write!(
f,
"encryption authentication failed: wrong key or corrupted ciphertext"
),
Error::WriteWriteConflict { node_id } => write!(
f,
"write-write conflict on node {node_id}: another transaction modified this node"
),
Error::NodeHasEdges { node_id } => write!(
f,
"node {node_id} has attached edges and cannot be deleted without removing them first"
),
Error::QueryTimeout => write!(f, "query timeout: deadline exceeded"),
Error::ReadOnly => write!(
f,
"read-only transaction: mutation statements are not allowed in ReadTx::query"
),
Error::QueryMemoryExceeded => write!(
f,
"query memory exceeded: BFS frontier exceeded the configured memory limit"
),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::Io(e) => Some(e),
_ => None,
}
}
}
impl From<std::io::Error> for Error {
fn from(e: std::io::Error) -> Self {
Error::Io(e)
}
}
pub type Result<T> = std::result::Result<T, Error>;
pub fn col_id_of(name: &str) -> u32 {
const FNV_PRIME: u32 = 16_777_619;
const OFFSET_BASIS: u32 = 2_166_136_261;
let mut hash = OFFSET_BASIS;
for byte in name.bytes() {
hash ^= byte as u32;
hash = hash.wrapping_mul(FNV_PRIME);
}
hash
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn page_id_roundtrip() {
let id = PageId(42);
assert_eq!(id.0, 42);
}
#[test]
fn lsn_ordering() {
assert!(Lsn(1) < Lsn(2));
}
#[test]
fn txn_id_copy() {
let t = TxnId(99);
let t2 = t;
assert_eq!(t, t2);
}
#[test]
fn node_id_packing_roundtrip() {
let label_id: u64 = 3;
let slot_id: u64 = 0x0000_BEEF_CAFE;
let packed = (label_id << 48) | (slot_id & 0x0000_FFFF_FFFF_FFFF);
let node = NodeId(packed);
let recovered_label = node.0 >> 48;
let recovered_slot = node.0 & 0x0000_FFFF_FFFF_FFFF;
assert_eq!(recovered_label, label_id);
assert_eq!(recovered_slot, slot_id);
}
#[test]
fn error_display() {
let e = Error::InvalidMagic;
assert!(!e.to_string().is_empty());
}
}