formatting

This commit is contained in:
Yuriy Dupyn 2024-02-05 23:11:38 +01:00
parent ad98cfafb2
commit c25c6edc6a
29 changed files with 886 additions and 571 deletions

View file

@ -1,6 +1,12 @@
use crate::syntax::RawQuerySyntax;
use minisql::{interpreter2::DbSchema, operation::Operation};
use nom::{branch::alt, character::complete::{multispace0, char}, multi::many1, sequence::{delimited, terminated}, IResult};
use nom::{
branch::alt,
character::complete::{char, multispace0},
multi::many1,
sequence::{delimited, terminated},
IResult,
};
use thiserror::Error;
use crate::{
@ -35,7 +41,10 @@ fn parse_statement(input: &str) -> IResult<&str, RawQuerySyntax> {
/// Parse one or more statements
#[allow(dead_code)]
fn parse_statement1(input: &str) -> IResult<&str, Vec<RawQuerySyntax>> {
many1(terminated(parse_statement, delimited(multispace0, char(';'), multispace0)))(input)
many1(terminated(
parse_statement,
delimited(multispace0, char(';'), multispace0),
))(input)
}
pub fn parse_and_validate(str_query: String, db_schema: &DbSchema) -> Result<Operation, Error> {
@ -53,11 +62,9 @@ mod test {
#[test]
fn test_parse_two_select() {
let (rest, sntx) = parse_statement1("SELECT * FROM users ; SELECT * FROM cities ; ").expect("should parse");
assert_eq!(
sntx.len(),
2
);
let (rest, sntx) = parse_statement1("SELECT * FROM users ; SELECT * FROM cities ; ")
.expect("should parse");
assert_eq!(sntx.len(), 2);
assert_eq!(rest, "");
}
@ -68,11 +75,10 @@ mod test {
SELECT * FROM users ;
INSERT INTO table1 (id, data) VALUES (u4, 30) ;
INSERT INTO table1 (id, data) VALUES (u5, 40) ;
"#).expect("should parse");
assert_eq!(
sntx.len(),
4
);
"#,
)
.expect("should parse");
assert_eq!(sntx.len(), 4);
assert_eq!(rest, "");
}
}

View file

@ -1,10 +1,19 @@
use minisql::type_system::DbType;
use nom::{
branch::alt, bytes::complete::{tag, take_while}, character::{complete::{alphanumeric1, anychar, char, multispace0, multispace1}, is_alphanumeric}, combinator::peek, error::make_error, sequence::{delimited, terminated}, IResult, Parser
branch::alt,
bytes::complete::{tag, take_while},
character::{
complete::{alphanumeric1, anychar, char, multispace0, multispace1},
is_alphanumeric,
},
combinator::peek,
error::make_error,
sequence::{delimited, terminated},
IResult, Parser,
};
use crate::syntax::Condition;
use super::literal::parse_literal;
use crate::syntax::Condition;
pub fn parse_table_name(input: &str) -> IResult<&str, &str> {
alt((
@ -16,11 +25,9 @@ pub fn parse_table_name(input: &str) -> IResult<&str, &str> {
pub fn parse_identifier(input: &str) -> IResult<&str, &str> {
let (_, first) = peek(anychar)(input)?;
if first.is_alphabetic() || first == '_' {
take_while(|c: char| {
match c {
'a'..='z' | 'A'..='Z' | '_' | '0'..='9' => true,
_ => false
}
take_while(|c: char| match c {
'a'..='z' | 'A'..='Z' | '_' | '0'..='9' => true,
_ => false,
})(input)
} else {
Err(nom::Err::Error(make_error(
@ -35,22 +42,13 @@ pub fn parse_column_name(input: &str) -> IResult<&str, String> {
}
pub fn parse_db_type(input: &str) -> IResult<&str, DbType> {
let (input, db_type) = alt(
(
tag("STRING")
.map(|_| DbType::String),
tag("INT")
.map(|_| DbType::Int),
tag("NUMBER")
.map(|_| DbType::Number),
tag("UUID")
.map(|_| DbType::Uuid),
delimited(tag("Option("), parse_db_type, tag(")"))
.map(|ty| {
DbType::Option(Box::new(ty))
})
)
)(input)?;
let (input, db_type) = alt((
tag("STRING").map(|_| DbType::String),
tag("INT").map(|_| DbType::Int),
tag("NUMBER").map(|_| DbType::Number),
tag("UUID").map(|_| DbType::Uuid),
delimited(tag("Option("), parse_db_type, tag(")")).map(|ty| DbType::Option(Box::new(ty))),
))(input)?;
Ok((input, db_type))
}
@ -122,10 +120,7 @@ mod tests {
parse_identifier("_variable__Test").expect("should parse").1,
"_variable__Test"
);
assert!(matches!(
parse_identifier("123_variable__Test"),
Err(_)
));
assert!(matches!(parse_identifier("123_variable__Test"), Err(_)));
}
#[test]
@ -139,8 +134,12 @@ mod tests {
#[test]
fn test_parse_nested_option_int_type() {
assert_eq!(
parse_db_type("Option(Option(Option(INT)))").expect("should parse").1,
DbType::Option(Box::new(DbType::Option(Box::new(DbType::Option(Box::new(DbType::Int))))))
parse_db_type("Option(Option(Option(INT)))")
.expect("should parse")
.1,
DbType::Option(Box::new(DbType::Option(Box::new(DbType::Option(
Box::new(DbType::Int)
)))))
);
}
}

View file

@ -77,8 +77,7 @@ mod tests {
#[test]
fn test_parse_create_no_quotes_table_name() {
parse_create("CREATE TABLE Table1(id UUID PRIMARY KEY,column1 INT)")
.expect("should parse");
parse_create("CREATE TABLE Table1(id UUID PRIMARY KEY,column1 INT)").expect("should parse");
}
#[test]
@ -91,8 +90,8 @@ mod tests {
#[test]
fn test_parse_create() {
let (_, create) = parse_create("CREATE TABLE \"Table1\"( id UUID , column1 INT )")
.expect("should parse");
let (_, create) =
parse_create("CREATE TABLE \"Table1\"( id UUID , column1 INT )").expect("should parse");
assert!(matches!(create, RawQuerySyntax::CreateTable(_)));
match create {
RawQuerySyntax::CreateTable(schema) => {
@ -114,11 +113,13 @@ mod tests {
_ => {}
}
}
#[test]
fn test_parse_create_option() {
let (_, create) = parse_create("CREATE TABLE games (id UUID PRIMARY KEY, name STRING, year Option(INT), price NUMBER)")
.expect("should parse");
let (_, create) = parse_create(
"CREATE TABLE games (id UUID PRIMARY KEY, name STRING, year Option(INT), price NUMBER)",
)
.expect("should parse");
assert!(matches!(create, RawQuerySyntax::CreateTable(_)));
match create {
RawQuerySyntax::CreateTable(schema) => {
@ -139,16 +140,12 @@ mod tests {
assert_eq!(column1_column.type_, DbType::String);
let column = schema.get_column(&"year".to_string());
let Some(column) = column else {
panic!()
};
let Some(column) = column else { panic!() };
assert_eq!(column.column_name, "year".to_string());
assert_eq!(column.type_, DbType::Option(Box::new(DbType::Int)));
let column = schema.get_column(&"price".to_string());
let Some(column) = column else {
panic!()
};
let Some(column) = column else { panic!() };
assert_eq!(column.column_name, "price".to_string());
assert_eq!(column.type_, DbType::Number);
}

View file

@ -29,22 +29,19 @@ mod tests {
#[test]
fn test_parse_delete() {
let (_, sntx) =
parse_delete("DELETE FROM \"T1\" WHERE id = 1").expect("should parse");
let (_, sntx) = parse_delete("DELETE FROM \"T1\" WHERE id = 1").expect("should parse");
assert!(matches!(sntx, RawQuerySyntax::Delete(_, _)))
}
#[test]
fn test_parse_delete_with_spaces() {
let (_, sntx) =
parse_delete("DELETE FROM T1 WHERE id = 1").expect("should parse");
let (_, sntx) = parse_delete("DELETE FROM T1 WHERE id = 1").expect("should parse");
assert!(matches!(sntx, RawQuerySyntax::Delete(_, _)))
}
#[test]
fn test_parse_delete_none() {
let (_, sntx) =
parse_delete("DELETE FROM games WHERE year = None").expect("should parse");
let (_, sntx) = parse_delete("DELETE FROM games WHERE year = None").expect("should parse");
if let RawQuerySyntax::Delete(tname, Some(Condition::Eq(column_name, lit))) = sntx {
assert_eq!(tname, "games".to_string());
assert_eq!(column_name, "year".to_string());

View file

@ -68,10 +68,7 @@ mod tests {
insertion_values,
vec![
("id".to_string(), Literal::Int(1)),
(
"data".to_string(),
Literal::String("Text".to_string())
)
("data".to_string(), Literal::String("Text".to_string()))
]
);
}
@ -83,8 +80,7 @@ mod tests {
#[test]
fn test_parse_insert_with_spaces() {
let sql =
"INSERT INTO \"MyTable\" ( id, data ) VALUES ( 1, \"Text\" )";
let sql = "INSERT INTO \"MyTable\" ( id, data ) VALUES ( 1, \"Text\" )";
let operation = parse_insert(sql).expect("should parse");
match operation {
("", RawQuerySyntax::Insert(table_name, insertion_values)) => {
@ -93,10 +89,7 @@ mod tests {
insertion_values,
vec![
("id".to_string(), Literal::Int(1)),
(
"data".to_string(),
Literal::String("Text".to_string())
)
("data".to_string(), Literal::String("Text".to_string()))
]
);
}
@ -117,18 +110,12 @@ mod tests {
insertion_values,
vec![
("id".to_string(), Literal::Uuid(12345)),
(
"name".to_string(),
Literal::String("Doom".to_string())
),
("name".to_string(), Literal::String("Doom".to_string())),
(
"year".to_string(),
Literal::Some(Box::new(Literal::Int(1993)))
),
(
"price".to_string(),
Literal::Number(6.5)
)
("price".to_string(), Literal::Number(6.5))
]
);
}

View file

@ -1,5 +1,12 @@
use nom::{
branch::alt, bytes::complete::tag, character::complete::{char, digit1, none_of, u64}, combinator::opt, error::make_error, multi::many0, sequence::{delimited, pair, preceded}, IResult, Parser
branch::alt,
bytes::complete::tag,
character::complete::{char, digit1, none_of, u64},
combinator::opt,
error::make_error,
multi::many0,
sequence::{delimited, pair, preceded},
IResult, Parser,
};
#[derive(Debug, PartialEq)]
@ -13,7 +20,13 @@ pub enum Literal {
}
pub fn parse_literal(input: &str) -> IResult<&str, Literal> {
alt((parse_option, parse_string, parse_number, parse_int, parse_uuid))(input)
alt((
parse_option,
parse_string,
parse_number,
parse_int,
parse_uuid,
))(input)
}
pub fn parse_number(input: &str) -> IResult<&str, Literal> {
@ -92,16 +105,16 @@ pub fn parse_string(input: &str) -> IResult<&str, Literal> {
}
pub fn parse_uuid(input: &str) -> IResult<&str, Literal> {
let (input, value) = pair(char('u'), u64)(input)
.map(|(input, (_, v))| (input, Literal::Uuid(v)))?;
let (input, value) =
pair(char('u'), u64)(input).map(|(input, (_, v))| (input, Literal::Uuid(v)))?;
Ok((input, value))
}
pub fn parse_option(input: &str) -> IResult<&str, Literal> {
let (input, inner) = alt((tag("None")
.map(|_| Literal::None), delimited(tag("Some("), parse_literal, tag(")")).map(|v| {
Literal::Some(Box::new(v))
})))(input)?;
let (input, inner) = alt((
tag("None").map(|_| Literal::None),
delimited(tag("Some("), parse_literal, tag(")")).map(|v| Literal::Some(Box::new(v))),
))(input)?;
Ok((input, inner))
}
@ -114,24 +127,15 @@ mod tests {
fn test_string_parser() {
assert_eq!(
parse_string(r#""simple""#),
Ok((
"",
Literal::String(String::from("simple"))
))
Ok(("", Literal::String(String::from("simple"))))
);
assert_eq!(
parse_string(r#""\"\t\r\n\\""#),
Ok((
"",
Literal::String(String::from("\"\t\r\n\\"))
))
Ok(("", Literal::String(String::from("\"\t\r\n\\"))))
);
assert_eq!(
parse_string(r#""name is \"John\".""#),
Ok((
"",
Literal::String(String::from("name is \"John\"."))
))
Ok(("", Literal::String(String::from("name is \"John\"."))))
);
}
@ -147,13 +151,11 @@ mod tests {
let (_, _) = parse_literal("\"STRING\"").expect("should parse");
let (input, value) =
parse_literal("\"abcdefghkjklmnopqrstuvwxyz!@#$%^&*()_+ \"").expect("should parse");
parse_literal("\"abcdefghkjklmnopqrstuvwxyz!@#$%^&*()_+ \"").expect("should parse");
assert_eq!(input, "");
assert_eq!(
value,
Literal::String(
"abcdefghkjklmnopqrstuvwxyz!@#$%^&*()_+ ".to_string()
)
Literal::String("abcdefghkjklmnopqrstuvwxyz!@#$%^&*()_+ ".to_string())
);
}
@ -192,18 +194,12 @@ mod tests {
#[test]
fn test_parse_int() {
assert_eq!(
parse_literal("5134616"),
Ok(("", Literal::Int(5134616)))
);
assert_eq!(parse_literal("5134616"), Ok(("", Literal::Int(5134616))));
}
#[test]
fn test_parse_uuid() {
assert_eq!(
parse_uuid("u131515"),
Ok(("", Literal::Uuid(131515)))
)
assert_eq!(parse_uuid("u131515"), Ok(("", Literal::Uuid(131515))))
}
#[test]
@ -214,7 +210,10 @@ mod tests {
);
assert_eq!(
parse_option("Some(Some(3))"),
Ok(("", Literal::Some(Box::new(Literal::Some(Box::new(Literal::Int(3)))))))
Ok((
"",
Literal::Some(Box::new(Literal::Some(Box::new(Literal::Int(3)))))
))
);
assert_eq!(
parse_option("Some(None)"),

View file

@ -42,7 +42,9 @@ pub fn try_parse_column_selection(input: &str) -> IResult<&str, ColumnSelection>
#[cfg(test)]
mod tests {
use crate::parsing::{
common::{parse_column_name, parse_table_name}, literal::Literal, select::parse_select
common::{parse_column_name, parse_table_name},
literal::Literal,
select::parse_select,
};
use crate::syntax::{ColumnSelection, RawQuerySyntax};
@ -137,7 +139,7 @@ mod tests {
}
}
}
#[test]
fn test_parse_select_option_none() {
use crate::syntax::Condition;

View file

@ -71,4 +71,3 @@ impl RawTableSchema {
.collect()
}
}

View file

@ -37,7 +37,7 @@ pub enum ValidationError {
expected_type: DbType,
},
#[error("Expected type {expected_type:?}, received None")]
UnexpectedNoneValue{ expected_type: DbType },
UnexpectedNoneValue { expected_type: DbType },
#[error("values for required columns {0:?} are missing")]
RequiredColumnsAreMissing(Vec<ColumnName>),
}
@ -284,7 +284,10 @@ fn validate_condition(
)?;
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, literal_to_value(value, &expected_type))))
Ok(Some(operation::Condition::Eq(
column,
literal_to_value(value, &expected_type),
)))
} else {
Err(ValidationError::TypeMismatch {
column_name: column_name.to_string(),
@ -339,10 +342,7 @@ where
None
}
fn get_table_schema(
db_schema: &DbSchema,
table_name: &TableName,
) -> Option<Arc<TableSchema>> {
fn get_table_schema(db_schema: &DbSchema, table_name: &TableName) -> Option<Arc<TableSchema>> {
let (_, _, table_schema) = db_schema
.iter()
.find(|(tname, _, _)| table_name.eq(tname))?;
@ -360,15 +360,18 @@ fn literal_to_value(lit: Literal, hint: &DbType) -> Value {
if let DbType::Option(t) = hint {
Value::None(*t.clone())
} else {
// By the time calling current function, hopefully we should be sure about the
// 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> {
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,
@ -379,7 +382,9 @@ fn type_from_literal_with_type_hint(lit: &Literal, hint: &DbType) -> Result<DbTy
if matches!(hint, DbType::Option(_)) {
hint.clone()
} else {
return Err(ValidationError::UnexpectedNoneValue { expected_type: hint.clone() })
return Err(ValidationError::UnexpectedNoneValue {
expected_type: hint.clone(),
});
}
}
})