mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-20 00:17:44 +00:00
fix(transfer): keep doris-family mysql connections selectable
This commit is contained in:
@@ -21,7 +21,7 @@ import TransferTaskTree from "@/components/transfer/TransferTaskTree.vue";
|
||||
import type { DatabaseType } from "@/types/database";
|
||||
import type { TransferTask, TransferTaskConfig } from "@/types/database";
|
||||
import { isSchemaAware, supportsTransfer } from "@/lib/database/databaseCapabilities";
|
||||
import { effectiveDatabaseTypeForConnection } from "@/lib/database/jdbcDialect";
|
||||
import { transferDatabaseTypeForConnection } from "@/lib/database/jdbcDialect";
|
||||
import { isDorisFamilyCatalogCapable } from "@/lib/database/databaseFeatureSupport";
|
||||
import { decodeTransferDatabaseOption, encodeTransferDatabaseOptions, isSameTransferDatabase, isTransferDatabaseSelected, normalizeTransferCatalog } from "@/lib/database/dataTransferSelection";
|
||||
import { formatDatabaseLabel } from "@/lib/database/defaultDatabase";
|
||||
@@ -60,7 +60,7 @@ const transferDialogStyle = {
|
||||
|
||||
const store = useConnectionStore();
|
||||
|
||||
const sqlConnections = computed(() => store.connections.filter((c) => supportsTransfer(effectiveDatabaseTypeForConnection(c))));
|
||||
const sqlConnections = computed(() => store.connections.filter((c) => supportsTransfer(transferDatabaseTypeForConnection(c))));
|
||||
|
||||
// Source state
|
||||
const sourceConnectionId = ref("");
|
||||
@@ -115,7 +115,7 @@ const treeDisabledGroups = computed<TransferObjectKind[]>(() => {
|
||||
}
|
||||
const sourceConfig = store.getConfig(sourceConnectionId.value);
|
||||
const targetConfig = store.getConfig(targetConnectionId.value);
|
||||
const allowed = crossFamilyTransferableKinds(effectiveDatabaseTypeForConnection(sourceConfig), effectiveDatabaseTypeForConnection(targetConfig));
|
||||
const allowed = crossFamilyTransferableKinds(transferDatabaseTypeForConnection(sourceConfig), transferDatabaseTypeForConnection(targetConfig));
|
||||
return presentKinds.filter((k) => k !== "TABLE" && !allowed.includes(k));
|
||||
});
|
||||
|
||||
@@ -133,9 +133,9 @@ const showCrossFamilyViewHint = computed(() => {
|
||||
const targetConfig = store.getConfig(targetConnectionId.value);
|
||||
if (transferContent.value === "dataOnly") return false;
|
||||
if (!sourceConfig || !targetConfig) return false;
|
||||
const allowed = crossFamilyTransferableKinds(effectiveDatabaseTypeForConnection(sourceConfig), effectiveDatabaseTypeForConnection(targetConfig));
|
||||
const allowed = crossFamilyTransferableKinds(transferDatabaseTypeForConnection(sourceConfig), transferDatabaseTypeForConnection(targetConfig));
|
||||
if (!allowed.includes("VIEW")) return false;
|
||||
return !isSameTransferFamily(effectiveDatabaseTypeForConnection(sourceConfig), effectiveDatabaseTypeForConnection(targetConfig)) && (selectedObjects.value.VIEW?.size ?? 0) > 0;
|
||||
return !isSameTransferFamily(transferDatabaseTypeForConnection(sourceConfig), transferDatabaseTypeForConnection(targetConfig)) && (selectedObjects.value.VIEW?.size ?? 0) > 0;
|
||||
});
|
||||
const pendingSourceSchemaPrefill = ref("");
|
||||
const pendingSelectedTablesPrefill = ref<string[] | null>(null);
|
||||
@@ -409,7 +409,7 @@ async function loadObjects(isCancelled: () => boolean = () => false) {
|
||||
const config = store.getConfig(connectionId);
|
||||
const needsSchema = isSchemaAware(config?.db_type);
|
||||
const schema = needsSchema && schemaValue ? schemaValue : database;
|
||||
const kinds = transferObjectKindsForDatabase(effectiveDatabaseTypeForConnection(config));
|
||||
const kinds = transferObjectKindsForDatabase(transferDatabaseTypeForConnection(config));
|
||||
const groups: Partial<Record<TransferObjectKind, string[]>> = {};
|
||||
for (const kind of kinds) {
|
||||
try {
|
||||
|
||||
@@ -67,7 +67,7 @@ describe("DataTransferDialog transfer prefill", () => {
|
||||
});
|
||||
|
||||
it("routes table-only database kinds through the table-list request", () => {
|
||||
expect(dialogSource).toContain("const kinds = transferObjectKindsForDatabase(effectiveDatabaseTypeForConnection(config))");
|
||||
expect(dialogSource).toContain("const kinds = transferObjectKindsForDatabase(transferDatabaseTypeForConnection(config))");
|
||||
expect(dialogSource).toContain("for (const kind of kinds)");
|
||||
expect(dialogSource).toContain('if (kind === "TABLE")');
|
||||
expect(dialogSource).toContain("await api.listTables(connectionId, database, schema");
|
||||
@@ -75,13 +75,18 @@ describe("DataTransferDialog transfer prefill", () => {
|
||||
|
||||
it("keeps MongoDB collection loading ahead of the generic object-kind path", () => {
|
||||
const mongoCollectionBranch = dialogSource.indexOf("if (isMongoConnection(connectionId))");
|
||||
const genericObjectKinds = dialogSource.indexOf("const kinds = transferObjectKindsForDatabase(effectiveDatabaseTypeForConnection(config))");
|
||||
const genericObjectKinds = dialogSource.indexOf("const kinds = transferObjectKindsForDatabase(transferDatabaseTypeForConnection(config))");
|
||||
|
||||
expect(mongoCollectionBranch).toBeGreaterThan(-1);
|
||||
expect(dialogSource).toContain("await api.mongoListCollections(connectionId, database)");
|
||||
expect(mongoCollectionBranch).toBeLessThan(genericObjectKinds);
|
||||
});
|
||||
|
||||
it("admits connections via the transfer resolver so doris-family mysql connections stay selectable", () => {
|
||||
expect(dialogSource).toContain("supportsTransfer(transferDatabaseTypeForConnection(c))");
|
||||
expect(dialogSource).not.toContain("supportsTransfer(effectiveDatabaseTypeForConnection(c))");
|
||||
});
|
||||
|
||||
it("disables non-table groups for data-only and cross-family transfers", () => {
|
||||
expect(dialogSource).toContain("treeDisabledGroups");
|
||||
expect(dialogSource).toContain('transferContent.value === "dataOnly"');
|
||||
|
||||
@@ -25,7 +25,10 @@ import {
|
||||
setGaussdbIdentifierQuoteStyle,
|
||||
setGaussdbTargetServerType,
|
||||
supportsGaussdbIdentifierQuoteStyle,
|
||||
transferDatabaseTypeForConnection,
|
||||
} from "@/lib/database/jdbcDialect";
|
||||
import { supportsTransfer } from "@/lib/database/databaseFeatureSupport";
|
||||
import { transferObjectKindsForDatabase } from "@/lib/database/transferObjectKinds";
|
||||
|
||||
describe("jdbc dialect inference", () => {
|
||||
it("detects InterSystems IRIS and Caché JDBC connections", () => {
|
||||
@@ -223,6 +226,34 @@ describe("jdbc dialect inference", () => {
|
||||
expect(effectiveDatabaseTypeForConnection(gbase8sProfileWithLegacyGbaseUrl)).toBe("jdbc");
|
||||
});
|
||||
|
||||
it("keeps doris-family mysql connections on the mysql transfer path", () => {
|
||||
// Doris/SelectDB/StarRocks connections are saved as db_type=mysql with a
|
||||
// doris-family driver_profile. The SQL dialect stays doris/starrocks
|
||||
// (3-part catalog names), but transfer admission and object kinds must ride
|
||||
// the raw mysql db_type: the standalone doris/starrocks manifest entries are
|
||||
// not transfer-capable, so the effective type would hide these connections
|
||||
// from the transfer dialog and drop their non-table object kinds.
|
||||
for (const driver_profile of ["starrocks", "doris", "selectdb"]) {
|
||||
const connection = { db_type: "mysql" as const, driver_profile };
|
||||
const transferType = transferDatabaseTypeForConnection(connection);
|
||||
|
||||
expect(transferType).toBe("mysql");
|
||||
expect(supportsTransfer(transferType)).toBe(true);
|
||||
expect(transferObjectKindsForDatabase(transferType)).toContain("VIEW");
|
||||
}
|
||||
});
|
||||
|
||||
it("resolves transfer types for non-doris-family connections through the effective type", () => {
|
||||
expect(transferDatabaseTypeForConnection(undefined)).toBeUndefined();
|
||||
expect(transferDatabaseTypeForConnection({ db_type: "mysql" })).toBe("mysql");
|
||||
expect(transferDatabaseTypeForConnection({ db_type: "postgres" })).toBe("postgres");
|
||||
expect(transferDatabaseTypeForConnection({ db_type: "gbase" })).toBe("mysql");
|
||||
expect(transferDatabaseTypeForConnection({ db_type: "jdbc", connection_string: "jdbc:gbase://localhost:5258/dbx_test" })).toBe("gbase");
|
||||
// A generic-JDBC Doris URL keeps its raw db_type (never admitted), matching
|
||||
// the pre-effective-type behavior for unknown jdbc connections.
|
||||
expect(transferDatabaseTypeForConnection({ db_type: "jdbc", connection_string: "jdbc:doris://localhost:9030/dbx_test" })).toBe("jdbc");
|
||||
});
|
||||
|
||||
it("uses Hive tree and execution semantics for Inceptor JDBC metadata", () => {
|
||||
const connection = {
|
||||
db_type: "jdbc" as const,
|
||||
|
||||
@@ -82,6 +82,22 @@ export function effectiveDatabaseTypeForConnection(connection?: JdbcDialectConne
|
||||
return inferJdbcDialect(connection) ?? "jdbc";
|
||||
}
|
||||
|
||||
/**
|
||||
* Database type the data-transfer pipeline treats a connection as. Doris-family
|
||||
* engines (Doris/SelectDB/StarRocks) are saved as `db_type=mysql` with a
|
||||
* doris/starrocks `driver_profile`, and the transfer backend routes them through
|
||||
* the MySQL object family (catalog routing is driver_profile-aware); the
|
||||
* standalone `doris`/`starrocks` manifest entries are not transfer-capable.
|
||||
* Resolve those connections back to their raw db_type so they stay selectable in
|
||||
* the transfer dialog and keep the MySQL object kinds — the effective type alone
|
||||
* would drop them from the connection list and disable every non-table kind.
|
||||
*/
|
||||
export function transferDatabaseTypeForConnection(connection?: JdbcDialectConnection): DatabaseType | undefined {
|
||||
const effective = effectiveDatabaseTypeForConnection(connection);
|
||||
if (effective === "doris" || effective === "starrocks") return connection?.db_type;
|
||||
return effective;
|
||||
}
|
||||
|
||||
export function connectionUsesConnectionRootSchemaMode(connection?: JdbcDialectConnection): boolean {
|
||||
const type = effectiveDatabaseTypeForConnection(connection);
|
||||
return !!type && CONNECTION_ROOT_SCHEMA_TYPES.has(type);
|
||||
|
||||
Reference in New Issue
Block a user