Native support for stddev and variance related window functions - #3061
Native support for stddev and variance related window functions#3061fulghum wants to merge 1 commit into
Conversation
|
|
c75133c to
ca32904
Compare
|
SummaryCoverage spans aggregate variance and standard-deviation behavior across numeric and floating-point inputs, grouped and moving-window calculations, null and empty-frame handling, duplicate ordering values, concurrent startup, precision limits, and recovery after extreme values. The broad results are healthy for ordinary use and many boundary conditions, but an extreme floating-point input exposes an incorrect result. Not safe to merge yet — the PR introduces a medium-severity correctness defect in grouped and window aggregates for extreme real-valued inputs, where finite variance can become NaN. Ordinary and double-precision cases remain healthy, but this is a direct, user-visible data-result error in the changed functionality rather than a test-environment issue. Tests run by ItoTip Reply with @itoqa to send us feedback on this test run. |
| return b.Eval(ctx) | ||
| } | ||
|
|
||
| // floatVariance computes the population (sample=false) or sample (sample=true) variance of n float64 values |
There was a problem hiding this comment.
Extreme real values return NaN
What failed: Running variance queries with an extreme REAL value returned NaN for the grouped and window population variance. The expected result is a finite value, with zero for the identical extreme values used by this check.
Impact · Steps · Stub / mock · Analysis · Why this is likely a bug
- Severity: Medium
- Impact: Queries using extreme REAL values can return NaN instead of the correct finite variance. Users may receive wrong results for these edge-case reports, while ordinary values continue to work.
- Steps to Reproduce:
- Create a table with REAL and DOUBLE PRECISION columns.
- Insert finite ordinary values and an extreme value such as 1.0e100 into both columns.
- Run grouped VAR_POP and STDDEV_POP queries for each column.
- Run the equivalent window VAR_POP and STDDEV_POP queries.
- Observe that the extreme REAL results contain NaN while the extreme DOUBLE PRECISION results are finite zero.
- Stub / mock content: No stubs, mocks, or bypasses were applied for this test in the recorded run.
- Code Analysis: The PR adds server/functions/aggregate/variance_aggregates.go and registers its floating-point aggregate implementation through initVarianceAggs. In floatVariance at lines 233-245, the sum-of-squares expression
(nf*sumX2 - sumX*sumX) / divisorcan overflow to infinity and then produce NaN when finite extreme values are equal. The only guard at lines 242-244 checksvariance < 0; IEEE NaN is not less than zero, so it is returned unchanged. The grouped path calls this function from floatVarianceBuffer.Eval at lines 269-281, and the window path rebuilds the same buffer and calls Eval at lines 324-335, matching both observed failure forms. The smallest practical fix is to handle non-finite variance before the negative clamp, using a numerically stable calculation or a targeted finite-result policy for the overflow case, and add an extreme REAL regression test for both grouped and window forms. The PR diff marks variance_aggregates.go as a newly added 335-line file, so the defective path is directly introduced by this PR. - Why this is likely a bug: The local PostgreSQL client completed successfully with exit status 0, and the raw result showed NaN only for the extreme REAL fixture in both grouped and window forms; ordinary and close fixtures were finite, and the corresponding DOUBLE PRECISION results were zero. This is therefore a deterministic SQL result defect rather than a browser, transport, or setup failure. The source inspection identifies the direct cause: overflow-prone arithmetic creates NaN and the added implementation does not reject or normalize it. Since the PR adds the entire variance implementation, the PR directly introduces the affected code path and the smallest fix is local to its floating-point variance handling.
Relevant code
server/functions/aggregate/variance_aggregates.go:230-245
func floatVariance(n int64, sumX, sumX2 float64, sample bool) float64 {
...
variance := (nf*sumX2 - sumX*sumX) / divisor
if variance < 0 {
return 0
}
return variance
}server/functions/aggregate/variance_aggregates.go:269-281
variance := floatVariance(b.count, b.sumX, b.sumX2, b.sample)
if b.sqrtResult {
return math.Sqrt(variance), nil
}
return variance, nilserver/functions/aggregate/variance_aggregates.go:324-335
b := &floatVarianceBuffer[T]{expr: w.expr, sample: w.sample, sqrtResult: w.sqrtResult}
for i := interval.Start; i < interval.End; i++ {
if err := b.Update(ctx, buf[i]); err != nil {
return nil, err
}
}
return b.Eval(ctx)Evidence Package
Copy prompt for an agent
Ito QA identified the following failure during automated PR testing. Please investigate and propose a fix.
**Medium severity — Extreme real values return NaN**
**What failed:** Running variance queries with an extreme REAL value returned NaN for the grouped and window population variance. The expected result is a finite value, with zero for the identical extreme values used by this check.
- **Impact:** Queries using extreme REAL values can return NaN instead of the correct finite variance. Users may receive wrong results for these edge-case reports, while ordinary values continue to work.
- **Steps to reproduce:**
1. Create a table with REAL and DOUBLE PRECISION columns.
2. Insert finite ordinary values and an extreme value such as 1.0e100 into both columns.
3. Run grouped VAR_POP and STDDEV_POP queries for each column.
4. Run the equivalent window VAR_POP and STDDEV_POP queries.
5. Observe that the extreme REAL results contain NaN while the extreme DOUBLE PRECISION results are finite zero.
- **Stub / mock content:** No stubs, mocks, or bypasses were applied for this test in the recorded run.
- **Code analysis:** The PR adds server/functions/aggregate/variance_aggregates.go and registers its floating-point aggregate implementation through initVarianceAggs. In floatVariance at lines 233-245, the sum-of-squares expression `(nf*sumX2 - sumX*sumX) / divisor` can overflow to infinity and then produce NaN when finite extreme values are equal. The only guard at lines 242-244 checks `variance < 0`; IEEE NaN is not less than zero, so it is returned unchanged. The grouped path calls this function from floatVarianceBuffer.Eval at lines 269-281, and the window path rebuilds the same buffer and calls Eval at lines 324-335, matching both observed failure forms. The smallest practical fix is to handle non-finite variance before the negative clamp, using a numerically stable calculation or a targeted finite-result policy for the overflow case, and add an extreme REAL regression test for both grouped and window forms. The PR diff marks variance_aggregates.go as a newly added 335-line file, so the defective path is directly introduced by this PR.
- **Why this is likely a bug:** The local PostgreSQL client completed successfully with exit status 0, and the raw result showed NaN only for the extreme REAL fixture in both grouped and window forms; ordinary and close fixtures were finite, and the corresponding DOUBLE PRECISION results were zero. This is therefore a deterministic SQL result defect rather than a browser, transport, or setup failure. The source inspection identifies the direct cause: overflow-prone arithmetic creates NaN and the added implementation does not reject or normalize it. Since the PR adds the entire variance implementation, the PR directly introduces the affected code path and the smallest fix is local to its floating-point variance handling.
**Relevant code:**
`server/functions/aggregate/variance_aggregates.go:230-245`
~~~go
func floatVariance(n int64, sumX, sumX2 float64, sample bool) float64 {
...
variance := (nf*sumX2 - sumX*sumX) / divisor
if variance < 0 {
return 0
}
return variance
}
~~~
`server/functions/aggregate/variance_aggregates.go:269-281`
~~~go
variance := floatVariance(b.count, b.sumX, b.sumX2, b.sample)
if b.sqrtResult {
return math.Sqrt(variance), nil
}
return variance, nil
~~~
`server/functions/aggregate/variance_aggregates.go:324-335`
~~~go
b := &floatVarianceBuffer[T]{expr: w.expr, sample: w.sample, sqrtResult: w.sqrtResult}
for i := interval.Start; i < interval.End; i++ {
if err := b.Update(ctx, buf[i]); err != nil {
return nil, err
}
}
return b.Eval(ctx)
~~~
Fixed a bug where
STDDEV_POP,STDDEV_SAMP,VAR_POP,VAR_SAMP, and theirvarianceandstddevaliases would crash the server when used as window functions over integer columns. They now return correct, Postgres-compatible numeric/double precision results instead of panicking.Fixes: #3038