PostgreSQL Translation Optimizations
The PostgreSQL backend lowers HFF programs to parameterized SQL. These translation choices preserve HFF value, presence, null, and no-value behavior while controlling generated SQL shape. They do not promise a PostgreSQL plan, physical evaluation order, local runtime budgets, or a performance result for other schemas and data.
SQL translation choices
Prune statically empty list-concat arms
For eligible PostgreSQL JSON/JSONB list concatenations, the builder can omit an arm proven empty after lowering and validating the full expression. The proof also passes through bounded generic deferred-list callback, aggregate, and fold pipelines when the empty branch remains available to a compatible consumer. When one stream remains, it can forward that stream while preserving its order and outer presence/null state. Runtime-created empty streams and other deferred-list consumers retain their existing lowering. See empty concat arms for the bounded proof, parity scope, and plan observations. No runtime-speed claim follows.
Share repeated flatmap rows
In eligible JSON/JSONB flatmap() callbacks, PostgreSQL can reuse ordered
rows for groups of structurally identical callback-free built-in expressions
in a nonempty literal list or strict built-in concat() tree. The expressions
need not be adjacent or account for every leaf: distinct expressions and
unbound literals remain at their original positions. Sharing requires strict
typed-tree identity, including resolved types, lexical binding IDs, literal
values, operation and guard details, child order, and backend order demand;
each shared expression must contain a bound lexical reference, which may be a
capture. Matching SQL text or result types alone do not qualify. The original
constructor is fully validated first, and order, null/no-value, captures, and
diagnostics are preserved. A nested callback, custom method, or unsupported
node in the list retains ordinary lowering for the whole list. See repeated flatmap
parameters
and repeated flatmap expressions
for the bounded proofs and SQL-shape observations; neither establishes runtime
speed.
Simplify unconstrained JSON item-payload guards
When a recovered object-item pair guard accepts Any at the value slot,
PostgreSQL can omit redundant per-key payload dispatch while retaining
Build-time schema-key text validation, pair null checks, and outer-list scans.
Payload-specific guards and key-only paths keep their existing lowering. See
unconstrained item-payload guards
for the bounded SQL-shape observations; they do not establish runtime speed.
When full validation proves every per-key payload matcher literal false,
PostgreSQL can omit that matcher CASE and its arguments while retaining
pair/list checks and Build-time key validation. Separately, a compatible
fixed-point JSON carrier can be reused after guard normalization when its
original producer proof establishes equivalence. Actual conversions and
unproven inputs retain normal normalization. See guard normalization
reuse for the
bounded rules and SQL-shape observations; no runtime-speed claim follows.
For item-pair and scalar item-slot guards, PostgreSQL can also reuse the fallback when every fully validated per-key matcher or slot guard-normalization expression, including the fallback, has identical SQL text. This includes identical nonconstant checks; nonuniform expressions retain their dispatch. Pair/list checks and Build diagnostics remain in force. See uniform item guards for the measured SQL-shape scope; no runtime-speed claim follows.
Avoid rebuilding guarded payloads for root observers
After one numeric-containing List/Map type guard, immediate root type(),
exists(), size(), and list count() can omit the normalized payload that
the observer does not consume. The source expression and full guard still
run; other guards and value consumers retain ordinary normalization. See
guarded root observers
for the bounded applicability and SQL-shape evidence. No runtime-speed claim
follows from these observations.
Traverse object keys directly for key-only items() consumers
When an items() callback or transformation observes only the fixed key
slot, PostgreSQL can scan object keys without normalizing values or building
key/value pair arrays. This includes eligible key-only any(), all(),
count(), map(), flatmap(), sum(), avg(), min(), and max()
projections, plus size() and zero-argument count(). The source expression,
key order, callback scope, null, and no-value behavior remain in force.
Consumers that read a value slot, inspect a complete pair/list shape, use a
dynamic index, or return pairs keep generic lowering. See direct object-key
traversal for
the supported proof and SQL-shape evidence; no runtime-speed claim follows.
Omit unobserved fields from deferred fold packets
For a deferred fold over constructed-object items, a direct item-field read or literal-string index can retain only the complete child subtree the callback uses. The lowering keeps the original object fields, order, types, source state, and full validation; whole-item reads, whole/root-item guards, dynamic-index, custom-translator, or otherwise unproven cases retain ordinary packet lowering. See observed fold fields for the bounded rules and SQL-shape evidence. SQL-shape observations are not runtime-speed claims.
Preserve fixed-list positions in deferred folds
When a deferred item pipeline constructs a fixed list and the fold callback reads a direct finite integral numeric literal index, PostgreSQL can transport the selected child with its declared type instead of re-expanding every list arm. Full source, seed, and callback validation remain in force; whole-list reads or guards, dynamic or fractional indexes, and custom translators retain ordinary lowering. See the fixed-list fold transport record for its bounded proof and SQL-shape evidence. No runtime-speed claim follows.
Carry native numeric inputs into deferred folds
Eligible direct Int and Float arms retain separate bigint and
double precision input columns, preserving their stored HFF families.
See the numeric input record
for applicability and SQL-shape evidence.
Carry native scalar inputs into deferred folds
Eligible deferred folds can carry direct Int, Float, Bool, String, Bytes, Duration, or Timestamp input arms in distinct native PostgreSQL columns. Each arm must have one stored HFF family matching its SQL kind; different arms may use different families, and nullable arms remain eligible. If any emitted arm does not meet the proof, the whole fold retains its JSON input transport. The accumulator remains JSON, and existing native or safe identity-fold paths keep priority. The numeric input record and the scalar input record describe the bounded cases and SQL-shape evidence. These changes make no runtime-speed claim.
Build list-valued deferred folds with ordered aggregation
For a restricted list-building callback, PostgreSQL can replace the growing
recursive list accumulator with ordered aggregation after the ordinary JSON
fold path declines. The seed must be []; the callback must append or prepend
a nonempty list literal with built-in concat(), and each list element must be
independent of the accumulator binding. Full source, seed, and callback
validation remains in place; other shapes retain the recursive fallback. See
the list-building fold record
for the four direction/order cases and SQL-shape observations. This is not a
runtime-speed claim.
Project selected children into native fold inputs
Deferred folds can carry children selected from constructed objects or fixed lists in native SQL columns when each nonempty root and observed child has a matching schema proof. Object field/literal-key reads and finite integral literal list indexes qualify; unselected child positions do not widen the typed layout. Source presence, order, null/no-value behavior, full validation, and the JSON accumulator remain unchanged. Unproven roots or selected children keep the whole fold on its JSON input path. See the selected fold-child projection record for the bounded rules and SQL-shape evidence. No runtime-speed claim follows.
Compare native Timestamp bindings by their physical columns
For eligible native BindTimestamp filter comparisons against a Timestamp
literal or another native bound Timestamp, the builder compares the physical
timestamptz base and optional 0–999 ns remainder directly. Native
before() and after() use this path as well. This avoids reconstructing
numeric Unix-epoch nanoseconds for these predicates while preserving the
storage and null contracts. Guards, structured/value expressions, and
JSON/JSONB Timestamp expressions keep their existing lowering. In the checked
indexed fixture, plain EXPLAIN used a Bitmap Heap Scan with a BitmapOr of
two Bitmap Index Scans for the direct predicate; the representative epoch
extraction expression used a sequential scan. This is plan evidence only, not
a timing result or a general planner guarantee. See the
native Timestamp predicate record
for the paired parity cases and reproduction.
Reuse bounded equality in JSON membership
For bounded List, Map, and Struct alternatives, in(), contains(), and
unique() can use schema-guided recursive equality after representation and
metadata checks. Any roots and nested Any leaves retain generic equality;
upstream normalization of a complete membership receiver also remains. The
measured SQL removed recursive document construction, though some barrier
counts increased. Plain EXPLAIN confirmed smaller plan trees in the tested
empty-table fixture; no execution timing was collected. See bounded JSON
membership equality
for profiles, parity scope, and limits.
Keep literals as bound parameters
HFF literals remain typed query parameters in Query.Args(). Scalar literals
are referenced directly rather than wrapped in one-row subqueries. Literal
null is SQL NULL; query values are not interpolated into SQL text. Computed
expressions and guards retain projection barriers where they need a named
value or a separate presence state.
Emit eligible scalar predicates directly
For eligible comparisons between a directly bound native scalar column and a literal, the backend can retain the comparison as a direct column predicate. Boolean composition, null tests, and scalar truth tests can use this path when their leaves are eligible. Nullable fields keep explicit non-null tests; unsupported expressions retain the general lowering. The measured fixture shortened one predicate and removed its projection barriers, but no execution speedup was measured. See direct scalar predicate shape for the exact fixture and its limits.
Direct native Float comparisons are also available for schema-valid finite
Float columns and literals, with scalar truth/null tests and same-Float-literal
in() covered by the same focused path. Other mixed Int/Float comparisons,
general computed expressions, guards, JSON, and structured values retain the general
lowering.
The focused cases remove OFFSET 0 barriers, but no plan or runtime-speed
claim follows from SQL shape alone. Plain EXPLAIN on an empty temporary
indexed Float table showed a Bitmap Index Scan for factor<1.5; this is a
fixture-specific plan observation, with no timing measurement. See direct
native Float predicates
for the measured filters and evidence limits.
Remove projections from eligible native-column filters
For eligible root filters, native exists(), same-kind bound-column
comparisons, bytea len() against an Int literal, Duration nanosecond
comparisons, and Int rounding identities can avoid nullable value projections.
Required exists() can become TRUE; nullable values retain non-null tests.
Guarded, structured-parent, JSON, mixed-kind, and callback paths retain their
general lowering. The native-column filter record
reports SQL shape and parity evidence without claiming execution speed.
Reuse qualified raw columns in generic expressions
When a native scalar binding has a qualified SQL column reference and its expression resolves to the exact registered column with constant-true no-value presence, generic expressions can reuse that column instead of projecting the raw value again. Nullable and optional fields qualify too; SQL NULL remains NULL. This removes leaf projections but leaves projections for computed methods, guard results, JSON, Timestamp, and structured bindings. Reuse stays within existing projection scopes; it does not authorize sharing across callback parameters or shadowed bindings. The qualified native-column projection record reports SQL-shape and plan observations, not execution timings.
Reuse repeated native operation results
Within one projection scope, repeated native Int arithmetic or negation and
built-in String lower()/upper() can reuse a completed result when the
operation, typed operands, presence, and result type match. Operands are still
translated and validated in source order. Literal cache hits remove only
freshly added literal parameters; nonliteral operands must add no new parameters
for a hit to be reused.
Callback scopes remain isolated, and custom method implementations are never
evaluated to form cache keys. See the native operation
reuse record
for filters, parity coverage, and SQL-shape measurements; no speed claim
follows from those measurements.
Reuse native identity views and proven presence
Int- and Null-valued round(), ceil(), and floor() views and native
Duration nanoseconds() can reuse an already projected operand, preserving
its carrier and no-value state. Eligible arithmetic, collection, JSON access,
regex, and method expressions can also omit tautological conditions when a later
operand's presence is proven TRUE. Float rounding, dynamic JSON decoding, and guard-dependent presence
retain their computed paths. The identity and presence record
reports paired parity and SQL-shape evidence; no runtime-speed claim follows.
Specialize exact mixed-numeric literal thresholds
For pure filter comparisons between a directly bound Int or Float column and
a typed literal of the other numeric kind, Build can derive an exact direct
threshold. For example, i>18.5 becomes i>=19, while
f>9007199254740993 becomes f>=9007199254740994; the compared column values
are not rounded. The rewrite covers all six comparisons and preserves nullable
column handling. Eligible identity-derived columns use the existing direct
path. Other computed, guarded, dynamic, JSON, and value-context operands
keep the general lowering; same-Float comparisons remain on the direct Float
path. Shape and plain-plan observations are in the exact mixed-numeric
threshold record.
They do not establish runtime speed. An indexed-fixture check with sequential
scans disabled showed candidate index conditions; it does not establish
default planner selection.
For JSON-backed scalar Float pairs, PostgreSQL consumes the original or
metadata-restored value instead of serializing to canonical JSON and decoding
again. The supported PostgreSQL Float model also removes the older exact
arithmetic templates and their repeated decomposition. In the measured
String|Float self-pair, generated WHERE text shortened from 6,570 to 1,308
bytes, with 16 to 4 projection barriers; plain plan inspection showed a
smaller plan tree. These are SQL-shape observations, not speed claims. Dynamic
JSON sorting now shares its compact exact numeric key with extrema and
distinct(). See the native Float lowering record
and Float model design.
Fold closed built-in conversions during Build
PostgreSQL Build resolves supported literal-only chains of exact built-in
method IDs into typed SQL values in eligible value positions. This preserves
source argument order and conversion failures without changing the compiled
HFF plan or using extensions as constant evaluators. Float-looking number()
input and Float operations stay on PostgreSQL's native path. See literal
conversion folding
for tested SQL shapes, accepted forms, and evidence limits.
Compose direct predicates in mixed boolean filters
Eligible native scalar leaves can remain in the outer WHERE when a filter
also contains predicates that need general lowering. Generic siblings share
one scalar subquery, which retains shared JSON parsing, metadata restoration,
or exact Float work. If planned generic scopes share a schema path or ancestor
path, a conservative check falls back to general lowering for the whole filter.
This also covers cheap native columns and can miss some safe composition
opportunities.
Parameter binding remains in source order. This changes the generated SQL
shape, not HFF null/no-value semantics or PostgreSQL's physical evaluation
order. The mixed predicate composition record
shows three examples and a fixture-specific plan inspection; no execution-speed
or default-planner index-use claim is made.
Prune numeric comparison branches excluded by JSON schema
Dynamic JSON comparisons emit only Int or Float branches admitted by the compiled receiver type. An Any value or a receiver type that permits both numeric subtypes keeps runtime dispatch; exact mixed Int/Float comparison remains unchanged. Null/no-value and runtime category mismatch behavior are preserved. The schema-guided numeric dispatch record shows the measured SQL-shape differences; no runtime-speed or plan claim is made.
Prune numeric arithmetic branches excluded by JSON schema
For JSON-backed arithmetic, negation, and numeric rounding, the translator
intersects the compiled value type with the stored schema's possible numeric
families after the existing representation and metadata checks. A
String|Int value therefore has only an Int input family; Int division still
returns Float. A
String|Float value retains PostgreSQL's general Float kernel, and an
Int|Float or Any value keeps each numeric family it can reach. Int
overflow, Int-to-Float promotion, Int division results, and the PostgreSQL
native Float Profile remain unchanged. Parity and SQL-shape checks passed, but
no runtime measurement was made. See the schema-guided arithmetic branch
record for
filters and limits.
Keep single-family JSON arithmetic results typed
When a compiled JSON arithmetic expression has one non-null numeric result
family and all reachable branches return that family, the translator can
return bigint or double precision directly instead of serializing the
result as JSON and decoding it again. Representation checks, JSONB metadata
recovery, presence, typed SQL-null carriers, Int overflow and promotion, and
the PostgreSQL Float Profile remain in place. Int division still returns
Float. Results with multiple possible numeric families retain JSON; Null-only
and Never results keep their existing null carrier.
The typed numeric result record
reports generated SQL and fixture-specific plain EXPLAIN structure; no
runtime timing was measured.
Prune impossible categories in dynamic JSON pair comparisons
For dynamic-versus-dynamic comparisons, the translator intersects the JSON categories permitted by both compiled operand types. It can skip numeric subtype branches or recursive container equality when no matching category can occur on both sides. Existing stored-value normalization, JSONB metadata recovery, and unsupported-union checks still run before category pruning. Any retains its full category set, but a restricted opposite operand can still narrow the intersection; Any-versus-Any stays generic. Comparisons between reachable containers retain recursive equality, and Float normalization still applies whenever a Float branch is possible. See the dynamic pair category pruning record for SQL-shape examples and limits; no timing or speed claim is made.
Specialize reachable JSON container-union equality
For finite compiled root unions in JSON-backed = and !=, recursive equality
can use compiled types at each stored position. This includes overlapping
alternatives such as List<Int>|List<Float>; lists compare by ordinal and
objects compare actual keys, retaining Map/Struct cross-shape behavior and
per-key Map fallback. Unconstrained Any leaves keep generic recursive
equality. Stored-value representability and JSONB metadata requirements remain
in force; consistency between stored values and sidecar metadata remains the
caller's responsibility. Partially overlapping schemas may still require
recursive metadata restoration. in(), contains(), and unique() retain
their existing equality paths. The
container-union equality record
gives the shape matrix and limits; these SQL reductions are not execution-time
claims.
Use PostgreSQL's native approximate Float model
Scalar Float comparisons avoid a canonical JSON serialize/decode round-trip.
Supported Float arithmetic, conversions, rounding, and formatting follow the
PostgreSQL float.* Profile details; extreme arithmetic magnitudes use the
documented checked fallback. Int remains exact and mixed Int/Float comparisons
remain exact over represented values. In the measured JSONB pair shapes,
String|Float self-comparison fell from 6,570 to 1,308 WHERE bytes and from
16 to 4 projection barriers; this is SQL-shape evidence only. Plain plan
inspection also showed fewer nested-loop nodes, without execution timing.
Dynamic JSON sorting now uses the shared compact numeric ordering key instead
of binary Float decomposition; see the dynamic sort ordering-key
record. See the
native Float lowering record
and backend-native Float design.
Remove proven arithmetic identities
For statically known Int and Float expressions, PostgreSQL Build removes only the proven literal-zero and positive-one identities. Required Int-to-Float casts, signed-zero behavior, null/no-value handling, and callback scope remain intact. Float addition by zero, dynamic numeric dispatch, multiplication by zero, and reassociation retain their existing lowering. The arithmetic identity record reports SQL and plain-plan shape only; no timing was measured.
Compare scalar JSON pairs without canonical JSON conversion
When two JSON-backed dynamic values can only compare as scalars, the translator
can consume their original JSON values or metadata-restored JSONB values
directly. Reachable numeric families are decoded once for the comparison.
Schema checks, JSONB metadata recovery, and unsupported-union checks still run
before this path. If matching list or object values are possible, structural
equality retains the normalized recursive path. See scalar JSON pair
decoding for the
SQL-shape and fixture-specific EXPLAIN evidence. No execution timing was
collected.
Reuse numeric comparison kernels within a scope
When a projection repeats mixed numeric comparisons, the translator can reuse native Float floor and Int/Float ordering projections. Within one scalar JSON pair, identical stored text can also share a decode when its numeric family, ambiguity check, and Float override match. Operators, operand direction, and presence remain comparison-specific, and caches do not cross callback, filter, or independent JSON-pair scopes. The numeric comparison kernel record reports generated-SQL and plain-plan shape only; no execution timing was collected.
Reuse validated JSON from map outputs
After full schema validation, a generated map() output can retain a
representation proof keyed by its exact SQL expression, native JSON format,
and captured stored type. Ordinary aliases and SQL-NULL-to-JSON-null
normalization preserve that proof; guards, type guards, extraction, and casts
do not. Presence and Float-loss handling remain separate, and canonical
serialization is unchanged. This reduces selected json_array_elements
expansion sites and corresponding Function Scan nodes. Projection barriers
and sampled Result/Nested Loop counts remain unchanged. See the
normalized map JSON reuse
record for the 11,648-case baseline/candidate parity matrix and limits; no
execution timing or memory measurement was made.
Reuse normalized JSON across compatible fold states
When the fold seed and callback results normalize compatibly to the inferred
accumulator representation, the translator can reuse the predecessor's
normalized JSON proof through the recursive callback scope. The proof retains
the exact value, native JSON format, and original stored type; reuse against a
target schema requires a separate value-stability and full-schema check. Per-key
Map/Struct widening is reused only where covered by the proof. Incompatible
shapes keep ordinary normalization. This reduces selected
json_array_elements and json_each expansion sites; the measured
projection-barrier counts remain unchanged. No execution timing or memory
measurement was made. See the
fold normalization proof
for source workloads, parity coverage, and limits. The fold matrix contains
17,216 PostgreSQL/local results on both baseline and candidate; it includes
Map-to-Struct widening and Float-format controls.
Omit unused input payloads from deferred folds
For eligible packet-based folds, PostgreSQL can omit carrying the input item payload when the callback does not reference its item binding anywhere in the callback body, including nested callbacks. Source evaluation, fold order, seed/result semantics, null/presence, first failure, and validation remain unchanged. The added validation probe reuses the prepared source plan. Captures of other bindings are allowed; a nested capture of the item or a custom extension in the source, seed, or callback body keeps ordinary lowering. Existing native and safe identity-fold paths remain preferred. The change omits only the deferred packet payload and decode; it does not replace the fold with a count, project unobserved fields, or prune accumulator state. See unused fold inputs for the bounded SQL-shape observations and current verification scope. No runtime-speed claim follows.
Lower homogeneous scalar membership directly
For eligible same-type scalar membership, the translator can use SQL
IN rather than encoding and decoding each value through JSON. The direct
predicate path handles literal membership on supported native scalar columns;
the expression path also avoids JSON conversion for homogeneous native scalar
values. Null candidates do not match, nullable receiver behavior is preserved,
and mixed numeric equality stays on the exact general path. For the eligible
root filter age:in(18,21), direct composition with scalar predicates
shortened the WHERE expression from 1,627 to 80 characters and removed all
three projection barriers; it binds two arguments. A same-type dynamic
membership expression is not measured here. For the same literal filter, an
intermediate native-IN lowering generated a 399-character WHERE and
retained three barriers; the direct root-predicate lowering generates 80
characters with no barriers and two arguments. Plain EXPLAIN on an empty
temporary table with an age index selected a Bitmap Index Scan for the direct
literal-root predicate. This plan result is fixture-specific; no execution
speed was measured. See
homogeneous scalar membership
for scope and limits.
Emit eligible native String method predicates directly
For contains(), startswith(), and endswith() on a directly bound native
String column with literal String or null arguments, the translator can emit
the predicate directly without intermediate projections. It can also lower
len() comparisons against integer literals directly. These paths preserve
null handling and COLLATE "C"; computed, dynamic/Any, JSON, and nonliteral
argument shapes retain the general lowering. The measured predicates were
shorter and had no OFFSET 0 barriers, but no plan or execution timing was
collected. The native String predicate shape
record gives the exact filters and limits.
For ~, !~, and built-in matches() in filter context, a directly bound
native String column with a literal pattern can use the translated PostgreSQL
operator directly. The builder binds only the translated pattern and keeps an
explicit null mask. Dynamic patterns, JSON/computed/guarded receivers, and
matches() in value contexts or callback bodies keep the general lowering.
An empty-table,
forced-index diagnostic showed the direct ~ predicate eligible for a
matching C-collated pg_trgm GIN index where the previous regexp_like()
shape was not. This is fixture-specific plan evidence, not a timing or
automatic-index-use guarantee. See
literal regex filter lowering
for exact shapes and null behavior.
Emit direct predicates for JSONB String lists
For a directly bound JSONB List<String>, contains() with one or more
literal String needles and the exact callback form
tags:any(item=>item="auth") can lower to @> without projection barriers.
For contains(), all needles must occur in the list, and duplicate needles do
not require extra occurrences. The callback path applies only when equality
uses the callback's own bound item; a callback parameter may shadow an outer
variable, but a captured/outer binding, complex body, guard, or type observation
uses the general lowering. Nullable containers retain an explicit null test;
null or dynamic needles and other list shapes also use the general path.
The measured contains() filter shrank from 1,164 characters and four
barriers to 84 characters and none; the measured any() filter shrank from
884 characters and seven barriers to the same 84-character predicate. Plain
EXPLAIN on an empty temporary fixture with default planner settings selected
Bitmap Index Scans for both jsonb_ops and jsonb_path_ops. These observations
do not establish production index selection or runtime speed; no execution
timing was collected. See the direct JSONB String-list membership
and simple JSONB String-list any
records for eligibility, parity, and limits.
Use containment for simple JSONB object and list filters
For a directly bound, metadata-free JSONB object, literal-key equality against
a String or Bool literal can use column @> jsonb_build_object(key, value)
with an explicit non-null filter. For a directly bound JSONB List<String>, a
simple all() equality callback can use column <@ jsonb_build_array(value).
The list subset preserves empty-list truth and duplicate-insensitive
containment. Missing keys and JSON-null values are still handled by these
predicates and evaluate false for the non-null literal examples above. Null
equality, numeric or nested paths, JSON text, metadata-backed object bindings,
nullable list elements, and complex callbacks do not use these specialized
paths. See the JSON guide for
examples and exact limits.
On a temporary GIN-indexed JSONB column, plain EXPLAIN selected a Bitmap
Index Scan for object @>. List <@ remained a Seq Scan even with sequential
scans disabled, so the checked list predicate did not expose a GIN index
condition. This is fixture-specific plan evidence only; it does not establish
runtime speed or production planner choices. See the
JSONB containment record
for parity coverage and the exact diagnostic.
Project computed steps once
Computed expressions are lowered through relational LATERAL projections.
OFFSET 0 barriers keep downstream SQL from recursively expanding earlier
expressions. A presence value proven to be true can remain a SQL constant;
dynamic presence from null/type guards remains explicit. The barriers describe
translation structure and do not constrain PostgreSQL's physical execution
order.
For items() callback pipelines, row-dependent built-in work after a filter
can be placed behind a per-row gate when it depends on the current item; nested
local bindings may also be used. The callback body must contain a method call
or explicit type guard; trivial value or Boolean forwarding adds no gate.
Nested-only work on constant data, root or outer captures, custom methods, and
independent call subtrees are not moved. The gate includes type checks and
normalization. It may add overhead when most rows pass. Plain EXPLAIN
verifies placement only, not runtime execution counts or speed. See the
filtered pipeline stage record
for tested profiles and SQL-shape limits.
Share plain paths within each projection scope
Repeated unguarded paths from scalar columns, schema-bound JSON/JSONB,
composites, flattened structs, and callback item fields can share a projection
within one scope. Callback paths are keyed by lexical binding identity, field
path, and type; captures, shadowed parameters, nested callbacks, and separate
scopes remain isolated. Guards, dynamic indexes, and method calls are not
cached as plain paths. The backend also omits a null check when the declared
type proves non-null, and an exact repeated type guard when its constraint
matches the inferred type. These choices assume stored database rows honor the
declared schema; Build does not inspect rows.
Read only selected struct fields
For native composites, an unguarded field chain can read the selected native attributes before converting the selected value. For flattened structs, it can project only the selected binding while retaining explicit parent-presence and parent-null expressions. A whole struct or guarded/dynamic access uses the general conversion path when required. See composite bindings and flattened struct bindings for representation requirements.
Select a JSON object value before normalization
For a schema-known Map, get(key) can validate the applicable Map schemas,
select the raw value, then decode or normalize only that candidate. A literal
key validates only its selected value schema, so unused Struct fields do not
add representation requirements. One known primitive category can decode
directly after a category check; selected unions and nested Maps retain
selected-value normalization. Dynamic access through heterogeneous Structs,
and values()/items(), still normalize the object. The
selected Map value record
reports SQL shape and controls; these measurements make no runtime-speed
claim.
Restore only a selected JSONB metadata subtree
For a fixed literal field chain, the translator can extract a selected subtree
before restoring numeric types. Direct metadata-backed BindJSON has this
support; it also applies when the chain crosses a flattened BindStruct or
reads a JSONB document in a relation item's physical binding. Parent
presence/null state is retained, and metadata lookup uses the subtree's
document-relative JSON Pointer. Whole-document reads and paths whose
resolution crosses a guard, computed value, or dynamic index retain the
general restoration path. Callback projection scopes remain isolated, while a
fixed path rooted at a physically bound relation item can qualify within its
own scope. Each extraction still needs an object check and projection; this
describes SQL shape, not a plan or runtime guarantee. The caller remains
responsible for stored document/metadata consistency; no per-attribute
metadata API is added. See the JSONB binding contract,
direct-binding measurements,
and flattened/relation measurements.
Skip child expansion for JSONB scalar leaves
During recursive metadata restoration, scalar leaves remain in the walk for numeric type/text restoration, pointer lookup, and reconstruction, but the recursive term does not try to expand them as children. The same leaf filter applies to direct, flattened, and relation-backed metadata documents. Independently, fixed field chains under flattened and relation bindings can use selected-subtree restoration as described above; other paths may still restore the full document. Scalar-leaf filtering does not validate stored documents or change the metadata contract. The measured profiles are in JSONB leaf expansion.
Reuse fixed Float SQL fragments during Build
This was a Build-allocation optimization for the earlier checked/unchecked Float SQL templates. The native Float model replaced those templates, so this mechanism is no longer active. The record retains the historical measurements: Float Build fragments.
Reuse repeated Float arithmetic operands
This was a SQL-scope cache for the earlier exact Float arithmetic
decomposition. The native Float model replaced that arithmetic path, and
dynamic sorting now uses the compact key shared with extrema and structural
distinct(). The record retains the historical measurements: Float operand reuse.
Sort recoverable dynamic JSON lists
For native JSON Any lists, dynamic sorting can consume the original array
after checking that the receiver is an array, instead of rebuilding a
comparison document. JSONB keeps comparison-document normalization when
numeric subtype restoration requires it. Dynamic Float keys now use the same
compact exact numeric ordering key as extrema and structural distinct();
the old binary Float decomposition is not used by this sorting path. The
homogeneous schema-resolved fast path and Float arithmetic lowering are
unchanged. Stable ordering, invalid-family checks, and representation recovery
remain in place. SQL-shape evidence for the shared key is in the
dynamic sort ordering-key record;
the earlier dynamic JSON sort measurements
cover the separate array-gating and comparison-document changes.
Group JSON-native structural distinct values once
For recoverable structural List, Map, and Struct element trees, PostgreSQL can
compute a structural key per item and use DISTINCT ON with the original
ordinal to retain the first value. Trees without Float or Any alternatives
use normalized JSONB equality keys. Trees that may contain Float or Any use
a recursive full-path/category key and the shared exact numeric ordering key.
Scalar element grouping continues to use its existing typed keys. Tagged
Bytes, Duration, and Timestamp alternatives conservatively retain the existing
type-aware pairwise equality path. Representation and metadata checks still
precede structural grouping, and the selected value remains the original
item. In the measured empty-table plans, the JSON-native keys removed
prior-row join nodes; this is plan-shape evidence, not an execution-speed
claim. See the
structural distinct grouping record
for fallback boundaries and parity details.
Skip pairwise checks for proven unique object items
For a recovered object items() list, each source pair has a distinct String
key under the required unique-object-key storage contract. unique() can omit
the pairwise comparison with either a source-key proof or a pair-equality
proof. Direct items, guards, filtering, an identity map, and singleton identity
flatmap can retain the source-key proof. Separately, distinct() proves its
output has no equal pairs, even if different pairs share a key. Receiver
evaluation and null/no-value handling remain; other transformations retain
the general comparison unless they establish one of these proofs.
This is SQL-shape evidence only: the tested JSON and JSONB filters removed the
pairwise array cross-product, while receiver normalization remained. See
unique object item keys
for the storage contract, measured query shapes, and limits.
Prune scalar leaves during canonical JSON serialization
The recursive token CTE keeps scalar and null rows as token sources but does not expand them as children. Float tokens project their decoded value once before canonical text formatting. This reduces generated SQL for the measured dynamic JSON shapes; it does not guarantee faster execution for every query or enforce the local JSON-depth limit. See [canonical JSON lowering] (../../optimizations/009-postgresql-canonical-json-lowering.md) for the workloads, controls, and limits.
Specialize recoverable JSON equality by declared root type
When both operands have definite roots in the same structured category, the
translator uses typed list or object recursion even when nested values are
Any. An Any value compared with a known scalar also uses the known scalar
category and comparison. Comparisons between two unconstrained roots, an
unconstrained root and a container, and ambiguous structural unions keep the
generic recursive path. Numeric representability and JSONB metadata checks
still apply to both operands. See the [measured JSON equality comparison]
(../../optimizations/007-postgresql-guided-json-equality.md) for workload and
control results; it does not promise a performance gain for other schemas.
Reuse parsed JSON within a projection scope
Repeated fromjson() calls on the same projected receiver can reuse the
complete validated JSON result within one projection scope. Each call keeps
its own presence state. Callback and shadowed-alias scopes remain isolated;
different SQL expressions are not merged just because they contain equal
runtime text. Full-document syntax, Unicode, duplicate-key, and nested numeric
validation still runs before the value is exposed. See the
measured parsing and validation comparison
for the workload, generated-SQL tradeoffs, and sample limits.
Within a projection scope, repeated unguarded literal-field chains rooted at
the same built-in fromjson() result can also share selected extraction and
decode work. Reuse is keyed by the parsed expression, presence, format, types,
ordering class, and field path. Full-document validation still occurs.
Guarded, indexed, extension, computed-root, and cross-callback paths retain
their ordinary lowering. See the
parsed JSON field reuse comparison; the
measured Function Scan counts did not change, and no execution timing was
collected.
Reuse constructed primitive JSON in callbacks
For map() and foldl()/foldr(), a newly computed native SQL value can be
encoded directly into callback JSON state when its complete stored schema is
one primitive family, optionally nullable, matching the SQL carrier.
SQL NULL becomes JSON null; presence and no-value remain separate. This
avoids parsing and normalizing the just-constructed scalar again. JSON
carriers, Any, wider unions, containers, and flatmap() results retain
schema-guided normalization.
The measured Float map and fold SQL shrank while their callback and recursive
query structure remained. No runtime timing was collected. See the
constructed callback JSON record
for controls and limits.
Read duration fraction digits as bytes
The duration parser validates its input grammar before processing fractional digits, then converts the validated ASCII fraction to UTF-8 bytes once and reads digits by byte offset while carrying exact decimal sums. Paired measurements show much lower PostgreSQL execution time for the tested long fractions, with increased Build size/allocation and a small regression on short compound inputs. The result is specific to the measured profile. See the duration parser measurements for ranges, controls, and limits.
Reuse a restored parent for overlapping paths
Within the same projection scope, a fixed descendant can reuse an unguarded
JSONB parent that has already been restored. The parent restoration uses
document-relative metadata pointers; the descendant is extracted from that
restored JSON text without a second metadata lookup. A direct nested BindJSON
can qualify within its binding boundary, and the most-specific physical
binding takes precedence. The translator does not hoist a parent that appears
later in the query. Guarded values are not shared, and callback and order-demand
projections keep separate caches. See the
measured overlapping-path comparison
for the workload that benefits and the controls that do not.
Use correlated existence checks for plain relation predicates
A plain relation path consumed by any() or all() can lower to correlated
EXISTS or NOT EXISTS directly, without first aggregating all related rows
into a JSON list. The caller's correlation predicate stays in the subquery's
WHERE clause. For flattened relation items, only callback-observed fields
need to be projected; a whole item is built only when the callback observes
that value. Guards, computed relation lists, or alias collisions can require
the general list-materialization path.
Inside these subqueries, eligible scalar predicates can also stay direct when
the callback uses direct native scalar fields and literals combined with
Boolean AND/OR/NOT. For example,
items:any(item=>item.price>100 item.active) removes four inner
CROSS JOIN LATERAL projections; two outer generic projections remain.
Nullable or optional struct item state and noneligible callback expressions
retain general lowering. This is a SQL-shape observation, not a runtime-speed
claim. See the relation callback predicate record
for scope and limits.
For a plain bound relation, has(0) and has(-1) can use this witness query
to test nonemptiness directly. The existing parent and relation presence/null
case remains around the EXISTS; no unique item ordering is needed for these
constant endpoints. Other indexes, including has(-2), and invalid Float
indexes retain the cardinality/general path. In the checked empty-table plan,
an indexed child scan appeared beneath a hashed SubPlan and no aggregate or
COUNT appeared. This is plan-shape evidence, not a claim about per-row early
termination or runtime speed. See the
relation endpoint existence record
for the reproduction and limits.
Average eligible relation projections directly
For a bound relation with a statically numeric projection, PostgreSQL can
compute a correlated AVG(numeric) without materializing the related values
as JSON. Int values enter as exact numeric; Float values pass through
round-trip-safe Float text before numeric aggregation and a checked Float
conversion. Null and guarded no-value contributions are skipped, and the
relation's configured presence/null state remains authoritative. This path
does not require relation ordering. Guarded or computed relation receivers
and projections whose numeric family is dynamic or otherwise unrecoverable
retain the general lowering. An outer-column capture in a supported numeric
projection remains correlated to the current row.
The checked fixture reduced generated WHERE sizes for five relation
averages: 2,710 to 487 bytes, 2,900 to 495, 3,027 to 865, 2,832 to 695, and
2,259 to 568. The first four queries reduced bound arguments from four to one;
the outer-threshold projection reduced three to one. A plain EXPLAIN on an
empty temporary fixture changed the representative physical Int projection
from 23 plan nodes to 7, while both plans retained two sequential scans. These
are SQL-shape and plan observations, not timing or execution-cost claims. See
direct relation averages
for the tested queries, reproduction commands, and applicability limits.
Lower direct collection emptiness and cardinality filters
An unguarded, statically typed root JSONB binding without type metadata can
answer empty List/Map/Struct equality or inequality, bare collection truth, and
zero-size comparisons from its physical JSONB value. Nonzero literal
size() comparisons use guarded jsonb_array_length for a List root. The
shape check keeps SQL NULL and JSON null from matching at the filter boundary.
For a plain relation, empty-list comparison, truthiness, and zero-cardinality
comparison use correlated EXISTS or NOT EXISTS, a state test, or a constant
result as appropriate. They do not need an order or a full list aggregate.
Nonzero relation thresholds retain the count-based path. Nested JSON paths,
metadata-backed JSONB, and guarded, computed, or dynamic receivers retain
general lowering. See direct collection predicate checks
for parity scope and supported forms.
Drop ordering only when the proof reaches the consumer
Relation and transformation order is preserved whenever the consumer can
observe it. The translator can omit an aggregate ORDER BY when an order
independence proof propagates through the relevant list expression—for
example, a mapped relation used only by any(), or a canonical integer/string
sort whose stable ties are indistinguishable. It keeps order where equal float
representations, indexing, returned sequence contents, or callback failure
precedence make it observable. Shared materializations used in both ordered
and unordered contexts remain separate.
The proof preserves HFF results; it does not promise database short-circuiting or callback evaluation order. See the measured order-independence record for one controlled relation workload.
Measure translation and database execution separately
Build benchmarks measure HFF-to-SQL translation, allocations, generated SQL bytes, and parameters. They do not measure PostgreSQL planning or execution. With Go 1.27.1, run:
go test ./backends/sql -run '^$' \
-bench '^BenchmarkPostgreSQL(StructuredBuild|FlattenedBuild|RelationBuild)$' \
-benchmem
For database measurements, the integration benchmark reports PostgreSQL's
planning and execution separately using EXPLAIN. Provide a dedicated tested
PostgreSQL 18.6 container; the harness verifies SQL result cardinality against
local HFF matches. It does not claim index use outside the measured schema or
measure Docker/fixture setup as database execution time:
HFF_POSTGRES_CONTAINER=<container-name-or-id> \
go test -tags=integration ./backends/sql -run '^$' \
-bench '^BenchmarkPostgreSQL(JSON|Relation)Execution$' \
-benchtime=1x -count=3
Set HFF_POSTGRES_PLAN_DIR to save the first plan and SQL sample per case:
HFF_POSTGRES_CONTAINER=<container-name-or-id> \
HFF_POSTGRES_PLAN_DIR=/tmp/hff-postgresql-plans \
go test -tags=integration ./backends/sql -run '^$' \
-bench '^BenchmarkPostgreSQL(JSON|Relation)Execution$' \
-benchtime=1x -count=3
The JSON/JSONB execution baseline uses unindexed temporary JSON tables. Relation cases use a controlled indexed correlation fixture. Keep schema, data, settings, and PostgreSQL version fixed when comparing runs; plan and latency conclusions are workload-specific.