STET

agent_patch

runs/2026-02-28__18-27-22__gpt-5-4/flux-pr-1791/flux-pr-1791.1-of-1.2026-02-28__18-27-22__gpt-5-4/agent-logs/agent.patch

12530 bytes

Back to adjudication
diff --git a/tmp/agent-patch-flux-pr-1791.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/ast/mod.rs b/app/src/ast/mod.rs
index a6dc682..4509daa 100644
--- a/tmp/agent-patch-flux-pr-1791.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/ast/mod.rs
+++ b/app/src/ast/mod.rs
@@ -92,6 +92,7 @@ pub use self::value::{
 
 use crate::ast::helpers::key_value_options::KeyValueOptions;
 use crate::ast::helpers::stmt_data_loading::{StageLoadSelectItem, StageParamsObject};
+use crate::keywords::Keyword;
 
 #[cfg(feature = "visitor")]
 pub use visitor::*;
@@ -2121,8 +2122,8 @@ pub struct CaseStatement {
     pub match_expr: Option<Expr>,
     pub when_blocks: Vec<ConditionalStatements>,
     pub else_block: Option<Vec<Statement>>,
-    /// TRUE if the statement ends with `END CASE` (vs `END`).
-    pub has_end_case: bool,
+    /// Optional keyword after `END`, such as `CASE`.
+    pub end_token: Option<Keyword>,
 }
 
 impl fmt::Display for CaseStatement {
@@ -2131,7 +2132,7 @@ impl fmt::Display for CaseStatement {
             match_expr,
             when_blocks,
             else_block,
-            has_end_case,
+            end_token,
         } = self;
 
         write!(f, "CASE")?;
@@ -2150,8 +2151,8 @@ impl fmt::Display for CaseStatement {
         }
 
         write!(f, " END")?;
-        if *has_end_case {
-            write!(f, " CASE")?;
+        if let Some(end_token) = end_token {
+            write!(f, " {end_token}")?;
         }
 
         Ok(())
@@ -2181,6 +2182,8 @@ pub struct IfStatement {
     pub if_block: ConditionalStatements,
     pub elseif_blocks: Vec<ConditionalStatements>,
     pub else_block: Option<Vec<Statement>>,
+    /// Optional keyword after `END`, such as `IF`.
+    pub end_token: Option<Keyword>,
 }
 
 impl fmt::Display for IfStatement {
@@ -2189,6 +2192,7 @@ impl fmt::Display for IfStatement {
             if_block,
             elseif_blocks,
             else_block,
+            end_token,
         } = self;
 
         write!(f, "{if_block}")?;
@@ -2202,7 +2206,9 @@ impl fmt::Display for IfStatement {
             format_statement_list(f, else_block)?;
         }
 
-        write!(f, " END IF")?;
+        if let Some(end_token) = end_token {
+            write!(f, " END {end_token}")?;
+        }
 
         Ok(())
     }
@@ -2234,18 +2240,21 @@ pub enum ConditionalStatementKind {
 #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
 pub struct ConditionalStatements {
     /// The condition expression.
-    pub condition: Expr,
-    /// Statement list of the `THEN` clause.
+    pub condition: Option<Expr>,
+    /// Statement list of the conditional body.
     pub statements: Vec<Statement>,
     pub kind: ConditionalStatementKind,
+    /// TRUE if the conditional body is introduced by `THEN`.
+    pub has_then_keyword: bool,
 }
 
 impl fmt::Display for ConditionalStatements {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         let ConditionalStatements {
-            condition: expr,
+            condition,
             statements,
             kind,
+            has_then_keyword,
         } = self;
 
         let kind = match kind {
@@ -2254,7 +2263,15 @@ impl fmt::Display for ConditionalStatements {
             ConditionalStatementKind::ElseIf => "ELSEIF",
         };
 
-        write!(f, "{kind} {expr} THEN")?;
+        write!(f, "{kind}")?;
+
+        if let Some(expr) = condition {
+            write!(f, " {expr}")?;
+        }
+
+        if *has_then_keyword {
+            write!(f, " THEN")?;
+        }
 
         if !statements.is_empty() {
             write!(f, " ")?;
diff --git a/tmp/agent-patch-flux-pr-1791.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/ast/spans.rs b/app/src/ast/spans.rs
index 11770d1..986a1f2 100644
--- a/tmp/agent-patch-flux-pr-1791.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/ast/spans.rs
+++ b/app/src/ast/spans.rs
@@ -742,7 +742,7 @@ impl Spanned for CaseStatement {
             match_expr,
             when_blocks,
             else_block,
-            has_end_case: _,
+            end_token: _,
         } = self;
 
         union_spans(
@@ -761,6 +761,7 @@ impl Spanned for IfStatement {
             if_block,
             elseif_blocks,
             else_block,
+            end_token: _,
         } = self;
 
         union_spans(
@@ -777,9 +778,15 @@ impl Spanned for ConditionalStatements {
             condition,
             statements,
             kind: _,
+            has_then_keyword: _,
         } = self;
 
-        union_spans(iter::once(condition.span()).chain(statements.iter().map(|s| s.span())))
+        union_spans(
+            condition
+                .iter()
+                .map(|expr| expr.span())
+                .chain(statements.iter().map(|s| s.span())),
+        )
     }
 }
 
diff --git a/tmp/agent-patch-flux-pr-1791.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/parser/mod.rs b/app/src/parser/mod.rs
index 5495499..0ef7d50 100644
--- a/tmp/agent-patch-flux-pr-1791.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/src/parser/mod.rs
+++ b/app/src/parser/mod.rs
@@ -654,13 +654,13 @@ impl<'a> Parser<'a> {
         };
 
         self.expect_keyword_is(Keyword::END)?;
-        let has_end_case = self.parse_keyword(Keyword::CASE);
+        let end_token = self.parse_one_of_keywords(&[Keyword::CASE]);
 
         Ok(Statement::Case(CaseStatement {
             match_expr,
             when_blocks,
             else_block,
-            has_end_case,
+            end_token,
         }))
     }
 
@@ -669,6 +669,11 @@ impl<'a> Parser<'a> {
     /// See [Statement::If]
     pub fn parse_if_stmt(&mut self) -> Result<Statement, ParserError> {
         self.expect_keyword_is(Keyword::IF)?;
+
+        if dialect_of!(self is MsSqlDialect) {
+            return self.parse_mssql_if_stmt();
+        }
+
         let if_block = self.parse_conditional_statements(
             ConditionalStatementKind::If,
             &[Keyword::ELSE, Keyword::ELSEIF, Keyword::END],
@@ -697,6 +702,7 @@ impl<'a> Parser<'a> {
             if_block,
             elseif_blocks,
             else_block,
+            end_token: Some(Keyword::IF),
         }))
     }
 
@@ -712,7 +718,7 @@ impl<'a> Parser<'a> {
         kind: ConditionalStatementKind,
         terminal_keywords: &[Keyword],
     ) -> Result<ConditionalStatements, ParserError> {
-        let condition = self.parse_expr()?;
+        let condition = Some(self.parse_expr()?);
         self.expect_keyword_is(Keyword::THEN)?;
         let statements = self.parse_statement_list(terminal_keywords)?;
 
@@ -720,6 +726,79 @@ impl<'a> Parser<'a> {
             condition,
             statements,
             kind,
+            has_then_keyword: true,
+        })
+    }
+
+    fn parse_mssql_if_stmt(&mut self) -> Result<Statement, ParserError> {
+        let if_block = self.parse_mssql_conditional_statements(ConditionalStatementKind::If)?;
+
+        let else_block = if self.parse_keyword(Keyword::ELSE) {
+            Some(self.parse_mssql_conditional_body()?)
+        } else {
+            None
+        };
+
+        Ok(Statement::If(IfStatement {
+            if_block,
+            elseif_blocks: vec![],
+            else_block,
+            end_token: None,
+        }))
+    }
+
+    fn parse_mssql_conditional_statements(
+        &mut self,
+        kind: ConditionalStatementKind,
+    ) -> Result<ConditionalStatements, ParserError> {
+        let condition = Some(self.parse_expr()?);
+        let statements = self.parse_mssql_conditional_body()?;
+
+        Ok(ConditionalStatements {
+            condition,
+            statements,
+            kind,
+            has_then_keyword: false,
+        })
+    }
+
+    fn parse_mssql_conditional_body(&mut self) -> Result<Vec<Statement>, ParserError> {
+        if self.peek_keyword(Keyword::BEGIN)
+            && !matches!(
+                self.peek_nth_token_ref(1).token,
+                Token::Word(ref w)
+                    if matches!(
+                        w.keyword,
+                        Keyword::TRANSACTION
+                            | Keyword::WORK
+                            | Keyword::TRY
+                            | Keyword::CATCH
+                            | Keyword::DEFERRED
+                            | Keyword::IMMEDIATE
+                            | Keyword::EXCLUSIVE
+                    )
+            )
+        {
+            return Ok(vec![self.parse_begin_end_block()?]);
+        }
+
+        let statement = self.parse_statement()?;
+        self.consume_token(&Token::SemiColon);
+        Ok(vec![statement])
+    }
+
+    fn parse_begin_end_block(&mut self) -> Result<Statement, ParserError> {
+        self.expect_keyword_is(Keyword::BEGIN)?;
+        let statements = self.parse_statements()?;
+        self.expect_keyword_is(Keyword::END)?;
+        Ok(Statement::StartTransaction {
+            modes: vec![],
+            begin: true,
+            transaction: None,
+            modifier: None,
+            statements,
+            exception_statements: None,
+            has_end_keyword: true,
         })
     }
 
diff --git a/tmp/agent-patch-flux-pr-1791.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_common.rs b/app/tests/sqlparser_common.rs
index 3653321..ec7a646 100644
--- a/tmp/agent-patch-flux-pr-1791.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_common.rs
+++ b/app/tests/sqlparser_common.rs
@@ -14229,9 +14229,11 @@ fn parse_case_statement() {
     };
 
     assert_eq!(Some(Expr::value(number("1"))), stmt.match_expr);
-    assert_eq!(Expr::value(number("2")), stmt.when_blocks[0].condition);
+    assert_eq!(Some(Expr::value(number("2"))), stmt.when_blocks[0].condition);
     assert_eq!(2, stmt.when_blocks[0].statements.len());
+    assert!(stmt.when_blocks[0].has_then_keyword);
     assert_eq!(1, stmt.else_block.unwrap().len());
+    assert_eq!(Some(Keyword::CASE), stmt.end_token);
 
     verified_stmt(concat!(
         "CASE 1",
@@ -14280,9 +14282,11 @@ fn parse_if_statement() {
     let Statement::If(stmt) = verified_stmt(sql) else {
         unreachable!()
     };
-    assert_eq!(Expr::value(number("1")), stmt.if_block.condition);
-    assert_eq!(Expr::value(number("2")), stmt.elseif_blocks[0].condition);
+    assert_eq!(Some(Expr::value(number("1"))), stmt.if_block.condition);
+    assert_eq!(Some(Expr::value(number("2"))), stmt.elseif_blocks[0].condition);
+    assert!(stmt.if_block.has_then_keyword);
     assert_eq!(1, stmt.else_block.unwrap().len());
+    assert_eq!(Some(Keyword::IF), stmt.end_token);
 
     verified_stmt(concat!(
         "IF 1 THEN",
diff --git a/tmp/agent-patch-flux-pr-1791.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_mssql.rs b/app/tests/sqlparser_mssql.rs
index 5d76fd0..5a5ab89 100644
--- a/tmp/agent-patch-flux-pr-1791.1-of-1.2026-02-28__18-27-22__gpt-5-4/app/tests/sqlparser_mssql.rs
+++ b/app/tests/sqlparser_mssql.rs
@@ -187,6 +187,52 @@ fn parse_mssql_create_procedure() {
     let _ = ms().verified_stmt("CREATE PROCEDURE [foo] AS BEGIN UPDATE bar SET col = 'test'; SELECT [foo] FROM BAR WHERE [FOO] > 10 END");
 }
 
+#[test]
+fn parse_mssql_if_statement_single_statement_bodies() {
+    let sql = "IF 1 SELECT 1 ELSE SELECT 2";
+    let Statement::If(stmt) = ms().verified_stmt(sql) else {
+        unreachable!()
+    };
+
+    assert_eq!(Some(Expr::value(number("1"))), stmt.if_block.condition);
+    assert!(!stmt.if_block.has_then_keyword);
+    assert_eq!(1, stmt.if_block.statements.len());
+    assert_eq!(1, stmt.else_block.unwrap().len());
+    assert_eq!(None, stmt.end_token);
+
+    ms().one_statement_parses_to(sql, sql);
+}
+
+#[test]
+fn parse_mssql_if_statement_begin_end_blocks() {
+    let sql = "IF 1 BEGIN SELECT 1; SELECT 2 END ELSE BEGIN SELECT 3 END";
+    let Statement::If(stmt) = ms().verified_stmt(sql) else {
+        unreachable!()
+    };
+
+    assert_eq!(1, stmt.if_block.statements.len());
+    assert!(matches!(
+        &stmt.if_block.statements[0],
+        Statement::StartTransaction {
+            begin: true,
+            has_end_keyword: true,
+            statements,
+            ..
+        } if statements.len() == 2
+    ));
+    assert!(matches!(
+        &stmt.else_block.as_ref().unwrap()[0],
+        Statement::StartTransaction {
+            begin: true,
+            has_end_keyword: true,
+            statements,
+            ..
+        } if statements.len() == 1
+    ));
+
+    ms().one_statement_parses_to(sql, sql);
+}
+
 #[test]
 fn parse_mssql_apply_join() {
     let _ = ms_and_generic().verified_only_select(