minisql/parser/src/parsing/create.rs
2024-02-05 23:11:38 +01:00

155 lines
5.6 KiB
Rust

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<ColumnSchema>> {
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);
}
_ => {}
}
}
}