Skip to content

Commit c672b47

Browse files
committed
Support for pg ADD GENERATED in ALTER COLUMN statements
Signed-off-by: Toby Hede <toby@cipherstash.com>
1 parent a75778c commit c672b47

File tree

5 files changed

+84
-6
lines changed

5 files changed

+84
-6
lines changed

src/ast/ddl.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,13 @@ pub enum AlterColumnOperation {
315315
/// PostgreSQL specific
316316
using: Option<Expr>,
317317
},
318+
/// `ADD GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( sequence_options ) ]`
319+
///
320+
/// Note: this is a PostgreSQL-specific operation.
321+
AddGenerated {
322+
generated_as: Option<GeneratedAs>,
323+
sequence_options: Option<Vec<SequenceOptions>>,
324+
},
318325
}
319326

320327
impl fmt::Display for AlterColumnOperation {
@@ -335,6 +342,32 @@ impl fmt::Display for AlterColumnOperation {
335342
write!(f, "SET DATA TYPE {data_type}")
336343
}
337344
}
345+
AlterColumnOperation::AddGenerated {
346+
generated_as,
347+
sequence_options,
348+
} => {
349+
let generated_as = match generated_as {
350+
Some(GeneratedAs::Always) => " ALWAYS",
351+
Some(GeneratedAs::ByDefault) => " BY DEFAULT",
352+
_ => "",
353+
};
354+
355+
write!(f, "ADD GENERATED{generated_as} AS IDENTITY",)?;
356+
if let Some(options) = sequence_options {
357+
if !options.is_empty() {
358+
write!(f, " (")?;
359+
}
360+
361+
for sequence_option in options {
362+
write!(f, "{sequence_option}")?;
363+
}
364+
365+
if !options.is_empty() {
366+
write!(f, " )")?;
367+
}
368+
}
369+
Ok(())
370+
}
338371
}
339372
}
340373
}

src/keywords.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,6 +552,7 @@ define_keywords!(
552552
REPLICATION,
553553
RESET,
554554
RESPECT,
555+
RESTART,
555556
RESTRICT,
556557
RESULT,
557558
RETAIN,

src/parser/mod.rs

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4841,7 +4841,11 @@ impl<'a> Parser<'a> {
48414841
let column_name = self.parse_identifier()?;
48424842
let is_postgresql = dialect_of!(self is PostgreSqlDialect);
48434843

4844-
let op = if self.parse_keywords(&[Keyword::SET, Keyword::NOT, Keyword::NULL]) {
4844+
let op: AlterColumnOperation = if self.parse_keywords(&[
4845+
Keyword::SET,
4846+
Keyword::NOT,
4847+
Keyword::NULL,
4848+
]) {
48454849
AlterColumnOperation::SetNotNull {}
48464850
} else if self.parse_keywords(&[Keyword::DROP, Keyword::NOT, Keyword::NULL]) {
48474851
AlterColumnOperation::DropNotNull {}
@@ -4861,11 +4865,37 @@ impl<'a> Parser<'a> {
48614865
None
48624866
};
48634867
AlterColumnOperation::SetDataType { data_type, using }
4868+
} else if self.parse_keywords(&[Keyword::ADD, Keyword::GENERATED]) {
4869+
let generated_as = if self.parse_keyword(Keyword::ALWAYS) {
4870+
Some(GeneratedAs::Always)
4871+
} else if self.parse_keywords(&[Keyword::BY, Keyword::DEFAULT]) {
4872+
Some(GeneratedAs::ByDefault)
4873+
} else {
4874+
None
4875+
};
4876+
4877+
let _ = self.expect_keywords(&[Keyword::AS, Keyword::IDENTITY]);
4878+
4879+
let mut sequence_options: Option<Vec<SequenceOptions>> = None;
4880+
4881+
if self.peek_token().token == Token::LParen {
4882+
self.expect_token(&Token::LParen)?;
4883+
sequence_options = self.parse_create_sequence_options().ok();
4884+
self.expect_token(&Token::RParen)?;
4885+
}
4886+
4887+
AlterColumnOperation::AddGenerated {
4888+
generated_as,
4889+
sequence_options,
4890+
}
48644891
} else {
4865-
return self.expected(
4866-
"SET/DROP NOT NULL, SET DEFAULT, SET DATA TYPE after ALTER COLUMN",
4867-
self.peek_token(),
4868-
);
4892+
let message = if is_postgresql {
4893+
"SET/DROP NOT NULL, SET DEFAULT, SET DATA TYPE, or ADD GENERATED after ALTER COLUMN"
4894+
} else {
4895+
"SET/DROP NOT NULL, SET DEFAULT, or SET DATA TYPE after ALTER COLUMN"
4896+
};
4897+
4898+
return self.expected(message, self.peek_token());
48694899
};
48704900
AlterTableOperation::AlterColumn { column_name, op }
48714901
} else if self.parse_keyword(Keyword::SWAP) {

tests/sqlparser_common.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3457,7 +3457,7 @@ fn parse_alter_table_alter_column_type() {
34573457
let res =
34583458
dialect.parse_sql_statements(&format!("{alter_stmt} ALTER COLUMN is_active TYPE TEXT"));
34593459
assert_eq!(
3460-
ParserError::ParserError("Expected SET/DROP NOT NULL, SET DEFAULT, SET DATA TYPE after ALTER COLUMN, found: TYPE".to_string()),
3460+
ParserError::ParserError("Expected SET/DROP NOT NULL, SET DEFAULT, or SET DATA TYPE after ALTER COLUMN, found: TYPE".to_string()),
34613461
res.unwrap_err()
34623462
);
34633463

tests/sqlparser_postgres.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,20 @@ fn parse_alter_table_alter_column() {
611611
}
612612
}
613613

614+
#[test]
615+
fn parse_alter_table_alter_column_add_generated() {
616+
pg_and_generic()
617+
.verified_stmt("ALTER TABLE t ALTER COLUMN id ADD GENERATED ALWAYS AS IDENTITY");
618+
pg_and_generic()
619+
.verified_stmt("ALTER TABLE t ALTER COLUMN id ADD GENERATED BY DEFAULT AS IDENTITY");
620+
pg_and_generic().verified_stmt("ALTER TABLE t ALTER COLUMN id ADD GENERATED AS IDENTITY");
621+
pg_and_generic().verified_stmt(
622+
"ALTER TABLE t ALTER COLUMN id ADD GENERATED AS IDENTITY ( INCREMENT 1 MINVALUE 1 )",
623+
);
624+
625+
pg_and_generic().verified_stmt("ALTER TABLE t ALTER COLUMN id ADD GENERATED AS IDENTITY ( )");
626+
}
627+
614628
#[test]
615629
fn parse_alter_table_add_columns() {
616630
match pg().verified_stmt("ALTER TABLE IF EXISTS ONLY tab ADD COLUMN a TEXT, ADD COLUMN b INT") {

0 commit comments

Comments
 (0)