diff --git a/Cargo.lock b/Cargo.lock index 4a6c4d54..adcf5c3c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -605,7 +605,7 @@ dependencies = [ [[package]] name = "polyglot-sql" -version = "0.13.6" +version = "0.13.7" dependencies = [ "criterion", "once_cell", @@ -624,7 +624,7 @@ dependencies = [ [[package]] name = "polyglot-sql-ast-derive" -version = "0.13.6" +version = "0.13.7" dependencies = [ "proc-macro2", "quote", @@ -633,7 +633,7 @@ dependencies = [ [[package]] name = "polyglot-sql-ffi" -version = "0.13.6" +version = "0.13.7" dependencies = [ "cbindgen", "polyglot-sql", @@ -643,11 +643,11 @@ dependencies = [ [[package]] name = "polyglot-sql-function-catalogs" -version = "0.13.6" +version = "0.13.7" [[package]] name = "polyglot-sql-python" -version = "0.13.6" +version = "0.13.7" dependencies = [ "polyglot-sql", "pyo3", @@ -658,7 +658,7 @@ dependencies = [ [[package]] name = "polyglot-sql-wasm" -version = "0.13.6" +version = "0.13.7" dependencies = [ "console_error_panic_hook", "js-sys", diff --git a/Cargo.toml b/Cargo.toml index 59bd6d81..db026e85 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ exclude = [ ] [workspace.package] -version = "0.13.6" +version = "0.13.7" edition = "2021" license = "MIT" authors = ["polyglot contributors"] diff --git a/README.md b/README.md index d9695225..f71d9881 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.6", default-features = false, features = ["all-dialects", "transpile"] } +polyglot-sql = { version = "0.13.7", 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 c677fdf6..6edc4e0f 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.6", features = ["function-catalog-clickhouse"] } +polyglot-sql = { version = "0.13.7", 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 983d8774..1fc3ecba 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.6" } +polyglot-sql-ast-derive = { path = "../polyglot-sql-ast-derive", version = "0.13.7" } 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.6", optional = true, default-features = false } +polyglot-sql-function-catalogs = { path = "../polyglot-sql-function-catalogs", version = "0.13.7", 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 01c87eff..8b16c939 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.6", default-features = false } +polyglot-sql = { version = "0.13.7", default-features = false } ``` ```toml polyglot-sql = { - version = "0.13.6", + version = "0.13.7", default-features = false, features = ["dialect-clickhouse"], } @@ -44,14 +44,14 @@ Examples: ```toml # Parse and generate SQL for one dialect. polyglot-sql = { - version = "0.13.6", + version = "0.13.7", default-features = false, features = ["generate", "dialect-clickhouse"], } # Cross-dialect transpilation. polyglot-sql = { - version = "0.13.6", + version = "0.13.7", 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 7b067be5..3b77797e 100644 --- a/crates/polyglot-sql/src/validation.rs +++ b/crates/polyglot-sql/src/validation.rs @@ -1614,6 +1614,15 @@ fn check_generic_function( } } +/// DuckDB rewrites these SQL keywords itself; they are not callable functions, +/// so only the bracket-free keyword form is valid. +const DUCKDB_KEYWORD_ONLY_FUNCTIONS: [&str; 4] = [ + "current_time", + "current_timestamp", + "localtime", + "localtimestamp", +]; + fn check_function_catalog( function: &Function, dialect: DialectType, @@ -1621,6 +1630,24 @@ fn check_function_catalog( strict: bool, errors: &mut Vec, ) { + if function_catalog.is_some() + && dialect == DialectType::DuckDB + && !function.no_parens + && DUCKDB_KEYWORD_ONLY_FUNCTIONS + .iter() + .any(|keyword| function_base_name(&function.name).eq_ignore_ascii_case(keyword)) + { + let message = format!( + "Unknown function '{}' for dialect {:?}", + function.name, dialect + ); + errors.push(if strict { + ValidationError::error(message, validation_codes::E_UNKNOWN_FUNCTION) + } else { + ValidationError::warning(message, validation_codes::E_UNKNOWN_FUNCTION) + }); + return; + } check_named_function_catalog( &function.name, function.args.len(), diff --git a/crates/polyglot-sql/src/validation/tests.rs b/crates/polyglot-sql/src/validation/tests.rs index ac16cffb..dc5f8e29 100644 --- a/crates/polyglot-sql/src/validation/tests.rs +++ b/crates/polyglot-sql/src/validation/tests.rs @@ -1125,6 +1125,42 @@ fn review_builtin_and_unknown_cast_targets_do_not_error() { } } +#[test] +fn duckdb_keyword_time_functions_are_valid_only_without_brackets() { + let options = SchemaValidationOptions { + semantic: true, + check_types: true, + ..Default::default() + }; + let schema = semantic_type_schema(); + for sql in [ + "SELECT CURRENT_TIMESTAMP AS ts", + "SELECT current_time AS t", + "SELECT LOCALTIMESTAMP AS ts", + "SELECT localtime AS t", + ] { + let result = validate_with_schema(sql, DialectType::DuckDB, &schema, &options); + assert!( + !result.errors.iter().any(|e| e.code == "E202"), + "{sql}: {:?}", + result.errors + ); + } + for sql in [ + "SELECT CURRENT_TIMESTAMP() AS ts", + "SELECT current_time() AS t", + "SELECT LOCALTIMESTAMP() AS ts", + "SELECT localtime() AS t", + ] { + let result = validate_with_schema(sql, DialectType::DuckDB, &schema, &options); + assert!( + !result.valid && result.errors.iter().any(|e| e.code == "E202"), + "{sql}: {:?}", + result.errors + ); + } +} + #[test] fn star_sourced_types_are_checked_without_set_operations() { let options = SchemaValidationOptions { diff --git a/examples/rust/Cargo.lock b/examples/rust/Cargo.lock index 973b2c3c..bb7e426f 100644 --- a/examples/rust/Cargo.lock +++ b/examples/rust/Cargo.lock @@ -71,7 +71,7 @@ dependencies = [ [[package]] name = "polyglot-sql" -version = "0.13.6" +version = "0.13.7" dependencies = [ "polyglot-sql-ast-derive", "polyglot-sql-function-catalogs", @@ -85,7 +85,7 @@ dependencies = [ [[package]] name = "polyglot-sql-ast-derive" -version = "0.13.6" +version = "0.13.7" dependencies = [ "proc-macro2", "quote", @@ -101,7 +101,7 @@ dependencies = [ [[package]] name = "polyglot-sql-function-catalogs" -version = "0.13.6" +version = "0.13.7" [[package]] name = "proc-macro2" diff --git a/examples/rust/Cargo.toml b/examples/rust/Cargo.toml index 1764682a..82d50ea2 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.6", path = "../../crates/polyglot-sql" } +polyglot-sql = { version = "0.13.7", path = "../../crates/polyglot-sql" } diff --git a/packages/documentation/package.json b/packages/documentation/package.json index e33858c3..5df851a1 100644 --- a/packages/documentation/package.json +++ b/packages/documentation/package.json @@ -1,6 +1,6 @@ { "name": "@polyglot-sql/documentation", - "version": "0.13.6", + "version": "0.13.7", "private": true, "type": "module", "scripts": { diff --git a/packages/go/README.md b/packages/go/README.md index 79887ea7..873839a0 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.6`. +example `packages/go/v0.13.7`. ## Native Library Setup diff --git a/packages/go/types.go b/packages/go/types.go index fe1eddc2..84ce5d14 100644 --- a/packages/go/types.go +++ b/packages/go/types.go @@ -2,7 +2,7 @@ package polyglot import "encoding/json" -const sdkVersion = "0.13.6" +const sdkVersion = "0.13.7" func Version() string { return sdkVersion diff --git a/packages/playground/package.json b/packages/playground/package.json index db70d002..5a52a05b 100644 --- a/packages/playground/package.json +++ b/packages/playground/package.json @@ -1,6 +1,6 @@ { "name": "@polyglot-sql/playground", - "version": "0.13.6", + "version": "0.13.7", "private": true, "type": "module", "scripts": { diff --git a/packages/python-docs/package.json b/packages/python-docs/package.json index f544f4bc..f8657aa7 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.6", + "version": "0.13.7", "private": true, "type": "module", "scripts": { diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 4f47c171..45607e62 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@polyglot-sql/sdk", - "version": "0.13.6", + "version": "0.13.7", "description": "SQL dialect translator powered by WebAssembly", "type": "module", "main": "./dist/index.cjs",