class DuckDB::AggregateFunction

DuckDB::AggregateFunction lets you register a custom aggregate function written in Ruby and call it from SQL.

An aggregate function folds many rows into a single value. You define its behaviour with four callbacks:

Only set_init is required. The other three have sensible defaults:

@note The default set_combine keeps the source state and discards the

target, which is only correct for single-threaded (single-partition)
execution. If DuckDB runs the aggregate in parallel it will produce
wrong results. Always supply an explicit +set_combine+ when the
aggregate must be parallel-safe.

Basic example: custom SUM

af = DuckDB::AggregateFunction.new
af.name        = 'my_sum'
af.return_type = DuckDB::LogicalType::BIGINT
af.add_parameter(DuckDB::LogicalType::BIGINT)

af.set_init    { 0 }
af.set_update  { |state, value| state + value }
af.set_combine { |s1, s2| s1 + s2 }

con.register_aggregate_function(af)
con.query('SELECT my_sum(i) FROM range(100) t(i)').first.first  # => 4950

Example: weighted average with Hash state

af = DuckDB::AggregateFunction.new
af.name        = 'weighted_avg'
af.return_type = DuckDB::LogicalType::DOUBLE
af.add_parameter(DuckDB::LogicalType::DOUBLE)  # value
af.add_parameter(DuckDB::LogicalType::DOUBLE)  # weight

af.set_init    { { sum: 0.0, weight: 0.0 } }
af.set_update  { |state, value, weight| { sum: state[:sum] + value * weight, weight: state[:weight] + weight } }
af.set_combine { |s1, s2| { sum: s1[:sum] + s2[:sum], weight: s1[:weight] + s2[:weight] } }
af.set_finalize { |state| state[:weight].zero? ? nil : state[:sum] / state[:weight] }

con.register_aggregate_function(af)