From de8f0c3c2ca94a5cabe4e183e8d917b883cb1c37 Mon Sep 17 00:00:00 2001 From: MohamedAbdeen21 Date: Wed, 7 May 2025 20:52:46 +0100 Subject: [PATCH 1/3] Add psql alter table operation REPLICA IDENTITY --- src/ast/ddl.rs | 30 ++++++++++++++++++++++++++++++ src/ast/mod.rs | 2 +- src/ast/spans.rs | 1 + src/parser/mod.rs | 19 ++++++++++++++++++- 4 files changed, 50 insertions(+), 2 deletions(-) diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs index a457a0655..74a289947 100644 --- a/src/ast/ddl.rs +++ b/src/ast/ddl.rs @@ -39,6 +39,27 @@ use crate::ast::{ use crate::keywords::Keyword; use crate::tokenizer::Token; +#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] +pub enum ReplicaIdentity { + None, + Full, + Default, + Index(Ident), +} + +impl fmt::Display for ReplicaIdentity { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + ReplicaIdentity::None => f.write_str("NONE"), + ReplicaIdentity::Full => f.write_str("FULL"), + ReplicaIdentity::Default => f.write_str("DEFAULT"), + ReplicaIdentity::Index(idx) => write!(f, "USING INDEX {}", idx), + } + } +} + /// An `ALTER TABLE` (`Statement::AlterTable`) operation #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -208,6 +229,12 @@ pub enum AlterTableOperation { old_partitions: Vec, new_partitions: Vec, }, + /// REPLICA IDENTITY { DEFAULT | USING INDEX index_name | FULL | NOTHING } + /// + /// Note: this is a PostgreSQL-specific operation. + ReplicaIdentity { + identity: ReplicaIdentity, + }, /// Add Partitions AddPartitions { if_not_exists: bool, @@ -729,6 +756,9 @@ impl fmt::Display for AlterTableOperation { AlterTableOperation::Lock { equals, lock } => { write!(f, "LOCK {}{}", if *equals { "= " } else { "" }, lock) } + AlterTableOperation::ReplicaIdentity { identity } => { + write!(f, "REPLICA IDENTITY {identity}") + } } } } diff --git a/src/ast/mod.rs b/src/ast/mod.rs index a72cb8add..dd77a3869 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -59,7 +59,7 @@ pub use self::ddl::{ DeferrableInitial, DropBehavior, GeneratedAs, GeneratedExpressionMode, IdentityParameters, IdentityProperty, IdentityPropertyFormatKind, IdentityPropertyKind, IdentityPropertyOrder, IndexOption, IndexType, KeyOrIndexDisplay, NullsDistinctOption, Owner, Partition, - ProcedureParam, ReferentialAction, TableConstraint, TagsColumnOption, + ProcedureParam, ReferentialAction, ReplicaIdentity, TableConstraint, TagsColumnOption, UserDefinedTypeCompositeAttributeDef, UserDefinedTypeRepresentation, ViewColumnDef, }; pub use self::dml::{CreateIndex, CreateTable, Delete, IndexColumn, Insert}; diff --git a/src/ast/spans.rs b/src/ast/spans.rs index ff2a61cf3..44230c70e 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -1174,6 +1174,7 @@ impl Spanned for AlterTableOperation { AlterTableOperation::Algorithm { .. } => Span::empty(), AlterTableOperation::AutoIncrement { value, .. } => value.span(), AlterTableOperation::Lock { .. } => Span::empty(), + AlterTableOperation::ReplicaIdentity { .. } => Span::empty(), } } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index fc6f44376..128f02c09 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -8770,6 +8770,23 @@ impl<'a> Parser<'a> { let equals = self.consume_token(&Token::Eq); let value = self.parse_number_value()?; AlterTableOperation::AutoIncrement { equals, value } + } else if self.parse_keywords(&[Keyword::REPLICA, Keyword::IDENTITY]) { + let identity = if self.parse_keyword(Keyword::NONE) { + ReplicaIdentity::None + } else if self.parse_keyword(Keyword::FULL) { + ReplicaIdentity::Full + } else if self.parse_keyword(Keyword::DEFAULT) { + ReplicaIdentity::Default + } else if self.parse_keywords(&[Keyword::USING, Keyword::INDEX]) { + ReplicaIdentity::Index(self.parse_identifier()?) + } else { + return self.expected( + "NONE, FULL, DEFAULT, or USING INDEX index_name after REPLICA IDENTITY", + self.peek_token(), + ); + }; + + AlterTableOperation::ReplicaIdentity { identity } } else { let options: Vec = self.parse_options_with_keywords(&[Keyword::SET, Keyword::TBLPROPERTIES])?; @@ -8779,7 +8796,7 @@ impl<'a> Parser<'a> { } } else { return self.expected( - "ADD, RENAME, PARTITION, SWAP, DROP, or SET TBLPROPERTIES after ALTER TABLE", + "ADD, RENAME, PARTITION, SWAP, DROP, REPLICA IDENTITY, or SET TBLPROPERTIES after ALTER TABLE", self.peek_token(), ); } From a675e080810f1c50732c93cf1b76bcdcc0f748d8 Mon Sep 17 00:00:00 2001 From: MohamedAbdeen21 Date: Wed, 7 May 2025 21:16:40 +0100 Subject: [PATCH 2/3] Add tests --- tests/sqlparser_postgres.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs index 859eca453..9e71883cc 100644 --- a/tests/sqlparser_postgres.rs +++ b/tests/sqlparser_postgres.rs @@ -5959,3 +5959,30 @@ fn parse_varbit_datatype() { _ => unreachable!(), } } + +#[test] +fn parse_alter_table_replica_identity() { + match pg_and_generic().verified_stmt("ALTER TABLE foo REPLICA IDENTITY FULL") { + Statement::AlterTable { operations, .. } => { + assert_eq!( + operations, + vec![AlterTableOperation::ReplicaIdentity { + identity: ReplicaIdentity::Full + }] + ); + } + _ => unreachable!(), + } + + match pg_and_generic().verified_stmt("ALTER TABLE foo REPLICA IDENTITY USING INDEX foo_idx") { + Statement::AlterTable { operations, .. } => { + assert_eq!( + operations, + vec![AlterTableOperation::ReplicaIdentity { + identity: ReplicaIdentity::Index("foo_idx".into()) + }] + ); + } + _ => unreachable!(), + } +} From c959ef9ee99c6aca73c073853bba2e586ae1dfa1 Mon Sep 17 00:00:00 2001 From: MohamedAbdeen21 Date: Sun, 11 May 2025 18:37:36 +0100 Subject: [PATCH 3/3] Add documentation for REPLICA IDENTITY enum --- src/ast/ddl.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs index 74a289947..270897130 100644 --- a/src/ast/ddl.rs +++ b/src/ast/ddl.rs @@ -39,6 +39,8 @@ use crate::ast::{ use crate::keywords::Keyword; use crate::tokenizer::Token; +/// ALTER TABLE operation REPLICA IDENTITY values +/// See [Postgres ALTER TABLE docs](https://www.postgresql.org/docs/current/sql-altertable.html) #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] @@ -232,6 +234,7 @@ pub enum AlterTableOperation { /// REPLICA IDENTITY { DEFAULT | USING INDEX index_name | FULL | NOTHING } /// /// Note: this is a PostgreSQL-specific operation. + /// Please refer to [PostgreSQL documentation](https://www.postgresql.org/docs/current/sql-altertable.html) ReplicaIdentity { identity: ReplicaIdentity, },