From c2a2c66b0bda1f37739a2894085dbd2614b2ea0a Mon Sep 17 00:00:00 2001 From: Shahmir Varqha Date: Thu, 15 Oct 2026 12:02:16 +0800 Subject: [PATCH] fix: preserve quoted paths in alias hovers --- src/sql/__tests__/hover-integration.test.ts | 33 +++++++++++++++++++++ src/sql/hover.ts | 14 +++++++-- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/sql/__tests__/hover-integration.test.ts b/src/sql/__tests__/hover-integration.test.ts index 6fc141b..46b791f 100644 --- a/src/sql/__tests__/hover-integration.test.ts +++ b/src/sql/__tests__/hover-integration.test.ts @@ -929,6 +929,39 @@ describe("Query-aware hover behavior - edge cases", () => { expect(html).toContain("users"); }); + it.each([ + [ + 'SELECT u.id FROM "my.table" u', + { + "my.table": [createCompletion("id", "quoted table column")], + my: { table: [createCompletion("id", "nested table column")] }, + }, + ], + [ + 'SELECT u.id FROM "my.db".users u', + { + "my.db": { users: [createCompletion("id", "quoted table column")] }, + my: { db: { users: [createCompletion("id", "nested table column")] } }, + }, + ], + ])("preserves quoted identifier boundaries in %s", async (doc, quotedSchema) => { + const source = createHoverSource({ schema: quotedSchema }); + const view = createMockView(doc); + const tooltip = await source(view, doc.indexOf("u.id") + 3, 1); + const html = renderTooltip(tooltip, view); + expect(html).toContain("quoted table column"); + expect(html).not.toContain("nested table column"); + }); + + it("does not resolve a quoted table alias through a different nested table", async () => { + const source = createHoverSource({ + schema: { "my.db": { users: ["id"] }, my: { db: { users: ["other_column"] } } }, + }); + const doc = 'SELECT u.other_column FROM "my.db".users u'; + const view = createMockView(doc); + expect(await source(view, doc.indexOf("u.other_column") + 3, 1)).toBeNull(); + }); + it("supports AS aliases", async () => { const source = createHoverSource({ schema }); const doc = "SELECT u.email FROM users AS u"; diff --git a/src/sql/hover.ts b/src/sql/hover.ts index 60fac15..e4d6e12 100644 --- a/src/sql/hover.ts +++ b/src/sql/hover.ts @@ -7,6 +7,7 @@ import { isObjectNamespace, type ResolvedNamespaceItem, resolveNamespaceItem, + traverseNamespacePath, } from "./namespace-utils.js"; import { NodeSqlParser } from "./parser.js"; import { QueryContextAnalyzer } from "./query-context.js"; @@ -264,15 +265,22 @@ function createHoverSource( ? [aliasTarget, ...restSegments].join(".").toLowerCase() : word; + const aliasTable = queryContext.tables.find( + (table) => table.alias?.toLowerCase() === firstSegment, + ); + const quotedAliasPath = aliasTable?.path.some((segment) => segment.includes(".")) + ? [...aliasTable.path, ...restSegments] + : null; + const tableRefs = new Set(queryContext.tables.map((table) => table.name.toLowerCase())); // Filter schema to only include tables referenced in the current statement const filteredSchema = filterSchemaByTableRefs(resolvedSchema, tableRefs); // Try to resolve in the filtered schema first (query-aware) - let namespaceResult = resolveNamespaceItem(filteredSchema, lookupWord, { - enableFuzzySearch, - }); + let namespaceResult = quotedAliasPath + ? traverseNamespacePath(filteredSchema, quotedAliasPath) + : resolveNamespaceItem(filteredSchema, lookupWord, { enableFuzzySearch }); // If no result in filtered schema and no tables were found in the // statement, fall back to the full schema to show any relevant information