fix(compare): let oracle pick schemas in data compare
This commit is contained in:
1 parent
5f9ba51769
commit
23f0b1255b
4 files changed
+148
-26
No files matched your search
@@ -319,13 +319,12 @@ async function loadDatabases(connectionId: string, side: "source" | "target") {
|
||||
if (!connectionId) return;
|
||||
await store.ensureConnected(connectionId);
|
||||
const config = store.getConfig(connectionId);
|
||||
const names =
|
||||
config?.db_type === "dameng"
|
||||
? await fetchNamespaceOptionsForConnection(connectionId, config)
|
||||
: databaseOptionsForConnection(
|
||||
(await api.listDatabases(connectionId)).map((database) => database.name),
|
||||
config,
|
||||
);
|
||||
const names = config
|
||||
? await fetchNamespaceOptionsForConnection(connectionId, config)
|
||||
: databaseOptionsForConnection(
|
||||
(await api.listDatabases(connectionId)).map((database) => database.name),
|
||||
config,
|
||||
);
|
||||
if (side === "source") {
|
||||
sourceDatabases.value = names;
|
||||
sourceDatabase.value = names.length === 1 ? names[0] : "";
|
||||
|
||||
+100
-11
@@ -7,18 +7,44 @@ import DataCompareDialog from "@/components/diff/DataCompareDialog.vue";
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
ensureConnected: vi.fn().mockResolvedValue(undefined),
|
||||
listDatabases: vi.fn().mockResolvedValue([{ name: "DBX_TEST" }, { name: "OPS$ORACLE" }]),
|
||||
listSchemas: vi.fn().mockResolvedValue(["DBX_TEST"]),
|
||||
listTables: vi.fn().mockResolvedValue([{ name: "CODEX_7046_META", table_type: "TABLE" }]),
|
||||
listDatabases: vi.fn().mockResolvedValue([]),
|
||||
listSchemas: vi.fn().mockResolvedValue(["DBX_TEST", "REPORTING", "SYS"]),
|
||||
listTables: vi.fn().mockResolvedValue([{ name: "CODEX_7467_META", table_type: "TABLE" }]),
|
||||
getColumns: vi.fn().mockResolvedValue([{ name: "ID", data_type: "NUMBER", is_primary_key: true }]),
|
||||
}));
|
||||
|
||||
vi.mock("@/stores/connectionStore", () => ({
|
||||
useConnectionStore: () => ({
|
||||
connections: [{ id: "oracle-11g", name: "Oracle XE 11g", db_type: "oracle", driver_profile: "oracle", database: "XE" }],
|
||||
getConfig: (id: string) => (id === "oracle-11g" ? { id, name: "Oracle XE 11g", db_type: "oracle", driver_profile: "oracle", database: "XE" } : undefined),
|
||||
ensureConnected: mocks.ensureConnected,
|
||||
}),
|
||||
function passthrough(tag: string) {
|
||||
return defineComponent({
|
||||
inheritAttrs: false,
|
||||
setup(_, { attrs, slots }) {
|
||||
return () => h(tag, attrs, slots.default?.());
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
vi.mock("@/stores/connectionStore", () => {
|
||||
const connections = [
|
||||
{ id: "oracle-11g", name: "Oracle XE 11g", db_type: "oracle", driver_profile: "oracle", database: "XE" },
|
||||
{
|
||||
id: "oracle-jdbc-11g",
|
||||
name: "Oracle JDBC 11g",
|
||||
db_type: "jdbc",
|
||||
driver_profile: "oracle",
|
||||
connection_string: "jdbc:oracle:thin:@//localhost:1521/XE",
|
||||
jdbc_driver_class: "oracle.jdbc.OracleDriver",
|
||||
},
|
||||
];
|
||||
return {
|
||||
useConnectionStore: () => ({
|
||||
connections,
|
||||
getConfig: (id: string) => connections.find((connection) => connection.id === id),
|
||||
ensureConnected: mocks.ensureConnected,
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("@/components/connection/ConnectionGroupBadge.vue", () => ({
|
||||
default: passthrough("span"),
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/backend/api", () => ({
|
||||
@@ -39,7 +65,7 @@ async function flushAsyncSetup() {
|
||||
|
||||
afterEach(() => {
|
||||
for (const app of mountedApps.splice(0)) app.unmount();
|
||||
document.body.innerHTML = "";
|
||||
document.body.textContent = "";
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
@@ -64,8 +90,71 @@ describe("DataCompareDialog source prefill", () => {
|
||||
app.mount(container);
|
||||
await flushAsyncSetup();
|
||||
|
||||
expect(mocks.listDatabases).not.toHaveBeenCalled();
|
||||
expect(mocks.listSchemas).toHaveBeenCalledWith("oracle-11g", "XE", true);
|
||||
|
||||
const searchableSelectTriggers = [...document.querySelectorAll<HTMLButtonElement>("button.dbx-searchable-select-trigger")];
|
||||
const sourceDatabaseTrigger = searchableSelectTriggers[1];
|
||||
expect(sourceDatabaseTrigger?.title).toBe("DBX_TEST");
|
||||
expect(sourceDatabaseTrigger?.disabled).toBe(false);
|
||||
sourceDatabaseTrigger?.click();
|
||||
await flushAsyncSetup();
|
||||
|
||||
const databaseOptions = [...document.querySelectorAll<HTMLButtonElement>(".dbx-searchable-select-list button")].map((button) => button.textContent?.trim());
|
||||
expect(databaseOptions).toEqual(expect.arrayContaining(["DBX_TEST", "REPORTING"]));
|
||||
expect(mocks.listTables).toHaveBeenCalledWith("oracle-11g", "DBX_TEST", "DBX_TEST");
|
||||
expect(document.body.textContent).toContain("CODEX_7046_META");
|
||||
expect(document.body.textContent).toContain("CODEX_7467_META");
|
||||
expect(document.body.textContent).not.toContain("暂无可比较的表");
|
||||
});
|
||||
|
||||
it("loads schemas and tables after selecting an Oracle JDBC target connection", async () => {
|
||||
const container = document.createElement("div");
|
||||
document.body.append(container);
|
||||
const app = createApp(
|
||||
defineComponent({
|
||||
setup: () => () =>
|
||||
h(DataCompareDialog, {
|
||||
open: true,
|
||||
prefillConnectionId: "oracle-11g",
|
||||
prefillDatabase: "DBX_TEST",
|
||||
prefillSchema: "DBX_TEST",
|
||||
prefillTable: "CODEX_7467_META",
|
||||
}),
|
||||
}),
|
||||
);
|
||||
mountedApps.push(app);
|
||||
app.use(i18n);
|
||||
app.mount(container);
|
||||
await flushAsyncSetup();
|
||||
|
||||
const searchableSelectTriggers = [...document.querySelectorAll<HTMLButtonElement>("button.dbx-searchable-select-trigger")];
|
||||
const targetConnectionTrigger = searchableSelectTriggers[3];
|
||||
expect(targetConnectionTrigger).toBeDefined();
|
||||
targetConnectionTrigger?.click();
|
||||
await flushAsyncSetup();
|
||||
|
||||
const jdbcConnectionOption = [...document.querySelectorAll<HTMLButtonElement>(".dbx-searchable-select-list button")].find((button) => button.textContent?.trim() === "Oracle JDBC 11g");
|
||||
expect(jdbcConnectionOption).toBeDefined();
|
||||
jdbcConnectionOption?.click();
|
||||
await flushAsyncSetup();
|
||||
|
||||
expect(mocks.listSchemas).toHaveBeenCalledWith("oracle-jdbc-11g", "", true);
|
||||
|
||||
const triggersAfterTargetLoad = [...document.querySelectorAll<HTMLButtonElement>("button.dbx-searchable-select-trigger")];
|
||||
const targetDatabaseTrigger = triggersAfterTargetLoad[4];
|
||||
expect(targetDatabaseTrigger?.disabled).toBe(false);
|
||||
targetDatabaseTrigger?.click();
|
||||
await flushAsyncSetup();
|
||||
|
||||
const targetDatabaseOptions = [...document.querySelectorAll<HTMLButtonElement>(".dbx-searchable-select-list button")].map((button) => button.textContent?.trim());
|
||||
expect(targetDatabaseOptions).toEqual(expect.arrayContaining(["DBX_TEST", "REPORTING"]));
|
||||
|
||||
const reportingOption = [...document.querySelectorAll<HTMLButtonElement>(".dbx-searchable-select-list button")].find((button) => button.textContent?.trim() === "REPORTING");
|
||||
expect(reportingOption).toBeDefined();
|
||||
reportingOption?.click();
|
||||
await flushAsyncSetup();
|
||||
|
||||
expect(mocks.listTables).toHaveBeenCalledWith("oracle-jdbc-11g", "REPORTING", "DBX_TEST");
|
||||
expect(document.body.textContent).toContain("CODEX_7467_META");
|
||||
});
|
||||
});
|
||||
@@ -112,6 +112,31 @@ describe("namespace options", () => {
|
||||
expect(options).toEqual(["ARCHIVE"]);
|
||||
});
|
||||
|
||||
it("uses schemas for native and inferred JDBC Oracle namespaces", async () => {
|
||||
mocks.listSchemas.mockResolvedValue(["APP", "REPORTING", "SYS"]);
|
||||
|
||||
await expect(
|
||||
fetchNamespaceOptionsForConnection("oracle-1", {
|
||||
db_type: "oracle",
|
||||
database: "XE",
|
||||
}),
|
||||
).resolves.toEqual(["APP", "REPORTING"]);
|
||||
expect(mocks.listSchemas).toHaveBeenCalledWith("oracle-1", "XE", true);
|
||||
|
||||
vi.clearAllMocks();
|
||||
mocks.listSchemas.mockResolvedValue(["APP", "REPORTING", "SYS"]);
|
||||
|
||||
await expect(
|
||||
fetchNamespaceOptionsForConnection("oracle-jdbc-1", {
|
||||
db_type: "jdbc",
|
||||
connection_string: "jdbc:oracle:thin:@//localhost:1521/XE",
|
||||
jdbc_driver_class: "oracle.jdbc.OracleDriver",
|
||||
}),
|
||||
).resolves.toEqual(["APP", "REPORTING"]);
|
||||
expect(mocks.listSchemas).toHaveBeenCalledWith("oracle-jdbc-1", "", true);
|
||||
expect(mocks.listDatabases).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("preserves listDatabases and visible database filtering for other databases", async () => {
|
||||
mocks.listDatabases.mockResolvedValue([{ name: "app" }, { name: "analytics" }, { name: "postgres" }]);
|
||||
|
||||
@@ -187,9 +212,15 @@ describe("namespace options", () => {
|
||||
).resolves.toEqual(["APP_USER", "REPORTING"]);
|
||||
});
|
||||
|
||||
it("identifies only Dameng top-level options as schemas", () => {
|
||||
it("identifies connection-root namespace options as schemas", () => {
|
||||
expect(namespaceOptionsAreSchemas({ db_type: "dameng" })).toBe(true);
|
||||
expect(namespaceOptionsAreSchemas({ db_type: "oracle" })).toBe(false);
|
||||
expect(namespaceOptionsAreSchemas({ db_type: "oracle" })).toBe(true);
|
||||
expect(
|
||||
namespaceOptionsAreSchemas({
|
||||
db_type: "jdbc",
|
||||
connection_string: "jdbc:oracle:thin:@//localhost:1521/XE",
|
||||
}),
|
||||
).toBe(true);
|
||||
expect(namespaceOptionsAreSchemas({ db_type: "postgres" })).toBe(false);
|
||||
});
|
||||
|
||||
|
||||
@@ -4,10 +4,12 @@ import { filterDatabaseNamesForConnection, filterSchemaNamesForConnection } from
|
||||
import { isDorisFamilyCatalogCapable, supportsQueryTargetDatabaseListing } from "@/lib/database/databaseFeatureSupport";
|
||||
import { usesTreeSchemaMode } from "@/lib/database/databaseCapabilities";
|
||||
import { isInternalDorisCatalog } from "@/lib/database/databaseFeatureSupport";
|
||||
import { connectionUsesConnectionRootSchemaMode } from "@/lib/database/jdbcDialect";
|
||||
import type { CatalogInfo, ConnectionConfig } from "@/types/database";
|
||||
import * as api from "@/lib/backend/api";
|
||||
|
||||
type NamespaceOptionsConnection = Pick<ConnectionConfig, "database" | "db_type" | "driver_profile" | "visible_databases" | "visible_schemas"> & Partial<Pick<ConnectionConfig, "database_info">>;
|
||||
type NamespaceOptionsConnection = Pick<ConnectionConfig, "database" | "db_type" | "driver_profile" | "driver_label" | "connection_string" | "jdbc_driver_class" | "jdbc_driver_paths" | "visible_databases" | "visible_database_patterns" | "visible_schemas" | "username" | "show_system_schemas"> &
|
||||
Partial<Pick<ConnectionConfig, "database_info">>;
|
||||
export function catalogDatabaseOptionsKey(connectionId: string, catalog: string): string {
|
||||
return `${connectionId}:${catalog}`;
|
||||
}
|
||||
@@ -39,15 +41,16 @@ export function databaseOptionsForConnection(databaseNames: string[], connection
|
||||
return names;
|
||||
}
|
||||
|
||||
export function namespaceOptionsAreSchemas(connection: Pick<ConnectionConfig, "db_type"> | undefined): boolean {
|
||||
return connection?.db_type === "dameng";
|
||||
export function namespaceOptionsAreSchemas(connection: NamespaceOptionsConnection | undefined): boolean {
|
||||
return connectionUsesConnectionRootSchemaMode(connection);
|
||||
}
|
||||
|
||||
export async function fetchNamespaceOptionsForConnection(connectionId: string, connection: NamespaceOptionsConnection): Promise<string[]> {
|
||||
if (connection.db_type === "dameng") {
|
||||
if (namespaceOptionsAreSchemas(connection)) {
|
||||
const database = connection.database || "";
|
||||
// Dameng users and schemas are not interchangeable: independent schemas
|
||||
// appear in listSchemas but not in the user-backed listDatabases result.
|
||||
// Oracle, Dameng, and their inferred JDBC dialects expose schemas directly
|
||||
// below the connection; their catalog/database listing is not the namespace
|
||||
// picker used by schema-aware UI surfaces.
|
||||
const schemas = await api.listSchemas(connectionId, database, true);
|
||||
return filterSchemaNamesForConnection(schemas, connection, database);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user