@@ -505,6 +505,17 @@ impl<'a> Parser<'a> {
505505 match self.peek_token().token {
506506 Token::EOF => break,
507507
508+ // end of statement
509+ Token::Word(word) => {
510+ if expecting_statement_delimiter && word.keyword == Keyword::END {
511+ break;
512+ }
513+
514+ if expecting_statement_delimiter && word.keyword == Keyword::GO {
515+ expecting_statement_delimiter = false;
516+ }
517+ }
518+
508519 // don't expect a semicolon statement delimiter after a newline when not otherwise required
509520 Token::Whitespace(Whitespace::Newline) => {
510521 if !self.options.require_semicolon_stmt_delimiter {
@@ -519,8 +530,10 @@ impl<'a> Parser<'a> {
519530 }
520531
521532 let statement = self.parse_statement()?;
533+ // Treat batch delimiter as an end of statement, so no additional statement delimiter expected here
534+ expecting_statement_delimiter = !matches!(statement, Statement::Go(_))
535+ && self.options.require_semicolon_stmt_delimiter;
522536 stmts.push(statement);
523- expecting_statement_delimiter = self.options.require_semicolon_stmt_delimiter;
524537 }
525538 Ok(stmts)
526539 }
@@ -660,6 +673,10 @@ impl<'a> Parser<'a> {
660673 Keyword::COMMENT if self.dialect.supports_comment_on() => self.parse_comment(),
661674 Keyword::PRINT => self.parse_print(),
662675 Keyword::RETURN => self.parse_return(),
676+ Keyword::GO => {
677+ self.prev_token();
678+ self.parse_go()
679+ }
663680 _ => self.expected("an SQL statement", next_token),
664681 },
665682 Token::LParen => {
@@ -4064,6 +4081,17 @@ impl<'a> Parser<'a> {
40644081 })
40654082 }
40664083
4084+ /// Return nth previous token, possibly whitespace
4085+ /// (or [`Token::EOF`] when before the beginning of the stream).
4086+ pub(crate) fn peek_prev_nth_token_no_skip_ref(&self, n: usize) -> &TokenWithSpan {
4087+ // 0 = next token, -1 = current token, -2 = previous token
4088+ let peek_index = self.index.saturating_sub(1).saturating_sub(n);
4089+ if peek_index == 0 {
4090+ return &EOF_TOKEN;
4091+ }
4092+ self.tokens.get(peek_index).unwrap_or(&EOF_TOKEN)
4093+ }
4094+
40674095 /// Return true if the next tokens exactly `expected`
40684096 ///
40694097 /// Does not advance the current token.
@@ -4180,6 +4208,29 @@ impl<'a> Parser<'a> {
41804208 )
41814209 }
41824210
4211+ /// Look backwards in the token stream and expect that there was only whitespace tokens until the previous newline or beginning of string
4212+ pub(crate) fn prev_only_whitespace_until_newline(&mut self) -> bool {
4213+ let mut look_back_count = 1;
4214+ loop {
4215+ let prev_token = self.peek_prev_nth_token_no_skip_ref(look_back_count);
4216+ match prev_token.token {
4217+ Token::EOF => break true,
4218+ Token::Whitespace(ref w) => match w {
4219+ Whitespace::Newline => break true,
4220+ // special consideration required for single line comments since that string includes the newline
4221+ Whitespace::SingleLineComment { comment, prefix: _ } => {
4222+ if comment.ends_with('\n') {
4223+ break true;
4224+ }
4225+ look_back_count += 1;
4226+ }
4227+ _ => look_back_count += 1,
4228+ },
4229+ _ => break false,
4230+ };
4231+ }
4232+ }
4233+
41834234 /// If the current token is the `expected` keyword, consume it and returns
41844235 /// true. Otherwise, no tokens are consumed and returns false.
41854236 #[must_use]
@@ -16534,6 +16585,71 @@ impl<'a> Parser<'a> {
1653416585 }
1653516586 }
1653616587
16588+ /// Parse [Statement::Go]
16589+ fn parse_go(&mut self) -> Result<Statement, ParserError> {
16590+ self.expect_keyword_is(Keyword::GO)?;
16591+
16592+ // disambiguate between GO as batch delimiter & GO as identifier (etc)
16593+ // compare:
16594+ // ```sql
16595+ // select 1 go
16596+ // ```
16597+ // vs
16598+ // ```sql
16599+ // select 1
16600+ // go
16601+ // ```
16602+ if !self.prev_only_whitespace_until_newline() {
16603+ parser_err!(
16604+ "GO may only be preceded by whitespace on a line",
16605+ self.peek_token().span.start
16606+ )?;
16607+ }
16608+
16609+ let count = loop {
16610+ // using this peek function because we want to halt this statement parsing upon newline
16611+ let next_token = self.peek_token_no_skip();
16612+ match next_token.token {
16613+ Token::EOF => break None::<u64>,
16614+ Token::Whitespace(ref w) => match w {
16615+ Whitespace::Newline => break None,
16616+ _ => _ = self.next_token_no_skip(),
16617+ },
16618+ Token::Number(s, _) => {
16619+ let value = Some(Self::parse::<u64>(s, next_token.span.start)?);
16620+ self.advance_token();
16621+ break value;
16622+ }
16623+ _ => self.expected("literal int or newline", next_token)?,
16624+ };
16625+ };
16626+
16627+ loop {
16628+ let next_token = self.peek_token_no_skip();
16629+ match next_token.token {
16630+ Token::EOF => break,
16631+ Token::Whitespace(ref w) => match w {
16632+ Whitespace::Newline => break,
16633+ Whitespace::SingleLineComment { comment, prefix: _ } => {
16634+ if comment.ends_with('\n') {
16635+ break;
16636+ }
16637+ _ = self.next_token_no_skip();
16638+ }
16639+ _ => _ = self.next_token_no_skip(),
16640+ },
16641+ _ => {
16642+ parser_err!(
16643+ "GO must be followed by a newline or EOF",
16644+ self.peek_token().span.start
16645+ )?;
16646+ }
16647+ };
16648+ }
16649+
16650+ Ok(Statement::Go(GoStatement { count }))
16651+ }
16652+
1653716653 /// Consume the parser and return its underlying token buffer
1653816654 pub fn into_tokens(self) -> Vec<TokenWithSpan> {
1653916655 self.tokens
@@ -16779,6 +16895,31 @@ mod tests {
1677916895 })
1678016896 }
1678116897
16898+ #[test]
16899+ fn test_peek_prev_nth_token_no_skip_ref() {
16900+ all_dialects().run_parser_method(
16901+ "SELECT 1;\n-- a comment\nRAISERROR('test', 16, 0);",
16902+ |parser| {
16903+ parser.index = 1;
16904+ assert_eq!(parser.peek_prev_nth_token_no_skip_ref(0), &Token::EOF);
16905+ assert_eq!(parser.index, 1);
16906+ parser.index = 7;
16907+ assert_eq!(
16908+ parser.token_at(parser.index - 1).token,
16909+ Token::Word(Word {
16910+ value: "RAISERROR".to_string(),
16911+ quote_style: None,
16912+ keyword: Keyword::RAISERROR,
16913+ })
16914+ );
16915+ assert_eq!(
16916+ parser.peek_prev_nth_token_no_skip_ref(2),
16917+ &Token::Whitespace(Whitespace::Newline)
16918+ );
16919+ },
16920+ );
16921+ }
16922+
1678216923 #[cfg(test)]
1678316924 mod test_parse_data_type {
1678416925 use crate::ast::{
0 commit comments