From 7942c59aa7fb0a5d48599bfdfa5045e602f74307 Mon Sep 17 00:00:00 2001 From: zipg Date: Tue, 15 Sep 2026 19:44:32 +0800 Subject: [PATCH] feat(duckdb): improve SQL completion keywords --- .../sql/sqlCompletion.context.spec.ts | 27 +++++++++++++ apps/desktop/src/lib/sql/sqlCompletion.ts | 40 ++++++++++++++++++- 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/apps/desktop/src/lib/__tests__/sql/sqlCompletion.context.spec.ts b/apps/desktop/src/lib/__tests__/sql/sqlCompletion.context.spec.ts index 7ea3f52c67..d4e90b2ffd 100644 --- a/apps/desktop/src/lib/__tests__/sql/sqlCompletion.context.spec.ts +++ b/apps/desktop/src/lib/__tests__/sql/sqlCompletion.context.spec.ts @@ -24,6 +24,33 @@ describe("sqlCompletion keyword snippets", () => { expect(shouldAutoOpenSqlCompletion(sql, sql.length)).toBe(true); expect(items).toEqual(expect.arrayContaining([expect.objectContaining({ label: "select *", type: "snippet" }), expect.objectContaining({ label: "SELECT", type: "keyword" })])); }); + + it("offers DuckDB-specific query and statement keywords", () => { + const expectedKeywords = ["QUALIFY", "SUMMARIZE", "PIVOT", "UNPIVOT", "ASOF", "POSITIONAL", "FROM"]; + + for (const keyword of expectedKeywords) { + const prefix = keyword.slice(0, 4); + const sql = keyword === "FROM" || keyword === "SUMMARIZE" ? prefix : `SELECT * FROM items ${prefix}`; + const items = buildSqlCompletionItems(sql, sql.length, { + tables: [], + columnsByTable: new Map(), + databaseType: "duckdb", + }); + + expect(items, keyword).toEqual(expect.arrayContaining([expect.objectContaining({ label: keyword, type: "keyword" })])); + } + }); + + it("does not expose DuckDB-only keywords to other databases", () => { + const sql = "SELECT * FROM items qua"; + const items = buildSqlCompletionItems(sql, sql.length, { + tables: [], + columnsByTable: new Map(), + databaseType: "postgres", + }); + + expect(items.some((item) => item.label === "QUALIFY")).toBe(false); + }); }); describe("SQL Server datepart completion", () => { diff --git a/apps/desktop/src/lib/sql/sqlCompletion.ts b/apps/desktop/src/lib/sql/sqlCompletion.ts index d2436c0582..dfc314de14 100644 --- a/apps/desktop/src/lib/sql/sqlCompletion.ts +++ b/apps/desktop/src/lib/sql/sqlCompletion.ts @@ -456,6 +456,44 @@ const MANTICORESEARCH_SQL_KEYWORDS = ["FACET", "MATCH", "SHOW", "SHOW META", "SH const SQLITE_SQL_KEYWORDS = ["AUTOINCREMENT", "INTEGER", "BLOB", "BOOLEAN", "WITHOUT ROWID", "VACUUM", "PRAGMA", "JSON_EXTRACT", "JSON_SET", "STRFTIME"]; +// DuckDB extends the common SQL grammar with analytical clauses, relation +// reshaping statements, and a few shorthand forms. Keep these scoped to +// DuckDB so database-specific completion does not leak into other dialects. +const DUCKDB_SQL_KEYWORDS = [ + "ASOF", + "ANTI", + "COLUMNS", + "COPY", + "DESCRIBE", + "EXCLUDE", + "EXPORT", + "FILTER", + "GROUP BY ALL", + "IMPORT", + "LATERAL", + "LIST", + "MAP", + "PIVOT", + "PIVOT_LONGER", + "PIVOT_WIDER", + "POSITIONAL", + "QUALIFY", + "READ_CSV", + "READ_JSON", + "READ_PARQUET", + "REPLACE", + "SAMPLE", + "SEMI", + "SHOW", + "STRUCT", + "SUMMARIZE", + "TABLESAMPLE", + "UNION BY NAME", + "UNPIVOT", + "USING SAMPLE", + "WINDOW", +]; + const SQLSERVER_SQL_KEYWORDS = [ "TOP", "IDENTITY", @@ -585,7 +623,7 @@ const DATABASE_SQL_KEYWORDS: Partial> = { oracle: ORACLE_SQL_KEYWORDS, "oceanbase-oracle": ORACLE_SQL_KEYWORDS, manticoresearch: MANTICORESEARCH_SQL_KEYWORDS, - duckdb: ["COMMENT"], + duckdb: ["COMMENT", ...DUCKDB_SQL_KEYWORDS], clickhouse: ["COMMENT"], doris: ["COMMENT", "MATERIALIZED", "MATERIALIZED VIEW", "DISTRIBUTED BY HASH", "DUPLICATE KEY", "AGGREGATE KEY", "UNIQUE KEY", "PRIMARY KEY", "PROPERTIES", "PARTITION BY", "BUCKETS", "LATERAL VIEW", "EXPLODE", "ARRAY", "MAP", "STRUCT", "BITMAP", "HLL"], starrocks: ["COMMENT", "MATERIALIZED", "MATERIALIZED VIEW", "DISTRIBUTED BY HASH", "DUPLICATE KEY", "PROPERTIES", "PARTITION BY", "BUCKETS", "LATERAL VIEW"],