diff --git a/minisql/src/error.rs b/minisql/src/error.rs index fe11cd2..0574aff 100644 --- a/minisql/src/error.rs +++ b/minisql/src/error.rs @@ -1,3 +1,5 @@ +use std::num::{ParseFloatError, ParseIntError}; +use std::str::Utf8Error; use thiserror::Error; use crate::internals::row::ColumnPosition; use crate::schema::{ColumnName, TableName}; @@ -25,3 +27,18 @@ pub enum Error { #[error("table {0} cannot be indexed on column {1}")] AttemptToIndexNonIndexableColumn(TableName, ColumnName), } + +#[derive(Debug, Error)] +pub enum TypeConversionError { + #[error("failed to decode bytes to string")] + TextDecodeFailed(#[from] Utf8Error), + #[error("failed to parse float from text")] + NumberDecodeFailed(#[from] ParseFloatError), + #[error("failed to parse int from text")] + IntDecodeFailed(#[from] ParseIntError), + #[error("uknown type with oid {oid} and size {size}")] + UnknownType { + oid: i32, + size: i32 + } +} diff --git a/minisql/src/type_system.rs b/minisql/src/type_system.rs index 4edc3ec..caa85ab 100644 --- a/minisql/src/type_system.rs +++ b/minisql/src/type_system.rs @@ -1,3 +1,5 @@ +use crate::error::TypeConversionError; + // ==============Types================ #[derive(Debug, Clone, Copy)] pub enum DbType { @@ -39,4 +41,155 @@ impl Value { }, } } + + pub fn type_oid(&self) -> i32 { + match self { + Self::Number(_) => 701, + Self::Indexable(val) => match val { + IndexableValue::String(_) => 25, + IndexableValue::Int(_) => 23, + IndexableValue::Uuid(_) => 2950, + }, + } + } + + pub fn type_size(&self) -> i32 { + match self { + Self::Number(_) => 8, + Self::Indexable(val) => match val { + IndexableValue::String(_) => -2, // null terminated string + IndexableValue::Int(_) => 8, + IndexableValue::Uuid(_) => 16, + }, + } + } + + 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(), + }, + } + } + + pub fn from_text_bytes(bytes: &[u8], type_oid: i32, type_size: i32) -> 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 }), + } + } +} + +mod tests { + use crate::error::TypeConversionError::UnknownType; + use super::{Value, IndexableValue}; + + #[test] + fn test_encode_number() { + let value = Value::Number(123.456); + let oid = value.type_oid(); + let size = value.type_size(); + + let bytes = value.as_text_bytes(); + let from_bytes = Value::from_text_bytes(&bytes, oid, size).unwrap(); + + assert_eq!(value, from_bytes); + + assert_eq!(oid, 701); + assert_eq!(size, 8); + } + + #[test] + fn test_encode_string() { + let value = Value::Indexable(IndexableValue::String("hello".to_string())); + let oid = value.type_oid(); + let size = value.type_size(); + + let bytes = value.as_text_bytes(); + let from_bytes = Value::from_text_bytes(&bytes, oid, size).unwrap(); + + assert_eq!(value, from_bytes); + + assert_eq!(oid, 25); + assert_eq!(size, -2); + } + + #[test] + fn test_encode_string_utf8() { + let value = Value::Indexable(IndexableValue::String("#速度与激情9 早上好中国 现在我有冰激淋 我很喜欢冰激淋 但是《速度与激情9》比冰激淋 🍧🍦🍨".to_string())); + let oid = value.type_oid(); + let size = value.type_size(); + + let bytes = value.as_text_bytes(); + let from_bytes = Value::from_text_bytes(&bytes, oid, size).unwrap(); + + assert_eq!(value, from_bytes); + + assert_eq!(oid, 25); + assert_eq!(size, -2); + } + + #[test] + fn test_encode_int() { + let value = Value::Indexable(IndexableValue::Int(123)); + let oid = value.type_oid(); + let size = value.type_size(); + + let bytes = value.as_text_bytes(); + let from_bytes = Value::from_text_bytes(&bytes, oid, size).unwrap(); + + assert_eq!(value, from_bytes); + + assert_eq!(oid, 23); + assert_eq!(size, 8); + } + + #[test] + fn test_encode_uuid() { + let value = Value::Indexable(IndexableValue::Uuid(123)); + let oid = value.type_oid(); + let size = value.type_size(); + + let bytes = value.as_text_bytes(); + let from_bytes = Value::from_text_bytes(&bytes, oid, size).unwrap(); + + assert_eq!(value, from_bytes); + + assert_eq!(oid, 2950); + assert_eq!(size, 16); + } + + #[test] + fn test_mismatched_size() { + let value = Value::Indexable(IndexableValue::Uuid(123)); + let oid = value.type_oid(); + let size = 8; + + let bytes = value.as_text_bytes(); + let from_bytes = Value::from_text_bytes(&bytes, oid, size); + + assert!(matches!(from_bytes, Err(UnknownType { oid: 2950, size: 8 }))) + } }