diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs index b648869d2..d008a3906 100644 --- a/src/dialect/mod.rs +++ b/src/dialect/mod.rs @@ -880,6 +880,15 @@ pub trait Dialect: Debug + Any { fn supports_table_hints(&self) -> bool { false } + + /// Returns whether it's the start of a single line comment + /// e.g. MySQL requires a space after `--` to be a single line comment + /// Otherwise it's interpreted as a double minus operator + /// + /// MySQL: + fn is_start_of_single_line_comment(&self, _ch: char) -> bool { + true + } } /// This represents the operators for which precedence must be defined diff --git a/src/dialect/mysql.rs b/src/dialect/mysql.rs index a67fe67b0..3aaef6154 100644 --- a/src/dialect/mysql.rs +++ b/src/dialect/mysql.rs @@ -125,6 +125,15 @@ impl Dialect for MySqlDialect { fn supports_table_hints(&self) -> bool { true } + + /// Returns whether it's the start of a single line comment + /// e.g. MySQL requires a space after `--` to be a single line comment + /// Otherwise it's interpreted as a double minus operator + /// + /// MySQL: + fn is_start_of_single_line_comment(&self, _ch: char) -> bool { + _ch == ' ' + } } /// `LOCK TABLES` diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 7742e8fae..ffe912ebe 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -1229,15 +1229,26 @@ impl<'a> Tokenizer<'a> { // operators '-' => { chars.next(); // consume the '-' - match chars.peek() { - Some('-') => { - chars.next(); // consume the second '-', starting a single-line comment - let comment = self.tokenize_single_line_comment(chars); - Ok(Some(Token::Whitespace(Whitespace::SingleLineComment { - prefix: "--".to_owned(), - comment, - }))) + + // Potential start of a single-line comment + if let Some('-') = chars.peek() { + if let Some(next_char) = chars.peekable.clone().nth(1) { + // MySQL requires a space after the -- for a single-line comment + // Otherwise it's interpreted as two minus signs + // e.g. UPDATE account SET balance=balance--1 + // WHERE account_id=5752; + if self.dialect.is_start_of_single_line_comment(next_char) { + chars.next(); // consume second '-' + let comment = self.tokenize_single_line_comment(chars); + return Ok(Some(Token::Whitespace(Whitespace::SingleLineComment { + prefix: "--".to_owned(), + comment, + }))); + } } + } + + match chars.peek() { Some('>') => { chars.next(); match chars.peek() { @@ -3685,4 +3696,39 @@ mod tests { ], ); } + + #[test] + fn test_mysql_space_after_single_line_comment_missing() { + let sql = "SELECT --'abc' FROM DUAL"; + let dialect = MySqlDialect {}; + let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap(); + let expected = vec![ + Token::make_keyword("SELECT"), + Token::Whitespace(Whitespace::Space), + Token::Minus, + Token::Minus, + Token::SingleQuotedString("abc".to_string()), + Token::Whitespace(Whitespace::Space), + Token::make_keyword("FROM"), + Token::Whitespace(Whitespace::Space), + Token::make_word("DUAL", None), + ]; + compare(expected, tokens); + } + + #[test] + fn test_mysql_space_after_single_line_comment_present() { + let sql = "SELECT -- 'abc' FROM DUAL"; + let dialect = MySqlDialect {}; + let tokens = Tokenizer::new(&dialect, sql).tokenize().unwrap(); + let expected = vec![ + Token::make_keyword("SELECT"), + Token::Whitespace(Whitespace::Space), + Token::Whitespace(Whitespace::SingleLineComment { + prefix: "--".to_string(), + comment: " 'abc' FROM DUAL".to_string(), + }), + ]; + compare(expected, tokens); + } }