agent_patch
runs/2026-02-28__18-27-22__gpt-5-1-codex-mini/flux-pr-1791/flux-pr-1791.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/agent-logs/agent.patch
33372 bytes
diff --git a/tmp/agent-patch-flux-pr-1791.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/ast/mod.rs b/app/src/ast/mod.rs
index a6dc682..3ee1904 100644
--- a/tmp/agent-patch-flux-pr-1791.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/ast/mod.rs
+++ b/app/src/ast/mod.rs
@@ -37,7 +37,8 @@ use serde::{Deserialize, Serialize};
#[cfg(feature = "visitor")]
use sqlparser_derive::{Visit, VisitMut};
-use crate::tokenizer::Span;
+use crate::keywords::Keyword;
+use crate::tokenizer::{Span, Token};
pub use self::data_type::{
ArrayElemTypeDef, BinaryLength, CharLengthUnits, CharacterLength, DataType, EnumMember,
@@ -2118,20 +2119,23 @@ pub enum Password {
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct CaseStatement {
+ /// The `CASE` token that starts the statement.
+ pub case_token: AttachedToken,
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,
+ pub when_blocks: Vec<ConditionalStatementBlock>,
+ pub else_block: Option<ConditionalStatementBlock>,
+ /// The last token of the statement (`END` or `CASE`).
+ pub end_case_token: AttachedToken,
}
impl fmt::Display for CaseStatement {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let CaseStatement {
+ case_token: _,
match_expr,
when_blocks,
else_block,
- has_end_case,
+ end_case_token: AttachedToken(end),
} = self;
write!(f, "CASE")?;
@@ -2145,13 +2149,15 @@ impl fmt::Display for CaseStatement {
}
if let Some(else_block) = else_block {
- write!(f, " ELSE ")?;
- format_statement_list(f, else_block)?;
+ write!(f, " {else_block}")?;
}
write!(f, " END")?;
- if *has_end_case {
- write!(f, " CASE")?;
+
+ if let Token::Word(w) = &end.token {
+ if w.keyword == Keyword::CASE {
+ write!(f, " CASE")?;
+ }
}
Ok(())
@@ -2160,7 +2166,7 @@ impl fmt::Display for CaseStatement {
/// An `IF` statement.
///
-/// Examples:
+/// Example (BigQuery or Snowflake):
/// ```sql
/// IF TRUE THEN
/// SELECT 1;
@@ -2171,16 +2177,22 @@ impl fmt::Display for CaseStatement {
/// SELECT 4;
/// END IF
/// ```
-///
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/procedural-language#if)
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/snowflake-scripting/if)
+///
+/// Example (MSSQL):
+/// ```sql
+/// IF 1=1 SELECT 1 ELSE SELECT 2
+/// ```
+/// [MSSQL](https://learn.microsoft.com/en-us/sql/t-sql/language-elements/if-else-transact-sql?view=sql-server-ver16)
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
pub struct IfStatement {
- pub if_block: ConditionalStatements,
- pub elseif_blocks: Vec<ConditionalStatements>,
- pub else_block: Option<Vec<Statement>>,
+ pub if_block: ConditionalStatementBlock,
+ pub elseif_blocks: Vec<ConditionalStatementBlock>,
+ pub else_block: Option<ConditionalStatementBlock>,
+ pub end_token: Option<AttachedToken>,
}
impl fmt::Display for IfStatement {
@@ -2189,82 +2201,128 @@ impl fmt::Display for IfStatement {
if_block,
elseif_blocks,
else_block,
+ end_token,
} = self;
write!(f, "{if_block}")?;
- if !elseif_blocks.is_empty() {
- write!(f, " {}", display_separated(elseif_blocks, " "))?;
+ for elseif_block in elseif_blocks {
+ write!(f, " {elseif_block}")?;
}
if let Some(else_block) = else_block {
- write!(f, " ELSE ")?;
- format_statement_list(f, else_block)?;
+ write!(f, " {else_block}")?;
}
- write!(f, " END IF")?;
+ if let Some(AttachedToken(end_token)) = end_token {
+ write!(f, " END {end_token}")?;
+ }
Ok(())
}
}
-/// Represents a type of [ConditionalStatements]
-#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
-#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
-#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
-pub enum ConditionalStatementKind {
- /// `WHEN <condition> THEN <statements>`
- When,
- /// `IF <condition> THEN <statements>`
- If,
- /// `ELSEIF <condition> THEN <statements>`
- ElseIf,
-}
-
/// A block within a [Statement::Case] or [Statement::If]-like statement
///
-/// Examples:
+/// Example 1:
/// ```sql
/// WHEN EXISTS(SELECT 1) THEN SELECT 1;
+/// ```
///
+/// Example 2:
+/// ```sql
/// IF TRUE THEN SELECT 1; SELECT 2;
/// ```
+///
+/// Example 3:
+/// ```sql
+/// ELSE SELECT 1; SELECT 2;
+/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
-pub struct ConditionalStatements {
- /// The condition expression.
- pub condition: Expr,
- /// Statement list of the `THEN` clause.
- pub statements: Vec<Statement>,
- pub kind: ConditionalStatementKind,
+pub struct ConditionalStatementBlock {
+ pub start_token: AttachedToken,
+ pub condition: Option<Expr>,
+ pub then_token: Option<AttachedToken>,
+ pub conditional_statements: ConditionalStatements,
}
-impl fmt::Display for ConditionalStatements {
+impl ConditionalStatementBlock {
+ pub fn statements(&self) -> &Vec<Statement> {
+ self.conditional_statements.statements()
+ }
+}
+
+impl fmt::Display for ConditionalStatementBlock {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- let ConditionalStatements {
- condition: expr,
- statements,
- kind,
+ let ConditionalStatementBlock {
+ start_token: AttachedToken(start_token),
+ condition,
+ then_token,
+ conditional_statements,
} = self;
- let kind = match kind {
- ConditionalStatementKind::When => "WHEN",
- ConditionalStatementKind::If => "IF",
- ConditionalStatementKind::ElseIf => "ELSEIF",
- };
+ write!(f, "{start_token}")?;
+
+ if let Some(condition) = condition {
+ write!(f, " {condition}")?;
+ }
- write!(f, "{kind} {expr} THEN")?;
+ if then_token.is_some() {
+ write!(f, " THEN")?;
+ }
- if !statements.is_empty() {
- write!(f, " ")?;
- format_statement_list(f, statements)?;
+ if !conditional_statements.statements().is_empty() {
+ write!(f, " {conditional_statements}")?;
}
Ok(())
}
}
+/// A list of statements in a [ConditionalStatementBlock].
+#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
+#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
+pub enum ConditionalStatements {
+ /// SELECT 1; SELECT 2; SELECT 3; ...
+ Sequence { statements: Vec<Statement> },
+ /// BEGIN SELECT 1; SELECT 2; SELECT 3; ... END
+ BeginEnd {
+ begin_token: AttachedToken,
+ statements: Vec<Statement>,
+ end_token: AttachedToken,
+ },
+}
+
+impl ConditionalStatements {
+ pub fn statements(&self) -> &Vec<Statement> {
+ match self {
+ ConditionalStatements::Sequence { statements } => statements,
+ ConditionalStatements::BeginEnd { statements, .. } => statements,
+ }
+ }
+}
+
+impl fmt::Display for ConditionalStatements {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self {
+ ConditionalStatements::Sequence { statements } => {
+ if !statements.is_empty() {
+ format_statement_list(f, statements)?;
+ }
+ Ok(())
+ }
+ ConditionalStatements::BeginEnd { statements, .. } => {
+ write!(f, "BEGIN ")?;
+ format_statement_list(f, statements)?;
+ write!(f, " END")
+ }
+ }
+ }
+}
+
/// A `RAISE` statement.
///
/// Examples:
diff --git a/tmp/agent-patch-flux-pr-1791.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/ast/spans.rs b/app/src/ast/spans.rs
index 11770d1..d5d02c0 100644
--- a/tmp/agent-patch-flux-pr-1791.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/ast/spans.rs
+++ b/app/src/ast/spans.rs
@@ -22,8 +22,9 @@ use crate::tokenizer::Span;
use super::{
dcl::SecondaryRoles, value::ValueWithSpan, AccessExpr, AlterColumnOperation,
- AlterIndexOperation, AlterTableOperation, Array, Assignment, AssignmentTarget, CaseStatement,
- CloseCursor, ClusteredIndex, ColumnDef, ColumnOption, ColumnOptionDef, ConditionalStatements,
+ AlterIndexOperation, AlterTableOperation, Array, Assignment, AssignmentTarget, AttachedToken,
+ CaseStatement, CloseCursor, ClusteredIndex, ColumnDef, ColumnOption, ColumnOptionDef,
+ ConditionalStatementBlock, ConditionalStatements,
ConflictTarget, ConnectBy, ConstraintCharacteristics, CopySource, CreateIndex, CreateTable,
CreateTableOptions, Cte, Delete, DoUpdate, ExceptSelectItem, ExcludeSelectItem, Expr,
ExprWithAlias, Fetch, FromTable, Function, FunctionArg, FunctionArgExpr,
@@ -739,19 +740,14 @@ impl Spanned for CreateIndex {
impl Spanned for CaseStatement {
fn span(&self) -> Span {
let CaseStatement {
- match_expr,
- when_blocks,
- else_block,
- has_end_case: _,
+ case_token: AttachedToken(start),
+ match_expr: _,
+ when_blocks: _,
+ else_block: _,
+ end_case_token: AttachedToken(end),
} = self;
- union_spans(
- match_expr
- .iter()
- .map(|e| e.span())
- .chain(when_blocks.iter().map(|b| b.span()))
- .chain(else_block.iter().flat_map(|e| e.iter().map(|s| s.span()))),
- )
+ union_spans([start.span, end.span].into_iter())
}
}
@@ -761,25 +757,48 @@ impl Spanned for IfStatement {
if_block,
elseif_blocks,
else_block,
+ end_token,
} = self;
union_spans(
iter::once(if_block.span())
.chain(elseif_blocks.iter().map(|b| b.span()))
- .chain(else_block.iter().flat_map(|e| e.iter().map(|s| s.span()))),
+ .chain(else_block.as_ref().map(|b| b.span()))
+ .chain(end_token.as_ref().map(|AttachedToken(t)| t.span)),
)
}
}
impl Spanned for ConditionalStatements {
fn span(&self) -> Span {
- let ConditionalStatements {
+ match self {
+ ConditionalStatements::Sequence { statements } => {
+ union_spans(statements.iter().map(|s| s.span()))
+ }
+ ConditionalStatements::BeginEnd {
+ begin_token: AttachedToken(start),
+ statements: _,
+ end_token: AttachedToken(end),
+ } => union_spans([start.span, end.span].into_iter()),
+ }
+ }
+}
+
+impl Spanned for ConditionalStatementBlock {
+ fn span(&self) -> Span {
+ let ConditionalStatementBlock {
+ start_token: AttachedToken(start_token),
condition,
- statements,
- kind: _,
+ then_token,
+ conditional_statements,
} = self;
- union_spans(iter::once(condition.span()).chain(statements.iter().map(|s| s.span())))
+ union_spans(
+ iter::once(start_token.span)
+ .chain(condition.as_ref().map(|c| c.span()))
+ .chain(then_token.as_ref().map(|AttachedToken(t)| t.span))
+ .chain(iter::once(conditional_statements.span())),
+ )
}
}
diff --git a/tmp/agent-patch-flux-pr-1791.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/dialect/mssql.rs b/app/src/dialect/mssql.rs
index 18a963a..d86d68a 100644
--- a/tmp/agent-patch-flux-pr-1791.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/dialect/mssql.rs
+++ b/app/src/dialect/mssql.rs
@@ -15,7 +15,16 @@
// specific language governing permissions and limitations
// under the License.
+use crate::ast::helpers::attached_token::AttachedToken;
+use crate::ast::{ConditionalStatementBlock, ConditionalStatements, IfStatement, Statement};
use crate::dialect::Dialect;
+use crate::keywords::{self, Keyword};
+use crate::parser::{Parser, ParserError};
+use crate::tokenizer::Token;
+#[cfg(not(feature = "std"))]
+use alloc::{vec, vec::Vec};
+
+const RESERVED_FOR_COLUMN_ALIAS: &[Keyword] = &[Keyword::IF, Keyword::ELSE];
/// A [`Dialect`] for [Microsoft SQL Server](https://www.microsoft.com/en-us/sql-server/)
#[derive(Debug)]
@@ -106,4 +115,123 @@ impl Dialect for MsSqlDialect {
fn supports_object_name_double_dot_notation(&self) -> bool {
true
}
+
+ fn is_column_alias(&self, kw: &Keyword, _parser: &mut Parser) -> bool {
+ !keywords::RESERVED_FOR_COLUMN_ALIAS.contains(kw) && !RESERVED_FOR_COLUMN_ALIAS.contains(kw)
+ }
+
+ fn parse_statement(&self, parser: &mut Parser) -> Option<Result<Statement, ParserError>> {
+ if parser.peek_keyword(Keyword::IF) {
+ Some(self.parse_if_stmt(parser))
+ } else {
+ None
+ }
+ }
+}
+
+impl MsSqlDialect {
+ /// ```sql
+ /// IF boolean_expression
+ /// { sql_statement | statement_block }
+ /// [ ELSE
+ /// { sql_statement | statement_block } ]
+ /// ```
+ fn parse_if_stmt(&self, parser: &mut Parser) -> Result<Statement, ParserError> {
+ let if_token = parser.expect_keyword(Keyword::IF)?;
+
+ let condition = parser.parse_expr()?;
+
+ let if_block = if parser.peek_keyword(Keyword::BEGIN) {
+ let begin_token = parser.expect_keyword(Keyword::BEGIN)?;
+ let statements = self.parse_statement_list(parser, Some(Keyword::END))?;
+ let end_token = parser.expect_keyword(Keyword::END)?;
+ ConditionalStatementBlock {
+ start_token: AttachedToken(if_token),
+ condition: Some(condition),
+ then_token: None,
+ conditional_statements: ConditionalStatements::BeginEnd {
+ begin_token: AttachedToken(begin_token),
+ statements,
+ end_token: AttachedToken(end_token),
+ },
+ }
+ } else {
+ let stmt = parser.parse_statement()?;
+ ConditionalStatementBlock {
+ start_token: AttachedToken(if_token),
+ condition: Some(condition),
+ then_token: None,
+ conditional_statements: ConditionalStatements::Sequence {
+ statements: vec![stmt],
+ },
+ }
+ };
+
+ while let Token::SemiColon = parser.peek_token_ref().token {
+ parser.advance_token();
+ }
+
+ let mut else_block = None;
+ if parser.peek_keyword(Keyword::ELSE) {
+ let else_token = parser.expect_keyword(Keyword::ELSE)?;
+ if parser.peek_keyword(Keyword::BEGIN) {
+ let begin_token = parser.expect_keyword(Keyword::BEGIN)?;
+ let statements = self.parse_statement_list(parser, Some(Keyword::END))?;
+ let end_token = parser.expect_keyword(Keyword::END)?;
+ else_block = Some(ConditionalStatementBlock {
+ start_token: AttachedToken(else_token),
+ condition: None,
+ then_token: None,
+ conditional_statements: ConditionalStatements::BeginEnd {
+ begin_token: AttachedToken(begin_token),
+ statements,
+ end_token: AttachedToken(end_token),
+ },
+ });
+ } else {
+ let stmt = parser.parse_statement()?;
+ else_block = Some(ConditionalStatementBlock {
+ start_token: AttachedToken(else_token),
+ condition: None,
+ then_token: None,
+ conditional_statements: ConditionalStatements::Sequence {
+ statements: vec![stmt],
+ },
+ });
+ }
+ }
+
+ Ok(Statement::If(IfStatement {
+ if_block,
+ else_block,
+ elseif_blocks: Vec::new(),
+ end_token: None,
+ }))
+ }
+
+ /// Parse a sequence of statements, optionally separated by semicolon.
+ ///
+ /// Stops parsing when reaching EOF or the given keyword.
+ fn parse_statement_list(
+ &self,
+ parser: &mut Parser,
+ terminal_keyword: Option<Keyword>,
+ ) -> Result<Vec<Statement>, ParserError> {
+ let mut stmts = Vec::new();
+ loop {
+ if let Token::EOF = parser.peek_token_ref().token {
+ break;
+ }
+ if let Some(term) = terminal_keyword {
+ if parser.peek_keyword(term) {
+ break;
+ }
+ }
+ stmts.push(parser.parse_statement()?);
+ while let Token::SemiColon = parser.peek_token_ref().token {
+ parser.advance_token();
+ }
+ }
+ Ok(stmts)
+ }
}
diff --git a/tmp/agent-patch-flux-pr-1791.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/parser/mod.rs b/app/src/parser/mod.rs
index 5495499..b9076bb 100644
--- a/tmp/agent-patch-flux-pr-1791.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/src/parser/mod.rs
+++ b/app/src/parser/mod.rs
@@ -631,7 +631,7 @@ impl<'a> Parser<'a> {
///
/// See [Statement::Case]
pub fn parse_case_stmt(&mut self) -> Result<Statement, ParserError> {
- self.expect_keyword_is(Keyword::CASE)?;
+ let case_token = self.expect_keyword(Keyword::CASE)?;
let match_expr = if self.peek_keyword(Keyword::WHEN) {
None
@@ -641,26 +641,26 @@ impl<'a> Parser<'a> {
self.expect_keyword_is(Keyword::WHEN)?;
let when_blocks = self.parse_keyword_separated(Keyword::WHEN, |parser| {
- parser.parse_conditional_statements(
- ConditionalStatementKind::When,
- &[Keyword::WHEN, Keyword::ELSE, Keyword::END],
- )
+ parser.parse_conditional_statement_block(&[Keyword::WHEN, Keyword::ELSE, Keyword::END])
})?;
let else_block = if self.parse_keyword(Keyword::ELSE) {
- Some(self.parse_statement_list(&[Keyword::END])?)
+ Some(self.parse_conditional_statement_block(&[Keyword::END])?)
} else {
None
};
- self.expect_keyword_is(Keyword::END)?;
- let has_end_case = self.parse_keyword(Keyword::CASE);
+ let mut end_case_token = self.expect_keyword(Keyword::END)?;
+ if self.peek_keyword(Keyword::CASE) {
+ end_case_token = self.expect_keyword(Keyword::CASE)?;
+ }
Ok(Statement::Case(CaseStatement {
+ case_token: AttachedToken(case_token),
match_expr,
when_blocks,
else_block,
- has_end_case,
+ end_case_token: AttachedToken(end_case_token),
}))
}
@@ -669,34 +669,38 @@ impl<'a> Parser<'a> {
/// See [Statement::If]
pub fn parse_if_stmt(&mut self) -> Result<Statement, ParserError> {
self.expect_keyword_is(Keyword::IF)?;
- let if_block = self.parse_conditional_statements(
- ConditionalStatementKind::If,
- &[Keyword::ELSE, Keyword::ELSEIF, Keyword::END],
- )?;
+ let if_block = self.parse_conditional_statement_block(&[
+ Keyword::ELSE,
+ Keyword::ELSEIF,
+ Keyword::END,
+ ])?;
let elseif_blocks = if self.parse_keyword(Keyword::ELSEIF) {
self.parse_keyword_separated(Keyword::ELSEIF, |parser| {
- parser.parse_conditional_statements(
- ConditionalStatementKind::ElseIf,
- &[Keyword::ELSEIF, Keyword::ELSE, Keyword::END],
- )
+ parser.parse_conditional_statement_block(&[
+ Keyword::ELSEIF,
+ Keyword::ELSE,
+ Keyword::END,
+ ])
})?
} else {
vec![]
};
let else_block = if self.parse_keyword(Keyword::ELSE) {
- Some(self.parse_statement_list(&[Keyword::END])?)
+ Some(self.parse_conditional_statement_block(&[Keyword::END])?)
} else {
None
};
- self.expect_keywords(&[Keyword::END, Keyword::IF])?;
+ self.expect_keyword_is(Keyword::END)?;
+ let end_token = self.expect_keyword(Keyword::IF)?;
Ok(Statement::If(IfStatement {
if_block,
elseif_blocks,
else_block,
+ end_token: Some(AttachedToken(end_token)),
}))
}
@@ -707,19 +711,29 @@ impl<'a> Parser<'a> {
/// ```sql
/// IF condition THEN statement1; statement2;
/// ```
- fn parse_conditional_statements(
+ fn parse_conditional_statement_block(
&mut self,
- kind: ConditionalStatementKind,
terminal_keywords: &[Keyword],
- ) -> Result<ConditionalStatements, ParserError> {
- let condition = self.parse_expr()?;
- self.expect_keyword_is(Keyword::THEN)?;
+ ) -> Result<ConditionalStatementBlock, ParserError> {
+ let start_token = self.get_current_token().clone(); // self.expect_keyword(keyword)?;
+ let mut then_token = None;
+
+ let condition = match &start_token.token {
+ Token::Word(w) if w.keyword == Keyword::ELSE => None,
+ _ => {
+ let expr = self.parse_expr()?;
+ then_token = Some(AttachedToken(self.expect_keyword(Keyword::THEN)?));
+ Some(expr)
+ }
+ };
+
let statements = self.parse_statement_list(terminal_keywords)?;
- Ok(ConditionalStatements {
+ Ok(ConditionalStatementBlock {
+ start_token: AttachedToken(start_token),
condition,
- statements,
- kind,
+ then_token,
+ conditional_statements: ConditionalStatements::Sequence { statements },
})
}
diff --git a/tmp/agent-patch-flux-pr-1791.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/tests/sqlparser_common.rs b/app/tests/sqlparser_common.rs
index 3653321..02d8aa7 100644
--- a/tmp/agent-patch-flux-pr-1791.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/tests/sqlparser_common.rs
+++ b/app/tests/sqlparser_common.rs
@@ -37,7 +37,7 @@ use sqlparser::dialect::{
};
use sqlparser::keywords::{Keyword, ALL_KEYWORDS};
use sqlparser::parser::{Parser, ParserError, ParserOptions};
-use sqlparser::tokenizer::Tokenizer;
+use sqlparser::tokenizer::{Location, Span, Tokenizer};
use sqlparser::tokenizer::{Location, Span};
use test_utils::{
all_dialects, all_dialects_where, alter_table_op, assert_eq_vec, call, expr_from_projection,
@@ -14229,9 +14229,12 @@ 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!(2, stmt.when_blocks[0].statements.len());
- assert_eq!(1, stmt.else_block.unwrap().len());
+ 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().statements().len());
verified_stmt(concat!(
"CASE 1",
@@ -14274,17 +14277,35 @@ fn parse_case_statement() {
);
}
+#[test]
+fn test_case_statement_span() {
+ let sql = "CASE 1 WHEN 2 THEN SELECT 1; SELECT 2; ELSE SELECT 3; END CASE";
+ let mut parser = Parser::new(&GenericDialect {}).try_with_sql(sql).unwrap();
+ assert_eq!(
+ parser.parse_statement().unwrap().span(),
+ Span::new(Location::new(1, 1), Location::new(1, sql.len() as u64 + 1))
+ );
+}
+
#[test]
fn parse_if_statement() {
+ let dialects = all_dialects_except(|d| d.is::<MsSqlDialect>());
+
let sql = "IF 1 THEN SELECT 1; ELSEIF 2 THEN SELECT 2; ELSE SELECT 3; END IF";
- let Statement::If(stmt) = verified_stmt(sql) else {
+ let Statement::If(IfStatement {
+ if_block,
+ elseif_blocks,
+ else_block,
+ ..
+ }) = dialects.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!(1, stmt.else_block.unwrap().len());
+ assert_eq!(Some(Expr::value(number("1"))), if_block.condition);
+ assert_eq!(Some(Expr::value(number("2"))), elseif_blocks[0].condition);
+ assert_eq!(1, else_block.unwrap().statements().len());
- verified_stmt(concat!(
+ dialects.verified_stmt(concat!(
"IF 1 THEN",
" SELECT 1;",
" SELECT 2;",
@@ -14300,7 +14321,7 @@ fn parse_if_statement() {
" SELECT 9;",
" END IF"
));
- verified_stmt(concat!(
+ dialects.verified_stmt(concat!(
"IF 1 THEN",
" SELECT 1;",
" SELECT 2;",
@@ -14309,7 +14330,7 @@ fn parse_if_statement() {
" SELECT 4;",
" END IF"
));
- verified_stmt(concat!(
+ dialects.verified_stmt(concat!(
"IF 1 THEN",
" SELECT 1;",
" SELECT 2;",
@@ -14319,22 +14340,79 @@ fn parse_if_statement() {
" SELECT 4;",
" END IF"
));
- verified_stmt(concat!("IF 1 THEN", " SELECT 1;", " SELECT 2;", " END IF"));
- verified_stmt(concat!(
+ dialects.verified_stmt(concat!("IF 1 THEN", " SELECT 1;", " SELECT 2;", " END IF"));
+ dialects.verified_stmt(concat!(
"IF (1) THEN",
" SELECT 1;",
" SELECT 2;",
" END IF"
));
- verified_stmt("IF 1 THEN END IF");
- verified_stmt("IF 1 THEN SELECT 1; ELSEIF 1 THEN END IF");
+ dialects.verified_stmt("IF 1 THEN END IF");
+ dialects.verified_stmt("IF 1 THEN SELECT 1; ELSEIF 1 THEN END IF");
assert_eq!(
ParserError::ParserError("Expected: IF, found: EOF".to_string()),
- parse_sql_statements("IF 1 THEN SELECT 1; ELSEIF 1 THEN SELECT 2; END").unwrap_err()
+ dialects
+ .parse_sql_statements("IF 1 THEN SELECT 1; ELSEIF 1 THEN SELECT 2; END")
+ .unwrap_err()
);
}
+#[test]
+fn test_if_statement_span() {
+ let sql = "IF 1=1 THEN SELECT 1; ELSEIF 1=2 THEN SELECT 2; ELSE SELECT 3; END IF";
+ let mut parser = Parser::new(&GenericDialect {}).try_with_sql(sql).unwrap();
+ assert_eq!(
+ parser.parse_statement().unwrap().span(),
+ Span::new(Location::new(1, 1), Location::new(1, sql.len() as u64 + 1))
+ );
+}
+
+#[test]
+fn test_if_statement_multiline_span() {
+ let sql_line1 = "IF 1 = 1 THEN SELECT 1;";
+ let sql_line2 = "ELSEIF 1 = 2 THEN SELECT 2;";
+ let sql_line3 = "ELSE SELECT 3;";
+ let sql_line4 = "END IF";
+ let sql = [sql_line1, sql_line2, sql_line3, sql_line4].join("\n");
+ let mut parser = Parser::new(&GenericDialect {}).try_with_sql(&sql).unwrap();
+ assert_eq!(
+ parser.parse_statement().unwrap().span(),
+ Span::new(
+ Location::new(1, 1),
+ Location::new(4, sql_line4.len() as u64 + 1)
+ )
+ );
+}
+
+#[test]
+fn test_conditional_statement_span() {
+ let sql = "IF 1=1 THEN SELECT 1; ELSEIF 1=2 THEN SELECT 2; ELSE SELECT 3; END IF";
+ let mut parser = Parser::new(&GenericDialect {}).try_with_sql(sql).unwrap();
+ match parser.parse_statement().unwrap() {
+ Statement::If(IfStatement {
+ if_block,
+ elseif_blocks,
+ else_block,
+ ..
+ }) => {
+ assert_eq!(
+ Span::new(Location::new(1, 1), Location::new(1, 21)),
+ if_block.span()
+ );
+ assert_eq!(
+ Span::new(Location::new(1, 23), Location::new(1, 47)),
+ elseif_blocks[0].span()
+ );
+ assert_eq!(
+ Span::new(Location::new(1, 49), Location::new(1, 62)),
+ else_block.unwrap().span()
+ );
+ }
+ stmt => panic!("Unexpected statement: {:?}", stmt),
+ }
+}
+
#[test]
fn parse_raise_statement() {
let sql = "RAISE USING MESSAGE = 42";
diff --git a/tmp/agent-patch-flux-pr-1791.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/tests/sqlparser_mssql.rs b/app/tests/sqlparser_mssql.rs
index 5d76fd0..48c96a7 100644
--- a/tmp/agent-patch-flux-pr-1791.1-of-1.2026-02-28__18-27-22__gpt-5-1-codex-mini/app/tests/sqlparser_mssql.rs
+++ b/app/tests/sqlparser_mssql.rs
@@ -23,7 +23,7 @@
mod test_utils;
use helpers::attached_token::AttachedToken;
-use sqlparser::tokenizer::Span;
+use sqlparser::tokenizer::{Location, Span};
use test_utils::*;
use sqlparser::ast::DataType::{Int, Text, Varbinary};
@@ -1922,6 +1922,104 @@ fn ms_and_generic() -> TestedDialects {
TestedDialects::new(vec![Box::new(MsSqlDialect {}), Box::new(GenericDialect {})])
}
+#[test]
+fn parse_mssql_if_else() {
+ // Simple statements and blocks
+ ms().verified_stmt("IF 1 = 1 SELECT '1'; ELSE SELECT '2';");
+ ms().verified_stmt("IF 1 = 1 BEGIN SET @A = 1; END ELSE SET @A = 2;");
+ ms().verified_stmt(
+ "IF DATENAME(weekday, GETDATE()) IN (N'Saturday', N'Sunday') SELECT 'Weekend'; ELSE SELECT 'Weekday';"
+ );
+ ms().verified_stmt(
+ "IF (SELECT COUNT(*) FROM a.b WHERE c LIKE 'x%') > 1 SELECT 'yes'; ELSE SELECT 'No';",
+ );
+
+ // Multiple statements
+ let stmts = ms()
+ .parse_sql_statements("DECLARE @A INT; IF 1=1 BEGIN SET @A = 1 END ELSE SET @A = 2")
+ .unwrap();
+ match &stmts[..] {
+ [Statement::Declare { .. }, Statement::If(stmt)] => {
+ assert_eq!(
+ stmt.to_string(),
+ "IF 1 = 1 BEGIN SET @A = 1; END ELSE SET @A = 2;"
+ );
+ }
+ _ => panic!("Unexpected statements: {:?}", stmts),
+ }
+}
+
+#[test]
+fn test_mssql_if_else_span() {
+ let sql = "IF 1 = 1 SELECT '1' ELSE SELECT '2'";
+ let mut parser = Parser::new(&MsSqlDialect {}).try_with_sql(sql).unwrap();
+ assert_eq!(
+ parser.parse_statement().unwrap().span(),
+ Span::new(Location::new(1, 1), Location::new(1, sql.len() as u64 + 1))
+ );
+}
+
+#[test]
+fn test_mssql_if_else_multiline_span() {
+ let sql_line1 = "IF 1 = 1";
+ let sql_line2 = "SELECT '1'";
+ let sql_line3 = "ELSE SELECT '2'";
+ let sql = [sql_line1, sql_line2, sql_line3].join("\n");
+ let mut parser = Parser::new(&MsSqlDialect {}).try_with_sql(&sql).unwrap();
+ assert_eq!(
+ parser.parse_statement().unwrap().span(),
+ Span::new(
+ Location::new(1, 1),
+ Location::new(3, sql_line3.len() as u64 + 1)
+ )
+ );
+}
+
+#[test]
+fn test_mssql_if_statements_span() {
+ // Simple statements
+ let mut sql = "IF 1 = 1 SELECT '1' ELSE SELECT '2'";
+ let mut parser = Parser::new(&MsSqlDialect {}).try_with_sql(sql).unwrap();
+ match parser.parse_statement().unwrap() {
+ Statement::If(IfStatement {
+ if_block,
+ else_block: Some(else_block),
+ ..
+ }) => {
+ assert_eq!(
+ if_block.span(),
+ Span::new(Location::new(1, 1), Location::new(1, 20))
+ );
+ assert_eq!(
+ else_block.span(),
+ Span::new(Location::new(1, 21), Location::new(1, 36))
+ );
+ }
+ stmt => panic!("Unexpected statement: {:?}", stmt),
+ }
+
+ // Blocks
+ sql = "IF 1 = 1 BEGIN SET @A = 1; END ELSE BEGIN SET @A = 2 END";
+ parser = Parser::new(&MsSqlDialect {}).try_with_sql(sql).unwrap();
+ match parser.parse_statement().unwrap() {
+ Statement::If(IfStatement {
+ if_block,
+ else_block: Some(else_block),
+ ..
+ }) => {
+ assert_eq!(
+ if_block.span(),
+ Span::new(Location::new(1, 1), Location::new(1, 31))
+ );
+ assert_eq!(
+ else_block.span(),
+ Span::new(Location::new(1, 32), Location::new(1, 57))
+ );
+ }
+ stmt => panic!("Unexpected statement: {:?}", stmt),
+ }
+}
+
#[test]
fn parse_mssql_merge_with_output() {
let stmt = "MERGE dso.products AS t \