minisql/parser/src/core.rs
Maxim Svistunov de8c6164cf Resolve TODOs in parsing
Return error for queries containing non-ASCII characters

Allow underscores in identifiers

Add a delete statement test with spaces

Remove trailing spaces and semicolons from tests and parsers

Complete the multiple statement parser TODO
2024-02-04 14:01:47 +01:00

90 lines
2.8 KiB
Rust

use crate::syntax::RawQuerySyntax;
use minisql::{interpreter::DbSchema, operation::Operation};
use nom::{branch::alt, character::complete::{multispace0, char}, multi::many1, sequence::{delimited, terminated}, IResult};
use thiserror::Error;
use crate::{
parsing::{
create::parse_create, delete::parse_delete, index::parse_create_index,
insert::parse_insert, select::parse_select,
},
validation::{validate_operation, ValidationError},
};
#[derive(Debug, Error)]
pub enum Error {
#[error("parsing error: {0}")]
ParsingError(String),
#[error("validation error: {0}")]
ValidationError(#[from] ValidationError),
}
/// Parse single statement
fn parse_statement(input: &str) -> IResult<&str, RawQuerySyntax> {
alt((
parse_insert,
parse_create,
parse_delete,
//parse_drop,
parse_select,
// parse_update,
parse_create_index,
))(input)
}
/// Parse one or more statements
fn parse_statement1(input: &str) -> IResult<&str, Vec<RawQuerySyntax>> {
many1(terminated(parse_statement, delimited(multispace0, char(';'), multispace0)))(input)
}
pub fn parse_and_validate(str_query: String, db_schema: &DbSchema) -> Result<Operation, Error> {
if let Some(non_ascii) = str_query.chars().find(|c| c.len_utf8() > 1) {
return Err(Error::ParsingError(
format!("Non ASCII character found: {}", non_ascii)
));
}
let (_, op) =
parse_statement(str_query.as_str()).map_err(|err| Error::ParsingError(err.to_string()))?;
Ok(validate_operation(op, db_schema)?)
}
#[cfg(test)]
mod test {
use crate::core::parse_statement1;
use crate::parse_and_validate;
use crate::Error;
#[test]
fn test_non_unicode() {
let result = parse_and_validate(format!("SELECT * FROM users WHERE name = \"\""), &Default::default());
assert!(matches!(result, Err(Error::ParsingError(_))));
if let Err(Error::ParsingError(err)) = result {
assert_eq!(err, format!("Non ASCII character found: {}", '京'));
}
}
#[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
);
assert_eq!(rest, "");
}
#[test]
fn test_parse_three_insert_one_select() {
let (rest, sntx) = parse_statement1(
r#"INSERT INTO table1 (id, data) VALUES (u1, 2);
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
);
assert_eq!(rest, "");
}
}