Skip to content

Commit

Permalink
refactor: refactor alter parser (#4933)
Browse files Browse the repository at this point in the history
refactor: alter parser
  • Loading branch information
CookiePieWw authored Nov 4, 2024
1 parent 4ab6dc2 commit fb82298
Showing 1 changed file with 116 additions and 73 deletions.
189 changes: 116 additions & 73 deletions src/sql/src/parsers/alter_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,90 +25,130 @@ use crate::statements::statement::Statement;

impl ParserContext<'_> {
pub(crate) fn parse_alter(&mut self) -> Result<Statement> {
let alter_table = self.parse_alter_table().context(error::SyntaxSnafu)?;
let alter_table = self.parse_alter_table()?;
Ok(Statement::Alter(alter_table))
}

fn parse_alter_table(&mut self) -> std::result::Result<AlterTable, ParserError> {
fn parse_alter_table(&mut self) -> Result<AlterTable> {
self.parser
.expect_keywords(&[Keyword::ALTER, Keyword::TABLE])?;
.expect_keywords(&[Keyword::ALTER, Keyword::TABLE])
.context(error::SyntaxSnafu)?;

let raw_table_name = self.parser.parse_object_name(false)?;
let raw_table_name = self
.parser
.parse_object_name(false)
.context(error::SyntaxSnafu)?;
let table_name = Self::canonicalize_object_name(raw_table_name);

let alter_operation = if self.parser.parse_keyword(Keyword::ADD) {
if let Some(constraint) = self.parser.parse_optional_table_constraint()? {
AlterTableOperation::AddConstraint(constraint)
} else {
let _ = self.parser.parse_keyword(Keyword::COLUMN);
let mut column_def = self.parser.parse_column_def()?;
column_def.name = Self::canonicalize_identifier(column_def.name);
let location = if self.parser.parse_keyword(Keyword::FIRST) {
Some(AddColumnLocation::First)
} else if let Token::Word(word) = self.parser.peek_token().token {
if word.value.eq_ignore_ascii_case("AFTER") {
let _ = self.parser.next_token();
let name = Self::canonicalize_identifier(self.parse_identifier()?);
Some(AddColumnLocation::After {
column_name: name.value,
})
} else {
None
}
let alter_operation = match self.parser.peek_token().token {
Token::Word(w) => {
if w.value.eq_ignore_ascii_case("MODIFY") {
self.parse_alter_table_modify()?
} else {
None
};
AlterTableOperation::AddColumn {
column_def,
location,
match w.keyword {
Keyword::ADD => self.parse_alter_table_add()?,
Keyword::DROP => {
let _ = self.parser.next_token();
self.parser
.expect_keyword(Keyword::COLUMN)
.context(error::SyntaxSnafu)?;
let name = Self::canonicalize_identifier(
self.parse_identifier().context(error::SyntaxSnafu)?,
);
AlterTableOperation::DropColumn { name }
}
Keyword::RENAME => {
let _ = self.parser.next_token();
let new_table_name_obj_raw =
self.parse_object_name().context(error::SyntaxSnafu)?;
let new_table_name_obj =
Self::canonicalize_object_name(new_table_name_obj_raw);
let new_table_name = match &new_table_name_obj.0[..] {
[table] => table.value.clone(),
_ => {
return Err(ParserError::ParserError(format!(
"expect table name, actual: {new_table_name_obj}"
)))
.context(error::SyntaxSnafu)
}
};
AlterTableOperation::RenameTable { new_table_name }
}
Keyword::SET => {
let _ = self.parser.next_token();
let options = self
.parser
.parse_comma_separated(parse_string_options)
.context(error::SyntaxSnafu)?
.into_iter()
.map(|(key, value)| ChangeTableOption { key, value })
.collect();
AlterTableOperation::ChangeTableOptions { options }
}
_ => self.expected(
"ADD or DROP or MODIFY or RENAME or SET after ALTER TABLE",
self.parser.peek_token(),
)?,
}
}
}
} else if self.parser.parse_keyword(Keyword::DROP) {
if self.parser.parse_keyword(Keyword::COLUMN) {
let name = Self::canonicalize_identifier(self.parse_identifier()?);
AlterTableOperation::DropColumn { name }
} else {
return Err(ParserError::ParserError(format!(
"expect keyword COLUMN after ALTER TABLE DROP, found {}",
self.parser.peek_token()
)));
}
} else if self.consume_token("MODIFY") {
let _ = self.parser.parse_keyword(Keyword::COLUMN);
let column_name = Self::canonicalize_identifier(self.parser.parse_identifier(false)?);
let target_type = self.parser.parse_data_type()?;
unexpected => self.unsupported(unexpected.to_string())?,
};
Ok(AlterTable::new(table_name, alter_operation))
}

AlterTableOperation::ChangeColumnType {
column_name,
target_type,
}
} else if self.parser.parse_keyword(Keyword::RENAME) {
let new_table_name_obj_raw = self.parse_object_name()?;
let new_table_name_obj = Self::canonicalize_object_name(new_table_name_obj_raw);
let new_table_name = match &new_table_name_obj.0[..] {
[table] => table.value.clone(),
_ => {
return Err(ParserError::ParserError(format!(
"expect table name, actual: {new_table_name_obj}"
)))
fn parse_alter_table_add(&mut self) -> Result<AlterTableOperation> {
let _ = self.parser.next_token();
if let Some(constraint) = self
.parser
.parse_optional_table_constraint()
.context(error::SyntaxSnafu)?
{
Ok(AlterTableOperation::AddConstraint(constraint))
} else {
let _ = self.parser.parse_keyword(Keyword::COLUMN);
let mut column_def = self.parser.parse_column_def().context(error::SyntaxSnafu)?;
column_def.name = Self::canonicalize_identifier(column_def.name);
let location = if self.parser.parse_keyword(Keyword::FIRST) {
Some(AddColumnLocation::First)
} else if let Token::Word(word) = self.parser.peek_token().token {
if word.value.eq_ignore_ascii_case("AFTER") {
let _ = self.parser.next_token();
let name = Self::canonicalize_identifier(
self.parse_identifier().context(error::SyntaxSnafu)?,
);
Some(AddColumnLocation::After {
column_name: name.value,
})
} else {
None
}
} else {
None
};
AlterTableOperation::RenameTable { new_table_name }
} else if self.parser.parse_keyword(Keyword::SET) {
let options = self
.parser
.parse_comma_separated(parse_string_options)?
.into_iter()
.map(|(key, value)| ChangeTableOption { key, value })
.collect();
AlterTableOperation::ChangeTableOptions { options }
} else {
return Err(ParserError::ParserError(format!(
"expect keyword ADD or DROP or MODIFY or RENAME after ALTER TABLE, found {}",
self.parser.peek_token()
)));
};
Ok(AlterTable::new(table_name, alter_operation))
Ok(AlterTableOperation::AddColumn {
column_def,
location,
})
}
}

fn parse_alter_table_modify(&mut self) -> Result<AlterTableOperation> {
let _ = self.parser.next_token();
self.parser
.expect_keyword(Keyword::COLUMN)
.context(error::SyntaxSnafu)?;
let column_name = Self::canonicalize_identifier(
self.parser
.parse_identifier(false)
.context(error::SyntaxSnafu)?,
);
let target_type = self.parser.parse_data_type().context(error::SyntaxSnafu)?;

Ok(AlterTableOperation::ChangeColumnType {
column_name,
target_type,
})
}
}

Expand Down Expand Up @@ -259,7 +299,10 @@ mod tests {
ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
.unwrap_err();
let err = result.output_msg();
assert!(err.contains("expect keyword COLUMN after ALTER TABLE DROP"));
assert_eq!(
err,
"sql parser error: Expected COLUMN, found: a at Line: 1, Column 30"
);

let sql = "ALTER TABLE my_metric_1 DROP COLUMN a";
let mut result =
Expand Down Expand Up @@ -404,7 +447,7 @@ mod tests {
ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
.unwrap_err();
let err = result.output_msg();
assert!(err.contains("expect keyword ADD or DROP or MODIFY or RENAME after ALTER TABLE"));
assert_eq!(err, "sql parser error: Expected ADD or DROP or MODIFY or RENAME or SET after ALTER TABLE, found: table_t");

let sql = "ALTER TABLE test_table RENAME table_t";
let mut result =
Expand Down

0 comments on commit fb82298

Please sign in to comment.