@@ -514,6 +514,12 @@ impl<'a> Parser<'a> {
514514 if expecting_statement_delimiter && word.keyword == Keyword::END {
515515 break;
516516 }
517+
518+ // MSSQL: the `GO` keyword is a batch separator which also means it concludes the current statement
519+ // `GO` may not be followed by a semicolon, so turn off that expectation
520+ if expecting_statement_delimiter && word.keyword == Keyword::GO {
521+ expecting_statement_delimiter = false;
522+ }
517523 }
518524 // don't expect a semicolon statement delimiter after a newline when not otherwise required
519525 Token::Whitespace(Whitespace::Newline) => {
@@ -529,8 +535,10 @@ impl<'a> Parser<'a> {
529535 }
530536
531537 let statement = self.parse_statement()?;
538+ // MSSQL: the `GO` keyword is a batch separator which also means it concludes the current statement
539+ // `GO` may not be followed by a semicolon, so turn off that expectation
540+ expecting_statement_delimiter = !matches!(statement, Statement::Go(_));
532541 stmts.push(statement);
533- expecting_statement_delimiter = self.options.require_semicolon_stmt_delimiter;
534542 }
535543 Ok(stmts)
536544 }
@@ -681,6 +689,10 @@ impl<'a> Parser<'a> {
681689 self.prev_token();
682690 self.parse_vacuum()
683691 }
692+ Keyword::GO => {
693+ self.prev_token();
694+ self.parse_go()
695+ }
684696 _ => self.expected("an SQL statement", next_token),
685697 },
686698 Token::LParen => {
@@ -4132,6 +4144,17 @@ impl<'a> Parser<'a> {
41324144 })
41334145 }
41344146
4147+ /// Return nth previous token, possibly whitespace
4148+ /// (or [`Token::EOF`] when before the beginning of the stream).
4149+ pub(crate) fn peek_prev_nth_token_no_skip_ref(&self, n: usize) -> &TokenWithSpan {
4150+ // 0 = next token, -1 = current token, -2 = previous token
4151+ let peek_index = self.index.saturating_sub(1).saturating_sub(n);
4152+ if peek_index == 0 {
4153+ return &EOF_TOKEN;
4154+ }
4155+ self.tokens.get(peek_index).unwrap_or(&EOF_TOKEN)
4156+ }
4157+
41354158 /// Return true if the next tokens exactly `expected`
41364159 ///
41374160 /// Does not advance the current token.
@@ -4248,6 +4271,29 @@ impl<'a> Parser<'a> {
42484271 )
42494272 }
42504273
4274+ /// Look backwards in the token stream and expect that there was only whitespace tokens until the previous newline or beginning of string
4275+ pub(crate) fn prev_only_whitespace_until_newline(&mut self) -> bool {
4276+ let mut look_back_count = 1;
4277+ loop {
4278+ let prev_token = self.peek_prev_nth_token_no_skip_ref(look_back_count);
4279+ match prev_token.token {
4280+ Token::EOF => break true,
4281+ Token::Whitespace(ref w) => match w {
4282+ Whitespace::Newline => break true,
4283+ // special consideration required for single line comments since that string includes the newline
4284+ Whitespace::SingleLineComment { comment, prefix: _ } => {
4285+ if comment.ends_with('\n') {
4286+ break true;
4287+ }
4288+ look_back_count += 1;
4289+ }
4290+ _ => look_back_count += 1,
4291+ },
4292+ _ => break false,
4293+ };
4294+ }
4295+ }
4296+
42514297 /// If the current token is the `expected` keyword, consume it and returns
42524298 /// true. Otherwise, no tokens are consumed and returns false.
42534299 #[must_use]
@@ -17342,7 +17388,7 @@ impl<'a> Parser<'a> {
1734217388 }
1734317389 }
1734417390
17345- /// /// Parse a `EXPORT DATA` statement.
17391+ /// Parse a `EXPORT DATA` statement.
1734617392 ///
1734717393 /// See [Statement::ExportData]
1734817394 fn parse_export_data(&mut self) -> Result<Statement, ParserError> {
@@ -17400,6 +17446,71 @@ impl<'a> Parser<'a> {
1740017446 }))
1740117447 }
1740217448
17449+ /// Parse [Statement::Go]
17450+ fn parse_go(&mut self) -> Result<Statement, ParserError> {
17451+ self.expect_keyword_is(Keyword::GO)?;
17452+
17453+ // disambiguate between GO as batch delimiter & GO as identifier (etc)
17454+ // compare:
17455+ // ```sql
17456+ // select 1 go
17457+ // ```
17458+ // vs
17459+ // ```sql
17460+ // select 1
17461+ // go
17462+ // ```
17463+ if !self.prev_only_whitespace_until_newline() {
17464+ parser_err!(
17465+ "GO may only be preceded by whitespace on a line",
17466+ self.peek_token().span.start
17467+ )?;
17468+ }
17469+
17470+ let count = loop {
17471+ // using this peek function because we want to halt this statement parsing upon newline
17472+ let next_token = self.peek_token_no_skip();
17473+ match next_token.token {
17474+ Token::EOF => break None::<u64>,
17475+ Token::Whitespace(ref w) => match w {
17476+ Whitespace::Newline => break None,
17477+ _ => _ = self.next_token_no_skip(),
17478+ },
17479+ Token::Number(s, _) => {
17480+ let value = Some(Self::parse::<u64>(s, next_token.span.start)?);
17481+ self.advance_token();
17482+ break value;
17483+ }
17484+ _ => self.expected("literal int or newline", next_token)?,
17485+ };
17486+ };
17487+
17488+ loop {
17489+ let next_token = self.peek_token_no_skip();
17490+ match next_token.token {
17491+ Token::EOF => break,
17492+ Token::Whitespace(ref w) => match w {
17493+ Whitespace::Newline => break,
17494+ Whitespace::SingleLineComment { comment, prefix: _ } => {
17495+ if comment.ends_with('\n') {
17496+ break;
17497+ }
17498+ _ = self.next_token_no_skip();
17499+ }
17500+ _ => _ = self.next_token_no_skip(),
17501+ },
17502+ _ => {
17503+ parser_err!(
17504+ "GO must be followed by a newline or EOF",
17505+ self.peek_token().span.start
17506+ )?;
17507+ }
17508+ };
17509+ }
17510+
17511+ Ok(Statement::Go(GoStatement { count }))
17512+ }
17513+
1740317514 /// Consume the parser and return its underlying token buffer
1740417515 pub fn into_tokens(self) -> Vec<TokenWithSpan> {
1740517516 self.tokens
@@ -17717,6 +17828,31 @@ mod tests {
1771717828 })
1771817829 }
1771917830
17831+ #[test]
17832+ fn test_peek_prev_nth_token_no_skip_ref() {
17833+ all_dialects().run_parser_method(
17834+ "SELECT 1;\n-- a comment\nRAISERROR('test', 16, 0);",
17835+ |parser| {
17836+ parser.index = 1;
17837+ assert_eq!(parser.peek_prev_nth_token_no_skip_ref(0), &Token::EOF);
17838+ assert_eq!(parser.index, 1);
17839+ parser.index = 7;
17840+ assert_eq!(
17841+ parser.token_at(parser.index - 1).token,
17842+ Token::Word(Word {
17843+ value: "RAISERROR".to_string(),
17844+ quote_style: None,
17845+ keyword: Keyword::RAISERROR,
17846+ })
17847+ );
17848+ assert_eq!(
17849+ parser.peek_prev_nth_token_no_skip_ref(2),
17850+ &Token::Whitespace(Whitespace::Newline)
17851+ );
17852+ },
17853+ );
17854+ }
17855+
1772017856 #[cfg(test)]
1772117857 mod test_parse_data_type {
1772217858 use crate::ast::{
0 commit comments