Skip to content

Commit 9569d1b

Browse files
authored
feat: comment and alter column support (apache#381)
* feat: add support for postgresql comment keyword * feat: add alter column and rename constraint
1 parent 823635d commit 9569d1b

File tree

6 files changed

+355
-3
lines changed

6 files changed

+355
-3
lines changed

src/ast/ddl.rs

+57
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,15 @@ pub enum AlterTableOperation {
6767
data_type: DataType,
6868
options: Vec<ColumnOption>,
6969
},
70+
/// `RENAME CONSTRAINT <old_constraint_name> TO <new_constraint_name>`
71+
///
72+
/// Note: this is a PostgreSQL-specific operation.
73+
RenameConstraint { old_name: Ident, new_name: Ident },
74+
/// `ALTER [ COLUMN ]`
75+
AlterColumn {
76+
column_name: Ident,
77+
op: AlterColumnOperation,
78+
},
7079
}
7180

7281
impl fmt::Display for AlterTableOperation {
@@ -85,6 +94,9 @@ impl fmt::Display for AlterTableOperation {
8594
AlterTableOperation::AddColumn { column_def } => {
8695
write!(f, "ADD COLUMN {}", column_def.to_string())
8796
}
97+
AlterTableOperation::AlterColumn { column_name, op } => {
98+
write!(f, "ALTER COLUMN {} {}", column_name, op)
99+
}
88100
AlterTableOperation::DropPartitions {
89101
partitions,
90102
if_exists,
@@ -139,6 +151,51 @@ impl fmt::Display for AlterTableOperation {
139151
write!(f, " {}", display_separated(options, " "))
140152
}
141153
}
154+
AlterTableOperation::RenameConstraint { old_name, new_name } => {
155+
write!(f, "RENAME CONSTRAINT {} TO {}", old_name, new_name)
156+
}
157+
}
158+
}
159+
}
160+
161+
/// An `ALTER COLUMN` (`Statement::AlterTable`) operation
162+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
163+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
164+
pub enum AlterColumnOperation {
165+
/// `SET NOT NULL`
166+
SetNotNull,
167+
/// `DROP NOT NULL`
168+
DropNotNull,
169+
/// `SET DEFAULT <expr>`
170+
SetDefault { value: Expr },
171+
/// `DROP DEFAULT`
172+
DropDefault,
173+
/// `[SET DATA] TYPE <data_type> [USING <expr>]`
174+
SetDataType {
175+
data_type: DataType,
176+
/// PostgreSQL specific
177+
using: Option<Expr>,
178+
},
179+
}
180+
181+
impl fmt::Display for AlterColumnOperation {
182+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
183+
match self {
184+
AlterColumnOperation::SetNotNull => write!(f, "SET NOT NULL",),
185+
AlterColumnOperation::DropNotNull => write!(f, "DROP NOT NULL",),
186+
AlterColumnOperation::SetDefault { value } => {
187+
write!(f, "SET DEFAULT {}", value)
188+
}
189+
AlterColumnOperation::DropDefault {} => {
190+
write!(f, "DROP DEFAULT")
191+
}
192+
AlterColumnOperation::SetDataType { data_type, using } => {
193+
if let Some(expr) = using {
194+
write!(f, "SET DATA TYPE {} USING {}", data_type, expr)
195+
} else {
196+
write!(f, "SET DATA TYPE {}", data_type)
197+
}
198+
}
142199
}
143200
}
144201
}

src/ast/mod.rs

+38-2
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ use serde::{Deserialize, Serialize};
3131

3232
pub use self::data_type::DataType;
3333
pub use self::ddl::{
34-
AlterTableOperation, ColumnDef, ColumnOption, ColumnOptionDef, ReferentialAction,
35-
TableConstraint,
34+
AlterColumnOperation, AlterTableOperation, ColumnDef, ColumnOption, ColumnOptionDef,
35+
ReferentialAction, TableConstraint,
3636
};
3737
pub use self::operator::{BinaryOperator, UnaryOperator};
3838
pub use self::query::{
@@ -592,6 +592,22 @@ impl fmt::Display for ShowCreateObject {
592592
}
593593
}
594594

595+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
596+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
597+
pub enum CommentObject {
598+
Column,
599+
Table,
600+
}
601+
602+
impl fmt::Display for CommentObject {
603+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
604+
match self {
605+
CommentObject::Column => f.write_str("COLUMN"),
606+
CommentObject::Table => f.write_str("TABLE"),
607+
}
608+
}
609+
}
610+
595611
/// A top-level statement (SELECT, INSERT, CREATE, etc.)
596612
#[allow(clippy::large_enum_variant)]
597613
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -779,6 +795,14 @@ pub enum Statement {
779795
snapshot: Option<Value>,
780796
session: bool,
781797
},
798+
/// `COMMENT ON ...`
799+
///
800+
/// Note: this is a PostgreSQL-specific statement.
801+
Comment {
802+
object_type: CommentObject,
803+
object_name: ObjectName,
804+
comment: Option<String>,
805+
},
782806
/// `COMMIT [ TRANSACTION | WORK ] [ AND [ NO ] CHAIN ]`
783807
Commit { chain: bool },
784808
/// `ROLLBACK [ TRANSACTION | WORK ] [ AND [ NO ] CHAIN ]`
@@ -1459,6 +1483,18 @@ impl fmt::Display for Statement {
14591483
}
14601484
write!(f, "AS {}", statement)
14611485
}
1486+
Statement::Comment {
1487+
object_type,
1488+
object_name,
1489+
comment,
1490+
} => {
1491+
write!(f, "COMMENT ON {} {} IS ", object_type, object_name)?;
1492+
if let Some(c) = comment {
1493+
write!(f, "'{}'", c)
1494+
} else {
1495+
write!(f, "NULL")
1496+
}
1497+
}
14621498
}
14631499
}
14641500
}

src/keywords.rs

+3
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ define_keywords!(
128128
COLLECT,
129129
COLUMN,
130130
COLUMNS,
131+
COMMENT,
131132
COMMIT,
132133
COMMITTED,
133134
COMPUTE,
@@ -161,6 +162,7 @@ define_keywords!(
161162
CURRENT_USER,
162163
CURSOR,
163164
CYCLE,
165+
DATA,
164166
DATABASE,
165167
DATE,
166168
DAY,
@@ -469,6 +471,7 @@ define_keywords!(
469471
TRUE,
470472
TRUNCATE,
471473
TRY_CAST,
474+
TYPE,
472475
UESCAPE,
473476
UNBOUNDED,
474477
UNCOMMITTED,

src/parser.rs

+70-1
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,9 @@ impl<'a> Parser<'a> {
191191
self.prev_token();
192192
Ok(self.parse_insert()?)
193193
}
194+
Keyword::COMMENT if dialect_of!(self is PostgreSqlDialect) => {
195+
Ok(self.parse_comment()?)
196+
}
194197
_ => self.expected("an SQL statement", Token::Word(w)),
195198
},
196199
Token::LParen => {
@@ -1944,7 +1947,12 @@ impl<'a> Parser<'a> {
19441947
}
19451948
}
19461949
} else if self.parse_keyword(Keyword::RENAME) {
1947-
if self.parse_keyword(Keyword::TO) {
1950+
if dialect_of!(self is PostgreSqlDialect) && self.parse_keyword(Keyword::CONSTRAINT) {
1951+
let old_name = self.parse_identifier()?;
1952+
self.expect_keyword(Keyword::TO)?;
1953+
let new_name = self.parse_identifier()?;
1954+
AlterTableOperation::RenameConstraint { old_name, new_name }
1955+
} else if self.parse_keyword(Keyword::TO) {
19481956
let table_name = self.parse_object_name()?;
19491957
AlterTableOperation::RenameTable { table_name }
19501958
} else {
@@ -2014,6 +2022,38 @@ impl<'a> Parser<'a> {
20142022
data_type,
20152023
options,
20162024
}
2025+
} else if self.parse_keyword(Keyword::ALTER) {
2026+
let _ = self.parse_keyword(Keyword::COLUMN);
2027+
let column_name = self.parse_identifier()?;
2028+
let is_postgresql = dialect_of!(self is PostgreSqlDialect);
2029+
2030+
let op = if self.parse_keywords(&[Keyword::SET, Keyword::NOT, Keyword::NULL]) {
2031+
AlterColumnOperation::SetNotNull {}
2032+
} else if self.parse_keywords(&[Keyword::DROP, Keyword::NOT, Keyword::NULL]) {
2033+
AlterColumnOperation::DropNotNull {}
2034+
} else if self.parse_keywords(&[Keyword::SET, Keyword::DEFAULT]) {
2035+
AlterColumnOperation::SetDefault {
2036+
value: self.parse_expr()?,
2037+
}
2038+
} else if self.parse_keywords(&[Keyword::DROP, Keyword::DEFAULT]) {
2039+
AlterColumnOperation::DropDefault {}
2040+
} else if self.parse_keywords(&[Keyword::SET, Keyword::DATA, Keyword::TYPE])
2041+
|| (is_postgresql && self.parse_keyword(Keyword::TYPE))
2042+
{
2043+
let data_type = self.parse_data_type()?;
2044+
let using = if is_postgresql && self.parse_keyword(Keyword::USING) {
2045+
Some(self.parse_expr()?)
2046+
} else {
2047+
None
2048+
};
2049+
AlterColumnOperation::SetDataType { data_type, using }
2050+
} else {
2051+
return self.expected(
2052+
"SET/DROP NOT NULL, SET DEFAULT, SET DATA TYPE after ALTER COLUMN",
2053+
self.peek_token(),
2054+
);
2055+
};
2056+
AlterTableOperation::AlterColumn { column_name, op }
20172057
} else {
20182058
return self.expected(
20192059
"ADD, RENAME, PARTITION or DROP after ALTER TABLE",
@@ -3547,6 +3587,35 @@ impl<'a> Parser<'a> {
35473587
statement,
35483588
})
35493589
}
3590+
3591+
fn parse_comment(&mut self) -> Result<Statement, ParserError> {
3592+
self.expect_keyword(Keyword::ON)?;
3593+
let token = self.next_token();
3594+
3595+
let (object_type, object_name) = match token {
3596+
Token::Word(w) if w.keyword == Keyword::COLUMN => {
3597+
let object_name = self.parse_object_name()?;
3598+
(CommentObject::Column, object_name)
3599+
}
3600+
Token::Word(w) if w.keyword == Keyword::TABLE => {
3601+
let object_name = self.parse_object_name()?;
3602+
(CommentObject::Table, object_name)
3603+
}
3604+
_ => self.expected("comment object_type", token)?,
3605+
};
3606+
3607+
self.expect_keyword(Keyword::IS)?;
3608+
let comment = if self.parse_keyword(Keyword::NULL) {
3609+
None
3610+
} else {
3611+
Some(self.parse_literal_string()?)
3612+
};
3613+
Ok(Statement::Comment {
3614+
object_type,
3615+
object_name,
3616+
comment,
3617+
})
3618+
}
35503619
}
35513620

35523621
impl Word {

tests/sqlparser_common.rs

+102
Original file line numberDiff line numberDiff line change
@@ -1950,6 +1950,108 @@ fn parse_alter_table_drop_column() {
19501950
}
19511951
}
19521952

1953+
#[test]
1954+
fn parse_alter_table_alter_column() {
1955+
let alter_stmt = "ALTER TABLE tab";
1956+
match verified_stmt(&format!(
1957+
"{} ALTER COLUMN is_active SET NOT NULL",
1958+
alter_stmt
1959+
)) {
1960+
Statement::AlterTable {
1961+
name,
1962+
operation: AlterTableOperation::AlterColumn { column_name, op },
1963+
} => {
1964+
assert_eq!("tab", name.to_string());
1965+
assert_eq!("is_active", column_name.to_string());
1966+
assert_eq!(op, AlterColumnOperation::SetNotNull {});
1967+
}
1968+
_ => unreachable!(),
1969+
}
1970+
1971+
one_statement_parses_to(
1972+
"ALTER TABLE tab ALTER is_active DROP NOT NULL",
1973+
"ALTER TABLE tab ALTER COLUMN is_active DROP NOT NULL",
1974+
);
1975+
1976+
match verified_stmt(&format!(
1977+
"{} ALTER COLUMN is_active SET DEFAULT false",
1978+
alter_stmt
1979+
)) {
1980+
Statement::AlterTable {
1981+
name,
1982+
operation: AlterTableOperation::AlterColumn { column_name, op },
1983+
} => {
1984+
assert_eq!("tab", name.to_string());
1985+
assert_eq!("is_active", column_name.to_string());
1986+
assert_eq!(
1987+
op,
1988+
AlterColumnOperation::SetDefault {
1989+
value: Expr::Value(Value::Boolean(false))
1990+
}
1991+
);
1992+
}
1993+
_ => unreachable!(),
1994+
}
1995+
1996+
match verified_stmt(&format!(
1997+
"{} ALTER COLUMN is_active DROP DEFAULT",
1998+
alter_stmt
1999+
)) {
2000+
Statement::AlterTable {
2001+
name,
2002+
operation: AlterTableOperation::AlterColumn { column_name, op },
2003+
} => {
2004+
assert_eq!("tab", name.to_string());
2005+
assert_eq!("is_active", column_name.to_string());
2006+
assert_eq!(op, AlterColumnOperation::DropDefault {});
2007+
}
2008+
_ => unreachable!(),
2009+
}
2010+
}
2011+
2012+
#[test]
2013+
fn parse_alter_table_alter_column_type() {
2014+
let alter_stmt = "ALTER TABLE tab";
2015+
match verified_stmt("ALTER TABLE tab ALTER COLUMN is_active SET DATA TYPE TEXT") {
2016+
Statement::AlterTable {
2017+
name,
2018+
operation: AlterTableOperation::AlterColumn { column_name, op },
2019+
} => {
2020+
assert_eq!("tab", name.to_string());
2021+
assert_eq!("is_active", column_name.to_string());
2022+
assert_eq!(
2023+
op,
2024+
AlterColumnOperation::SetDataType {
2025+
data_type: DataType::Text,
2026+
using: None,
2027+
}
2028+
);
2029+
}
2030+
_ => unreachable!(),
2031+
}
2032+
2033+
let res = Parser::parse_sql(
2034+
&GenericDialect {},
2035+
&format!("{} ALTER COLUMN is_active TYPE TEXT", alter_stmt),
2036+
);
2037+
assert_eq!(
2038+
ParserError::ParserError("Expected SET/DROP NOT NULL, SET DEFAULT, SET DATA TYPE after ALTER COLUMN, found: TYPE".to_string()),
2039+
res.unwrap_err()
2040+
);
2041+
2042+
let res = Parser::parse_sql(
2043+
&GenericDialect {},
2044+
&format!(
2045+
"{} ALTER COLUMN is_active SET DATA TYPE TEXT USING 'text'",
2046+
alter_stmt
2047+
),
2048+
);
2049+
assert_eq!(
2050+
ParserError::ParserError("Expected end of statement, found: USING".to_string()),
2051+
res.unwrap_err()
2052+
);
2053+
}
2054+
19532055
#[test]
19542056
fn parse_bad_constraint() {
19552057
let res = parse_sql_statements("ALTER TABLE tab ADD");

0 commit comments

Comments
 (0)