fix(structure): load indexes when tab becomes visible
This commit is contained in:
5 files changed
+106
-76
No files matched your search
+48
@@ -383,4 +383,52 @@ describe("TableStructureEditor metadata loading", () => {
|
||||
expect(mocks.loadObjectMetadataFacet.mock.calls.map((call) => call[1])).toEqual(expectedFacets);
|
||||
expect(mocks.loadObjectDdl).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("loads index metadata when an initialized column draft opens the indexes tab", async () => {
|
||||
mocks.connection.db_type = "postgres";
|
||||
mocks.connection.name = "postgres";
|
||||
mocks.connection.driver_label = "postgres";
|
||||
mocks.ensureConnected.mockResolvedValue(undefined);
|
||||
mocks.listDataTypes.mockResolvedValue([]);
|
||||
mocks.buildTableStructureChangeSql.mockResolvedValue({ statements: [], warnings: [] });
|
||||
mocks.loadObjectMetadataFacet.mockImplementation(async (_request, facet: string) => ({
|
||||
value:
|
||||
facet === "indexes"
|
||||
? [
|
||||
{
|
||||
name: "idx_users_id",
|
||||
columns: ["id"],
|
||||
is_unique: false,
|
||||
is_primary: false,
|
||||
},
|
||||
]
|
||||
: facet === "comment"
|
||||
? ""
|
||||
: [],
|
||||
cacheStatus: "remote",
|
||||
}));
|
||||
|
||||
const root = document.createElement("div");
|
||||
document.body.append(root);
|
||||
const app = createApp(TableStructureEditor, {
|
||||
connectionId: mocks.connection.id,
|
||||
database: "test",
|
||||
schema: "public",
|
||||
tableName: "users",
|
||||
initialTab: "indexes",
|
||||
draft: {
|
||||
...draft(),
|
||||
loadedMetadataFacets: ["columns", "comment"],
|
||||
},
|
||||
});
|
||||
mountedApps.push(app);
|
||||
app.mount(root);
|
||||
await nextTick();
|
||||
await Promise.resolve();
|
||||
await nextTick();
|
||||
|
||||
await vi.waitFor(() => expect(mocks.loadObjectMetadataFacet).toHaveBeenCalledTimes(1));
|
||||
expect(mocks.loadObjectMetadataFacet.mock.calls.map((call) => call[1])).toEqual(["indexes"]);
|
||||
await vi.waitFor(() => expect(root.querySelector('[data-index-row-index="0"]')).not.toBeNull());
|
||||
});
|
||||
});
|
||||
@@ -35,7 +35,7 @@ import { getMysqlDataTypeHelp } from "@/lib/table/mysqlDataTypeHelp";
|
||||
import { getPostgresDataTypeHelp } from "@/lib/table/postgresDataTypeHelp";
|
||||
import { getSqliteDataTypeHelp } from "@/lib/table/sqliteDataTypeHelp";
|
||||
import { getTableMetadataCapabilities, firstStructureMetadataTab, isStructureMetadataTabSupported } from "@/lib/table/tableMetadataCapabilities";
|
||||
import { shouldLoadTableStructureTriggers, TRIGGERS_ONLY_REFRESH_SCOPE, visibleTableStructureRefreshScope, type TableStructureRefreshScope } from "@/lib/table/tableStructureMetadataLoading";
|
||||
import { hasTableStructureRefreshWork, unloadedTableStructureRefreshScope, visibleTableStructureRefreshScope, type TableStructureRefreshScope } from "@/lib/table/tableStructureMetadataLoading";
|
||||
import { canAddTableStructureColumn, getTableStructureCapabilities, hasLocalTableColumnOrderChange, isPhysicalTableColumnOrderChange, supportsLocalTableColumnReorder } from "@/lib/table/tableStructureCapabilities";
|
||||
import { orderedColumnIndexes, uniqueDataGridColumnOrderKeys } from "@/lib/dataGrid/dataGridColumnOrder";
|
||||
import { loadTableDataGridColumnOrder, notifyTableDataGridColumnOrderChanged, removeTableDataGridColumnOrder, saveTableDataGridColumnOrder, tableDataGridColumnOrderScopeKey } from "@/lib/dataGrid/dataGridColumnLayoutStorage";
|
||||
@@ -925,6 +925,7 @@ function createCurrentDraft(initialized = true): TableStructureEditorDraft {
|
||||
foreignKeys: cloneDraftValue(foreignKeys.value),
|
||||
triggers: cloneDraftValue(triggers.value),
|
||||
triggersLoaded: triggersLoaded.value,
|
||||
loadedMetadataFacets: [...loadedMetadataFacets],
|
||||
scrollPositions: cloneDraftValue(structureScrollPositions.value),
|
||||
initialized,
|
||||
};
|
||||
@@ -951,6 +952,17 @@ function restoreDraft(draft: TableStructureEditorDraft) {
|
||||
triggers.value = cloneDraftValue(draft.triggers || []);
|
||||
// Drafts created before lazy trigger loading always contained live trigger metadata.
|
||||
triggersLoaded.value = draft.triggersLoaded ?? true;
|
||||
loadedMetadataFacets.clear();
|
||||
if (draft.loadedMetadataFacets) {
|
||||
for (const facet of draft.loadedMetadataFacets) loadedMetadataFacets.add(facet);
|
||||
} else {
|
||||
const activeScope = visibleTableStructureRefreshScope(draft.activeTab || "columns");
|
||||
if (activeScope.columns) loadedMetadataFacets.add("columns");
|
||||
if (activeScope.indexes || draft.indexes?.length) loadedMetadataFacets.add("indexes");
|
||||
if (activeScope.foreignKeys || draft.foreignKeys?.length) loadedMetadataFacets.add("foreign-keys");
|
||||
if (activeScope.triggers || triggersLoaded.value) loadedMetadataFacets.add("triggers");
|
||||
if (activeScope.tableComment) loadedMetadataFacets.add("comment");
|
||||
}
|
||||
structureScrollPositions.value = cloneDraftValue(draft.scrollPositions || {});
|
||||
restoringDraft = false;
|
||||
draftHydrated = !needsColumnDraftMetadataHydration();
|
||||
@@ -1244,15 +1256,6 @@ function setSecondaryMetadataLoading(scope: TableStructureRefreshScope, value: b
|
||||
if (scope.triggers && tableMetadataCapabilities.value.triggers) triggersLoading.value = value;
|
||||
}
|
||||
|
||||
function hasLoadedMetadataScope(scope: TableStructureRefreshScope): boolean {
|
||||
if (scope.columns && !loadedMetadataFacets.has("columns")) return false;
|
||||
if (scope.indexes && !loadedMetadataFacets.has("indexes")) return false;
|
||||
if (scope.foreignKeys && !loadedMetadataFacets.has("foreign-keys")) return false;
|
||||
if (scope.triggers && !loadedMetadataFacets.has("triggers")) return false;
|
||||
if (scope.tableComment && !loadedMetadataFacets.has("comment")) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
async function fetchTableCommentValue(connectionId: string, database: string, schema: string, tableName: string, catalog?: string): Promise<string | undefined> {
|
||||
try {
|
||||
return (await api.getTableComment(connectionId, database, schema, tableName, catalog)) || "";
|
||||
@@ -2420,7 +2423,10 @@ onMounted(() => {
|
||||
}
|
||||
structureEditorReady = true;
|
||||
if (props.draft?.initialized) {
|
||||
void hydrateRestoredDraftFromDatabase().then(() => applyInitialStructureTarget());
|
||||
void hydrateRestoredDraftFromDatabase().then(() => {
|
||||
applyInitialStructureTarget();
|
||||
void loadActiveTableStructureMetadataIfNeeded();
|
||||
});
|
||||
} else if (isCreateMode.value) {
|
||||
markDraftHydratedAndSync();
|
||||
} else if (activeTab.value === "ddl") {
|
||||
@@ -2436,7 +2442,10 @@ onActivated(() => {
|
||||
if (props.draft?.initialized && !draftHydrated) {
|
||||
restoreDraft(props.draft);
|
||||
applyInitialStructureTarget();
|
||||
void hydrateRestoredDraftFromDatabase().then(() => applyInitialStructureTarget());
|
||||
void hydrateRestoredDraftFromDatabase().then(() => {
|
||||
applyInitialStructureTarget();
|
||||
void loadActiveTableStructureMetadataIfNeeded();
|
||||
});
|
||||
}
|
||||
restoreStructureScrollPosition();
|
||||
});
|
||||
@@ -2542,25 +2551,6 @@ watch(activeTab, () => {
|
||||
syncDraftToParent();
|
||||
});
|
||||
|
||||
async function loadTriggersIfNeeded() {
|
||||
if (
|
||||
!shouldLoadTableStructureTriggers({
|
||||
activeTab: activeTab.value,
|
||||
isCreateMode: isCreateMode.value,
|
||||
supported: tableMetadataCapabilities.value.triggers,
|
||||
loaded: triggersLoaded.value,
|
||||
loading: triggersLoading.value,
|
||||
structureLoading: loading.value,
|
||||
})
|
||||
)
|
||||
return;
|
||||
await loadStructure(true, TRIGGERS_ONLY_REFRESH_SCOPE, true, { blockSecondaryMetadata: true, preserveDraft: true });
|
||||
}
|
||||
|
||||
watch([activeTab, loading], () => {
|
||||
void loadTriggersIfNeeded();
|
||||
});
|
||||
|
||||
watch(
|
||||
columns,
|
||||
(items) => {
|
||||
@@ -2595,21 +2585,20 @@ watch(refreshVersion, (version, previous) => {
|
||||
void loadStructure(true, visibleTableStructureRefreshScope(activeTab.value));
|
||||
});
|
||||
|
||||
watch(
|
||||
activeTab,
|
||||
(tab) => {
|
||||
if (!structureEditorReady) return;
|
||||
if (tab === "ddl") {
|
||||
void fetchDdl();
|
||||
} else if (!isCreateMode.value && !props.draft?.initialized && !loading.value) {
|
||||
const scope = visibleTableStructureRefreshScope(tab);
|
||||
if (!hasLoadedMetadataScope(scope)) {
|
||||
void loadStructure(false, scope, true, { blockSecondaryMetadata: true }).then(() => applyInitialStructureTarget());
|
||||
}
|
||||
}
|
||||
},
|
||||
{ flush: "sync" },
|
||||
);
|
||||
async function loadActiveTableStructureMetadataIfNeeded() {
|
||||
if (!structureEditorReady || isCreateMode.value) return;
|
||||
if (activeTab.value === "ddl") {
|
||||
await fetchDdl();
|
||||
return;
|
||||
}
|
||||
if (loading.value || secondaryMetadataLoading.value) return;
|
||||
const scope = unloadedTableStructureRefreshScope(activeTab.value, loadedMetadataFacets);
|
||||
if (!hasTableStructureRefreshWork(scope)) return;
|
||||
await loadStructure(true, scope, true, { blockSecondaryMetadata: true, preserveDraft: true });
|
||||
applyInitialStructureTarget();
|
||||
}
|
||||
|
||||
watch([activeTab, loading, secondaryMetadataLoading], () => void loadActiveTableStructureMetadataIfNeeded(), { flush: "sync" });
|
||||
|
||||
watch([activeTab, ddlLoading], ([tab, loading]) => {
|
||||
if (tab === "ddl" && !loading) {
|
||||
|
||||
Reference in new issue
Block a user