fix(jdbc): show schemas for generic JDBC connections
This commit is contained in:
5 files changed
+144
-5
No files matched your search
@@ -50,6 +50,21 @@ function oracleConnection(): ConnectionConfig {
|
||||
} as ConnectionConfig;
|
||||
}
|
||||
|
||||
function genericJdbcConnection(): ConnectionConfig {
|
||||
return {
|
||||
id: "jdbc-1",
|
||||
name: "Generic JDBC",
|
||||
db_type: "jdbc",
|
||||
host: "127.0.0.1",
|
||||
port: 0,
|
||||
username: "app",
|
||||
password: "",
|
||||
database: "testdb",
|
||||
driver_profile: "jdbc",
|
||||
connection_string: "jdbc:example://127.0.0.1/testdb",
|
||||
} as ConnectionConfig;
|
||||
}
|
||||
|
||||
function procedure(name: string): ObjectInfo {
|
||||
return {
|
||||
name,
|
||||
@@ -160,6 +175,77 @@ describe("connectionStore metadata loading", () => {
|
||||
expect(node.isExpanded).toBe(true);
|
||||
});
|
||||
|
||||
it("discovers schema nodes for unknown generic JDBC databases", async () => {
|
||||
const listSchemaInfos = vi.fn().mockResolvedValue([
|
||||
{ name: "app", comment: null },
|
||||
{ name: "reporting", comment: null },
|
||||
]);
|
||||
const listTables = vi.fn().mockResolvedValue([]);
|
||||
|
||||
vi.doMock("@/lib/backend/tauriRuntime", () => ({ isTauriRuntime: () => false }));
|
||||
vi.doMock("@/lib/backend/api", () => ({
|
||||
checkConnectionHealth: vi.fn().mockResolvedValue(undefined),
|
||||
deleteSchemaCachePrefix: vi.fn().mockResolvedValue(undefined),
|
||||
listSchemaInfos,
|
||||
listTables,
|
||||
loadSchemaCache: vi.fn().mockResolvedValue(null),
|
||||
saveSchemaCache: vi.fn().mockResolvedValue(undefined),
|
||||
saveConnections: vi.fn().mockResolvedValue(undefined),
|
||||
saveSidebarLayout: vi.fn().mockResolvedValue(undefined),
|
||||
}));
|
||||
|
||||
const { useConnectionStore } = await import("@/stores/connectionStore");
|
||||
const store = useConnectionStore();
|
||||
const connection = genericJdbcConnection();
|
||||
const databaseNode: TreeNode = { id: "jdbc-1:testdb", label: "testdb", type: "database", connectionId: connection.id, database: "testdb", isExpanded: false, children: [] };
|
||||
store.connections = [connection];
|
||||
store.connectedIds.add(connection.id);
|
||||
store.treeNodes = [{ id: connection.id, label: connection.name, type: "connection", connectionId: connection.id, isExpanded: true, children: [databaseNode] }];
|
||||
|
||||
await store.loadTreeNodeChildren(databaseNode, { force: true });
|
||||
|
||||
expect(listSchemaInfos).toHaveBeenCalledWith(connection.id, "testdb");
|
||||
expect(listTables).not.toHaveBeenCalled();
|
||||
expect(databaseNode.children?.map((node) => [node.type, node.label, node.schema])).toEqual([
|
||||
["schema", "app", "app"],
|
||||
["schema", "reporting", "reporting"],
|
||||
]);
|
||||
});
|
||||
|
||||
it("keeps the flat object tree for unknown generic JDBC databases without schemas", async () => {
|
||||
const listSchemaInfos = vi.fn().mockResolvedValue([]);
|
||||
const listTables = vi.fn().mockResolvedValue([{ name: "t", table_type: "TABLE", comment: null }]);
|
||||
|
||||
vi.doMock("@/lib/backend/tauriRuntime", () => ({ isTauriRuntime: () => false }));
|
||||
vi.doMock("@/lib/backend/api", () => ({
|
||||
checkConnectionHealth: vi.fn().mockResolvedValue(undefined),
|
||||
deleteSchemaCachePrefix: vi.fn().mockResolvedValue(undefined),
|
||||
listObjects: vi.fn().mockResolvedValue([]),
|
||||
listSchemaInfos,
|
||||
listTables,
|
||||
loadSchemaCache: vi.fn().mockResolvedValue(null),
|
||||
saveSchemaCache: vi.fn().mockResolvedValue(undefined),
|
||||
saveConnections: vi.fn().mockResolvedValue(undefined),
|
||||
saveSidebarLayout: vi.fn().mockResolvedValue(undefined),
|
||||
}));
|
||||
|
||||
const { useConnectionStore } = await import("@/stores/connectionStore");
|
||||
const { useSettingsStore } = await import("@/stores/settingsStore");
|
||||
const store = useConnectionStore();
|
||||
useSettingsStore().editorSettings.sidebarObjectDisplay = "simple";
|
||||
const connection = genericJdbcConnection();
|
||||
const databaseNode: TreeNode = { id: "jdbc-1:testdb", label: "testdb", type: "database", connectionId: connection.id, database: "testdb", isExpanded: false, children: [] };
|
||||
store.connections = [connection];
|
||||
store.connectedIds.add(connection.id);
|
||||
store.treeNodes = [{ id: connection.id, label: connection.name, type: "connection", connectionId: connection.id, isExpanded: true, children: [databaseNode] }];
|
||||
|
||||
await store.loadTreeNodeChildren(databaseNode, { force: true });
|
||||
|
||||
expect(listSchemaInfos).toHaveBeenCalledWith(connection.id, "testdb");
|
||||
expect(listTables).toHaveBeenCalled();
|
||||
expect(databaseNode.children?.map((node) => [node.type, node.label, node.schema])).toEqual([["table", "t", undefined]]);
|
||||
});
|
||||
|
||||
it("renders simple-mode table children without waiting for supplemental objects", async () => {
|
||||
const tables: TableInfo[] = [{ name: "users", table_type: "BASE TABLE", comment: null }];
|
||||
const listTables = vi.fn().mockResolvedValue(tables);
|
||||
|
||||
@@ -47,7 +47,7 @@ import * as api from "@/lib/backend/api";
|
||||
import { isTauriRuntime } from "@/lib/backend/tauriRuntime";
|
||||
import { useTunnelProfileStore } from "@/stores/tunnelProfileStore";
|
||||
import { connectionIsDorisFamilyCatalogCapable, isInternalDorisCatalog, isSchemaAware, normalizeSidebarObjectKind, sidebarObjectKindsForDatabase, usesTreeSchemaMode } from "@/lib/database/databaseCapabilities";
|
||||
import { connectionObjectTreeNodeSchema, connectionObjectTreeQuerySchema, connectionUsesDatabaseObjectTreeMode, effectiveDatabaseTypeForConnection } from "@/lib/database/jdbcDialect";
|
||||
import { connectionObjectTreeNodeSchema, connectionObjectTreeQuerySchema, connectionShouldDiscoverJdbcSchemas, connectionUsesDatabaseObjectTreeMode, effectiveDatabaseTypeForConnection } from "@/lib/database/jdbcDialect";
|
||||
import { buildDatabaseTreeNodes, buildDuckDbConnectionTreeNodes, compareSidebarNames, sortSidebarDatabases, sortSidebarNames, shouldIncludeDefaultDatabaseNode } from "@/lib/database/databaseTree";
|
||||
import { buildSqlServerDatabaseTreeNodes } from "@/lib/database/sqlServerTree";
|
||||
import { collapseExpandedTreeNodes } from "@/lib/sidebar/sidebarTreeCollapse";
|
||||
@@ -3148,6 +3148,13 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
children: [],
|
||||
};
|
||||
});
|
||||
if (schemas.length === 0 && connectionShouldDiscoverJdbcSchemas(getConfig(connectionId))) {
|
||||
// Generic JDBC drivers vary widely: prefer schema navigation when the
|
||||
// driver reports schemas, but keep the legacy flat object tree when it
|
||||
// reports none so non-schema engines do not expand into an empty node.
|
||||
await loadTables(connectionId, database, undefined, options);
|
||||
return;
|
||||
}
|
||||
if (isPostgresLikeForExtensions(getConfig(connectionId)?.db_type)) {
|
||||
children.push(buildExtensionManagementNode(connectionId, database));
|
||||
}
|
||||
@@ -4395,7 +4402,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
const effectiveDbType = effectiveDatabaseTypeForConnection(config);
|
||||
if (config?.db_type === "sqlserver") {
|
||||
await loadSqlServerDatabaseObjects(node.connectionId, node.database, options);
|
||||
} else if (usesTreeSchemaMode(effectiveDbType) && !connectionUsesDatabaseObjectTreeMode(config)) {
|
||||
} else if ((usesTreeSchemaMode(effectiveDbType) && !connectionUsesDatabaseObjectTreeMode(config)) || connectionShouldDiscoverJdbcSchemas(config)) {
|
||||
await loadSchemas(node.connectionId, node.database, options);
|
||||
} else {
|
||||
await loadTables(node.connectionId, node.database, undefined, options);
|
||||
|
||||
Reference in new issue
Block a user