124 lines
4.4 KiB
Rust
124 lines
4.4 KiB
Rust
use super::common::{parse_column_name, parse_condition, parse_table_name};
|
|
use crate::syntax::{ColumnSelection, RawQuerySyntax};
|
|
use nom::{
|
|
branch::alt,
|
|
bytes::complete::tag,
|
|
character::complete::{char, multispace0, multispace1},
|
|
combinator::map,
|
|
error::Error,
|
|
multi::separated_list0,
|
|
sequence::terminated,
|
|
IResult,
|
|
};
|
|
|
|
pub fn parse_select(input: &str) -> IResult<&str, RawQuerySyntax> {
|
|
let (input, _) = tag("SELECT")(input)?;
|
|
let (input, _) = multispace1(input)?;
|
|
|
|
let (input, column_selection) = try_parse_column_selection(input)?;
|
|
let (input, _) = multispace0(input)?;
|
|
let (input, _) = tag("FROM")(input)?;
|
|
let (input, _) = multispace1(input)?;
|
|
let (input, table_name) = parse_table_name(input)?;
|
|
let (input, _) = multispace0(input)?;
|
|
let (input, condition) = parse_condition(input)?;
|
|
let (input, _) = multispace0(input)?;
|
|
// TODO: make it optional?
|
|
let (input, _) = tag(";")(input)?;
|
|
Ok((
|
|
input,
|
|
RawQuerySyntax::Select(table_name.to_string(), column_selection, condition),
|
|
))
|
|
}
|
|
|
|
pub fn try_parse_column_selection(input: &str) -> IResult<&str, ColumnSelection> {
|
|
let all_parser = map(tag::<&str, &str, Error<&str>>("*"), |_| {
|
|
ColumnSelection::All
|
|
});
|
|
let columns_parser = map(
|
|
separated_list0(terminated(char(','), multispace0), parse_column_name),
|
|
ColumnSelection::Columns,
|
|
);
|
|
alt((all_parser, columns_parser))(input)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use crate::parsing::{
|
|
common::{parse_column_name, parse_table_name},
|
|
select::parse_select,
|
|
};
|
|
use crate::syntax::{ColumnSelection, RawQuerySyntax};
|
|
|
|
#[test]
|
|
fn test_parse_select_all() {
|
|
let sql = "SELECT * FROM \"MyTable\";";
|
|
let operation = parse_select(sql).expect("should parse");
|
|
match operation {
|
|
("", RawQuerySyntax::Select(table_name, column_selection, maybe_condition)) => {
|
|
assert_eq!(table_name, "MyTable");
|
|
assert!(matches!(column_selection, ColumnSelection::All));
|
|
assert!(matches!(maybe_condition, None));
|
|
}
|
|
(input, _) => {
|
|
println!("Input to be parsed: {}", input);
|
|
panic!("expected select operation")
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_column_name() {
|
|
parse_column_name("1abc").expect_err("variable names should not start with number");
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_table_name() {
|
|
parse_table_name("\"\"").expect_err("Empty table names are not allowed");
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_select_columns() {
|
|
let sql = "SELECT name , email FROM \"AddressBook\" ;";
|
|
let operation = parse_select(sql).expect("should parse");
|
|
match operation {
|
|
("", RawQuerySyntax::Select(table_name, column_selection, maybe_condition)) => {
|
|
assert_eq!(table_name, "AddressBook");
|
|
assert!(matches!(column_selection, ColumnSelection::Columns(_)));
|
|
match column_selection {
|
|
ColumnSelection::Columns(column_names) => {
|
|
assert_eq!(column_names, vec!["name", "email"]);
|
|
}
|
|
_ => {
|
|
panic!("should select columns")
|
|
}
|
|
}
|
|
assert!(matches!(maybe_condition, None));
|
|
}
|
|
(input, _) => {
|
|
println!("Input to be parsed: {}", input);
|
|
panic!("expected select operation")
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_parse_select_where() {
|
|
use crate::syntax::Condition;
|
|
let sql = "SELECT * FROM \"AddressBook\" WHERE id = 5 ;";
|
|
let operation = parse_select(sql).expect("should parse");
|
|
match operation {
|
|
("", RawQuerySyntax::Select(table_name, column_selection, maybe_condition)) => {
|
|
assert_eq!(table_name, "AddressBook");
|
|
assert!(matches!(column_selection, ColumnSelection::All));
|
|
assert!(matches!(maybe_condition, Some(Condition::Eq(_, _))));
|
|
}
|
|
(input, _) => {
|
|
println!("Input to be parsed: {}", input);
|
|
panic!("expected select operation")
|
|
}
|
|
}
|
|
}
|
|
// TODO: a test with multiple statements
|
|
// TODO: allow underscores in identifiers
|
|
}
|