Skip to content

Commit

Permalink
Implement sum and average over TimeSpan
Browse files Browse the repository at this point in the history
  • Loading branch information
roji committed Jul 9, 2022
1 parent 82a758c commit be2d7cf
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,24 @@ public static T[] JsonAgg<T>(this DbFunctions _, IEnumerable<T> input)
public static T[] JsonbAgg<T>(this DbFunctions _, IEnumerable<T> input)
=> throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(JsonbAgg)));

/// <summary>
/// Computes the sum of the non-null input intervals. Corresponds to the PostgreSQL <c>sum</c> aggregate function.
/// </summary>
/// <param name="_">The <see cref="DbFunctions" /> instance.</param>
/// <param name="input">The input values to be summed.</param>
/// <seealso href="https://www.postgresql.org/docs/current/functions-aggregate.html">PostgreSQL documentation for aggregate functions.</seealso>
public static TimeSpan? Sum(this DbFunctions _, IEnumerable<TimeSpan> input)
=> throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Sum)));

/// <summary>
/// Computes the average (arithmetic mean) of the non-null input intervals. Corresponds to the PostgreSQL <c>avg</c> aggregate function.
/// </summary>
/// <param name="_">The <see cref="DbFunctions" /> instance.</param>
/// <param name="input">The input values to be computed into an average.</param>
/// <seealso href="https://www.postgresql.org/docs/current/functions-aggregate.html">PostgreSQL documentation for aggregate functions.</seealso>
public static TimeSpan? Average(this DbFunctions _, IEnumerable<TimeSpan> input)
=> throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Average)));

#region Range

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,26 @@ public NpgsqlMiscAggregateMethodTranslator(
returnType: sqlExpression.Type,
sqlExpression.TypeMapping);

case nameof(NpgsqlAggregateDbFunctionsExtensions.Sum):
return _sqlExpressionFactory.AggregateFunction(
"sum",
new[] { sqlExpression },
source,
nullable: true,
argumentsPropagateNullability: FalseArrays[1],
returnType: sqlExpression.Type,
sqlExpression.TypeMapping);

case nameof(NpgsqlAggregateDbFunctionsExtensions.Average):
return _sqlExpressionFactory.AggregateFunction(
"avg",
new[] { sqlExpression },
source,
nullable: true,
argumentsPropagateNullability: FalseArrays[1],
returnType: sqlExpression.Type,
sqlExpression.TypeMapping);

case nameof(NpgsqlAggregateDbFunctionsExtensions.JsonbObjectAgg):
case nameof(NpgsqlAggregateDbFunctionsExtensions.JsonObjectAgg):
var isJsonb = method.Name == nameof(NpgsqlAggregateDbFunctionsExtensions.JsonbObjectAgg);
Expand Down
39 changes: 39 additions & 0 deletions test/EFCore.PG.FunctionalTests/Query/GearsOfWarQueryNpgsqlTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,45 @@ await AssertQuery(
WHERE (date_part('epoch', m.""Duration"") / 0.001) < 3700000.0");
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual async Task GroupBy_Property_Select_Sum_over_TimeSpan(bool async)
{
await AssertQueryScalar(
async,
ss => ss.Set<Mission>()
.GroupBy(o => o.Id)
.Select(g => EF.Functions.Sum(g.Select(o => o.Duration))),
ss => ss.Set<Mission>()
.GroupBy(o => o.Id)
.Select(g => (TimeSpan?)new TimeSpan(g.Sum(o => o.Duration.Ticks))));

// TODO: Should not have COALESCE here
AssertSql(
@"SELECT COALESCE(sum(m.""Duration""), INTERVAL '00:00:00')
FROM ""Missions"" AS m
GROUP BY m.""Id""");
}

[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual async Task GroupBy_Property_Select_Average_over_TimeSpan(bool async)
{
await AssertQueryScalar(
async,
ss => ss.Set<Mission>()
.GroupBy(o => o.Id)
.Select(g => EF.Functions.Average(g.Select(o => o.Duration))),
ss => ss.Set<Mission>()
.GroupBy(o => o.Id)
.Select(g => (TimeSpan?)new TimeSpan((long)g.Average(o => o.Duration.Ticks))));

AssertSql(
@"SELECT avg(m.""Duration"")
FROM ""Missions"" AS m
GROUP BY m.""Id""");
}

#endregion TimeSpan

#region DateOnly
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3163,6 +3163,8 @@ into g
GROUP BY o.""CustomerID""");
}

// See aggregate tests over TimeSpan in GearsOfWarQueryNpsgqlTest

private void AssertSql(params string[] expected)
=> Fixture.TestSqlLoggerFactory.AssertBaseline(expected);

Expand Down

0 comments on commit be2d7cf

Please sign in to comment.