mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-21 04:50:12 +00:00
fix(sql): quote MySQL completion identifiers
This commit is contained in:
@@ -9,7 +9,7 @@ import { sqlSemanticDialectFor } from "@/lib/sql/semantic/dialect";
|
||||
import { findActiveSqlStatementSpan, tokenizeSqlSemantic } from "@/lib/sql/semantic/tokens";
|
||||
import type { SqlSemanticBuildOptions, SqlSemanticSpan } from "@/lib/sql/semantic/types";
|
||||
import { DEFAULT_SQL_SNIPPETS, MANTICORESEARCH_SQL_SNIPPETS, resolveSqlSnippetBodyForDatabase } from "@/lib/sql/sqlSnippetTemplates";
|
||||
import { requiresPostgresIdentifierQuote } from "@/lib/sql/sqlIdentifier";
|
||||
import { requiresMysqlIdentifierQuote, requiresPostgresIdentifierQuote } from "@/lib/sql/sqlIdentifier";
|
||||
import { identifierMatchScore, matchesIdentifierSearch } from "@/lib/sql/identifierSearch";
|
||||
import { containsHan, orderedSubsequenceSpan, pinyinFirstLetters } from "@/lib/common/pinyin";
|
||||
import { quoteTableIdentifier } from "@/lib/table/tableSelectSql";
|
||||
@@ -3091,6 +3091,25 @@ export function quoteSqlIdentifier(identifier: string, dialect?: "mysql" | "post
|
||||
|
||||
const POSTGRES_IDENTIFIER_KEYWORDS = new Set(SQL_KEYWORDS.map((keyword) => keyword.toLowerCase()));
|
||||
|
||||
// Unlike quoteSqlIdentifier (also used by the data grid condition editor, which
|
||||
// intentionally leaves MySQL identifiers unquoted and applies backticks itself
|
||||
// at insertion time), SQL editor completion apply/insertText needs the quoting
|
||||
// baked in up front, so MySQL reserved-word identifiers get backtick-quoted here.
|
||||
function quoteCompletionApplyIdentifier(identifier: string, dialect?: "mysql" | "postgres" | "sqlserver"): string {
|
||||
if (dialect === "mysql") {
|
||||
if (!requiresMysqlIdentifierQuote(identifier, POSTGRES_IDENTIFIER_KEYWORDS)) return identifier;
|
||||
return `\`${identifier.replaceAll("`", "``")}\``;
|
||||
}
|
||||
return quoteSqlIdentifier(identifier, dialect);
|
||||
}
|
||||
|
||||
function quoteCompletionApplyName(applyName: string, dialect?: "mysql" | "postgres" | "sqlserver"): string {
|
||||
if (dialect !== "mysql") return applyName;
|
||||
const parts = splitQualifiedNameRawParts(applyName);
|
||||
if (parts.length === 0) return applyName;
|
||||
return parts.map((part) => (isQuotedIdentifier(part) ? part : quoteCompletionApplyIdentifier(part, dialect))).join(".");
|
||||
}
|
||||
|
||||
function quoteSelectStarColumnIdentifier(identifier: string, dialect?: "mysql" | "postgres" | "sqlserver", databaseType?: DatabaseType): string {
|
||||
if (!requiresPostgresIdentifierQuote(identifier, POSTGRES_IDENTIFIER_KEYWORDS)) return identifier;
|
||||
if (databaseType) return quoteTableIdentifier(databaseType, identifier);
|
||||
@@ -3138,7 +3157,7 @@ function resolveTableSchemaQualification(
|
||||
// Keep Oracle's current-schema behavior, but qualify the generic/PostgreSQL/SQL Server paths.
|
||||
const ambiguousTableName = databaseType !== "oracle" && (schemasByTableName.get(normalizeIdentifierPart(table.name))?.size ?? 0) > 1;
|
||||
const schemaQualification = !!table.schema && (oracleSchemaQualification || ambiguousTableName);
|
||||
const defaultApplyName = schemaQualification ? `${quoteSqlIdentifier(table.schema!, dialect)}.${quoteSqlIdentifier(table.name, dialect)}` : quoteSqlIdentifier(table.name, dialect);
|
||||
const defaultApplyName = schemaQualification ? `${quoteCompletionApplyIdentifier(table.schema!, dialect)}.${quoteCompletionApplyIdentifier(table.name, dialect)}` : quoteCompletionApplyIdentifier(table.name, dialect);
|
||||
return { ambiguousTableName, schemaQualification, defaultApplyName };
|
||||
}
|
||||
|
||||
@@ -3164,7 +3183,7 @@ function buildTableItems(
|
||||
const { ambiguousTableName, defaultApplyName } = resolveTableSchemaQualification(table, dialect, databaseType, currentSchema, schemasByTableName);
|
||||
const suppliedApplyName = table.applyName?.trim();
|
||||
const suppliedApplyNameIsQualified = suppliedApplyName?.includes(".") === true;
|
||||
const applyName = qualifiedByContext ? quoteSqlIdentifier(table.name, dialect) : ambiguousTableName && !!table.schema && (!suppliedApplyName || !suppliedApplyNameIsQualified) ? defaultApplyName : (suppliedApplyName ?? defaultApplyName);
|
||||
const applyName = qualifiedByContext ? quoteCompletionApplyIdentifier(table.name, dialect) : ambiguousTableName && !!table.schema && (!suppliedApplyName || !suppliedApplyNameIsQualified) ? defaultApplyName : (suppliedApplyName ?? defaultApplyName);
|
||||
const alias = autoAliasTables ? generateTableCompletionAlias(table.name, existingAliases) : "";
|
||||
return {
|
||||
label: table.name,
|
||||
@@ -3260,7 +3279,7 @@ function buildSchemaItems(prefix: string, schemas: string[], dialect?: "mysql" |
|
||||
label: schema,
|
||||
type: "schema" as const,
|
||||
detail: "schema",
|
||||
apply: `${quoteSqlIdentifier(schema, dialect)}.`,
|
||||
apply: `${quoteCompletionApplyIdentifier(schema, dialect)}.`,
|
||||
boost: computeBoost(schema, prefix) + 700,
|
||||
}));
|
||||
}
|
||||
@@ -3275,10 +3294,11 @@ function buildObjectItems(context: SqlCompletionContext, objects: SqlCompletionO
|
||||
.map((object) => {
|
||||
const qualifiedByContext = objectIsQualifiedByContext(object, context);
|
||||
const objectInCurrentSchema = !!currentSchema && !!object.schema && normalizeIdentifierPart(object.schema) === normalizeIdentifierPart(currentSchema);
|
||||
const suppliedApplyName = object.applyName ? quoteCompletionApplyName(object.applyName, dialect) : undefined;
|
||||
const applyName =
|
||||
qualifiedByContext || (context.qualifier && object.schema?.toLowerCase() === context.qualifier.toLowerCase())
|
||||
? quoteSqlIdentifier(object.name, dialect)
|
||||
: (object.applyName ?? (object.schema && !objectInCurrentSchema ? `${quoteSqlIdentifier(object.schema, dialect)}.${quoteSqlIdentifier(object.name, dialect)}` : quoteSqlIdentifier(object.name, dialect)));
|
||||
? quoteCompletionApplyIdentifier(object.name, dialect)
|
||||
: (suppliedApplyName ?? (object.schema && !objectInCurrentSchema ? `${quoteCompletionApplyIdentifier(object.schema, dialect)}.${quoteCompletionApplyIdentifier(object.name, dialect)}` : quoteCompletionApplyIdentifier(object.name, dialect)));
|
||||
const locationDetail = object.type === "trigger" && object.parentName ? `trigger on ${object.parentName}` : object.parentName ? `${object.type} in ${object.parentName}` : object.schema ? `${object.type} in ${object.schema}` : object.type;
|
||||
const signature = object.signature?.trim();
|
||||
const detail = [locationDetail, signature ? `(${signature})` : undefined, object.dataType ? `[${object.dataType}]` : undefined].filter(Boolean).join(" ");
|
||||
@@ -3492,7 +3512,7 @@ export function buildSelectStarExpansion(context: SqlCompletionContext, columnsB
|
||||
|
||||
return references
|
||||
.flatMap((reference) => {
|
||||
const qualifier = reference.aliasSql ?? (reference.alias ? quoteSqlIdentifier(reference.alias, dialect) : quoteSqlIdentifier(reference.name, dialect));
|
||||
const qualifier = reference.aliasSql ?? (reference.alias ? quoteCompletionApplyIdentifier(reference.alias, dialect) : quoteCompletionApplyIdentifier(reference.name, dialect));
|
||||
return uniqueColumnsByName(columnsForSelectAllReferencedTable(reference, columnsByTable)).map((column) => `${qualifier}.${quoteSelectStarColumnIdentifier(column.name, dialect, databaseType)}`);
|
||||
})
|
||||
.join(", ");
|
||||
@@ -3533,7 +3553,7 @@ function buildSelectAllColumnItems(context: SqlCompletionContext, columnsByTable
|
||||
const label = `${displayRef}.*`;
|
||||
if (!selectAllColumnItemMatchesPrefix(label, ref, columns, context.prefix)) continue;
|
||||
|
||||
const qualifier = context.qualifier || ref.alias || (shouldQualify ? quoteSqlIdentifier(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 countText = (t?.starExpansionColumns ?? "{count} columns").replace("{count}", String(columns.length));
|
||||
items.push({
|
||||
@@ -3556,7 +3576,7 @@ function buildInsertAllColumnItems(context: SqlCompletionContext, columnsByTable
|
||||
const label = `${context.insertTable}.*`;
|
||||
if (!selectAllColumnItemMatchesPrefix(label, { name: context.insertTable, schema: context.insertSchema }, columns, context.prefix)) return [];
|
||||
|
||||
const columnList = columns.map((column) => quoteSqlIdentifier(column.name, dialect)).join(", ");
|
||||
const columnList = columns.map((column) => quoteCompletionApplyIdentifier(column.name, dialect)).join(", ");
|
||||
const valuesKeyword = applySqlKeywordCase("VALUES", keywordCase);
|
||||
const valueList = columns.map((_, index) => `\${${index + 1}:value}`).join(", ");
|
||||
const expansion = `${columnList}) ${valuesKeyword} (${valueList})`;
|
||||
@@ -4042,10 +4062,10 @@ function normalizeCompletionKey(key: string): string {
|
||||
|
||||
function buildColumnApply(column: SqlCompletionColumn & { displayLabel: string }, context: SqlCompletionContext, dialect?: "mysql" | "postgres" | "sqlserver"): string {
|
||||
if (context.qualifier || column.displayLabel === column.name || !column.displayLabel.includes(".")) {
|
||||
return quoteSqlIdentifier(column.name, dialect);
|
||||
return quoteCompletionApplyIdentifier(column.name, dialect);
|
||||
}
|
||||
const qualifier = column.sourceQualifierSql ?? quoteSqlIdentifier(column.sourceAlias ?? column.table, dialect);
|
||||
return `${qualifier}.${quoteSqlIdentifier(column.name, dialect)}`;
|
||||
const qualifier = column.sourceQualifierSql ?? quoteCompletionApplyIdentifier(column.sourceAlias ?? column.table, dialect);
|
||||
return `${qualifier}.${quoteCompletionApplyIdentifier(column.name, dialect)}`;
|
||||
}
|
||||
|
||||
function isKeyColumn(name: string): boolean {
|
||||
@@ -4146,11 +4166,11 @@ function buildDirectionalForeignKeyJoinConditionItems(owner: SqlCompletionRefere
|
||||
function buildJoinConditionPart(owner: SqlCompletionReferencedTable, ownerColumn: string, referenced: SqlCompletionReferencedTable, referencedColumn: string, dialect?: "mysql" | "postgres" | "sqlserver"): { label: string; apply: string } {
|
||||
const ownerRef = owner.alias || owner.name;
|
||||
const referencedRef = referenced.alias || referenced.name;
|
||||
const ownerApplyRef = owner.alias ? owner.alias : quoteSqlIdentifier(owner.name, dialect);
|
||||
const referencedApplyRef = referenced.alias ? referenced.alias : quoteSqlIdentifier(referenced.name, dialect);
|
||||
const ownerApplyRef = owner.alias ? owner.alias : quoteCompletionApplyIdentifier(owner.name, dialect);
|
||||
const referencedApplyRef = referenced.alias ? referenced.alias : quoteCompletionApplyIdentifier(referenced.name, dialect);
|
||||
return {
|
||||
label: `${ownerRef}.${ownerColumn} = ${referencedRef}.${referencedColumn}`,
|
||||
apply: `${ownerApplyRef}.${quoteSqlIdentifier(ownerColumn, dialect)} = ${referencedApplyRef}.${quoteSqlIdentifier(referencedColumn, dialect)}`,
|
||||
apply: `${ownerApplyRef}.${quoteCompletionApplyIdentifier(ownerColumn, dialect)} = ${referencedApplyRef}.${quoteCompletionApplyIdentifier(referencedColumn, dialect)}`,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4188,8 +4208,8 @@ function buildJoinConditionItemsForPair(left: SqlCompletionReferencedTable, left
|
||||
const items: SqlCompletionItem[] = [];
|
||||
const leftRef = left.alias || left.name;
|
||||
const rightRef = right.alias || right.name;
|
||||
const leftApplyRef = left.alias ? left.alias : quoteSqlIdentifier(left.name, dialect);
|
||||
const rightApplyRef = right.alias ? right.alias : quoteSqlIdentifier(right.name, dialect);
|
||||
const leftApplyRef = left.alias ? left.alias : quoteCompletionApplyIdentifier(left.name, dialect);
|
||||
const rightApplyRef = right.alias ? right.alias : quoteCompletionApplyIdentifier(right.name, dialect);
|
||||
const leftTableKey = singularTableName(left.name);
|
||||
const rightTableKey = singularTableName(right.name);
|
||||
|
||||
@@ -4204,7 +4224,7 @@ function buildJoinConditionItemsForPair(left: SqlCompletionReferencedTable, left
|
||||
emittedPairs.add(key);
|
||||
const label = `${leftRef}.${leftColumn.name} = ${rightRef}.${rightColumn.name}`;
|
||||
if (prefix && !matchesPrefix(label, prefix)) return;
|
||||
const apply = `${leftApplyRef}.${quoteSqlIdentifier(leftColumn.name, dialect)} = ${rightApplyRef}.${quoteSqlIdentifier(rightColumn.name, dialect)}`;
|
||||
const apply = `${leftApplyRef}.${quoteCompletionApplyIdentifier(leftColumn.name, dialect)} = ${rightApplyRef}.${quoteCompletionApplyIdentifier(rightColumn.name, dialect)}`;
|
||||
items.push({
|
||||
label,
|
||||
type: "snippet",
|
||||
@@ -4308,8 +4328,8 @@ function buildCompositeHeuristicJoinConditionItems(
|
||||
|
||||
const leftRef = left.alias || left.name;
|
||||
const rightRef = right.alias || right.name;
|
||||
const leftApplyRef = left.alias ? left.alias : quoteSqlIdentifier(left.name, dialect);
|
||||
const rightApplyRef = right.alias ? right.alias : quoteSqlIdentifier(right.name, dialect);
|
||||
const leftApplyRef = left.alias ? left.alias : quoteCompletionApplyIdentifier(left.name, dialect);
|
||||
const rightApplyRef = right.alias ? right.alias : quoteCompletionApplyIdentifier(right.name, dialect);
|
||||
const items: SqlCompletionItem[] = [];
|
||||
|
||||
for (const candidate of candidates.slice(0, 2)) {
|
||||
@@ -4337,7 +4357,7 @@ function buildCompositeHeuristicJoinConditionItems(
|
||||
function buildHeuristicJoinConditionPart(leftRef: string, leftApplyRef: string, leftColumn: SqlCompletionColumn, rightRef: string, rightApplyRef: string, rightColumn: SqlCompletionColumn, dialect?: "mysql" | "postgres" | "sqlserver"): { label: string; apply: string } {
|
||||
return {
|
||||
label: `${leftRef}.${leftColumn.name} = ${rightRef}.${rightColumn.name}`,
|
||||
apply: `${leftApplyRef}.${quoteSqlIdentifier(leftColumn.name, dialect)} = ${rightApplyRef}.${quoteSqlIdentifier(rightColumn.name, dialect)}`,
|
||||
apply: `${leftApplyRef}.${quoteCompletionApplyIdentifier(leftColumn.name, dialect)} = ${rightApplyRef}.${quoteCompletionApplyIdentifier(rightColumn.name, dialect)}`,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -4568,7 +4588,7 @@ function buildNonAggregatedColumnItems(context: SqlCompletionContext, columnsByT
|
||||
label: col.name,
|
||||
type: "column" as const,
|
||||
detail: "non-aggregated column — required in GROUP BY",
|
||||
apply: quoteSqlIdentifier(col.name, dialect),
|
||||
apply: quoteCompletionApplyIdentifier(col.name, dialect),
|
||||
boost: 2800 - items.length,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -190,11 +190,15 @@ export function requiresPostgresIdentifierQuote(identifier: string, additionalKe
|
||||
return POSTGRES_RESERVED_IDENTIFIER_KEYWORDS.has(identifier) || additionalKeywords?.has(identifier) === true;
|
||||
}
|
||||
|
||||
export function requiresMysqlIdentifierQuote(identifier: string, additionalKeywords?: ReadonlySet<string>): boolean {
|
||||
return requiresPostgresIdentifierQuote(identifier, additionalKeywords) || MYSQL_ONLY_RESERVED_IDENTIFIER_KEYWORDS.has(identifier);
|
||||
}
|
||||
|
||||
export function quoteGaussDbJdbcIdentifier(identifier: string, identifierQuote: string): string {
|
||||
if (isExplicitlyQuotedSqlIdentifier(identifier)) return identifier;
|
||||
const quote = identifierQuote.trim();
|
||||
if (!quote) return identifier;
|
||||
const requiresQuote = !SIMPLE_LOWER_IDENTIFIER.test(identifier) || POSTGRES_RESERVED_IDENTIFIER_KEYWORDS.has(identifier) || (quote === "`" && MYSQL_ONLY_RESERVED_IDENTIFIER_KEYWORDS.has(identifier));
|
||||
const requiresQuote = quote === "`" ? requiresMysqlIdentifierQuote(identifier) : requiresPostgresIdentifierQuote(identifier);
|
||||
if (!requiresQuote) return identifier;
|
||||
return `${quote}${identifier.replaceAll(quote, quote + quote)}${quote}`;
|
||||
}
|
||||
|
||||
+141
@@ -497,6 +497,103 @@ test("quotes PostgreSQL reserved-word table identifiers when completion inserts
|
||||
assert.equal(table?.apply, '"order"');
|
||||
});
|
||||
|
||||
test("quotes MySQL reserved-word table identifiers with backticks when completion inserts them", () => {
|
||||
const sql = "select * from ord";
|
||||
const items = buildSqlCompletionItems(sql, sql.length, {
|
||||
tables: [{ name: "order", schema: "public", type: "table" }],
|
||||
columnsByTable: new Map(),
|
||||
dialect: "mysql",
|
||||
});
|
||||
|
||||
const table = items.find((item) => item.type === "table" && item.label === "order");
|
||||
assert.equal(table?.apply, "`order`");
|
||||
});
|
||||
|
||||
test("leaves safe MySQL table identifiers unquoted when completion inserts them", () => {
|
||||
const sql = "select * from article";
|
||||
const items = buildSqlCompletionItems(sql, sql.length, {
|
||||
tables: [{ name: "article", schema: "public", type: "table" }],
|
||||
columnsByTable: new Map(),
|
||||
dialect: "mysql",
|
||||
});
|
||||
|
||||
const table = items.find((item) => item.type === "table" && item.label === "article");
|
||||
assert.equal(table?.apply, "article");
|
||||
});
|
||||
|
||||
test("quotes MySQL reserved-word column identifiers with backticks when completion inserts them", () => {
|
||||
const reservedColumns = ["order", "do", "returning", "ilike", "window", "true"];
|
||||
const reservedColumnsByTable = new Map<string, SqlCompletionColumn[]>([["public.bookings", reservedColumns.map((name) => ({ name, table: "bookings", schema: "public", dataType: "text" }))]]);
|
||||
const sql = "select from bookings";
|
||||
const items = buildSqlCompletionItems(sql, "select ".length, {
|
||||
tables: [{ name: "bookings", schema: "public", type: "table" }],
|
||||
columnsByTable: reservedColumnsByTable,
|
||||
dialect: "mysql",
|
||||
});
|
||||
|
||||
for (const name of reservedColumns) {
|
||||
const column = items.find((item) => item.type === "column" && item.label === name);
|
||||
assert.equal(column?.apply, `\`${name}\``, `expected reserved column "${name}" to be quoted`);
|
||||
}
|
||||
});
|
||||
|
||||
test("quotes MySQL-only reserved identifiers in schema, table, column, and join completions", () => {
|
||||
const schemaSql = "select * from data";
|
||||
const schemaItems = buildSqlCompletionItems(schemaSql, schemaSql.length, {
|
||||
tables: [],
|
||||
columnsByTable: new Map(),
|
||||
schemas: ["database"],
|
||||
dialect: "mysql",
|
||||
});
|
||||
assert.equal(schemaItems.find((item) => item.type === "schema" && item.label === "database")?.apply, "`database`.");
|
||||
|
||||
const tableSql = "select * from access";
|
||||
const tableItems = buildSqlCompletionItems(tableSql, tableSql.length, {
|
||||
tables: [{ name: "accessible", schema: "app", type: "table" }],
|
||||
columnsByTable: new Map(),
|
||||
dialect: "mysql",
|
||||
});
|
||||
assert.equal(tableItems.find((item) => item.type === "table" && item.label === "accessible")?.apply, "`accessible`");
|
||||
|
||||
const columnsByReservedTable = new Map<string, SqlCompletionColumn[]>([["app.accounts", [{ name: "use", table: "accounts", schema: "app", dataType: "text" }]]]);
|
||||
const columnSql = "select us from accounts";
|
||||
const columnItems = buildSqlCompletionItems(columnSql, "select us".length, {
|
||||
tables: [{ name: "accounts", schema: "app", type: "table" }],
|
||||
columnsByTable: columnsByReservedTable,
|
||||
dialect: "mysql",
|
||||
});
|
||||
assert.equal(columnItems.find((item) => item.type === "column" && item.label === "use")?.apply, "`use`");
|
||||
|
||||
const joinColumns = new Map<string, SqlCompletionColumn[]>([
|
||||
["app.accounts", [{ name: "accessible", table: "accounts", schema: "app", dataType: "bigint" }]],
|
||||
["app.members", [{ name: "accessible", table: "members", schema: "app", dataType: "bigint" }]],
|
||||
]);
|
||||
const joinSql = "select * from app.accounts a join app.members m on ";
|
||||
const joinItems = buildSqlCompletionItems(joinSql, joinSql.length, {
|
||||
tables: [
|
||||
{ name: "accounts", schema: "app", type: "table" },
|
||||
{ name: "members", schema: "app", type: "table" },
|
||||
],
|
||||
columnsByTable: joinColumns,
|
||||
foreignKeysByTable: new Map([
|
||||
[
|
||||
"app.accounts",
|
||||
[
|
||||
{
|
||||
name: "accounts_members_accessible_fkey",
|
||||
column: "accessible",
|
||||
ref_schema: "app",
|
||||
ref_table: "members",
|
||||
ref_column: "accessible",
|
||||
},
|
||||
],
|
||||
],
|
||||
]),
|
||||
dialect: "mysql",
|
||||
});
|
||||
assert.equal(joinItems.find((item) => item.label === "a.accessible = m.accessible")?.apply, "a.`accessible` = m.`accessible`");
|
||||
});
|
||||
|
||||
test("suggests matching table names after FROM", () => {
|
||||
const sql = "select * from us";
|
||||
const items = buildSqlCompletionItems(sql, sql.length, {
|
||||
@@ -1867,6 +1964,50 @@ test("suggests stored procedures after CALL", () => {
|
||||
);
|
||||
});
|
||||
|
||||
test("quotes MySQL routine apply names for current and qualified databases", () => {
|
||||
const currentItems = buildSqlCompletionItems("select us", "select us".length, {
|
||||
tables: [],
|
||||
columnsByTable: new Map(),
|
||||
objects: [{ name: "use", schema: "app", type: "function", applyName: "use" }],
|
||||
databaseType: "mysql",
|
||||
dialect: "mysql",
|
||||
currentSchema: "app",
|
||||
});
|
||||
assert.equal(currentItems.find((item) => item.type === "function" && item.label === "use")?.apply, "`use`()");
|
||||
|
||||
const qualifiedItems = buildSqlCompletionItems("select us", "select us".length, {
|
||||
tables: [],
|
||||
columnsByTable: new Map(),
|
||||
objects: [{ name: "use", schema: "database", type: "function", applyName: "database.use" }],
|
||||
databaseType: "mysql",
|
||||
dialect: "mysql",
|
||||
currentSchema: "app",
|
||||
});
|
||||
assert.equal(qualifiedItems.find((item) => item.type === "function" && item.label === "use")?.apply, "`database`.`use`()");
|
||||
});
|
||||
|
||||
test("preserves already quoted routine apply names and non-MySQL spelling", () => {
|
||||
const mysqlItems = buildSqlCompletionItems("select us", "select us".length, {
|
||||
tables: [],
|
||||
columnsByTable: new Map(),
|
||||
objects: [{ name: "use", schema: "database", type: "function", applyName: "`database`.`use`" }],
|
||||
databaseType: "mysql",
|
||||
dialect: "mysql",
|
||||
currentSchema: "app",
|
||||
});
|
||||
assert.equal(mysqlItems.find((item) => item.type === "function" && item.label === "use")?.apply, "`database`.`use`()");
|
||||
|
||||
const postgresItems = buildSqlCompletionItems("select ord", "select ord".length, {
|
||||
tables: [],
|
||||
columnsByTable: new Map(),
|
||||
objects: [{ name: "order", schema: "app", type: "function", applyName: "app.order" }],
|
||||
databaseType: "postgres",
|
||||
dialect: "postgres",
|
||||
currentSchema: "public",
|
||||
});
|
||||
assert.equal(postgresItems.find((item) => item.type === "function" && item.label === "order")?.apply, "app.order()");
|
||||
});
|
||||
|
||||
test("prioritizes referenced table columns in WHERE field input", () => {
|
||||
const sql = "select * from A1User WHERE userc";
|
||||
const items = buildSqlCompletionItems(sql, sql.length, {
|
||||
|
||||
Reference in New Issue
Block a user