Parsing and validation for Option
Add option type and option value parsers value->literal in parser, implement Option literal
This commit is contained in:
parent
de8c6164cf
commit
6245dba4f0
8 changed files with 322 additions and 109 deletions
|
|
@ -1,6 +1,7 @@
|
|||
use std::collections::{BTreeMap, HashSet};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::parsing::literal::Literal;
|
||||
use crate::syntax;
|
||||
use crate::syntax::{ColumnSchema, RawQuerySyntax, RawTableSchema};
|
||||
use minisql::operation;
|
||||
|
|
@ -34,6 +35,8 @@ pub enum ValidationError {
|
|||
received_type: DbType,
|
||||
expected_type: DbType,
|
||||
},
|
||||
#[error("Expected type {expected_type:?}, received None")]
|
||||
UnexpectedNoneValue{ expected_type: DbType },
|
||||
#[error("values for required columns {0:?} are missing")]
|
||||
RequiredColumnsAreMissing(Vec<ColumnName>),
|
||||
}
|
||||
|
|
@ -240,7 +243,7 @@ fn validate_insert(
|
|||
.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();
|
||||
let value_type = type_from_literal_with_type_hint(&value, &expected_type)?;
|
||||
if value_type != expected_type {
|
||||
return Err(ValidationError::TypeMismatch {
|
||||
column_name: column_name.to_string(),
|
||||
|
|
@ -248,7 +251,7 @@ fn validate_insert(
|
|||
expected_type,
|
||||
});
|
||||
}
|
||||
values_map.insert(column, value);
|
||||
values_map.insert(column, literal_to_value(value, &expected_type));
|
||||
}
|
||||
|
||||
// WARNING: If you use `values_map: HashMap<_,_>`, this is not gonna sort values by key.
|
||||
|
|
@ -278,9 +281,9 @@ fn validate_condition(
|
|||
let (column, expected_type) = schema.get_typed_column(&column_name).ok_or(
|
||||
ValidationError::ColumnsDoNotExist(vec![column_name.to_string()]),
|
||||
)?;
|
||||
let value_type: DbType = value.to_type();
|
||||
let value_type: DbType = type_from_literal_with_type_hint(&value, &expected_type)?;
|
||||
if expected_type.eq(&value_type) {
|
||||
Ok(Some(operation::Condition::Eq(column, value)))
|
||||
Ok(Some(operation::Condition::Eq(column, literal_to_value(value, &expected_type))))
|
||||
} else {
|
||||
Err(ValidationError::TypeMismatch {
|
||||
column_name: column_name.to_string(),
|
||||
|
|
@ -345,6 +348,42 @@ fn get_table_schema<'a>(
|
|||
Some(table_schema)
|
||||
}
|
||||
|
||||
fn literal_to_value(lit: Literal, hint: &DbType) -> Value {
|
||||
match lit {
|
||||
Literal::Number(v) => Value::Number(v),
|
||||
Literal::String(v) => Value::String(v),
|
||||
Literal::Int(v) => Value::Int(v),
|
||||
Literal::Uuid(v) => Value::Uuid(v),
|
||||
Literal::Some(v) => Value::Some(Box::new(literal_to_value(*v, hint))),
|
||||
Literal::None => {
|
||||
if let DbType::Option(t) = hint {
|
||||
Value::None(*t.clone())
|
||||
} else {
|
||||
// By the time calling current function, hopefully we should be sure about the
|
||||
// type we want from the literal
|
||||
panic!()
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn type_from_literal_with_type_hint(lit: &Literal, hint: &DbType) -> Result<DbType, ValidationError> {
|
||||
Ok(match lit {
|
||||
Literal::Number(_) => DbType::Number,
|
||||
Literal::String(_) => DbType::String,
|
||||
Literal::Int(_) => DbType::Int,
|
||||
Literal::Uuid(_) => DbType::Uuid,
|
||||
Literal::Some(l) => type_from_literal_with_type_hint(l, hint)?,
|
||||
Literal::None => {
|
||||
if matches!(hint, DbType::Option(_)) {
|
||||
hint.clone()
|
||||
} else {
|
||||
return Err(ValidationError::UnexpectedNoneValue { expected_type: hint.clone() })
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
@ -565,7 +604,7 @@ mod tests {
|
|||
let syntax: RawQuerySyntax = Select(
|
||||
"users".to_string(),
|
||||
ColumnSelection::All,
|
||||
Some(Eq("age".to_string(), Value::Int(25))),
|
||||
Some(Eq("age".to_string(), Literal::Int(25))),
|
||||
);
|
||||
let result = validate_operation(syntax, &db_schema);
|
||||
assert!(matches!(result, Ok(Operation::Select(_, _, _))));
|
||||
|
|
@ -632,7 +671,7 @@ mod tests {
|
|||
let syntax: RawQuerySyntax = Select(
|
||||
"users".to_string(),
|
||||
ColumnSelection::All,
|
||||
Some(Eq("does_not_exist".to_string(), Value::Int(25))),
|
||||
Some(Eq("does_not_exist".to_string(), Literal::Int(25))),
|
||||
);
|
||||
let result = validate_operation(syntax, &db_schema);
|
||||
assert!(matches!(result, Err(ValidationError::ColumnsDoNotExist(_))));
|
||||
|
|
@ -646,7 +685,7 @@ mod tests {
|
|||
let syntax: RawQuerySyntax = Select(
|
||||
"users".to_string(),
|
||||
ColumnSelection::All,
|
||||
Some(Eq("age".to_string(), Value::String("25".to_string()))),
|
||||
Some(Eq("age".to_string(), Literal::String("25".to_string()))),
|
||||
);
|
||||
let result = validate_operation(syntax, &db_schema);
|
||||
assert!(matches!(result, Err(ValidationError::TypeMismatch { .. })));
|
||||
|
|
@ -663,9 +702,9 @@ mod tests {
|
|||
let syntax: RawQuerySyntax = Insert(
|
||||
"users".to_string(),
|
||||
vec![
|
||||
("name".to_string(), Value::String("Alice".to_string())),
|
||||
("id".to_string(), Value::Uuid(0)),
|
||||
("age".to_string(), Value::Int(25)),
|
||||
("name".to_string(), Literal::String("Alice".to_string())),
|
||||
("id".to_string(), Literal::Uuid(0)),
|
||||
("age".to_string(), Literal::Int(25)),
|
||||
],
|
||||
);
|
||||
let result = validate_operation(syntax, &db_schema);
|
||||
|
|
@ -698,10 +737,10 @@ mod tests {
|
|||
let syntax: RawQuerySyntax = Insert(
|
||||
"users".to_string(),
|
||||
vec![
|
||||
("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)),
|
||||
("name".to_string(), Literal::String("Alice".to_string())),
|
||||
("id".to_string(), Literal::Uuid(0)),
|
||||
("age".to_string(), Literal::Int(25)),
|
||||
("does_not_exist".to_string(), Literal::Int(25)),
|
||||
],
|
||||
);
|
||||
let result = validate_operation(syntax, &db_schema);
|
||||
|
|
@ -716,9 +755,9 @@ mod tests {
|
|||
let syntax: RawQuerySyntax = Insert(
|
||||
"users".to_string(),
|
||||
vec![
|
||||
("name".to_string(), Value::String("Alice".to_string())),
|
||||
("id".to_string(), Value::Uuid(0)),
|
||||
("age".to_string(), Value::Number(25.0)),
|
||||
("name".to_string(), Literal::String("Alice".to_string())),
|
||||
("id".to_string(), Literal::Uuid(0)),
|
||||
("age".to_string(), Literal::Number(25.0)),
|
||||
],
|
||||
);
|
||||
let result = validate_operation(syntax, &db_schema);
|
||||
|
|
@ -754,7 +793,7 @@ mod tests {
|
|||
|
||||
let syntax: RawQuerySyntax = Delete(
|
||||
"users".to_string(),
|
||||
Some(Eq("age".to_string(), Value::Int(25))),
|
||||
Some(Eq("age".to_string(), Literal::Int(25))),
|
||||
);
|
||||
let result = validate_operation(syntax, &db_schema);
|
||||
assert!(matches!(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue