Skip to content

Support LATERAL subqueries with HAVING clause #21198

Open

Description

@neilconway

Describe the bug

This currently doesn't work. The issue:

  • For normal LATERAL subqueries without HAVING (but with COUNT(*) or a similar agg), we rewrite the LATERAL subquery into a grouped agg subquery and LEFT JOIN against it. We add a CASE ... WHEN __always_true construct to convert unmatched outer rows from NULL to the agg's default value (0 for count).
  • When the LATERAL subquery has a HAVING clause, there's another possibility: an outer row might be unmatched because the entire group was excluded because it failed the HAVING clause. So the correct behavior is to elide the entire outer row, not substitute a 0 for the NULL count value.

We can fix this by

  • Pulling the HAVING clause out of the subquery and evaluating it over the subquery's results instead. This requires rewriting it in terms of the post-join schema.
  • Arrange to omit rows from the final result set that don't satisfy the rewritten HAVING clause

This is similar to what we already do in scalar_subquery_to_join, except slightly different because we want to omit the row entirely, not return NULL.

To Reproduce

No response

Expected behavior

No response

Additional context

No response

Activity

  1. added theissue type on Mar 27, 2026
  2. neilconway commented on Mar 27, 2026

    @neilconway
    ContributorAuthor

    take

  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)

  4. raghavyadav01 commented on Oct 29, 2026

    @raghavyadav01
    Contributor

    @neilconway are you still working on this? If not, could I pick it up?

    On main this doesn't fail, it returns wrong rows: an inner LATERAL with HAVING keeps the outer rows that fail the HAVING, with NULL.

    CREATE TABLE a(k INT) AS VALUES (1), (2), (3);
    CREATE TABLE b(k INT, v INT) AS VALUES (1, 10), (1, 20), (2, 30);
    SELECT a.k, s.c FROM a, LATERAL (SELECT count(*) AS c FROM b WHERE b.k = a.k HAVING count(*) > 1) s;
    -- main: (1, 2), (2, NULL), (3, NULL)
    -- DuckDB: (1, 2)

    Following #5492 (comment), the fix would be to make this shape error rather than support it.

  5. removed their assignment
    on Oct 29, 2026
  6. neilconway commented on Oct 29, 2026

    @neilconway
    ContributorAuthor

    @raghavyadav01 sure, feel free to take it on.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions