feat(postgres): complete sequence names in regclass literals
Closes #4005
This commit is contained in:
1 parent
a67bcb09db
commit
91e7463ee5
11 files changed
+580
-24
No files matched your search
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { buildSelectStarExpansion, buildSqlCompletionItems, getSqlCompletionContext, selectStarResultColumnsMatch, shouldAutoOpenSqlCompletion } from "@/lib/sql/sqlCompletion";
|
||||
import { buildPostgresSequenceLiteralCompletionItems, buildSelectStarExpansion, buildSqlCompletionItems, getPostgresSequenceLiteralCompletionContext, getSqlCompletionContext, selectStarResultColumnsMatch, shouldAutoOpenSqlCompletion } from "@/lib/sql/sqlCompletion";
|
||||
import { sqlCompletionContextFromSemantic } from "@/lib/sql/semantic/completion";
|
||||
import { buildSqlSemanticModel } from "@/lib/sql/semantic/model";
|
||||
import { originForSqlCompletionProvider, originForTypedSqlCompletionStart, shouldAllowSqlCompletionTrigger, type SqlCompletionTriggerFacts } from "@/lib/sql/sqlCompletionTriggerPolicy";
|
||||
@@ -17,6 +17,96 @@ describe("sqlCompletion keyword snippets", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("PostgreSQL sequence literal completion", () => {
|
||||
it.each(["nextval", "currval", "setval"])("recognizes the first %s regclass literal", (functionName) => {
|
||||
const sql = `SELECT ${functionName}('public.order_`;
|
||||
const context = getPostgresSequenceLiteralCompletionContext(sql, sql.length, "postgres");
|
||||
|
||||
expect(context).toEqual(
|
||||
expect.objectContaining({
|
||||
prefix: "order_",
|
||||
schema: "public",
|
||||
schemaQuoted: false,
|
||||
nameQuoted: false,
|
||||
}),
|
||||
);
|
||||
expect(context?.from).toBe(sql.lastIndexOf("order_"));
|
||||
expect(shouldAutoOpenSqlCompletion(sql, sql.length, { databaseType: "postgres" })).toBe(true);
|
||||
});
|
||||
|
||||
it("preserves quoted mixed-case schema and sequence identifiers", () => {
|
||||
const sql = `SELECT pg_catalog.nextval('"App"."Order`;
|
||||
const context = getPostgresSequenceLiteralCompletionContext(sql, sql.length, "postgres");
|
||||
|
||||
expect(context).toEqual(
|
||||
expect.objectContaining({
|
||||
prefix: "Order",
|
||||
schema: "App",
|
||||
schemaQuoted: true,
|
||||
nameQuoted: true,
|
||||
nameQuoteClosed: false,
|
||||
}),
|
||||
);
|
||||
expect(
|
||||
buildPostgresSequenceLiteralCompletionItems(context!, [
|
||||
{ name: "OrderSequence", schema: "App", type: "sequence" },
|
||||
{ name: "order_sequence", schema: "App", type: "sequence" },
|
||||
]),
|
||||
).toEqual([
|
||||
expect.objectContaining({
|
||||
label: "OrderSequence",
|
||||
apply: 'OrderSequence"',
|
||||
replaceClosingQuote: '"',
|
||||
detail: "sequence in App",
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
it("keeps doubled apostrophes inside the sequence identifier and escapes insertion", () => {
|
||||
const sql = `SELECT nextval('"customer''s_`;
|
||||
const context = getPostgresSequenceLiteralCompletionContext(sql, sql.length, "postgres");
|
||||
|
||||
expect(context).toEqual(expect.objectContaining({ prefix: "customer's_", nameQuoted: true }));
|
||||
expect(buildPostgresSequenceLiteralCompletionItems(context!, [{ name: "customer's_seq", schema: "public", type: "sequence" }])).toEqual([expect.objectContaining({ label: "customer's_seq", apply: `customer''s_seq"` })]);
|
||||
});
|
||||
|
||||
it("quotes an accepted mixed-case identifier in an unquoted literal", () => {
|
||||
const sql = "SELECT nextval('mix";
|
||||
const context = getPostgresSequenceLiteralCompletionContext(sql, sql.length, "postgres");
|
||||
|
||||
expect(buildPostgresSequenceLiteralCompletionItems(context!, [{ name: "MixedSequence", schema: "public", type: "sequence" }])).toEqual([expect.objectContaining({ label: "MixedSequence", filterText: "MixedSequence", apply: '"MixedSequence"' })]);
|
||||
});
|
||||
|
||||
it("does not expose sequence metadata through ordinary SQL object completion", () => {
|
||||
const sql = "SELECT order_";
|
||||
const items = buildSqlCompletionItems(sql, sql.length, {
|
||||
tables: [],
|
||||
objects: [{ name: "order_seq", schema: "public", type: "sequence" }],
|
||||
columnsByTable: new Map(),
|
||||
databaseType: "postgres",
|
||||
dialect: "postgres",
|
||||
});
|
||||
|
||||
expect(items.some((item) => item.label === "order_seq")).toBe(false);
|
||||
});
|
||||
|
||||
it.each([
|
||||
["SELECT 'order_", "postgres"],
|
||||
["SELECT nextval('order_", "mysql"],
|
||||
["SELECT nextval('order_') || 'suffix", "postgres"],
|
||||
["SELECT setval(42, 'order_", "postgres"],
|
||||
["SELECT app.nextval('order_", "postgres"],
|
||||
["SELECT app. nextval('order_", "postgres"],
|
||||
["SELECT app.pg_catalog.nextval('order_", "postgres"],
|
||||
[`SELECT "PG_CATALOG".nextval('order_`, "postgres"],
|
||||
["SELECT $$ nextval('order_", "postgres"],
|
||||
["SELECT $body$ nextval('order_", "postgres"],
|
||||
] as const)("does not enable sequence completion for unrelated literals: %s (%s)", (sql, databaseType) => {
|
||||
expect(getPostgresSequenceLiteralCompletionContext(sql, sql.length, databaseType)).toBeNull();
|
||||
expect(shouldAutoOpenSqlCompletion(sql, sql.length, { databaseType })).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("SELECT star expansion", () => {
|
||||
it("reuses completion column ordering for an unqualified star", () => {
|
||||
const sql = "SELECT * FROM apis";
|
||||
|
||||
Reference in new issue
Block a user