From 44bc53820f9d4c8a6e4477e38f30dbe61b1147be Mon Sep 17 00:00:00 2001 From: francy51 Date: Tue, 24 Feb 2026 15:44:09 -0500 Subject: [PATCH] Fix Postgres Better Auth migration index SQL --- lib/auth.ts | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 70 insertions(+), 2 deletions(-) diff --git a/lib/auth.ts b/lib/auth.ts index 18af457..259e7c2 100644 --- a/lib/auth.ts +++ b/lib/auth.ts @@ -21,6 +21,67 @@ function parseCsvList(value: string | undefined) { .filter((entry) => entry.length > 0); } +function isPostgresConnectionString(value: string | undefined) { + if (!value) { + return false; + } + + try { + const protocol = new URL(value).protocol.toLowerCase(); + return protocol === 'postgres:' || protocol === 'postgresql:'; + } catch { + return /^postgres(?:ql)?:\/\//i.test(value); + } +} + +function splitSqlStatements(sqlText: string) { + return sqlText + .split(';') + .map((statement) => statement.trim()) + .filter((statement) => statement.length > 0); +} + +function buildPostgresMigrationPlan(sqlText: string) { + const immediateStatements: string[] = []; + const deferredIndexStatements: string[] = []; + const addIndexPattern = /^alter table\s+([^\s]+)\s+add\s+index\s+([^\s]+)\s+\((.+)\)$/i; + + for (const rawStatement of splitSqlStatements(sqlText)) { + const statement = rawStatement.replace(/\s+/g, ' ').trim(); + const match = statement.match(addIndexPattern); + if (!match) { + immediateStatements.push(statement); + continue; + } + + const [, tableName, indexName, columns] = match; + deferredIndexStatements.push(`create index if not exists ${indexName} on ${tableName} (${columns})`); + } + + return [...immediateStatements, ...deferredIndexStatements]; +} + +async function runPostgresMigrations(pool: Pool, sqlText: string) { + const statements = buildPostgresMigrationPlan(sqlText); + if (statements.length === 0) { + return; + } + + const client = await pool.connect(); + try { + await client.query('BEGIN'); + for (const statement of statements) { + await client.query(statement); + } + await client.query('COMMIT'); + } catch (error) { + await client.query('ROLLBACK'); + throw error; + } finally { + client.release(); + } +} + function getPool() { const connectionString = process.env.DATABASE_URL?.trim(); if (!connectionString) { @@ -73,11 +134,18 @@ export function getAuth() { export async function ensureAuthSchema() { const auth = getAuth(); + const connectionString = process.env.DATABASE_URL?.trim(); if (!migrationPromise) { migrationPromise = (async () => { - const { runMigrations } = await getMigrations(auth.options); - await runMigrations(); + const migrations = await getMigrations(auth.options); + if (isPostgresConnectionString(connectionString)) { + const sql = await migrations.compileMigrations(); + await runPostgresMigrations(getPool(), sql); + return; + } + + await migrations.runMigrations(); })(); }