diff --git a/mayfly_go_web/src/views/ops/db/SqlExec.vue b/mayfly_go_web/src/views/ops/db/SqlExec.vue index c762f093..f6522990 100644 --- a/mayfly_go_web/src/views/ops/db/SqlExec.vue +++ b/mayfly_go_web/src/views/ops/db/SqlExec.vue @@ -482,7 +482,7 @@ const addQueryTab = async (db: any, dbName: string, sqlName: string = '') => { state.tabs.set(key, tab); // 注册当前sql编辑框提示词 - registerDbCompletionItemProvider('sql', tab.dbId, tab.db, tab.params.dbs); + registerDbCompletionItemProvider(tab.dbId, tab.db, tab.params.dbs, nowDbInst.value.type); }; /** @@ -554,7 +554,7 @@ const onTabChange = () => { if (nowTab?.type == TabType.Query) { // 注册sql提示 - registerDbCompletionItemProvider('sql', nowTab.dbId, nowTab.db, nowTab.params.dbs); + registerDbCompletionItemProvider(nowTab.dbId, nowTab.db, nowTab.params.dbs, nowDbInst.value.type); } }; diff --git a/mayfly_go_web/src/views/ops/db/db.ts b/mayfly_go_web/src/views/ops/db/db.ts index c5e31523..493bf9fc 100644 --- a/mayfly_go_web/src/views/ops/db/db.ts +++ b/mayfly_go_web/src/views/ops/db/db.ts @@ -2,19 +2,11 @@ import { dbApi } from './api'; import { getTextWidth } from '@/common/utils/string'; import SqlExecBox from './component/sqleditor/SqlExecBox'; - -import { language as sqlLanguage } from 'monaco-editor/esm/vs/basic-languages/mysql/mysql.js'; -import { language as addSqlLanguage } from './dialect/mysql_dialect'; import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'; import { editor, languages, Position } from 'monaco-editor'; import { registerCompletionItemProvider } from '@/components/monaco/completionItemProvider'; -import { getDbDialect } from './dialect'; - -const sqlCompletionKeywords = [...sqlLanguage.keywords, ...addSqlLanguage.keywords]; -const sqlCompletionOperators = [...sqlLanguage.operators, ...addSqlLanguage.operators]; -const sqlCompletionBuiltinFunctions = [...sqlLanguage.builtinFunctions, ...addSqlLanguage.builtinFunctions]; -const sqlCompletionBuiltinVariables = [...sqlLanguage.builtinVariables, ...addSqlLanguage.builtinVariables]; +import { EditorCompletionItem, getDbDialect } from './dialect'; const dbInstCache: Map = new Map(); @@ -511,16 +503,38 @@ export class TabInfo { } } +function registerCompletions( + completions: EditorCompletionItem[], + suggestions: languages.CompletionItem[], + kind: monaco.languages.CompletionItemKind, + range: any +) { + // mysql关键字 + completions.forEach((item: EditorCompletionItem) => { + let { label, insertText, description } = item; + suggestions.push({ + label: { label, description }, + kind, + insertText: insertText || label, + range, + }); + }); +} + /** * 注册数据库表、字段等信息提示 * - * @param language 语言 * @param dbId 数据库id * @param db 库名 * @param dbs 该库所有库名 + * @param dbType 数据库类型 */ -export function registerDbCompletionItemProvider(language: string, dbId: number, db: string, dbs: any[] = []) { - registerCompletionItemProvider(language, { +export function registerDbCompletionItemProvider(dbId: number, db: string, dbs: any[] = [], dbType: string) { + let dbDialect = getDbDialect(dbType); + let dbDialectInfo = dbDialect.getInfo(); + + let { keywords, operators, functions, variables } = dbDialectInfo.editorCompletions; + registerCompletionItemProvider('sql', { triggerCharacters: ['.', ' '], provideCompletionItems: async (model: editor.ITextModel, position: Position): Promise => { let word = model.getWordUntilPosition(position); @@ -664,73 +678,10 @@ export function registerDbCompletionItemProvider(language: string, dbId: number, }); }); - // mysql关键字 - sqlCompletionKeywords.forEach((item: any) => { - suggestions.push({ - label: { - label: item, - description: 'keyword', - }, - kind: monaco.languages.CompletionItemKind.Keyword, - insertText: item, - range, - }); - }); - - // 操作符 - sqlCompletionOperators.forEach((item: any) => { - suggestions.push({ - label: { - label: item, - description: 'opt', - }, - kind: monaco.languages.CompletionItemKind.Operator, - insertText: item, - range, - }); - }); - - let replacedFunctions = [] as string[]; - - // 添加的函数 - addSqlLanguage.replaceFunctions.forEach((item: any) => { - replacedFunctions.push(item.label); - suggestions.push({ - label: { - label: item.label, - description: item.description, - }, - kind: monaco.languages.CompletionItemKind.Function, - insertText: item.insertText, - range, - }); - }); - - // 内置函数 - sqlCompletionBuiltinFunctions.forEach((item: any) => { - replacedFunctions.indexOf(item) < 0 && - suggestions.push({ - label: { - label: item, - description: 'func', - }, - kind: monaco.languages.CompletionItemKind.Function, - insertText: item, - range, - }); - }); - // 内置变量 - sqlCompletionBuiltinVariables.forEach((item: string) => { - suggestions.push({ - label: { - label: item, - description: 'var', - }, - kind: monaco.languages.CompletionItemKind.Variable, - insertText: item, - range, - }); - }); + registerCompletions(keywords, suggestions, monaco.languages.CompletionItemKind.Keyword, range); + registerCompletions(operators, suggestions, monaco.languages.CompletionItemKind.Operator, range); + registerCompletions(functions, suggestions, monaco.languages.CompletionItemKind.Function, range); + registerCompletions(variables, suggestions, monaco.languages.CompletionItemKind.Variable, range); // 默认提示 return { diff --git a/mayfly_go_web/src/views/ops/db/dialect/dm_dialect.ts b/mayfly_go_web/src/views/ops/db/dialect/dm_dialect.ts index a14a944e..0e43c728 100644 --- a/mayfly_go_web/src/views/ops/db/dialect/dm_dialect.ts +++ b/mayfly_go_web/src/views/ops/db/dialect/dm_dialect.ts @@ -1,5 +1,5 @@ -import { DbDialect, sqlColumnType, DialectInfo, RowDefinition, IndexDefinition } from './index'; - +import { DbDialect, sqlColumnType, DialectInfo, RowDefinition, IndexDefinition, EditorCompletionItem, commonCustomKeywords, EditorCompletion } from './index'; +import { language as sqlLanguage } from 'monaco-editor/esm/vs/basic-languages/sql/sql.js'; export { DMDialect, DM_TYPE_LIST }; // 参考文档:https://eco.dameng.com/document/dm/zh-cn/sql-dev/dmpl-sql-datatype.html#%E5%AD%97%E7%AC%A6%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B @@ -42,15 +42,323 @@ const DM_TYPE_LIST: sqlColumnType[] = [ { udtName: 'BFILE', dataType: 'BFILE', desc: '二进制文件', space: '', range: '100G-1' }, ]; -const dmDialectInfo: DialectInfo = { - icon: 'iconfont icon-db-dm', - defaultPort: 5236, - formatSqlDialect: 'postgresql', - columnTypes: DM_TYPE_LIST.sort((a, b) => a.udtName.localeCompare(b.udtName)), -}; +const replaceFunctions: EditorCompletionItem[] = [ + // 数值函数 + { label: 'ABS', insertText: 'ABS(n)', description: '求数值 n 的绝对值' }, + { label: 'ACOS', insertText: 'ACOS(n)', description: '求数值 n 的反余弦值' }, + { label: 'ASIN', insertText: 'ASIN(n)', description: '求数值 n 的反正弦值' }, + { label: 'ATAN', insertText: 'ATAN(n)', description: '求数值 n 的反正切值' }, + { label: 'ATAN2', insertText: 'ATAN2(n1,n2)', description: '求数值 n1/n2 的反正切值' }, + { label: 'CEIL', insertText: 'CEIL(n)', description: '求大于或等于数值 n 的最小整数' }, + { label: 'CEILING', insertText: 'CEILING(n)', description: '求大于或等于数值 n 的最小整数,等价于 CEIL(n)' }, + { label: 'COS', insertText: 'COS(n)', description: '求数值 n 的余弦值' }, + { label: 'COSH', insertText: 'COSH(n)', description: '求数值 n 的双曲余弦值' }, + { label: 'COT', insertText: 'COT(n)', description: '求数值 n 的余切值' }, + { label: 'DEGREES', insertText: 'DEGREES(n)', description: '求弧度 n 对应的角度值' }, + { label: 'EXP', insertText: 'EXP(n)', description: '求数值 n 的自然指数' }, + { label: 'FLOOR', insertText: 'FLOOR(n)', description: '求小于或等于数值 n 的最大整数' }, + { label: 'GREATEST', insertText: 'GREATEST(n, n2)', description: '求一个或多个数中最大的一个' }, + { label: 'GREAT', insertText: 'GREAT(n1, n2)', description: '求 n1、n2 两个数中最大的一个' }, + { label: 'LEAST', insertText: 'LEAST(n, n2)', description: '求一个或多个数中最小的一个' }, + { label: 'LN', insertText: 'LN(n)', description: '求数值 n 的自然对数' }, + { label: 'LOG', insertText: 'LOG(n1,n2)', description: '求数值 n2 以 n1 为底数的对数' }, + { label: 'LOG10', insertText: 'LOG10(n)', description: '求数值 n 以 10 为底的对数' }, + { label: 'MOD', insertText: 'MOD(m,n)', description: '求数值 m 被数值 n 除的余数' }, + { label: 'PI', insertText: 'PI()', description: '得到常数 π' }, + { label: 'POWER', insertText: 'POWER(n1,n2)', description: '求数值 n2 以 n1 为基数的指数' }, + { label: 'RADIANS', insertText: 'RADIANS(n)', description: '求角度 n 对应的弧度值' }, + { label: 'RAND', insertText: 'RAND', description: '求一个 0 到 1 之间的随机浮点数' }, + { label: 'ROUND', insertText: 'ROUND(n [,m]})', description: '返回四舍五入到小数点后面 m 位的 n 值' }, + { label: 'SIGN', insertText: 'SIGN(n)', description: '判断数值的数学符号' }, + { label: 'SIN', insertText: 'SIN(n)', description: '求数值 n 的正弦值' }, + { label: 'SINH', insertText: 'SINH(n)', description: '求数值 n 的双曲正弦值' }, + { label: 'SQRT', insertText: 'SQRT(n)', description: '求数值 n 的平方根' }, + { label: 'TAN', insertText: 'TAN(n)', description: '求数值 n 的正切值' }, + { label: 'TANH', insertText: 'TANH(n)', description: '求数值 n 的双曲正切值' }, + { label: 'TO_NUMBER', insertText: 'TO_NUMBER (char [,fmt])', description: '将 CHAR、VARCHAR、VARCHAR2 等类型的字符串转换为 DECIMAL 类型的数值' }, + { label: 'TRUNC', insertText: 'TRUNC(n [,m])', description: "截取数值函数,str 内只能为数字和'-', '+', '.' 的组合" }, + { label: 'TRUNCATE', insertText: 'TRUNCATE(n [,m])', description: '截取数值函数,等价于 TRUNC 函数' }, + { label: 'TO_CHAR', insertText: 'TO_CHAR(n [, fmt])', description: '将数值类型的数据转换为 VARCHAR 类型输出' }, + { label: 'BITAND', insertText: 'BITAND(n1, n2)', description: '求两个数值型数值按位进行 AND 运算的结果' }, + { label: 'NANVL', insertText: 'NANVL(n1, n2)', description: '有一个参数为空则返回空,否则返回 n1 的值' }, + { label: 'REMAINDER', insertText: 'REMAINDER(n1, n2)', description: '计算 n1 除 n2 的余数,余数取绝对值更小的那一个' }, + { label: 'TO_BINARY_FLOAT', insertText: 'TO_BINARY_FLOAT(n)', description: '将 number、real 或 double 类型数值转换成 binary float 类型' }, + { label: 'TO_BINARY_DOUBLE', insertText: 'TO_BINARY_DOUBLE(n)', description: '将 number、real 或 float 类型数值转换成 binary double 类型' }, + // 字符串函数 + { label: 'ASCII', insertText: 'ASCII(char)', description: '返回字符对应的整数' }, + { label: 'ASCIISTR', insertText: 'ASCIISTR(char)', description: '将字符串 char 中,非 ASCII 的字符转成 \\XXXX(UTF-16)格式,ASCII 字符保持不变' }, + { label: 'BIT_LENGTH', insertText: 'BIT_LENGTH(char)', description: '求字符串的位长度' }, + { label: 'CHAR', insertText: 'CHAR(n)', description: '返回整数 n 对应的字符' }, + { label: 'CHAR_LENGTH', insertText: 'CHAR_LENGTH(char)', description: '求字符串的串长度' }, + { label: 'CHARACTER_LENGTH', insertText: 'CHARACTER_LENGTH(char)', description: '求字符串的串长度' }, + { label: 'CHR', insertText: 'CHR(n)', description: '返回整数 n 对应的字符,等价于 CHAR(n)' }, + { label: 'CONCAT', insertText: 'CONCAT(char1,char2,char3,...)', description: '顺序联结多个字符串成为一个字符串' }, + { + label: 'DIFFERENCE', + insertText: 'DIFFERENCE(char1,char2)', + description: '返回两个值串同一位置出现相同字符的个数。', + }, + { label: 'INITCAP', insertText: 'INITCAP(char)', description: '将字符串中单词的首字符转换成大写的字符' }, + { + label: 'INS', + insertText: 'INS(char1,begin,n,char2)', + description: '删除在字符串 char1 中以 begin 参数所指位置开始的 n 个字符, 再把 char2 插入到 char1 串的 begin 所指位置', + }, + { + label: 'INSERT', + insertText: 'INSERT(char1,n1,n2,char2)', + description: '将字符串 char1 从 n1 的位置开始删除 n2 个字符,并将 char2 插入到 char1 中 n1 的位置', + }, + { + label: 'INSSTR', + insertText: 'INSERT(char1,n1,n2,char2)', + description: '将字符串 char1 从 n1 的位置开始删除 n2 个字符,并将 char2 插入到 char1 中 n1 的位置', + }, + { + label: 'INSTR', + insertText: 'INSTR(char1,char2[,n,[m]])', + description: '从输入字符串char1的第n个字符开始查找字符串 char2 的第 m 次出现的位置,以字符计算', + }, + { label: 'INSTRB', insertText: 'INSTRB(char1,char2[,n,[m]])', description: '从 char1 的第 n 个字节开始查找字符串 char2 的第 m 次出现的位置,以字节计算' }, + { label: 'LCASE', insertText: 'LCASE(char)', description: '将大写的字符串转换为小写的字符串' }, + { label: 'LEFT', insertText: 'LEFT(char,n)', description: '返回字符串最左边的 n 个字符组成的字符串' }, + { label: 'LEFTSTR', insertText: 'LEFTSTR(char,n)', description: '返回字符串最左边的 n 个字符组成的字符串' }, + { label: 'LEN', insertText: 'LEN(char)', description: '返回给定字符串表达式的字符(而不是字节)个数(汉字为一个字符),其中不包含尾随空格' }, + { label: 'LENGTH', insertText: 'LENGTH(clob)', description: '返回给定字符串表达式的字符(而不是字节)个数(汉字为一个字符),其中包含尾随空格' }, + { label: 'OCTET_LENGTH', insertText: 'OCTET_LENGTH(char)', description: '返回输入字符串的字节数' }, + { label: 'LOCATE', insertText: 'LOCATE(char1,char2[,n])', description: '返回 char1 在 char2 中首次出现的位置' }, + { label: 'LOWER', insertText: 'LOWER(char)', description: '将大写的字符串转换为小写的字符串' }, + { label: 'LPAD', insertText: 'LPAD(char1,n,char2)', description: '在输入字符串的左边填充上 char2 指定的字符,将其拉伸至 n 个字节长度' }, + { label: 'LTRIM', insertText: 'LTRIM(str[,set])', description: '删除字符串 str 左边起,出现在 set 中的任何字符,当遇到不在 set 中的第一个字符时返回结果' }, + { label: 'POSITION', insertText: 'POSITION(char1, char2)', description: '求串 1 在串 2 中第一次出现的位置' }, + { label: 'REPEAT', insertText: 'REPEAT(char,n)', description: '返回将字符串重复 n 次形成的字符串' }, + { label: 'REPEATSTR', insertText: 'REPEATSTR(char,n)', description: '返回将字符串重复 n 次形成的字符串' }, + { + label: 'REPLACE', + insertText: 'REPLACE(str, search [,replace])', + description: '将输入字符串 STR 中所有出现的字符串 search 都替换成字符串 replace ,其中 str 为 char、clob 或 text 类型', + }, + { label: 'REPLICATE', insertText: 'REPLICATE(char,times)', description: '把字符串 char 自己复制 times 份' }, + { label: 'REVERSE', insertText: 'REVERSE(char)', description: '将字符串反序' }, + { label: 'RIGHT', insertText: 'RIGHT', description: '返回字符串最右边 n 个字符组成的字符串' }, + { label: 'RIGHTSTR', insertText: 'RIGHTSTR(char,n)', description: '返回字符串最右边 n 个字符组成的字符串' }, + { label: 'RPAD', insertText: 'RPAD(char1,n[,char2])', description: '类似 LPAD 函数,只是向右拉伸该字符串使之达到 n 个字节长度' }, + { label: 'RTRIM', insertText: 'RTRIM(str[,set])', description: '删除字符串str右边起出现的set中的任何字符,当遇到不在 set 中的第一个字符时返回结果' }, + { label: 'SOUNDEX', insertText: 'SOUNDEX(char)', description: '返回一个表示字符串发音的字符串' }, + { label: 'SPACE', insertText: 'SPACE(n)', description: '返回一个包含 n 个空格的字符串' }, + { label: 'STRPOSDEC', insertText: 'STRPOSDEC(char[,pos])', description: '把字符串 char 中指定位置 pos 上的字节值减一' }, + { label: 'STRPOSINC', insertText: 'STRPOSINC(char)', description: '把字符串 char 中最后一个字节的值加一' }, + { label: 'STRPOSINC', insertText: 'STRPOSINC(char,pos)', description: '把字符串 char 中指定位置 pos 上的字节值加一' }, + { + label: 'STUFF', + insertText: 'STUFF(char1,begin,n,char2)', + description: '删除在字符串 char1 中以 begin 参数所指位置开始的 n 个字符, 再把 char2 插入到 char1 串的 begin 所指位置', + }, + { label: 'SUBSTR', insertText: 'SUBSTR(char[,m[,n]])', description: '返回 char 中从字符位置 m 开始的 n 个字符' }, + { label: 'SUBSTRING', insertText: 'SUBSTRING(char [FROM m [FOR n]])', description: '返回 char 中从字符位置 m 开始的 n 个字符' }, + { label: 'SUBSTRB', insertText: 'SUBSTRB(char,m[,n])', description: 'SUBSTR 函数等价的单字节形式' }, + { label: 'TO_CHAR', insertText: 'TO_CHAR(character)', description: '将 VARCHAR、CLOB、TEXT 类型的数据转化为 VARCHAR 类型输出' }, + { label: 'TRANSLATE', insertText: 'TRANSLATE(char,from,to)', description: '将所有出现在搜索字符集中的字符转换成字符集中的相应字符' }, + { label: 'TRIM', insertText: 'TRIM([<[char] | char> FROM] str)', description: '删去字符串 str 中由 char 指定的字符' }, + { label: 'UCASE', insertText: 'UCASE(char)', description: '将小写的字符串转换为大写的字符串' }, + { label: 'UPPER', insertText: 'UPPER(char)', description: '将小写的字符串转换为大写的字符串' }, + { label: 'NLS_UPPER', insertText: 'NLS_UPPER(char)', description: '将小写的字符串转换为大写的字符串' }, + { label: 'REGEXP', insertText: 'REGEXP', description: '根据符合 POSIX 标准的正则表达式进行字符串匹配' }, + { + label: 'OVERLAY', + insertText: 'OVERLAY(char1 PLACING char2 FROM int [FOR int])', + description: '字符串覆盖函数,用 char2 覆盖 char1 中指定的子串,返回修改后的 char1', + }, + { label: 'TEXT_EQUAL', insertText: 'TEXT_EQUAL(n1,n2)', description: '返回两个 LONGVARCHAR 类型的值的比较结果,相同返回 1,否则返回 0' }, + { label: 'BLOB_EQUAL', insertText: 'BLOB_EQUAL(n1,n2)', description: '返回两个 LONGVARBINARY 类型的值的比较结果,相同返回 1,否则返回 0' }, + { label: 'NLSSORT', insertText: 'NLSSORT(str1 [,nls_sort=str2])', description: '返回对自然语言排序的编码' }, + { label: 'GREATEST', insertText: 'GREATEST(char {,char})', description: '求一个或多个字符串中最大的字符串' }, + { label: 'GREAT', insertText: 'GREAT (char1, char2)', description: '求 char 1、char 2 中最大的字符串' }, + { label: 'TO_SINGLE_BYTE', insertText: 'TO_SINGLE_BYTE (char)', description: '将多字节形式的字符(串)转换为对应的单字节形式' }, + { label: 'TO_MULTI_BYTE', insertText: 'TO_MULTI_BYTE (char)', description: '将单字节形式的字符(串)转换为对应的多字节形式' }, + { label: 'EMPTY_CLOB', insertText: 'EMPTY_CLOB ()', description: '初始化 clob 字段' }, + { label: 'EMPTY_BLOB', insertText: 'EMPTY_BLOB ()', description: '初始化 blob 字段' }, + { + label: 'UNISTR', + insertText: 'UNISTR (char)', + description: '将字符串 char 中,ASCII 编码或 Unicode 编码(‗XXXX‘4 个 16 进制字符格式)转成本地字符。对于其他字符保持不变', + }, + { label: 'ISNULL', insertText: 'ISNULL(char)', description: '判断表达式是否为 NULL' }, + { label: 'CONCAT_WS', insertText: 'CONCAT_WS(delim, char1,char2,char3,…)', description: '顺序联结多个字符串成为一个字符串,并用 delim 分割' }, + { label: 'SUBSTRING_INDEX', insertText: 'SUBSTRING_INDEX(char, delim, count)', description: '按关键字截取字符串,截取到指定分隔符出现指定次数位置之前' }, + { label: 'COMPOSE', insertText: 'COMPOSE(varchar str)', description: '在 UTF8 库下,将 str 以本地编码的形式返回' }, + { + label: 'FIND_IN_SET', + insertText: 'FIND_IN_SET(str, strlist[,separator])', + description: '查询 strlist 中是否包含 str,返回 str 在 strlist 中第一次出现的位置或 NULL', + }, + { label: 'TRUNC', insertText: 'TRUNC(str1, str2)', description: '截取字符串函数' }, + //日期时间函数 + { label: 'ADD_DAYS', insertText: 'ADD_DAYS(date,n)', description: '返回日期加上 n 天后的新日期' }, + { label: 'ADD_MONTHS', insertText: 'ADD_MONTHS(date,n)', description: '在输入日期上加上指定的几个月返回一个新日期' }, + { label: 'ADD_WEEKS', insertText: 'ADD_WEEKS(date,n)', description: '返回日期加上 n 个星期后的新日期' }, + { label: 'CURDATE', insertText: 'CURDATE()', description: '返回系统当前日期' }, + { label: 'CURTIME', insertText: 'CURTIME(n)', description: '返回系统当前时间' }, + { label: 'CURRENT_DATE', insertText: 'CURRENT_DATE()', description: '返回系统当前日期' }, + { label: 'CURRENT_TIME', insertText: 'CURRENT_TIME(n)', description: '返回系统当前时间' }, + { label: 'CURRENT_TIMESTAMP', insertText: 'CURRENT_TIMESTAMP(n)', description: '返回系统当前带会话时区信息的时间戳' }, + { label: 'DATEADD', insertText: 'DATEADD(datepart,n,date)', description: '向指定的日期加上一段时间' }, + { label: 'DATEDIFF', insertText: 'DATEDIFF(datepart,date1,date2)', description: '返回跨两个指定日期的日期和时间边界数' }, + { label: 'DATEPART', insertText: 'DATEPART(datepart,date)', description: '返回代表日期的指定部分的整数' }, + { label: 'DAY', insertText: 'DAY(date)', description: '返回日期中的天数' }, + { label: 'DAYNAME', insertText: 'DAYNAME(date)', description: '返回日期的星期名称' }, + { label: 'DAYOFMONTH', insertText: 'DAYOFMONTH(date)', description: '返回日期为所在月份中的第几天' }, + { label: 'DAYOFWEEK', insertText: 'DAYOFWEEK(date)', description: '返回日期为所在星期中的第几天' }, + { label: 'DAYOFYEAR', insertText: 'DAYOFYEAR(date)', description: '返回日期为所在年中的第几天' }, + { label: 'DAYS_BETWEEN', insertText: 'DAYS_BETWEEN(date1,date2)', description: '返回两个日期之间的天数' }, + { label: 'EXTRACT', insertText: 'EXTRACT(时间字段 FROM date)', description: '抽取日期时间或时间间隔类型中某一个字段的值' }, + { label: 'GETDATE', insertText: 'GETDATE(n)', description: '返回系统当前时间戳' }, + { label: 'GREATEST', insertText: 'GREATEST(date {,date})', description: '求一个或多个日期中的最大日期' }, + { label: 'GREAT', insertText: 'GREAT(date1,date2)', description: '求 date1、date2 中的最大日期' }, + { label: 'HOUR', insertText: 'HOUR(time)', description: '返回时间中的小时分量' }, + { label: 'LAST_DAY', insertText: 'LAST_DAY(date)', description: '返回输入日期所在月份最后一天的日期' }, + { label: 'LEAST', insertText: 'LEAST(date {,date})', description: '求一个或多个日期中的最小日期' }, + { label: 'MINUTE', insertText: 'MINUTE(time)', description: '返回时间中的分钟分量' }, + { label: 'MONTH', insertText: 'MONTH(date)', description: '返回日期中的月份分量' }, + { label: 'MONTHNAME', insertText: 'MONTHNAME(date)', description: '返回日期中月分量的名称' }, + { label: 'MONTHS_BETWEEN', insertText: 'MONTHS_BETWEEN(date1,date2)', description: '返回两个日期之间的月份数' }, + { label: 'NEXT_DAY', insertText: 'NEXT_DAY(date1,char2)', description: '返回输入日期指定若干天后的日期' }, + { label: 'NOW', insertText: 'NOW(n)', description: '返回系统当前时间戳' }, + { label: 'QUARTER', insertText: 'QUARTER(date)', description: '返回日期在所处年中的季节数' }, + { label: 'SECOND', insertText: 'SECOND(time)', description: '返回时间中的秒分量' }, + { label: 'ROUND', insertText: 'ROUND(date1[, fmt])', description: '把日期四舍五入到最接近格式元素指定的形式' }, + { label: 'TIMESTAMPADD', insertText: 'TIMESTAMPADD(datepart,n,timestamp)', description: '返回时间戳 timestamp 加上 n 个 datepart 指定的时间段的结果' }, + { + label: 'TIMESTAMPDIFF', + insertText: 'TIMESTAMPDIFF(datepart,timestamp1,timestamp2)', + description: '返回一个表明timestamp2与timestamp1之间的指定 datepart 类型时间间隔的整数', + }, + { label: 'SYSDATE', insertText: 'SYSDATE()', description: '返回系统的当前日期' }, + { label: 'TO_DATE', insertText: "TO_DATE(CHAR[,fmt[,'nls']])", description: '字符串转换为日期时间数据类型' }, + { + label: 'FROM_TZ', + insertText: 'FROM_TZ(timestamp,timezonetz_name])', + description: '将时间戳类型 timestamp 和时区类型 timezone(或时区名称 tz_name ) 转 化 为 timestamp with timezone 类型', + }, + { label: 'TZ_OFFSET', insertText: 'TZ_OFFSET(timezone|[tz_name])', description: '返回给定的时区或时区名和标准时区(UTC)的偏移量' }, + { label: 'TRUNC', insertText: 'TRUNC(date[,fmt])', description: '把日期截断到最接近格式元素指定的形式' }, + { label: 'WEEK', insertText: 'WEEK(date)', description: '返回日期为所在年中的第几周' }, + { label: 'WEEKDAY', insertText: 'WEEKDAY(date)', description: '返回当前日期的星期值' }, + { label: 'WEEKS_BETWEEN', insertText: 'WEEKS_BETWEEN(date1,date2)', description: '返回两个日期之间相差周数' }, + { label: 'YEAR', insertText: 'YEAR(date)', description: '返回日期的年分量' }, + { label: 'YEARS_BETWEEN', insertText: 'YEARS_BETWEEN(date1,date2)', description: '返回两个日期之间相差年数' }, + { label: 'LOCALTIME', insertText: 'LOCALTIME(n)', description: '返回系统当前时间' }, + { label: 'LOCALTIMESTAMP', insertText: 'LOCALTIMESTAMP(n)', description: '返回系统当前时间戳' }, + { label: 'OVERLAPS', insertText: 'OVERLAPS', description: '返回两个时间段是否存在重叠' }, + { + label: 'TO_CHAR', + insertText: 'TO_CHAR(date[,fmt[,nls]])', + description: '将日期数据类型 DATE 转换为一个在日期语法 fmt 中指定语法的 VARCHAR 类型字符串。', + }, + { label: 'SYSTIMESTAMP', insertText: 'SYSTIMESTAMP(n)', description: '返回系统当前带数据库时区信息的时间戳' }, + { label: 'NUMTODSINTERVAL', insertText: 'NUMTODSINTERVAL(dec,interval_unit)', description: '转换一个指定的 DEC 类型到 INTERVAL DAY TO SECOND' }, + { label: 'NUMTOYMINTERVAL', insertText: 'NUMTOYMINTERVAL (dec,interval_unit)', description: '转换一个指定的 DEC 类型值到 INTERVAL YEAR TO MONTH' }, + { label: 'WEEK', insertText: 'WEEK(date, mode)', description: '根据指定的 mode 计算日期为年中的第几周' }, + { + label: 'UNIX_TIMESTAMP', + insertText: 'UNIX_TIMESTAMP (datetime)', + description: "返回自标准时区的'1970-01-01 00:00:00 +0:00'的到本地会话时区的指定时间的秒数差", + }, + { label: 'FROM_UNIXTIME', insertText: 'FROM_UNIXTIME(unixtime)', description: "返回将自'1970-01-01 00:00:00'的秒数差转成本地会话时区的时间戳类型" }, + { + label: 'FROM_UNIXTIME', + insertText: 'FROM_UNIXTIME(unixtime, fmt)', + description: "将自'1970-01-01 00:00:00'的秒数差转成本地会话时区的指定 fmt 格式的时间串", + }, + { label: 'SESSIONTIMEZONE', insertText: 'SESSIONTIMEZONE', description: '返回当前会话的时区' }, + { label: 'DBTIMEZONE', insertText: 'DBTIMEZONE', description: '返回当前数据库的时区' }, + { label: 'DATE_FORMAT', insertText: 'DATE_FORMAT(d, format)', description: '以不同的格式显示日期/时间数据' }, + { label: 'TIME_TO_SEC', insertText: 'TIME_TO_SEC(d)', description: '将时间换算成秒' }, + { label: 'SEC_TO_TIME', insertText: 'SEC_TO_TIME(sec)', description: '将秒换算成时间' }, + { label: 'TO_DAYS', insertText: 'TO_DAYS(timestamp)', description: '转换成公元 0 年 1 月 1 日的天数差' }, + { label: 'DATE_ADD', insertText: 'DATE_ADD(datetime, interval)', description: '返回一个日期或时间值加上一个时间间隔的时间值' }, + { label: 'DATE_SUB', insertText: 'DATE_SUB(datetime, interval)', description: '返回一个日期或时间值减去一个时间间隔的时间值' }, + { label: 'SYS_EXTRACT_UTC', insertText: 'SYS_EXTRACT_UTC(d timestamp)', description: '将所给时区信息转换为 UTC 时区信息' }, + { label: 'TO_DSINTERVAL', insertText: 'TO_DSINTERVAL(d timestamp)', description: '转换一个 timestamp 类型值到 INTERVAL DAY TO SECOND' }, + { label: 'TO_YMINTERVAL', insertText: 'TO_YMINTERVAL(d timestamp)', description: '转换一个 timestamp 类型值到 INTERVAL YEAR TO MONTH' }, + // 空值判断函数 + { label: 'COALESCE', insertText: 'COALESCE(n1,n2,…nx)', description: '返回第一个非空的值' }, + { label: 'IFNULL', insertText: 'IFNULL(n1,n2)', description: '当 n1 为非空时,返回 n1;若 n1 为空,则返回 n2' }, + { label: 'ISNULL', insertText: 'ISNULL(n1,n2)', description: '当 n1 为非空时,返回 n1;若 n1 为空,则返回 n2' }, + { label: 'NULLIF', insertText: 'NULLIF(n1,n2)', description: '如果 n1=n2 返回 NULL,否则返回 n1' }, + { label: 'NVL', insertText: 'NVL(n1,n2)', description: '返回第一个非空的值' }, + { label: 'NULL_EQU', insertText: 'NULL_EQU', description: '返回两个类型相同的值的比较' }, + // 类型转换函数 + { label: 'CAST', insertText: 'CAST(value AS 类型说明)', description: '将 value 转换为指定的类型' }, + { + label: 'CONVERT', + insertText: 'CONVERT(类型说明,value)', + description: + '用于当 INI 参数 ENABLE_CS_CVT=0 时,将 value 转换为指定的类型;用于当 INI 参数 ENABLE_CS_CVT=1 时,将字符串从原串编码格式转换成目的编码格式', + }, + { + label: 'CONVERT', + insertText: 'CONVERT(char, dest_char_set [,source_char_set ] )', + description: + '用于当 INI 参数 ENABLE_CS_CVT=0 时,将 value 转换为指定的类型;用于当 INI 参数 ENABLE_CS_CVT=1 时,将字符串从原串编码格式转换成目的编码格式', + }, + { label: 'HEXTORAW', insertText: 'HEXTORAW(exp)', description: '将 exp 转换为 BLOB 类型' }, + { label: 'RAWTOHEX', insertText: 'RAWTOHEX(exp)', description: '将 exp 转换为 VARCHAR 类型' }, + { label: 'BINTOCHAR', insertText: 'BINTOCHAR(exp)', description: '将 exp 转换为 CHAR' }, + { label: 'TO_BLOB', insertText: 'TO_BLOB(value)', description: '将 value 转换为 blob' }, + { label: 'UNHEX', insertText: 'UNHEX(exp)', description: '将十六进制的 exp 转换为格式字符串' }, + { label: 'HEX', insertText: 'HEX(exp)', description: '将字符串的 exp 转换为十六进制字符串' }, + // 杂类函数 + { label: 'DECODE', insertText: 'DECODE(exp, search1, result1, … searchn, resultn [,default])', description: '查表译码' }, + { label: 'ISDATE', insertText: 'ISDATE(exp)', description: '判断表达式是否为有效的日期' }, + { label: 'ISNUMERIC', insertText: 'ISNUMERIC(exp)', description: '判断表达式是否为有效的数值' }, + { label: 'DM_HASH', insertText: 'DM_HASH(exp)', description: '根据给定表达式生成 HASH 值' }, + { label: 'LNNVL', insertText: 'LNNVL(condition)', description: '根据表达式计算结果返回布尔值' }, + { label: 'LENGTHB', insertText: 'LENGTHB(value)', description: '返回 value 的字节数' }, + { + label: 'FIELD', + insertText: 'FIELD(value, e1, e2, e3, e4...en)', + description: '返回 value 在列表 e1, e2, e3, e4...en 中的位置序号,不在输入列表时则返回 0', + }, + { label: 'ORA_HASH', insertText: 'ORA_HASH(exp [,max_bucket [,seed_value]])', description: '为表达式 exp 生成 HASH 桶值' }, +]; +let dmDialectInfo: DialectInfo; class DMDialect implements DbDialect { - getInfo() { + getInfo(): DialectInfo { + if (dmDialectInfo) { + return dmDialectInfo; + } + + let { keywords, operators, builtinVariables } = sqlLanguage; + let functionNames = replaceFunctions.map((a) => a.label); + let excludeKeywords = new Set(functionNames.concat(operators)); + + let editorCompletions: EditorCompletion = { + keywords: keywords + .filter((a: string) => !excludeKeywords.has(a)) // 移除已存在的operator、function + .map((a: string): EditorCompletionItem => ({ label: a, description: 'keyword' })) + .concat( + // 加上自定义的关键字 + commonCustomKeywords.map( + (a): EditorCompletionItem => ({ + label: a, + description: 'keyword', + }) + ) + ), + operators: operators.map((a: string): EditorCompletionItem => ({ label: a, description: 'operator' })), + functions: replaceFunctions, + variables: builtinVariables.map((a: string): EditorCompletionItem => ({ label: a, description: 'var' })), + }; + + dmDialectInfo = { + icon: 'iconfont icon-db-dm', + defaultPort: 5236, + formatSqlDialect: 'postgresql', + columnTypes: DM_TYPE_LIST.sort((a, b) => a.udtName.localeCompare(b.udtName)), + editorCompletions, + }; return dmDialectInfo; } diff --git a/mayfly_go_web/src/views/ops/db/dialect/index.ts b/mayfly_go_web/src/views/ops/db/dialect/index.ts index 93024fac..f11d140d 100644 --- a/mayfly_go_web/src/views/ops/db/dialect/index.ts +++ b/mayfly_go_web/src/views/ops/db/dialect/index.ts @@ -30,6 +30,27 @@ export interface IndexDefinition { indexType: string; indexComment?: string; } +export const commonCustomKeywords = ['GROUP BY', 'ORDER BY', 'LEFT JOIN', 'RIGHT JOIN', 'INNER JOIN', 'SELECT * FROM']; + +export interface EditorCompletionItem { + /** 用于显示 */ + label: string; + /** 用于插入编辑器,可预置一些变量方便使用函数 */ + insertText?: string; + /** 用于描述 */ + description: string; +} + +export interface EditorCompletion { + /** 关键字 */ + keywords: EditorCompletionItem[]; + /** 操作关键字 */ + operators: EditorCompletionItem[]; + /** 函数,包括内置函数和自定义函数 */ + functions: EditorCompletionItem[]; + /** 内置变量 */ + variables: EditorCompletionItem[]; +} // 数据库基础信息 export interface DialectInfo { @@ -52,6 +73,11 @@ export interface DialectInfo { * 列字段类型 */ columnTypes: sqlColumnType[]; + + /** + * 编辑器一些固定代码提示(关键字、操作符) + */ + editorCompletions: EditorCompletion; } export const DbType = { diff --git a/mayfly_go_web/src/views/ops/db/dialect/mysql_dialect.ts b/mayfly_go_web/src/views/ops/db/dialect/mysql_dialect.ts index 279a2473..c2bf17f9 100644 --- a/mayfly_go_web/src/views/ops/db/dialect/mysql_dialect.ts +++ b/mayfly_go_web/src/views/ops/db/dialect/mysql_dialect.ts @@ -1,6 +1,7 @@ -import { DbDialect, DialectInfo, IndexDefinition, RowDefinition } from './index'; +import { commonCustomKeywords, DbDialect, DialectInfo, EditorCompletion, EditorCompletionItem, IndexDefinition, RowDefinition } from './index'; +import { language as mysqlLanguage } from 'monaco-editor/esm/vs/basic-languages/mysql/mysql.js'; -export { MYSQL_TYPE_LIST, MysqlDialect, language }; +export { MYSQL_TYPE_LIST, MysqlDialect }; const MYSQL_TYPE_LIST = [ 'bigint', @@ -29,15 +30,83 @@ const MYSQL_TYPE_LIST = [ 'varchar', ]; -const mysqlDialectInfo: DialectInfo = { - icon: 'iconfont icon-op-mysql', - defaultPort: 3306, - formatSqlDialect: 'mysql', - columnTypes: MYSQL_TYPE_LIST.map((a) => ({ udtName: a, dataType: a, desc: '', space: '' })), -}; +const replaceFunctions: EditorCompletionItem[] = [ + /** 字符串相关函数 */ + { label: 'CONCAT', insertText: 'CONCAT(str1,str2,...)', description: '多字符串合并' }, + { label: 'ASCII', insertText: 'ASCII(char)', description: '返回字符的ASCII值' }, + { label: 'BIT_LENGTH', insertText: 'BIT_LENGTH(str1)', description: '多字符串合并' }, + { label: 'INSTR', insertText: 'INSTR(str,substr)', description: '返回字符串substr所在str位置' }, + { label: 'LEFT', insertText: 'LEFT(str,len)', description: '返回字符串str的左端len个字符' }, + { label: 'RIGHT', insertText: 'RIGHT(str,len)', description: '返回字符串str的右端len个字符' }, + { label: 'MID', insertText: 'MID(str,pos,len)', description: '返回字符串str的位置pos起len个字符' }, + { label: 'SUBSTRING', insertText: 'SUBSTRING(exp, start, length)', description: '截取字符串' }, + { label: 'REPLACE', insertText: 'REPLACE(str,from_str,to_str)', description: '替换字符串' }, + { label: 'REPEAT', insertText: 'REPEAT(str,count)', description: '重复字符串count遍' }, + { label: 'UPPER', insertText: 'UPPER(str)', description: '返回大写的字符串' }, + { label: 'LOWER', insertText: 'LOWER(str)', description: '返回小写的字符串' }, + { label: 'TRIM', insertText: 'TRIM(str)', description: '去除字符串首尾空格' }, + /** 数学相关函数 */ + { label: 'ABS', insertText: 'ABS(n)', description: '返回n的绝对值' }, + { label: 'FLOOR', insertText: 'FLOOR(n)', description: '返回不大于n的最大整数' }, + { label: 'CEILING', insertText: 'CEILING(n)', description: '返回不小于n的最小整数值' }, + { label: 'ROUND', insertText: 'ROUND(n,d)', description: '返回n的四舍五入值,保留d(默认0)位小数' }, + { label: 'RAND', insertText: 'RAND()', description: '返回在范围0到1.0内的随机浮点值' }, + + /** 日期函数 */ + { label: 'DATE', insertText: "DATE('date')", description: '返回指定表达式的日期部分' }, + { label: 'WEEK', insertText: "WEEK('date')", description: '返回指定日期是一年中的第几周' }, + { label: 'MONTH', insertText: "MONTH('date')", description: '返回指定日期的月份' }, + { label: 'QUARTER', insertText: "QUARTER('date')", description: '返回指定日期是一年的第几个季度' }, + { label: 'YEAR', insertText: "YEAR('date')", description: '返回指定日期的年份' }, + { label: 'DATE_ADD', insertText: "DATE_ADD('date', interval 1 day)", description: '日期函数加减运算' }, + { label: 'DATE_SUB', insertText: "DATE_SUB('date', interval 1 day)", description: '日期函数加减运算' }, + { label: 'DATE_FORMAT', insertText: "DATE_FORMAT('date', '%Y-%m-%d %h:%i:%s')", description: '' }, + { label: 'CURDATE', insertText: 'CURDATE()', description: '返回当前日期' }, + { label: 'CURTIME', insertText: 'CURTIME()', description: '返回当前时间' }, + { label: 'NOW', insertText: 'NOW()', description: '返回当前日期时间' }, + { label: 'DATEDIFF', insertText: 'DATEDIFF(expr1,expr2)', description: '返回结束日expr1和起始日expr2之间的天数' }, + { label: 'UNIX_TIMESTAMP', insertText: 'UNIX_TIMESTAMP()', description: '返回指定时间(默认当前)unix时间戳' }, + { label: 'FROM_UNIXTIME', insertText: 'FROM_UNIXTIME(timestamp)', description: '把时间戳格式为年月日时分秒' }, + + /** 逻辑函数 */ + { label: 'IFNULL', insertText: 'IFNULL(expression, alt_value)', description: '表达式为空取第二个参数值,否则取表达式值' }, + { label: 'IF', insertText: 'IF(expr1, expr2, expr3)', description: 'expr1为true则取expr2,否则取expr3' }, + { label: 'CASE', insertText: '\n(\n CASE\n WHEN expr1 THEN expr2\n ELSE expr3\n END\n ) col', description: 'CASE WHEN THEN ELSE END' }, +]; + +let mysqlDialectInfo: DialectInfo; class MysqlDialect implements DbDialect { getInfo(): DialectInfo { + if (mysqlDialectInfo) { + return mysqlDialectInfo; + } + + let { keywords, operators, builtinVariables, builtinFunctions } = mysqlLanguage; + let replaceFunctionNames = replaceFunctions.map((a) => a.label); + let functions = builtinFunctions + .filter((a: string) => replaceFunctionNames.indexOf(a) < 0) // 删除重写的函数 + .map((a: string): EditorCompletionItem => ({ label: a, insertText: `${a}()`, description: 'func' })) + .concat(replaceFunctions); + + let excludeKeywords = new Set(builtinFunctions.concat(replaceFunctionNames).concat(operators)); + let editorCompletions: EditorCompletion = { + keywords: keywords + .filter((a: string) => !excludeKeywords.has(a)) // 移除已存在的operator、function + .map((a: string): EditorCompletionItem => ({ label: a, description: 'keyword' })) + .concat(commonCustomKeywords.map((a): EditorCompletionItem => ({ label: a, description: 'keyword' }))), + operators: operators.map((a: string): EditorCompletionItem => ({ label: a, description: 'operator' })), + functions, + variables: builtinVariables.map((a: string): EditorCompletionItem => ({ label: a, description: 'var' })), + }; + + mysqlDialectInfo = { + icon: 'iconfont icon-op-mysql', + defaultPort: 3306, + formatSqlDialect: 'mysql', + columnTypes: MYSQL_TYPE_LIST.map((a) => ({ udtName: a, dataType: a, desc: '', space: '' })), + editorCompletions, + }; return mysqlDialectInfo; } @@ -235,56 +304,3 @@ class MysqlDialect implements DbDialect { return ''; } } - -// src/basic-languages/mysql/mysql.ts -var language = { - keywords: ['GROUP BY', 'ORDER BY', 'LEFT JOIN', 'RIGHT JOIN', 'INNER JOIN', 'SELECT * FROM'], - operators: [], - builtinFunctions: [], - builtinVariables: [], - replaceFunctions: [ - // 自定义修改函数提示 - - /** 字符串相关函数 */ - { label: 'CONCAT', insertText: 'CONCAT(str1,str2,...)', description: '多字符串合并' }, - { label: 'ASCII', insertText: 'ASCII(char)', description: '返回字符的ASCII值' }, - { label: 'BIT_LENGTH', insertText: 'BIT_LENGTH(str1)', description: '多字符串合并' }, - { label: 'INSTR', insertText: 'INSTR(str,substr)', description: '返回字符串substr所在str位置' }, - { label: 'LEFT', insertText: 'LEFT(str,len)', description: '返回字符串str的左端len个字符' }, - { label: 'RIGHT', insertText: 'RIGHT(str,len)', description: '返回字符串str的右端len个字符' }, - { label: 'MID', insertText: 'MID(str,pos,len)', description: '返回字符串str的位置pos起len个字符' }, - { label: 'SUBSTRING', insertText: 'SUBSTRING(exp, start, length)', description: '截取字符串' }, - { label: 'REPLACE', insertText: 'REPLACE(str,from_str,to_str)', description: '替换字符串' }, - { label: 'REPEAT', insertText: 'REPEAT(str,count)', description: '重复字符串count遍' }, - { label: 'UPPER', insertText: 'UPPER(str)', description: '返回大写的字符串' }, - { label: 'LOWER', insertText: 'LOWER(str)', description: '返回小写的字符串' }, - { label: 'TRIM', insertText: 'TRIM(str)', description: '去除字符串首尾空格' }, - /** 数学相关函数 */ - { label: 'ABS', insertText: 'ABS(n)', description: '返回n的绝对值' }, - { label: 'FLOOR', insertText: 'FLOOR(n)', description: '返回不大于n的最大整数' }, - { label: 'CEILING', insertText: 'CEILING(n)', description: '返回不小于n的最小整数值' }, - { label: 'ROUND', insertText: 'ROUND(n,d)', description: '返回n的四舍五入值,保留d(默认0)位小数' }, - { label: 'RAND', insertText: 'RAND()', description: '返回在范围0到1.0内的随机浮点值' }, - - /** 日期函数 */ - { label: 'DATE', insertText: "DATE('date')", description: '返回指定表达式的日期部分' }, - { label: 'WEEK', insertText: "WEEK('date')", description: '返回指定日期是一年中的第几周' }, - { label: 'MONTH', insertText: "MONTH('date')", description: '返回指定日期的月份' }, - { label: 'QUARTER', insertText: "QUARTER('date')", description: '返回指定日期是一年的第几个季度' }, - { label: 'YEAR', insertText: "YEAR('date')", description: '返回指定日期的年份' }, - { label: 'DATE_ADD', insertText: "DATE_ADD('date', interval 1 day)", description: '日期函数加减运算' }, - { label: 'DATE_SUB', insertText: "DATE_SUB('date', interval 1 day)", description: '日期函数加减运算' }, - { label: 'DATE_FORMAT', insertText: "DATE_FORMAT('date', '%Y-%m-%d %h:%i:%s')", description: '' }, - { label: 'CURDATE', insertText: 'CURDATE()', description: '返回当前日期' }, - { label: 'CURTIME', insertText: 'CURTIME()', description: '返回当前时间' }, - { label: 'NOW', insertText: 'NOW()', description: '返回当前日期时间' }, - { label: 'DATEDIFF', insertText: 'DATEDIFF(expr1,expr2)', description: '返回结束日expr1和起始日expr2之间的天数' }, - { label: 'UNIX_TIMESTAMP', insertText: 'UNIX_TIMESTAMP()', description: '返回指定时间(默认当前)unix时间戳' }, - { label: 'FROM_UNIXTIME', insertText: 'FROM_UNIXTIME(timestamp)', description: '把时间戳格式为年月日时分秒' }, - - /** 逻辑函数 */ - { label: 'IFNULL', insertText: 'IFNULL(expression, alt_value)', description: '表达式为空取第二个参数值,否则取表达式值' }, - { label: 'IF', insertText: 'IF(expr1, expr2, expr3)', description: 'expr1为true则取expr2,否则取expr3' }, - { label: 'CASE', insertText: '(CASE \n WHEN expr1 THEN expr2 \n ELSE expr3) col', description: 'CASE WHEN THEN ELSE' }, - ], -}; diff --git a/mayfly_go_web/src/views/ops/db/dialect/postgres_dialect.ts b/mayfly_go_web/src/views/ops/db/dialect/postgres_dialect.ts index 93301bee..7bb0ad43 100644 --- a/mayfly_go_web/src/views/ops/db/dialect/postgres_dialect.ts +++ b/mayfly_go_web/src/views/ops/db/dialect/postgres_dialect.ts @@ -1,4 +1,5 @@ -import { DbDialect, DialectInfo, IndexDefinition, RowDefinition, sqlColumnType } from './index'; +import { commonCustomKeywords, DbDialect, DialectInfo, EditorCompletion, EditorCompletionItem, IndexDefinition, RowDefinition, sqlColumnType } from './index'; +import { language as pgsqlLanguage } from 'monaco-editor/esm/vs/basic-languages/pgsql/pgsql.js'; export { PostgresqlDialect, GAUSS_TYPE_LIST }; @@ -82,16 +83,42 @@ const GAUSS_TYPE_LIST: sqlColumnType[] = [ { udtName: 'macaddr', dataType: 'macaddr', desc: 'MAC地址', space: '6字节' }, ]; -const postgresDialectInfo: DialectInfo = { - icon: 'iconfont icon-op-postgres', - defaultPort: 5432, - formatSqlDialect: 'postgresql', - columnTypes: GAUSS_TYPE_LIST.sort((a, b) => a.udtName.localeCompare(b.udtName)), -}; +const replaceFunctions: EditorCompletionItem[] = []; + +let pgDialectInfo: DialectInfo; class PostgresqlDialect implements DbDialect { getInfo(): DialectInfo { - return postgresDialectInfo; + if (pgDialectInfo) { + return pgDialectInfo; + } + + let { keywords, operators, builtinVariables, builtinFunctions } = pgsqlLanguage; + let replaceFunctionNames = replaceFunctions.map((a) => a.label); + let functions = builtinFunctions + .filter((a: string) => replaceFunctionNames.indexOf(a) < 0) + .map((a: string): EditorCompletionItem => ({ label: a, insertText: `${a}()`, description: 'func' })) + .concat(replaceFunctions); + let excludeKeywords = new Set(builtinFunctions.concat(replaceFunctionNames).concat(operators)); + + let editorCompletions: EditorCompletion = { + keywords: keywords + .filter((a: string) => !excludeKeywords.has(a)) // 移除已存在的operator、function + .map((a: string): EditorCompletionItem => ({ label: a, description: 'keyword' })) + .concat(commonCustomKeywords.map((a): EditorCompletionItem => ({ label: a, description: 'keyword' }))), + operators: operators.map((a: string): EditorCompletionItem => ({ label: a, description: 'operator' })), + functions, + variables: builtinVariables.map((a: string): EditorCompletionItem => ({ label: a, description: 'var' })), + }; + + pgDialectInfo = { + icon: 'iconfont icon-op-postgres', + defaultPort: 5432, + formatSqlDialect: 'postgresql', + columnTypes: GAUSS_TYPE_LIST.sort((a, b) => a.udtName.localeCompare(b.udtName)), + editorCompletions, + }; + return pgDialectInfo; } getDefaultSelectSql(table: string, condition: string, orderBy: string, pageNum: number, limit: number) {