fix: 优化 WHERE 字段补全排序

This commit is contained in:
zipg 2026-06-29 19:42:53 +08:00 committed by GitHub
parent 32f865ff52
commit e3ec2228d3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 91 additions and 2 deletions

View File

@ -1356,7 +1356,8 @@ async function provideSqlCompletions(currentState: import("@codemirror/state").E
scheduleCompletionMetadataRefresh(completionContext);
if (!explicit) return localResult;
}
if (!explicit) {
const shouldResolveAsyncColumnCompletion = completionContext.suggestColumns && completionContext.referencedTables.length > 0 && completionContext.prefix.length > 0;
if (!explicit && !shouldResolveAsyncColumnCompletion) {
scheduleCompletionMetadataRefresh(completionContext);
return null;
}

View File

@ -134,6 +134,17 @@ describe("sqlCompletion scoped context classification", () => {
expect(context.suggestColumns).toBe(true);
});
it("classifies unqualified WHERE field input as column context", () => {
const sql = "SELECT * FROM A1User WHERE userc";
const context = getSqlCompletionContext(sql, sql.length);
expect(context.contextKind).toBe("column");
expect(context.prefix).toBe("userc");
expect(context.referencedTables).toEqual(expect.arrayContaining([expect.objectContaining({ name: "A1User" })]));
expect(context.suggestColumns).toBe(true);
expect(context.suggestRoutines).toBe(false);
});
it("classifies CALL routine contexts", () => {
const sql = "CALL usp_";
const context = getSqlCompletionContext(sql, sql.length);

View File

@ -1174,7 +1174,8 @@ class SqlCompletionProvider {
return dedupeAndSort(buildMongoCompletionItemsFromContext({ mode: "root", prefix: context.prefix, from: 0 }).map(mongoCompletionItemToSqlCompletionItem));
}
if (!context.exclusiveTableSuggestions && !context.exclusiveColumnSuggestions && !context.exclusiveRoutineSuggestions) {
const preferReferencedColumns = hasMatchingReferencedColumnPrefix(context, this.input.columnsByTable);
if (!preferReferencedColumns && !context.exclusiveTableSuggestions && !context.exclusiveColumnSuggestions && !context.exclusiveRoutineSuggestions) {
const snippets = this.databaseType === "manticoresearch" ? [...(this.input.snippets ?? DEFAULT_SQL_SNIPPETS), ...MANTICORESEARCH_SQL_SNIPPETS] : (this.input.snippets ?? DEFAULT_SQL_SNIPPETS);
this.items.push(...buildSnippetItems(context.prefix, snippets, this.input.keywordCase));
this.items.push(...buildFunctionSnippetItems(context.prefix, getFunctionDescriptions(this.t), this.databaseType));
@ -2786,6 +2787,8 @@ function buildColumnItems(context: SqlCompletionContext, columnsByTable: Map<str
const qualifiedTarget = qualifiedTableTargetFromContext(context);
const relatedTables = context.referencedTables.filter((table) => referencedTableMatchesColumnQualifier(table, q, qLower, qualifiedTarget));
relevantCols = allColumns.filter((column) => relatedTables.some((table) => columnMatchesReferencedTable(column, table)) || (!!qualifiedTarget && columnMatchesQualifiedTable(column, qualifiedTarget)));
} else if (context.referencedTables.length > 0) {
relevantCols = allColumns.filter((column) => context.referencedTables.some((table) => columnMatchesReferencedTable(column, table)));
}
// Count name frequencies to detect duplicates across tables
@ -2833,6 +2836,21 @@ function buildColumnItems(context: SqlCompletionContext, columnsByTable: Map<str
.sort(compareCompletionItems);
}
function hasMatchingReferencedColumnPrefix(context: SqlCompletionContext, columnsByTable: Map<string, SqlCompletionColumn[]>): boolean {
if (!context.suggestColumns || !context.prefix || context.referencedTables.length === 0) return false;
for (const [key, cols] of columnsByTable.entries()) {
for (const column of cols) {
if (!matchesPrefix(column.name, context.prefix)) continue;
if (context.referencedTables.some((table) => columnMatchesReferencedTable({ ...column, key }, table))) {
return true;
}
}
}
return false;
}
function qualifiedTableTargetFromContext(context: SqlCompletionContext): { schema: string; table: string } | null {
const parts = context.qualifierParts ?? context.qualifier?.split(".").filter(Boolean) ?? [];
if (parts.length < 2) return null;

View File

@ -836,6 +836,65 @@ test("suggests stored procedures after CALL", () => {
);
});
test("prioritizes referenced table columns in WHERE field input", () => {
const sql = "select * from A1User WHERE userc";
const items = buildSqlCompletionItems(sql, sql.length, {
tables: [{ name: "A1User", schema: "dbo", type: "table" }],
objects: [
{ name: "P1UserCodeGenerate", schema: "dbo", type: "procedure" },
{ name: "F22UserAccUnit", schema: "dbo", type: "function" },
],
columnsByTable: new Map([
[
"dbo.A1User",
[
{ name: "UserCode", table: "A1User", schema: "dbo", dataType: "varchar" },
{ name: "UserName", table: "A1User", schema: "dbo", dataType: "varchar" },
],
],
[
"dbo.OtherUserTable",
[
{ name: "UserCheck", table: "OtherUserTable", schema: "dbo", dataType: "varchar" },
],
],
]),
databaseType: "sqlserver",
});
assert.deepEqual(
items.map((item) => [item.label, item.type]),
[["UserCode", "column"]],
);
});
test("keeps snippets below matching WHERE field columns", () => {
const sql = "select * from demo_2000_tables.t_0001 WHERE i";
const items = buildSqlCompletionItems(sql, sql.length, {
tables: [{ name: "t_0001", schema: "demo_2000_tables", type: "table" }],
columnsByTable: new Map([
[
"demo_2000_tables.t_0001",
[
{ name: "id", table: "t_0001", schema: "demo_2000_tables", dataType: "int", comment: "注释test" },
{ name: "image_url", table: "t_0001", schema: "demo_2000_tables", dataType: "varchar(512)", comment: "xixixi" },
{ name: "image_mime", table: "t_0001", schema: "demo_2000_tables", dataType: "varchar(64)", comment: "hahaha" },
],
],
]),
});
assert.deepEqual(
items.slice(0, 3).map((item) => [item.label, item.type]),
[
["id", "column"],
["image_url", "column"],
["image_mime", "column"],
],
);
assert.equal(items.some((item) => item.type === "snippet" && item.label === "insert into"), false);
});
test("suggests user functions and triggers with fuzzy matching", () => {
const sql = "select fun";
const items = buildSqlCompletionItems(sql, sql.length, {