Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions mode/sql/sql.js
Original file line number Diff line number Diff line change
Expand Up @@ -315,8 +315,8 @@ CodeMirror.defineMode("sql", function(config, parserConfig) {
CodeMirror.defineMIME("text/x-mysql", {
name: "sql",
client: set("charset clear connect edit ego exit go help nopager notee nowarning pager print prompt quit rehash source status system tee"),
keywords: set(sqlKeywords + "accessible action add after algorithm all analyze asensitive at authors auto_increment autocommit avg avg_row_length before binary binlog both btree cache call cascade cascaded case catalog_name chain change changed character check checkpoint checksum class_origin client_statistics close coalesce code collate collation collations column columns comment commit committed completion concurrent condition connection consistent constraint contains continue contributors convert cross current current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec declare default delay_key_write delayed delimiter des_key_file describe deterministic dev_pop dev_samp deviance diagnostics directory disable discard distinctrow div dual dumpfile each elseif enable enclosed end ends engine engines enum errors escape escaped even event events every execute exists exit explain extended fast fetch field fields first flush for force foreign found_rows full fulltext function general get global grant grants group group_concat handler hash help high_priority hosts hour_microsecond hour_minute hour_second if ignore ignore_server_ids import index index_statistics infile inner innodb inout insensitive insert_method install interval invoker isolation iterate key keys kill language last leading leave left level limit linear lines list load local localtime localtimestamp lock logs low_priority master master_heartbeat_period master_ssl_verify_server_cert masters match max max_rows maxvalue message_text middleint migrate min min_rows minute_microsecond minute_second mod mode modifies modify mutex mysql_errno natural next no no_write_to_binlog offline offset one online open optimize option optionally out outer outfile pack_keys parser partition partitions password phase plugin plugins prepare preserve prev primary privileges procedure processlist profile profiles purge query quick range read read_write reads real rebuild recover references regexp relaylog release remove rename reorganize repair repeatable replace require resignal restrict resume return returns revoke right rlike rollback rollup row row_format rtree savepoint schedule schema schema_name schemas second_microsecond security sensitive separator serializable server session share show signal slave slow smallint snapshot soname spatial specific sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting starts status std stddev stddev_pop stddev_samp storage straight_join subclass_origin sum suspend table_name table_statistics tables tablespace temporary terminated to trailing transaction trigger triggers truncate uncommitted undo uninstall unique unlock upgrade usage use use_frm user user_resources user_statistics using utc_date utc_time utc_timestamp value variables varying view views warnings when while with work write xa xor year_month zerofill begin do then else loop repeat"),
builtin: set("bool boolean bit blob decimal double float long longblob longtext medium mediumblob mediumint mediumtext time timestamp tinyblob tinyint tinytext text bigint int int1 int2 int3 int4 int8 integer float float4 float8 double char varbinary varchar varcharacter precision date datetime year unsigned signed numeric"),
keywords: set(sqlKeywords + "accessible account action add admin after algorithm all alter analyze and any as asc ascii asensitive at attribute auto_increment avg avg_row_length before begin between bigint binary bit blob both by cache call cascade cascaded case cast catalog_name chain change character check checksum collate collation column columns comment commit committed condition constraint contains continue convert create cross cume_dist current current_date current_time current_timestamp current_user cursor data database databases day_hour day_microsecond day_minute day_second deallocate dec decimal declare default definer delay_key_write delayed delete dense_rank desc describe deterministic distinct distinctrow div do drop dual each else elseif empty enable enclosed end engine engines enforced enum errors escape escaped except execute exists exit explain extract false fetch first first_value float float4 float8 flush following for force foreign found_rows from full fulltext function generated get global grant grants group group_concat groups having high_priority histogram hour_microsecond hour_minute hour_second if ignore in index indexes infile inner inout insensitive insert int int1 int2 int3 int4 int8 integer intersect interval into invisible invoker isolation json json_array json_object json_table join key keys kill lag language last last_value lead leading left like limit linear lines load local localtime localtimestamp lock long longblob longtext low_priority match max max_rows maxvalue medium mediumblob mediumint mediumtext member merge min min_rows minute_microsecond minute_second mod mode modifies modify month natural not nowait nth_value ntile null numeric of offset on only optimize optimizer_costs option optionally or order others out outer outfile over overflows partition partitions password percent_rank persist plugin plugins precision primary privileges procedure purge range rank read reads real recursive references regexp rename repeat replace require resignal restrict return returning revoke right rlike rollback rollup row row_number rows rtree savepoint schedule schema schemas second_microsecond select sensitive separator serializable session set share show signal signed smallint spatial specific sql sql_big_result sql_buffer_result sql_calc_found_rows sql_no_cache sql_small_result sqlexception sqlstate sqlwarning ssl start starting stats_auto_recalc stats_persistent stats_sample_pages status stored straight_join system table tables tablespace temporary then time timestamp tinyblob tinyint tinytext to trailing transaction trigger triggers true truncate type unbounded undo union unique unlock unsigned update usage use user using utc_date utc_time utc_timestamp values varbinary varchar varcharacter varying view visible when where window with work write xor year year_month zerofill"),
builtin: set("bigint binary bit blob bool boolean char date datetime decimal double enum float geometry geometrycollection int integer json linestring long longblob longtext medium mediumblob mediumint mediumtext multilinestring multipoint multipolygon numeric point polygon real set smallint text time timestamp tinyblob tinyint tinytext varbinary varchar year"),
atoms: set("false true null unknown"),
operatorChars: /^[*+\-%<>!=&|^]/,
dateSQL: set("date time timestamp"),
Expand Down
165 changes: 165 additions & 0 deletions mode/sql/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/5/LICENSE

/* global CodeMirror, test, testCM, is, eq */

(function() {
var mode = CodeMirror.getMode({indentUnit: 2}, "text/x-mysql");
function MT(name) { test.mode(name, mode, Array.prototype.slice.call(arguments, 1)); }

MT("simple_select",
"[keyword SELECT] [number 1]",
"[keyword SELECT] * [keyword FROM] users");

MT("where_with_parentheses",
"[keyword SELECT] * [keyword FROM] users [keyword WHERE] [bracket (] age > [number 18] [bracket )]");

MT("function_call",
"[keyword SELECT] COUNT[bracket (]*[bracket )] [keyword FROM] users");

MT("nested_parentheses",
"[keyword SELECT] [bracket (][bracket (]a + b[bracket )] * c[bracket )]");

MT("string_with_parentheses",
"[keyword SELECT] [string '(test)'] [keyword FROM] t");

MT("comment_with_parentheses",
"[keyword SELECT] [number 1] [comment -- (test)]");

MT("multiline_comment",
"[comment /* (test) */]");

MT("subquery",
"[keyword SELECT] * [keyword FROM] [bracket (]",
" [keyword SELECT] [number 1]",
"[bracket )] subq");

MT("in_operator",
"[keyword SELECT] * [keyword FROM] t [keyword WHERE] id [keyword IN] [bracket (][number 1], [number 2], [number 3][bracket )]");

MT("cte_recursive",
"[keyword WITH] [keyword RECURSIVE] t [keyword AS] [bracket (]",
" [keyword SELECT] [number 1]",
"[bracket )]",
"[keyword SELECT] * [keyword FROM] t");

MT("window_function",
"[keyword SELECT] ROW_NUMBER[bracket (][bracket )] [keyword OVER] [bracket (][keyword PARTITION] [keyword BY] dept[bracket )]",
"[keyword FROM] emp");

MT("json_table",
"[keyword SELECT] * [keyword FROM] JSON_TABLE[bracket (]doc, [string '$.a[*]'] [keyword COLUMNS] [bracket (]x [keyword INT] [keyword PATH] [string '$'][bracket )][bracket )] jt");

MT("invisible_index",
"[keyword CREATE] [keyword INDEX] idx [keyword ON] t[bracket (]c[bracket )] [keyword INVISIBLE]");

MT("roles",
"[keyword CREATE] [keyword ROLE] reporting_role");

MT("date_literal",
"[keyword SELECT] [keyword DATE] [string '2024-01-01']");

// ODBC escape syntax intentionally not supported
MT("no_curly_braces",
"[keyword SELECT] {d [string '2023-01-01']}");

// Tests for SQL mode - focusing on text/x-mysql

testCM("sql_mysql_keyword_invisible", function(cm) {
var tokens = cm.getLineTokens(0);
var invisible = tokens.find(function(t) {
return t.string.toUpperCase() === "INVISIBLE";
});
is(invisible, "INVISIBLE token should exist");
eq(invisible.type, "keyword");
}, {
mode: "text/x-mysql",
value: "CREATE INDEX idx ON t(c) INVISIBLE"
});

testCM("sql_mysql_role_token_exists", function(cm) {
var tokens = cm.getLineTokens(0);
var role = tokens.find(function(t) {
return t.string.toUpperCase() === "ROLE";
});
is(role, "ROLE token should exist");
}, {
mode: "text/x-mysql",
value: "CREATE ROLE reporting_role"
});

testCM("sql_mysql_keyword_json_table", function(cm) {
var tokens = cm.getLineTokens(0);
var jt = tokens.find(function(t) {
return t.string.toUpperCase() === "JSON_TABLE";
});
is(jt, "JSON_TABLE token should exist");
eq(jt.type, "keyword");
}, {
mode: "text/x-mysql",
value: "SELECT * FROM JSON_TABLE(doc, '$.a[*]' COLUMNS(x INT PATH '$')) jt"
});

testCM("sql_mysql_window_keywords", function(cm) {
var tokens = cm.getLineTokens(0);
var kws = tokens
.filter(function(t) { return t.type === "keyword"; })
.map(function(t) { return t.string.toUpperCase(); });

is(kws.indexOf("OVER") !== -1);
is(kws.indexOf("PARTITION") !== -1);
is(kws.indexOf("ROW_NUMBER") !== -1);
}, {
mode: "text/x-mysql",
value: "SELECT ROW_NUMBER() OVER (PARTITION BY dept) FROM emp"
});

testCM("sql_mysql_with_recursive_keywords", function(cm) {
var tokens = cm.getLineTokens(0);
var kws = tokens
.filter(function(t) { return t.type === "keyword"; })
.map(function(t) { return t.string.toUpperCase(); });

is(kws.indexOf("WITH") !== -1);
is(kws.indexOf("RECURSIVE") !== -1);
}, {
mode: "text/x-mysql",
value: "WITH RECURSIVE t AS (SELECT 1) SELECT * FROM t"
});

testCM("sql_mysql_date_type_token", function(cm) {
var tokens = cm.getLineTokens(0);
var date = tokens.find(function(t) {
return t.string.toUpperCase() === "DATE";
});
is(date, "DATE token should exist");
}, {
mode: "text/x-mysql",
value: "CREATE TABLE t (d DATE)"
});

testCM("sql_mysql_schema_tokens", function(cm) {
var tokens = cm.getLineTokens(0);
var schemas = tokens.find(function(t) {
return t.string.toUpperCase() === "SCHEMAS";
});
is(schemas, "SCHEMAS token should exist");
is(schemas.type === "keyword" || schemas.type === "builtin");
}, {
mode: "text/x-mysql",
value: "SHOW SCHEMAS"
});

testCM("sql_mysql_invisible_identifier_untouched", function(cm) {
var tokens = cm.getLineTokens(0);
var ident = tokens.find(function(t) {
return t.string === "idx";
});
is(ident);
is(ident.type !== "keyword");
}, {
mode: "text/x-mysql",
value: "CREATE INDEX idx ON t(c) INVISIBLE"
});

})();
6 changes: 6 additions & 0 deletions test/run.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ var server = require('http').createServer(function (req, res) {
req.addListener('end', function () {
files.serve(req, res, function (err/*, result */) {
if (err) {
// 404 is NORMAL (favicon.ico etc.)
if (err.status === 404) {
res.end();
return;
}

console.error(err);
process.exit(1);
}
Expand Down
1 change: 0 additions & 1 deletion test/sql-hint-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,6 @@
list: [{"text":"schema.users","className":"CodeMirror-hint-table"},
{"text":"schema.countries","className":"CodeMirror-hint-table"},
{"text":"SCHEMA","className":"CodeMirror-hint-keyword"},
{"text":"SCHEMA_NAME","className":"CodeMirror-hint-keyword"},
{"text":"SCHEMAS","className":"CodeMirror-hint-keyword"}],
from: Pos(0, 7),
to: Pos(0, 12)
Expand Down