Skip to content

Commit d361fec

Browse files
committed
Add support for Postgres ALTER TYPE {RENAME TO|{ADD|RENAME} VALUE}
1 parent 8e6c862 commit d361fec

File tree

5 files changed

+250
-7
lines changed

5 files changed

+250
-7
lines changed

src/ast/ddl.rs

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,95 @@ impl fmt::Display for AlterIndexOperation {
640640
}
641641
}
642642

643+
/// An `ALTER TYPE` statement (`Statement::AlterType`)
644+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
645+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
646+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
647+
pub struct AlterType {
648+
pub name: ObjectName,
649+
pub operation: AlterTypeOperation,
650+
}
651+
652+
/// An [AlterType] operation
653+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
654+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
655+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
656+
pub enum AlterTypeOperation {
657+
Rename(AlterTypeRename),
658+
AddValue(AlterTypeAddValue),
659+
RenameValue(AlterTypeRenameValue),
660+
}
661+
662+
/// See [AlterTypeOperation::Rename]
663+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
664+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
665+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
666+
pub struct AlterTypeRename {
667+
pub new_name: Ident,
668+
}
669+
670+
/// See [AlterTypeOperation::AddValue]
671+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
672+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
673+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
674+
pub struct AlterTypeAddValue {
675+
pub if_not_exists: bool,
676+
pub value: Ident,
677+
pub position: Option<AlterTypeAddValuePosition>,
678+
}
679+
680+
/// See [AlterTypeAddValue]
681+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
682+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
683+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
684+
pub enum AlterTypeAddValuePosition {
685+
Before(Ident),
686+
After(Ident),
687+
}
688+
689+
/// See [AlterTypeOperation::RenameValue]
690+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
691+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
692+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
693+
pub struct AlterTypeRenameValue {
694+
pub from: Ident,
695+
pub to: Ident,
696+
}
697+
698+
impl fmt::Display for AlterTypeOperation {
699+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
700+
match self {
701+
Self::Rename(AlterTypeRename { new_name }) => {
702+
write!(f, "RENAME TO {new_name}")
703+
}
704+
Self::AddValue(AlterTypeAddValue {
705+
if_not_exists,
706+
value,
707+
position,
708+
}) => {
709+
write!(f, "ADD VALUE")?;
710+
if *if_not_exists {
711+
write!(f, " IF NOT EXISTS")?;
712+
}
713+
write!(f, " {value}")?;
714+
match position {
715+
Some(AlterTypeAddValuePosition::Before(neighbor_value)) => {
716+
write!(f, " BEFORE {neighbor_value}")?;
717+
}
718+
Some(AlterTypeAddValuePosition::After(neighbor_value)) => {
719+
write!(f, " AFTER {neighbor_value}")?;
720+
}
721+
None => {}
722+
};
723+
Ok(())
724+
}
725+
Self::RenameValue(AlterTypeRenameValue { from, to }) => {
726+
write!(f, "RENAME VALUE {from} TO {to}")
727+
}
728+
}
729+
}
730+
}
731+
643732
/// An `ALTER COLUMN` (`Statement::AlterTable`) operation
644733
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
645734
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]

src/ast/mod.rs

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,15 @@ pub use self::dcl::{
4848
};
4949
pub use self::ddl::{
5050
AlterColumnOperation, AlterConnectorOwner, AlterIndexOperation, AlterPolicyOperation,
51-
AlterTableOperation, ClusteredBy, ColumnDef, ColumnOption, ColumnOptionDef, ColumnPolicy,
52-
ColumnPolicyProperty, ConstraintCharacteristics, CreateConnector, CreateFunction, Deduplicate,
53-
DeferrableInitial, DropBehavior, GeneratedAs, GeneratedExpressionMode, IdentityParameters,
54-
IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder,
55-
IndexOption, IndexType, KeyOrIndexDisplay, NullsDistinctOption, Owner, Partition,
56-
ProcedureParam, ReferentialAction, TableConstraint, TagsColumnOption,
57-
UserDefinedTypeCompositeAttributeDef, UserDefinedTypeRepresentation, ViewColumnDef,
51+
AlterTableOperation, AlterType, AlterTypeAddValue, AlterTypeAddValuePosition,
52+
AlterTypeOperation, AlterTypeRename, AlterTypeRenameValue, ClusteredBy, ColumnDef,
53+
ColumnOption, ColumnOptionDef, ColumnPolicy, ColumnPolicyProperty, ConstraintCharacteristics,
54+
CreateConnector, CreateFunction, Deduplicate, DeferrableInitial, DropBehavior, GeneratedAs,
55+
GeneratedExpressionMode, IdentityParameters, IdentityProperty, IdentityPropertyFormatKind,
56+
IdentityPropertyKind, IdentityPropertyOrder, IndexOption, IndexType, KeyOrIndexDisplay,
57+
NullsDistinctOption, Owner, Partition, ProcedureParam, ReferentialAction, TableConstraint,
58+
TagsColumnOption, UserDefinedTypeCompositeAttributeDef, UserDefinedTypeRepresentation,
59+
ViewColumnDef,
5860
};
5961
pub use self::dml::{CreateIndex, CreateTable, Delete, Insert};
6062
pub use self::operator::{BinaryOperator, UnaryOperator};
@@ -2691,6 +2693,11 @@ pub enum Statement {
26912693
with_options: Vec<SqlOption>,
26922694
},
26932695
/// ```sql
2696+
/// ALTER TYPE
2697+
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-altertype.html)
2698+
/// ```
2699+
AlterType(AlterType),
2700+
/// ```sql
26942701
/// ALTER ROLE
26952702
/// ```
26962703
AlterRole {
@@ -4438,6 +4445,9 @@ impl fmt::Display for Statement {
44384445
}
44394446
write!(f, " AS {query}")
44404447
}
4448+
Statement::AlterType(AlterType { name, operation }) => {
4449+
write!(f, "ALTER TYPE {name} {operation}")
4450+
}
44414451
Statement::AlterRole { name, operation } => {
44424452
write!(f, "ALTER ROLE {name} {operation}")
44434453
}

src/ast/spans.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ impl Spanned for Values {
215215
/// - [Statement::CopyIntoSnowflake]
216216
/// - [Statement::CreateSecret]
217217
/// - [Statement::CreateRole]
218+
/// - [Statement::AlterType]
218219
/// - [Statement::AlterRole]
219220
/// - [Statement::AttachDatabase]
220221
/// - [Statement::AttachDuckDBDatabase]
@@ -427,6 +428,7 @@ impl Spanned for Statement {
427428
.chain(with_options.iter().map(|i| i.span())),
428429
),
429430
// These statements need to be implemented
431+
Statement::AlterType { .. } => Span::empty(),
430432
Statement::AlterRole { .. } => Span::empty(),
431433
Statement::AttachDatabase { .. } => Span::empty(),
432434
Statement::AttachDuckDBDatabase { .. } => Span::empty(),

src/parser/mod.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8042,6 +8042,7 @@ impl<'a> Parser<'a> {
80428042
pub fn parse_alter(&mut self) -> Result<Statement, ParserError> {
80438043
let object_type = self.expect_one_of_keywords(&[
80448044
Keyword::VIEW,
8045+
Keyword::TYPE,
80458046
Keyword::TABLE,
80468047
Keyword::INDEX,
80478048
Keyword::ROLE,
@@ -8050,6 +8051,7 @@ impl<'a> Parser<'a> {
80508051
])?;
80518052
match object_type {
80528053
Keyword::VIEW => self.parse_alter_view(),
8054+
Keyword::TYPE => self.parse_alter_type(),
80538055
Keyword::TABLE => {
80548056
let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]);
80558057
let only = self.parse_keyword(Keyword::ONLY); // [ ONLY ]
@@ -8122,6 +8124,55 @@ impl<'a> Parser<'a> {
81228124
})
81238125
}
81248126

8127+
/// Parse a [Statement::AlterType]
8128+
pub fn parse_alter_type(&mut self) -> Result<Statement, ParserError> {
8129+
let name = self.parse_object_name(false)?;
8130+
8131+
if self.parse_keywords(&[Keyword::RENAME, Keyword::TO]) {
8132+
let new_name = self.parse_identifier()?;
8133+
Ok(Statement::AlterType(AlterType {
8134+
name,
8135+
operation: AlterTypeOperation::Rename(AlterTypeRename { new_name }),
8136+
}))
8137+
} else if self.parse_keywords(&[Keyword::ADD, Keyword::VALUE]) {
8138+
let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
8139+
let new_enum_value = self.parse_identifier()?;
8140+
let position = if self.parse_keyword(Keyword::BEFORE) {
8141+
Some(AlterTypeAddValuePosition::Before(self.parse_identifier()?))
8142+
} else if self.parse_keyword(Keyword::AFTER) {
8143+
Some(AlterTypeAddValuePosition::After(self.parse_identifier()?))
8144+
} else {
8145+
None
8146+
};
8147+
8148+
Ok(Statement::AlterType(AlterType {
8149+
name,
8150+
operation: AlterTypeOperation::AddValue(AlterTypeAddValue {
8151+
if_not_exists,
8152+
value: new_enum_value,
8153+
position,
8154+
}),
8155+
}))
8156+
} else if self.parse_keywords(&[Keyword::RENAME, Keyword::VALUE]) {
8157+
let existing_enum_value = self.parse_identifier()?;
8158+
self.expect_keyword(Keyword::TO)?;
8159+
let new_enum_value = self.parse_identifier()?;
8160+
8161+
Ok(Statement::AlterType(AlterType {
8162+
name,
8163+
operation: AlterTypeOperation::RenameValue(AlterTypeRenameValue {
8164+
from: existing_enum_value,
8165+
to: new_enum_value,
8166+
}),
8167+
}))
8168+
} else {
8169+
return self.expected_ref(
8170+
"{RENAME TO | { RENAME | ADD } VALUE}",
8171+
self.peek_token_ref(),
8172+
);
8173+
}
8174+
}
8175+
81258176
/// Parse a `CALL procedure_name(arg1, arg2, ...)`
81268177
/// or `CALL procedure_name` statement
81278178
pub fn parse_call(&mut self) -> Result<Statement, ParserError> {

tests/sqlparser_postgres.rs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5313,6 +5313,97 @@ fn parse_create_type_as_enum() {
53135313
}
53145314
}
53155315

5316+
#[test]
5317+
fn parse_alter_type() {
5318+
struct TestCase {
5319+
sql: &'static str,
5320+
name: &'static str,
5321+
operation: AlterTypeOperation,
5322+
}
5323+
vec![
5324+
TestCase {
5325+
sql: "ALTER TYPE public.my_type RENAME TO my_new_type",
5326+
name: "public.my_type",
5327+
operation: AlterTypeOperation::Rename(AlterTypeRename {
5328+
new_name: Ident::new("my_new_type"),
5329+
}),
5330+
},
5331+
TestCase {
5332+
sql: "ALTER TYPE public.my_type ADD VALUE IF NOT EXISTS 'label3.5' BEFORE 'label4'",
5333+
name: "public.my_type",
5334+
operation: AlterTypeOperation::AddValue(AlterTypeAddValue {
5335+
if_not_exists: true,
5336+
value: Ident::with_quote('\'', "label3.5"),
5337+
position: Some(AlterTypeAddValuePosition::Before(Ident::with_quote(
5338+
'\'', "label4",
5339+
))),
5340+
}),
5341+
},
5342+
TestCase {
5343+
sql: "ALTER TYPE public.my_type ADD VALUE 'label3.5' BEFORE 'label4'",
5344+
name: "public.my_type",
5345+
operation: AlterTypeOperation::AddValue(AlterTypeAddValue {
5346+
if_not_exists: false,
5347+
value: Ident::with_quote('\'', "label3.5"),
5348+
position: Some(AlterTypeAddValuePosition::Before(Ident::with_quote(
5349+
'\'', "label4",
5350+
))),
5351+
}),
5352+
},
5353+
TestCase {
5354+
sql: "ALTER TYPE public.my_type ADD VALUE IF NOT EXISTS 'label3.5' AFTER 'label3'",
5355+
name: "public.my_type",
5356+
operation: AlterTypeOperation::AddValue(AlterTypeAddValue {
5357+
if_not_exists: true,
5358+
value: Ident::with_quote('\'', "label3.5"),
5359+
position: Some(AlterTypeAddValuePosition::After(Ident::with_quote(
5360+
'\'', "label3",
5361+
))),
5362+
}),
5363+
},
5364+
TestCase {
5365+
sql: "ALTER TYPE public.my_type ADD VALUE 'label3.5' AFTER 'label3'",
5366+
name: "public.my_type",
5367+
operation: AlterTypeOperation::AddValue(AlterTypeAddValue {
5368+
if_not_exists: false,
5369+
value: Ident::with_quote('\'', "label3.5"),
5370+
position: Some(AlterTypeAddValuePosition::After(Ident::with_quote(
5371+
'\'', "label3",
5372+
))),
5373+
}),
5374+
},
5375+
TestCase {
5376+
sql: "ALTER TYPE public.my_type ADD VALUE IF NOT EXISTS 'label5'",
5377+
name: "public.my_type",
5378+
operation: AlterTypeOperation::AddValue(AlterTypeAddValue {
5379+
if_not_exists: true,
5380+
value: Ident::with_quote('\'', "label5"),
5381+
position: None,
5382+
}),
5383+
},
5384+
TestCase {
5385+
sql: "ALTER TYPE public.my_type ADD VALUE 'label5'",
5386+
name: "public.my_type",
5387+
operation: AlterTypeOperation::AddValue(AlterTypeAddValue {
5388+
if_not_exists: false,
5389+
value: Ident::with_quote('\'', "label5"),
5390+
position: None,
5391+
}),
5392+
},
5393+
]
5394+
.into_iter()
5395+
.enumerate()
5396+
.for_each(|(index, tc)| {
5397+
let statement = pg_and_generic().verified_stmt(tc.sql);
5398+
if let Statement::AlterType(AlterType { name, operation }) = statement {
5399+
assert_eq!(tc.name, name.to_string(), "TestCase[{index}].name");
5400+
assert_eq!(tc.operation, operation, "TestCase[{index}].operation");
5401+
} else {
5402+
unreachable!("{:?} should parse to Statement::AlterType", tc.sql);
5403+
}
5404+
});
5405+
}
5406+
53165407
#[test]
53175408
fn parse_bitstring_literal() {
53185409
let select = pg_and_generic().verified_only_select("SELECT B'111'");

0 commit comments

Comments
 (0)