@@ -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]
@@ -17378,7 +17424,7 @@ impl<'a> Parser<'a> {
1737817424 }
1737917425 }
1738017426
17381- /// /// Parse a `EXPORT DATA` statement.
17427+ /// Parse a `EXPORT DATA` statement.
1738217428 ///
1738317429 /// See [Statement::ExportData]
1738417430 fn parse_export_data(&mut self) -> Result<Statement, ParserError> {
@@ -17436,6 +17482,71 @@ impl<'a> Parser<'a> {
1743617482 }))
1743717483 }
1743817484
17485+ /// Parse [Statement::Go]
17486+ fn parse_go(&mut self) -> Result<Statement, ParserError> {
17487+ self.expect_keyword_is(Keyword::GO)?;
17488+
17489+ // disambiguate between GO as batch delimiter & GO as identifier (etc)
17490+ // compare:
17491+ // ```sql
17492+ // select 1 go
17493+ // ```
17494+ // vs
17495+ // ```sql
17496+ // select 1
17497+ // go
17498+ // ```
17499+ if !self.prev_only_whitespace_until_newline() {
17500+ parser_err!(
17501+ "GO may only be preceded by whitespace on a line",
17502+ self.peek_token().span.start
17503+ )?;
17504+ }
17505+
17506+ let count = loop {
17507+ // using this peek function because we want to halt this statement parsing upon newline
17508+ let next_token = self.peek_token_no_skip();
17509+ match next_token.token {
17510+ Token::EOF => break None::<u64>,
17511+ Token::Whitespace(ref w) => match w {
17512+ Whitespace::Newline => break None,
17513+ _ => _ = self.next_token_no_skip(),
17514+ },
17515+ Token::Number(s, _) => {
17516+ let value = Some(Self::parse::<u64>(s, next_token.span.start)?);
17517+ self.advance_token();
17518+ break value;
17519+ }
17520+ _ => self.expected("literal int or newline", next_token)?,
17521+ };
17522+ };
17523+
17524+ loop {
17525+ let next_token = self.peek_token_no_skip();
17526+ match next_token.token {
17527+ Token::EOF => break,
17528+ Token::Whitespace(ref w) => match w {
17529+ Whitespace::Newline => break,
17530+ Whitespace::SingleLineComment { comment, prefix: _ } => {
17531+ if comment.ends_with('\n') {
17532+ break;
17533+ }
17534+ _ = self.next_token_no_skip();
17535+ }
17536+ _ => _ = self.next_token_no_skip(),
17537+ },
17538+ _ => {
17539+ parser_err!(
17540+ "GO must be followed by a newline or EOF",
17541+ self.peek_token().span.start
17542+ )?;
17543+ }
17544+ };
17545+ }
17546+
17547+ Ok(Statement::Go(GoStatement { count }))
17548+ }
17549+
1743917550 /// Consume the parser and return its underlying token buffer
1744017551 pub fn into_tokens(self) -> Vec<TokenWithSpan> {
1744117552 self.tokens
@@ -17784,6 +17895,31 @@ mod tests {
1778417895 })
1778517896 }
1778617897
17898+ #[test]
17899+ fn test_peek_prev_nth_token_no_skip_ref() {
17900+ all_dialects().run_parser_method(
17901+ "SELECT 1;\n-- a comment\nRAISERROR('test', 16, 0);",
17902+ |parser| {
17903+ parser.index = 1;
17904+ assert_eq!(parser.peek_prev_nth_token_no_skip_ref(0), &Token::EOF);
17905+ assert_eq!(parser.index, 1);
17906+ parser.index = 7;
17907+ assert_eq!(
17908+ parser.token_at(parser.index - 1).token,
17909+ Token::Word(Word {
17910+ value: "RAISERROR".to_string(),
17911+ quote_style: None,
17912+ keyword: Keyword::RAISERROR,
17913+ })
17914+ );
17915+ assert_eq!(
17916+ parser.peek_prev_nth_token_no_skip_ref(2),
17917+ &Token::Whitespace(Whitespace::Newline)
17918+ );
17919+ },
17920+ );
17921+ }
17922+
1778717923 #[cfg(test)]
1778817924 mod test_parse_data_type {
1778917925 use crate::ast::{
0 commit comments