Skip to content

Correlated IN subquery fails with "Projections require unique expression names" when its column is cast #26033

Open

Description

@comphead

Describe the bug

A correlated IN subquery fails to plan when its output column is cast, for example because the IN value has a different type. The query is valid, and DuckDB runs it.

To Reproduce

CREATE TABLE o(k INT, b BIGINT) AS VALUES (1, 1), (2, 2), (3, 30);
CREATE TABLE i(y INT) AS VALUES (1), (2), (3);

SELECT o.k FROM o WHERE o.b IN (SELECT i.y FROM i WHERE i.y = o.k) ORDER BY o.k;
Optimizer rule 'decorrelate_predicate_subquery' failed
caused by
Error during planning: Projections require unique expression names but the expression "CAST(i.y AS Int64)" at position 0 and "i.y" at position 1 have the same name. Consider aliasing ("AS") one of them.

The same error happens for 1 IN (...), o.k + 0 IN (...), NOT IN, and an explicit SELECT CAST(i.y AS BIGINT) in the subquery. With SELECT i.y + 0 AS y, the error is Schema contains qualified field name i.y and unqualified field name y which would be ambiguous.

It works when no cast is needed, as in o.k IN (SELECT i.y FROM i WHERE i.y = o.b), when the subquery is not correlated, or when the cast has another name (CAST(i.y AS BIGINT) AS y2).

Expected behavior

1, 2, which DuckDB 1.1.2 returns.

Additional context

PullUpCorrelatedExpr adds the correlated column i.y to the projection of the subquery, so that the join can read it. It skips the column only when the projection already holds the exact expression i.y:

fn collect_missing_exprs(
&self,
exprs: &[Expr],
correlated_subquery_cols: &BTreeSet<Column>,
) -> Result<Vec<Expr>> {
let mut missing_exprs = vec![];
for expr in exprs {
if !missing_exprs.contains(expr) {
missing_exprs.push(expr.clone())
}
}
for col in correlated_subquery_cols.iter() {
let col_expr = Expr::Column(col.clone());
if !missing_exprs.contains(&col_expr) {
missing_exprs.push(col_expr)
}
}

Here the projection holds CAST(i.y AS Int64), which type coercion added for the IN comparison and which has the same output name. LogicalPlanBuilder::project then rejects the new projection.

#4478 is the same name rule for a projection that a user writes, SELECT a, CAST(a AS INT). Here the user writes no duplicate name, and the pull up creates it.

Found on main at c3ef346.

Activity

  1. self-assigned this
    on Oct 4, 2026
  2. vinay121314 commented on Oct 5, 2026

    @vinay121314

    Hi @comphead, are you actively working on this? If not, I'd be happy to pick it up.

  3. alamb commented on Oct 28, 2026

    @alamb
    Contributor

    I think we should implement support support for this feature as part of the more general subquery decorrelation project described here

    in my opinion WE SHOULD NOT try and extend the existing subquery rewrites to support this type of query as that risks introducing many more wrong results, see #5492 (comment)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions