diff --git a/apps/desktop/src/components/sidebar/ConnectionTree.vue b/apps/desktop/src/components/sidebar/ConnectionTree.vue index c19ede3d79..d73e401505 100644 --- a/apps/desktop/src/components/sidebar/ConnectionTree.vue +++ b/apps/desktop/src/components/sidebar/ConnectionTree.vue @@ -230,7 +230,15 @@ async function locateActiveTabInSidebar() { clearSearchScopeFilter(); } - const nodePath = findNodePathForActiveTab(tab, store.treeNodes); + let nodePath = findNodePathForActiveTab(tab, store.treeNodes); + if (!nodePath) { + // The first load may have served a stale schema cache whose async refresh + // replaced the database node before its tables finished loading, so the + // table isn't in the tree yet. Force a synchronous reload and retry once so + // locate reaches the table, not just the database (issue #715). + await ensureTreeLoadedForTab(tab, { force: true }); + nodePath = findNodePathForActiveTab(tab, store.treeNodes); + } if (!nodePath) return; for (const ancestor of nodePath) { @@ -256,23 +264,30 @@ async function locateActiveTabInSidebar() { await scrollToSidebarNode(match.id); } -async function ensureTreeLoadedForTab(tab: QueryTab) { +async function ensureTreeLoadedForTab(tab: QueryTab, opts?: { force?: boolean }) { const connId = tab.connectionId; if (!connId) return; const config = store.getConfig(connId); if (!config) return; + // When forcing, bypass the cached children check so we reload from the + // source. A stale schema cache otherwise serves children and triggers an + // async background refresh that can replace nodes mid-flight, leaving the + // tree without the target table by the time we search for it (issue #715). + const force = opts?.force ?? false; + const loadOptions = force ? { force: true } : undefined; + // Ensure databases are loaded under the connection const connNode = store.treeNodes.find((n) => n.id === connId); - if (connNode && (!connNode.children || connNode.children.length === 0)) { + if (connNode && (force || !connNode.children || connNode.children.length === 0)) { try { if (config.db_type === "redis") { await store.loadRedisDatabases(connId); } else if (config.db_type === "mongodb" || config.db_type === "elasticsearch") { await store.loadMongoDatabases(connId); } else { - await store.loadDatabases(connId); + await store.loadDatabases(connId, loadOptions); } } catch { return; @@ -283,23 +298,24 @@ async function ensureTreeLoadedForTab(tab: QueryTab) { // Find the database node const dbNode = findDatabaseNode(store.treeNodes, connId, tab.database); - if (!dbNode || (dbNode.children && dbNode.children.length > 0)) return; + if (!dbNode) return; + if (!force && dbNode.children && dbNode.children.length > 0) return; // Load database contents try { if (config.db_type === "sqlserver") { - await store.loadSqlServerDatabaseObjects(connId, tab.database); + await store.loadSqlServerDatabaseObjects(connId, tab.database, loadOptions); } else if (usesTreeSchemaMode(config.db_type) && !connectionUsesDatabaseObjectTreeMode(config)) { - await store.loadSchemas(connId, tab.database); + await store.loadSchemas(connId, tab.database, loadOptions); // If we have a schema, also load tables under that schema if (tab.schema) { const schemaNode = findSchemaNode(store.treeNodes, connId, tab.database, tab.schema); - if (schemaNode && (!schemaNode.children || schemaNode.children.length === 0)) { - await store.loadTables(connId, tab.database, tab.schema); + if (schemaNode && (force || !schemaNode.children || schemaNode.children.length === 0)) { + await store.loadTables(connId, tab.database, tab.schema, loadOptions); } } } else { - await store.loadTables(connId, tab.database); + await store.loadTables(connId, tab.database, undefined, loadOptions); } } catch { // Node just won't have children loaded