forked from zhurui/management
700 lines
12 KiB
JavaScript
700 lines
12 KiB
JavaScript
/**
|
|
* @param {string} value
|
|
* @returns {RegExp}
|
|
* */
|
|
|
|
/**
|
|
* @param {RegExp | string } re
|
|
* @returns {string}
|
|
*/
|
|
function source(re) {
|
|
if (!re) return null;
|
|
if (typeof re === "string") return re;
|
|
|
|
return re.source;
|
|
}
|
|
|
|
/**
|
|
* @param {...(RegExp | string) } args
|
|
* @returns {string}
|
|
*/
|
|
function concat(...args) {
|
|
const joined = args.map((x) => source(x)).join("");
|
|
return joined;
|
|
}
|
|
|
|
/**
|
|
* Any of the passed expresssions may match
|
|
*
|
|
* Creates a huge this | this | that | that match
|
|
* @param {(RegExp | string)[] } args
|
|
* @returns {string}
|
|
*/
|
|
function either(...args) {
|
|
const joined = '(' + args.map((x) => source(x)).join("|") + ")";
|
|
return joined;
|
|
}
|
|
|
|
/*
|
|
Language: SQL
|
|
Website: https://en.wikipedia.org/wiki/SQL
|
|
Category: common, database
|
|
*/
|
|
|
|
function sql(hljs) {
|
|
const COMMENT_MODE = hljs.COMMENT('--', '$');
|
|
const STRING = {
|
|
className: 'string',
|
|
variants: [
|
|
{
|
|
begin: /'/,
|
|
end: /'/,
|
|
contains: [
|
|
{begin: /''/ }
|
|
]
|
|
}
|
|
]
|
|
};
|
|
const QUOTED_IDENTIFIER = {
|
|
begin: /"/,
|
|
end: /"/,
|
|
contains: [ { begin: /""/ } ]
|
|
};
|
|
|
|
const LITERALS = [
|
|
"true",
|
|
"false",
|
|
// Not sure it's correct to call NULL literal, and clauses like IS [NOT] NULL look strange that way.
|
|
// "null",
|
|
"unknown"
|
|
];
|
|
|
|
const MULTI_WORD_TYPES = [
|
|
"double precision",
|
|
"large object",
|
|
"with timezone",
|
|
"without timezone"
|
|
];
|
|
|
|
const TYPES = [
|
|
'bigint',
|
|
'binary',
|
|
'blob',
|
|
'boolean',
|
|
'char',
|
|
'character',
|
|
'clob',
|
|
'date',
|
|
'dec',
|
|
'decfloat',
|
|
'decimal',
|
|
'float',
|
|
'int',
|
|
'integer',
|
|
'interval',
|
|
'nchar',
|
|
'nclob',
|
|
'national',
|
|
'numeric',
|
|
'real',
|
|
'row',
|
|
'smallint',
|
|
'time',
|
|
'timestamp',
|
|
'varchar',
|
|
'varying', // modifier (character varying)
|
|
'varbinary'
|
|
];
|
|
|
|
const NON_RESERVED_WORDS = [
|
|
"add",
|
|
"asc",
|
|
"collation",
|
|
"desc",
|
|
"final",
|
|
"first",
|
|
"last",
|
|
"view"
|
|
];
|
|
|
|
// https://jakewheat.github.io/sql-overview/sql-2016-foundation-grammar.html#reserved-word
|
|
const RESERVED_WORDS = [
|
|
"abs",
|
|
"acos",
|
|
"all",
|
|
"allocate",
|
|
"alter",
|
|
"and",
|
|
"any",
|
|
"are",
|
|
"array",
|
|
"array_agg",
|
|
"array_max_cardinality",
|
|
"as",
|
|
"asensitive",
|
|
"asin",
|
|
"asymmetric",
|
|
"at",
|
|
"atan",
|
|
"atomic",
|
|
"authorization",
|
|
"avg",
|
|
"begin",
|
|
"begin_frame",
|
|
"begin_partition",
|
|
"between",
|
|
"bigint",
|
|
"binary",
|
|
"blob",
|
|
"boolean",
|
|
"both",
|
|
"by",
|
|
"call",
|
|
"called",
|
|
"cardinality",
|
|
"cascaded",
|
|
"case",
|
|
"cast",
|
|
"ceil",
|
|
"ceiling",
|
|
"char",
|
|
"char_length",
|
|
"character",
|
|
"character_length",
|
|
"check",
|
|
"classifier",
|
|
"clob",
|
|
"close",
|
|
"coalesce",
|
|
"collate",
|
|
"collect",
|
|
"column",
|
|
"commit",
|
|
"condition",
|
|
"connect",
|
|
"constraint",
|
|
"contains",
|
|
"convert",
|
|
"copy",
|
|
"corr",
|
|
"corresponding",
|
|
"cos",
|
|
"cosh",
|
|
"count",
|
|
"covar_pop",
|
|
"covar_samp",
|
|
"create",
|
|
"cross",
|
|
"cube",
|
|
"cume_dist",
|
|
"current",
|
|
"current_catalog",
|
|
"current_date",
|
|
"current_default_transform_group",
|
|
"current_path",
|
|
"current_role",
|
|
"current_row",
|
|
"current_schema",
|
|
"current_time",
|
|
"current_timestamp",
|
|
"current_path",
|
|
"current_role",
|
|
"current_transform_group_for_type",
|
|
"current_user",
|
|
"cursor",
|
|
"cycle",
|
|
"date",
|
|
"day",
|
|
"deallocate",
|
|
"dec",
|
|
"decimal",
|
|
"decfloat",
|
|
"declare",
|
|
"default",
|
|
"define",
|
|
"delete",
|
|
"dense_rank",
|
|
"deref",
|
|
"describe",
|
|
"deterministic",
|
|
"disconnect",
|
|
"distinct",
|
|
"double",
|
|
"drop",
|
|
"dynamic",
|
|
"each",
|
|
"element",
|
|
"else",
|
|
"empty",
|
|
"end",
|
|
"end_frame",
|
|
"end_partition",
|
|
"end-exec",
|
|
"equals",
|
|
"escape",
|
|
"every",
|
|
"except",
|
|
"exec",
|
|
"execute",
|
|
"exists",
|
|
"exp",
|
|
"external",
|
|
"extract",
|
|
"false",
|
|
"fetch",
|
|
"filter",
|
|
"first_value",
|
|
"float",
|
|
"floor",
|
|
"for",
|
|
"foreign",
|
|
"frame_row",
|
|
"free",
|
|
"from",
|
|
"full",
|
|
"function",
|
|
"fusion",
|
|
"get",
|
|
"global",
|
|
"grant",
|
|
"group",
|
|
"grouping",
|
|
"groups",
|
|
"having",
|
|
"hold",
|
|
"hour",
|
|
"identity",
|
|
"in",
|
|
"indicator",
|
|
"initial",
|
|
"inner",
|
|
"inout",
|
|
"insensitive",
|
|
"insert",
|
|
"int",
|
|
"integer",
|
|
"intersect",
|
|
"intersection",
|
|
"interval",
|
|
"into",
|
|
"is",
|
|
"join",
|
|
"json_array",
|
|
"json_arrayagg",
|
|
"json_exists",
|
|
"json_object",
|
|
"json_objectagg",
|
|
"json_query",
|
|
"json_table",
|
|
"json_table_primitive",
|
|
"json_value",
|
|
"lag",
|
|
"language",
|
|
"large",
|
|
"last_value",
|
|
"lateral",
|
|
"lead",
|
|
"leading",
|
|
"left",
|
|
"like",
|
|
"like_regex",
|
|
"listagg",
|
|
"ln",
|
|
"local",
|
|
"localtime",
|
|
"localtimestamp",
|
|
"log",
|
|
"log10",
|
|
"lower",
|
|
"match",
|
|
"match_number",
|
|
"match_recognize",
|
|
"matches",
|
|
"max",
|
|
"member",
|
|
"merge",
|
|
"method",
|
|
"min",
|
|
"minute",
|
|
"mod",
|
|
"modifies",
|
|
"module",
|
|
"month",
|
|
"multiset",
|
|
"national",
|
|
"natural",
|
|
"nchar",
|
|
"nclob",
|
|
"new",
|
|
"no",
|
|
"none",
|
|
"normalize",
|
|
"not",
|
|
"nth_value",
|
|
"ntile",
|
|
"null",
|
|
"nullif",
|
|
"numeric",
|
|
"octet_length",
|
|
"occurrences_regex",
|
|
"of",
|
|
"offset",
|
|
"old",
|
|
"omit",
|
|
"on",
|
|
"one",
|
|
"only",
|
|
"open",
|
|
"or",
|
|
"order",
|
|
"out",
|
|
"outer",
|
|
"over",
|
|
"overlaps",
|
|
"overlay",
|
|
"parameter",
|
|
"partition",
|
|
"pattern",
|
|
"per",
|
|
"percent",
|
|
"percent_rank",
|
|
"percentile_cont",
|
|
"percentile_disc",
|
|
"period",
|
|
"portion",
|
|
"position",
|
|
"position_regex",
|
|
"power",
|
|
"precedes",
|
|
"precision",
|
|
"prepare",
|
|
"primary",
|
|
"procedure",
|
|
"ptf",
|
|
"range",
|
|
"rank",
|
|
"reads",
|
|
"real",
|
|
"recursive",
|
|
"ref",
|
|
"references",
|
|
"referencing",
|
|
"regr_avgx",
|
|
"regr_avgy",
|
|
"regr_count",
|
|
"regr_intercept",
|
|
"regr_r2",
|
|
"regr_slope",
|
|
"regr_sxx",
|
|
"regr_sxy",
|
|
"regr_syy",
|
|
"release",
|
|
"result",
|
|
"return",
|
|
"returns",
|
|
"revoke",
|
|
"right",
|
|
"rollback",
|
|
"rollup",
|
|
"row",
|
|
"row_number",
|
|
"rows",
|
|
"running",
|
|
"savepoint",
|
|
"scope",
|
|
"scroll",
|
|
"search",
|
|
"second",
|
|
"seek",
|
|
"select",
|
|
"sensitive",
|
|
"session_user",
|
|
"set",
|
|
"show",
|
|
"similar",
|
|
"sin",
|
|
"sinh",
|
|
"skip",
|
|
"smallint",
|
|
"some",
|
|
"specific",
|
|
"specifictype",
|
|
"sql",
|
|
"sqlexception",
|
|
"sqlstate",
|
|
"sqlwarning",
|
|
"sqrt",
|
|
"start",
|
|
"static",
|
|
"stddev_pop",
|
|
"stddev_samp",
|
|
"submultiset",
|
|
"subset",
|
|
"substring",
|
|
"substring_regex",
|
|
"succeeds",
|
|
"sum",
|
|
"symmetric",
|
|
"system",
|
|
"system_time",
|
|
"system_user",
|
|
"table",
|
|
"tablesample",
|
|
"tan",
|
|
"tanh",
|
|
"then",
|
|
"time",
|
|
"timestamp",
|
|
"timezone_hour",
|
|
"timezone_minute",
|
|
"to",
|
|
"trailing",
|
|
"translate",
|
|
"translate_regex",
|
|
"translation",
|
|
"treat",
|
|
"trigger",
|
|
"trim",
|
|
"trim_array",
|
|
"true",
|
|
"truncate",
|
|
"uescape",
|
|
"union",
|
|
"unique",
|
|
"unknown",
|
|
"unnest",
|
|
"update ",
|
|
"upper",
|
|
"user",
|
|
"using",
|
|
"value",
|
|
"values",
|
|
"value_of",
|
|
"var_pop",
|
|
"var_samp",
|
|
"varbinary",
|
|
"varchar",
|
|
"varying",
|
|
"versioning",
|
|
"when",
|
|
"whenever",
|
|
"where",
|
|
"width_bucket",
|
|
"window",
|
|
"with",
|
|
"within",
|
|
"without",
|
|
"year",
|
|
];
|
|
|
|
// these are reserved words we have identified to be functions
|
|
// and should only be highlighted in a dispatch-like context
|
|
// ie, array_agg(...), etc.
|
|
const RESERVED_FUNCTIONS = [
|
|
"abs",
|
|
"acos",
|
|
"array_agg",
|
|
"asin",
|
|
"atan",
|
|
"avg",
|
|
"cast",
|
|
"ceil",
|
|
"ceiling",
|
|
"coalesce",
|
|
"corr",
|
|
"cos",
|
|
"cosh",
|
|
"count",
|
|
"covar_pop",
|
|
"covar_samp",
|
|
"cume_dist",
|
|
"dense_rank",
|
|
"deref",
|
|
"element",
|
|
"exp",
|
|
"extract",
|
|
"first_value",
|
|
"floor",
|
|
"json_array",
|
|
"json_arrayagg",
|
|
"json_exists",
|
|
"json_object",
|
|
"json_objectagg",
|
|
"json_query",
|
|
"json_table",
|
|
"json_table_primitive",
|
|
"json_value",
|
|
"lag",
|
|
"last_value",
|
|
"lead",
|
|
"listagg",
|
|
"ln",
|
|
"log",
|
|
"log10",
|
|
"lower",
|
|
"max",
|
|
"min",
|
|
"mod",
|
|
"nth_value",
|
|
"ntile",
|
|
"nullif",
|
|
"percent_rank",
|
|
"percentile_cont",
|
|
"percentile_disc",
|
|
"position",
|
|
"position_regex",
|
|
"power",
|
|
"rank",
|
|
"regr_avgx",
|
|
"regr_avgy",
|
|
"regr_count",
|
|
"regr_intercept",
|
|
"regr_r2",
|
|
"regr_slope",
|
|
"regr_sxx",
|
|
"regr_sxy",
|
|
"regr_syy",
|
|
"row_number",
|
|
"sin",
|
|
"sinh",
|
|
"sqrt",
|
|
"stddev_pop",
|
|
"stddev_samp",
|
|
"substring",
|
|
"substring_regex",
|
|
"sum",
|
|
"tan",
|
|
"tanh",
|
|
"translate",
|
|
"translate_regex",
|
|
"treat",
|
|
"trim",
|
|
"trim_array",
|
|
"unnest",
|
|
"upper",
|
|
"value_of",
|
|
"var_pop",
|
|
"var_samp",
|
|
"width_bucket",
|
|
];
|
|
|
|
// these functions can
|
|
const POSSIBLE_WITHOUT_PARENS = [
|
|
"current_catalog",
|
|
"current_date",
|
|
"current_default_transform_group",
|
|
"current_path",
|
|
"current_role",
|
|
"current_schema",
|
|
"current_transform_group_for_type",
|
|
"current_user",
|
|
"session_user",
|
|
"system_time",
|
|
"system_user",
|
|
"current_time",
|
|
"localtime",
|
|
"current_timestamp",
|
|
"localtimestamp"
|
|
];
|
|
|
|
// those exist to boost relevance making these very
|
|
// "SQL like" keyword combos worth +1 extra relevance
|
|
const COMBOS = [
|
|
"create table",
|
|
"insert into",
|
|
"primary key",
|
|
"foreign key",
|
|
"not null",
|
|
"alter table",
|
|
"add constraint",
|
|
"grouping sets",
|
|
"on overflow",
|
|
"character set",
|
|
"respect nulls",
|
|
"ignore nulls",
|
|
"nulls first",
|
|
"nulls last",
|
|
"depth first",
|
|
"breadth first"
|
|
];
|
|
|
|
const FUNCTIONS = RESERVED_FUNCTIONS;
|
|
|
|
const KEYWORDS = [...RESERVED_WORDS, ...NON_RESERVED_WORDS].filter((keyword) => {
|
|
return !RESERVED_FUNCTIONS.includes(keyword);
|
|
});
|
|
|
|
const VARIABLE = {
|
|
className: "variable",
|
|
begin: /@[a-z0-9]+/,
|
|
};
|
|
|
|
const OPERATOR = {
|
|
className: "operator",
|
|
begin: /[-+*/=%^~]|&&?|\|\|?|!=?|<(?:=>?|<|>)?|>[>=]?/,
|
|
relevance: 0,
|
|
};
|
|
|
|
const FUNCTION_CALL = {
|
|
begin: concat(/\b/, either(...FUNCTIONS), /\s*\(/),
|
|
keywords: {
|
|
built_in: FUNCTIONS
|
|
}
|
|
};
|
|
|
|
// keywords with less than 3 letters are reduced in relevancy
|
|
function reduceRelevancy(list, {exceptions, when} = {}) {
|
|
const qualifyFn = when;
|
|
exceptions = exceptions || [];
|
|
return list.map((item) => {
|
|
if (item.match(/\|\d+$/) || exceptions.includes(item)) {
|
|
return item;
|
|
} else if (qualifyFn(item)) {
|
|
return `${item}|0`;
|
|
} else {
|
|
return item;
|
|
}
|
|
});
|
|
}
|
|
|
|
return {
|
|
name: 'SQL',
|
|
case_insensitive: true,
|
|
// does not include {} or HTML tags `</`
|
|
illegal: /[{}]|<\//,
|
|
keywords: {
|
|
$pattern: /\b[\w\.]+/,
|
|
keyword:
|
|
reduceRelevancy(KEYWORDS, { when: (x) => x.length < 3 }),
|
|
literal: LITERALS,
|
|
type: TYPES,
|
|
built_in: POSSIBLE_WITHOUT_PARENS
|
|
},
|
|
contains: [
|
|
{
|
|
begin: either(...COMBOS),
|
|
keywords: {
|
|
$pattern: /[\w\.]+/,
|
|
keyword: KEYWORDS.concat(COMBOS),
|
|
literal: LITERALS,
|
|
type: TYPES
|
|
},
|
|
},
|
|
{
|
|
className: "type",
|
|
begin: either(...MULTI_WORD_TYPES)
|
|
},
|
|
FUNCTION_CALL,
|
|
VARIABLE,
|
|
STRING,
|
|
QUOTED_IDENTIFIER,
|
|
hljs.C_NUMBER_MODE,
|
|
hljs.C_BLOCK_COMMENT_MODE,
|
|
COMMENT_MODE,
|
|
OPERATOR
|
|
]
|
|
};
|
|
}
|
|
|
|
module.exports = sql;
|