mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-19 16:16:45 +00:00
feat(connection): add Dremio JDBC connection support
This commit is contained in:
@@ -0,0 +1,44 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 42 41">
|
||||
<path
|
||||
fill="#383c42"
|
||||
fill-rule="evenodd"
|
||||
d="M9.327 25.504c-1.25.57-3.69 1.84-3.46 3.22.32.85 1.31 0 1.68-.4a6.783 6.783 0 0 0 1.78-2.77v-.05Zm7.48-2.99c-.83 2.17-3.25 3.2-3.92 2.83-.34-.18 0-.87 0-1.34-.49.19-1.3.55-1.79.75a5 5 0 0 1 0 7.59c2 3 1 5.58-1.79 7.59l-.11.07-.06-.2a9.488 9.488 0 0 0-1.92-3.77c-.49-.57-1.05-1.09-1.6-1.61-.72-.68-1.42-1.38-2.07-2.12-2.75-2.88-3.84-7.95-3.48-11.87a15.6 15.6 0 0 1 1.74-5.75 17.628 17.628 0 0 1 2.8-3.91 21.806 21.806 0 0 1 9.07-5.67 15.038 15.038 0 0 1 9.32.43c.541.287 1.057.622 1.54 1l.71.67.08.08 13.37-6.85c1.1-.55 1.93-.55 2.3-.15.37.4-.15 1.43-.93 2l-11.5 8.31c.137.793-.099 1.604-.64 2.2 1.53.53.18 2.77-.49 3.57-1.92 2.27-5.43 4.05-8.56 5.35-.84.36-1.32.51-2.07.79v.01Z"
|
||||
/>
|
||||
<path
|
||||
fill="#31d3db"
|
||||
fill-rule="evenodd"
|
||||
d="M27.297 14.664c.86-1.08.26-1.7-.34-1.1a5.06 5.06 0 0 1-4.57 1.36c-2.35-.39-2.46-.75-2.06-1.1 2.728.918 5.734-.102 7.34-2.49.58-.83-1.36-1.49-2.5-3-5.48-7.44-22.64-.99-24.28 11.94-.54 4.036.647 8.114 3.27 11.23 2 2.269 4.18 3.22 5.41 6.79 2.4-2.06 2-4.39.27-6.43 1.258-.638 2.086-1.892 2.181-3.3.094-1.407-.559-2.759-1.721-3.56-1 3.61-4.51 6.28-5.33 3.72-.53-2.61 3.77-4.34 10.65-7 5.76-2.16 9.48-3.94 11.68-7.12"
|
||||
/>
|
||||
<path
|
||||
fill="#fff"
|
||||
fill-rule="evenodd"
|
||||
d="M27.296 14.664c.86-1.08.26-1.7-.34-1.1a5.06 5.06 0 0 1-3.96 1.44c-2.37-.17-4.26-.64-6-.45a17.199 17.199 0 0 0-8.52 4.32c-3 3.08-6.32 9.479-3 10.72a1.06 1.06 0 0 1-.48-.86c-.53-2.61 3.77-4.341 10.65-7.001 5.76-2.16 9.48-3.94 11.68-7.12"
|
||||
opacity=".92"
|
||||
/>
|
||||
<path
|
||||
fill="#31d3db"
|
||||
fill-rule="evenodd"
|
||||
d="M12.546 19.714c1.77.87 1.42 3.91 1.16 4.67 3.36-1 2.36-5.93 3.92-7.45-2-.06-5.62 1.91-5.08 2.78"
|
||||
/>
|
||||
<path
|
||||
fill="#383c42"
|
||||
fill-rule="evenodd"
|
||||
d="M18.226 10.694a1 1 0 1 1 1.31 1.51 1 1 0 0 1-1.31-1.51"
|
||||
/>
|
||||
<path
|
||||
fill="#31d3db"
|
||||
fill-rule="evenodd"
|
||||
d="M26.386 11.104 39.876 1.714c.38-.27.88-.85.67-1.13-.21-.28-.79-.13-1.29.1l-14.58 7.88c-.47 1 .48 3.05 1.72 2.54"
|
||||
/>
|
||||
<path
|
||||
fill="#fff"
|
||||
fill-rule="evenodd"
|
||||
d="m25.176 9.254 14.09-8.25c.364-.248.79-.387 1.23-.4-.21-.24-.79-.1-1.27.13l-14.58 7.83c-.294.941.069 1.964.89 2.51a3.583 3.583 0 0 1-.36-1.84"
|
||||
opacity=".92"
|
||||
/>
|
||||
<path
|
||||
fill="#21aaad"
|
||||
fill-rule="evenodd"
|
||||
d="M3.266 13.354a.94.94 0 1 1 1.15 1.46.94.94 0 0 1-1.15-1.46m2.82-3.008a.83.83 0 1 1 1.08 1.26.83.83 0 0 1-1.08-1.26m8.6-3.99a.86.86 0 1 1 1.62.57.86.86 0 0 1-1.62-.57m2.61-.415a.7.7 0 1 1 1.26.62.7.7 0 0 1-1.26-.62m-10.452 6.29a.55.55 0 1 1 .64.89.55.55 0 0 1-.64-.89m-4.291 3.52a.58.58 0 1 1 .69.94.58.58 0 0 1-.69-.94M27.537 6.684l-.63.32a2.95 2.95 0 0 0 1.34 2.21 4.08 4.08 0 0 1-.71-2.53Zm3.31-1.82-.55.3a2.5 2.5 0 0 0 1.13 1.84 3.6 3.6 0 0 1-.53-2.14h-.05Zm3.32-1.8-.42.23c0 .54.39 1.35.83 1.46a2.58 2.58 0 0 1-.36-1.69h-.05Zm3.2-1.72-.37.2c.09.441.326.839.67 1.13a3.3 3.3 0 0 1-.3-1.33Z"
|
||||
/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
@@ -50,12 +50,17 @@ type DbPickerView = "icon" | "list";
|
||||
type ConfigTab = "connection" | "advanced" | "tls" | "transport";
|
||||
type MqTokenSigningMode = "none" | "hs256" | "rs256";
|
||||
type NacosAuthKind = NacosAuthConfig["kind"];
|
||||
type DremioConnectionMode = "arrow-flight-sql" | "legacy";
|
||||
type JdbcDriverSelectItem = {
|
||||
id: string;
|
||||
label: string;
|
||||
paths: string[];
|
||||
};
|
||||
|
||||
const DREMIO_ARROW_FLIGHT_SQL_JDBC_URL = "jdbc:arrow-flight-sql://127.0.0.1:32010";
|
||||
const DREMIO_ARROW_FLIGHT_SQL_JDBC_DRIVER_CLASS = "org.apache.arrow.driver.jdbc.ArrowFlightJdbcDriver";
|
||||
const DREMIO_LEGACY_JDBC_URL = "jdbc:dremio:direct=127.0.0.1:31010";
|
||||
const DREMIO_LEGACY_JDBC_DRIVER_CLASS = "com.dremio.jdbc.Driver";
|
||||
const NACOS_DEFAULT_CONSOLE_URL = "http://127.0.0.1:8085";
|
||||
const NACOS_LEGACY_SERVER_PORT = "8848";
|
||||
const NACOS_DOCKER_CONSOLE_PORT = "8085";
|
||||
@@ -305,6 +310,11 @@ const connectionUrlInput = ref("");
|
||||
const appliedConnectionUrlInput = ref("");
|
||||
const oceanbaseSubMode = ref<"mysql" | "oracle">("mysql");
|
||||
const h2ConnectionMode = ref<H2ConnectionMode>("file");
|
||||
const dremioConnectionMode = ref<DremioConnectionMode>("legacy");
|
||||
const dremioConnectionUrls = ref<Record<DremioConnectionMode, string>>({
|
||||
"arrow-flight-sql": DREMIO_ARROW_FLIGHT_SQL_JDBC_URL,
|
||||
legacy: DREMIO_LEGACY_JDBC_URL,
|
||||
});
|
||||
const dialogStep = ref<DialogStep>("select");
|
||||
const dbPickerView = ref<DbPickerView>("icon");
|
||||
const dbSearchQuery = ref("");
|
||||
@@ -521,6 +531,7 @@ const driverProfiles: Record<
|
||||
hive: { type: "hive", port: 10000, user: "", label: "Apache Hive", icon: "hive" },
|
||||
db2: { type: "db2", port: 50000, user: "db2inst1", label: "IBM DB2", icon: "db2" },
|
||||
informix: { type: "informix", port: 9088, user: "informix", label: "Informix", icon: "informix" },
|
||||
dremio: { type: "jdbc", port: 31010, user: "", label: "Dremio", icon: "dremio" },
|
||||
neo4j: { type: "neo4j", port: 7687, user: "neo4j", label: "Neo4j", icon: "neo4j" },
|
||||
cassandra: { type: "cassandra", port: 9042, user: "cassandra", label: "Cassandra", icon: "cassandra" },
|
||||
bigquery: {
|
||||
@@ -800,6 +811,79 @@ function applyNacosServerAddr(config: LegacyConnectionConfig, serverAddr: string
|
||||
config.ssl = parsed.protocol === "https:";
|
||||
}
|
||||
|
||||
function applyDremioConnectionMode(mode: DremioConnectionMode) {
|
||||
rememberCurrentDremioConnectionUrl();
|
||||
dremioConnectionMode.value = mode;
|
||||
form.value.connection_string = dremioConnectionUrls.value[mode] || dremioDefaultConnectionUrl(mode);
|
||||
if (isDremioGeneratedDefaultDriverClass(form.value.jdbc_driver_class)) {
|
||||
form.value.jdbc_driver_class = dremioDefaultDriverClass(mode);
|
||||
}
|
||||
}
|
||||
|
||||
function rememberCurrentDremioConnectionUrl() {
|
||||
if (form.value.driver_profile !== "dremio") return;
|
||||
const url = form.value.connection_string?.trim();
|
||||
dremioConnectionUrls.value[dremioConnectionMode.value] = url || dremioDefaultConnectionUrl();
|
||||
}
|
||||
|
||||
function resetDremioConnectionUrls(mode: DremioConnectionMode = "legacy", url?: string) {
|
||||
dremioConnectionUrls.value = {
|
||||
"arrow-flight-sql": DREMIO_ARROW_FLIGHT_SQL_JDBC_URL,
|
||||
legacy: DREMIO_LEGACY_JDBC_URL,
|
||||
};
|
||||
if (url?.trim()) {
|
||||
dremioConnectionUrls.value[mode] = url.trim();
|
||||
}
|
||||
}
|
||||
|
||||
function dremioDefaultConnectionUrl(mode = dremioConnectionMode.value) {
|
||||
return mode === "legacy" ? DREMIO_LEGACY_JDBC_URL : DREMIO_ARROW_FLIGHT_SQL_JDBC_URL;
|
||||
}
|
||||
|
||||
function dremioDefaultDriverClass(mode = dremioConnectionMode.value) {
|
||||
return mode === "legacy" ? DREMIO_LEGACY_JDBC_DRIVER_CLASS : DREMIO_ARROW_FLIGHT_SQL_JDBC_DRIVER_CLASS;
|
||||
}
|
||||
|
||||
function isDremioGeneratedDefaultConnectionUrl(value: string) {
|
||||
const url = value.trim();
|
||||
return url === DREMIO_ARROW_FLIGHT_SQL_JDBC_URL || url === `${DREMIO_ARROW_FLIGHT_SQL_JDBC_URL}?useEncryption=false` || url === DREMIO_LEGACY_JDBC_URL;
|
||||
}
|
||||
|
||||
function isDremioGeneratedDefaultDriverClass(value: string | undefined) {
|
||||
const driverClass = value?.trim() || "";
|
||||
return !driverClass || driverClass === DREMIO_ARROW_FLIGHT_SQL_JDBC_DRIVER_CLASS || driverClass === DREMIO_LEGACY_JDBC_DRIVER_CLASS;
|
||||
}
|
||||
|
||||
function restoreDremioConnectionDefaultsIfEmpty() {
|
||||
if (form.value.driver_profile !== "dremio") return;
|
||||
if (!form.value.connection_string?.trim()) {
|
||||
form.value.connection_string = dremioDefaultConnectionUrl();
|
||||
}
|
||||
if (isDremioGeneratedDefaultDriverClass(form.value.jdbc_driver_class)) {
|
||||
form.value.jdbc_driver_class = dremioDefaultDriverClass();
|
||||
}
|
||||
}
|
||||
|
||||
function syncDremioConnectionModeFromUrl() {
|
||||
if (form.value.driver_profile !== "dremio") return;
|
||||
restoreDremioConnectionDefaultsIfEmpty();
|
||||
const nextMode = dremioConnectionModeForConfig({
|
||||
connection_string: form.value.connection_string,
|
||||
jdbc_driver_class: "",
|
||||
});
|
||||
dremioConnectionUrls.value[nextMode] = form.value.connection_string?.trim() || dremioDefaultConnectionUrl(nextMode);
|
||||
if (nextMode === dremioConnectionMode.value) return;
|
||||
dremioConnectionMode.value = nextMode;
|
||||
if (isDremioGeneratedDefaultDriverClass(form.value.jdbc_driver_class)) {
|
||||
form.value.jdbc_driver_class = dremioDefaultDriverClass(nextMode);
|
||||
}
|
||||
}
|
||||
|
||||
function dremioConnectionModeForConfig(config: Pick<ConnectionConfig, "connection_string" | "jdbc_driver_class">): DremioConnectionMode {
|
||||
const haystack = `${config.connection_string || ""}\n${config.jdbc_driver_class || ""}`.toLowerCase();
|
||||
return haystack.includes("jdbc:dremio:") || haystack.includes("com.dremio.jdbc.driver") ? "legacy" : "arrow-flight-sql";
|
||||
}
|
||||
|
||||
function isCustomCompatibleProfile() {
|
||||
return selectedType.value === "custom_mysql" || selectedType.value === "custom_postgres";
|
||||
}
|
||||
@@ -835,6 +919,10 @@ function applyProfile(val: string, preserveConnectionFields = false) {
|
||||
form.value.jdbc_driver_class = "";
|
||||
form.value.jdbc_driver_paths = [];
|
||||
jdbcDriverPathsInput.value = "";
|
||||
if (val === "dremio") {
|
||||
resetDremioConnectionUrls();
|
||||
applyDremioConnectionMode("legacy");
|
||||
}
|
||||
}
|
||||
if (profile.type === "prestosql") {
|
||||
form.value.connection_string = undefined;
|
||||
@@ -955,6 +1043,8 @@ watch(
|
||||
if (profile === "gbase8a" || profile === "gbase8s") {
|
||||
selectedType.value = "gbase";
|
||||
}
|
||||
dremioConnectionMode.value = profile === "dremio" ? dremioConnectionModeForConfig(config) : "legacy";
|
||||
resetDremioConnectionUrls(dremioConnectionMode.value, profile === "dremio" ? config.connection_string : undefined);
|
||||
mongoUseUrl.value = !!config.connection_string;
|
||||
jdbcDriverPathsInput.value = (config.jdbc_driver_paths || []).join("\n");
|
||||
jdbcManualClasspathOpen.value = config.db_type === "prestosql" || (config.jdbc_driver_paths || []).length > 0;
|
||||
@@ -971,6 +1061,8 @@ watch(
|
||||
resetNacosFields();
|
||||
oceanbaseSubMode.value = "mysql";
|
||||
h2ConnectionMode.value = "file";
|
||||
dremioConnectionMode.value = "legacy";
|
||||
resetDremioConnectionUrls();
|
||||
dialogStep.value = "select";
|
||||
configTab.value = "connection";
|
||||
}
|
||||
@@ -1118,6 +1210,7 @@ const iconTypeMap: Record<string, string> = {
|
||||
hive: "hive",
|
||||
db2: "db2",
|
||||
informix: "informix",
|
||||
dremio: "dremio",
|
||||
iris: "iris",
|
||||
neo4j: "neo4j",
|
||||
cassandra: "cassandra",
|
||||
@@ -1202,6 +1295,7 @@ const dbOptions: DbOption[] = [
|
||||
{ value: "manticoresearch", label: "Manticore Search" },
|
||||
{ value: "custom_mysql", label: "Custom (MySQL)" },
|
||||
{ value: "custom_postgres", label: "Custom (PostgreSQL)" },
|
||||
{ value: "dremio", label: "Dremio" },
|
||||
];
|
||||
|
||||
const dbCategories = computed<DbCategory[]>(() => [{ key: "all", title: "", options: dbOptions }]);
|
||||
@@ -1236,6 +1330,7 @@ const isH2FileMode = computed(() => form.value.db_type === "h2" && h2ConnectionM
|
||||
const usesLocalFilePathInput = computed(() => isLocalFileTypeDb(form.value.db_type) && (form.value.db_type !== "h2" || isH2FileMode.value));
|
||||
|
||||
const connectionUrlPlaceholder = computed(() => getUrlPlaceholder(form.value.db_type));
|
||||
const jdbcUsernamePlaceholder = computed(() => (form.value.driver_profile === "dremio" ? "" : "sa"));
|
||||
const filePathPlaceholder = computed(() => {
|
||||
if (form.value.db_type === "duckdb") return "/path/to/database.duckdb or :memory:";
|
||||
if (form.value.db_type === "access") return "/path/to/database.accdb";
|
||||
@@ -1526,6 +1621,83 @@ function formValueForSubmit(): Omit<ConnectionConfig, "id"> {
|
||||
return applyParsedConnectionUrl(form.value, parseConnectionUrl(url, selectedType.value));
|
||||
}
|
||||
|
||||
function applyDremioJdbcMetadata(config: LegacyConnectionConfig) {
|
||||
config.connection_string = config.connection_string?.trim() || dremioDefaultConnectionUrl();
|
||||
try {
|
||||
const parsed = parseConnectionUrl(config.connection_string);
|
||||
if (parsed.driverProfile !== "dremio") return;
|
||||
config.host = parsed.host;
|
||||
config.port = parsed.port;
|
||||
config.database = config.database?.trim() || parsed.database;
|
||||
config.connection_string = dremioConnectionStringForSubmit(config.connection_string, config.url_params, config.database);
|
||||
config.url_params = "";
|
||||
if (!config.username) config.username = parsed.username;
|
||||
if (!config.password) config.password = parsed.password;
|
||||
} catch {
|
||||
// Keep custom JDBC input editable; the agent will surface driver-specific URL errors.
|
||||
}
|
||||
}
|
||||
|
||||
function dremioConnectionStringForSubmit(connectionString: string, urlParams: string | undefined, database: string | undefined) {
|
||||
const params = dremioSubmitUrlParams(connectionString, urlParams, database);
|
||||
if (!params) return connectionString;
|
||||
return `${connectionString}${dremioSubmitUrlParamSeparator(connectionString)}${params}`;
|
||||
}
|
||||
|
||||
function dremioSubmitUrlParams(connectionString: string | undefined, urlParams: string | undefined, database: string | undefined) {
|
||||
const existingKeys = dremioUrlParamKeys(connectionString || "");
|
||||
const extraParams = filterDremioUrlParams(urlParams || "", existingKeys);
|
||||
if (database?.trim() && !existingKeys.has("schema") && !dremioUrlParamKeys(extraParams.join("&")).has("schema")) {
|
||||
extraParams.push(`schema=${database.trim()}`);
|
||||
}
|
||||
return extraParams.join(dremioConnectionStringUsesLegacyUrlParams(connectionString || "") ? ";" : "&");
|
||||
}
|
||||
|
||||
function dremioSubmitUrlParamSeparator(connectionString: string) {
|
||||
if (dremioConnectionStringUsesLegacyUrlParams(connectionString)) {
|
||||
return connectionString.endsWith(";") ? "" : ";";
|
||||
}
|
||||
return connectionString.includes("?") ? (connectionString.endsWith("?") || connectionString.endsWith("&") ? "" : "&") : "?";
|
||||
}
|
||||
|
||||
function dremioConnectionStringUsesLegacyUrlParams(connectionString: string) {
|
||||
if (/^jdbc:dremio:/i.test(connectionString)) return true;
|
||||
if (/^jdbc:arrow-flight-sql:\/\//i.test(connectionString)) return false;
|
||||
return dremioConnectionMode.value === "legacy";
|
||||
}
|
||||
|
||||
function filterDremioUrlParams(urlParams: string, existingKeys: Set<string>) {
|
||||
const result: string[] = [];
|
||||
for (const part of urlParams.split(/[&;]/)) {
|
||||
const param = part.trim();
|
||||
if (!param) continue;
|
||||
const key = param.split("=")[0]?.trim().toLowerCase();
|
||||
if (!key || existingKeys.has(key)) continue;
|
||||
result.push(param);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function dremioUrlParamKeys(value: string) {
|
||||
const keys = new Set<string>();
|
||||
const params = dremioUrlParamString(value);
|
||||
for (const part of params.split(/[&;]/)) {
|
||||
const key = part.split("=")[0]?.trim().toLowerCase();
|
||||
if (key) keys.add(key);
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
function dremioUrlParamString(value: string) {
|
||||
if (/^jdbc:dremio:/i.test(value)) {
|
||||
return value.split(";").slice(1).join(";");
|
||||
}
|
||||
const queryStart = value.indexOf("?");
|
||||
if (queryStart < 0) return value;
|
||||
const fragmentStart = value.indexOf("#", queryStart + 1);
|
||||
return value.slice(queryStart + 1, fragmentStart < 0 ? undefined : fragmentStart);
|
||||
}
|
||||
|
||||
function generateConnectionName(): string {
|
||||
const label = selectedProfile().label;
|
||||
const rand = Math.random().toString(36).slice(2, 6);
|
||||
@@ -1688,9 +1860,13 @@ function connectionConfigForSubmit(id: string): ConnectionConfig {
|
||||
}
|
||||
if (jdbcBackedDatabaseTypes.has(config.db_type)) {
|
||||
if (config.db_type === "jdbc") {
|
||||
config.host = "";
|
||||
config.port = 0;
|
||||
config.connection_string = config.connection_string?.trim() || "";
|
||||
if (config.driver_profile === "dremio") {
|
||||
applyDremioJdbcMetadata(config);
|
||||
} else {
|
||||
config.host = "";
|
||||
config.port = 0;
|
||||
config.connection_string = config.connection_string?.trim() || "";
|
||||
}
|
||||
} else if (config.db_type === "prestosql") {
|
||||
config.connection_string = undefined;
|
||||
config.jdbc_driver_class = config.jdbc_driver_class?.trim() || "io.prestosql.jdbc.PrestoDriver";
|
||||
@@ -2161,6 +2337,8 @@ function resetForm() {
|
||||
mongoUseUrl.value = false;
|
||||
resetMqFields();
|
||||
oceanbaseSubMode.value = "mysql";
|
||||
dremioConnectionMode.value = "legacy";
|
||||
resetDremioConnectionUrls();
|
||||
jdbcDriverPathsInput.value = "";
|
||||
selectedJdbcDriverPath.value = "";
|
||||
connectionUrlInput.value = "";
|
||||
@@ -2925,13 +3103,24 @@ function openExternalUrl(url: string) {
|
||||
|
||||
<!-- JDBC: optional external plugin -->
|
||||
<template v-if="isJdbcConnection">
|
||||
<div v-if="form.driver_profile === 'dremio'" class="grid grid-cols-4 items-center gap-4">
|
||||
<Label :class="connectionLabelClass">{{ t("connection.mode") }}</Label>
|
||||
<div class="col-span-3 flex gap-2">
|
||||
<Button size="sm" :variant="dremioConnectionMode === 'arrow-flight-sql' ? 'default' : 'outline'" @click="applyDremioConnectionMode('arrow-flight-sql')">
|
||||
{{ t("connection.dremioArrowFlightSqlMode") }}
|
||||
</Button>
|
||||
<Button size="sm" :variant="dremioConnectionMode === 'legacy' ? 'default' : 'outline'" @click="applyDremioConnectionMode('legacy')">
|
||||
{{ t("connection.dremioLegacyJdbcMode") }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label :class="connectionLabelClass">{{ t("connection.jdbcUrl") }}</Label>
|
||||
<Input v-model="form.connection_string" class="col-span-3" :placeholder="t('connection.jdbcUrlPlaceholder')" />
|
||||
<Input v-model="form.connection_string" class="col-span-3" :placeholder="t('connection.jdbcUrlPlaceholder')" @blur="syncDremioConnectionModeFromUrl" />
|
||||
</div>
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label :class="connectionLabelClass">{{ t("connection.user") }}</Label>
|
||||
<Input v-model="form.username" class="col-span-3" placeholder="sa" />
|
||||
<Input v-model="form.username" class="col-span-3" :placeholder="jdbcUsernamePlaceholder" />
|
||||
</div>
|
||||
<div class="grid grid-cols-4 items-center gap-4">
|
||||
<Label :class="connectionLabelClass">{{ t("connection.password") }}</Label>
|
||||
|
||||
@@ -52,6 +52,7 @@ const assetIcons: Record<string, string> = {
|
||||
kylin: "apache_kylin",
|
||||
cockroachdb: "cockroachdb",
|
||||
db2: "db2",
|
||||
dremio: "dremio",
|
||||
bigquery: "bigquery",
|
||||
cassandra: "cassandra",
|
||||
doris: "doris",
|
||||
|
||||
@@ -351,6 +351,8 @@ export default {
|
||||
proxyUsernamePlaceholder: "Optional",
|
||||
proxyPassword: "Proxy Password",
|
||||
proxyPasswordPlaceholder: "Optional",
|
||||
dremioArrowFlightSqlMode: "Arrow Flight SQL",
|
||||
dremioLegacyJdbcMode: "Legacy JDBC",
|
||||
jdbcUrl: "JDBC URL",
|
||||
jdbcUrlPlaceholder: "jdbc:postgresql://localhost:5432/database",
|
||||
jdbcDriverClass: "Driver Class (optional)",
|
||||
|
||||
@@ -353,6 +353,8 @@ export default withEnglishFallback({
|
||||
proxyUsernamePlaceholder: "Opcional",
|
||||
proxyPassword: "Contraseña del proxy",
|
||||
proxyPasswordPlaceholder: "Opcional",
|
||||
dremioArrowFlightSqlMode: "Arrow Flight SQL",
|
||||
dremioLegacyJdbcMode: "JDBC heredado",
|
||||
jdbcUrl: "URL JDBC",
|
||||
jdbcUrlPlaceholder: "jdbc:postgresql://localhost:5432/base_de_datos",
|
||||
jdbcDriverClass: "Clase del driver (opcional)",
|
||||
|
||||
@@ -356,6 +356,8 @@ export default withEnglishFallback({
|
||||
proxyUsernamePlaceholder: "Opzionale",
|
||||
proxyPassword: "Password Proxy",
|
||||
proxyPasswordPlaceholder: "Opzionale",
|
||||
dremioArrowFlightSqlMode: "Arrow Flight SQL",
|
||||
dremioLegacyJdbcMode: "JDBC legacy",
|
||||
jdbcUrl: "URL JDBC",
|
||||
jdbcUrlPlaceholder: "jdbc:postgresql://localhost:5432/database",
|
||||
jdbcDriverClass: "Classe Driver (opzionale)",
|
||||
|
||||
@@ -346,6 +346,8 @@ export default withEnglishFallback({
|
||||
proxyUsernamePlaceholder: "任意",
|
||||
proxyPassword: "プロキシパスワード",
|
||||
proxyPasswordPlaceholder: "任意",
|
||||
dremioArrowFlightSqlMode: "Arrow Flight SQL",
|
||||
dremioLegacyJdbcMode: "Legacy JDBC",
|
||||
jdbcUrl: "JDBC URL",
|
||||
jdbcUrlPlaceholder: "jdbc:postgresql://localhost:5432/database",
|
||||
jdbcDriverClass: "ドライバークラス(任意)",
|
||||
|
||||
@@ -365,6 +365,8 @@ export default withEnglishFallback({
|
||||
proxyUsernamePlaceholder: "Opcional",
|
||||
proxyPassword: "Senha do Proxy",
|
||||
proxyPasswordPlaceholder: "Opcional",
|
||||
dremioArrowFlightSqlMode: "Arrow Flight SQL",
|
||||
dremioLegacyJdbcMode: "JDBC legado",
|
||||
jdbcUrl: "URL JDBC",
|
||||
jdbcUrlPlaceholder: "jdbc:postgresql://localhost:5432/database",
|
||||
jdbcDriverClass: "Classe do Driver (opcional)",
|
||||
|
||||
@@ -354,6 +354,8 @@ export default withEnglishFallback({
|
||||
proxyUsernamePlaceholder: "可选",
|
||||
proxyPassword: "代理密码",
|
||||
proxyPasswordPlaceholder: "可选",
|
||||
dremioArrowFlightSqlMode: "Arrow Flight SQL",
|
||||
dremioLegacyJdbcMode: "Legacy JDBC",
|
||||
jdbcUrl: "JDBC URL",
|
||||
jdbcUrlPlaceholder: "jdbc:postgresql://localhost:5432/database",
|
||||
jdbcDriverClass: "驱动类(可选)",
|
||||
|
||||
@@ -353,6 +353,8 @@ export default withEnglishFallback({
|
||||
proxyUsernamePlaceholder: "可選",
|
||||
proxyPassword: "代理伺服器密碼",
|
||||
proxyPasswordPlaceholder: "可選",
|
||||
dremioArrowFlightSqlMode: "Arrow Flight SQL",
|
||||
dremioLegacyJdbcMode: "Legacy JDBC",
|
||||
jdbcUrl: "JDBC URL",
|
||||
jdbcUrlPlaceholder: "jdbc:postgresql://localhost:5432/database",
|
||||
jdbcDriverClass: "驅動程式類別(可選)",
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { parseConnectionUrl } from "../connectionUrl";
|
||||
|
||||
describe("Dremio JDBC connection URLs", () => {
|
||||
it("parses legacy direct JDBC URLs into a Dremio JDBC profile", () => {
|
||||
const parsed = parseConnectionUrl("jdbc:dremio:direct=dremio.example.com:31010;schema=analytics;user=admin;password=secret;ssl=true");
|
||||
|
||||
expect(parsed.dbType).toBe("jdbc");
|
||||
expect(parsed.driverProfile).toBe("dremio");
|
||||
expect(parsed.driverLabel).toBe("Dremio");
|
||||
expect(parsed.host).toBe("dremio.example.com");
|
||||
expect(parsed.port).toBe(31010);
|
||||
expect(parsed.database).toBe("analytics");
|
||||
expect(parsed.username).toBe("admin");
|
||||
expect(parsed.password).toBe("secret");
|
||||
expect(parsed.urlParams).toBe("ssl=true");
|
||||
expect(parsed.connectionString).toBe("jdbc:dremio:direct=dremio.example.com:31010;schema=analytics;user=admin;password=secret;ssl=true");
|
||||
});
|
||||
|
||||
it("uses the Dremio default port when the JDBC URL omits a port", () => {
|
||||
const parsed = parseConnectionUrl("jdbc:dremio:direct=dremio.example.com");
|
||||
|
||||
expect(parsed.port).toBe(31010);
|
||||
expect(parsed.database).toBeUndefined();
|
||||
});
|
||||
|
||||
it("parses legacy ZooKeeper JDBC URLs into a Dremio JDBC profile", () => {
|
||||
const parsed = parseConnectionUrl("jdbc:dremio:zk=zk.example.com;schema=analytics;tag=prod");
|
||||
|
||||
expect(parsed.dbType).toBe("jdbc");
|
||||
expect(parsed.driverProfile).toBe("dremio");
|
||||
expect(parsed.host).toBe("zk.example.com");
|
||||
expect(parsed.port).toBe(2181);
|
||||
expect(parsed.database).toBe("analytics");
|
||||
expect(parsed.urlParams).toBe("tag=prod");
|
||||
});
|
||||
|
||||
it("parses Arrow Flight SQL JDBC URLs into a Dremio JDBC profile", () => {
|
||||
const parsed = parseConnectionUrl("jdbc:arrow-flight-sql://admin:secret@dremio.example.com:32010?schema=analytics&useEncryption=true");
|
||||
|
||||
expect(parsed.dbType).toBe("jdbc");
|
||||
expect(parsed.driverProfile).toBe("dremio");
|
||||
expect(parsed.driverLabel).toBe("Dremio");
|
||||
expect(parsed.host).toBe("dremio.example.com");
|
||||
expect(parsed.port).toBe(32010);
|
||||
expect(parsed.database).toBe("analytics");
|
||||
expect(parsed.username).toBe("admin");
|
||||
expect(parsed.password).toBe("secret");
|
||||
expect(parsed.urlParams).toBe("schema=analytics&useEncryption=true");
|
||||
expect(parsed.ssl).toBe(true);
|
||||
expect(parsed.connectionString).toBe("jdbc:arrow-flight-sql://admin:secret@dremio.example.com:32010?schema=analytics&useEncryption=true");
|
||||
});
|
||||
|
||||
it("uses the Arrow Flight SQL default port when the JDBC URL omits a port", () => {
|
||||
const parsed = parseConnectionUrl("jdbc:arrow-flight-sql://dremio.example.com");
|
||||
|
||||
expect(parsed.port).toBe(32010);
|
||||
expect(parsed.database).toBeUndefined();
|
||||
});
|
||||
|
||||
it("parses unencrypted Arrow Flight SQL JDBC URLs", () => {
|
||||
const parsed = parseConnectionUrl("jdbc:arrow-flight-sql://dremio.example.com:32010?useEncryption=false");
|
||||
|
||||
expect(parsed.port).toBe(32010);
|
||||
expect(parsed.urlParams).toBe("useEncryption=false");
|
||||
expect(parsed.ssl).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -419,6 +419,67 @@ function parseJdbcInformixUrl(source: string): ParsedConnectionUrl | null {
|
||||
};
|
||||
}
|
||||
|
||||
function parseJdbcDremioUrl(source: string): ParsedConnectionUrl | null {
|
||||
const match = /^jdbc:dremio:(?<mode>direct|zk)=(?<host>\[[^\]]+\]|[^:;]+)(?::(?<port>\d+))?(?:;(?<params>.*))?$/i.exec(source);
|
||||
if (!match?.groups) return null;
|
||||
|
||||
const props = new Map<string, string>();
|
||||
const urlParams: string[] = [];
|
||||
for (const part of (match.groups.params || "").split(";")) {
|
||||
if (!part) continue;
|
||||
const [rawKey, ...rest] = part.split("=");
|
||||
const key = rawKey.trim();
|
||||
const value = rest.join("=");
|
||||
const normalizedKey = key.toLowerCase();
|
||||
if (normalizedKey === "schema" || normalizedKey === "user" || normalizedKey === "password") {
|
||||
props.set(normalizedKey, value);
|
||||
} else {
|
||||
urlParams.push(part);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
dbType: "jdbc",
|
||||
driverProfile: "dremio",
|
||||
driverLabel: "Dremio",
|
||||
host: match.groups.host.replace(/^\[/, "").replace(/\]$/, ""),
|
||||
port: match.groups.port ? Number(match.groups.port) : match.groups.mode.toLowerCase() === "zk" ? 2181 : 31010,
|
||||
username: decodeUrlPart(props.get("user") || ""),
|
||||
password: decodeUrlPart(props.get("password") || ""),
|
||||
database: decodeUrlPart(props.get("schema") || "") || undefined,
|
||||
urlParams: urlParams.join(";"),
|
||||
ssl: false,
|
||||
connectionString: source,
|
||||
};
|
||||
}
|
||||
|
||||
function parseJdbcDremioArrowFlightSqlUrl(source: string): ParsedConnectionUrl | null {
|
||||
if (!/^jdbc:arrow-flight-sql:\/\//i.test(source)) return null;
|
||||
|
||||
let parsed: URL;
|
||||
try {
|
||||
parsed = new URL(source.replace(/^jdbc:/i, ""));
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
|
||||
const urlParams = parsed.search.replace(/^\?/, "");
|
||||
|
||||
return {
|
||||
dbType: "jdbc",
|
||||
driverProfile: "dremio",
|
||||
driverLabel: "Dremio",
|
||||
host: parsed.hostname.replace(/^\[(.*)]$/, "$1"),
|
||||
port: parsed.port ? Number(parsed.port) : 32010,
|
||||
username: decodeUrlPart(parsed.username),
|
||||
password: decodeUrlPart(parsed.password),
|
||||
database: queryParamValue(urlParams, "schema") || undefined,
|
||||
urlParams,
|
||||
ssl: queryParamValue(urlParams, "useEncryption")?.toLowerCase() !== "false",
|
||||
connectionString: source,
|
||||
};
|
||||
}
|
||||
|
||||
export function parseConnectionUrl(value: string, preferredProfile?: string): ParsedConnectionUrl {
|
||||
const input = value.trim();
|
||||
if (!input) {
|
||||
@@ -432,6 +493,10 @@ export function parseConnectionUrl(value: string, preferredProfile?: string): Pa
|
||||
if (jdbcGbase8s) return jdbcGbase8s;
|
||||
const jdbcInformix = parseJdbcInformixUrl(input);
|
||||
if (jdbcInformix) return jdbcInformix;
|
||||
const jdbcDremioArrowFlightSql = parseJdbcDremioArrowFlightSqlUrl(input);
|
||||
if (jdbcDremioArrowFlightSql) return jdbcDremioArrowFlightSql;
|
||||
const jdbcDremio = parseJdbcDremioUrl(input);
|
||||
if (jdbcDremio) return jdbcDremio;
|
||||
const jdbcOracle = parseJdbcOracleUrl(input);
|
||||
if (jdbcOracle) return jdbcOracle;
|
||||
const jdbcSqlServer = parseJdbcSqlServerUrl(input);
|
||||
|
||||
@@ -18,6 +18,10 @@ else
|
||||
fi
|
||||
|
||||
exec "$JAVA_BIN" \
|
||||
-XX:+IgnoreUnrecognizedVMOptions \
|
||||
--add-opens=java.base/java.nio=ALL-UNNAMED \
|
||||
--add-opens=java.base/sun.nio.ch=ALL-UNNAMED \
|
||||
${DBX_JAVA_OPTS:-} \
|
||||
-Dfile.encoding=UTF-8 \
|
||||
-Dsun.stdout.encoding=UTF-8 \
|
||||
-Dsun.stderr.encoding=UTF-8 \
|
||||
|
||||
@@ -28,4 +28,4 @@ echo Java runtime not found. Install Java or the optional DBX JDBC runtime. >&2
|
||||
exit /b 127
|
||||
|
||||
:run
|
||||
"%JAVA_BIN%" -Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 -Dsun.stderr.encoding=UTF-8 -jar "%~dp0..\lib\dbx-jdbc-plugin.jar"
|
||||
"%JAVA_BIN%" -XX:+IgnoreUnrecognizedVMOptions --add-opens=java.base/java.nio=ALL-UNNAMED --add-opens=java.base/sun.nio.ch=ALL-UNNAMED %DBX_JAVA_OPTS% -Dfile.encoding=UTF-8 -Dsun.stdout.encoding=UTF-8 -Dsun.stderr.encoding=UTF-8 -jar "%~dp0..\lib\dbx-jdbc-plugin.jar"
|
||||
|
||||
@@ -1704,7 +1704,7 @@ public final class DbxJdbcPlugin {
|
||||
}
|
||||
|
||||
private static String jdbcUrlParamSeparator(String base) {
|
||||
if (urlMatchesPrefix(base, "jdbc:sqlserver:")) {
|
||||
if (urlMatchesPrefix(base, "jdbc:sqlserver:") || urlMatchesPrefix(base, "jdbc:dremio:")) {
|
||||
return base.endsWith(";") ? "" : ";";
|
||||
}
|
||||
if (jdbcUrlUsesColonProperties(base)) {
|
||||
|
||||
@@ -315,6 +315,21 @@ final class DbxJdbcPluginTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void jdbcUrlAppendsDremioConnectionUrlParamsWithSemicolon() throws Exception {
|
||||
JsonNode connection = MAPPER.readTree("""
|
||||
{
|
||||
"connection_string": "jdbc:dremio:direct=dremio.example.com:31010",
|
||||
"url_params": "schema=Samples;ssl=true"
|
||||
}
|
||||
""");
|
||||
|
||||
assertEquals(
|
||||
"jdbc:dremio:direct=dremio.example.com:31010;schema=Samples;ssl=true",
|
||||
DbxJdbcPlugin.jdbcUrl(connection)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void jdbcUrlAppendsDb2ConnectionUrlParamsWithColonProperties() throws Exception {
|
||||
JsonNode connection = MAPPER.readTree("""
|
||||
|
||||
Reference in New Issue
Block a user