minisql/parser/src/parsing/insert.rs
Maxim Svistunov 6245dba4f0 Parsing and validation for Option
Add option type and option value parsers

value->literal in parser, implement Option literal
2024-02-04 14:01:47 +01:00

140 lines
4.5 KiB
Rust

use super::{
common::{parse_identifier, parse_table_name},
literal::{parse_literal, Literal},
};
use crate::syntax::RawQuerySyntax;
use nom::{
bytes::complete::tag,
character::complete::{char, multispace0, multispace1},
combinator::map,
multi::separated_list0,
sequence::terminated,
IResult,
};
pub fn parse_insert(input: &str) -> IResult<&str, RawQuerySyntax> {
let (input, _) = tag("INSERT")(input)?;
let (input, _) = multispace1(input)?;
let (input, _) = tag("INTO")(input)?;
let (input, _) = multispace1(input)?;
let (input, table_name) = parse_table_name(input)?;
let (input, _) = multispace1(input)?;
let (input, _) = char('(')(input)?;
let (input, _) = multispace0(input)?;
let (input, column_names) = parse_columns(input)?;
let (input, _) = multispace0(input)?;
let (input, _) = char(')')(input)?;
let (input, _) = multispace1(input)?;
let (input, _) = tag("VALUES")(input)?;
let (input, _) = multispace0(input)?;
let (input, _) = char('(')(input)?;
let (input, _) = multispace0(input)?;
let (input, values) = parse_values(input)?;
let (input, _) = multispace0(input)?;
let (input, _) = char(')')(input)?;
Ok((
input,
RawQuerySyntax::Insert(
table_name.to_string(),
column_names.into_iter().zip(values).collect(),
),
))
}
pub fn parse_columns(input: &str) -> IResult<&str, Vec<String>> {
separated_list0(
terminated(char(','), multispace0),
map(parse_identifier, |name| name.to_string()),
)(input)
}
pub fn parse_values(input: &str) -> IResult<&str, Vec<Literal>> {
separated_list0(terminated(char(','), multispace0), parse_literal)(input)
}
#[cfg(test)]
mod tests {
use super::parse_insert;
use crate::{parsing::literal::Literal, syntax::RawQuerySyntax};
#[test]
fn test_parse_insert() {
let sql = "INSERT INTO \"MyTable\" (id, data) VALUES(1, \"Text\")";
let syntax = parse_insert(sql).expect("should parse");
match syntax {
("", RawQuerySyntax::Insert(table_name, insertion_values)) => {
assert_eq!(table_name, "MyTable");
assert_eq!(
insertion_values,
vec![
("id".to_string(), Literal::Int(1)),
(
"data".to_string(),
Literal::String("Text".to_string())
)
]
);
}
_ => {
unreachable!()
}
}
}
#[test]
fn test_parse_insert_with_spaces() {
let sql =
"INSERT INTO \"MyTable\" ( id, data ) VALUES ( 1, \"Text\" )";
let operation = parse_insert(sql).expect("should parse");
match operation {
("", RawQuerySyntax::Insert(table_name, insertion_values)) => {
assert_eq!(table_name, "MyTable");
assert_eq!(
insertion_values,
vec![
("id".to_string(), Literal::Int(1)),
(
"data".to_string(),
Literal::String("Text".to_string())
)
]
);
}
_ => {
unreachable!()
}
}
}
#[test]
fn test_parse_insert_option() {
let sql = r#"INSERT INTO games (id, name, year, price) VALUES (u12345, "Doom", Some(1993), 6.5);"#;
let syntax = parse_insert(sql).expect("should parse");
match syntax {
(";", RawQuerySyntax::Insert(table_name, insertion_values)) => {
assert_eq!(table_name, "games");
assert_eq!(
insertion_values,
vec![
("id".to_string(), Literal::Uuid(12345)),
(
"name".to_string(),
Literal::String("Doom".to_string())
),
(
"year".to_string(),
Literal::Some(Box::new(Literal::Int(1993)))
),
(
"price".to_string(),
Literal::Number(6.5)
)
]
);
}
_ => {
panic!()
}
}
}
}