UNPKG

sql-formatter

Version:

Format whitespace in a SQL query to make it more readable

323 lines 14.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.clickhouse = void 0; const expandPhrases_js_1 = require("../../expandPhrases.js"); const token_js_1 = require("../../lexer/token.js"); const clickhouse_functions_js_1 = require("./clickhouse.functions.js"); const clickhouse_keywords_js_1 = require("./clickhouse.keywords.js"); const reservedSelect = (0, expandPhrases_js_1.expandPhrases)([ 'SELECT [DISTINCT]', // https://clickhouse.com/docs/sql-reference/statements/alter/view 'MODIFY QUERY SELECT [DISTINCT]', ]); const reservedClauses = (0, expandPhrases_js_1.expandPhrases)([ 'SET', // https://clickhouse.com/docs/sql-reference/statements/select 'WITH', 'FROM', 'SAMPLE', 'PREWHERE', 'WHERE', 'GROUP BY', 'HAVING', 'QUALIFY', 'ORDER BY', 'LIMIT', 'SETTINGS', 'INTO OUTFILE', 'FORMAT', // https://clickhouse.com/docs/sql-reference/window-functions 'WINDOW', 'PARTITION BY', // https://clickhouse.com/docs/sql-reference/statements/insert-into 'INSERT INTO', 'VALUES', // https://clickhouse.com/docs/sql-reference/statements/create/view#refreshable-materialized-view 'DEPENDS ON', // https://clickhouse.com/docs/sql-reference/statements/move 'MOVE {USER | ROLE | QUOTA | SETTINGS PROFILE | ROW POLICY}', // https://clickhouse.com/docs/sql-reference/statements/grant 'GRANT', // https://clickhouse.com/docs/sql-reference/statements/revoke 'REVOKE', // https://clickhouse.com/docs/sql-reference/statements/check-grant 'CHECK GRANT', // https://clickhouse.com/docs/sql-reference/statements/set-role 'SET [DEFAULT] ROLE [NONE | ALL | ALL EXCEPT]', // https://clickhouse.com/docs/sql-reference/statements/optimize 'DEDUPLICATE BY', // https://clickhouse.com/docs/sql-reference/statements/alter/statistics 'MODIFY STATISTICS', // Used for ALTER INDEX ... TYPE and ALTER STATISTICS ... TYPE 'TYPE', // https://clickhouse.com/docs/sql-reference/statements/alter 'ALTER USER [IF EXISTS]', 'ALTER [ROW] POLICY [IF EXISTS]', // https://clickhouse.com/docs/sql-reference/statements/drop 'DROP {USER | ROLE | QUOTA | PROFILE | SETTINGS PROFILE | ROW POLICY | POLICY} [IF EXISTS]', ]); const standardOnelineClauses = (0, expandPhrases_js_1.expandPhrases)([ // https://clickhouse.com/docs/sql-reference/statements/create 'CREATE [OR REPLACE] [TEMPORARY] TABLE [IF NOT EXISTS]', ]); const tabularOnelineClauses = (0, expandPhrases_js_1.expandPhrases)([ 'ALL EXCEPT', 'ON CLUSTER', // https://clickhouse.com/docs/sql-reference/statements/update 'UPDATE', // https://clickhouse.com/docs/sql-reference/statements/system 'SYSTEM RELOAD {DICTIONARIES | DICTIONARY | FUNCTIONS | FUNCTION | ASYNCHRONOUS METRICS}', 'SYSTEM DROP {DNS CACHE | MARK CACHE | ICEBERG METADATA CACHE | TEXT INDEX DICTIONARY CACHE | TEXT INDEX HEADER CACHE | TEXT INDEX POSTINGS CACHE | REPLICA | DATABASE REPLICA | UNCOMPRESSED CACHE | COMPILED EXPRESSION CACHE | QUERY CONDITION CACHE | QUERY CACHE | FORMAT SCHEMA CACHE | FILESYSTEM CACHE}', 'SYSTEM FLUSH LOGS', 'SYSTEM RELOAD {CONFIG | USERS}', 'SYSTEM SHUTDOWN', 'SYSTEM KILL', 'SYSTEM FLUSH DISTRIBUTED', 'SYSTEM START DISTRIBUTED SENDS', 'SYSTEM {STOP | START} {LISTEN | MERGES | TTL MERGES | MOVES | FETCHES | REPLICATED SENDS | REPLICATION QUEUES | PULLING REPLICATION LOG}', 'SYSTEM {SYNC | RESTART | RESTORE} REPLICA', 'SYSTEM {SYNC | RESTORE} DATABASE REPLICA', 'SYSTEM RESTART REPLICAS', 'SYSTEM UNFREEZE', 'SYSTEM WAIT LOADING PARTS', 'SYSTEM {LOAD | UNLOAD} PRIMARY KEY', 'SYSTEM {STOP | START} [REPLICATED] VIEW', 'SYSTEM {STOP | START} VIEWS', 'SYSTEM {REFRESH | CANCEL | WAIT} VIEW', 'WITH NAME', // https://clickhouse.com/docs/sql-reference/statements/show 'SHOW [CREATE] {TABLE | TEMPORARY TABLE | DICTIONARY | VIEW | DATABASE}', 'SHOW DATABASES [[NOT] {LIKE | ILIKE}]', 'SHOW [FULL] [TEMPORARY] TABLES [FROM | IN]', 'SHOW [EXTENDED] [FULL] COLUMNS {FROM | IN}', // https://clickhouse.com/docs/sql-reference/statements/attach 'ATTACH {TABLE | DICTIONARY | DATABASE} [IF NOT EXISTS]', // https://clickhouse.com/docs/sql-reference/statements/detach 'DETACH {TABLE | DICTIONARY | DATABASE} [IF EXISTS]', 'PERMANENTLY', 'SYNC', // https://clickhouse.com/docs/sql-reference/statements/drop 'DROP {DICTIONARY | DATABASE | PROFILE | VIEW | FUNCTION | NAMED COLLECTION} [IF EXISTS]', 'DROP [TEMPORARY] TABLE [IF EXISTS] [IF EMPTY]', // https://clickhouse.com/docs/sql-reference/statements/alter/table#rename 'RENAME TO', // https://clickhouse.com/docs/sql-reference/statements/exists 'EXISTS [TEMPORARY] {TABLE | DICTIONARY | DATABASE}', // https://clickhouse.com/docs/sql-reference/statements/kill 'KILL QUERY', // https://clickhouse.com/docs/sql-reference/statements/optimize 'OPTIMIZE TABLE', // https://clickhouse.com/docs/sql-reference/statements/rename 'RENAME {TABLE | DICTIONARY | DATABASE}', // https://clickhouse.com/docs/sql-reference/statements/exchange 'EXCHANGE {TABLES | DICTIONARIES}', // https://clickhouse.com/docs/sql-reference/statements/truncate 'TRUNCATE TABLE [IF EXISTS]', // https://clickhouse.com/docs/sql-reference/statements/execute_as 'EXECUTE AS', // https://clickhouse.com/docs/sql-reference/statements/use 'USE', 'TO', // https://clickhouse.com/docs/sql-reference/statements/undrop 'UNDROP TABLE', // https://clickhouse.com/docs/sql-reference/statements/create 'CREATE {DATABASE | NAMED COLLECTION} [IF NOT EXISTS]', 'CREATE [OR REPLACE] {VIEW | DICTIONARY} [IF NOT EXISTS]', 'CREATE MATERIALIZED VIEW [IF NOT EXISTS]', 'CREATE FUNCTION', 'CREATE {USER | ROLE | QUOTA | SETTINGS PROFILE} [IF NOT EXISTS | OR REPLACE]', 'CREATE [ROW] POLICY [IF NOT EXISTS | OR REPLACE]', // https://clickhouse.com/docs/sql-reference/statements/create/table#replace-table 'REPLACE [TEMPORARY] TABLE [IF NOT EXISTS]', // https://clickhouse.com/docs/sql-reference/statements/alter 'ALTER {ROLE | QUOTA | SETTINGS PROFILE} [IF EXISTS]', 'ALTER [TEMPORARY] TABLE', 'ALTER NAMED COLLECTION [IF EXISTS]', // https://clickhouse.com/docs/sql-reference/statements/alter/user 'GRANTEES', 'NOT IDENTIFIED', 'RESET AUTHENTICATION METHODS TO NEW', '{IDENTIFIED | ADD IDENTIFIED} [WITH | BY]', '[ADD | DROP] HOST {LOCAL | NAME | REGEXP | IP | LIKE}', 'VALID UNTIL', 'DROP [ALL] {PROFILES | SETTINGS}', '{ADD | MODIFY} SETTINGS', 'ADD PROFILES', // https://clickhouse.com/docs/sql-reference/statements/alter/apply-deleted-mask 'APPLY DELETED MASK', 'IN PARTITION', // https://clickhouse.com/docs/sql-reference/statements/alter/column '{ADD | DROP | RENAME | CLEAR | COMMENT | MODIFY | ALTER | MATERIALIZE} COLUMN', // https://clickhouse.com/docs/sql-reference/statements/alter/partition '{DETACH | DROP | ATTACH | FETCH | MOVE} {PART | PARTITION}', 'DROP DETACHED {PART | PARTITION}', '{FORGET | REPLACE} PARTITION', 'CLEAR COLUMN', '{FREEZE | UNFREEZE} [PARTITION]', 'CLEAR INDEX', 'TO {DISK | VOLUME}', '[DELETE | REWRITE PARTS] IN PARTITION', // https://clickhouse.com/docs/sql-reference/statements/alter/setting '{MODIFY | RESET} SETTING', // https://clickhouse.com/docs/sql-reference/statements/alter/delete 'DELETE WHERE', // https://clickhouse.com/docs/sql-reference/statements/alter/order-by 'MODIFY ORDER BY', // https://clickhouse.com/docs/sql-reference/statements/alter/sample-by '{MODIFY | REMOVE} SAMPLE BY', // https://clickhouse.com/docs/sql-reference/statements/alter/skipping-index '{ADD | MATERIALIZE | CLEAR} INDEX [IF NOT EXISTS]', 'DROP INDEX [IF EXISTS]', 'GRANULARITY', 'AFTER', 'FIRST', // https://clickhouse.com/docs/sql-reference/statements/alter/constraint 'ADD CONSTRAINT [IF NOT EXISTS]', 'DROP CONSTRAINT [IF EXISTS]', // https://clickhouse.com/docs/sql-reference/statements/alter/ttl 'MODIFY TTL', 'REMOVE TTL', // https://clickhouse.com/docs/sql-reference/statements/alter/statistics 'ADD STATISTICS [IF NOT EXISTS]', '{DROP | CLEAR} STATISTICS [IF EXISTS]', 'MATERIALIZE STATISTICS [ALL | IF EXISTS]', // https://clickhouse.com/docs/sql-reference/statements/alter/quota 'KEYED BY', 'NOT KEYED', 'FOR [RANDOMIZED] INTERVAL', // https://clickhouse.com/docs/sql-reference/statements/alter/row-policy 'AS {PERMISSIVE | RESTRICTIVE}', 'FOR SELECT', // https://clickhouse.com/docs/sql-reference/statements/alter/projection 'ADD PROJECTION [IF NOT EXISTS]', '{DROP | MATERIALIZE | CLEAR} PROJECTION [IF EXISTS]', // https://clickhouse.com/docs/sql-reference/statements/create/view#refreshable-materialized-view 'REFRESH {EVERY | AFTER}', 'RANDOMIZE FOR', 'APPEND', 'APPEND TO', // https://clickhouse.com/docs/sql-reference/statements/delete 'DELETE FROM', // https://clickhouse.com/docs/sql-reference/statements/explain 'EXPLAIN [AST | SYNTAX | QUERY TREE | PLAN | PIPELINE | ESTIMATE | TABLE OVERRIDE]', // https://clickhouse.com/docs/sql-reference/statements/grant 'GRANT ON CLUSTER', 'GRANT CURRENT GRANTS', 'WITH GRANT OPTION', // https://clickhouse.com/docs/sql-reference/statements/revoke 'REVOKE ON CLUSTER', 'ADMIN OPTION FOR', // https://clickhouse.com/docs/sql-reference/statements/check-table 'CHECK TABLE', 'PARTITION ID', // https://clickhouse.com/docs/sql-reference/statements/describe-table '{DESC | DESCRIBE} TABLE', ]); const reservedSetOperations = (0, expandPhrases_js_1.expandPhrases)([ // https://clickhouse.com/docs/sql-reference/statements/select/union 'UNION [ALL | DISTINCT]', // https://clickhouse.com/docs/sql-reference/statements/parallel_with 'PARALLEL WITH', ]); const reservedJoins = (0, expandPhrases_js_1.expandPhrases)([ // https://clickhouse.com/docs/sql-reference/statements/select/join '[GLOBAL] [INNER|LEFT|RIGHT|FULL|CROSS] [OUTER|SEMI|ANTI|ANY|ALL|ASOF] JOIN', // https://clickhouse.com/docs/sql-reference/statements/select/array-join '[LEFT] ARRAY JOIN', ]); const reservedKeywordPhrases = (0, expandPhrases_js_1.expandPhrases)([ '{ROWS | RANGE} BETWEEN', 'ALTER MATERIALIZE STATISTICS', ]); // https://clickhouse.com/docs/sql-reference/syntax exports.clickhouse = { name: 'clickhouse', tokenizerOptions: { reservedSelect, reservedClauses: [...reservedClauses, ...standardOnelineClauses, ...tabularOnelineClauses], reservedSetOperations, reservedJoins, reservedKeywordPhrases, reservedKeywords: clickhouse_keywords_js_1.keywords, reservedDataTypes: clickhouse_keywords_js_1.dataTypes, reservedFunctionNames: clickhouse_functions_js_1.functions, extraParens: ['[]', '{}'], lineCommentTypes: ['#', '--'], nestedBlockComments: false, underscoresInNumbers: true, stringTypes: ['$$', "''-qq-bs"], identTypes: ['""-qq-bs', '``'], paramTypes: { // https://clickhouse.com/docs/sql-reference/syntax#defining-and-using-query-parameters custom: [ { // Parameters are like {foo:Uint64} or {foo:Map(String, String)} // We include `'` in the negated character class to be a little sneaky: // map literals have quoted keys, and we use this to avoid confusing // them for named parameters. This means that the map literal `{'foo':1}` // will be formatted as `{'foo': 1}` rather than `{foo: 1}`. regex: String.raw `\{[^:']+:[^}]+\}`, key: v => { const match = /\{([^:]+):/.exec(v); return match ? match[1].trim() : v; }, }, ], }, operators: [ // Strings, arithmetic '%', '||', // Ternary '?', ':', // Comparison '==', '<=>', // Lambda creation '->', ], postProcess, }, formatOptions: { onelineClauses: [...standardOnelineClauses, ...tabularOnelineClauses], tabularOnelineClauses, }, }; /** * 1. Formats GRANT statements to use RESERVED_KEYWORD instead of RESERVED_SELECT * for SELECT GRANTs * 2. Formats SET(100) as RESERVED_FUNCTION_NAME instead of RESERVED_KEYWORD * so it appears as a function rather than a statement. */ function postProcess(tokens) { return tokens.map((token, i) => { const nextToken = tokens[i + 1] || token_js_1.EOF_TOKEN; const prevToken = tokens[i - 1] || token_js_1.EOF_TOKEN; // If we have queries like // > GRANT SELECT, INSERT ON db.table TO john // > GRANT SELECT(a, b), SELECT(c) ON db.table TO john // we want to format them as // > GRANT // > SELECT, // > INSERT ON db.table // > TO john // > GRANT // > SELECT(a, b), // > SELECT(c) ON db.table // > TO john // To do this we need to convert the SELECT keyword to a RESERVED_KEYWORD. if (token.type === token_js_1.TokenType.RESERVED_SELECT && (nextToken.type === token_js_1.TokenType.COMMA || prevToken.type === token_js_1.TokenType.RESERVED_CLAUSE || prevToken.type === token_js_1.TokenType.COMMA)) { return Object.assign(Object.assign({}, token), { type: token_js_1.TokenType.RESERVED_KEYWORD }); } // We should format `set(100)` as-is rather than `SET (100)` if (token_js_1.isToken.SET(token) && nextToken.type === token_js_1.TokenType.OPEN_PAREN) { return Object.assign(Object.assign({}, token), { type: token_js_1.TokenType.RESERVED_FUNCTION_NAME }); } return token; }); } //# sourceMappingURL=clickhouse.formatter.js.map