8000 feat(postgres)!: return pgvector's vector and halfvec as number[] by colinhacks · Pull Request #90 · colinhacks/reylo · GitHub
[go: up one dir, main page]

Skip to content

feat(postgres)!: return pgvector's vector and halfvec as number[] - #90

Merged
colinhacks merged 3 commits into
mainfrom
feat-pgvector-arrays
Oct 23, 2026
Merged

colinhacks merged 3 commits into
mainfrom
feat-pgvector-arrays

Conversation

@colinhacks
Copy link
Copy Markdown
Owner

pgvector's vector and halfvec come back from .run() as number[] instead of their text, and their parameters take one, the way MySQL's VECTOR already works. Both Neon and Supabase ship pgvector; the other TypeScript tools that type these columns (the pgvector npm package, Drizzle, Prisma 8) use number[] too.

  • A new kind, vector (src/values.ts): typed number[], decoded with JSON.parse, since pgvector prints the shortest text of each float ([1,2.5,1e-07]) and that round-trips exactly. vector[] is number[][].
  • Parameters (src/execute.ts): a number[] 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. A rows(…) member takes the same (src/rows.ts). The text itself still passes.
  • Bun.sql, which hands a vector over as text, converts it; a query defined by hand learns the type by its name from the catalog, which now also returns each created type's own row and its base type's name (src/decode.ts).
  • Inside a JSON document a vector stays a string, which is what Postgres writes there (pgvector has no cast to JSON). A sparsevec stays its text ({1:1.5,3:2}/4), and a halfvec reads back the half-precision float it stored (0.1 is 0.099975586).
  • The Postgres docs page's type and parameter tables and its Arrays section, and the skill, say so.

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, a rows(…) 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.

Copilot AI balanced review requested due to automatic review settings October 23, 2026 21:29
@vercel
vercel Bot commented Oct 23, 2026 •
Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
reylo Ready Ready Preview Oct 23, 2026 10:58pm UTC

Request Review

Copilot AI left a comment
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has insufficient remaining quota.

@pullfrog pullfrog Bot left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 vector kind — SQL_KINDS maps vector and halfvec to it, and it is decoded with JSON.parse. MySQL's VECTOR is unaffected because the engine types it as Type::Array(float4), not as a scalar named vector.
  • Parameters — encode writes a number[] as pgvector's text and refuses NaN, infinities, non-numbers and empty arrays. A vector[] stops recursing at the vector element, and rows(…) members follow the same rule.
  • Schemas and rendering — a column is an array of float, and inside a document the schema says string.
  • Hand-defined queries — catalogSql now also returns each requested type's own row and its base type's name, so a bare vector column 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.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using claude-opus-5-5 | 𝕏

Comment thread src/decode.ts Resolved
Comment thread src/schema.ts Resolved
Comment thread src/values.ts Resolved
@pullfrog pullfrog Bot left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ 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 or vector[] array decode as arrays again.
  • Guard in learn() — An arrays set keeps a type's own row from overwriting the k[] kind its element row set, whatever order the rows arrive in. The fake catalog in tests/decode.test.ts now sends an array type's own row after its element's row, which exercises this guard.
  • Vectors inside a document — src/render.ts's json table now maps vector and halfvec to string, which matches the JSON Schema. A new assertion in tests/render.test.ts covers it.
  • Comment wrap — The long line in the kind comment in src/values.ts is now wrapped.

Pullfrog  | View workflow run | Using claude-opus-5-5 | 𝕏

@colinhacks
colinhacks force-pushed the feat-pgvector-arrays branch from 20a3387 to 734d026 Compare October 23, 2026 22:13
@pullfrog pullfrog Bot left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ 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.ts change — main (#85) already maps vector, halfvec and sparsevec to string in the json table, so the PR no longer needs its own entry. The new assertion in tests/render.test.ts checks that a vector inside a document is still typed as string.
  • 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 to kindSchema("vector") and be described as an array.
  • Resolved the skill's conflict — The types line in skills/using-reylo/SKILL.md keeps its pgvector clause next to the .try() line from #88.

The render, decode, execute and rows tests pass locally (80 of 80).

Pullfrog  | View workflow run | Using 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.
@colinhacks
colinhacks force-pushed the feat-pgvector-arrays branch from 734d026 to 5ed4063 Compare October 23, 2026 22:57
@pullfrog pullfrog Bot left a comment
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ No new issues found. The rebase merges this PR's vector kind with the interval kind 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 ScalarKind and the kind comment — In src/values.ts, vector now sits next to interval. The comment describing each kind keeps both entries, and time is no longer listed as text after #93.
  • Merged BUN_KINDS — The pattern now includes both interval and vector. The comment above it adds "a pgvector vector as its text" and keeps #93's interval clause.
  • Merged encode — In src/execute.ts, the pgvector branch runs before the string path, and #93's isTime → postgresYear and interval → postgresInterval handling is unchanged after it.

Checks run locally:

  • tsc reports no errors in src/, so every Record<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 plain node has no global Temporal; they don't involve this PR.

Pullfrog  | View workflow run | Using claude-opus-5-5 | 𝕏

@colinhacks
colinhacks merged commit 3a14b0a into main Oct 23, 2026
6 checks passed
@colinhacks
colinhacks deleted the feat-pgvector-arrays branch October 23, 2026 23:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

0