fix(xpq): render an empty BitsValue as NULL, not B'' - #164
Open
loganintech wants to merge 1 commit into
Open
Conversation
BitsValue.Value() hardcoded Valid: true, so pgtype.Bits.Value() could never
take its `if !src.Valid { return nil, nil }` path. An empty input therefore
encoded to the empty string and reached Postgres as a zero-length bit.
That is only reachable through the generated Distance() helper, which calls
BytesToBitVector unconditionally with no length check. Against a sized bit
column, a zero-length operand makes pgvector's distance operators raise
"different bit lengths N and 0" (SQLSTATE 22000). Record() is unaffected --
it only calls BytesToBitVector when the value is exactly ExpectedBytesLen.
Emitting NULL instead lets the comparison propagate NULL, which the
generated COALESCE around the distance expression already absorbs.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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
BitsValue.Value()hardcodedValid: true, sopgtype.Bits.Value()could never reach itsif !src.Valid { return nil, nil }early return. An empty input encoded to the empty string and arrived at Postgres as a zero-length bit.Why it matters
BytesToBitVectorhas three callers:proto_format_bits.tmpl— guarded, only invoked when the value is exactlyExpectedBytesLen. Unaffected.query_builder.tmpl:146and:364— the generatedDistance()helper, which calls it unconditionally with no length check.So a nil or empty
[]bytepassed toDistance()renders asB''. Against a sizedbit(N)column, pgvector's distance operators reject a zero-length operand:Emitting
NULLinstead lets the comparison propagateNULL, which theCOALESCEthe generated query already wraps around the distance expression absorbs correctly.Found while investigating a ConductorOne sandbox incident. That incident turned out to have a different root cause (a malformed entry in an HNSW index aborting autovacuum), but this is a real, independently reachable bug on the read path with the same signature.
Why here and not in the template
This is a runtime fix, so a version bump repairs every already-generated
.pb.pgdb.gowithout regeneration. A guard inquery_builder.tmplwould only take effect after every consumer regenerates.Test
pgdb/v1/xpq/bits_value_test.gocovering nil, zero-length, and populated inputs, plus a case pinning that an empty operand does not render asB''.go build ./...,gofmtclean,go test ./pgdb/v1/xpq/passes.internal/pgtestandpgdb/v1(TestSearchPathsFull) — bothdid not find PostgreSQL executables installed; verified they fail identically onmain.🤖 Generated with Claude Code