061. Omit unused item payloads from PostgreSQL deferred folds
- Status: Implemented
- Date: 2026-10-16
Summary
For a bounded class of PostgreSQL JSON/JSONB folds, the lowering omits the deferred item payload when the fold callback never reads its item binding. It preserves the source rows, fold order, seed, callback result, null/presence, and first-failure behavior. This is a SQL-shape optimization; it does not turn the fold into a count or remove the accumulator.
Problem and mechanism
Some callback pipelines produce a typed packet for each input row. A fold may then use only the accumulator or a separate captured value, leaving the packet payload unused. Carrying and decoding that payload through each recursive step adds SQL without affecting the callback result.
The candidate applies only after the existing native and safe identity-fold paths have had priority. It proves that the fold item's lexical binding is unused throughout the callback body, including nested callbacks. A capture of another binding is allowed; a nested callback that captures the fold item makes it used. If any custom extension signature occurs in the fold, this candidate path is not used.
The original source, seed, and callback are fully validated before the optimized packet path is selected. The added validation probe reuses the prepared source plan; source rows, filters, and order remain unchanged. The fold still processes every source row and preserves ordinary null, presence, no-value, seed behavior, and recursive first failure. Only the unused item payload and its decoding are omitted from the packet; accumulator state is unchanged. No native projection or size/count rewrite is attempted.
Applicability
| Fold callback | Status | Reason |
|---|---|---|
(a,v)=>a+1 |
Candidate | The item binding is unused throughout the body. |
(a,v)=>[1,2]:foldl(a,(b,v)=>b+v) |
Candidate | The nested v shadows the outer item binding; the outer item is not captured. |
(a,v)=>a+offset |
Candidate | A capture of a different binding does not observe the item. |
(a,v)=>a+v:size |
Not applied | The callback reads the item. |
(a,v)=>[1]:foldl(a,(b,x)=>b+v:size) |
Not applied | The nested callback captures the item binding. |
| A custom extension signature in the source, seed, or callback body | Not applied | Extension validation is not duplicated in this candidate path. |
| Existing native or safe identity-fold case | Earlier path | This packet-fold optimization does not replace the established lowering. |
Reproduction
The SQL-shape baseline is f82b408. The implementation is based on that revision. The public API generator below builds parameterized PostgreSQL WHERE expressions for JSON and JSONB and writes each query and argument list; it does not execute queries.
Save as /private/tmp/hff-unused-fold-audit.go:
package main
import (
"context"
"encoding/json"
"fmt"
"os"
"strings"
"github.com/ornew/hff"
sql "github.com/ornew/hff/backends/sql"
"github.com/ornew/hff/capabilities"
"github.com/ornew/hff/engine"
"github.com/ornew/hff/schema"
)
func main() {
e, err := engine.New(
engine.WithSchema(schema.Struct(
schema.Optional(schema.Nullable(schema.Field("labels", schema.Struct(
schema.Optional(schema.Nullable(schema.Bytes("asset"))),
schema.Optional(schema.Nullable(schema.String("name"))),
)))),
schema.Int("offset"),
)),
engine.WithCapabilities(capabilities.Standard()),
)
if err != nil {
panic(err)
}
sources := []string{
`labels:items:map(p=>p[1]):foldl(0,(a,v)=>a+1)=labels:size`,
`labels:items:map(p=>p[1]):foldr(0,(v,a)=>a+1)=labels:size`,
`labels:items:map(p=>[p[1],p[1]]):foldl(0,(a,v)=>a+1)=labels:size`,
`labels:items:map(p=>{"unused":p[1],"nested":[p[1],p[1]]}):foldl(0,(a,v)=>a+1)=labels:size`,
`labels:items:map(p=>[p[1],p[1]]):foldr(0,(v,a)=>a+1)=labels:size`,
`labels:items:map(p=>[p[1],p[1]]):foldl(0,(a,v)=>[1,2]:foldl(a,(b,v)=>b+v))=labels:size*3`,
`labels:items:map(p=>[p[1],p[1]]):foldl(0,(a,v)=>a+v:size)>0`,
`labels:items:map(p=>[p[1],p[1]]):foldl(0,(a,v)=>[1]:foldl(a,(b,x)=>b+v:size))>0`,
`labels:items:map(p=>[p[1],p[1]]):foldl(0,(a,v)=>a+offset)=labels:size*offset`,
}
var results []map[string]any
for _, format := range []sql.JSONFormat{sql.JSON, sql.JSONB} {
for _, source := range sources {
program, err := e.Compile(context.Background(), source)
if err != nil {
panic(err)
}
query, err := sql.Build(context.Background(), program,
sql.WithDialect(sql.PostgreSQL),
sql.WithBindings(
sql.BindJSON(hff.Path{"labels"}, sql.Column("records", "labels"), format),
sql.Bind(hff.Path{"offset"}, sql.Column("records", "offset")),
),
)
if err != nil {
panic(err)
}
results = append(results, map[string]any{
"format": format,
"source": source,
"where": query.Where(),
"args": query.Args(),
})
fmt.Fprintf(os.Stderr, "%s %dB %dargs %darray %drecursive %s\n",
format, len(query.Where()), len(query.Args()),
strings.Count(query.Where(), "json_build_array("),
strings.Count(query.Where(), "WITH RECURSIVE"), source,
)
}
}
if err := json.NewEncoder(os.Stdout).Encode(results); err != nil {
panic(err)
}
}
Run it from the baseline and candidate checkouts with Go 1.27.1:
GOTOOLCHAIN=go1.27.1 go run /private/tmp/hff-unused-fold-audit.go \
> /private/tmp/hff-unused-fold-before.json \
2> /private/tmp/hff-unused-fold-before.log
GOTOOLCHAIN=go1.27.1 go run /private/tmp/hff-unused-fold-audit.go \
> /private/tmp/hff-unused-fold-after.json \
2> /private/tmp/hff-unused-fold-after.log
Results
The table reports generated WHERE bytes and json_build_array call sites,
baseline to candidate. These call-site counts describe SQL shape, not array
expansions, runtime, allocations, or database work.
| Fold workload | JSON bytes / json_build_array calls |
JSONB bytes / json_build_array calls |
|---|---|---|
Direct map() item, foldl() |
5,566 / 2 → 5,512 / 1 | 5,576 / 2 → 5,522 / 1 |
Direct map() item, foldr() |
5,567 / 2 → 5,513 / 1 | 5,577 / 2 → 5,523 / 1 |
Repeated-list item, foldl() |
5,810 / 3 → 5,512 / 1 | 5,820 / 3 → 5,522 / 1 |
Nested object/list item, foldl() |
6,417 / 4 → 6,022 / 1 | 6,427 / 4 → 6,032 / 1 |
Repeated-list item, foldr() |
5,811 / 3 → 5,513 / 1 | 5,821 / 3 → 5,523 / 1 |
Nested shadowed fold, foldl() |
7,594 / 4 → 7,296 / 2 | 7,604 / 4 → 7,306 / 2 |
Item used by size() |
6,171 / 3 → 6,171 / 3 | 6,180 / 3 → 6,180 / 3 |
| Nested callback captures item | 7,396 / 4 → 7,396 / 4 | 7,405 / 4 → 7,405 / 4 |
| Capture of other binding, item unused | 6,122 / 3 → 5,824 / 1 | 6,132 / 3 → 5,834 / 1 |
The related PostgreSQL selection passed 183,566 logged query/record results across 547 groups, including 13,904 new typed-parameter results across 40 groups. A separate set of 72 independent expected-value checks also passed. These are selected parity results, not a claim of full backend conformance.
Nine public sources were prepared and executed with omitted type OIDs for both
JSON formats on both baseline and candidate, for 36 positive cases; all passed.
Typed-parameter parity includes a separate nullable-source fold case whose
Query.Args contains an unused bound argument. PostgreSQL rejects its untyped
PREPARE on both baseline and candidate because the SQL has no matching
placeholder; this negative result is not included in the logged parity total
or the 36 positive omitted-OID cases.
Plain EXPLAIN used PostgreSQL 18.6 with JIT disabled and a 30-second
statement timeout, against records(labels, offset) rows containing a JSON
object with asset: "YQ==" and name: "Alice", plus offset = 2. For the
direct item workload, Function Scan, Aggregate, Recursive Union, and WorkTable
counts were unchanged at 5, 4, 1, and 1. The repeated-list, nested-object, and
nested-shadowed-fold workloads each removed one Aggregate node; the shadowed
fold retained its two Recursive Union and WorkTable nodes. These plan-shape
observations do not establish runtime improvements.
Root unit, vet, integration-vet, SQL race, and WASM checks passed. Raw generator
output is retained at /private/tmp/hff-unused-fold-before.json,
/private/tmp/hff-unused-fold-after.json,
/private/tmp/hff-unused-fold-before.log, and
/private/tmp/hff-unused-fold-after.log; validation logs are at
/private/tmp/hff-unused-fold-final-pg.log and
/private/tmp/hff-unused-fold-final-checks.log.
Limitations
This optimization applies only to packet-based PostgreSQL folds that pass the
binding-use proof and contain no custom extension signature in the source,
seed, or callback body. It does not produce native typed projections, prune
observed fields or accumulator state, or rewrite the fold as
size()/count(). Input-using folds, nested item captures, extensions, and
other uncertain sources retain their ordinary path. No runtime-performance
claim is made.