From e05a766538f73720da9cf8c051219636ca9fa498 Mon Sep 17 00:00:00 2001 From: Yoav Cohen Date: Tue, 20 Jan 2026 19:11:09 +0100 Subject: [PATCH] Redshift: Support implicit string concatenation using newline --- src/dialect/mod.rs | 13 +++++++++++++ src/dialect/redshift.rs | 4 ++++ src/parser/mod.rs | 27 +++++++++++++++++++++++++++ tests/sqlparser_common.rs | 19 +++++++++++++++++++ 4 files changed, 63 insertions(+) diff --git a/src/dialect/mod.rs b/src/dialect/mod.rs index cd7fdee12..8aceffe52 100644 --- a/src/dialect/mod.rs +++ b/src/dialect/mod.rs @@ -489,6 +489,19 @@ pub trait Dialect: Debug + Any { false } + /// Returns true if the dialect supports concatenating string literals with a newline. + /// For example, the following statement would return `true`: + /// ```sql + /// SELECT 'abc' in ( + /// 'a' + /// 'b' + /// 'c' + /// ); + /// ``` + fn supports_string_literal_concatenation_with_newline(&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/redshift.rs b/src/dialect/redshift.rs index 43c0646ce..c028061d2 100644 --- a/src/dialect/redshift.rs +++ b/src/dialect/redshift.rs @@ -147,4 +147,8 @@ impl Dialect for RedshiftSqlDialect { fn supports_create_table_like_parenthesized(&self) -> bool { true } + + fn supports_string_literal_concatenation_with_newline(&self) -> bool { + true + } } diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 149365c47..d4c9879ff 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -11273,7 +11273,34 @@ impl<'a> Parser<'a> { str.push_str(s.clone().as_str()); self.advance_token(); } + } else if self + .dialect + .supports_string_literal_concatenation_with_newline() + { + // We are iterating over tokens including whitespaces, to identify + // string literals separated by newlines so we can concatenate them. + let mut after_newline = false; + loop { + match self.peek_token_no_skip().token { + Token::Whitespace(Whitespace::Newline) => { + after_newline = true; + self.next_token_no_skip(); + } + Token::Whitespace(_) => { + self.next_token_no_skip(); + } + Token::SingleQuotedString(ref s) | Token::DoubleQuotedString(ref s) + if after_newline => + { + str.push_str(s.clone().as_str()); + self.next_token_no_skip(); + after_newline = false; + } + _ => break, + } + } } + str } diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index bbbf0d835..5d1dca573 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -17647,6 +17647,25 @@ fn parse_adjacent_string_literal_concatenation() { 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!'"); + + let dialects = all_dialects_where(|d| d.supports_string_literal_concatenation_with_newline()); + let sql = r#" + SELECT 'abc' in ('a' + 'b' + 'c', + 'd' + )"#; + dialects.one_statement_parses_to(sql, "SELECT 'abc' IN ('abc', 'd')"); + + let sql = r#" + SELECT 'abc' in ('a' + 'b' + -- COMMENT + 'c', + -- COMMENT + 'd' + )"#; + dialects.one_statement_parses_to(sql, "SELECT 'abc' IN ('abc', 'd')"); } #[test]