fix(import): preserve explicit DBeaver database names

This commit is contained in:
tatuke authored and GitHub committed 2026-08-21 18:12:39 +08:00
1 parent 2bb85dcdc9
commit 68acf9f132
2 files changed
+270 -4

No files matched your search

+257 -2
View File
@@ -1,6 +1,6 @@
import { describe, expect, it } from "vitest";
import type { SidebarLayout, SidebarOrderEntry } from "@/types/database";
import { parseDbeaverConnections, parseDbeaverImport } from "@/lib/imports/dbeaverImport";
import type { SidebarLayout, SidebarOrderEntry } from "../../../types/database";
import { parseDbeaverConnections, parseDbeaverImport } from "../../imports/dbeaverImport";
function payload(dataSources: Record<string, unknown>) {
return JSON.stringify({ format: "dbeaver-import", dataSources: JSON.stringify(dataSources) });
@@ -17,6 +17,16 @@ function mysqlConnection(id: string, name: string, folder?: string) {
};
}
function clickhouseConnection(id: string, name: string, configuration: Record<string, unknown>) {
return {
id,
name,
provider: "clickhouse",
driver: "com_clickhouse",
configuration,
};
}
function layoutLabels(layout: SidebarLayout, connectionNames: Map<string, string>): unknown[] {
const groupNames = new Map(layout.groups.map((group) => [group.id, group.name]));
const visit = (entries: SidebarOrderEntry[]): unknown[] => entries.map((entry) => (entry.type === "connection" ? connectionNames.get(entry.id) : { group: groupNames.get(entry.id), children: visit(entry.children ?? []) }));
@@ -50,6 +60,251 @@ describe("DBeaver folder import", () => {
expect(connection?.database).toBeUndefined();
});
it("keeps the default database empty for MySQL root JDBC URLs", async () => {
const [connection] = await parseDbeaverConnections(
payload({
connections: {
mysql: {
id: "mysql",
name: "208",
provider: "mysql",
driver: "mysql8",
configuration: {
host: "192.168.3.12",
port: "51345",
url: "jdbc:mysql://192.168.3.12:51345/",
},
},
},
}),
);
expect(connection).toMatchObject({
name: "208",
db_type: "mysql",
host: "192.168.3.12",
port: 51345,
});
expect(connection?.database).toBeUndefined();
});
it("keeps the default database empty for ClickHouse root JDBC URLs", async () => {
const [connection] = await parseDbeaverConnections(
payload({
connections: {
clickhouse: clickhouseConnection("clickhouse", "CH", {
host: "192.168.1.39",
port: "8123",
url: "jdbc:clickhouse://192.168.1.39:8123",
}),
},
}),
);
expect(connection).toMatchObject({
name: "CH",
db_type: "clickhouse",
host: "192.168.1.39",
port: 8123,
});
expect(connection?.database).toBeUndefined();
});
it("keeps the default database empty for SQL Server connections without databaseName", async () => {
const [connection] = await parseDbeaverConnections(
payload({
connections: {
sqlserver: {
id: "sqlserver",
name: "MSSQL",
provider: "sqlserver",
driver: "mssql_jdbc",
configuration: {
host: "192.168.10.25",
port: "1433",
url: "jdbc:sqlserver://192.168.10.25:1433;encrypt=true",
},
},
},
}),
);
expect(connection).toMatchObject({
name: "MSSQL",
db_type: "sqlserver",
host: "192.168.10.25",
port: 1433,
});
expect(connection?.database).toBeUndefined();
});
it("keeps the default database empty for Oracle connections without a configured service", async () => {
const [connection] = await parseDbeaverConnections(
payload({
connections: {
oracle: {
id: "oracle",
name: "Oracle",
provider: "oracle",
driver: "oracle_jdbc",
configuration: {
host: "192.168.10.30",
port: "1521",
},
},
},
}),
);
expect(connection).toMatchObject({
name: "Oracle",
db_type: "oracle",
host: "192.168.10.30",
port: 1521,
});
expect(connection?.database).toBeUndefined();
});
it("keeps the default database empty for PostgreSQL root JDBC URLs", async () => {
const [connection] = await parseDbeaverConnections(
payload({
connections: {
postgres: {
id: "postgres",
name: "PG",
provider: "postgresql",
driver: "postgres-jdbc",
configuration: {
host: "192.168.10.40",
port: "5432",
url: "jdbc:postgresql://192.168.10.40:5432/",
},
},
},
}),
);
expect(connection).toMatchObject({
name: "PG",
db_type: "postgres",
host: "192.168.10.40",
port: 5432,
});
expect(connection?.database).toBeUndefined();
});
it("keeps the default database empty for MariaDB root JDBC URLs", async () => {
const [connection] = await parseDbeaverConnections(
payload({
connections: {
mariadb: {
id: "mariadb",
name: "MariaDB",
provider: "mysql",
driver: "mariadb",
configuration: {
host: "192.168.10.50",
port: "3306",
url: "jdbc:mariadb://192.168.10.50:3306/",
},
},
},
}),
);
expect(connection).toMatchObject({
name: "MariaDB",
db_type: "mysql",
driver_profile: "mariadb",
host: "192.168.10.50",
port: 3306,
});
expect(connection?.database).toBeUndefined();
});
it("keeps the default database empty for DB2 connections (fallback to JDBC) without database configured", async () => {
const [connection] = await parseDbeaverConnections(
payload({
connections: {
db2: {
id: "db2",
name: "DB2",
provider: "db2",
driver: "db2",
configuration: {
host: "192.168.10.60",
port: "50000",
url: "jdbc:db2://192.168.10.60:50000/",
},
},
},
}),
);
expect(connection).toMatchObject({
name: "DB2",
db_type: "jdbc",
host: "192.168.10.60",
port: 50000,
});
expect(connection?.database).toBeUndefined();
});
it("does not treat an opaque JDBC host and port as the database", async () => {
const [connection] = await parseDbeaverConnections(
payload({
connections: {
opaque: {
id: "opaque",
name: "Opaque JDBC",
provider: "generic",
driver: "com.example.Driver",
configuration: {
url: "jdbc:example:db.example.com:1234",
},
},
},
}),
);
expect(connection).toMatchObject({
name: "Opaque JDBC",
db_type: "jdbc",
connection_string: "jdbc:example:db.example.com:1234",
});
expect(connection?.database).toBeUndefined();
});
it("preserves explicit database names that resemble the host or port", async () => {
const connections = await parseDbeaverConnections(
payload({
connections: {
mysql: {
id: "mysql",
name: "208",
provider: "mysql",
driver: "mysql8",
configuration: {
host: "192.168.3.12",
port: "51345",
database: "192.168.3.12:51345",
url: "jdbc:mysql://192.168.3.12:51345/",
},
},
clickhouse: clickhouseConnection("clickhouse", "CH", {
host: "192.168.1.39",
port: "8123",
database: "8123",
url: "jdbc:clickhouse://192.168.1.39:8123",
}),
},
}),
);
expect(connections[0]?.database).toBe("192.168.3.12:51345");
expect(connections[1]?.database).toBe("8123");
});
it("keeps parseDbeaverConnections compatible when no folders exist", async () => {
const connections = await parseDbeaverConnections(payload({ connections: { root: mysqlConnection("root", "Root") } }));
+13 -2
View File
@@ -81,6 +81,15 @@ function getNumber(value: unknown) {
return Number.isFinite(parsed) && parsed > 0 ? parsed : 0;
}
function firstNonEmptyString(...values: unknown[]) {
for (const value of values) {
if (typeof value !== "string") continue;
const normalized = value.trim();
if (normalized) return normalized;
}
return "";
}
function inferProfile(entry: DbeaverConnectionEntry): ConnectionProfile {
if (/^jdbcx:/i.test(getString(entry.configuration?.url))) return profileMap.jdbcx;
if (normalizeKey(entry.provider) === "opentenbase") return profileMap.opentenbase;
@@ -179,6 +188,7 @@ function parseJdbcUrl(url: string, profile: ConnectionProfile) {
try {
const parsed = new URL(withoutJdbc);
if (!parsed.hostname) return result;
result.host = parsed.hostname;
result.port = parsed.port ? Number(parsed.port) : profile.port;
result.database = parsed.pathname.replace(/^\/+/, "").split("/")[0] || undefined;
@@ -223,7 +233,8 @@ function buildConnection(entry: DbeaverConnectionEntry, credentials: ReturnType<
const config = entry.configuration || {};
const url = getString(config.url);
const parsedUrl = parseJdbcUrl(url, profile);
const configuredDatabase = getString(config.database || config["database-name"] || config.schema || parsedUrl.database);
const configuredPort = getNumber(config.port || config["host-port"] || parsedUrl.port) || profile.port;
const configuredDatabase = firstNonEmptyString(config.database, config["database-name"], config.schema, parsedUrl.database);
const host = getString(config.host || config["host-name"] || parsedUrl.host || (profile.dbType === "sqlite" ? configuredDatabase : "127.0.0.1"));
const database = profile.dbType === "sqlite" ? "" : configuredDatabase;
const name = getString(entry.name || database || host || profile.label);
@@ -236,7 +247,7 @@ function buildConnection(entry: DbeaverConnectionEntry, credentials: ReturnType<
driver_label: profile.label,
url_params: getString(parsedUrl.params),
host,
port: getNumber(config.port || config["host-port"] || parsedUrl.port) || profile.port,
port: configuredPort,
username: credentials.username || getString(parsedUrl.username) || profile.user,
password: credentials.password || getString(parsedUrl.password),
database: database || undefined,