diff --git a/apps/desktop/src/stores/__tests__/queryStore.database-open.spec.ts b/apps/desktop/src/stores/__tests__/queryStore.database-open.spec.ts index c6349dc9ef..259715863f 100644 --- a/apps/desktop/src/stores/__tests__/queryStore.database-open.spec.ts +++ b/apps/desktop/src/stores/__tests__/queryStore.database-open.spec.ts @@ -122,6 +122,54 @@ describe("queryStore database open state", () => { expect(store.openTableStructure("doris-1", "sales", undefined, "orders", undefined, undefined, "iceberg_catalog")).toBe(icebergTabId); }); + it("keeps same-name structure editors in different schemas isolated", async () => { + const { useQueryStore } = await import("@/stores/queryStore"); + const store = useQueryStore(); + + const publicTabId = store.openTableStructure("pg-1", "app", "public", "users"); + const archiveTabId = store.openTableStructure("pg-1", "app", "archive", "users"); + + expect(archiveTabId).not.toBe(publicTabId); + expect(store.tabs.filter((tab) => tab.mode === "structure")).toHaveLength(2); + expect(store.tabs.find((tab) => tab.id === publicTabId)?.schema).toBe("public"); + expect(store.tabs.find((tab) => tab.id === archiveTabId)?.schema).toBe("archive"); + expect(store.openTableStructure("pg-1", "app", "public", "users")).toBe(publicTabId); + expect(store.activeTabId).toBe(publicTabId); + }); + + it("preserves case-sensitive schema and table identities", async () => { + const { useQueryStore } = await import("@/stores/queryStore"); + const store = useQueryStore(); + + const quotedTabId = store.openTableStructure("pg-1", "app", "Sales", "Users"); + const schemaCaseTabId = store.openTableStructure("pg-1", "app", "sales", "Users"); + const tableCaseTabId = store.openTableStructure("pg-1", "app", "Sales", "users"); + + expect(new Set([quotedTabId, schemaCaseTabId, tableCaseTabId])).toHaveLength(3); + expect(store.openTableStructure("pg-1", "app", "Sales", "Users")).toBe(quotedTabId); + }); + + it("reuses only matching schema-less structure targets", async () => { + const { useQueryStore } = await import("@/stores/queryStore"); + const store = useQueryStore(); + + const schemaLessTabId = store.openTableStructure("mysql-1", "app", undefined, "users"); + const reusedTabId = store.openTableStructure("mysql-1", "app", undefined, "users", "columns", { kind: "column", name: "id" }); + const explicitSchemaTabId = store.openTableStructure("mysql-1", "app", "public", "users"); + const firstCreateTabId = store.openTableStructure("mysql-1", "app", undefined, ""); + const secondCreateTabId = store.openTableStructure("mysql-1", "app", undefined, ""); + + expect(reusedTabId).toBe(schemaLessTabId); + expect(store.tabs.find((tab) => tab.id === schemaLessTabId)).toMatchObject({ + schema: undefined, + structureInitialTab: "columns", + structureInitialTabRequestId: 1, + structureInitialTarget: { kind: "column", name: "id" }, + }); + expect(explicitSchemaTabId).not.toBe(schemaLessTabId); + expect(secondCreateTabId).not.toBe(firstCreateTabId); + }); + it("clears the catalog when changing a query tab connection", async () => { const { useQueryStore } = await import("@/stores/queryStore"); const store = useQueryStore(); diff --git a/apps/desktop/src/stores/queryStore.ts b/apps/desktop/src/stores/queryStore.ts index 9ec85463ec..71e111ba5d 100644 --- a/apps/desktop/src/stores/queryStore.ts +++ b/apps/desktop/src/stores/queryStore.ts @@ -2244,7 +2244,7 @@ export const useQueryStore = defineStore("query", () => { function openTableStructure(connectionId: string, database: string, schema?: string, tableName?: string, initialTab?: TableInfoTab, initialTarget?: TableStructureEditorTarget, catalog?: string) { const resolvedTableName = tableName || ""; if (resolvedTableName) { - const existing = tabs.value.find((tab) => tab.mode === "structure" && tab.connectionId === connectionId && tab.database === database && (tab.catalog || "") === (catalog || "") && (tab.structureTableName || "") === resolvedTableName); + const existing = tabs.value.find((tab) => tab.mode === "structure" && tab.connectionId === connectionId && tab.database === database && (tab.catalog || "") === (catalog || "") && (tab.schema || "") === (schema || "") && (tab.structureTableName || "") === resolvedTableName); if (existing) { applyTableStructureInitialTab(existing, initialTab, initialTarget); switchTab(existing.id);