From f49c30feb6d5984f96cdc3a87dbf09b6b4b3ad04 Mon Sep 17 00:00:00 2001 From: carl <44021312+achristmascarl@users.noreply.github.com> Date: Wed, 23 Jul 2025 11:53:17 -0400 Subject: [PATCH] Postgres: Support parenthesized `SET` options for `ALTER TABLE` (#1947) Co-authored-by: Ifeanyi Ubah --- src/ast/ddl.rs | 13 +++++++++++++ src/ast/spans.rs | 3 +++ src/parser/mod.rs | 13 +++++++++---- tests/sqlparser_common.rs | 28 ++++++++++++++++++++++++++++ 4 files changed, 53 insertions(+), 4 deletions(-) diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs index 51e05784..92936a6f 100644 --- a/src/ast/ddl.rs +++ b/src/ast/ddl.rs @@ -351,6 +351,16 @@ pub enum AlterTableOperation { ValidateConstraint { name: Ident, }, + /// Arbitrary parenthesized `SET` options. + /// + /// Example: + /// ```sql + /// SET (scale_factor = 0.01, threshold = 500)` + /// ``` + /// [PostgreSQL](https://www.postgresql.org/docs/current/sql-altertable.html) + SetOptionsParens { + options: Vec, + }, } /// An `ALTER Policy` (`Statement::AlterPolicy`) operation @@ -791,6 +801,9 @@ impl fmt::Display for AlterTableOperation { AlterTableOperation::ValidateConstraint { name } => { write!(f, "VALIDATE CONSTRAINT {name}") } + AlterTableOperation::SetOptionsParens { options } => { + write!(f, "SET ({})", display_comma_separated(options)) + } } } } diff --git a/src/ast/spans.rs b/src/ast/spans.rs index 4deedca0..91523925 100644 --- a/src/ast/spans.rs +++ b/src/ast/spans.rs @@ -1202,6 +1202,9 @@ impl Spanned for AlterTableOperation { AlterTableOperation::Lock { .. } => Span::empty(), AlterTableOperation::ReplicaIdentity { .. } => Span::empty(), AlterTableOperation::ValidateConstraint { name } => name.span, + AlterTableOperation::SetOptionsParens { options } => { + union_spans(options.iter().map(|i| i.span())) + } } } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 5ac19d13..058423f1 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -9045,17 +9045,22 @@ impl<'a> Parser<'a> { let name = self.parse_identifier()?; AlterTableOperation::ValidateConstraint { name } } else { - let options: Vec = + let mut options = self.parse_options_with_keywords(&[Keyword::SET, Keyword::TBLPROPERTIES])?; if !options.is_empty() { AlterTableOperation::SetTblProperties { table_properties: options, } } else { - return self.expected( - "ADD, RENAME, PARTITION, SWAP, DROP, REPLICA IDENTITY, or SET TBLPROPERTIES after ALTER TABLE", + options = self.parse_options(Keyword::SET)?; + if !options.is_empty() { + AlterTableOperation::SetOptionsParens { options } + } else { + return self.expected( + "ADD, RENAME, PARTITION, SWAP, DROP, REPLICA IDENTITY, SET, or SET TBLPROPERTIES after ALTER TABLE", self.peek_token(), - ); + ); + } } }; Ok(operation) diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 5ae12ff4..074d9eab 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -4728,6 +4728,34 @@ fn parse_alter_table() { } _ => unreachable!(), } + + let set_storage_parameters = "ALTER TABLE tab SET (autovacuum_vacuum_scale_factor = 0.01, autovacuum_vacuum_threshold = 500)"; + match alter_table_op(verified_stmt(set_storage_parameters)) { + AlterTableOperation::SetOptionsParens { options } => { + assert_eq!( + options, + [ + SqlOption::KeyValue { + key: Ident { + value: "autovacuum_vacuum_scale_factor".to_string(), + quote_style: None, + span: Span::empty(), + }, + value: Expr::Value(test_utils::number("0.01").with_empty_span()), + }, + SqlOption::KeyValue { + key: Ident { + value: "autovacuum_vacuum_threshold".to_string(), + quote_style: None, + span: Span::empty(), + }, + value: Expr::Value(test_utils::number("500").with_empty_span()), + } + ], + ); + } + _ => unreachable!(), + } } #[test]