Merge pull request #21 from ivanceras/parse_insert

Implement parsing insert statement
This commit is contained in:
Andy Grove 2018-09-29 15:40:05 -06:00 committed by GitHub
commit e6ad7ab715
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 170 additions and 7 deletions

View file

@ -383,6 +383,7 @@ impl Dialect for GenericSqlDialect {
"UNION",
"ALL",
"INSERT",
"INTO",
"UPDATE",
"DELETE",
"IN",
@ -432,6 +433,7 @@ impl Dialect for GenericSqlDialect {
"DATE",
"TIME",
"TIMESTAMP",
"VALUES",
];
}

View file

@ -89,6 +89,7 @@ impl Parser {
"SELECT" => Ok(self.parse_select()?),
"CREATE" => Ok(self.parse_create()?),
"DELETE" => Ok(self.parse_delete()?),
"INSERT" => Ok(self.parse_insert()?),
_ => return parser_err!(format!("No prefix parser for keyword {}", k)),
},
Token::Mult => Ok(ASTNode::SQLWildcard),
@ -266,7 +267,8 @@ impl Parser {
/// Get the previous token and decrement the token index
pub fn prev_token(&mut self) -> Option<Token> {
if self.index > 0 {
Some(self.tokens[self.index - 1].clone())
self.index = self.index - 1;
Some(self.tokens[self.index].clone())
} else {
None
}
@ -313,10 +315,10 @@ impl Parser {
} else {
Ok(false)
},
_ => parser_err!(format!(
other => parser_err!(format!(
"expected token {:?} but was {:?}",
expected,
self.prev_token()
other,
)),
}
}
@ -426,6 +428,59 @@ impl Parser {
}
}
pub fn parse_compound_identifier(&mut self, separator: &Token) -> Result<ASTNode, ParserError> {
let mut idents = vec![];
let mut expect_identifier = true;
loop {
let token = &self.next_token();
match token{
Some(token) => match token{
Token::Identifier(s) => if expect_identifier{
expect_identifier = false;
idents.push(s.to_string());
}else{
self.prev_token();
break;
}
token if token == separator => {
if expect_identifier{
return parser_err!(format!("Expecting identifier, found {:?}", token));
}else{
expect_identifier = true;
continue;
}
}
_ => {
self.prev_token();
break;
}
}
None => {
self.prev_token();
break;
}
}
}
Ok(ASTNode::SQLCompoundIdentifier(idents))
}
pub fn parse_tablename(&mut self) -> Result<String, ParserError> {
let identifier = self.parse_compound_identifier(&Token::Period)?;
match identifier{
ASTNode::SQLCompoundIdentifier(idents) => Ok(idents.join(".")),
other => parser_err!(format!("Expecting compound identifier, found: {:?}", other)),
}
}
pub fn parse_column_names(&mut self) -> Result<Vec<String>, ParserError> {
let identifier = self.parse_compound_identifier(&Token::Comma)?;
match identifier{
ASTNode::SQLCompoundIdentifier(idents) => Ok(idents),
other => parser_err!(format!("Expecting compound identifier, found: {:?}", other)),
}
}
pub fn parse_precision(&mut self) -> Result<usize, ParserError> {
//TODO: error handling
Ok(self.parse_optional_precision()?.unwrap())
@ -542,6 +597,24 @@ impl Parser {
}
}
/// Parse an INSERT statement
pub fn parse_insert(&mut self) -> Result<ASTNode, ParserError> {
self.parse_keyword("INTO");
let table_name = self.parse_tablename()?;
let columns = if self.consume_token(&Token::LParen)?{
let column_names = self.parse_column_names()?;
self.consume_token(&Token::RParen)?;
column_names
}else{
vec![]
};
self.parse_keyword("VALUES");
self.consume_token(&Token::LParen)?;
let values = self.parse_expr_list()?;
self.consume_token(&Token::RParen)?;
Ok(ASTNode::SQLInsert{table_name, columns, values: vec![values]})
}
/// Parse a comma-delimited list of SQL expressions
pub fn parse_expr_list(&mut self) -> Result<Vec<ASTNode>, ParserError> {
let mut expr_list: Vec<ASTNode> = vec![];
@ -622,6 +695,19 @@ mod tests {
use super::super::dialect::GenericSqlDialect;
use super::*;
#[test]
fn test_prev_index(){
let sql: &str = "SELECT version()";
let mut tokenizer = Tokenizer::new(&GenericSqlDialect{}, &sql);
let tokens = tokenizer.tokenize().expect("error tokenizing");
let mut parser = Parser::new(tokens);
assert_eq!(parser.prev_token(), None);
assert_eq!(parser.next_token(), Some(Token::Keyword("SELECT".into())));
assert_eq!(parser.next_token(), Some(Token::Identifier("version".into())));
assert_eq!(parser.prev_token(), Some(Token::Identifier("version".into())));
assert_eq!(parser.prev_token(), Some(Token::Keyword("SELECT".into())));
}
#[test]
fn parse_delete_statement() {
let sql: &str = "DELETE FROM 'table'";
@ -685,6 +771,77 @@ mod tests {
}
}
#[test]
fn parse_simple_insert() {
let sql = String::from("INSERT INTO customer VALUES(1, 2, 3)");
let ast = parse_sql(&sql);
match ast {
ASTNode::SQLInsert {
table_name, columns, values, ..
} => {
assert_eq!(table_name, "customer");
assert!(columns.is_empty());
assert_eq!(vec![vec![ASTNode::SQLLiteralLong(1),ASTNode::SQLLiteralLong(2),ASTNode::SQLLiteralLong(3)]], values);
}
_ => assert!(false),
}
}
#[test]
fn parse_common_insert() {
let sql = String::from("INSERT INTO public.customer VALUES(1, 2, 3)");
let ast = parse_sql(&sql);
match ast {
ASTNode::SQLInsert {
table_name, columns, values, ..
} => {
assert_eq!(table_name, "public.customer");
assert!(columns.is_empty());
assert_eq!(vec![vec![ASTNode::SQLLiteralLong(1),ASTNode::SQLLiteralLong(2),ASTNode::SQLLiteralLong(3)]], values);
}
_ => assert!(false),
}
}
#[test]
fn parse_complex_insert() {
let sql = String::from("INSERT INTO db.public.customer VALUES(1,2,3)");
let ast = parse_sql(&sql);
match ast {
ASTNode::SQLInsert {
table_name, columns, values, ..
} => {
assert_eq!(table_name, "db.public.customer");
assert!(columns.is_empty());
assert_eq!(vec![vec![ASTNode::SQLLiteralLong(1),ASTNode::SQLLiteralLong(2),ASTNode::SQLLiteralLong(3)]], values);
}
_ => assert!(false),
}
}
#[test]
fn parse_invalid_table_name() {
let mut parser = parser("db.public..customer");
let ast = parser.parse_tablename();
assert!(ast.is_err());
}
#[test]
fn parse_insert_with_columns() {
let sql = String::from("INSERT INTO public.customer (id, name, active) VALUES(1, 2, 3)");
let ast = parse_sql(&sql);
match ast {
ASTNode::SQLInsert {
table_name, columns, values, ..
} => {
assert_eq!(table_name, "public.customer");
assert_eq!(columns, vec!["id".to_string(), "name".to_string(), "active".to_string()]);
assert_eq!(vec![vec![ASTNode::SQLLiteralLong(1),ASTNode::SQLLiteralLong(2),ASTNode::SQLLiteralLong(3)]], values);
}
_ => assert!(false),
}
}
#[test]
fn parse_select_wildcard() {
let sql = String::from("SELECT * FROM customer");
@ -951,12 +1108,16 @@ mod tests {
}
fn parse_sql(sql: &str) -> ASTNode {
let dialect = GenericSqlDialect {};
let mut tokenizer = Tokenizer::new(&dialect, &sql);
let tokens = tokenizer.tokenize().unwrap();
let mut parser = Parser::new(tokens);
let mut parser = parser(sql);
let ast = parser.parse().unwrap();
ast
}
fn parser(sql: &str) -> Parser {
let dialect = GenericSqlDialect {};
let mut tokenizer = Tokenizer::new(&dialect, &sql);
let tokens = tokenizer.tokenize().unwrap();
Parser::new(tokens)
}
}