fix(presto): optimize metadata completion

This commit is contained in:
luoianun authored and GitHub committed 2026-07-01 19:05:45 +08:00
1 parent f5f6bc0b43
commit 95e3cb6571
9 files changed
+528 -37

No files matched your search

@@ -156,6 +156,8 @@ const completionTranslations = computed(() => ({
functionDescriptions: Object.fromEntries(SQL_FUNCTION_NAMES.map((name) => [name, t(`editor.completion.functionDescriptions.${name}`)])) as Record<string, string>,
}));
const MAX_COMPLETION_TABLES = 200;
const PRESTO_ON_DEMAND_TABLE_COMPLETION_MIN_PREFIX = 2;
const PRESTO_ON_DEMAND_TABLE_COMPLETION_LIMIT = 20;
const MAX_JOIN_FK_PREFETCH_TABLES = 24;
const MAX_SEMANTIC_DIAGNOSTIC_COLUMN_TABLES = 4;
const liveFontSize = ref(settingsStore.editorSettings.fontSize);
@@ -717,6 +719,12 @@ function usesOnDemandOnlyCompletionColumns(): boolean {
return usesOnDemandOnlyEditorColumnMetadata(props.databaseType);
}
function allowsOnDemandQualifiedTableCompletion(prefix: string): boolean {
if (!usesLocalOnlyCompletionMetadata()) return false;
if (props.databaseType !== "prestosql" && props.databaseType !== "trino") return false;
return prefix.trim().length >= PRESTO_ON_DEMAND_TABLE_COMPLETION_MIN_PREFIX;
}
function completionMetadataTarget(table: { name: string; schema?: string | null }): { database: string; schema?: string } | null {
if (props.database == null) return null;
if (supportsDatabaseQualifierCompletion() && table.schema) {
@@ -1814,9 +1822,12 @@ async function performAsyncCompletionWithResult(epoch: number, completionContext
// If qualifier didn't match any table names, try it as a schema name
let qualifierIsSchema = false;
if (completionContext.qualifier && !qualifierDatabase && !isReferencedTableQualifier(completionContext) && tables.length === 0 && (completionContext.suggestTables || completionContext.exclusiveColumnSuggestions)) {
const schemaTables = localOnlyMetadata
? connectionStore.lookupLocalCompletionTables(props.connectionId!, props.database!, completionContext.prefix, MAX_COMPLETION_TABLES, completionContext.qualifier)
: await listCompletionTablesWithLatencyBudget(props.connectionId!, props.database!, completionContext.prefix, MAX_COMPLETION_TABLES, completionContext.qualifier);
let schemaTables = connectionStore.lookupLocalCompletionTables(props.connectionId!, props.database!, completionContext.prefix, MAX_COMPLETION_TABLES, completionContext.qualifier);
if (!localOnlyMetadata) {
schemaTables = await listCompletionTablesWithLatencyBudget(props.connectionId!, props.database!, completionContext.prefix, MAX_COMPLETION_TABLES, completionContext.qualifier);
} else if (schemaTables.length === 0 && allowsOnDemandQualifiedTableCompletion(completionContext.prefix)) {
schemaTables = await listCompletionTablesWithLatencyBudget(props.connectionId!, props.database!, completionContext.prefix, PRESTO_ON_DEMAND_TABLE_COMPLETION_LIMIT, completionContext.qualifier);
}
if (schemaTables.length > 0) {
tables = schemaTables;
qualifierIsSchema = true;
@@ -1868,7 +1879,8 @@ async function performAsyncCompletionWithResult(epoch: number, completionContext
}
}
const shouldFetchColumnsForCompletion = !onDemandOnlyColumns || completionContext.suggestColumns || completionContext.exclusiveColumnSuggestions || !!completionContext.insertTable;
const isTableNameCompletionContext = completionContext.suggestTables || completionContext.exclusiveTableSuggestions;
const shouldFetchColumnsForCompletion = !onDemandOnlyColumns || ((completionContext.suggestColumns || completionContext.exclusiveColumnSuggestions) && !isTableNameCompletionContext) || !!completionContext.insertTable;
if (shouldFetchColumnsForCompletion) {
await Promise.all(
refs.map(async (refTable) => {
@@ -35,7 +35,7 @@ describe("prestoSqlBuiltinDriver", () => {
expect(row.version).toBe("350");
expect(row.installed).toBe(false);
expect(row.installed_version).toBeNull();
expect(row.jre).toBe("");
expect(row.jre).toBe("21");
});
it("marks PrestoSQL installed when its Maven coordinate is present", () => {
@@ -180,6 +180,17 @@ describe("sqlCompletion scoped context classification", () => {
expect(context.referencedTables).toEqual(expect.arrayContaining([expect.objectContaining({ schema: "dbo", name: "Users", alias: "u" }), expect.objectContaining({ name: "Orders", alias: "o" })]));
});
it("treats schema-qualified table prefixes in FROM as table completion input", () => {
const sql = "SELECT * FROM dws_game_sdk_base.di";
const context = getSqlCompletionContext(sql, sql.length);
expect(context.qualifier).toBe("dws_game_sdk_base");
expect(context.prefix).toBe("di");
expect(context.suggestTables).toBe(true);
expect(context.exclusiveTableSuggestions).toBe(true);
expect(context.suggestColumns).toBe(true);
});
it("exposes CTEs as table-like referenced tables", () => {
const sql = "WITH recent_orders(id, total) AS (SELECT id, total FROM orders) SELECT * FROM recent_orders ro WHERE ro.";
const context = getSqlCompletionContext(sql, sql.length);
@@ -24,8 +24,8 @@ export function prestoSqlBuiltinDriverRow(bundles: JdbcMavenBundleInfo[]): Agent
installed: Boolean(installedBundle),
installed_version: installedBundle ? PRESTOSQL_JDBC_DRIVER_VERSION : null,
update_available: false,
requires_java_runtime: false,
jre: "",
requires_java_runtime: true,
jre: "21",
jre_installed: true,
};
}
@@ -83,4 +83,33 @@ describe("connectionStore completion assistant", () => {
expect(listTables).toHaveBeenCalledWith("pg-1", "app", "public", "acc", 20);
expect(tables).toEqual([{ name: "accounts", schema: "public", type: "table" }]);
});
it("keeps schema-qualified local table completion scoped to the selected schema", async () => {
const completionAssistantSearch = vi.fn().mockRejectedValue(new Error("assistant unavailable"));
const listTables = vi.fn(async (_connectionId: string, _database: string, schema: string, filter: string) => {
if (schema === "dim_game_base" && filter === "dim") {
return [{ name: "dim_game", table_type: "BASE TABLE", comment: null }];
}
return [];
});
vi.doMock("@/lib/tauriRuntime", () => ({ isTauriRuntime: () => false }));
vi.doMock("@/lib/api", () => ({
checkConnectionHealth: vi.fn().mockResolvedValue(undefined),
completionAssistantSearch,
listSchemas: vi.fn().mockResolvedValue(["dim_game_base", "dws_game_sdk_base"]),
listTables,
}));
const { useConnectionStore } = await import("@/stores/connectionStore");
const store = useConnectionStore();
store.connections = [postgresConnection()];
store.connectedIds.add("pg-1");
const dimTables = await store.listCompletionTables("pg-1", "app", "dim", 20, "dim_game_base");
const dwsTables = store.lookupLocalCompletionTables("pg-1", "app", "d", 20, "dws_game_sdk_base");
expect(dimTables).toEqual([{ name: "dim_game", schema: "dim_game_base", type: "table" }]);
expect(dwsTables).toEqual([]);
});
});
+2 -2
View File
@@ -2966,7 +2966,7 @@ export const useConnectionStore = defineStore("connection", () => {
function lookupLocalCompletionTables(connectionId: string, database: string, filter = "", limit?: number, schema?: string): SqlCompletionTable[] {
const allScopes = [...completionTableIndex.entries()].filter(([key]) => key.startsWith(`${connectionId}:${database}:`)).map(([, entry]) => entry);
const preferred = schema ? completionTableIndex.get(completionScopeKey(connectionId, database, schema)) : undefined;
const scopes = preferred ? [preferred, ...allScopes.filter((entry) => entry !== preferred)] : allScopes;
const scopes = schema ? (preferred ? [preferred] : []) : allScopes;
const treeTables = completionTablesFromTree(treeNodes.value, connectionId, database, schema);
const ranked = scopes
.flatMap((entry) => entry?.tables ?? [])
@@ -2980,7 +2980,7 @@ export const useConnectionStore = defineStore("connection", () => {
function lookupLocalCompletionObjects(connectionId: string, database: string, filter = "", limit?: number, schema?: string): SqlCompletionObject[] {
const allScopes = [...completionObjectIndex.entries()].filter(([key]) => key.startsWith(`${connectionId}:${database}:`)).map(([, entry]) => entry);
const preferred = schema ? completionObjectIndex.get(completionScopeKey(connectionId, database, schema)) : undefined;
const scopes = preferred ? [preferred, ...allScopes.filter((entry) => entry !== preferred)] : allScopes;
const scopes = schema ? (preferred ? [preferred] : []) : allScopes;
const ranked = scopes
.flatMap((entry) => entry?.objects ?? [])
.map((object) => ({ object, score: objectMatchScore(object, filter, schema) }))