Skip to content

Commit 1e75231

Browse files
committed
feat: add support for postgresql comment keyword
1 parent e3269c1 commit 1e75231

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)]
@@ -787,6 +803,14 @@ pub enum Statement {
787803
StartTransaction { modes: Vec<TransactionMode> },
788804
/// `SET TRANSACTION ...`
789805
SetTransaction { modes: Vec<TransactionMode> },
806+
/// `COMMENT ON ...`
807+
///
808+
/// Note: this is a PostgreSQL-specific statement.
809+
Comment {
810+
object_type: CommentObject,
811+
object_name: ObjectName,
812+
comment: Option<String>,
813+
},
790814
/// `COMMIT [ TRANSACTION | WORK ] [ AND [ NO ] CHAIN ]`
791815
Commit { chain: bool },
792816
/// `ROLLBACK [ TRANSACTION | WORK ] [ AND [ NO ] CHAIN ]`
@@ -1456,6 +1480,21 @@ impl fmt::Display for Statement {
14561480
}
14571481
write!(f, "AS {}", statement)
14581482
}
1483+
Statement::Comment {
1484+
object_type,
1485+
object_name,
1486+
comment,
1487+
} => write!(
1488+
f,
1489+
"COMMENT ON {} {} IS {}",
1490+
object_type,
1491+
object_name,
1492+
if let Some(c) = comment {
1493+
format!("'{}'", c)
1494+
} else {
1495+
"NULL".to_string()
1496+
}
1497+
),
14591498
}
14601499
}
14611500
}

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 => {
@@ -3494,6 +3497,35 @@ impl<'a> Parser<'a> {
34943497
statement,
34953498
})
34963499
}
3500+
3501+
fn parse_comment(&mut self) -> Result<Statement, ParserError> {
3502+
self.expect_keyword(Keyword::ON)?;
3503+
let token = self.next_token();
3504+
3505+
let (object_type, object_name) = match token {
3506+
Token::Word(w) if w.keyword == Keyword::COLUMN => {
3507+
let object_name = self.parse_object_name()?;
3508+
(CommentObject::Column, object_name)
3509+
}
3510+
Token::Word(w) if w.keyword == Keyword::TABLE => {
3511+
let object_name = self.parse_object_name()?;
3512+
(CommentObject::Table, object_name)
3513+
}
3514+
_ => self.expected("comment object_type", token)?,
3515+
};
3516+
3517+
self.expect_keyword(Keyword::IS)?;
3518+
let comment = if self.parse_keyword(Keyword::NULL) {
3519+
None
3520+
} else {
3521+
Some(self.parse_literal_string()?)
3522+
};
3523+
Ok(Statement::Comment {
3524+
object_type,
3525+
object_name,
3526+
comment,
3527+
})
3528+
}
34973529
}
34983530

34993531
impl Word {

tests/sqlparser_postgres.rs

+41
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,47 @@ fn parse_map_access_expr() {
723723
);
724724
}
725725

726+
#[test]
727+
fn parse_comments() {
728+
match pg().verified_stmt("COMMENT ON COLUMN tab.name IS 'comment'") {
729+
Statement::Comment {
730+
object_type,
731+
object_name,
732+
comment: Some(comment),
733+
} => {
734+
assert_eq!("comment", comment);
735+
assert_eq!("tab.name", object_name.to_string());
736+
assert_eq!(CommentObject::Column, object_type);
737+
}
738+
_ => unreachable!(),
739+
}
740+
741+
match pg().verified_stmt("COMMENT ON TABLE public.tab IS 'comment'") {
742+
Statement::Comment {
743+
object_type,
744+
object_name,
745+
comment: Some(comment),
746+
} => {
747+
assert_eq!("comment", comment);
748+
assert_eq!("public.tab", object_name.to_string());
749+
assert_eq!(CommentObject::Table, object_type);
750+
}
751+
_ => unreachable!(),
752+
}
753+
754+
match pg().verified_stmt("COMMENT ON TABLE public.tab IS NULL") {
755+
Statement::Comment {
756+
object_type,
757+
object_name,
758+
comment: None,
759+
} => {
760+
assert_eq!("public.tab", object_name.to_string());
761+
assert_eq!(CommentObject::Table, object_type);
762+
}
763+
_ => unreachable!(),
764+
}
765+
}
766+
726767
fn pg() -> TestedDialects {
727768
TestedDialects {
728769
dialects: vec![Box::new(PostgreSqlDialect {})],

0 commit comments

Comments
 (0)