@@ -265,6 +265,22 @@ impl ParserOptions {
265265 self.unescape = unescape;
266266 self
267267 }
268+
269+ /// Set if semicolon statement delimiters are required.
270+ ///
271+ /// If this option is `true`, the following SQL will not parse. If the option is `false`, the SQL will parse.
272+ ///
273+ /// ```sql
274+ /// SELECT 1
275+ /// SELECT 2
276+ /// ```
277+ pub fn with_require_semicolon_stmt_delimiter(
278+ mut self,
279+ require_semicolon_stmt_delimiter: bool,
280+ ) -> Self {
281+ self.require_semicolon_stmt_delimiter = require_semicolon_stmt_delimiter;
282+ self
283+ }
268284}
269285
270286#[derive(Copy, Clone)]
@@ -355,7 +371,11 @@ impl<'a> Parser<'a> {
355371 state: ParserState::Normal,
356372 dialect,
357373 recursion_counter: RecursionCounter::new(DEFAULT_REMAINING_DEPTH),
358- options: ParserOptions::new().with_trailing_commas(dialect.supports_trailing_commas()),
374+ options: ParserOptions::new()
375+ .with_trailing_commas(dialect.supports_trailing_commas())
376+ .with_require_semicolon_stmt_delimiter(
377+ !dialect.supports_statements_without_semicolon_delimiter(),
378+ ),
359379 }
360380 }
361381
@@ -478,10 +498,10 @@ impl<'a> Parser<'a> {
478498 match self.peek_token().token {
479499 Token::EOF => break,
480500
481- // end of statement
482- Token::Word(word ) => {
483- if expecting_statement_delimiter && word.keyword == Keyword::END {
484- break ;
501+ // don't expect a semicolon statement delimiter after a newline when not otherwise required
502+ Token::Whitespace(Whitespace::Newline ) => {
503+ if !self.options.require_semicolon_stmt_delimiter {
504+ expecting_statement_delimiter = false ;
485505 }
486506
487507 if expecting_statement_delimiter && word.keyword == Keyword::GO {
@@ -496,9 +516,9 @@ impl<'a> Parser<'a> {
496516 }
497517
498518 let statement = self.parse_statement()?;
499- // Treat batch delimiter as an end of statement, so no additional statement delimiter expected here
500- expecting_statement_delimiter = !matches!(statement, Statement::Go(_));
501519 stmts.push(statement);
520+ // Treat batch delimiter as an end of statement, so no additional statement delimiter expected here
521+ expecting_statement_delimiter = !matches!(statement, Statement::Go(_)) && self.options.require_semicolon_stmt_delimiter;
502522 }
503523 Ok(stmts)
504524 }
@@ -4559,6 +4579,18 @@ impl<'a> Parser<'a> {
45594579 return Ok(vec![]);
45604580 }
45614581
4582+ if end_token == Token::SemiColon
4583+ && self
4584+ .dialect
4585+ .supports_statements_without_semicolon_delimiter()
4586+ {
4587+ if let Token::Word(ref kw) = self.peek_token().token {
4588+ if kw.keyword != Keyword::NoKeyword {
4589+ return Ok(vec![]);
4590+ }
4591+ }
4592+ }
4593+
45624594 if self.options.trailing_commas && self.peek_tokens() == [Token::Comma, end_token] {
45634595 let _ = self.consume_token(&Token::Comma);
45644596 return Ok(vec![]);
@@ -4576,6 +4608,9 @@ impl<'a> Parser<'a> {
45764608 ) -> Result<Vec<Statement>, ParserError> {
45774609 let mut values = vec![];
45784610 loop {
4611+ // ignore empty statements (between successive statement delimiters)
4612+ while self.consume_token(&Token::SemiColon) {}
4613+
45794614 match &self.peek_nth_token_ref(0).token {
45804615 Token::EOF => break,
45814616 Token::Word(w) => {
@@ -4587,7 +4622,13 @@ impl<'a> Parser<'a> {
45874622 }
45884623
45894624 values.push(self.parse_statement()?);
4590- self.expect_token(&Token::SemiColon)?;
4625+
4626+ if self.options.require_semicolon_stmt_delimiter {
4627+ self.expect_token(&Token::SemiColon)?;
4628+ }
4629+
4630+ // ignore empty statements (between successive statement delimiters)
4631+ while self.consume_token(&Token::SemiColon) {}
45914632 }
45924633 Ok(values)
45934634 }
@@ -16413,7 +16454,28 @@ impl<'a> Parser<'a> {
1641316454
1641416455 /// Parse [Statement::Return]
1641516456 fn parse_return(&mut self) -> Result<Statement, ParserError> {
16416- match self.maybe_parse(|p| p.parse_expr())? {
16457+ let rs = self.maybe_parse(|p| {
16458+ let expr = p.parse_expr()?;
16459+
16460+ match &expr {
16461+ Expr::Value(_)
16462+ | Expr::Function(_)
16463+ | Expr::UnaryOp { .. }
16464+ | Expr::BinaryOp { .. }
16465+ | Expr::Case { .. }
16466+ | Expr::Cast { .. }
16467+ | Expr::Convert { .. }
16468+ | Expr::Subquery(_) => Ok(expr),
16469+ // todo: how to retstrict to variables?
16470+ Expr::Identifier(id) if id.value.starts_with('@') => Ok(expr),
16471+ _ => parser_err!(
16472+ "Non-returnable expression found following RETURN",
16473+ p.peek_token().span.start
16474+ ),
16475+ }
16476+ })?;
16477+
16478+ match rs {
1641716479 Some(expr) => Ok(Statement::Return(ReturnStatement {
1641816480 value: Some(ReturnStatementValue::Expr(expr)),
1641916481 })),
0 commit comments