From e226a5670ecaf79ed0a683715e64f4b38f2a3cab Mon Sep 17 00:00:00 2001 From: vrustx <279631638@qq.com> Date: Thu, 16 Jul 2026 15:20:03 +0800 Subject: [PATCH] fix(navigator): preserve table identity when opening objects --- .../src/composables/useNavigationTargets.ts | 12 ++++++++ .../desktop/src/lib/table/tableDataTabMeta.ts | 15 +++++++++- packages/app-tests/tableDataTabMeta.test.ts | 29 +++++++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/apps/desktop/src/composables/useNavigationTargets.ts b/apps/desktop/src/composables/useNavigationTargets.ts index d48f3134b..c16b8018f 100644 --- a/apps/desktop/src/composables/useNavigationTargets.ts +++ b/apps/desktop/src/composables/useNavigationTargets.ts @@ -50,6 +50,18 @@ async function openTableTarget(target: NavigationTarget, options: { tableInfoTab })(); const targetTab = queryStore.tabs.find((tab) => tab.id === tabId); if (targetTab) targetTab.tableInfoTab = options.tableInfoTab; + // Stamp the new table identity synchronously so SQL rebuilds (refresh, + // filters, row count) never read a stale tableMeta from a reused tab or + // fall back to parsing the schema-qualified tab title (issue #3613). + queryStore.setTableMeta(tabId, { + schema: target.schema, + catalog: target.catalog, + database: target.database, + tableName: target.tableName, + tableType: target.tableType ?? "TABLE", + columns: [], + primaryKeys: [], + }); queryStore.setExecuting(tabId, true); try { diff --git a/apps/desktop/src/lib/table/tableDataTabMeta.ts b/apps/desktop/src/lib/table/tableDataTabMeta.ts index df0d47563..00d92dbc9 100644 --- a/apps/desktop/src/lib/table/tableDataTabMeta.ts +++ b/apps/desktop/src/lib/table/tableDataTabMeta.ts @@ -2,6 +2,19 @@ import type { QueryTab, ColumnInfo } from "@/types/database"; export type DataTabTableMeta = NonNullable; +// Data tabs opened from the object browser are titled ".". +// When the tab has no usable tableMeta yet, strip the schema prefix so SQL +// rebuilt from this fallback does not qualify the table twice +// (e.g. [dbo].[dbo.users] on SQL Server — see issue #3613). +function titleTableName(tab: QueryTab): string { + const title = tab.title.trim(); + const schema = tab.schema?.trim(); + if (schema && title.length > schema.length + 1 && title.startsWith(`${schema}.`)) { + return title.slice(schema.length + 1); + } + return title; +} + function fallbackColumnInfo(name: string): ColumnInfo { return { name, @@ -16,7 +29,7 @@ function fallbackColumnInfo(name: string): ColumnInfo { export function tableMetaForDataTab(tab: QueryTab | undefined): DataTabTableMeta | undefined { if (!tab || tab.mode !== "data") return undefined; if (tab.tableMeta?.columns.length) return tab.tableMeta; - const tableName = tab.title.trim(); + const tableName = tab.tableMeta?.tableName.trim() || titleTableName(tab); if (!tableName) return undefined; // Keep filters usable when the table identity loaded but its column metadata did not. diff --git a/packages/app-tests/tableDataTabMeta.test.ts b/packages/app-tests/tableDataTabMeta.test.ts index 704160377..305527f1a 100644 --- a/packages/app-tests/tableDataTabMeta.test.ts +++ b/packages/app-tests/tableDataTabMeta.test.ts @@ -122,6 +122,35 @@ test("uses result columns when persisted table metadata has no columns", () => { ); }); +test("prefers the tableMeta table name over a schema-qualified tab title", () => { + // Data tabs opened from the object browser are titled ".
"; + // rebuilding SQL from the title would qualify the table twice (issue #3613). + const tableMeta = { + schema: "dbo", + tableName: "wcs_dispatch_task", + columns: [], + primaryKeys: [], + }; + + const meta = tableMetaForDataTab(tab({ title: "dbo.wcs_dispatch_task", schema: "dbo", tableMeta })); + + assert.equal(meta?.tableName, "wcs_dispatch_task"); + assert.equal(meta?.schema, "dbo"); +}); + +test("strips the schema prefix from the tab title when no tableMeta exists", () => { + const meta = tableMetaForDataTab(tab({ title: "dbo.wcs_dispatch_task", schema: "dbo" })); + + assert.equal(meta?.tableName, "wcs_dispatch_task"); + assert.equal(meta?.schema, "dbo"); +}); + +test("keeps a dotted tab title intact when it does not start with the schema", () => { + const meta = tableMetaForDataTab(tab({ title: "audit.2024_log", schema: "public" })); + + assert.equal(meta?.tableName, "audit.2024_log"); +}); + test("does not infer table metadata for query tabs", () => { assert.equal(tableMetaForDataTab(tab({ mode: "query" })), undefined); });