@@ -475,6 +475,12 @@ impl<'a> Parser<'a> {
475475 if expecting_statement_delimiter && word.keyword == Keyword::END {
476476 break;
477477 }
478+ // Treat batch delimiter as an end of statement
479+ if expecting_statement_delimiter && dialect_of!(self is MsSqlDialect) {
480+ if let Some(Statement::Go(GoStatement { count: _ })) = stmts.last() {
481+ expecting_statement_delimiter = false;
482+ }
483+ }
478484 }
479485 _ => {}
480486 }
@@ -618,6 +624,7 @@ impl<'a> Parser<'a> {
618624 // `COMMENT` is snowflake specific https://docs.snowflake.com/en/sql-reference/sql/comment
619625 Keyword::COMMENT if self.dialect.supports_comment_on() => self.parse_comment(),
620626 Keyword::PRINT => self.parse_print(),
627+ Keyword::GO => self.parse_go(),
621628 _ => self.expected("an SQL statement", next_token),
622629 },
623630 Token::LParen => {
@@ -15059,11 +15066,64 @@ impl<'a> Parser<'a> {
1505915066
1506015067 /// Parse [Statement::Print]
1506115068 fn parse_print(&mut self) -> Result<Statement, ParserError> {
15062- Ok(Statement::Print(PrintStatement {
1506315069 message: Box::new(self.parse_expr()?),
1506415070 }))
1506515071 }
1506615072
15073+ fn parse_go(&mut self) -> Result<Statement, ParserError> {
15074+ // previous token should be a newline (skipping non-newline whitespace)
15075+ // see also, `previous_token`
15076+ let mut look_back_count = 2;
15077+ loop {
15078+ let prev_index = self.index.saturating_sub(look_back_count);
15079+ if prev_index == 0 {
15080+ break;
15081+ }
15082+ let prev_token = self.token_at(prev_index);
15083+ match prev_token.token {
15084+ Token::Whitespace(ref w) => match w {
15085+ Whitespace::Newline => break,
15086+ _ => look_back_count += 1,
15087+ },
15088+ _ => {
15089+ if prev_token == self.get_current_token() {
15090+ // if we are at the start of the statement, we can skip this check
15091+ break;
15092+ }
15093+
15094+ self.expected("newline before GO", prev_token.clone())?
15095+ }
15096+ };
15097+ }
15098+
15099+ let count = loop {
15100+ // using this peek function because we want to halt this statement parsing upon newline
15101+ let next_token = self.peek_token_no_skip();
15102+ match next_token.token {
15103+ Token::EOF => break None::<u64>,
15104+ Token::Whitespace(ref w) => match w {
15105+ Whitespace::Newline => break None,
15106+ _ => _ = self.next_token_no_skip(),
15107+ },
15108+ Token::Number(s, _) => {
15109+ let value = Some(Self::parse::<u64>(s, next_token.span.start)?);
15110+ self.advance_token();
15111+ break value;
15112+ }
15113+ _ => self.expected("literal int or newline", next_token)?,
15114+ };
15115+ };
15116+
15117+ if self.peek_token().token == Token::SemiColon {
15118+ parser_err!(
15119+ "GO may not end with a semicolon",
15120+ self.peek_token().span.start
15121+ )?;
15122+ }
15123+
15124+ Ok(Statement::Go(GoStatement { count }))
15125+ }
15126+
1506715127 /// Consume the parser and return its underlying token buffer
1506815128 pub fn into_tokens(self) -> Vec<TokenWithSpan> {
1506915129 self.tokens
0 commit comments