Skip to content

Add support for Postgres ALTER TYPE #1727

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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