use nom::{ bytes::complete::tag, character::complete::{char, multispace0, multispace1}, combinator::opt, multi::separated_list0, sequence::terminated, IResult, }; use super::common::{parse_db_type, parse_identifier, parse_table_name}; use crate::syntax::{ColumnSchema, RawQuerySyntax, RawTableSchema}; 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 (input, _) = char(')')(input)?; let schema = RawTableSchema { table_name: table_name.to_string(), columns: column_definitions, }; Ok((input, RawQuerySyntax::CreateTable(schema))) } fn parse_column_definitions(input: &str) -> IResult<&str, Vec> { 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")) } fn parse_column_definition(input: &str) -> IResult<&str, ColumnSchema> { 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, ColumnSchema { column_name: identifier.to_string(), type_: db_type, is_primary: pk, }, )) } #[cfg(test)] mod tests { use minisql::type_system::DbType; 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(schema) => { assert_eq!(schema.table_name, "Table1"); assert_eq!(schema.number_of_columns(), 2); let result_id = schema.get_column(&"id".to_string()); assert!(matches!(result_id, Some(_))); let Some(id_column) = result_id else { panic!() }; assert_eq!(id_column.column_name, "id".to_string()); let result_column1 = schema.get_column(&"column1".to_string()); assert!(matches!(result_column1, Some(_))); let Some(column1_column) = result_column1 else { panic!() }; assert_eq!(column1_column.column_name, "column1".to_string()); } _ => {} } } #[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"); assert!(matches!(create, RawQuerySyntax::CreateTable(_))); match create { RawQuerySyntax::CreateTable(schema) => { assert_eq!(schema.table_name, "games"); assert_eq!(schema.number_of_columns(), 4); let result_id = schema.get_column(&"id".to_string()); assert!(matches!(result_id, Some(_))); let Some(id_column) = result_id else { panic!() }; assert_eq!(id_column.column_name, "id".to_string()); let result_column1 = schema.get_column(&"name".to_string()); assert!(matches!(result_column1, Some(_))); let Some(column1_column) = result_column1 else { panic!() }; assert_eq!(column1_column.column_name, "name".to_string()); assert_eq!(column1_column.type_, DbType::String); let column = schema.get_column(&"year".to_string()); 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!() }; assert_eq!(column.column_name, "price".to_string()); assert_eq!(column.type_, DbType::Number); } _ => {} } } }