Repository navigation
feat(postgres)!: return pgvector's vector and halfvec as number[] - #90
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Important
The widened catalog query in src/decode.ts breaks hand-defined queries that return any array of a created type, such as an enum's: they now come back as raw text instead of an array.
Reviewed changes
I reviewed the whole PR: pgvector's vector and halfvec become a new vector kind, typed number[], across decoding, parameter binding, structured rows, JSON Schema, Bun.sql and the catalog lookup for hand-defined queries.
- New
vectorkind —SQL_KINDSmapsvectorandhalfvecto it, and it is decoded withJSON.parse. MySQL'sVECTORis unaffected because the engine types it asType::Array(float4), not as a scalar namedvector. - Parameters —
encodewrites anumber[]as pgvector's text and refusesNaN, infinities, non-numbers and empty arrays. Avector[]stops recursing at the vector element, androws(…)members follow the same rule. - Schemas and rendering — a column is an array of
float, and inside a document the schema saysstring. - Hand-defined queries —
catalogSqlnow also returns each requested type's own row and its base type's name, so a barevectorcolumn is learned by name. - Docs and skill — the Postgres type and parameter tables, a new pgvector's vectors block, and the skill's types line.
claude-opus-5-5 | 𝕏
There was a problem hiding this comment.
✅ No new issues found. The two new commits fix all three points from the last review.
Reviewed changes
I reviewed cfa4bca and 20a3387, the commits pushed since the last Pullfrog review at 5fcece6.
- Catalog query for created types — Array types no longer match the branch that returns a type's own row, because it now requires
e.typcategory <> 'A'. Hand-defined queries returning an enum, domain orvector[]array decode as arrays again. - Guard in
learn()— Anarraysset keeps a type's own row from overwriting thek[]kind its element row set, whatever order the rows arrive in. The fake catalog intests/decode.test.tsnow sends an array type's own row after its element's row, which exercises this guard. - Vectors inside a document —
src/render.ts'sjsontable now mapsvectorandhalfvectostring, which matches the JSON Schema. A new assertion intests/render.test.tscovers it. - Comment wrap — The long line in the kind comment in
src/values.tsis now wrapped.
claude-opus-5-5 | 𝕏
20a3387 to
734d026
Compare
There was a problem hiding this comment.
✅ No new issues found. The rebase keeps the PR's behavior the same.
Reviewed changes
I reviewed the branch as rebased onto main (81f3a2f) at 734d026, against the last reviewed head, 20a3387.
- Dropped the
src/render.tschange —main(#85) already mapsvector,halfvecandsparsevectostringin thejsontable, so the PR no longer needs its own entry. The new assertion intests/render.test.tschecks that a vector inside a document is still typed asstring. - Kept the in-JSON case in
src/schema.ts— It still has to be there. Without it, a vector inside a document would fall through tokindSchema("vector")and be described as an array. - Resolved the skill's conflict — The types line in
skills/using-reylo/SKILL.mdkeeps its pgvector clause next to the.try()line from #88.
The render, decode, execute and rows tests pass locally (80 of 80).
claude-opus-5-5 | 𝕏
A vector column came back as its text, "[1,2.5,-3]". It is an array of its numbers now, as MySQL's VECTOR is, and a vector parameter, an array of vectors and a rows() member take one, refusing NaN, infinities and an empty vector before the query runs. A query defined by hand learns the type by its name too. Inside a JSON document a vector stays its text, which is what Postgres writes there; a sparsevec stays text.
734d026 to
5ed4063
Compare
There was a problem hiding this comment.
✅ No new issues found. The rebase merges this PR's
vectorkind with theintervalkind from #93 without changing what either one does.
Reviewed changes
I reviewed the branch as rebased onto main (5280fa9) at 5ed4063, against the last reviewed head, 734d026. The PR's own changes are the same as before. The only new work is resolving the conflicts with #93.
- Merged
ScalarKindand the kind comment — Insrc/values.ts,vectornow sits next tointerval. The comment describing each kind keeps both entries, andtimeis no longer listed as text after #93. - Merged
BUN_KINDS— The pattern now includes bothintervalandvector. The comment above it adds "a pgvector vector as its text" and keeps #93's interval clause. - Merged
encode— Insrc/execute.ts, the pgvector branch runs before the string path, and #93'sisTime→postgresYearandinterval→postgresIntervalhandling is unchanged after it.
Checks run locally:
tscreports no errors insrc/, so everyRecord<ScalarKind, …>table covers both new kinds.- The render, decode, execute, rows, values and JSON Schema tests pass, except two in
values.test.ts(interval and timestamptz). Those two fail only because plainnodehas no globalTemporal; they don't involve this PR.
claude-opus-5-5 | 𝕏

pgvector's
vectorandhalfveccome back from.run()asnumber[]instead of their text, and their parameters take one, the way MySQL'sVECTORalready works. Both Neon and Supabase ship pgvector; the other TypeScript tools that type these columns (the pgvector npm package, Drizzle, Prisma 8) usenumber[]too.vector(src/values.ts): typednumber[], decoded withJSON.parse, since pgvector prints the shortest text of each float ([1,2.5,1e-07]) and that round-trips exactly.vector[]isnumber[][].src/execute.ts): anumber[]is written as the text pgvector reads;NaN, infinities, a non-number and an empty array are refused before the query runs, as pgvector would refuse them. An array of vectors is an array literal of those texts. Arows(…)member takes the same (src/rows.ts). The text itself still passes.src/decode.ts).sparsevecstays its text ({1:1.5,3:2}/4), and ahalfvecreads back the half-precision float it stored (0.1is0.099975586).Checked live, through modules the loader built against Supabase's and Neon's Postgres 17 images: pg, postgres.js, PGlite with its pgvector, and Bun.sql return and bind the same values (columns,
vector[],<->with a parameter,= any(:qs), an insert, arows(…)insert, a hand-defined query). The unit tests cover the decoders on every driver's path, the binding and its refusals, the types and schemas; the driver matrix (2184 rows, 0 different), the loader's tests and the docs build pass.