codegen: adopt go-optional (Option[T]) across the generated surface, add :maybe-one - #21
Merged
Conversation
Generated code now represents absence with moznion/go-optional's Option[T] instead of bare pointers, uniformly and with no opt-out: - @if-present parameters become optional.Option[T] fields (None omits the guarded fragments; presence checks compile to IsSome()). - Nullable result columns become optional.Option[T] row fields. - New :maybe-one annotation: :one with "no row" as a normal outcome — returns (optional.Option[Row], error) and maps pgx.ErrNoRows / sql.ErrNoRows to (None, nil). The driver boundary is deliberately unchanged: optional parameters bind via UnwrapAsPtr() (the same *T the driver saw before), and nullable columns scan through *T temporaries converted with optional.FromNillable — nothing rides on Option's sql.Scanner, whose pgx path detours through a lossy driver.Value conversion. The generated Ptr helper is gone; callers write optional.Some(v). Examples regenerated for all three dialects with a FindUserByEmail :maybe-one showcase; the devdb E2E suites cover Some/None binding, NULL-heavy scans into Option fields, and the maybe-one hit/miss paths on postgres, mysql, and sqlite. Design doc 17 records the decision and the deliberate non-goals (config tri-state, internal comma-ok APIs, LSP). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P7UbMMpmzxDECU9gqxAAZ3
The examples' main packages import it directly; go mod tidy moves it out of the indirect block. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P7UbMMpmzxDECU9gqxAAZ3
moznion
added a commit
that referenced
this pull request
Aug 9, 2026
Routine currency bumps flagged by the dependency audit: - google.golang.org/protobuf v1.34.2 -> v1.36.11 (pg_query parse-tree decoding; ~2 years behind) - testcontainers-go (+ mysql/postgres modules) v0.43.0 -> v0.44.0 - ncruces/go-sqlite3 v0.35.2 -> v0.35.3 - transitively: golang.org/x/sys v0.47.0, x/text v0.40.0 Rebuilt on top of the merged #21/#23 main. Verified with the full devdb E2E suite (real postgres/mysql containers under the new testcontainers, in-process sqlite on the new wasm build) plus the unit suites; the corpus byte-identity gates pin that the protobuf bump changes no oracle/catalog bytes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P7UbMMpmzxDECU9gqxAAZ3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Generated code now represents absence with moznion/go-optional's
Option[T]instead of bare pointers — uniformly, with no pointer fallback and no config knob:@if-presentparameters →optional.Option[T]fields.None(the zero value) omits the guarded fragments; presence checks compile toarg.X.IsSome().optional.Option[T]row fields.:maybe-oneannotation —:onewith "no row" as a normal outcome: returns(optional.Option[Row], error)and mapspgx.ErrNoRows/sql.ErrNoRowsto(None, nil).Ptr[T]helper is removed; call sites writeoptional.Some(v).The driver boundary is unchanged, by construction
UnwrapAsPtr()— the driver sees exactly the*Tit saw before;runtime.BuildArgs/ResolveArgsuntouched.*Ttemporaries and convert withoptional.FromNillableafterwards.Nothing rides on go-optional's
sql.Scanner/driver.Valuer: pgx routes unknown Scanners through a lossydriver.Valuedetour (numericet al.), andOption[T]'s underlying[]Tshape is ambiguous to pgx's reflection-based plan selection.docs/design/17-go-optional-adoption.mdrecords the decision; a future pgx-native codec (go-optional/pgxoption, separate PR) can enable direct scanning as an optimization.Not touched, deliberately
internal/config's tri-state*bool(yaml.v3 has no decode-side TextUnmarshaler), internal comma-ok APIs,runtime/'s public types, and the stdlib-only LSP.Verification
IsSome/UnwrapAsPtremission,*T-temporary scan path,:maybe-onefor both placeholder styles (pgx and database/sql flavors).go test -tags devdb ./internal/e2e/fully green (postgres + mysql + sqlite containers): NULL-heavy scans into Option fields, Some/None bind shapes, maybe-one hit (SomewithNonecolumn) and miss (None, no error) on all three dialects; cold→warm CLI round-trips; native-oracle corpus byte-identity gates.golangci-lint run --build-tags devdb ./...and standalonestaticcheck -checks all: 0 issues;goimportsclean (generated import blocks are goimports-stable).FindUserByEmail :maybe-oneshowcase; spec, manual (getting-started / template-language / runtime / sqlc-migration), README, and CLAUDE.md updated.🤖 Generated with Claude Code
https://claude.ai/code/session_01P7UbMMpmzxDECU9gqxAAZ3