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.
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)whenfis an aggregate over a join whose one side isDISTINCT: the outerGROUP BYlosesb. 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-common54.1.0,datafusion-optimizer54.1.0): an inner join keeps aDISTINCTside's uniqueness as a dependency inMultimode (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_projectionskeeps the smallest group-key set those dependencies allow whenever the parent reads no key (optimize_projections/mod.rs:169, throughget_required_group_by_exprs_indices,functional_dependencies.rs:549).Reproduced on
datafusion55.1.0 with two in-memory tables; the three functions are unchanged on upstreammain. ASELECT DISTINCT a, b FROM funder the count is wrong on 55.1.0 as well. One aggregate over the frame,count(*)besidecount(DISTINCT struct(a, b)), is right on both.The repro, two tables, wrong with the
DISTINCTand right without it:The grain check is one aggregate over the frame (
crates/session/src/cube.rs, the declared-grain block inbuild), 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, thejoinedmetric).Done when the DataFusion the lock resolves plans the repro right, and the constraint in the grain check's comment is dropped.