Ensure pgvector extension in migrator + add pgvector diagnostic

The baseline schema needs the `vector` extension (memories.vision_embedding),
but nothing created it on deploy — a fresh DB hit "could not access file
vector" and migrations failed.

- migrate.ts now runs CREATE EXTENSION IF NOT EXISTS vector (superuser) before
  applying migrations, with a clear error if the Postgres image lacks pgvector.
- /api/debug-migration GET now reports pgvector status (binaryAvailable /
  installed) so the image/extension can be checked from the browser.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Manohar Gupta 2026-05-30 00:49:43 +05:30
parent deaa1810d7
commit 470df7fb9f
2 changed files with 37 additions and 1 deletions

View file

@ -13,7 +13,19 @@ export async function GET() {
const circleTables = await sql.unsafe(
`SELECT tablename FROM pg_tables WHERE schemaname = 'public' AND (tablename LIKE 'circle%' OR tablename = 'post_reports') ORDER BY tablename`
);
return NextResponse.json({ migrations, circleTables });
// pgvector status: is the binary available in this Postgres image, and is
// the extension actually created? (diagnoses "could not access file vector")
const vectorRows = await sql.unsafe(
`SELECT name, default_version, installed_version FROM pg_available_extensions WHERE name = 'vector'`
);
const v = vectorRows[0] as { default_version?: string; installed_version?: string } | undefined;
const pgvector = {
binaryAvailable: !!v, // false → wrong Postgres image (no pgvector)
installed: !!v?.installed_version, // false → needs CREATE EXTENSION vector
availableVersion: v?.default_version ?? null,
installedVersion: v?.installed_version ?? null,
};
return NextResponse.json({ migrations, circleTables, pgvector });
} catch (err: unknown) {
return NextResponse.json({ error: err instanceof Error ? err.message : String(err) });
}

View file

@ -29,6 +29,30 @@ async function main() {
const client = postgres(url, { max: 1 });
try {
// The baseline schema uses pgvector (memories.vision_embedding vector(1536)
// + ivfflat index), so the `vector` extension MUST exist before migrations
// run. We do it here — not in a migration file — because CREATE EXTENSION
// needs a superuser (this runner uses DATABASE_URL_SUPERUSER). Idempotent.
//
// This only succeeds if the Postgres image actually ships the pgvector
// binaries (e.g. the pgvector/pgvector image). A plain `postgres` image
// will fail here with "could not access file vector" — that's an infra
// problem (wrong DB image), and failing loud is correct.
console.log("[migrate] ensuring pgvector 'vector' extension...");
try {
await client`CREATE EXTENSION IF NOT EXISTS vector`;
console.log("[migrate] pgvector extension ready.");
} catch (extErr) {
console.error(
"[migrate] FAILED to create the pgvector 'vector' extension.\n" +
" → The Postgres image must include pgvector (use the pgvector/pgvector image,\n" +
" e.g. pgvector/pgvector:pg18 to match prod), and the migration connection\n" +
" (DATABASE_URL_SUPERUSER) must be a superuser.",
extErr
);
throw extErr;
}
console.log("[migrate] applying pending migrations...");
await migrate(drizzle(client), { migrationsFolder: "./drizzle" });
console.log("[migrate] done — schema is up to date.");