Add parsing (incl. validation)

Ensure identifiers start with alphabetical character

Rename parse_variable_name -> parse_column_name

Add DB value parsers and condition parser placeholder

Fix number parser, basic condition parser

Move select parser to select module

Add create statement parser

Move condition parser to common; add delete statement parser

Add drop statement parser

Add insert parser

Add update parser, combine operation parsers into one

Add initial validation, fix compiler warnings

Validation WIP

Allow more spaces in create statement, update TableSchema struct

Add create index parser and validator

Add todo in parse_identifier

Rework the new structure, many other changes
This commit is contained in:
Maxim Svistunov 2024-01-26 18:20:45 +01:00
parent 143dc0e5ce
commit 61c0a34253
20 changed files with 1138 additions and 39 deletions

109
parser/src/create.rs Normal file
View file

@ -0,0 +1,109 @@
use minisql::{operation::Operation, schema::{ColumnName, TableSchema}, type_system::DbType};
use nom::{
bytes::complete::tag,
character::complete::{char, multispace0, multispace1},
multi::separated_list0,
sequence::terminated,
IResult, combinator::opt,
};
use crate::common::{parse_table_name, parse_identifier, parse_db_type};
pub fn parse_create(input: &str) -> IResult<&str, Operation> {
let (input, _) = tag("CREATE")(input)?;
let (input, _) = multispace1(input)?;
let (input, _) = tag("TABLE")(input)?;
let (input, _) = multispace1(input)?;
let (input, table_name) = parse_table_name(input)?;
let (input, _) = multispace0(input)?;
let (input, _) = char('(')(input)?;
let (input, _) = multispace0(input)?;
let (input, column_definitions) = parse_column_definitions(input)?;
let mut column_name_position_mapping = Vec::new();
let mut types: Vec<DbType> = Vec::new();
let mut primary_key = None;
for (position, (column_name, db_type, pk)) in column_definitions.iter().enumerate() {
types.push(db_type.clone());
if *pk {
primary_key = Some(position);
}
column_name_position_mapping.push((column_name.clone(), position));
}
let (input, _) = char(')')(input)?;
let (input, _) = multispace0(input)?;
let (input, _) = char(';')(input)?;
let schema = TableSchema::new(
table_name.to_string(),
primary_key.unwrap_or_default(),
column_name_position_mapping,
types
);
Ok((
input,
Operation::CreateTable(table_name.to_string(), schema),
))
}
pub fn parse_column_definitions(input: &str) -> IResult<&str, Vec<(ColumnName, DbType, bool)>> {
separated_list0(terminated(char(','), multispace0), parse_column_definition)(input)
}
fn parse_primary_key(input: &str) -> IResult<&str, &str> {
let (input, _) = multispace1(input)?;
let (input, _) = tag("PRIMARY")(input)?;
let (input, _) = multispace1(input)?;
let (input, _) = tag("KEY")(input)?;
Ok((input, "PRIMARY KEY"))
}
pub fn parse_column_definition(input: &str) -> IResult<&str, (ColumnName, DbType, bool)> {
let (input, identifier) = parse_identifier(input)?;
let (input, _) = multispace1(input)?;
let (input, db_type) = parse_db_type(input)?;
let (input, pk) = opt(parse_primary_key)(input).map(|(input, pk)| (input, pk.is_some()))?;
let (input, _) = multispace0(input)?;
Ok((input, (identifier.to_string(), db_type, pk)))
}
#[cfg(test)]
mod tests {
use minisql::operation::Operation;
use crate::create::parse_create;
#[test]
fn test_parse_create_no_spaces() {
parse_create("CREATE TABLE \"Table1\"(id UUID ,column1 INT);").expect("should parse");
}
#[test]
fn test_parse_create_primary_key() {
parse_create("CREATE TABLE \"Table1\"(id UUID PRIMARY KEY,column1 INT);").expect("should parse");
}
#[test]
fn test_parse_create_no_quotes_table_name() {
parse_create("CREATE TABLE Table1(id UUID PRIMARY KEY,column1 INT);").expect("should parse");
}
#[test]
fn test_parse_create_primary_key_with_spaces() {
parse_create("CREATE TABLE \"Table1\" ( id UUID PRIMARY KEY , column1 INT ) ;").expect("should parse");
}
#[test]
fn test_parse_create() {
let (_, create) = parse_create("CREATE TABLE \"Table1\"( id UUID , column1 INT );").expect("should parse");
assert!(matches!(create, Operation::CreateTable(_ ,_)));
match create {
Operation::CreateTable(name, schema) => {
assert_eq!(name, "Table1");
assert_eq!(schema.number_of_columns(), 2);
assert_eq!(schema.column_position_from_column_name(&"id".to_string()).unwrap(), 0);
assert_eq!(schema.column_position_from_column_name(&"column1".to_string()).unwrap(), 1);
}
_ => {}
}
}
}