Skip to content

Commit ec5194c

Browse files
committed
Allow stored procedures to be defined without BEGIN/END
- formerly, a semicolon after the last statement in a procedure was non-canonical (because they were added via `join`); a `BeginEndStatements` statements list will always write them out - `BeginEndStatements` begin/end tokens won't be written when empty - EOF now concludes parsing a statement list
1 parent ef6aa55 commit ec5194c

File tree

3 files changed

+70
-54
lines changed

3 files changed

+70
-54
lines changed

src/ast/mod.rs

+3-6
Original file line numberDiff line numberDiff line change
@@ -3826,7 +3826,7 @@ pub enum Statement {
38263826
or_alter: bool,
38273827
name: ObjectName,
38283828
params: Option<Vec<ProcedureParam>>,
3829-
body: Vec<Statement>,
3829+
body: BeginEndStatements,
38303830
},
38313831
/// ```sql
38323832
/// CREATE MACRO
@@ -4702,11 +4702,8 @@ impl fmt::Display for Statement {
47024702
write!(f, " ({})", display_comma_separated(p))?;
47034703
}
47044704
}
4705-
write!(
4706-
f,
4707-
" AS BEGIN {body} END",
4708-
body = display_separated(body, "; ")
4709-
)
4705+
4706+
write!(f, " AS {body}")
47104707
}
47114708
Statement::CreateMacro {
47124709
or_replace,

src/parser/mod.rs

+18-4
Original file line numberDiff line numberDiff line change
@@ -15474,14 +15474,28 @@ impl<'a> Parser<'a> {
1547415474
let name = self.parse_object_name(false)?;
1547515475
let params = self.parse_optional_procedure_parameters()?;
1547615476
self.expect_keyword_is(Keyword::AS)?;
15477-
self.expect_keyword_is(Keyword::BEGIN)?;
15478-
let statements = self.parse_statements()?;
15479-
self.expect_keyword_is(Keyword::END)?;
15477+
15478+
let begin_token: AttachedToken = self
15479+
.expect_keyword(Keyword::BEGIN)
15480+
.map(AttachedToken)
15481+
.unwrap_or_else(|_| AttachedToken::empty());
15482+
let statements = self.parse_statement_list(&[Keyword::END])?;
15483+
let end_token = match &begin_token.0.token {
15484+
Token::Word(w) if w.keyword == Keyword::BEGIN => {
15485+
AttachedToken(self.expect_keyword(Keyword::END)?)
15486+
}
15487+
_ => AttachedToken::empty(),
15488+
};
15489+
1548015490
Ok(Statement::CreateProcedure {
1548115491
name,
1548215492
or_alter,
1548315493
params,
15484-
body: statements,
15494+
body: BeginEndStatements {
15495+
begin_token,
15496+
statements,
15497+
end_token,
15498+
},
1548515499
})
1548615500
}
1548715501

tests/sqlparser_mssql.rs

+49-44
Original file line numberDiff line numberDiff line change
@@ -100,48 +100,52 @@ fn parse_mssql_delimited_identifiers() {
100100

101101
#[test]
102102
fn parse_create_procedure() {
103-
let sql = "CREATE OR ALTER PROCEDURE test (@foo INT, @bar VARCHAR(256)) AS BEGIN SELECT 1 END";
103+
let sql = "CREATE OR ALTER PROCEDURE test (@foo INT, @bar VARCHAR(256)) AS BEGIN SELECT 1; END";
104104

105105
assert_eq!(
106106
ms().verified_stmt(sql),
107107
Statement::CreateProcedure {
108108
or_alter: true,
109-
body: vec![Statement::Query(Box::new(Query {
110-
with: None,
111-
limit_clause: None,
112-
fetch: None,
113-
locks: vec![],
114-
for_clause: None,
115-
order_by: None,
116-
settings: None,
117-
format_clause: None,
118-
pipe_operators: vec![],
119-
body: Box::new(SetExpr::Select(Box::new(Select {
120-
select_token: AttachedToken::empty(),
121-
distinct: None,
122-
top: None,
123-
top_before_distinct: false,
124-
projection: vec![SelectItem::UnnamedExpr(Expr::Value(
125-
(number("1")).with_empty_span()
126-
))],
127-
into: None,
128-
from: vec![],
129-
lateral_views: vec![],
130-
prewhere: None,
131-
selection: None,
132-
group_by: GroupByExpr::Expressions(vec![], vec![]),
133-
cluster_by: vec![],
134-
distribute_by: vec![],
135-
sort_by: vec![],
136-
having: None,
137-
named_window: vec![],
138-
window_before_qualify: false,
139-
qualify: None,
140-
value_table_mode: None,
141-
connect_by: None,
142-
flavor: SelectFlavor::Standard,
143-
})))
144-
}))],
109+
body: BeginEndStatements {
110+
begin_token: AttachedToken::empty(),
111+
statements: vec![Statement::Query(Box::new(Query {
112+
with: None,
113+
limit_clause: None,
114+
fetch: None,
115+
locks: vec![],
116+
for_clause: None,
117+
order_by: None,
118+
settings: None,
119+
format_clause: None,
120+
pipe_operators: vec![],
121+
body: Box::new(SetExpr::Select(Box::new(Select {
122+
select_token: AttachedToken::empty(),
123+
distinct: None,
124+
top: None,
125+
top_before_distinct: false,
126+
projection: vec![SelectItem::UnnamedExpr(Expr::Value(
127+
(number("1")).with_empty_span()
128+
))],
129+
into: None,
130+
from: vec![],
131+
lateral_views: vec![],
132+
prewhere: None,
133+
selection: None,
134+
group_by: GroupByExpr::Expressions(vec![], vec![]),
135+
cluster_by: vec![],
136+
distribute_by: vec![],
137+
sort_by: vec![],
138+
having: None,
139+
named_window: vec![],
140+
window_before_qualify: false,
141+
qualify: None,
142+
value_table_mode: None,
143+
connect_by: None,
144+
flavor: SelectFlavor::Standard,
145+
})))
146+
}))],
147+
end_token: AttachedToken::empty(),
148+
},
145149
params: Some(vec![
146150
ProcedureParam {
147151
name: Ident {
@@ -174,19 +178,20 @@ fn parse_create_procedure() {
174178

175179
#[test]
176180
fn parse_mssql_create_procedure() {
177-
let _ = ms_and_generic().verified_stmt("CREATE OR ALTER PROCEDURE foo AS BEGIN SELECT 1 END");
178-
let _ = ms_and_generic().verified_stmt("CREATE PROCEDURE foo AS BEGIN SELECT 1 END");
181+
let _ = ms_and_generic().verified_stmt("CREATE OR ALTER PROCEDURE foo AS SELECT 1;");
182+
let _ = ms_and_generic().verified_stmt("CREATE OR ALTER PROCEDURE foo AS BEGIN SELECT 1; END");
183+
let _ = ms_and_generic().verified_stmt("CREATE PROCEDURE foo AS BEGIN SELECT 1; END");
179184
let _ = ms().verified_stmt(
180-
"CREATE PROCEDURE foo AS BEGIN SELECT [myColumn] FROM [myschema].[mytable] END",
185+
"CREATE PROCEDURE foo AS BEGIN SELECT [myColumn] FROM [myschema].[mytable]; END",
181186
);
182187
let _ = ms_and_generic().verified_stmt(
183-
"CREATE PROCEDURE foo (@CustomerName NVARCHAR(50)) AS BEGIN SELECT * FROM DEV END",
188+
"CREATE PROCEDURE foo (@CustomerName NVARCHAR(50)) AS BEGIN SELECT * FROM DEV; END",
184189
);
185-
let _ = ms().verified_stmt("CREATE PROCEDURE [foo] AS BEGIN UPDATE bar SET col = 'test' END");
190+
let _ = ms().verified_stmt("CREATE PROCEDURE [foo] AS BEGIN UPDATE bar SET col = 'test'; END");
186191
// Test a statement with END in it
187-
let _ = ms().verified_stmt("CREATE PROCEDURE [foo] AS BEGIN SELECT [foo], CASE WHEN [foo] IS NULL THEN 'empty' ELSE 'notempty' END AS [foo] END");
192+
let _ = ms().verified_stmt("CREATE PROCEDURE [foo] AS BEGIN SELECT [foo], CASE WHEN [foo] IS NULL THEN 'empty' ELSE 'notempty' END AS [foo]; END");
188193
// Multiple statements
189-
let _ = ms().verified_stmt("CREATE PROCEDURE [foo] AS BEGIN UPDATE bar SET col = 'test'; SELECT [foo] FROM BAR WHERE [FOO] > 10 END");
194+
let _ = ms().verified_stmt("CREATE PROCEDURE [foo] AS BEGIN UPDATE bar SET col = 'test'; SELECT [foo] FROM BAR WHERE [FOO] > 10; END");
190195
}
191196

192197
#[test]

0 commit comments

Comments
 (0)