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
12 changes: 6 additions & 6 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ exclude = [
]

[workspace.package]
version = "0.13.4"
version = "0.13.5"
edition = "2021"
license = "MIT"
authors = ["polyglot contributors"]
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.4", default-features = false, features = ["all-dialects", "transpile"] }
polyglot-sql = { version = "0.13.5", 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.
Expand Down
2 changes: 1 addition & 1 deletion crates/polyglot-sql-function-catalogs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ With `polyglot-sql` compile-time wiring (recommended):

```toml
[dependencies]
polyglot-sql = { version = "0.13.4", features = ["function-catalog-clickhouse"] }
polyglot-sql = { version = "0.13.5", features = ["function-catalog-clickhouse"] }
```

Then run schema validation with type checks:
Expand Down
4 changes: 2 additions & 2 deletions crates/polyglot-sql/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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.4" }
polyglot-sql-ast-derive = { path = "../polyglot-sql-ast-derive", version = "0.13.5" }
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.4", optional = true, default-features = false }
polyglot-sql-function-catalogs = { path = "../polyglot-sql-function-catalogs", version = "0.13.5", optional = true, default-features = false }

[dev-dependencies]
pretty_assertions = "1.4"
Expand Down
8 changes: 4 additions & 4 deletions crates/polyglot-sql/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.4", default-features = false }
polyglot-sql = { version = "0.13.5", default-features = false }
```

```toml
polyglot-sql = {
version = "0.13.4",
version = "0.13.5",
default-features = false,
features = ["dialect-clickhouse"],
}
Expand All @@ -44,14 +44,14 @@ Examples:
```toml
# Parse and generate SQL for one dialect.
polyglot-sql = {
version = "0.13.4",
version = "0.13.5",
default-features = false,
features = ["generate", "dialect-clickhouse"],
}

# Cross-dialect transpilation.
polyglot-sql = {
version = "0.13.4",
version = "0.13.5",
default-features = false,
features = ["transpile", "dialect-clickhouse", "dialect-postgresql"],
}
Expand Down
52 changes: 35 additions & 17 deletions crates/polyglot-sql/src/validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4558,23 +4558,23 @@ pub fn validate_parsed_with_schema(
));
let mut statement = apply_projection_alias_bindings(statement, &bindings);
if options.check_types {
// Expand known stars using the same qualification path as lineage.
// Open/partial schemas remain conservative if qualification fails.
if statement.dfs().any(|node| {
matches!(
node,
Expression::Union(_) | Expression::Intersect(_) | Expression::Except(_)
)
}) && statement
.dfs()
.any(|node| matches!(node, Expression::Star(_)))
{
if let Ok(qualified) = crate::optimizer::qualify_schema_aware_expression(
statement.clone(),
&resolver_schema,
Some(dialect),
) {
statement = qualified;
// 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) {
crate::lineage::expand_cte_stars(
&mut statement,
Some(&resolver_schema as &dyn crate::schema::Schema),
);
if has_projection_star(&statement) {
if let Ok(qualified) = crate::optimizer::qualify_schema_aware_expression(
statement.clone(),
&resolver_schema,
Some(dialect),
) {
statement = qualified;
}
}
}
annotate_types(&mut statement, Some(&resolver_schema), Some(dialect));
Expand Down Expand Up @@ -4604,6 +4604,24 @@ pub fn validate_parsed_with_schema(
ValidationResult::with_errors(all_errors)
}

fn has_projection_star(statement: &Expression) -> bool {
statement.dfs().any(|node| {
matches!(node, Expression::Select(select) if select.expressions.iter().any(|projection| {
let mut projection = projection;
loop {
match projection {
Expression::Alias(alias) => projection = &alias.this,
Expression::Annotated(annotated) => projection = &annotated.this,
Expression::Paren(paren) => projection = &paren.this,
_ => break,
}
}
matches!(projection, Expression::Star(_))
|| matches!(projection, Expression::Column(column) if column.name.name == "*")
}))
})
}

/// Validate an already bound project-query scope without rebuilding its tree.
/// The caller retains the standalone path for type checks, semantic lint, stars,
/// and statement kinds that require DML-specific binding.
Expand Down
40 changes: 40 additions & 0 deletions crates/polyglot-sql/src/validation/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1125,6 +1125,46 @@ fn review_builtin_and_unknown_cast_targets_do_not_error() {
}
}

#[test]
fn star_sourced_types_are_checked_without_set_operations() {
let options = SchemaValidationOptions {
semantic: true,
check_types: true,
..Default::default()
};
for sql in [
"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",
] {
let result = validate_with_schema(
sql,
DialectType::Snowflake,
&semantic_type_schema(),
&options,
);
assert!(
result.errors.iter().any(|e| e.code == "W210"),
"{sql}: {:?}",
result.errors
);
}
let result = validate_with_schema(
"WITH a AS (SELECT * FROM orders) SELECT 1 FROM a JOIN orders AS o ON a.i = o.i",
DialectType::Snowflake,
&semantic_type_schema(),
&options,
);
assert!(
!result
.errors
.iter()
.any(|e| e.code.get(1..).is_some_and(|n| n.starts_with("21"))),
"{:?}",
result.errors
);
}

fn semantic_type_schema() -> ValidationSchema {
serde_json::from_value(serde_json::json!({"tables":[{"name":"orders","columns":[
{"name":"i","type":"INTEGER"}, {"name":"s","type":"VARCHAR"},
Expand Down
6 changes: 3 additions & 3 deletions examples/rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion examples/rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ edition = "2021"
publish = false

[dependencies]
polyglot-sql = { version = "0.13.4", path = "../../crates/polyglot-sql" }
polyglot-sql = { version = "0.13.5", path = "../../crates/polyglot-sql" }
2 changes: 1 addition & 1 deletion packages/documentation/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@polyglot-sql/documentation",
"version": "0.13.4",
"version": "0.13.5",
"private": true,
"type": "module",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion packages/go/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.4`.
example `packages/go/v0.13.5`.

## Native Library Setup

Expand Down
2 changes: 1 addition & 1 deletion packages/go/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package polyglot

import "encoding/json"

const sdkVersion = "0.13.4"
const sdkVersion = "0.13.5"

func Version() string {
return sdkVersion
Expand Down
2 changes: 1 addition & 1 deletion packages/playground/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@polyglot-sql/playground",
"version": "0.13.4",
"version": "0.13.5",
"private": true,
"type": "module",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion packages/python-docs/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@polyglot-sql/python-docs",
"version": "0.13.4",
"version": "0.13.5",
"private": true,
"type": "module",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion packages/sdk/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@polyglot-sql/sdk",
"version": "0.13.4",
"version": "0.13.5",
"description": "SQL dialect translator powered by WebAssembly",
"type": "module",
"main": "./dist/index.cjs",
Expand Down
Loading