Skip to content

Commit f5993b3

Browse files
committed
Add support for Postgres ALTER TYPE {RENAME TO|{ADD|RENAME} VALUE}
1 parent 68c41a9 commit f5993b3

File tree

8 files changed

+268
-9
lines changed

8 files changed

+268
-9
lines changed

src/ast/ddl.rs

+62
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,68 @@ impl fmt::Display for AlterIndexOperation {
640640
}
641641
}
642642

643+
/// An `ALTER TYPE` (`Statement::AlterType`) operation
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 enum AlterTypeOperation {
648+
Rename {
649+
new_name: Ident,
650+
},
651+
AddValue {
652+
if_not_exists: bool,
653+
value: Ident,
654+
position: AlterTypeOperationAddValuePosition,
655+
},
656+
RenameValue {
657+
from: Ident,
658+
to: Ident,
659+
},
660+
}
661+
662+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
663+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
664+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
665+
pub enum AlterTypeOperationAddValuePosition {
666+
Default,
667+
Before(Ident),
668+
After(Ident),
669+
}
670+
671+
impl fmt::Display for AlterTypeOperation {
672+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
673+
match self {
674+
Self::Rename { new_name } => {
675+
write!(f, "RENAME TO {new_name}")
676+
}
677+
Self::AddValue {
678+
if_not_exists,
679+
value,
680+
position,
681+
} => {
682+
write!(f, "ADD VALUE")?;
683+
if *if_not_exists {
684+
write!(f, " IF NOT EXISTS")?;
685+
}
686+
write!(f, " {value}")?;
687+
match position {
688+
AlterTypeOperationAddValuePosition::Before(neighbor_value) => {
689+
write!(f, " BEFORE {neighbor_value}")?;
690+
}
691+
AlterTypeOperationAddValuePosition::After(neighbor_value) => {
692+
write!(f, " AFTER {neighbor_value}")?;
693+
}
694+
AlterTypeOperationAddValuePosition::Default => {}
695+
};
696+
Ok(())
697+
}
698+
Self::RenameValue { from, to } => {
699+
write!(f, "RENAME VALUE {from} TO {to}")
700+
}
701+
}
702+
}
703+
}
704+
643705
/// An `ALTER COLUMN` (`Statement::AlterTable`) operation
644706
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
645707
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]

src/ast/mod.rs

+19-7
Original file line numberDiff line numberDiff line change
@@ -48,13 +48,14 @@ 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, AlterTypeOperation, AlterTypeOperationAddValuePosition, ClusteredBy,
52+
ColumnDef, ColumnOption, ColumnOptionDef, ColumnPolicy, ColumnPolicyProperty,
53+
ConstraintCharacteristics, CreateConnector, CreateFunction, Deduplicate, DeferrableInitial,
54+
DropBehavior, GeneratedAs, GeneratedExpressionMode, IdentityParameters, IdentityProperty,
55+
IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder, IndexOption,
56+
IndexType, KeyOrIndexDisplay, NullsDistinctOption, Owner, Partition, ProcedureParam,
57+
ReferentialAction, TableConstraint, TagsColumnOption, UserDefinedTypeCompositeAttributeDef,
58+
UserDefinedTypeRepresentation, ViewColumnDef,
5859
};
5960
pub use self::dml::{CreateIndex, CreateTable, Delete, Insert};
6061
pub use self::operator::{BinaryOperator, UnaryOperator};
@@ -2691,6 +2692,14 @@ pub enum Statement {
26912692
with_options: Vec<SqlOption>,
26922693
},
26932694
/// ```sql
2695+
/// ALTER TYPE
2696+
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-altertype.html)
2697+
/// ```
2698+
AlterType {
2699+
name: ObjectName,
2700+
operation: AlterTypeOperation,
2701+
},
2702+
/// ```sql
26942703
/// ALTER ROLE
26952704
/// ```
26962705
AlterRole {
@@ -4438,6 +4447,9 @@ impl fmt::Display for Statement {
44384447
}
44394448
write!(f, " AS {query}")
44404449
}
4450+
Statement::AlterType { name, operation } => {
4451+
write!(f, "ALTER TYPE {name} {operation}")
4452+
}
44414453
Statement::AlterRole { name, operation } => {
44424454
write!(f, "ALTER ROLE {name} {operation}")
44434455
}

src/ast/spans.rs

+2
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/dialect/generic.rs

+4
Original file line numberDiff line numberDiff line change
@@ -155,4 +155,8 @@ impl Dialect for GenericDialect {
155155
fn supports_match_against(&self) -> bool {
156156
true
157157
}
158+
159+
fn supports_alter_type(&self) -> bool {
160+
true
161+
}
158162
}

src/dialect/mod.rs

+10
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,16 @@ pub trait Dialect: Debug + Any {
490490
false
491491
}
492492

493+
/// Return true if the dialect supports ALTER TYPE statements
494+
///
495+
/// Example:
496+
/// ```sql
497+
/// ALTER TYPE some_enum ADD VALUE 'some_value';
498+
/// ```
499+
fn supports_alter_type(&self) -> bool {
500+
false
501+
}
502+
493503
/// Dialect-specific infix parser override
494504
///
495505
/// This method is called to parse the next infix expression.

src/dialect/postgresql.rs

+4
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,10 @@ impl Dialect for PostgreSqlDialect {
258258
fn supports_array_typedef_size(&self) -> bool {
259259
true
260260
}
261+
262+
fn supports_alter_type(&self) -> bool {
263+
true
264+
}
261265
}
262266

263267
pub fn parse_create(parser: &mut Parser) -> Option<Result<Statement, ParserError>> {

src/parser/mod.rs

+56-2
Original file line numberDiff line numberDiff line change
@@ -8040,16 +8040,21 @@ impl<'a> Parser<'a> {
80408040
}
80418041

80428042
pub fn parse_alter(&mut self) -> Result<Statement, ParserError> {
8043-
let object_type = self.expect_one_of_keywords(&[
8043+
let mut keywords = vec![
80448044
Keyword::VIEW,
80458045
Keyword::TABLE,
80468046
Keyword::INDEX,
80478047
Keyword::ROLE,
80488048
Keyword::POLICY,
80498049
Keyword::CONNECTOR,
8050-
])?;
8050+
];
8051+
if self.dialect.supports_alter_type() {
8052+
keywords.push(Keyword::TYPE);
8053+
}
8054+
let object_type = self.expect_one_of_keywords(&keywords)?;
80518055
match object_type {
80528056
Keyword::VIEW => self.parse_alter_view(),
8057+
Keyword::TYPE => self.parse_alter_type(),
80538058
Keyword::TABLE => {
80548059
let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]);
80558060
let only = self.parse_keyword(Keyword::ONLY); // [ ONLY ]
@@ -8122,6 +8127,55 @@ impl<'a> Parser<'a> {
81228127
})
81238128
}
81248129

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

tests/sqlparser_postgres.rs

+111
Original file line numberDiff line numberDiff line change
@@ -5320,6 +5320,117 @@ fn parse_create_type_as_enum() {
53205320
}
53215321
}
53225322

5323+
#[test]
5324+
fn parse_alter_type() {
5325+
struct TestCase {
5326+
sql: &'static str,
5327+
canonical: &'static str,
5328+
name: &'static str,
5329+
operation: AlterTypeOperation,
5330+
}
5331+
vec![
5332+
TestCase {
5333+
sql: r#"ALTER TYPE public.my_type
5334+
RENAME TO my_new_type"#,
5335+
canonical: "ALTER TYPE public.my_type RENAME TO my_new_type",
5336+
name: "public.my_type",
5337+
operation: AlterTypeOperation::Rename {
5338+
new_name: Ident::new("my_new_type"),
5339+
},
5340+
},
5341+
TestCase {
5342+
sql: r#"ALTER TYPE public.my_type
5343+
ADD VALUE IF NOT EXISTS 'label3.5'
5344+
BEFORE 'label4'"#,
5345+
canonical:
5346+
"ALTER TYPE public.my_type ADD VALUE IF NOT EXISTS 'label3.5' BEFORE 'label4'",
5347+
name: "public.my_type",
5348+
operation: AlterTypeOperation::AddValue {
5349+
if_not_exists: true,
5350+
value: Ident::with_quote('\'', "label3.5"),
5351+
position: AlterTypeOperationAddValuePosition::Before(Ident::with_quote(
5352+
'\'', "label4",
5353+
)),
5354+
},
5355+
},
5356+
TestCase {
5357+
sql: r#"ALTER TYPE public.my_type
5358+
ADD VALUE 'label3.5' BEFORE 'label4'"#,
5359+
canonical: "ALTER TYPE public.my_type ADD VALUE 'label3.5' BEFORE 'label4'",
5360+
name: "public.my_type",
5361+
operation: AlterTypeOperation::AddValue {
5362+
if_not_exists: false,
5363+
value: Ident::with_quote('\'', "label3.5"),
5364+
position: AlterTypeOperationAddValuePosition::Before(Ident::with_quote(
5365+
'\'', "label4",
5366+
)),
5367+
},
5368+
},
5369+
TestCase {
5370+
sql: r#"ALTER TYPE public.my_type
5371+
ADD VALUE IF NOT EXISTS 'label3.5'
5372+
AFTER 'label3'"#,
5373+
canonical:
5374+
"ALTER TYPE public.my_type ADD VALUE IF NOT EXISTS 'label3.5' AFTER 'label3'",
5375+
name: "public.my_type",
5376+
operation: AlterTypeOperation::AddValue {
5377+
if_not_exists: true,
5378+
value: Ident::with_quote('\'', "label3.5"),
5379+
position: AlterTypeOperationAddValuePosition::After(Ident::with_quote(
5380+
'\'', "label3",
5381+
)),
5382+
},
5383+
},
5384+
TestCase {
5385+
sql: r#"ALTER TYPE public.my_type
5386+
ADD VALUE 'label3.5'
5387+
AFTER 'label3'"#,
5388+
canonical: "ALTER TYPE public.my_type ADD VALUE 'label3.5' AFTER 'label3'",
5389+
name: "public.my_type",
5390+
operation: AlterTypeOperation::AddValue {
5391+
if_not_exists: false,
5392+
value: Ident::with_quote('\'', "label3.5"),
5393+
position: AlterTypeOperationAddValuePosition::After(Ident::with_quote(
5394+
'\'', "label3",
5395+
)),
5396+
},
5397+
},
5398+
TestCase {
5399+
sql: r#"ALTER TYPE public.my_type
5400+
ADD VALUE IF NOT EXISTS 'label5'"#,
5401+
canonical: "ALTER TYPE public.my_type ADD VALUE IF NOT EXISTS 'label5'",
5402+
name: "public.my_type",
5403+
operation: AlterTypeOperation::AddValue {
5404+
if_not_exists: true,
5405+
value: Ident::with_quote('\'', "label5"),
5406+
position: AlterTypeOperationAddValuePosition::Default,
5407+
},
5408+
},
5409+
TestCase {
5410+
sql: r#"ALTER TYPE public.my_type
5411+
ADD VALUE 'label5'"#,
5412+
canonical: "ALTER TYPE public.my_type ADD VALUE 'label5'",
5413+
name: "public.my_type",
5414+
operation: AlterTypeOperation::AddValue {
5415+
if_not_exists: false,
5416+
value: Ident::with_quote('\'', "label5"),
5417+
position: AlterTypeOperationAddValuePosition::Default,
5418+
},
5419+
},
5420+
]
5421+
.into_iter()
5422+
.enumerate()
5423+
.for_each(|(index, tc)| {
5424+
let statement = pg_and_generic().one_statement_parses_to(tc.sql, tc.canonical);
5425+
if let Statement::AlterType { name, operation } = statement {
5426+
assert_eq!(tc.name, name.to_string(), "TestCase[{index}].name");
5427+
assert_eq!(tc.operation, operation, "TestCase[{index}].operation");
5428+
} else {
5429+
unreachable!("{:?} should parse to Statement::AlterType", tc.canonical);
5430+
}
5431+
});
5432+
}
5433+
53235434
#[test]
53245435
fn parse_bitstring_literal() {
53255436
let select = pg_and_generic().verified_only_select("SELECT B'111'");

0 commit comments

Comments
 (0)