Skip to content

Commit 2b37e4a

Browse files
srijsalamb
andauthored
Add support for CREATE TYPE (AS) statements (#888)
Co-authored-by: Andrew Lamb <[email protected]>
1 parent e8cad6a commit 2b37e4a

File tree

4 files changed

+120
-0
lines changed

4 files changed

+120
-0
lines changed

src/ast/ddl.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -715,3 +715,43 @@ impl fmt::Display for ReferentialAction {
715715
})
716716
}
717717
}
718+
719+
/// SQL user defined type definition
720+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
721+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
722+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
723+
pub enum UserDefinedTypeRepresentation {
724+
Composite {
725+
attributes: Vec<UserDefinedTypeCompositeAttributeDef>,
726+
},
727+
}
728+
729+
impl fmt::Display for UserDefinedTypeRepresentation {
730+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
731+
match self {
732+
UserDefinedTypeRepresentation::Composite { attributes } => {
733+
write!(f, "({})", display_comma_separated(attributes))
734+
}
735+
}
736+
}
737+
}
738+
739+
/// SQL user defined type attribute definition
740+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
741+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
742+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
743+
pub struct UserDefinedTypeCompositeAttributeDef {
744+
pub name: Ident,
745+
pub data_type: DataType,
746+
pub collation: Option<ObjectName>,
747+
}
748+
749+
impl fmt::Display for UserDefinedTypeCompositeAttributeDef {
750+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
751+
write!(f, "{} {}", self.name, self.data_type)?;
752+
if let Some(collation) = &self.collation {
753+
write!(f, " COLLATE {collation}")?;
754+
}
755+
Ok(())
756+
}
757+
}

src/ast/mod.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ pub use self::data_type::{
3131
pub use self::ddl::{
3232
AlterColumnOperation, AlterIndexOperation, AlterTableOperation, ColumnDef, ColumnOption,
3333
ColumnOptionDef, GeneratedAs, IndexType, KeyOrIndexDisplay, ReferentialAction, TableConstraint,
34+
UserDefinedTypeCompositeAttributeDef, UserDefinedTypeRepresentation,
3435
};
3536
pub use self::operator::{BinaryOperator, UnaryOperator};
3637
pub use self::query::{
@@ -1711,6 +1712,11 @@ pub enum Statement {
17111712
sequence_options: Vec<SequenceOptions>,
17121713
owned_by: Option<ObjectName>,
17131714
},
1715+
/// CREATE TYPE `<name>`
1716+
CreateType {
1717+
name: ObjectName,
1718+
representation: UserDefinedTypeRepresentation,
1719+
},
17141720
}
17151721

17161722
impl fmt::Display for Statement {
@@ -2921,6 +2927,12 @@ impl fmt::Display for Statement {
29212927
}
29222928
Ok(())
29232929
}
2930+
Statement::CreateType {
2931+
name,
2932+
representation,
2933+
} => {
2934+
write!(f, "CREATE TYPE {name} AS {representation}")
2935+
}
29242936
}
29252937
}
29262938
}

src/parser.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2365,6 +2365,8 @@ impl<'a> Parser<'a> {
23652365
self.parse_create_role()
23662366
} else if self.parse_keyword(Keyword::SEQUENCE) {
23672367
self.parse_create_sequence(temporary)
2368+
} else if self.parse_keyword(Keyword::TYPE) {
2369+
self.parse_create_type()
23682370
} else {
23692371
self.expected("an object type after CREATE", self.peek_token())
23702372
}
@@ -7053,6 +7055,46 @@ impl<'a> Parser<'a> {
70537055
window_frame,
70547056
})
70557057
}
7058+
7059+
pub fn parse_create_type(&mut self) -> Result<Statement, ParserError> {
7060+
let name = self.parse_object_name()?;
7061+
self.expect_keyword(Keyword::AS)?;
7062+
7063+
let mut attributes = vec![];
7064+
if !self.consume_token(&Token::LParen) || self.consume_token(&Token::RParen) {
7065+
return Ok(Statement::CreateType {
7066+
name,
7067+
representation: UserDefinedTypeRepresentation::Composite { attributes },
7068+
});
7069+
}
7070+
7071+
loop {
7072+
let attr_name = self.parse_identifier()?;
7073+
let attr_data_type = self.parse_data_type()?;
7074+
let attr_collation = if self.parse_keyword(Keyword::COLLATE) {
7075+
Some(self.parse_object_name()?)
7076+
} else {
7077+
None
7078+
};
7079+
attributes.push(UserDefinedTypeCompositeAttributeDef {
7080+
name: attr_name,
7081+
data_type: attr_data_type,
7082+
collation: attr_collation,
7083+
});
7084+
let comma = self.consume_token(&Token::Comma);
7085+
if self.consume_token(&Token::RParen) {
7086+
// allow a trailing comma
7087+
break;
7088+
} else if !comma {
7089+
return self.expected("',' or ')' after attribute definition", self.peek_token());
7090+
}
7091+
}
7092+
7093+
Ok(Statement::CreateType {
7094+
name,
7095+
representation: UserDefinedTypeRepresentation::Composite { attributes },
7096+
})
7097+
}
70567098
}
70577099

70587100
impl Word {

tests/sqlparser_common.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7092,3 +7092,29 @@ fn parse_trailing_comma() {
70927092

70937093
trailing_commas.verified_stmt("SELECT DISTINCT ON (album_id) name FROM track");
70947094
}
7095+
7096+
#[test]
7097+
fn parse_create_type() {
7098+
let create_type =
7099+
verified_stmt("CREATE TYPE db.type_name AS (foo INT, bar TEXT COLLATE \"de_DE\")");
7100+
assert_eq!(
7101+
Statement::CreateType {
7102+
name: ObjectName(vec![Ident::new("db"), Ident::new("type_name")]),
7103+
representation: UserDefinedTypeRepresentation::Composite {
7104+
attributes: vec![
7105+
UserDefinedTypeCompositeAttributeDef {
7106+
name: Ident::new("foo"),
7107+
data_type: DataType::Int(None),
7108+
collation: None,
7109+
},
7110+
UserDefinedTypeCompositeAttributeDef {
7111+
name: Ident::new("bar"),
7112+
data_type: DataType::Text,
7113+
collation: Some(ObjectName(vec![Ident::with_quote('\"', "de_DE")])),
7114+
}
7115+
]
7116+
}
7117+
},
7118+
create_type
7119+
);
7120+
}

0 commit comments

Comments
 (0)