Add some tests for Validation
This commit is contained in:
parent
10ba1dd3e4
commit
052236d892
8 changed files with 343 additions and 77 deletions
|
|
@ -1,16 +1,11 @@
|
|||
use std::num::{ParseFloatError, ParseIntError};
|
||||
use std::str::Utf8Error;
|
||||
use thiserror::Error;
|
||||
use crate::internals::row::ColumnPosition;
|
||||
use crate::schema::{ColumnName, TableName};
|
||||
use crate::type_system::{DbType, Uuid, Value};
|
||||
use crate::type_system::Uuid;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
#[error("column position {1} of table {0} does not exist")]
|
||||
ColumnPositionDoesNotExist(TableName, ColumnPosition),
|
||||
#[error("column {1} of table {0} has unexpected type {2:?} and value {3:?}")]
|
||||
ValueDoesNotMatchExpectedType(TableName, ColumnName, DbType, Value),
|
||||
pub enum RuntimeError {
|
||||
#[error("table {0} already contains row with id {1}")]
|
||||
AttemptingToInsertAlreadyPresentId(TableName, Uuid),
|
||||
#[error("table {0} cannot be indexed on column {1}")]
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::error::RuntimeError;
|
||||
use crate::internals::column_index::ColumnIndex;
|
||||
use crate::internals::row::{ColumnPosition, Row};
|
||||
use crate::restricted_row::RestrictedRow;
|
||||
|
|
@ -108,22 +108,16 @@ impl Table {
|
|||
// ======Insertion======
|
||||
pub fn insert_row_at(&mut self, id: Uuid, row: Row) -> DbResult<()> {
|
||||
if self.rows.get(&id).is_some() {
|
||||
return Err(Error::AttemptingToInsertAlreadyPresentId(
|
||||
return Err(RuntimeError::AttemptingToInsertAlreadyPresentId(
|
||||
self.table_name().clone(),
|
||||
id,
|
||||
));
|
||||
}
|
||||
|
||||
for (column_position, column_index) in &mut self.indexes {
|
||||
match row.get(*column_position) {
|
||||
Some(Value::Indexable(val)) => column_index.add(val.clone(), id),
|
||||
Some(_) => {}
|
||||
None => {
|
||||
return Err(Error::ColumnPositionDoesNotExist(
|
||||
self.schema.table_name().clone(), // Note that I can't simply use self.table_name() here because of rust borrowing rules.
|
||||
*column_position,
|
||||
))
|
||||
}
|
||||
for (column, column_index) in &mut self.indexes {
|
||||
match &row[*column] {
|
||||
Value::Indexable(val) => column_index.add(val.clone(), id),
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -206,19 +200,7 @@ impl Table {
|
|||
if self.schema.is_primary(column_position) {
|
||||
match value {
|
||||
IndexableValue::Uuid(id) => Ok(Some(HashSet::from([*id]))),
|
||||
_ => {
|
||||
// TODO: This validation step is not really necessary.
|
||||
let column_name: ColumnName = self
|
||||
.schema
|
||||
.column_name_from_column_position(column_position)?;
|
||||
let type_ = self.schema.column_type(column_position);
|
||||
Err(Error::ValueDoesNotMatchExpectedType(
|
||||
self.table_name().clone(),
|
||||
column_name,
|
||||
type_,
|
||||
Value::Indexable(value.clone()),
|
||||
))
|
||||
}
|
||||
_ => unreachable!() // SAFETY: Validation guarantees primary column has correct Uuid type.
|
||||
}
|
||||
} else {
|
||||
match self.indexes.get(&column_position) {
|
||||
|
|
@ -240,26 +222,21 @@ impl Table {
|
|||
fn update_index_from_table(
|
||||
column_index: &mut ColumnIndex,
|
||||
table: &Table,
|
||||
column_position: ColumnPosition,
|
||||
column: ColumnPosition,
|
||||
) -> DbResult<()> {
|
||||
for (id, row) in &table.rows {
|
||||
let value = match row.get(column_position) {
|
||||
Some(Value::Indexable(value)) => value.clone(),
|
||||
Some(_) => {
|
||||
let value = match &row[column] {
|
||||
Value::Indexable(value) => value.clone(),
|
||||
_ => {
|
||||
let column_name: ColumnName = table
|
||||
.schema
|
||||
.column_name_from_column_position(column_position)?;
|
||||
return Err(Error::AttemptToIndexNonIndexableColumn(
|
||||
.column_name_from_column(column);
|
||||
// TODO: Perhaps this should be handled in validation?
|
||||
return Err(RuntimeError::AttemptToIndexNonIndexableColumn(
|
||||
table.table_name().to_string(),
|
||||
column_name,
|
||||
));
|
||||
}
|
||||
None => {
|
||||
return Err(Error::ColumnPositionDoesNotExist(
|
||||
table.table_name().to_string(),
|
||||
column_position,
|
||||
))
|
||||
}
|
||||
};
|
||||
column_index.add(value, *id)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use crate::internals::row::ColumnPosition;
|
|||
use crate::interpreter::TablePosition;
|
||||
|
||||
// Validated operation. Constructed by validation crate.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Operation {
|
||||
Select(TablePosition, ColumnSelection, Option<Condition>),
|
||||
Insert(TablePosition, InsertionValues),
|
||||
|
|
@ -13,11 +13,12 @@ pub enum Operation {
|
|||
CreateIndex(TablePosition, ColumnPosition),
|
||||
}
|
||||
|
||||
// Assumes that these are sorted by column position.
|
||||
pub type InsertionValues = Vec<Value>;
|
||||
|
||||
pub type ColumnSelection = Vec<ColumnPosition>;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Condition {
|
||||
Eq(ColumnPosition, Value),
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
use crate::error::Error;
|
||||
use crate::error::RuntimeError;
|
||||
|
||||
pub type DbResult<A> = Result<A, Error>;
|
||||
pub type DbResult<A> = Result<A, RuntimeError>;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
use crate::error::Error;
|
||||
use crate::internals::row::{ColumnPosition, Row};
|
||||
use crate::operation::{InsertionValues, ColumnSelection};
|
||||
use crate::result::DbResult;
|
||||
|
|
@ -7,7 +6,7 @@ use bimap::BiMap;
|
|||
|
||||
// Note that it is nice to split metadata from the data because
|
||||
// then you can give the metadata to the parser without giving it the data.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct TableSchema {
|
||||
table_name: TableName, // used for descriptive errors
|
||||
primary_key: ColumnPosition,
|
||||
|
|
@ -67,19 +66,15 @@ impl TableSchema {
|
|||
self.primary_key == column_position
|
||||
}
|
||||
|
||||
pub fn column_name_from_column_position(
|
||||
&self,
|
||||
column_position: ColumnPosition,
|
||||
) -> DbResult<ColumnName> {
|
||||
// Assumes `column` comes from a validated source.
|
||||
pub fn column_name_from_column(&self, column: ColumnPosition) -> ColumnName {
|
||||
match self
|
||||
.column_name_position_mapping
|
||||
.get_by_right(&column_position)
|
||||
.get_by_right(&column)
|
||||
{
|
||||
Some(column_name) => Ok(column_name.clone()),
|
||||
None => Err(Error::ColumnPositionDoesNotExist(
|
||||
self.table_name.clone(),
|
||||
column_position,
|
||||
)),
|
||||
Some(column_name) => column_name.clone(),
|
||||
None => unreachable!() // SAFETY: The only way this function can get a column is from
|
||||
// validation, which guarantees there is such a colun.
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -95,8 +90,8 @@ impl TableSchema {
|
|||
|
||||
let id: Uuid = match row.get(self.primary_key) {
|
||||
Some(Value::Indexable(IndexableValue::Uuid(id))) => *id,
|
||||
Some(_) => unreachable!(), // SAFETY: Should be guaranteed by validation
|
||||
None => unreachable!(), // SAFETY: Should be guaranteed by validation
|
||||
Some(_) => unreachable!(), // SAFETY: Should be guaranteed by validation (type-safety)
|
||||
None => unreachable!(), // SAFETY: Should be guaranteed by validation (missing columns)
|
||||
};
|
||||
|
||||
Ok((id, row))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue