diff --git a/docs/generated/sql/aggregates.md b/docs/generated/sql/aggregates.md index 41ccfc184264..84b0671b82cb 100644 --- a/docs/generated/sql/aggregates.md +++ b/docs/generated/sql/aggregates.md @@ -37,6 +37,8 @@ avg(arg1: int) → decimal

Calculates the average of the selected values.

+avg(arg1: interval) → interval

Calculates the average of the selected values.

+
bool_and(arg1: bool) → bool

Calculates the boolean value of ANDing all selected values.

bool_or(arg1: bool) → bool

Calculates the boolean value of ORing all selected values.

diff --git a/pkg/sql/logictest/testdata/logic_test/aggregate b/pkg/sql/logictest/testdata/logic_test/aggregate index 05e46a06d60b..1c78f8994bf3 100644 --- a/pkg/sql/logictest/testdata/logic_test/aggregate +++ b/pkg/sql/logictest/testdata/logic_test/aggregate @@ -5,7 +5,8 @@ CREATE TABLE kv ( k INT PRIMARY KEY, v INT, w INT, - s STRING + s STRING, + i INTERVAL ) # Aggregate functions return NULL if there are no rows. @@ -36,6 +37,11 @@ SELECT jsonb_agg(1) FROM kv ---- NULL +query TTTT +SELECT min(i), avg(i), max(i), sum(i) FROM kv +---- +NULL NULL NULL NULL + query IIIIRRRRBBT SELECT min(v), max(v), count(v), sum_int(1), avg(v), sum(v), stddev(v), variance(v), bool_and(v = 1), bool_and(v = 1), xor_agg(s::bytes) FROM kv ---- @@ -147,12 +153,12 @@ SELECT (SELECT COALESCE(max(1), 0) FROM generate_series(1,0)) statement OK INSERT INTO kv VALUES -(1, 2, 3, 'a'), -(3, 4, 5, 'a'), -(5, NULL, 5, NULL), -(6, 2, 3, 'b'), -(7, 2, 2, 'b'), -(8, 4, 2, 'A') +(1, 2, 3, 'a', '1min'), +(3, 4, 5, 'a', '2sec'), +(5, NULL, 5, NULL, NULL), +(6, 2, 3, 'b', '1ms'), +(7, 2, 2, 'b', '4 days'), +(8, 4, 2, 'A', '3 years') # Aggregate functions triggers aggregation and computation for every row even when applied to a constant. # NB: The XOR result is 00 because \x01 is XOR'd an even number of times. @@ -588,6 +594,11 @@ SELECT avg(k), avg(v), sum(k), sum(v) FROM kv ---- 5 2.8 30 14 +query TTTT +SELECT min(i), avg(i), max(i), sum(i) FROM kv +---- +00:00:00.001 7 mons 6 days 19:12:12.4002 3 years 3 years 4 days 00:01:02.001 + query RRRR SELECT avg(k::decimal), avg(v::decimal), sum(k::decimal), sum(v::decimal) FROM kv ---- diff --git a/pkg/sql/logictest/testdata/logic_test/window b/pkg/sql/logictest/testdata/logic_test/window index c2e644a6a4e5..e349a116ba2d 100644 --- a/pkg/sql/logictest/testdata/logic_test/window +++ b/pkg/sql/logictest/testdata/logic_test/window @@ -10,6 +10,7 @@ CREATE TABLE kv ( d DECIMAL, s STRING, b BOOL, + i INTERVAL, FAMILY (k, v, w, f, b), FAMILY (d), FAMILY (s) @@ -17,12 +18,12 @@ CREATE TABLE kv ( statement OK INSERT INTO kv VALUES -(1, 2, 3, 1.0, 1, 'a', true), -(3, 4, 5, 2, 8, 'a', true), -(5, NULL, 5, 9.9, -321, NULL, false), -(6, 2, 3, 4.4, 4.4, 'b', true), -(7, 2, 2, 6, 7.9, 'b', true), -(8, 4, 2, 3, 3, 'A', false) +(1, 2, 3, 1.0, 1, 'a', true, '1min'), +(3, 4, 5, 2, 8, 'a', true, '2sec'), +(5, NULL, 5, 9.9, -321, NULL, false, NULL), +(6, 2, 3, 4.4, 4.4, 'b', true, '1ms'), +(7, 2, 2, 6, 7.9, 'b', true, '4 days'), +(8, 4, 2, 3, 3, 'A', false, '3 years') query error window functions are not allowed in GROUP BY SELECT * FROM kv GROUP BY v, count(w) OVER () @@ -210,38 +211,38 @@ SELECT avg(k) OVER (w ORDER BY w) FROM kv WINDOW w AS (PARTITION BY v) ORDER BY 7 8 -query IIIRRTBR colnames +query IIIRRTBTR colnames SELECT *, avg(k) OVER (w ORDER BY w) FROM kv WINDOW w AS (PARTITION BY v) ORDER BY 1 ---- -k v w f d s b avg -1 2 3 1 1 a true 4.6666666666666666667 -3 4 5 2 8 a true 5.5 -5 NULL 5 9.9 -321 NULL false 5 -6 2 3 4.4 4.4 b true 4.6666666666666666667 -7 2 2 6 7.9 b true 7 -8 4 2 3 3 A false 8 +k v w f d s b i avg +1 2 3 1 1 a true 00:01:00 4.6666666666666666667 +3 4 5 2 8 a true 00:00:02 5.5 +5 NULL 5 9.9 -321 NULL false NULL 5 +6 2 3 4.4 4.4 b true 00:00:00.001 4.6666666666666666667 +7 2 2 6 7.9 b true 4 days 7 +8 4 2 3 3 A false 3 years 8 -query IIIRRTBR colnames +query IIIRRTBTR colnames SELECT *, avg(k) OVER w FROM kv WINDOW w AS (PARTITION BY v ORDER BY w) ORDER BY avg(k) OVER w, k ---- -k v w f d s b avg -1 2 3 1 1 a true 4.6666666666666666667 -6 2 3 4.4 4.4 b true 4.6666666666666666667 -5 NULL 5 9.9 -321 NULL false 5 -3 4 5 2 8 a true 5.5 -7 2 2 6 7.9 b true 7 -8 4 2 3 3 A false 8 +k v w f d s b i avg +1 2 3 1 1 a true 00:01:00 4.6666666666666666667 +6 2 3 4.4 4.4 b true 00:00:00.001 4.6666666666666666667 +5 NULL 5 9.9 -321 NULL false NULL 5 +3 4 5 2 8 a true 00:00:02 5.5 +7 2 2 6 7.9 b true 4 days 7 +8 4 2 3 3 A false 3 years 8 -query IIIRRTB colnames +query IIIRRTBT colnames SELECT * FROM kv WINDOW w AS (PARTITION BY v ORDER BY w) ORDER BY avg(k) OVER w DESC, k ---- -k v w f d s b -8 4 2 3 3 A false -7 2 2 6 7.9 b true -3 4 5 2 8 a true -5 NULL 5 9.9 -321 NULL false -1 2 3 1 1 a true -6 2 3 4.4 4.4 b true +k v w f d s b i +8 4 2 3 3 A false 3 years +7 2 2 6 7.9 b true 4 days +3 4 5 2 8 a true 00:00:02 +5 NULL 5 9.9 -321 NULL false NULL +1 2 3 1 1 a true 00:01:00 +6 2 3 4.4 4.4 b true 00:00:00.001 query error window "w" is already defined SELECT avg(k) OVER w FROM kv WINDOW w AS (), w AS () @@ -1900,6 +1901,20 @@ DELETE FROM kv WHERE k = 12 query error FILTER specified but rank\(\) is not an aggregate function SELECT k, rank() FILTER (WHERE k=1) OVER () FROM kv +query TT +SELECT i, avg(i) OVER (ORDER BY i) FROM kv ORDER BY i +---- +NULL NULL +NULL NULL +NULL NULL +NULL NULL +00:00:00.001 00:00:00.001 +00:00:02 00:00:01.0005 +00:01:00 00:00:20.667 +4 days 1 day 00:00:15.50025 +3 years 7 mons 6 days 19:12:12.4002 + + # Issue #14606: correctly handle aggregation functions above the windowing level query I SELECT max(i) * (row_number() OVER (ORDER BY max(i))) FROM (SELECT 1 AS i, 2 AS j) GROUP BY j diff --git a/pkg/sql/sem/builtins/aggregate_builtins.go b/pkg/sql/sem/builtins/aggregate_builtins.go index 3532d2727bcb..ddf68372efa0 100644 --- a/pkg/sql/sem/builtins/aggregate_builtins.go +++ b/pkg/sql/sem/builtins/aggregate_builtins.go @@ -125,6 +125,8 @@ var aggregates = map[string]builtinDefinition{ "Calculates the average of the selected values."), makeAggOverload([]*types.T{types.Decimal}, types.Decimal, newDecimalAvgAggregate, "Calculates the average of the selected values."), + makeAggOverload([]*types.T{types.Interval}, types.Interval, newIntervalAvgAggregate, + "Calculates the average of the selected values."), ), "bool_and": makeBuiltin(aggProps(), @@ -557,6 +559,11 @@ func newDecimalAvgAggregate( ) tree.AggregateFunc { return &avgAggregate{agg: newDecimalSumAggregate(params, evalCtx, arguments)} } +func newIntervalAvgAggregate( + params []*types.T, evalCtx *tree.EvalContext, arguments tree.Datums, +) tree.AggregateFunc { + return &avgAggregate{agg: newIntervalSumAggregate(params, evalCtx, arguments)} +} // Add accumulates the passed datum into the average. func (a *avgAggregate) Add(ctx context.Context, datum tree.Datum, other ...tree.Datum) error { @@ -586,6 +593,8 @@ func (a *avgAggregate) Result() (tree.Datum, error) { count := apd.New(int64(a.count), 0) _, err := tree.DecimalCtx.Quo(&t.Decimal, &t.Decimal, count) return t, err + case *tree.DInterval: + return &tree.DInterval{Duration: t.Duration.Div(int64(a.count))}, nil default: return nil, errors.AssertionFailedf("unexpected SUM result type: %s", t) } diff --git a/pkg/sql/sem/builtins/aggregate_builtins_test.go b/pkg/sql/sem/builtins/aggregate_builtins_test.go index 3b54bb162ef5..2de43dbc7071 100644 --- a/pkg/sql/sem/builtins/aggregate_builtins_test.go +++ b/pkg/sql/sem/builtins/aggregate_builtins_test.go @@ -74,6 +74,10 @@ func TestAvgDecimalResultDeepCopy(t *testing.T) { testAggregateResultDeepCopy(t, newDecimalAvgAggregate, makeDecimalTestDatum(10)) } +func TestAvgIntervalResultDeepCopy(t *testing.T) { + testAggregateResultDeepCopy(t, newIntervalAvgAggregate, makeIntervalTestDatum(10)) +} + func TestBoolAndResultDeepCopy(t *testing.T) { testAggregateResultDeepCopy(t, newBoolAndAggregate, makeBoolTestDatum(10)) } @@ -329,6 +333,14 @@ func BenchmarkAvgAggregateDecimal(b *testing.B) { } } +func BenchmarkAvgAggregateInterval(b *testing.B) { + for _, count := range []int{1000} { + b.Run(fmt.Sprintf("count=%d", count), func(b *testing.B) { + runBenchmarkAggregate(b, newIntervalAvgAggregate, makeIntervalTestDatum(count)) + }) + } +} + func BenchmarkCountAggregate(b *testing.B) { for _, count := range []int{1000} { b.Run(fmt.Sprintf("count=%d", count), func(b *testing.B) { diff --git a/pkg/sql/sem/builtins/window_frame_builtins.go b/pkg/sql/sem/builtins/window_frame_builtins.go index 3b8a7097a35c..609ae8383ae0 100644 --- a/pkg/sql/sem/builtins/window_frame_builtins.go +++ b/pkg/sql/sem/builtins/window_frame_builtins.go @@ -408,6 +408,8 @@ func (w *avgWindowFunc) Compute( count := apd.New(int64(frameSize), 0) _, err := tree.DecimalCtx.Quo(&avg.Decimal, &dd.Decimal, count) return &avg, err + case *tree.DInterval: + return &tree.DInterval{Duration: t.Duration.Div(int64(frameSize))}, nil default: return nil, errors.AssertionFailedf("unexpected SUM result type: %s", t) }