STET

agent_patch

runs/2026-02-28__18-27-22__gpt-5-3-codex/flux-pr-1908/flux-pr-1908.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/agent-logs/agent.patch

12287 bytes

Back to adjudication
diff --git a/tmp/agent-patch-flux-pr-1908.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/ast/ddl.rs b/app/src/ast/ddl.rs
index 9134412..bc5dad2 100644
--- a/tmp/agent-patch-flux-pr-1908.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/ast/ddl.rs
+++ b/app/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 {
+        table_constraint: TableConstraint,
+        not_valid: bool,
+    },
     /// `ADD [COLUMN] [IF NOT EXISTS] <column_def>`
     AddColumn {
         /// `[COLUMN]`.
@@ -308,6 +311,12 @@ pub enum AlterTableOperation {
     OwnerTo {
         new_owner: Owner,
     },
+    /// `VALIDATE CONSTRAINT <name>`
+    ///
+    /// Note: this is PostgreSQL-specific <https://www.postgresql.org/docs/current/sql-altertable.html>
+    ValidateConstraint {
+        name: Ident,
+    },
     /// Snowflake table clustering options
     /// <https://docs.snowflake.com/en/sql-reference/sql/alter-table#clustering-actions-clusteringaction>
     ClusterBy {
@@ -494,7 +503,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 {
+                table_constraint,
+                not_valid,
+            } => {
+                write!(f, "ADD {table_constraint}")?;
+                if *not_valid {
+                    write!(f, " NOT VALID")?;
+                }
+                Ok(())
+            }
             AlterTableOperation::AddColumn {
                 column_keyword,
                 if_not_exists,
@@ -715,6 +733,9 @@ impl fmt::Display for AlterTableOperation {
             AlterTableOperation::OwnerTo { new_owner } => {
                 write!(f, "OWNER TO {new_owner}")
             }
+            AlterTableOperation::ValidateConstraint { name } => {
+                write!(f, "VALIDATE CONSTRAINT {name}")
+            }
             AlterTableOperation::SetTblProperties { table_properties } => {
                 write!(
                     f,
diff --git a/tmp/agent-patch-flux-pr-1908.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/ast/spans.rs b/app/src/ast/spans.rs
index 0088260..319ed0f 100644
--- a/tmp/agent-patch-flux-pr-1908.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/ast/spans.rs
+++ b/app/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 {
+                table_constraint,
+                not_valid: _,
+            } => table_constraint.span(),
             AlterTableOperation::AddColumn {
                 column_keyword: _,
                 if_not_exists: _,
@@ -1188,6 +1191,7 @@ impl Spanned for AlterTableOperation {
                 union_spans(table_properties.iter().map(|i| i.span()))
             }
             AlterTableOperation::OwnerTo { .. } => Span::empty(),
+            AlterTableOperation::ValidateConstraint { name } => name.span,
             AlterTableOperation::ClusterBy { exprs } => union_spans(exprs.iter().map(|e| e.span())),
             AlterTableOperation::DropClusteringKey => Span::empty(),
             AlterTableOperation::SuspendRecluster => Span::empty(),
diff --git a/tmp/agent-patch-flux-pr-1908.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/keywords.rs b/app/src/keywords.rs
index a8bbca3..49a54e8 100644
--- a/tmp/agent-patch-flux-pr-1908.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/keywords.rs
+++ b/app/src/keywords.rs
@@ -981,6 +981,7 @@ define_keywords!(
     UUID,
     VACUUM,
     VALID,
+    VALIDATE,
     VALIDATION_MODE,
     VALUE,
     VALUES,
diff --git a/tmp/agent-patch-flux-pr-1908.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/parser/mod.rs b/app/src/parser/mod.rs
index 6360817..e6d0a1e 100644
--- a/tmp/agent-patch-flux-pr-1908.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/parser/mod.rs
+++ b/app/src/parser/mod.rs
@@ -8477,7 +8477,15 @@ 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 = if dialect_of!(self is PostgreSqlDialect | GenericDialect) {
+                    self.parse_keywords(&[Keyword::NOT, Keyword::VALID])
+                } else {
+                    false
+                };
+                AlterTableOperation::AddConstraint {
+                    table_constraint: constraint,
+                    not_valid,
+                }
             } else if dialect_of!(self is ClickHouseDialect|GenericDialect)
                 && self.parse_keyword(Keyword::PROJECTION)
             {
@@ -8780,6 +8788,11 @@ impl<'a> Parser<'a> {
         {
             let new_owner = self.parse_owner()?;
             AlterTableOperation::OwnerTo { new_owner }
+        } else if dialect_of!(self is PostgreSqlDialect | GenericDialect)
+            && self.parse_keywords(&[Keyword::VALIDATE, Keyword::CONSTRAINT])
+        {
+            let name = self.parse_identifier()?;
+            AlterTableOperation::ValidateConstraint { name }
         } else if dialect_of!(self is ClickHouseDialect|GenericDialect)
             && self.parse_keyword(Keyword::ATTACH)
         {
@@ -8895,7 +8908,7 @@ impl<'a> Parser<'a> {
                 }
             } else {
                 return self.expected(
-                    "ADD, RENAME, PARTITION, SWAP, DROP, REPLICA IDENTITY, or SET TBLPROPERTIES after ALTER TABLE",
+                    "ADD, RENAME, PARTITION, SWAP, DROP, VALIDATE CONSTRAINT, REPLICA IDENTITY, or SET TBLPROPERTIES after ALTER TABLE",
                     self.peek_token(),
                 );
             }
diff --git a/tmp/agent-patch-flux-pr-1908.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/test_utils.rs b/app/src/test_utils.rs
index db7b3dd..e7ebd0b 100644
--- a/tmp/agent-patch-flux-pr-1908.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/test_utils.rs
+++ b/app/src/test_utils.rs
@@ -479,19 +479,28 @@ pub fn index_column(stmt: Statement) -> Expr {
             }
         }
         Statement::AlterTable { operations, .. } => match operations.first().unwrap() {
-            AlterTableOperation::AddConstraint(TableConstraint::Index { columns, .. }) => {
+            AlterTableOperation::AddConstraint {
+                table_constraint: TableConstraint::Index { columns, .. },
+                ..
+            } => {
                 columns.first().unwrap().column.expr.clone()
             }
-            AlterTableOperation::AddConstraint(TableConstraint::Unique { columns, .. }) => {
+            AlterTableOperation::AddConstraint {
+                table_constraint: TableConstraint::Unique { columns, .. },
+                ..
+            } => {
                 columns.first().unwrap().column.expr.clone()
             }
-            AlterTableOperation::AddConstraint(TableConstraint::PrimaryKey { columns, .. }) => {
+            AlterTableOperation::AddConstraint {
+                table_constraint: TableConstraint::PrimaryKey { columns, .. },
+                ..
+            } => {
                 columns.first().unwrap().column.expr.clone()
             }
-            AlterTableOperation::AddConstraint(TableConstraint::FulltextOrSpatial {
-                columns,
+            AlterTableOperation::AddConstraint {
+                table_constraint: TableConstraint::FulltextOrSpatial { columns, .. },
                 ..
-            }) => columns.first().unwrap().column.expr.clone(),
+            } => 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 CREATE INDEX, ALTER TABLE, or CREATE TABLE, got: {stmt:?}"),
diff --git a/tmp/agent-patch-flux-pr-1908.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/tests/sqlparser_common.rs b/app/tests/sqlparser_common.rs
index 380bd47..223b0e3 100644
--- a/tmp/agent-patch-flux-pr-1908.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/tests/sqlparser_common.rs
+++ b/app/tests/sqlparser_common.rs
@@ -4956,7 +4956,10 @@ fn parse_alter_table_constraints() {
         match alter_table_op(verified_stmt(&format!(
             "ALTER TABLE tab ADD {constraint_text}"
         ))) {
-            AlterTableOperation::AddConstraint(constraint) => {
+            AlterTableOperation::AddConstraint {
+                table_constraint: constraint,
+                not_valid: false,
+            } => {
                 assert_eq!(constraint_text, constraint.to_string());
             }
             _ => unreachable!(),
diff --git a/tmp/agent-patch-flux-pr-1908.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/tests/sqlparser_postgres.rs b/app/tests/sqlparser_postgres.rs
index e1a49c6..6b4eded 100644
--- a/tmp/agent-patch-flux-pr-1908.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/tests/sqlparser_postgres.rs
+++ b/app/tests/sqlparser_postgres.rs
@@ -606,9 +606,13 @@ 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 {
+                table_constraint:
+                    TableConstraint::Unique {
+                        nulls_distinct, ..
+                    },
+                not_valid: false,
+            } => {
                 assert_eq!(nulls_distinct, &NullsDistinctOption::NotDistinct)
             }
             _ => unreachable!(),
@@ -619,6 +623,47 @@ fn parse_alter_table_constraints_unique_nulls_distinct() {
     pg_and_generic().verified_stmt("ALTER TABLE t ADD CONSTRAINT b UNIQUE (c)");
 }
 
+#[test]
+fn parse_alter_table_add_constraint_not_valid() {
+    match pg_and_generic().verified_stmt(
+        "ALTER TABLE t ADD CONSTRAINT fk FOREIGN KEY (x) REFERENCES other_t (id) NOT VALID",
+    ) {
+        Statement::AlterTable { operations, .. } => match &operations[0] {
+            AlterTableOperation::AddConstraint {
+                table_constraint:
+                    TableConstraint::ForeignKey {
+                        name,
+                        columns,
+                        foreign_table,
+                        referred_columns,
+                        ..
+                    },
+                not_valid: true,
+            } => {
+                assert_eq!(name.as_ref().map(ToString::to_string), Some("fk".to_string()));
+                assert_eq!(columns, &vec!["x".into()]);
+                assert_eq!(foreign_table, &ObjectName(vec!["other_t".into()]));
+                assert_eq!(referred_columns, &vec!["id".into()]);
+            }
+            _ => unreachable!(),
+        },
+        _ => unreachable!(),
+    }
+}
+
+#[test]
+fn parse_alter_table_validate_constraint() {
+    match pg_and_generic().verified_stmt("ALTER TABLE t VALIDATE CONSTRAINT fk") {
+        Statement::AlterTable { operations, .. } => {
+            assert_eq!(
+                operations,
+                vec![AlterTableOperation::ValidateConstraint { name: "fk".into() }]
+            );
+        }
+        _ => unreachable!(),
+    }
+}
+
 #[test]
 fn parse_alter_table_disable() {
     pg_and_generic().verified_stmt("ALTER TABLE tab DISABLE ROW LEVEL SECURITY");