From 2cdbadec8cdc2bde34d7910e8b6ed49c2eef7ec5 Mon Sep 17 00:00:00 2001 From: mawkone Date: Sun, 14 Jun 2026 14:36:19 -0700 Subject: [PATCH] fix(databases): execute exact row count fallback for empty tables and tighten table spacing --- .../project/database-table-tree.tsx | 6 +-- vibn-frontend/lib/db-introspect.ts | 46 +++++++++++++++++++ 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/vibn-frontend/components/project/database-table-tree.tsx b/vibn-frontend/components/project/database-table-tree.tsx index 245fb03..28afc57 100644 --- a/vibn-frontend/components/project/database-table-tree.tsx +++ b/vibn-frontend/components/project/database-table-tree.tsx @@ -232,7 +232,7 @@ function formatCount(n: number) { const treeWrap: React.CSSProperties = { display: "flex", flexDirection: "column", - gap: 6, + gap: 0, }; const list: React.CSSProperties = { listStyle: "none", @@ -240,14 +240,14 @@ const list: React.CSSProperties = { padding: 0, display: "flex", flexDirection: "column", - gap: 1, + gap: 0, }; const row: React.CSSProperties = { display: "flex", alignItems: "center", gap: 8, width: "100%", - padding: "6px 8px 6px 6px", + padding: "4px 8px 4px 6px", border: "1px solid transparent", borderRadius: THEME.radiusSm, cursor: "pointer", diff --git a/vibn-frontend/lib/db-introspect.ts b/vibn-frontend/lib/db-introspect.ts index ca42f27..8af87df 100644 --- a/vibn-frontend/lib/db-introspect.ts +++ b/vibn-frontend/lib/db-introspect.ts @@ -124,6 +124,52 @@ async function pgListTables(container: string): Promise { }); if (all.length >= MAX_TABLES) break; } + + // ── Exact-count fallback for unanalyzed tables ── + // If Postgres reports 0 rows (usually because the table is new and hasn't + // been analyzed yet), we run a fast exact COUNT(*) just for those tables + // so the UI doesn't falsely report '0' when there is real data. + const suspectTables = all.filter( + (t) => + t.approxRows === 0 && (multi ? t.schema.startsWith(`${db}.`) : true), + ); + + if (suspectTables.length > 0) { + const unionQueries = suspectTables.map((t) => { + const rawSchema = multi ? t.schema.split(".")[1] : t.schema; + return `SELECT '${rawSchema}|${t.name}|' || count(*) FROM "${rawSchema}"."${t.name}"`; + }); + + const exactQuery = unionQueries.join(" UNION ALL "); + const exactCmd = + `docker exec ${sq(container)} bash -c ` + + sq( + `psql -U "$POSTGRES_USER" -d ${sqInner(db)} -tAF '|' -c ${sqInner(exactQuery)}`, + ); + + try { + const exactRes = await runOnCoolifyHost(exactCmd, { + timeoutMs: SSH_TIMEOUT_MS, + }); + if (exactRes.code === 0) { + const exactMap = new Map(); + for (const line of exactRes.stdout.split("\n")) { + const [sch, nm, cnt] = line.trim().split("|"); + if (sch && nm && cnt) exactMap.set(`${sch}.${nm}`, Number(cnt)); + } + for (const t of suspectTables) { + const rawSchema = multi ? t.schema.split(".")[1] : t.schema; + const exactCount = exactMap.get(`${rawSchema}.${t.name}`); + if (exactCount !== undefined) { + t.approxRows = exactCount; + } + } + } + } catch (e) { + // Fall back to 0 if the exact count fails for any reason + console.warn("[db-introspect] Failed to fetch exact counts:", e); + } + } } return all.slice(0, MAX_TABLES); }