Skip to content

DataFusion drops a group key under a count over an aggregate on a join with a DISTINCT side #55

Description

@phas02

What it buys. A count over a grouped frame that is right. The engine at the pin returns the wrong row count for SELECT count(*) FROM (SELECT … FROM f GROUP BY a, b) when f is an aggregate over a join whose one side is DISTINCT: the outer GROUP BY loses b. The cube's grain check had that shape and reported a composite grain broken over a frame that held it; any read of that shape through the door returns the same wrong count.

What stands.

  • The mechanism, in the crates the lock resolves (datafusion-common 54.1.0, datafusion-optimizer 54.1.0): an inner join keeps a DISTINCT side's uniqueness as a dependency in Multi mode (functional_dependencies.rs:342); an aggregate over that join emits a dependency from the matched determinant to the whole output row (functional_dependencies.rs:427), false where the determinant covers only part of the group keys; optimize_projections keeps the smallest group-key set those dependencies allow whenever the parent reads no key (optimize_projections/mod.rs:169, through get_required_group_by_exprs_indices, functional_dependencies.rs:549).

  • Reproduced on datafusion 55.1.0 with two in-memory tables; the three functions are unchanged on upstream main. A SELECT DISTINCT a, b FROM f under the count is wrong on 55.1.0 as well. One aggregate over the frame, count(*) beside count(DISTINCT struct(a, b)), is right on both.

  • The repro, two tables, wrong with the DISTINCT and right without it:

    WITH dates AS (SELECT DISTINCT date AS as_of FROM bank_transactions WHERE date IN (10, 20)),
         snap  AS (SELECT d.as_of, i.vendor_id FROM dates d JOIN invoices i ON i.date <= d.as_of),
         op    AS (SELECT as_of AS date, vendor_id, count(*) AS n FROM snap GROUP BY as_of, vendor_id)
    SELECT count(*) FROM (SELECT count(*) AS c FROM op GROUP BY date, vendor_id);
  • The grain check is one aggregate over the frame (crates/session/src/cube.rs, the declared-grain block in build), and the cube suite holds a composite grain over this join shape (crates/scripts/tests/suite/cube.rs, the_declared_grain_gates_the_frame_one_row_per_key, the joined metric).

Done when the DataFusion the lock resolves plans the repro right, and the constraint in the grain check's comment is dropped.

Activity

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

    upstreamParked on a dependency's own roadmap

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions