You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Severity: HIGH — silent semantic collapse, directly on the sketch path
AggIntent::Quantile, AggIntent::Cardinality and AggIntent::TopK carry no input column, unlike every other value reducer. Two aggregates over different columns therefore lower to byte-identical L3 intents.
This is load-bearing: Cardinality is what binds to an HLL sketch and Quantile to a t-digest/KLL. Neither can be built without knowing which column feeds it.
Contrast with the reducers that do carry a column:
sum(v) -> Sum { col: Some(1) }
sum(w) -> Sum { col: Some(2) }
Consequences
CSE miscompilation.crates/plan/src/cse.rs dedupes on AggIntent equality. median(v) and median(w) are equal, so a query computing both collapses to one.
Sketch binding is underdetermined.crates/plan/src/bind.rs maps Cardinality → HLL and Quantile → quantile sketch, but cannot know the input column.
crates/l2/src/lower.rs:660-683. The col binding is already in scope and threaded into Sum / Avg / Min / Max / StdDev / Variance, but simply not passed to the three intents below it:
AggFunc::Sum => AggIntent::Sum{ col },// …AggFunc::Quantile(q) => AggIntent::Quantile{q:*q,accuracy: acc.clone()},// col droppedAggFunc::CountDistinct => AggIntent::Cardinality{accuracy: acc.clone()},// col droppedAggFunc::HeavyHitters{ k } => AggIntent::TopK{k:*k asusize,accuracy: acc.clone()},// col dropped
It reads as an omission rather than a deliberate deferral to L4 — AggFunc::Quantile/CountDistinct at L2 (relational.rs) do carry the ColumnRef, so the information exists and is discarded at the boundary.
Suggested fix
Add col: Option<ColumnId> to Quantile and Cardinality (mirroring Sum's #[serde(default)]), and thread col through at lower.rs. TopK needs a decision: its ranked column may legitimately be the implicit aggregate output rather than a base column (see #13, #25) — worth splitting out if so.
Consumers to update: crates/plan/src/boundary.rs, crates/plan/src/bind.rs, crates/plan/src/cse.rs, crates/l2/src/canonicalize.rs.
Notes
Affects PromQL identically (quantile(0.5, …), count_values), not just SQL.
Severity: HIGH — silent semantic collapse, directly on the sketch path
AggIntent::Quantile,AggIntent::CardinalityandAggIntent::TopKcarry no input column, unlike every other value reducer. Two aggregates over different columns therefore lower to byte-identical L3 intents.This is load-bearing:
Cardinalityis what binds to an HLL sketch andQuantileto a t-digest/KLL. Neither can be built without knowing which column feeds it.Reproduction (verified on
main@da2ac8e)lowers to:
Contrast with the reducers that do carry a column:
Consequences
crates/plan/src/cse.rsdedupes onAggIntentequality.median(v)andmedian(w)are equal, so a query computing both collapses to one.crates/plan/src/bind.rsmapsCardinality→ HLL andQuantile→ quantile sketch, but cannot know the input column.approx_percentile_cont(v * 8, 0.95)silently succeeds and drops the expression entirely, because the column slot that would have rejected it does not exist.SUM(v * 8)correctly errors viareducer_col(unsupported aggregate: sum over a non-column expression) — see SQL: expression GROUP BY (date_trunc time bucketing), aggregates over expressions, and GROUPING SETS/ROLLUP/CUBE are rejected #110.Root cause
crates/l2/src/lower.rs:660-683. Thecolbinding is already in scope and threaded intoSum/Avg/Min/Max/StdDev/Variance, but simply not passed to the three intents below it:It reads as an omission rather than a deliberate deferral to L4 —
AggFunc::Quantile/CountDistinctat L2 (relational.rs) do carry theColumnRef, so the information exists and is discarded at the boundary.Suggested fix
Add
col: Option<ColumnId>toQuantileandCardinality(mirroringSum's#[serde(default)]), and threadcolthrough atlower.rs.TopKneeds a decision: its ranked column may legitimately be the implicit aggregate output rather than a base column (see #13, #25) — worth splitting out if so.Consumers to update:
crates/plan/src/boundary.rs,crates/plan/src/bind.rs,crates/plan/src/cse.rs,crates/l2/src/canonicalize.rs.Notes
quantile(0.5, …),count_values), not just SQL.medianfor SQL: subquery-valued predicates (IN/EXISTS/scalar), DISTINCT ON, and median/array_agg unsupported #111: mappingmedian→Quantile(0.5)is a one-liner, but it would inherit this defect, so the foundation should land first.#[serde(default)]on the new field keeps existing serialized L3 readable.