Skip to content

Commit c22e57a

Browse files
iffyioayman-sigma
authored andcommitted
Add CASE and IF statement support (apache#1741)
1 parent 64c4a17 commit c22e57a

File tree

5 files changed

+473
-22
lines changed

5 files changed

+473
-22
lines changed

src/ast/mod.rs

+190-8
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,15 @@ where
151151
DisplaySeparated { slice, sep: ", " }
152152
}
153153

154+
/// Writes the given statements to the formatter, each ending with
155+
/// a semicolon and space separated.
156+
fn format_statement_list(f: &mut fmt::Formatter, statements: &[Statement]) -> fmt::Result {
157+
write!(f, "{}", display_separated(statements, "; "))?;
158+
// We manually insert semicolon for the last statement,
159+
// since display_separated doesn't handle that case.
160+
write!(f, ";")
161+
}
162+
154163
/// An identifier, decomposed into its value or character data and the quote style.
155164
#[derive(Debug, Clone, PartialOrd, Ord)]
156165
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@@ -2098,6 +2107,173 @@ pub enum Password {
20982107
NullPassword,
20992108
}
21002109

2110+
/// A `CASE` statement.
2111+
///
2112+
/// Examples:
2113+
/// ```sql
2114+
/// CASE
2115+
/// WHEN EXISTS(SELECT 1)
2116+
/// THEN SELECT 1 FROM T;
2117+
/// WHEN EXISTS(SELECT 2)
2118+
/// THEN SELECT 1 FROM U;
2119+
/// ELSE
2120+
/// SELECT 1 FROM V;
2121+
/// END CASE;
2122+
/// ```
2123+
///
2124+
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/procedural-language#case_search_expression)
2125+
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/snowflake-scripting/case)
2126+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
2127+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2128+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
2129+
pub struct CaseStatement {
2130+
pub match_expr: Option<Expr>,
2131+
pub when_blocks: Vec<ConditionalStatements>,
2132+
pub else_block: Option<Vec<Statement>>,
2133+
/// TRUE if the statement ends with `END CASE` (vs `END`).
2134+
pub has_end_case: bool,
2135+
}
2136+
2137+
impl fmt::Display for CaseStatement {
2138+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2139+
let CaseStatement {
2140+
match_expr,
2141+
when_blocks,
2142+
else_block,
2143+
has_end_case,
2144+
} = self;
2145+
2146+
write!(f, "CASE")?;
2147+
2148+
if let Some(expr) = match_expr {
2149+
write!(f, " {expr}")?;
2150+
}
2151+
2152+
if !when_blocks.is_empty() {
2153+
write!(f, " {}", display_separated(when_blocks, " "))?;
2154+
}
2155+
2156+
if let Some(else_block) = else_block {
2157+
write!(f, " ELSE ")?;
2158+
format_statement_list(f, else_block)?;
2159+
}
2160+
2161+
write!(f, " END")?;
2162+
if *has_end_case {
2163+
write!(f, " CASE")?;
2164+
}
2165+
2166+
Ok(())
2167+
}
2168+
}
2169+
2170+
/// An `IF` statement.
2171+
///
2172+
/// Examples:
2173+
/// ```sql
2174+
/// IF TRUE THEN
2175+
/// SELECT 1;
2176+
/// SELECT 2;
2177+
/// ELSEIF TRUE THEN
2178+
/// SELECT 3;
2179+
/// ELSE
2180+
/// SELECT 4;
2181+
/// END IF
2182+
/// ```
2183+
///
2184+
/// [BigQuery](https://cloud.google.com/bigquery/docs/reference/standard-sql/procedural-language#if)
2185+
/// [Snowflake](https://docs.snowflake.com/en/sql-reference/snowflake-scripting/if)
2186+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
2187+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2188+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
2189+
pub struct IfStatement {
2190+
pub if_block: ConditionalStatements,
2191+
pub elseif_blocks: Vec<ConditionalStatements>,
2192+
pub else_block: Option<Vec<Statement>>,
2193+
}
2194+
2195+
impl fmt::Display for IfStatement {
2196+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2197+
let IfStatement {
2198+
if_block,
2199+
elseif_blocks,
2200+
else_block,
2201+
} = self;
2202+
2203+
write!(f, "{if_block}")?;
2204+
2205+
if !elseif_blocks.is_empty() {
2206+
write!(f, " {}", display_separated(elseif_blocks, " "))?;
2207+
}
2208+
2209+
if let Some(else_block) = else_block {
2210+
write!(f, " ELSE ")?;
2211+
format_statement_list(f, else_block)?;
2212+
}
2213+
2214+
write!(f, " END IF")?;
2215+
2216+
Ok(())
2217+
}
2218+
}
2219+
2220+
/// Represents a type of [ConditionalStatements]
2221+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
2222+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2223+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
2224+
pub enum ConditionalStatementKind {
2225+
/// `WHEN <condition> THEN <statements>`
2226+
When,
2227+
/// `IF <condition> THEN <statements>`
2228+
If,
2229+
/// `ELSEIF <condition> THEN <statements>`
2230+
ElseIf,
2231+
}
2232+
2233+
/// A block within a [Statement::Case] or [Statement::If]-like statement
2234+
///
2235+
/// Examples:
2236+
/// ```sql
2237+
/// WHEN EXISTS(SELECT 1) THEN SELECT 1;
2238+
///
2239+
/// IF TRUE THEN SELECT 1; SELECT 2;
2240+
/// ```
2241+
#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
2242+
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2243+
#[cfg_attr(feature = "visitor", derive(Visit, VisitMut))]
2244+
pub struct ConditionalStatements {
2245+
/// The condition expression.
2246+
pub condition: Expr,
2247+
/// Statement list of the `THEN` clause.
2248+
pub statements: Vec<Statement>,
2249+
pub kind: ConditionalStatementKind,
2250+
}
2251+
2252+
impl fmt::Display for ConditionalStatements {
2253+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
2254+
let ConditionalStatements {
2255+
condition: expr,
2256+
statements,
2257+
kind,
2258+
} = self;
2259+
2260+
let kind = match kind {
2261+
ConditionalStatementKind::When => "WHEN",
2262+
ConditionalStatementKind::If => "IF",
2263+
ConditionalStatementKind::ElseIf => "ELSEIF",
2264+
};
2265+
2266+
write!(f, "{kind} {expr} THEN")?;
2267+
2268+
if !statements.is_empty() {
2269+
write!(f, " ")?;
2270+
format_statement_list(f, statements)?;
2271+
}
2272+
2273+
Ok(())
2274+
}
2275+
}
2276+
21012277
/// Represents an expression assignment within a variable `DECLARE` statement.
21022278
///
21032279
/// Examples:
@@ -2665,6 +2841,10 @@ pub enum Statement {
26652841
file_format: Option<FileFormat>,
26662842
source: Box<Query>,
26672843
},
2844+
/// A `CASE` statement.
2845+
Case(CaseStatement),
2846+
/// An `IF` statement.
2847+
If(IfStatement),
26682848
/// ```sql
26692849
/// CALL <function>
26702850
/// ```
@@ -3958,6 +4138,12 @@ impl fmt::Display for Statement {
39584138
}
39594139
Ok(())
39604140
}
4141+
Statement::Case(stmt) => {
4142+
write!(f, "{stmt}")
4143+
}
4144+
Statement::If(stmt) => {
4145+
write!(f, "{stmt}")
4146+
}
39614147
Statement::AttachDatabase {
39624148
schema_name,
39634149
database_file_name,
@@ -4960,18 +5146,14 @@ impl fmt::Display for Statement {
49605146
write!(f, " {}", display_comma_separated(modes))?;
49615147
}
49625148
if !statements.is_empty() {
4963-
write!(f, " {}", display_separated(statements, "; "))?;
4964-
// We manually insert semicolon for the last statement,
4965-
// since display_separated doesn't handle that case.
4966-
write!(f, ";")?;
5149+
write!(f, " ")?;
5150+
format_statement_list(f, statements)?;
49675151
}
49685152
if let Some(exception_statements) = exception_statements {
49695153
write!(f, " EXCEPTION WHEN ERROR THEN")?;
49705154
if !exception_statements.is_empty() {
4971-
write!(f, " {}", display_separated(exception_statements, "; "))?;
4972-
// We manually insert semicolon for the last statement,
4973-
// since display_separated doesn't handle that case.
4974-
write!(f, ";")?;
5155+
write!(f, " ")?;
5156+
format_statement_list(f, exception_statements)?;
49755157
}
49765158
}
49775159
if *has_end_keyword {

src/ast/spans.rs

+64-14
Original file line numberDiff line numberDiff line change
@@ -22,20 +22,21 @@ use crate::tokenizer::Span;
2222

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

4142
/// Given an iterator of spans, return the [Span::union] of all spans.
@@ -334,6 +335,8 @@ impl Spanned for Statement {
334335
file_format: _,
335336
source,
336337
} => source.span(),
338+
Statement::Case(stmt) => stmt.span(),
339+
Statement::If(stmt) => stmt.span(),
337340
Statement::Call(function) => function.span(),
338341
Statement::Copy {
339342
source,
@@ -732,6 +735,53 @@ impl Spanned for CreateIndex {
732735
}
733736
}
734737

738+
impl Spanned for CaseStatement {
739+
fn span(&self) -> Span {
740+
let CaseStatement {
741+
match_expr,
742+
when_blocks,
743+
else_block,
744+
has_end_case: _,
745+
} = self;
746+
747+
union_spans(
748+
match_expr
749+
.iter()
750+
.map(|e| e.span())
751+
.chain(when_blocks.iter().map(|b| b.span()))
752+
.chain(else_block.iter().flat_map(|e| e.iter().map(|s| s.span()))),
753+
)
754+
}
755+
}
756+
757+
impl Spanned for IfStatement {
758+
fn span(&self) -> Span {
759+
let IfStatement {
760+
if_block,
761+
elseif_blocks,
762+
else_block,
763+
} = self;
764+
765+
union_spans(
766+
iter::once(if_block.span())
767+
.chain(elseif_blocks.iter().map(|b| b.span()))
768+
.chain(else_block.iter().flat_map(|e| e.iter().map(|s| s.span()))),
769+
)
770+
}
771+
}
772+
773+
impl Spanned for ConditionalStatements {
774+
fn span(&self) -> Span {
775+
let ConditionalStatements {
776+
condition,
777+
statements,
778+
kind: _,
779+
} = self;
780+
781+
union_spans(iter::once(condition.span()).chain(statements.iter().map(|s| s.span())))
782+
}
783+
}
784+
735785
/// # partial span
736786
///
737787
/// Missing spans:

src/keywords.rs

+1
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,7 @@ define_keywords!(
297297
ELEMENT,
298298
ELEMENTS,
299299
ELSE,
300+
ELSEIF,
300301
EMPTY,
301302
ENABLE,
302303
ENABLE_SCHEMA_EVOLUTION,

0 commit comments

Comments
 (0)