diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs index f9120972..3c7be8f7 100644 --- a/src/dialect/mod.rs +++ b/src/dialect/mod.rs @@ -476,6 +476,12 @@ pub trait Dialect: Debug + Any { false } + /// Returns true if the dialect supports concatenating of string literal + /// Example: `SELECT 'Hello ' "world" => SELECT 'Hello world'` + fn supports_string_literal_concatenation(&self) -> bool { + false + } + /// Does the dialect support trailing commas in the projection list? fn supports_projection_trailing_commas(&self) -> bool { self.supports_trailing_commas() diff --git a/src/dialect/mysql.rs b/src/dialect/mysql.rs index 6cf24e14..8c63bfda 100644 --- a/src/dialect/mysql.rs +++ b/src/dialect/mysql.rs @@ -71,6 +71,11 @@ impl Dialect for MySqlDialect { true } + /// see + fn supports_string_literal_concatenation(&self) -> bool { + true + } + fn ignores_wildcard_escapes(&self) -> bool { true } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 0afab28e..54db367d 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -9914,8 +9914,12 @@ impl<'a> Parser<'a> { // bigdecimal feature is enabled, and is otherwise a no-op // (i.e., it returns the input string). Token::Number(n, l) => ok_value(Value::Number(Self::parse(n, span.start)?, l)), - Token::SingleQuotedString(ref s) => ok_value(Value::SingleQuotedString(s.to_string())), - Token::DoubleQuotedString(ref s) => ok_value(Value::DoubleQuotedString(s.to_string())), + Token::SingleQuotedString(ref s) => ok_value(Value::SingleQuotedString( + self.maybe_concat_string_literal(s.to_string()), + )), + Token::DoubleQuotedString(ref s) => ok_value(Value::DoubleQuotedString( + self.maybe_concat_string_literal(s.to_string()), + )), Token::TripleSingleQuotedString(ref s) => { ok_value(Value::TripleSingleQuotedString(s.to_string())) } @@ -9985,6 +9989,18 @@ impl<'a> Parser<'a> { } } + fn maybe_concat_string_literal(&mut self, mut str: String) -> String { + if self.dialect.supports_string_literal_concatenation() { + while let Token::SingleQuotedString(ref s) | Token::DoubleQuotedString(ref s) = + self.peek_token_ref().token + { + str.push_str(s.clone().as_str()); + self.advance_token(); + } + } + str + } + /// Parse an unsigned numeric literal pub fn parse_number_value(&mut self) -> Result { let value_wrapper = self.parse_value()?; diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 0ed61c99..aa47c0f7 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -17150,3 +17150,13 @@ fn test_parse_semantic_view_table_factor() { _ => panic!("Expected Query statement"), } } + +#[test] +fn parse_adjacent_string_literal_concatenation() { + let sql = r#"SELECT 'M' "y" 'S' "q" 'l'"#; + let dialects = all_dialects_where(|d| d.supports_string_literal_concatenation()); + dialects.one_statement_parses_to(sql, r"SELECT 'MySql'"); + + let sql = "SELECT * FROM t WHERE col = 'Hello' \n ' ' \t 'World!'"; + dialects.one_statement_parses_to(sql, r"SELECT * FROM t WHERE col = 'Hello World!'"); +}