Skip to content

fix: apply redirect correctly when resolving wildcard on this - #5875

Merged
max-sixty merged 3 commits into
PRQL:mainfrom
bioteam:kg/wildcard-redirect-fix
May 11, 2026
Merged

fix: apply redirect correctly when resolving wildcard on this#5875
max-sixty merged 3 commits into
PRQL:mainfrom
bioteam:kg/wildcard-redirect-fix

Conversation

@kgutwin

@kgutwin kgutwin commented May 11, 2026

Copy link
Copy Markdown
Collaborator

The following PRQL fails compilation:

from albums
select { album_id, title, artist_id }
sort this.*

With the error:

Error: 
   ╭─[ :1:1 ]
   │
 1 │ ╭─▶ from albums
   ┆ ┆   
 3 │ ├─▶ sort this.*
   │ │                 
   │ ╰───────────────── table instance cannot be referenced directly
   │     
   │     Help: column name might be missing?
───╯

This turns out to be because the process for resolving the wildcard this.* ends up returning the incorrect tuple {albums = {this.albums.album_id, this.albums.title, this.albums.artist_id}} as seen in the debug log below:

[DEBUG prqlc::semantic::resolver::expr] resolving ident this.`*`...
[TRACE prqlc::semantic::module] lookup: this._self
[TRACE prqlc::semantic::module] lookup: _self
[TRACE prqlc::semantic::module] ... following redirect albums
[TRACE prqlc::semantic::module] lookup: _self
[TRACE prqlc::semantic::module] ... result of redirect albums: {["albums", "_self"]}
[TRACE prqlc::semantic::module] ... following redirect this
[TRACE prqlc::semantic::module] lookup: this._self
[TRACE prqlc::semantic::module] ... following redirect albums
[TRACE prqlc::semantic::module] lookup: this._self
[TRACE prqlc::semantic::module] ... result of redirect albums: {}
[TRACE prqlc::semantic::module] ... result of redirect this: {}
[TRACE prqlc::semantic::module] ... following redirect that
[TRACE prqlc::semantic::module] lookup: this._self
[TRACE prqlc::semantic::module] ... result of redirect that: {}
[TRACE prqlc::semantic::module] ... following redirect _param
[TRACE prqlc::semantic::module] lookup: this._self
[TRACE prqlc::semantic::module] ... result of redirect _param: {}
[TRACE prqlc::semantic::module] ... following redirect std
[TRACE prqlc::semantic::module] lookup: this._self
[TRACE prqlc::semantic::module] ... result of redirect std: {}
[DEBUG prqlc::semantic::resolver::expr] ... resolved to _wildcard_match
[DEBUG prqlc::semantic::resolver::expr] ... which is Expr: {albums = {this.albums.album_id, this.albums.title, this.albums.artist_id}}

The tuple instead should be {this.albums.album_id, this.albums.title, this.albums.artist_id}.

The underlying cause of this issue is in this section of resolve_ident_wildcard():

let mut res = self.root_mod.module.lookup(&ident_self);
if res.contains(&ident_self) {
res = HashSet::from_iter([ident_self]);
}
if res.len() != 1 {
return Err(format!("Unknown relation {ident}"));
}
let module_fq_self = res.into_iter().next().unwrap();

When ident_self is ["this", "_self"], self.root_mod.module.lookup(&ident_self) returns a HashSet containing both ident_self and the redirect (["this", "albums", "_self"]). However, the next if res.contains() statement drops the redirect, so the value of module_fq_self ends up just being ["this", "_self"]. This ends up getting wildcard-expanded to the tuple representing the literal module this._self, rather than the correct redirect this.albums._self.

This PR makes two small changes:

  • After successfully following a redirect in Module::lookup(), we remove the ident that triggered the redirect from the result set. This avoids inadvertently triggering "ambiguous name" results.
  • The resolve_ident_wildcard() function has been refactored to use the same match decls.len() structure as used in other resolve_ident functions. This harmonizes error conditions across the name resolve process and avoids the bug behavior triggered by the current if res.contains() logic.

@prql-bot prql-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CI is failing on queries::results::wildcard_this because the new integration test is missing the integration__queries__results__wildcard_this.snap snapshot — task prqlc:test-all (or task prqlc:pull-request) accepts the snapshot locally, but the file then has to be committed alongside the others.

Project guidance (prqlc/prqlc/tests/CLAUDE.md) actually prefers small inline insta::assert_snapshot! tests in prqlc/prqlc/tests/integration/sql.rs over .prql integration tests: each .prql file generates ~6 snapshots, and for a compilation-stage fix like this you can get equivalent coverage with one snapshot. There's a near-twin pattern at tests/integration/sql.rs:6020 (test_select_bare_wildcard). Replacing the new files with a single test_sort_this_wildcard would also sidestep the missing-results-snapshot issue entirely.

Substantively the fix reads correctly to me — the Module::lookup change drops the literal match from res only when a redirect actually resolves the same ident non-empty, and the only place that ambiguity arises in practice is _self lookups (since columns live in per-input sub-modules, not directly under this). The resolve_ident_wildcard cleanup harmonizes nicely with resolve_ident_core/resolve_ident_fallback.

@max-sixty
max-sixty merged commit 2c0465a into PRQL:main May 11, 2026
34 of 35 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants