agent_patch
runs/2026-02-28__18-27-22__gpt-5-3-codex/flux-pr-1791/flux-pr-1791.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/agent-logs/agent.patch
13852 bytes
diff --git a/tmp/agent-patch-flux-pr-1791.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/ast/mod.rs b/app/src/ast/mod.rs
index a6dc682..a0dec63 100644
--- a/tmp/agent-patch-flux-pr-1791.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/ast/mod.rs
+++ b/app/src/ast/mod.rs
@@ -2121,8 +2121,10 @@ 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 `END` token.
+ pub end_token: Option<AttachedToken>,
+ /// Optional `CASE` token after `END`.
+ pub case_token: Option<AttachedToken>,
}
impl fmt::Display for CaseStatement {
@@ -2131,7 +2133,8 @@ impl fmt::Display for CaseStatement {
match_expr,
when_blocks,
else_block,
- has_end_case,
+ end_token,
+ case_token,
} = self;
write!(f, "CASE")?;
@@ -2149,8 +2152,10 @@ impl fmt::Display for CaseStatement {
format_statement_list(f, else_block)?;
}
- write!(f, " END")?;
- if *has_end_case {
+ if end_token.is_some() {
+ write!(f, " END")?;
+ }
+ if case_token.is_some() {
write!(f, " CASE")?;
}
@@ -2181,6 +2186,10 @@ pub struct IfStatement {
pub if_block: ConditionalStatements,
pub elseif_blocks: Vec<ConditionalStatements>,
pub else_block: Option<Vec<Statement>>,
+ /// Optional `END` token.
+ pub end_token: Option<AttachedToken>,
+ /// Optional `IF` token after `END`.
+ pub if_token: Option<AttachedToken>,
}
impl fmt::Display for IfStatement {
@@ -2189,6 +2198,8 @@ impl fmt::Display for IfStatement {
if_block,
elseif_blocks,
else_block,
+ end_token,
+ if_token,
} = self;
write!(f, "{if_block}")?;
@@ -2202,7 +2213,12 @@ impl fmt::Display for IfStatement {
format_statement_list(f, else_block)?;
}
- write!(f, " END IF")?;
+ if end_token.is_some() {
+ write!(f, " END")?;
+ if if_token.is_some() {
+ write!(f, " IF")?;
+ }
+ }
Ok(())
}
@@ -2234,9 +2250,11 @@ 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 clause body.
pub statements: Vec<Statement>,
+ /// Optional `THEN` token.
+ pub then_token: Option<AttachedToken>,
pub kind: ConditionalStatementKind,
}
@@ -2245,6 +2263,7 @@ impl fmt::Display for ConditionalStatements {
let ConditionalStatements {
condition: expr,
statements,
+ then_token,
kind,
} = self;
@@ -2254,7 +2273,13 @@ impl fmt::Display for ConditionalStatements {
ConditionalStatementKind::ElseIf => "ELSEIF",
};
- write!(f, "{kind} {expr} THEN")?;
+ write!(f, "{kind}")?;
+ if let Some(expr) = expr {
+ write!(f, " {expr}")?;
+ }
+ if then_token.is_some() {
+ 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-3-codex/app/src/ast/spans.rs b/app/src/ast/spans.rs
index 11770d1..d0587a9 100644
--- a/tmp/agent-patch-flux-pr-1791.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/ast/spans.rs
+++ b/app/src/ast/spans.rs
@@ -742,7 +742,8 @@ impl Spanned for CaseStatement {
match_expr,
when_blocks,
else_block,
- has_end_case: _,
+ end_token: _,
+ case_token: _,
} = self;
union_spans(
@@ -761,6 +762,8 @@ impl Spanned for IfStatement {
if_block,
elseif_blocks,
else_block,
+ end_token: _,
+ if_token: _,
} = self;
union_spans(
@@ -776,10 +779,16 @@ impl Spanned for ConditionalStatements {
let ConditionalStatements {
condition,
statements,
+ then_token: _,
kind: _,
} = self;
- union_spans(iter::once(condition.span()).chain(statements.iter().map(|s| s.span())))
+ union_spans(
+ condition
+ .iter()
+ .map(|c| c.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-3-codex/app/src/dialect/mssql.rs b/app/src/dialect/mssql.rs
index 18a963a..8bdc384 100644
--- a/tmp/agent-patch-flux-pr-1791.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/dialect/mssql.rs
+++ b/app/src/dialect/mssql.rs
@@ -15,13 +15,23 @@
// specific language governing permissions and limitations
// under the License.
+use crate::ast::Statement;
use crate::dialect::Dialect;
+use crate::keywords::Keyword;
+use crate::parser::{Parser, ParserError};
/// A [`Dialect`] for [Microsoft SQL Server](https://www.microsoft.com/en-us/sql-server/)
#[derive(Debug)]
pub struct MsSqlDialect {}
impl Dialect for MsSqlDialect {
+ fn parse_statement(&self, parser: &mut Parser) -> Option<Result<Statement, ParserError>> {
+ if parser.peek_keyword(Keyword::IF) {
+ return Some(parser.parse_mssql_if_stmt());
+ }
+ None
+ }
+
fn is_delimited_identifier_start(&self, ch: char) -> bool {
ch == '"' || ch == '['
}
diff --git a/tmp/agent-patch-flux-pr-1791.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 5495499..1401175 100644
--- a/tmp/agent-patch-flux-pr-1791.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/src/parser/mod.rs
+++ b/app/src/parser/mod.rs
@@ -644,6 +644,7 @@ impl<'a> Parser<'a> {
parser.parse_conditional_statements(
ConditionalStatementKind::When,
&[Keyword::WHEN, Keyword::ELSE, Keyword::END],
+ true,
)
})?;
@@ -653,14 +654,19 @@ impl<'a> Parser<'a> {
None
};
- self.expect_keyword_is(Keyword::END)?;
- let has_end_case = self.parse_keyword(Keyword::CASE);
+ let end_token = Some(self.expect_keyword(Keyword::END)?.into());
+ let case_token = if self.parse_keyword(Keyword::CASE) {
+ Some(self.get_current_token().clone().into())
+ } else {
+ None
+ };
Ok(Statement::Case(CaseStatement {
match_expr,
when_blocks,
else_block,
- has_end_case,
+ end_token,
+ case_token,
}))
}
@@ -672,6 +678,7 @@ impl<'a> Parser<'a> {
let if_block = self.parse_conditional_statements(
ConditionalStatementKind::If,
&[Keyword::ELSE, Keyword::ELSEIF, Keyword::END],
+ true,
)?;
let elseif_blocks = if self.parse_keyword(Keyword::ELSEIF) {
@@ -679,6 +686,7 @@ impl<'a> Parser<'a> {
parser.parse_conditional_statements(
ConditionalStatementKind::ElseIf,
&[Keyword::ELSEIF, Keyword::ELSE, Keyword::END],
+ true,
)
})?
} else {
@@ -691,12 +699,52 @@ impl<'a> Parser<'a> {
None
};
- self.expect_keywords(&[Keyword::END, Keyword::IF])?;
+ let end_token = Some(self.expect_keyword(Keyword::END)?.into());
+ let if_token = Some(self.expect_keyword(Keyword::IF)?.into());
Ok(Statement::If(IfStatement {
if_block,
elseif_blocks,
else_block,
+ end_token,
+ if_token,
+ }))
+ }
+
+ /// Parse an MSSQL `IF` statement.
+ ///
+ /// See [Statement::If]
+ pub fn parse_mssql_if_stmt(&mut self) -> Result<Statement, ParserError> {
+ self.expect_keyword_is(Keyword::IF)?;
+ let condition = self.parse_expr()?;
+ let if_statement = self.parse_mssql_if_else_statement()?;
+
+ let semicolon_before_else = self.peek_token_ref().token == Token::SemiColon
+ && matches!(
+ &self.peek_nth_token_ref(1).token,
+ Token::Word(w) if w.keyword == Keyword::ELSE
+ );
+ if semicolon_before_else {
+ self.next_token();
+ }
+
+ let else_block = if self.parse_keyword(Keyword::ELSE) {
+ Some(vec![self.parse_mssql_if_else_statement()?])
+ } else {
+ None
+ };
+
+ Ok(Statement::If(IfStatement {
+ if_block: ConditionalStatements {
+ condition: Some(condition),
+ statements: vec![if_statement],
+ then_token: None,
+ kind: ConditionalStatementKind::If,
+ },
+ elseif_blocks: vec![],
+ else_block,
+ end_token: None,
+ if_token: None,
}))
}
@@ -711,18 +759,53 @@ impl<'a> Parser<'a> {
&mut self,
kind: ConditionalStatementKind,
terminal_keywords: &[Keyword],
+ requires_then_token: bool,
) -> Result<ConditionalStatements, ParserError> {
let condition = self.parse_expr()?;
- self.expect_keyword_is(Keyword::THEN)?;
+ let then_token = if requires_then_token {
+ Some(self.expect_keyword(Keyword::THEN)?.into())
+ } else {
+ None
+ };
let statements = self.parse_statement_list(terminal_keywords)?;
Ok(ConditionalStatements {
- condition,
+ condition: Some(condition),
statements,
+ then_token,
kind,
})
}
+ fn parse_mssql_if_else_statement(&mut self) -> Result<Statement, ParserError> {
+ if self.peek_keyword(Keyword::BEGIN) {
+ self.parse_mssql_begin_end_block()
+ } else {
+ self.parse_statement()
+ }
+ }
+
+ fn parse_mssql_begin_end_block(&mut self) -> Result<Statement, ParserError> {
+ self.expect_keyword_is(Keyword::BEGIN)?;
+
+ let mut statements = vec![];
+ while !self.peek_keyword(Keyword::END) {
+ statements.push(self.parse_statement()?);
+ let _ = self.consume_token(&Token::SemiColon);
+ }
+
+ 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,
+ })
+ }
+
/// Parse a `RAISE` statement.
///
/// See [Statement::Raise]
diff --git a/tmp/agent-patch-flux-pr-1791.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 3653321..2b88447 100644
--- a/tmp/agent-patch-flux-pr-1791.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/tests/sqlparser_common.rs
+++ b/app/tests/sqlparser_common.rs
@@ -14229,7 +14229,7 @@ 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_eq!(1, stmt.else_block.unwrap().len());
@@ -14280,8 +14280,8 @@ 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_eq!(1, stmt.else_block.unwrap().len());
verified_stmt(concat!(
diff --git a/tmp/agent-patch-flux-pr-1791.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/tests/sqlparser_mssql.rs b/app/tests/sqlparser_mssql.rs
index 5d76fd0..d181272 100644
--- a/tmp/agent-patch-flux-pr-1791.1-of-1.2026-02-28__18-27-22__gpt-5-3-codex/app/tests/sqlparser_mssql.rs
+++ b/app/tests/sqlparser_mssql.rs
@@ -1915,6 +1915,34 @@ fn parse_mssql_table_identifier_with_default_schema() {
ms().verified_stmt("SELECT * FROM mydatabase..MyTable");
}
+#[test]
+fn parse_mssql_if_else_statement_single_statements() {
+ let sql = "IF @x = 1 SELECT 1 ELSE SELECT 2";
+ let Statement::If(stmt) = ms().verified_stmt(sql) else {
+ unreachable!();
+ };
+
+ assert_eq!(Some(Expr::BinaryOp {
+ left: Box::new(Expr::Identifier(Ident::new("@x"))),
+ op: BinaryOperator::Eq,
+ right: Box::new(Expr::value(number("1"))),
+ }), stmt.if_block.condition);
+ assert!(stmt.if_block.then_token.is_none());
+ assert!(stmt.end_token.is_none());
+ assert_eq!(1, stmt.if_block.statements.len());
+ assert_eq!(Some(1), stmt.else_block.map(|s| s.len()));
+}
+
+#[test]
+fn parse_mssql_if_else_statement_begin_end_blocks() {
+ ms().verified_stmt(
+ "IF @x = 1 BEGIN SELECT 1; SELECT 2; END ELSE BEGIN SELECT 3; SELECT 4; END",
+ );
+ ms().verified_stmt(
+ "IF @x = 1 BEGIN SELECT 1 SELECT 2 END ELSE BEGIN SELECT 3 SELECT 4 END",
+ );
+}
+
fn ms() -> TestedDialects {
TestedDialects::new(vec![Box::new(MsSqlDialect {})])
}