import pg from "pg"; const { Client } = pg; if (!process.env.DATABASE_URL) { throw new Error( "DATABASE_URL must be set. Did you forget to provision a database?", ); } async function tableExists(client: pg.Client, table: string): Promise { const result = await client.query( `SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = $1`, [table], ); return (result.rowCount ?? 0) > 0; } async function main(): Promise { const client = new Client({ connectionString: process.env.DATABASE_URL }); await client.connect(); try { await client.query("BEGIN"); if (await tableExists(client, "app_permissions")) { const dupes = await client.query( `SELECT count(*)::int AS dup_groups, coalesce(sum(c - 1), 0)::int AS extra_rows FROM ( SELECT app_id, permission_id, count(*) AS c FROM app_permissions GROUP BY app_id, permission_id HAVING count(*) > 1 ) d`, ); const dupGroups = (dupes.rows[0]?.dup_groups ?? 0) as number; const extraRows = (dupes.rows[0]?.extra_rows ?? 0) as number; if (dupGroups > 0) { await client.query( `CREATE TEMP TABLE app_permissions_dedup ON COMMIT DROP AS SELECT DISTINCT app_id, permission_id FROM app_permissions`, ); await client.query(`DELETE FROM app_permissions`); await client.query( `INSERT INTO app_permissions (app_id, permission_id) SELECT app_id, permission_id FROM app_permissions_dedup`, ); console.log( `[pre-push] Removed ${extraRows} duplicate app_permissions row(s) across ${dupGroups} (app_id, permission_id) key(s).`, ); } else { console.log("[pre-push] app_permissions has no duplicates."); } } else { console.log("[pre-push] app_permissions table not present yet — skipping dedup."); } if ( (await tableExists(client, "executive_meeting_notifications")) && (await tableExists(client, "executive_meetings")) ) { const orphan = await client.query( `DELETE FROM executive_meeting_notifications n WHERE n.meeting_id IS NOT NULL AND NOT EXISTS ( SELECT 1 FROM executive_meetings m WHERE m.id = n.meeting_id )`, ); const removed = orphan.rowCount ?? 0; if (removed > 0) { console.log( `[pre-push] Removed ${removed} orphan executive_meeting_notifications row(s).`, ); } else { console.log("[pre-push] No orphan executive_meeting_notifications rows."); } } else { console.log( "[pre-push] executive_meeting_notifications/executive_meetings not both present — skipping orphan cleanup.", ); } await client.query("COMMIT"); } catch (err) { await client.query("ROLLBACK").catch(() => {}); throw err; } finally { await client.end(); } } main().catch((err) => { console.error("[pre-push] cleanup failed:", err); process.exit(1); });