Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,11 @@ jobs:
- name: Extract corpus
run: tar --zstd -xf datasets.tar.zst
- name: Run coverage
run: cargo tarpaulin --out Xml
# Exclude timemachine: it links several sqlglot-rust versions side by side,
# each exporting the same unmangled extern "C" FFI symbols, which collide at
# link time once tarpaulin disables dead-code stripping. It has no unit tests
# of its own, so excluding it loses no coverage.
run: cargo tarpaulin --workspace --exclude timemachine --out Xml
- name: Upload to Codecov
uses: codecov/codecov-action@v5
with:
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

## June 2026: parser refresh and a failed-to-parse badge

- Updated the benchmarked parsers to their latest versions: polyglot-sql 0.4.4 to 0.5.1, sqlglot-rust 0.10.0 to 0.10.1, and the git-tracked sqlparser-rs and pg_query.rs to their current commits (sqlparser-rs now at b3760221). turso_parser stays on 0.6.1 since the only newer version is a prerelease.
- The time machine gains the new release points (polyglot-sql 0.5.1 and sqlglot-rust 0.10.1) so the trends end at the current code.
- Each parser page gains a failed-to-parse badge in the meta-grid showing how many statements the parser rejected that it was expected to accept, summed across every dialect. It is a neutral coverage figure (every parser misses some real-world SQL), with the percentage and denominator in the tooltip.
- Added three CI-practice badges per parser: cargo deny (whether CI enforces a dependency policy with cargo deny), cargo audit (whether CI scans dependencies against the RustSec advisory database, via cargo audit or cargo deny check advisories), and cargo mutants (whether CI runs mutation testing). As of this snapshot turso is the only parser running cargo deny (its licenses check only), and none run cargo audit or cargo mutants.

## June 2026: robustness badges

- Each parser page gains a Robustness section with six per-parser badges mined from the parser's own source and behavior, so a chooser can weigh crash-safety alongside speed and coverage.
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ qusql-parse = "0.8.0"
polyglot-sql = { git = "https://github.com/tobilg/polyglot" }
databend-common-ast = "0.2.5"
orql = { git = "https://codeberg.org/xitep/orql" }
sqlglot-rust = "0.10.0"
sqlglot-rust = "0.10.1"
sqlite3-parser = "0.16.0"
turso_parser = "0.6.1"
fallible-iterator = "0.3.0"
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ On their home dialect the reference bindings are exact by construction, so the m

| Parser | Version | Source | Implementation | Dialects |
| --- | --- | --- | --- | --- |
| **[sqlparser-rs](https://github.com/sqlparser-rs/sqlparser-rs)** | 0.62.0 | git [`575ee26`](https://github.com/sqlparser-rs/sqlparser-rs/commit/575ee264dffa3c5039dc3d15abd903fd420eba2b) | Pure Rust, handwritten recursive descent | 14 dedicated dialects |
| **[sqlglot-rust](https://crates.io/crates/sqlglot-rust)** | 0.10.0 | crates.io | Pure Rust, standalone port of Python sqlglot | 30 (parser currently dialect-agnostic) |
| **[polyglot-sql](https://github.com/tobilg/polyglot)** | 0.4.4 | git [`320cfa8`](https://github.com/tobilg/polyglot/commit/320cfa8a148d624caeff2145f2e5bf1432ec8604) | Pure Rust, transpiler | 32 |
| **[sqlparser-rs](https://github.com/sqlparser-rs/sqlparser-rs)** | 0.62.0 | git [`b376022`](https://github.com/sqlparser-rs/sqlparser-rs/commit/b3760221) | Pure Rust, handwritten recursive descent | 14 dedicated dialects |
| **[sqlglot-rust](https://crates.io/crates/sqlglot-rust)** | 0.10.1 | crates.io | Pure Rust, standalone port of Python sqlglot | 30 (parser currently dialect-agnostic) |
| **[polyglot-sql](https://github.com/tobilg/polyglot)** | 0.5.1 | git [`e3a8913`](https://github.com/tobilg/polyglot/commit/e3a8913a) | Pure Rust, transpiler | 32 |
| **[pg_query.rs](https://github.com/pganalyze/pg_query.rs)** | 6.1.1 | git [`7e189a9`](https://github.com/pganalyze/pg_query.rs/commit/7e189a9dd1d4e441a2d44e6655c793f101bba3fa) | Rust FFI to C (libpg_query) | PostgreSQL |
| **[qusql-parse](https://crates.io/crates/qusql-parse)** | 0.8.0 | crates.io | Pure Rust, zero-copy | PostgreSQL, MariaDB/MySQL, SQLite |
| **[databend-common-ast](https://github.com/datafuselabs/databend)** | 0.2.5 | crates.io | Pure Rust, zero-copy, Pratt | PostgreSQL, MySQL, Hive |
Expand Down
2 changes: 1 addition & 1 deletion featurescan/data/depth.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
"guarded": false,
"shape_rejected": false,
"limit_depth": null,
"crash_depth": 548,
"crash_depth": 412,
"ceil": 50000
},
{
Expand Down
33 changes: 17 additions & 16 deletions featurescan/data/featurescan.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
"package": "sqlparser",
"version": "0.62.0",
"counts": {
"loc": 68907,
"loc": 69033,
"test_lines": 4276,
"code_loc": 64631,
"code_loc": 64757,
"files": 43,
"parse_failures": 0,
"panic": 3,
Expand All @@ -27,11 +27,12 @@
},
"lints": {
"lints": {
"unreachable": "forbid"
"unreachable": "forbid",
"unsafe_code": "forbid"
},
"workspace_inherited": false
},
"forbids_unsafe": false,
"forbids_unsafe": true,
"direct_deps": 6,
"serde_dep": true
},
Expand Down Expand Up @@ -134,12 +135,12 @@
{
"parser": "polyglot-sql",
"package": "polyglot-sql",
"version": "0.4.4",
"version": "0.5.1",
"counts": {
"loc": 239954,
"test_lines": 16786,
"code_loc": 223168,
"files": 74,
"loc": 241432,
"test_lines": 16915,
"code_loc": 224517,
"files": 75,
"parse_failures": 0,
"panic": 5,
"unreachable": 212,
Expand Down Expand Up @@ -198,22 +199,22 @@
{
"parser": "sqlglot-rust",
"package": "sqlglot-rust",
"version": "0.10.0",
"version": "0.10.1",
"counts": {
"loc": 29937,
"test_lines": 3951,
"code_loc": 25986,
"loc": 36480,
"test_lines": 3986,
"code_loc": 32494,
"files": 29,
"parse_failures": 0,
"panic": 0,
"unreachable": 4,
"unreachable": 6,
"unimplemented": 0,
"todo": 0,
"assert": 1,
"unwrap": 21,
"unwrap": 33,
"expect": 4,
"unwrap_unchecked": 0,
"index": 162,
"index": 165,
"unsafe_blocks": 11,
"unsafe_fns": 5,
"unsafe_impls": 2,
Expand Down
4 changes: 2 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -386,10 +386,10 @@ impl BenchParser {
Self::Sqlparser => "0.62.0",
Self::PgQuery | Self::PgQuerySummary => "6.1.1",
Self::Qusql => "0.8.0",
Self::Polyglot => "0.4.4",
Self::Polyglot => "0.5.1",
Self::Databend => "0.2.5",
Self::Orql => "0.1.0",
Self::Sqlglot => "0.10.0",
Self::Sqlglot => "0.10.1",
Self::Sqlite3 => "0.16.0",
Self::Turso => "0.6.1",
}
Expand Down
3 changes: 2 additions & 1 deletion timemachine/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,14 @@ sqlparser_v0_62 = { package = "sqlparser", version = "=0.62.0" }

# sqlglot-rust minors (0.9 and 0.10 are the two semver-incompatible groups).
sqlglot_v0_9 = { package = "sqlglot-rust", version = "=0.9.37" }
sqlglot_v0_10 = { package = "sqlglot-rust", version = "=0.10.0" }
sqlglot_v0_10 = { package = "sqlglot-rust", version = "=0.10.1" }

# polyglot-sql minors (latest patch of each).
polyglot_v0_1 = { package = "polyglot-sql", version = "=0.1.15" }
polyglot_v0_2 = { package = "polyglot-sql", version = "=0.2.3" }
polyglot_v0_3 = { package = "polyglot-sql", version = "=0.3.11" }
polyglot_v0_4 = { package = "polyglot-sql", version = "=0.4.4" }
polyglot_v0_5 = { package = "polyglot-sql", version = "=0.5.1" }

# databend-common-ast minors (single-statement parser).
databend_v0_0 = { package = "databend-common-ast", version = "=0.0.3" }
Expand Down
1 change: 1 addition & 0 deletions timemachine/src/families/polyglot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,3 +101,4 @@ polyglot_version!(PolyglotV0_1, polyglot_v0_1, "0.1.15", "2026-03-16");
polyglot_version!(PolyglotV0_2, polyglot_v0_2, "0.2.3", "2026-04-05");
polyglot_version!(PolyglotV0_3, polyglot_v0_3, "0.3.11", "2026-05-15");
polyglot_version!(PolyglotV0_4, polyglot_v0_4, "0.4.4", "2026-06-03");
polyglot_version!(PolyglotV0_5, polyglot_v0_5, "0.5.1", "2026-06-09");
2 changes: 1 addition & 1 deletion timemachine/src/families/sqlglot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -108,4 +108,4 @@ macro_rules! sqlglot_version {
}

sqlglot_version!(SqlglotV0_9, sqlglot_v0_9, "0.9.37", "2026-05-28");
sqlglot_version!(SqlglotV0_10, sqlglot_v0_10, "0.10.0", "2026-06-03");
sqlglot_version!(SqlglotV0_10, sqlglot_v0_10, "0.10.1", "2026-06-05");
1 change: 1 addition & 0 deletions timemachine/src/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ pub fn all() -> Vec<Box<dyn Parser>> {
Box::new(polyglot::PolyglotV0_2),
Box::new(polyglot::PolyglotV0_3),
Box::new(polyglot::PolyglotV0_4),
Box::new(polyglot::PolyglotV0_5),
Box::new(databend::DatabendV0_0),
Box::new(databend::DatabendV0_1),
Box::new(databend::DatabendV0_2),
Expand Down
Binary file modified web/assets/bench.json.zst
Binary file not shown.
Binary file modified web/assets/history.json.zst
Binary file not shown.
51 changes: 46 additions & 5 deletions web/src/components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ use crate::Route;
use dioxus::prelude::*;
use dioxus_free_icons::icons::fa_brands_icons::{FaGit, FaGithub, FaRust};
use dioxus_free_icons::icons::fa_solid_icons::{
FaArrowLeftLong, FaArrowsRotate, FaBomb, FaBox, FaBug, FaBuilding, FaCalendarDays,
FaChartColumn, FaChartLine, FaCode, FaCodeCommit, FaCodeFork, FaCopy, FaCube, FaDatabase,
FaDownload, FaFlaskVial, FaHeartPulse, FaLayerGroup, FaMicrochip, FaMobileScreen,
FaScaleBalanced, FaServer, FaShieldHalved, FaSitemap, FaStar, FaStopwatch, FaTableCells, FaTag,
FaTriangleExclamation, FaUsers, FaVial,
FaArrowLeftLong, FaArrowsRotate, FaBan, FaBomb, FaBox, FaBug, FaBuilding, FaCalendarDays,
FaChartColumn, FaChartLine, FaCircleXmark, FaCode, FaCodeCommit, FaCodeFork, FaCopy, FaCube,
FaDatabase, FaDna, FaDownload, FaFileShield, FaFlaskVial, FaHeartPulse, FaLayerGroup,
FaMicrochip, FaMobileScreen, FaScaleBalanced, FaServer, FaShieldHalved, FaSitemap, FaStar,
FaStopwatch, FaTableCells, FaTag, FaTriangleExclamation, FaUsers, FaVial,
};
use dioxus_free_icons::Icon;
use std::cmp::Ordering;
Expand Down Expand Up @@ -1458,8 +1458,12 @@ fn parser_meta_pills(parser: &str) -> Element {
{meta_flag(rsx! { Icon { width: 12, height: 12, fill: "currentColor".to_string(), icon: FaRust } }, "impl", if m.pure_rust { "pure Rust".to_string() } else { "C FFI".to_string() }, m.pure_rust, crate::metadata::pure_rust_description(m.pure_rust))}
{meta_flag(rsx! { Icon { width: 12, height: 12, fill: "currentColor".to_string(), icon: FaHeartPulse } }, "maintained", if crate::metadata::maintained(m.last_release) { "active".to_string() } else { "stale".to_string() }, crate::metadata::maintained(m.last_release), &crate::metadata::maintenance_description(m.last_release))}
{meta_flag(rsx! { Icon { width: 12, height: 12, fill: "currentColor".to_string(), icon: FaFlaskVial } }, "miri/san", if m.sanitizers.is_empty() { "no".to_string() } else { m.sanitizers.to_string() }, !m.sanitizers.is_empty(), &crate::metadata::sanitizer_description(m.sanitizers))}
{meta_flag(rsx! { Icon { width: 12, height: 12, fill: "currentColor".to_string(), icon: FaBan } }, "cargo deny", if m.cargo_deny { "yes".to_string() } else { "no".to_string() }, m.cargo_deny, crate::metadata::cargo_deny_description(m.cargo_deny))}
{meta_flag(rsx! { Icon { width: 12, height: 12, fill: "currentColor".to_string(), icon: FaFileShield } }, "cargo audit", if m.cargo_audit { "yes".to_string() } else { "no".to_string() }, m.cargo_audit, crate::metadata::cargo_audit_description(m.cargo_audit))}
{meta_flag(rsx! { Icon { width: 12, height: 12, fill: "currentColor".to_string(), icon: FaDna } }, "cargo mutants", if m.cargo_mutants { "yes".to_string() } else { "no".to_string() }, m.cargo_mutants, crate::metadata::cargo_mutants_description(m.cargo_mutants))}
{feat.map_or_else(|| rsx! {}, panic_discipline_pill)}
{empirical_panic_pill(panic)}
{failures_pill(crate::data::failure_totals(parser))}
{feat.map_or_else(|| rsx! {}, |f| unsafe_pill(f, m.unsafe_note))}
{depth.map_or_else(|| rsx! {}, depth_pill)}
{feat.map_or_else(|| rsx! {}, deps_pill)}
Expand Down Expand Up @@ -1553,6 +1557,43 @@ fn empirical_panic_pill(totals: Option<(usize, usize)>) -> Element {
)
}

/// Failed-to-parse pill: how many statements the parser rejected that it was
/// expected to accept, summed across every dialect. Neutral (informational):
/// every parser misses some real-world SQL, so this is a coverage figure, not an
/// alarm, and a red flag on every parser would dilute the genuine red flags. The
/// value is the absolute count the user asked for; the rate is in the tooltip.
fn failures_pill(totals: Option<(usize, usize)>) -> Element {
let (value, desc) = match totals {
None => (
"n/a".to_string(),
"Failed-to-parse count not available in this snapshot.".to_string(),
),
Some((rejected, expected)) => {
let pct = if expected > 0 {
100.0 * rejected as f64 / expected as f64
} else {
0.0
};
(
commas(rejected),
format!(
"Failed to parse: {} of {} statements ({pct:.2}%) that this parser was \
expected to accept were rejected, summed across every dialect. These are the \
statements listed on the parser's failures download.",
commas(rejected),
commas(expected)
),
)
}
};
meta_item(
rsx! { Icon { width: 12, height: 12, fill: "currentColor".to_string(), icon: FaCircleXmark } },
"failed to parse",
value,
desc,
)
}

/// Unsafe-surface pill: forbidden, none, or a count of unsafe occurrences.
fn unsafe_pill(f: &viz::ParserFeatures, note: &str) -> Element {
let total = f.counts.unsafe_total();
Expand Down
19 changes: 19 additions & 0 deletions web/src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,25 @@ pub fn panic_totals(parser: &str) -> Option<(usize, usize)> {
(attempted > 0).then_some((panicked, attempted))
}

/// Aggregate failed-to-parse totals for one parser across every dialect:
/// `(rejected, expected)`. A statement counts as failed when the parser was
/// expected to accept it (reference-valid statements in reference dialects, every
/// statement in provenance dialects) but rejected it. This is the same set the
/// per-parser failures download lists. Returns `None` when nothing was expected
/// (e.g. an older snapshot without the failures section), so the caller can show
/// the badge as unmeasured rather than 0.
#[must_use]
pub fn failure_totals(parser: &str) -> Option<(usize, usize)> {
let (mut rejected, mut expected) = (0usize, 0usize);
for dialect in &bundle().dialects {
if let Some(f) = dialect.failures.iter().find(|f| f.parser == parser) {
rejected += f.rejected_total;
expected += f.expected_total;
}
}
(expected > 0).then_some((rejected, expected))
}

#[cfg(test)]
mod tests {
/// The committed snapshot must decompress and deserialize into the shared
Expand Down
Loading
Loading