Add a PostgreSQL method translator

This tutorial adds a small service-specific method and translates it into a parameterized PostgreSQL predicate. It builds the SQL artifact only; the service remains responsible for connecting to PostgreSQL and executing it.

Declare and enable the method

Each extension signature has its own stable ID. Add the declaration to engine configuration and select its capability explicitly:

declaration, err := hff.NewMethodDeclaration(hff.MethodConfig{
    ID: "service.withsuffix.v1",
    Name: "withsuffix",
    Receiver: schema.StringType,
    Result: schema.StringType,
    BaseCost: 1,
})
if err != nil {
    return err
}

features := capabilities.Standard().Allow(capabilities.Method(declaration.ID()))
e, err := engine.New(
    engine.WithSchema(schema.Struct(schema.String("name"))),
    engine.WithCapabilities(features),
    engine.WithMethods(declaration),
)
if err != nil {
    return err
}
program, err := e.Compile(ctx, `name:withsuffix="Alice!"`)
if err != nil {
    return err
}

Compilation validates the HFF declaration and capability. Backend support is a separate check performed by sql.Build.

Translate to a typed SQL expression

Register a PostgreSQL translator for the same declaration:

translator := hffsql.Implement(declaration, hffsql.PostgreSQL,
    func(call *hffsql.Call, receiver hffsql.Expression, args []hffsql.Expression) (hffsql.Expression, error) {
        suffix, err := call.Parameter(hff.String("!"))
        if err != nil {
            return hffsql.Expression{}, err
        }
        return call.Expression(schema.StringType, "{} || {}", receiver, suffix)
    },
)
query, err := hffsql.Build(ctx, program,
    hffsql.WithDialect(hffsql.PostgreSQL),
    hffsql.WithBindings(hffsql.Bind(hff.Path{"name"}, hffsql.Column("records", "name"))),
    hffsql.WithMethods(translator),
)
if err != nil {
    return err
}

The template is trusted code owned by the service. Each {} is replaced with an expression scoped to the current translator call. Use call.Parameter(hff.Value) to bind values; do not interpolate query text or record data into SQL templates. HFF checks expression ownership and declared scalar types, but the service remains responsible for the template's semantic correctness. If the translator cannot preserve the method's behavior, return an error so Build reports a source-located backend failure.

HFF retains signature selection, capability enforcement, null/no-value handling, and argument evaluation. The translator runs during pure query construction, not once per database row. Translators must be concurrency-safe for concurrent builds and must not retain the call after returning.

Check backend guarantees

Ask what the dialect can enforce before requesting execution limits:

guarantees, err := hffsql.ExecutionGuarantees(hffsql.PostgreSQL)
if err != nil {
    return err
}
fmt.Println(guarantees.MaxSteps, guarantees.MaxGeneratedValues)

PostgreSQL currently reports false for both limits. A positive request through WithExecutionLimits therefore fails Build instead of being ignored. These HFF budgets are not database timeouts; configure database cancellation and timeouts through the database driver and context.

Run the complete example

From the repository root, run:

go run ./examples/sql-extensions

The example prints whether a predicate was built, its parameter count, and the PostgreSQL budget guarantees. It never opens a database connection. For the complete current scalar support matrix and parameter-mapping rules, see the PostgreSQL backend guide.

Expected output:

Predicate built: true
Parameter count: 2
PostgreSQL MaxSteps guarantee: false
Explore HFF
Search documentation

Search the language specification and guides.