Skip to content
Query.Farm
Talk with Us

Scalar functions

On this page

Vectorized one-row-to-one-value functions and reflection-driven ScalarFn dispatch.

source
public interface IScalarFunction

Description

The raw contract a scalar function implements. A hardcoded implementation (see examples/01-minimal-scalar-worker 's UpperCaseFunction , or the ANY-typed dynamic- output fixtures under fixtures/QueryFarm.Vgi.ExampleWorker/Scalar ) is a fully supported way to implement this interface directly; the attribute-driven convenience base class ScalarFn ( [Param] / [ConstParam] / [Setting] / [OutputLength] reflection dispatch, ported from vgi-java's ScalarFn.ComputePlan /vgi-python's ScalarFunction ) builds on top of this same contract without changing it.

source
public sealed class ScalarBindParams

Description

Parameters an IScalarFunction sees at bind time.

Public members

public Schema? InputSchema { get; init; }

The concrete per-call argument schema DuckDB resolved (decoded from Protocol.BindRequest.InputSchema) — populated for a normal call; null for a zero-argument function or a pre-bind catalog probe. Needed by an ANY-typed function to derive its actual (promoted) output type — see IScalarFunction.ResolveOutputSchema.

public byte[] Arguments { get; init; }

Opaque, not-yet-decoded serialized argument-schema/const-value bytes from Protocol.BindRequest.Arguments — an embedded IPC batch with a single column named args whose type is struct(positional_0: T0, positional_1: T1, …) , one row, indexed 0.. over CONST parameters only (in Compute declaration order) — ScalarFn decodes this itself for [ConstParam] parameters; a hand-rolled IScalarFunction needing const values directly (e.g. a nested struct/binary const) can decode it the same way.

public byte[]? Secrets { get; init; }

Opaque, not-yet-decoded serialized Protocol.BindRequest.Secrets bytes — an embedded IPC batch, one row, one column per resolved secret. Decode with Internal.SecretArgCodec.Decode. null when this function declared no IScalarFunction.RequiredSecrets (or none matched) — a scalar function only sees a STATICALLY pre-resolved secret here (see Attributes.SecretAttribute), never a dynamic scope-based one.

public byte[]? Settings { get; init; }

Opaque, not-yet-decoded serialized Protocol.BindRequest.Settings bytes — an embedded IPC batch, one row, columns named literally by DuckDB setting key. null when this function declared no IScalarFunction.RequiredSettings.

public required string FunctionName { get; init; }
source
public abstract class ScalarFn : IScalarFunction

Description

Attribute-driven convenience base class for IScalarFunction — reflects a subclass's own Compute method exactly once (see ComputePlan) and dispatches Process against it, so a subclass just writes: private void Compute([Param] StringArray value, StringArray.Builder result) { for (var i = 0; i < value.Length; i++) { if (value.IsNull(i)) { result.AppendNull(); continue; } result.Append(value.GetString(i).ToUpperInvariant()); } } See ComputePlan's doc comment for exactly which parameter shapes are supported (and which ANY-typed/varargs shapes are deliberately NOT — those implement IScalarFunction directly instead).

Public members

public RecordBatch Process(ScalarProcessParams processParams) ;
public abstract string Name { get; }
public virtual FunctionNullHandling? NullHandling;
public virtual FunctionStability? Stability;
public virtual IReadOnlyDictionary<string, string>? CacheControlMetadata;
public virtual IReadOnlyList<RequiredSecret> RequiredSecrets;
public virtual IReadOnlyList<string> RequiredSettings;
public virtual Schema ArgumentsSchema;
public virtual Schema OutputSchema;
public virtual Schema ResolveOutputSchema(Schema? inputSchema) ;
public virtual string Description;
public virtual string SchemaName;
public virtual void Bind(ScalarBindParams bindParams) ;
source
public sealed class ScalarProcessParams

Description

Parameters an IScalarFunction sees per exchange turn.

Public members

public byte[] Arguments { get; init; }

Same shape/meaning as ScalarBindParams.Arguments — carried again on every batch (rather than cached as instance state on the shared, potentially concurrently-invoked IScalarFunction singleton) so const-argument values stay correct across concurrent calls of the same function with different const arguments.

public byte[]? Secrets { get; init; }

Same shape/meaning as ScalarBindParams.Secrets, threaded through per-batch for the same reason as Arguments.

public byte[]? Settings { get; init; }

Same shape/meaning as ScalarBindParams.Settings, threaded through per-batch for the same reason as Arguments.

public required RecordBatch Input { get; init; }

One column per positional argument, in declaration order. Column NAMES on the wire are DuckDB's own synthetic "col_0", "col_1", … — not IScalarFunction.ArgumentsSchema's (cosmetic) names — so implementations should index by position, not by name.

public required Schema OutputSchema { get; init; }

The RESOLVED per-call output schema (from IScalarFunction.ResolveOutputSchema) — the returned RecordBatch must use exactly this schema.