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
+5
View File
@@ -3869,6 +3869,11 @@ export default {
editComment: "Edit comment",
commentPlaceholder: "Enter column comment...",
tableCommentPlaceholder: "Enter table comment...",
mysqlTableEngine: "Storage engine",
mysqlTableEnginePlaceholder: "Select a storage engine",
mysqlTableEngineSearchPlaceholder: "Search storage engines",
mysqlTableEngineEmpty: "No storage engines available",
mysqlTableEngineLoadFailed: "Failed to load storage engines: {message}",
owner: "Owner",
ownerPlaceholder: "Enter role name...",
ownerSearchPlaceholder: "Search or enter a role name...",
+5
View File
@@ -3684,6 +3684,11 @@ export default withEnglishFallback({
editComment: "Editar comentario",
commentPlaceholder: "Ingresar comentario de columna...",
tableCommentPlaceholder: "Ingresar comentario de tabla...",
mysqlTableEngine: "Motor de almacenamiento",
mysqlTableEnginePlaceholder: "Seleccionar un motor de almacenamiento",
mysqlTableEngineSearchPlaceholder: "Buscar motores de almacenamiento",
mysqlTableEngineEmpty: "No hay motores de almacenamiento disponibles",
mysqlTableEngineLoadFailed: "Error al cargar los motores de almacenamiento: {message}",
owner: "Propietario",
ownerPlaceholder: "Ingrese el nombre del rol...",
ownerSearchPlaceholder: "Buscar o ingresar un rol...",
+5
View File
@@ -3682,6 +3682,11 @@ export default withEnglishFallback({
editComment: "Modifica commento",
commentPlaceholder: "Inserisci commento colonna...",
tableCommentPlaceholder: "Inserisci commento tabella...",
mysqlTableEngine: "Motore di archiviazione",
mysqlTableEnginePlaceholder: "Seleziona un motore di archiviazione",
mysqlTableEngineSearchPlaceholder: "Cerca motori di archiviazione",
mysqlTableEngineEmpty: "Nessun motore di archiviazione disponibile",
mysqlTableEngineLoadFailed: "Impossibile caricare i motori di archiviazione: {message}",
owner: "Proprietario",
ownerPlaceholder: "Inserisci il nome del ruolo...",
ownerSearchPlaceholder: "Cerca o inserisci un ruolo...",
+5
View File
@@ -3744,6 +3744,11 @@ export default withEnglishFallback({
editComment: "コメントを編集",
commentPlaceholder: "列コメントを入力...",
tableCommentPlaceholder: "テーブルコメントを入力...",
mysqlTableEngine: "ストレージエンジン",
mysqlTableEnginePlaceholder: "ストレージエンジンを選択",
mysqlTableEngineSearchPlaceholder: "ストレージエンジンを検索",
mysqlTableEngineEmpty: "利用可能なストレージエンジンがありません",
mysqlTableEngineLoadFailed: "ストレージエンジンの読み込みに失敗しました: {message}",
owner: "所有者",
ownerPlaceholder: "ロール名を入力...",
ownerSearchPlaceholder: "ロール名を検索または入力...",
+5
View File
@@ -3684,6 +3684,11 @@ export default withEnglishFallback({
editComment: "Editar comentário",
commentPlaceholder: "Insira o comentário da coluna...",
tableCommentPlaceholder: "Insira o comentário da tabela...",
mysqlTableEngine: "Mecanismo de armazenamento",
mysqlTableEnginePlaceholder: "Selecionar um mecanismo de armazenamento",
mysqlTableEngineSearchPlaceholder: "Pesquisar mecanismos de armazenamento",
mysqlTableEngineEmpty: "Nenhum mecanismo de armazenamento disponível",
mysqlTableEngineLoadFailed: "Falha ao carregar os mecanismos de armazenamento: {message}",
owner: "Proprietário",
ownerPlaceholder: "Insira o nome da função...",
ownerSearchPlaceholder: "Pesquise ou insira uma função...",
+5
View File
@@ -3855,6 +3855,11 @@ export default withEnglishFallback({
editComment: "编辑注释",
commentPlaceholder: "输入字段注释...",
tableCommentPlaceholder: "输入表注释...",
mysqlTableEngine: "存储引擎",
mysqlTableEnginePlaceholder: "选择存储引擎",
mysqlTableEngineSearchPlaceholder: "搜索存储引擎",
mysqlTableEngineEmpty: "没有可用的存储引擎",
mysqlTableEngineLoadFailed: "加载存储引擎失败:{message}",
owner: "所有者",
ownerPlaceholder: "输入角色名...",
ownerSearchPlaceholder: "搜索或输入角色名...",
+5
View File
@@ -3425,6 +3425,11 @@ export default withEnglishFallback({
editComment: "編輯註解",
commentPlaceholder: "輸入欄位註解……",
tableCommentPlaceholder: "輸入資料表註解……",
mysqlTableEngine: "儲存引擎",
mysqlTableEnginePlaceholder: "選擇儲存引擎",
mysqlTableEngineSearchPlaceholder: "搜尋儲存引擎",
mysqlTableEngineEmpty: "沒有可用的儲存引擎",
mysqlTableEngineLoadFailed: "載入儲存引擎失敗:{message}",
owner: "擁有者",
ownerPlaceholder: "輸入角色名稱……",
ownerSearchPlaceholder: "搜尋或輸入角色名稱……",
@@ -0,0 +1,59 @@
import { describe, expect, it } from "vitest";
import type { QueryResult } from "@/types/database";
import { mysqlTableEngineDraft, mysqlTableEngineSqlOption, mysqlTableEngineSql, parseMysqlTableEngineMetadata, refreshMysqlTableEngineDraft, supportsMysqlTableEngine } from "@/lib/table/mysqlTableEngine";
function result(columns: string[], rows: QueryResult["rows"]): QueryResult {
return { columns, rows, affected_rows: 0, execution_time_ms: 1 };
}
describe("mysqlTableEngine", () => {
it("builds mode-independent metadata SQL for quoted and Unicode names", () => {
const sql = mysqlTableEngineSql("db'x", "订单\\archive");
expect(sql).toContain("TABLE_SCHEMA = CONVERT(X'64622778' USING utf8mb4)");
expect(sql).toContain("TABLE_NAME = CONVERT(X'e8aea2e58d955c61726368697665' USING utf8mb4)");
expect(sql).not.toContain("db'x");
});
it("keeps supported engines, the server default, and the current table engine", () => {
const engines = result(
["Engine", "Support", "Comment"],
[
["InnoDB", "DEFAULT", ""],
["MyISAM", "YES", ""],
["FEDERATED", "NO", ""],
],
);
const table = result(["engine"], [["FEDERATED"]]);
expect(parseMysqlTableEngineMetadata(engines, table)).toEqual({
currentEngine: "FEDERATED",
defaultEngine: "InnoDB",
engines: ["FEDERATED", "InnoDB", "MyISAM"],
});
});
it("uses the default for a new table and preserves a dirty edit across refresh", () => {
const initial = { currentEngine: "MyISAM", defaultEngine: "InnoDB", engines: ["InnoDB", "MyISAM"] };
expect(mysqlTableEngineDraft(initial, true)).toEqual({ value: "InnoDB", originalValue: "InnoDB" });
expect(mysqlTableEngineDraft(initial, false)).toEqual({ value: "MyISAM", originalValue: "MyISAM" });
expect(refreshMysqlTableEngineDraft({ ...initial, currentEngine: "InnoDB" }, { value: "ARCHIVE", originalValue: "MyISAM" }, false, true)).toEqual({
value: "ARCHIVE",
originalValue: "InnoDB",
});
});
it("sends the selected engine for creates and only changed engines for edits", () => {
expect(mysqlTableEngineSqlOption({ value: "InnoDB", originalValue: "InnoDB" }, true, true)).toBe("InnoDB");
expect(mysqlTableEngineSqlOption({ value: "innodb", originalValue: "InnoDB" }, false, true)).toBeUndefined();
expect(mysqlTableEngineSqlOption({ value: "MyISAM", originalValue: "InnoDB" }, false, true)).toBe("MyISAM");
expect(mysqlTableEngineSqlOption({ value: "MyISAM", originalValue: "InnoDB" }, false, false)).toBeUndefined();
});
it("is limited to native MySQL connections", () => {
expect(supportsMysqlTableEngine({ db_type: "mysql", driver_profile: "mysql" })).toBe(true);
expect(supportsMysqlTableEngine({ db_type: "mysql", driver_profile: undefined })).toBe(true);
expect(supportsMysqlTableEngine({ db_type: "jdbc", driver_profile: "mysql" })).toBe(false);
expect(supportsMysqlTableEngine({ db_type: "mysql", driver_profile: "goldendb" })).toBe(false);
});
});
@@ -0,0 +1,86 @@
import type { ConnectionConfig, QueryResult } from "@/types/database";
export interface MysqlTableEngineMetadata {
currentEngine?: string;
defaultEngine?: string;
engines: string[];
}
export interface MysqlTableEngineDraft {
value: string;
originalValue: string;
}
export const MYSQL_STORAGE_ENGINES_SQL = "SHOW ENGINES";
function mysqlUtf8Literal(value: string): string {
const bytes = new TextEncoder().encode(value);
const hex = Array.from(bytes, (byte) => byte.toString(16).padStart(2, "0")).join("");
return `CONVERT(X'${hex}' USING utf8mb4)`;
}
export function mysqlTableEngineSql(database: string, table: string): string {
return ["SELECT ENGINE AS engine", "FROM information_schema.TABLES", `WHERE TABLE_SCHEMA = ${mysqlUtf8Literal(database)}`, ` AND TABLE_NAME = ${mysqlUtf8Literal(table)}`, "LIMIT 1"].join("\n");
}
function columnIndex(result: QueryResult, name: string): number {
return result.columns.findIndex((column) => column.trim().toLowerCase() === name.toLowerCase());
}
function cellText(result: QueryResult, rowIndex: number, index: number): string {
if (index < 0) return "";
const value = result.rows[rowIndex]?.[index];
return value === null || value === undefined ? "" : String(value).trim();
}
export function parseMysqlTableEngineMetadata(enginesResult: QueryResult, tableResult?: QueryResult): MysqlTableEngineMetadata {
const engineIndex = columnIndex(enginesResult, "Engine");
const supportIndex = columnIndex(enginesResult, "Support");
const engines: string[] = [];
let defaultEngine: string | undefined;
const seen = new Set<string>();
for (let rowIndex = 0; rowIndex < enginesResult.rows.length; rowIndex += 1) {
const engine = cellText(enginesResult, rowIndex, engineIndex);
const support = cellText(enginesResult, rowIndex, supportIndex).toUpperCase();
if (!engine || (support !== "YES" && support !== "DEFAULT")) continue;
const key = engine.toLowerCase();
if (!seen.has(key)) {
engines.push(engine);
seen.add(key);
}
if (support === "DEFAULT") defaultEngine = engine;
}
let currentEngine: string | undefined;
if (tableResult) {
currentEngine = cellText(tableResult, 0, columnIndex(tableResult, "engine")) || undefined;
if (currentEngine && !seen.has(currentEngine.toLowerCase())) engines.unshift(currentEngine);
}
return { currentEngine, defaultEngine, engines };
}
export function mysqlTableEngineDraft(metadata: MysqlTableEngineMetadata, isCreateMode: boolean): MysqlTableEngineDraft {
const current = (isCreateMode ? metadata.defaultEngine || metadata.engines[0] : metadata.currentEngine) || "";
return { value: current, originalValue: current };
}
export function refreshMysqlTableEngineDraft(metadata: MysqlTableEngineMetadata, current: MysqlTableEngineDraft, isCreateMode: boolean, preserveDraft: boolean): MysqlTableEngineDraft {
const server = mysqlTableEngineDraft(metadata, isCreateMode);
if (!preserveDraft || current.value === current.originalValue) return server;
return { value: current.value, originalValue: server.originalValue };
}
export function mysqlTableEngineSqlOption(draft: MysqlTableEngineDraft, isCreateMode: boolean, supported: boolean): string | undefined {
const value = draft.value.trim();
if (!supported || !value) return undefined;
if (!isCreateMode && value.toLowerCase() === draft.originalValue.trim().toLowerCase()) return undefined;
return value;
}
export function supportsMysqlTableEngine(connection: Pick<ConnectionConfig, "db_type" | "driver_profile"> | undefined): boolean {
if (connection?.db_type !== "mysql") return false;
const profile = connection.driver_profile?.trim().toLowerCase();
return !profile || profile === "mysql";
}
@@ -83,6 +83,7 @@ export interface BuildTableStructureChangeSqlOptions {
triggers?: EditableStructureTrigger[];
tableComment?: string;
originalTableComment?: string;
mysqlEngine?: string;
/** The target table is a PostgreSQL partitioned parent (`relkind = 'p'`);
* the backend rejects `CREATE INDEX CONCURRENTLY` on such tables (fail
* closed) instead of downgrading to a blocking `CREATE INDEX`. */
+2
View File
@@ -1022,6 +1022,8 @@ export interface TableStructureEditorDraft {
originalTableComment: string;
mysqlAutoIncrementValue?: string;
originalMysqlAutoIncrementValue?: string;
mysqlTableEngine?: string;
originalMysqlTableEngine?: string;
tableOwner?: string;
originalTableOwner?: string;
columns: import("@/lib/table/tableStructureEditorSql").EditableStructureColumn[];