Support DROP [TABLE|VIEW]

Co-authored-by: Jamie Brandon <jamie@scattered-thoughts.net>
This commit is contained in:
Nikhil Benesch 2019-04-04 10:51:34 -04:00
parent 4f944dd4aa
commit 187376e657
No known key found for this signature in database
GPG key ID: F7386C5DEADABA7F
4 changed files with 145 additions and 0 deletions

View file

@ -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,

View file

@ -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(),
}
}
}

View file

@ -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)

View file

@ -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");