Introduce optional type/value

This commit is contained in:
Yuriy Dupyn 2024-02-01 14:35:25 +01:00
parent 597c52caa0
commit 339686f5c5
4 changed files with 255 additions and 204 deletions

View file

@ -85,18 +85,18 @@ impl Table {
value: Value,
) -> DbResult<impl Iterator<Item = RestrictedRow> + '_> {
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)),
@ -113,8 +113,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)
}
}
@ -127,8 +127,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
@ -168,12 +168,12 @@ impl Table {
}
pub fn delete_rows_where_eq(&mut self, column: Column, value: Value) -> DbResult<usize> {
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)),
}
}
@ -226,9 +226,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(),

View file

@ -2,7 +2,7 @@ use crate::internals::table::Table;
use crate::operation::{ColumnSelection, Condition, Operation};
use crate::restricted_row::RestrictedRow;
use crate::result::DbResult;
use crate::schema::{Column, TableName, TablePosition, TableSchema};
use crate::schema::{TableName, TablePosition, TableSchema};
use bimap::BiMap;
use serde::{Deserialize, Serialize};
@ -491,102 +491,103 @@ mod tests {
}
}
pub fn example() {
use crate::type_system::{DbType, IndexableValue, Value};
use Condition::*;
use IndexableValue::*;
use Operation::*;
use Value::*;
// TODO
// pub fn example() {
// use crate::type_system::{DbType, IndexableValue, Value};
// use Condition::*;
// use IndexableValue::*;
// use Operation::*;
// use Value::*;
let id_column: Column = 0;
let name_column: Column = 1;
// let age_column: ColumnPosition = 2;
// let id_column: Column = 0;
// let name_column: Column = 1;
// // let age_column: ColumnPosition = 2;
let users_schema: TableSchema = {
TableSchema::new(
"users".to_string(),
"id".to_string(),
vec![
"id".to_string(), // 0
"name".to_string(), // 1
"age".to_string(), // 2
],
vec![DbType::Uuid, DbType::String, DbType::Int],
)
};
let users_position: TablePosition = 0;
// let users_schema: TableSchema = {
// TableSchema::new(
// "users".to_string(),
// "id".to_string(),
// vec![
// "id".to_string(), // 0
// "name".to_string(), // 1
// "age".to_string(), // 2
// ],
// vec![DbType::Uuid, DbType::String, DbType::Int],
// )
// };
// let users_position: TablePosition = 0;
let mut state = State::new();
state
.interpret(Operation::CreateTable(users_schema.clone()))
.unwrap();
// let mut state = State::new();
// state
// .interpret(Operation::CreateTable(users_schema.clone()))
// .unwrap();
let (id0, name0, age0) = (
Indexable(Uuid(0)),
Indexable(String("Plato".to_string())),
Indexable(Int(64)),
);
println!("==INSERT Plato==");
state
.interpret(Insert(
users_position,
vec![id0.clone(), name0.clone(), age0.clone()],
))
.unwrap();
// let (id0, name0, age0) = (
// Indexable(Uuid(0)),
// Indexable(String("Plato".to_string())),
// Indexable(Int(64)),
// );
// println!("==INSERT Plato==");
// state
// .interpret(Insert(
// users_position,
// vec![id0.clone(), name0.clone(), age0.clone()],
// ))
// .unwrap();
let (id1, name1, age1) = (
Indexable(Uuid(1)),
Indexable(String("Aristotle".to_string())),
Indexable(Int(20)),
);
println!("==INSERT Aristotle==");
state
.interpret(Insert(
users_position,
vec![id1.clone(), name1.clone(), age1.clone()],
))
.unwrap();
println!();
// let (id1, name1, age1) = (
// Indexable(Uuid(1)),
// Indexable(String("Aristotle".to_string())),
// Indexable(Int(20)),
// );
// println!("==INSERT Aristotle==");
// state
// .interpret(Insert(
// users_position,
// vec![id1.clone(), name1.clone(), age1.clone()],
// ))
// .unwrap();
// println!();
{
let response: Response = state
.interpret(Operation::Select(
users_position,
users_schema.all_selection(),
None,
))
.unwrap();
println!("==SELECT ALL==");
println!("{:?}", response);
println!();
}
{
let response: Response = state
.interpret(Select(
users_position,
users_schema.all_selection(),
Some(Eq(id_column, id0.clone())),
))
.unwrap();
println!("==SELECT Plato==");
println!("{:?}", response);
println!();
}
// {
// let response: Response = state
// .interpret(Operation::Select(
// users_position,
// users_schema.all_selection(),
// None,
// ))
// .unwrap();
// println!("==SELECT ALL==");
// println!("{:?}", response);
// println!();
// }
// {
// let response: Response = state
// .interpret(Select(
// users_position,
// users_schema.all_selection(),
// Some(Eq(id_column, id0.clone())),
// ))
// .unwrap();
// println!("==SELECT Plato==");
// println!("{:?}", response);
// println!();
// }
{
{
// TODO: Why do I have to write these braces explicitely? Why doesn't Rust compiler
// "infer" them?
let _delete_response: Response = state
.interpret(Delete(users_position, Some(Eq(id_column, id0.clone()))))
.unwrap();
println!("==DELETE Plato==");
}
let response: Response = state
.interpret(Select(users_position, vec![name_column, id_column], None))
.unwrap();
println!("==SELECT All==");
println!("{:?}", response);
println!();
}
}
// {
// {
// // TODO: Why do I have to write these braces explicitely? Why doesn't Rust compiler
// // "infer" them?
// let _delete_response: Response = state
// .interpret(Delete(users_position, Some(Eq(id_column, id0.clone()))))
// .unwrap();
// println!("==DELETE Plato==");
// }
// let response: Response = state
// .interpret(Select(users_position, vec![name_column, id_column], None))
// .unwrap();
// println!("==SELECT All==");
// println!("{:?}", response);
// println!();
// }
// }

View file

@ -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<DbType> {
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(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)
};

View file

@ -1,36 +1,55 @@
use crate::error::TypeConversionError;
use std::cmp::Ordering;
use serde::{Deserialize, Serialize};
// ==============Types================
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Debug, Clone, Ord, PartialEq, Eq, Serialize, Deserialize)]
pub enum DbType {
String,
Int,
Number,
Uuid,
Option(Box<DbType>)
}
impl PartialOrd for DbType {
// TODO: Explain why we need this (because of IndexableValue::None contains a type)
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
todo!()
}
}
// ==============Values================
pub type Uuid = u64;
// TODO: What about nulls? I would rather not have that in SQL, it sucks.
// I would rather have non-nullable values by default,
// and something like an explicit Option type for nulls.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(try_from = "String", into = "String")]
pub enum Value {
Number(f64), // TODO: Can't put floats as keys in maps, since they don't implement Eq. What to
// do?
Indexable(IndexableValue),
Number(f64),
String(String),
Int(u64),
Uuid(Uuid),
Some(Box<Value>),
// 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, Ord, Eq, Clone, PartialEq, Serialize, Deserialize)]
#[serde(try_from = "String", into = "String")]
pub enum IndexableValue {
String(String),
Int(u64),
Uuid(Uuid),
// TODO: what about null?
Some(Box<IndexableValue>),
None(DbType), // See Value::None
}
impl PartialOrd for IndexableValue {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
todo!()
}
}
impl DbType {
@ -40,25 +59,28 @@ impl DbType {
Self::Int => true,
Self::Number => false,
Self::Uuid => true,
Self::Option(type_) => type_.is_indexable()
}
}
pub fn type_oid(&self) -> i32 {
match self {
Self::String => 25,
Self::Int => 23,
Self::Number => 701,
Self::Uuid => 2950,
}
// match self {
// Self::String => 25,
// Self::Int => 23,
// Self::Number => 701,
// Self::Uuid => 2950,
// }
todo!()
}
pub fn type_size(&self) -> i16 {
match self {
Self::String => -2, // null terminated string
Self::Int => 8,
Self::Number => 8,
Self::Uuid => 16,
}
// match self {
// Self::String => -2, // null terminated string
// Self::Int => 8,
// Self::Number => 8,
// Self::Uuid => 16,
// }
todo!()
}
}
@ -66,63 +88,84 @@ 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 as_text_bytes(&self) -> Vec<u8> {
pub fn to_indexable(&self) -> Option<IndexableValue> {
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::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())),
}
}
pub fn as_text_bytes(&self) -> Vec<u8> {
// 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!()
}
pub fn from_text_bytes(
bytes: &[u8],
type_oid: i32,
type_size: i16,
) -> Result<Value, TypeConversionError> {
match (type_oid, type_size) {
(701, 8) => {
let s = std::str::from_utf8(bytes)?;
let n = s.parse::<f64>()?;
Ok(Value::Number(n))
// match (type_oid, type_size) {
// (701, 8) => {
// let s = std::str::from_utf8(bytes)?;
// let n = s.parse::<f64>()?;
// 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::<u64>()?;
// Ok(Value::Indexable(IndexableValue::Int(n)))
// }
// (2950, 16) => {
// let s = std::str::from_utf8(bytes)?;
// let n = s.parse::<Uuid>()?;
// Ok(Value::Indexable(IndexableValue::Uuid(n)))
// }
// (oid, size) => Err(TypeConversionError::UnknownType { oid, size }),
// }
todo!()
}
(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::<u64>()?;
Ok(Value::Indexable(IndexableValue::Int(n)))
}
(2950, 16) => {
let s = std::str::from_utf8(bytes)?;
let n = s.parse::<Uuid>()?;
Ok(Value::Indexable(IndexableValue::Uuid(n)))
}
(oid, size) => Err(TypeConversionError::UnknownType { oid, size }),
}
fn indexable_value_to_string(value: IndexableValue) -> String {
match value {
IndexableValue::String(s) => format!("String({s})"),
IndexableValue::Int(i) => format!("Int({i})"),
IndexableValue::Uuid(u) => format!("Uuid({u})"),
IndexableValue::Some(val) => format!("Some({})", indexable_value_to_string(*val)),
IndexableValue::Some(val) => todo!(),
IndexableValue::None(type_) => "None()".to_string(),
}
}
// Own string serialization so enums can be used as keys in maps
impl From<IndexableValue> 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})"),
}
indexable_value_to_string(value)
}
}
@ -135,35 +178,37 @@ impl TryFrom<String> for IndexableValue {
if value.starts_with("String(") {
let s = value[7..value.len() - 1].to_string();
return Ok(Self::String(s));
}
if value.starts_with("Int(") {
Ok(Self::String(s))
} else if value.starts_with("Int(") {
let s = value[4..value.len() - 1].to_string();
let i = s
.parse::<u64>()
.map_err(|e| format!("Invalid Int: {}", e))?;
return Ok(Self::Int(i));
}
if value.starts_with("Uuid(") {
Ok(Self::Int(i))
} else if value.starts_with("Uuid(") {
let s = value[5..value.len() - 1].to_string();
let u = s
.parse::<u64>()
.map_err(|e| format!("Invalid UUID: {}", e))?;
return Ok(Self::Uuid(u));
}
Ok(Self::Uuid(u))
} else if value.starts_with("Some(") {
// TODO: This needs some recursion
todo!()
} else if value.starts_with("None(") {
todo!()
} else {
Err(format!("Invalid IndexableValue: {}", value))
}
}
}
impl From<Value> for String {
fn from(value: Value) -> Self {
match value {
Value::Number(n) => format!("Number({n})"),
Value::Indexable(i) => format!("Indexable({})", String::from(i)),
}
// match value {
// Value::Number(n) => format!("Number({n})"),
// Value::Indexable(i) => format!("Indexable({})", String::from(i)),
// }
todo!()
}
}
@ -174,21 +219,21 @@ impl TryFrom<String> for Value {
return Err(format!("Invalid Value: {}", value));
}
if value.starts_with("Number(") {
let s = value[7..value.len() - 1].to_string();
let n = s
.parse::<f64>()
.map_err(|e| format!("Invalid Number: {}", e))?;
return Ok(Self::Number(n));
}
// if value.starts_with("Number(") {
// let s = value[7..value.len() - 1].to_string();
// let n = s
// .parse::<f64>()
// .map_err(|e| format!("Invalid Number: {}", e))?;
// Ok(Self::Number(n))
// } else if value.starts_with("Indexable(") {
// let s = value[10..value.len() - 1].to_string();
// let i = IndexableValue::try_from(s)?;
// Ok(Self::Indexable(i))
// } else {
// Err(format!("Invalid Value: {}", value))
// }
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))
todo!()
}
}