fix(sql): preserve database quoting in star expansion

Closes #6247
This commit is contained in:
t8y2 committed 2026-08-14 14:14:28 +00:00
1 parent b9e63dc57e
commit c5eea21821
2 files changed
+33 -6

No files matched your search

@@ -417,6 +417,33 @@ WHERE a.id = b.fk_kpi_set_score_id`,
expect(star?.apply).toBe("id, u.name"); expect(star?.apply).toBe("id, u.name");
}); });
it.each([
["Oracle", "oracle", "mysql", '"ID", o."created at", o."SELECT", o.safe_name'],
["MySQL", "mysql", "mysql", "`ID`, o.`created at`, o.`SELECT`, o.safe_name"],
["PostgreSQL", "postgres", "postgres", '"ID", o."created at", o."SELECT", o.safe_name'],
["SQL Server", "sqlserver", "sqlserver", "[ID], o.[created at], o.[SELECT], o.safe_name"],
["dialect fallback", undefined, "mysql", "`ID`, o.`created at`, o.`SELECT`, o.safe_name"],
] as const)("uses %s identifier quoting in qualified star completion items", (_label, databaseType, dialect, expected) => {
const columnsByTable = new Map<string, SqlCompletionColumn[]>([["orders", ["ID", "created at", "SELECT", "safe_name"].map((name) => ({ name, table: "orders" }))]]);
const starItems = semanticCompletion("SELECT o.*| FROM orders o", { columnsByTable }, { databaseType, dialect }).items;
const selectAllItems = semanticCompletion("SELECT o.| FROM orders o", { columnsByTable }, { databaseType, dialect }).items;
expect(starItems.find((item) => item.label === "* \u2192 columns")?.apply).toBe(expected);
expect(selectAllItems.find((item) => item.label === "o.*")?.apply).toBe(expected);
});
it("uses Oracle quoting for an unqualified multi-table star completion item", () => {
const columnsByTable = new Map<string, SqlCompletionColumn[]>([
["ORDERS", ["ID", "created at"].map((name) => ({ name, table: "ORDERS" }))],
["AUDIT", ["ID", "SELECT"].map((name) => ({ name, table: "AUDIT" }))],
]);
const { items } = semanticCompletion("SELECT *| FROM ORDERS o JOIN AUDIT a ON a.ID = o.ID", { columnsByTable }, { databaseType: "oracle", dialect: "mysql" });
expect(items.find((item) => item.label === "* \u2192 columns")?.apply).toBe('o."ID", o."created at", a."ID", a."SELECT"');
});
it("generates collision-free table aliases from semantic row sources", () => { it("generates collision-free table aliases from semantic row sources", () => {
const { items } = semanticCompletion("SELECT * FROM order_items oi JOIN ord|", { const { items } = semanticCompletion("SELECT * FROM order_items oi JOIN ord|", {
tables: [{ name: "order_items", type: "table" }], tables: [{ name: "order_items", type: "table" }],
+6 -6
View File
@@ -1433,7 +1433,7 @@ class SqlCompletionProvider {
if (!context.exclusiveTableSuggestions && context.suggestColumns) { if (!context.exclusiveTableSuggestions && context.suggestColumns) {
this.items.push(...buildColumnItems(context, this.input.columnsByTable, this.dialect)); this.items.push(...buildColumnItems(context, this.input.columnsByTable, this.dialect));
this.items.push(...buildSelectAllColumnItems(context, this.input.columnsByTable, this.t, this.dialect)); this.items.push(...buildSelectAllColumnItems(context, this.input.columnsByTable, this.t, this.dialect, this.databaseType));
this.items.push(...buildInsertAllColumnItems(context, this.input.columnsByTable, this.t, this.dialect, this.input.keywordCase)); this.items.push(...buildInsertAllColumnItems(context, this.input.columnsByTable, this.t, this.dialect, this.input.keywordCase));
} }
@@ -1466,7 +1466,7 @@ class SqlCompletionProvider {
} }
if (context.onStar) { if (context.onStar) {
const starItem = buildStarExpansionItem(context, this.input.columnsByTable, this.t, this.dialect); const starItem = buildStarExpansionItem(context, this.input.columnsByTable, this.t, this.dialect, this.databaseType);
if (starItem) this.items.push(starItem); if (starItem) this.items.push(starItem);
} }
@@ -3518,8 +3518,8 @@ export function buildSelectStarExpansion(context: SqlCompletionContext, columnsB
.join(", "); .join(", ");
} }
function buildStarExpansionItem(context: SqlCompletionContext, columnsByTable: Map<string, SqlCompletionColumn[]>, t?: SqlCompletionTranslations, dialect?: "mysql" | "postgres" | "sqlserver"): SqlCompletionItem | null { function buildStarExpansionItem(context: SqlCompletionContext, columnsByTable: Map<string, SqlCompletionColumn[]>, t?: SqlCompletionTranslations, dialect?: "mysql" | "postgres" | "sqlserver", databaseType?: DatabaseType): SqlCompletionItem | null {
const expansion = buildSelectStarExpansion(context, columnsByTable, dialect); const expansion = buildSelectStarExpansion(context, columnsByTable, dialect, context.qualifier, databaseType);
if (!expansion) return null; if (!expansion) return null;
const columnCount = selectStarExpansionColumns(context, columnsByTable).length; const columnCount = selectStarExpansionColumns(context, columnsByTable).length;
return { return {
@@ -3531,7 +3531,7 @@ function buildStarExpansionItem(context: SqlCompletionContext, columnsByTable: M
}; };
} }
function buildSelectAllColumnItems(context: SqlCompletionContext, columnsByTable: Map<string, SqlCompletionColumn[]>, t?: SqlCompletionTranslations, dialect?: "mysql" | "postgres" | "sqlserver"): SqlCompletionItem[] { function buildSelectAllColumnItems(context: SqlCompletionContext, columnsByTable: Map<string, SqlCompletionColumn[]>, t?: SqlCompletionTranslations, dialect?: "mysql" | "postgres" | "sqlserver", databaseType?: DatabaseType): SqlCompletionItem[] {
if (!context.selectListColumnContext || context.statementKind !== "select" || context.onStar || context.referencedTables.length === 0) { if (!context.selectListColumnContext || context.statementKind !== "select" || context.onStar || context.referencedTables.length === 0) {
return []; return [];
} }
@@ -3554,7 +3554,7 @@ function buildSelectAllColumnItems(context: SqlCompletionContext, columnsByTable
if (!selectAllColumnItemMatchesPrefix(label, ref, columns, context.prefix)) continue; if (!selectAllColumnItemMatchesPrefix(label, ref, columns, context.prefix)) continue;
const qualifier = context.qualifier || ref.alias || (shouldQualify ? quoteCompletionApplyIdentifier(ref.name, dialect) : undefined); const qualifier = context.qualifier || ref.alias || (shouldQualify ? quoteCompletionApplyIdentifier(ref.name, dialect) : undefined);
const expansion = buildSelectAllColumnExpansion(columns, qualifier, !!context.qualifier, dialect); const expansion = buildSelectAllColumnExpansion(columns, qualifier, !!context.qualifier, dialect, databaseType);
const countText = (t?.starExpansionColumns ?? "{count} columns").replace("{count}", String(columns.length)); const countText = (t?.starExpansionColumns ?? "{count} columns").replace("{count}", String(columns.length));
items.push({ items.push({
label, label,