fix(clickhouse): load table index metadata

This commit is contained in:
t8y2 committed 2026-06-11 12:23:28 +08:00
1 parent 293df56274
commit 521a3021c4
6 files changed
+267 -42

No files matched your search

+37 -26
View File
@@ -133,6 +133,7 @@ import { useCellDetailEditor, type UseCellDetailEditorReturn } from "@/composabl
import { useTheme } from "@/composables/useTheme";
import { useSettingsStore } from "@/stores/settingsStore";
import { nextDataGridSortState, type DataGridSortDirection } from "@/lib/dataGridSort";
import { getTableMetadataCapabilities } from "@/lib/tableMetadataCapabilities";
const SqlPreviewPanel = defineAsyncComponent(() => import("@/components/editor/SqlPreviewPanel.vue"));
@@ -5175,6 +5176,7 @@ async function prefetchCopyStatements() {
const sqlOneLiner = computed(() => props.sql?.replace(/\s+/g, " ").trim() || "");
type TableInfoTab = "columns" | "indexes" | "foreignKeys" | "triggers" | "ddl";
type TableInfoTabItem = { id: TableInfoTab; label: string; icon: Component; count?: number };
const TABLE_INFO_DRAWER_MIN_WIDTH = 240;
const CELL_DETAIL_PANEL_MIN_HEIGHT = 180;
@@ -5268,26 +5270,34 @@ function toggleCellDetailPanelLayout() {
});
}
const tableInfoTabs = computed(
() =>
[
{
id: "columns" as const,
label: t("grid.tableInfoColumns"),
icon: ListTree,
count: props.tableMeta?.columns.length,
},
{ id: "indexes" as const, label: t("grid.tableInfoIndexes"), icon: KeyRound, count: indexes.value.length },
{
id: "foreignKeys" as const,
label: t("grid.tableInfoForeignKeys"),
icon: Link2,
count: foreignKeys.value.length,
},
{ id: "triggers" as const, label: t("grid.tableInfoTriggers"), icon: RotateCcw, count: triggers.value.length },
{ id: "ddl" as const, label: "DDL", icon: Code2 },
] satisfies Array<{ id: TableInfoTab; label: string; icon: Component; count?: number }>,
);
const tableMetadataCapabilities = computed(() => getTableMetadataCapabilities(props.databaseType));
const tableInfoTabs = computed(() => {
const tabs: TableInfoTabItem[] = [
{
id: "columns",
label: t("grid.tableInfoColumns"),
icon: ListTree,
count: props.tableMeta?.columns.length,
},
{ id: "indexes", label: t("grid.tableInfoIndexes"), icon: KeyRound, count: indexes.value.length },
];
if (tableMetadataCapabilities.value.foreignKeys) {
tabs.push({
id: "foreignKeys",
label: t("grid.tableInfoForeignKeys"),
icon: Link2,
count: foreignKeys.value.length,
});
}
if (tableMetadataCapabilities.value.triggers) {
tabs.push({ id: "triggers", label: t("grid.tableInfoTriggers"), icon: RotateCcw, count: triggers.value.length });
}
tabs.push({ id: "ddl", label: "DDL", icon: Code2 });
return tabs;
});
const tableInfoTabListStyle = computed(() => ({
gridTemplateColumns: `repeat(${tableInfoTabs.value.length}, minmax(0, 1fr))`,
}));
async function toggleTableInfo(tab: TableInfoTab = activeTableInfoTab.value) {
if (showTableInfo.value && activeTableInfoTab.value === tab) {
@@ -5299,11 +5309,12 @@ async function toggleTableInfo(tab: TableInfoTab = activeTableInfoTab.value) {
}
async function selectTableInfoTab(tab: TableInfoTab) {
activeTableInfoTab.value = tab;
if (tab === "ddl") await fetchDdl();
else if (tab === "indexes") await fetchIndexes();
else if (tab === "foreignKeys") await fetchForeignKeys();
else if (tab === "triggers") await fetchTriggers();
const nextTab = tableInfoTabs.value.some((item) => item.id === tab) ? tab : "columns";
activeTableInfoTab.value = nextTab;
if (nextTab === "ddl") await fetchDdl();
else if (nextTab === "indexes") await fetchIndexes();
else if (nextTab === "foreignKeys") await fetchForeignKeys();
else if (nextTab === "triggers") await fetchTriggers();
}
async function fetchDdl() {
@@ -6882,7 +6893,7 @@ const gridContextMenuItems = computed<ContextMenuItem[]>(() => {
<X class="w-3 h-3" />
</Button>
</div>
<div class="grid grid-cols-5 border-b bg-background shrink-0">
<div class="grid border-b bg-background shrink-0" :style="tableInfoTabListStyle">
<button
v-for="tab in tableInfoTabs"
:key="tab.id"
@@ -22,6 +22,7 @@ import { type SqlHighlighter, createShikiSqlHighlighter } from "@/lib/sqlHighlig
import { copyToClipboard } from "@/lib/clipboard";
import { queryTimeoutSecsForConnection } from "@/lib/queryTimeout";
import { type EditableStructureColumn, type EditableStructureIndex } from "@/lib/tableStructureEditorSql";
import { getTableMetadataCapabilities } from "@/lib/tableMetadataCapabilities";
import { getTableStructureCapabilities } from "@/lib/tableStructureCapabilities";
import { connectionObjectTreeQuerySchema, effectiveDatabaseTypeForConnection } from "@/lib/jdbcDialect";
import { buildStructureTargetLabel, combineDataTypeForDatabase, createColumnDrafts, createIndexDrafts, getDataTypeOptions, getDefaultLengthForType, splitDataType, toColumnNames } from "@/lib/tableStructureEditorState";
@@ -268,6 +269,7 @@ function onIndexColResize(e: MouseEvent, col: number) {
const connection = computed(() => (props.connectionId ? store.getConfig(props.connectionId) : undefined));
const databaseType = computed(() => effectiveDatabaseTypeForConnection(connection.value));
const structureCapabilities = computed(() => getTableStructureCapabilities(databaseType.value));
const tableMetadataCapabilities = computed(() => getTableMetadataCapabilities(databaseType.value));
const structureDialect = computed(() => structureCapabilities.value.dialect);
const isTableCommentDisabled = computed(() => !structureCapabilities.value.comment);
const dataTypeOptions = computed(() => getDataTypeOptions(databaseType.value));
@@ -372,8 +374,8 @@ async function loadStructure(silent = false) {
const nextColumns = await api.getColumns(props.connectionId, props.database, metadataSchema.value, props.tableName);
const [nextIndexes, nextForeignKeys, nextTriggers] = await Promise.all([
api.listIndexes(props.connectionId, props.database, metadataSchema.value, props.tableName).catch(() => []),
api.listForeignKeys(props.connectionId, props.database, metadataSchema.value, props.tableName).catch(() => []),
api.listTriggers(props.connectionId, props.database, metadataSchema.value, props.tableName).catch(() => []),
tableMetadataCapabilities.value.foreignKeys ? api.listForeignKeys(props.connectionId, props.database, metadataSchema.value, props.tableName).catch(() => []) : Promise.resolve([]),
tableMetadataCapabilities.value.triggers ? api.listTriggers(props.connectionId, props.database, metadataSchema.value, props.tableName).catch(() => []) : Promise.resolve([]),
]);
columns.value = createColumnDrafts(nextColumns, databaseType.value);
indexes.value = createIndexDrafts(nextIndexes);
@@ -670,6 +672,11 @@ onBeforeUnmount(() => {
unregisterStructureEditorShortcuts();
});
watch(tableMetadataCapabilities, (capabilities) => {
if (activeTab.value === "foreignKeys" && !capabilities.foreignKeys) activeTab.value = "columns";
if (activeTab.value === "triggers" && !capabilities.triggers) activeTab.value = "columns";
});
watch(
[isCreateMode, databaseType, () => props.schema, () => props.tableName, newTableName, tableComment, columns, indexes],
() => {
@@ -730,8 +737,8 @@ watch(activeTab, (tab) => {
<TabsList>
<TabsTrigger value="columns">{{ t("structureEditor.columns") }}</TabsTrigger>
<TabsTrigger value="indexes">{{ t("structureEditor.indexes") }}</TabsTrigger>
<TabsTrigger value="foreignKeys">{{ t("structureEditor.foreignKeys") }}</TabsTrigger>
<TabsTrigger value="triggers">{{ t("structureEditor.triggers") }}</TabsTrigger>
<TabsTrigger v-if="tableMetadataCapabilities.foreignKeys" value="foreignKeys">{{ t("structureEditor.foreignKeys") }}</TabsTrigger>
<TabsTrigger v-if="tableMetadataCapabilities.triggers" value="triggers">{{ t("structureEditor.triggers") }}</TabsTrigger>
<TabsTrigger value="ddl" v-if="!isCreateMode">DDL</TabsTrigger>
</TabsList>
<div class="flex shrink-0 items-center gap-1.5">
@@ -1077,7 +1084,7 @@ watch(activeTab, (tab) => {
</table>
</TabsContent>
<TabsContent value="foreignKeys" class="m-0 min-h-0 flex-1 overflow-auto p-[var(--structure-cell-px)]">
<TabsContent v-if="tableMetadataCapabilities.foreignKeys" value="foreignKeys" class="m-0 min-h-0 flex-1 overflow-auto p-[var(--structure-cell-px)]">
<div v-if="foreignKeys.length === 0" class="py-10 text-center text-muted-foreground">
{{ t("structureEditor.emptyReadonly") }}
</div>
@@ -1089,7 +1096,7 @@ watch(activeTab, (tab) => {
</div>
</TabsContent>
<TabsContent value="triggers" class="m-0 min-h-0 flex-1 overflow-auto p-[var(--structure-cell-px)]">
<TabsContent v-if="tableMetadataCapabilities.triggers" value="triggers" class="m-0 min-h-0 flex-1 overflow-auto p-[var(--structure-cell-px)]">
<div v-if="triggers.length === 0" class="py-10 text-center text-muted-foreground">
{{ t("structureEditor.emptyReadonly") }}
</div>