From 9b12d5a04d076c8547efa1ac6e63739f121f7c52 Mon Sep 17 00:00:00 2001 From: Keming Date: Sun, 3 Aug 2025 18:14:31 +0800 Subject: [PATCH 1/4] fix: add recursive protection to Convert Signed-off-by: Keming --- src/ast/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index c0b6e1c6d..4f8236b50 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -115,6 +115,7 @@ where Self: Into, { #[inline(always)] + #[cfg_attr(feature = "recursive-protection", recursive::recursive)] fn convert(value: Self) -> T { Self::into(value) } From 817110e7223145e5e1ebfe6de8072ffdfe364b0f Mon Sep 17 00:00:00 2001 From: Keming Date: Sun, 3 Aug 2025 22:19:54 +0800 Subject: [PATCH 2/4] add test for statement convertion Signed-off-by: Keming --- tests/sqlparser_common.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index 7f4596602..781cc59f0 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -13351,3 +13351,17 @@ fn parse_range_range_align_to_calculate() { "Expected: end of statement, found: )", ); } + +#[test] +fn convert_to_datafusion_statement_overflow() { + let expr = std::iter::repeat("num BETWEEN 0 AND 1") + .take(1000) + .collect::>() + .join(" OR "); + let sql = format!("SELECT num FROM numbers WHERE {}", expr); + + let mut statements = Parser::parse_sql(&GenericDialect {}, sql.as_str()).unwrap(); + let statement = statements.pop().unwrap(); + let df_statement: df_sqlparser::ast::Statement = statement.into(); + assert_eq!(df_statement.to_string(), sql); +} From 27d285beb91a7db71416f8da00054df76ac9d228 Mon Sep 17 00:00:00 2001 From: Keming Date: Sun, 3 Aug 2025 23:01:45 +0800 Subject: [PATCH 3/4] fix lint Signed-off-by: Keming --- tests/sqlparser_common.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/sqlparser_common.rs b/tests/sqlparser_common.rs index cbb36f828..499cc0279 100644 --- a/tests/sqlparser_common.rs +++ b/tests/sqlparser_common.rs @@ -13351,11 +13351,10 @@ fn parse_range_range_align_to_calculate() { #[test] fn convert_to_datafusion_statement_overflow() { - let expr = std::iter::repeat("num BETWEEN 0 AND 1") - .take(1000) + let expr = std::iter::repeat_n("num BETWEEN 0 AND 1", 1000) .collect::>() .join(" OR "); - let sql = format!("SELECT num FROM numbers WHERE {}", expr); + let sql = format!("SELECT num FROM numbers WHERE {expr}"); let mut statements = Parser::parse_sql(&GenericDialect {}, sql.as_str()).unwrap(); let statement = statements.pop().unwrap(); From b88168d4046739116067e329f9f5ec28e953136e Mon Sep 17 00:00:00 2001 From: Keming Date: Sun, 3 Aug 2025 23:09:28 +0800 Subject: [PATCH 4/4] fix cargo fmt Signed-off-by: Keming --- src/ast/mod.rs | 12 ++++++++++-- src/parser/mod.rs | 3 +-- tests/sqlparser_duckdb.rs | 4 +++- tests/sqlparser_mysql.rs | 3 +-- tests/sqlparser_snowflake.rs | 12 +++++++----- 5 files changed, 22 insertions(+), 12 deletions(-) diff --git a/src/ast/mod.rs b/src/ast/mod.rs index a3d49518c..77936c2b0 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -4576,9 +4576,17 @@ impl fmt::Display for Statement { "{hivevar}{name} = {l_paren}{value}{r_paren}", hivevar = if *hivevar { "HIVEVAR:" } else { "" }, name = variables, - l_paren = if parenthesized { "(" } else { Default::default() }, + l_paren = if parenthesized { + "(" + } else { + Default::default() + }, value = display_comma_separated(value), - r_paren = if parenthesized { ")" } else { Default::default() }, + r_paren = if parenthesized { + ")" + } else { + Default::default() + }, ) } Statement::SetTimeZone { local, value } => { diff --git a/src/parser/mod.rs b/src/parser/mod.rs index 624b60b3d..97457e2ca 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -2364,8 +2364,7 @@ impl<'a> Parser<'a> { self.expect_token(&Token::LParen)?; let mut trim_where = None; if let Token::Word(word) = self.peek_token().token { - if [Keyword::BOTH, Keyword::LEADING, Keyword::TRAILING].contains(&word.keyword) - { + if [Keyword::BOTH, Keyword::LEADING, Keyword::TRAILING].contains(&word.keyword) { trim_where = Some(self.parse_trim_where()?); } } diff --git a/tests/sqlparser_duckdb.rs b/tests/sqlparser_duckdb.rs index c9408c74c..6fa56c2ae 100644 --- a/tests/sqlparser_duckdb.rs +++ b/tests/sqlparser_duckdb.rs @@ -778,7 +778,9 @@ fn parse_use() { for "e in "e_styles { // Test double identifier with different type of quotes assert_eq!( - duckdb().verified_stmt(&format!("USE {quote}CATALOG{quote}.{quote}my_schema{quote}")), + duckdb().verified_stmt(&format!( + "USE {quote}CATALOG{quote}.{quote}my_schema{quote}" + )), Statement::Use(Use::Object(ObjectName(vec![ Ident::with_quote(quote, "CATALOG"), Ident::with_quote(quote, "my_schema") diff --git a/tests/sqlparser_mysql.rs b/tests/sqlparser_mysql.rs index ff1f5e41b..bae724787 100644 --- a/tests/sqlparser_mysql.rs +++ b/tests/sqlparser_mysql.rs @@ -598,8 +598,7 @@ fn parse_use() { for "e in "e_styles { // Test single identifier with different type of quotes assert_eq!( - mysql_and_generic() - .verified_stmt(&format!("USE {quote}{object_name}{quote}")), + mysql_and_generic().verified_stmt(&format!("USE {quote}{object_name}{quote}")), Statement::Use(Use::Object(ObjectName(vec![Ident::with_quote( quote, object_name.to_string(), diff --git a/tests/sqlparser_snowflake.rs b/tests/sqlparser_snowflake.rs index 855d4f394..e2b47d729 100644 --- a/tests/sqlparser_snowflake.rs +++ b/tests/sqlparser_snowflake.rs @@ -2234,9 +2234,7 @@ fn test_snowflake_stage_object_names() { .zip(allowed_object_names.iter_mut()) { let (formatted_name, object_name) = it; - let sql = format!( - "COPY INTO {formatted_name} FROM 'gcs://mybucket/./../a.csv'" - ); + let sql = format!("COPY INTO {formatted_name} FROM 'gcs://mybucket/./../a.csv'"); match snowflake().verified_stmt(&sql) { Statement::CopyIntoSnowflake { into, .. } => { assert_eq!(into.0, object_name.0) @@ -2714,7 +2712,9 @@ fn parse_use() { for "e in "e_styles { // Test double identifier with different type of quotes assert_eq!( - snowflake().verified_stmt(&format!("USE {quote}CATALOG{quote}.{quote}my_schema{quote}")), + snowflake().verified_stmt(&format!( + "USE {quote}CATALOG{quote}.{quote}my_schema{quote}" + )), Statement::Use(Use::Object(ObjectName(vec![ Ident::with_quote(quote, "CATALOG"), Ident::with_quote(quote, "my_schema") @@ -2747,7 +2747,9 @@ fn parse_use() { )]))) ); assert_eq!( - snowflake().verified_stmt(&format!("USE SCHEMA {quote}CATALOG{quote}.{quote}my_schema{quote}")), + snowflake().verified_stmt(&format!( + "USE SCHEMA {quote}CATALOG{quote}.{quote}my_schema{quote}" + )), Statement::Use(Use::Schema(ObjectName(vec![ Ident::with_quote(quote, "CATALOG"), Ident::with_quote(quote, "my_schema")