fix(informix): resolve metadata owners correctly
This commit is contained in:
10 files changed
+530
-79
No files matched your search
@@ -3,6 +3,7 @@ import type { ConnectionConfig } from "@/types/database";
|
||||
import {
|
||||
GAUSSDB_M_JDBC_DRIVER_CLASS,
|
||||
connectionObjectTreeNodeSchema,
|
||||
connectionObjectTreeQuerySchema,
|
||||
connectionQueryExecutionSchema,
|
||||
connectionShouldLoadIdentifierQuote,
|
||||
connectionUsesDatabaseObjectTreeMode,
|
||||
@@ -223,4 +224,22 @@ describe("object tree node schema", () => {
|
||||
it("uses the SQLite database alias to qualify attached tables", () => {
|
||||
expect(connectionObjectTreeNodeSchema({ db_type: "sqlite" }, "analytics")).toBe("analytics");
|
||||
});
|
||||
|
||||
it("keeps unqualified Informix metadata on the login owner", () => {
|
||||
expect(connectionObjectTreeQuerySchema({ db_type: "informix" }, "prulife")).toBe("");
|
||||
expect(connectionObjectTreeNodeSchema({ db_type: "informix" }, "prulife")).toBeUndefined();
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ db_type: "jdbc" as const, connection_string: "jdbc:informix-sqli://localhost:9088/prulife" },
|
||||
{ db_type: "gbase" as const, driver_profile: "gbase8s" },
|
||||
])("keeps compatible Informix metadata on the login owner", (connection) => {
|
||||
expect(connectionObjectTreeQuerySchema(connection, "prulife")).toBe("");
|
||||
expect(connectionObjectTreeNodeSchema(connection, "prulife")).toBeUndefined();
|
||||
});
|
||||
|
||||
it("preserves explicit Informix owners", () => {
|
||||
expect(connectionObjectTreeQuerySchema({ db_type: "informix" }, "prulife", "xtdpcky")).toBe("xtdpcky");
|
||||
expect(connectionObjectTreeNodeSchema({ db_type: "informix" }, "prulife", "xtdpcky")).toBe("xtdpcky");
|
||||
});
|
||||
});
|
||||
@@ -162,6 +162,7 @@ export function connectionQueryExecutionSchema(connection: JdbcDialectConnection
|
||||
export function connectionObjectTreeQuerySchema(connection: JdbcDialectConnection | undefined, database: string, schema?: string): string {
|
||||
if (connection?.db_type === "jdbc" && inferJdbcDialect(connection) === "databend") return schema || database;
|
||||
if (connectionUsesDatabaseObjectTreeMode(connection)) return "";
|
||||
if (effectiveDatabaseTypeForConnection(connection) === "informix") return schema || "";
|
||||
return schema || database;
|
||||
}
|
||||
|
||||
@@ -176,6 +177,7 @@ export function connectionObjectTreeNodeSchema(connection: JdbcDialectConnection
|
||||
if (connectionUsesDatabaseObjectTreeMode(connection)) return undefined;
|
||||
if (schema) return schema;
|
||||
const type = effectiveDatabaseTypeForConnection(connection);
|
||||
if (type === "informix") return undefined;
|
||||
if (type === "sqlite") return database;
|
||||
return isSchemaAware(type) ? database : undefined;
|
||||
}
|
||||
|
||||
@@ -46,6 +46,20 @@ describe("TreeNodeLoadRegistry", () => {
|
||||
).toBe(db);
|
||||
});
|
||||
|
||||
it("does not let a user-cancelled load reclaim ownership", () => {
|
||||
const registry = new TreeNodeLoadRegistry();
|
||||
const node: TreeNodeLike = { id: "c1:db:schema", connectionId: "c1", isLoading: false, children: [] };
|
||||
const load = registry.begin(node);
|
||||
|
||||
registry.cancelPrefix("c1:db");
|
||||
node.isLoading = false;
|
||||
const reclaimed = load.reclaim(node);
|
||||
|
||||
expect(reclaimed).toBe(load);
|
||||
expect(reclaimed.isCurrent()).toBe(false);
|
||||
expect(node.isLoading).toBe(false);
|
||||
});
|
||||
|
||||
it("invalidates pruned descendant generations even when root no longer contains them", () => {
|
||||
const registry = new TreeNodeLoadRegistry();
|
||||
const db: TreeNodeLike = { id: "c1:db", connectionId: "c1", isLoading: false, children: [] };
|
||||
|
||||
@@ -28,12 +28,13 @@ export type TreeNodeLoadEpoch = {
|
||||
|
||||
export class TreeNodeLoadRegistry {
|
||||
private readonly generations = new Map<string, number>();
|
||||
private readonly cancellationGenerations = new Map<string, number>();
|
||||
|
||||
begin(node: TreeNodeLike): TreeNodeLoadHandle {
|
||||
const generation = (this.generations.get(node.id) ?? 0) + 1;
|
||||
this.generations.set(node.id, generation);
|
||||
node.isLoading = true;
|
||||
return this.createHandle(node.id, generation);
|
||||
return this.createHandle(node.id, generation, this.cancellationGenerations.get(node.id) ?? 0);
|
||||
}
|
||||
|
||||
/** Snapshot the current generation without claiming ownership (no spinner). */
|
||||
@@ -61,6 +62,22 @@ export class TreeNodeLoadRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* User cancellation is stronger than ordinary invalidation: a load waiting for
|
||||
* connection recovery must not reclaim ownership after its node was collapsed.
|
||||
*/
|
||||
cancelPrefix(nodeId: string): void {
|
||||
const prefix = `${nodeId}:`;
|
||||
const affectedIds = new Set<string>([nodeId]);
|
||||
for (const id of this.generations.keys()) {
|
||||
if (id.startsWith(prefix)) affectedIds.add(id);
|
||||
}
|
||||
for (const id of affectedIds) {
|
||||
this.cancellationGenerations.set(id, (this.cancellationGenerations.get(id) ?? 0) + 1);
|
||||
}
|
||||
this.invalidatePrefix(nodeId);
|
||||
}
|
||||
|
||||
/** Bump recorded generations for descendants only (not the parent load itself). */
|
||||
invalidateDescendants(parentId: string): void {
|
||||
const prefix = `${parentId}:`;
|
||||
@@ -90,7 +107,7 @@ export class TreeNodeLoadRegistry {
|
||||
return this.generations.get(nodeId) === generation;
|
||||
}
|
||||
|
||||
private createHandle(nodeId: string, generation: number): TreeNodeLoadHandle {
|
||||
private createHandle(nodeId: string, generation: number, cancellationGeneration: number): TreeNodeLoadHandle {
|
||||
const registry = this;
|
||||
return {
|
||||
nodeId,
|
||||
@@ -99,6 +116,9 @@ export class TreeNodeLoadRegistry {
|
||||
return registry.isCurrent(nodeId, generation);
|
||||
},
|
||||
reclaim(node: TreeNodeLike) {
|
||||
if (!registry.isCurrent(nodeId, generation) && (registry.cancellationGenerations.get(nodeId) ?? 0) !== cancellationGeneration) {
|
||||
return this;
|
||||
}
|
||||
if (node.id !== nodeId) {
|
||||
return registry.begin(node);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user