Skip to content

Join planning inserts IS NOT NULL filters that provably pass 100% of rows when input statistics report zero nulls #24942

Open

Description

@lxc512157407

Summary

Join planning inserts null-rejecting IS NOT NULL filters on nullable join keys (filter_null_join_keys), which are then pushed down to scans. When the underlying data contains no NULLs — the overwhelmingly common case for join keys, e.g. TPC-H primary/foreign keys — these filters pass 100% of rows, yet every input batch still pays a full predicate evaluation plus a full batch copy.

On TPC-H SF=10 we measured selectivity = 100% (6.00M/6.00M) for such filters on join keys.

Why this is wasteful

FilterExec::statistics_helper already derives the forward direction: a surviving IS NOT NULL conjunct implies null_count = Exact(0) for the output. The inverse is missing: when input statistics already prove null_count = Exact(0) for a column, a bare Column IS NOT NULL conjunct is vacuously true and could be dropped from the predicate at plan-construction time (FilterExecBuilder::build). If all conjuncts are dropped, the predicate degenerates to lit(true) and the filter becomes a no-op pass-through (or can be elided entirely).

Non-provable cases (statistics Absent or null_count Inexact) must of course keep the predicate unchanged — this is strictly a statistics-driven refinement.

Impact

  • Every join query over sources that report exact zero null-counts (Parquet with statistics, custom TableProviders with exact stats) executes no-op filter evaluations per batch.
  • For plans with many joins (TPC-H), the redundant filters stack up on the scan side.

Related

Activity

  1. 2010YOUY01 commented on Sep 4, 2026

    @2010YOUY01
    Member

    Do you have a minimal SQL reproducer for this case? I find the issue and PR are mostly implementation details, so it's a bit hard to figure out the full story.

  2. lxc512157407 commented on Sep 9, 2026

    @lxc512157407
    Author

    Hi @2010YOUU01 - here's a minimal SQL reproducer that triggers this:

    -- Setup: two tables with nullable join keys
    CREATE TABLE a (id INT, val INT);
    INSERT INTO a VALUES (1, 10), (2, 20), (3, 30);
    CREATE TABLE b (id INT, val INT);
    INSERT INTO b VALUES (1, 100), (2, 200);
    
    -- Join planning inserts IS NOT NULL on nullable keys (filter_null_join_keys)
    -- EXPLAIN shows the filter is pushed down to the scan:
    SELECT a.val
    FROM a
    JOIN b ON a.id = b.id;
    
    -- Even when data provably contains no NULLs (primary keys),
    -- the filter still runs at 100% selectivity:
    SELECT a.val
    FROM a
    WHERE a.id IS NOT NULL;
    
    -- With stats that report null_count=0 on id column:
    -- the IS NOT NULL conjunct is provably true and could be dropped.

    The PR #24821 adds statistics-driven simplification that drops these
    provably-true IS NOT NULL conjuncts at FilterExec build time. The issue
    tracks adding this optimization to the join planning phase as well.

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions