Skip to content
Open
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
9 changes: 6 additions & 3 deletions prqlc/prqlc/src/semantic/resolver/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,13 @@ impl Resolver<'_> {
// Get the missing params (params that don't have args yet)
let missing = inner_closure.params[inner_closure.args.len()..].to_vec();

// Create wrapper params and add references to them as args to the inner closure
// Create wrapper params and add references to them as args to the inner closure.
// The param names must be globally unique: nested partial applications share the
// NS_PARAM namespace, so a per-materialization index would collide and resolve to
// the wrong binding (issue #5978).
let mut wrapper_params = Vec::with_capacity(missing.len());
for (i, param) in missing.iter().enumerate() {
let param_name = format!("_partial_{i}");
for param in missing.iter() {
let param_name = format!("_partial_{}", self.id.gen());
let substitute_arg = Expr::new(Ident::from_path(vec![
NS_PARAM.to_string(),
param_name.clone(),
Expand Down
27 changes: 27 additions & 0 deletions prqlc/prqlc/tests/integration/sql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7385,6 +7385,33 @@ fn test_partial_application_of_transform() {
");
}

/// Regression test for issue #5978: currying a function through more than two
/// `let`-bound wrappers produced the wrong result, because the wrapper params
/// synthesized during partial-application materialization were named by a
/// per-materialization index (`_partial_0`, `_partial_1`, …) and collided
/// across nested partial applications sharing the `NS_PARAM` namespace.
#[test]
fn test_nested_currying() {
// `y` (curried one arg at a time) must match `z` (curried directly).
assert_snapshot!(compile(r#"
let f1 = func a b c -> a + b + c
let f2 = func a b -> f1 a b
let f3 = func a -> f2 a

from t | derive {
y = (((f3 100) 20) 3),
z = ((f2 100 20) 3),
}
"#).unwrap(), @"
SELECT
*,
100 + 20 + 3 AS y,
100 + 20 + 3 AS z
FROM
t
");
}

#[test]
fn test_tuple_map() {
assert_snapshot!(compile(r###"
Expand Down
Loading