feat(sql): improve MySQL function completion

This commit is contained in:
Freedom
2026-07-22 13:44:16 +08:00
committed by GitHub
parent 1fb3e2816b
commit bf41838006
10 changed files with 517 additions and 5 deletions
@@ -176,6 +176,44 @@ const SQL_FUNCTION_NAMES = [
"DATE_SUB",
"EXTRACT",
"NOW",
"CURRENT_DATE",
"CURRENT_TIME",
"CURRENT_TIMESTAMP",
"CURDATE",
"CURTIME",
"LOCALTIME",
"LOCALTIMESTAMP",
"UTC_DATE",
"UTC_TIME",
"UTC_TIMESTAMP",
"SYSDATE",
"DATE",
"TIME",
"TIMESTAMPDIFF",
"YEAR",
"MONTH",
"DAY",
"HOUR",
"MINUTE",
"SECOND",
"DAYOFWEEK",
"DAYOFYEAR",
"LAST_DAY",
"STR_TO_DATE",
"IF",
"LEFT",
"RIGHT",
"SUBSTRING_INDEX",
"CHAR_LENGTH",
"INSTR",
"LOCATE",
"LPAD",
"RPAD",
"FIND_IN_SET",
"RAND",
"MD5",
"SHA1",
"SHA2",
"ROUND",
"FLOOR",
"CEIL",
+38
View File
@@ -636,6 +636,44 @@ export default {
DATE_SUB: "Subtracts an interval from a date",
EXTRACT: "Extracts a date part",
NOW: "Returns the current date and time",
CURRENT_DATE: "Returns the current date",
CURRENT_TIME: "Returns the current time",
CURRENT_TIMESTAMP: "Returns the current date and time",
CURDATE: "Returns the current date",
CURTIME: "Returns the current time",
LOCALTIME: "Returns the current local time",
LOCALTIMESTAMP: "Returns the current local date and time",
UTC_DATE: "Returns the current UTC date",
UTC_TIME: "Returns the current UTC time",
UTC_TIMESTAMP: "Returns the current UTC date and time",
SYSDATE: "Returns the current date and time",
DATE: "Extracts the date part",
TIME: "Extracts the time part",
TIMESTAMPDIFF: "Returns the difference between two datetimes",
YEAR: "Extracts the year",
MONTH: "Extracts the month",
DAY: "Extracts the day",
HOUR: "Extracts the hour",
MINUTE: "Extracts the minute",
SECOND: "Extracts the second",
DAYOFWEEK: "Returns the weekday index",
DAYOFYEAR: "Returns the day of year",
LAST_DAY: "Returns the last day of the month",
STR_TO_DATE: "Converts a string to a date",
IF: "Returns a value based on a condition",
LEFT: "Returns the leftmost characters",
RIGHT: "Returns the rightmost characters",
SUBSTRING_INDEX: "Returns a substring before a delimiter count",
CHAR_LENGTH: "Returns the character length",
INSTR: "Returns the position of a substring",
LOCATE: "Returns the position of a substring",
LPAD: "Left-pads a string to a length",
RPAD: "Right-pads a string to a length",
FIND_IN_SET: "Returns the index in a comma-separated list",
RAND: "Returns a random floating-point value",
MD5: "Returns the MD5 hash",
SHA1: "Returns the SHA1 hash",
SHA2: "Returns the SHA2 hash",
ROUND: "Rounds to the specified decimal places",
FLOOR: "Rounds down",
CEIL: "Rounds up",
+38
View File
@@ -617,6 +617,44 @@ export default withEnglishFallback({
DATE_SUB: "Resta un intervalo de una fecha",
EXTRACT: "Extrae una parte de la fecha",
NOW: "Devuelve la fecha y hora actual",
CURRENT_DATE: "Devuelve la fecha actual",
CURRENT_TIME: "Devuelve la hora actual",
CURRENT_TIMESTAMP: "Devuelve la fecha y hora actual",
CURDATE: "Devuelve la fecha actual",
CURTIME: "Devuelve la hora actual",
LOCALTIME: "Devuelve la hora local actual",
LOCALTIMESTAMP: "Devuelve la fecha y hora local actual",
UTC_DATE: "Devuelve la fecha UTC actual",
UTC_TIME: "Devuelve la hora UTC actual",
UTC_TIMESTAMP: "Devuelve la fecha y hora UTC actual",
SYSDATE: "Devuelve la fecha y hora actual",
DATE: "Extrae la parte de fecha",
TIME: "Extrae la parte de hora",
TIMESTAMPDIFF: "Devuelve la diferencia entre dos fechas y horas",
YEAR: "Extrae el año",
MONTH: "Extrae el mes",
DAY: "Extrae el día",
HOUR: "Extrae la hora",
MINUTE: "Extrae el minuto",
SECOND: "Extrae el segundo",
DAYOFWEEK: "Devuelve el índice del día de la semana",
DAYOFYEAR: "Devuelve el día del año",
LAST_DAY: "Devuelve el último día del mes",
STR_TO_DATE: "Convierte una cadena en fecha",
IF: "Devuelve un valor según una condición",
LEFT: "Devuelve los caracteres más a la izquierda",
RIGHT: "Devuelve los caracteres más a la derecha",
SUBSTRING_INDEX: "Devuelve una subcadena antes de un delimitador",
CHAR_LENGTH: "Devuelve la longitud en caracteres",
INSTR: "Devuelve la posición de una subcadena",
LOCATE: "Devuelve la posición de una subcadena",
LPAD: "Rellena una cadena por la izquierda",
RPAD: "Rellena una cadena por la derecha",
FIND_IN_SET: "Devuelve el índice en una lista separada por comas",
RAND: "Devuelve un valor flotante aleatorio",
MD5: "Devuelve el hash MD5",
SHA1: "Devuelve el hash SHA1",
SHA2: "Devuelve el hash SHA2",
ROUND: "Redondea a los decimales especificados",
FLOOR: "Redondea hacia abajo",
CEIL: "Redondea hacia arriba",
+38
View File
@@ -615,6 +615,44 @@ export default withEnglishFallback({
DATE_SUB: "Sottrae un intervallo da una data",
EXTRACT: "Estrae una parte di data",
NOW: "Restituisce la data e l'ora correnti",
CURRENT_DATE: "Restituisce la data corrente",
CURRENT_TIME: "Restituisce l'ora corrente",
CURRENT_TIMESTAMP: "Restituisce la data e l'ora correnti",
CURDATE: "Restituisce la data corrente",
CURTIME: "Restituisce l'ora corrente",
LOCALTIME: "Restituisce l'ora locale corrente",
LOCALTIMESTAMP: "Restituisce la data e l'ora locale correnti",
UTC_DATE: "Restituisce la data UTC corrente",
UTC_TIME: "Restituisce l'ora UTC corrente",
UTC_TIMESTAMP: "Restituisce la data e l'ora UTC correnti",
SYSDATE: "Restituisce la data e l'ora correnti",
DATE: "Estrae la parte data",
TIME: "Estrae la parte ora",
TIMESTAMPDIFF: "Restituisce la differenza tra due date/ore",
YEAR: "Estrae l'anno",
MONTH: "Estrae il mese",
DAY: "Estrae il giorno",
HOUR: "Estrae l'ora",
MINUTE: "Estrae il minuto",
SECOND: "Estrae il secondo",
DAYOFWEEK: "Restituisce l'indice del giorno della settimana",
DAYOFYEAR: "Restituisce il giorno dell'anno",
LAST_DAY: "Restituisce l'ultimo giorno del mese",
STR_TO_DATE: "Converte una stringa in data",
IF: "Restituisce un valore in base a una condizione",
LEFT: "Restituisce i caratteri più a sinistra",
RIGHT: "Restituisce i caratteri più a destra",
SUBSTRING_INDEX: "Restituisce una sottostringa prima di un delimitatore",
CHAR_LENGTH: "Restituisce la lunghezza in caratteri",
INSTR: "Restituisce la posizione di una sottostringa",
LOCATE: "Restituisce la posizione di una sottostringa",
LPAD: "Riempie una stringa a sinistra",
RPAD: "Riempie una stringa a destra",
FIND_IN_SET: "Restituisce l'indice in un elenco separato da virgole",
RAND: "Restituisce un valore mobile casuale",
MD5: "Restituisce l'hash MD5",
SHA1: "Restituisce l'hash SHA1",
SHA2: "Restituisce l'hash SHA2",
ROUND: "Arrotonda alla quantità specificata di decimali",
FLOOR: "Arrotonda per difetto",
CEIL: "Arrotonda per eccesso",
+38
View File
@@ -614,6 +614,44 @@ export default withEnglishFallback({
DATE_SUB: "日付から間隔を減算",
EXTRACT: "日付の一部を抽出",
NOW: "現在の日時を返す",
CURRENT_DATE: "現在の日付を返す",
CURRENT_TIME: "現在の時刻を返す",
CURRENT_TIMESTAMP: "現在の日時を返す",
CURDATE: "現在の日付を返す",
CURTIME: "現在の時刻を返す",
LOCALTIME: "現在のローカル時刻を返す",
LOCALTIMESTAMP: "現在のローカル日時を返す",
UTC_DATE: "現在の UTC 日付を返す",
UTC_TIME: "現在の UTC 時刻を返す",
UTC_TIMESTAMP: "現在の UTC 日時を返す",
SYSDATE: "現在の日時を返す",
DATE: "日付部分を抽出",
TIME: "時刻部分を抽出",
TIMESTAMPDIFF: "二つの日時の差を返す",
YEAR: "年を抽出",
MONTH: "月を抽出",
DAY: "日を抽出",
HOUR: "時を抽出",
MINUTE: "分を抽出",
SECOND: "秒を抽出",
DAYOFWEEK: "曜日インデックスを返す",
DAYOFYEAR: "年内通算日を返す",
LAST_DAY: "月末日を返す",
STR_TO_DATE: "文字列を日付に変換",
IF: "条件に応じた値を返す",
LEFT: "左端の文字を返す",
RIGHT: "右端の文字を返す",
SUBSTRING_INDEX: "区切り文字で部分文字列を返す",
CHAR_LENGTH: "文字数を返す",
INSTR: "部分文字列の位置を返す",
LOCATE: "部分文字列の位置を返す",
LPAD: "文字列を左埋めする",
RPAD: "文字列を右埋めする",
FIND_IN_SET: "カンマ区切りリスト内の位置を返す",
RAND: "乱数を返す",
MD5: "MD5 ハッシュを返す",
SHA1: "SHA1 ハッシュを返す",
SHA2: "SHA2 ハッシュを返す",
ROUND: "指定された小数点以下桁数で丸める",
FLOOR: "切り捨て",
CEIL: "切り上げ",
+38
View File
@@ -616,6 +616,44 @@ export default withEnglishFallback({
DATE_SUB: "Subtrai um intervalo de uma data",
EXTRACT: "Extrai uma parte da data",
NOW: "Retorna a data e hora atuais",
CURRENT_DATE: "Retorna a data atual",
CURRENT_TIME: "Retorna a hora atual",
CURRENT_TIMESTAMP: "Retorna a data e hora atuais",
CURDATE: "Retorna a data atual",
CURTIME: "Retorna a hora atual",
LOCALTIME: "Retorna a hora local atual",
LOCALTIMESTAMP: "Retorna a data e hora local atuais",
UTC_DATE: "Retorna a data UTC atual",
UTC_TIME: "Retorna a hora UTC atual",
UTC_TIMESTAMP: "Retorna a data e hora UTC atuais",
SYSDATE: "Retorna a data e hora atuais",
DATE: "Extrai a parte da data",
TIME: "Extrai a parte da hora",
TIMESTAMPDIFF: "Retorna a diferença entre duas datas/horas",
YEAR: "Extrai o ano",
MONTH: "Extrai o mês",
DAY: "Extrai o dia",
HOUR: "Extrai a hora",
MINUTE: "Extrai o minuto",
SECOND: "Extrai o segundo",
DAYOFWEEK: "Retorna o índice do dia da semana",
DAYOFYEAR: "Retorna o dia do ano",
LAST_DAY: "Retorna o último dia do mês",
STR_TO_DATE: "Converte uma string em data",
IF: "Retorna um valor com base em uma condição",
LEFT: "Retorna os caracteres mais à esquerda",
RIGHT: "Retorna os caracteres mais à direita",
SUBSTRING_INDEX: "Retorna uma substring antes de um delimitador",
CHAR_LENGTH: "Retorna o comprimento em caracteres",
INSTR: "Retorna a posição de uma substring",
LOCATE: "Retorna a posição de uma substring",
LPAD: "Preenche uma string à esquerda",
RPAD: "Preenche uma string à direita",
FIND_IN_SET: "Retorna o índice em uma lista separada por vírgulas",
RAND: "Retorna um valor flutuante aleatório",
MD5: "Retorna o hash MD5",
SHA1: "Retorna o hash SHA1",
SHA2: "Retorna o hash SHA2",
ROUND: "Arredonda para o número de casas decimais especificado",
FLOOR: "Arredonda para baixo",
CEIL: "Arredonda para cima",
+38
View File
@@ -638,6 +638,44 @@ export default withEnglishFallback({
DATE_SUB: "对日期进行减法运算",
EXTRACT: "提取日期中的指定部分",
NOW: "返回当前日期时间",
CURRENT_DATE: "返回当前日期",
CURRENT_TIME: "返回当前时间",
CURRENT_TIMESTAMP: "返回当前日期时间",
CURDATE: "返回当前日期",
CURTIME: "返回当前时间",
LOCALTIME: "返回当前本地时间",
LOCALTIMESTAMP: "返回当前本地日期时间",
UTC_DATE: "返回当前 UTC 日期",
UTC_TIME: "返回当前 UTC 时间",
UTC_TIMESTAMP: "返回当前 UTC 日期时间",
SYSDATE: "返回当前日期时间",
DATE: "提取日期部分",
TIME: "提取时间部分",
TIMESTAMPDIFF: "计算两个日期时间的差值",
YEAR: "提取年份",
MONTH: "提取月份",
DAY: "提取日",
HOUR: "提取小时",
MINUTE: "提取分钟",
SECOND: "提取秒",
DAYOFWEEK: "返回星期索引",
DAYOFYEAR: "返回一年中的第几天",
LAST_DAY: "返回当月最后一天",
STR_TO_DATE: "将字符串转换为日期",
IF: "按条件返回值",
LEFT: "返回左侧字符",
RIGHT: "返回右侧字符",
SUBSTRING_INDEX: "按分隔符截取子串",
CHAR_LENGTH: "返回字符长度",
INSTR: "返回子串位置",
LOCATE: "返回子串位置",
LPAD: "左侧填充字符串",
RPAD: "右侧填充字符串",
FIND_IN_SET: "返回在逗号分隔列表中的位置",
RAND: "返回随机浮点数",
MD5: "返回 MD5 哈希",
SHA1: "返回 SHA1 哈希",
SHA2: "返回 SHA2 哈希",
ROUND: "四舍五入到指定小数位",
FLOOR: "向下取整",
CEIL: "向上取整",
+38
View File
@@ -616,6 +616,44 @@ export default withEnglishFallback({
DATE_SUB: "對日期進行減法運算",
EXTRACT: "擷取日期部分",
NOW: "回傳目前日期與時間",
CURRENT_DATE: "回傳目前日期",
CURRENT_TIME: "回傳目前時間",
CURRENT_TIMESTAMP: "回傳目前日期與時間",
CURDATE: "回傳目前日期",
CURTIME: "回傳目前時間",
LOCALTIME: "回傳目前本地時間",
LOCALTIMESTAMP: "回傳目前本地日期與時間",
UTC_DATE: "回傳目前 UTC 日期",
UTC_TIME: "回傳目前 UTC 時間",
UTC_TIMESTAMP: "回傳目前 UTC 日期與時間",
SYSDATE: "回傳目前日期與時間",
DATE: "擷取日期部分",
TIME: "擷取時間部分",
TIMESTAMPDIFF: "計算兩個日期時間的差值",
YEAR: "擷取年份",
MONTH: "擷取月份",
DAY: "擷取日",
HOUR: "擷取小時",
MINUTE: "擷取分鐘",
SECOND: "擷取秒",
DAYOFWEEK: "回傳星期索引",
DAYOFYEAR: "回傳一年中的第幾天",
LAST_DAY: "回傳當月最後一天",
STR_TO_DATE: "將字串轉換為日期",
IF: "依條件回傳值",
LEFT: "回傳左側字元",
RIGHT: "回傳右側字元",
SUBSTRING_INDEX: "依分隔符號擷取子字串",
CHAR_LENGTH: "回傳字元長度",
INSTR: "回傳子字串位置",
LOCATE: "回傳子字串位置",
LPAD: "左側填充字串",
RPAD: "右側填充字串",
FIND_IN_SET: "回傳在逗號分隔清單中的位置",
RAND: "回傳隨機浮點數",
MD5: "回傳 MD5 雜湊",
SHA1: "回傳 SHA1 雜湊",
SHA2: "回傳 SHA2 雜湊",
ROUND: "四捨五入到指定小數位",
FLOOR: "向下捨入",
CEIL: "向上捨入",
+116 -5
View File
@@ -724,6 +724,44 @@ function getFunctionDescriptions(t?: SqlCompletionTranslations): Map<string, str
["DATE_SUB", d.DATE_SUB || "Subtracts from a date"],
["EXTRACT", d.EXTRACT || "Extracts a part from a date"],
["NOW", d.NOW || "Returns the current date and time"],
["CURRENT_DATE", d.CURRENT_DATE || "Returns the current date"],
["CURRENT_TIME", d.CURRENT_TIME || "Returns the current time"],
["CURRENT_TIMESTAMP", d.CURRENT_TIMESTAMP || "Returns the current date and time"],
["CURDATE", d.CURDATE || "Returns the current date"],
["CURTIME", d.CURTIME || "Returns the current time"],
["LOCALTIME", d.LOCALTIME || "Returns the current local time"],
["LOCALTIMESTAMP", d.LOCALTIMESTAMP || "Returns the current local date and time"],
["UTC_DATE", d.UTC_DATE || "Returns the current UTC date"],
["UTC_TIME", d.UTC_TIME || "Returns the current UTC time"],
["UTC_TIMESTAMP", d.UTC_TIMESTAMP || "Returns the current UTC date and time"],
["SYSDATE", d.SYSDATE || "Returns the current date and time"],
["DATE", d.DATE || "Extracts the date part"],
["TIME", d.TIME || "Extracts the time part"],
["TIMESTAMPDIFF", d.TIMESTAMPDIFF || "Returns the difference between two datetimes"],
["YEAR", d.YEAR || "Extracts the year"],
["MONTH", d.MONTH || "Extracts the month"],
["DAY", d.DAY || "Extracts the day"],
["HOUR", d.HOUR || "Extracts the hour"],
["MINUTE", d.MINUTE || "Extracts the minute"],
["SECOND", d.SECOND || "Extracts the second"],
["DAYOFWEEK", d.DAYOFWEEK || "Returns the weekday index"],
["DAYOFYEAR", d.DAYOFYEAR || "Returns the day of year"],
["LAST_DAY", d.LAST_DAY || "Returns the last day of the month"],
["STR_TO_DATE", d.STR_TO_DATE || "Converts a string to a date"],
["IF", d.IF || "Returns a value based on a condition"],
["LEFT", d.LEFT || "Returns the leftmost characters"],
["RIGHT", d.RIGHT || "Returns the rightmost characters"],
["SUBSTRING_INDEX", d.SUBSTRING_INDEX || "Returns a substring before a delimiter count"],
["CHAR_LENGTH", d.CHAR_LENGTH || "Returns the character length"],
["INSTR", d.INSTR || "Returns the position of a substring"],
["LOCATE", d.LOCATE || "Returns the position of a substring"],
["LPAD", d.LPAD || "Left-pads a string to a length"],
["RPAD", d.RPAD || "Right-pads a string to a length"],
["FIND_IN_SET", d.FIND_IN_SET || "Returns the index in a comma-separated list"],
["RAND", d.RAND || "Returns a random floating-point value"],
["MD5", d.MD5 || "Returns the MD5 hash"],
["SHA1", d.SHA1 || "Returns the SHA1 hash"],
["SHA2", d.SHA2 || "Returns the SHA2 hash"],
["ROUND", d.ROUND || "Rounds to the specified precision"],
["FLOOR", d.FLOOR || "Rounds down"],
["CEIL", d.CEIL || "Rounds up"],
@@ -890,8 +928,8 @@ const SQL_FUNCTION_SIGNATURES = new Map<string, string[]>([
["DATE_FORMAT", ["date", "format"]],
["DATEDIFF", ["date1", "date2"]],
["TIMESTAMPDIFF", ["unit", "datetime_expr1", "datetime_expr2"]],
["DATE_ADD", ["date", "interval"]],
["DATE_SUB", ["date", "interval"]],
["DATE_ADD", ["date", "INTERVAL expr unit"]],
["DATE_SUB", ["date", "INTERVAL expr unit"]],
["EXTRACT", ["unit", "date"]],
["YEAR", ["date"]],
["MONTH", ["date"]],
@@ -967,6 +1005,48 @@ const MYSQL_FUNCTION_SIGNATURES = new Map<string, string[]>([
["FROM_UNIXTIME", ["unix_timestamp"]],
["UNIX_TIMESTAMP", []],
["SYSDATE", []],
["CURRENT_DATE", []],
["CURRENT_TIME", []],
["CURRENT_TIMESTAMP", []],
["CURDATE", []],
["CURTIME", []],
["LOCALTIME", []],
["LOCALTIMESTAMP", []],
["UTC_DATE", []],
["UTC_TIME", []],
["UTC_TIMESTAMP", []],
["DATE", ["expression"]],
["TIME", ["expression"]],
["DATE_ADD", ["date", "INTERVAL expr unit"]],
["DATE_SUB", ["date", "INTERVAL expr unit"]],
["DATEDIFF", ["date1", "date2"]],
["TIMESTAMPDIFF", ["unit", "datetime_expr1", "datetime_expr2"]],
["YEAR", ["date"]],
["MONTH", ["date"]],
["DAY", ["date"]],
["HOUR", ["datetime"]],
["MINUTE", ["datetime"]],
["SECOND", ["datetime"]],
["DAYOFWEEK", ["date"]],
["DAYOFYEAR", ["date"]],
["LAST_DAY", ["date"]],
["STR_TO_DATE", ["string", "format"]],
["IFNULL", ["expression", "fallback"]],
["IF", ["condition", "true_value", "false_value"]],
["CONCAT_WS", ["separator", "...values"]],
["LEFT", ["string", "length"]],
["RIGHT", ["string", "length"]],
["SUBSTRING_INDEX", ["string", "delimiter", "count"]],
["CHAR_LENGTH", ["string"]],
["INSTR", ["string", "substring"]],
["LOCATE", ["substring", "string"]],
["LPAD", ["string", "length", "pad"]],
["RPAD", ["string", "length", "pad"]],
["FIND_IN_SET", ["string", "string_list"]],
["RAND", []],
["MD5", ["string"]],
["SHA1", ["string"]],
["SHA2", ["string", "bit_length"]],
["JSON_EXTRACT", ["json", "path"]],
["JSON_UNQUOTE", ["json"]],
["GROUP_CONCAT", ["expression"]],
@@ -974,6 +1054,9 @@ const MYSQL_FUNCTION_SIGNATURES = new Map<string, string[]>([
["NOW", []],
]);
/** Keywords that may also exist as built-in functions; keep both completion entries. */
const DUAL_ROLE_SQL_KEYWORDS = new Set(["LEFT", "RIGHT", "IF"]);
const SQLITE_FUNCTION_SIGNATURES = new Map<string, string[]>([
["JSON_EXTRACT", ["json", "path"]],
["JSON_SET", ["json", "path", "value"]],
@@ -1052,6 +1135,11 @@ const DATABASE_FUNCTION_SIGNATURES: Partial<Record<DatabaseType, Map<string, str
manticoresearch: MANTICORESEARCH_FUNCTION_SIGNATURES,
};
const MYSQL_FUNCTION_APPLY_TEMPLATES = new Map<string, string>([
["DATE_ADD", "DATE_ADD(${date}, INTERVAL ${expr} ${unit})"],
["DATE_SUB", "DATE_SUB(${date}, INTERVAL ${expr} ${unit})"],
]);
const COMMON_SQL_FUNCTION_NAMES = new Set([
"COUNT",
"SUM",
@@ -1265,6 +1353,7 @@ export interface SqlCompletionContext {
autoAliasTableCompletions: boolean;
tableAliasAfterCursor?: boolean;
contextKind: SqlCompletionContextKind;
dataTypeContext: boolean;
}
export interface SqlFunctionSignatureHelp {
@@ -1356,7 +1445,7 @@ class SqlCompletionProvider {
this.items.push(...buildSnippetItems(context.prefix, snippets, this.input.keywordCase));
}
if (!preferReferencedColumns || context.suggestRoutines) {
const functionItems = buildFunctionSnippetItems(context.prefix, getFunctionDescriptions(this.t), this.databaseType);
const functionItems = context.dataTypeContext ? [] : buildFunctionSnippetItems(context.prefix, getFunctionDescriptions(this.t), this.databaseType);
this.items.push(...(preferReferencedColumns ? functionItems.filter((item) => item.label.toLowerCase().startsWith(context.prefix.toLowerCase())) : functionItems));
if (isOracleLikeDatabase(this.databaseType)) {
this.items.push(...buildOracleSystemValueItems(context.prefix, this.input.keywordCase));
@@ -1845,6 +1934,7 @@ export function getSqlCompletionContext(sql: string, cursor: number): SqlComplet
const suggestRoutines = inCallRoutineContext || oracleTableFunctionContext || inPotentialPackageMemberContext || (!exclusiveTableSuggestions && !exclusiveColumnSuggestions && !insertInfo && !updateInfo?.inSetClause && prefix.length >= 2);
const statementKind = detectStatementKind(beforeCursor || fullStatement);
const dataTypeContext = isCreateTableColumnTypeContext(beforeToken);
const preferredKeywords = qualifier ? [] : preferredKeywordsForCompletion(beforeCursor, beforeToken, selectListColumnContext, exclusiveTableSuggestions, updateInfo, deleteInfo);
const contextKind = detectCompletionContextKind({
qualifier,
@@ -1892,9 +1982,30 @@ export function getSqlCompletionContext(sql: string, cursor: number): SqlComplet
autoAliasTableCompletions,
tableAliasAfterCursor,
contextKind,
dataTypeContext,
};
}
function isCreateTableColumnTypeContext(beforeToken: string): boolean {
const cleaned = stripSqlLiterals(beforeToken);
if (!/^\s*create\s+table\b/i.test(cleaned)) return false;
const bodyStart = cleaned.indexOf("(");
if (bodyStart < 0) return false;
let depth = 0;
let segmentStart = bodyStart + 1;
for (let index = bodyStart + 1; index < cleaned.length; index += 1) {
const char = cleaned[index];
if (char === "(") depth += 1;
else if (char === ")") depth = Math.max(0, depth - 1);
else if (char === "," && depth === 0) segmentStart = index + 1;
}
const segment = cleaned.slice(segmentStart).trim();
return /^(?:[A-Za-z_][\w$]*|`[^`]+`|"[^"]+"|\[[^\]]+\])$/.test(segment);
}
function removeActiveTableCompletionReference(referencedTables: SqlCompletionReferencedTable[], prefix: string, qualifier?: string): SqlCompletionReferencedTable[] {
const activeName = normalizeIdentifierPart(prefix);
const activeQualifier = qualifier ? normalizeIdentifierPart(qualifier) : undefined;
@@ -4017,7 +4128,7 @@ function buildFunctionSnippetItems(prefix: string, functionDescriptions: Map<str
label: name,
type: "function" as const,
detail: functionDescriptions.get(name) ?? "function",
apply: `${name}(${paramStr})`,
apply: (databaseType === "mysql" ? MYSQL_FUNCTION_APPLY_TEMPLATES.get(name) : undefined) ?? `${name}(${paramStr})`,
boost: computeBoost(name, prefix) + 300,
});
}
@@ -4132,7 +4243,7 @@ function buildKeywordItems(prefix: string, context: SqlCompletionContext, databa
return activeSqlKeywords(databaseType)
.filter((keyword) => {
if (functionSignatures.has(keyword)) return false;
if (functionSignatures.has(keyword) && !DATA_TYPE_KEYWORDS.has(keyword) && !DUAL_ROLE_SQL_KEYWORDS.has(keyword)) return false;
if (WINDOW_FUNCTIONS.has(keyword)) return false;
if (!matchesPrefix(keyword, prefix)) return false;
if (!showDdl && isDml && (DDL_ONLY_KEYWORDS.has(keyword) || DATA_TYPE_KEYWORDS.has(keyword))) return false;
+97
View File
@@ -133,6 +133,46 @@ test("suggests database-specific data types and functions", () => {
columnsByTable: new Map(),
databaseType: "mysql",
});
const mysqlCurrentDateItems = buildSqlCompletionItems("select current_d", "select current_d".length, {
tables: [],
columnsByTable: new Map(),
databaseType: "mysql",
});
const mysqlCurrentTimestampItems = buildSqlCompletionItems("select current_t", "select current_t".length, {
tables: [],
columnsByTable: new Map(),
databaseType: "mysql",
});
const mysqlCurdateItems = buildSqlCompletionItems("select curd", "select curd".length, {
tables: [],
columnsByTable: new Map(),
databaseType: "mysql",
});
const mysqlIfnullItems = buildSqlCompletionItems("select ifn", "select ifn".length, {
tables: [],
columnsByTable: new Map(),
databaseType: "mysql",
});
const mysqlDateAddItems = buildSqlCompletionItems("select date_a", "select date_a".length, {
tables: [],
columnsByTable: new Map(),
databaseType: "mysql",
});
const mysqlDateSubItems = buildSqlCompletionItems("select date_s", "select date_s".length, {
tables: [],
columnsByTable: new Map(),
databaseType: "mysql",
});
const mysqlSubstringIndexItems = buildSqlCompletionItems("select substring_i", "select substring_i".length, {
tables: [],
columnsByTable: new Map(),
databaseType: "mysql",
});
const mysqlLeftItems = buildSqlCompletionItems("select lef", "select lef".length, {
tables: [],
columnsByTable: new Map(),
databaseType: "mysql",
});
const postgresDateItems = buildSqlCompletionItems("select date_f", "select date_f".length, {
tables: [],
columnsByTable: new Map(),
@@ -144,10 +184,48 @@ test("suggests database-specific data types and functions", () => {
assert.ok(functionItems.some((item) => item.type === "function" && item.label === "JSONB_BUILD_OBJECT"));
assert.ok(mysqlFunctionItems.some((item) => item.type === "function" && item.label === "DATE_FORMAT"));
assert.ok(mysqlSysdateItems.some((item) => item.type === "function" && item.label === "SYSDATE"));
assert.ok(mysqlCurrentDateItems.some((item) => item.type === "function" && item.label === "CURRENT_DATE"));
assert.ok(mysqlCurrentTimestampItems.some((item) => item.type === "function" && item.label === "CURRENT_TIMESTAMP"));
assert.ok(mysqlCurrentTimestampItems.some((item) => item.type === "function" && item.label === "CURRENT_TIME"));
assert.ok(mysqlCurdateItems.some((item) => item.type === "function" && item.label === "CURDATE"));
assert.ok(mysqlIfnullItems.some((item) => item.type === "function" && item.label === "IFNULL"));
assert.equal(
mysqlDateAddItems.find((item) => item.type === "function" && item.label === "DATE_ADD")?.apply,
"DATE_ADD(${date}, INTERVAL ${expr} ${unit})",
);
assert.equal(
mysqlDateSubItems.find((item) => item.type === "function" && item.label === "DATE_SUB")?.apply,
"DATE_SUB(${date}, INTERVAL ${expr} ${unit})",
);
assert.ok(mysqlSubstringIndexItems.some((item) => item.type === "function" && item.label === "SUBSTRING_INDEX"));
assert.ok(mysqlLeftItems.some((item) => item.type === "function" && item.label === "LEFT"));
assert.ok(mysqlLeftItems.some((item) => item.type === "keyword" && item.label === "LEFT"));
assert.equal(
postgresDateItems.some((item) => item.type === "function" && item.label === "DATE_FORMAT"),
false,
);
const mysqlDateTypeItems = buildSqlCompletionItems("CREATE TABLE t (d dat", "CREATE TABLE t (d dat".length, {
tables: [],
columnsByTable: new Map(),
databaseType: "mysql",
});
const mysqlTimeTypeItems = buildSqlCompletionItems("CREATE TABLE t (tm tim", "CREATE TABLE t (tm tim".length, {
tables: [],
columnsByTable: new Map(),
databaseType: "mysql",
});
assert.equal(mysqlDateTypeItems[0]?.label, "DATE");
assert.equal(mysqlDateTypeItems.some((item) => item.type === "function" && item.label === "DATE"), false);
assert.equal(mysqlTimeTypeItems[0]?.label, "TIME");
assert.equal(mysqlTimeTypeItems.some((item) => item.type === "function" && item.label === "TIME"), false);
const mysqlCreateViewItems = buildSqlCompletionItems("CREATE VIEW v AS SELECT dat", "CREATE VIEW v AS SELECT dat".length, {
tables: [],
columnsByTable: new Map(),
databaseType: "mysql",
});
assert.ok(mysqlCreateViewItems.some((item) => item.type === "function" && item.label === "DATE"));
});
test("suggests Oracle SQL, PL/SQL, and data type keywords", () => {
@@ -772,6 +850,25 @@ test("suggests compound JOIN keywords while typing a join modifier", () => {
assert.equal(items[leftJoinIndex]?.apply, "LEFT JOIN ");
});
test("keeps MySQL LEFT JOIN ranking when LEFT() is also a function", () => {
const sql = "select * from users le";
const items = buildSqlCompletionItems(sql, sql.length, {
tables,
columnsByTable,
databaseType: "mysql",
});
const leftJoinIndex = items.findIndex((item) => item.type === "keyword" && item.label === "LEFT JOIN");
const leftKeywordIndex = items.findIndex((item) => item.type === "keyword" && item.label === "LEFT");
const leftFunctionIndex = items.findIndex((item) => item.type === "function" && item.label === "LEFT");
assert.ok(leftJoinIndex >= 0);
assert.ok(leftKeywordIndex >= 0);
assert.ok(leftFunctionIndex >= 0);
assert.ok(leftJoinIndex < leftKeywordIndex, "LEFT JOIN should rank ahead of LEFT keyword");
assert.ok(leftJoinIndex < leftFunctionIndex, "LEFT JOIN should rank ahead of LEFT()");
});
test("suggests JOIN after a join modifier", () => {
const sql = "select * from users left ";
const items = buildSqlCompletionItems(sql, sql.length, {