This is an automated email from the ASF dual-hosted git repository.

iffyio pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git


The following commit(s) were added to refs/heads/main by this push:
     new 418b9422 Postgres: support `ADD CONSTRAINT NOT VALID` and `VALIDATE 
CONSTRAINT` (#1908)
418b9422 is described below

commit 418b94227a5cf6ae721629f95a279c40b6a196ad
Author: carl <[email protected]>
AuthorDate: Thu Jul 3 12:19:26 2025 -0400

    Postgres: support `ADD CONSTRAINT NOT VALID` and `VALIDATE CONSTRAINT` 
(#1908)
---
 src/ast/ddl.rs              | 25 +++++++++++++++++++++---
 src/ast/spans.rs            |  6 +++++-
 src/keywords.rs             |  1 +
 src/parser/mod.rs           |  9 ++++++++-
 src/test_utils.rs           | 31 +++++++++++++++++-------------
 tests/sqlparser_common.rs   |  2 +-
 tests/sqlparser_postgres.rs | 47 ++++++++++++++++++++++++++++++++++++++++++---
 7 files changed, 99 insertions(+), 22 deletions(-)

diff --git a/src/ast/ddl.rs b/src/ast/ddl.rs
index 9134412c..9d500203 100644
--- a/src/ast/ddl.rs
+++ b/src/ast/ddl.rs
@@ -67,8 +67,11 @@ impl fmt::Display for ReplicaIdentity {
 #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
 #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
 pub enum AlterTableOperation {
-    /// `ADD <table_constraint>`
-    AddConstraint(TableConstraint),
+    /// `ADD <table_constraint> [NOT VALID]`
+    AddConstraint {
+        constraint: TableConstraint,
+        not_valid: bool,
+    },
     /// `ADD [COLUMN] [IF NOT EXISTS] <column_def>`
     AddColumn {
         /// `[COLUMN]`.
@@ -344,6 +347,10 @@ pub enum AlterTableOperation {
         equals: bool,
         value: ValueWithSpan,
     },
+    /// `VALIDATE CONSTRAINT <name>`
+    ValidateConstraint {
+        name: Ident,
+    },
 }
 
 /// An `ALTER Policy` (`Statement::AlterPolicy`) operation
@@ -494,7 +501,16 @@ impl fmt::Display for AlterTableOperation {
                 display_separated(new_partitions, " "),
                 ine = if *if_not_exists { " IF NOT EXISTS" } else { "" }
             ),
-            AlterTableOperation::AddConstraint(c) => write!(f, "ADD {c}"),
+            AlterTableOperation::AddConstraint {
+                not_valid,
+                constraint,
+            } => {
+                write!(f, "ADD {constraint}")?;
+                if *not_valid {
+                    write!(f, " NOT VALID")?;
+                }
+                Ok(())
+            }
             AlterTableOperation::AddColumn {
                 column_keyword,
                 if_not_exists,
@@ -772,6 +788,9 @@ impl fmt::Display for AlterTableOperation {
             AlterTableOperation::ReplicaIdentity { identity } => {
                 write!(f, "REPLICA IDENTITY {identity}")
             }
+            AlterTableOperation::ValidateConstraint { name } => {
+                write!(f, "VALIDATE CONSTRAINT {name}")
+            }
         }
     }
 }
diff --git a/src/ast/spans.rs b/src/ast/spans.rs
index 00882602..26205496 100644
--- a/src/ast/spans.rs
+++ b/src/ast/spans.rs
@@ -1075,7 +1075,10 @@ impl Spanned for CreateTableOptions {
 impl Spanned for AlterTableOperation {
     fn span(&self) -> Span {
         match self {
-            AlterTableOperation::AddConstraint(table_constraint) => 
table_constraint.span(),
+            AlterTableOperation::AddConstraint {
+                constraint,
+                not_valid: _,
+            } => constraint.span(),
             AlterTableOperation::AddColumn {
                 column_keyword: _,
                 if_not_exists: _,
@@ -1196,6 +1199,7 @@ impl Spanned for AlterTableOperation {
             AlterTableOperation::AutoIncrement { value, .. } => value.span(),
             AlterTableOperation::Lock { .. } => Span::empty(),
             AlterTableOperation::ReplicaIdentity { .. } => Span::empty(),
+            AlterTableOperation::ValidateConstraint { name } => name.span,
         }
     }
 }
diff --git a/src/keywords.rs b/src/keywords.rs
index a8bbca3d..49a54e8a 100644
--- a/src/keywords.rs
+++ b/src/keywords.rs
@@ -981,6 +981,7 @@ define_keywords!(
     UUID,
     VACUUM,
     VALID,
+    VALIDATE,
     VALIDATION_MODE,
     VALUE,
     VALUES,
diff --git a/src/parser/mod.rs b/src/parser/mod.rs
index 6360817f..1c40a964 100644
--- a/src/parser/mod.rs
+++ b/src/parser/mod.rs
@@ -8477,7 +8477,11 @@ impl<'a> Parser<'a> {
     pub fn parse_alter_table_operation(&mut self) -> 
Result<AlterTableOperation, ParserError> {
         let operation = if self.parse_keyword(Keyword::ADD) {
             if let Some(constraint) = self.parse_optional_table_constraint()? {
-                AlterTableOperation::AddConstraint(constraint)
+                let not_valid = self.parse_keywords(&[Keyword::NOT, 
Keyword::VALID]);
+                AlterTableOperation::AddConstraint {
+                    constraint,
+                    not_valid,
+                }
             } else if dialect_of!(self is ClickHouseDialect|GenericDialect)
                 && self.parse_keyword(Keyword::PROJECTION)
             {
@@ -8886,6 +8890,9 @@ impl<'a> Parser<'a> {
             };
 
             AlterTableOperation::ReplicaIdentity { identity }
+        } else if self.parse_keywords(&[Keyword::VALIDATE, 
Keyword::CONSTRAINT]) {
+            let name = self.parse_identifier()?;
+            AlterTableOperation::ValidateConstraint { name }
         } else {
             let options: Vec<SqlOption> =
                 self.parse_options_with_keywords(&[Keyword::SET, 
Keyword::TBLPROPERTIES])?;
diff --git a/src/test_utils.rs b/src/test_utils.rs
index db7b3dd6..544ceaef 100644
--- a/src/test_utils.rs
+++ b/src/test_utils.rs
@@ -479,20 +479,25 @@ pub fn index_column(stmt: Statement) -> Expr {
             }
         }
         Statement::AlterTable { operations, .. } => match 
operations.first().unwrap() {
-            AlterTableOperation::AddConstraint(TableConstraint::Index { 
columns, .. }) => {
-                columns.first().unwrap().column.expr.clone()
-            }
-            AlterTableOperation::AddConstraint(TableConstraint::Unique { 
columns, .. }) => {
-                columns.first().unwrap().column.expr.clone()
-            }
-            AlterTableOperation::AddConstraint(TableConstraint::PrimaryKey { 
columns, .. }) => {
-                columns.first().unwrap().column.expr.clone()
+            AlterTableOperation::AddConstraint { constraint, .. } => {
+                match constraint {
+                    TableConstraint::Index { columns, .. } => {
+                        columns.first().unwrap().column.expr.clone()
+                    }
+                    TableConstraint::Unique { columns, .. } => {
+                        columns.first().unwrap().column.expr.clone()
+                    }
+                    TableConstraint::PrimaryKey { columns, .. } => {
+                        columns.first().unwrap().column.expr.clone()
+                    }
+                    TableConstraint::FulltextOrSpatial {
+                        columns,
+                        ..
+                    } => columns.first().unwrap().column.expr.clone(),
+                    _ => panic!("Expected an index, unique, primary, full 
text, or spatial constraint (foreign key does not support general key part 
expressions)"),
+                }
             }
-            
AlterTableOperation::AddConstraint(TableConstraint::FulltextOrSpatial {
-                columns,
-                ..
-            }) => columns.first().unwrap().column.expr.clone(),
-            _ => panic!("Expected an index, unique, primary, full text, or 
spatial constraint (foreign key does not support general key part 
expressions)"),
+            _ => panic!("Expected a constraint"),
         },
         _ => panic!("Expected CREATE INDEX, ALTER TABLE, or CREATE TABLE, got: 
{stmt:?}"),
     }
diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs
index 380bd475..9ca985b3 100644
--- a/tests/sqlparser_common.rs
+++ b/tests/sqlparser_common.rs
@@ -4956,7 +4956,7 @@ fn parse_alter_table_constraints() {
         match alter_table_op(verified_stmt(&format!(
             "ALTER TABLE tab ADD {constraint_text}"
         ))) {
-            AlterTableOperation::AddConstraint(constraint) => {
+            AlterTableOperation::AddConstraint { constraint, .. } => {
                 assert_eq!(constraint_text, constraint.to_string());
             }
             _ => unreachable!(),
diff --git a/tests/sqlparser_postgres.rs b/tests/sqlparser_postgres.rs
index e1a49c69..16ba5f23 100644
--- a/tests/sqlparser_postgres.rs
+++ b/tests/sqlparser_postgres.rs
@@ -606,9 +606,10 @@ fn parse_alter_table_constraints_unique_nulls_distinct() {
         .verified_stmt("ALTER TABLE t ADD CONSTRAINT b UNIQUE NULLS NOT 
DISTINCT (c)")
     {
         Statement::AlterTable { operations, .. } => match &operations[0] {
-            AlterTableOperation::AddConstraint(TableConstraint::Unique {
-                nulls_distinct, ..
-            }) => {
+            AlterTableOperation::AddConstraint {
+                constraint: TableConstraint::Unique { nulls_distinct, .. },
+                ..
+            } => {
                 assert_eq!(nulls_distinct, &NullsDistinctOption::NotDistinct)
             }
             _ => unreachable!(),
@@ -6229,3 +6230,43 @@ fn parse_ts_datatypes() {
         _ => unreachable!(),
     }
 }
+
+#[test]
+fn parse_alter_table_constraint_not_valid() {
+    match pg_and_generic().verified_stmt(
+        "ALTER TABLE foo ADD CONSTRAINT bar FOREIGN KEY (baz) REFERENCES 
other(ref) NOT VALID",
+    ) {
+        Statement::AlterTable { operations, .. } => {
+            assert_eq!(
+                operations,
+                vec![AlterTableOperation::AddConstraint {
+                    constraint: TableConstraint::ForeignKey {
+                        name: Some("bar".into()),
+                        index_name: None,
+                        columns: vec!["baz".into()],
+                        foreign_table: ObjectName::from(vec!["other".into()]),
+                        referred_columns: vec!["ref".into()],
+                        on_delete: None,
+                        on_update: None,
+                        characteristics: None,
+                    },
+                    not_valid: true,
+                }]
+            );
+        }
+        _ => unreachable!(),
+    }
+}
+
+#[test]
+fn parse_alter_table_validate_constraint() {
+    match pg_and_generic().verified_stmt("ALTER TABLE foo VALIDATE CONSTRAINT 
bar") {
+        Statement::AlterTable { operations, .. } => {
+            assert_eq!(
+                operations,
+                vec![AlterTableOperation::ValidateConstraint { name: 
"bar".into() }]
+            );
+        }
+        _ => unreachable!(),
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to