Add some tests for Validation
This commit is contained in:
parent
10ba1dd3e4
commit
052236d892
8 changed files with 343 additions and 77 deletions
|
|
@ -29,8 +29,8 @@ pub fn parse_statements<'a>(input: &'a str) -> IResult<&str, Vec<RawQuerySyntax>
|
|||
many0(parse_statement)(input)
|
||||
}
|
||||
|
||||
pub fn parse_and_validate(query: String, db_schema: &DbSchema) -> Result<Operation, Error> {
|
||||
let (_, op) = parse_statement(query.as_str())
|
||||
pub fn parse_and_validate(str_query: String, db_schema: &DbSchema) -> Result<Operation, Error> {
|
||||
let (_, op) = parse_statement(str_query.as_str())
|
||||
.map_err(|err| {
|
||||
Error::ParsingError(err.to_string())
|
||||
})?;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
use std::collections::HashSet;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::{HashSet, BTreeMap};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::syntax;
|
||||
|
|
@ -28,8 +27,8 @@ pub enum ValidationError {
|
|||
}
|
||||
|
||||
/// Validates and converts the raw syntax into a proper interpreter operation based on db schema.
|
||||
pub fn validate_operation(query: RawQuerySyntax, db_schema: &DbSchema) -> Result<Operation, ValidationError> {
|
||||
match query {
|
||||
pub fn validate_operation(syntax: RawQuerySyntax, db_schema: &DbSchema) -> Result<Operation, ValidationError> {
|
||||
match syntax {
|
||||
RawQuerySyntax::Select(table_name, column_selection, condition) => {
|
||||
validate_select(table_name, column_selection, condition, db_schema)
|
||||
},
|
||||
|
|
@ -76,9 +75,9 @@ pub fn validate_select(table_name: TableName, column_selection: syntax::ColumnSe
|
|||
let non_existant_columns: Vec<ColumnName> =
|
||||
columns.iter().filter_map(|column|
|
||||
if schema.does_column_exist(&column) {
|
||||
Some(column.clone())
|
||||
} else {
|
||||
None
|
||||
} else {
|
||||
Some(column.clone())
|
||||
}).collect();
|
||||
if non_existant_columns.len() > 0 {
|
||||
Err(ValidationError::ColumnsDoNotExist(non_existant_columns))
|
||||
|
|
@ -120,7 +119,10 @@ pub fn validate_insert(table_name: TableName, insertion_values: syntax::Insertio
|
|||
}
|
||||
|
||||
// Check types and prepare for creation of InsertionValues for the interpreter
|
||||
let mut values_map: HashMap<_, Value> = HashMap::new();
|
||||
let mut values_map: BTreeMap<_, Value> = BTreeMap::new(); // The reason for using BTreeMap
|
||||
// instead of HashMap is that we need
|
||||
// to get the values in a vector
|
||||
// sorted by the key.
|
||||
for (column_name, value) in insertion_values {
|
||||
let (column, expected_type) = schema.get_column(&column_name).ok_or(ValidationError::ColumnsDoNotExist(vec![column_name.to_string()]))?; // By the previous validation steps this is never gonna trigger an error.
|
||||
let value_type = value.to_type();
|
||||
|
|
@ -130,9 +132,10 @@ pub fn validate_insert(table_name: TableName, insertion_values: syntax::Insertio
|
|||
values_map.insert(column, value);
|
||||
}
|
||||
|
||||
// These are values ordered by the column position
|
||||
let values: operation::InsertionValues = values_map.into_values().collect();
|
||||
// WARNING: If you use `values_map: HashMap<_,_>`, this is not gonna sort values by key.
|
||||
let values: operation::InsertionValues = values_map.into_values().collect();
|
||||
|
||||
// Note that one of the values is id.
|
||||
Ok(Operation::Insert(table_position, values))
|
||||
}
|
||||
|
||||
|
|
@ -191,3 +194,298 @@ fn get_table_schema<'a>(db_schema: &DbSchema<'a>, table_name: &'a TableName) ->
|
|||
let (_, _, table_schema) = db_schema.iter().find(|(tname, _, _)| table_name.eq(tname))?;
|
||||
Some(table_schema)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::syntax::{RawQuerySyntax, ColumnSelection, Condition};
|
||||
use minisql::type_system::{Value, IndexableValue};
|
||||
use minisql::operation::Operation;
|
||||
use minisql::operation;
|
||||
use minisql::schema::TableSchema;
|
||||
use super::*;
|
||||
|
||||
use RawQuerySyntax::*;
|
||||
use Value::*;
|
||||
use IndexableValue::*;
|
||||
use Condition::*;
|
||||
|
||||
fn users_schema() -> TableSchema {
|
||||
let id = 0;
|
||||
let name = 1;
|
||||
let age = 2;
|
||||
|
||||
TableSchema::new(
|
||||
"users".to_string(),
|
||||
id,
|
||||
vec!(
|
||||
("id".to_string(), id),
|
||||
("name".to_string(), name),
|
||||
("age".to_string(), age),
|
||||
),
|
||||
vec![DbType::Uuid, DbType::String, DbType::Int],
|
||||
)
|
||||
}
|
||||
|
||||
fn db_schema(users_schema: &TableSchema) -> DbSchema {
|
||||
vec![
|
||||
("users".to_string(), 0, users_schema),
|
||||
]
|
||||
}
|
||||
|
||||
fn empty_db_schema() -> DbSchema<'static> {
|
||||
vec![]
|
||||
}
|
||||
|
||||
// ====CreateTable====
|
||||
#[test]
|
||||
fn test_create_basic() {
|
||||
let users_schema: TableSchema = users_schema();
|
||||
let db_schema: DbSchema = empty_db_schema();
|
||||
|
||||
let syntax: RawQuerySyntax = CreateTable("users".to_string(), users_schema.clone());
|
||||
let result = validate_operation(syntax, &db_schema);
|
||||
assert!(matches!(result, Ok(Operation::CreateTable(_, _))));
|
||||
|
||||
let Ok(Operation::CreateTable(table_name, _)) = result else { panic!() };
|
||||
assert!(table_name == "users".to_string());
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn test_create_duplicates_in_schema() {
|
||||
// let id = 0;
|
||||
// let name = 1;
|
||||
|
||||
// let users_schema = TableSchema::new(
|
||||
// "users".to_string(),
|
||||
// id,
|
||||
// vec!(
|
||||
// ("id".to_string(), id),
|
||||
// ("name".to_string(), name),
|
||||
// ("name".to_string(), name + 1),
|
||||
// ),
|
||||
// vec![DbType::Uuid, DbType::String, DbType::Int],
|
||||
// );
|
||||
|
||||
// let db_schema: DbSchema = empty_db_schema();
|
||||
|
||||
// let syntax: RawQuerySyntax = CreateTable("users".to_string(), users_schema.clone());
|
||||
// let result = validate_operation(syntax, &db_schema);
|
||||
// println!("{:?}", result);
|
||||
// assert!(matches!(result, Err(ValidationError::DuplicateColumn(_))));
|
||||
|
||||
// // TODO
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn test_create_already_exists() {
|
||||
let users_schema: TableSchema = users_schema();
|
||||
let db_schema: DbSchema = db_schema(&users_schema);
|
||||
|
||||
let syntax: RawQuerySyntax = CreateTable("users".to_string(), users_schema.clone());
|
||||
let result = validate_operation(syntax, &db_schema);
|
||||
assert!(matches!(result, Err(ValidationError::TableAlreadyExists(_))));
|
||||
}
|
||||
|
||||
// ====Select====
|
||||
#[test]
|
||||
fn test_select_basic() {
|
||||
let users_schema: TableSchema = users_schema();
|
||||
let db_schema: DbSchema = db_schema(&users_schema);
|
||||
let users_position = 0;
|
||||
let id = 0;
|
||||
let name = 1;
|
||||
let age = 2;
|
||||
|
||||
let syntax: RawQuerySyntax = Select("users".to_string(), ColumnSelection::All, None);
|
||||
let result = validate_operation(syntax, &db_schema);
|
||||
assert!(matches!(result, Ok(Operation::Select(_, _, _))));
|
||||
|
||||
let Ok(Operation::Select(table_position, column_selection, condition)) = result else { panic!() };
|
||||
|
||||
assert!(table_position == users_position);
|
||||
assert!(condition == None);
|
||||
assert!(column_selection == vec![id, name, age]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_select_non_existent_table() {
|
||||
let users_schema: TableSchema = users_schema();
|
||||
let db_schema: DbSchema = db_schema(&users_schema);
|
||||
|
||||
let syntax: RawQuerySyntax = Select("does_not_exist".to_string(), ColumnSelection::All, None);
|
||||
let result = validate_operation(syntax, &db_schema);
|
||||
assert!(matches!(result, Err(ValidationError::TableDoesNotExist(_))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_select_eq() {
|
||||
let users_schema: TableSchema = users_schema();
|
||||
let db_schema: DbSchema = db_schema(&users_schema);
|
||||
|
||||
let users_position = 0;
|
||||
let id = 0;
|
||||
let name = 1;
|
||||
let age = 2;
|
||||
|
||||
let syntax: RawQuerySyntax = Select("users".to_string(), ColumnSelection::All, Some(Eq("age".to_string(), Indexable(Int(25)))));
|
||||
let result = validate_operation(syntax, &db_schema);
|
||||
assert!(matches!(result, Ok(Operation::Select(_, _, _))));
|
||||
|
||||
let Ok(Operation::Select(table_position, column_selection, condition)) = result else { panic!() };
|
||||
|
||||
assert!(table_position == users_position);
|
||||
assert!(column_selection == vec![id, name, age]);
|
||||
|
||||
assert!(condition == Some(operation::Condition::Eq(age, Indexable(Int(25)))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_select_eq_columns_selection() {
|
||||
let users_schema: TableSchema = users_schema();
|
||||
let db_schema: DbSchema = db_schema(&users_schema);
|
||||
|
||||
let users_position = 0;
|
||||
let name = 1;
|
||||
let age = 2;
|
||||
|
||||
let syntax: RawQuerySyntax = Select("users".to_string(), ColumnSelection::Columns(vec!["age".to_string(), "name".to_string(), "age".to_string()]), None);
|
||||
let result = validate_operation(syntax, &db_schema);
|
||||
assert!(matches!(result, Ok(Operation::Select(_, _, _))));
|
||||
|
||||
let Ok(Operation::Select(table_position, column_selection, condition)) = result else { panic!() };
|
||||
|
||||
assert!(table_position == users_position);
|
||||
assert!(column_selection == vec![age, name, age]);
|
||||
assert!(condition == None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_select_eq_columns_selection_nonexistent_column_selected() {
|
||||
let users_schema: TableSchema = users_schema();
|
||||
let db_schema: DbSchema = db_schema(&users_schema);
|
||||
|
||||
let syntax: RawQuerySyntax = Select("users".to_string(), ColumnSelection::Columns(vec!["age".to_string(), "does_not_exist".to_string()]), None);
|
||||
let result = validate_operation(syntax, &db_schema);
|
||||
assert!(matches!(result, Err(ValidationError::ColumnsDoNotExist(_))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_select_eq_non_existent_column() {
|
||||
let users_schema: TableSchema = users_schema();
|
||||
let db_schema: DbSchema = db_schema(&users_schema);
|
||||
|
||||
let syntax: RawQuerySyntax = Select("users".to_string(), ColumnSelection::All, Some(Eq("does_not_exist".to_string(), Indexable(Int(25)))));
|
||||
let result = validate_operation(syntax, &db_schema);
|
||||
assert!(matches!(result, Err(ValidationError::ColumnsDoNotExist(_))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_select_eq_type_error() {
|
||||
let users_schema: TableSchema = users_schema();
|
||||
let db_schema: DbSchema = db_schema(&users_schema);
|
||||
|
||||
let syntax: RawQuerySyntax = Select("users".to_string(), ColumnSelection::All, Some(Eq("age".to_string(), Indexable(String("25".to_string())))));
|
||||
let result = validate_operation(syntax, &db_schema);
|
||||
assert!(matches!(result, Err(ValidationError::TypeMismatch { .. })));
|
||||
}
|
||||
|
||||
// ====Insert====
|
||||
#[test]
|
||||
fn test_insert() {
|
||||
let users_schema: TableSchema = users_schema();
|
||||
let db_schema: DbSchema = db_schema(&users_schema);
|
||||
|
||||
let users_position = 0;
|
||||
|
||||
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))),
|
||||
]);
|
||||
let result = validate_operation(syntax, &db_schema);
|
||||
assert!(matches!(result, Ok(Operation::Insert(_, _))));
|
||||
|
||||
let Ok(Operation::Insert(table_position, values)) = result else { panic!() };
|
||||
|
||||
assert!(table_position == users_position);
|
||||
// Recall the order is
|
||||
// let id = 0;
|
||||
// let name = 1;
|
||||
// let age = 2;
|
||||
assert!(values == vec![Indexable(Uuid(0)), Indexable(String("Alice".to_string())), Indexable(Int(25))]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_insert_non_existent_column() {
|
||||
let users_schema: TableSchema = users_schema();
|
||||
let db_schema: DbSchema = db_schema(&users_schema);
|
||||
|
||||
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))),
|
||||
]);
|
||||
let result = validate_operation(syntax, &db_schema);
|
||||
assert!(matches!(result, Err(ValidationError::ColumnsDoNotExist(_))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_insert_ill_typed_column() {
|
||||
let users_schema: TableSchema = users_schema();
|
||||
let db_schema: DbSchema = db_schema(&users_schema);
|
||||
|
||||
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)),
|
||||
]);
|
||||
let result = validate_operation(syntax, &db_schema);
|
||||
assert!(matches!(result, Err(ValidationError::TypeMismatch { .. })));
|
||||
}
|
||||
|
||||
// ====Delete====
|
||||
#[test]
|
||||
fn test_delete_all() {
|
||||
let users_schema: TableSchema = users_schema();
|
||||
let db_schema: DbSchema = db_schema(&users_schema);
|
||||
|
||||
let users_position = 0;
|
||||
|
||||
let syntax: RawQuerySyntax = Delete("users".to_string(), None);
|
||||
let result = validate_operation(syntax, &db_schema);
|
||||
assert!(matches!(result, Ok(Operation::Delete(_, None))));
|
||||
|
||||
let Ok(Operation::Delete(table_position, _)) = result else { panic!() };
|
||||
|
||||
assert!(table_position == users_position);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_delete_eq() {
|
||||
let users_schema: TableSchema = users_schema();
|
||||
let db_schema: DbSchema = db_schema(&users_schema);
|
||||
|
||||
let users_position = 0;
|
||||
let age = 2;
|
||||
|
||||
let syntax: RawQuerySyntax = Delete("users".to_string(), Some(Eq("age".to_string(), Indexable(Int(25)))));
|
||||
let result = validate_operation(syntax, &db_schema);
|
||||
assert!(matches!(result, Ok(Operation::Delete(_, Some(operation::Condition::Eq(_, _))))));
|
||||
|
||||
let Ok(Operation::Delete(table_position, Some(operation::Condition::Eq(column_position, value)))) = result else { panic!() };
|
||||
|
||||
assert!(table_position == users_position);
|
||||
assert!(column_position == age);
|
||||
assert!(value == Indexable(Int(25)));
|
||||
// assert!(condition == None);
|
||||
}
|
||||
|
||||
// ====CreateIndex====
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue