feat(mysql): 支持查看和修改表存储引擎

This commit is contained in:
zipg authored and GitHub committed 2026-08-26 11:05:08 +08:00
1 parent 2e8673a7ea
commit 60e3d61934
21 files changed
+485 -5

No files matched your search

@@ -33,6 +33,7 @@ import { invalidateObjectMetadataCache, loadObjectMetadataFacet, type ObjectMeta
import { invalidateTableMetadataCache } from "@/lib/metadata/tableMetadataCache";
import { type BuildTableStructureChangeSqlOptions, type EditableStructureColumn, type EditableStructureForeignKey, type EditableStructureIndex, type EditableStructureTrigger } from "@/lib/table/tableStructureEditorSql";
import { buildMysqlAutoIncrementCounterStatement, canEditMysqlAutoIncrementCounter, refreshMysqlAutoIncrementCounterDraft } from "@/lib/table/mysqlAutoIncrementCounter";
import { MYSQL_STORAGE_ENGINES_SQL, mysqlTableEngineSql, mysqlTableEngineSqlOption, parseMysqlTableEngineMetadata, refreshMysqlTableEngineDraft, supportsMysqlTableEngine } from "@/lib/table/mysqlTableEngine";
import { PRESET_FIELDS_TEMPLATE_ID, createTableColumnTemplateDrafts } from "@/lib/table/tableColumnTemplates";
import { getMysqlDataTypeHelp } from "@/lib/table/mysqlDataTypeHelp";
import { getPostgresDataTypeHelp, gaussdbMTypeDisplayName } from "@/lib/table/postgresDataTypeHelp";
@@ -917,6 +918,11 @@ const mysqlAutoIncrementValue = ref<string>();
const originalMysqlAutoIncrementValue = ref<string>();
const mysqlAutoIncrementLoading = ref(false);
const mysqlAutoIncrementLoadError = ref("");
const mysqlTableEngine = ref("");
const originalMysqlTableEngine = ref("");
const mysqlTableEngineOptions = ref<string[]>([]);
const mysqlTableEngineLoading = ref(false);
const mysqlTableEngineLoadError = ref("");
const tableOwner = ref("");
const originalTableOwner = ref("");
const tableOwnerLoading = ref(false);
@@ -927,6 +933,7 @@ const tableOwnerRolesLoadError = ref("");
const supportsTableOwner = computed(() => !isCreateMode.value && databaseType.value === "postgres");
const canEditMysqlAutoIncrement = computed(() => canEditMysqlAutoIncrementCounter(connection.value, isCreateMode.value, columns.value));
const canBuildMysqlAutoIncrement = computed(() => canEditMysqlAutoIncrement.value && !mysqlAutoIncrementLoading.value && !mysqlAutoIncrementLoadError.value && originalMysqlAutoIncrementValue.value !== undefined);
const supportsMysqlEngine = computed(() => supportsMysqlTableEngine(connection.value));
const tableOwnerOptions = computed(() => {
const owner = tableOwner.value;
if (!owner || tableOwnerRoles.value.includes(owner)) return tableOwnerRoles.value;
@@ -950,6 +957,7 @@ let structureLoadRequestId = 0;
let tableOwnerLoadRequestId = 0;
let tableOwnerRolesLoadRequestId = 0;
let mysqlAutoIncrementLoadRequestId = 0;
let mysqlTableEngineLoadRequestId = 0;
let dataTypeOptionsRequestId = 0;
let sqlPreviewDebounceTimer: ReturnType<typeof setTimeout> | undefined;
let deferredSqlPreviewRefresh = false;
@@ -1137,6 +1145,8 @@ function createCurrentDraft(initialized = true): TableStructureEditorDraft {
originalTableComment: originalTableComment.value,
mysqlAutoIncrementValue: mysqlAutoIncrementValue.value,
originalMysqlAutoIncrementValue: originalMysqlAutoIncrementValue.value,
mysqlTableEngine: mysqlTableEngine.value,
originalMysqlTableEngine: originalMysqlTableEngine.value,
tableOwner: tableOwner.value,
originalTableOwner: originalTableOwner.value,
columns: cloneDraftValue(columns.value),
@@ -1169,6 +1179,8 @@ function restoreDraft(draft: TableStructureEditorDraft) {
originalTableComment.value = draft.originalTableComment || "";
mysqlAutoIncrementValue.value = draft.mysqlAutoIncrementValue;
originalMysqlAutoIncrementValue.value = draft.originalMysqlAutoIncrementValue;
mysqlTableEngine.value = draft.mysqlTableEngine || "";
originalMysqlTableEngine.value = draft.originalMysqlTableEngine || "";
tableOwner.value = draft.tableOwner || "";
originalTableOwner.value = draft.originalTableOwner || "";
columns.value = cloneDraftValue(draft.columns || []);
@@ -1252,7 +1264,7 @@ function markDraftHydratedAndSync() {
function hasPendingStructureChanges(): boolean {
if (isCreateMode.value) {
return !!newTableName.value.trim() || !!tableComment.value.trim() || columns.value.length > 0 || indexes.value.length > 0 || foreignKeys.value.length > 0 || triggers.value.length > 0;
return !!newTableName.value.trim() || !!tableComment.value.trim() || mysqlTableEngine.value !== originalMysqlTableEngine.value || columns.value.length > 0 || indexes.value.length > 0 || foreignKeys.value.length > 0 || triggers.value.length > 0;
}
const scope = captureStructureRefreshScope();
return (
@@ -1261,6 +1273,7 @@ function hasPendingStructureChanges(): boolean {
scope.foreignKeys ||
scope.triggers ||
scope.tableComment ||
mysqlTableEngine.value.toLowerCase() !== originalMysqlTableEngine.value.toLowerCase() ||
(canBuildMysqlAutoIncrement.value && mysqlAutoIncrementValue.value !== originalMysqlAutoIncrementValue.value) ||
(supportsTableOwner.value && tableOwner.value.trim() !== originalTableOwner.value.trim())
);
@@ -1405,6 +1418,7 @@ function structureChangeOptions(): BuildTableStructureChangeSqlOptions {
triggers: triggers.value,
tableComment: tableComment.value,
originalTableComment: isCreateMode.value ? undefined : originalTableComment.value,
mysqlEngine: mysqlTableEngineSqlOption({ value: mysqlTableEngine.value, originalValue: originalMysqlTableEngine.value }, isCreateMode.value, supportsMysqlEngine.value && !mysqlTableEngineLoading.value && !mysqlTableEngineLoadError.value),
partitioned: isPartitionedParent.value,
isGaussdbMMode: connection.value?.driver_profile?.toLowerCase() === "gaussdb-m",
};
@@ -1491,6 +1505,7 @@ const canApply = computed(
!saving.value &&
!postSaveRefreshing.value &&
!secondaryMetadataLoading.value &&
!mysqlTableEngineLoading.value &&
!sqlPreviewLoading.value &&
!sqlPreviewPending.value &&
pendingStatements.value.length > 0 &&
@@ -1542,6 +1557,12 @@ function resetState() {
mysqlAutoIncrementLoadRequestId += 1;
mysqlAutoIncrementLoading.value = false;
mysqlAutoIncrementLoadError.value = "";
mysqlTableEngine.value = "";
originalMysqlTableEngine.value = "";
mysqlTableEngineOptions.value = [];
mysqlTableEngineLoadRequestId += 1;
mysqlTableEngineLoading.value = false;
mysqlTableEngineLoadError.value = "";
tableOwner.value = "";
originalTableOwner.value = "";
tableOwnerLoadRequestId += 1;
@@ -1578,9 +1599,9 @@ async function reloadStructureFromDatabase() {
loadedMetadataFacets.clear();
if (refreshDdl) {
ddlFetched.value = false;
await Promise.all([fetchDdl(true), loadTableOwner(true), loadTableOwnerRoles()]);
await Promise.all([fetchDdl(true), loadTableOwner(true), loadTableOwnerRoles(), loadMysqlTableEngine(true)]);
} else {
await Promise.all([loadStructure(false, visibleTableStructureRefreshScope(activeTab.value), true, { blockSecondaryMetadata: true, forceDdl: true, forceMetadata: true }), loadTableOwner(true), loadTableOwnerRoles()]);
await Promise.all([loadStructure(false, visibleTableStructureRefreshScope(activeTab.value), true, { blockSecondaryMetadata: true, forceDdl: true, forceMetadata: true }), loadTableOwner(true), loadTableOwnerRoles(), loadMysqlTableEngine(true)]);
}
}
@@ -1641,6 +1662,43 @@ async function loadMysqlAutoIncrementCounter(preserveDraft = false) {
}
}
async function loadMysqlTableEngine(preserveDraft = false) {
const requestId = ++mysqlTableEngineLoadRequestId;
const connectionId = props.connectionId;
const database = props.database;
if (!supportsMysqlEngine.value || !connectionId || !database) {
mysqlTableEngine.value = "";
originalMysqlTableEngine.value = "";
mysqlTableEngineOptions.value = [];
mysqlTableEngineLoading.value = false;
mysqlTableEngineLoadError.value = "";
return;
}
mysqlTableEngineLoading.value = true;
mysqlTableEngineLoadError.value = "";
try {
await store.ensureConnected(connectionId);
const [enginesResult, tableResult] = await Promise.all([
api.executeQuery(connectionId, database, MYSQL_STORAGE_ENGINES_SQL, undefined, undefined, { maxRows: 100 }),
isCreateMode.value || !props.tableName ? Promise.resolve(undefined) : api.executeQuery(connectionId, database, mysqlTableEngineSql(database, props.tableName), undefined, undefined, { maxRows: 1 }),
]);
if (requestId !== mysqlTableEngineLoadRequestId) return;
const metadata = parseMysqlTableEngineMetadata(enginesResult, tableResult);
const draft = refreshMysqlTableEngineDraft(metadata, { value: mysqlTableEngine.value, originalValue: originalMysqlTableEngine.value }, isCreateMode.value, preserveDraft);
const options = [...metadata.engines];
if (draft.value && !options.some((option) => option.toLowerCase() === draft.value.toLowerCase())) options.unshift(draft.value);
mysqlTableEngineOptions.value = options;
mysqlTableEngine.value = draft.value;
originalMysqlTableEngine.value = draft.originalValue;
} catch (error: any) {
if (requestId !== mysqlTableEngineLoadRequestId) return;
mysqlTableEngineLoadError.value = error?.message || String(error);
} finally {
if (requestId === mysqlTableEngineLoadRequestId) mysqlTableEngineLoading.value = false;
}
}
async function loadTableOwner(force = false, preserveDraft = false) {
const connectionId = props.connectionId;
const database = props.database;
@@ -1852,7 +1910,7 @@ async function loadStructure(
async function refreshStructureAfterSave(scope: TableStructureRefreshScope, characterLengthUnitsAfterSave: ReadonlyMap<string, string>) {
try {
await Promise.all([loadStructure(true, scope, false, { blockSecondaryMetadata: true, characterLengthUnitsAfterSave }), loadTableOwner(true)]);
await Promise.all([loadStructure(true, scope, false, { blockSecondaryMetadata: true, characterLengthUnitsAfterSave }), loadTableOwner(true), loadMysqlTableEngine(false)]);
} catch (e) {
console.warn("[DBX][structure-editor:post-save-refresh-failed]", e);
} finally {
@@ -3354,6 +3412,7 @@ onMounted(() => {
observeStructureHorizontalScroller();
void loadTableOwner(false, props.draft?.tableOwner !== undefined);
void loadTableOwnerRoles();
void loadMysqlTableEngine(props.draft?.mysqlTableEngine !== undefined);
if (props.draft?.initialized) {
void hydrateRestoredDraftFromDatabase().then(() => {
applyInitialStructureTarget();
@@ -3375,6 +3434,9 @@ onActivated(() => {
void loadDynamicDataTypeOptions();
if (supportsTableOwner.value && !loadedMetadataFacets.has("owner")) void loadTableOwner(false, props.draft?.tableOwner !== undefined);
if (supportsTableOwner.value && !tableOwnerRolesLoading.value && tableOwnerRoles.value.length === 0 && !tableOwnerRolesLoadError.value) void loadTableOwnerRoles();
if (supportsMysqlEngine.value && !mysqlTableEngineLoading.value && mysqlTableEngineOptions.value.length === 0 && !mysqlTableEngineLoadError.value) {
void loadMysqlTableEngine(props.draft?.mysqlTableEngine !== undefined);
}
if (props.draft?.initialized && !draftHydrated) {
restoreDraft(props.draft);
applyInitialStructureTarget();
@@ -3503,6 +3565,10 @@ watch(
originalMysqlAutoIncrementValue,
mysqlAutoIncrementLoading,
mysqlAutoIncrementLoadError,
mysqlTableEngine,
originalMysqlTableEngine,
mysqlTableEngineLoading,
mysqlTableEngineLoadError,
tableOwner,
columns,
indexes,
@@ -3625,6 +3691,29 @@ watch([activeTab, ddlLoading], ([tab, loading]) => {
</Tooltip>
</div>
<div v-if="supportsMysqlEngine" class="flex shrink-0 items-center gap-2">
<label class="shrink-0 font-medium text-muted-foreground">{{ t("structureEditor.mysqlTableEngine") }}</label>
<SearchableSelect
v-model="mysqlTableEngine"
:options="mysqlTableEngineOptions"
:placeholder="t('structureEditor.mysqlTableEnginePlaceholder')"
:search-placeholder="t('structureEditor.mysqlTableEngineSearchPlaceholder')"
:empty-text="t('structureEditor.mysqlTableEngineEmpty')"
:loading-text="t('common.loading')"
:loading="mysqlTableEngineLoading"
:disabled="mysqlTableEngineLoading || !!mysqlTableEngineLoadError || saving"
:trigger-class="[structureMonoControlClass, 'w-[220px] max-w-[220px]']"
data-mysql-table-engine-select
/>
<Loader2 v-if="mysqlTableEngineLoading" :class="[structureIconClass, 'animate-spin text-muted-foreground']" />
<Tooltip v-else-if="mysqlTableEngineLoadError">
<TooltipTrigger as-child>
<AlertTriangle :class="[structureIconClass, 'shrink-0 text-destructive']" />
</TooltipTrigger>
<TooltipContent>{{ t("structureEditor.mysqlTableEngineLoadFailed", { message: mysqlTableEngineLoadError }) }}</TooltipContent>
</Tooltip>
</div>
<div v-if="canEditMysqlAutoIncrement" class="flex shrink-0 items-center gap-2">
<label class="shrink-0 font-medium text-muted-foreground">AUTO_INCREMENT</label>
<Input