feat(ui): add View DDL for tables in sidebar and object browser
This commit is contained in:
parent
b1f38fd974
commit
11b6fc2d74
|
|
@ -0,0 +1,211 @@
|
|||
<script setup lang="ts">
|
||||
import { nextTick, onUnmounted, ref, shallowRef, watch } from "vue";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import { Clipboard, Loader2, RefreshCw } from "@lucide/vue";
|
||||
import { useToast } from "@/composables/useToast";
|
||||
import { useTheme } from "@/composables/useTheme";
|
||||
import { useSettingsStore } from "@/stores/settingsStore";
|
||||
import { loadEditorTheme, editorFontTheme } from "@/lib/editorThemes";
|
||||
import { copyToClipboard } from "@/lib/clipboard";
|
||||
import * as api from "@/lib/api";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||
import EditorSearchPanel from "@/components/editor/EditorSearchPanel.vue";
|
||||
import type { EditorView } from "@codemirror/view";
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
open: boolean;
|
||||
connectionId: string;
|
||||
database: string;
|
||||
schema?: string;
|
||||
tableName: string;
|
||||
/** SQL dialect for syntax highlighting. Non-PG/non-MSSQL databases fall back to MySQL (same as QueryEditor's source viewer). */
|
||||
dialect: "mysql" | "postgres" | "sqlserver";
|
||||
}>(),
|
||||
{},
|
||||
);
|
||||
|
||||
const emit = defineEmits<{
|
||||
"update:open": [value: boolean];
|
||||
}>();
|
||||
|
||||
const { t } = useI18n();
|
||||
const { toast } = useToast();
|
||||
const { isDark } = useTheme();
|
||||
const settingsStore = useSettingsStore();
|
||||
|
||||
const ddlContent = ref("");
|
||||
const ddlLoading = ref(false);
|
||||
const ddlError = ref("");
|
||||
const ddlEditorContainer = ref<HTMLDivElement>();
|
||||
const ddlSearchPanelRef = ref<InstanceType<typeof EditorSearchPanel>>();
|
||||
const ddlEditorView = shallowRef<EditorView | null>(null);
|
||||
|
||||
/** Fetches the table DDL when the dialog opens. */
|
||||
watch(
|
||||
() => props.open,
|
||||
async (open) => {
|
||||
if (!open) return;
|
||||
ddlContent.value = "";
|
||||
ddlError.value = "";
|
||||
ddlLoading.value = true;
|
||||
try {
|
||||
const schema = props.schema || props.database;
|
||||
const ddl = await api.getTableDdl(props.connectionId, props.database, schema, props.tableName);
|
||||
ddlContent.value = ddl;
|
||||
} catch (e: any) {
|
||||
ddlError.value = e?.message || String(e);
|
||||
} finally {
|
||||
ddlLoading.value = false;
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
);
|
||||
|
||||
/**
|
||||
* Creates a lightweight read-only CodeMirror editor inside the dialog.
|
||||
*
|
||||
* Follows the same pattern as QueryEditor:
|
||||
* - cmSearch with hidden createPanel replaces the default search UI,
|
||||
* so EditorSearchPanel is the only visible search panel.
|
||||
* - Prec.highest overrides Cmd+F to open EditorSearchPanel.
|
||||
* - Editor theme/font are loaded from user settings for consistent appearance.
|
||||
*/
|
||||
async function initDdlEditor(content: string) {
|
||||
if (!ddlEditorContainer.value) return;
|
||||
destroyDdlEditor();
|
||||
const [{ EditorView, keymap }, { EditorState, Prec }, langSql, { basicSetup }, { search: cmSearch }] = await Promise.all([import("@codemirror/view"), import("@codemirror/state"), import("@codemirror/lang-sql"), import("codemirror"), import("@codemirror/search")]);
|
||||
const editorTheme = settingsStore.editorSettings.theme;
|
||||
const appAppearance = isDark.value ? "dark" : "light";
|
||||
const fontSize = settingsStore.editorSettings.fontSize;
|
||||
const fontFamily = settingsStore.editorSettings.fontFamily;
|
||||
const themeExt = await loadEditorTheme(editorTheme, appAppearance);
|
||||
const fontExt = editorFontTheme(EditorView, fontSize, fontFamily, { fixedHeight: true, scrollable: true });
|
||||
const baseDialect = props.dialect === "postgres" ? langSql.PostgreSQL : props.dialect === "sqlserver" ? langSql.MSSQL : langSql.MySQL;
|
||||
const dialect = langSql.SQLDialect.define({ ...baseDialect.spec });
|
||||
const state = EditorState.create({
|
||||
doc: content,
|
||||
extensions: [
|
||||
// Enable search functionality but hide the default panel —
|
||||
// EditorSearchPanel provides the visible search UI instead, same as QueryEditor.
|
||||
cmSearch({
|
||||
top: true,
|
||||
createPanel: () => {
|
||||
const dom = document.createElement("span");
|
||||
dom.style.display = "none";
|
||||
return { dom };
|
||||
},
|
||||
}),
|
||||
basicSetup,
|
||||
langSql.sql({ dialect }),
|
||||
themeExt,
|
||||
fontExt,
|
||||
// Intercept Cmd+F at highest precedence so EditorSearchPanel opens
|
||||
// instead of the default search panel (which is hidden above).
|
||||
Prec.highest(keymap.of([{ key: "Mod-f", run: () => ddlSearchPanelRef.value?.openSearch() ?? false, preventDefault: true }])),
|
||||
// Remove CodeMirror's default 1px dotted focus outline,
|
||||
// which is visible below the content when the DDL is short.
|
||||
EditorView.theme({
|
||||
"&.cm-focused": { outline: "none" },
|
||||
}),
|
||||
EditorState.readOnly.of(true),
|
||||
],
|
||||
});
|
||||
const editorView = new EditorView({ state, parent: ddlEditorContainer.value });
|
||||
ddlEditorView.value = editorView;
|
||||
editorView.focus();
|
||||
}
|
||||
|
||||
/** Tears down the CodeMirror instance when the dialog closes. */
|
||||
function destroyDdlEditor() {
|
||||
ddlEditorView.value?.destroy();
|
||||
ddlEditorView.value = null;
|
||||
}
|
||||
|
||||
/** Copies the DDL text to clipboard. */
|
||||
function copyDdlContent() {
|
||||
if (ddlContent.value) {
|
||||
copyToClipboard(ddlContent.value);
|
||||
toast(t("contextMenu.ddlCopied"), 2000);
|
||||
}
|
||||
}
|
||||
|
||||
// When DDL finishes loading, create the editor inside the dialog.
|
||||
watch(ddlLoading, (loading) => {
|
||||
if (!loading && ddlContent.value && props.open) {
|
||||
nextTick(() => initDdlEditor(ddlContent.value));
|
||||
}
|
||||
});
|
||||
|
||||
// Destroy CodeMirror when the dialog hides, so the editor's event listeners
|
||||
// and DOM aren't consuming resources while the dialog is closed.
|
||||
// The editor is re-created on next open via the ddlLoading watch.
|
||||
watch(
|
||||
() => props.open,
|
||||
(open) => {
|
||||
if (!open) destroyDdlEditor();
|
||||
},
|
||||
);
|
||||
|
||||
// Safety net: destroy editor when component eventually unmounts.
|
||||
onUnmounted(() => {
|
||||
destroyDdlEditor();
|
||||
});
|
||||
|
||||
function retry() {
|
||||
ddlError.value = "";
|
||||
ddlLoading.value = true;
|
||||
ddlContent.value = "";
|
||||
const schema = props.schema || props.database;
|
||||
api
|
||||
.getTableDdl(props.connectionId, props.database, schema, props.tableName)
|
||||
.then((ddl) => {
|
||||
ddlContent.value = ddl;
|
||||
})
|
||||
.catch((e: any) => {
|
||||
ddlError.value = e?.message || String(e);
|
||||
})
|
||||
.finally(() => {
|
||||
ddlLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function onClose() {
|
||||
emit("update:open", false);
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Dialog :open="props.open" @update:open="onClose">
|
||||
<DialogContent class="sm:max-w-190">
|
||||
<DialogHeader>
|
||||
<DialogTitle>DDL - {{ props.tableName }}</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div class="grid gap-3">
|
||||
<div v-if="ddlLoading" class="flex min-h-80 items-center justify-center gap-2 text-sm text-muted-foreground">
|
||||
<Loader2 class="h-4 w-4 animate-spin" />
|
||||
<span>{{ t("contextMenu.viewDdlLoading") }}</span>
|
||||
</div>
|
||||
<div v-else-if="ddlError" class="flex min-h-80 flex-col items-center justify-center gap-3 text-sm">
|
||||
<p class="text-destructive">{{ ddlError }}</p>
|
||||
<Button variant="outline" size="sm" @click="retry">
|
||||
<RefreshCw />
|
||||
{{ t("common.retry") }}
|
||||
</Button>
|
||||
</div>
|
||||
<div v-else class="relative min-h-80 max-h-[60vh] overflow-hidden rounded border">
|
||||
<div ref="ddlEditorContainer" class="h-full" />
|
||||
<EditorSearchPanel v-if="ddlEditorView" ref="ddlSearchPanelRef" :view="ddlEditorView" />
|
||||
</div>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" @click="onClose">{{ t("common.close") }}</Button>
|
||||
<Button variant="outline" :disabled="!ddlContent" @click="copyDdlContent">
|
||||
<Clipboard class="h-4 w-4" />
|
||||
{{ t("grid.copyDdl") }}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
|
@ -48,7 +48,7 @@ import type { ConnectionConfig, ForeignKeyInfo, ObjectInfo, ObjectSourceKind } f
|
|||
import { sortTablesByFkDependency, type TableWithFk } from "@/lib/tableDependencySort";
|
||||
import { isSchemaAware } from "@/lib/databaseCapabilities";
|
||||
import { supportsSchemaDiagram, supportsTableImport, supportsTableStructureEditing, supportsTableTruncate } from "@/lib/databaseFeatureSupport";
|
||||
import { connectionUsesDatabaseObjectTreeMode, effectiveDatabaseTypeForConnection, tableStructureDatabaseTypeForConnection } from "@/lib/jdbcDialect";
|
||||
import { codeMirrorSqlDialect, connectionUsesDatabaseObjectTreeMode, effectiveDatabaseTypeForConnection, tableStructureDatabaseTypeForConnection } from "@/lib/jdbcDialect";
|
||||
import { buildTableSelectSql } from "@/lib/tableSelectSql";
|
||||
import { buildDropObjectSql, buildDuplicateTableStructureSql, buildEmptyTableSql, buildTruncateTableSql, type TableAdminSqlOptions } from "@/lib/dbAdminSql";
|
||||
import { useToast } from "@/composables/useToast";
|
||||
|
|
@ -65,6 +65,7 @@ import { useExportTracker, type ExportTask } from "@/composables/useExportTracke
|
|||
import { useSettingsStore } from "@/stores/settingsStore";
|
||||
import { useQueryStore } from "@/stores/queryStore";
|
||||
import QueryEditor from "@/components/editor/QueryEditor.vue";
|
||||
import DdlViewDialog from "./DdlViewDialog.vue";
|
||||
import type { SqlFormatDialect } from "@/lib/sqlFormatter";
|
||||
import { isCancelSearchShortcut } from "@/lib/keyboardShortcuts";
|
||||
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||
|
|
@ -130,6 +131,8 @@ const duplicateTarget = ref<ObjectBrowserRow | null>(null);
|
|||
const duplicateTableName = ref("");
|
||||
const showProcedureExecutionConfirm = ref(false);
|
||||
const procedureExecutionTarget = ref<ObjectBrowserRow | null>(null);
|
||||
const ddlDialogTarget = ref<ObjectBrowserRow | null>(null);
|
||||
const showDdlDialog = ref(false);
|
||||
const selectedTableIds = ref<Set<string>>(new Set());
|
||||
const expandedPartitionParentIds = ref<Set<string>>(new Set());
|
||||
const showBatchDropConfirm = ref(false);
|
||||
|
|
@ -150,11 +153,7 @@ const canOpenStructureEditor = computed(() => supportsTableStructureEditing(tabl
|
|||
const canOpenDiagram = computed(() => !!props.database && supportsSchemaDiagram(effectiveDatabaseType.value));
|
||||
const canOpenTableImport = computed(() => !!props.database && supportsTableImport(effectiveDatabaseType.value));
|
||||
const supportsTruncateTable = computed(() => supportsTableTruncate(effectiveDatabaseType.value));
|
||||
const sourceDialect = computed<"mysql" | "postgres" | "sqlserver">(() => {
|
||||
if (effectiveDatabaseType.value === "postgres" || effectiveDatabaseType.value === "gaussdb" || effectiveDatabaseType.value === "kwdb" || effectiveDatabaseType.value === "opengauss") return "postgres";
|
||||
if (effectiveDatabaseType.value === "sqlserver") return "sqlserver";
|
||||
return "mysql";
|
||||
});
|
||||
const sourceDialect = computed(() => codeMirrorSqlDialect(effectiveDatabaseType.value));
|
||||
const sourceFormatDialect = computed<SqlFormatDialect>(() => {
|
||||
switch (effectiveDatabaseType.value) {
|
||||
case "mysql":
|
||||
|
|
@ -1127,6 +1126,14 @@ function exportDataSubmenu(item: ObjectBrowserRow): ContextMenuItem {
|
|||
function getTableMenuItems(item: ObjectBrowserRow): ContextMenuItem[] {
|
||||
return [
|
||||
{ label: t("contextMenu.viewData"), action: () => openRow(item), icon: Table2 },
|
||||
{
|
||||
label: t("contextMenu.viewDdl"),
|
||||
action: () => {
|
||||
ddlDialogTarget.value = item;
|
||||
showDdlDialog.value = true;
|
||||
},
|
||||
icon: FileCode,
|
||||
},
|
||||
...(canOpenStructureEditor.value ? [{ label: t("contextMenu.editStructure"), action: () => openStructureEditor(item), icon: PencilRuler }] : []),
|
||||
...(canRename(item) ? [{ label: t("contextMenu.renameObject"), action: () => requestRename(item), icon: Pencil }] : []),
|
||||
{ label: t("contextMenu.newQuery"), action: () => openNewQuery(item), icon: TerminalSquare },
|
||||
|
|
@ -1483,6 +1490,8 @@ function getObjectBrowserMenuItems(item: ObjectBrowserRow): ContextMenuItem[] {
|
|||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
<DdlViewDialog v-if="ddlDialogTarget" :connection-id="props.connection.id" :database="props.database" :schema="ddlDialogTarget.schema || selectedSchema" :table-name="ddlDialogTarget.name" :dialect="sourceDialect" v-model:open="showDdlDialog" />
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
|
|
|||
|
|
@ -92,8 +92,9 @@ import {
|
|||
import { buildRenameObjectSql, supportsObjectRename, type RenameableObjectType } from "@/lib/objectRenameSql";
|
||||
import { buildRoutineRenameObjectSourceStatements, supportsSourceBackedRoutineRename } from "@/lib/objectSourceEditor";
|
||||
import { buildViewDdl } from "@/lib/viewDdl";
|
||||
import DdlViewDialog from "@/components/objects/DdlViewDialog.vue";
|
||||
import { getTableStructureCapabilities } from "@/lib/tableStructureCapabilities";
|
||||
import { connectionObjectTreeNodeSchema, connectionObjectTreeQuerySchema, connectionUsesDatabaseObjectTreeMode, effectiveDatabaseTypeForConnection, tableStructureDatabaseTypeForConnection } from "@/lib/jdbcDialect";
|
||||
import { codeMirrorSqlDialect, connectionObjectTreeNodeSchema, connectionObjectTreeQuerySchema, connectionUsesDatabaseObjectTreeMode, effectiveDatabaseTypeForConnection, tableStructureDatabaseTypeForConnection } from "@/lib/jdbcDialect";
|
||||
import { hexToRgba } from "@/lib/color";
|
||||
import { focusSidebarRenameInput } from "@/lib/sidebarRenameFocus";
|
||||
import { hasTreeNodeDatabaseContext } from "@/lib/treeNodeContext";
|
||||
|
|
@ -1207,6 +1208,12 @@ const showDuplicateDialog = ref(false);
|
|||
const duplicateTableName = ref("");
|
||||
const duplicateStructureSource = ref<DuplicateStructureSource | null>(null);
|
||||
|
||||
const ddlTarget = ref<TreeNode | null>(null);
|
||||
const showDdlDialog = ref(false);
|
||||
const ddlDialect = computed(() => {
|
||||
if (!ddlTarget.value?.connectionId) return "mysql";
|
||||
return codeMirrorSqlDialect(effectiveDatabaseTypeForConnection(connectionStore.getConfig(ddlTarget.value.connectionId)));
|
||||
});
|
||||
const showCreateDatabaseDialog = ref(false);
|
||||
const createDatabaseName = ref("");
|
||||
const createDatabaseCharset = ref("utf8mb4");
|
||||
|
|
@ -3207,6 +3214,16 @@ function treeItemMenuItems(): ContextMenuItem[] {
|
|||
items.push({ label: t("contextMenu.copyName"), action: copyName, icon: Copy, shortcut: shortcutCopyName.value });
|
||||
items.push({ label: "", separator: true });
|
||||
items.push({ label: t("contextMenu.viewData"), action: openData, icon: TableProperties });
|
||||
if (node.type === "table") {
|
||||
items.push({
|
||||
label: t("contextMenu.viewDdl"),
|
||||
action: () => {
|
||||
ddlTarget.value = node;
|
||||
showDdlDialog.value = true;
|
||||
},
|
||||
icon: FileCode,
|
||||
});
|
||||
}
|
||||
if (node.type === "view") {
|
||||
items.push({ label: t("contextMenu.editView"), action: viewObjectSource, icon: Pencil });
|
||||
items.push({ label: t("contextMenu.viewSource"), action: viewObjectSource, icon: Code2 });
|
||||
|
|
@ -3707,6 +3724,8 @@ function treeItemMenuItems(): ContextMenuItem[] {
|
|||
</Dialog>
|
||||
|
||||
<DangerConfirmDialog v-model:open="showDropSchemaConfirm" :title="t('contextMenu.confirmDropSchemaTitle')" :message="t('contextMenu.confirmDropSchemaMessage', { name: node.label })" :sql="dropSchemaPreviewSql" :confirm-label="t('contextMenu.dropSchema')" @confirm="confirmDropSchema" />
|
||||
|
||||
<DdlViewDialog v-if="ddlTarget" :connection-id="ddlTarget.connectionId!" :database="ddlTarget.database!" :schema="ddlTarget.schema" :table-name="ddlTarget.label" :dialect="ddlDialect" v-model:open="showDdlDialog" />
|
||||
</template>
|
||||
|
||||
<style>
|
||||
|
|
|
|||
|
|
@ -807,6 +807,7 @@
|
|||
close: "Close",
|
||||
cancel: "Cancel",
|
||||
save: "Save",
|
||||
retry: "Retry",
|
||||
more: "More",
|
||||
},
|
||||
explain: {
|
||||
|
|
@ -1116,6 +1117,8 @@
|
|||
editView: "Edit View",
|
||||
viewSource: "View Source",
|
||||
viewDdl: "View DDL",
|
||||
viewDdlLoading: "Loading DDL...",
|
||||
ddlCopied: "DDL copied",
|
||||
dropObject: "Drop Object",
|
||||
dropView: "Drop View",
|
||||
dropColumn: "Drop Column",
|
||||
|
|
|
|||
|
|
@ -694,6 +694,7 @@
|
|||
loading: "Cargando...",
|
||||
stopping: "Deteniendo...",
|
||||
close: "Cerrar",
|
||||
retry: "Reintentar",
|
||||
more: "Más",
|
||||
},
|
||||
explain: {
|
||||
|
|
@ -946,6 +947,8 @@
|
|||
editView: "Editar vista",
|
||||
viewSource: "Ver código fuente",
|
||||
viewDdl: "Ver DDL",
|
||||
viewDdlLoading: "Cargando DDL...",
|
||||
ddlCopied: "DDL copiado",
|
||||
dropObject: "Eliminar objeto",
|
||||
dropView: "Eliminar vista",
|
||||
dropColumn: "Eliminar columna",
|
||||
|
|
|
|||
|
|
@ -754,6 +754,7 @@
|
|||
loading: "Caricamento...",
|
||||
stopping: "Interruzione...",
|
||||
close: "Chiudi",
|
||||
retry: "Riprova",
|
||||
more: "Altro",
|
||||
},
|
||||
explain: {
|
||||
|
|
@ -1058,6 +1059,8 @@
|
|||
editView: "Modifica Vista",
|
||||
viewSource: "Visualizza Origine",
|
||||
viewDdl: "Visualizza DDL",
|
||||
viewDdlLoading: "Caricamento DDL...",
|
||||
ddlCopied: "DDL copiato",
|
||||
dropObject: "Elimina Oggetto",
|
||||
dropView: "Elimina Vista",
|
||||
dropColumn: "Elimina Colonna",
|
||||
|
|
|
|||
|
|
@ -754,6 +754,7 @@
|
|||
loading: "Carregando...",
|
||||
stopping: "Parando...",
|
||||
close: "Fechar",
|
||||
retry: "Tentar novamente",
|
||||
more: "Mais",
|
||||
},
|
||||
explain: {
|
||||
|
|
@ -1058,6 +1059,8 @@
|
|||
editView: "Editar Visão",
|
||||
viewSource: "Ver Código-fonte",
|
||||
viewDdl: "Ver DDL",
|
||||
viewDdlLoading: "Carregando DDL...",
|
||||
ddlCopied: "DDL copiado",
|
||||
dropObject: "Remover Objeto",
|
||||
dropView: "Remover Visão",
|
||||
dropColumn: "Remover Coluna",
|
||||
|
|
|
|||
|
|
@ -806,6 +806,7 @@
|
|||
close: "关闭",
|
||||
cancel: "取消",
|
||||
save: "保存",
|
||||
retry: "重试",
|
||||
more: "更多",
|
||||
},
|
||||
explain: {
|
||||
|
|
@ -1115,6 +1116,8 @@
|
|||
editView: "编辑视图",
|
||||
viewSource: "查看源码",
|
||||
viewDdl: "查看 DDL",
|
||||
viewDdlLoading: "正在读取 DDL...",
|
||||
ddlCopied: "DDL 已复制",
|
||||
dropObject: "删除对象",
|
||||
dropView: "删除视图",
|
||||
dropColumn: "删除字段",
|
||||
|
|
|
|||
|
|
@ -733,6 +733,7 @@
|
|||
loading: "載入中……",
|
||||
stopping: "正在停止……",
|
||||
close: "關閉",
|
||||
retry: "重試",
|
||||
more: "更多",
|
||||
},
|
||||
explain: {
|
||||
|
|
@ -1037,6 +1038,8 @@
|
|||
editView: "編輯檢視",
|
||||
viewSource: "檢視原始碼",
|
||||
viewDdl: "檢視 DDL",
|
||||
viewDdlLoading: "正在讀取 DDL...",
|
||||
ddlCopied: "DDL 已複製",
|
||||
dropObject: "刪除物件",
|
||||
dropView: "刪除檢視",
|
||||
dropColumn: "刪除欄位",
|
||||
|
|
|
|||
|
|
@ -69,3 +69,10 @@ export function connectionObjectTreeNodeSchema(connection: JdbcDialectConnection
|
|||
const type = effectiveDatabaseTypeForConnection(connection);
|
||||
return isSchemaAware(type) ? database : undefined;
|
||||
}
|
||||
|
||||
/** Maps a database type to the corresponding CodeMirror SQL dialect name used by QueryEditor and DdlViewDialog. */
|
||||
export function codeMirrorSqlDialect(dbType: DatabaseType | undefined): "mysql" | "postgres" | "sqlserver" {
|
||||
if (dbType === "postgres" || dbType === "gaussdb" || dbType === "kwdb" || dbType === "opengauss") return "postgres";
|
||||
if (dbType === "sqlserver") return "sqlserver";
|
||||
return "mysql";
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue