mirror of
https://github.com/apache/datafusion-sqlparser-rs.git
synced 2025-09-01 11:47:20 +00:00
Support DROP [TABLE|VIEW]
Co-authored-by: Jamie Brandon <jamie@scattered-thoughts.net>
This commit is contained in:
parent
4f944dd4aa
commit
187376e657
4 changed files with 145 additions and 0 deletions
|
@ -71,6 +71,7 @@ define_keywords!(
|
||||||
CALL,
|
CALL,
|
||||||
CALLED,
|
CALLED,
|
||||||
CARDINALITY,
|
CARDINALITY,
|
||||||
|
CASCADE,
|
||||||
CASCADED,
|
CASCADED,
|
||||||
CASE,
|
CASE,
|
||||||
CAST,
|
CAST,
|
||||||
|
@ -178,6 +179,7 @@ define_keywords!(
|
||||||
HOLD,
|
HOLD,
|
||||||
HOUR,
|
HOUR,
|
||||||
IDENTITY,
|
IDENTITY,
|
||||||
|
IF,
|
||||||
IN,
|
IN,
|
||||||
INDICATOR,
|
INDICATOR,
|
||||||
INNER,
|
INNER,
|
||||||
|
@ -290,6 +292,7 @@ define_keywords!(
|
||||||
REGR_SXY,
|
REGR_SXY,
|
||||||
REGR_SYY,
|
REGR_SYY,
|
||||||
RELEASE,
|
RELEASE,
|
||||||
|
RESTRICT,
|
||||||
RESULT,
|
RESULT,
|
||||||
RETURN,
|
RETURN,
|
||||||
RETURNS,
|
RETURNS,
|
||||||
|
|
|
@ -394,6 +394,13 @@ pub enum SQLStatement {
|
||||||
name: SQLObjectName,
|
name: SQLObjectName,
|
||||||
operation: AlterOperation,
|
operation: AlterOperation,
|
||||||
},
|
},
|
||||||
|
/// DROP TABLE
|
||||||
|
SQLDrop {
|
||||||
|
object_type: SQLObjectType,
|
||||||
|
if_exists: bool,
|
||||||
|
names: Vec<SQLObjectName>,
|
||||||
|
cascade: bool,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ToString for SQLStatement {
|
impl ToString for SQLStatement {
|
||||||
|
@ -502,6 +509,18 @@ impl ToString for SQLStatement {
|
||||||
SQLStatement::SQLAlterTable { name, operation } => {
|
SQLStatement::SQLAlterTable { name, operation } => {
|
||||||
format!("ALTER TABLE {} {}", name.to_string(), operation.to_string())
|
format!("ALTER TABLE {} {}", name.to_string(), operation.to_string())
|
||||||
}
|
}
|
||||||
|
SQLStatement::SQLDrop {
|
||||||
|
object_type,
|
||||||
|
if_exists,
|
||||||
|
names,
|
||||||
|
cascade,
|
||||||
|
} => format!(
|
||||||
|
"DROP {}{} {}{}",
|
||||||
|
object_type.to_string(),
|
||||||
|
if *if_exists { " IF EXISTS" } else { "" },
|
||||||
|
comma_separated_string(&names),
|
||||||
|
if *cascade { " CASCADE" } else { "" },
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -608,3 +627,18 @@ impl FromStr for FileFormat {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum SQLObjectType {
|
||||||
|
Table,
|
||||||
|
View,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SQLObjectType {
|
||||||
|
fn to_string(&self) -> String {
|
||||||
|
match self {
|
||||||
|
SQLObjectType::Table => "TABLE".into(),
|
||||||
|
SQLObjectType::View => "VIEW".into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -97,6 +97,7 @@ impl Parser {
|
||||||
Ok(SQLStatement::SQLQuery(Box::new(self.parse_query()?)))
|
Ok(SQLStatement::SQLQuery(Box::new(self.parse_query()?)))
|
||||||
}
|
}
|
||||||
"CREATE" => Ok(self.parse_create()?),
|
"CREATE" => Ok(self.parse_create()?),
|
||||||
|
"DROP" => Ok(self.parse_drop()?),
|
||||||
"DELETE" => Ok(self.parse_delete()?),
|
"DELETE" => Ok(self.parse_delete()?),
|
||||||
"INSERT" => Ok(self.parse_insert()?),
|
"INSERT" => Ok(self.parse_insert()?),
|
||||||
"ALTER" => Ok(self.parse_alter()?),
|
"ALTER" => Ok(self.parse_alter()?),
|
||||||
|
@ -738,6 +739,43 @@ impl Parser {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parse_drop(&mut self) -> Result<SQLStatement, ParserError> {
|
||||||
|
let object_type = if self.parse_keyword("TABLE") {
|
||||||
|
SQLObjectType::Table
|
||||||
|
} else if self.parse_keyword("VIEW") {
|
||||||
|
SQLObjectType::View
|
||||||
|
} else {
|
||||||
|
return parser_err!(format!(
|
||||||
|
"Unexpected token after DROP: {:?}",
|
||||||
|
self.peek_token()
|
||||||
|
));
|
||||||
|
};
|
||||||
|
let if_exists = self.parse_keywords(vec!["IF", "EXISTS"]);
|
||||||
|
let mut names = vec![self.parse_object_name()?];
|
||||||
|
loop {
|
||||||
|
let token = &self.next_token();
|
||||||
|
if let Some(Token::Comma) = token {
|
||||||
|
names.push(self.parse_object_name()?)
|
||||||
|
} else {
|
||||||
|
if token.is_some() {
|
||||||
|
self.prev_token();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let cascade = self.parse_keyword("CASCADE");
|
||||||
|
let restrict = self.parse_keyword("RESTRICT");
|
||||||
|
if cascade && restrict {
|
||||||
|
return parser_err!("Cannot specify both CASCADE and RESTRICT in DROP");
|
||||||
|
}
|
||||||
|
Ok(SQLStatement::SQLDrop {
|
||||||
|
object_type,
|
||||||
|
if_exists,
|
||||||
|
names,
|
||||||
|
cascade,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_create_table(&mut self) -> Result<SQLStatement, ParserError> {
|
pub fn parse_create_table(&mut self) -> Result<SQLStatement, ParserError> {
|
||||||
let table_name = self.parse_object_name()?;
|
let table_name = self.parse_object_name()?;
|
||||||
// parse optional column list (schema)
|
// parse optional column list (schema)
|
||||||
|
|
|
@ -1200,6 +1200,76 @@ fn parse_create_materialized_view() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_drop_table() {
|
||||||
|
let sql = "DROP TABLE foo";
|
||||||
|
match verified_stmt(sql) {
|
||||||
|
SQLStatement::SQLDrop {
|
||||||
|
object_type,
|
||||||
|
if_exists,
|
||||||
|
names,
|
||||||
|
cascade,
|
||||||
|
} => {
|
||||||
|
assert_eq!(false, if_exists);
|
||||||
|
assert_eq!(SQLObjectType::Table, object_type);
|
||||||
|
assert_eq!(
|
||||||
|
vec!["foo"],
|
||||||
|
names.iter().map(|n| n.to_string()).collect::<Vec<_>>()
|
||||||
|
);
|
||||||
|
assert_eq!(false, cascade);
|
||||||
|
}
|
||||||
|
_ => assert!(false),
|
||||||
|
}
|
||||||
|
|
||||||
|
let sql = "DROP TABLE IF EXISTS foo, bar CASCADE";
|
||||||
|
match verified_stmt(sql) {
|
||||||
|
SQLStatement::SQLDrop {
|
||||||
|
object_type,
|
||||||
|
if_exists,
|
||||||
|
names,
|
||||||
|
cascade,
|
||||||
|
} => {
|
||||||
|
assert_eq!(true, if_exists);
|
||||||
|
assert_eq!(SQLObjectType::Table, object_type);
|
||||||
|
assert_eq!(
|
||||||
|
vec!["foo", "bar"],
|
||||||
|
names.iter().map(|n| n.to_string()).collect::<Vec<_>>()
|
||||||
|
);
|
||||||
|
assert_eq!(true, cascade);
|
||||||
|
}
|
||||||
|
_ => assert!(false),
|
||||||
|
}
|
||||||
|
|
||||||
|
let sql = "DROP TABLE";
|
||||||
|
assert_eq!(
|
||||||
|
ParserError::ParserError("Expected identifier, found: EOF".to_string()),
|
||||||
|
parse_sql_statements(sql).unwrap_err(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let sql = "DROP TABLE IF EXISTS foo, bar CASCADE RESTRICT";
|
||||||
|
assert_eq!(
|
||||||
|
ParserError::ParserError("Cannot specify both CASCADE and RESTRICT in DROP".to_string()),
|
||||||
|
parse_sql_statements(sql).unwrap_err(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn parse_drop_view() {
|
||||||
|
let sql = "DROP VIEW myschema.myview";
|
||||||
|
match verified_stmt(sql) {
|
||||||
|
SQLStatement::SQLDrop {
|
||||||
|
names, object_type, ..
|
||||||
|
} => {
|
||||||
|
assert_eq!(
|
||||||
|
vec!["myschema.myview"],
|
||||||
|
names.iter().map(|n| n.to_string()).collect::<Vec<_>>()
|
||||||
|
);
|
||||||
|
assert_eq!(SQLObjectType::View, object_type);
|
||||||
|
}
|
||||||
|
_ => assert!(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_invalid_subquery_without_parens() {
|
fn parse_invalid_subquery_without_parens() {
|
||||||
let res = parse_sql_statements("SELECT SELECT 1 FROM bar WHERE 1=1 FROM baz");
|
let res = parse_sql_statements("SELECT SELECT 1 FROM bar WHERE 1=1 FROM baz");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue