feat(gaussdb): add target server type selection

This commit is contained in:
Andrew authored and GitHub committed 2026-08-14 22:56:28 +08:00
1 parent 5695ef6e77
commit 722b6e7d19
13 files changed
+197 -2

No files matched your search

@@ -118,7 +118,19 @@ import {
elasticsearchKibanaBasePathFromConfig,
type ElasticsearchConnectionMode,
} from "@/lib/connection/elasticsearchKibanaProxy";
import { GAUSSDB_M_JDBC_DRIVER_CLASS, gaussdbConnectionMode, gaussdbIdentifierQuoteStyle, setGaussdbConnectionMode, setGaussdbIdentifierQuoteStyle, supportsGaussdbIdentifierQuoteStyle, type GaussdbConnectionMode, type GaussdbIdentifierQuoteStyle } from "@/lib/database/jdbcDialect";
import {
GAUSSDB_M_JDBC_DRIVER_CLASS,
gaussdbConnectionMode,
gaussdbIdentifierQuoteStyle,
gaussdbTargetServerType,
setGaussdbConnectionMode,
setGaussdbIdentifierQuoteStyle,
setGaussdbTargetServerType,
supportsGaussdbIdentifierQuoteStyle,
type GaussdbConnectionMode,
type GaussdbIdentifierQuoteStyle,
type GaussdbTargetServerType,
} from "@/lib/database/jdbcDialect";
import { normalizeStoredConnectionDatabase } from "@/lib/database/sqliteNamespace";
import {
createJdbcProductConnectionFieldsByMode,
@@ -532,6 +544,14 @@ const gaussdbQuoteStyle = computed<GaussdbIdentifierQuoteStyle>({
},
});
const gaussdbTargetServerTypeComputed = computed<GaussdbTargetServerType>({
get: () => gaussdbTargetServerType(form.value),
set: (value) => {
setGaussdbTargetServerType(form.value, value);
resetTestState();
},
});
const gaussdbHostEntries = ref<GaussdbHostEntry[]>(parseGaussdbHosts(form.value.host, form.value.port));
watch(
@@ -3866,8 +3886,10 @@ function connectionConfigForSubmit(id: string, generatedName = ""): ConnectionCo
config.external_config = sqlServerPortExplicitFromConfig(config) ? { portExplicit: true } : undefined;
} else if (supportsGaussdbIdentifierQuoteStyle(config)) {
const style = gaussdbIdentifierQuoteStyle(config);
const targetServerType = gaussdbTargetServerType(config);
config.external_config = undefined;
setGaussdbIdentifierQuoteStyle(config, style);
setGaussdbTargetServerType(config, targetServerType);
} else if (!isDoltDriverProfile(config.driver_profile)) {
config.external_config = undefined;
}
@@ -7789,6 +7811,24 @@ function openExternalUrl(url: string) {
<p class="text-xs leading-5 text-muted-foreground">{{ t("connection.gaussdbIdentifierQuoteHint") }}</p>
</div>
</div>
<div v-if="isGaussdbMJdbcConnection" class="grid grid-cols-4 items-start gap-4">
<Label :class="connectionLabelSmallPaddedClass">{{ t("connection.gaussdbTargetServerType") }}</Label>
<div class="col-span-3 grid gap-1">
<Select v-model="gaussdbTargetServerTypeComputed">
<SelectTrigger class="h-9">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="master">{{ t("connection.gaussdbTargetServerTypeMaster") }}</SelectItem>
<SelectItem value="slave">{{ t("connection.gaussdbTargetServerTypeSlave") }}</SelectItem>
<SelectItem value="any">{{ t("connection.gaussdbTargetServerTypeAny") }}</SelectItem>
</SelectContent>
</Select>
<p class="text-xs leading-5 text-muted-foreground">
{{ t("connection.gaussdbTargetServerTypeHint") }}
</p>
</div>
</div>
<div class="grid grid-cols-4 items-center gap-4">
<Label :class="connectionLabelSmallClass">{{ t("connection.connectTimeout") }}</Label>
<div class="col-span-3 grid grid-cols-2 gap-2">
+5
View File
@@ -907,6 +907,11 @@ export default {
gaussdbIdentifierQuoteDouble: 'Oracle/PostgreSQL (double quotes ")',
gaussdbIdentifierQuoteBacktick: "MySQL/M (backticks `)",
gaussdbIdentifierQuoteHint: "Auto uses JDBC driver metadata or the native GaussDB default. Select a mode manually only when generated SQL uses the wrong identifier quotes.",
gaussdbTargetServerType: "Target server type",
gaussdbTargetServerTypeMaster: "Master (primary)",
gaussdbTargetServerTypeSlave: "Slave (standby)",
gaussdbTargetServerTypeAny: "Any (no preference)",
gaussdbTargetServerTypeHint: "Controls which node in a GaussDB cluster the JDBC driver connects to. Master tries the primary, slave tries a standby, any connects to whichever is available. Only applies in M (JDBC) mode.",
saveAndConnect: "Save & Connect",
save: "Save",
editTitle: "Edit Connection",
+5
View File
@@ -726,6 +726,11 @@ export default withEnglishFallback({
gaussdbIdentifierQuoteDouble: 'Oracle/PostgreSQL (comillas dobles ")',
gaussdbIdentifierQuoteBacktick: "MySQL/M (acentos graves `)",
gaussdbIdentifierQuoteHint: "El modo automático usa los metadatos del controlador JDBC o el valor predeterminado de GaussDB nativo. Seleccione un modo manualmente solo si el SQL generado usa comillas de identificador incorrectas.",
gaussdbTargetServerType: "Tipo de servidor de destino",
gaussdbTargetServerTypeMaster: "Principal (primario)",
gaussdbTargetServerTypeSlave: "Réplica (en espera)",
gaussdbTargetServerTypeAny: "Cualquiera (sin preferencia)",
gaussdbTargetServerTypeHint: "Controla a qué nodo del clúster GaussDB se conecta el controlador JDBC. Se aplica solo al modo M (JDBC).",
saveAndConnect: "Guardar y conectar",
save: "Guardar",
editTitle: "Editar conexión",
+5
View File
@@ -725,6 +725,11 @@ export default withEnglishFallback({
gaussdbIdentifierQuoteDouble: 'Oracle/PostgreSQL (virgolette doppie ")',
gaussdbIdentifierQuoteBacktick: "MySQL/M (accenti gravi `)",
gaussdbIdentifierQuoteHint: "La modalità automatica usa i metadati del driver JDBC o il valore predefinito di GaussDB nativo. Seleziona manualmente una modalità solo se l'SQL generato usa virgolette errate per gli identificatori.",
gaussdbTargetServerType: "Tipo di server di destinazione",
gaussdbTargetServerTypeMaster: "Master (primario)",
gaussdbTargetServerTypeSlave: "Slave (standby)",
gaussdbTargetServerTypeAny: "Qualsiasi (nessuna preferenza)",
gaussdbTargetServerTypeHint: "Controlla il nodo del cluster GaussDB a cui si connette il driver JDBC. Si applica solo alla modalità M (JDBC).",
saveAndConnect: "Salva & Connetti",
save: "Salva",
editTitle: "Modifica Connessione",
+5
View File
@@ -691,6 +691,11 @@ export default withEnglishFallback({
gaussdbIdentifierQuoteDouble: 'Oracle/PostgreSQL(二重引用符 ")',
gaussdbIdentifierQuoteBacktick: "MySQL/M(バッククォート `)",
gaussdbIdentifierQuoteHint: "自動モードでは JDBC ドライバーのメタデータまたはネイティブ GaussDB の既定値を使用します。生成された SQL の識別子引用符が誤っている場合のみ手動で選択してください。",
gaussdbTargetServerType: "接続先サーバー種別",
gaussdbTargetServerTypeMaster: "マスター(プライマリ)",
gaussdbTargetServerTypeSlave: "スレーブ(スタンバイ)",
gaussdbTargetServerTypeAny: "任意(優先なし)",
gaussdbTargetServerTypeHint: "JDBC ドライバーが接続する GaussDB クラスターのノードを制御します。M モード(JDBC)にのみ適用されます。",
saveAndConnect: "保存して接続",
save: "保存",
editTitle: "接続を編集",
+5
View File
@@ -801,6 +801,11 @@ export default withEnglishFallback({
gaussdbMJdbcDriverHint: "드라이버는 각 GaussDB 서버 버전과 함께 배포됩니다. 해당 GaussDB 드라이버 패키지에서 gaussdbjdbc.jar를 가져오세요.",
gaussdbMJdbcDriverRequired: "GaussDB M 모드에는 공급업체 JDBC 드라이버가 필요합니다. 서버 버전에 맞는 gaussdbjdbc.jar를 선택하세요.",
gaussdbMJdbcPluginInstalling: "JDBC 런타임 설치 중...",
gaussdbTargetServerType: "대상 서버 유형",
gaussdbTargetServerTypeMaster: "마스터(기본)",
gaussdbTargetServerTypeSlave: "슬레이브(대기)",
gaussdbTargetServerTypeAny: "임의(선호 없음)",
gaussdbTargetServerTypeHint: "JDBC 드라이버가 연결할 GaussDB 클러스터 노드를 제어합니다. M 모드(JDBC)에만 적용됩니다.",
saveAndConnect: "저장 후 연결",
save: "저장",
editTitle: "연결 편집",
+5
View File
@@ -726,6 +726,11 @@ export default withEnglishFallback({
gaussdbIdentifierQuoteDouble: 'Oracle/PostgreSQL (aspas duplas ")',
gaussdbIdentifierQuoteBacktick: "MySQL/M (crases `)",
gaussdbIdentifierQuoteHint: "O modo automático usa os metadados do driver JDBC ou o padrão do GaussDB nativo. Selecione um modo manualmente apenas quando o SQL gerado usar aspas de identificador incorretas.",
gaussdbTargetServerType: "Tipo de servidor de destino",
gaussdbTargetServerTypeMaster: "Master (primário)",
gaussdbTargetServerTypeSlave: "Slave (standby)",
gaussdbTargetServerTypeAny: "Qualquer (sem preferência)",
gaussdbTargetServerTypeHint: "Controla a qual nó do cluster GaussDB o driver JDBC se conecta. Aplica-se apenas ao modo M (JDBC).",
saveAndConnect: "Salvar e Conectar",
save: "Salvar",
editTitle: "Editar Conexão",
+5
View File
@@ -831,6 +831,11 @@ export default withEnglishFallback({
gaussdbIdentifierQuoteDouble: 'Oracle/PostgreSQL(双引号 ")',
gaussdbIdentifierQuoteBacktick: "MySQL/M(反引号 `)",
gaussdbIdentifierQuoteHint: "自动模式使用 JDBC 驱动元数据或原生 GaussDB 默认值。仅当生成的 SQL 使用了错误的标识符引号时手动选择。",
gaussdbTargetServerType: "目标服务器类型",
gaussdbTargetServerTypeMaster: "主节点(Primary)",
gaussdbTargetServerTypeSlave: "备节点(Standby)",
gaussdbTargetServerTypeAny: "任意(不指定)",
gaussdbTargetServerTypeHint: "控制 JDBC 驱动连接到 GaussDB 集群中的哪个节点。主节点优先连接主库,备节点连接备库,任意节点连接可用节点。仅适用于 M 模式(JDBC)。",
saveAndConnect: "保存并连接",
save: "保存",
editTitle: "编辑连接",
+5
View File
@@ -725,6 +725,11 @@ export default withEnglishFallback({
gaussdbIdentifierQuoteDouble: 'Oracle/PostgreSQL(雙引號 ")',
gaussdbIdentifierQuoteBacktick: "MySQL/M(反引號 `)",
gaussdbIdentifierQuoteHint: "自動模式使用 JDBC 驅動程式中繼資料或原生 GaussDB 預設值。僅在產生的 SQL 使用錯誤的識別字引號時手動選擇。",
gaussdbTargetServerType: "目標伺服器類型",
gaussdbTargetServerTypeMaster: "主節點(Primary)",
gaussdbTargetServerTypeSlave: "備節點(Standby)",
gaussdbTargetServerTypeAny: "任意(不指定)",
gaussdbTargetServerTypeHint: "控制 JDBC 驅動程式連線到 GaussDB 叢集中的哪個節點。僅適用於 M 模式(JDBC)。",
saveAndConnect: "儲存並連線",
save: "儲存",
editTitle: "編輯連線",
@@ -0,0 +1,27 @@
import { readFileSync } from "node:fs";
import { parse } from "vue/compiler-sfc";
import ts from "typescript";
import { describe, expect, it } from "vitest";
const dialogSource = readFileSync(new URL("../../../components/connection/ConnectionDialog.vue", import.meta.url), "utf8");
const parsedDialog = parse(dialogSource, { filename: "ConnectionDialog.vue" });
function connectionConfigForSubmitSource(): string {
const script = parsedDialog.descriptor.scriptSetup;
expect(parsedDialog.errors).toEqual([]);
expect(script).toBeDefined();
const source = ts.createSourceFile("ConnectionDialog.vue.ts", script!.content, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
const declaration = source.statements.find((statement): statement is ts.FunctionDeclaration => ts.isFunctionDeclaration(statement) && statement.name?.text === "connectionConfigForSubmit");
expect(declaration).toBeDefined();
return declaration!.getText();
}
describe("ConnectionDialog driver profile configuration", () => {
it("preserves GaussDB routing and Dolt external configuration on submit", () => {
const submitConfig = connectionConfigForSubmitSource();
expect(submitConfig).toContain("setGaussdbTargetServerType(config, targetServerType)");
expect(submitConfig).toContain("else if (!isDoltDriverProfile(config.driver_profile))");
});
});
@@ -13,9 +13,11 @@ import {
gaussdbConnectionMode,
gaussdbIdentifierQuoteOverride,
gaussdbIdentifierQuoteStyle,
gaussdbTargetServerType,
inferJdbcDialect,
setGaussdbConnectionMode,
setGaussdbIdentifierQuoteStyle,
setGaussdbTargetServerType,
supportsGaussdbIdentifierQuoteStyle,
} from "@/lib/database/jdbcDialect";
@@ -217,6 +219,23 @@ describe("GaussDB connection mode", () => {
expect(connection.driver_profile).toBe("gaussdb");
expect(connection.jdbc_driver_class).toBeUndefined();
});
it("uses the driver default and preserves targetServerType from legacy URL fields", () => {
const connection = { db_type: "gaussdb", driver_profile: "gaussdb-m" } as ConnectionConfig;
expect(gaussdbTargetServerType(connection)).toBe("any");
connection.url_params = "currentSchema=app&targetServerType=slave";
expect(gaussdbTargetServerType(connection)).toBe("slave");
connection.url_params = undefined;
connection.connection_string = "jdbc:gaussdb://db.internal:8000/app?targetServerType=MASTER&ssl=true";
expect(gaussdbTargetServerType(connection)).toBe("master");
setGaussdbTargetServerType(connection, "any");
expect(gaussdbTargetServerType(connection)).toBe("any");
expect(connection.external_config).toEqual({ gaussdbTargetServerType: "any" });
});
});
describe("query execution schema", () => {
+33 -1
View File
@@ -2,12 +2,14 @@ import type { ConnectionConfig, DatabaseType } from "@/types/database";
import { isSchemaAware, usesDatabaseObjectTreeMode, usesTreeSchemaMode } from "@/lib/database/databaseFeatureSupport";
import type { CodeMirrorSqlDialectName } from "@/lib/editor/codemirrorSqlDialect";
type JdbcDialectConnection = Partial<Pick<ConnectionConfig, "db_type" | "driver_profile" | "driver_label" | "connection_string" | "jdbc_driver_class" | "jdbc_driver_paths" | "database_info" | "external_config">>;
type JdbcDialectConnection = Partial<Pick<ConnectionConfig, "db_type" | "driver_profile" | "driver_label" | "connection_string" | "url_params" | "jdbc_driver_class" | "jdbc_driver_paths" | "database_info" | "external_config">>;
export type GaussdbIdentifierQuoteStyle = "auto" | "double" | "backtick";
export type GaussdbConnectionMode = "native" | "m-jdbc";
export type GaussdbTargetServerType = "master" | "slave" | "any";
const GAUSSDB_IDENTIFIER_QUOTE_STYLE_KEY = "gaussdbIdentifierQuoteStyle";
const GAUSSDB_TARGET_SERVER_TYPE_KEY = "gaussdbTargetServerType";
export const GAUSSDB_M_JDBC_DRIVER_PROFILE = "gaussdb-m";
export const GAUSSDB_M_JDBC_DRIVER_CLASS = "com.huawei.gaussdb.jdbc.Driver";
@@ -125,6 +127,36 @@ export function setGaussdbIdentifierQuoteStyle(
connection.external_config = Object.keys(external).length > 0 ? external : undefined;
}
export function gaussdbTargetServerType(connection: JdbcDialectConnection | undefined): GaussdbTargetServerType {
const external = externalConfigRecord(connection?.external_config);
return normalizeGaussdbTargetServerType(external[GAUSSDB_TARGET_SERVER_TYPE_KEY]) ?? gaussdbTargetServerTypeFromUrl(connection) ?? "any";
}
export function setGaussdbTargetServerType(connection: Pick<ConnectionConfig, "db_type"> & Partial<Pick<ConnectionConfig, "driver_profile" | "driver_label" | "connection_string" | "jdbc_driver_class" | "jdbc_driver_paths" | "database_info" | "external_config">>, value: GaussdbTargetServerType) {
const external = externalConfigRecord(connection.external_config);
external[GAUSSDB_TARGET_SERVER_TYPE_KEY] = value;
connection.external_config = Object.keys(external).length > 0 ? external : undefined;
}
function normalizeGaussdbTargetServerType(value: unknown): GaussdbTargetServerType | undefined {
if (typeof value !== "string") return undefined;
const normalized = value.trim().toLowerCase();
return normalized === "master" || normalized === "slave" || normalized === "any" ? normalized : undefined;
}
function gaussdbTargetServerTypeFromUrl(connection: JdbcDialectConnection | undefined): GaussdbTargetServerType | undefined {
const values = [connection?.url_params, connection?.connection_string?.split("?", 2)[1]?.split("#", 1)[0]];
for (const value of values) {
const params = new URLSearchParams((value || "").trim().replace(/^\?/, "").replace(/;/g, "&"));
for (const [key, paramValue] of params) {
if (key.toLowerCase() === "targetservertype") {
return normalizeGaussdbTargetServerType(paramValue);
}
}
}
return undefined;
}
export function sqlSnippetDatabaseTypeForConnection(connection?: JdbcDialectConnection): DatabaseType | undefined {
// ASE uses T-SQL snippets, but mapping it globally to SQL Server would also
// enable incompatible SQL Server metadata and pagination behavior.