fix(doris): complete external catalog suggestions
This commit is contained in:
11 files changed
+254
-81
No files matched your search
@@ -50,4 +50,11 @@ describe("openTabsPersistence originalSql round-trip", () => {
|
||||
expect(restored.sql).toBe("SELECT 1");
|
||||
expect(restored.originalSql).toBe("");
|
||||
});
|
||||
|
||||
it("preserves an external Doris catalog across tab restore", () => {
|
||||
const [restored] = roundTrip([queryTab({ database: "dbx_catalog_completion", catalog: "dbx_mysql_catalog" })]);
|
||||
|
||||
expect(restored.database).toBe("dbx_catalog_completion");
|
||||
expect(restored.catalog).toBe("dbx_mysql_catalog");
|
||||
});
|
||||
});
|
||||
@@ -77,4 +77,27 @@ describe("completionTreeIndex", () => {
|
||||
expect(completionTablesFromTree(tree, "conn", "hive", "sales_analytics")).toEqual(expected);
|
||||
expect(completionTablesFromTree(tree, "conn", "hive")).toEqual(expected);
|
||||
});
|
||||
|
||||
it("isolates Doris internal and external catalog tables with the same database name", () => {
|
||||
const tree: TreeNode[] = [
|
||||
{
|
||||
id: "internal-orders",
|
||||
label: "orders",
|
||||
type: "table",
|
||||
connectionId: "doris",
|
||||
database: "sales",
|
||||
},
|
||||
{
|
||||
id: "hive-orders",
|
||||
label: "orders",
|
||||
type: "table",
|
||||
connectionId: "doris",
|
||||
catalog: "hive_catalog",
|
||||
database: "sales",
|
||||
},
|
||||
];
|
||||
|
||||
expect(completionTablesFromTree(tree, "doris", "sales")).toEqual([{ name: "orders", schema: undefined, type: "table" }]);
|
||||
expect(completionTablesFromTree(tree, "doris", "sales", undefined, "hive_catalog")).toEqual([{ name: "orders", catalog: "hive_catalog", schema: undefined, type: "table" }]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,25 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const contentAreaSource = readFileSync(new URL("../../../components/layout/ContentArea.vue", import.meta.url), "utf8");
|
||||
const queryEditorSource = readFileSync(new URL("../../../components/editor/QueryEditor.vue", import.meta.url), "utf8");
|
||||
const sidebarRuntimeSource = readFileSync(new URL("../../../components/sidebar/SidebarTreeRuntimeHost.vue", import.meta.url), "utf8");
|
||||
const openTabsPersistenceSource = readFileSync(new URL("../../app/openTabsPersistence.ts", import.meta.url), "utf8");
|
||||
|
||||
describe("Doris external catalog completion wiring", () => {
|
||||
it("preserves the selected catalog from the sidebar query action into QueryEditor", () => {
|
||||
expect(sidebarRuntimeSource).toContain('queryStore.createTab(node.connectionId, node.database, undefined, "query", node.schema, undefined, node.catalog)');
|
||||
expect(contentAreaSource).toContain(':catalog="activeTab.catalog"');
|
||||
expect(queryEditorSource).toContain("catalog?: string;");
|
||||
expect(openTabsPersistenceSource).toContain("catalog: tab.catalog");
|
||||
});
|
||||
|
||||
it("includes catalog scope in table and column completion requests", () => {
|
||||
expect(queryEditorSource).toMatch(/lookupLocalCompletionTables\([\s\S]*?props\.catalog/);
|
||||
expect(queryEditorSource).toMatch(/listCompletionTables\([\s\S]*?props\.catalog/);
|
||||
expect(queryEditorSource).toMatch(/listCompletionColumns\([\s\S]*?catalog\)/);
|
||||
expect(queryEditorSource).toMatch(/watch\(\s*\(\) => props\.catalog,[\s\S]*?refreshCompletionCache\(\)/);
|
||||
expect(queryEditorSource).toMatch(/function shouldLoadCompletionObjects[\s\S]*?if \(props\.catalog\) return false/);
|
||||
expect(queryEditorSource).toMatch(/if \(!props\.catalog && props\.databaseType !== "oracle"[\s\S]*?listCompletionObjects/);
|
||||
});
|
||||
});
|
||||
@@ -20,6 +20,7 @@ export interface SavedOpenTab {
|
||||
customTitle?: boolean;
|
||||
connectionId: string;
|
||||
database: string;
|
||||
catalog?: string;
|
||||
schema?: string;
|
||||
sql: string;
|
||||
originalSql?: string;
|
||||
@@ -84,6 +85,7 @@ export function serializeOpenTabs(tabs: QueryTab[]): SavedOpenTab[] {
|
||||
...(tab.customTitle ? { customTitle: true } : {}),
|
||||
connectionId: tab.connectionId,
|
||||
database: tab.database,
|
||||
...(tab.catalog !== undefined ? { catalog: tab.catalog } : {}),
|
||||
schema: tab.schema,
|
||||
sql: shouldPersistTabSql(tab) ? tab.sql : "",
|
||||
// Only round-trip originalSql for plain query tabs (no savedSqlId / externalSqlPath):
|
||||
|
||||
@@ -16,14 +16,19 @@ export function completionSchemasFromTree(nodes: readonly TreeNode[], connection
|
||||
return schemas;
|
||||
}
|
||||
|
||||
export function completionTablesFromTree(nodes: readonly TreeNode[], connectionId: string, database: string, schema?: string): SqlCompletionTable[] {
|
||||
export function completionTablesFromTree(nodes: readonly TreeNode[], connectionId: string, database: string, schema?: string, catalog?: string): SqlCompletionTable[] {
|
||||
const preferredSchema = schema?.toLowerCase();
|
||||
const preferredCatalog = catalog?.toLowerCase();
|
||||
const tables: SqlCompletionTable[] = [];
|
||||
visitTree(nodes, (node) => {
|
||||
if (node.connectionId !== connectionId || node.database !== database || !TABLE_NODE_TYPES.has(node.type)) return;
|
||||
// External catalogs can contain databases and tables with the same names as
|
||||
// the internal catalog. Keep the two metadata scopes isolated.
|
||||
if ((node.catalog?.toLowerCase() ?? undefined) !== preferredCatalog) return;
|
||||
if (preferredSchema && node.schema?.toLowerCase() !== preferredSchema) return;
|
||||
tables.push({
|
||||
name: node.tableName || node.label,
|
||||
catalog: node.catalog,
|
||||
schema: node.schema,
|
||||
type: node.type === "materialized_view" ? "materialized_view" : node.type === "view" ? "view" : "table",
|
||||
});
|
||||
@@ -43,7 +48,7 @@ function dedupeCompletionTables(tables: SqlCompletionTable[]): SqlCompletionTabl
|
||||
const seen = new Set<string>();
|
||||
const result: SqlCompletionTable[] = [];
|
||||
for (const table of tables) {
|
||||
const key = `${table.schema ?? ""}.${table.name}`.toLowerCase();
|
||||
const key = `${table.catalog ?? ""}.${table.schema ?? ""}.${table.name}`.toLowerCase();
|
||||
if (seen.has(key)) continue;
|
||||
seen.add(key);
|
||||
result.push(table);
|
||||
|
||||
@@ -1255,6 +1255,7 @@ function sqlAliasKeywordWords(...sources: Array<string | undefined>): string[] {
|
||||
|
||||
export interface SqlCompletionTable {
|
||||
name: string;
|
||||
catalog?: string;
|
||||
database?: string;
|
||||
schema?: string;
|
||||
type?: SqlObjectNavigationType;
|
||||
|
||||
Reference in new issue
Block a user