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

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():

letmut res = self.root_mod.module.lookup(&ident_self);
if res.contains(&ident_self){
res = HashSet::from_iter([ident_self]);
}
if res.len() != 1{
returnErr(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-botprql-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:mainMay 11, 2026
34 of 35 checks passed
Sign up for freeto 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

@kgutwin@prql-bot@max-sixty
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
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

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():

letmut res = self.root_mod.module.lookup(&ident_self);
if res.contains(&ident_self){
res = HashSet::from_iter([ident_self]);
}
if res.len() != 1{
returnErr(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-botprql-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:mainMay 11, 2026
34 of 35 checks passed
Sign up for freeto 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

@kgutwin@prql-bot@max-sixty
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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

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():

letmut res = self.root_mod.module.lookup(&ident_self);
if res.contains(&ident_self){
res = HashSet::from_iter([ident_self]);
}
if res.len() != 1{
returnErr(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-botprql-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:mainMay 11, 2026
34 of 35 checks passed
Sign up for freeto 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

@kgutwin@prql-bot@max-sixty
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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

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():

letmut res = self.root_mod.module.lookup(&ident_self);
if res.contains(&ident_self){
res = HashSet::from_iter([ident_self]);
}
if res.len() != 1{
returnErr(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-botprql-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:mainMay 11, 2026
34 of 35 checks passed
Sign up for freeto 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

@kgutwin@prql-bot@max-sixty
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
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

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():

letmut res = self.root_mod.module.lookup(&ident_self);
if res.contains(&ident_self){
res = HashSet::from_iter([ident_self]);
}
if res.len() != 1{
returnErr(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-botprql-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:mainMay 11, 2026
34 of 35 checks passed
Sign up for freeto 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

@kgutwin@prql-bot@max-sixty
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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

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():

letmut res = self.root_mod.module.lookup(&ident_self);
if res.contains(&ident_self){
res = HashSet::from_iter([ident_self]);
}
if res.len() != 1{
returnErr(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-botprql-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:mainMay 11, 2026
34 of 35 checks passed
Sign up for freeto 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

@kgutwin@prql-bot@max-sixty
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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

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():

letmut res = self.root_mod.module.lookup(&ident_self);
if res.contains(&ident_self){
res = HashSet::from_iter([ident_self]);
}
if res.len() != 1{
returnErr(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-botprql-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:mainMay 11, 2026
34 of 35 checks passed
Sign up for freeto 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

@kgutwin@prql-bot@max-sixty
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
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

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():

letmut res = self.root_mod.module.lookup(&ident_self);
if res.contains(&ident_self){
res = HashSet::from_iter([ident_self]);
}
if res.len() != 1{
returnErr(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-botprql-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:mainMay 11, 2026
34 of 35 checks passed
Sign up for freeto 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

@kgutwin@prql-bot@max-sixty