mirror of
https://github.com/GreptimeTeam/greptimedb.git
synced 2026-10-03 18:45:35 +00:00
* feat(function): add mergeable binary average states Signed-off-by: discord9 <discord9@163.com> * feat(function): expose avg_calc as an OSS scalar Signed-off-by: discord9 <discord9@163.com> * fix(function): borrow invalid AVG scalar argument type Signed-off-by: discord9 <discord9@163.com> * test(function): verify public AVG state SQL finalization Signed-off-by: discord9 <discord9@163.com> * fix(function): return canonical AVG1 state for empty window frames DataFusion's plain-aggregate window executor bypasses the accumulator and calls default_value() directly when a window frame contains no rows. create_udaf's SimpleAggregateUDF derives default_value from the return type, which yields SQL NULL for Binary output instead of the canonical AVG1 empty state, so empty frames and frames over only NULL inputs became observable differently (IS NULL, direct state saves, state comparisons). Register avg_state/avg_merge through a small AggregateUDFImpl (AvgUdaf) that keeps the existing accumulator and declares the canonical AVG1 empty state as default_value, restoring the documented contract. Add a unit test asserting default_value equals the empty accumulator's evaluate() and an sqlness case covering the empty-frame window. Signed-off-by: discord9 <discord9@outlook.com> --------- Signed-off-by: discord9 <discord9@163.com> Signed-off-by: discord9 <discord9@outlook.com> Co-authored-by: discord9 <discord9@outlook.com>
75 lines
2.0 KiB
SQL
75 lines
2.0 KiB
SQL
-- Migrated from DuckDB test: test/sql/aggregate/aggregates/test_avg.test
|
|
-- Test AVG aggregate function
|
|
|
|
-- scalar average
|
|
SELECT AVG(3);
|
|
|
|
-- FIXME(dennis): unsupported type
|
|
-- SELECT AVG(NULL);
|
|
|
|
SELECT AVG(3::SMALLINT), AVG(NULL::SMALLINT);
|
|
|
|
SELECT AVG(3::DOUBLE), AVG(NULL::DOUBLE);
|
|
|
|
-- test average with table
|
|
CREATE TABLE integers(i INTEGER, ts TIMESTAMP TIME INDEX);
|
|
|
|
INSERT INTO integers VALUES (1, 1000), (2, 2000), (3, 3000);
|
|
|
|
SELECT AVG(i), AVG(1), AVG(DISTINCT i), AVG(NULL) FROM integers;
|
|
|
|
SELECT AVG(i) FROM integers WHERE i > 100;
|
|
|
|
-- empty average
|
|
CREATE TABLE vals(i INTEGER, j DOUBLE, k BIGINT, ts TIMESTAMP TIME INDEX);
|
|
|
|
INSERT INTO vals VALUES (NULL, NULL, NULL, 1000);
|
|
|
|
SELECT AVG(i), AVG(j), AVG(k) FROM vals;
|
|
|
|
-- test with mixed values
|
|
DROP TABLE vals;
|
|
|
|
CREATE TABLE vals(i INTEGER, j DOUBLE, ts TIMESTAMP TIME INDEX);
|
|
|
|
INSERT INTO vals VALUES (1, 1.5, 1000), (2, 2.5, 2000), (3, 3.5, 3000), (NULL, NULL, 4000);
|
|
|
|
SELECT AVG(i), AVG(j) FROM vals;
|
|
|
|
-- FIXME(dennis): AVG(DISTINCT) not supported
|
|
-- https://github.com/apache/datafusion/issues/2408
|
|
-- SELECT AVG(DISTINCT i), AVG(DISTINCT j) FROM vals;
|
|
|
|
-- AVG1 state scalar calculation
|
|
SELECT avg_calc(avg_state(CAST(i AS DOUBLE))) FROM integers;
|
|
|
|
-- Merged states retain their unequal group weights.
|
|
WITH states AS (
|
|
SELECT avg_state(CAST(i AS DOUBLE)) AS state FROM integers WHERE i < 3
|
|
UNION ALL
|
|
SELECT avg_state(CAST(i AS DOUBLE)) AS state FROM integers WHERE i >= 3
|
|
)
|
|
SELECT avg_calc(avg_merge(state)) FROM states;
|
|
|
|
-- Empty, null-only, and null binary states calculate to NULL.
|
|
SELECT avg_calc(avg_state(CAST(i AS DOUBLE))) FROM integers WHERE i > 100;
|
|
SELECT avg_calc(avg_state(NULL::DOUBLE));
|
|
SELECT avg_calc(NULL::BYTEA);
|
|
|
|
-- Empty window frames return the canonical AVG1 state, not SQL NULL.
|
|
SELECT
|
|
i,
|
|
avg_state(CAST(i AS DOUBLE)) OVER (
|
|
ORDER BY i
|
|
ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING
|
|
) AS state
|
|
FROM integers;
|
|
|
|
-- Invalid AVG1 state propagates an error.
|
|
SELECT avg_calc(X'00');
|
|
|
|
-- cleanup
|
|
DROP TABLE integers;
|
|
|
|
DROP TABLE vals;
|