use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum IsolationLevel {
ReadUncommitted,
ReadCommitted,
RepeatableRead,
Serializable,
}
impl IsolationLevel {
pub fn as_str(&self) -> &'static str {
match self {
IsolationLevel::ReadUncommitted => "READ UNCOMMITTED",
IsolationLevel::ReadCommitted => "READ COMMITTED",
IsolationLevel::RepeatableRead => "REPEATABLE READ",
IsolationLevel::Serializable => "SERIALIZABLE",
}
}
pub fn allows_dirty_reads(&self) -> bool {
matches!(self, IsolationLevel::ReadUncommitted)
}
pub fn allows_non_repeatable_reads(&self) -> bool {
matches!(
self,
IsolationLevel::ReadUncommitted | IsolationLevel::ReadCommitted
)
}
pub fn allows_phantom_reads(&self) -> bool {
matches!(
self,
IsolationLevel::ReadUncommitted
| IsolationLevel::ReadCommitted
| IsolationLevel::RepeatableRead
)
}
pub fn strictness_level(&self) -> u8 {
match self {
IsolationLevel::ReadUncommitted => 0,
IsolationLevel::ReadCommitted => 1,
IsolationLevel::RepeatableRead => 2,
IsolationLevel::Serializable => 3,
}
}
pub fn is_at_least_as_strict_as(&self, other: &IsolationLevel) -> bool {
self.strictness_level() >= other.strictness_level()
}
pub fn default() -> Self {
IsolationLevel::ReadCommitted
}
}
impl std::fmt::Display for IsolationLevel {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
impl std::str::FromStr for IsolationLevel {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_uppercase().as_str() {
"READ UNCOMMITTED" | "READ_UNCOMMITTED" => Ok(IsolationLevel::ReadUncommitted),
"READ COMMITTED" | "READ_COMMITTED" => Ok(IsolationLevel::ReadCommitted),
"REPEATABLE READ" | "REPEATABLE_READ" => Ok(IsolationLevel::RepeatableRead),
"SERIALIZABLE" => Ok(IsolationLevel::Serializable),
_ => Err(format!("Unknown isolation level: {}", s)),
}
}
}
impl From<crate::ast::ast::IsolationLevel> for IsolationLevel {
fn from(ast_level: crate::ast::ast::IsolationLevel) -> Self {
match ast_level {
crate::ast::ast::IsolationLevel::ReadUncommitted => IsolationLevel::ReadUncommitted,
crate::ast::ast::IsolationLevel::ReadCommitted => IsolationLevel::ReadCommitted,
crate::ast::ast::IsolationLevel::RepeatableRead => IsolationLevel::RepeatableRead,
crate::ast::ast::IsolationLevel::Serializable => IsolationLevel::Serializable,
}
}
}
impl From<IsolationLevel> for crate::ast::ast::IsolationLevel {
fn from(txn_level: IsolationLevel) -> Self {
match txn_level {
IsolationLevel::ReadUncommitted => crate::ast::ast::IsolationLevel::ReadUncommitted,
IsolationLevel::ReadCommitted => crate::ast::ast::IsolationLevel::ReadCommitted,
IsolationLevel::RepeatableRead => crate::ast::ast::IsolationLevel::RepeatableRead,
IsolationLevel::Serializable => crate::ast::ast::IsolationLevel::Serializable,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_isolation_level_strictness() {
assert!(
IsolationLevel::Serializable.is_at_least_as_strict_as(&IsolationLevel::ReadCommitted)
);
assert!(IsolationLevel::ReadCommitted
.is_at_least_as_strict_as(&IsolationLevel::ReadUncommitted));
assert!(!IsolationLevel::ReadUncommitted
.is_at_least_as_strict_as(&IsolationLevel::Serializable));
}
#[test]
fn test_isolation_level_properties() {
assert!(IsolationLevel::ReadUncommitted.allows_dirty_reads());
assert!(!IsolationLevel::ReadCommitted.allows_dirty_reads());
assert!(IsolationLevel::ReadCommitted.allows_non_repeatable_reads());
assert!(!IsolationLevel::RepeatableRead.allows_non_repeatable_reads());
assert!(IsolationLevel::RepeatableRead.allows_phantom_reads());
assert!(!IsolationLevel::Serializable.allows_phantom_reads());
}
#[test]
fn test_isolation_level_parsing() {
assert_eq!(
"READ COMMITTED".parse::<IsolationLevel>().unwrap(),
IsolationLevel::ReadCommitted
);
assert_eq!(
"serializable".parse::<IsolationLevel>().unwrap(),
IsolationLevel::Serializable
);
}
}