diff --git a/apps/desktop/src/components/icons/DatabaseIcon.vue b/apps/desktop/src/components/icons/DatabaseIcon.vue
index 4925452357..8709ec0068 100644
--- a/apps/desktop/src/components/icons/DatabaseIcon.vue
+++ b/apps/desktop/src/components/icons/DatabaseIcon.vue
@@ -52,6 +52,7 @@ const assetIcons: Record
= {
kylin: "apache_kylin",
cockroachdb: "cockroachdb",
db2: "db2",
+ dremio: "dremio",
bigquery: "bigquery",
cassandra: "cassandra",
doris: "doris",
diff --git a/apps/desktop/src/i18n/locales/en.ts b/apps/desktop/src/i18n/locales/en.ts
index 68c5911872..ffb10e6cf7 100644
--- a/apps/desktop/src/i18n/locales/en.ts
+++ b/apps/desktop/src/i18n/locales/en.ts
@@ -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)",
diff --git a/apps/desktop/src/i18n/locales/es.ts b/apps/desktop/src/i18n/locales/es.ts
index 67d7df02e8..c2e571b506 100644
--- a/apps/desktop/src/i18n/locales/es.ts
+++ b/apps/desktop/src/i18n/locales/es.ts
@@ -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)",
diff --git a/apps/desktop/src/i18n/locales/it.ts b/apps/desktop/src/i18n/locales/it.ts
index 21adc74ab9..81318f2aba 100644
--- a/apps/desktop/src/i18n/locales/it.ts
+++ b/apps/desktop/src/i18n/locales/it.ts
@@ -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)",
diff --git a/apps/desktop/src/i18n/locales/ja.ts b/apps/desktop/src/i18n/locales/ja.ts
index 41cb2fe2fb..91c353e6f4 100644
--- a/apps/desktop/src/i18n/locales/ja.ts
+++ b/apps/desktop/src/i18n/locales/ja.ts
@@ -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: "ドライバークラス(任意)",
diff --git a/apps/desktop/src/i18n/locales/pt-BR.ts b/apps/desktop/src/i18n/locales/pt-BR.ts
index a16e2336e0..6d41959020 100644
--- a/apps/desktop/src/i18n/locales/pt-BR.ts
+++ b/apps/desktop/src/i18n/locales/pt-BR.ts
@@ -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)",
diff --git a/apps/desktop/src/i18n/locales/zh-CN.ts b/apps/desktop/src/i18n/locales/zh-CN.ts
index 0a463f4091..e5d5ca355c 100644
--- a/apps/desktop/src/i18n/locales/zh-CN.ts
+++ b/apps/desktop/src/i18n/locales/zh-CN.ts
@@ -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: "驱动类(可选)",
diff --git a/apps/desktop/src/i18n/locales/zh-TW.ts b/apps/desktop/src/i18n/locales/zh-TW.ts
index 2a9bb33dd3..092f0c6b6f 100644
--- a/apps/desktop/src/i18n/locales/zh-TW.ts
+++ b/apps/desktop/src/i18n/locales/zh-TW.ts
@@ -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: "驅動程式類別(可選)",
diff --git a/apps/desktop/src/lib/__tests__/connectionUrl.dremio.spec.ts b/apps/desktop/src/lib/__tests__/connectionUrl.dremio.spec.ts
new file mode 100644
index 0000000000..af5315e578
--- /dev/null
+++ b/apps/desktop/src/lib/__tests__/connectionUrl.dremio.spec.ts
@@ -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);
+ });
+});
diff --git a/apps/desktop/src/lib/connectionUrl.ts b/apps/desktop/src/lib/connectionUrl.ts
index 34676c14c7..bda71045cb 100644
--- a/apps/desktop/src/lib/connectionUrl.ts
+++ b/apps/desktop/src/lib/connectionUrl.ts
@@ -419,6 +419,67 @@ function parseJdbcInformixUrl(source: string): ParsedConnectionUrl | null {
};
}
+function parseJdbcDremioUrl(source: string): ParsedConnectionUrl | null {
+ const match = /^jdbc:dremio:(?direct|zk)=(?\[[^\]]+\]|[^:;]+)(?::(?\d+))?(?:;(?.*))?$/i.exec(source);
+ if (!match?.groups) return null;
+
+ const props = new Map();
+ 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);
diff --git a/plugins/jdbc/bin/dbx-jdbc-plugin b/plugins/jdbc/bin/dbx-jdbc-plugin
index 112d9815fa..fbd64675e0 100755
--- a/plugins/jdbc/bin/dbx-jdbc-plugin
+++ b/plugins/jdbc/bin/dbx-jdbc-plugin
@@ -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 \
diff --git a/plugins/jdbc/bin/dbx-jdbc-plugin.bat b/plugins/jdbc/bin/dbx-jdbc-plugin.bat
index 4a4b98f04e..bb24940e97 100644
--- a/plugins/jdbc/bin/dbx-jdbc-plugin.bat
+++ b/plugins/jdbc/bin/dbx-jdbc-plugin.bat
@@ -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"
diff --git a/plugins/jdbc/src/main/java/app/dbx/jdbc/DbxJdbcPlugin.java b/plugins/jdbc/src/main/java/app/dbx/jdbc/DbxJdbcPlugin.java
index 1a0f5ccbab..5112395ea4 100644
--- a/plugins/jdbc/src/main/java/app/dbx/jdbc/DbxJdbcPlugin.java
+++ b/plugins/jdbc/src/main/java/app/dbx/jdbc/DbxJdbcPlugin.java
@@ -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)) {
diff --git a/plugins/jdbc/src/test/java/app/dbx/jdbc/DbxJdbcPluginTest.java b/plugins/jdbc/src/test/java/app/dbx/jdbc/DbxJdbcPluginTest.java
index d56dcfd26d..8dcf29fc38 100644
--- a/plugins/jdbc/src/test/java/app/dbx/jdbc/DbxJdbcPluginTest.java
+++ b/plugins/jdbc/src/test/java/app/dbx/jdbc/DbxJdbcPluginTest.java
@@ -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("""