Skip to content

Commit 0d2976d

Browse files
romanbRoman Borschel
and
Roman Borschel
authored
Add support for MSSQL IF/ELSE statements. (#1791)
Co-authored-by: Roman Borschel <roman@cluvio.com>
1 parent 4deed26 commit 0d2976d

File tree

6 files changed

+525
-129
lines changed

6 files changed

+525
-129
lines changed

src/ast/mod.rs

+112-54
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ use serde::{Deserialize, Serialize};
3737
#[cfg(feature = "visitor")]
3838
use sqlparser_derive::{Visit, VisitMut};
3939

40-
use crate::tokenizer::Span;
40+
use crate::keywords::Keyword;
41+
use crate::tokenizer::{Span, Token};
4142

4243
pub use self::data_type::{
4344
ArrayElemTypeDef, BinaryLength, CharLengthUnits, CharacterLength, DataType, EnumMember,
@@ -2118,20 +2119,23 @@ pub enum Password {
21182119
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
21192120
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
21202121
pub struct CaseStatement {
2122+
/// The `CASE` token that starts the statement.
2123+
pub case_token: AttachedToken,
21212124
pub match_expr: Option<Expr>,
2122-
pub when_blocks: Vec<ConditionalStatements>,
2123-
pub else_block: Option<Vec<Statement>>,
2124-
/// TRUE if the statement ends with `END CASE` (vs `END`).
2125-
pub has_end_case: bool,
2125+
pub when_blocks: Vec<ConditionalStatementBlock>,
2126+
pub else_block: Option<ConditionalStatementBlock>,
2127+
/// The last token of the statement (`END` or `CASE`).
2128+
pub end_case_token: AttachedToken,
21262129
}
21272130

21282131
impl fmt::Display for CaseStatement {
21292132
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
21302133
let CaseStatement {
2134+
case_token: _,
21312135
match_expr,
21322136
when_blocks,
21332137
else_block,
2134-
has_end_case,
2138+
end_case_token: AttachedToken(end),
21352139
} = self;
21362140

21372141
write!(f, "CASE")?;
@@ -2145,13 +2149,15 @@ impl fmt::Display for CaseStatement {
21452149
}
21462150

21472151
if let Some(else_block) = else_block {
2148-
write!(f, " ELSE ")?;
2149-
format_statement_list(f, else_block)?;
2152+
write!(f, " {else_block}")?;
21502153
}
21512154

21522155
write!(f, " END")?;
2153-
if *has_end_case {
2154-
write!(f, " CASE")?;
2156+
2157+
if let Token::Word(w) = &end.token {
2158+
if w.keyword == Keyword::CASE {
2159+
write!(f, " CASE")?;
2160+
}
21552161
}
21562162

21572163
Ok(())
@@ -2160,7 +2166,7 @@ impl fmt::Display for CaseStatement {
21602166

21612167
/// An `IF` statement.
21622168
///
2163-
/// Examples:
2169+
/// Example (BigQuery or Snowflake):
21642170
/// ```sql
21652171
/// IF TRUE THEN
21662172
/// SELECT 1;
@@ -2171,16 +2177,22 @@ impl fmt::Display for CaseStatement {
21712177
/// SELECT 4;
21722178
/// END IF
21732179
/// ```
2174-
///
21752180
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/procedural-language#if)
21762181
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/snowflake-scripting/if)
2182+
///
2183+
/// Example (MSSQL):
2184+
/// ```sql
2185+
/// IF 1=1 SELECT 1 ELSE SELECT 2
2186+
/// ```
2187+
/// [MSSQL](https://learn.microsoft.com/en-us/sql/t-sql/language-elements/if-else-transact-sql?view=sql-server-ver16)
21772188
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
21782189
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
21792190
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
21802191
pub struct IfStatement {
2181-
pub if_block: ConditionalStatements,
2182-
pub elseif_blocks: Vec<ConditionalStatements>,
2183-
pub else_block: Option<Vec<Statement>>,
2192+
pub if_block: ConditionalStatementBlock,
2193+
pub elseif_blocks: Vec<ConditionalStatementBlock>,
2194+
pub else_block: Option<ConditionalStatementBlock>,
2195+
pub end_token: Option<AttachedToken>,
21842196
}
21852197

21862198
impl fmt::Display for IfStatement {
@@ -2189,82 +2201,128 @@ impl fmt::Display for IfStatement {
21892201
if_block,
21902202
elseif_blocks,
21912203
else_block,
2204+
end_token,
21922205
} = self;
21932206

21942207
write!(f, "{if_block}")?;
21952208

2196-
if !elseif_blocks.is_empty() {
2197-
write!(f, " {}", display_separated(elseif_blocks, " "))?;
2209+
for elseif_block in elseif_blocks {
2210+
write!(f, " {elseif_block}")?;
21982211
}
21992212

22002213
if let Some(else_block) = else_block {
2201-
write!(f, " ELSE ")?;
2202-
format_statement_list(f, else_block)?;
2214+
write!(f, " {else_block}")?;
22032215
}
22042216

2205-
write!(f, " END IF")?;
2217+
if let Some(AttachedToken(end_token)) = end_token {
2218+
write!(f, " END {end_token}")?;
2219+
}
22062220

22072221
Ok(())
22082222
}
22092223
}
22102224

2211-
/// Represents a type of [ConditionalStatements]
2212-
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
2213-
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2214-
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
2215-
pub enum ConditionalStatementKind {
2216-
/// `WHEN <condition> THEN <statements>`
2217-
When,
2218-
/// `IF <condition> THEN <statements>`
2219-
If,
2220-
/// `ELSEIF <condition> THEN <statements>`
2221-
ElseIf,
2222-
}
2223-
22242225
/// A block within a [Statement::Case] or [Statement::If]-like statement
22252226
///
2226-
/// Examples:
2227+
/// Example 1:
22272228
/// ```sql
22282229
/// WHEN EXISTS(SELECT 1) THEN SELECT 1;
2230+
/// ```
22292231
///
2232+
/// Example 2:
2233+
/// ```sql
22302234
/// IF TRUE THEN SELECT 1; SELECT 2;
22312235
/// ```
2236+
///
2237+
/// Example 3:
2238+
/// ```sql
2239+
/// ELSE SELECT 1; SELECT 2;
2240+
/// ```
22322241
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
22332242
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
22342243
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
2235-
pub struct ConditionalStatements {
2236-
/// The condition expression.
2237-
pub condition: Expr,
2238-
/// Statement list of the `THEN` clause.
2239-
pub statements: Vec<Statement>,
2240-
pub kind: ConditionalStatementKind,
2244+
pub struct ConditionalStatementBlock {
2245+
pub start_token: AttachedToken,
2246+
pub condition: Option<Expr>,
2247+
pub then_token: Option<AttachedToken>,
2248+
pub conditional_statements: ConditionalStatements,
22412249
}
22422250

2243-
impl fmt::Display for ConditionalStatements {
2251+
impl ConditionalStatementBlock {
2252+
pub fn statements(&self) -> &Vec<Statement> {
2253+
self.conditional_statements.statements()
2254+
}
2255+
}
2256+
2257+
impl fmt::Display for ConditionalStatementBlock {
22442258
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2245-
let ConditionalStatements {
2246-
condition: expr,
2247-
statements,
2248-
kind,
2259+
let ConditionalStatementBlock {
2260+
start_token: AttachedToken(start_token),
2261+
condition,
2262+
then_token,
2263+
conditional_statements,
22492264
} = self;
22502265

2251-
let kind = match kind {
2252-
ConditionalStatementKind::When => "WHEN",
2253-
ConditionalStatementKind::If => "IF",
2254-
ConditionalStatementKind::ElseIf => "ELSEIF",
2255-
};
2266+
write!(f, "{start_token}")?;
2267+
2268+
if let Some(condition) = condition {
2269+
write!(f, " {condition}")?;
2270+
}
22562271

2257-
write!(f, "{kind} {expr} THEN")?;
2272+
if then_token.is_some() {
2273+
write!(f, " THEN")?;
2274+
}
22582275

2259-
if !statements.is_empty() {
2260-
write!(f, " ")?;
2261-
format_statement_list(f, statements)?;
2276+
if !conditional_statements.statements().is_empty() {
2277+
write!(f, " {conditional_statements}")?;
22622278
}
22632279

22642280
Ok(())
22652281
}
22662282
}
22672283

2284+
/// A list of statements in a [ConditionalStatementBlock].
2285+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
2286+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2287+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
2288+
pub enum ConditionalStatements {
2289+
/// SELECT 1; SELECT 2; SELECT 3; ...
2290+
Sequence { statements: Vec<Statement> },
2291+
/// BEGIN SELECT 1; SELECT 2; SELECT 3; ... END
2292+
BeginEnd {
2293+
begin_token: AttachedToken,
2294+
statements: Vec<Statement>,
2295+
end_token: AttachedToken,
2296+
},
2297+
}
2298+
2299+
impl ConditionalStatements {
2300+
pub fn statements(&self) -> &Vec<Statement> {
2301+
match self {
2302+
ConditionalStatements::Sequence { statements } => statements,
2303+
ConditionalStatements::BeginEnd { statements, .. } => statements,
2304+
}
2305+
}
2306+
}
2307+
2308+
impl fmt::Display for ConditionalStatements {
2309+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2310+
match self {
2311+
ConditionalStatements::Sequence { statements } => {
2312+
if !statements.is_empty() {
2313+
format_statement_list(f, statements)?;
2314+
}
2315+
Ok(())
2316+
}
2317+
ConditionalStatements::BeginEnd { statements, .. } => {
2318+
write!(f, "BEGIN ")?;
2319+
format_statement_list(f, statements)?;
2320+
write!(f, " END")
2321+
}
2322+
}
2323+
}
2324+
}
2325+
22682326
/// A `RAISE` statement.
22692327
///
22702328
/// Examples:

src/ast/spans.rs

+50-31
Original file line numberDiff line numberDiff line change
@@ -22,21 +22,22 @@ use crate::tokenizer::Span;
2222

2323
use super::{
2424
dcl::SecondaryRoles, value::ValueWithSpan, AccessExpr, AlterColumnOperation,
25-
AlterIndexOperation, AlterTableOperation, Array, Assignment, AssignmentTarget, CaseStatement,
26-
CloseCursor, ClusteredIndex, ColumnDef, ColumnOption, ColumnOptionDef, ConditionalStatements,
27-
ConflictTarget, ConnectBy, ConstraintCharacteristics, CopySource, CreateIndex, CreateTable,
28-
CreateTableOptions, Cte, Delete, DoUpdate, ExceptSelectItem, ExcludeSelectItem, Expr,
29-
ExprWithAlias, Fetch, FromTable, Function, FunctionArg, FunctionArgExpr,
30-
FunctionArgumentClause, FunctionArgumentList, FunctionArguments, GroupByExpr, HavingBound,
31-
IfStatement, IlikeSelectItem, Insert, Interpolate, InterpolateExpr, Join, JoinConstraint,
32-
JoinOperator, JsonPath, JsonPathElem, LateralView, LimitClause, MatchRecognizePattern, Measure,
33-
NamedWindowDefinition, ObjectName, ObjectNamePart, Offset, OnConflict, OnConflictAction,
34-
OnInsert, OrderBy, OrderByExpr, OrderByKind, Partition, PivotValueSource, ProjectionSelect,
35-
Query, RaiseStatement, RaiseStatementValue, ReferentialAction, RenameSelectItem,
36-
ReplaceSelectElement, ReplaceSelectItem, Select, SelectInto, SelectItem, SetExpr, SqlOption,
37-
Statement, Subscript, SymbolDefinition, TableAlias, TableAliasColumnDef, TableConstraint,
38-
TableFactor, TableObject, TableOptionsClustered, TableWithJoins, UpdateTableFromKind, Use,
39-
Value, Values, ViewColumnDef, WildcardAdditionalOptions, With, WithFill,
25+
AlterIndexOperation, AlterTableOperation, Array, Assignment, AssignmentTarget, AttachedToken,
26+
CaseStatement, CloseCursor, ClusteredIndex, ColumnDef, ColumnOption, ColumnOptionDef,
27+
ConditionalStatementBlock, ConditionalStatements, ConflictTarget, ConnectBy,
28+
ConstraintCharacteristics, CopySource, CreateIndex, CreateTable, CreateTableOptions, Cte,
29+
Delete, DoUpdate, ExceptSelectItem, ExcludeSelectItem, Expr, ExprWithAlias, Fetch, FromTable,
30+
Function, FunctionArg, FunctionArgExpr, FunctionArgumentClause, FunctionArgumentList,
31+
FunctionArguments, GroupByExpr, HavingBound, IfStatement, IlikeSelectItem, Insert, Interpolate,
32+
InterpolateExpr, Join, JoinConstraint, JoinOperator, JsonPath, JsonPathElem, LateralView,
33+
LimitClause, MatchRecognizePattern, Measure, NamedWindowDefinition, ObjectName, ObjectNamePart,
34+
Offset, OnConflict, OnConflictAction, OnInsert, OrderBy, OrderByExpr, OrderByKind, Partition,
35+
PivotValueSource, ProjectionSelect, Query, RaiseStatement, RaiseStatementValue,
36+
ReferentialAction, RenameSelectItem, ReplaceSelectElement, ReplaceSelectItem, Select,
37+
SelectInto, SelectItem, SetExpr, SqlOption, Statement, Subscript, SymbolDefinition, TableAlias,
38+
TableAliasColumnDef, TableConstraint, TableFactor, TableObject, TableOptionsClustered,
39+
TableWithJoins, UpdateTableFromKind, Use, Value, Values, ViewColumnDef,
40+
WildcardAdditionalOptions, With, WithFill,
4041
};
4142

4243
/// Given an iterator of spans, return the [Span::union] of all spans.
@@ -739,19 +740,14 @@ impl Spanned for CreateIndex {
739740
impl Spanned for CaseStatement {
740741
fn span(&self) -> Span {
741742
let CaseStatement {
742-
match_expr,
743-
when_blocks,
744-
else_block,
745-
has_end_case: _,
743+
case_token: AttachedToken(start),
744+
match_expr: _,
745+
when_blocks: _,
746+
else_block: _,
747+
end_case_token: AttachedToken(end),
746748
} = self;
747749

748-
union_spans(
749-
match_expr
750-
.iter()
751-
.map(|e| e.span())
752-
.chain(when_blocks.iter().map(|b| b.span()))
753-
.chain(else_block.iter().flat_map(|e| e.iter().map(|s| s.span()))),
754-
)
750+
union_spans([start.span, end.span].into_iter())
755751
}
756752
}
757753

@@ -761,25 +757,48 @@ impl Spanned for IfStatement {
761757
if_block,
762758
elseif_blocks,
763759
else_block,
760+
end_token,
764761
} = self;
765762

766763
union_spans(
767764
iter::once(if_block.span())
768765
.chain(elseif_blocks.iter().map(|b| b.span()))
769-
.chain(else_block.iter().flat_map(|e| e.iter().map(|s| s.span()))),
766+
.chain(else_block.as_ref().map(|b| b.span()))
767+
.chain(end_token.as_ref().map(|AttachedToken(t)| t.span)),
770768
)
771769
}
772770
}
773771

774772
impl Spanned for ConditionalStatements {
775773
fn span(&self) -> Span {
776-
let ConditionalStatements {
774+
match self {
775+
ConditionalStatements::Sequence { statements } => {
776+
union_spans(statements.iter().map(|s| s.span()))
777+
}
778+
ConditionalStatements::BeginEnd {
779+
begin_token: AttachedToken(start),
780+
statements: _,
781+
end_token: AttachedToken(end),
782+
} => union_spans([start.span, end.span].into_iter()),
783+
}
784+
}
785+
}
786+
787+
impl Spanned for ConditionalStatementBlock {
788+
fn span(&self) -> Span {
789+
let ConditionalStatementBlock {
790+
start_token: AttachedToken(start_token),
777791
condition,
778-
statements,
779-
kind: _,
792+
then_token,
793+
conditional_statements,
780794
} = self;
781795

782-
union_spans(iter::once(condition.span()).chain(statements.iter().map(|s| s.span())))
796+
union_spans(
797+
iter::once(start_token.span)
798+
.chain(condition.as_ref().map(|c| c.span()))
799+
.chain(then_token.as_ref().map(|AttachedToken(t)| t.span))
800+
.chain(iter::once(conditional_statements.span())),
801+
)
783802
}
784803
}
785804

0 commit comments

Comments
 (0)