This commit is contained in:
Yuriy Dupyn 2024-01-27 22:46:19 +01:00
parent 9771a89716
commit 11dc992476
10 changed files with 157 additions and 403 deletions

View file

@ -2,8 +2,10 @@ use std::collections::HashSet;
use std::collections::HashMap;
use thiserror::Error;
use crate::syntax::{ColumnSelection, Condition, InsertionValues, RawQuerySyntax};
use minisql::{operation::{ColumnSelectionForInterpreter, ConditionForInterpreter, InsertionValuesForInterpreter, OperationForInterpreter}, type_system::Value, schema::{TableSchema, ColumnName, TableName}, type_system::DbType, interpreter::{TablePosition, DbSchema}};
use crate::syntax;
use crate::syntax::RawQuerySyntax;
use minisql::operation;
use minisql::{operation::Operation, type_system::Value, schema::{TableSchema, ColumnName, TableName}, type_system::DbType, interpreter::{TablePosition, DbSchema}};
#[derive(Debug, Error)]
pub enum ValidationError {
@ -26,7 +28,7 @@ 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<OperationForInterpreter, ValidationError> {
pub fn validate_operation(query: RawQuerySyntax, db_schema: &DbSchema) -> Result<Operation, ValidationError> {
match query {
RawQuerySyntax::Select(table_name, column_selection, condition) => {
validate_select(table_name, column_selection, condition, db_schema)
@ -52,7 +54,7 @@ fn validate_table_exists<'a>(db_schema: &DbSchema<'a>, table_name: &'a TableName
.map(|(_, table_position, table_schema)| (*table_position, *table_schema))
}
pub fn validate_create(table_name: TableName, table_schema: TableSchema, db_schema: &DbSchema) -> Result<OperationForInterpreter, ValidationError> {
pub fn validate_create(table_name: TableName, table_schema: TableSchema, db_schema: &DbSchema) -> Result<Operation, ValidationError> {
if let Some(_) = get_table_schema(db_schema, &table_name) {
return Err(ValidationError::TableAlreadyExists(table_name.to_string()));
}
@ -64,13 +66,13 @@ pub fn validate_create(table_name: TableName, table_schema: TableSchema, db_sche
)?;
// TODO: Ensure it has a primary key??
Ok(OperationForInterpreter::CreateTable(table_name, table_schema))
Ok(Operation::CreateTable(table_name, table_schema))
}
pub fn validate_select(table_name: TableName, column_selection: ColumnSelection, condition: Option<Condition>, db_schema: &DbSchema) -> Result<OperationForInterpreter, ValidationError> {
pub fn validate_select(table_name: TableName, column_selection: syntax::ColumnSelection, condition: Option<syntax::Condition>, db_schema: &DbSchema) -> Result<Operation, ValidationError> {
let (table_position, schema) = validate_table_exists(db_schema, &table_name)?;
match column_selection {
ColumnSelection::Columns(columns) => {
syntax::ColumnSelection::Columns(columns) => {
let non_existant_columns: Vec<ColumnName> =
columns.iter().filter_map(|column|
if schema.does_column_exist(&column) {
@ -81,20 +83,20 @@ pub fn validate_select(table_name: TableName, column_selection: ColumnSelection,
if non_existant_columns.len() > 0 {
Err(ValidationError::ColumnsDoNotExist(non_existant_columns))
} else {
let selection: ColumnSelectionForInterpreter =
let selection: operation::ColumnSelection =
columns.iter().filter_map(|column_name| schema.get_column_position(column_name)).collect();
let validated_condition = validate_condition(condition, schema)?;
Ok(OperationForInterpreter::Select(table_position, selection, validated_condition))
Ok(Operation::Select(table_position, selection, validated_condition))
}
}
ColumnSelection::All => {
syntax::ColumnSelection::All => {
let validated_condition = validate_condition(condition, schema)?;
Ok(OperationForInterpreter::Select(table_position, schema.all_selection(), validated_condition))
Ok(Operation::Select(table_position, schema.all_selection(), validated_condition))
}
}
}
pub fn validate_insert(table_name: TableName, insertion_values: InsertionValues, db_schema: &DbSchema) -> Result<OperationForInterpreter, ValidationError> {
pub fn validate_insert(table_name: TableName, insertion_values: syntax::InsertionValues, db_schema: &DbSchema) -> Result<Operation, ValidationError> {
let (table_position, schema) = validate_table_exists(db_schema, &table_name)?;
// Check for duplicate columns in insertion_values.
@ -120,7 +122,7 @@ pub fn validate_insert(table_name: TableName, insertion_values: InsertionValues,
// Check types and prepare for creation of InsertionValues for the interpreter
let mut values_map: HashMap<_, Value> = HashMap::new();
for (column_name, value) in insertion_values {
let (column, expected_type) = schema.get_column0(&column_name).ok_or(ValidationError::ColumnsDoNotExist(vec![column_name.to_string()]))?; // By the previous validation steps this is never gonna trigger an error.
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();
if value_type != expected_type {
return Err(ValidationError::TypeMismatch { column_name: column_name.to_string(), received_type: value_type, expected_type });
@ -129,26 +131,26 @@ pub fn validate_insert(table_name: TableName, insertion_values: InsertionValues,
}
// These are values ordered by the column position
let values: InsertionValuesForInterpreter = values_map.into_values().collect();
let values: operation::InsertionValues = values_map.into_values().collect();
Ok(OperationForInterpreter::Insert(table_position, values))
Ok(Operation::Insert(table_position, values))
}
pub fn validate_delete(table_name: TableName, condition: Option<Condition>, db_schema: &DbSchema) -> Result<OperationForInterpreter, ValidationError> {
pub fn validate_delete(table_name: TableName, condition: Option<syntax::Condition>, db_schema: &DbSchema) -> Result<Operation, ValidationError> {
let (table_position, schema) = validate_table_exists(db_schema, &table_name)?;
let validated_condition = validate_condition(condition, schema)?;
Ok(OperationForInterpreter::Delete(table_position, validated_condition))
Ok(Operation::Delete(table_position, validated_condition))
}
fn validate_condition(condition: Option<Condition>, schema: &TableSchema) -> Result<Option<ConditionForInterpreter>, ValidationError> {
fn validate_condition(condition: Option<syntax::Condition>, schema: &TableSchema) -> Result<Option<operation::Condition>, ValidationError> {
match condition {
Some(condition) => {
match condition {
Condition::Eq(column_name, value) => {
let (column, expected_type) = schema.get_column0(&column_name).ok_or(ValidationError::ColumnsDoNotExist(vec![column_name.to_string()]))?;
syntax::Condition::Eq(column_name, value) => {
let (column, expected_type) = schema.get_column(&column_name).ok_or(ValidationError::ColumnsDoNotExist(vec![column_name.to_string()]))?;
let value_type: DbType = value.to_type();
if expected_type.eq(&value_type) {
Ok(Some(ConditionForInterpreter::Eq(column, value)))
Ok(Some(operation::Condition::Eq(column, value)))
} else {
return Err(ValidationError::TypeMismatch { column_name: column_name.to_string(), received_type: value_type, expected_type });
}
@ -159,27 +161,27 @@ fn validate_condition(condition: Option<Condition>, schema: &TableSchema) -> Res
}
}
fn validate_create_index(table_name: TableName, column_name: ColumnName, db_schema: &DbSchema) -> Result<OperationForInterpreter, ValidationError> {
fn validate_create_index(table_name: TableName, column_name: ColumnName, db_schema: &DbSchema) -> Result<Operation, ValidationError> {
// TODO: You should disallow indexing of Number columns.
let (table_position, schema) = validate_table_exists(db_schema, &table_name)?;
schema
.get_column_position(&column_name)
.map_or_else(
|| Err(ValidationError::ColumnsDoNotExist(vec![column_name.to_string()])),
|column| Ok(OperationForInterpreter::CreateIndex(table_position, column))
|column| Ok(Operation::CreateIndex(table_position, column))
)
}
// ===Helpers===
fn find_first_duplicate<A>(xs: &[A]) -> Option<&A>
where A: Eq + std::hash::Hash
fn find_first_duplicate<T>(ts: &[T]) -> Option<&T>
where T: Eq + std::hash::Hash
{
let mut already_seen_elements: HashSet<&A> = HashSet::new();
for x in xs {
if already_seen_elements.contains(x) {
return Some(x);
let mut already_seen_elements: HashSet<&T> = HashSet::new();
for t in ts {
if already_seen_elements.contains(t) {
return Some(t);
} else {
already_seen_elements.insert(&x);
already_seen_elements.insert(&t);
}
}
None