feat: add CROSS APPLY / OUTER APPLY support for SQL Server completion (#168)

This commit is contained in:
t8y2 2026-05-09 12:14:42 +08:00
parent b899d98977
commit 94927faa4c
2 changed files with 13 additions and 7 deletions

View File

@ -81,6 +81,12 @@ const SQL_KEYWORDS = [
"LAST_VALUE",
"NTILE",
"CROSS",
"APPLY",
"CROSS APPLY",
"OUTER APPLY",
"OPENJSON",
"OPENXML",
"OPENROWSET",
"FULL",
"NATURAL",
"USING",
@ -116,7 +122,7 @@ const SQL_KEYWORDS = [
"PRAGMA",
];
const TABLE_TRIGGER_KEYWORDS = new Set(["from", "join", "update", "into", "table", "describe", "explain"]);
const TABLE_TRIGGER_KEYWORDS = new Set(["from", "join", "update", "into", "table", "describe", "explain", "apply"]);
const JOIN_MODIFIERS = new Set(["left", "right", "inner", "outer", "cross", "full", "natural"]);
export interface SqlCompletionTable {
@ -336,6 +342,7 @@ function extractReferencedTables(sql: string): SqlCompletionReferencedTable[] {
"inner",
"outer",
"cross",
"apply",
"full",
"natural",
"on",
@ -413,7 +420,7 @@ function extractReferencedTables(sql: string): SqlCompletionReferencedTable[] {
]);
const pattern =
/\b(?:from|join|update|into)\s+((?:"[^"]+"|`[^`]+`|[A-Za-z_][\w$]*)(?:\.(?:"[^"]+"|`[^`]+`|[A-Za-z_][\w$]*))?)(?:\s+(?:as\s+)?([A-Za-z_][\w$]*))?/gi;
/\b(?:from|join|update|into|apply)\s+((?:"[^"]+"|`[^`]+`|[A-Za-z_][\w$]*)(?:\.(?:"[^"]+"|`[^`]+`|[A-Za-z_][\w$]*))?)(?:\s+(?:as\s+)?([A-Za-z_][\w$]*))?/gi;
const referenced: SqlCompletionReferencedTable[] = [];
for (const match of sql.matchAll(pattern)) {
const rawName = match[1];

View File

@ -56,11 +56,11 @@ test("suggests columns for an explicit alias qualifier", () => {
columnsByTable,
});
const columnItems = items.filter((item) => item.type === "column");
assert.deepEqual(
items.map((item) => item.label),
columnItems.map((item) => item.label),
["id", "name", "email"],
);
assert.ok(items.every((item) => item.type === "column"));
});
test("suggests columns from referenced tables in select list", () => {
@ -96,15 +96,14 @@ test("suggests tables after comma in FROM clause", () => {
assert.ok(items.some((item) => item.label === "orders" && item.type === "table"));
});
test("suggests tables as fallback when typing an identifier", () => {
test("suggests keywords when typing without context", () => {
const sql = "us";
const items = buildSqlCompletionItems(sql, sql.length, {
tables,
columnsByTable,
});
assert.ok(items.some((item) => item.label === "users" && item.type === "table"));
assert.ok(items.some((item) => item.type === "keyword"));
assert.ok(items.some((item) => item.type === "keyword" && item.label === "USING"));
});
test("always includes keywords alongside table suggestions", () => {