Skip to content

Releases: apstndb/spanenc

v0.3.2

Choose a tag to compare

@apstndb apstndb released this 11 Jun 15:53
9b578f8

Final feature release of spanenc. Development moved to spancodec, which merges spanenc and spandec into a single codec module (see its v0.1.0 release and migration table). spanenc v0.3.x remains usable but frozen; the module carries a Deprecated: notice as of this release.

Added since v0.3.1

  • RowEncoder.MustResultSetMetadata (#3): ResultSetMetadata panicking on error, for package-level wiring next to MustNewRowEncoder — row-type resolution is deterministic per T and column mask, and Must construction alone does not guarantee an inferable row type. Surveyed from apstndb/spanner-mycli#661.
  • Custom value encoders (#5 phase 1): WithValueEncoder[T] registers a per-call encoder for Go types outside the client's coverage (uint32 and other integer width variants, time.Duration, external types that cannot implement spanner.Encoder), consulted before the client mirror; the new ErrFallthrough defers to the built-in encoding. Registrations apply per element of []T; WithGoType[T] supplies the ARRAY element type for nil/empty slices and for ValuesFromSlice/ArrayValueFromSlice static inference. Exact-type matching only; no package-global registry. Hazard (documented and pinned by test): registering time.Time bypasses the spanner.CommitTimestamp sentinel.

#5 phase 2 (construction-time registrations informing RowEncoder.RowType, options on TypeFor/RowTypeFor) ships in spancodec v0.1.0, not here.

Requirements

  • github.com/apstndb/spanvalue v0.7.3+.
  • cloud.google.com/go/spanner: v1.84.1 MVS floor; mirrored semantics track v1.91.0.

v0.3.1

Choose a tag to compare

@apstndb apstndb released this 11 Jun 04:50
8d2c5ed

Patch release since v0.3.0: additive RowEncoder APIs and internal adoption of spanvalue gcvctor constructors. No breaking changes. Mirrored semantics still track cloud.google.com/go/spanner v1.91.0.

Added

  • RowEncoder.Row(v, encodeOpts...) / RowEncoder.Rows(items, encodeOpts...) (#2): encode structs into real *spanner.Row values so client-side (virtual) result sets flow through the same *spanner.Row pipelines as server query results. Row is Values + spanner.NewRow (the client deep-clones the GCVs without re-encoding, so rows carry exactly the encoder's output including typed NULLs and do not alias encoder state). Rows returns a lazy iter.Seq2[*spanner.Row, error] — each row is encoded only when yielded (pinned by test), and an encode failure yields (nil, err) once and stops. Pairs with writer.RunRowSeq / writer.WriteRowSeq introduced in spanvalue v0.7.4, whose abort-on-yielded-error contract matches by design.
  • MustNewRowEncoder[T]: NewRowEncoder panicking on error, for package-level encoders of compile-time-known struct types — prefer it over local panic-on-error wrappers (surveyed from the spanner-mycli PoC, apstndb/spanner-mycli#657).

Changed (no behavior change)

  • Hand-rolled nullable encoding helpers were replaced with the gcvctor constructors added for spanenc in spanvalue v0.7.2–v0.7.3 (apstndb/spanvalue#232, apstndb/spanvalue#236): IntervalFromNullable, NumericFromNullable (behind the existing NUMERIC validation), PGNumericFromNullable, JSONFromNullable, PGJSONBFromNullable. Client-mirror semantics are unchanged and pinned by tests, including the quoted-JSON-string wire for string-valued NullJSON / PGJsonB (fixed on the spanvalue side in v0.7.3; spanenc deliberately skipped the v0.7.2 helpers whose string special-case diverged from the client).

Documentation

  • Adoption guide: the string-only-row exception now spells out when routing display strings through GCVs is worth it (a single cell pipeline shared with server results, or scaffolding call sites that will gain typed columns) and asks adopters to record that intent in a comment; NULL cells should be detected with spanvalue.IsNull rather than inspecting the protobuf value kind by hand.

Requirements

  • github.com/apstndb/spanvalue v0.7.3+ (gcvctor JSONFromNullable / PGJSONBFromNullable with client-compatible marshal semantics). Streaming RowEncoder.Rows output through writer.WriteRowSeq additionally needs spanvalue v0.7.4+.
  • github.com/apstndb/structfields v0.1.0 / structfields/spannertag v0.1.0 (unchanged).
  • cloud.google.com/go/spanner: go.mod declares only the v1.84.1 MVS floor (mirrored semantics track v1.91.0); your module controls the actual client version.

Full changelog

v0.3.0...v0.3.1

v0.3.0

Choose a tag to compare

@apstndb apstndb released this 10 Jun 19:42
3afd801

Stable release consolidating v0.3.0-alpha.1 and v0.3.0-alpha.2 plus documentation work. Mirrored semantics track cloud.google.com/go/spanner v1.91.0.

Added since v0.2.0

  • Column masks for the plain-Go-value helpers (ColumnMaskOption): WithColumns(...) (include) / WithoutColumns(...) (exclude) on MutationColumnsAndValues, MutationMap, and ParamsMap. Masked output keeps struct declaration order; unknown columns, read-only columns in a write-shaped include list, or combining both kinds return the new ErrInvalidColumnMask.
  • WithLossOfPrecisionHandling(spanner.NumericError | spanner.NumericRound) (EncodeOption): per-call NUMERIC loss-of-precision control reusing the client's enum. The client's package-global is never read; the default here is spanner.NumericError (validate → ErrNumericOutOfRange), while the client's global defaults to NumericRound.
  • ParamsMap: struct → map[string]any for spanner.Statement Params (no GCV conversion; read-only fields included, since they are ordinary bindable values).
  • ResultSetMetadataFor[T] / ResultSetMetadataFromGoType: RowTypeFor wrapped in *sppb.ResultSetMetadata for writer metadata and client-side virtual result sets.
  • RowEncoder[T] (NewRowEncoder): compiled row codec — field listing, column mask, and row type resolved once; per row only Values(v, encodeOpts...) runs. Columns() / RowType() / ResultSetMetadata() are mask-aware; agreement with StructColumnsAndValues is pinned by tests.

Documentation

  • Package-doc Adoption guide (readable from go doc / pkg.go.dev): when GCV-based rows pay off vs plain string rows; Columns() vs ResultSetMetadata(); formatting stability across spanvalue upgrades; cross-links to spanvalue.FormatRowColumns so adopters need no per-app GCV→string bridge. Driven by spanner-mycli PoC feedback (#1).
  • ExampleStructColumns_tagOptions pins the tag-option quirk (row-shaped helpers parse ; options; STRUCT-typed values keep the client's raw-tag behavior).
  • Corrected an earlier doc claim: the client's global loss-of-precision default is NumericRound, not NumericError.

Design notes

Two decisions from review are deliberate API shape: option types stay split (EncodeOption vs ColumnMaskOption) so applicability is fixed at compile time, and ParamsMap / MutationMap remain separate functions because read-only handling is determined by the destination (Params vs mutations).

Requirements

  • github.com/apstndb/spanvalue v0.7.1+ (gcvctor UTC-timestamp wire format). Formatting of GCVs is owned by spanvalue, so upgrading it alongside spanenc can change rendered output (for example FLOAT64 display); review those golden-test diffs separately from the spanenc adoption.
  • github.com/apstndb/structfields v0.1.0 / structfields/spannertag v0.1.0.
  • cloud.google.com/go/spanner: go.mod declares only the v1.84.1 MVS floor (mirrored semantics track v1.91.0); your module controls the actual client version.

v0.3.0-alpha.2

v0.3.0-alpha.2 Pre-release
Pre-release

Choose a tag to compare

@apstndb apstndb released this 10 Jun 17:47
b4f11c5

Pre-release adding result-set-shaped helpers surveyed from spanner-mycli / spannersh use cases (client-side virtual result sets such as SHOW-style outputs and status rows). Mirrored semantics still track cloud.google.com/go/spanner v1.91.0; go.mod stays at the v1.84.1 MVS floor.

Added

  • ResultSetMetadataFor[T]() / ResultSetMetadataFromGoType(reflect.Type) — wraps RowTypeFor into *sppb.ResultSetMetadata, the shape expected by result-set consumers such as spanvalue/writer's WithMetadata.
  • RowEncoder[T] (NewRowEncoder[T](maskOpts ...ColumnMaskOption)) — compiled row codec for streaming many rows of one struct type: field listing, column mask validation, and row type are resolved once at construction instead of per row.
    • Columns() []string, RowType() (*sppb.StructType, error), ResultSetMetadata() (*sppb.ResultSetMetadata, error) (all mask-aware), Values(v T, opts ...EncodeOption) ([]spanner.GenericColumnValue, error).
    • Read-shaped like StructColumnsAndValues (read-only fields included; an include mask may name them); a test pins the agreement between RowEncoder and StructColumnsAndValues.
    • T may be a struct or pointer-to-struct type; a nil pointer row returns ErrNilStructPointer.
    • Includes a writer integration test streaming a SHOW VARIABLES-style virtual result set to CSV.

Notes

Everything from v0.3.0-alpha.1 (column masks, WithLossOfPrecisionHandling, ParamsMap) is included. Versioning policy (now recorded in AGENTS.md): the module stays on v0; breaking changes bump the minor version, everything else the patch version.

Requirements

  • github.com/apstndb/spanvalue v0.7.1+ (gcvctor UTC-timestamp wire format). Formatting of GCVs is owned by spanvalue, so upgrading it alongside spanenc can change rendered output (for example FLOAT64 display); review those golden-test diffs separately from the spanenc adoption.
  • github.com/apstndb/structfields v0.1.0 / structfields/spannertag v0.1.0.
  • cloud.google.com/go/spanner: go.mod declares only the v1.84.1 MVS floor (mirrored semantics track v1.91.0); your module controls the actual client version.

(Requirements section added 2026-06-11.)

v0.3.0-alpha.1

v0.3.0-alpha.1 Pre-release
Pre-release

Choose a tag to compare

@apstndb apstndb released this 10 Jun 17:06
248a70a

Pre-release adding column masks, per-call NUMERIC loss-of-precision control, and Statement.Params extraction. Mirrored semantics still track cloud.google.com/go/spanner v1.91.0; go.mod stays at the v1.84.1 MVS floor.

Added

  • Column masks for the plain-Go-value helpers, written as either an include list or an exclude list (update-mask style):
    • WithColumns(columns ...string) — include mask; output keeps struct declaration order regardless of argument order.
    • WithoutColumns(columns ...string) — exclude mask.
    • Apply to MutationColumnsAndValues, MutationMap, and ParamsMap via the new ColumnMaskOption type. Strict validation: unknown columns, read-only columns in a write-shaped include list, or combining include with exclude return the new sentinel ErrInvalidColumnMask. Excluding a read-only column is a no-op.
  • WithLossOfPrecisionHandling(spanner.NumericError | spanner.NumericRound) (EncodeOption, accepted by ValueOf, StructColumnsAndValues, ValuesFromSlice, ArrayValueFromSlice): per-call NUMERIC loss-of-precision control reusing the client's enum vocabulary. The client's package-global spanner.LossOfPrecisionHandling is never read; the per-call default is spanner.NumericError (validate, returning ErrNumericOutOfRange), while the client's global defaults to NumericRound — pass it explicitly for silent rounding. Like the client, rounding affects only the fractional part.
  • ParamsMap(v any, opts ...ColumnMaskOption) (map[string]any, error): struct → column-name-to-Go-value map for binding as spanner.Statement Params, with no GCV conversion (the client encodes at execution). Unlike the write-shaped MutationMap, read-only fields are included (they are ordinary bindable values) and an include mask may name them.

Changed

  • Docs: corrected an inaccurate claim that the client's default loss-of-precision handling is NumericError; the client's global actually defaults to NumericRound (silent rounding). spanenc's per-call default deliberately stays on validation.

Design notes

Two deliberate API decisions from review: option types stay split (EncodeOption vs ColumnMaskOption) so option applicability is fixed at compile time, and ParamsMap / MutationMap stay separate functions — the read-only handling is determined by the destination (Params vs mutations), so it is encoded in the function name rather than an option.

v0.2.0

Choose a tag to compare

@apstndb apstndb released this 10 Jun 16:08
de28363

Tracks the read-only struct tag feature from cloud.google.com/go/spanner and restructures the tag parser. Mirrored semantics now track spanner v1.91.0; the go.mod requirement intentionally stays at the v1.84.1 MVS floor (see "Dependency policy" below).

Added

  • Read-only column tags (spanner:"->", spanner:"Name;readonly", spanner:"Name;->"; case-insensitive readonly), mirroring spanner v1.86.0 (googleapis/google-cloud-go#12895):
    • Tags now split on ; with the column name first (a tag like spanner:"Name;readonly" previously yielded the literal column name Name;readonly).
    • Read-shaped listings (StructColumns, RowTypeFor, StructColumnsAndValues) include read-only fields, because they are readable via ToStruct.
    • Write-shaped helpers (MutationColumnsAndValues, MutationMap) exclude them, mirroring structToMutationParams.
    • STRUCT-typed values (ValueOf on a struct, TypeFor) keep the client's raw-tag quirk: tag options leak verbatim into STRUCT field names.
  • CI: latest-deps job tests against cloud.google.com/go/spanner@latest to catch upstream drift early.

Changed

  • The spanner tag parser is no longer local: it comes from the new github.com/apstndb/structfields/spannertag nested module (Apache-2.0 port of the client's unexported spannerTag / spannerTagParser / fieldCache), preserving the invariant that upstream-derived code never lives in this MIT module.
  • Dependencies are now all tagged releases: spanvalue v0.7.1 (gcvctor UTC-timestamp wire format; replaces the v0.1.x pseudo-version pin), structfields v0.1.0, structfields/spannertag v0.1.0.

Dependency policy

The mirrored-semantics version (v1.91.0) is independent of the go.mod requirement: go.mod declares only the minimum client version whose APIs this module uses (currently the v1.84.1 floor inherited from spanvalue), so downstream modules keep control of the spanner client version under MVS.

v0.1.1

Choose a tag to compare

@apstndb apstndb released this 10 Jun 14:55
dc55c32

Documentation-only release so pkg.go.dev shows the corrected README.

Changed

  • README: link github.com/apstndb/spanvalue/writer to pkg.go.dev with a short description (CSV/TSV/JSONL/SQL INSERT exporters for GCV rows) in the struct-streaming example.
  • README: trim the license section to "MIT" (the Apache-2.0 fork of upstream code lives separately in structfields).

No code changes; v0.1.0 remains the initial implementation. v0.1.0 was already cached by proxy.golang.org, so it was not re-tagged (re-tagging a published version causes permanent checksum mismatches).

v0.1.0

Choose a tag to compare

@apstndb apstndb released this 10 Jun 14:42
16e3ad7

Initial release. Experimental: the API may change while encodeValue parity is proven against client library releases. Mirrored behavior tracks cloud.google.com/go/spanner v1.84.1.

Added

  • ValueOf(any) (spanner.GenericColumnValue, error) — Go value → GCV mirroring the client's internal encodeValue: all concrete parameter/mutation types (scalars, spanner.Null* wrappers including sql.NullString and uuid.NullUUID, pointers and slices with typed-NULL rules), spanner.Encoder, protobuf messages and enums (including typed-nil → typed NULL), named variants of base types (getDecodableSpannerType behavior), Go structs and struct slices as STRUCT values, spanner.CommitTimestamp, and GenericColumnValue passthrough (deep clone).
  • TypeFor[T]() / TypeFromGoType(reflect.Type) — static Go type → *sppb.Type inference; ErrTypeNotInferable for value-dependent types (spanner.Encoder, GenericColumnValue, NullProtoMessage/NullProtoEnum, interfaces).
  • StructColumns[T]() / StructColumnsFromGoTypespanner-tag column names with the client's mutation/ToStruct field listing (embedded flattening, shadowing, spanner:"-"), the helper requested in googleapis/google-cloud-go#13800.
  • RowTypeFor[T]() / RowTypeFromGoType — row-shaped *sppb.StructType for writer metadata.
  • StructColumnsAndValues(any) — struct → column names + GCVs for spanvalue/writer.
  • MutationColumnsAndValues(any) / MutationMap(any) — struct → plain Go cols/vals or map for the non-Struct mutation constructors (spanner.Update, spanner.UpdateMap, ...), enabling column masking by name.
  • ValuesFromSlice[T] / ArrayValueFromSlice[T] — homogeneous slice → (element *sppb.Type, []*structpb.Value) or ARRAY GCV; interface element types rejected.
  • Sentinel errors: ErrUnsupportedType, ErrUntypedNil, ErrNotStruct, ErrNilStructPointer, ErrEmbeddedStructField, ErrTypeNotInferable, ErrInvalidSource, ErrNumericOutOfRange.

Semantics

Deliberate strictness divergences from the client are documented in the package documentation: untyped nil and nil struct pointers return errors, GCV inputs with nil Type are rejected, NUMERIC is always validated, and non-finite floats / JSON use gcvctor's canonical wire forms.

Dependencies

  • github.com/apstndb/structfields v0.1.0 — Apache-2.0 exported fork of cloud.google.com/go/internal/fields; all upstream-derived code lives there, keeping spanenc MIT.
  • github.com/apstndb/spanvalue is pinned to a pseudo-version of main (v0.7.1-0.20260610130420) because v0.7.0 predates gcvctor's UTC-timestamp fix; it will move to the next tagged release.