Aggregate functions
On this page
Per-group accumulation: update, combine, finalize.
interface AggregateBindParams
Section titled âinterface AggregateBindParamsâexport interface AggregateBindParams<TArgs = Record<string, any>>Fields
argsTArgsargumentsArgumentsinputSchemaVgiSchema | nullsettingsRecord<string, any>secretsRecord<string, Record<string, any>>
interface AggregateFinalizeParams
Section titled âinterface AggregateFinalizeParamsâexport interface AggregateFinalizeParams<TArgs = Record<string, any>, TState = any>Fields
groupIdsbigint[]statesMap<bigint, TState | null>outputSchemaVgiSchemaargsTArgs
interface AggregateFunctionConfig
Section titled âinterface AggregateFunctionConfigâexport interface AggregateFunctionConfig<TArgs = Record<string, any>, TState = any>Fields
namestringoutputTypeVgiDataTypeArrow type of the aggregateâs output (one value per group). DuckDB uses this to build the result column type at bind time. May be overridden at bind time by
onBind(returning a different Arrow type) for aggregates whose return type depends on the input column type (e.g. vgi_generic_sum: BIGINT input â BIGINT output, DOUBLE â DOUBLE).initialState(params: AggregateBindParams<TArgs>) => TStateupdate(params: AggregateUpdateParams<TArgs, TState>) => voidFold a batch of input rows into the provided per-group state map. Implementations mutate
statesin place.groupIds[i]identifies which group row i belongs to;columns[k][i]is the value for the kth declared column argument at row i.combine(source: TState, target: TState, params: AggregateBindParams<TArgs>) => TStatefinalize(params: AggregateFinalizeParams<TArgs, TState>) => VgiBatchMust return a single-column RecordBatch with
groupIds.lengthrows.descriptionstringoptionalargsRecord<string, VgiDataType>optionalMap of positional argument name â Arrow type. Passed through to DuckDB function registration. Use
null(untyped) for varargs placeholders.varargsstring[]optionalNames of args that accept a variable number of column arguments. DuckDB treats these as varargs of the declared Arrow type; at call time the function may receive 1..N columns of that type. update() sees all of them as consecutive entries in
columns.constParamsstring[]optionalNames of args whose value is constant (known at bind time) and must be folded away by DuckDB before reaching update(). The value arrives in
bindParams.args[name]; it is NOT passed as an input column in the update batch. Use for aggregates parameterized by a literal (e.g. percentile=0.5 in vgi_percentile).onBind(params: AggregateBindParams<TArgs>) => VgiDataType | Promise<VgiDataType>optionalOptional bind-time hook for dynamic output types. Receives the bind parameters (including input_schema) and returns the Arrow type to advertise for this invocation. Defaults to returning
config.outputType.argDefaultsRecord<string, any>optionalOptional per-arg default values (positional only here).
argConstraintsRecord<string, ArgumentConstraints>optionalPer-argument discovery constraints (choices / ge / le / gt / lt / pattern), keyed by argument name. Surfaced via
vgi_function_arguments()and enforced at aggregate_bind for const params: a value violating a declared constraint fails the bind with an ArgumentValidationError.examplesFunctionExample[]optionalcategoriesstring[]optionaltagsRecord<string, string>optionalArbitrary function-level tags surfaced into duckdb_functions().tags.
nullHandlingâDEFAULTâ | âSPECIALâoptionalrequiredSecretsstring[]optionalDuckDB secret types this aggregate needs. Advertised on the catalogâs
required_secretsso the C++ extension pre-resolves matching secrets and delivers their VALUES on the aggregate_bind request (keyed by secret name). The secret is read at bind time only â update/combine/finalize receive an empty ResolvedSecrets. Mirrors vgi-pythonâsSecret()annotation on an aggregateon_bind.
interface AggregateUpdateParams
Section titled âinterface AggregateUpdateParamsâexport interface AggregateUpdateParams<TArgs = Record<string, any>, TState = any>Fields
statesMap<bigint, TState>groupIdsbigint[]columnsany[]argsTArgsensureState(gid: bigint) => TStateGet-or-initialize accessor for per-group state. Call this inside the loop only when the row actually contributes to the aggregate â skipping it (e.g. on NULL input under NullHandling.DEFAULT) leaves
stateswithout an entry for the group, so finalize() can return SQL NULL for that group (matches vgi-pythonâs âstate absent = NULLâ semantics for SUM/AVG/MIN/MAX over zero non-null rows).
function defineAggregate
Section titled âfunction defineAggregateâexport function defineAggregate<TArgs = Record<string, any>, TState = any>(config: AggregateFunctionConfig<TArgs, TState>,): VgiFunction & { aggregateConfig: AggregateFunctionConfig<TArgs, TState> }Description
Declare an aggregate function. The returned VgiFunction has kind: "aggregate" plus a config handle the dispatch layer uses to drive the
aggregate_* RPCs; the scalar/table-function code paths ignore it.
const GROUP_COLUMN_NAME
Section titled âconst GROUP_COLUMN_NAMEâconst GROUP_COLUMN_NAME = â__vgi_group_idâ