diff --git a/minisql/src/type_system.rs b/minisql/src/type_system.rs index 08a2352..35588cd 100644 --- a/minisql/src/type_system.rs +++ b/minisql/src/type_system.rs @@ -1,6 +1,7 @@ use crate::error::TypeConversionError; -use std::cmp::Ordering; use serde::{Deserialize, Serialize}; +use std::cmp::Ordering; +use std::fmt::format; // ==============Types================ #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -9,7 +10,7 @@ pub enum DbType { Int, Number, Uuid, - Option(Box) + Option(Box), } // ==============Values================ @@ -48,7 +49,7 @@ impl PartialOrd for IndexableValue { (IndexableValue::None(_), IndexableValue::Some(_)) => Some(Ordering::Less), (IndexableValue::Some(_), IndexableValue::None(_)) => Some(Ordering::Greater), (IndexableValue::Some(v0), IndexableValue::Some(v1)) => v0.partial_cmp(v1), - _ => None + _ => None, } } } @@ -64,29 +65,38 @@ impl Ord for IndexableValue { (IndexableValue::Some(_), IndexableValue::None(_)) => Ordering::Greater, (IndexableValue::Some(v0), IndexableValue::Some(v1)) => v0.cmp(v1), _ => - // SAFETY: - // We are using indexable values as keys in key-value maps. - // When validation is done, it can't happen that we will be comparing two values - // of different types. - // Ofcourse another option is to artificialy order e.g. - // None < Some(...) < String < Int < Uuid - // where ... is again None < Some(...) < String < Int < Uuid - // where ... - // infinitely deep total order. But this is pointless for our usecase. - unreachable!() + // SAFETY: + // We are using indexable values as keys in key-value maps. + // When validation is done, it can't happen that we will be comparing two values + // of different types. + // Ofcourse another option is to artificialy order e.g. + // None < Some(...) < String < Int < Uuid + // where ... is again None < Some(...) < String < Int < Uuid + // where ... + // infinitely deep total order. But this is pointless for our usecase. + { + unreachable!() + } } - } } impl DbType { + fn new_n_option(n: usize, inside: DbType) -> DbType { + if n == 0 { + inside + } else { + DbType::Option(Box::new(DbType::new_n_option(n - 1, inside))) + } + } + pub fn is_indexable(&self) -> bool { match self { Self::String => true, Self::Int => true, Self::Number => false, Self::Uuid => true, - Self::Option(type_) => type_.is_indexable() + Self::Option(type_) => type_.is_indexable(), } } @@ -121,24 +131,39 @@ impl DbType { } } + fn from_oid_and_size(oid: i32, size: i16) -> Option { + match (oid, size) { + (25, -2) => Some(DbType::String), + (23, 8) => Some(DbType::Int), + (701, 8) => Some(DbType::Number), + (2950, 16) => Some(DbType::Uuid), + _ => None, + } + } + pub fn type_oid(&self) -> i32 { - // match self { - // Self::String => 25, - // Self::Int => 23, - // Self::Number => 701, - // Self::Uuid => 2950, - // } - todo!() + match self { + Self::String => 25, + Self::Int => 23, + Self::Number => 701, + Self::Uuid => 2950, + Self::Option(t) => { + let oid = t.type_oid(); + let type_part = (oid & 0x0000FFFF) as u16; + let nest_part = ((oid & 0xFFFF0000) >> 16) as u16 + 1; + (type_part | (nest_part << 16)) as i32 + } + } } pub fn type_size(&self) -> i16 { - // match self { - // Self::String => -2, // null terminated string - // Self::Int => 8, - // Self::Number => 8, - // Self::Uuid => 16, - // } - todo!() + match self { + Self::String => -2, // null terminated string + Self::Int => 8, + Self::Number => 8, + Self::Uuid => 16, + Self::Option(t) => t.type_size(), + } } } @@ -216,17 +241,20 @@ impl Value { } } - pub fn as_text_bytes(&self) -> Vec { - // match self { - // Self::Number(n) => format!("{n}").into_bytes(), - // Self::Indexable(i) => match i { - // IndexableValue::String(s) => format!("{s}\0").into_bytes(), - // IndexableValue::Int(i) => format!("{i}").into_bytes(), - // IndexableValue::Uuid(u) => format!("{u}").into_bytes(), - // }, - // } - todo!() + match self { + Self::Number(n) => format!("{n}").into_bytes(), + Self::String(s) => format!("{s}\0").into_bytes(), + Self::Int(i) => format!("{i}").into_bytes(), + Self::Uuid(u) => format!("u{u}").into_bytes(), + Self::None(_) => b"None".into(), + Self::Some(val) => { + let mut bytes = Vec::from(b"Some("); + bytes.append(&mut val.as_text_bytes()); + bytes.push(b')'); + bytes + } + } } pub fn from_text_bytes( @@ -234,30 +262,68 @@ impl Value { type_oid: i32, type_size: i16, ) -> Result { - // match (type_oid, type_size) { - // (701, 8) => { - // let s = std::str::from_utf8(bytes)?; - // let n = s.parse::()?; - // Ok(Value::Number(n)) - // } - // (25, -2) => { - // let s = std::str::from_utf8(bytes)?; - // let s = &s[..s.len() - 1]; // remove null terminator - // Ok(Value::Indexable(IndexableValue::String(s.to_string()))) - // } - // (23, 8) => { - // let s = std::str::from_utf8(bytes)?; - // let n = s.parse::()?; - // Ok(Value::Indexable(IndexableValue::Int(n))) - // } - // (2950, 16) => { - // let s = std::str::from_utf8(bytes)?; - // let n = s.parse::()?; - // Ok(Value::Indexable(IndexableValue::Uuid(n))) - // } - // (oid, size) => Err(TypeConversionError::UnknownType { oid, size }), - // } - todo!() + let text = std::str::from_utf8(bytes)?; + let type_part = (type_oid & 0x0000FFFF) as u16; + let nest_part = ((type_oid as u32 & 0xFFFF0000) >> 16) as u16; + + Self::internal_from_text_bytes(text, type_part, nest_part, type_size) + } + + fn internal_from_text_bytes( + text: &str, + type_part: u16, + nest_part: u16, + type_size: i16, + ) -> Result { + if text == "None" { + let db_type = + DbType::from_oid_and_size(type_part as i32, type_size).ok_or_else(|| { + TypeConversionError::UnknownType { + oid: type_part as i32, + size: type_size, + } + })?; + return if nest_part == 0 { + Ok(Value::None(db_type)) + } else { + Ok(Value::None(DbType::new_n_option( + nest_part as usize, + db_type, + ))) + }; + } + + if text.starts_with("Some(") { + let val = Self::from_text_bytes( + &text[5..text.len() - 1].as_bytes(), + type_part as i32, + type_size, + )?; + return Ok(Value::Some(Box::new(val))); + } + + match (type_part, type_size) { + (701, 8) => { + let n = text.parse::()?; + Ok(Value::Number(n)) + } + (25, -2) => { + let s = &text[..text.len() - 1]; // remove null terminator + Ok(Value::String(s.to_string())) + } + (23, 8) => { + let n = text.parse::()?; + Ok(Value::Int(n)) + } + (2950, 16) => { + let n = text.parse::()?; + Ok(Value::Uuid(n)) + } + (oid, size) => Err(TypeConversionError::UnknownType { + oid: oid as i32, + size, + }), + } } } @@ -267,7 +333,9 @@ impl IndexableValue { IndexableValue::String(str) => Value::String(str), IndexableValue::Int(n) => Value::Int(n), IndexableValue::Uuid(id) => Value::Uuid(id), - IndexableValue::Some(indexable_value) => Value::Some(Box::new(indexable_value.to_value())), + IndexableValue::Some(indexable_value) => { + Value::Some(Box::new(indexable_value.to_value())) + } IndexableValue::None(type_) => Value::None(type_), } } @@ -322,7 +390,6 @@ impl From for Value { } } - // Own string serialization so enums can be used as keys in maps impl From for String { fn from(value: IndexableValue) -> Self {