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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.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)

- 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
Expand Down
27 changes: 20 additions & 7 deletions prqlc/bindings/js/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@ use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn compile(prql_query: &str, options: Option<CompileOptions>) -> Option<String> {
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())),
)
}

Expand Down Expand Up @@ -109,17 +113,26 @@ impl CompileOptions {
}
}

impl From<CompileOptions> for prqlc::Options {
fn from(o: CompileOptions) -> Self {
let target = Target::from_str(&o.target).unwrap_or_default();
impl TryFrom<CompileOptions> for prqlc::Options {
type Error = prqlc::ErrorMessages;

fn try_from(o: CompileOptions) -> Result<Self, Self::Error> {
// An empty `target` is the unset default, and means `sql.any`. Anything
// 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()
} 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()
}
})
}
}

Expand Down
24 changes: 24 additions & 0 deletions prqlc/bindings/js/tests/test_all.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,30 @@ 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.postgrez";

expect(() => prqlc.compile("from a | take 10", opts)).to.throw(
"sql.postgrez",
);
});

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", () => {
Expand Down
Loading