From 0cdc91858ac1321c9a6d5fe12ae41ef1131267e5 Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Sun, 27 Sep 2026 02:28:07 +0100 Subject: [PATCH] perf: skip requalification for stars no scope consumes Validation fell back to whole-statement qualification whenever a star remained after ordered CTE expansion, including a star in the root SELECT's own projection. That star feeds no expression in the statement, so type checks never need its expansion. Only fall back for unresolved stars read by an outer scope or a set operation. Bump to 0.13.6. --- Cargo.lock | 12 +++++----- Cargo.toml | 2 +- README.md | 2 +- .../polyglot-sql-function-catalogs/README.md | 2 +- crates/polyglot-sql/Cargo.toml | 4 ++-- crates/polyglot-sql/README.md | 8 +++---- crates/polyglot-sql/src/validation.rs | 24 ++++++++++++++----- crates/polyglot-sql/src/validation/tests.rs | 2 ++ examples/rust/Cargo.lock | 6 ++--- examples/rust/Cargo.toml | 2 +- packages/documentation/package.json | 2 +- packages/go/README.md | 2 +- packages/go/types.go | 2 +- packages/playground/package.json | 2 +- packages/python-docs/package.json | 2 +- packages/sdk/package.json | 2 +- 16 files changed, 45 insertions(+), 31 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ac5e0d42..4a6c4d54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -605,7 +605,7 @@ dependencies = [ [[package]] name = "polyglot-sql" -version = "0.13.5" +version = "0.13.6" dependencies = [ "criterion", "once_cell", @@ -624,7 +624,7 @@ dependencies = [ [[package]] name = "polyglot-sql-ast-derive" -version = "0.13.5" +version = "0.13.6" dependencies = [ "proc-macro2", "quote", @@ -633,7 +633,7 @@ dependencies = [ [[package]] name = "polyglot-sql-ffi" -version = "0.13.5" +version = "0.13.6" dependencies = [ "cbindgen", "polyglot-sql", @@ -643,11 +643,11 @@ dependencies = [ [[package]] name = "polyglot-sql-function-catalogs" -version = "0.13.5" +version = "0.13.6" [[package]] name = "polyglot-sql-python" -version = "0.13.5" +version = "0.13.6" dependencies = [ "polyglot-sql", "pyo3", @@ -658,7 +658,7 @@ dependencies = [ [[package]] name = "polyglot-sql-wasm" -version = "0.13.5" +version = "0.13.6" dependencies = [ "console_error_panic_hook", "js-sys", diff --git a/Cargo.toml b/Cargo.toml index bb38d7ea..59bd6d81 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ exclude = [ ] [workspace.package] -version = "0.13.5" +version = "0.13.6" edition = "2021" license = "MIT" authors = ["polyglot contributors"] diff --git a/README.md b/README.md index 225f2966..d9695225 100644 --- a/README.md +++ b/README.md @@ -306,7 +306,7 @@ If you want to disable `stacker` for a native Rust build, turn off default featu ```toml [dependencies] -polyglot-sql = { version = "0.13.5", default-features = false, features = ["all-dialects", "transpile"] } +polyglot-sql = { version = "0.13.6", default-features = false, features = ["all-dialects", "transpile"] } ``` That can reduce overhead slightly on trusted inputs, but you lose the default stack-growth protection for deeply nested SQL. diff --git a/crates/polyglot-sql-function-catalogs/README.md b/crates/polyglot-sql-function-catalogs/README.md index 157fb145..c677fdf6 100644 --- a/crates/polyglot-sql-function-catalogs/README.md +++ b/crates/polyglot-sql-function-catalogs/README.md @@ -25,7 +25,7 @@ With `polyglot-sql` compile-time wiring (recommended): ```toml [dependencies] -polyglot-sql = { version = "0.13.5", features = ["function-catalog-clickhouse"] } +polyglot-sql = { version = "0.13.6", features = ["function-catalog-clickhouse"] } ``` Then run schema validation with type checks: diff --git a/crates/polyglot-sql/Cargo.toml b/crates/polyglot-sql/Cargo.toml index c37b1d68..983d8774 100644 --- a/crates/polyglot-sql/Cargo.toml +++ b/crates/polyglot-sql/Cargo.toml @@ -100,14 +100,14 @@ function-catalog-all-dialects = [ [dependencies] regex = { version = "1", optional = true } -polyglot-sql-ast-derive = { path = "../polyglot-sql-ast-derive", version = "0.13.5" } +polyglot-sql-ast-derive = { path = "../polyglot-sql-ast-derive", version = "0.13.6" } serde = { workspace = true } serde_json = { workspace = true } thiserror = { workspace = true } unicode-segmentation = { workspace = true } stacker = { version = "0.1", optional = true } ts-rs = { version = "12.0", features = ["serde-compat"], optional = true } -polyglot-sql-function-catalogs = { path = "../polyglot-sql-function-catalogs", version = "0.13.5", optional = true, default-features = false } +polyglot-sql-function-catalogs = { path = "../polyglot-sql-function-catalogs", version = "0.13.6", optional = true, default-features = false } [dev-dependencies] pretty_assertions = "1.4" diff --git a/crates/polyglot-sql/README.md b/crates/polyglot-sql/README.md index c5fe2ebd..01c87eff 100644 --- a/crates/polyglot-sql/README.md +++ b/crates/polyglot-sql/README.md @@ -25,12 +25,12 @@ By default, `polyglot-sql` enables the full public API. Parser-only consumers ca disable default features and opt into only the dialect parsers they need: ```toml -polyglot-sql = { version = "0.13.5", default-features = false } +polyglot-sql = { version = "0.13.6", default-features = false } ``` ```toml polyglot-sql = { - version = "0.13.5", + version = "0.13.6", default-features = false, features = ["dialect-clickhouse"], } @@ -44,14 +44,14 @@ Examples: ```toml # Parse and generate SQL for one dialect. polyglot-sql = { - version = "0.13.5", + version = "0.13.6", default-features = false, features = ["generate", "dialect-clickhouse"], } # Cross-dialect transpilation. polyglot-sql = { - version = "0.13.5", + version = "0.13.6", default-features = false, features = ["transpile", "dialect-clickhouse", "dialect-postgresql"], } diff --git a/crates/polyglot-sql/src/validation.rs b/crates/polyglot-sql/src/validation.rs index 4ba0b2a2..7b067be5 100644 --- a/crates/polyglot-sql/src/validation.rs +++ b/crates/polyglot-sql/src/validation.rs @@ -4560,14 +4560,15 @@ pub fn validate_parsed_with_schema( if options.check_types { // Give the type checker star-sourced column types for every query. // Ordered CTE/source star expansion is the cheap lineage path; full - // qualification is only needed for stars it cannot resolve, such as - // stars over derived tables. Open/partial schemas remain conservative. - if has_projection_star(&statement) { + // qualification is only needed for unresolved stars that another scope + // consumes, such as a star over a derived table inside a subquery. + // Open/partial schemas remain conservative. + if has_projection_star(&statement, false) { crate::lineage::expand_cte_stars( &mut statement, Some(&resolver_schema as &dyn crate::schema::Schema), ); - if has_projection_star(&statement) { + if has_projection_star(&statement, true) { if let Ok(qualified) = crate::optimizer::qualify_schema_aware_expression( statement.clone(), &resolver_schema, @@ -4604,9 +4605,20 @@ pub fn validate_parsed_with_schema( ValidationResult::with_errors(all_errors) } -fn has_projection_star(statement: &Expression) -> bool { +/// Whether any SELECT projects a star. With `consumed_only`, the root SELECT's +/// own projection is ignored: its star feeds no expression in the statement, so +/// only stars read by an outer scope or a set operation affect type checks. +fn has_projection_star(statement: &Expression, consumed_only: bool) -> bool { + let root = match statement { + Expression::Select(select) if consumed_only => { + Some(select.as_ref() as *const crate::expressions::Select) + } + _ => None, + }; statement.dfs().any(|node| { - matches!(node, Expression::Select(select) if select.expressions.iter().any(|projection| { + matches!(node, Expression::Select(select) + if root != Some(select.as_ref() as *const crate::expressions::Select) + && select.expressions.iter().any(|projection| { let mut projection = projection; loop { match projection { diff --git a/crates/polyglot-sql/src/validation/tests.rs b/crates/polyglot-sql/src/validation/tests.rs index 13a198f5..ac16cffb 100644 --- a/crates/polyglot-sql/src/validation/tests.rs +++ b/crates/polyglot-sql/src/validation/tests.rs @@ -1136,6 +1136,8 @@ fn star_sourced_types_are_checked_without_set_operations() { "WITH a AS (SELECT * FROM orders) SELECT 1 FROM a JOIN orders AS o ON a.s = o.i", "WITH a AS (SELECT * FROM orders), b AS (SELECT * FROM a) SELECT i FROM b WHERE s = i", "SELECT 1 FROM (SELECT * FROM orders) AS a JOIN orders AS o ON a.s = o.i", + "SELECT * FROM (SELECT o.s = p.i AS x FROM orders AS o JOIN orders AS p ON TRUE) AS a", + "SELECT * FROM (SELECT * FROM orders) AS a WHERE a.s = a.i", ] { let result = validate_with_schema( sql, diff --git a/examples/rust/Cargo.lock b/examples/rust/Cargo.lock index c70b2093..973b2c3c 100644 --- a/examples/rust/Cargo.lock +++ b/examples/rust/Cargo.lock @@ -71,7 +71,7 @@ dependencies = [ [[package]] name = "polyglot-sql" -version = "0.13.5" +version = "0.13.6" dependencies = [ "polyglot-sql-ast-derive", "polyglot-sql-function-catalogs", @@ -85,7 +85,7 @@ dependencies = [ [[package]] name = "polyglot-sql-ast-derive" -version = "0.13.5" +version = "0.13.6" dependencies = [ "proc-macro2", "quote", @@ -101,7 +101,7 @@ dependencies = [ [[package]] name = "polyglot-sql-function-catalogs" -version = "0.13.5" +version = "0.13.6" [[package]] name = "proc-macro2" diff --git a/examples/rust/Cargo.toml b/examples/rust/Cargo.toml index c90ba3b2..1764682a 100644 --- a/examples/rust/Cargo.toml +++ b/examples/rust/Cargo.toml @@ -5,4 +5,4 @@ edition = "2021" publish = false [dependencies] -polyglot-sql = { version = "0.13.5", path = "../../crates/polyglot-sql" } +polyglot-sql = { version = "0.13.6", path = "../../crates/polyglot-sql" } diff --git a/packages/documentation/package.json b/packages/documentation/package.json index e9acc43d..e33858c3 100644 --- a/packages/documentation/package.json +++ b/packages/documentation/package.json @@ -1,6 +1,6 @@ { "name": "@polyglot-sql/documentation", - "version": "0.13.5", + "version": "0.13.6", "private": true, "type": "module", "scripts": { diff --git a/packages/go/README.md b/packages/go/README.md index 7d093f0c..79887ea7 100644 --- a/packages/go/README.md +++ b/packages/go/README.md @@ -21,7 +21,7 @@ go get github.com/tobilg/polyglot/packages/go ``` Go module releases use nested tags that match the root Polyglot release, for -example `packages/go/v0.13.5`. +example `packages/go/v0.13.6`. ## Native Library Setup diff --git a/packages/go/types.go b/packages/go/types.go index ee9117e8..fe1eddc2 100644 --- a/packages/go/types.go +++ b/packages/go/types.go @@ -2,7 +2,7 @@ package polyglot import "encoding/json" -const sdkVersion = "0.13.5" +const sdkVersion = "0.13.6" func Version() string { return sdkVersion diff --git a/packages/playground/package.json b/packages/playground/package.json index 4f53b638..db70d002 100644 --- a/packages/playground/package.json +++ b/packages/playground/package.json @@ -1,6 +1,6 @@ { "name": "@polyglot-sql/playground", - "version": "0.13.5", + "version": "0.13.6", "private": true, "type": "module", "scripts": { diff --git a/packages/python-docs/package.json b/packages/python-docs/package.json index 0365ccae..f544f4bc 100644 --- a/packages/python-docs/package.json +++ b/packages/python-docs/package.json @@ -1,6 +1,6 @@ { "name": "@polyglot-sql/python-docs", - "version": "0.13.5", + "version": "0.13.6", "private": true, "type": "module", "scripts": { diff --git a/packages/sdk/package.json b/packages/sdk/package.json index cb1ee5f5..4f47c171 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@polyglot-sql/sdk", - "version": "0.13.5", + "version": "0.13.6", "description": "SQL dialect translator powered by WebAssembly", "type": "module", "main": "./dist/index.cjs",