UNPKG

sql-formatter

Version:

Format whitespace in a SQL query to make it more readable

254 lines 8.42 kB
import { expandPhrases } from '../../expandPhrases.js'; import { EOF_TOKEN, isToken, TokenType } from '../../lexer/token.js'; import { functions } from './bigquery.functions.js'; import { dataTypes, keywords } from './bigquery.keywords.js'; const reservedSelect = expandPhrases(['SELECT [ALL | DISTINCT] [AS STRUCT | AS VALUE]']); const reservedClauses = expandPhrases([ // Queries: https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax 'WITH [RECURSIVE]', 'FROM', 'WHERE', 'GROUP BY', 'HAVING', 'QUALIFY', 'WINDOW', 'PARTITION BY', 'ORDER BY', 'LIMIT', 'OFFSET', 'OMIT RECORD IF', // Data modification: https://cloud.google.com/bigquery/docs/reference/standard-sql/dml-syntax // - insert: 'INSERT [INTO]', 'VALUES', // - update: 'SET', // - merge: 'MERGE [INTO]', 'WHEN [NOT] MATCHED [BY SOURCE | BY TARGET] [THEN]', 'UPDATE SET', 'CLUSTER BY', 'FOR SYSTEM_TIME AS OF', 'WITH CONNECTION', 'WITH PARTITION COLUMNS', 'REMOTE WITH CONNECTION', ]); const standardOnelineClauses = expandPhrases([ 'CREATE [OR REPLACE] [TEMP|TEMPORARY|SNAPSHOT|EXTERNAL] TABLE [IF NOT EXISTS]', ]); const tabularOnelineClauses = expandPhrases([ // - create: // https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language 'CREATE [OR REPLACE] [MATERIALIZED] VIEW [IF NOT EXISTS]', // - update: 'UPDATE', // - delete: 'DELETE [FROM]', // - drop table: 'DROP [SNAPSHOT | EXTERNAL] TABLE [IF EXISTS]', // - alter table: 'ALTER TABLE [IF EXISTS]', 'ADD COLUMN [IF NOT EXISTS]', 'DROP COLUMN [IF EXISTS]', 'RENAME TO', 'ALTER COLUMN [IF EXISTS]', 'SET DEFAULT COLLATE', 'SET OPTIONS', 'DROP NOT NULL', 'SET DATA TYPE', // - alter schema 'ALTER SCHEMA [IF EXISTS]', // - alter view 'ALTER [MATERIALIZED] VIEW [IF EXISTS]', // - alter bi_capacity 'ALTER BI_CAPACITY', // - truncate: 'TRUNCATE TABLE', // - create schema 'CREATE SCHEMA [IF NOT EXISTS]', 'DEFAULT COLLATE', // stored procedures 'CREATE [OR REPLACE] [TEMP|TEMPORARY|TABLE] FUNCTION [IF NOT EXISTS]', 'CREATE [OR REPLACE] PROCEDURE [IF NOT EXISTS]', // row access policy 'CREATE [OR REPLACE] ROW ACCESS POLICY [IF NOT EXISTS]', 'GRANT TO', 'FILTER USING', // capacity 'CREATE CAPACITY', 'AS JSON', // reservation 'CREATE RESERVATION', // assignment 'CREATE ASSIGNMENT', // search index 'CREATE SEARCH INDEX [IF NOT EXISTS]', // drop 'DROP SCHEMA [IF EXISTS]', 'DROP [MATERIALIZED] VIEW [IF EXISTS]', 'DROP [TABLE] FUNCTION [IF EXISTS]', 'DROP PROCEDURE [IF EXISTS]', 'DROP ROW ACCESS POLICY', 'DROP ALL ROW ACCESS POLICIES', 'DROP CAPACITY [IF EXISTS]', 'DROP RESERVATION [IF EXISTS]', 'DROP ASSIGNMENT [IF EXISTS]', 'DROP SEARCH INDEX [IF EXISTS]', 'DROP [IF EXISTS]', // DCL, https://cloud.google.com/bigquery/docs/reference/standard-sql/data-control-language 'GRANT', 'REVOKE', // Script, https://cloud.google.com/bigquery/docs/reference/standard-sql/scripting 'DECLARE', 'EXECUTE IMMEDIATE', 'LOOP', 'END LOOP', 'REPEAT', 'END REPEAT', 'WHILE', 'END WHILE', 'BREAK', 'LEAVE', 'CONTINUE', 'ITERATE', 'FOR', 'END FOR', 'BEGIN', 'BEGIN TRANSACTION', 'COMMIT TRANSACTION', 'ROLLBACK TRANSACTION', 'RAISE', 'RETURN', 'CALL', // Debug, https://cloud.google.com/bigquery/docs/reference/standard-sql/debugging-statements 'ASSERT', // Other, https://cloud.google.com/bigquery/docs/reference/standard-sql/other-statements 'EXPORT DATA', ]); const reservedSetOperations = expandPhrases([ 'UNION {ALL | DISTINCT}', 'EXCEPT DISTINCT', 'INTERSECT DISTINCT', ]); const reservedJoins = expandPhrases([ 'JOIN', '{LEFT | RIGHT | FULL} [OUTER] JOIN', '{INNER | CROSS} JOIN', ]); const reservedPhrases = expandPhrases([ // https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax#tablesample_operator 'TABLESAMPLE SYSTEM', // From DDL: https://cloud.google.com/bigquery/docs/reference/standard-sql/data-definition-language 'ANY TYPE', 'ALL COLUMNS', 'NOT DETERMINISTIC', // inside window definitions '{ROWS | RANGE} BETWEEN', // comparison operator 'IS [NOT] DISTINCT FROM', ]); // https://cloud.google.com/bigquery/docs/reference/#standard-sql-reference export const bigquery = { name: 'bigquery', tokenizerOptions: { reservedSelect, reservedClauses: [...reservedClauses, ...tabularOnelineClauses, ...standardOnelineClauses], reservedSetOperations, reservedJoins, reservedPhrases, reservedKeywords: keywords, reservedDataTypes: dataTypes, reservedFunctionNames: functions, extraParens: ['[]'], stringTypes: [ // The triple-quoted strings are listed first, so they get matched first. // Otherwise the first two quotes of """ will get matched as an empty "" string. { quote: '""".."""', prefixes: ['R', 'B', 'RB', 'BR'] }, { quote: "'''..'''", prefixes: ['R', 'B', 'RB', 'BR'] }, '""-bs', "''-bs", { quote: '""-raw', prefixes: ['R', 'B', 'RB', 'BR'], requirePrefix: true }, { quote: "''-raw", prefixes: ['R', 'B', 'RB', 'BR'], requirePrefix: true }, ], identTypes: ['``'], identChars: { dashes: true }, paramTypes: { positional: true, named: ['@'], quoted: ['@'] }, variableTypes: [{ regex: String.raw `@@\w+` }], lineCommentTypes: ['--', '#'], operators: ['&', '|', '^', '~', '>>', '<<', '||', '=>'], postProcess, }, formatOptions: { onelineClauses: [...standardOnelineClauses, ...tabularOnelineClauses], tabularOnelineClauses, }, }; function postProcess(tokens) { return detectArraySubscripts(combineParameterizedTypes(tokens)); } // Converts OFFSET token inside array from RESERVED_CLAUSE to RESERVED_FUNCTION_NAME // See: https://cloud.google.com/bigquery/docs/reference/standard-sql/functions-and-operators#array_subscript_operator function detectArraySubscripts(tokens) { let prevToken = EOF_TOKEN; return tokens.map(token => { if (token.text === 'OFFSET' && prevToken.text === '[') { prevToken = token; return Object.assign(Object.assign({}, token), { type: TokenType.RESERVED_FUNCTION_NAME }); } else { prevToken = token; return token; } }); } // Combines multiple tokens forming a parameterized type like STRUCT<ARRAY<INT64>> into a single token function combineParameterizedTypes(tokens) { var _a; const processed = []; for (let i = 0; i < tokens.length; i++) { const token = tokens[i]; if ((isToken.ARRAY(token) || isToken.STRUCT(token)) && ((_a = tokens[i + 1]) === null || _a === void 0 ? void 0 : _a.text) === '<') { const endIndex = findClosingAngleBracketIndex(tokens, i + 1); const typeDefTokens = tokens.slice(i, endIndex + 1); processed.push({ type: TokenType.IDENTIFIER, raw: typeDefTokens.map(formatTypeDefToken('raw')).join(''), text: typeDefTokens.map(formatTypeDefToken('text')).join(''), start: token.start, }); i = endIndex; } else { processed.push(token); } } return processed; } const formatTypeDefToken = (key) => (token) => { if (token.type === TokenType.IDENTIFIER || token.type === TokenType.COMMA) { return token[key] + ' '; } else { return token[key]; } }; function findClosingAngleBracketIndex(tokens, startIndex) { let level = 0; for (let i = startIndex; i < tokens.length; i++) { const token = tokens[i]; if (token.text === '<') { level++; } else if (token.text === '>') { level--; } else if (token.text === '>>') { level -= 2; } if (level === 0) { return i; } } return tokens.length - 1; } //# sourceMappingURL=bigquery.formatter.js.map