This is an automated email from the ASF dual-hosted git repository.

github-merge-queue[bot] pushed a commit to branch 
gh-readonly-queue/main/pr-2528-f8c94d24b75470d5468949f6edbc963950f95070
in repository https://gitbox.apache.org/repos/asf/datafusion-sqlparser-rs.git

commit d6afa0146ce929c1a82e175e613ba78d1e6673eb
Author: Luca Cappelletti <[email protected]>
AuthorDate: Tue Sep 22 09:09:07 2026 +0000

    Fuzz: Display roundtrip (#2528)
---
 docs/fuzzing.md                           |  4 ++
 fuzz/Cargo.toml                           |  7 ++++
 fuzz/fuzz_targets/fuzz_parse_roundtrip.rs | 61 +++++++++++++++++++++++++++++++
 fuzz/fuzz_targets/fuzz_parse_sql.rs       |  6 ++-
 4 files changed, 76 insertions(+), 2 deletions(-)

diff --git a/docs/fuzzing.md b/docs/fuzzing.md
index 3d165510..5028f3eb 100644
--- a/docs/fuzzing.md
+++ b/docs/fuzzing.md
@@ -27,6 +27,10 @@ cd fuzz
 cargo +nightly fuzz run fuzz_parse_sql -- -max_total_time=600
 ```
 
+There are two targets. `fuzz_parse_sql` parses the input with every dialect.
+`fuzz_parse_roundtrip` additionally re-parses the SQL rendered by `Display` 
and fails when a
+rendered statement no longer parses.
+
 ClusterFuzzLite runs continuous fuzzing. Every pull request fuzzes for 10 
minutes in
 `code-change` mode, a daily batch job grows the shared corpus stored on the
 `clusterfuzzlite` branch, and a daily prune compacts it.
diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml
index 4ee8c504..ed69281a 100644
--- a/fuzz/Cargo.toml
+++ b/fuzz/Cargo.toml
@@ -38,3 +38,10 @@ path = "fuzz_targets/fuzz_parse_sql.rs"
 test = false
 doc = false
 bench = false
+
+[[bin]]
+name = "fuzz_parse_roundtrip"
+path = "fuzz_targets/fuzz_parse_roundtrip.rs"
+test = false
+doc = false
+bench = false
diff --git a/fuzz/fuzz_targets/fuzz_parse_roundtrip.rs 
b/fuzz/fuzz_targets/fuzz_parse_roundtrip.rs
new file mode 100644
index 00000000..f1b27d65
--- /dev/null
+++ b/fuzz/fuzz_targets/fuzz_parse_roundtrip.rs
@@ -0,0 +1,61 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+#![no_main]
+
+use libfuzzer_sys::fuzz_target;
+use sqlparser::dialect::{
+    AnsiDialect, BigQueryDialect, ClickHouseDialect, DatabricksDialect, 
Dialect, DuckDbDialect,
+    GenericDialect, HiveDialect, MsSqlDialect, MySqlDialect, OracleDialect, 
PostgreSqlDialect,
+    RedshiftSqlDialect, SQLiteDialect, SnowflakeDialect, SparkSqlDialect, 
TeradataDialect,
+};
+use sqlparser::parser::Parser;
+
+fuzz_target!(|sql: &str| {
+    let dialects: [(&str, &dyn Dialect); 16] = [
+        ("ansi", &AnsiDialect {}),
+        ("bigquery", &BigQueryDialect {}),
+        ("clickhouse", &ClickHouseDialect {}),
+        ("databricks", &DatabricksDialect {}),
+        ("duckdb", &DuckDbDialect {}),
+        ("generic", &GenericDialect {}),
+        ("hive", &HiveDialect {}),
+        ("mssql", &MsSqlDialect {}),
+        ("mysql", &MySqlDialect {}),
+        ("oracle", &OracleDialect {}),
+        ("postgres", &PostgreSqlDialect {}),
+        ("redshift", &RedshiftSqlDialect {}),
+        ("sqlite", &SQLiteDialect {}),
+        ("snowflake", &SnowflakeDialect {}),
+        ("spark", &SparkSqlDialect {}),
+        ("teradata", &TeradataDialect {}),
+    ];
+    for (name, dialect) in dialects {
+        let Ok(statements) = Parser::parse_sql(dialect, sql) else {
+            continue;
+        };
+        for statement in &statements {
+            let rendered = statement.to_string();
+            // SQL the AST renders must parse back. A failure is a Display bug
+            if let Err(err) = Parser::parse_sql(dialect, &rendered) {
+                panic!(
+                    "{name}: displayed SQL failed to re-parse\n  display: 
{rendered}\n  error: {err}"
+                );
+            }
+        }
+    }
+});
diff --git a/fuzz/fuzz_targets/fuzz_parse_sql.rs 
b/fuzz/fuzz_targets/fuzz_parse_sql.rs
index 89c84184..cc594559 100644
--- a/fuzz/fuzz_targets/fuzz_parse_sql.rs
+++ b/fuzz/fuzz_targets/fuzz_parse_sql.rs
@@ -21,11 +21,11 @@ use libfuzzer_sys::fuzz_target;
 use sqlparser::dialect::{
     AnsiDialect, BigQueryDialect, ClickHouseDialect, DatabricksDialect, 
Dialect, DuckDbDialect,
     GenericDialect, HiveDialect, MsSqlDialect, MySqlDialect, OracleDialect, 
PostgreSqlDialect,
-    RedshiftSqlDialect, SQLiteDialect, SnowflakeDialect,
+    RedshiftSqlDialect, SQLiteDialect, SnowflakeDialect, SparkSqlDialect, 
TeradataDialect,
 };
 use sqlparser::parser::Parser;
 fuzz_target!(|sql: &str| {
-    let dialects: [&dyn Dialect; 14] = [
+    let dialects: [&dyn Dialect; 16] = [
         &AnsiDialect {},
         &BigQueryDialect {},
         &ClickHouseDialect {},
@@ -40,6 +40,8 @@ fuzz_target!(|sql: &str| {
         &RedshiftSqlDialect {},
         &SQLiteDialect {},
         &SnowflakeDialect {},
+        &SparkSqlDialect {},
+        &TeradataDialect {},
     ];
     for dialect in dialects {
         let _ = Parser::parse_sql(dialect, sql);


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to