@@ -531,6 +531,12 @@ impl<'a> Parser<'a> {
531531 if expecting_statement_delimiter && word.keyword == Keyword::END {
532532 break;
533533 }
534+
535+ // MSSQL: the `GO` keyword is a batch separator which also means it concludes the current statement
536+ // `GO` may not be followed by a semicolon, so turn off that expectation
537+ if expecting_statement_delimiter && word.keyword == Keyword::GO {
538+ expecting_statement_delimiter = false;
539+ }
534540 }
535541 // don't expect a semicolon statement delimiter after a newline when not otherwise required
536542 Token::Whitespace(Whitespace::Newline) => {
@@ -546,8 +552,10 @@ impl<'a> Parser<'a> {
546552 }
547553
548554 let statement = self.parse_statement()?;
555+ // MSSQL: the `GO` keyword is a batch separator which also means it concludes the current statement
556+ // `GO` may not be followed by a semicolon, so turn off that expectation
557+ expecting_statement_delimiter = !matches!(statement, Statement::Go(_)) && self.options.require_semicolon_stmt_delimiter;
549558 stmts.push(statement);
550- expecting_statement_delimiter = self.options.require_semicolon_stmt_delimiter;
551559 }
552560 Ok(stmts)
553561 }
@@ -740,6 +748,10 @@ impl<'a> Parser<'a> {
740748 self.parse_vacuum()
741749 }
742750 Keyword::RESET => self.parse_reset().map(Into::into),
751+ Keyword::GO => {
752+ self.prev_token();
753+ self.parse_go()
754+ }
743755 _ => self.expected("an SQL statement", next_token),
744756 },
745757 Token::LParen => {
@@ -4473,6 +4485,17 @@ impl<'a> Parser<'a> {
44734485 self.tokens.get(self.index + n).unwrap_or(&EOF_TOKEN)
44744486 }
44754487
4488+ /// Return nth previous token, possibly whitespace
4489+ /// (or [`Token::EOF`] when before the beginning of the stream).
4490+ pub(crate) fn peek_prev_nth_token_no_skip_ref(&self, n: usize) -> &TokenWithSpan {
4491+ // 0 = next token, -1 = current token, -2 = previous token
4492+ let peek_index = self.index.saturating_sub(1).saturating_sub(n);
4493+ if peek_index == 0 {
4494+ return &EOF_TOKEN;
4495+ }
4496+ self.tokens.get(peek_index).unwrap_or(&EOF_TOKEN)
4497+ }
4498+
44764499 /// Return true if the next tokens exactly `expected`
44774500 ///
44784501 /// Does not advance the current token.
@@ -4589,6 +4612,29 @@ impl<'a> Parser<'a> {
45894612 )
45904613 }
45914614
4615+ /// Look backwards in the token stream and expect that there was only whitespace tokens until the previous newline or beginning of string
4616+ pub(crate) fn prev_only_whitespace_until_newline(&mut self) -> bool {
4617+ let mut look_back_count = 1;
4618+ loop {
4619+ let prev_token = self.peek_prev_nth_token_no_skip_ref(look_back_count);
4620+ match prev_token.token {
4621+ Token::EOF => break true,
4622+ Token::Whitespace(ref w) => match w {
4623+ Whitespace::Newline => break true,
4624+ // special consideration required for single line comments since that string includes the newline
4625+ Whitespace::SingleLineComment { comment, prefix: _ } => {
4626+ if comment.ends_with('\n') {
4627+ break true;
4628+ }
4629+ look_back_count += 1;
4630+ }
4631+ _ => look_back_count += 1,
4632+ },
4633+ _ => break false,
4634+ };
4635+ }
4636+ }
4637+
45924638 /// If the current token is the `expected` keyword, consume it and returns
45934639 /// true. Otherwise, no tokens are consumed and returns false.
45944640 #[must_use]
@@ -19700,6 +19746,71 @@ impl<'a> Parser<'a> {
1970019746 }))
1970119747 }
1970219748
19749+ /// Parse [Statement::Go]
19750+ fn parse_go(&mut self) -> Result<Statement, ParserError> {
19751+ self.expect_keyword_is(Keyword::GO)?;
19752+
19753+ // disambiguate between GO as batch delimiter & GO as identifier (etc)
19754+ // compare:
19755+ // ```sql
19756+ // select 1 go
19757+ // ```
19758+ // vs
19759+ // ```sql
19760+ // select 1
19761+ // go
19762+ // ```
19763+ if !self.prev_only_whitespace_until_newline() {
19764+ parser_err!(
19765+ "GO may only be preceded by whitespace on a line",
19766+ self.peek_token().span.start
19767+ )?;
19768+ }
19769+
19770+ let count = loop {
19771+ // using this peek function because we want to halt this statement parsing upon newline
19772+ let next_token = self.peek_token_no_skip();
19773+ match next_token.token {
19774+ Token::EOF => break None::<u64>,
19775+ Token::Whitespace(ref w) => match w {
19776+ Whitespace::Newline => break None,
19777+ _ => _ = self.next_token_no_skip(),
19778+ },
19779+ Token::Number(s, _) => {
19780+ let value = Some(Self::parse::<u64>(s, next_token.span.start)?);
19781+ self.advance_token();
19782+ break value;
19783+ }
19784+ _ => self.expected("literal int or newline", next_token)?,
19785+ };
19786+ };
19787+
19788+ loop {
19789+ let next_token = self.peek_token_no_skip();
19790+ match next_token.token {
19791+ Token::EOF => break,
19792+ Token::Whitespace(ref w) => match w {
19793+ Whitespace::Newline => break,
19794+ Whitespace::SingleLineComment { comment, prefix: _ } => {
19795+ if comment.ends_with('\n') {
19796+ break;
19797+ }
19798+ _ = self.next_token_no_skip();
19799+ }
19800+ _ => _ = self.next_token_no_skip(),
19801+ },
19802+ _ => {
19803+ parser_err!(
19804+ "GO must be followed by a newline or EOF",
19805+ self.peek_token().span.start
19806+ )?;
19807+ }
19808+ };
19809+ }
19810+
19811+ Ok(Statement::Go(GoStatement { count }))
19812+ }
19813+
1970319814 /// Consume the parser and return its underlying token buffer
1970419815 pub fn into_tokens(self) -> Vec<TokenWithSpan> {
1970519816 self.tokens
@@ -20066,6 +20177,31 @@ mod tests {
2006620177 })
2006720178 }
2006820179
20180+ #[test]
20181+ fn test_peek_prev_nth_token_no_skip_ref() {
20182+ all_dialects().run_parser_method(
20183+ "SELECT 1;\n-- a comment\nRAISERROR('test', 16, 0);",
20184+ |parser| {
20185+ parser.index = 1;
20186+ assert_eq!(parser.peek_prev_nth_token_no_skip_ref(0), &Token::EOF);
20187+ assert_eq!(parser.index, 1);
20188+ parser.index = 7;
20189+ assert_eq!(
20190+ parser.token_at(parser.index - 1).token,
20191+ Token::Word(Word {
20192+ value: "RAISERROR".to_string(),
20193+ quote_style: None,
20194+ keyword: Keyword::RAISERROR,
20195+ })
20196+ );
20197+ assert_eq!(
20198+ parser.peek_prev_nth_token_no_skip_ref(2),
20199+ &Token::Whitespace(Whitespace::Newline)
20200+ );
20201+ },
20202+ );
20203+ }
20204+
2006920205 #[cfg(test)]
2007020206 mod test_parse_data_type {
2007120207 use crate::ast::{
0 commit comments