Skip to content

v0.75.0 — per-row binding, streaming TableReader, @table / @entry, schema reserved-name check

Choose a tag to compare

@trendvidia trendvidia released this 12 May 01:05
· 8 commits to main since this release
5cdc711

Per-row binding release. Implements protowire v0.74.0 on the Go port. First tagged release after v0.72.0 — collapses the un-tagged v0.73.0 and v0.74.0 CHANGELOG sections together with the v0.75.0 binding sugar into a single release that consumers can go get. Wire format unchanged across all three releases' worth of changes.

What's new since v0.72.0

v0.75.0 (this release's headline content)

  • pxf.TableReader.Scan(proto.Message) + pxf.BindRow helper — the per-row binding sugar that turns the streaming row API into a one-liner loop:
    tr, err := pxf.NewTableReader(r)
    for {
        msg := dynamicpb.NewMessage(desc)
        if err := tr.Scan(msg); errors.Is(err, io.EOF) { break }
        process(msg)
    }
    Same cell-state semantics as the materializing path: empty = absent (pxf.default applied, pxf.required errors), null = clear (wrappers / optional / oneof), value = set. WKT timestamps and durations, enum-by-name resolution, and proto3 wrappers all bind correctly because the implementation reuses the existing Unmarshal pipeline (rather than growing a parallel ~50-arm Value-to-FieldDescriptor switch that would drift). BindRow is also exported standalone for callers iterating Result.Tables()[i].Rows from the materializing path.

v0.74.0 (rolled in)

  • pxf.TableReader — streaming @table consumption. Reads rows one at a time from an io.Reader with working-set memory bounded by the size of the largest single row — not by the size of the row sequence. The shape consumers asked for the moment they saw the v0.73 materializing-only API. Multi-table documents chain via tr.Tail(). Per-row arity and v1 cell-grammar errors surface as the offending row is consumed (not deferred to end-of-input), per draft §3.4.4 "Streaming consumption". New API: NewTableReader, Type(), Columns(), Directives(), Tail(), Next(), plus ErrNoTable.

v0.73.0 (rolled in)

  • Schema reserved-name check. pxf.ValidateFile / pxf.ValidateDescriptor walk a protobuf FileDescriptor and report every message-field, oneof, or enum-value name that case-sensitively collides with null, true, or false — names that lex as PXF value keywords and produce silently-unreachable bindings. Runs by default at the top of every Unmarshal* call; UnmarshalOptions.SkipValidate opts out for pre-validated descriptors.
  • @entry directive + zero-or-more prefix list. named_directive accepts *( IDENT ) between @<name> and the optional { ... } block (was [ IDENT ]). The parser uses one-token lookahead to keep a body field key from being eaten as a directive prefix. pxf.Directive grows a Prefixes []string field; the legacy Type field is preserved (populated when len(Prefixes) == 1) so v0.72.0-era consumers (chameleon's @header reader) keep working unchanged.
  • @table directive. New top-level form for bulk-row datasets — the protowire-native CSV replacement:
    @table <type> ( col1, col2, ... )
    ( val1, val2, ... )
    ( val1, val2, ... )
    
    Three cell states (empty / null / value) map onto the existing pxf.required / pxf.default semantics with zero new annotations. v1 restrictions: scalar cells only (no [...]/{...}), unqualified column names only, strict row arity, standalone document (no @type, no top-level body entries). Tables flow through UnmarshalFull via Result.Tables(). Per-row binding via TableReader.Scan (new in this release) closes the loop.

Backward compatibility

  • Schema reserved-name check rejects schemas declaring enum Choice { null = 0; } and similar. These bindings were already silently broken; rejecting them at decode time surfaces a pre-existing bug rather than introducing one. Callers that need to keep accepting them can set UnmarshalOptions.SkipValidate = true (not recommended).
  • Directive.Type semantics shift slightly: today it's "the optional second identifier after @<name>"; now it's "the single identifier, if there was exactly one." Practical impact is zero — v0.72.0 only emitted directives with zero-or-one identifier, and the back-compat rule preserves that case. Two-prefix directives (new in @entry) leave Type empty by design; new code reads Prefixes directly.
  • Wire format unchanged across all three rolled-in releases.

Conformance

Ports that don't already have these features must:

  1. Add a descriptor-bind reserved-name check (~30 LoC).
  2. Relax named_directive to accept zero-or-more prefix identifiers.
  3. Implement @table (new lexer keyword, new parser path, consumer-API surface).
  4. Optionally expose a streaming row API (MAY per draft §3.4.4 — not required for spec conformance, but recommended for the CSV-replacement workload).

See CHANGELOG.md for the per-version-section breakdown.