Skip to content

Commit b8a184c

Browse files
committed
Extract common logic to look back for a newline to a helper
1 parent f7253a0 commit b8a184c

File tree

3 files changed

+49
-52
lines changed

3 files changed

+49
-52
lines changed

src/dialect/mssql.rs

+9-21
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use crate::ast::{ConditionalStatementBlock, ConditionalStatements, IfStatement,
2020
use crate::dialect::Dialect;
2121
use crate::keywords::{self, Keyword};
2222
use crate::parser::{Parser, ParserError};
23-
use crate::tokenizer::{Token, Whitespace};
23+
use crate::tokenizer::Token;
2424
#[cfg(not(feature = "std"))]
2525
use alloc::{vec, vec::Vec};
2626

@@ -117,26 +117,14 @@ impl Dialect for MsSqlDialect {
117117
}
118118

119119
fn is_column_alias(&self, kw: &Keyword, parser: &mut Parser) -> bool {
120-
// if:
121-
// - keyword is `GO`, and
122-
// - looking backwards there's only (any) whitespace preceded by a newline
123-
// then: `GO` iSN'T a column alias
124-
if kw == &Keyword::GO {
125-
let mut look_back_count = 2;
126-
loop {
127-
let prev_index = parser.index().saturating_sub(look_back_count);
128-
if prev_index == 0 {
129-
break;
130-
}
131-
let prev_token = parser.token_at(prev_index);
132-
match prev_token.token {
133-
Token::Whitespace(ref w) => match w {
134-
Whitespace::Newline => return false,
135-
_ => look_back_count += 1,
136-
},
137-
_ => break,
138-
};
139-
}
120+
// if we find maybe whitespace then a newline looking backward, then `GO` ISN'T a column alias
121+
// if we can't find a newline then we assume that `GO` IS a column alias
122+
if kw == &Keyword::GO
123+
&& parser
124+
.expect_previously_only_whitespace_until_newline()
125+
.is_ok()
126+
{
127+
return false;
140128
}
141129

142130
!keywords::RESERVED_FOR_COLUMN_ALIAS.contains(kw) && !RESERVED_FOR_COLUMN_ALIAS.contains(kw)

src/parser/mod.rs

+39-30
Original file line numberDiff line numberDiff line change
@@ -4070,6 +4070,44 @@ impl<'a> Parser<'a> {
40704070
)
40714071
}
40724072

4073+
/// Look backwards in the token stream and expect that there was only whitespace tokens until the previous newline
4074+
pub fn expect_previously_only_whitespace_until_newline(&mut self) -> Result<(), ParserError> {
4075+
let mut look_back_count = 2;
4076+
loop {
4077+
let prev_index = self.index.saturating_sub(look_back_count);
4078+
if prev_index == 0 {
4079+
break;
4080+
}
4081+
let prev_token = self.token_at(prev_index);
4082+
match prev_token.token {
4083+
Token::Whitespace(ref w) => match w {
4084+
Whitespace::Newline => break,
4085+
// special consideration required for single line comments since that string includes the newline
4086+
Whitespace::SingleLineComment { comment, prefix: _ } => {
4087+
if comment.ends_with('\n') {
4088+
break;
4089+
}
4090+
look_back_count += 1;
4091+
}
4092+
_ => look_back_count += 1,
4093+
},
4094+
_ => {
4095+
let current_token = self.get_current_token();
4096+
if prev_token == current_token {
4097+
// if we are at the start of the statement, we can skip this check
4098+
break;
4099+
}
4100+
4101+
self.expected(
4102+
&format!("newline before current token ({})", current_token),
4103+
prev_token.clone(),
4104+
)?
4105+
}
4106+
};
4107+
}
4108+
Ok(())
4109+
}
4110+
40734111
/// If the current token is the `expected` keyword, consume it and returns
40744112
/// true. Otherwise, no tokens are consumed and returns false.
40754113
#[must_use]
@@ -15081,36 +15119,7 @@ impl<'a> Parser<'a> {
1508115119

1508215120
/// Parse [Statement::Go]
1508315121
fn parse_go(&mut self) -> Result<Statement, ParserError> {
15084-
// previous token should be a newline (skipping non-newline whitespace)
15085-
// see also, `previous_token`
15086-
let mut look_back_count = 2;
15087-
loop {
15088-
let prev_index = self.index.saturating_sub(look_back_count);
15089-
if prev_index == 0 {
15090-
break;
15091-
}
15092-
let prev_token = self.token_at(prev_index);
15093-
match prev_token.token {
15094-
Token::Whitespace(ref w) => match w {
15095-
Whitespace::Newline => break,
15096-
Whitespace::SingleLineComment { comment, prefix: _ } => {
15097-
if comment.ends_with('\n') {
15098-
break;
15099-
}
15100-
look_back_count += 1;
15101-
}
15102-
_ => look_back_count += 1,
15103-
},
15104-
_ => {
15105-
if prev_token == self.get_current_token() {
15106-
// if we are at the start of the statement, we can skip this check
15107-
break;
15108-
}
15109-
15110-
self.expected("newline before GO", prev_token.clone())?
15111-
}
15112-
};
15113-
}
15122+
self.expect_previously_only_whitespace_until_newline()?;
1511415123

1511515124
let count = loop {
1511615125
// using this peek function because we want to halt this statement parsing upon newline

tests/sqlparser_mssql.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2167,7 +2167,7 @@ fn parse_mssql_go_keyword() {
21672167
let err = ms().parse_sql_statements(invalid_go_position);
21682168
assert_eq!(
21692169
err.unwrap_err().to_string(),
2170-
"sql parser error: Expected: newline before GO, found: ;"
2170+
"sql parser error: Expected: newline before current token (GO), found: ;"
21712171
);
21722172

21732173
let invalid_go_count = "SELECT 1\nGO x";

0 commit comments

Comments
 (0)