mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-08-04 06:18:17 +00:00
Merge pull request #21 from ivanceras/parse_insert
Implement parsing insert statement
This commit is contained in:
commit
e6ad7ab715
2 changed files with 170 additions and 7 deletions
|
@ -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",
|
||||
];
|
||||
}
|
||||
|
||||
|
|
175
src/sqlparser.rs
175
src/sqlparser.rs
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue