1 parent
e977e886c3
commit
cf652f212e
2 files changed
+86
-2
No files matched your search
@@ -59,6 +59,16 @@ function oracleConnection(): ConnectionConfig {
|
||||
} as ConnectionConfig;
|
||||
}
|
||||
|
||||
function oceanBaseOracleConnection(): ConnectionConfig {
|
||||
return {
|
||||
...oracleConnection(),
|
||||
id: "oceanbase-oracle-1",
|
||||
name: "OceanBase Oracle",
|
||||
db_type: "oceanbase-oracle",
|
||||
database: "OBORCL",
|
||||
} as ConnectionConfig;
|
||||
}
|
||||
|
||||
function sapHanaConnection(): ConnectionConfig {
|
||||
return {
|
||||
...postgresConnection(),
|
||||
@@ -678,6 +688,80 @@ describe("connectionStore completion assistant", () => {
|
||||
expect(completionAssistantSearch).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it.each(["empty", "error"] as const)("normalizes unquoted OceanBase Oracle identifiers after an %s assistant result", async (assistantResult) => {
|
||||
const completionAssistantSearch = assistantResult === "empty" ? vi.fn().mockResolvedValue({ candidates: [], incomplete: false, fallback_used: false }) : vi.fn().mockRejectedValue(new Error("assistant unavailable"));
|
||||
const getColumns = vi.fn().mockResolvedValue([{ name: "ORDER_ID", data_type: "NUMBER", is_nullable: false, column_default: null, is_primary_key: true, extra: null, comment: null }]);
|
||||
|
||||
vi.doMock("@/lib/backend/tauriRuntime", () => ({ isTauriRuntime: () => false }));
|
||||
vi.doMock("@/lib/backend/api", () => ({
|
||||
checkConnectionHealth: vi.fn().mockResolvedValue(undefined),
|
||||
completionAssistantSearch,
|
||||
getColumns,
|
||||
}));
|
||||
|
||||
const { useConnectionStore } = await import("@/stores/connectionStore");
|
||||
const store = useConnectionStore();
|
||||
store.connections = [oceanBaseOracleConnection()];
|
||||
store.connectedIds.add("oceanbase-oracle-1");
|
||||
|
||||
const lower = await store.listCompletionColumns("oceanbase-oracle-1", "OBORCL", "orders_alias", "app", { tableQuoted: false, schemaQuoted: false });
|
||||
const upper = await store.listCompletionColumns("oceanbase-oracle-1", "OBORCL", "ORDERS_ALIAS", "APP", { tableQuoted: false, schemaQuoted: false });
|
||||
|
||||
expect(completionAssistantSearch).toHaveBeenCalledOnce();
|
||||
expect(completionAssistantSearch).toHaveBeenCalledWith(expect.objectContaining({ schema: "APP", parent_schema: "APP", parent_name: "ORDERS_ALIAS" }));
|
||||
expect(getColumns).toHaveBeenCalledOnce();
|
||||
expect(getColumns).toHaveBeenCalledWith("oceanbase-oracle-1", "OBORCL", "APP", "ORDERS_ALIAS", undefined, undefined);
|
||||
expect(lower).toEqual([expect.objectContaining({ name: "ORDER_ID", table: "ORDERS_ALIAS", schema: "APP" })]);
|
||||
expect(upper).toEqual(lower);
|
||||
});
|
||||
|
||||
it("keeps quoted OceanBase Oracle identifiers exact and isolated from unquoted cache entries", async () => {
|
||||
const completionAssistantSearch = vi.fn(async (request: { parent_name?: string | null; parent_schema?: string | null }) => ({
|
||||
candidates: [
|
||||
{
|
||||
name: request.parent_name === "ORDERS_ALIAS" ? "UPPER_ID" : "MixedId",
|
||||
kind: "column",
|
||||
schema: request.parent_schema,
|
||||
parent_schema: request.parent_schema,
|
||||
parent_name: request.parent_name,
|
||||
data_type: "NUMBER",
|
||||
},
|
||||
],
|
||||
incomplete: false,
|
||||
fallback_used: false,
|
||||
}));
|
||||
const getColumns = vi.fn();
|
||||
|
||||
vi.doMock("@/lib/backend/tauriRuntime", () => ({ isTauriRuntime: () => false }));
|
||||
vi.doMock("@/lib/backend/api", () => ({
|
||||
checkConnectionHealth: vi.fn().mockResolvedValue(undefined),
|
||||
completionAssistantSearch,
|
||||
getColumns,
|
||||
}));
|
||||
|
||||
const { useConnectionStore } = await import("@/stores/connectionStore");
|
||||
const store = useConnectionStore();
|
||||
store.connections = [oceanBaseOracleConnection()];
|
||||
store.connectedIds.add("oceanbase-oracle-1");
|
||||
|
||||
const unquoted = await store.listCompletionColumns("oceanbase-oracle-1", "OBORCL", "orders_alias", "app", { tableQuoted: false, schemaQuoted: false });
|
||||
const unquotedCached = await store.listCompletionColumns("oceanbase-oracle-1", "OBORCL", "ORDERS_ALIAS", "APP", { tableQuoted: false, schemaQuoted: false });
|
||||
const quoted = await store.listCompletionColumns("oceanbase-oracle-1", "OBORCL", "Orders_Alias", "AppSchema", { tableQuoted: true, schemaQuoted: true });
|
||||
const quotedCached = await store.listCompletionColumns("oceanbase-oracle-1", "OBORCL", "Orders_Alias", "AppSchema", { tableQuoted: true, schemaQuoted: true });
|
||||
|
||||
expect(completionAssistantSearch.mock.calls.map(([request]) => ({ schema: request.schema, parent_schema: request.parent_schema, parent_name: request.parent_name }))).toEqual([
|
||||
{ schema: "APP", parent_schema: "APP", parent_name: "ORDERS_ALIAS" },
|
||||
{ schema: "AppSchema", parent_schema: "AppSchema", parent_name: "Orders_Alias" },
|
||||
]);
|
||||
expect(getColumns).not.toHaveBeenCalled();
|
||||
expect(unquoted).toEqual([expect.objectContaining({ name: "UPPER_ID", table: "ORDERS_ALIAS", schema: "APP" })]);
|
||||
expect(unquotedCached).toEqual(unquoted);
|
||||
expect(quoted).toEqual([expect.objectContaining({ name: "MixedId", table: "Orders_Alias", schema: "AppSchema" })]);
|
||||
expect(quotedCached).toEqual(quoted);
|
||||
expect(store.lookupLocalCompletionColumns("oceanbase-oracle-1", "OBORCL", "orders_alias", "app", undefined, { tableQuoted: false, schemaQuoted: false })).toEqual(unquoted);
|
||||
expect(store.lookupLocalCompletionColumns("oceanbase-oracle-1", "OBORCL", "Orders_Alias", "AppSchema", undefined, { tableQuoted: true, schemaQuoted: true })).toEqual(quoted);
|
||||
});
|
||||
|
||||
it("normalizes unquoted SAP HANA table and schema names before column lookup", async () => {
|
||||
const completionAssistantSearch = vi.fn(async (request: { parent_name?: string | null; parent_schema?: string | null }) => ({
|
||||
candidates: [
|
||||
|
||||
@@ -6634,7 +6634,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
|
||||
function completionColumnsKey(connectionId: string, database: string, table: string, schema?: string, catalog?: string, context?: { tableQuoted?: boolean; schemaQuoted?: boolean }): string {
|
||||
const databaseType = getConfig(connectionId)?.db_type;
|
||||
if (databaseType === "oracle") {
|
||||
if (databaseType === "oracle" || databaseType === "oceanbase-oracle") {
|
||||
const normalizedTable = context?.tableQuoted === false ? table.toUpperCase() : table;
|
||||
const normalizedSchema = schema && context?.schemaQuoted === false ? schema.toUpperCase() : (schema ?? "");
|
||||
return `${connectionId}:${database}:${catalog ?? ""}:${normalizedSchema}:${normalizedTable}`;
|
||||
@@ -7736,7 +7736,7 @@ export const useConnectionStore = defineStore("connection", () => {
|
||||
|
||||
async function listCompletionColumns(connectionId: string, database: string, table: string, schema?: string, context?: { clientSessionId?: string; version?: number; tableQuoted?: boolean; schemaQuoted?: boolean }, catalog?: string): Promise<SqlCompletionColumn[]> {
|
||||
const config = getConfig(connectionId);
|
||||
const oracleIdentifier = config?.db_type === "oracle";
|
||||
const oracleIdentifier = config?.db_type === "oracle" || config?.db_type === "oceanbase-oracle";
|
||||
const uppercaseUnquotedIdentifier = oracleIdentifier || config?.db_type === "saphana";
|
||||
const completionTable = uppercaseUnquotedIdentifier && context?.tableQuoted === false ? table.toUpperCase() : table;
|
||||
const rawCompletionSchema = schema?.trim() || (config?.db_type === "dameng" ? config.username?.trim() || undefined : undefined);
|
||||
|
||||
Reference in new issue
Block a user