fix(gbase): support 8a and 8s profiles

This commit is contained in:
t8y2 committed 2026-06-14 10:16:24 +08:00
1 parent 213162aad3
commit cbc7f9b0db
41 files changed
+596 -50

No files matched your search

@@ -129,6 +129,7 @@ const defaultForm = (): ConnectionForm => ({
redis_cluster_nodes: "",
redis_key_separator: ":",
etcd_endpoints: "",
gbase_server: "",
read_only: false,
visible_databases: undefined,
});
@@ -388,7 +389,8 @@ const driverProfiles: Record<
vertica: { type: "vertica", port: 5433, user: "dbadmin", label: "Vertica", icon: "vertica" },
firebird: { type: "firebird", port: 3050, user: "SYSDBA", label: "Firebird", icon: "firebird" },
exasol: { type: "exasol", port: 8563, user: "sys", label: "Exasol", icon: "exasol" },
gbase: { type: "gbase", port: 5258, user: "gbasedbt", label: "GBase", icon: "gbase" },
gbase: { type: "gbase", port: 5258, user: "gbasedbt", label: "GBase 8a", icon: "gbase" },
gbase8a: { type: "gbase", port: 5258, user: "gbasedbt", label: "GBase 8a", icon: "gbase" },
gbase8s: { type: "gbase", port: 9088, user: "gbasedbt", label: "GBase 8s", icon: "gbase" },
opengauss: {
type: "opengauss",
@@ -545,7 +547,7 @@ function switchOceanbaseMode(mode: "mysql" | "oracle") {
resetTestState();
}
function switchGbaseProfile(profile: "gbase" | "gbase8s") {
function switchGbaseProfile(profile: "gbase8a" | "gbase8s") {
applyProfile(profile, false);
selectedType.value = "gbase";
resetTestState();
@@ -603,7 +605,7 @@ watch(
if (profile === "oceanbase") {
oceanbaseSubMode.value = config.driver_profile === "oceanbase-oracle" ? "oracle" : "mysql";
}
if (profile === "gbase8s") {
if (profile === "gbase8a" || profile === "gbase8s") {
selectedType.value = "gbase";
}
mongoUseUrl.value = !!config.connection_string;
@@ -1590,7 +1592,7 @@ function applyConnectionPrefill(draft: ConnectionDeepLinkDraft) {
oceanbaseSubMode.value = "oracle";
selectedType.value = "oceanbase";
}
if (draft.driverProfile === "gbase8s") {
if (draft.driverProfile === "gbase8a" || draft.driverProfile === "gbase8s") {
selectedType.value = "gbase";
}
customDriverName.value = isCustomCompatibleProfile() ? draft.driverLabel : "";
@@ -2184,7 +2186,7 @@ function openExternalUrl(url: string) {
<div v-if="selectedType === 'gbase'" class="grid grid-cols-4 items-center gap-4">
<Label class="text-right text-xs">{{ t("connection.version") }}</Label>
<div class="col-span-3 flex gap-2">
<Button size="sm" :variant="form.driver_profile === 'gbase8s' ? 'outline' : 'default'" @click="switchGbaseProfile('gbase')"> GBase </Button>
<Button size="sm" :variant="form.driver_profile === 'gbase8s' ? 'outline' : 'default'" @click="switchGbaseProfile('gbase8a')"> GBase 8a </Button>
<Button size="sm" :variant="form.driver_profile === 'gbase8s' ? 'default' : 'outline'" @click="switchGbaseProfile('gbase8s')"> GBase 8s </Button>
</div>
</div>
@@ -2595,6 +2597,11 @@ function openExternalUrl(url: string) {
<Input v-model.number="form.port" type="number" class="col-span-1" />
</div>
<div v-if="form.driver_profile === 'gbase8s'" class="grid grid-cols-4 items-center gap-4">
<Label class="text-right text-xs">{{ t("connection.gbaseServer") }}</Label>
<Input v-model="form.gbase_server" class="col-span-3" placeholder="gbase01" />
</div>
<div class="grid grid-cols-4 items-center gap-4">
<Label class="text-right">{{ t("connection.user") }}</Label>
<Input v-model="form.username" class="col-span-3" />
@@ -66,6 +66,7 @@ const assetIcons: Record<string, string> = {
firebird: "firebird.webp",
exasol: "exasol.webp",
gbase: "gbase.webp",
gbase8a: "gbase.webp",
gbase8s: "gbase.webp",
tdsql: "tdsql.webp",
polardb: "polardb.webp",
@@ -643,6 +643,11 @@ defineExpose({ focusSearch, refreshData, handleModRTarget });
{{ t(queryExecutionLabelKey(activeTab)) }}
<span class="ml-1 tabular-nums text-muted-foreground/80">· {{ queryRunningElapsedSeconds }}s</span>
</div>
<Button variant="destructive" size="sm" class="h-7 gap-1.5" :disabled="!canCancelQueryExecution(activeTab)" @click="emit('cancel')">
<Loader2 v-if="activeTab.isCancelling" class="h-3.5 w-3.5 animate-spin" />
<Square v-else class="h-3.5 w-3.5 fill-current" />
{{ t("toolbar.stopQuery") }}
</Button>
</div>
<div v-else-if="!activeTab.result" class="flex-1 min-h-0 flex flex-col items-center justify-center gap-1 text-muted-foreground text-sm">
<div>{{ t("editor.pressToExecute", { mod: shortcutModifier }) }}</div>
@@ -47,7 +47,7 @@ import * as api from "@/lib/api";
import type { ConnectionConfig, ObjectInfo, ObjectSourceKind } from "@/types/database";
import { isSchemaAware } from "@/lib/databaseCapabilities";
import { supportsSchemaDiagram, supportsTableImport, supportsTableStructureEditing, supportsTableTruncate } from "@/lib/databaseFeatureSupport";
import { connectionUsesDatabaseObjectTreeMode, effectiveDatabaseTypeForConnection } from "@/lib/jdbcDialect";
import { 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";
@@ -106,6 +106,7 @@ const sourceError = ref("");
const sourceRow = ref<ObjectBrowserRow | null>(null);
const sourceEditing = ref(false);
const effectiveDatabaseType = computed(() => effectiveDatabaseTypeForConnection(props.connection) ?? props.connection.db_type);
const tableStructureDatabaseType = computed(() => tableStructureDatabaseTypeForConnection(props.connection) ?? props.connection.db_type);
const sourceDraft = ref("");
const sourceSaving = ref(false);
const sourceSaveError = ref("");
@@ -144,7 +145,7 @@ const procedureCount = computed(() => rows.value.filter((row) => row.type === "P
const functionCount = computed(() => rows.value.filter((row) => row.type === "FUNCTION").length);
const sequenceCount = computed(() => rows.value.filter((row) => row.type === "SEQUENCE").length);
const packageCount = computed(() => rows.value.filter((row) => row.type === "PACKAGE" || row.type === "PACKAGE_BODY").length);
const canOpenStructureEditor = computed(() => supportsTableStructureEditing(effectiveDatabaseType.value));
const canOpenStructureEditor = computed(() => supportsTableStructureEditing(tableStructureDatabaseType.value));
const canOpenDiagram = computed(() => !!props.database && supportsSchemaDiagram(effectiveDatabaseType.value));
const canOpenTableImport = computed(() => !!props.database && supportsTableImport(effectiveDatabaseType.value));
const supportsTruncateTable = computed(() => supportsTableTruncate(effectiveDatabaseType.value));
@@ -326,7 +326,9 @@ async function ensureTreeLoadedForTarget(target: ActiveTabSidebarTarget, opts?:
if (!dbNode) return;
const targetSchema = "schema" in target ? target.schema : undefined;
const databaseChildrenLoaded = !!dbNode.children && dbNode.children.length > 0;
const shouldLoadSchemaTables = target.type === "table" && !!targetSchema && usesTreeSchemaMode(config.db_type) && !connectionUsesDatabaseObjectTreeMode(config);
const effectiveDbType = effectiveDatabaseTypeForConnection(config);
const usesSchemaTree = usesTreeSchemaMode(effectiveDbType) && !connectionUsesDatabaseObjectTreeMode(config);
const shouldLoadSchemaTables = target.type === "table" && !!targetSchema && usesSchemaTree;
if (!force && databaseChildrenLoaded && !shouldLoadSchemaTables) return;
// Load database contents
@@ -335,7 +337,7 @@ async function ensureTreeLoadedForTarget(target: ActiveTabSidebarTarget, opts?:
if (force || !databaseChildrenLoaded) {
await store.loadSqlServerDatabaseObjects(connId, target.database, loadOptions);
}
} else if (usesTreeSchemaMode(config.db_type) && !connectionUsesDatabaseObjectTreeMode(config)) {
} else if (usesSchemaTree) {
if (force || !databaseChildrenLoaded) {
await store.loadSchemas(connId, target.database, loadOptions);
}
@@ -91,7 +91,7 @@ import { buildRenameObjectSql, supportsObjectRename, type RenameableObjectType }
import { buildRoutineRenameObjectSourceStatements, supportsSourceBackedRoutineRename } from "@/lib/objectSourceEditor";
import { buildViewDdl } from "@/lib/viewDdl";
import { getTableStructureCapabilities } from "@/lib/tableStructureCapabilities";
import { connectionObjectTreeNodeSchema, connectionObjectTreeQuerySchema, connectionUsesDatabaseObjectTreeMode, effectiveDatabaseTypeForConnection } from "@/lib/jdbcDialect";
import { connectionObjectTreeNodeSchema, connectionObjectTreeQuerySchema, connectionUsesDatabaseObjectTreeMode, effectiveDatabaseTypeForConnection, tableStructureDatabaseTypeForConnection } from "@/lib/jdbcDialect";
import { hexToRgba } from "@/lib/color";
import { focusSidebarRenameInput } from "@/lib/sidebarRenameFocus";
import { hasTreeNodeDatabaseContext } from "@/lib/treeNodeContext";
@@ -164,6 +164,10 @@ function currentDatabaseType(): DatabaseType | undefined {
return props.node.connectionId ? effectiveDatabaseTypeForConnection(connectionStore.getConfig(props.node.connectionId)) : undefined;
}
function currentTableStructureDatabaseType(): DatabaseType | undefined {
return props.node.connectionId ? tableStructureDatabaseTypeForConnection(connectionStore.getConfig(props.node.connectionId)) : undefined;
}
function rawDatabaseType(): DatabaseType | undefined {
return props.node.connectionId ? connectionStore.getConfig(props.node.connectionId)?.db_type : undefined;
}
@@ -172,6 +176,10 @@ function databaseTypeForNode(node: TreeNode): DatabaseType | undefined {
return node.connectionId ? effectiveDatabaseTypeForConnection(connectionStore.getConfig(node.connectionId)) : undefined;
}
function tableStructureDatabaseTypeForNode(node: TreeNode): DatabaseType | undefined {
return node.connectionId ? tableStructureDatabaseTypeForConnection(connectionStore.getConfig(node.connectionId)) : undefined;
}
function hasNodeDatabaseContext(node: TreeNode): node is TreeNode & { connectionId: string; database: string } {
return !!node.connectionId && hasTreeNodeDatabaseContext(node);
}
@@ -361,9 +369,10 @@ async function toggle() {
queryStore.updateSql(tab, node.label);
} else if (node.type === "database" && node.connectionId && hasTreeNodeDatabaseContext(node)) {
const config = connectionStore.getConfig(node.connectionId);
const effectiveDbType = effectiveDatabaseTypeForConnection(config);
if (config?.db_type === "sqlserver") {
await connectionStore.loadSqlServerDatabaseObjects(node.connectionId, node.database);
} else if (usesTreeSchemaMode(config?.db_type) && !connectionUsesDatabaseObjectTreeMode(config)) {
} else if (usesTreeSchemaMode(effectiveDbType) && !connectionUsesDatabaseObjectTreeMode(config)) {
await connectionStore.loadSchemas(node.connectionId, node.database);
} else {
await connectionStore.loadTables(node.connectionId, node.database);
@@ -701,6 +710,20 @@ async function openData() {
return queryStore.createTab(node.connectionId, node.database, node.label, "data", tableSchema);
})();
console.info("[DBX][openData:tab-created]", { traceId, tabId, elapsed: elapsed() });
// Cancel any previous execution on this tab before starting a new one
const existingTab = queryStore.tabs.find((t) => t.id === tabId);
if (existingTab?.isExecuting && existingTab.executionId) {
await queryStore.cancelTabExecution(tabId);
}
const openDataId = uuid();
// Clear previous result so DataGrid doesn't show its internal loading overlay (without stop button)
const tab = queryStore.tabs.find((t) => t.id === tabId);
if (tab) {
tab.result = undefined;
tab.results = undefined;
}
queryStore.setTableMeta(tabId, {
schema: tableSchema,
tableName: node.label,
@@ -708,11 +731,15 @@ async function openData() {
columns: [],
primaryKeys: [],
});
queryStore.setExecuting(tabId, true);
queryStore.setExecutingWithId(tabId, openDataId);
// Helper to check if this openData call is still active (not superseded by a newer click)
const isActive = () => queryStore.tabs.find((t) => t.id === tabId)?.executionId === openDataId;
try {
console.info("[DBX][openData:ensure-connected:start]", { traceId, elapsed: elapsed() });
await connectionStore.ensureConnected(node.connectionId);
if (!isActive()) return;
console.info("[DBX][openData:ensure-connected:done]", { traceId, elapsed: elapsed() });
if (!config) throw new Error("Connection config not found");
@@ -730,6 +757,7 @@ async function openData() {
elapsed: elapsed(),
});
columns = await api.getColumns(node.connectionId, node.database, querySchema, node.label);
if (!isActive()) return;
primaryKeys = editablePrimaryKeys(effectiveDbType, columns, node.type === "view" ? "VIEW" : "TABLE");
console.info("[DBX][openData:get-columns:done]", {
traceId,
@@ -748,6 +776,9 @@ async function openData() {
console.warn("[DBX][openData:get-columns:error]", { traceId, elapsed: elapsed(), error });
}
// Check if superseded by a newer openData call
if (!isActive()) return;
const includeRowId = usesSyntheticRowIdKey(effectiveDbType, primaryKeys);
const fallbackOrderColumns = effectiveDbType === "sqlserver" && !primaryKeys.length ? columns.slice(0, 1).map((column) => column.name) : undefined;
const sql = await buildTableSelectSql({
@@ -773,6 +804,7 @@ async function openData() {
await queryStore.executeTabSql(tabId, sql);
console.info("[DBX][openData:execute:done]", { traceId, tabId, elapsed: elapsed() });
} catch (e: any) {
if (!isActive()) return;
console.error("[DBX][openData:error]", { traceId, elapsed: elapsed(), error: e });
queryStore.setErrorResult(tabId, e);
}
@@ -980,7 +1012,7 @@ function dropObjectSqlOptions(): DropObjectSqlOptions | null {
function dropObjectSqlOptionsForNode(node: TreeNode): DropObjectSqlOptions | null {
if (node.type !== "view" && node.type !== "procedure" && node.type !== "function") return null;
return {
databaseType: databaseTypeForNode(node),
databaseType: tableStructureDatabaseTypeForNode(node),
objectType: node.type === "view" ? "VIEW" : node.type === "procedure" ? "PROCEDURE" : "FUNCTION",
schema: node.schema,
name: node.label,
@@ -1421,7 +1453,7 @@ const supportsTruncate = computed(() => {
const canCreateTable = computed(() => {
const config = props.node.connectionId ? connectionStore.getConfig(props.node.connectionId) : undefined;
return (props.node.type === "database" || props.node.type === "schema" || props.node.type === "group-tables") && !!props.node.database && supportsTableStructureEditing(effectiveDatabaseTypeForConnection(config));
return (props.node.type === "database" || props.node.type === "schema" || props.node.type === "group-tables") && !!props.node.database && supportsTableStructureEditing(tableStructureDatabaseTypeForConnection(config));
});
const canCreateDatabase = computed(() => {
@@ -1446,12 +1478,12 @@ const canDropDatabase = computed(() => {
const canCreateSchema = computed(() => {
const config = props.node.connectionId ? connectionStore.getConfig(props.node.connectionId) : undefined;
return props.node.type === "database" && usesTreeSchemaMode(config?.db_type) && !connectionUsesDatabaseObjectTreeMode(config);
return props.node.type === "database" && usesTreeSchemaMode(effectiveDatabaseTypeForConnection(config)) && !connectionUsesDatabaseObjectTreeMode(config);
});
const canDropSchema = computed(() => {
const config = props.node.connectionId ? connectionStore.getConfig(props.node.connectionId) : undefined;
return props.node.type === "schema" && usesTreeSchemaMode(config?.db_type) && !connectionUsesDatabaseObjectTreeMode(config);
return props.node.type === "schema" && usesTreeSchemaMode(effectiveDatabaseTypeForConnection(config)) && !connectionUsesDatabaseObjectTreeMode(config);
});
function tableAdminSqlOptions(): TableAdminSqlOptions {
@@ -2325,7 +2357,7 @@ const canOpenTableImport = computed(() => {
return props.node.type === "table" && !!props.node.database && supportsTableImport(currentDatabaseType());
});
const canOpenStructureEditor = computed(() => {
return props.node.type === "table" && !!props.node.database && supportsTableStructureEditing(currentDatabaseType());
return props.node.type === "table" && !!props.node.database && supportsTableStructureEditing(currentTableStructureDatabaseType());
});
const canOpenFieldLineage = computed(() => {
return props.node.type === "column" && !!props.node.database && !!props.node.tableName && supportsFieldLineage(currentDatabaseType());
@@ -24,7 +24,7 @@ import { queryTimeoutSecsForConnection } from "@/lib/queryTimeout";
import { type EditableStructureColumn, type EditableStructureForeignKey, type EditableStructureIndex, type EditableStructureTrigger } from "@/lib/tableStructureEditorSql";
import { getTableMetadataCapabilities } from "@/lib/tableMetadataCapabilities";
import { getTableStructureCapabilities } from "@/lib/tableStructureCapabilities";
import { connectionObjectTreeQuerySchema, effectiveDatabaseTypeForConnection } from "@/lib/jdbcDialect";
import { connectionObjectTreeQuerySchema, tableStructureDatabaseTypeForConnection } from "@/lib/jdbcDialect";
import {
buildStructureTargetLabel,
combineDataTypeForDatabase,
@@ -279,7 +279,7 @@ function onIndexColResize(e: MouseEvent, col: number) {
}
const connection = computed(() => (props.connectionId ? store.getConfig(props.connectionId) : undefined));
const databaseType = computed(() => effectiveDatabaseTypeForConnection(connection.value));
const databaseType = computed(() => tableStructureDatabaseTypeForConnection(connection.value));
const structureCapabilities = computed(() => getTableStructureCapabilities(databaseType.value));
const tableMetadataCapabilities = computed(() => getTableMetadataCapabilities(databaseType.value));
const structureDialect = computed(() => structureCapabilities.value.dialect);