Skip to content

Commit 35de9a7

Browse files
committed
feat: add support for postgresql comment keyword
1 parent 4c121a9 commit 35de9a7

File tree

4 files changed

+113
-0
lines changed

4 files changed

+113
-0
lines changed

src/ast/mod.rs

+39
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,22 @@ impl fmt::Display for ShowCreateObject {
604604
}
605605
}
606606

607+
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
608+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
609+
pub enum CommentObject {
610+
Column,
611+
Table,
612+
}
613+
614+
impl fmt::Display for CommentObject {
615+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
616+
match self {
617+
CommentObject::Column => f.write_str("COLUMN"),
618+
CommentObject::Table => f.write_str("TABLE"),
619+
}
620+
}
621+
}
622+
607623
/// A top-level statement (SELECT, INSERT, CREATE, etc.)
608624
#[allow(clippy::large_enum_variant)]
609625
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -791,6 +807,14 @@ pub enum Statement {
791807
snapshot: Option<Value>,
792808
session: bool,
793809
},
810+
/// `COMMENT ON ...`
811+
///
812+
/// Note: this is a PostgreSQL-specific statement.
813+
Comment {
814+
object_type: CommentObject,
815+
object_name: ObjectName,
816+
comment: Option<String>,
817+
},
794818
/// `COMMIT [ TRANSACTION | WORK ] [ AND [ NO ] CHAIN ]`
795819
Commit { chain: bool },
796820
/// `ROLLBACK [ TRANSACTION | WORK ] [ AND [ NO ] CHAIN ]`
@@ -1471,6 +1495,21 @@ impl fmt::Display for Statement {
14711495
}
14721496
write!(f, "AS {}", statement)
14731497
}
1498+
Statement::Comment {
1499+
object_type,
1500+
object_name,
1501+
comment,
1502+
} => write!(
1503+
f,
1504+
"COMMENT ON {} {} IS {}",
1505+
object_type,
1506+
object_name,
1507+
if let Some(c) = comment {
1508+
format!("'{}'", c)
1509+
} else {
1510+
"NULL".to_string()
1511+
}
1512+
),
14741513
}
14751514
}
14761515
}

src/keywords.rs

+1
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,

src/parser.rs

+32
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,9 @@ impl<'a> Parser<'a> {
175175
self.prev_token();
176176
Ok(self.parse_insert()?)
177177
}
178+
Keyword::COMMENT if dialect_of!(self is PostgreSqlDialect) => {
179+
Ok(self.parse_comment()?)
180+
}
178181
_ => self.expected("an SQL statement", Token::Word(w)),
179182
},
180183
Token::LParen => {
@@ -3511,6 +3514,35 @@ impl<'a> Parser<'a> {
35113514
statement,
35123515
})
35133516
}
3517+
3518+
fn parse_comment(&mut self) -> Result<Statement, ParserError> {
3519+
self.expect_keyword(Keyword::ON)?;
3520+
let token = self.next_token();
3521+
3522+
let (object_type, object_name) = match token {
3523+
Token::Word(w) if w.keyword == Keyword::COLUMN => {
3524+
let object_name = self.parse_object_name()?;
3525+
(CommentObject::Column, object_name)
3526+
}
3527+
Token::Word(w) if w.keyword == Keyword::TABLE => {
3528+
let object_name = self.parse_object_name()?;
3529+
(CommentObject::Table, object_name)
3530+
}
3531+
_ => self.expected("comment object_type", token)?,
3532+
};
3533+
3534+
self.expect_keyword(Keyword::IS)?;
3535+
let comment = if self.parse_keyword(Keyword::NULL) {
3536+
None
3537+
} else {
3538+
Some(self.parse_literal_string()?)
3539+
};
3540+
Ok(Statement::Comment {
3541+
object_type,
3542+
object_name,
3543+
comment,
3544+
})
3545+
}
35143546
}
35153547

35163548
impl Word {

tests/sqlparser_postgres.rs

+41
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,47 @@ fn test_transaction_statement() {
749749
);
750750
}
751751

752+
#[test]
753+
fn parse_comments() {
754+
match pg().verified_stmt("COMMENT ON COLUMN tab.name IS 'comment'") {
755+
Statement::Comment {
756+
object_type,
757+
object_name,
758+
comment: Some(comment),
759+
} => {
760+
assert_eq!("comment", comment);
761+
assert_eq!("tab.name", object_name.to_string());
762+
assert_eq!(CommentObject::Column, object_type);
763+
}
764+
_ => unreachable!(),
765+
}
766+
767+
match pg().verified_stmt("COMMENT ON TABLE public.tab IS 'comment'") {
768+
Statement::Comment {
769+
object_type,
770+
object_name,
771+
comment: Some(comment),
772+
} => {
773+
assert_eq!("comment", comment);
774+
assert_eq!("public.tab", object_name.to_string());
775+
assert_eq!(CommentObject::Table, object_type);
776+
}
777+
_ => unreachable!(),
778+
}
779+
780+
match pg().verified_stmt("COMMENT ON TABLE public.tab IS NULL") {
781+
Statement::Comment {
782+
object_type,
783+
object_name,
784+
comment: None,
785+
} => {
786+
assert_eq!("public.tab", object_name.to_string());
787+
assert_eq!(CommentObject::Table, object_type);
788+
}
789+
_ => unreachable!(),
790+
}
791+
}
792+
752793
fn pg() -> TestedDialects {
753794
TestedDialects {
754795
dialects: vec![Box::new(PostgreSqlDialect {})],

0 commit comments

Comments
 (0)