mirror of
https://github.com/GreptimeTeam/greptimedb.git
synced 2026-01-10 07:12:54 +00:00
feat: support ALTER TABLE ... MODIFY COLUMN ... ... (#3796)
* feat: support `ALTER COLUMN xxx TYPE xxx` * fix: test `test_parse_alter_change_column_type` * style: code fmt * style: move to new test: `test_make_alter_column_type_region_request` * style: simplify the code * style: remove `v1::region::ChangeColumnType` * resolve conflicts * fix: test `test_make_alter_column_type_region_request` * style: simplify the code * rebase * rebase * rebase * fix: `ALTER COLUMN ... TYPE` -> `MODIFY COLUMN` * fix: `parser` -> `self.parser` * Apply suggestions from code review --------- Co-authored-by: Ruihang Xia <waynestxia@gmail.com> Co-authored-by: WenyXu <wenymedia@gmail.com>
This commit is contained in:
@@ -30,24 +30,24 @@ impl<'a> ParserContext<'a> {
|
||||
}
|
||||
|
||||
fn parse_alter_table(&mut self) -> std::result::Result<AlterTable, ParserError> {
|
||||
let parser = &mut self.parser;
|
||||
parser.expect_keywords(&[Keyword::ALTER, Keyword::TABLE])?;
|
||||
self.parser
|
||||
.expect_keywords(&[Keyword::ALTER, Keyword::TABLE])?;
|
||||
|
||||
let raw_table_name = parser.parse_object_name(false)?;
|
||||
let raw_table_name = self.parser.parse_object_name(false)?;
|
||||
let table_name = Self::canonicalize_object_name(raw_table_name);
|
||||
|
||||
let alter_operation = if parser.parse_keyword(Keyword::ADD) {
|
||||
if let Some(constraint) = parser.parse_optional_table_constraint()? {
|
||||
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 _ = parser.parse_keyword(Keyword::COLUMN);
|
||||
let mut column_def = parser.parse_column_def()?;
|
||||
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 parser.parse_keyword(Keyword::FIRST) {
|
||||
let location = if self.parser.parse_keyword(Keyword::FIRST) {
|
||||
Some(AddColumnLocation::First)
|
||||
} else if let Token::Word(word) = parser.peek_token().token {
|
||||
} else if let Token::Word(word) = self.parser.peek_token().token {
|
||||
if word.value.to_ascii_uppercase() == "AFTER" {
|
||||
let _ = parser.next_token();
|
||||
let _ = self.parser.next_token();
|
||||
let name = Self::canonicalize_identifier(self.parse_identifier()?);
|
||||
Some(AddColumnLocation::After {
|
||||
column_name: name.value,
|
||||
@@ -63,17 +63,26 @@ impl<'a> ParserContext<'a> {
|
||||
location,
|
||||
}
|
||||
}
|
||||
} else if parser.parse_keyword(Keyword::DROP) {
|
||||
if parser.parse_keyword(Keyword::COLUMN) {
|
||||
} 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 {}",
|
||||
parser.peek_token()
|
||||
self.parser.peek_token()
|
||||
)));
|
||||
}
|
||||
} else if parser.parse_keyword(Keyword::RENAME) {
|
||||
} 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()?;
|
||||
|
||||
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[..] {
|
||||
@@ -87,8 +96,8 @@ impl<'a> ParserContext<'a> {
|
||||
AlterTableOperation::RenameTable { new_table_name }
|
||||
} else {
|
||||
return Err(ParserError::ParserError(format!(
|
||||
"expect keyword ADD or DROP or RENAME after ALTER TABLE, found {}",
|
||||
parser.peek_token()
|
||||
"expect keyword ADD or DROP or MODIFY or RENAME after ALTER TABLE, found {}",
|
||||
self.parser.peek_token()
|
||||
)));
|
||||
};
|
||||
Ok(AlterTable::new(table_name, alter_operation))
|
||||
@@ -253,6 +262,52 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_alter_change_column_type() {
|
||||
let sql_1 = "ALTER TABLE my_metric_1 MODIFY COLUMN a STRING";
|
||||
let result_1 = ParserContext::create_with_dialect(
|
||||
sql_1,
|
||||
&GreptimeDbDialect {},
|
||||
ParseOptions::default(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let sql_2 = "ALTER TABLE my_metric_1 MODIFY a STRING";
|
||||
let mut result_2 = ParserContext::create_with_dialect(
|
||||
sql_2,
|
||||
&GreptimeDbDialect {},
|
||||
ParseOptions::default(),
|
||||
)
|
||||
.unwrap();
|
||||
assert_eq!(result_1, result_2);
|
||||
assert_eq!(1, result_2.len());
|
||||
|
||||
let statement = result_2.remove(0);
|
||||
assert_matches!(statement, Statement::Alter { .. });
|
||||
match statement {
|
||||
Statement::Alter(alter_table) => {
|
||||
assert_eq!("my_metric_1", alter_table.table_name().0[0].value);
|
||||
|
||||
let alter_operation = alter_table.alter_operation();
|
||||
assert_matches!(
|
||||
alter_operation,
|
||||
AlterTableOperation::ChangeColumnType { .. }
|
||||
);
|
||||
match alter_operation {
|
||||
AlterTableOperation::ChangeColumnType {
|
||||
column_name,
|
||||
target_type,
|
||||
} => {
|
||||
assert_eq!("a", column_name.value);
|
||||
assert_eq!(DataType::String(None), *target_type);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_alter_rename_table() {
|
||||
let sql = "ALTER TABLE test_table table_t";
|
||||
@@ -260,7 +315,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 RENAME after ALTER TABLE"));
|
||||
assert!(err.contains("expect keyword ADD or DROP or MODIFY or RENAME after ALTER TABLE"));
|
||||
|
||||
let sql = "ALTER TABLE test_table RENAME table_t";
|
||||
let mut result =
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
use std::fmt::{Debug, Display};
|
||||
|
||||
use common_query::AddColumnLocation;
|
||||
use sqlparser::ast::{ColumnDef, Ident, ObjectName, TableConstraint};
|
||||
use sqlparser::ast::{ColumnDef, DataType, Ident, ObjectName, TableConstraint};
|
||||
use sqlparser_derive::{Visit, VisitMut};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Visit, VisitMut)]
|
||||
@@ -58,6 +58,11 @@ pub enum AlterTableOperation {
|
||||
column_def: ColumnDef,
|
||||
location: Option<AddColumnLocation>,
|
||||
},
|
||||
/// `MODIFY <column_name> [target_type]`
|
||||
ChangeColumnType {
|
||||
column_name: Ident,
|
||||
target_type: DataType,
|
||||
},
|
||||
/// `DROP COLUMN <name>`
|
||||
DropColumn { name: Ident },
|
||||
/// `RENAME <new_table_name>`
|
||||
@@ -82,6 +87,12 @@ impl Display for AlterTableOperation {
|
||||
AlterTableOperation::RenameTable { new_table_name } => {
|
||||
write!(f, r#"RENAME {new_table_name}"#)
|
||||
}
|
||||
AlterTableOperation::ChangeColumnType {
|
||||
column_name,
|
||||
target_type,
|
||||
} => {
|
||||
write!(f, r#"MODIFY COLUMN {column_name} {target_type}"#)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,6 +128,27 @@ ALTER TABLE monitor ADD COLUMN app STRING DEFAULT 'shop' PRIMARY KEY"#,
|
||||
}
|
||||
}
|
||||
|
||||
let sql = r"alter table monitor modify column load_15 string;";
|
||||
let stmts =
|
||||
ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
|
||||
.unwrap();
|
||||
assert_eq!(1, stmts.len());
|
||||
assert_matches!(&stmts[0], Statement::Alter { .. });
|
||||
|
||||
match &stmts[0] {
|
||||
Statement::Alter(set) => {
|
||||
let new_sql = format!("\n{}", set);
|
||||
assert_eq!(
|
||||
r#"
|
||||
ALTER TABLE monitor MODIFY COLUMN load_15 STRING"#,
|
||||
&new_sql
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
let sql = r"alter table monitor drop column load_15;";
|
||||
let stmts =
|
||||
ParserContext::create_with_dialect(sql, &GreptimeDbDialect {}, ParseOptions::default())
|
||||
|
||||
Reference in New Issue
Block a user