diff --git a/AUTHORS b/AUTHORS index 6da529551a..a9aea56e4a 100644 --- a/AUTHORS +++ b/AUTHORS @@ -13,6 +13,7 @@ Boris Verkhovskiy Christian Jorgensen Christopher Manouvrier Damon Davison +Daniƫl van Eeden Davut Can Abacigil eeeXun Erik Hirmo diff --git a/README.md b/README.md index 12bee84efb..64fcec73c4 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ It started as a port of a [PHP Library][], but has since considerably diverged. It supports various SQL dialects: -GCP BigQuery, IBM DB2, Apache Hive, MariaDB, MySQL, Couchbase N1QL, Oracle PL/SQL, PostgreSQL, Amazon Redshift, SingleStoreDB, Snowflake, Spark, SQL Server Transact-SQL, Trino (and Presto). +GCP BigQuery, IBM DB2, Apache Hive, MariaDB, MySQL, TiDB, Couchbase N1QL, Oracle PL/SQL, PostgreSQL, Amazon Redshift, SingleStoreDB, Snowflake, Spark, SQL Server Transact-SQL, Trino (and Presto). See [language option docs](docs/language.md) for more details. It does not support: @@ -123,7 +123,7 @@ sql-formatter -h ``` usage: sql-formatter [-h] [-o OUTPUT] \ -[-l {bigquery,db2,db2i,hive,mariadb,mysql,n1ql,plsql,postgresql,redshift,singlestoredb,snowflake,spark,sql,sqlite,transactsql,trino,tsql}] [-c CONFIG] [--version] [FILE] +[-l {bigquery,db2,db2i,hive,mariadb,mysql,n1ql,plsql,postgresql,redshift,singlestoredb,snowflake,spark,sql,sqlite,tidb,transactsql,trino,tsql}] [-c CONFIG] [--version] [FILE] SQL Formatter @@ -135,7 +135,7 @@ optional arguments: -o, --output OUTPUT File to write SQL output (defaults to stdout) --fix Update the file in-place - -l, --language {bigquery,db2,db2i,hive,mariadb,mysql,n1ql,plsql,postgresql,redshift,singlestoredb,snowflake,spark,sql,sqlite,trino,tsql} + -l, --language {bigquery,db2,db2i,hive,mariadb,mysql,n1ql,plsql,postgresql,redshift,singlestoredb,snowflake,spark,sql,sqlite,tidb,trino,tsql} SQL dialect (defaults to basic sql) -c, --config CONFIG Path to config JSON file or json string (will use default configs if unspecified) diff --git a/docs/dialect.md b/docs/dialect.md index 781982f571..798f3cc7c5 100644 --- a/docs/dialect.md +++ b/docs/dialect.md @@ -27,6 +27,7 @@ The following dialects can be imported from `"sql-formatter"` module: - `hive` - [Apache Hive][] - `mariadb` - [MariaDB][] - `mysql` - [MySQL][] +- `tidb` - [TiDB][] - `n1ql` - [Couchbase N1QL][] - `plsql` - [Oracle PL/SQL][] - `postgresql` - [PostgreSQL][] @@ -75,6 +76,7 @@ You likely only want to use this if your other alternative is to fork SQL Format [apache hive]: https://hive.apache.org/ [mariadb]: https://mariadb.com/ [mysql]: https://www.mysql.com/ +[tidb]: https://github.com/pingcap/tidb/ [couchbase n1ql]: http://www.couchbase.com/n1ql [oracle pl/sql]: http://www.oracle.com/technetwork/database/features/plsql/index.html [postgresql]: https://www.postgresql.org/ diff --git a/docs/language.md b/docs/language.md index df38263c11..83b0328c98 100644 --- a/docs/language.md +++ b/docs/language.md @@ -19,6 +19,7 @@ const result = format('SELECT * FROM tbl', { dialect: 'sqlite' }); - `"hive"` - [Apache Hive][] - `"mariadb"` - [MariaDB][] - `"mysql"` - [MySQL][] +- `"tidb"` - [TiDB][] - `"n1ql"` - [Couchbase N1QL][] - `"plsql"` - [Oracle PL/SQL][] - `"postgresql"` - [PostgreSQL][] @@ -52,6 +53,7 @@ See docs for [dialect][] option. [apache hive]: https://hive.apache.org/ [mariadb]: https://mariadb.com/ [mysql]: https://www.mysql.com/ +[tidb]: https://github.com/pingcap/tidb/ [couchbase n1ql]: http://www.couchbase.com/n1ql [oracle pl/sql]: http://www.oracle.com/technetwork/database/features/plsql/index.html [postgresql]: https://www.postgresql.org/ diff --git a/docs/params.md b/docs/params.md index 575d54ceb9..5af2229f43 100644 --- a/docs/params.md +++ b/docs/params.md @@ -121,6 +121,7 @@ The placeholder types available by default depend on SQL dialect used: - snowflake - _no support_ - sqlite - `?`, `?1`, `:name`, `@name`, `$name` - spark - _no support_ +- tidb - `?` - tsql - `@name`, `@"name"`, `@[name]` - trino - _no support_ diff --git a/package.json b/package.json index ba5acba758..3fbc40593d 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "db2", "db2i", "sqlite", + "tidb", "trino", "presto", "prestosql", diff --git a/src/allDialects.ts b/src/allDialects.ts index 5bc46b8996..790ef2095b 100644 --- a/src/allDialects.ts +++ b/src/allDialects.ts @@ -4,6 +4,7 @@ export { db2i } from './languages/db2i/db2i.formatter.js'; export { hive } from './languages/hive/hive.formatter.js'; export { mariadb } from './languages/mariadb/mariadb.formatter.js'; export { mysql } from './languages/mysql/mysql.formatter.js'; +export { tidb } from './languages/tidb/tidb.formatter.js'; export { n1ql } from './languages/n1ql/n1ql.formatter.js'; export { plsql } from './languages/plsql/plsql.formatter.js'; export { postgresql } from './languages/postgresql/postgresql.formatter.js'; diff --git a/src/index.ts b/src/index.ts index 036203c4c9..d4aab01736 100644 --- a/src/index.ts +++ b/src/index.ts @@ -9,6 +9,7 @@ export { db2i } from './languages/db2i/db2i.formatter.js'; export { hive } from './languages/hive/hive.formatter.js'; export { mariadb } from './languages/mariadb/mariadb.formatter.js'; export { mysql } from './languages/mysql/mysql.formatter.js'; +export { tidb } from './languages/tidb/tidb.formatter.js'; export { n1ql } from './languages/n1ql/n1ql.formatter.js'; export { plsql } from './languages/plsql/plsql.formatter.js'; export { postgresql } from './languages/postgresql/postgresql.formatter.js'; diff --git a/src/languages/tidb/tidb.formatter.ts b/src/languages/tidb/tidb.formatter.ts new file mode 100644 index 0000000000..abbb8a0301 --- /dev/null +++ b/src/languages/tidb/tidb.formatter.ts @@ -0,0 +1,221 @@ +import { DialectOptions } from '../../dialect.js'; +import { expandPhrases } from '../../expandPhrases.js'; +import { postProcess } from '../mariadb/likeMariaDb.js'; +import { dataTypes, keywords } from './tidb.keywords.js'; +import { functions } from './tidb.functions.js'; + +const reservedSelect = expandPhrases(['SELECT [ALL | DISTINCT | DISTINCTROW]']); + +const reservedClauses = expandPhrases([ + // queries + 'WITH [RECURSIVE]', + 'FROM', + 'WHERE', + 'GROUP BY', + 'HAVING', + 'WINDOW', + 'PARTITION BY', + 'ORDER BY', + 'LIMIT', + 'OFFSET', + // Data manipulation + // - insert: + 'INSERT [LOW_PRIORITY | DELAYED | HIGH_PRIORITY] [IGNORE] [INTO]', + 'REPLACE [LOW_PRIORITY | DELAYED] [INTO]', + 'VALUES', + 'ON DUPLICATE KEY UPDATE', + // - update: + 'SET', +]); + +const standardOnelineClauses = expandPhrases(['CREATE [TEMPORARY] TABLE [IF NOT EXISTS]']); + +const tabularOnelineClauses = expandPhrases([ + // https://docs.pingcap.com/tidb/stable/sql-statement-create-view + 'CREATE [OR REPLACE] [SQL SECURITY DEFINER | SQL SECURITY INVOKER] VIEW [IF NOT EXISTS]', + // https://docs.pingcap.com/tidb/stable/sql-statement-update + 'UPDATE [LOW_PRIORITY] [IGNORE]', + // https://docs.pingcap.com/tidb/stable/sql-statement-delete + 'DELETE [LOW_PRIORITY] [QUICK] [IGNORE] FROM', + // https://docs.pingcap.com/tidb/stable/sql-statement-drop-table + 'DROP [TEMPORARY] TABLE [IF EXISTS]', + // https://docs.pingcap.com/tidb/stable/sql-statement-alter-table + 'ALTER TABLE', + 'ADD [COLUMN]', + '{CHANGE | MODIFY} [COLUMN]', + 'DROP [COLUMN]', + 'RENAME [TO | AS]', + 'RENAME COLUMN', + 'ALTER [COLUMN]', + '{SET | DROP} DEFAULT', // for alter column + // https://docs.pingcap.com/tidb/stable/sql-statement-truncate + 'TRUNCATE [TABLE]', + // https://docs.pingcap.com/tidb/stable/sql-statement-alter-database + 'ALTER DATABASE', + // https://docs.pingcap.com/tidb/stable/sql-statement-alter-instance + 'ALTER INSTANCE', + 'ALTER RESOURCE GROUP', + 'ALTER SEQUENCE', + // https://docs.pingcap.com/tidb/stable/sql-statement-alter-user + 'ALTER USER', + 'ALTER VIEW', + 'ANALYZE TABLE', + 'CHECK TABLE', + 'CHECKSUM TABLE', + 'COMMIT', + 'CREATE DATABASE', + 'CREATE INDEX', + 'CREATE RESOURCE GROUP', + 'CREATE ROLE', + 'CREATE SEQUENCE', + 'CREATE USER', + 'DEALLOCATE PREPARE', + 'DESCRIBE', + 'DROP DATABASE', + 'DROP INDEX', + 'DROP RESOURCE GROUP', + 'DROP ROLE', + 'DROP TABLESPACE', + 'DROP USER', + 'DROP VIEW', + 'EXPLAIN', + 'FLUSH', + // https://docs.pingcap.com/tidb/stable/sql-statement-grant-privileges + 'GRANT', + 'IMPORT TABLE', + 'INSTALL COMPONENT', + 'INSTALL PLUGIN', + 'KILL', + 'LOAD DATA', + 'LOCK INSTANCE FOR BACKUP', + 'LOCK TABLES', + 'OPTIMIZE TABLE', + 'PREPARE', + 'RELEASE SAVEPOINT', + 'RENAME TABLE', + 'RENAME USER', + 'REPAIR TABLE', + 'RESET', + 'REVOKE', + 'ROLLBACK', + 'ROLLBACK TO SAVEPOINT', + 'SAVEPOINT', + 'SET CHARACTER SET', + 'SET DEFAULT ROLE', + 'SET NAMES', + 'SET PASSWORD', + 'SET RESOURCE GROUP', + 'SET ROLE', + 'SET TRANSACTION', + 'SHOW', + 'SHOW BINARY LOGS', + 'SHOW BINLOG EVENTS', + 'SHOW CHARACTER SET', + 'SHOW COLLATION', + 'SHOW COLUMNS', + 'SHOW CREATE DATABASE', + 'SHOW CREATE TABLE', + 'SHOW CREATE USER', + 'SHOW CREATE VIEW', + 'SHOW DATABASES', + 'SHOW ENGINE', + 'SHOW ENGINES', + 'SHOW ERRORS', + 'SHOW EVENTS', + 'SHOW GRANTS', + 'SHOW INDEX', + 'SHOW MASTER STATUS', + 'SHOW OPEN TABLES', + 'SHOW PLUGINS', + 'SHOW PRIVILEGES', + 'SHOW PROCESSLIST', + 'SHOW PROFILE', + 'SHOW PROFILES', + 'SHOW STATUS', + 'SHOW TABLE STATUS', + 'SHOW TABLES', + 'SHOW TRIGGERS', + 'SHOW VARIABLES', + 'SHOW WARNINGS', + // https://docs.pingcap.com/tidb/stable/sql-statement-table + 'TABLE', + 'UNINSTALL COMPONENT', + 'UNINSTALL PLUGIN', + 'UNLOCK INSTANCE', + 'UNLOCK TABLES', + // https://docs.pingcap.com/tidb/stable/sql-statement-use + 'USE', +]); + +const reservedSetOperations = expandPhrases(['UNION [ALL | DISTINCT]']); + +const reservedJoins = expandPhrases([ + 'JOIN', + '{LEFT | RIGHT} [OUTER] JOIN', + '{INNER | CROSS} JOIN', + 'NATURAL [INNER] JOIN', + 'NATURAL {LEFT | RIGHT} [OUTER] JOIN', + // non-standard joins + 'STRAIGHT_JOIN', +]); + +const reservedPhrases = expandPhrases([ + 'ON {UPDATE | DELETE} [SET NULL]', + 'CHARACTER SET', + '{ROWS | RANGE} BETWEEN', + 'IDENTIFIED BY', +]); + +// https://docs.pingcap.com/tidb/stable/basic-features +export const tidb: DialectOptions = { + name: 'tidb', + tokenizerOptions: { + reservedSelect, + reservedClauses: [...reservedClauses, ...standardOnelineClauses, ...tabularOnelineClauses], + reservedSetOperations, + reservedJoins, + reservedPhrases, + supportsXor: true, + reservedKeywords: keywords, + reservedDataTypes: dataTypes, + reservedFunctionNames: functions, + // TODO: support _ char set prefixes such as _utf8, _latin1, _binary, _utf8mb4, etc. + stringTypes: [ + '""-qq-bs', + { quote: "''-qq-bs", prefixes: ['N'] }, + { quote: "''-raw", prefixes: ['B', 'X'], requirePrefix: true }, + ], + identTypes: ['``'], + identChars: { first: '$', rest: '$', allowFirstCharNumber: true }, + variableTypes: [ + { regex: '@@?[A-Za-z0-9_.$]+' }, + { quote: '""-qq-bs', prefixes: ['@'], requirePrefix: true }, + { quote: "''-qq-bs", prefixes: ['@'], requirePrefix: true }, + { quote: '``', prefixes: ['@'], requirePrefix: true }, + ], + paramTypes: { positional: true }, + lineCommentTypes: ['--', '#'], + operators: [ + '%', + ':=', + '&', + '|', + '^', + '~', + '<<', + '>>', + '<=>', + '->', + '->>', + '&&', + '||', + '!', + '*.*', // Not actually an operator + ], + postProcess, + }, + formatOptions: { + onelineClauses: [...standardOnelineClauses, ...tabularOnelineClauses], + tabularOnelineClauses, + }, +}; diff --git a/src/languages/tidb/tidb.functions.ts b/src/languages/tidb/tidb.functions.ts new file mode 100644 index 0000000000..c8680f5187 --- /dev/null +++ b/src/languages/tidb/tidb.functions.ts @@ -0,0 +1,295 @@ +export const functions: string[] = [ + // https://docs.pingcap.com/tidb/stable/sql-statement-show-builtins + // https://docs.pingcap.com/tidb/stable/functions-and-operators-overview + 'ABS', + 'ACOS', + 'ADDDATE', + 'ADDTIME', + 'AES_DECRYPT', + 'AES_ENCRYPT', + // 'AND', + 'ANY_VALUE', + 'ASCII', + 'ASIN', + 'ATAN', + 'ATAN2', + 'BENCHMARK', + 'BIN', + 'BIN_TO_UUID', + 'BIT_COUNT', + 'BIT_LENGTH', + 'BITAND', + 'BITNEG', + 'BITOR', + 'BITXOR', + 'CASE', + 'CEIL', + 'CEILING', + 'CHAR_FUNC', + 'CHAR_LENGTH', + 'CHARACTER_LENGTH', + 'CHARSET', + 'COALESCE', + 'COERCIBILITY', + 'COLLATION', + 'COMPRESS', + 'CONCAT', + 'CONCAT_WS', + 'CONNECTION_ID', + 'CONV', + 'CONVERT', + 'CONVERT_TZ', + 'COS', + 'COT', + 'CRC32', + 'CURDATE', + 'CURRENT_DATE', + 'CURRENT_RESOURCE_GROUP', + 'CURRENT_ROLE', + 'CURRENT_TIME', + 'CURRENT_TIMESTAMP', + 'CURRENT_USER', + 'CURTIME', + 'DATABASE', + 'DATE', + 'DATE_ADD', + 'DATE_FORMAT', + 'DATE_SUB', + 'DATEDIFF', + 'DAY', + 'DAYNAME', + 'DAYOFMONTH', + 'DAYOFWEEK', + 'DAYOFYEAR', + 'DECODE', + 'DEFAULT_FUNC', + 'DEGREES', + 'DES_DECRYPT', + 'DES_ENCRYPT', + 'DIV', + 'ELT', + 'ENCODE', + 'ENCRYPT', + 'EQ', + 'EXP', + 'EXPORT_SET', + 'EXTRACT', + 'FIELD', + 'FIND_IN_SET', + 'FLOOR', + 'FORMAT', + 'FORMAT_BYTES', + 'FORMAT_NANO_TIME', + 'FOUND_ROWS', + 'FROM_BASE64', + 'FROM_DAYS', + 'FROM_UNIXTIME', + 'GE', + 'GET_FORMAT', + 'GET_LOCK', + 'GETPARAM', + 'GREATEST', + 'GROUPING', + 'GT', + 'HEX', + 'HOUR', + 'IF', + 'IFNULL', + 'ILIKE', + // 'IN', + 'INET6_ATON', + 'INET6_NTOA', + 'INET_ATON', + 'INET_NTOA', + 'INSERT_FUNC', + 'INSTR', + 'INTDIV', + 'INTERVAL', + 'IS_FREE_LOCK', + 'IS_IPV4', + 'IS_IPV4_COMPAT', + 'IS_IPV4_MAPPED', + 'IS_IPV6', + 'IS_USED_LOCK', + 'IS_UUID', + 'ISFALSE', + 'ISNULL', + 'ISTRUE', + 'JSON_ARRAY', + 'JSON_ARRAY_APPEND', + 'JSON_ARRAY_INSERT', + 'JSON_CONTAINS', + 'JSON_CONTAINS_PATH', + 'JSON_DEPTH', + 'JSON_EXTRACT', + 'JSON_INSERT', + 'JSON_KEYS', + 'JSON_LENGTH', + 'JSON_MEMBEROF', + 'JSON_MERGE', + 'JSON_MERGE_PATCH', + 'JSON_MERGE_PRESERVE', + 'JSON_OBJECT', + 'JSON_OVERLAPS', + 'JSON_PRETTY', + 'JSON_QUOTE', + 'JSON_REMOVE', + 'JSON_REPLACE', + 'JSON_SEARCH', + 'JSON_SET', + 'JSON_STORAGE_FREE', + 'JSON_STORAGE_SIZE', + 'JSON_TYPE', + 'JSON_UNQUOTE', + 'JSON_VALID', + 'LAST_DAY', + 'LAST_INSERT_ID', + 'LASTVAL', + 'LCASE', + 'LE', + 'LEAST', + 'LEFT', + 'LEFTSHIFT', + 'LENGTH', + 'LIKE', + 'LN', + 'LOAD_FILE', + 'LOCALTIME', + 'LOCALTIMESTAMP', + 'LOCATE', + 'LOG', + 'LOG10', + 'LOG2', + 'LOWER', + 'LPAD', + 'LT', + 'LTRIM', + 'MAKE_SET', + 'MAKEDATE', + 'MAKETIME', + 'MASTER_POS_WAIT', + 'MD5', + 'MICROSECOND', + 'MID', + 'MINUS', + 'MINUTE', + 'MOD', + 'MONTH', + 'MONTHNAME', + 'MUL', + 'NAME_CONST', + 'NE', + 'NEXTVAL', + 'NOT', + 'NOW', + 'NULLEQ', + 'OCT', + 'OCTET_LENGTH', + 'OLD_PASSWORD', + // 'OR', + 'ORD', + 'PASSWORD_FUNC', + 'PERIOD_ADD', + 'PERIOD_DIFF', + 'PI', + 'PLUS', + 'POSITION', + 'POW', + 'POWER', + 'QUARTER', + 'QUOTE', + 'RADIANS', + 'RAND', + 'RANDOM_BYTES', + 'REGEXP', + 'REGEXP_INSTR', + 'REGEXP_LIKE', + 'REGEXP_REPLACE', + 'REGEXP_SUBSTR', + 'RELEASE_ALL_LOCKS', + 'RELEASE_LOCK', + 'REPEAT', + 'REPLACE', + 'REVERSE', + 'RIGHT', + 'RIGHTSHIFT', + 'ROUND', + 'ROW_COUNT', + 'RPAD', + 'RTRIM', + 'SCHEMA', + 'SEC_TO_TIME', + 'SECOND', + 'SESSION_USER', + 'SETVAL', + 'SETVAR', + 'SHA', + 'SHA1', + 'SHA2', + 'SIGN', + 'SIN', + 'SLEEP', + 'SM3', + 'SPACE', + 'SQRT', + 'STR_TO_DATE', + 'STRCMP', + 'SUBDATE', + 'SUBSTR', + 'SUBSTRING', + 'SUBSTRING_INDEX', + 'SUBTIME', + 'SYSDATE', + 'SYSTEM_USER', + 'TAN', + 'TIDB_BOUNDED_STALENESS', + 'TIDB_CURRENT_TSO', + 'TIDB_DECODE_BINARY_PLAN', + 'TIDB_DECODE_KEY', + 'TIDB_DECODE_PLAN', + 'TIDB_DECODE_SQL_DIGESTS', + 'TIDB_ENCODE_SQL_DIGEST', + 'TIDB_IS_DDL_OWNER', + 'TIDB_PARSE_TSO', + 'TIDB_PARSE_TSO_LOGICAL', + 'TIDB_ROW_CHECKSUM', + 'TIDB_SHARD', + 'TIDB_VERSION', + 'TIME', + 'TIME_FORMAT', + 'TIME_TO_SEC', + 'TIMEDIFF', + 'TIMESTAMP', + 'TIMESTAMPADD', + 'TIMESTAMPDIFF', + 'TO_BASE64', + 'TO_DAYS', + 'TO_SECONDS', + 'TRANSLATE', + 'TRIM', + 'TRUNCATE', + 'UCASE', + 'UNARYMINUS', + 'UNCOMPRESS', + 'UNCOMPRESSED_LENGTH', + 'UNHEX', + 'UNIX_TIMESTAMP', + 'UPPER', + // 'USER', + 'UTC_DATE', + 'UTC_TIME', + 'UTC_TIMESTAMP', + 'UUID', + 'UUID_SHORT', + 'UUID_TO_BIN', + 'VALIDATE_PASSWORD_STRENGTH', + 'VERSION', + 'VITESS_HASH', + 'WEEK', + 'WEEKDAY', + 'WEEKOFYEAR', + 'WEIGHT_STRING', + // 'XOR', + 'YEAR', + 'YEARWEEK', +]; diff --git a/src/languages/tidb/tidb.keywords.ts b/src/languages/tidb/tidb.keywords.ts new file mode 100644 index 0000000000..1d45867503 --- /dev/null +++ b/src/languages/tidb/tidb.keywords.ts @@ -0,0 +1,703 @@ +export const keywords: string[] = [ + // https://docs.pingcap.com/tidb/stable/keywords + 'ADD', // (R) + 'ALL', // (R) + 'ALTER', // (R) + 'ANALYZE', // (R) + 'AND', // (R) + 'ARRAY', // (R) + 'AS', // (R) + 'ASC', // (R) + 'BETWEEN', // (R) + 'BIGINT', // (R) + 'BINARY', // (R) + 'BLOB', // (R) + 'BOTH', // (R) + 'BY', // (R) + 'CALL', // (R) + 'CASCADE', // (R) + 'CASE', // (R) + 'CHANGE', // (R) + 'CHAR', // (R) + 'CHARACTER', // (R) + 'CHECK', // (R) + 'COLLATE', // (R) + 'COLUMN', // (R) + 'CONSTRAINT', // (R) + 'CONTINUE', // (R) + 'CONVERT', // (R) + 'CREATE', // (R) + 'CROSS', // (R) + 'CUME_DIST', // (R) + 'CURRENT_DATE', // (R) + 'CURRENT_ROLE', // (R) + 'CURRENT_TIME', // (R) + 'CURRENT_TIMESTAMP', // (R) + 'CURRENT_USER', // (R) + 'CURSOR', // (R) + 'DATABASE', // (R) + 'DATABASES', // (R) + 'DAY_HOUR', // (R) + 'DAY_MICROSECOND', // (R) + 'DAY_MINUTE', // (R) + 'DAY_SECOND', // (R) + 'DECIMAL', // (R) + 'DEFAULT', // (R) + 'DELAYED', // (R) + 'DELETE', // (R) + 'DENSE_RANK', // (R) + 'DESC', // (R) + 'DESCRIBE', // (R) + 'DISTINCT', // (R) + 'DISTINCTROW', // (R) + 'DIV', // (R) + 'DOUBLE', // (R) + 'DROP', // (R) + 'DUAL', // (R) + 'ELSE', // (R) + 'ELSEIF', // (R) + 'ENCLOSED', // (R) + 'ESCAPED', // (R) + 'EXCEPT', // (R) + 'EXISTS', // (R) + 'EXIT', // (R) + 'EXPLAIN', // (R) + 'FALSE', // (R) + 'FETCH', // (R) + 'FIRST_VALUE', // (R) + 'FLOAT', // (R) + 'FLOAT4', // (R) + 'FLOAT8', // (R) + 'FOR', // (R) + 'FORCE', // (R) + 'FOREIGN', // (R) + 'FROM', // (R) + 'FULLTEXT', // (R) + 'GENERATED', // (R) + 'GRANT', // (R) + 'GROUP', // (R) + 'GROUPS', // (R) + 'HAVING', // (R) + 'HIGH_PRIORITY', // (R) + 'HOUR_MICROSECOND', // (R) + 'HOUR_MINUTE', // (R) + 'HOUR_SECOND', // (R) + 'IF', // (R) + 'IGNORE', // (R) + 'ILIKE', // (R) + 'IN', // (R) + 'INDEX', // (R) + 'INFILE', // (R) + 'INNER', // (R) + 'INOUT', // (R) + 'INSERT', // (R) + 'INT', // (R) + 'INT1', // (R) + 'INT2', // (R) + 'INT3', // (R) + 'INT4', // (R) + 'INT8', // (R) + 'INTEGER', // (R) + 'INTERSECT', // (R) + 'INTERVAL', // (R) + 'INTO', // (R) + 'IS', // (R) + 'ITERATE', // (R) + 'JOIN', // (R) + 'KEY', // (R) + 'KEYS', // (R) + 'KILL', // (R) + 'LAG', // (R) + 'LAST_VALUE', // (R) + 'LEAD', // (R) + 'LEADING', // (R) + 'LEAVE', // (R) + 'LEFT', // (R) + 'LIKE', // (R) + 'LIMIT', // (R) + 'LINEAR', // (R) + 'LINES', // (R) + 'LOAD', // (R) + 'LOCALTIME', // (R) + 'LOCALTIMESTAMP', // (R) + 'LOCK', // (R) + 'LONG', // (R) + 'LONGBLOB', // (R) + 'LONGTEXT', // (R) + 'LOW_PRIORITY', // (R) + 'MATCH', // (R) + 'MAXVALUE', // (R) + 'MEDIUMBLOB', // (R) + 'MEDIUMINT', // (R) + 'MEDIUMTEXT', // (R) + 'MIDDLEINT', // (R) + 'MINUTE_MICROSECOND', // (R) + 'MINUTE_SECOND', // (R) + 'MOD', // (R) + 'NATURAL', // (R) + 'NOT', // (R) + 'NO_WRITE_TO_BINLOG', // (R) + 'NTH_VALUE', // (R) + 'NTILE', // (R) + 'NULL', // (R) + 'NUMERIC', // (R) + 'OF', // (R) + 'ON', // (R) + 'OPTIMIZE', // (R) + 'OPTION', // (R) + 'OPTIONALLY', // (R) + 'OR', // (R) + 'ORDER', // (R) + 'OUT', // (R) + 'OUTER', // (R) + 'OUTFILE', // (R) + 'OVER', // (R) + 'PARTITION', // (R) + 'PERCENT_RANK', // (R) + 'PRECISION', // (R) + 'PRIMARY', // (R) + 'PROCEDURE', // (R) + 'RANGE', // (R) + 'RANK', // (R) + 'READ', // (R) + 'REAL', // (R) + 'RECURSIVE', // (R) + 'REFERENCES', // (R) + 'REGEXP', // (R) + 'RELEASE', // (R) + 'RENAME', // (R) + 'REPEAT', // (R) + 'REPLACE', // (R) + 'REQUIRE', // (R) + 'RESTRICT', // (R) + 'REVOKE', // (R) + 'RIGHT', // (R) + 'RLIKE', // (R) + 'ROW', // (R) + 'ROWS', // (R) + 'ROW_NUMBER', // (R) + 'SECOND_MICROSECOND', // (R) + 'SELECT', // (R) + 'SET', // (R) + 'SHOW', // (R) + 'SMALLINT', // (R) + 'SPATIAL', // (R) + 'SQL', // (R) + 'SQLEXCEPTION', // (R) + 'SQLSTATE', // (R) + 'SQLWARNING', // (R) + 'SQL_BIG_RESULT', // (R) + 'SQL_CALC_FOUND_ROWS', // (R) + 'SQL_SMALL_RESULT', // (R) + 'SSL', // (R) + 'STARTING', // (R) + 'STATS_EXTENDED', // (R) + 'STORED', // (R) + 'STRAIGHT_JOIN', // (R) + 'TABLE', // (R) + 'TABLESAMPLE', // (R) + 'TERMINATED', // (R) + 'THEN', // (R) + 'TINYBLOB', // (R) + 'TINYINT', // (R) + 'TINYTEXT', // (R) + 'TO', // (R) + 'TRAILING', // (R) + 'TRIGGER', // (R) + 'TRUE', // (R) + 'TiDB_CURRENT_TSO', // (R) + 'UNION', // (R) + 'UNIQUE', // (R) + 'UNLOCK', // (R) + 'UNSIGNED', // (R) + 'UNTIL', // (R) + 'UPDATE', // (R) + 'USAGE', // (R) + 'USE', // (R) + 'USING', // (R) + 'UTC_DATE', // (R) + 'UTC_TIME', // (R) + 'UTC_TIMESTAMP', // (R) + 'VALUES', // (R) + 'VARBINARY', // (R) + 'VARCHAR', // (R) + 'VARCHARACTER', // (R) + 'VARYING', // (R) + 'VIRTUAL', // (R) + 'WHEN', // (R) + 'WHERE', // (R) + 'WHILE', // (R) + 'WINDOW', // (R) + 'WITH', // (R) + 'WRITE', // (R) + 'XOR', // (R) + 'YEAR_MONTH', // (R) + 'ZEROFILL', // (R) + 'ACCOUNT', + 'ACTION', + 'ADVISE', + 'AFTER', + 'AGAINST', + 'AGO', + 'ALGORITHM', + 'ALWAYS', + 'ANY', + 'ASCII', + 'ATTRIBUTE', + 'ATTRIBUTES', + 'AUTO_ID_CACHE', + 'AUTO_INCREMENT', + 'AUTO_RANDOM', + 'AUTO_RANDOM_BASE', + 'AVG', + 'AVG_ROW_LENGTH', + 'BACKEND', + 'BACKUP', + 'BACKUPS', + 'BDR', + 'BEGIN', + 'BERNOULLI', + 'BINDING', + 'BINDINGS', + 'BINDING_CACHE', + 'BINLOG', + 'BIT', + 'BLOCK', + 'BOOL', + 'BOOLEAN', + 'BTREE', + 'BYTE', + 'CACHE', + 'CALIBRATE', + 'CAPTURE', + 'CASCADED', + 'CAUSAL', + 'CHAIN', + 'CHARSET', + 'CHECKPOINT', + 'CHECKSUM', + 'CIPHER', + 'CLEANUP', + 'CLIENT', + 'CLIENT_ERRORS_SUMMARY', + 'CLOSE', + 'CLUSTER', + 'CLUSTERED', + 'COALESCE', + 'COLLATION', + 'COLUMNS', + 'COLUMN_FORMAT', + 'COMMENT', + 'COMMIT', + 'COMMITTED', + 'COMPACT', + 'COMPRESSED', + 'COMPRESSION', + 'CONCURRENCY', + 'CONFIG', + 'CONNECTION', + 'CONSISTENCY', + 'CONSISTENT', + 'CONTEXT', + 'CPU', + 'CSV_BACKSLASH_ESCAPE', + 'CSV_DELIMITER', + 'CSV_HEADER', + 'CSV_NOT_NULL', + 'CSV_NULL', + 'CSV_SEPARATOR', + 'CSV_TRIM_LAST_SEPARATORS', + 'CURRENT', + 'CYCLE', + 'DATA', + 'DATE', + 'DATETIME', + 'DAY', + 'DEALLOCATE', + 'DECLARE', + 'DEFINER', + 'DELAY_KEY_WRITE', + 'DIGEST', + 'DIRECTORY', + 'DISABLE', + 'DISABLED', + 'DISCARD', + 'DISK', + 'DO', + 'DUPLICATE', + 'DYNAMIC', + 'ENABLE', + 'ENABLED', + 'ENCRYPTION', + 'END', + 'ENFORCED', + 'ENGINE', + 'ENGINES', + 'ENUM', + 'ERROR', + 'ERRORS', + 'ESCAPE', + 'EVENT', + 'EVENTS', + 'EVOLVE', + 'EXCHANGE', + 'EXCLUSIVE', + 'EXECUTE', + 'EXPANSION', + 'EXPIRE', + 'EXTENDED', + 'FAILED_LOGIN_ATTEMPTS', + 'FAULTS', + 'FIELDS', + 'FILE', + 'FIRST', + 'FIXED', + 'FLUSH', + 'FOLLOWING', + 'FORMAT', + 'FOUND', + 'FULL', + 'FUNCTION', + 'GENERAL', + 'GLOBAL', + 'GRANTS', + 'HANDLER', + 'HASH', + 'HELP', + 'HISTOGRAM', + 'HISTORY', + 'HOSTS', + 'HOUR', + 'HYPO', + 'IDENTIFIED', + 'IMPORT', + 'IMPORTS', + 'INCREMENT', + 'INCREMENTAL', + 'INDEXES', + 'INSERT_METHOD', + 'INSTANCE', + 'INVISIBLE', + 'INVOKER', + 'IO', + 'IPC', + 'ISOLATION', + 'ISSUER', + 'JSON', + 'KEY_BLOCK_SIZE', + 'LABELS', + 'LANGUAGE', + 'LAST', + 'LASTVAL', + 'LAST_BACKUP', + 'LESS', + 'LEVEL', + 'LIST', + 'LOCAL', + 'LOCATION', + 'LOCKED', + 'LOGS', + 'MASTER', + 'MAX_CONNECTIONS_PER_HOUR', + 'MAX_IDXNUM', + 'MAX_MINUTES', + 'MAX_QUERIES_PER_HOUR', + 'MAX_ROWS', + 'MAX_UPDATES_PER_HOUR', + 'MAX_USER_CONNECTIONS', + 'MB', + 'MEMBER', + 'MEMORY', + 'MERGE', + 'MICROSECOND', + 'MINUTE', + 'MINVALUE', + 'MIN_ROWS', + 'MODE', + 'MODIFY', + 'MONTH', + 'NAMES', + 'NATIONAL', + 'NCHAR', + 'NEVER', + 'NEXT', + 'NEXTVAL', + 'NO', + 'NOCACHE', + 'NOCYCLE', + 'NODEGROUP', + 'NOMAXVALUE', + 'NOMINVALUE', + 'NONCLUSTERED', + 'NONE', + 'NOWAIT', + 'NULLS', + 'NVARCHAR', + 'OFF', + 'OFFSET', + 'OLTP_READ_ONLY', + 'OLTP_READ_WRITE', + 'OLTP_WRITE_ONLY', + 'ONLINE', + 'ONLY', + 'ON_DUPLICATE', + 'OPEN', + 'OPTIONAL', + 'PACK_KEYS', + 'PAGE', + 'PARSER', + 'PARTIAL', + 'PARTITIONING', + 'PARTITIONS', + 'PASSWORD', + 'PASSWORD_LOCK_TIME', + 'PAUSE', + 'PERCENT', + 'PER_DB', + 'PER_TABLE', + 'PLUGINS', + 'POINT', + 'POLICY', + 'PRECEDING', + 'PREPARE', + 'PRESERVE', + 'PRE_SPLIT_REGIONS', + 'PRIVILEGES', + 'PROCESS', + 'PROCESSLIST', + 'PROFILE', + 'PROFILES', + 'PROXY', + 'PURGE', + 'QUARTER', + 'QUERIES', + 'QUERY', + 'QUICK', + 'RATE_LIMIT', + 'REBUILD', + 'RECOVER', + 'REDUNDANT', + 'RELOAD', + 'REMOVE', + 'REORGANIZE', + 'REPAIR', + 'REPEATABLE', + 'REPLICA', + 'REPLICAS', + 'REPLICATION', + 'REQUIRED', + 'RESOURCE', + 'RESPECT', + 'RESTART', + 'RESTORE', + 'RESTORES', + 'RESUME', + 'REUSE', + 'REVERSE', + 'ROLE', + 'ROLLBACK', + 'ROLLUP', + 'ROUTINE', + 'ROW_COUNT', + 'ROW_FORMAT', + 'RTREE', + 'SAN', + 'SAVEPOINT', + 'SECOND', + 'SECONDARY', + 'SECONDARY_ENGINE', + 'SECONDARY_LOAD', + 'SECONDARY_UNLOAD', + 'SECURITY', + 'SEND_CREDENTIALS_TO_TIKV', + 'SEPARATOR', + 'SEQUENCE', + 'SERIAL', + 'SERIALIZABLE', + 'SESSION', + 'SETVAL', + 'SHARD_ROW_ID_BITS', + 'SHARE', + 'SHARED', + 'SHUTDOWN', + 'SIGNED', + 'SIMPLE', + 'SKIP', + 'SKIP_SCHEMA_FILES', + 'SLAVE', + 'SLOW', + 'SNAPSHOT', + 'SOME', + 'SOURCE', + 'SQL_BUFFER_RESULT', + 'SQL_CACHE', + 'SQL_NO_CACHE', + 'SQL_TSI_DAY', + 'SQL_TSI_HOUR', + 'SQL_TSI_MINUTE', + 'SQL_TSI_MONTH', + 'SQL_TSI_QUARTER', + 'SQL_TSI_SECOND', + 'SQL_TSI_WEEK', + 'SQL_TSI_YEAR', + 'START', + 'STATS_AUTO_RECALC', + 'STATS_COL_CHOICE', + 'STATS_COL_LIST', + 'STATS_OPTIONS', + 'STATS_PERSISTENT', + 'STATS_SAMPLE_PAGES', + 'STATS_SAMPLE_RATE', + 'STATUS', + 'STORAGE', + 'STRICT_FORMAT', + 'SUBJECT', + 'SUBPARTITION', + 'SUBPARTITIONS', + 'SUPER', + 'SWAPS', + 'SWITCHES', + 'SYSTEM', + 'SYSTEM_TIME', + 'TABLES', + 'TABLESPACE', + 'TABLE_CHECKSUM', + 'TEMPORARY', + 'TEMPTABLE', + 'TEXT', + 'THAN', + 'TIKV_IMPORTER', + 'TIME', + 'TIMESTAMP', + 'TOKEN_ISSUER', + 'TPCC', + 'TPCH_10', + 'TRACE', + 'TRADITIONAL', + 'TRANSACTION', + 'TRIGGERS', + 'TRUNCATE', + 'TSO', + 'TTL', + 'TTL_ENABLE', + 'TTL_JOB_INTERVAL', + 'TYPE', + 'UNBOUNDED', + 'UNCOMMITTED', + 'UNDEFINED', + 'UNICODE', + 'UNKNOWN', + 'UNSET', + 'USER', + 'VALIDATION', + 'VALUE', + 'VARIABLES', + 'VIEW', + 'VISIBLE', + 'WAIT', + 'WARNINGS', + 'WEEK', + 'WEIGHT_STRING', + 'WITHOUT', + 'WORKLOAD', + 'X509', + 'YEAR', + 'ADMIN', + 'BATCH', + 'BUCKETS', + 'BUILTINS', + 'CANCEL', + 'CARDINALITY', + 'CMSKETCH', + 'COLUMN_STATS_USAGE', + 'CORRELATION', + 'DDL', + 'DEPENDENCY', + 'DEPTH', + 'DRAINER', + 'DRY', + 'HISTOGRAMS_IN_FLIGHT', + 'JOB', + 'JOBS', + 'NODE_ID', + 'NODE_STATE', + 'OPTIMISTIC', + 'PESSIMISTIC', + 'PUMP', + 'REGION', + 'REGIONS', + 'RESET', + 'RUN', + 'SAMPLERATE', + 'SAMPLES', + 'SESSION_STATES', + 'SPLIT', + 'STATISTICS', + 'STATS', + 'STATS_BUCKETS', + 'STATS_HEALTHY', + 'STATS_HISTOGRAMS', + 'STATS_LOCKED', + 'STATS_META', + 'STATS_TOPN', + 'TELEMETRY', + 'TELEMETRY_ID', + 'TIDB', + 'TIFLASH', + 'TOPN', + 'WIDTH', +]; + +export const dataTypes: string[] = [ + // https://docs.pingcap.com/tidb/stable/data-type-overview + 'BIGINT', // (R) + 'BINARY', // (R) + 'BIT', + 'BLOB', // (R) + 'BOOL', // (R) + 'BOOLEAN', // (R) + 'CHAR', // (R) + 'CHARACTER', // (R) + 'DATE', // (R) + 'DATETIME', // (R) + 'DEC', // (R) + 'DECIMAL', // (R) + 'DOUBLE PRECISION', + 'DOUBLE', // (R) + 'ENUM', + 'FIXED', + 'FLOAT', // (R) + 'FLOAT4', // (R) + 'FLOAT8', // (R) + 'INT', // (R) + 'INT1', // (R) + 'INT2', // (R) + 'INT3', // (R) + 'INT4', // (R) + 'INT8', // (R) + 'INTEGER', // (R) + 'LONGBLOB', // (R) + 'LONGTEXT', // (R) + 'MEDIUMBLOB', // (R) + 'MEDIUMINT', // (R) + 'MEDIUMTEXT', // (R) + 'MIDDLEINT', // (R) + 'NATIONAL CHAR', // (R) + 'NATIONAL VARCHAR', // (R) + 'NUMERIC', // (R) + 'PRECISION', // (R) + 'REAL', // (R) + 'SMALLINT', // (R) + 'TEXT', + 'TIME', + 'TIMESTAMP', // (R) + 'TINYBLOB', // (R) + 'TINYINT', // (R) + 'TINYTEXT', // (R) + 'VARBINARY', // (R) + 'VARCHAR', // (R) + 'VARCHARACTER', // (R) + 'VARYING', // (R) + 'YEAR', + // 'SET' // handled as special-case in postProcess +]; diff --git a/src/sqlFormatter.ts b/src/sqlFormatter.ts index 50cb2cbed9..b35ac56e22 100644 --- a/src/sqlFormatter.ts +++ b/src/sqlFormatter.ts @@ -19,6 +19,7 @@ const dialectNameMap: RecordOptions + diff --git a/test/tidb.test.ts b/test/tidb.test.ts new file mode 100644 index 0000000000..0490942add --- /dev/null +++ b/test/tidb.test.ts @@ -0,0 +1,101 @@ +import dedent from 'dedent-js'; + +import { format as originalFormat, FormatFn } from '../src/sqlFormatter.js'; +import behavesLikeMariaDbFormatter from './behavesLikeMariaDbFormatter.js'; + +import supportsJoin from './features/join.js'; +import supportsOperators from './features/operators.js'; +import supportsWindow from './features/window.js'; +import supportsSetOperations from './features/setOperations.js'; +import supportsLimiting from './features/limiting.js'; +import supportsCreateTable from './features/createTable.js'; +import supportsParams from './options/param.js'; +import supportsCreateView from './features/createView.js'; +import supportsAlterTable from './features/alterTable.js'; +import supportsStrings from './features/strings.js'; +import supportsConstraints from './features/constraints.js'; +import supportsDataTypeCase from './options/dataTypeCase.js'; + +describe('TiDBFormatter', () => { + const language = 'tidb'; + const format: FormatFn = (query, cfg = {}) => originalFormat(query, { ...cfg, language }); + + behavesLikeMariaDbFormatter(format); + + // in addition to string types listed in behavesLikeMariaDbFormatter + supportsStrings(format, ["N''"]); + + supportsJoin(format, { + without: ['FULL'], + additionally: ['STRAIGHT_JOIN'], + }); + supportsSetOperations(format, ['UNION', 'UNION ALL', 'UNION DISTINCT']); + supportsOperators( + format, + ['%', ':=', '&', '|', '^', '~', '<<', '>>', '<=>', '->', '->>', '&&', '||', '!'], + { + logicalOperators: ['AND', 'OR', 'XOR'], + any: true, + } + ); + supportsWindow(format); + supportsLimiting(format, { limit: true, offset: true }); + supportsCreateTable(format, { ifNotExists: true, columnComment: true, tableComment: true }); + supportsConstraints(format, [ + 'RESTRICT', + 'CASCADE', + 'SET NULL', + 'NO ACTION', + 'NOW', + 'CURRENT_TIMESTAMP', + ]); + supportsParams(format, { positional: true }); + supportsCreateView(format, { orReplace: true }); + supportsAlterTable(format, { + addColumn: true, + dropColumn: true, + modify: true, + renameTo: true, + renameColumn: true, + }); + supportsDataTypeCase(format); + + it(`supports @"name" variables`, () => { + expect(format(`SELECT @"foo fo", @"foo\\"x", @"foo""y" FROM tbl;`)).toBe(dedent` + SELECT + @"foo fo", + @"foo\\"x", + @"foo""y" + FROM + tbl; + `); + }); + + it(`supports @'name' variables`, () => { + expect(format(`SELECT @'bar ar', @'bar\\'x', @'bar''y' FROM tbl;`)).toBe(dedent` + SELECT + @'bar ar', + @'bar\\'x', + @'bar''y' + FROM + tbl; + `); + }); + + it('formats ALTER TABLE ... ALTER COLUMN', () => { + expect( + format( + `ALTER TABLE t ALTER COLUMN foo SET DEFAULT 10; + ALTER TABLE t ALTER COLUMN foo DROP DEFAULT;` + ) + ).toBe(dedent` + ALTER TABLE t + ALTER COLUMN foo + SET DEFAULT 10; + + ALTER TABLE t + ALTER COLUMN foo + DROP DEFAULT; + `); + }); +});