feat(sidebar): lazily load database object groups

This commit is contained in:
t8y2 committed 2026-06-07 23:44:40 +08:00
1 parent cdad4a32ff
commit 7bd8d29a79
15 files changed
+484 -68

No files matched your search

+110 -15
View File
@@ -20,11 +20,17 @@ import {
import type { SqlCompletionColumn, SqlCompletionObject, SqlCompletionTable } from "@/lib/sqlCompletion";
import * as api from "@/lib/api";
import { isTauriRuntime } from "@/lib/tauriRuntime";
import { isSchemaAware, usesTreeSchemaMode } from "@/lib/databaseCapabilities";
import {
isSchemaAware,
normalizeSidebarObjectKind,
sidebarObjectKindsForDatabase,
usesTreeSchemaMode,
} from "@/lib/databaseCapabilities";
import {
connectionObjectTreeNodeSchema,
connectionObjectTreeQuerySchema,
connectionUsesDatabaseObjectTreeMode,
effectiveDatabaseTypeForConnection,
} from "@/lib/jdbcDialect";
import {
buildDatabaseTreeNodes,
@@ -42,12 +48,16 @@ import {
normalizeVisibleDatabaseSelection,
} from "@/lib/visibleDatabases";
import {
buildObjectGroupPlaceholderNodes,
buildGroupedObjectTreeNodes,
buildSimpleObjectTreeNodes,
buildTableTreeNodes,
expandCachedObjectBrowserNodes,
mergeTableInfosIntoObjects,
objectGroupRefreshParentId,
objectTypesForGroupNode,
tablePartitionGroups,
type DatabaseObjectTreeKind,
} from "@/lib/tableTree";
import {
hasTreeNodeDatabaseContext,
@@ -439,6 +449,11 @@ export const useConnectionStore = defineStore("connection", () => {
return parts.map((part) => encodeURIComponent(part)).join(":");
}
function supportedSidebarObjectTypes(config?: ConnectionConfig): DatabaseObjectTreeKind[] {
const dbType = effectiveDatabaseTypeForConnection(config);
return sidebarObjectKindsForDatabase(dbType);
}
function refreshStaleTreeNode(node: TreeNode) {
if (staleTreeRefreshIds.has(node.id)) return;
staleTreeRefreshIds.add(node.id);
@@ -1070,7 +1085,12 @@ export const useConnectionStore = defineStore("connection", () => {
try {
await ensureConnected(connectionId);
if (useCachedChildren(node, options)) return;
const cacheKey = schemaCacheKey(connectionId, database, "sqlserver-objects");
const simpleObjectDisplay = useSettingsStore().editorSettings.sidebarObjectDisplay === "simple";
const cacheKey = schemaCacheKey(
connectionId,
database,
simpleObjectDisplay ? "sqlserver-objects-simple-v2" : "sqlserver-objects-grouped-v2",
);
if (!options?.force) {
const cached = await loadPersistedTreeChildren(node, cacheKey);
if (cached.hit) {
@@ -1079,12 +1099,14 @@ export const useConnectionStore = defineStore("connection", () => {
}
}
const [schemas, defaultSchemaObjects] = await Promise.all([
api.listSchemas(connectionId, database),
api.listObjects(connectionId, database, SQLSERVER_DEFAULT_SCHEMA),
]);
const config = getConfig(connectionId);
const schemas = await api.listSchemas(connectionId, database);
const defaultSchemaObjects = simpleObjectDisplay
? await api.listObjects(connectionId, database, SQLSERVER_DEFAULT_SCHEMA)
: [];
const children = buildSqlServerDatabaseTreeNodes(connectionId, database, schemas, defaultSchemaObjects, {
simpleObjectDisplay: useSettingsStore().editorSettings.sidebarObjectDisplay === "simple",
lazyObjectTypes: simpleObjectDisplay ? undefined : supportedSidebarObjectTypes(config),
simpleObjectDisplay,
});
setChildren(node, children);
await savePersistedTreeChildren(cacheKey, children);
@@ -1105,7 +1127,13 @@ export const useConnectionStore = defineStore("connection", () => {
try {
await ensureConnected(connectionId);
if (useCachedChildren(node, options)) return;
const cacheKey = schemaCacheKey(connectionId, database, schema || "", "objects");
const simpleObjectDisplay = useSettingsStore().editorSettings.sidebarObjectDisplay === "simple";
const cacheKey = schemaCacheKey(
connectionId,
database,
schema || "",
simpleObjectDisplay ? "objects-simple-v2" : "objects-grouped-v2",
);
if (!options?.force) {
const cached = await loadPersistedTreeChildren(node, cacheKey);
if (cached.hit) {
@@ -1117,18 +1145,14 @@ export const useConnectionStore = defineStore("connection", () => {
const config = getConfig(connectionId);
const querySchema = connectionObjectTreeQuerySchema(config, database, schema);
const effectiveSchema = connectionObjectTreeNodeSchema(config, database, schema);
const simpleObjectDisplay = useSettingsStore().editorSettings.sidebarObjectDisplay === "simple";
let children: TreeNode[];
if (simpleObjectDisplay) {
const tables = await api.listTables(connectionId, database, querySchema);
children = buildTableTreeNodes({ nodeId, connectionId, database, schema: effectiveSchema, tables });
} else {
try {
const [objects, tables] = await Promise.all([
api.listObjects(connectionId, database, querySchema),
api.listTables(connectionId, database, querySchema),
]);
children = buildGroupedObjectTreeNodes({
children = buildSimpleObjectTreeNodes({
nodeId,
connectionId,
database,
@@ -1139,6 +1163,14 @@ export const useConnectionStore = defineStore("connection", () => {
const tables = await api.listTables(connectionId, database, querySchema);
children = buildTableTreeNodes({ nodeId, connectionId, database, schema: effectiveSchema, tables });
}
} else {
children = buildObjectGroupPlaceholderNodes({
nodeId,
connectionId,
database,
schema: effectiveSchema,
objectTypes: supportedSidebarObjectTypes(config),
});
}
setChildren(node, children);
await savePersistedTreeChildren(cacheKey, children);
@@ -1151,6 +1183,61 @@ export const useConnectionStore = defineStore("connection", () => {
}
}
async function loadObjectGroupChildren(node: TreeNode, options?: LoadTreeOptions) {
if (!node.connectionId || !hasTreeNodeDatabaseContext(node)) return;
node.isLoading = true;
try {
await ensureConnected(node.connectionId);
if (useCachedChildren(node, options)) return;
const objectTypes = objectTypesForGroupNode(node.type);
const parentNodeId = objectGroupRefreshParentId(node);
if (!objectTypes || !parentNodeId) return;
const config = getConfig(node.connectionId);
const querySchema = connectionObjectTreeQuerySchema(config, node.database, node.schema);
const effectiveSchema = connectionObjectTreeNodeSchema(config, node.database, node.schema);
const cacheKey = schemaCacheKey(node.connectionId, node.database, node.schema || "", node.type, "objects-v1");
if (!options?.force) {
const cached = await loadPersistedTreeChildren(node, cacheKey);
if (cached.hit) {
if (cached.isStale) refreshStaleTreeNode(node);
return;
}
}
const wantsOnlyTablesOrViews = objectTypes.every((objectType) => objectType === "TABLE" || objectType === "VIEW");
const objects = wantsOnlyTablesOrViews
? mergeTableInfosIntoObjects(
[],
await api.listTables(node.connectionId, node.database, querySchema),
effectiveSchema,
)
: await api.listObjects(node.connectionId, node.database, querySchema, objectTypes);
const grouped = buildGroupedObjectTreeNodes({
nodeId: parentNodeId,
connectionId: node.connectionId,
database: node.database,
schema: effectiveSchema,
objects: objects.filter((object) => objectTypes.includes(normalizedObjectTreeKind(object.object_type))),
});
const refreshedGroup = grouped.find((group) => group.type === node.type);
const children = refreshedGroup?.children ?? [];
node.objectCount = refreshedGroup?.objectCount ?? children.length;
setChildren(node, children);
await savePersistedTreeChildren(cacheKey, children);
node.isExpanded = true;
} catch (e) {
recordMetadataLoadError(node.connectionId, e);
throw e;
} finally {
node.isLoading = false;
}
}
function normalizedObjectTreeKind(type: string): DatabaseObjectTreeKind {
return normalizeSidebarObjectKind(type);
}
async function loadTableGroups(
connectionId: string,
database: string,
@@ -1431,9 +1518,10 @@ export const useConnectionStore = defineStore("connection", () => {
node.type === "group-views" ||
node.type === "group-procedures" ||
node.type === "group-functions" ||
node.type === "group-packages" ||
node.type === "group-partitions"
node.type === "group-packages"
) {
await loadObjectGroupChildren(node, options);
} else if (node.type === "group-partitions") {
node.isExpanded = true;
}
}
@@ -1448,6 +1536,12 @@ export const useConnectionStore = defineStore("connection", () => {
}
async function refreshTreeNode(node: TreeNode) {
if (objectTypesForGroupNode(node.type)) {
clearLoadedChildrenCache(node.id);
await loadObjectGroupChildren(node, { force: true });
return;
}
const parentId = objectGroupRefreshParentId(node);
const parentNode = parentId ? findNode(treeNodes.value, parentId) : null;
if (parentNode) {
@@ -2166,6 +2260,7 @@ export const useConnectionStore = defineStore("connection", () => {
loadSchemas,
loadSqlServerDatabaseObjects,
loadTables,
loadObjectGroupChildren,
loadTableGroups,
loadColumns,
loadIndexes,