From 912f7b068a042bb7c8985c0c26532a57fa71f804 Mon Sep 17 00:00:00 2001 From: prql-bot <107324867+prql-bot@users.noreply.github.com> Date: Thu, 27 Aug 2026 09:34:29 +0000 Subject: [PATCH 1/2] fix: report an error for an unknown target in the JS binding --- CHANGELOG.md | 7 +++++++ prqlc/bindings/js/src/lib.rs | 27 ++++++++++++++++++++------- prqlc/bindings/js/tests/test_all.mjs | 22 ++++++++++++++++++++++ 3 files changed, 49 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9042f05030b1..ad1ac5d1b6c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -54,6 +54,13 @@ **Integrations**: +- The `prqlc` JS/wasm package now reports an error for an unknown `target`, + rather than silently compiling to generic SQL. A typo such as + `opts.target = "sql.postgress"` previously fell back to `sql.any`, so the + caller got working-but-wrong SQL with no signal. The Python and C bindings + already propagated this error; an unset (empty) `target` still means + `sql.any`. (@prql-bot, #6238) + - The `prqlc` Python package now declares `requires-python = ">=3.10"`. Python 3.9 reached end-of-life in October 2025 and was never exercised by the test matrix, which runs 3.10 and 3.12. The stale `>=3.9` claim also blocked every diff --git a/prqlc/bindings/js/src/lib.rs b/prqlc/bindings/js/src/lib.rs index 31af3061c890..edaef7079e71 100644 --- a/prqlc/bindings/js/src/lib.rs +++ b/prqlc/bindings/js/src/lib.rs @@ -7,9 +7,13 @@ use wasm_bindgen::prelude::*; #[wasm_bindgen] pub fn compile(prql_query: &str, options: Option) -> Option { + let options = match options.map(prqlc::Options::try_from).transpose() { + Ok(options) => options.unwrap_or_default(), + Err(e) => return return_or_throw(Err(e)), + }; + return_or_throw( - prqlc::compile(prql_query, &options.map(|x| x.into()).unwrap_or_default()) - .map_err(|e| e.composed(&prql_query.into())), + prqlc::compile(prql_query, &options).map_err(|e| e.composed(&prql_query.into())), ) } @@ -109,17 +113,26 @@ impl CompileOptions { } } -impl From for prqlc::Options { - fn from(o: CompileOptions) -> Self { - let target = Target::from_str(&o.target).unwrap_or_default(); +impl TryFrom for prqlc::Options { + type Error = prqlc::ErrorMessages; + + fn try_from(o: CompileOptions) -> Result { + // An empty `target` is the unset default, and means `sql.any`. Anything + // else has to parse — otherwise a typo such as `sql.postgress` would + // silently compile to generic SQL. + let target = if o.target.is_empty() { + Target::default() + } else { + Target::from_str(&o.target).map_err(prqlc::ErrorMessages::from)? + }; - prqlc::Options { + Ok(prqlc::Options { format: o.format, target, signature_comment: o.signature_comment, display: prqlc::DisplayOptions::Plain, ..Default::default() - } + }) } } diff --git a/prqlc/bindings/js/tests/test_all.mjs b/prqlc/bindings/js/tests/test_all.mjs index 716de8e73534..1e61d6c530d9 100644 --- a/prqlc/bindings/js/tests/test_all.mjs +++ b/prqlc/bindings/js/tests/test_all.mjs @@ -70,6 +70,28 @@ describe("prqlc-js", () => { ); assert(res.includes("target:sql.mssql")); }); + + it("should throw on an unknown target rather than silently using sql.any", () => { + const opts = new prqlc.CompileOptions(); + opts.target = "sql.postgress"; + + expect(() => prqlc.compile("from a | take 10", opts)).to.throw(); + }); + + it("should treat an unset target as sql.any", () => { + const opts = new prqlc.CompileOptions(); + opts.format = false; + opts.signature_comment = false; + + const res = prqlc.compile( + "prql target:sql.mssql\nfrom a | take 10", + opts, + ); + assert.equal( + res, + "SELECT * FROM a ORDER BY (SELECT NULL) OFFSET 0 ROWS FETCH FIRST 10 ROWS ONLY", + ); + }); }); describe("prql_to_pl", () => { From 896ed05404e8958be110ec95e6af4ff05c7f3102 Mon Sep 17 00:00:00 2001 From: prql-bot <107324867+prql-bot@users.noreply.github.com> Date: Thu, 27 Aug 2026 09:42:08 +0000 Subject: [PATCH 2/2] fix: use a target the typos hook doesn't rewrite MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `postgress` is in the typos dictionary, so the `typos` pre-commit hook — which runs with `--write-changes` — rewrote the test's target to the valid `sql.postgres`, inverting what the test asserts. Also assert the thrown message names the unknown target. --- CHANGELOG.md | 2 +- prqlc/bindings/js/src/lib.rs | 2 +- prqlc/bindings/js/tests/test_all.mjs | 6 ++++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ad1ac5d1b6c2..de42c41ff80c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -56,7 +56,7 @@ - The `prqlc` JS/wasm package now reports an error for an unknown `target`, rather than silently compiling to generic SQL. A typo such as - `opts.target = "sql.postgress"` previously fell back to `sql.any`, so the + `opts.target = "sql.postgrez"` previously fell back to `sql.any`, so the caller got working-but-wrong SQL with no signal. The Python and C bindings already propagated this error; an unset (empty) `target` still means `sql.any`. (@prql-bot, #6238) diff --git a/prqlc/bindings/js/src/lib.rs b/prqlc/bindings/js/src/lib.rs index edaef7079e71..26b69c88126b 100644 --- a/prqlc/bindings/js/src/lib.rs +++ b/prqlc/bindings/js/src/lib.rs @@ -118,7 +118,7 @@ impl TryFrom for prqlc::Options { fn try_from(o: CompileOptions) -> Result { // An empty `target` is the unset default, and means `sql.any`. Anything - // else has to parse — otherwise a typo such as `sql.postgress` would + // else has to parse — otherwise a typo such as `sql.postgrez` would // silently compile to generic SQL. let target = if o.target.is_empty() { Target::default() diff --git a/prqlc/bindings/js/tests/test_all.mjs b/prqlc/bindings/js/tests/test_all.mjs index 1e61d6c530d9..30ea8b27ee6c 100644 --- a/prqlc/bindings/js/tests/test_all.mjs +++ b/prqlc/bindings/js/tests/test_all.mjs @@ -73,9 +73,11 @@ describe("prqlc-js", () => { it("should throw on an unknown target rather than silently using sql.any", () => { const opts = new prqlc.CompileOptions(); - opts.target = "sql.postgress"; + opts.target = "sql.postgrez"; - expect(() => prqlc.compile("from a | take 10", opts)).to.throw(); + expect(() => prqlc.compile("from a | take 10", opts)).to.throw( + "sql.postgrez", + ); }); it("should treat an unset target as sql.any", () => {