fix(oracle): lazy load table triggers
This commit is contained in:
1 parent
132ac8a2ec
commit
5600872565
4 files changed
+116
-25
No files matched your search
@@ -32,6 +32,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 { 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";
|
||||
@@ -172,24 +173,9 @@ const warnings = ref<string[]>([]);
|
||||
const sqliteSchemaRevision = ref<string>();
|
||||
const foreignKeys = ref<EditableStructureForeignKey[]>([]);
|
||||
const triggers = ref<EditableStructureTrigger[]>([]);
|
||||
const triggersLoaded = ref(false);
|
||||
const secondaryMetadataLoading = computed(() => indexesLoading.value || foreignKeysLoading.value || triggersLoading.value);
|
||||
|
||||
interface StructureRefreshScope {
|
||||
columns: boolean;
|
||||
indexes: boolean;
|
||||
foreignKeys: boolean;
|
||||
triggers: boolean;
|
||||
tableComment: boolean;
|
||||
}
|
||||
|
||||
const FULL_STRUCTURE_REFRESH_SCOPE: StructureRefreshScope = {
|
||||
columns: true,
|
||||
indexes: true,
|
||||
foreignKeys: true,
|
||||
triggers: true,
|
||||
tableComment: true,
|
||||
};
|
||||
|
||||
function sameList(left: string[] | null | undefined, right: string[] | null | undefined): boolean {
|
||||
const a = left ?? [];
|
||||
const b = right ?? [];
|
||||
@@ -251,7 +237,7 @@ function triggerChanged(trigger: EditableStructureTrigger): boolean {
|
||||
return trigger.name !== original.name || trigger.timing !== original.timing || trigger.event !== original.event || !sameText(trigger.statement, original.statement);
|
||||
}
|
||||
|
||||
function captureStructureRefreshScope(): StructureRefreshScope {
|
||||
function captureStructureRefreshScope(): TableStructureRefreshScope {
|
||||
return {
|
||||
columns: columns.value.some(columnChanged),
|
||||
indexes: indexes.value.some(indexChanged),
|
||||
@@ -921,6 +907,7 @@ function createCurrentDraft(initialized = true): TableStructureEditorDraft {
|
||||
indexes: cloneDraftValue(indexes.value),
|
||||
foreignKeys: cloneDraftValue(foreignKeys.value),
|
||||
triggers: cloneDraftValue(triggers.value),
|
||||
triggersLoaded: triggersLoaded.value,
|
||||
scrollPositions: cloneDraftValue(structureScrollPositions.value),
|
||||
initialized,
|
||||
};
|
||||
@@ -945,6 +932,8 @@ function restoreDraft(draft: TableStructureEditorDraft) {
|
||||
indexes.value = cloneDraftValue(draft.indexes || []);
|
||||
foreignKeys.value = cloneDraftValue(draft.foreignKeys || []);
|
||||
triggers.value = cloneDraftValue(draft.triggers || []);
|
||||
// Drafts created before lazy trigger loading always contained live trigger metadata.
|
||||
triggersLoaded.value = draft.triggersLoaded ?? true;
|
||||
structureScrollPositions.value = cloneDraftValue(draft.scrollPositions || {});
|
||||
restoringDraft = false;
|
||||
draftHydrated = !needsColumnDraftMetadataHydration();
|
||||
@@ -1195,6 +1184,7 @@ function resetState() {
|
||||
sqliteSchemaRevision.value = undefined;
|
||||
foreignKeys.value = [];
|
||||
triggers.value = [];
|
||||
triggersLoaded.value = false;
|
||||
selectedColumnId.value = null;
|
||||
ddlContent.value = "";
|
||||
ddlFetched.value = false;
|
||||
@@ -1213,10 +1203,14 @@ function resetState() {
|
||||
async function reloadStructureFromDatabase() {
|
||||
if (isCreateMode.value) return;
|
||||
draftHydrated = false;
|
||||
await loadStructure(false, FULL_STRUCTURE_REFRESH_SCOPE, true, { blockSecondaryMetadata: true });
|
||||
if (activeTab.value !== "triggers") {
|
||||
triggers.value = [];
|
||||
triggersLoaded.value = false;
|
||||
}
|
||||
await loadStructure(false, visibleTableStructureRefreshScope(activeTab.value), true, { blockSecondaryMetadata: true });
|
||||
}
|
||||
|
||||
function setSecondaryMetadataLoading(scope: StructureRefreshScope, value: boolean) {
|
||||
function setSecondaryMetadataLoading(scope: TableStructureRefreshScope, value: boolean) {
|
||||
if (scope.indexes && tableMetadataCapabilities.value.indexes) indexesLoading.value = value;
|
||||
if (scope.foreignKeys && tableMetadataCapabilities.value.foreignKeys) foreignKeysLoading.value = value;
|
||||
if (scope.triggers && tableMetadataCapabilities.value.triggers) triggersLoading.value = value;
|
||||
@@ -1236,7 +1230,7 @@ async function fetchTableCommentValue(connectionId: string, database: string, sc
|
||||
}
|
||||
}
|
||||
|
||||
async function loadStructure(silent = false, scope: StructureRefreshScope = FULL_STRUCTURE_REFRESH_SCOPE, showErrors = true, options: { blockSecondaryMetadata?: boolean; preserveDraft?: boolean; damengLengthUnitsAfterSave?: ReadonlyMap<string, string> } = {}) {
|
||||
async function loadStructure(silent = false, scope: TableStructureRefreshScope = visibleTableStructureRefreshScope(activeTab.value), showErrors = true, options: { blockSecondaryMetadata?: boolean; preserveDraft?: boolean; damengLengthUnitsAfterSave?: ReadonlyMap<string, string> } = {}) {
|
||||
const connectionId = props.connectionId;
|
||||
const database = props.database;
|
||||
const catalog = props.catalog;
|
||||
@@ -1289,7 +1283,10 @@ async function loadStructure(silent = false, scope: StructureRefreshScope = FULL
|
||||
if (requestId !== structureLoadRequestId) return;
|
||||
if (nextIndexes) indexes.value = createIndexDrafts(nextIndexes);
|
||||
if (nextForeignKeys) foreignKeys.value = createForeignKeyDrafts(nextForeignKeys);
|
||||
if (nextTriggers) triggers.value = createTriggerDrafts(nextTriggers);
|
||||
if (nextTriggers) {
|
||||
triggers.value = createTriggerDrafts(nextTriggers);
|
||||
triggersLoaded.value = true;
|
||||
}
|
||||
};
|
||||
|
||||
secondaryMetadataScheduled = true;
|
||||
@@ -1321,7 +1318,7 @@ async function loadStructure(silent = false, scope: StructureRefreshScope = FULL
|
||||
}
|
||||
}
|
||||
|
||||
async function refreshStructureAfterSave(scope: StructureRefreshScope, damengLengthUnitsAfterSave: ReadonlyMap<string, string>) {
|
||||
async function refreshStructureAfterSave(scope: TableStructureRefreshScope, damengLengthUnitsAfterSave: ReadonlyMap<string, string>) {
|
||||
try {
|
||||
await loadStructure(true, scope, false, { blockSecondaryMetadata: true, damengLengthUnitsAfterSave });
|
||||
} catch (e) {
|
||||
@@ -2294,7 +2291,7 @@ function addItemForActiveTab(): boolean {
|
||||
addForeignKey();
|
||||
return true;
|
||||
}
|
||||
if (activeTab.value === "triggers" && canEditTriggers.value) {
|
||||
if (activeTab.value === "triggers" && canEditTriggers.value && !triggersLoading.value) {
|
||||
addTrigger();
|
||||
return true;
|
||||
}
|
||||
@@ -2350,7 +2347,7 @@ onMounted(() => {
|
||||
} else if (isCreateMode.value) {
|
||||
markDraftHydratedAndSync();
|
||||
} else {
|
||||
void loadStructure(false, FULL_STRUCTURE_REFRESH_SCOPE, true, { blockSecondaryMetadata: true }).then(() => applyInitialStructureTarget());
|
||||
void loadStructure(false, visibleTableStructureRefreshScope(activeTab.value), true, { blockSecondaryMetadata: true }).then(() => applyInitialStructureTarget());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -2466,6 +2463,25 @@ 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) => {
|
||||
@@ -2493,7 +2509,11 @@ watch(refreshVersion, (version, previous) => {
|
||||
skipNextRefreshVersion = false;
|
||||
return;
|
||||
}
|
||||
void loadStructure(true);
|
||||
if (activeTab.value !== "triggers") {
|
||||
triggers.value = [];
|
||||
triggersLoaded.value = false;
|
||||
}
|
||||
void loadStructure(true, visibleTableStructureRefreshScope(activeTab.value));
|
||||
});
|
||||
|
||||
watch(
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { shouldLoadTableStructureTriggers, visibleTableStructureRefreshScope } from "@/lib/table/tableStructureMetadataLoading";
|
||||
|
||||
describe("table structure metadata loading", () => {
|
||||
it("does not request triggers while opening the default columns tab", () => {
|
||||
expect(visibleTableStructureRefreshScope("columns").triggers).toBe(false);
|
||||
});
|
||||
|
||||
it("requests triggers when the structure editor opens on the trigger tab", () => {
|
||||
expect(visibleTableStructureRefreshScope("triggers").triggers).toBe(true);
|
||||
});
|
||||
|
||||
it("loads trigger metadata once when the trigger tab becomes visible", () => {
|
||||
const base = {
|
||||
activeTab: "triggers" as const,
|
||||
isCreateMode: false,
|
||||
supported: true,
|
||||
loading: false,
|
||||
structureLoading: false,
|
||||
};
|
||||
|
||||
expect(shouldLoadTableStructureTriggers({ ...base, loaded: false })).toBe(true);
|
||||
expect(shouldLoadTableStructureTriggers({ ...base, loaded: true })).toBe(false);
|
||||
});
|
||||
|
||||
it("waits for the initial structure load and skips create mode", () => {
|
||||
const base = {
|
||||
activeTab: "triggers" as const,
|
||||
supported: true,
|
||||
loaded: false,
|
||||
loading: false,
|
||||
};
|
||||
|
||||
expect(shouldLoadTableStructureTriggers({ ...base, isCreateMode: false, structureLoading: true })).toBe(false);
|
||||
expect(shouldLoadTableStructureTriggers({ ...base, isCreateMode: true, structureLoading: false })).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,33 @@
|
||||
import type { TableInfoTab } from "@/types/database";
|
||||
|
||||
export interface TableStructureRefreshScope {
|
||||
columns: boolean;
|
||||
indexes: boolean;
|
||||
foreignKeys: boolean;
|
||||
triggers: boolean;
|
||||
tableComment: boolean;
|
||||
}
|
||||
|
||||
export function visibleTableStructureRefreshScope(activeTab: TableInfoTab): TableStructureRefreshScope {
|
||||
return {
|
||||
columns: true,
|
||||
indexes: true,
|
||||
foreignKeys: true,
|
||||
// Trigger definitions can contain large source bodies, so defer them until
|
||||
// the trigger editor is actually visible.
|
||||
triggers: activeTab === "triggers",
|
||||
tableComment: true,
|
||||
};
|
||||
}
|
||||
|
||||
export const TRIGGERS_ONLY_REFRESH_SCOPE: TableStructureRefreshScope = {
|
||||
columns: false,
|
||||
indexes: false,
|
||||
foreignKeys: false,
|
||||
triggers: true,
|
||||
tableComment: false,
|
||||
};
|
||||
|
||||
export function shouldLoadTableStructureTriggers(options: { activeTab: TableInfoTab; isCreateMode: boolean; supported: boolean; loaded: boolean; loading: boolean; structureLoading: boolean }): boolean {
|
||||
return options.activeTab === "triggers" && !options.isCreateMode && options.supported && !options.loaded && !options.loading && !options.structureLoading;
|
||||
}
|
||||
@@ -754,6 +754,7 @@ export interface TableStructureEditorDraft {
|
||||
indexes: import("@/lib/table/tableStructureEditorSql").EditableStructureIndex[];
|
||||
foreignKeys: import("@/lib/table/tableStructureEditorSql").EditableStructureForeignKey[];
|
||||
triggers: import("@/lib/table/tableStructureEditorSql").EditableStructureTrigger[];
|
||||
triggersLoaded?: boolean;
|
||||
scrollPositions?: Partial<Record<TableInfoTab, TableStructureEditorViewport>>;
|
||||
initialized: boolean;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user