038. Remove projections from eligible native-column filters

  • Status: Implemented
  • Date: 2026-10-14

Problem and mechanism

Some filters over directly bound PostgreSQL scalar columns were translated through nullable value projections even when the operation could be evaluated on the column itself. For eligible filter expressions, the translator now emits direct predicates and keeps explicit non-null checks where the HFF value is nullable. A required native field's exists() check becomes TRUE without a value projection; nullable native fields use IS NOT NULL.

The covered forms include direct exists() and same-kind native-column comparisons; len() on a native bytea compared with an Int literal; the Int-valued Duration:nanoseconds() comparison against a native Int column; and eligible Int round(), ceil(), and floor() identity operations. Previously supported direct identity rewrites remain eligible. These changes remove intermediate projections while preserving outer-filter null behavior, parameter order, and the SQL value type.

The rewrite is limited to the proven native scalar forms. Guard-presence tests, Float-versus-Int comparisons, JSON paths, structured-parent state, and callback bodies keep their general lowering. Other qualified-column and identity projection opportunities remain outside this record.

Applicability

Filter form Status Reason
i:exists on a required native scalar Applied The schema guarantees a non-null value, so the predicate becomes TRUE.
i:exists on a nullable native scalar Applied Emits an IS NOT NULL predicate.
Same-kind native scalar columns such as i=j or s<t Applied Emits direct column comparisons with nullable masks.
Native bytea len() compared with an Int literal Applied Emits octet_length(bytea) and retains the receiver-null mask.
d:nanoseconds compared with an Int column Applied The result is already an Int carrier and needs no value projection.
Int round(), ceil(), or floor() in an eligible filter Applied These operations preserve the Int value and subtype.
Guarded, structured-parent, JSON, mixed-kind, or callback forms Not applied Their presence, representation, or scope still requires general lowering.

Reproduction

The baseline is commit 2b3b1be. The checks used Go 1.27.1 on Darwin/arm64 and PostgreSQL 18.6 on Debian/aarch64 with extra_float_digits=1. Float handling follows the PostgreSQL Profile; this work does not change that contract.

The shape fixture used this temporary table:

CREATE TEMP TABLE records (
  i bigint, j bigint,
  f double precision, g double precision,
  s text, t text,
  p boolean, q boolean,
  b bytea, c bytea,
  d bigint, e bigint,
  items json, data json
);

The corresponding HFF schema used Int for i/j, Float for f/g, String for s/t, Bool for p/q, Bytes for b/c, and Duration for d/e. Each field used Bind(hff.Path{name}, Column("records", name)), with Duration stored as bigint nanoseconds. The fixture tested each scalar as required and nullable; items was List<Int> and data was Map<Any>, both bound with BindJSON(..., JSON). The temporary shape harness measured the WHERE byte length, OFFSET 0 count, and argument count for these representative queries:

i:exists
i=j
s<t
b:len>0
d:nanoseconds=i
i:floor>1
i:exists b:len>0 d:nanoseconds=i

The durable integration fixture scalar_column_where_integration_test.go compares SQL with local evaluation over required, nullable, and optional profiles. It covers Int, Float, String, Bool, Bytes, and Duration values; nulls, omitted optional fields, signed zero, numeric boundaries, byte contents, string ordering, callback controls, and mixed-kind fallback. The same 30,740 local-oracle comparisons passed against the baseline and candidate.

Run the focused checks with:

GOTOOLCHAIN=go1.27.1 go test ./backends/sql -run '^TestNativeColumnFilters'
HFF_POSTGRES_CONTAINER=<container> GOTOOLCHAIN=go1.27.1 go test -tags=integration ./backends/sql -run '^TestPostgreSQLNativeColumnFiltersMatchLocal$'

The temporary shape generator and emitted SQL are not committed; the table and query list above describe the measured inputs. The committed integration test is the reproducible semantic oracle.

Results

The table reports generated WHERE bytes and OFFSET 0 barriers. These are SQL-shape measurements, not execution timings.

Workload Filter Baseline bytes / barriers Candidate bytes / barriers
Nullable existence i:exists 173 / 2 27 / 0
Same-kind Int comparison i=j 261 / 3 91 / 0
Same-kind String comparison s<t 285 / 3 115 / 0
Bytes length b:len>0 282 / 3 82 / 0
Duration nanoseconds d:nanoseconds=i 328 / 4 91 / 0
Int identity i:floor>1 260 / 3 60 / 0
Composed filters i:exists b:len>0 d:nanoseconds=i 820 / 9 212 / 0
Required existence i:exists 173 / 2 4 / 0
Direct null-test control i!=null 27 / 0 27 / 0
Mixed Int/Float control i=f 867 / 5 867 / 5
Callback control items:any(x=>i:exists) 679 / 6 679 / 6

Candidate plain EXPLAIN on the empty fixture showed the optimized filters as direct scan predicates without SubPlan or Nested Loop nodes; required exists() needed no filter. This plan observation is fixture-specific and does not measure execution or promise a plan for populated data.

Correctness and tradeoffs

Paired local/PostgreSQL comparisons passed for both revisions. Nullable columns retain explicit non-null checks, so applying prefix ! preserves the ordinary HFF truth rule for a false-on-null predicate. Required exists() is folded only when the schema guarantees a non-null value. Same-kind comparisons retain the database column's carrier; String comparisons explicitly use COLLATE "C" for HFF code-point ordering. bytea length uses octet_length; Duration nanoseconds already have an Int carrier. Int round(), ceil(), and floor() remain identity operations.

The candidate also preserves surrounding argument order. Guarded checks, structured-parent presence, JSON recovery, mixed numeric comparisons, and callback scopes retain the existing lowering because those cases require additional value or presence state.

Decision and limits

Use direct predicates for the eligible native-column forms above. The change reduces generated SQL and removes projection barriers in those cases, but no runtime speed, allocation, or general plan improvement was measured. Broader qualified-column projection elimination remains separate work.

Explore HFF
Search documentation

Search the language specification and guides.