Skip to content

Commit

Permalink
Add support for Postgres ALTER TYPE (#1727)
Browse files Browse the repository at this point in the history
  • Loading branch information
jvatic authored Feb 17, 2025
1 parent 68c41a9 commit c75a992
Show file tree
Hide file tree
Showing 6 changed files with 271 additions and 61 deletions.
89 changes: 89 additions & 0 deletions src/ast/ddl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -640,6 +640,95 @@ impl fmt::Display for AlterIndexOperation {
}
}

/// An `ALTER TYPE` statement (`Statement::AlterType`)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct AlterType {
pub name: ObjectName,
pub operation: AlterTypeOperation,
}

/// An [AlterType] operation
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum AlterTypeOperation {
Rename(AlterTypeRename),
AddValue(AlterTypeAddValue),
RenameValue(AlterTypeRenameValue),
}

/// See [AlterTypeOperation::Rename]
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct AlterTypeRename {
pub new_name: Ident,
}

/// See [AlterTypeOperation::AddValue]
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct AlterTypeAddValue {
pub if_not_exists: bool,
pub value: Ident,
pub position: Option<AlterTypeAddValuePosition>,
}

/// See [AlterTypeAddValue]
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub enum AlterTypeAddValuePosition {
Before(Ident),
After(Ident),
}

/// See [AlterTypeOperation::RenameValue]
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct AlterTypeRenameValue {
pub from: Ident,
pub to: Ident,
}

impl fmt::Display for AlterTypeOperation {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Rename(AlterTypeRename { new_name }) => {
write!(f, "RENAME TO {new_name}")
}
Self::AddValue(AlterTypeAddValue {
if_not_exists,
value,
position,
}) => {
write!(f, "ADD VALUE")?;
if *if_not_exists {
write!(f, " IF NOT EXISTS")?;
}
write!(f, " {value}")?;
match position {
Some(AlterTypeAddValuePosition::Before(neighbor_value)) => {
write!(f, " BEFORE {neighbor_value}")?;
}
Some(AlterTypeAddValuePosition::After(neighbor_value)) => {
write!(f, " AFTER {neighbor_value}")?;
}
None => {}
};
Ok(())
}
Self::RenameValue(AlterTypeRenameValue { from, to }) => {
write!(f, "RENAME VALUE {from} TO {to}")
}
}
}
}

/// An `ALTER COLUMN` (`Statement::AlterTable`) operation
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
Expand Down
24 changes: 17 additions & 7 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,15 @@ pub use self::dcl::{
};
pub use self::ddl::{
AlterColumnOperation, AlterConnectorOwner, AlterIndexOperation, AlterPolicyOperation,
AlterTableOperation, ClusteredBy, ColumnDef, ColumnOption, ColumnOptionDef, ColumnPolicy,
ColumnPolicyProperty, ConstraintCharacteristics, CreateConnector, CreateFunction, Deduplicate,
DeferrableInitial, DropBehavior, GeneratedAs, GeneratedExpressionMode, IdentityParameters,
IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder,
IndexOption, IndexType, KeyOrIndexDisplay, NullsDistinctOption, Owner, Partition,
ProcedureParam, ReferentialAction, TableConstraint, TagsColumnOption,
UserDefinedTypeCompositeAttributeDef, UserDefinedTypeRepresentation, ViewColumnDef,
AlterTableOperation, AlterType, AlterTypeAddValue, AlterTypeAddValuePosition,
AlterTypeOperation, AlterTypeRename, AlterTypeRenameValue, ClusteredBy, ColumnDef,
ColumnOption, ColumnOptionDef, ColumnPolicy, ColumnPolicyProperty, ConstraintCharacteristics,
CreateConnector, CreateFunction, Deduplicate, DeferrableInitial, DropBehavior, GeneratedAs,
GeneratedExpressionMode, IdentityParameters, IdentityProperty, IdentityPropertyFormatKind,
IdentityPropertyKind, IdentityPropertyOrder, IndexOption, IndexType, KeyOrIndexDisplay,
NullsDistinctOption, Owner, Partition, ProcedureParam, ReferentialAction, TableConstraint,
TagsColumnOption, UserDefinedTypeCompositeAttributeDef, UserDefinedTypeRepresentation,
ViewColumnDef,
};
pub use self::dml::{CreateIndex, CreateTable, Delete, Insert};
pub use self::operator::{BinaryOperator, UnaryOperator};
Expand Down Expand Up @@ -2691,6 +2693,11 @@ pub enum Statement {
with_options: Vec<SqlOption>,
},
/// ```sql
/// ALTER TYPE
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-altertype.html)
/// ```
AlterType(AlterType),
/// ```sql
/// ALTER ROLE
/// ```
AlterRole {
Expand Down Expand Up @@ -4438,6 +4445,9 @@ impl fmt::Display for Statement {
}
write!(f, " AS {query}")
}
Statement::AlterType(AlterType { name, operation }) => {
write!(f, "ALTER TYPE {name} {operation}")
}
Statement::AlterRole { name, operation } => {
write!(f, "ALTER ROLE {name} {operation}")
}
Expand Down
2 changes: 2 additions & 0 deletions src/ast/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ impl Spanned for Values {
/// - [Statement::CopyIntoSnowflake]
/// - [Statement::CreateSecret]
/// - [Statement::CreateRole]
/// - [Statement::AlterType]
/// - [Statement::AlterRole]
/// - [Statement::AttachDatabase]
/// - [Statement::AttachDuckDBDatabase]
Expand Down Expand Up @@ -427,6 +428,7 @@ impl Spanned for Statement {
.chain(with_options.iter().map(|i| i.span())),
),
// These statements need to be implemented
Statement::AlterType { .. } => Span::empty(),
Statement::AlterRole { .. } => Span::empty(),
Statement::AttachDatabase { .. } => Span::empty(),
Statement::AttachDuckDBDatabase { .. } => Span::empty(),
Expand Down
44 changes: 0 additions & 44 deletions src/dialect/postgresql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
// limitations under the License.
use log::debug;

use crate::ast::{ObjectName, Statement, UserDefinedTypeRepresentation};
use crate::dialect::{Dialect, Precedence};
use crate::keywords::Keyword;
use crate::parser::{Parser, ParserError};
Expand Down Expand Up @@ -135,15 +134,6 @@ impl Dialect for PostgreSqlDialect {
}
}

fn parse_statement(&self, parser: &mut Parser) -> Option<Result<Statement, ParserError>> {
if parser.parse_keyword(Keyword::CREATE) {
parser.prev_token(); // unconsume the CREATE in case we don't end up parsing anything
parse_create(parser)
} else {
None
}
}

fn supports_filter_during_aggregation(&self) -> bool {
true
}
Expand Down Expand Up @@ -259,37 +249,3 @@ impl Dialect for PostgreSqlDialect {
true
}
}

pub fn parse_create(parser: &mut Parser) -> Option<Result<Statement, ParserError>> {
let name = parser.maybe_parse(|parser| -> Result<ObjectName, ParserError> {
parser.expect_keyword_is(Keyword::CREATE)?;
parser.expect_keyword_is(Keyword::TYPE)?;
let name = parser.parse_object_name(false)?;
parser.expect_keyword_is(Keyword::AS)?;
parser.expect_keyword_is(Keyword::ENUM)?;
Ok(name)
});

match name {
Ok(name) => name.map(|name| parse_create_type_as_enum(parser, name)),
Err(e) => Some(Err(e)),
}
}

// https://www.postgresql.org/docs/current/sql-createtype.html
pub fn parse_create_type_as_enum(
parser: &mut Parser,
name: ObjectName,
) -> Result<Statement, ParserError> {
if !parser.consume_token(&Token::LParen) {
return parser.expected("'(' after CREATE TYPE AS ENUM", parser.peek_token());
}

let labels = parser.parse_comma_separated0(|p| p.parse_identifier(), Token::RParen)?;
parser.expect_token(&Token::RParen)?;

Ok(Statement::CreateType {
name,
representation: UserDefinedTypeRepresentation::Enum { labels },
})
}
69 changes: 69 additions & 0 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8042,6 +8042,7 @@ impl<'a> Parser<'a> {
pub fn parse_alter(&mut self) -> Result<Statement, ParserError> {
let object_type = self.expect_one_of_keywords(&[
Keyword::VIEW,
Keyword::TYPE,
Keyword::TABLE,
Keyword::INDEX,
Keyword::ROLE,
Expand All @@ -8050,6 +8051,7 @@ impl<'a> Parser<'a> {
])?;
match object_type {
Keyword::VIEW => self.parse_alter_view(),
Keyword::TYPE => self.parse_alter_type(),
Keyword::TABLE => {
let if_exists = self.parse_keywords(&[Keyword::IF, Keyword::EXISTS]);
let only = self.parse_keyword(Keyword::ONLY); // [ ONLY ]
Expand Down Expand Up @@ -8122,6 +8124,55 @@ impl<'a> Parser<'a> {
})
}

/// Parse a [Statement::AlterType]
pub fn parse_alter_type(&mut self) -> Result<Statement, ParserError> {
let name = self.parse_object_name(false)?;

if self.parse_keywords(&[Keyword::RENAME, Keyword::TO]) {
let new_name = self.parse_identifier()?;
Ok(Statement::AlterType(AlterType {
name,
operation: AlterTypeOperation::Rename(AlterTypeRename { new_name }),
}))
} else if self.parse_keywords(&[Keyword::ADD, Keyword::VALUE]) {
let if_not_exists = self.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);
let new_enum_value = self.parse_identifier()?;
let position = if self.parse_keyword(Keyword::BEFORE) {
Some(AlterTypeAddValuePosition::Before(self.parse_identifier()?))
} else if self.parse_keyword(Keyword::AFTER) {
Some(AlterTypeAddValuePosition::After(self.parse_identifier()?))
} else {
None
};

Ok(Statement::AlterType(AlterType {
name,
operation: AlterTypeOperation::AddValue(AlterTypeAddValue {
if_not_exists,
value: new_enum_value,
position,
}),
}))
} else if self.parse_keywords(&[Keyword::RENAME, Keyword::VALUE]) {
let existing_enum_value = self.parse_identifier()?;
self.expect_keyword(Keyword::TO)?;
let new_enum_value = self.parse_identifier()?;

Ok(Statement::AlterType(AlterType {
name,
operation: AlterTypeOperation::RenameValue(AlterTypeRenameValue {
from: existing_enum_value,
to: new_enum_value,
}),
}))
} else {
return self.expected_ref(
"{RENAME TO | { RENAME | ADD } VALUE}",
self.peek_token_ref(),
);
}
}

/// Parse a `CALL procedure_name(arg1, arg2, ...)`
/// or `CALL procedure_name` statement
pub fn parse_call(&mut self) -> Result<Statement, ParserError> {
Expand Down Expand Up @@ -14222,6 +14273,10 @@ impl<'a> Parser<'a> {
let name = self.parse_object_name(false)?;
self.expect_keyword_is(Keyword::AS)?;

if self.parse_keyword(Keyword::ENUM) {
return self.parse_create_type_enum(name);
}

let mut attributes = vec![];
if !self.consume_token(&Token::LParen) || self.consume_token(&Token::RParen) {
return Ok(Statement::CreateType {
Expand Down Expand Up @@ -14258,6 +14313,20 @@ impl<'a> Parser<'a> {
})
}

/// Parse remainder of `CREATE TYPE AS ENUM` statement (see [Statement::CreateType] and [Self::parse_create_type])
///
/// See [PostgreSQL](https://www.postgresql.org/docs/current/sql-createtype.html)
pub fn parse_create_type_enum(&mut self, name: ObjectName) -> Result<Statement, ParserError> {
self.expect_token(&Token::LParen)?;
let labels = self.parse_comma_separated0(|p| p.parse_identifier(), Token::RParen)?;
self.expect_token(&Token::RParen)?;

Ok(Statement::CreateType {
name,
representation: UserDefinedTypeRepresentation::Enum { labels },
})
}

fn parse_parenthesized_identifiers(&mut self) -> Result<Vec<Ident>, ParserError> {
self.expect_token(&Token::LParen)?;
let partitions = self.parse_comma_separated(|p| p.parse_identifier())?;
Expand Down
Loading

0 comments on commit c75a992

Please sign in to comment.