diff --git a/Cargo.lock b/Cargo.lock index 2944fae..d76a63c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -284,6 +284,7 @@ name = "minisql" version = "0.1.0" dependencies = [ "bimap", + "proto", "serde", "thiserror", ] @@ -429,6 +430,9 @@ version = "0.1.0" dependencies = [ "async-trait", "bincode", + "rand", + "rand_pcg", + "rand_seeder", "thiserror", "tokio", ] @@ -568,8 +572,6 @@ dependencies = [ "parser", "proto", "rand", - "rand_pcg", - "rand_seeder", "serde_json", "tokio", ] diff --git a/minisql/Cargo.toml b/minisql/Cargo.toml index 3bfcbfa..f44d711 100644 --- a/minisql/Cargo.toml +++ b/minisql/Cargo.toml @@ -10,3 +10,4 @@ rust-version = "1.74" bimap = { version = "0.6.3", features = ["serde"] } serde = { version = "1.0.196", features = ["derive"] } thiserror = "1.0.50" +proto = { path = "../proto" } diff --git a/minisql/src/error.rs b/minisql/src/error.rs index adc2933..d74af4e 100644 --- a/minisql/src/error.rs +++ b/minisql/src/error.rs @@ -1,5 +1,6 @@ use crate::schema::{ColumnName, TableName}; use crate::type_system::Uuid; +use proto::message::primitive::pgoid::PgOid; use std::num::{ParseFloatError, ParseIntError}; use std::str::Utf8Error; use thiserror::Error; @@ -23,5 +24,5 @@ pub enum TypeConversionError { #[error("failed to parse int from text")] IntDecodeFailed(#[from] ParseIntError), #[error("unknown type with oid {oid} and size {size}")] - UnknownType { oid: i32, size: i16 }, + UnknownType { oid: PgOid, size: i16 }, } diff --git a/minisql/src/internals/table.rs b/minisql/src/internals/table.rs index 66df647..7a17288 100644 --- a/minisql/src/internals/table.rs +++ b/minisql/src/internals/table.rs @@ -84,18 +84,18 @@ impl Table { value: Value, ) -> DbResult + '_> { let restrict_columns_of_row = move |row: Row| row.restrict_columns(&selected_columns); - match value { - Value::Indexable(value) => match self.fetch_ids_from_index(column, &value)? { + match value.to_indexable() { + Some(indexable_value) => match self.fetch_ids_from_index(column, &indexable_value)? { Some(ids) => Ok(self .get_rows_by_ids(ids) .into_iter() .map(restrict_columns_of_row)), None => Ok(self - .get_rows_by_value(column, &Value::Indexable(value)) + .get_rows_by_value(column, &value) .into_iter() .map(restrict_columns_of_row)), }, - _ => Ok(self + None => Ok(self .get_rows_by_value(column, &value) .into_iter() .map(restrict_columns_of_row)), @@ -112,8 +112,8 @@ impl Table { } for (column, column_index) in &mut self.indexes { - if let Value::Indexable(val) = &row[*column] { - column_index.add(val.clone(), id) + if let Some(indexable_value) = &row[*column].to_indexable() { + column_index.add(indexable_value.clone(), id) } } @@ -126,8 +126,8 @@ impl Table { match self.rows.remove(&id) { Some(row) => { for (column, column_index) in &mut self.indexes { - if let Value::Indexable(value) = &row[*column] { - let _ = column_index.remove(value, id); + if let Some(indexable_value) = &row[*column].to_indexable() { + let _ = column_index.remove(indexable_value, id); }; } 1 @@ -167,12 +167,12 @@ impl Table { } pub fn delete_rows_where_eq(&mut self, column: Column, value: Value) -> DbResult { - match value { - Value::Indexable(value) => match self.fetch_ids_from_index(column, &value)? { + match value.to_indexable() { + Some(indexable_value) => match self.fetch_ids_from_index(column, &indexable_value)? { Some(ids) => Ok(self.delete_rows_by_ids(ids)), - None => Ok(self.delete_rows_by_value(column, &Value::Indexable(value))), + None => Ok(self.delete_rows_by_value(column, &value)), }, - _ => Ok(self.delete_rows_by_value(column, &value)), + None => Ok(self.delete_rows_by_value(column, &value)), } } @@ -225,9 +225,9 @@ fn update_index_from_table( column: Column, ) -> DbResult<()> { for (id, row) in &table.rows { - let value = match &row[column] { - Value::Indexable(value) => value.clone(), - _ => { + let value = match &row[column].to_indexable() { + Some(indexable_value) => indexable_value.clone(), + None => { let column_name: ColumnName = table.schema.column_name_from_column(column); return Err(RuntimeError::AttemptToIndexNonIndexableColumn( table.table_name().to_string(), diff --git a/minisql/src/interpreter.rs b/minisql/src/interpreter.rs index 09b5e18..0a569cd 100644 --- a/minisql/src/interpreter.rs +++ b/minisql/src/interpreter.rs @@ -210,9 +210,6 @@ mod tests { #[test] fn test_insert_select_basic1() { - use IndexableValue::*; - use Value::*; - let mut state = State::new(); let users_schema = users_schema(); let users = 0; @@ -222,9 +219,9 @@ mod tests { .unwrap(); let (id, name, age) = ( - Indexable(Uuid(0)), - Indexable(String("Plato".to_string())), - Indexable(Int(64)), + Value::Uuid(0), + Value::String("Plato".to_string()), + Value::Int(64), ); state .interpret(Operation::Insert( @@ -254,9 +251,7 @@ mod tests { #[test] fn test_insert_select_basic2() { use Condition::*; - use IndexableValue::*; use Operation::*; - use Value::*; let mut state = State::new(); let users_schema = users_schema(); @@ -268,9 +263,9 @@ mod tests { state.interpret(CreateTable(users_schema.clone())).unwrap(); let (id0, name0, age0) = ( - Indexable(Uuid(0)), - Indexable(String("Plato".to_string())), - Indexable(Int(64)), + Value::Uuid(0), + Value::String("Plato".to_string()), + Value::Int(64), ); state .interpret(Insert( @@ -280,9 +275,9 @@ mod tests { .unwrap(); let (id1, name1, age1) = ( - Indexable(Uuid(1)), - Indexable(String("Aristotle".to_string())), - Indexable(Int(20)), + Value::Uuid(1), + Value::String("Aristotle".to_string()), + Value::Int(20), ); state .interpret(Insert( @@ -364,9 +359,7 @@ mod tests { #[test] fn test_delete() { use Condition::*; - use IndexableValue::*; use Operation::*; - use Value::*; let mut state = State::new(); let users_schema = users_schema(); @@ -377,9 +370,9 @@ mod tests { state.interpret(CreateTable(users_schema.clone())).unwrap(); let (id0, name0, age0) = ( - Indexable(Uuid(0)), - Indexable(String("Plato".to_string())), - Indexable(Int(64)), + Value::Uuid(0), + Value::String("Plato".to_string()), + Value::Int(64), ); state .interpret(Insert( @@ -389,9 +382,9 @@ mod tests { .unwrap(); let (id1, name1, age1) = ( - Indexable(Uuid(1)), - Indexable(String("Aristotle".to_string())), - Indexable(Int(20)), + Value::Uuid(1), + Value::String("Aristotle".to_string()), + Value::Int(20), ); state .interpret(Insert( @@ -427,9 +420,7 @@ mod tests { #[test] fn test_index() { - use IndexableValue::*; use Operation::*; - use Value::*; let mut state = State::new(); let users_schema = users_schema(); @@ -444,9 +435,9 @@ mod tests { .unwrap(); let (id0, name0, age0) = ( - Indexable(Uuid(0)), - Indexable(String("Plato".to_string())), - Indexable(Int(64)), + Value::Uuid(0), + Value::String("Plato".to_string()), + Value::Int(64), ); state .interpret(Insert( @@ -456,9 +447,9 @@ mod tests { .unwrap(); let (id1, name1, age1) = ( - Indexable(Uuid(1)), - Indexable(String("Aristotle".to_string())), - Indexable(Int(20)), + Value::Uuid(1), + Value::String("Aristotle".to_string()), + Value::Int(20), ); state .interpret(Insert( @@ -480,7 +471,7 @@ mod tests { let aristotle_id = 1; let plato_ids = index - .get(&String("Plato".to_string())) + .get(&IndexableValue::String("Plato".to_string())) .cloned() .unwrap_or(HashSet::new()); assert!(plato_ids.contains(&plato_id)); @@ -490,11 +481,9 @@ mod tests { } pub fn example() { - use crate::type_system::{DbType, IndexableValue, Value}; + use crate::type_system::{DbType, Value}; use Condition::*; - use IndexableValue::*; use Operation::*; - use Value::*; let id_column: Column = 0; let name_column: Column = 1; @@ -520,9 +509,9 @@ pub fn example() { .unwrap(); let (id0, name0, age0) = ( - Indexable(Uuid(0)), - Indexable(String("Plato".to_string())), - Indexable(Int(64)), + Value::Uuid(0), + Value::String("Plato".to_string()), + Value::Int(64), ); println!("==INSERT Plato=="); state @@ -533,9 +522,9 @@ pub fn example() { .unwrap(); let (id1, name1, age1) = ( - Indexable(Uuid(1)), - Indexable(String("Aristotle".to_string())), - Indexable(Int(20)), + Value::Uuid(1), + Value::String("Aristotle".to_string()), + Value::Int(20), ); println!("==INSERT Aristotle=="); state diff --git a/minisql/src/schema.rs b/minisql/src/schema.rs index ed5ddce..6833a3d 100644 --- a/minisql/src/schema.rs +++ b/minisql/src/schema.rs @@ -1,7 +1,7 @@ use crate::internals::row::Row; use crate::operation::{ColumnSelection, InsertionValues}; use crate::result::DbResult; -use crate::type_system::{DbType, IndexableValue, Uuid, Value}; +use crate::type_system::{DbType, IndexableValue, Uuid}; use bimap::BiMap; use serde::{Deserialize, Serialize}; @@ -51,7 +51,7 @@ impl TableSchema { } pub fn column_type(&self, column: Column) -> DbType { - self.types[column] + self.types[column].clone() } pub fn get_columns(&self) -> Vec<&ColumnName> { @@ -88,7 +88,7 @@ impl TableSchema { pub fn get_type_at(&self, column_name: &ColumnName) -> Option { let position = self.get_column(column_name)?; - self.types.get(position).copied() + self.types.get(position).cloned() } pub fn is_primary(&self, column: Column) -> bool { @@ -115,8 +115,13 @@ impl TableSchema { let row: Row = Row::new_from_insertion_values(insertion_values); let id: Uuid = match row.get(self.primary_key) { - Some(Value::Indexable(IndexableValue::Uuid(id))) => *id, - Some(_) => unreachable!(), // SAFETY: Should be guaranteed by validation (type-safety) + Some(value) => { + match value.to_indexable() { + Some(IndexableValue::Uuid(id)) => id, + Some(_) => unreachable!(), // SAFETY: Should be guaranteed by validation (type-safety) + None => unreachable!(), // SAFETY: Should be guaranteed by validation (type-safety) + } + } None => unreachable!(), // SAFETY: Should be guaranteed by validation (missing columns) }; diff --git a/minisql/src/type_system.rs b/minisql/src/type_system.rs index e13bfb7..e30edea 100644 --- a/minisql/src/type_system.rs +++ b/minisql/src/type_system.rs @@ -1,62 +1,157 @@ use crate::error::TypeConversionError; +use proto::message::primitive::pgoid::PgOid; use serde::{Deserialize, Serialize}; +use std::cmp::Ordering; // ==============Types================ -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum DbType { String, Int, Number, Uuid, + Option(Box), } // ==============Values================ pub type Uuid = u64; - #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[serde(try_from = "String", into = "String")] pub enum Value { - // Note that it doesn't really make sense to compare floats on equality without specifying - // precision. You can ofcourse convert a float to string or to a bytevector and then compare - // equality of those, but that's not the right equality. And ofcourse Rust designers are aware - // of this, so floats don't implement the Eq trait. - // This ofcourse complicates indexing of Number columns. - // - // Either we'd have to design a specific key-value map data-structure where keys are floats, - // s.t. to index with a given float K you also specify a tolerance error so that the resulting - // value set will contain all values whose keys are close to K within that tolerence. This - // seems highly non-trivial. - // - // So we choose to make a distinction between indexable and non-indexable types, and Number is - // not indexable. Number(f64), - Indexable(IndexableValue), + String(String), + Int(u64), + Uuid(Uuid), + Some(Box), + // Note that without polymorphic types, + // we can't type None on its own. Hence we require additional type information in the value + // itself. + None(DbType), } -#[derive(Debug, Ord, Eq, Clone, PartialOrd, PartialEq, Serialize, Deserialize)] +#[derive(Debug, Eq, Clone, PartialEq, Serialize, Deserialize)] #[serde(try_from = "String", into = "String")] pub enum IndexableValue { String(String), Int(u64), Uuid(Uuid), + Some(Box), + None(DbType), // See Value::None +} + +impl PartialOrd for IndexableValue { + fn partial_cmp(&self, other: &Self) -> Option { + match (self, other) { + (IndexableValue::String(s0), IndexableValue::String(s1)) => s0.partial_cmp(s1), + (IndexableValue::Int(n0), IndexableValue::Int(n1)) => n0.partial_cmp(n1), + (IndexableValue::Uuid(id0), IndexableValue::Uuid(id1)) => id0.partial_cmp(id1), + (IndexableValue::None(_), IndexableValue::None(_)) => Some(Ordering::Equal), + (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, + } + } +} + +impl Ord for IndexableValue { + fn cmp(&self, other: &Self) -> Ordering { + match (self, other) { + (IndexableValue::String(s0), IndexableValue::String(s1)) => s0.cmp(s1), + (IndexableValue::Int(n0), IndexableValue::Int(n1)) => n0.cmp(n1), + (IndexableValue::Uuid(id0), IndexableValue::Uuid(id1)) => id0.cmp(id1), + (IndexableValue::None(_), IndexableValue::None(_)) => Ordering::Equal, + (IndexableValue::None(_), IndexableValue::Some(_)) => Ordering::Less, + (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!() + } + } + } } 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(), } } - pub fn type_oid(&self) -> i32 { + fn to_json_key_string(self) -> String { match self { - Self::String => 25, - Self::Int => 23, - Self::Number => 701, - Self::Uuid => 2950, + DbType::String => format!("String()"), + DbType::Int => format!("Int()"), + DbType::Number => format!("Number()"), + DbType::Uuid => format!("Uuid()"), + DbType::Option(type_) => format!("Option({})", type_.to_json_key_string()), + } + } + + fn parse_json_key_string(str: &str) -> Result { + if !str.ends_with(')') { + return Err(format!("Invalid DbType: {}", str)); + } + + if str.starts_with("String(") { + Ok(DbType::String) + } else if str.starts_with("Int(") { + Ok(DbType::Int) + } else if str.starts_with("Number(") { + Ok(DbType::Number) + } else if str.starts_with("Uuid(") { + Ok(DbType::Uuid) + } else if str.starts_with("Option(") { + let s = &str[7..str.len() - 1]; + Ok(DbType::Option(Box::new(Self::parse_json_key_string(s)?))) + } else { + Err(format!("Invalid DbType: {}", str)) + } + } + + fn from_oid_and_size(oid: PgOid, size: i16) -> Option { + let (type_part, _) = oid.as_nested(); + match (type_part, 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) -> PgOid { + match self { + Self::String => PgOid::Simple(25), + Self::Int => PgOid::Simple(23), + Self::Number => PgOid::Simple(701), + Self::Uuid => PgOid::Simple(2950), + Self::Option(t) => { + let (inner_type, inner_nest) = t.type_oid().as_nested(); + PgOid::Nested(inner_type, inner_nest + 1) + } } } @@ -66,6 +161,7 @@ impl DbType { Self::Int => 8, Self::Number => 8, Self::Uuid => 16, + Self::Option(t) => t.type_size(), } } } @@ -74,137 +170,266 @@ impl Value { pub fn to_type(&self) -> DbType { match self { Self::Number(_) => DbType::Number, - Self::Indexable(val) => match val { - IndexableValue::String(_) => DbType::String, - IndexableValue::Int(_) => DbType::Int, - IndexableValue::Uuid(_) => DbType::Uuid, - }, + Self::String(_) => DbType::String, + Self::Int(_) => DbType::Int, + Self::Uuid(_) => DbType::Uuid, + Self::Some(val) => DbType::Option(Box::new(val.to_type())), + Self::None(type_) => type_.clone(), } } + + pub fn to_indexable(&self) -> Option { + match self { + Self::Number(_) => None, + Self::String(s) => Some(IndexableValue::String(s.to_string())), + Self::Int(n) => Some(IndexableValue::Int(*n)), + Self::Uuid(id) => Some(IndexableValue::Uuid(*id)), + Self::Some(val) => val.to_indexable(), + Self::None(type_) => Some(IndexableValue::None(type_.clone())), + } + } + + fn to_json_key_string(self) -> String { + match self { + Self::Number(x) => format!("Number({x})"), + Self::String(s) => format!("String({s})"), + Self::Int(i) => format!("Int({i})"), + Self::Uuid(u) => format!("Uuid({u})"), + Self::Some(val) => format!("Some({})", val.to_json_key_string()), + Self::None(_type_) => "None()".to_string(), + } + } + + // We don't really need this, since only indexable values are keys in maps. It is here for consistency. + fn parse_json_key_string(str: &str) -> Result { + if !str.ends_with(')') { + return Err(format!("Invalid IndexableValue: {}", str)); + } + + if str.starts_with("Number(") { + let s = str[7..str.len() - 1].to_string(); + let n = s + .parse::() + .map_err(|e| format!("Invalid Number: {}", e))?; + Ok(Self::Number(n)) + } else if str.starts_with("String(") { + let s = str[7..str.len() - 1].to_string(); + Ok(Self::String(s)) + } else if str.starts_with("Int(") { + let s = str[4..str.len() - 1].to_string(); + let i = s + .parse::() + .map_err(|e| format!("Invalid Int: {}", e))?; + Ok(Self::Int(i)) + } else if str.starts_with("Uuid(") { + let s = str[5..str.len() - 1].to_string(); + let u = s + .parse::() + .map_err(|e| format!("Invalid UUID: {}", e))?; + Ok(Self::Uuid(u)) + } else if str.starts_with("Some(") { + let s = str[5..str.len() - 1].to_string(); + let val: Value = Self::parse_json_key_string(&s)?; + Ok(Self::Some(Box::new(val))) + } else if str.starts_with("None(") { + let s = str[5..str.len() - 1].to_string(); + let type_: DbType = TryFrom::try_from(s)?; + Ok(Value::None(type_)) + } else { + Err(format!("Invalid IndexableValue: {}", str)) + } + } + 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(), - }, + 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( bytes: &[u8], - type_oid: i32, + type_oid: PgOid, type_size: i16, ) -> Result { - match (type_oid, type_size) { + let text = std::str::from_utf8(bytes)?; + let (type_part, nest_part) = type_oid.as_nested(); + + 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 oid = PgOid::Nested(type_part, nest_part); + let db_type = DbType::from_oid_and_size(oid, type_size).ok_or_else(|| { + TypeConversionError::UnknownType { + oid, + 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 inner = &text[5..text.len() - 1]; + let val = Self::internal_from_text_bytes(inner, type_part, nest_part - 1, type_size)?; + return Ok(Value::Some(Box::new(val))); + } + + match (type_part, type_size) { (701, 8) => { - let s = std::str::from_utf8(bytes)?; - let n = s.parse::()?; + let n = text.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()))) + let s = &text[..text.len() - 1]; // remove null terminator + Ok(Value::String(s.to_string())) } (23, 8) => { - let s = std::str::from_utf8(bytes)?; - let n = s.parse::()?; - Ok(Value::Indexable(IndexableValue::Int(n))) + let n = text.parse::()?; + Ok(Value::Int(n)) } (2950, 16) => { - let s = std::str::from_utf8(bytes)?; - let n = s.parse::()?; - Ok(Value::Indexable(IndexableValue::Uuid(n))) + let n = text[1..].parse::()?; + Ok(Value::Uuid(n)) } - (oid, size) => Err(TypeConversionError::UnknownType { oid, size }), + (oid, size) => Err(TypeConversionError::UnknownType { + oid: PgOid::Nested(oid, nest_part), + size, + }), } } } +impl IndexableValue { + fn to_value(self) -> Value { + match self { + 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::None(type_) => Value::None(type_), + } + } + + fn to_json_key_string(self) -> String { + match self { + IndexableValue::String(s) => format!("String({s})"), + IndexableValue::Int(i) => format!("Int({i})"), + IndexableValue::Uuid(u) => format!("Uuid({u})"), + IndexableValue::Some(val) => format!("Some({})", val.to_json_key_string()), + IndexableValue::None(_type_) => "None()".to_string(), + } + } + + fn parse_json_key_string(str: &str) -> Result { + if !str.ends_with(')') { + return Err(format!("Invalid IndexableValue: {}", str)); + } + + if str.starts_with("String(") { + let s = str[7..str.len() - 1].to_string(); + Ok(IndexableValue::String(s)) + } else if str.starts_with("Int(") { + let s = str[4..str.len() - 1].to_string(); + let i = s + .parse::() + .map_err(|e| format!("Invalid Int: {}", e))?; + Ok(IndexableValue::Int(i)) + } else if str.starts_with("Uuid(") { + let s = str[5..str.len() - 1].to_string(); + let u = s + .parse::() + .map_err(|e| format!("Invalid UUID: {}", e))?; + Ok(IndexableValue::Uuid(u)) + } else if str.starts_with("Some(") { + let s = str[5..str.len() - 1].to_string(); + let val: IndexableValue = Self::parse_json_key_string(&s)?; + Ok(IndexableValue::Some(Box::new(val))) + } else if str.starts_with("None(") { + let s = str[5..str.len() - 1].to_string(); + let type_: DbType = DbType::parse_json_key_string(&s)?; + Ok(IndexableValue::None(type_)) + } else { + Err(format!("Invalid IndexableValue: {}", str)) + } + } +} + +impl From for Value { + fn from(indexable_value: IndexableValue) -> Self { + indexable_value.to_value() + } +} + // Own string serialization so enums can be used as keys in maps impl From for String { fn from(value: IndexableValue) -> Self { - match value { - IndexableValue::String(s) => format!("String({s})"), - IndexableValue::Int(i) => format!("Int({i})"), - IndexableValue::Uuid(u) => format!("Uuid({u})"), - } + value.to_json_key_string() + } +} + +impl From for String { + fn from(type_: DbType) -> Self { + type_.to_json_key_string() + } +} + +impl TryFrom for DbType { + type Error = String; + fn try_from(str: String) -> Result { + Self::parse_json_key_string(&str) } } impl TryFrom for IndexableValue { type Error = String; - fn try_from(value: String) -> Result { - if !value.ends_with(')') { - return Err(format!("Invalid IndexableValue: {}", value)); - } - - if value.starts_with("String(") { - let s = value[7..value.len() - 1].to_string(); - return Ok(Self::String(s)); - } - - if value.starts_with("Int(") { - let s = value[4..value.len() - 1].to_string(); - let i = s - .parse::() - .map_err(|e| format!("Invalid Int: {}", e))?; - return Ok(Self::Int(i)); - } - - if value.starts_with("Uuid(") { - let s = value[5..value.len() - 1].to_string(); - let u = s - .parse::() - .map_err(|e| format!("Invalid UUID: {}", e))?; - return Ok(Self::Uuid(u)); - } - - Err(format!("Invalid IndexableValue: {}", value)) + fn try_from(str: String) -> Result { + Self::parse_json_key_string(&str) } } impl From for String { fn from(value: Value) -> Self { - match value { - Value::Number(n) => format!("Number({n})"), - Value::Indexable(i) => format!("Indexable({})", String::from(i)), - } + value.to_json_key_string() } } impl TryFrom for Value { type Error = String; fn try_from(value: String) -> Result { - if !value.ends_with(')') { - return Err(format!("Invalid Value: {}", value)); - } - - if value.starts_with("Number(") { - let s = value[7..value.len() - 1].to_string(); - let n = s - .parse::() - .map_err(|e| format!("Invalid Number: {}", e))?; - return Ok(Self::Number(n)); - } - - if value.starts_with("Indexable(") { - let s = value[10..value.len() - 1].to_string(); - let i = IndexableValue::try_from(s)?; - return Ok(Self::Indexable(i)); - } - - Err(format!("Invalid Value: {}", value)) + Value::parse_json_key_string(&value) } } #[cfg(test)] mod tests { - use super::{IndexableValue, Value}; + use super::{DbType, Value}; use crate::error::TypeConversionError::UnknownType; - use crate::type_system::Value::{Indexable, Number}; + use proto::message::primitive::pgoid::PgOid; #[test] fn test_encode_number() { @@ -217,14 +442,15 @@ mod tests { let from_bytes = Value::from_text_bytes(&bytes, oid, size).unwrap(); assert_eq!(value, from_bytes); + assert_eq!(bytes, b"123.456"); - assert_eq!(oid, 701); + assert_eq!(oid.as_simple(), 701); assert_eq!(size, 8); } #[test] fn test_encode_string() { - let value = Value::Indexable(IndexableValue::String("hello".to_string())); + let value = Value::String("hello".to_string()); let vtype = value.to_type(); let oid = vtype.type_oid(); let size = vtype.type_size(); @@ -233,14 +459,15 @@ mod tests { let from_bytes = Value::from_text_bytes(&bytes, oid, size).unwrap(); assert_eq!(value, from_bytes); + assert_eq!(bytes, b"hello\0"); - assert_eq!(oid, 25); + assert_eq!(oid.as_simple(), 25); assert_eq!(size, -2); } #[test] fn test_encode_string_utf8() { - let value = Value::Indexable(IndexableValue::String("#速度与激情9 早上好中国 现在我有冰激淋 我很喜欢冰激淋 但是《速度与激情9》比冰激淋 🍧🍦🍨".to_string())); + let value = Value::String("#速度与激情9 早上好中国 现在我有冰激淋 我很喜欢冰激淋 但是《速度与激情9》比冰激淋 🍧🍦🍨".to_string()); let vtype = value.to_type(); let oid = vtype.type_oid(); let size = vtype.type_size(); @@ -250,13 +477,13 @@ mod tests { assert_eq!(value, from_bytes); - assert_eq!(oid, 25); + assert_eq!(oid.as_simple(), 25); assert_eq!(size, -2); } #[test] fn test_encode_int() { - let value = Value::Indexable(IndexableValue::Int(123)); + let value = Value::Int(123); let vtype = value.to_type(); let oid = vtype.type_oid(); let size = vtype.type_size(); @@ -265,14 +492,15 @@ mod tests { let from_bytes = Value::from_text_bytes(&bytes, oid, size).unwrap(); assert_eq!(value, from_bytes); + assert_eq!(bytes, b"123"); - assert_eq!(oid, 23); + assert_eq!(oid.as_simple(), 23); assert_eq!(size, 8); } #[test] fn test_encode_uuid() { - let value = Value::Indexable(IndexableValue::Uuid(123)); + let value = Value::Uuid(123); let vtype = value.to_type(); let oid = vtype.type_oid(); let size = vtype.type_size(); @@ -281,14 +509,15 @@ mod tests { let from_bytes = Value::from_text_bytes(&bytes, oid, size).unwrap(); assert_eq!(value, from_bytes); + assert_eq!(bytes, b"u123"); - assert_eq!(oid, 2950); + assert_eq!(oid.as_simple(), 2950); assert_eq!(size, 16); } #[test] fn test_mismatched_size() { - let value = Value::Indexable(IndexableValue::Uuid(123)); + let value = Value::Uuid(123); let vtype = value.to_type(); let oid = vtype.type_oid(); let size = 8; @@ -296,22 +525,24 @@ mod tests { let bytes = value.as_text_bytes(); let from_bytes = Value::from_text_bytes(&bytes, oid, size); + println!("{from_bytes:?}"); + assert!(matches!( from_bytes, - Err(UnknownType { oid: 2950, size: 8 }) + Err(UnknownType { + oid: PgOid::Nested(2950, 0), + size: 8 + }) )) } #[test] fn test_value_stringification() { let pairs = vec![ - (Number(1.0), "Number(1)"), - ( - Indexable(IndexableValue::String("hello".to_string())), - "Indexable(String(hello))", - ), - (Indexable(IndexableValue::Int(123)), "Indexable(Int(123))"), - (Indexable(IndexableValue::Uuid(123)), "Indexable(Uuid(123))"), + (Value::Number(1.0), "Number(1)"), + (Value::String("hello".to_string()), "String(hello)"), + (Value::Int(123), "Int(123)"), + (Value::Uuid(123), "Uuid(123)"), ]; for (value, string) in pairs { @@ -322,4 +553,75 @@ mod tests { assert_eq!(deserialized, Ok(value)); } } + + #[test] + fn test_encode_nested_value() { + let value = Value::Some(Box::new(Value::Some(Box::new(Value::Int(123))))); + let vtype = value.to_type(); + let oid = vtype.type_oid(); + let size = vtype.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!(bytes, b"Some(Some(123))"); + + assert_eq!(oid.as_simple(), PgOid::Nested(23, 2).as_simple()); + assert_eq!(size, 8); + } + + #[test] + fn test_encode_nested_none() { + let value = Value::Some(Box::new(Value::Some(Box::new(Value::None( + DbType::Option(Box::new(DbType::Int)), + ))))); + let vtype = value.to_type(); + let oid = vtype.type_oid(); + let size = vtype.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!(bytes, b"Some(Some(None))"); + + assert_eq!(oid.as_simple(), PgOid::Nested(23, 3).as_simple()); + assert_eq!(size, 8); + } + + #[test] + fn test_encode_surface_none() { + let value = Value::Some(Box::new(Value::None(DbType::Number))); + let vtype = value.to_type(); + let oid = vtype.type_oid(); + let size = vtype.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!(bytes, b"Some(None)"); + + assert_eq!(oid.as_simple(), PgOid::Nested(701, 1).as_simple()); + assert_eq!(size, 8); + } + + #[test] + fn test_encode_none() { + let value = Value::None(DbType::Number); + let vtype = value.to_type(); + let oid = vtype.type_oid(); + let size = vtype.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!(bytes, b"None"); + + assert_eq!(oid.as_simple(), 701); + assert_eq!(size, 8); + } } diff --git a/parser/src/parsing/common.rs b/parser/src/parsing/common.rs index 508289e..fac2a0a 100644 --- a/parser/src/parsing/common.rs +++ b/parser/src/parsing/common.rs @@ -82,11 +82,11 @@ mod tests { #[test] fn test_parse_equality() { - use minisql::type_system::{IndexableValue, Value}; + use minisql::type_system::Value; match parse_equality("id = 1") { Ok(("", Condition::Eq(column_name, value))) => { assert!(column_name.eq("id")); - assert_eq!(value, Value::Indexable(IndexableValue::Int(1))) + assert_eq!(value, Value::Int(1)) } _ => { panic!("should parse"); diff --git a/parser/src/parsing/insert.rs b/parser/src/parsing/insert.rs index 0b62574..2dfa061 100644 --- a/parser/src/parsing/insert.rs +++ b/parser/src/parsing/insert.rs @@ -57,7 +57,7 @@ pub fn parse_values(input: &str) -> IResult<&str, Vec> { #[cfg(test)] mod tests { - use minisql::type_system::{IndexableValue, Value}; + use minisql::type_system::Value; use super::parse_insert; use crate::syntax::RawQuerySyntax; @@ -72,10 +72,10 @@ mod tests { assert_eq!( insertion_values, vec![ - ("id".to_string(), Value::Indexable(IndexableValue::Int(1))), + ("id".to_string(), Value::Int(1)), ( "data".to_string(), - Value::Indexable(IndexableValue::String("Text".to_string())) + Value::String("Text".to_string()) ) ] ); @@ -97,10 +97,10 @@ mod tests { assert_eq!( insertion_values, vec![ - ("id".to_string(), Value::Indexable(IndexableValue::Int(1))), + ("id".to_string(), Value::Int(1)), ( "data".to_string(), - Value::Indexable(IndexableValue::String("Text".to_string())) + Value::String("Text".to_string()) ) ] ); diff --git a/parser/src/parsing/literal.rs b/parser/src/parsing/literal.rs index c37f963..8ca72e8 100644 --- a/parser/src/parsing/literal.rs +++ b/parser/src/parsing/literal.rs @@ -1,4 +1,4 @@ -use minisql::type_system::{IndexableValue, Value}; +use minisql::type_system::Value; use nom::{ branch::alt, character::complete::{char, digit1, none_of, u64}, @@ -34,13 +34,13 @@ pub fn parse_number(input: &str) -> IResult<&str, Value> { let value = format!("{}{}", sign.unwrap_or('+'), digits) .parse::() .map_err(|_| nom::Err::Failure(make_error(input, nom::error::ErrorKind::Fail)))?; - Ok((input, Value::Indexable(IndexableValue::Int(value)))) + Ok((input, Value::Int(value))) } } } pub fn parse_int(input: &str) -> IResult<&str, Value> { - u64(input).map(|(input, v)| (input, Value::Indexable(IndexableValue::Int(v)))) + u64(input).map(|(input, v)| (input, Value::Int(v))) } fn escape_tab(input: &str) -> IResult<&str, char> { @@ -85,19 +85,19 @@ pub fn parse_string(input: &str) -> IResult<&str, Value> { // Combine the characters into a string let value: String = content.into_iter().collect(); - Ok((input, Value::Indexable(IndexableValue::String(value)))) + Ok((input, Value::String(value))) } pub fn parse_uuid(input: &str) -> IResult<&str, Value> { let (input, value) = pair(char('u'), u64)(input) - .map(|(input, (_, v))| (input, Value::Indexable(IndexableValue::Uuid(v))))?; + .map(|(input, (_, v))| (input, Value::Uuid(v)))?; Ok((input, value)) } #[cfg(test)] mod tests { use crate::parsing::literal::{parse_db_value, parse_string, parse_uuid}; - use minisql::type_system::{IndexableValue, Value}; + use minisql::type_system::Value; #[test] fn test_string_parser() { @@ -105,21 +105,21 @@ mod tests { parse_string(r#""simple""#), Ok(( "", - Value::Indexable(IndexableValue::String(String::from("simple"))) + Value::String(String::from("simple")) )) ); assert_eq!( parse_string(r#""\"\t\r\n\\""#), Ok(( "", - Value::Indexable(IndexableValue::String(String::from("\"\t\r\n\\"))) + Value::String(String::from("\"\t\r\n\\")) )) ); assert_eq!( parse_string(r#""name is \"John\".""#), Ok(( "", - Value::Indexable(IndexableValue::String(String::from("name is \"John\"."))) + Value::String(String::from("name is \"John\".")) )) ); } @@ -128,7 +128,7 @@ mod tests { fn test_parse_db_value() { let (input, value) = parse_db_value("5").expect("should parse"); assert_eq!(input, ""); - assert_eq!(value, Value::Indexable(IndexableValue::Int(5))); + assert_eq!(value, Value::Int(5)); let (input, value) = parse_db_value("5.5").expect("should parse"); assert_eq!(input, ""); @@ -140,9 +140,9 @@ mod tests { assert_eq!(input, ""); assert_eq!( value, - Value::Indexable(IndexableValue::String( + Value::String( "abcdefghkjklmnopqrstuvwxyz!@#$%^&*()_+ ".to_string() - )) + ) ); } @@ -183,7 +183,7 @@ mod tests { fn test_parse_int() { assert_eq!( parse_db_value("5134616"), - Ok(("", Value::Indexable(IndexableValue::Int(5134616)))) + Ok(("", Value::Int(5134616))) ); } @@ -191,7 +191,7 @@ mod tests { fn test_parse_uuid() { assert_eq!( parse_uuid("u131515"), - Ok(("", Value::Indexable(IndexableValue::Uuid(131515)))) + Ok(("", Value::Uuid(131515))) ) } } diff --git a/parser/src/validation.rs b/parser/src/validation.rs index bd948a7..9959abf 100644 --- a/parser/src/validation.rs +++ b/parser/src/validation.rs @@ -105,7 +105,7 @@ fn validate_table_schema(raw_table_schema: RawTableSchema) -> Result TableSchema { TableSchema::new( @@ -567,7 +565,7 @@ mod tests { let syntax: RawQuerySyntax = Select( "users".to_string(), ColumnSelection::All, - Some(Eq("age".to_string(), Indexable(Int(25)))), + Some(Eq("age".to_string(), Value::Int(25))), ); let result = validate_operation(syntax, &db_schema); assert!(matches!(result, Ok(Operation::Select(_, _, _)))); @@ -579,7 +577,7 @@ mod tests { assert!(table_position == users_position); assert!(column_selection == vec![id, name, age]); - assert!(condition == Some(operation::Condition::Eq(age, Indexable(Int(25))))); + assert!(condition == Some(operation::Condition::Eq(age, Value::Int(25)))); } #[test] @@ -634,7 +632,7 @@ mod tests { let syntax: RawQuerySyntax = Select( "users".to_string(), ColumnSelection::All, - Some(Eq("does_not_exist".to_string(), Indexable(Int(25)))), + Some(Eq("does_not_exist".to_string(), Value::Int(25))), ); let result = validate_operation(syntax, &db_schema); assert!(matches!(result, Err(ValidationError::ColumnsDoNotExist(_)))); @@ -648,7 +646,7 @@ mod tests { let syntax: RawQuerySyntax = Select( "users".to_string(), ColumnSelection::All, - Some(Eq("age".to_string(), Indexable(String("25".to_string())))), + Some(Eq("age".to_string(), Value::String("25".to_string()))), ); let result = validate_operation(syntax, &db_schema); assert!(matches!(result, Err(ValidationError::TypeMismatch { .. }))); @@ -665,9 +663,9 @@ mod tests { let syntax: RawQuerySyntax = Insert( "users".to_string(), vec![ - ("name".to_string(), Indexable(String("Alice".to_string()))), - ("id".to_string(), Indexable(Uuid(0))), - ("age".to_string(), Indexable(Int(25))), + ("name".to_string(), Value::String("Alice".to_string())), + ("id".to_string(), Value::Uuid(0)), + ("age".to_string(), Value::Int(25)), ], ); let result = validate_operation(syntax, &db_schema); @@ -685,9 +683,9 @@ mod tests { assert!( values == vec![ - Indexable(Uuid(0)), - Indexable(String("Alice".to_string())), - Indexable(Int(25)) + Value::Uuid(0), + Value::String("Alice".to_string()), + Value::Int(25) ] ); } @@ -700,10 +698,10 @@ mod tests { let syntax: RawQuerySyntax = Insert( "users".to_string(), vec![ - ("name".to_string(), Indexable(String("Alice".to_string()))), - ("id".to_string(), Indexable(Uuid(0))), - ("age".to_string(), Indexable(Int(25))), - ("does_not_exist".to_string(), Indexable(Int(25))), + ("name".to_string(), Value::String("Alice".to_string())), + ("id".to_string(), Value::Uuid(0)), + ("age".to_string(), Value::Int(25)), + ("does_not_exist".to_string(), Value::Int(25)), ], ); let result = validate_operation(syntax, &db_schema); @@ -718,9 +716,9 @@ mod tests { let syntax: RawQuerySyntax = Insert( "users".to_string(), vec![ - ("name".to_string(), Indexable(String("Alice".to_string()))), - ("id".to_string(), Indexable(Uuid(0))), - ("age".to_string(), Number(25.0)), + ("name".to_string(), Value::String("Alice".to_string())), + ("id".to_string(), Value::Uuid(0)), + ("age".to_string(), Value::Number(25.0)), ], ); let result = validate_operation(syntax, &db_schema); @@ -756,7 +754,7 @@ mod tests { let syntax: RawQuerySyntax = Delete( "users".to_string(), - Some(Eq("age".to_string(), Indexable(Int(25)))), + Some(Eq("age".to_string(), Value::Int(25))), ); let result = validate_operation(syntax, &db_schema); assert!(matches!( @@ -772,7 +770,7 @@ mod tests { assert!(table_position == users_position); assert!(column == age); - assert!(value == Indexable(Int(25))); + assert!(value == Value::Int(25)); } // ====CreateIndex==== diff --git a/proto/Cargo.toml b/proto/Cargo.toml index c8c9134..40f5991 100644 --- a/proto/Cargo.toml +++ b/proto/Cargo.toml @@ -8,3 +8,6 @@ bincode = "2.0.0-rc.3" tokio = { version = "1.34.0", features = ["io-util", "macros", "test-util"] } async-trait = "0.1.74" thiserror = "1.0.50" +rand_seeder = "0.2.3" +rand_pcg = "0.3.1" +rand = "0.8.5" diff --git a/proto/src/message/backend.rs b/proto/src/message/backend.rs index 869fe14..cfa1078 100644 --- a/proto/src/message/backend.rs +++ b/proto/src/message/backend.rs @@ -1,6 +1,7 @@ use crate::message::errors::{ProtoDeserializeError, ProtoSerializeError}; use crate::message::primitive::data::MessageData; use crate::message::primitive::pglist::PgList; +use crate::message::primitive::pgoid::PgOid; use crate::message::primitive::pgstring::PgString; use crate::message::proto_message::ProtoMessage; use bincode::{Decode, Encode}; @@ -186,9 +187,9 @@ impl From for BackendMessage { #[derive(Debug, Clone, Encode, Decode)] pub struct ColumnDescription { pub name: PgString, - pub table_oid: i32, + pub table_oid: PgOid, pub column_index: i16, - pub type_oid: i32, + pub type_oid: PgOid, pub type_size: i16, pub type_modifier: i32, pub format_code: i16, @@ -330,9 +331,9 @@ mod tests { let backend = BackendMessage::RowDescription(RowDescriptionData { columns: PgList::from(vec![ColumnDescription { name: PgString::from("Some name"), - table_oid: 123, + table_oid: PgOid::Simple(123), column_index: 456, - type_oid: 789, + type_oid: PgOid::Simple(789), type_size: 101, type_modifier: 112, format_code: 113, @@ -347,9 +348,9 @@ mod tests { let columns: Vec = columns.into(); let column = &columns[0]; column.name.as_str() == "Some name" - && column.table_oid == 123 + && column.table_oid.as_simple() == 123 && column.column_index == 456 - && column.type_oid == 789 + && column.type_oid.as_simple() == 789 && column.type_size == 101 && column.type_modifier == 112 && column.format_code == 113 diff --git a/proto/src/message/primitive/mod.rs b/proto/src/message/primitive/mod.rs index 4e84a1b..11b0f1a 100644 --- a/proto/src/message/primitive/mod.rs +++ b/proto/src/message/primitive/mod.rs @@ -1,3 +1,4 @@ pub(crate) mod data; pub mod pglist; +pub mod pgoid; pub mod pgstring; diff --git a/proto/src/message/primitive/pgoid.rs b/proto/src/message/primitive/pgoid.rs new file mode 100644 index 0000000..36806da --- /dev/null +++ b/proto/src/message/primitive/pgoid.rs @@ -0,0 +1,79 @@ +use bincode::de::Decoder; +use bincode::enc::Encoder; +use bincode::error::{DecodeError, EncodeError}; +use bincode::{BorrowDecode, Decode, Encode}; +use rand::Rng; +use rand_pcg::Pcg64; +use rand_seeder::Seeder; +use std::fmt::Display; + +#[derive(Debug, Clone, Copy, BorrowDecode)] +pub enum PgOid { + Simple(i32), + Nested(u16, u16), +} + +impl PgOid { + /// Generates a pseudo-random OID from a name. + pub fn from_unique_name(name: &str) -> PgOid { + let mut rng: Pcg64 = Seeder::from(name).make_rng(); + let oid = rng.gen::(); + PgOid::Simple(oid) + } + + pub fn as_nested(&self) -> (u16, u16) { + match self { + PgOid::Nested(a, b) => (*a, *b), + PgOid::Simple(oid) => { + let type_part = (oid & 0x0000FFFF) as u16; + let nest_part = (((*oid as u32) & 0xFFFF0000) >> 16) as u16; + (type_part, nest_part) + } + } + } + + pub fn as_simple(&self) -> i32 { + match self { + PgOid::Simple(oid) => *oid, + PgOid::Nested(type_part, nest_part) => { + let oid = (*type_part as u32) | ((*nest_part as u32) << 16); + oid as i32 + } + } + } +} + +impl Display for PgOid { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let oid = self.as_simple(); + write!(f, "{}", oid) + } +} + +impl Encode for PgOid { + fn encode(&self, encoder: &mut E) -> Result<(), EncodeError> { + let oid = self.as_simple(); + oid.encode(encoder) + } +} + +impl Decode for PgOid { + fn decode(decoder: &mut D) -> Result { + let oid = i32::decode(decoder)?; + Ok(PgOid::Simple(oid)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_pgoid() { + let oid = PgOid::Simple(0x12345678); + let (type_part, nest_part) = oid.as_nested(); + assert_eq!(type_part, 0x5678); + assert_eq!(nest_part, 0x1234); + assert_eq!(oid.as_simple(), 0x12345678); + } +} diff --git a/server/Cargo.toml b/server/Cargo.toml index 2b76e8e..67c87d7 100644 --- a/server/Cargo.toml +++ b/server/Cargo.toml @@ -11,8 +11,6 @@ anyhow = "1.0.76" clap = { version = "4.4.18", features = ["derive"] } async-trait = "0.1.74" rand = "0.8.5" -rand_seeder = "0.2.3" -rand_pcg = "0.3.1" serde_json = "1.0.112" minisql = { path = "../minisql" } proto = { path = "../proto" } diff --git a/server/src/proto_wrapper.rs b/server/src/proto_wrapper.rs index 960522e..1ddf59c 100644 --- a/server/src/proto_wrapper.rs +++ b/server/src/proto_wrapper.rs @@ -7,10 +7,8 @@ use proto::message::backend::{ ReadyForQueryData, RowDescriptionData, }; use proto::message::primitive::pglist::PgList; +use proto::message::primitive::pgoid::PgOid; use proto::writer::backend::BackendProtoWriter; -use rand::Rng; -use rand_pcg::Pcg64; -use rand_seeder::Seeder; use std::fmt; pub enum CompleteStatus { @@ -123,7 +121,7 @@ fn column_to_description( column: Column, ) -> anyhow::Result { let table_name = schema.table_name(); - let table_oid = table_name_to_oid(table_name); + let table_oid = PgOid::from_unique_name(table_name); let column_type = schema.column_type(column); let name = schema.column_name_from_column(column); @@ -142,10 +140,3 @@ fn column_to_description( format_code: 0, // text format }) } - -/// Hashes the table name into single i32 used as a substitute for the table OID -/// in the PostgreSQL protocol. -fn table_name_to_oid(table_name: &str) -> i32 { - let mut rng: Pcg64 = Seeder::from(table_name).make_rng(); - rng.gen::() -}