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;
 |