use minisql::{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 super::common::{parse_table_name, parse_identifier, parse_db_type}; use crate::syntax::RawQuerySyntax; pub fn parse_create(input: &str) -> IResult<&str, RawQuerySyntax> { 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 = 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, RawQuerySyntax::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 crate::parsing::create::parse_create; use crate::syntax::RawQuerySyntax; #[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, RawQuerySyntax::CreateTable(_ ,_))); match create { RawQuerySyntax::CreateTable(name, schema) => { assert_eq!(name, "Table1"); assert_eq!(schema.number_of_columns(), 2); assert_eq!(schema.get_column_position(&"id".to_string()).unwrap(), 0); assert_eq!(schema.get_column_position(&"column1".to_string()).unwrap(), 1); } _ => {} } } }