Add option type and option value parsers value->literal in parser, implement Option literal
140 lines
4.5 KiB
Rust
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!()
|
|
}
|
|
}
|
|
}
|
|
}
|