mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-09-26 23:49:10 +00:00
Implement parsing insert statement
This commit is contained in:
parent
7d27abdfb4
commit
5adce6a013
2 changed files with 170 additions and 6 deletions
|
@ -383,6 +383,7 @@ impl Dialect for GenericSqlDialect {
|
||||||
"UNION",
|
"UNION",
|
||||||
"ALL",
|
"ALL",
|
||||||
"INSERT",
|
"INSERT",
|
||||||
|
"INTO",
|
||||||
"UPDATE",
|
"UPDATE",
|
||||||
"DELETE",
|
"DELETE",
|
||||||
"IN",
|
"IN",
|
||||||
|
@ -432,6 +433,7 @@ impl Dialect for GenericSqlDialect {
|
||||||
"DATE",
|
"DATE",
|
||||||
"TIME",
|
"TIME",
|
||||||
"TIMESTAMP",
|
"TIMESTAMP",
|
||||||
|
"VALUES",
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
174
src/sqlparser.rs
174
src/sqlparser.rs
|
@ -89,6 +89,7 @@ impl Parser {
|
||||||
"SELECT" => Ok(self.parse_select()?),
|
"SELECT" => Ok(self.parse_select()?),
|
||||||
"CREATE" => Ok(self.parse_create()?),
|
"CREATE" => Ok(self.parse_create()?),
|
||||||
"DELETE" => Ok(self.parse_delete()?),
|
"DELETE" => Ok(self.parse_delete()?),
|
||||||
|
"INSERT" => Ok(self.parse_insert()?),
|
||||||
_ => return parser_err!(format!("No prefix parser for keyword {}", k)),
|
_ => return parser_err!(format!("No prefix parser for keyword {}", k)),
|
||||||
},
|
},
|
||||||
Token::Mult => Ok(ASTNode::SQLWildcard),
|
Token::Mult => Ok(ASTNode::SQLWildcard),
|
||||||
|
@ -314,10 +315,10 @@ impl Parser {
|
||||||
} else {
|
} else {
|
||||||
Ok(false)
|
Ok(false)
|
||||||
},
|
},
|
||||||
_ => parser_err!(format!(
|
other => parser_err!(format!(
|
||||||
"expected token {:?} but was {:?}",
|
"expected token {:?} but was {:?}",
|
||||||
expected,
|
expected,
|
||||||
self.prev_token()
|
other,
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -427,6 +428,61 @@ 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();
|
||||||
|
println!("--> token: {:?}", 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)?;
|
||||||
|
println!(" peek : {:?}", self.peek_token());
|
||||||
|
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> {
|
pub fn parse_precision(&mut self) -> Result<usize, ParserError> {
|
||||||
//TODO: error handling
|
//TODO: error handling
|
||||||
Ok(self.parse_optional_precision()?.unwrap())
|
Ok(self.parse_optional_precision()?.unwrap())
|
||||||
|
@ -543,6 +599,29 @@ 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()?;
|
||||||
|
println!("table_name: {}", table_name);
|
||||||
|
println!("peek token: {:?}", self.peek_token());
|
||||||
|
let columns = if self.consume_token(&Token::LParen)?{
|
||||||
|
println!("Got (");
|
||||||
|
let column_names = self.parse_column_names()?;
|
||||||
|
println!("column names: {:?}", column_names);
|
||||||
|
self.consume_token(&Token::RParen)?;
|
||||||
|
column_names
|
||||||
|
}else{
|
||||||
|
vec![]
|
||||||
|
};
|
||||||
|
println!("column names: {:?}", columns);
|
||||||
|
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
|
/// Parse a comma-delimited list of SQL expressions
|
||||||
pub fn parse_expr_list(&mut self) -> Result<Vec<ASTNode>, ParserError> {
|
pub fn parse_expr_list(&mut self) -> Result<Vec<ASTNode>, ParserError> {
|
||||||
let mut expr_list: Vec<ASTNode> = vec![];
|
let mut expr_list: Vec<ASTNode> = vec![];
|
||||||
|
@ -699,6 +778,85 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_simple_insert() {
|
||||||
|
let sql = String::from("INSERT INTO customer VALUES(1, 2, 3)");
|
||||||
|
println!("SQL: {}", sql);
|
||||||
|
let ast = parse_sql(&sql);
|
||||||
|
println!("ast: {:?}", ast);
|
||||||
|
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)");
|
||||||
|
println!("SQL: {}", sql);
|
||||||
|
let ast = parse_sql(&sql);
|
||||||
|
println!("ast: {:?}", ast);
|
||||||
|
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)");
|
||||||
|
println!("SQL: {}", sql);
|
||||||
|
let ast = parse_sql(&sql);
|
||||||
|
println!("ast: {:?}", ast);
|
||||||
|
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)");
|
||||||
|
println!("SQL: {}", sql);
|
||||||
|
let ast = parse_sql(&sql);
|
||||||
|
println!("ast: {:?}", ast);
|
||||||
|
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]
|
#[test]
|
||||||
fn parse_select_wildcard() {
|
fn parse_select_wildcard() {
|
||||||
let sql = String::from("SELECT * FROM customer");
|
let sql = String::from("SELECT * FROM customer");
|
||||||
|
@ -965,12 +1123,16 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_sql(sql: &str) -> ASTNode {
|
fn parse_sql(sql: &str) -> ASTNode {
|
||||||
let dialect = GenericSqlDialect {};
|
let mut parser = parser(sql);
|
||||||
let mut tokenizer = Tokenizer::new(&dialect, &sql);
|
|
||||||
let tokens = tokenizer.tokenize().unwrap();
|
|
||||||
let mut parser = Parser::new(tokens);
|
|
||||||
let ast = parser.parse().unwrap();
|
let ast = parser.parse().unwrap();
|
||||||
ast
|
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