├── .eslintignore ├── .gitignore ├── .eslintrc ├── .travis.yml ├── .editorconfig ├── .babelrc ├── test ├── sqlFormatterTest.js ├── Db2FormatterTest.js ├── N1qlFormatterTest.js ├── PlSqlFormatterTest.js ├── StandardSqlFormatterTest.js └── behavesLikeSqlFormatter.js ├── src ├── core │ ├── tokenTypes.js │ ├── Params.js │ ├── Indentation.js │ ├── InlineBlock.js │ ├── Formatter.js │ └── Tokenizer.js ├── sqlFormatter.js └── languages │ ├── N1qlFormatter.js │ ├── StandardSqlFormatter.js │ ├── PlSqlFormatter.js │ └── Db2Formatter.js ├── lib ├── core │ ├── tokenTypes.js │ ├── Params.js │ ├── Indentation.js │ ├── InlineBlock.js │ ├── Formatter.js │ └── Tokenizer.js ├── sqlFormatter.js └── languages │ ├── N1qlFormatter.js │ ├── StandardSqlFormatter.js │ ├── PlSqlFormatter.js │ └── Db2Formatter.js ├── webpack.config.js ├── LICENSE ├── package.json ├── README.md ├── index.html └── dist └── sql-formatter.min.js /.eslintignore: -------------------------------------------------------------------------------- 1 | /lib 2 | /dist 3 | /coverage 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | coverage 4 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "zt/base", 3 | "env": { 4 | "jest": true 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: node 3 | 4 | install: 5 | - npm install 6 | - npm install coveralls 7 | script: 8 | - npm run check 9 | - npm run build 10 | after_script: 11 | - cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | charset = utf-8 6 | trim_trailing_whitespace = true 7 | insert_final_newline = true 8 | indent_style = space 9 | indent_size = 4 10 | 11 | [{package.json,.travis.yml}] 12 | indent_style = space 13 | indent_size = 2 14 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | ["es2015", {"loose": true}] 4 | ], 5 | "plugins": [ 6 | "add-module-exports", 7 | "transform-class-properties", 8 | "transform-function-bind", 9 | "transform-object-rest-spread", 10 | "transform-es3-member-expression-literals", 11 | "transform-es3-property-literals" 12 | ] 13 | } 14 | -------------------------------------------------------------------------------- /test/sqlFormatterTest.js: -------------------------------------------------------------------------------- 1 | import sqlFormatter from "./../src/sqlFormatter"; 2 | 3 | describe("sqlFormatter", function() { 4 | it("throws error when unsupported language parameter specified", function() { 5 | expect(() => { 6 | sqlFormatter.format("SELECT *", {language: "blah"}); 7 | }).toThrow("Unsupported SQL dialect: blah"); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /src/core/tokenTypes.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Constants for token types 3 | */ 4 | export default { 5 | WHITESPACE: "whitespace", 6 | WORD: "word", 7 | STRING: "string", 8 | RESERVED: "reserved", 9 | RESERVED_TOPLEVEL: "reserved-toplevel", 10 | RESERVED_NEWLINE: "reserved-newline", 11 | OPERATOR: "operator", 12 | OPEN_PAREN: "open-paren", 13 | CLOSE_PAREN: "close-paren", 14 | LINE_COMMENT: "line-comment", 15 | BLOCK_COMMENT: "block-comment", 16 | NUMBER: "number", 17 | PLACEHOLDER: "placeholder" 18 | }; 19 | -------------------------------------------------------------------------------- /lib/core/tokenTypes.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | /** 5 | * Constants for token types 6 | */ 7 | exports["default"] = { 8 | WHITESPACE: "whitespace", 9 | WORD: "word", 10 | STRING: "string", 11 | RESERVED: "reserved", 12 | RESERVED_TOPLEVEL: "reserved-toplevel", 13 | RESERVED_NEWLINE: "reserved-newline", 14 | OPERATOR: "operator", 15 | OPEN_PAREN: "open-paren", 16 | CLOSE_PAREN: "close-paren", 17 | LINE_COMMENT: "line-comment", 18 | BLOCK_COMMENT: "block-comment", 19 | NUMBER: "number", 20 | PLACEHOLDER: "placeholder" 21 | }; 22 | module.exports = exports["default"]; -------------------------------------------------------------------------------- /src/core/Params.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Handles placeholder replacement with given params. 3 | */ 4 | export default class Params { 5 | /** 6 | * @param {Object} params 7 | */ 8 | constructor(params) { 9 | this.params = params; 10 | this.index = 0; 11 | } 12 | 13 | /** 14 | * Returns param value that matches given placeholder with param key. 15 | * @param {Object} token 16 | * @param {String} token.key Placeholder key 17 | * @param {String} token.value Placeholder value 18 | * @return {String} param or token.value when params are missing 19 | */ 20 | get({key, value}) { 21 | if (!this.params) { 22 | return value; 23 | } 24 | if (key) { 25 | return this.params[key]; 26 | } 27 | return this.params[this.index ++]; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require("webpack"); 2 | 3 | const config = { 4 | module: { 5 | loaders: [{ 6 | test: /\.js$/, 7 | loader: "babel-loader", 8 | exclude: /node_modules/ 9 | }] 10 | }, 11 | output: { 12 | library: "sqlFormatter", 13 | libraryTarget: "umd" 14 | }, 15 | plugins: [ 16 | new webpack.optimize.OccurrenceOrderPlugin(), 17 | ] 18 | }; 19 | 20 | if (process.env.NODE_ENV === "production") { 21 | config.plugins.push( 22 | new webpack.optimize.UglifyJsPlugin({ 23 | compressor: { 24 | pure_getters: true, 25 | unsafe: true, 26 | unsafe_comps: true, 27 | warnings: false 28 | } 29 | }) 30 | ); 31 | } 32 | 33 | module.exports = config; 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016-present ZeroTurnaround LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /lib/core/Params.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | 5 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 6 | 7 | /** 8 | * Handles placeholder replacement with given params. 9 | */ 10 | var Params = function () { 11 | /** 12 | * @param {Object} params 13 | */ 14 | function Params(params) { 15 | _classCallCheck(this, Params); 16 | 17 | this.params = params; 18 | this.index = 0; 19 | } 20 | 21 | /** 22 | * Returns param value that matches given placeholder with param key. 23 | * @param {Object} token 24 | * @param {String} token.key Placeholder key 25 | * @param {String} token.value Placeholder value 26 | * @return {String} param or token.value when params are missing 27 | */ 28 | 29 | 30 | Params.prototype.get = function get(_ref) { 31 | var key = _ref.key, 32 | value = _ref.value; 33 | 34 | if (!this.params) { 35 | return value; 36 | } 37 | if (key) { 38 | return this.params[key]; 39 | } 40 | return this.params[this.index++]; 41 | }; 42 | 43 | return Params; 44 | }(); 45 | 46 | exports["default"] = Params; 47 | module.exports = exports["default"]; -------------------------------------------------------------------------------- /src/sqlFormatter.js: -------------------------------------------------------------------------------- 1 | import Db2Formatter from "./languages/Db2Formatter"; 2 | import N1qlFormatter from "./languages/N1qlFormatter"; 3 | import PlSqlFormatter from "./languages/PlSqlFormatter"; 4 | import StandardSqlFormatter from "./languages/StandardSqlFormatter"; 5 | 6 | export default { 7 | /** 8 | * Format whitespaces in a query to make it easier to read. 9 | * 10 | * @param {String} query 11 | * @param {Object} cfg 12 | * @param {String} cfg.language Query language, default is Standard SQL 13 | * @param {String} cfg.indent Characters used for indentation, default is " " (2 spaces) 14 | * @param {Object} cfg.params Collection of params for placeholder replacement 15 | * @return {String} 16 | */ 17 | format: (query, cfg) => { 18 | cfg = cfg || {}; 19 | 20 | switch (cfg.language) { 21 | case "db2": 22 | return new Db2Formatter(cfg).format(query); 23 | case "n1ql": 24 | return new N1qlFormatter(cfg).format(query); 25 | case "pl/sql": 26 | return new PlSqlFormatter(cfg).format(query); 27 | case "sql": 28 | case undefined: 29 | return new StandardSqlFormatter(cfg).format(query); 30 | default: 31 | throw Error(`Unsupported SQL dialect: ${cfg.language}`); 32 | } 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /lib/sqlFormatter.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | 5 | var _Db2Formatter = require("./languages/Db2Formatter"); 6 | 7 | var _Db2Formatter2 = _interopRequireDefault(_Db2Formatter); 8 | 9 | var _N1qlFormatter = require("./languages/N1qlFormatter"); 10 | 11 | var _N1qlFormatter2 = _interopRequireDefault(_N1qlFormatter); 12 | 13 | var _PlSqlFormatter = require("./languages/PlSqlFormatter"); 14 | 15 | var _PlSqlFormatter2 = _interopRequireDefault(_PlSqlFormatter); 16 | 17 | var _StandardSqlFormatter = require("./languages/StandardSqlFormatter"); 18 | 19 | var _StandardSqlFormatter2 = _interopRequireDefault(_StandardSqlFormatter); 20 | 21 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 22 | 23 | exports["default"] = { 24 | /** 25 | * Format whitespaces in a query to make it easier to read. 26 | * 27 | * @param {String} query 28 | * @param {Object} cfg 29 | * @param {String} cfg.language Query language, default is Standard SQL 30 | * @param {String} cfg.indent Characters used for indentation, default is " " (2 spaces) 31 | * @param {Object} cfg.params Collection of params for placeholder replacement 32 | * @return {String} 33 | */ 34 | format: function format(query, cfg) { 35 | cfg = cfg || {}; 36 | 37 | switch (cfg.language) { 38 | case "db2": 39 | return new _Db2Formatter2["default"](cfg).format(query); 40 | case "n1ql": 41 | return new _N1qlFormatter2["default"](cfg).format(query); 42 | case "pl/sql": 43 | return new _PlSqlFormatter2["default"](cfg).format(query); 44 | case "sql": 45 | case undefined: 46 | return new _StandardSqlFormatter2["default"](cfg).format(query); 47 | default: 48 | throw Error("Unsupported SQL dialect: " + cfg.language); 49 | } 50 | } 51 | }; 52 | module.exports = exports["default"]; -------------------------------------------------------------------------------- /src/core/Indentation.js: -------------------------------------------------------------------------------- 1 | import repeat from "lodash/repeat"; 2 | import last from "lodash/last"; 3 | 4 | const INDENT_TYPE_TOP_LEVEL = "top-level"; 5 | const INDENT_TYPE_BLOCK_LEVEL = "block-level"; 6 | 7 | /** 8 | * Manages indentation levels. 9 | * 10 | * There are two types of indentation levels: 11 | * 12 | * - BLOCK_LEVEL : increased by open-parenthesis 13 | * - TOP_LEVEL : increased by RESERVED_TOPLEVEL words 14 | */ 15 | export default class Indentation { 16 | /** 17 | * @param {String} indent Indent value, default is " " (2 spaces) 18 | */ 19 | constructor(indent) { 20 | this.indent = indent || " "; 21 | this.indentTypes = []; 22 | } 23 | 24 | /** 25 | * Returns current indentation string. 26 | * @return {String} 27 | */ 28 | getIndent() { 29 | return repeat(this.indent, this.indentTypes.length); 30 | } 31 | 32 | /** 33 | * Increases indentation by one top-level indent. 34 | */ 35 | increaseToplevel() { 36 | this.indentTypes.push(INDENT_TYPE_TOP_LEVEL); 37 | } 38 | 39 | /** 40 | * Increases indentation by one block-level indent. 41 | */ 42 | increaseBlockLevel() { 43 | this.indentTypes.push(INDENT_TYPE_BLOCK_LEVEL); 44 | } 45 | 46 | /** 47 | * Decreases indentation by one top-level indent. 48 | * Does nothing when the previous indent is not top-level. 49 | */ 50 | decreaseTopLevel() { 51 | if (last(this.indentTypes) === INDENT_TYPE_TOP_LEVEL) { 52 | this.indentTypes.pop(); 53 | } 54 | } 55 | 56 | /** 57 | * Decreases indentation by one block-level indent. 58 | * If there are top-level indents within the block-level indent, 59 | * throws away these as well. 60 | */ 61 | decreaseBlockLevel() { 62 | while (this.indentTypes.length > 0) { 63 | const type = this.indentTypes.pop(); 64 | if (type !== INDENT_TYPE_TOP_LEVEL) { 65 | break; 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /test/Db2FormatterTest.js: -------------------------------------------------------------------------------- 1 | import sqlFormatter from "./../src/sqlFormatter"; 2 | import behavesLikeSqlFormatter from "./behavesLikeSqlFormatter"; 3 | 4 | describe("Db2Formatter", function() { 5 | behavesLikeSqlFormatter("db2"); 6 | 7 | it("formats FETCH FIRST like LIMIT", function() { 8 | expect(sqlFormatter.format( 9 | "SELECT col1 FROM tbl ORDER BY col2 DESC FETCH FIRST 20 ROWS ONLY;", 10 | {language: "db2"} 11 | )).toBe( 12 | "SELECT\n" + 13 | " col1\n" + 14 | "FROM\n" + 15 | " tbl\n" + 16 | "ORDER BY\n" + 17 | " col2 DESC\n" + 18 | "FETCH FIRST\n" + 19 | " 20 ROWS ONLY;" 20 | ); 21 | }); 22 | 23 | it("formats only -- as a line comment", function() { 24 | const result = sqlFormatter.format( 25 | "SELECT col FROM\n" + 26 | "-- This is a comment\n" + 27 | "MyTable;\n", 28 | {language: "db2"} 29 | ); 30 | expect(result).toBe( 31 | "SELECT\n" + 32 | " col\n" + 33 | "FROM\n" + 34 | " -- This is a comment\n" + 35 | " MyTable;" 36 | ); 37 | }); 38 | 39 | it("recognizes @ and # as part of identifiers", function() { 40 | const result = sqlFormatter.format( 41 | "SELECT col#1, @col2 FROM tbl\n", 42 | {language: "db2"} 43 | ); 44 | expect(result).toBe( 45 | "SELECT\n" + 46 | " col#1,\n" + 47 | " @col2\n" + 48 | "FROM\n" + 49 | " tbl" 50 | ); 51 | }); 52 | 53 | it("recognizes :variables", function() { 54 | expect(sqlFormatter.format("SELECT :variable;", {language: "db2"})).toBe( 55 | "SELECT\n" + 56 | " :variable;" 57 | ); 58 | }); 59 | 60 | it("replaces :variables with param values", function() { 61 | const result = sqlFormatter.format( 62 | "SELECT :variable", 63 | {language: "db2", params: {"variable": "\"variable value\""}} 64 | ); 65 | expect(result).toBe( 66 | "SELECT\n" + 67 | " \"variable value\"" 68 | ); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sql-formatter", 3 | "version": "2.3.3", 4 | "description": "Formats whitespaces in a SQL query to make it more readable", 5 | "license": "MIT", 6 | "main": "lib/sqlFormatter.js", 7 | "keywords": [ 8 | "sql", 9 | "formatter", 10 | "format", 11 | "n1ql", 12 | "whitespaces" 13 | ], 14 | "authors": [ 15 | "Rene Saarsoo", 16 | "Uku Pattak" 17 | ], 18 | "files": [ 19 | "dist", 20 | "lib", 21 | "src" 22 | ], 23 | "scripts": { 24 | "clean": "rimraf lib dist", 25 | "lint": "eslint .", 26 | "test": "jest", 27 | "test:watch": "npm run test -- --watch", 28 | "check": "npm run lint && npm run test", 29 | "prepare": "npm run build", 30 | "build": "npm run build:commonjs && npm run build:umd && npm run build:umd:min", 31 | "build:commonjs": "babel src --out-dir lib", 32 | "build:umd": "cross-env NODE_ENV=development webpack src/sqlFormatter.js dist/sql-formatter.js", 33 | "build:umd:min": "cross-env NODE_ENV=production webpack src/sqlFormatter.js dist/sql-formatter.min.js", 34 | "prepublishOnly": "npm run clean && npm run check && npm run build" 35 | }, 36 | "repository": { 37 | "type": "git", 38 | "url": "https://github.com/zeroturnaround/sql-formatter.git" 39 | }, 40 | "bugs": { 41 | "url": "https://github.com/zeroturnaround/sql-formatter/issues" 42 | }, 43 | "dependencies": { 44 | "lodash": "^4.16.0" 45 | }, 46 | "devDependencies": { 47 | "babel-cli": "^6.14.0", 48 | "babel-core": "^6.11.4", 49 | "babel-eslint": "^7.1.0", 50 | "babel-jest": "^17.0.2", 51 | "babel-loader": "^6.2.4", 52 | "babel-plugin-add-module-exports": "^0.2.1", 53 | "babel-plugin-transform-class-properties": "^6.11.5", 54 | "babel-plugin-transform-es3-member-expression-literals": "^6.5.0", 55 | "babel-plugin-transform-es3-property-literals": "^6.5.0", 56 | "babel-plugin-transform-function-bind": "^6.8.0", 57 | "babel-plugin-transform-object-rest-spread": "^6.8.0", 58 | "babel-preset-es2015": "^6.14.0", 59 | "cross-env": "^3.1.3", 60 | "eslint": "^3.7.1", 61 | "eslint-config-zt": "^1.3.0", 62 | "eslint-plugin-react": "^7.6.1", 63 | "jest": "^17.0.2", 64 | "rimraf": "^2.3.4", 65 | "webpack": "^1.13.1" 66 | }, 67 | "jest": { 68 | "testPathDirs": [ 69 | "test" 70 | ], 71 | "testRegex": ".*Test", 72 | "collectCoverage": true 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SQL Formatter [![NPM version](https://img.shields.io/npm/v/sql-formatter.svg)](https://npmjs.com/package/sql-formatter) [![Build Status](https://travis-ci.org/zeroturnaround/sql-formatter.svg?branch=master)](https://travis-ci.org/zeroturnaround/sql-formatter) [![Coverage Status](https://coveralls.io/repos/github/zeroturnaround/sql-formatter/badge.svg?branch=master)](https://coveralls.io/github/zeroturnaround/sql-formatter?branch=master) 2 | 3 | **SQL Formatter** is a JavaScript library for pretty-printing SQL queries. 4 | It started as a port of a [PHP Library][], but has since considerably diverged. 5 | It supports [Standard SQL][], [Couchbase N1QL][], [IBM DB2][] and [Oracle PL/SQL][] dialects. 6 | 7 | → [Try the demo.](https://zeroturnaround.github.io/sql-formatter/) 8 | 9 | ## Install 10 | 11 | Get the latest version from NPM: 12 | 13 | ``` 14 | npm install sql-formatter 15 | ``` 16 | 17 | ## Usage 18 | 19 | ```js 20 | import sqlFormatter from "sql-formatter"; 21 | 22 | console.log(sqlFormatter.format("SELECT * FROM table1")); 23 | ``` 24 | 25 | This will output: 26 | 27 | ``` 28 | SELECT 29 | * 30 | FROM 31 | table1 32 | ``` 33 | 34 | You can also pass in configuration options: 35 | 36 | ```js 37 | sqlFormatter.format("SELECT *", { 38 | language: "n1ql", // Defaults to "sql" 39 | indent: " " // Defaults to two spaces 40 | }); 41 | ``` 42 | 43 | Currently just four SQL dialects are supported: 44 | 45 | - **sql** - [Standard SQL][] 46 | - **n1ql** - [Couchbase N1QL][] 47 | - **db2** - [IBM DB2][] 48 | - **pl/sql** - [Oracle PL/SQL][] 49 | 50 | ### Placeholders replacement 51 | 52 | ```js 53 | // Named placeholders 54 | sqlFormatter.format("SELECT * FROM tbl WHERE foo = @foo", { 55 | params: {foo: "'bar'"} 56 | })); 57 | 58 | // Indexed placeholders 59 | sqlFormatter.format("SELECT * FROM tbl WHERE foo = ?", { 60 | params: ["'bar'"] 61 | })); 62 | ``` 63 | 64 | Both result in: 65 | 66 | ``` 67 | SELECT 68 | * 69 | FROM 70 | tbl 71 | WHERE 72 | foo = 'bar' 73 | ``` 74 | 75 | ## Usage without NPM 76 | 77 | If you don't use a module bundler, clone the repository, run `npm install` and grab a file from `/dist` directory to use inside a ` 113 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /src/languages/StandardSqlFormatter.js: -------------------------------------------------------------------------------- 1 | import Formatter from "../core/Formatter"; 2 | import Tokenizer from "../core/Tokenizer"; 3 | 4 | const reservedWords = [ 5 | "ACCESSIBLE", "ACTION", "AGAINST", "AGGREGATE", "ALGORITHM", "ALL", "ALTER", "ANALYSE", "ANALYZE", "AS", "ASC", "AUTOCOMMIT", 6 | "AUTO_INCREMENT", 7 | "BACKUP", "BEGIN", "BETWEEN", "BINLOG", "BOTH", 8 | "CASCADE", "CASE", "CHANGE", "CHANGED", "CHARACTER SET", "CHARSET", "CHECK", "CHECKSUM", "COLLATE", "COLLATION", "COLUMN", "COLUMNS", 9 | "COMMENT", "COMMIT", "COMMITTED", "COMPRESSED", "CONCURRENT", "CONSTRAINT", "CONTAINS", "CONVERT", "CREATE", "CROSS", 10 | "CURRENT_TIMESTAMP", 11 | "DATABASE", "DATABASES", "DAY", "DAY_HOUR", "DAY_MINUTE", "DAY_SECOND", "DEFAULT", "DEFINER", "DELAYED", "DELETE", "DESC", "DESCRIBE", 12 | "DETERMINISTIC", "DISTINCT", "DISTINCTROW", "DIV", "DO", "DROP", "DUMPFILE", "DUPLICATE", "DYNAMIC", 13 | "ELSE", "ENCLOSED", "END", "ENGINE", "ENGINES", "ENGINE_TYPE", "ESCAPE", "ESCAPED", "EVENTS", "EXEC", "EXECUTE", "EXISTS", "EXPLAIN", 14 | "EXTENDED", 15 | "FAST", "FETCH", "FIELDS", "FILE", "FIRST", "FIXED", "FLUSH", "FOR", "FORCE", "FOREIGN", "FULL", "FULLTEXT", "FUNCTION", 16 | "GLOBAL", "GRANT", "GRANTS", "GROUP_CONCAT", 17 | "HEAP", "HIGH_PRIORITY", "HOSTS", "HOUR", "HOUR_MINUTE", "HOUR_SECOND", 18 | "IDENTIFIED", "IF", "IFNULL", "IGNORE", "IN", "INDEX", "INDEXES", "INFILE", "INSERT", "INSERT_ID", "INSERT_METHOD", "INTERVAL", 19 | "INTO", "INVOKER", "IS", "ISOLATION", 20 | "KEY", "KEYS", "KILL", 21 | "LAST_INSERT_ID", "LEADING", "LEVEL", "LIKE", "LINEAR", "LINES", "LOAD", "LOCAL", "LOCK", "LOCKS", "LOGS", "LOW_PRIORITY", 22 | "MARIA", "MASTER", "MASTER_CONNECT_RETRY", "MASTER_HOST", "MASTER_LOG_FILE", "MATCH", "MAX_CONNECTIONS_PER_HOUR", 23 | "MAX_QUERIES_PER_HOUR", "MAX_ROWS", "MAX_UPDATES_PER_HOUR", "MAX_USER_CONNECTIONS", "MEDIUM", "MERGE", "MINUTE", "MINUTE_SECOND", 24 | "MIN_ROWS", "MODE", "MODIFY", "MONTH", "MRG_MYISAM", "MYISAM", 25 | "NAMES", "NATURAL", "NOT", "NOW()", "NULL", 26 | "OFFSET", "ON DELETE", "ON UPDATE", "ON", "ONLY", "OPEN", "OPTIMIZE", "OPTION", "OPTIONALLY", "OUTFILE", 27 | "PACK_KEYS", "PAGE", "PARTIAL", "PARTITION", "PARTITIONS", "PASSWORD", "PRIMARY", "PRIVILEGES", "PROCEDURE", "PROCESS", "PROCESSLIST", 28 | "PURGE", 29 | "QUICK", 30 | "RAID0", "RAID_CHUNKS", "RAID_CHUNKSIZE", "RAID_TYPE", "RANGE", "READ", "READ_ONLY", "READ_WRITE", "REFERENCES", "REGEXP", "RELOAD", 31 | "RENAME", "REPAIR", "REPEATABLE", "REPLACE", "REPLICATION", "RESET", "RESTORE", "RESTRICT", "RETURN", "RETURNS", "REVOKE", "RLIKE", 32 | "ROLLBACK", "ROW", "ROWS", "ROW_FORMAT", 33 | "SECOND", "SECURITY", "SEPARATOR", "SERIALIZABLE", "SESSION", "SHARE", "SHOW", "SHUTDOWN", "SLAVE", "SONAME", "SOUNDS", "SQL", 34 | "SQL_AUTO_IS_NULL", "SQL_BIG_RESULT", "SQL_BIG_SELECTS", "SQL_BIG_TABLES", "SQL_BUFFER_RESULT", "SQL_CACHE", "SQL_CALC_FOUND_ROWS", 35 | "SQL_LOG_BIN", "SQL_LOG_OFF", "SQL_LOG_UPDATE", "SQL_LOW_PRIORITY_UPDATES", "SQL_MAX_JOIN_SIZE", "SQL_NO_CACHE", 36 | "SQL_QUOTE_SHOW_CREATE", "SQL_SAFE_UPDATES", "SQL_SELECT_LIMIT", "SQL_SLAVE_SKIP_COUNTER", "SQL_SMALL_RESULT", "SQL_WARNINGS", 37 | "START", "STARTING", "STATUS", "STOP", "STORAGE", "STRAIGHT_JOIN", "STRING", "STRIPED", "SUPER", 38 | "TABLE", "TABLES", "TEMPORARY", "TERMINATED", "THEN", "TO", "TRAILING", "TRANSACTIONAL", "TRUE", "TRUNCATE", "TYPE", "TYPES", 39 | "UNCOMMITTED", "UNIQUE", "UNLOCK", "UNSIGNED", "USAGE", "USE", "USING", 40 | "VARIABLES", "VIEW", "WHEN", "WITH", "WORK", "WRITE", 41 | "YEAR_MONTH" 42 | ]; 43 | 44 | const reservedToplevelWords = [ 45 | "ADD", "AFTER", "ALTER COLUMN", "ALTER TABLE", 46 | "DELETE FROM", 47 | "EXCEPT", 48 | "FETCH FIRST", "FROM", 49 | "GROUP BY", "GO", 50 | "HAVING", 51 | "INSERT INTO", "INSERT", "INTERSECT", 52 | "LIMIT", 53 | "MODIFY", 54 | "ORDER BY", 55 | "SELECT", "SET CURRENT SCHEMA", "SET SCHEMA", "SET", 56 | "UNION ALL", "UNION", "UPDATE", 57 | "VALUES", 58 | "WHERE" 59 | ]; 60 | 61 | const reservedNewlineWords = [ 62 | "AND", 63 | "CROSS APPLY", "CROSS JOIN", 64 | "ELSE", 65 | "INNER JOIN", 66 | "JOIN", 67 | "LEFT JOIN", "LEFT OUTER JOIN", 68 | "OR", "OUTER APPLY", "OUTER JOIN", 69 | "RIGHT JOIN", "RIGHT OUTER JOIN", 70 | "WHEN", 71 | "XOR" 72 | ]; 73 | 74 | let tokenizer; 75 | 76 | export default class StandardSqlFormatter { 77 | /** 78 | * @param {Object} cfg Different set of configurations 79 | */ 80 | constructor(cfg) { 81 | this.cfg = cfg; 82 | } 83 | 84 | /** 85 | * Format the whitespace in a Standard SQL string to make it easier to read 86 | * 87 | * @param {String} query The Standard SQL string 88 | * @return {String} formatted string 89 | */ 90 | format(query) { 91 | if (!tokenizer) { 92 | tokenizer = new Tokenizer({ 93 | reservedWords, 94 | reservedToplevelWords, 95 | reservedNewlineWords, 96 | stringTypes: [`""`, "N''", "''", "``", "[]", "{{}}"], 97 | openParens: ["(", "CASE"], 98 | closeParens: [")", "END"], 99 | indexedPlaceholderTypes: ["?"], 100 | namedPlaceholderTypes: ["@", ":"], 101 | lineCommentTypes: ["#", "--"] 102 | }); 103 | } 104 | return new Formatter(this.cfg, tokenizer).format(query); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /lib/languages/StandardSqlFormatter.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | 5 | var _Formatter = require("../core/Formatter"); 6 | 7 | var _Formatter2 = _interopRequireDefault(_Formatter); 8 | 9 | var _Tokenizer = require("../core/Tokenizer"); 10 | 11 | var _Tokenizer2 = _interopRequireDefault(_Tokenizer); 12 | 13 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 14 | 15 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 16 | 17 | var reservedWords = ["ACCESSIBLE", "ACTION", "AGAINST", "AGGREGATE", "ALGORITHM", "ALL", "ALTER", "ANALYSE", "ANALYZE", "AS", "ASC", "AUTOCOMMIT", "AUTO_INCREMENT", "BACKUP", "BEGIN", "BETWEEN", "BINLOG", "BOTH", "CASCADE", "CASE", "CHANGE", "CHANGED", "CHARACTER SET", "CHARSET", "CHECK", "CHECKSUM", "COLLATE", "COLLATION", "COLUMN", "COLUMNS", "COMMENT", "COMMIT", "COMMITTED", "COMPRESSED", "CONCURRENT", "CONSTRAINT", "CONTAINS", "CONVERT", "CREATE", "CROSS", "CURRENT_TIMESTAMP", "DATABASE", "DATABASES", "DAY", "DAY_HOUR", "DAY_MINUTE", "DAY_SECOND", "DEFAULT", "DEFINER", "DELAYED", "DELETE", "DESC", "DESCRIBE", "DETERMINISTIC", "DISTINCT", "DISTINCTROW", "DIV", "DO", "DROP", "DUMPFILE", "DUPLICATE", "DYNAMIC", "ELSE", "ENCLOSED", "END", "ENGINE", "ENGINES", "ENGINE_TYPE", "ESCAPE", "ESCAPED", "EVENTS", "EXEC", "EXECUTE", "EXISTS", "EXPLAIN", "EXTENDED", "FAST", "FETCH", "FIELDS", "FILE", "FIRST", "FIXED", "FLUSH", "FOR", "FORCE", "FOREIGN", "FULL", "FULLTEXT", "FUNCTION", "GLOBAL", "GRANT", "GRANTS", "GROUP_CONCAT", "HEAP", "HIGH_PRIORITY", "HOSTS", "HOUR", "HOUR_MINUTE", "HOUR_SECOND", "IDENTIFIED", "IF", "IFNULL", "IGNORE", "IN", "INDEX", "INDEXES", "INFILE", "INSERT", "INSERT_ID", "INSERT_METHOD", "INTERVAL", "INTO", "INVOKER", "IS", "ISOLATION", "KEY", "KEYS", "KILL", "LAST_INSERT_ID", "LEADING", "LEVEL", "LIKE", "LINEAR", "LINES", "LOAD", "LOCAL", "LOCK", "LOCKS", "LOGS", "LOW_PRIORITY", "MARIA", "MASTER", "MASTER_CONNECT_RETRY", "MASTER_HOST", "MASTER_LOG_FILE", "MATCH", "MAX_CONNECTIONS_PER_HOUR", "MAX_QUERIES_PER_HOUR", "MAX_ROWS", "MAX_UPDATES_PER_HOUR", "MAX_USER_CONNECTIONS", "MEDIUM", "MERGE", "MINUTE", "MINUTE_SECOND", "MIN_ROWS", "MODE", "MODIFY", "MONTH", "MRG_MYISAM", "MYISAM", "NAMES", "NATURAL", "NOT", "NOW()", "NULL", "OFFSET", "ON DELETE", "ON UPDATE", "ON", "ONLY", "OPEN", "OPTIMIZE", "OPTION", "OPTIONALLY", "OUTFILE", "PACK_KEYS", "PAGE", "PARTIAL", "PARTITION", "PARTITIONS", "PASSWORD", "PRIMARY", "PRIVILEGES", "PROCEDURE", "PROCESS", "PROCESSLIST", "PURGE", "QUICK", "RAID0", "RAID_CHUNKS", "RAID_CHUNKSIZE", "RAID_TYPE", "RANGE", "READ", "READ_ONLY", "READ_WRITE", "REFERENCES", "REGEXP", "RELOAD", "RENAME", "REPAIR", "REPEATABLE", "REPLACE", "REPLICATION", "RESET", "RESTORE", "RESTRICT", "RETURN", "RETURNS", "REVOKE", "RLIKE", "ROLLBACK", "ROW", "ROWS", "ROW_FORMAT", "SECOND", "SECURITY", "SEPARATOR", "SERIALIZABLE", "SESSION", "SHARE", "SHOW", "SHUTDOWN", "SLAVE", "SONAME", "SOUNDS", "SQL", "SQL_AUTO_IS_NULL", "SQL_BIG_RESULT", "SQL_BIG_SELECTS", "SQL_BIG_TABLES", "SQL_BUFFER_RESULT", "SQL_CACHE", "SQL_CALC_FOUND_ROWS", "SQL_LOG_BIN", "SQL_LOG_OFF", "SQL_LOG_UPDATE", "SQL_LOW_PRIORITY_UPDATES", "SQL_MAX_JOIN_SIZE", "SQL_NO_CACHE", "SQL_QUOTE_SHOW_CREATE", "SQL_SAFE_UPDATES", "SQL_SELECT_LIMIT", "SQL_SLAVE_SKIP_COUNTER", "SQL_SMALL_RESULT", "SQL_WARNINGS", "START", "STARTING", "STATUS", "STOP", "STORAGE", "STRAIGHT_JOIN", "STRING", "STRIPED", "SUPER", "TABLE", "TABLES", "TEMPORARY", "TERMINATED", "THEN", "TO", "TRAILING", "TRANSACTIONAL", "TRUE", "TRUNCATE", "TYPE", "TYPES", "UNCOMMITTED", "UNIQUE", "UNLOCK", "UNSIGNED", "USAGE", "USE", "USING", "VARIABLES", "VIEW", "WHEN", "WITH", "WORK", "WRITE", "YEAR_MONTH"]; 18 | 19 | var reservedToplevelWords = ["ADD", "AFTER", "ALTER COLUMN", "ALTER TABLE", "DELETE FROM", "EXCEPT", "FETCH FIRST", "FROM", "GROUP BY", "GO", "HAVING", "INSERT INTO", "INSERT", "INTERSECT", "LIMIT", "MODIFY", "ORDER BY", "SELECT", "SET CURRENT SCHEMA", "SET SCHEMA", "SET", "UNION ALL", "UNION", "UPDATE", "VALUES", "WHERE"]; 20 | 21 | var reservedNewlineWords = ["AND", "CROSS APPLY", "CROSS JOIN", "ELSE", "INNER JOIN", "JOIN", "LEFT JOIN", "LEFT OUTER JOIN", "OR", "OUTER APPLY", "OUTER JOIN", "RIGHT JOIN", "RIGHT OUTER JOIN", "WHEN", "XOR"]; 22 | 23 | var tokenizer = void 0; 24 | 25 | var StandardSqlFormatter = function () { 26 | /** 27 | * @param {Object} cfg Different set of configurations 28 | */ 29 | function StandardSqlFormatter(cfg) { 30 | _classCallCheck(this, StandardSqlFormatter); 31 | 32 | this.cfg = cfg; 33 | } 34 | 35 | /** 36 | * Format the whitespace in a Standard SQL string to make it easier to read 37 | * 38 | * @param {String} query The Standard SQL string 39 | * @return {String} formatted string 40 | */ 41 | 42 | 43 | StandardSqlFormatter.prototype.format = function format(query) { 44 | if (!tokenizer) { 45 | tokenizer = new _Tokenizer2["default"]({ 46 | reservedWords: reservedWords, 47 | reservedToplevelWords: reservedToplevelWords, 48 | reservedNewlineWords: reservedNewlineWords, 49 | stringTypes: ["\"\"", "N''", "''", "``", "[]", "{{}}"], 50 | openParens: ["(", "CASE"], 51 | closeParens: [")", "END"], 52 | indexedPlaceholderTypes: ["?"], 53 | namedPlaceholderTypes: ["@", ":"], 54 | lineCommentTypes: ["#", "--"] 55 | }); 56 | } 57 | return new _Formatter2["default"](this.cfg, tokenizer).format(query); 58 | }; 59 | 60 | return StandardSqlFormatter; 61 | }(); 62 | 63 | exports["default"] = StandardSqlFormatter; 64 | module.exports = exports["default"]; -------------------------------------------------------------------------------- /src/languages/PlSqlFormatter.js: -------------------------------------------------------------------------------- 1 | import Formatter from "../core/Formatter"; 2 | import Tokenizer from "../core/Tokenizer"; 3 | 4 | const reservedWords = [ 5 | "A", "ACCESSIBLE", "AGENT", "AGGREGATE", "ALL", "ALTER", "ANY", "ARRAY", "AS", "ASC", "AT", "ATTRIBUTE", "AUTHID", "AVG", 6 | "BETWEEN", "BFILE_BASE", "BINARY_INTEGER", "BINARY", "BLOB_BASE", "BLOCK", "BODY", "BOOLEAN", "BOTH", "BOUND", 7 | "BULK", "BY", "BYTE", 8 | "C", "CALL", "CALLING", "CASCADE", "CASE", "CHAR_BASE", "CHAR", "CHARACTER", "CHARSET", "CHARSETFORM", "CHARSETID", 9 | "CHECK", "CLOB_BASE", "CLONE", "CLOSE", "CLUSTER", "CLUSTERS", "COALESCE", "COLAUTH", "COLLECT", "COLUMNS", "COMMENT", 10 | "COMMIT", "COMMITTED", "COMPILED", "COMPRESS", "CONNECT", "CONSTANT", "CONSTRUCTOR", "CONTEXT", "CONTINUE", "CONVERT", 11 | "COUNT", "CRASH", "CREATE", "CREDENTIAL", "CURRENT", "CURRVAL", "CURSOR", "CUSTOMDATUM", 12 | "DANGLING", "DATA", "DATE_BASE", "DATE", "DAY", "DECIMAL", "DEFAULT", "DEFINE", "DELETE", "DESC", 13 | "DETERMINISTIC", "DIRECTORY", "DISTINCT", "DO", "DOUBLE", "DROP", "DURATION", 14 | "ELEMENT", "ELSIF", "EMPTY", "ESCAPE", "EXCEPTIONS", "EXCLUSIVE", "EXECUTE", "EXISTS", 15 | "EXIT", "EXTENDS", "EXTERNAL", "EXTRACT", 16 | "FALSE", "FETCH", "FINAL", "FIRST", "FIXED", "FLOAT", "FOR", "FORALL", "FORCE", "FROM", "FUNCTION", 17 | "GENERAL", "GOTO", "GRANT", "GROUP", "HASH", "HEAP", "HIDDEN", "HOUR", 18 | "IDENTIFIED", "IF", "IMMEDIATE", "IN", "INCLUDING", "INDEX", "INDEXES", "INDICATOR", "INDICES", "INFINITE", 19 | "INSTANTIABLE", "INT", "INTEGER", "INTERFACE", "INTERVAL", "INTO", "INVALIDATE", "IS", "ISOLATION", 20 | "JAVA", 21 | "LANGUAGE", "LARGE", "LEADING", "LENGTH", "LEVEL", "LIBRARY", "LIKE", "LIKE2", "LIKE4", "LIKEC", "LIMITED", "LOCAL", 22 | "LOCK", "LONG", 23 | "MAP", "MAX", "MAXLEN", "MEMBER", "MERGE", "MIN", "MINUS", "MINUTE", "MLSLABEL", "MOD", "MODE", "MONTH", "MULTISET", 24 | "NAME", "NAN", "NATIONAL", "NATIVE", "NATURAL", "NATURALN", "NCHAR", "NEW", "NEXTVAL", "NOCOMPRESS", "NOCOPY", "NOT", 25 | "NOWAIT", "NULL", "NULLIF", "NUMBER_BASE", "NUMBER", 26 | "OBJECT", "OCICOLL", "OCIDATE", "OCIDATETIME", "OCIDURATION", "OCIINTERVAL", "OCILOBLOCATOR", "OCINUMBER", "OCIRAW", 27 | "OCIREF", "OCIREFCURSOR", "OCIROWID", "OCISTRING", "OCITYPE", "OF", "OLD", "ON", "ONLY", "OPAQUE", "OPEN", "OPERATOR", 28 | "OPTION", "ORACLE", "ORADATA", "ORDER", "ORGANIZATION", "ORLANY", "ORLVARY", "OTHERS", "OUT", "OVERLAPS", 29 | "OVERRIDING", 30 | "PACKAGE", "PARALLEL_ENABLE", "PARAMETER", "PARAMETERS", "PARENT", "PARTITION", "PASCAL", "PCTFREE", "PIPE", "PIPELINED", 31 | "PLS_INTEGER", "PLUGGABLE", "POSITIVE", "POSITIVEN", "PRAGMA", "PRECISION", "PRIOR", "PRIVATE", "PROCEDURE", "PUBLIC", 32 | "RAISE", "RANGE", "RAW", "READ", "REAL", "RECORD", "REF", "REFERENCE", "RELEASE", "RELIES_ON", "REM", "REMAINDER", 33 | "RENAME", "RESOURCE", "RESULT_CACHE", "RESULT", "RETURN", "RETURNING", "REVERSE", "REVOKE", "ROLLBACK", "ROW", "ROWID", 34 | "ROWNUM", "ROWTYPE", 35 | "SAMPLE", "SAVE", "SAVEPOINT", "SB1", "SB2", "SB4", "SECOND", "SEGMENT", "SELF", "SEPARATE", "SEQUENCE", 36 | "SERIALIZABLE", "SHARE", "SHORT", "SIZE_T", "SIZE", "SMALLINT", "SOME", "SPACE", "SPARSE", "SQL", "SQLCODE", 37 | "SQLDATA", "SQLERRM", "SQLNAME", "SQLSTATE", "STANDARD", "START", "STATIC", "STDDEV", "STORED", "STRING", "STRUCT", 38 | "STYLE", "SUBMULTISET", "SUBPARTITION", "SUBSTITUTABLE", "SUBTYPE", "SUCCESSFUL", "SUM", "SYNONYM", "SYSDATE", 39 | "TABAUTH", "TABLE", "TDO", "THE", "THEN", "TIME", "TIMESTAMP", "TIMEZONE_ABBR", "TIMEZONE_HOUR", "TIMEZONE_MINUTE", 40 | "TIMEZONE_REGION", "TO", "TRAILING", "TRANSACTION", "TRANSACTIONAL", "TRIGGER", "TRUE", "TRUSTED", "TYPE", 41 | "UB1", "UB2", "UB4", "UID", "UNDER", "UNIQUE", "UNPLUG", "UNSIGNED", "UNTRUSTED", "USE", "USER", "USING", 42 | "VALIDATE", "VALIST", "VALUE", "VARCHAR", "VARCHAR2", "VARIABLE", "VARIANCE", "VARRAY", "VARYING", "VIEW", "VIEWS", "VOID", 43 | "WHENEVER", "WHILE", "WITH", "WORK", "WRAPPED", "WRITE", 44 | "YEAR", 45 | "ZONE", 46 | ]; 47 | 48 | const reservedToplevelWords = [ 49 | "ADD", "ALTER COLUMN", "ALTER TABLE", 50 | "BEGIN", 51 | "CONNECT BY", 52 | "DECLARE", "DELETE FROM", "DELETE", 53 | "END", "EXCEPT", "EXCEPTION", 54 | "FETCH FIRST", "FROM", 55 | "GROUP BY", 56 | "HAVING", 57 | "INSERT INTO", "INSERT", "INTERSECT", 58 | "LIMIT", "LOOP", 59 | "MODIFY", 60 | "ORDER BY", 61 | "SELECT", "SET CURRENT SCHEMA", "SET SCHEMA", "SET", "START WITH", 62 | "UNION ALL", "UNION", "UPDATE", 63 | "VALUES", 64 | "WHERE" 65 | ]; 66 | 67 | const reservedNewlineWords = [ 68 | "AND", 69 | "CROSS APPLY", "CROSS JOIN", 70 | "ELSE", "END", 71 | "INNER JOIN", 72 | "JOIN", 73 | "LEFT JOIN", "LEFT OUTER JOIN", 74 | "OR", "OUTER APPLY", "OUTER JOIN", 75 | "RIGHT JOIN", "RIGHT OUTER JOIN", 76 | "WHEN", 77 | "XOR" 78 | ]; 79 | 80 | let tokenizer; 81 | 82 | export default class PlSqlFormatter { 83 | /** 84 | * @param {Object} cfg Different set of configurations 85 | */ 86 | constructor(cfg) { 87 | this.cfg = cfg; 88 | } 89 | 90 | /** 91 | * Format the whitespace in a PL/SQL string to make it easier to read 92 | * 93 | * @param {String} query The PL/SQL string 94 | * @return {String} formatted string 95 | */ 96 | format(query) { 97 | if (!tokenizer) { 98 | tokenizer = new Tokenizer({ 99 | reservedWords, 100 | reservedToplevelWords, 101 | reservedNewlineWords, 102 | stringTypes: [`""`, "N''", "''", "``"], 103 | openParens: ["(", "CASE"], 104 | closeParens: [")", "END"], 105 | indexedPlaceholderTypes: ["?"], 106 | namedPlaceholderTypes: [":"], 107 | lineCommentTypes: ["--"], 108 | specialWordChars: ["_", "$", "#", ".", "@"] 109 | }); 110 | } 111 | return new Formatter(this.cfg, tokenizer).format(query); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /lib/languages/PlSqlFormatter.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | 5 | var _Formatter = require("../core/Formatter"); 6 | 7 | var _Formatter2 = _interopRequireDefault(_Formatter); 8 | 9 | var _Tokenizer = require("../core/Tokenizer"); 10 | 11 | var _Tokenizer2 = _interopRequireDefault(_Tokenizer); 12 | 13 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 14 | 15 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 16 | 17 | var reservedWords = ["A", "ACCESSIBLE", "AGENT", "AGGREGATE", "ALL", "ALTER", "ANY", "ARRAY", "AS", "ASC", "AT", "ATTRIBUTE", "AUTHID", "AVG", "BETWEEN", "BFILE_BASE", "BINARY_INTEGER", "BINARY", "BLOB_BASE", "BLOCK", "BODY", "BOOLEAN", "BOTH", "BOUND", "BULK", "BY", "BYTE", "C", "CALL", "CALLING", "CASCADE", "CASE", "CHAR_BASE", "CHAR", "CHARACTER", "CHARSET", "CHARSETFORM", "CHARSETID", "CHECK", "CLOB_BASE", "CLONE", "CLOSE", "CLUSTER", "CLUSTERS", "COALESCE", "COLAUTH", "COLLECT", "COLUMNS", "COMMENT", "COMMIT", "COMMITTED", "COMPILED", "COMPRESS", "CONNECT", "CONSTANT", "CONSTRUCTOR", "CONTEXT", "CONTINUE", "CONVERT", "COUNT", "CRASH", "CREATE", "CREDENTIAL", "CURRENT", "CURRVAL", "CURSOR", "CUSTOMDATUM", "DANGLING", "DATA", "DATE_BASE", "DATE", "DAY", "DECIMAL", "DEFAULT", "DEFINE", "DELETE", "DESC", "DETERMINISTIC", "DIRECTORY", "DISTINCT", "DO", "DOUBLE", "DROP", "DURATION", "ELEMENT", "ELSIF", "EMPTY", "ESCAPE", "EXCEPTIONS", "EXCLUSIVE", "EXECUTE", "EXISTS", "EXIT", "EXTENDS", "EXTERNAL", "EXTRACT", "FALSE", "FETCH", "FINAL", "FIRST", "FIXED", "FLOAT", "FOR", "FORALL", "FORCE", "FROM", "FUNCTION", "GENERAL", "GOTO", "GRANT", "GROUP", "HASH", "HEAP", "HIDDEN", "HOUR", "IDENTIFIED", "IF", "IMMEDIATE", "IN", "INCLUDING", "INDEX", "INDEXES", "INDICATOR", "INDICES", "INFINITE", "INSTANTIABLE", "INT", "INTEGER", "INTERFACE", "INTERVAL", "INTO", "INVALIDATE", "IS", "ISOLATION", "JAVA", "LANGUAGE", "LARGE", "LEADING", "LENGTH", "LEVEL", "LIBRARY", "LIKE", "LIKE2", "LIKE4", "LIKEC", "LIMITED", "LOCAL", "LOCK", "LONG", "MAP", "MAX", "MAXLEN", "MEMBER", "MERGE", "MIN", "MINUS", "MINUTE", "MLSLABEL", "MOD", "MODE", "MONTH", "MULTISET", "NAME", "NAN", "NATIONAL", "NATIVE", "NATURAL", "NATURALN", "NCHAR", "NEW", "NEXTVAL", "NOCOMPRESS", "NOCOPY", "NOT", "NOWAIT", "NULL", "NULLIF", "NUMBER_BASE", "NUMBER", "OBJECT", "OCICOLL", "OCIDATE", "OCIDATETIME", "OCIDURATION", "OCIINTERVAL", "OCILOBLOCATOR", "OCINUMBER", "OCIRAW", "OCIREF", "OCIREFCURSOR", "OCIROWID", "OCISTRING", "OCITYPE", "OF", "OLD", "ON", "ONLY", "OPAQUE", "OPEN", "OPERATOR", "OPTION", "ORACLE", "ORADATA", "ORDER", "ORGANIZATION", "ORLANY", "ORLVARY", "OTHERS", "OUT", "OVERLAPS", "OVERRIDING", "PACKAGE", "PARALLEL_ENABLE", "PARAMETER", "PARAMETERS", "PARENT", "PARTITION", "PASCAL", "PCTFREE", "PIPE", "PIPELINED", "PLS_INTEGER", "PLUGGABLE", "POSITIVE", "POSITIVEN", "PRAGMA", "PRECISION", "PRIOR", "PRIVATE", "PROCEDURE", "PUBLIC", "RAISE", "RANGE", "RAW", "READ", "REAL", "RECORD", "REF", "REFERENCE", "RELEASE", "RELIES_ON", "REM", "REMAINDER", "RENAME", "RESOURCE", "RESULT_CACHE", "RESULT", "RETURN", "RETURNING", "REVERSE", "REVOKE", "ROLLBACK", "ROW", "ROWID", "ROWNUM", "ROWTYPE", "SAMPLE", "SAVE", "SAVEPOINT", "SB1", "SB2", "SB4", "SECOND", "SEGMENT", "SELF", "SEPARATE", "SEQUENCE", "SERIALIZABLE", "SHARE", "SHORT", "SIZE_T", "SIZE", "SMALLINT", "SOME", "SPACE", "SPARSE", "SQL", "SQLCODE", "SQLDATA", "SQLERRM", "SQLNAME", "SQLSTATE", "STANDARD", "START", "STATIC", "STDDEV", "STORED", "STRING", "STRUCT", "STYLE", "SUBMULTISET", "SUBPARTITION", "SUBSTITUTABLE", "SUBTYPE", "SUCCESSFUL", "SUM", "SYNONYM", "SYSDATE", "TABAUTH", "TABLE", "TDO", "THE", "THEN", "TIME", "TIMESTAMP", "TIMEZONE_ABBR", "TIMEZONE_HOUR", "TIMEZONE_MINUTE", "TIMEZONE_REGION", "TO", "TRAILING", "TRANSACTION", "TRANSACTIONAL", "TRIGGER", "TRUE", "TRUSTED", "TYPE", "UB1", "UB2", "UB4", "UID", "UNDER", "UNIQUE", "UNPLUG", "UNSIGNED", "UNTRUSTED", "USE", "USER", "USING", "VALIDATE", "VALIST", "VALUE", "VARCHAR", "VARCHAR2", "VARIABLE", "VARIANCE", "VARRAY", "VARYING", "VIEW", "VIEWS", "VOID", "WHENEVER", "WHILE", "WITH", "WORK", "WRAPPED", "WRITE", "YEAR", "ZONE"]; 18 | 19 | var reservedToplevelWords = ["ADD", "ALTER COLUMN", "ALTER TABLE", "BEGIN", "CONNECT BY", "DECLARE", "DELETE FROM", "DELETE", "END", "EXCEPT", "EXCEPTION", "FETCH FIRST", "FROM", "GROUP BY", "HAVING", "INSERT INTO", "INSERT", "INTERSECT", "LIMIT", "LOOP", "MODIFY", "ORDER BY", "SELECT", "SET CURRENT SCHEMA", "SET SCHEMA", "SET", "START WITH", "UNION ALL", "UNION", "UPDATE", "VALUES", "WHERE"]; 20 | 21 | var reservedNewlineWords = ["AND", "CROSS APPLY", "CROSS JOIN", "ELSE", "END", "INNER JOIN", "JOIN", "LEFT JOIN", "LEFT OUTER JOIN", "OR", "OUTER APPLY", "OUTER JOIN", "RIGHT JOIN", "RIGHT OUTER JOIN", "WHEN", "XOR"]; 22 | 23 | var tokenizer = void 0; 24 | 25 | var PlSqlFormatter = function () { 26 | /** 27 | * @param {Object} cfg Different set of configurations 28 | */ 29 | function PlSqlFormatter(cfg) { 30 | _classCallCheck(this, PlSqlFormatter); 31 | 32 | this.cfg = cfg; 33 | } 34 | 35 | /** 36 | * Format the whitespace in a PL/SQL string to make it easier to read 37 | * 38 | * @param {String} query The PL/SQL string 39 | * @return {String} formatted string 40 | */ 41 | 42 | 43 | PlSqlFormatter.prototype.format = function format(query) { 44 | if (!tokenizer) { 45 | tokenizer = new _Tokenizer2["default"]({ 46 | reservedWords: reservedWords, 47 | reservedToplevelWords: reservedToplevelWords, 48 | reservedNewlineWords: reservedNewlineWords, 49 | stringTypes: ["\"\"", "N''", "''", "``"], 50 | openParens: ["(", "CASE"], 51 | closeParens: [")", "END"], 52 | indexedPlaceholderTypes: ["?"], 53 | namedPlaceholderTypes: [":"], 54 | lineCommentTypes: ["--"], 55 | specialWordChars: ["_", "$", "#", ".", "@"] 56 | }); 57 | } 58 | return new _Formatter2["default"](this.cfg, tokenizer).format(query); 59 | }; 60 | 61 | return PlSqlFormatter; 62 | }(); 63 | 64 | exports["default"] = PlSqlFormatter; 65 | module.exports = exports["default"]; -------------------------------------------------------------------------------- /test/N1qlFormatterTest.js: -------------------------------------------------------------------------------- 1 | import sqlFormatter from "./../src/sqlFormatter"; 2 | import behavesLikeSqlFormatter from "./behavesLikeSqlFormatter"; 3 | 4 | describe("N1qlFormatter", function() { 5 | behavesLikeSqlFormatter("n1ql"); 6 | 7 | it("formats SELECT query with element selection expression", function() { 8 | const result = sqlFormatter.format("SELECT orderlines[0].productId FROM orders;", {language: "n1ql"}); 9 | expect(result).toBe( 10 | "SELECT\n" + 11 | " orderlines[0].productId\n" + 12 | "FROM\n" + 13 | " orders;" 14 | ); 15 | }); 16 | 17 | it("formats SELECT query with primary key quering", function() { 18 | const result = sqlFormatter.format( 19 | "SELECT fname, email FROM tutorial USE KEYS ['dave', 'ian'];", 20 | {language: "n1ql"} 21 | ); 22 | expect(result).toBe( 23 | "SELECT\n" + 24 | " fname,\n" + 25 | " email\n" + 26 | "FROM\n" + 27 | " tutorial\n" + 28 | "USE KEYS\n" + 29 | " ['dave', 'ian'];" 30 | ); 31 | }); 32 | 33 | it("formats INSERT with {} object literal", function() { 34 | const result = sqlFormatter.format( 35 | "INSERT INTO heroes (KEY, VALUE) VALUES ('123', {'id':1,'type':'Tarzan'});", 36 | {language: "n1ql"} 37 | ); 38 | expect(result).toBe( 39 | "INSERT INTO\n" + 40 | " heroes (KEY, VALUE)\n" + 41 | "VALUES\n" + 42 | " ('123', {'id': 1, 'type': 'Tarzan'});" 43 | ); 44 | }); 45 | 46 | it("formats INSERT with large object and array literals", function() { 47 | const result = sqlFormatter.format( 48 | "INSERT INTO heroes (KEY, VALUE) VALUES ('123', {'id': 1, 'type': 'Tarzan', " + 49 | "'array': [123456789, 123456789, 123456789, 123456789, 123456789], 'hello': 'world'});", 50 | {language: "n1ql"} 51 | ); 52 | expect(result).toBe( 53 | "INSERT INTO\n" + 54 | " heroes (KEY, VALUE)\n" + 55 | "VALUES\n" + 56 | " (\n" + 57 | " '123',\n" + 58 | " {\n" + 59 | " 'id': 1,\n" + 60 | " 'type': 'Tarzan',\n" + 61 | " 'array': [\n" + 62 | " 123456789,\n" + 63 | " 123456789,\n" + 64 | " 123456789,\n" + 65 | " 123456789,\n" + 66 | " 123456789\n" + 67 | " ],\n" + 68 | " 'hello': 'world'\n" + 69 | " }\n" + 70 | " );" 71 | ); 72 | }); 73 | 74 | it("formats SELECT query with UNNEST toplevel reserver word", function() { 75 | const result = sqlFormatter.format( 76 | "SELECT * FROM tutorial UNNEST tutorial.children c;", 77 | {language: "n1ql"} 78 | ); 79 | expect(result).toBe( 80 | "SELECT\n" + 81 | " *\n" + 82 | "FROM\n" + 83 | " tutorial\n" + 84 | "UNNEST\n" + 85 | " tutorial.children c;" 86 | ); 87 | }); 88 | 89 | it("formats SELECT query with NEST and USE KEYS", function() { 90 | const result = sqlFormatter.format( 91 | "SELECT * FROM usr " + 92 | "USE KEYS 'Elinor_33313792' NEST orders_with_users orders " + 93 | "ON KEYS ARRAY s.order_id FOR s IN usr.shipped_order_history END;", 94 | {language: "n1ql"} 95 | ); 96 | expect(result).toBe( 97 | "SELECT\n" + 98 | " *\n" + 99 | "FROM\n" + 100 | " usr\n" + 101 | "USE KEYS\n" + 102 | " 'Elinor_33313792'\n" + 103 | "NEST\n" + 104 | " orders_with_users orders ON KEYS ARRAY s.order_id FOR s IN usr.shipped_order_history END;" 105 | ); 106 | }); 107 | 108 | it("formats explained DELETE query with USE KEYS and RETURNING", function() { 109 | const result = sqlFormatter.format( 110 | "EXPLAIN DELETE FROM tutorial t USE KEYS 'baldwin' RETURNING t", 111 | {language: "n1ql"} 112 | ); 113 | expect(result).toBe( 114 | "EXPLAIN DELETE FROM\n" + 115 | " tutorial t\n" + 116 | "USE KEYS\n" + 117 | " 'baldwin' RETURNING t" 118 | ); 119 | }); 120 | 121 | it("formats UPDATE query with USE KEYS and RETURNING", function() { 122 | const result = sqlFormatter.format( 123 | "UPDATE tutorial USE KEYS 'baldwin' SET type = 'actor' RETURNING tutorial.type", 124 | {language: "n1ql"} 125 | ); 126 | expect(result).toBe( 127 | "UPDATE\n" + 128 | " tutorial\n" + 129 | "USE KEYS\n" + 130 | " 'baldwin'\n" + 131 | "SET\n" + 132 | " type = 'actor' RETURNING tutorial.type" 133 | ); 134 | }); 135 | 136 | it("recognizes $variables", function() { 137 | const result = sqlFormatter.format( 138 | "SELECT $variable, $'var name', $\"var name\", $`var name`;", 139 | {language: "n1ql"} 140 | ); 141 | expect(result).toBe( 142 | "SELECT\n" + 143 | " $variable,\n" + 144 | " $'var name',\n" + 145 | " $\"var name\",\n" + 146 | " $`var name`;" 147 | ); 148 | }); 149 | 150 | it("replaces $variables with param values", function() { 151 | const result = sqlFormatter.format( 152 | "SELECT $variable, $'var name', $\"var name\", $`var name`;", { 153 | language: "n1ql", 154 | params: { 155 | "variable": "\"variable value\"", 156 | "var name": "'var value'" 157 | } 158 | } 159 | ); 160 | expect(result).toBe( 161 | "SELECT\n" + 162 | " \"variable value\",\n" + 163 | " 'var value',\n" + 164 | " 'var value',\n" + 165 | " 'var value';" 166 | ); 167 | }); 168 | 169 | it("replaces $ numbered placeholders with param values", function() { 170 | const result = sqlFormatter.format("SELECT $1, $2, $0;", { 171 | language: "n1ql", 172 | params: { 173 | 0: "first", 174 | 1: "second", 175 | 2: "third" 176 | } 177 | }); 178 | expect(result).toBe( 179 | "SELECT\n" + 180 | " second,\n" + 181 | " third,\n" + 182 | " first;" 183 | ); 184 | }); 185 | }); 186 | -------------------------------------------------------------------------------- /lib/languages/Db2Formatter.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | 5 | var _Formatter = require("../core/Formatter"); 6 | 7 | var _Formatter2 = _interopRequireDefault(_Formatter); 8 | 9 | var _Tokenizer = require("../core/Tokenizer"); 10 | 11 | var _Tokenizer2 = _interopRequireDefault(_Tokenizer); 12 | 13 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 14 | 15 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 16 | 17 | var reservedWords = ["ABS", "ACTIVATE", "ALIAS", "ALL", "ALLOCATE", "ALLOW", "ALTER", "ANY", "ARE", "ARRAY", "AS", "ASC", "ASENSITIVE", "ASSOCIATE", "ASUTIME", "ASYMMETRIC", "AT", "ATOMIC", "ATTRIBUTES", "AUDIT", "AUTHORIZATION", "AUX", "AUXILIARY", "AVG", "BEFORE", "BEGIN", "BETWEEN", "BIGINT", "BINARY", "BLOB", "BOOLEAN", "BOTH", "BUFFERPOOL", "BY", "CACHE", "CALL", "CALLED", "CAPTURE", "CARDINALITY", "CASCADED", "CASE", "CAST", "CCSID", "CEIL", "CEILING", "CHAR", "CHARACTER", "CHARACTER_LENGTH", "CHAR_LENGTH", "CHECK", "CLOB", "CLONE", "CLOSE", "CLUSTER", "COALESCE", "COLLATE", "COLLECT", "COLLECTION", "COLLID", "COLUMN", "COMMENT", "COMMIT", "CONCAT", "CONDITION", "CONNECT", "CONNECTION", "CONSTRAINT", "CONTAINS", "CONTINUE", "CONVERT", "CORR", "CORRESPONDING", "COUNT", "COUNT_BIG", "COVAR_POP", "COVAR_SAMP", "CREATE", "CROSS", "CUBE", "CUME_DIST", "CURRENT", "CURRENT_DATE", "CURRENT_DEFAULT_TRANSFORM_GROUP", "CURRENT_LC_CTYPE", "CURRENT_PATH", "CURRENT_ROLE", "CURRENT_SCHEMA", "CURRENT_SERVER", "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_TIMEZONE", "CURRENT_TRANSFORM_GROUP_FOR_TYPE", "CURRENT_USER", "CURSOR", "CYCLE", "DATA", "DATABASE", "DATAPARTITIONNAME", "DATAPARTITIONNUM", "DATE", "DAY", "DAYS", "DB2GENERAL", "DB2GENRL", "DB2SQL", "DBINFO", "DBPARTITIONNAME", "DBPARTITIONNUM", "DEALLOCATE", "DEC", "DECIMAL", "DECLARE", "DEFAULT", "DEFAULTS", "DEFINITION", "DELETE", "DENSERANK", "DENSE_RANK", "DEREF", "DESCRIBE", "DESCRIPTOR", "DETERMINISTIC", "DIAGNOSTICS", "DISABLE", "DISALLOW", "DISCONNECT", "DISTINCT", "DO", "DOCUMENT", "DOUBLE", "DROP", "DSSIZE", "DYNAMIC", "EACH", "EDITPROC", "ELEMENT", "ELSE", "ELSEIF", "ENABLE", "ENCODING", "ENCRYPTION", "END", "END-EXEC", "ENDING", "ERASE", "ESCAPE", "EVERY", "EXCEPTION", "EXCLUDING", "EXCLUSIVE", "EXEC", "EXECUTE", "EXISTS", "EXIT", "EXP", "EXPLAIN", "EXTENDED", "EXTERNAL", "EXTRACT", "FALSE", "FENCED", "FETCH", "FIELDPROC", "FILE", "FILTER", "FINAL", "FIRST", "FLOAT", "FLOOR", "FOR", "FOREIGN", "FREE", "FULL", "FUNCTION", "FUSION", "GENERAL", "GENERATED", "GET", "GLOBAL", "GOTO", "GRANT", "GRAPHIC", "GROUP", "GROUPING", "HANDLER", "HASH", "HASHED_VALUE", "HINT", "HOLD", "HOUR", "HOURS", "IDENTITY", "IF", "IMMEDIATE", "IN", "INCLUDING", "INCLUSIVE", "INCREMENT", "INDEX", "INDICATOR", "INDICATORS", "INF", "INFINITY", "INHERIT", "INNER", "INOUT", "INSENSITIVE", "INSERT", "INT", "INTEGER", "INTEGRITY", "INTERSECTION", "INTERVAL", "INTO", "IS", "ISOBID", "ISOLATION", "ITERATE", "JAR", "JAVA", "KEEP", "KEY", "LABEL", "LANGUAGE", "LARGE", "LATERAL", "LC_CTYPE", "LEADING", "LEAVE", "LEFT", "LIKE", "LINKTYPE", "LN", "LOCAL", "LOCALDATE", "LOCALE", "LOCALTIME", "LOCALTIMESTAMP", "LOCATOR", "LOCATORS", "LOCK", "LOCKMAX", "LOCKSIZE", "LONG", "LOOP", "LOWER", "MAINTAINED", "MATCH", "MATERIALIZED", "MAX", "MAXVALUE", "MEMBER", "MERGE", "METHOD", "MICROSECOND", "MICROSECONDS", "MIN", "MINUTE", "MINUTES", "MINVALUE", "MOD", "MODE", "MODIFIES", "MODULE", "MONTH", "MONTHS", "MULTISET", "NAN", "NATIONAL", "NATURAL", "NCHAR", "NCLOB", "NEW", "NEW_TABLE", "NEXTVAL", "NO", "NOCACHE", "NOCYCLE", "NODENAME", "NODENUMBER", "NOMAXVALUE", "NOMINVALUE", "NONE", "NOORDER", "NORMALIZE", "NORMALIZED", "NOT", "NULL", "NULLIF", "NULLS", "NUMERIC", "NUMPARTS", "OBID", "OCTET_LENGTH", "OF", "OFFSET", "OLD", "OLD_TABLE", "ON", "ONLY", "OPEN", "OPTIMIZATION", "OPTIMIZE", "OPTION", "ORDER", "OUT", "OUTER", "OVER", "OVERLAPS", "OVERLAY", "OVERRIDING", "PACKAGE", "PADDED", "PAGESIZE", "PARAMETER", "PART", "PARTITION", "PARTITIONED", "PARTITIONING", "PARTITIONS", "PASSWORD", "PATH", "PERCENTILE_CONT", "PERCENTILE_DISC", "PERCENT_RANK", "PIECESIZE", "PLAN", "POSITION", "POWER", "PRECISION", "PREPARE", "PREVVAL", "PRIMARY", "PRIQTY", "PRIVILEGES", "PROCEDURE", "PROGRAM", "PSID", "PUBLIC", "QUERY", "QUERYNO", "RANGE", "RANK", "READ", "READS", "REAL", "RECOVERY", "RECURSIVE", "REF", "REFERENCES", "REFERENCING", "REFRESH", "REGR_AVGX", "REGR_AVGY", "REGR_COUNT", "REGR_INTERCEPT", "REGR_R2", "REGR_SLOPE", "REGR_SXX", "REGR_SXY", "REGR_SYY", "RELEASE", "RENAME", "REPEAT", "RESET", "RESIGNAL", "RESTART", "RESTRICT", "RESULT", "RESULT_SET_LOCATOR", "RETURN", "RETURNS", "REVOKE", "RIGHT", "ROLE", "ROLLBACK", "ROLLUP", "ROUND_CEILING", "ROUND_DOWN", "ROUND_FLOOR", "ROUND_HALF_DOWN", "ROUND_HALF_EVEN", "ROUND_HALF_UP", "ROUND_UP", "ROUTINE", "ROW", "ROWNUMBER", "ROWS", "ROWSET", "ROW_NUMBER", "RRN", "RUN", "SAVEPOINT", "SCHEMA", "SCOPE", "SCRATCHPAD", "SCROLL", "SEARCH", "SECOND", "SECONDS", "SECQTY", "SECURITY", "SENSITIVE", "SEQUENCE", "SESSION", "SESSION_USER", "SIGNAL", "SIMILAR", "SIMPLE", "SMALLINT", "SNAN", "SOME", "SOURCE", "SPECIFIC", "SPECIFICTYPE", "SQL", "SQLEXCEPTION", "SQLID", "SQLSTATE", "SQLWARNING", "SQRT", "STACKED", "STANDARD", "START", "STARTING", "STATEMENT", "STATIC", "STATMENT", "STAY", "STDDEV_POP", "STDDEV_SAMP", "STOGROUP", "STORES", "STYLE", "SUBMULTISET", "SUBSTRING", "SUM", "SUMMARY", "SYMMETRIC", "SYNONYM", "SYSFUN", "SYSIBM", "SYSPROC", "SYSTEM", "SYSTEM_USER", "TABLE", "TABLESAMPLE", "TABLESPACE", "THEN", "TIME", "TIMESTAMP", "TIMEZONE_HOUR", "TIMEZONE_MINUTE", "TO", "TRAILING", "TRANSACTION", "TRANSLATE", "TRANSLATION", "TREAT", "TRIGGER", "TRIM", "TRUE", "TRUNCATE", "TYPE", "UESCAPE", "UNDO", "UNIQUE", "UNKNOWN", "UNNEST", "UNTIL", "UPPER", "USAGE", "USER", "USING", "VALIDPROC", "VALUE", "VARCHAR", "VARIABLE", "VARIANT", "VARYING", "VAR_POP", "VAR_SAMP", "VCAT", "VERSION", "VIEW", "VOLATILE", "VOLUMES", "WHEN", "WHENEVER", "WHILE", "WIDTH_BUCKET", "WINDOW", "WITH", "WITHIN", "WITHOUT", "WLM", "WRITE", "XMLELEMENT", "XMLEXISTS", "XMLNAMESPACES", "YEAR", "YEARS"]; 18 | 19 | var reservedToplevelWords = ["ADD", "AFTER", "ALTER COLUMN", "ALTER TABLE", "DELETE FROM", "EXCEPT", "FETCH FIRST", "FROM", "GROUP BY", "GO", "HAVING", "INSERT INTO", "INTERSECT", "LIMIT", "ORDER BY", "SELECT", "SET CURRENT SCHEMA", "SET SCHEMA", "SET", "UNION ALL", "UPDATE", "VALUES", "WHERE"]; 20 | 21 | var reservedNewlineWords = ["AND", "CROSS JOIN", "INNER JOIN", "JOIN", "LEFT JOIN", "LEFT OUTER JOIN", "OR", "OUTER JOIN", "RIGHT JOIN", "RIGHT OUTER JOIN"]; 22 | 23 | var tokenizer = void 0; 24 | 25 | var Db2Formatter = function () { 26 | /** 27 | * @param {Object} cfg Different set of configurations 28 | */ 29 | function Db2Formatter(cfg) { 30 | _classCallCheck(this, Db2Formatter); 31 | 32 | this.cfg = cfg; 33 | } 34 | 35 | /** 36 | * Formats DB2 query to make it easier to read 37 | * 38 | * @param {String} query The DB2 query string 39 | * @return {String} formatted string 40 | */ 41 | 42 | 43 | Db2Formatter.prototype.format = function format(query) { 44 | if (!tokenizer) { 45 | tokenizer = new _Tokenizer2["default"]({ 46 | reservedWords: reservedWords, 47 | reservedToplevelWords: reservedToplevelWords, 48 | reservedNewlineWords: reservedNewlineWords, 49 | stringTypes: ["\"\"", "''", "``", "[]"], 50 | openParens: ["("], 51 | closeParens: [")"], 52 | indexedPlaceholderTypes: ["?"], 53 | namedPlaceholderTypes: [":"], 54 | lineCommentTypes: ["--"], 55 | specialWordChars: ["#", "@"] 56 | }); 57 | } 58 | return new _Formatter2["default"](this.cfg, tokenizer).format(query); 59 | }; 60 | 61 | return Db2Formatter; 62 | }(); 63 | 64 | exports["default"] = Db2Formatter; 65 | module.exports = exports["default"]; -------------------------------------------------------------------------------- /src/languages/Db2Formatter.js: -------------------------------------------------------------------------------- 1 | import Formatter from "../core/Formatter"; 2 | import Tokenizer from "../core/Tokenizer"; 3 | 4 | const reservedWords = [ 5 | "ABS", "ACTIVATE", "ALIAS", "ALL", "ALLOCATE", "ALLOW", "ALTER", "ANY", "ARE", "ARRAY", "AS", "ASC", 6 | "ASENSITIVE", "ASSOCIATE", "ASUTIME", "ASYMMETRIC", "AT", "ATOMIC", "ATTRIBUTES", "AUDIT", "AUTHORIZATION", "AUX", "AUXILIARY", "AVG", 7 | "BEFORE", "BEGIN", "BETWEEN", "BIGINT", "BINARY", "BLOB", "BOOLEAN", "BOTH", "BUFFERPOOL", "BY", 8 | "CACHE", "CALL", "CALLED", "CAPTURE", "CARDINALITY", "CASCADED", "CASE", "CAST", "CCSID", "CEIL", "CEILING", "CHAR", "CHARACTER", 9 | "CHARACTER_LENGTH", "CHAR_LENGTH", "CHECK", "CLOB", "CLONE", "CLOSE", "CLUSTER", "COALESCE", "COLLATE", "COLLECT", "COLLECTION", 10 | "COLLID", "COLUMN", "COMMENT", "COMMIT", "CONCAT", "CONDITION", "CONNECT", "CONNECTION", "CONSTRAINT", "CONTAINS", "CONTINUE", 11 | "CONVERT", "CORR", "CORRESPONDING", "COUNT", "COUNT_BIG", "COVAR_POP", "COVAR_SAMP", "CREATE", "CROSS", "CUBE", "CUME_DIST", "CURRENT", 12 | "CURRENT_DATE", "CURRENT_DEFAULT_TRANSFORM_GROUP", "CURRENT_LC_CTYPE", "CURRENT_PATH", "CURRENT_ROLE", "CURRENT_SCHEMA", 13 | "CURRENT_SERVER", "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_TIMEZONE", "CURRENT_TRANSFORM_GROUP_FOR_TYPE", "CURRENT_USER", "CURSOR", 14 | "CYCLE", 15 | "DATA", "DATABASE", "DATAPARTITIONNAME", "DATAPARTITIONNUM", "DATE", "DAY", "DAYS", "DB2GENERAL", "DB2GENRL", "DB2SQL", "DBINFO", 16 | "DBPARTITIONNAME", "DBPARTITIONNUM", "DEALLOCATE", "DEC", "DECIMAL", "DECLARE", "DEFAULT", "DEFAULTS", "DEFINITION", "DELETE", 17 | "DENSERANK", "DENSE_RANK", "DEREF", "DESCRIBE", "DESCRIPTOR", "DETERMINISTIC", "DIAGNOSTICS", "DISABLE", "DISALLOW", "DISCONNECT", 18 | "DISTINCT", "DO", "DOCUMENT", "DOUBLE", "DROP", "DSSIZE", "DYNAMIC", 19 | "EACH", "EDITPROC", "ELEMENT", "ELSE", "ELSEIF", "ENABLE", "ENCODING", "ENCRYPTION", "END", "END-EXEC", "ENDING", "ERASE", "ESCAPE", 20 | "EVERY", "EXCEPTION", "EXCLUDING", "EXCLUSIVE", "EXEC", "EXECUTE", "EXISTS", "EXIT", "EXP", "EXPLAIN", "EXTENDED", "EXTERNAL", 21 | "EXTRACT", 22 | "FALSE", "FENCED", "FETCH", "FIELDPROC", "FILE", "FILTER", "FINAL", "FIRST", "FLOAT", "FLOOR", "FOR", "FOREIGN", "FREE", "FULL", 23 | "FUNCTION", "FUSION", 24 | "GENERAL", "GENERATED", "GET", "GLOBAL", "GOTO", "GRANT", "GRAPHIC", "GROUP", "GROUPING", 25 | "HANDLER", "HASH", "HASHED_VALUE", "HINT", "HOLD", "HOUR", "HOURS", 26 | "IDENTITY", "IF", "IMMEDIATE", "IN", "INCLUDING", "INCLUSIVE", "INCREMENT", "INDEX", "INDICATOR", "INDICATORS", "INF", "INFINITY", 27 | "INHERIT", "INNER", "INOUT", "INSENSITIVE", "INSERT", "INT", "INTEGER", "INTEGRITY", "INTERSECTION", "INTERVAL", "INTO", 28 | "IS", "ISOBID", "ISOLATION", "ITERATE", 29 | "JAR", "JAVA", 30 | "KEEP", "KEY", 31 | "LABEL", "LANGUAGE", "LARGE", "LATERAL", "LC_CTYPE", "LEADING", "LEAVE", "LEFT", "LIKE", "LINKTYPE", "LN", "LOCAL", 32 | "LOCALDATE", "LOCALE", "LOCALTIME", "LOCALTIMESTAMP", "LOCATOR", "LOCATORS", "LOCK", "LOCKMAX", "LOCKSIZE", "LONG", "LOOP", "LOWER", 33 | "MAINTAINED", "MATCH", "MATERIALIZED", "MAX", "MAXVALUE", "MEMBER", "MERGE", "METHOD", "MICROSECOND", "MICROSECONDS", "MIN", "MINUTE", 34 | "MINUTES", "MINVALUE", "MOD", "MODE", "MODIFIES", "MODULE", "MONTH", "MONTHS", "MULTISET", 35 | "NAN", "NATIONAL", "NATURAL", "NCHAR", "NCLOB", "NEW", "NEW_TABLE", "NEXTVAL", "NO", "NOCACHE", "NOCYCLE", "NODENAME", "NODENUMBER", 36 | "NOMAXVALUE", "NOMINVALUE", "NONE", "NOORDER", "NORMALIZE", "NORMALIZED", "NOT", "NULL", "NULLIF", "NULLS", "NUMERIC", "NUMPARTS", 37 | "OBID", "OCTET_LENGTH", "OF", "OFFSET", "OLD", "OLD_TABLE", "ON", "ONLY", "OPEN", "OPTIMIZATION", "OPTIMIZE", "OPTION", "ORDER", 38 | "OUT", "OUTER", "OVER", "OVERLAPS", "OVERLAY", "OVERRIDING", 39 | "PACKAGE", "PADDED", "PAGESIZE", "PARAMETER", "PART", "PARTITION", "PARTITIONED", "PARTITIONING", "PARTITIONS", "PASSWORD", "PATH", 40 | "PERCENTILE_CONT", "PERCENTILE_DISC", "PERCENT_RANK", "PIECESIZE", "PLAN", "POSITION", "POWER", "PRECISION", "PREPARE", "PREVVAL", 41 | "PRIMARY", "PRIQTY", "PRIVILEGES", "PROCEDURE", "PROGRAM", "PSID", "PUBLIC", 42 | "QUERY", "QUERYNO", 43 | "RANGE", "RANK", "READ", "READS", "REAL", "RECOVERY", "RECURSIVE", "REF", "REFERENCES", "REFERENCING", "REFRESH", "REGR_AVGX", 44 | "REGR_AVGY", "REGR_COUNT", "REGR_INTERCEPT", "REGR_R2", "REGR_SLOPE", "REGR_SXX", "REGR_SXY", "REGR_SYY", "RELEASE", "RENAME", "REPEAT", 45 | "RESET", "RESIGNAL", "RESTART", "RESTRICT", "RESULT", "RESULT_SET_LOCATOR", "RETURN", "RETURNS", "REVOKE", "RIGHT", "ROLE", "ROLLBACK", 46 | "ROLLUP", "ROUND_CEILING", "ROUND_DOWN", "ROUND_FLOOR", "ROUND_HALF_DOWN", "ROUND_HALF_EVEN", "ROUND_HALF_UP", "ROUND_UP", "ROUTINE", 47 | "ROW", "ROWNUMBER", "ROWS", "ROWSET", "ROW_NUMBER", "RRN", "RUN", 48 | "SAVEPOINT", "SCHEMA", "SCOPE", "SCRATCHPAD", "SCROLL", "SEARCH", "SECOND", "SECONDS", "SECQTY", "SECURITY", "SENSITIVE", 49 | "SEQUENCE", "SESSION", "SESSION_USER", "SIGNAL", "SIMILAR", "SIMPLE", "SMALLINT", "SNAN", "SOME", "SOURCE", "SPECIFIC", 50 | "SPECIFICTYPE", "SQL", "SQLEXCEPTION", "SQLID", "SQLSTATE", "SQLWARNING", "SQRT", "STACKED", "STANDARD", "START", "STARTING", 51 | "STATEMENT", "STATIC", "STATMENT", "STAY", "STDDEV_POP", "STDDEV_SAMP", "STOGROUP", "STORES", "STYLE", "SUBMULTISET", "SUBSTRING", 52 | "SUM", "SUMMARY", "SYMMETRIC", "SYNONYM", "SYSFUN", "SYSIBM", "SYSPROC", "SYSTEM", "SYSTEM_USER", 53 | "TABLE", "TABLESAMPLE", "TABLESPACE", "THEN", "TIME", "TIMESTAMP", "TIMEZONE_HOUR", "TIMEZONE_MINUTE", "TO", "TRAILING", "TRANSACTION", 54 | "TRANSLATE", "TRANSLATION", "TREAT", "TRIGGER", "TRIM", "TRUE", "TRUNCATE", "TYPE", 55 | "UESCAPE", "UNDO", "UNIQUE", "UNKNOWN", "UNNEST", "UNTIL", "UPPER", "USAGE", "USER", "USING", 56 | "VALIDPROC", "VALUE", "VARCHAR", "VARIABLE", "VARIANT", "VARYING", "VAR_POP", "VAR_SAMP", "VCAT", "VERSION", "VIEW", 57 | "VOLATILE", "VOLUMES", "WHEN", "WHENEVER", "WHILE", "WIDTH_BUCKET", "WINDOW", "WITH", "WITHIN", "WITHOUT", "WLM", "WRITE", 58 | "XMLELEMENT", "XMLEXISTS", "XMLNAMESPACES", 59 | "YEAR", "YEARS" 60 | ]; 61 | 62 | const reservedToplevelWords = [ 63 | "ADD", "AFTER", "ALTER COLUMN", "ALTER TABLE", 64 | "DELETE FROM", 65 | "EXCEPT", 66 | "FETCH FIRST", "FROM", 67 | "GROUP BY", "GO", 68 | "HAVING", 69 | "INSERT INTO", "INTERSECT", 70 | "LIMIT", 71 | "ORDER BY", 72 | "SELECT", "SET CURRENT SCHEMA", "SET SCHEMA", "SET", 73 | "UNION ALL", "UPDATE", 74 | "VALUES", 75 | "WHERE" 76 | ]; 77 | 78 | const reservedNewlineWords = [ 79 | "AND", 80 | "CROSS JOIN", 81 | "INNER JOIN", 82 | "JOIN", 83 | "LEFT JOIN", "LEFT OUTER JOIN", 84 | "OR", "OUTER JOIN", 85 | "RIGHT JOIN", "RIGHT OUTER JOIN" 86 | ]; 87 | 88 | let tokenizer; 89 | 90 | export default class Db2Formatter { 91 | /** 92 | * @param {Object} cfg Different set of configurations 93 | */ 94 | constructor(cfg) { 95 | this.cfg = cfg; 96 | } 97 | 98 | /** 99 | * Formats DB2 query to make it easier to read 100 | * 101 | * @param {String} query The DB2 query string 102 | * @return {String} formatted string 103 | */ 104 | format(query) { 105 | if (!tokenizer) { 106 | tokenizer = new Tokenizer({ 107 | reservedWords, 108 | reservedToplevelWords, 109 | reservedNewlineWords, 110 | stringTypes: [`""`, "''", "``", "[]"], 111 | openParens: ["("], 112 | closeParens: [")"], 113 | indexedPlaceholderTypes: ["?"], 114 | namedPlaceholderTypes: [":"], 115 | lineCommentTypes: ["--"], 116 | specialWordChars: ["#", "@"] 117 | }); 118 | } 119 | return new Formatter(this.cfg, tokenizer).format(query); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/core/Formatter.js: -------------------------------------------------------------------------------- 1 | import includes from "lodash/includes"; 2 | import trimEnd from "lodash/trimEnd"; 3 | import tokenTypes from "./tokenTypes"; 4 | import Indentation from "./Indentation"; 5 | import InlineBlock from "./InlineBlock"; 6 | import Params from "./Params"; 7 | 8 | export default class Formatter { 9 | /** 10 | * @param {Object} cfg 11 | * @param {Object} cfg.indent 12 | * @param {Object} cfg.params 13 | * @param {Tokenizer} tokenizer 14 | */ 15 | constructor(cfg, tokenizer) { 16 | this.cfg = cfg || {}; 17 | this.indentation = new Indentation(this.cfg.indent); 18 | this.inlineBlock = new InlineBlock(); 19 | this.params = new Params(this.cfg.params); 20 | this.tokenizer = tokenizer; 21 | this.previousReservedWord = {}; 22 | this.tokens = []; 23 | this.index = 0; 24 | } 25 | 26 | /** 27 | * Formats whitespaces in a SQL string to make it easier to read. 28 | * 29 | * @param {String} query The SQL query string 30 | * @return {String} formatted query 31 | */ 32 | format(query) { 33 | this.tokens = this.tokenizer.tokenize(query); 34 | const formattedQuery = this.getFormattedQueryFromTokens(); 35 | 36 | return formattedQuery.trim(); 37 | } 38 | 39 | getFormattedQueryFromTokens() { 40 | let formattedQuery = ""; 41 | 42 | this.tokens.forEach((token, index) => { 43 | this.index = index; 44 | 45 | if (token.type === tokenTypes.WHITESPACE) { 46 | // ignore (we do our own whitespace formatting) 47 | } 48 | else if (token.type === tokenTypes.LINE_COMMENT) { 49 | formattedQuery = this.formatLineComment(token, formattedQuery); 50 | } 51 | else if (token.type === tokenTypes.BLOCK_COMMENT) { 52 | formattedQuery = this.formatBlockComment(token, formattedQuery); 53 | } 54 | else if (token.type === tokenTypes.RESERVED_TOPLEVEL) { 55 | formattedQuery = this.formatToplevelReservedWord(token, formattedQuery); 56 | this.previousReservedWord = token; 57 | } 58 | else if (token.type === tokenTypes.RESERVED_NEWLINE) { 59 | formattedQuery = this.formatNewlineReservedWord(token, formattedQuery); 60 | this.previousReservedWord = token; 61 | } 62 | else if (token.type === tokenTypes.RESERVED) { 63 | formattedQuery = this.formatWithSpaces(token, formattedQuery); 64 | this.previousReservedWord = token; 65 | } 66 | else if (token.type === tokenTypes.OPEN_PAREN) { 67 | formattedQuery = this.formatOpeningParentheses(token, formattedQuery); 68 | } 69 | else if (token.type === tokenTypes.CLOSE_PAREN) { 70 | formattedQuery = this.formatClosingParentheses(token, formattedQuery); 71 | } 72 | else if (token.type === tokenTypes.PLACEHOLDER) { 73 | formattedQuery = this.formatPlaceholder(token, formattedQuery); 74 | } 75 | else if (token.value === ",") { 76 | formattedQuery = this.formatComma(token, formattedQuery); 77 | } 78 | else if (token.value === ":") { 79 | formattedQuery = this.formatWithSpaceAfter(token, formattedQuery); 80 | } 81 | else if (token.value === ".") { 82 | formattedQuery = this.formatWithoutSpaces(token, formattedQuery); 83 | } 84 | else if (token.value === ";") { 85 | formattedQuery = this.formatQuerySeparator(token, formattedQuery); 86 | } 87 | else { 88 | formattedQuery = this.formatWithSpaces(token, formattedQuery); 89 | } 90 | }); 91 | return formattedQuery; 92 | } 93 | 94 | formatLineComment(token, query) { 95 | return this.addNewline(query + token.value); 96 | } 97 | 98 | formatBlockComment(token, query) { 99 | return this.addNewline(this.addNewline(query) + this.indentComment(token.value)); 100 | } 101 | 102 | indentComment(comment) { 103 | return comment.replace(/\n/g, "\n" + this.indentation.getIndent()); 104 | } 105 | 106 | formatToplevelReservedWord(token, query) { 107 | this.indentation.decreaseTopLevel(); 108 | 109 | query = this.addNewline(query); 110 | 111 | this.indentation.increaseToplevel(); 112 | 113 | query += this.equalizeWhitespace(token.value); 114 | return this.addNewline(query); 115 | } 116 | 117 | formatNewlineReservedWord(token, query) { 118 | return this.addNewline(query) + this.equalizeWhitespace(token.value) + " "; 119 | } 120 | 121 | // Replace any sequence of whitespace characters with single space 122 | equalizeWhitespace(string) { 123 | return string.replace(/\s+/g, " "); 124 | } 125 | 126 | // Opening parentheses increase the block indent level and start a new line 127 | formatOpeningParentheses(token, query) { 128 | // Take out the preceding space unless there was whitespace there in the original query 129 | // or another opening parens or line comment 130 | const preserveWhitespaceFor = [ 131 | tokenTypes.WHITESPACE, 132 | tokenTypes.OPEN_PAREN, 133 | tokenTypes.LINE_COMMENT, 134 | ]; 135 | if (!includes(preserveWhitespaceFor, this.previousToken().type)) { 136 | query = trimEnd(query); 137 | } 138 | query += token.value; 139 | 140 | this.inlineBlock.beginIfPossible(this.tokens, this.index); 141 | 142 | if (!this.inlineBlock.isActive()) { 143 | this.indentation.increaseBlockLevel(); 144 | query = this.addNewline(query); 145 | } 146 | return query; 147 | } 148 | 149 | // Closing parentheses decrease the block indent level 150 | formatClosingParentheses(token, query) { 151 | if (this.inlineBlock.isActive()) { 152 | this.inlineBlock.end(); 153 | return this.formatWithSpaceAfter(token, query); 154 | } 155 | else { 156 | this.indentation.decreaseBlockLevel(); 157 | return this.formatWithSpaces(token, this.addNewline(query)); 158 | } 159 | } 160 | 161 | formatPlaceholder(token, query) { 162 | return query + this.params.get(token) + " "; 163 | } 164 | 165 | // Commas start a new line (unless within inline parentheses or SQL "LIMIT" clause) 166 | formatComma(token, query) { 167 | query = this.trimTrailingWhitespace(query) + token.value + " "; 168 | 169 | if (this.inlineBlock.isActive()) { 170 | return query; 171 | } 172 | else if (/^LIMIT$/i.test(this.previousReservedWord.value)) { 173 | return query; 174 | } 175 | else { 176 | return this.addNewline(query); 177 | } 178 | } 179 | 180 | formatWithSpaceAfter(token, query) { 181 | return this.trimTrailingWhitespace(query) + token.value + " "; 182 | } 183 | 184 | formatWithoutSpaces(token, query) { 185 | return this.trimTrailingWhitespace(query) + token.value; 186 | } 187 | 188 | formatWithSpaces(token, query) { 189 | return query + token.value + " "; 190 | } 191 | 192 | formatQuerySeparator(token, query) { 193 | return this.trimTrailingWhitespace(query) + token.value + "\n"; 194 | } 195 | 196 | addNewline(query) { 197 | return trimEnd(query) + "\n" + this.indentation.getIndent(); 198 | } 199 | 200 | trimTrailingWhitespace(query) { 201 | if (this.previousNonWhitespaceToken().type === tokenTypes.LINE_COMMENT) { 202 | return trimEnd(query) + "\n"; 203 | } 204 | else { 205 | return trimEnd(query); 206 | } 207 | } 208 | 209 | previousNonWhitespaceToken() { 210 | let n = 1; 211 | while (this.previousToken(n).type === tokenTypes.WHITESPACE) { 212 | n++; 213 | } 214 | return this.previousToken(n); 215 | } 216 | 217 | previousToken(offset = 1) { 218 | return this.tokens[this.index - offset] || {}; 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /lib/core/Formatter.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | 5 | var _includes = require("lodash/includes"); 6 | 7 | var _includes2 = _interopRequireDefault(_includes); 8 | 9 | var _trimEnd = require("lodash/trimEnd"); 10 | 11 | var _trimEnd2 = _interopRequireDefault(_trimEnd); 12 | 13 | var _tokenTypes = require("./tokenTypes"); 14 | 15 | var _tokenTypes2 = _interopRequireDefault(_tokenTypes); 16 | 17 | var _Indentation = require("./Indentation"); 18 | 19 | var _Indentation2 = _interopRequireDefault(_Indentation); 20 | 21 | var _InlineBlock = require("./InlineBlock"); 22 | 23 | var _InlineBlock2 = _interopRequireDefault(_InlineBlock); 24 | 25 | var _Params = require("./Params"); 26 | 27 | var _Params2 = _interopRequireDefault(_Params); 28 | 29 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 30 | 31 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 32 | 33 | var Formatter = function () { 34 | /** 35 | * @param {Object} cfg 36 | * @param {Object} cfg.indent 37 | * @param {Object} cfg.params 38 | * @param {Tokenizer} tokenizer 39 | */ 40 | function Formatter(cfg, tokenizer) { 41 | _classCallCheck(this, Formatter); 42 | 43 | this.cfg = cfg || {}; 44 | this.indentation = new _Indentation2["default"](this.cfg.indent); 45 | this.inlineBlock = new _InlineBlock2["default"](); 46 | this.params = new _Params2["default"](this.cfg.params); 47 | this.tokenizer = tokenizer; 48 | this.previousReservedWord = {}; 49 | this.tokens = []; 50 | this.index = 0; 51 | } 52 | 53 | /** 54 | * Formats whitespaces in a SQL string to make it easier to read. 55 | * 56 | * @param {String} query The SQL query string 57 | * @return {String} formatted query 58 | */ 59 | 60 | 61 | Formatter.prototype.format = function format(query) { 62 | this.tokens = this.tokenizer.tokenize(query); 63 | var formattedQuery = this.getFormattedQueryFromTokens(); 64 | 65 | return formattedQuery.trim(); 66 | }; 67 | 68 | Formatter.prototype.getFormattedQueryFromTokens = function getFormattedQueryFromTokens() { 69 | var _this = this; 70 | 71 | var formattedQuery = ""; 72 | 73 | this.tokens.forEach(function (token, index) { 74 | _this.index = index; 75 | 76 | if (token.type === _tokenTypes2["default"].WHITESPACE) { 77 | // ignore (we do our own whitespace formatting) 78 | } else if (token.type === _tokenTypes2["default"].LINE_COMMENT) { 79 | formattedQuery = _this.formatLineComment(token, formattedQuery); 80 | } else if (token.type === _tokenTypes2["default"].BLOCK_COMMENT) { 81 | formattedQuery = _this.formatBlockComment(token, formattedQuery); 82 | } else if (token.type === _tokenTypes2["default"].RESERVED_TOPLEVEL) { 83 | formattedQuery = _this.formatToplevelReservedWord(token, formattedQuery); 84 | _this.previousReservedWord = token; 85 | } else if (token.type === _tokenTypes2["default"].RESERVED_NEWLINE) { 86 | formattedQuery = _this.formatNewlineReservedWord(token, formattedQuery); 87 | _this.previousReservedWord = token; 88 | } else if (token.type === _tokenTypes2["default"].RESERVED) { 89 | formattedQuery = _this.formatWithSpaces(token, formattedQuery); 90 | _this.previousReservedWord = token; 91 | } else if (token.type === _tokenTypes2["default"].OPEN_PAREN) { 92 | formattedQuery = _this.formatOpeningParentheses(token, formattedQuery); 93 | } else if (token.type === _tokenTypes2["default"].CLOSE_PAREN) { 94 | formattedQuery = _this.formatClosingParentheses(token, formattedQuery); 95 | } else if (token.type === _tokenTypes2["default"].PLACEHOLDER) { 96 | formattedQuery = _this.formatPlaceholder(token, formattedQuery); 97 | } else if (token.value === ",") { 98 | formattedQuery = _this.formatComma(token, formattedQuery); 99 | } else if (token.value === ":") { 100 | formattedQuery = _this.formatWithSpaceAfter(token, formattedQuery); 101 | } else if (token.value === ".") { 102 | formattedQuery = _this.formatWithoutSpaces(token, formattedQuery); 103 | } else if (token.value === ";") { 104 | formattedQuery = _this.formatQuerySeparator(token, formattedQuery); 105 | } else { 106 | formattedQuery = _this.formatWithSpaces(token, formattedQuery); 107 | } 108 | }); 109 | return formattedQuery; 110 | }; 111 | 112 | Formatter.prototype.formatLineComment = function formatLineComment(token, query) { 113 | return this.addNewline(query + token.value); 114 | }; 115 | 116 | Formatter.prototype.formatBlockComment = function formatBlockComment(token, query) { 117 | return this.addNewline(this.addNewline(query) + this.indentComment(token.value)); 118 | }; 119 | 120 | Formatter.prototype.indentComment = function indentComment(comment) { 121 | return comment.replace(/\n/g, "\n" + this.indentation.getIndent()); 122 | }; 123 | 124 | Formatter.prototype.formatToplevelReservedWord = function formatToplevelReservedWord(token, query) { 125 | this.indentation.decreaseTopLevel(); 126 | 127 | query = this.addNewline(query); 128 | 129 | this.indentation.increaseToplevel(); 130 | 131 | query += this.equalizeWhitespace(token.value); 132 | return this.addNewline(query); 133 | }; 134 | 135 | Formatter.prototype.formatNewlineReservedWord = function formatNewlineReservedWord(token, query) { 136 | return this.addNewline(query) + this.equalizeWhitespace(token.value) + " "; 137 | }; 138 | 139 | // Replace any sequence of whitespace characters with single space 140 | 141 | 142 | Formatter.prototype.equalizeWhitespace = function equalizeWhitespace(string) { 143 | return string.replace(/\s+/g, " "); 144 | }; 145 | 146 | // Opening parentheses increase the block indent level and start a new line 147 | 148 | 149 | Formatter.prototype.formatOpeningParentheses = function formatOpeningParentheses(token, query) { 150 | // Take out the preceding space unless there was whitespace there in the original query 151 | // or another opening parens or line comment 152 | var preserveWhitespaceFor = [_tokenTypes2["default"].WHITESPACE, _tokenTypes2["default"].OPEN_PAREN, _tokenTypes2["default"].LINE_COMMENT]; 153 | if (!(0, _includes2["default"])(preserveWhitespaceFor, this.previousToken().type)) { 154 | query = (0, _trimEnd2["default"])(query); 155 | } 156 | query += token.value; 157 | 158 | this.inlineBlock.beginIfPossible(this.tokens, this.index); 159 | 160 | if (!this.inlineBlock.isActive()) { 161 | this.indentation.increaseBlockLevel(); 162 | query = this.addNewline(query); 163 | } 164 | return query; 165 | }; 166 | 167 | // Closing parentheses decrease the block indent level 168 | 169 | 170 | Formatter.prototype.formatClosingParentheses = function formatClosingParentheses(token, query) { 171 | if (this.inlineBlock.isActive()) { 172 | this.inlineBlock.end(); 173 | return this.formatWithSpaceAfter(token, query); 174 | } else { 175 | this.indentation.decreaseBlockLevel(); 176 | return this.formatWithSpaces(token, this.addNewline(query)); 177 | } 178 | }; 179 | 180 | Formatter.prototype.formatPlaceholder = function formatPlaceholder(token, query) { 181 | return query + this.params.get(token) + " "; 182 | }; 183 | 184 | // Commas start a new line (unless within inline parentheses or SQL "LIMIT" clause) 185 | 186 | 187 | Formatter.prototype.formatComma = function formatComma(token, query) { 188 | query = this.trimTrailingWhitespace(query) + token.value + " "; 189 | 190 | if (this.inlineBlock.isActive()) { 191 | return query; 192 | } else if (/^LIMIT$/i.test(this.previousReservedWord.value)) { 193 | return query; 194 | } else { 195 | return this.addNewline(query); 196 | } 197 | }; 198 | 199 | Formatter.prototype.formatWithSpaceAfter = function formatWithSpaceAfter(token, query) { 200 | return this.trimTrailingWhitespace(query) + token.value + " "; 201 | }; 202 | 203 | Formatter.prototype.formatWithoutSpaces = function formatWithoutSpaces(token, query) { 204 | return this.trimTrailingWhitespace(query) + token.value; 205 | }; 206 | 207 | Formatter.prototype.formatWithSpaces = function formatWithSpaces(token, query) { 208 | return query + token.value + " "; 209 | }; 210 | 211 | Formatter.prototype.formatQuerySeparator = function formatQuerySeparator(token, query) { 212 | return this.trimTrailingWhitespace(query) + token.value + "\n"; 213 | }; 214 | 215 | Formatter.prototype.addNewline = function addNewline(query) { 216 | return (0, _trimEnd2["default"])(query) + "\n" + this.indentation.getIndent(); 217 | }; 218 | 219 | Formatter.prototype.trimTrailingWhitespace = function trimTrailingWhitespace(query) { 220 | if (this.previousNonWhitespaceToken().type === _tokenTypes2["default"].LINE_COMMENT) { 221 | return (0, _trimEnd2["default"])(query) + "\n"; 222 | } else { 223 | return (0, _trimEnd2["default"])(query); 224 | } 225 | }; 226 | 227 | Formatter.prototype.previousNonWhitespaceToken = function previousNonWhitespaceToken() { 228 | var n = 1; 229 | while (this.previousToken(n).type === _tokenTypes2["default"].WHITESPACE) { 230 | n++; 231 | } 232 | return this.previousToken(n); 233 | }; 234 | 235 | Formatter.prototype.previousToken = function previousToken() { 236 | var offset = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1; 237 | 238 | return this.tokens[this.index - offset] || {}; 239 | }; 240 | 241 | return Formatter; 242 | }(); 243 | 244 | exports["default"] = Formatter; 245 | module.exports = exports["default"]; -------------------------------------------------------------------------------- /test/PlSqlFormatterTest.js: -------------------------------------------------------------------------------- 1 | import sqlFormatter from "./../src/sqlFormatter"; 2 | import behavesLikeSqlFormatter from "./behavesLikeSqlFormatter"; 3 | 4 | describe("PlSqlFormatter", function() { 5 | behavesLikeSqlFormatter("pl/sql"); 6 | 7 | it("formats FETCH FIRST like LIMIT", function() { 8 | expect(sqlFormatter.format( 9 | "SELECT col1 FROM tbl ORDER BY col2 DESC FETCH FIRST 20 ROWS ONLY;", 10 | {language: "pl/sql"} 11 | )).toBe( 12 | "SELECT\n" + 13 | " col1\n" + 14 | "FROM\n" + 15 | " tbl\n" + 16 | "ORDER BY\n" + 17 | " col2 DESC\n" + 18 | "FETCH FIRST\n" + 19 | " 20 ROWS ONLY;" 20 | ); 21 | }); 22 | 23 | it("formats only -- as a line comment", function() { 24 | const result = sqlFormatter.format( 25 | "SELECT col FROM\n" + 26 | "-- This is a comment\n" + 27 | "MyTable;\n", 28 | {language: "pl/sql"} 29 | ); 30 | expect(result).toBe( 31 | "SELECT\n" + 32 | " col\n" + 33 | "FROM\n" + 34 | " -- This is a comment\n" + 35 | " MyTable;" 36 | ); 37 | }); 38 | 39 | it("recognizes _, $, #, . and @ as part of identifiers", function() { 40 | const result = sqlFormatter.format( 41 | "SELECT my_col$1#, col.2@ FROM tbl\n", 42 | {language: "pl/sql"} 43 | ); 44 | expect(result).toBe( 45 | "SELECT\n" + 46 | " my_col$1#,\n" + 47 | " col.2@\n" + 48 | "FROM\n" + 49 | " tbl" 50 | ); 51 | }); 52 | 53 | it("formats short CREATE TABLE", function() { 54 | expect(sqlFormatter.format( 55 | "CREATE TABLE items (a INT PRIMARY KEY, b TEXT);" 56 | )).toBe( 57 | "CREATE TABLE items (a INT PRIMARY KEY, b TEXT);" 58 | ); 59 | }); 60 | 61 | it("formats long CREATE TABLE", function() { 62 | expect(sqlFormatter.format( 63 | "CREATE TABLE items (a INT PRIMARY KEY, b TEXT, c INT NOT NULL, d INT NOT NULL);" 64 | )).toBe( 65 | "CREATE TABLE items (\n" + 66 | " a INT PRIMARY KEY,\n" + 67 | " b TEXT,\n" + 68 | " c INT NOT NULL,\n" + 69 | " d INT NOT NULL\n" + 70 | ");" 71 | ); 72 | }); 73 | 74 | it("formats INSERT without INTO", function() { 75 | const result = sqlFormatter.format( 76 | "INSERT Customers (ID, MoneyBalance, Address, City) VALUES (12,-123.4, 'Skagen 2111','Stv');" 77 | ); 78 | expect(result).toBe( 79 | "INSERT\n" + 80 | " Customers (ID, MoneyBalance, Address, City)\n" + 81 | "VALUES\n" + 82 | " (12, -123.4, 'Skagen 2111', 'Stv');" 83 | ); 84 | }); 85 | 86 | it("formats ALTER TABLE ... MODIFY query", function() { 87 | const result = sqlFormatter.format( 88 | "ALTER TABLE supplier MODIFY supplier_name char(100) NOT NULL;" 89 | ); 90 | expect(result).toBe( 91 | "ALTER TABLE\n" + 92 | " supplier\n" + 93 | "MODIFY\n" + 94 | " supplier_name char(100) NOT NULL;" 95 | ); 96 | }); 97 | 98 | it("formats ALTER TABLE ... ALTER COLUMN query", function() { 99 | const result = sqlFormatter.format( 100 | "ALTER TABLE supplier ALTER COLUMN supplier_name VARCHAR(100) NOT NULL;" 101 | ); 102 | expect(result).toBe( 103 | "ALTER TABLE\n" + 104 | " supplier\n" + 105 | "ALTER COLUMN\n" + 106 | " supplier_name VARCHAR(100) NOT NULL;" 107 | ); 108 | }); 109 | 110 | it("recognizes [] strings", function() { 111 | expect(sqlFormatter.format("[foo JOIN bar]")).toBe("[foo JOIN bar]"); 112 | expect(sqlFormatter.format("[foo ]] JOIN bar]")).toBe("[foo ]] JOIN bar]"); 113 | }); 114 | 115 | it("recognizes :variables", function() { 116 | const result = sqlFormatter.format( 117 | "SELECT :variable, :a1_2.3$, :'var name', :\"var name\", :`var name`, :[var name];" 118 | ); 119 | expect(result).toBe( 120 | "SELECT\n" + 121 | " :variable,\n" + 122 | " :a1_2.3$,\n" + 123 | " :'var name',\n" + 124 | " :\"var name\",\n" + 125 | " :`var name`,\n" + 126 | " :[var name];" 127 | ); 128 | }); 129 | 130 | it("replaces :variables with param values", function() { 131 | const result = sqlFormatter.format( 132 | "SELECT :variable, :a1_2.3$, :'var name', :\"var name\", :`var name`," + 133 | " :[var name], :'escaped \\'var\\'', :\"^*& weird \\\" var \";", 134 | { 135 | params: { 136 | "variable": "\"variable value\"", 137 | "a1_2.3$": "'weird value'", 138 | "var name": "'var value'", 139 | "escaped 'var'": "'weirder value'", 140 | "^*& weird \" var ": "'super weird value'" 141 | } 142 | } 143 | ); 144 | expect(result).toBe( 145 | "SELECT\n" + 146 | " \"variable value\",\n" + 147 | " 'weird value',\n" + 148 | " 'var value',\n" + 149 | " 'var value',\n" + 150 | " 'var value',\n" + 151 | " 'var value',\n" + 152 | " 'weirder value',\n" + 153 | " 'super weird value';" 154 | ); 155 | }); 156 | 157 | it("recognizes ?[0-9]* placeholders", function() { 158 | const result = sqlFormatter.format("SELECT ?1, ?25, ?;"); 159 | expect(result).toBe( 160 | "SELECT\n" + 161 | " ?1,\n" + 162 | " ?25,\n" + 163 | " ?;" 164 | ); 165 | }); 166 | 167 | it("replaces ? numbered placeholders with param values", function() { 168 | const result = sqlFormatter.format("SELECT ?1, ?2, ?0;", { 169 | params: { 170 | 0: "first", 171 | 1: "second", 172 | 2: "third" 173 | } 174 | }); 175 | expect(result).toBe( 176 | "SELECT\n" + 177 | " second,\n" + 178 | " third,\n" + 179 | " first;" 180 | ); 181 | }); 182 | 183 | it("replaces ? indexed placeholders with param values", function() { 184 | const result = sqlFormatter.format("SELECT ?, ?, ?;", { 185 | params: ["first", "second", "third"] 186 | }); 187 | expect(result).toBe( 188 | "SELECT\n" + 189 | " first,\n" + 190 | " second,\n" + 191 | " third;" 192 | ); 193 | }); 194 | 195 | it("formats SELECT query with CROSS JOIN", function() { 196 | const result = sqlFormatter.format("SELECT a, b FROM t CROSS JOIN t2 on t.id = t2.id_t"); 197 | expect(result).toBe( 198 | "SELECT\n" + 199 | " a,\n" + 200 | " b\n" + 201 | "FROM\n" + 202 | " t\n" + 203 | " CROSS JOIN t2 on t.id = t2.id_t" 204 | ); 205 | }); 206 | 207 | it("formats SELECT query with CROSS APPLY", function() { 208 | const result = sqlFormatter.format("SELECT a, b FROM t CROSS APPLY fn(t.id)", ); 209 | expect(result).toBe( 210 | "SELECT\n" + 211 | " a,\n" + 212 | " b\n" + 213 | "FROM\n" + 214 | " t\n" + 215 | " CROSS APPLY fn(t.id)" 216 | ); 217 | }); 218 | 219 | it("formats simple SELECT", function() { 220 | const result = sqlFormatter.format("SELECT N, M FROM t"); 221 | expect(result).toBe( 222 | "SELECT\n" + 223 | " N,\n" + 224 | " M\n" + 225 | "FROM\n" + 226 | " t" 227 | ); 228 | }); 229 | 230 | it("formats simple SELECT with national characters", function() { 231 | const result = sqlFormatter.format("SELECT N'value'"); 232 | expect(result).toBe( 233 | "SELECT\n" + 234 | " N'value'" 235 | ); 236 | }); 237 | 238 | it("formats SELECT query with OUTER APPLY", function() { 239 | const result = sqlFormatter.format("SELECT a, b FROM t OUTER APPLY fn(t.id)"); 240 | expect(result).toBe( 241 | "SELECT\n" + 242 | " a,\n" + 243 | " b\n" + 244 | "FROM\n" + 245 | " t\n" + 246 | " OUTER APPLY fn(t.id)" 247 | ); 248 | }); 249 | 250 | it("formats CASE ... WHEN with a blank expression", function() { 251 | const result = sqlFormatter.format( 252 | "CASE WHEN option = 'foo' THEN 1 WHEN option = 'bar' THEN 2 WHEN option = 'baz' THEN 3 ELSE 4 END;" 253 | ); 254 | 255 | expect(result).toBe( 256 | "CASE\n" + 257 | " WHEN option = 'foo' THEN 1\n" + 258 | " WHEN option = 'bar' THEN 2\n" + 259 | " WHEN option = 'baz' THEN 3\n" + 260 | " ELSE 4\n" + 261 | "END;" 262 | ); 263 | }); 264 | 265 | it("formats CASE ... WHEN inside SELECT", function() { 266 | const result = sqlFormatter.format( 267 | "SELECT foo, bar, CASE baz WHEN 'one' THEN 1 WHEN 'two' THEN 2 ELSE 3 END FROM table" 268 | ); 269 | 270 | expect(result).toBe( 271 | "SELECT\n" + 272 | " foo,\n" + 273 | " bar,\n" + 274 | " CASE\n" + 275 | " baz\n" + 276 | " WHEN 'one' THEN 1\n" + 277 | " WHEN 'two' THEN 2\n" + 278 | " ELSE 3\n" + 279 | " END\n" + 280 | "FROM\n" + 281 | " table" 282 | ); 283 | }); 284 | 285 | it("formats CASE ... WHEN with an expression", function() { 286 | const result = sqlFormatter.format( 287 | "CASE toString(getNumber()) WHEN 'one' THEN 1 WHEN 'two' THEN 2 WHEN 'three' THEN 3 ELSE 4 END;" 288 | ); 289 | 290 | expect(result).toBe( 291 | "CASE\n" + 292 | " toString(getNumber())\n" + 293 | " WHEN 'one' THEN 1\n" + 294 | " WHEN 'two' THEN 2\n" + 295 | " WHEN 'three' THEN 3\n" + 296 | " ELSE 4\n" + 297 | "END;" 298 | ); 299 | }); 300 | }); 301 | -------------------------------------------------------------------------------- /src/core/Tokenizer.js: -------------------------------------------------------------------------------- 1 | import isEmpty from "lodash/isEmpty"; 2 | import escapeRegExp from "lodash/escapeRegExp"; 3 | import tokenTypes from "./tokenTypes"; 4 | 5 | export default class Tokenizer { 6 | /** 7 | * @param {Object} cfg 8 | * @param {String[]} cfg.reservedWords Reserved words in SQL 9 | * @param {String[]} cfg.reservedToplevelWords Words that are set to new line separately 10 | * @param {String[]} cfg.reservedNewlineWords Words that are set to newline 11 | * @param {String[]} cfg.stringTypes String types to enable: "", '', ``, [], N'' 12 | * @param {String[]} cfg.openParens Opening parentheses to enable, like (, [ 13 | * @param {String[]} cfg.closeParens Closing parentheses to enable, like ), ] 14 | * @param {String[]} cfg.indexedPlaceholderTypes Prefixes for indexed placeholders, like ? 15 | * @param {String[]} cfg.namedPlaceholderTypes Prefixes for named placeholders, like @ and : 16 | * @param {String[]} cfg.lineCommentTypes Line comments to enable, like # and -- 17 | * @param {String[]} cfg.specialWordChars Special chars that can be found inside of words, like @ and # 18 | */ 19 | constructor(cfg) { 20 | this.WHITESPACE_REGEX = /^(\s+)/; 21 | this.NUMBER_REGEX = /^((-\s*)?[0-9]+(\.[0-9]+)?|0x[0-9a-fA-F]+|0b[01]+)\b/; 22 | this.OPERATOR_REGEX = /^(!=|<>|==|<=|>=|!<|!>|\|\||::|->>|->|~~\*|~~|!~~\*|!~~|~\*|!~\*|!~|.)/; 23 | 24 | this.BLOCK_COMMENT_REGEX = /^(\/\*[^]*?(?:\*\/|$))/; 25 | this.LINE_COMMENT_REGEX = this.createLineCommentRegex(cfg.lineCommentTypes); 26 | 27 | this.RESERVED_TOPLEVEL_REGEX = this.createReservedWordRegex(cfg.reservedToplevelWords); 28 | this.RESERVED_NEWLINE_REGEX = this.createReservedWordRegex(cfg.reservedNewlineWords); 29 | this.RESERVED_PLAIN_REGEX = this.createReservedWordRegex(cfg.reservedWords); 30 | 31 | this.WORD_REGEX = this.createWordRegex(cfg.specialWordChars); 32 | this.STRING_REGEX = this.createStringRegex(cfg.stringTypes); 33 | 34 | this.OPEN_PAREN_REGEX = this.createParenRegex(cfg.openParens); 35 | this.CLOSE_PAREN_REGEX = this.createParenRegex(cfg.closeParens); 36 | 37 | this.INDEXED_PLACEHOLDER_REGEX = this.createPlaceholderRegex(cfg.indexedPlaceholderTypes, "[0-9]*"); 38 | this.IDENT_NAMED_PLACEHOLDER_REGEX = this.createPlaceholderRegex(cfg.namedPlaceholderTypes, "[a-zA-Z0-9._$]+"); 39 | this.STRING_NAMED_PLACEHOLDER_REGEX = this.createPlaceholderRegex( 40 | cfg.namedPlaceholderTypes, 41 | this.createStringPattern(cfg.stringTypes) 42 | ); 43 | } 44 | 45 | createLineCommentRegex(lineCommentTypes) { 46 | return new RegExp(`^((?:${lineCommentTypes.map(c => escapeRegExp(c)).join("|")}).*?(?:\n|$))`); 47 | } 48 | 49 | createReservedWordRegex(reservedWords) { 50 | const reservedWordsPattern = reservedWords.join("|").replace(/ /g, "\\s+"); 51 | return new RegExp(`^(${reservedWordsPattern})\\b`, "i"); 52 | } 53 | 54 | createWordRegex(specialChars = []) { 55 | return new RegExp(`^([\\w${specialChars.join("")}]+)`); 56 | } 57 | 58 | createStringRegex(stringTypes) { 59 | return new RegExp( 60 | "^(" + this.createStringPattern(stringTypes) + ")" 61 | ); 62 | } 63 | 64 | // This enables the following string patterns: 65 | // 1. backtick quoted string using `` to escape 66 | // 2. square bracket quoted string (SQL Server) using ]] to escape 67 | // 3. double quoted string using "" or \" to escape 68 | // 4. single quoted string using '' or \' to escape 69 | // 5. national character quoted string using N'' or N\' to escape 70 | // 6. redash parameters 71 | createStringPattern(stringTypes) { 72 | const patterns = { 73 | "``": "((`[^`]*($|`))+)", 74 | "[]": "((\\[[^\\]]*($|\\]))(\\][^\\]]*($|\\]))*)", 75 | "\"\"": "((\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*(\"|$))+)", 76 | "''": "(('[^'\\\\]*(?:\\\\.[^'\\\\]*)*('|$))+)", 77 | "N''": "((N'[^N'\\\\]*(?:\\\\.[^N'\\\\]*)*('|$))+)", 78 | "{{}}": "({{(.*?)}})", 79 | }; 80 | 81 | return stringTypes.map(t => patterns[t]).join("|"); 82 | } 83 | 84 | createParenRegex(parens) { 85 | return new RegExp( 86 | "^(" + parens.map(p => this.escapeParen(p)).join("|") + ")", 87 | "i" 88 | ); 89 | } 90 | 91 | escapeParen(paren) { 92 | if (paren.length === 1) { 93 | // A single punctuation character 94 | return escapeRegExp(paren); 95 | } 96 | else { 97 | // longer word 98 | return "\\b" + paren + "\\b"; 99 | } 100 | } 101 | 102 | createPlaceholderRegex(types, pattern) { 103 | if (isEmpty(types)) { 104 | return false; 105 | } 106 | const typesRegex = types.map(escapeRegExp).join("|"); 107 | 108 | return new RegExp(`^((?:${typesRegex})(?:${pattern}))`); 109 | } 110 | 111 | /** 112 | * Takes a SQL string and breaks it into tokens. 113 | * Each token is an object with type and value. 114 | * 115 | * @param {String} input The SQL string 116 | * @return {Object[]} tokens An array of tokens. 117 | * @return {String} token.type 118 | * @return {String} token.value 119 | */ 120 | tokenize(input) { 121 | const tokens = []; 122 | let token; 123 | 124 | // Keep processing the string until it is empty 125 | while (input.length) { 126 | // Get the next token and the token type 127 | token = this.getNextToken(input, token); 128 | // Advance the string 129 | input = input.substring(token.value.length); 130 | 131 | tokens.push(token); 132 | } 133 | return tokens; 134 | } 135 | 136 | getNextToken(input, previousToken) { 137 | return this.getWhitespaceToken(input) || 138 | this.getCommentToken(input) || 139 | this.getStringToken(input) || 140 | this.getOpenParenToken(input) || 141 | this.getCloseParenToken(input) || 142 | this.getPlaceholderToken(input) || 143 | this.getNumberToken(input) || 144 | this.getReservedWordToken(input, previousToken) || 145 | this.getWordToken(input) || 146 | this.getOperatorToken(input); 147 | } 148 | 149 | getWhitespaceToken(input) { 150 | return this.getTokenOnFirstMatch({ 151 | input, 152 | type: tokenTypes.WHITESPACE, 153 | regex: this.WHITESPACE_REGEX 154 | }); 155 | } 156 | 157 | getCommentToken(input) { 158 | return this.getLineCommentToken(input) || this.getBlockCommentToken(input); 159 | } 160 | 161 | getLineCommentToken(input) { 162 | return this.getTokenOnFirstMatch({ 163 | input, 164 | type: tokenTypes.LINE_COMMENT, 165 | regex: this.LINE_COMMENT_REGEX 166 | }); 167 | } 168 | 169 | getBlockCommentToken(input) { 170 | return this.getTokenOnFirstMatch({ 171 | input, 172 | type: tokenTypes.BLOCK_COMMENT, 173 | regex: this.BLOCK_COMMENT_REGEX 174 | }); 175 | } 176 | 177 | getStringToken(input) { 178 | return this.getTokenOnFirstMatch({ 179 | input, 180 | type: tokenTypes.STRING, 181 | regex: this.STRING_REGEX 182 | }); 183 | } 184 | 185 | getOpenParenToken(input) { 186 | return this.getTokenOnFirstMatch({ 187 | input, 188 | type: tokenTypes.OPEN_PAREN, 189 | regex: this.OPEN_PAREN_REGEX 190 | }); 191 | } 192 | 193 | getCloseParenToken(input) { 194 | return this.getTokenOnFirstMatch({ 195 | input, 196 | type: tokenTypes.CLOSE_PAREN, 197 | regex: this.CLOSE_PAREN_REGEX 198 | }); 199 | } 200 | 201 | getPlaceholderToken(input) { 202 | return this.getIdentNamedPlaceholderToken(input) || 203 | this.getStringNamedPlaceholderToken(input) || 204 | this.getIndexedPlaceholderToken(input); 205 | } 206 | 207 | getIdentNamedPlaceholderToken(input) { 208 | return this.getPlaceholderTokenWithKey({ 209 | input, 210 | regex: this.IDENT_NAMED_PLACEHOLDER_REGEX, 211 | parseKey: (v) => v.slice(1) 212 | }); 213 | } 214 | 215 | getStringNamedPlaceholderToken(input) { 216 | return this.getPlaceholderTokenWithKey({ 217 | input, 218 | regex: this.STRING_NAMED_PLACEHOLDER_REGEX, 219 | parseKey: (v) => this.getEscapedPlaceholderKey({key: v.slice(2, -1), quoteChar: v.slice(-1)}) 220 | }); 221 | } 222 | 223 | getIndexedPlaceholderToken(input) { 224 | return this.getPlaceholderTokenWithKey({ 225 | input, 226 | regex: this.INDEXED_PLACEHOLDER_REGEX, 227 | parseKey: (v) => v.slice(1) 228 | }); 229 | } 230 | 231 | getPlaceholderTokenWithKey({input, regex, parseKey}) { 232 | const token = this.getTokenOnFirstMatch({input, regex, type: tokenTypes.PLACEHOLDER}); 233 | if (token) { 234 | token.key = parseKey(token.value); 235 | } 236 | return token; 237 | } 238 | 239 | getEscapedPlaceholderKey({key, quoteChar}) { 240 | return key.replace(new RegExp(escapeRegExp("\\") + quoteChar, "g"), quoteChar); 241 | } 242 | 243 | // Decimal, binary, or hex numbers 244 | getNumberToken(input) { 245 | return this.getTokenOnFirstMatch({ 246 | input, 247 | type: tokenTypes.NUMBER, 248 | regex: this.NUMBER_REGEX 249 | }); 250 | } 251 | 252 | // Punctuation and symbols 253 | getOperatorToken(input) { 254 | return this.getTokenOnFirstMatch({ 255 | input, 256 | type: tokenTypes.OPERATOR, 257 | regex: this.OPERATOR_REGEX 258 | }); 259 | } 260 | 261 | getReservedWordToken(input, previousToken) { 262 | // A reserved word cannot be preceded by a "." 263 | // this makes it so in "mytable.from", "from" is not considered a reserved word 264 | if (previousToken && previousToken.value && previousToken.value === ".") { 265 | return; 266 | } 267 | return this.getToplevelReservedToken(input) || this.getNewlineReservedToken(input) || this.getPlainReservedToken(input); 268 | } 269 | 270 | getToplevelReservedToken(input) { 271 | return this.getTokenOnFirstMatch({ 272 | input, 273 | type: tokenTypes.RESERVED_TOPLEVEL, 274 | regex: this.RESERVED_TOPLEVEL_REGEX 275 | }); 276 | } 277 | 278 | getNewlineReservedToken(input) { 279 | return this.getTokenOnFirstMatch({ 280 | input, 281 | type: tokenTypes.RESERVED_NEWLINE, 282 | regex: this.RESERVED_NEWLINE_REGEX 283 | }); 284 | } 285 | 286 | getPlainReservedToken(input) { 287 | return this.getTokenOnFirstMatch({ 288 | input, 289 | type: tokenTypes.RESERVED, 290 | regex: this.RESERVED_PLAIN_REGEX 291 | }); 292 | } 293 | 294 | getWordToken(input) { 295 | return this.getTokenOnFirstMatch({ 296 | input, 297 | type: tokenTypes.WORD, 298 | regex: this.WORD_REGEX 299 | }); 300 | } 301 | 302 | getTokenOnFirstMatch({input, type, regex}) { 303 | const matches = input.match(regex); 304 | 305 | if (matches) { 306 | return {type, value: matches[1]}; 307 | } 308 | } 309 | } 310 | -------------------------------------------------------------------------------- /test/StandardSqlFormatterTest.js: -------------------------------------------------------------------------------- 1 | import sqlFormatter from "./../src/sqlFormatter"; 2 | import behavesLikeSqlFormatter from "./behavesLikeSqlFormatter"; 3 | 4 | describe("StandardSqlFormatter", function() { 5 | behavesLikeSqlFormatter(); 6 | 7 | it("formats short CREATE TABLE", function() { 8 | expect(sqlFormatter.format( 9 | "CREATE TABLE items (a INT PRIMARY KEY, b TEXT);" 10 | )).toBe( 11 | "CREATE TABLE items (a INT PRIMARY KEY, b TEXT);" 12 | ); 13 | }); 14 | 15 | it("formats long CREATE TABLE", function() { 16 | expect(sqlFormatter.format( 17 | "CREATE TABLE items (a INT PRIMARY KEY, b TEXT, c INT NOT NULL, d INT NOT NULL);" 18 | )).toBe( 19 | "CREATE TABLE items (\n" + 20 | " a INT PRIMARY KEY,\n" + 21 | " b TEXT,\n" + 22 | " c INT NOT NULL,\n" + 23 | " d INT NOT NULL\n" + 24 | ");" 25 | ); 26 | }); 27 | 28 | it("formats INSERT without INTO", function() { 29 | const result = sqlFormatter.format( 30 | "INSERT Customers (ID, MoneyBalance, Address, City) VALUES (12,-123.4, 'Skagen 2111','Stv');" 31 | ); 32 | expect(result).toBe( 33 | "INSERT\n" + 34 | " Customers (ID, MoneyBalance, Address, City)\n" + 35 | "VALUES\n" + 36 | " (12, -123.4, 'Skagen 2111', 'Stv');" 37 | ); 38 | }); 39 | 40 | it("formats ALTER TABLE ... MODIFY query", function() { 41 | const result = sqlFormatter.format( 42 | "ALTER TABLE supplier MODIFY supplier_name char(100) NOT NULL;" 43 | ); 44 | expect(result).toBe( 45 | "ALTER TABLE\n" + 46 | " supplier\n" + 47 | "MODIFY\n" + 48 | " supplier_name char(100) NOT NULL;" 49 | ); 50 | }); 51 | 52 | it("formats ALTER TABLE ... ALTER COLUMN query", function() { 53 | const result = sqlFormatter.format( 54 | "ALTER TABLE supplier ALTER COLUMN supplier_name VARCHAR(100) NOT NULL;" 55 | ); 56 | expect(result).toBe( 57 | "ALTER TABLE\n" + 58 | " supplier\n" + 59 | "ALTER COLUMN\n" + 60 | " supplier_name VARCHAR(100) NOT NULL;" 61 | ); 62 | }); 63 | 64 | it("recognizes [] strings", function() { 65 | expect(sqlFormatter.format("[foo JOIN bar]")).toBe("[foo JOIN bar]"); 66 | expect(sqlFormatter.format("[foo ]] JOIN bar]")).toBe("[foo ]] JOIN bar]"); 67 | }); 68 | 69 | it("recognizes @variables", function() { 70 | const result = sqlFormatter.format( 71 | "SELECT @variable, @a1_2.3$, @'var name', @\"var name\", @`var name`, @[var name];" 72 | ); 73 | expect(result).toBe( 74 | "SELECT\n" + 75 | " @variable,\n" + 76 | " @a1_2.3$,\n" + 77 | " @'var name',\n" + 78 | " @\"var name\",\n" + 79 | " @`var name`,\n" + 80 | " @[var name];" 81 | ); 82 | }); 83 | 84 | it("replaces @variables with param values", function() { 85 | const result = sqlFormatter.format( 86 | "SELECT @variable, @a1_2.3$, @'var name', @\"var name\", @`var name`, @[var name], @'var\\name';", 87 | { 88 | params: { 89 | "variable": "\"variable value\"", 90 | "a1_2.3$": "'weird value'", 91 | "var name": "'var value'", 92 | "var\\name": "'var\\ value'" 93 | } 94 | } 95 | ); 96 | expect(result).toBe( 97 | "SELECT\n" + 98 | " \"variable value\",\n" + 99 | " 'weird value',\n" + 100 | " 'var value',\n" + 101 | " 'var value',\n" + 102 | " 'var value',\n" + 103 | " 'var value',\n" + 104 | " 'var\\ value';" 105 | ); 106 | }); 107 | 108 | it("recognizes :variables", function() { 109 | const result = sqlFormatter.format( 110 | "SELECT :variable, :a1_2.3$, :'var name', :\"var name\", :`var name`, :[var name];" 111 | ); 112 | expect(result).toBe( 113 | "SELECT\n" + 114 | " :variable,\n" + 115 | " :a1_2.3$,\n" + 116 | " :'var name',\n" + 117 | " :\"var name\",\n" + 118 | " :`var name`,\n" + 119 | " :[var name];" 120 | ); 121 | }); 122 | 123 | it("replaces :variables with param values", function() { 124 | const result = sqlFormatter.format( 125 | "SELECT :variable, :a1_2.3$, :'var name', :\"var name\", :`var name`," + 126 | " :[var name], :'escaped \\'var\\'', :\"^*& weird \\\" var \";", 127 | { 128 | params: { 129 | "variable": "\"variable value\"", 130 | "a1_2.3$": "'weird value'", 131 | "var name": "'var value'", 132 | "escaped 'var'": "'weirder value'", 133 | "^*& weird \" var ": "'super weird value'" 134 | } 135 | } 136 | ); 137 | expect(result).toBe( 138 | "SELECT\n" + 139 | " \"variable value\",\n" + 140 | " 'weird value',\n" + 141 | " 'var value',\n" + 142 | " 'var value',\n" + 143 | " 'var value',\n" + 144 | " 'var value',\n" + 145 | " 'weirder value',\n" + 146 | " 'super weird value';" 147 | ); 148 | }); 149 | 150 | it("recognizes ?[0-9]* placeholders", function() { 151 | const result = sqlFormatter.format("SELECT ?1, ?25, ?;"); 152 | expect(result).toBe( 153 | "SELECT\n" + 154 | " ?1,\n" + 155 | " ?25,\n" + 156 | " ?;" 157 | ); 158 | }); 159 | 160 | it("replaces ? numbered placeholders with param values", function() { 161 | const result = sqlFormatter.format("SELECT ?1, ?2, ?0;", { 162 | params: { 163 | 0: "first", 164 | 1: "second", 165 | 2: "third" 166 | } 167 | }); 168 | expect(result).toBe( 169 | "SELECT\n" + 170 | " second,\n" + 171 | " third,\n" + 172 | " first;" 173 | ); 174 | }); 175 | 176 | it("replaces ? indexed placeholders with param values", function() { 177 | const result = sqlFormatter.format("SELECT ?, ?, ?;", { 178 | params: ["first", "second", "third"] 179 | }); 180 | expect(result).toBe( 181 | "SELECT\n" + 182 | " first,\n" + 183 | " second,\n" + 184 | " third;" 185 | ); 186 | }); 187 | 188 | it("formats query with GO batch separator", function() { 189 | const result = sqlFormatter.format("SELECT 1 GO SELECT 2", { 190 | params: ["first", "second", "third"] 191 | }); 192 | expect(result).toBe( 193 | "SELECT\n" + 194 | " 1\n" + 195 | "GO\n" + 196 | "SELECT\n" + 197 | " 2" 198 | ); 199 | }); 200 | 201 | it("formats SELECT query with CROSS JOIN", function() { 202 | const result = sqlFormatter.format("SELECT a, b FROM t CROSS JOIN t2 on t.id = t2.id_t"); 203 | expect(result).toBe( 204 | "SELECT\n" + 205 | " a,\n" + 206 | " b\n" + 207 | "FROM\n" + 208 | " t\n" + 209 | " CROSS JOIN t2 on t.id = t2.id_t" 210 | ); 211 | }); 212 | 213 | it("formats SELECT query with CROSS APPLY", function() { 214 | const result = sqlFormatter.format("SELECT a, b FROM t CROSS APPLY fn(t.id)"); 215 | expect(result).toBe( 216 | "SELECT\n" + 217 | " a,\n" + 218 | " b\n" + 219 | "FROM\n" + 220 | " t\n" + 221 | " CROSS APPLY fn(t.id)" 222 | ); 223 | }); 224 | 225 | it("formats simple SELECT", function() { 226 | const result = sqlFormatter.format("SELECT N, M FROM t"); 227 | expect(result).toBe( 228 | "SELECT\n" + 229 | " N,\n" + 230 | " M\n" + 231 | "FROM\n" + 232 | " t" 233 | ); 234 | }); 235 | 236 | it("formats simple SELECT with national characters (MSSQL)", function() { 237 | const result = sqlFormatter.format("SELECT N'value'"); 238 | expect(result).toBe( 239 | "SELECT\n" + 240 | " N'value'" 241 | ); 242 | }); 243 | 244 | it("formats SELECT query with OUTER APPLY", function() { 245 | const result = sqlFormatter.format("SELECT a, b FROM t OUTER APPLY fn(t.id)"); 246 | expect(result).toBe( 247 | "SELECT\n" + 248 | " a,\n" + 249 | " b\n" + 250 | "FROM\n" + 251 | " t\n" + 252 | " OUTER APPLY fn(t.id)" 253 | ); 254 | }); 255 | 256 | it("formats FETCH FIRST like LIMIT", function() { 257 | const result = sqlFormatter.format( 258 | "SELECT * FETCH FIRST 2 ROWS ONLY;" 259 | ); 260 | expect(result).toBe( 261 | "SELECT\n" + 262 | " *\n" + 263 | "FETCH FIRST\n" + 264 | " 2 ROWS ONLY;" 265 | ); 266 | }); 267 | 268 | it("formats CASE ... WHEN with a blank expression", function() { 269 | const result = sqlFormatter.format( 270 | "CASE WHEN option = 'foo' THEN 1 WHEN option = 'bar' THEN 2 WHEN option = 'baz' THEN 3 ELSE 4 END;" 271 | ); 272 | 273 | expect(result).toBe( 274 | "CASE\n" + 275 | " WHEN option = 'foo' THEN 1\n" + 276 | " WHEN option = 'bar' THEN 2\n" + 277 | " WHEN option = 'baz' THEN 3\n" + 278 | " ELSE 4\n" + 279 | "END;" 280 | ); 281 | }); 282 | 283 | it("formats CASE ... WHEN inside SELECT", function() { 284 | const result = sqlFormatter.format( 285 | "SELECT foo, bar, CASE baz WHEN 'one' THEN 1 WHEN 'two' THEN 2 ELSE 3 END FROM table" 286 | ); 287 | 288 | expect(result).toBe( 289 | "SELECT\n" + 290 | " foo,\n" + 291 | " bar,\n" + 292 | " CASE\n" + 293 | " baz\n" + 294 | " WHEN 'one' THEN 1\n" + 295 | " WHEN 'two' THEN 2\n" + 296 | " ELSE 3\n" + 297 | " END\n" + 298 | "FROM\n" + 299 | " table" 300 | ); 301 | }); 302 | 303 | it("formats CASE ... WHEN with an expression", function() { 304 | const result = sqlFormatter.format( 305 | "CASE toString(getNumber()) WHEN 'one' THEN 1 WHEN 'two' THEN 2 WHEN 'three' THEN 3 ELSE 4 END;" 306 | ); 307 | 308 | expect(result).toBe( 309 | "CASE\n" + 310 | " toString(getNumber())\n" + 311 | " WHEN 'one' THEN 1\n" + 312 | " WHEN 'two' THEN 2\n" + 313 | " WHEN 'three' THEN 3\n" + 314 | " ELSE 4\n" + 315 | "END;" 316 | ); 317 | }); 318 | 319 | it("recognizes lowercase CASE ... END", function() { 320 | const result = sqlFormatter.format( 321 | "case when option = 'foo' then 1 else 2 end;" 322 | ); 323 | 324 | expect(result).toBe( 325 | "case\n" + 326 | " when option = 'foo' then 1\n" + 327 | " else 2\n" + 328 | "end;" 329 | ); 330 | }); 331 | 332 | // Regression test for issue #43 333 | it("ignores words CASE and END inside other strings", function() { 334 | const result = sqlFormatter.format( 335 | "SELECT CASEDATE, ENDDATE FROM table1;" 336 | ); 337 | 338 | expect(result).toBe( 339 | "SELECT\n" + 340 | " CASEDATE,\n" + 341 | " ENDDATE\n" + 342 | "FROM\n" + 343 | " table1;" 344 | ); 345 | }); 346 | 347 | it("formats tricky line comments", function() { 348 | expect(sqlFormatter.format("SELECT a#comment, here\nFROM b--comment")).toBe( 349 | "SELECT\n" + 350 | " a #comment, here\n" + 351 | "FROM\n" + 352 | " b --comment" 353 | ); 354 | }); 355 | 356 | it("formats line comments followed by semicolon", function() { 357 | expect(sqlFormatter.format("SELECT a FROM b\n--comment\n;")).toBe( 358 | "SELECT\n" + 359 | " a\n" + 360 | "FROM\n" + 361 | " b --comment\n" + 362 | ";" 363 | ); 364 | }); 365 | 366 | it("formats line comments followed by comma", function() { 367 | expect(sqlFormatter.format("SELECT a --comment\n, b")).toBe( 368 | "SELECT\n" + 369 | " a --comment\n" + 370 | ",\n" + 371 | " b" 372 | ); 373 | }); 374 | 375 | it("formats line comments followed by close-paren", function() { 376 | expect(sqlFormatter.format("SELECT ( a --comment\n )")).toBe( 377 | "SELECT\n" + 378 | " (a --comment\n" + 379 | ")" 380 | ); 381 | }); 382 | 383 | it("formats line comments followed by open-paren", function() { 384 | expect(sqlFormatter.format("SELECT a --comment\n()")).toBe( 385 | "SELECT\n" + 386 | " a --comment\n" + 387 | " ()" 388 | ); 389 | }); 390 | 391 | it("formats lonely semicolon", function() { 392 | expect(sqlFormatter.format(";")).toBe(";"); 393 | }); 394 | }); 395 | -------------------------------------------------------------------------------- /lib/core/Tokenizer.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | exports.__esModule = true; 4 | 5 | var _isEmpty = require("lodash/isEmpty"); 6 | 7 | var _isEmpty2 = _interopRequireDefault(_isEmpty); 8 | 9 | var _escapeRegExp = require("lodash/escapeRegExp"); 10 | 11 | var _escapeRegExp2 = _interopRequireDefault(_escapeRegExp); 12 | 13 | var _tokenTypes = require("./tokenTypes"); 14 | 15 | var _tokenTypes2 = _interopRequireDefault(_tokenTypes); 16 | 17 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } 18 | 19 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 20 | 21 | var Tokenizer = function () { 22 | /** 23 | * @param {Object} cfg 24 | * @param {String[]} cfg.reservedWords Reserved words in SQL 25 | * @param {String[]} cfg.reservedToplevelWords Words that are set to new line separately 26 | * @param {String[]} cfg.reservedNewlineWords Words that are set to newline 27 | * @param {String[]} cfg.stringTypes String types to enable: "", '', ``, [], N'' 28 | * @param {String[]} cfg.openParens Opening parentheses to enable, like (, [ 29 | * @param {String[]} cfg.closeParens Closing parentheses to enable, like ), ] 30 | * @param {String[]} cfg.indexedPlaceholderTypes Prefixes for indexed placeholders, like ? 31 | * @param {String[]} cfg.namedPlaceholderTypes Prefixes for named placeholders, like @ and : 32 | * @param {String[]} cfg.lineCommentTypes Line comments to enable, like # and -- 33 | * @param {String[]} cfg.specialWordChars Special chars that can be found inside of words, like @ and # 34 | */ 35 | function Tokenizer(cfg) { 36 | _classCallCheck(this, Tokenizer); 37 | 38 | this.WHITESPACE_REGEX = /^(\s+)/; 39 | this.NUMBER_REGEX = /^((-\s*)?[0-9]+(\.[0-9]+)?|0x[0-9a-fA-F]+|0b[01]+)\b/; 40 | this.OPERATOR_REGEX = /^(!=|<>|==|<=|>=|!<|!>|\|\||::|->>|->|~~\*|~~|!~~\*|!~~|~\*|!~\*|!~|.)/; 41 | 42 | this.BLOCK_COMMENT_REGEX = /^(\/\*[^]*?(?:\*\/|$))/; 43 | this.LINE_COMMENT_REGEX = this.createLineCommentRegex(cfg.lineCommentTypes); 44 | 45 | this.RESERVED_TOPLEVEL_REGEX = this.createReservedWordRegex(cfg.reservedToplevelWords); 46 | this.RESERVED_NEWLINE_REGEX = this.createReservedWordRegex(cfg.reservedNewlineWords); 47 | this.RESERVED_PLAIN_REGEX = this.createReservedWordRegex(cfg.reservedWords); 48 | 49 | this.WORD_REGEX = this.createWordRegex(cfg.specialWordChars); 50 | this.STRING_REGEX = this.createStringRegex(cfg.stringTypes); 51 | 52 | this.OPEN_PAREN_REGEX = this.createParenRegex(cfg.openParens); 53 | this.CLOSE_PAREN_REGEX = this.createParenRegex(cfg.closeParens); 54 | 55 | this.INDEXED_PLACEHOLDER_REGEX = this.createPlaceholderRegex(cfg.indexedPlaceholderTypes, "[0-9]*"); 56 | this.IDENT_NAMED_PLACEHOLDER_REGEX = this.createPlaceholderRegex(cfg.namedPlaceholderTypes, "[a-zA-Z0-9._$]+"); 57 | this.STRING_NAMED_PLACEHOLDER_REGEX = this.createPlaceholderRegex(cfg.namedPlaceholderTypes, this.createStringPattern(cfg.stringTypes)); 58 | } 59 | 60 | Tokenizer.prototype.createLineCommentRegex = function createLineCommentRegex(lineCommentTypes) { 61 | return new RegExp("^((?:" + lineCommentTypes.map(function (c) { 62 | return (0, _escapeRegExp2["default"])(c); 63 | }).join("|") + ").*?(?:\n|$))"); 64 | }; 65 | 66 | Tokenizer.prototype.createReservedWordRegex = function createReservedWordRegex(reservedWords) { 67 | var reservedWordsPattern = reservedWords.join("|").replace(/ /g, "\\s+"); 68 | return new RegExp("^(" + reservedWordsPattern + ")\\b", "i"); 69 | }; 70 | 71 | Tokenizer.prototype.createWordRegex = function createWordRegex() { 72 | var specialChars = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; 73 | 74 | return new RegExp("^([\\w" + specialChars.join("") + "]+)"); 75 | }; 76 | 77 | Tokenizer.prototype.createStringRegex = function createStringRegex(stringTypes) { 78 | return new RegExp("^(" + this.createStringPattern(stringTypes) + ")"); 79 | }; 80 | 81 | // This enables the following string patterns: 82 | // 1. backtick quoted string using `` to escape 83 | // 2. square bracket quoted string (SQL Server) using ]] to escape 84 | // 3. double quoted string using "" or \" to escape 85 | // 4. single quoted string using '' or \' to escape 86 | // 5. national character quoted string using N'' or N\' to escape 87 | // 6. redash parameters 88 | 89 | 90 | Tokenizer.prototype.createStringPattern = function createStringPattern(stringTypes) { 91 | var patterns = { 92 | "``": "((`[^`]*($|`))+)", 93 | "[]": "((\\[[^\\]]*($|\\]))(\\][^\\]]*($|\\]))*)", 94 | "\"\"": "((\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*(\"|$))+)", 95 | "''": "(('[^'\\\\]*(?:\\\\.[^'\\\\]*)*('|$))+)", 96 | "N''": "((N'[^N'\\\\]*(?:\\\\.[^N'\\\\]*)*('|$))+)", 97 | "{{}}": "({{(.*?)}})" 98 | }; 99 | 100 | return stringTypes.map(function (t) { 101 | return patterns[t]; 102 | }).join("|"); 103 | }; 104 | 105 | Tokenizer.prototype.createParenRegex = function createParenRegex(parens) { 106 | var _this = this; 107 | 108 | return new RegExp("^(" + parens.map(function (p) { 109 | return _this.escapeParen(p); 110 | }).join("|") + ")", "i"); 111 | }; 112 | 113 | Tokenizer.prototype.escapeParen = function escapeParen(paren) { 114 | if (paren.length === 1) { 115 | // A single punctuation character 116 | return (0, _escapeRegExp2["default"])(paren); 117 | } else { 118 | // longer word 119 | return "\\b" + paren + "\\b"; 120 | } 121 | }; 122 | 123 | Tokenizer.prototype.createPlaceholderRegex = function createPlaceholderRegex(types, pattern) { 124 | if ((0, _isEmpty2["default"])(types)) { 125 | return false; 126 | } 127 | var typesRegex = types.map(_escapeRegExp2["default"]).join("|"); 128 | 129 | return new RegExp("^((?:" + typesRegex + ")(?:" + pattern + "))"); 130 | }; 131 | 132 | /** 133 | * Takes a SQL string and breaks it into tokens. 134 | * Each token is an object with type and value. 135 | * 136 | * @param {String} input The SQL string 137 | * @return {Object[]} tokens An array of tokens. 138 | * @return {String} token.type 139 | * @return {String} token.value 140 | */ 141 | 142 | 143 | Tokenizer.prototype.tokenize = function tokenize(input) { 144 | var tokens = []; 145 | var token = void 0; 146 | 147 | // Keep processing the string until it is empty 148 | while (input.length) { 149 | // Get the next token and the token type 150 | token = this.getNextToken(input, token); 151 | // Advance the string 152 | input = input.substring(token.value.length); 153 | 154 | tokens.push(token); 155 | } 156 | return tokens; 157 | }; 158 | 159 | Tokenizer.prototype.getNextToken = function getNextToken(input, previousToken) { 160 | return this.getWhitespaceToken(input) || this.getCommentToken(input) || this.getStringToken(input) || this.getOpenParenToken(input) || this.getCloseParenToken(input) || this.getPlaceholderToken(input) || this.getNumberToken(input) || this.getReservedWordToken(input, previousToken) || this.getWordToken(input) || this.getOperatorToken(input); 161 | }; 162 | 163 | Tokenizer.prototype.getWhitespaceToken = function getWhitespaceToken(input) { 164 | return this.getTokenOnFirstMatch({ 165 | input: input, 166 | type: _tokenTypes2["default"].WHITESPACE, 167 | regex: this.WHITESPACE_REGEX 168 | }); 169 | }; 170 | 171 | Tokenizer.prototype.getCommentToken = function getCommentToken(input) { 172 | return this.getLineCommentToken(input) || this.getBlockCommentToken(input); 173 | }; 174 | 175 | Tokenizer.prototype.getLineCommentToken = function getLineCommentToken(input) { 176 | return this.getTokenOnFirstMatch({ 177 | input: input, 178 | type: _tokenTypes2["default"].LINE_COMMENT, 179 | regex: this.LINE_COMMENT_REGEX 180 | }); 181 | }; 182 | 183 | Tokenizer.prototype.getBlockCommentToken = function getBlockCommentToken(input) { 184 | return this.getTokenOnFirstMatch({ 185 | input: input, 186 | type: _tokenTypes2["default"].BLOCK_COMMENT, 187 | regex: this.BLOCK_COMMENT_REGEX 188 | }); 189 | }; 190 | 191 | Tokenizer.prototype.getStringToken = function getStringToken(input) { 192 | return this.getTokenOnFirstMatch({ 193 | input: input, 194 | type: _tokenTypes2["default"].STRING, 195 | regex: this.STRING_REGEX 196 | }); 197 | }; 198 | 199 | Tokenizer.prototype.getOpenParenToken = function getOpenParenToken(input) { 200 | return this.getTokenOnFirstMatch({ 201 | input: input, 202 | type: _tokenTypes2["default"].OPEN_PAREN, 203 | regex: this.OPEN_PAREN_REGEX 204 | }); 205 | }; 206 | 207 | Tokenizer.prototype.getCloseParenToken = function getCloseParenToken(input) { 208 | return this.getTokenOnFirstMatch({ 209 | input: input, 210 | type: _tokenTypes2["default"].CLOSE_PAREN, 211 | regex: this.CLOSE_PAREN_REGEX 212 | }); 213 | }; 214 | 215 | Tokenizer.prototype.getPlaceholderToken = function getPlaceholderToken(input) { 216 | return this.getIdentNamedPlaceholderToken(input) || this.getStringNamedPlaceholderToken(input) || this.getIndexedPlaceholderToken(input); 217 | }; 218 | 219 | Tokenizer.prototype.getIdentNamedPlaceholderToken = function getIdentNamedPlaceholderToken(input) { 220 | return this.getPlaceholderTokenWithKey({ 221 | input: input, 222 | regex: this.IDENT_NAMED_PLACEHOLDER_REGEX, 223 | parseKey: function parseKey(v) { 224 | return v.slice(1); 225 | } 226 | }); 227 | }; 228 | 229 | Tokenizer.prototype.getStringNamedPlaceholderToken = function getStringNamedPlaceholderToken(input) { 230 | var _this2 = this; 231 | 232 | return this.getPlaceholderTokenWithKey({ 233 | input: input, 234 | regex: this.STRING_NAMED_PLACEHOLDER_REGEX, 235 | parseKey: function parseKey(v) { 236 | return _this2.getEscapedPlaceholderKey({ key: v.slice(2, -1), quoteChar: v.slice(-1) }); 237 | } 238 | }); 239 | }; 240 | 241 | Tokenizer.prototype.getIndexedPlaceholderToken = function getIndexedPlaceholderToken(input) { 242 | return this.getPlaceholderTokenWithKey({ 243 | input: input, 244 | regex: this.INDEXED_PLACEHOLDER_REGEX, 245 | parseKey: function parseKey(v) { 246 | return v.slice(1); 247 | } 248 | }); 249 | }; 250 | 251 | Tokenizer.prototype.getPlaceholderTokenWithKey = function getPlaceholderTokenWithKey(_ref) { 252 | var input = _ref.input, 253 | regex = _ref.regex, 254 | parseKey = _ref.parseKey; 255 | 256 | var token = this.getTokenOnFirstMatch({ input: input, regex: regex, type: _tokenTypes2["default"].PLACEHOLDER }); 257 | if (token) { 258 | token.key = parseKey(token.value); 259 | } 260 | return token; 261 | }; 262 | 263 | Tokenizer.prototype.getEscapedPlaceholderKey = function getEscapedPlaceholderKey(_ref2) { 264 | var key = _ref2.key, 265 | quoteChar = _ref2.quoteChar; 266 | 267 | return key.replace(new RegExp((0, _escapeRegExp2["default"])("\\") + quoteChar, "g"), quoteChar); 268 | }; 269 | 270 | // Decimal, binary, or hex numbers 271 | 272 | 273 | Tokenizer.prototype.getNumberToken = function getNumberToken(input) { 274 | return this.getTokenOnFirstMatch({ 275 | input: input, 276 | type: _tokenTypes2["default"].NUMBER, 277 | regex: this.NUMBER_REGEX 278 | }); 279 | }; 280 | 281 | // Punctuation and symbols 282 | 283 | 284 | Tokenizer.prototype.getOperatorToken = function getOperatorToken(input) { 285 | return this.getTokenOnFirstMatch({ 286 | input: input, 287 | type: _tokenTypes2["default"].OPERATOR, 288 | regex: this.OPERATOR_REGEX 289 | }); 290 | }; 291 | 292 | Tokenizer.prototype.getReservedWordToken = function getReservedWordToken(input, previousToken) { 293 | // A reserved word cannot be preceded by a "." 294 | // this makes it so in "mytable.from", "from" is not considered a reserved word 295 | if (previousToken && previousToken.value && previousToken.value === ".") { 296 | return; 297 | } 298 | return this.getToplevelReservedToken(input) || this.getNewlineReservedToken(input) || this.getPlainReservedToken(input); 299 | }; 300 | 301 | Tokenizer.prototype.getToplevelReservedToken = function getToplevelReservedToken(input) { 302 | return this.getTokenOnFirstMatch({ 303 | input: input, 304 | type: _tokenTypes2["default"].RESERVED_TOPLEVEL, 305 | regex: this.RESERVED_TOPLEVEL_REGEX 306 | }); 307 | }; 308 | 309 | Tokenizer.prototype.getNewlineReservedToken = function getNewlineReservedToken(input) { 310 | return this.getTokenOnFirstMatch({ 311 | input: input, 312 | type: _tokenTypes2["default"].RESERVED_NEWLINE, 313 | regex: this.RESERVED_NEWLINE_REGEX 314 | }); 315 | }; 316 | 317 | Tokenizer.prototype.getPlainReservedToken = function getPlainReservedToken(input) { 318 | return this.getTokenOnFirstMatch({ 319 | input: input, 320 | type: _tokenTypes2["default"].RESERVED, 321 | regex: this.RESERVED_PLAIN_REGEX 322 | }); 323 | }; 324 | 325 | Tokenizer.prototype.getWordToken = function getWordToken(input) { 326 | return this.getTokenOnFirstMatch({ 327 | input: input, 328 | type: _tokenTypes2["default"].WORD, 329 | regex: this.WORD_REGEX 330 | }); 331 | }; 332 | 333 | Tokenizer.prototype.getTokenOnFirstMatch = function getTokenOnFirstMatch(_ref3) { 334 | var input = _ref3.input, 335 | type = _ref3.type, 336 | regex = _ref3.regex; 337 | 338 | var matches = input.match(regex); 339 | 340 | if (matches) { 341 | return { type: type, value: matches[1] }; 342 | } 343 | }; 344 | 345 | return Tokenizer; 346 | }(); 347 | 348 | exports["default"] = Tokenizer; 349 | module.exports = exports["default"]; -------------------------------------------------------------------------------- /test/behavesLikeSqlFormatter.js: -------------------------------------------------------------------------------- 1 | import sqlFormatter from "./../src/sqlFormatter"; 2 | 3 | /** 4 | * Core tests for all SQL formatters 5 | * @param {String} language 6 | */ 7 | export default function behavesLikeSqlFormatter(language) { 8 | it("uses given indent config for indention", function() { 9 | const result = sqlFormatter.format( 10 | "SELECT count(*),Column1 FROM Table1;", 11 | {language, indent: " "} 12 | ); 13 | 14 | expect(result).toBe( 15 | "SELECT\n" + 16 | " count(*),\n" + 17 | " Column1\n" + 18 | "FROM\n" + 19 | " Table1;" 20 | ); 21 | }); 22 | 23 | function format(query) { 24 | return sqlFormatter.format(query, {language}); 25 | } 26 | 27 | it("formats simple SET SCHEMA queries", function() { 28 | const result = format("SET SCHEMA tetrisdb; SET CURRENT SCHEMA bingodb;"); 29 | expect(result).toBe( 30 | "SET SCHEMA\n" + 31 | " tetrisdb;\n" + 32 | "SET CURRENT SCHEMA\n" + 33 | " bingodb;" 34 | ); 35 | }); 36 | 37 | it("formats simple SELECT query", function() { 38 | const result = format("SELECT count(*),Column1 FROM Table1;"); 39 | expect(result).toBe( 40 | "SELECT\n" + 41 | " count(*),\n" + 42 | " Column1\n" + 43 | "FROM\n" + 44 | " Table1;" 45 | ); 46 | }); 47 | 48 | it("formats complex SELECT", function() { 49 | const result = format( 50 | "SELECT DISTINCT name, ROUND(age/7) field1, 18 + 20 AS field2, 'some string' FROM foo;" 51 | ); 52 | expect(result).toBe( 53 | "SELECT\n" + 54 | " DISTINCT name,\n" + 55 | " ROUND(age / 7) field1,\n" + 56 | " 18 + 20 AS field2,\n" + 57 | " 'some string'\n" + 58 | "FROM\n" + 59 | " foo;" 60 | ); 61 | }); 62 | 63 | it("formats SELECT with complex WHERE", function() { 64 | const result = sqlFormatter.format( 65 | "SELECT * FROM foo WHERE Column1 = 'testing'" + 66 | "AND ( (Column2 = Column3 OR Column4 >= NOW()) );" 67 | ); 68 | expect(result).toBe( 69 | "SELECT\n" + 70 | " *\n" + 71 | "FROM\n" + 72 | " foo\n" + 73 | "WHERE\n" + 74 | " Column1 = 'testing'\n" + 75 | " AND (\n" + 76 | " (\n" + 77 | " Column2 = Column3\n" + 78 | " OR Column4 >= NOW()\n" + 79 | " )\n" + 80 | " );" 81 | ); 82 | }); 83 | 84 | it("formats SELECT with toplevel reserved words", function() { 85 | const result = format( 86 | "SELECT * FROM foo WHERE name = 'John' GROUP BY some_column " + 87 | "HAVING column > 10 ORDER BY other_column LIMIT 5;" 88 | ); 89 | expect(result).toBe( 90 | "SELECT\n" + 91 | " *\n" + 92 | "FROM\n" + 93 | " foo\n" + 94 | "WHERE\n" + 95 | " name = 'John'\n" + 96 | "GROUP BY\n" + 97 | " some_column\n" + 98 | "HAVING\n" + 99 | " column > 10\n" + 100 | "ORDER BY\n" + 101 | " other_column\n" + 102 | "LIMIT\n" + 103 | " 5;" 104 | ); 105 | }); 106 | 107 | it("formats LIMIT with two comma-separated values on single line", function() { 108 | const result = format( 109 | "LIMIT 5, 10;" 110 | ); 111 | expect(result).toBe( 112 | "LIMIT\n" + 113 | " 5, 10;" 114 | ); 115 | }); 116 | 117 | it("formats LIMIT of single value followed by another SELECT using commas", function() { 118 | const result = format( 119 | "LIMIT 5; SELECT foo, bar;" 120 | ); 121 | expect(result).toBe( 122 | "LIMIT\n" + 123 | " 5;\n" + 124 | "SELECT\n" + 125 | " foo,\n" + 126 | " bar;" 127 | ); 128 | }); 129 | 130 | it("formats LIMIT of single value and OFFSET", function() { 131 | const result = format( 132 | "LIMIT 5 OFFSET 8;" 133 | ); 134 | expect(result).toBe( 135 | "LIMIT\n" + 136 | " 5 OFFSET 8;" 137 | ); 138 | }); 139 | 140 | it("recognizes LIMIT in lowercase", function() { 141 | const result = format( 142 | "limit 5, 10;" 143 | ); 144 | expect(result).toBe( 145 | "limit\n" + 146 | " 5, 10;" 147 | ); 148 | }); 149 | 150 | it("preserves case of keywords", function() { 151 | const result = format( 152 | "select distinct * frOM foo left join bar WHERe a > 1 and b = 3" 153 | ); 154 | expect(result).toBe( 155 | "select\n" + 156 | " distinct *\n" + 157 | "frOM\n" + 158 | " foo\n" + 159 | " left join bar\n" + 160 | "WHERe\n" + 161 | " a > 1\n" + 162 | " and b = 3" 163 | ); 164 | }); 165 | 166 | it("formats SELECT query with SELECT query inside it", function() { 167 | const result = format( 168 | "SELECT *, SUM(*) AS sum FROM (SELECT * FROM Posts LIMIT 30) WHERE a > b" 169 | ); 170 | expect(result).toBe( 171 | "SELECT\n" + 172 | " *,\n" + 173 | " SUM(*) AS sum\n" + 174 | "FROM\n" + 175 | " (\n" + 176 | " SELECT\n" + 177 | " *\n" + 178 | " FROM\n" + 179 | " Posts\n" + 180 | " LIMIT\n" + 181 | " 30\n" + 182 | " )\n" + 183 | "WHERE\n" + 184 | " a > b" 185 | ); 186 | }); 187 | 188 | it("formats SELECT query with INNER JOIN", function() { 189 | const result = format( 190 | "SELECT customer_id.from, COUNT(order_id) AS total FROM customers " + 191 | "INNER JOIN orders ON customers.customer_id = orders.customer_id;" 192 | ); 193 | expect(result).toBe( 194 | "SELECT\n" + 195 | " customer_id.from,\n" + 196 | " COUNT(order_id) AS total\n" + 197 | "FROM\n" + 198 | " customers\n" + 199 | " INNER JOIN orders ON customers.customer_id = orders.customer_id;" 200 | ); 201 | }); 202 | 203 | it("formats SELECT query with different comments", function() { 204 | const result = format( 205 | "SELECT\n" + 206 | "/*\n" + 207 | " * This is a block comment\n" + 208 | " */\n" + 209 | "* FROM\n" + 210 | "-- This is another comment\n" + 211 | "MyTable # One final comment\n" + 212 | "WHERE 1 = 2;" 213 | ); 214 | expect(result).toBe( 215 | "SELECT\n" + 216 | " /*\n" + 217 | " * This is a block comment\n" + 218 | " */\n" + 219 | " *\n" + 220 | "FROM\n" + 221 | " -- This is another comment\n" + 222 | " MyTable # One final comment\n" + 223 | "WHERE\n" + 224 | " 1 = 2;" 225 | ); 226 | }); 227 | 228 | it("formats simple INSERT query", function() { 229 | const result = format( 230 | "INSERT INTO Customers (ID, MoneyBalance, Address, City) VALUES (12,-123.4, 'Skagen 2111','Stv');" 231 | ); 232 | expect(result).toBe( 233 | "INSERT INTO\n" + 234 | " Customers (ID, MoneyBalance, Address, City)\n" + 235 | "VALUES\n" + 236 | " (12, -123.4, 'Skagen 2111', 'Stv');" 237 | ); 238 | }); 239 | 240 | it("keeps short parenthized list with nested parenthesis on single line", function() { 241 | const result = format( 242 | "SELECT (a + b * (c - NOW()));" 243 | ); 244 | expect(result).toBe( 245 | "SELECT\n" + 246 | " (a + b * (c - NOW()));" 247 | ); 248 | }); 249 | 250 | it("breaks long parenthized lists to multiple lines", function() { 251 | const result = format( 252 | "INSERT INTO some_table (id_product, id_shop, id_currency, id_country, id_registration) (" + 253 | "SELECT IF(dq.id_discounter_shopping = 2, dq.value, dq.value / 100)," + 254 | "IF (dq.id_discounter_shopping = 2, 'amount', 'percentage') FROM foo);" 255 | ); 256 | expect(result).toBe( 257 | "INSERT INTO\n" + 258 | " some_table (\n" + 259 | " id_product,\n" + 260 | " id_shop,\n" + 261 | " id_currency,\n" + 262 | " id_country,\n" + 263 | " id_registration\n" + 264 | " ) (\n" + 265 | " SELECT\n" + 266 | " IF(\n" + 267 | " dq.id_discounter_shopping = 2,\n" + 268 | " dq.value,\n" + 269 | " dq.value / 100\n" + 270 | " ),\n" + 271 | " IF (\n" + 272 | " dq.id_discounter_shopping = 2,\n" + 273 | " 'amount',\n" + 274 | " 'percentage'\n" + 275 | " )\n" + 276 | " FROM\n" + 277 | " foo\n" + 278 | " );" 279 | ); 280 | }); 281 | 282 | it("formats simple UPDATE query", function() { 283 | const result = format( 284 | "UPDATE Customers SET ContactName='Alfred Schmidt', City='Hamburg' WHERE CustomerName='Alfreds Futterkiste';" 285 | ); 286 | expect(result).toBe( 287 | "UPDATE\n" + 288 | " Customers\n" + 289 | "SET\n" + 290 | " ContactName = 'Alfred Schmidt',\n" + 291 | " City = 'Hamburg'\n" + 292 | "WHERE\n" + 293 | " CustomerName = 'Alfreds Futterkiste';" 294 | ); 295 | }); 296 | 297 | it("formats simple DELETE query", function() { 298 | const result = format( 299 | "DELETE FROM Customers WHERE CustomerName='Alfred' AND Phone=5002132;" 300 | ); 301 | expect(result).toBe( 302 | "DELETE FROM\n" + 303 | " Customers\n" + 304 | "WHERE\n" + 305 | " CustomerName = 'Alfred'\n" + 306 | " AND Phone = 5002132;" 307 | ); 308 | }); 309 | 310 | it("formats simple DROP query", function() { 311 | const result = format( 312 | "DROP TABLE IF EXISTS admin_role;" 313 | ); 314 | expect(result).toBe( 315 | "DROP TABLE IF EXISTS admin_role;" 316 | ); 317 | }); 318 | 319 | it("formats uncomplete query", function() { 320 | const result = format("SELECT count("); 321 | expect(result).toBe( 322 | "SELECT\n" + 323 | " count(" 324 | ); 325 | }); 326 | 327 | it("formats query that ends with open comment", function() { 328 | const result = format("SELECT count(*)\n/*Comment"); 329 | expect(result).toBe( 330 | "SELECT\n" + 331 | " count(*)\n" + 332 | " /*Comment" 333 | ); 334 | }); 335 | 336 | it("formats UPDATE query with AS part", function() { 337 | const result = format( 338 | "UPDATE customers SET totalorders = ordersummary.total FROM ( SELECT * FROM bank) AS ordersummary" 339 | ); 340 | expect(result).toBe( 341 | "UPDATE\n" + 342 | " customers\n" + 343 | "SET\n" + 344 | " totalorders = ordersummary.total\n" + 345 | "FROM\n" + 346 | " (\n" + 347 | " SELECT\n" + 348 | " *\n" + 349 | " FROM\n" + 350 | " bank\n" + 351 | " ) AS ordersummary" 352 | ); 353 | }); 354 | 355 | it("formats top-level and newline multi-word reserved words with inconsistent spacing", function() { 356 | const result = format("SELECT * FROM foo LEFT \t OUTER \n JOIN bar ORDER \n BY blah"); 357 | expect(result).toBe( 358 | "SELECT\n" + 359 | " *\n" + 360 | "FROM\n" + 361 | " foo\n" + 362 | " LEFT OUTER JOIN bar\n" + 363 | "ORDER BY\n" + 364 | " blah" 365 | ); 366 | }); 367 | 368 | it("formats long double parenthized queries to multiple lines", function() { 369 | const result = format("((foo = '0123456789-0123456789-0123456789-0123456789'))"); 370 | expect(result).toBe( 371 | "(\n" + 372 | " (\n" + 373 | " foo = '0123456789-0123456789-0123456789-0123456789'\n" + 374 | " )\n" + 375 | ")" 376 | ); 377 | }); 378 | 379 | it("formats short double parenthized queries to one line", function() { 380 | const result = format("((foo = 'bar'))"); 381 | expect(result).toBe("((foo = 'bar'))"); 382 | }); 383 | 384 | it("formats single-char operators", function() { 385 | expect(format("foo = bar")).toBe("foo = bar"); 386 | expect(format("foo < bar")).toBe("foo < bar"); 387 | expect(format("foo > bar")).toBe("foo > bar"); 388 | expect(format("foo + bar")).toBe("foo + bar"); 389 | expect(format("foo - bar")).toBe("foo - bar"); 390 | expect(format("foo * bar")).toBe("foo * bar"); 391 | expect(format("foo / bar")).toBe("foo / bar"); 392 | expect(format("foo % bar")).toBe("foo % bar"); 393 | }); 394 | 395 | it("formats multi-char operators", function() { 396 | expect(format("foo != bar")).toBe("foo != bar"); 397 | expect(format("foo <> bar")).toBe("foo <> bar"); 398 | expect(format("foo == bar")).toBe("foo == bar"); // N1QL 399 | expect(format("foo || bar")).toBe("foo || bar"); // Oracle, Postgres, N1QL string concat 400 | 401 | expect(format("foo <= bar")).toBe("foo <= bar"); 402 | expect(format("foo >= bar")).toBe("foo >= bar"); 403 | 404 | expect(format("foo !< bar")).toBe("foo !< bar"); 405 | expect(format("foo !> bar")).toBe("foo !> bar"); 406 | }); 407 | 408 | it("formats logical operators", function() { 409 | expect(format("foo ALL bar")).toBe("foo ALL bar"); 410 | expect(format("foo = ANY (1, 2, 3)")).toBe("foo = ANY (1, 2, 3)"); 411 | expect(format("EXISTS bar")).toBe("EXISTS bar"); 412 | expect(format("foo IN (1, 2, 3)")).toBe("foo IN (1, 2, 3)"); 413 | expect(format("foo LIKE 'hello%'")).toBe("foo LIKE 'hello%'"); 414 | expect(format("foo IS NULL")).toBe("foo IS NULL"); 415 | expect(format("UNIQUE foo")).toBe("UNIQUE foo"); 416 | }); 417 | 418 | it("formats AND/OR operators", function() { 419 | expect(format("foo BETWEEN bar AND baz")).toBe("foo BETWEEN bar\nAND baz"); 420 | expect(format("foo AND bar")).toBe("foo\nAND bar"); 421 | expect(format("foo OR bar")).toBe("foo\nOR bar"); 422 | }); 423 | 424 | it("recognizes strings", function() { 425 | expect(format("\"foo JOIN bar\"")).toBe("\"foo JOIN bar\""); 426 | expect(format("'foo JOIN bar'")).toBe("'foo JOIN bar'"); 427 | expect(format("`foo JOIN bar`")).toBe("`foo JOIN bar`"); 428 | }); 429 | 430 | it("recognizes escaped strings", function() { 431 | expect(format("\"foo \\\" JOIN bar\"")).toBe("\"foo \\\" JOIN bar\""); 432 | expect(format("'foo \\' JOIN bar'")).toBe("'foo \\' JOIN bar'"); 433 | expect(format("`foo `` JOIN bar`")).toBe("`foo `` JOIN bar`"); 434 | }); 435 | 436 | it("formats postgres specific operators", function() { 437 | expect(format("column::int")).toBe("column :: int"); 438 | expect(format("v->2")).toBe("v -> 2"); 439 | expect(format("v->>2")).toBe( "v ->> 2"); 440 | expect(format("foo ~~ 'hello'")).toBe("foo ~~ 'hello'"); 441 | expect(format("foo !~ 'hello'")).toBe("foo !~ 'hello'"); 442 | expect(format("foo ~* 'hello'")).toBe("foo ~* 'hello'"); 443 | expect(format("foo ~~* 'hello'")).toBe("foo ~~* 'hello'"); 444 | expect(format("foo !~~ 'hello'")).toBe("foo !~~ 'hello'"); 445 | expect(format("foo !~* 'hello'")).toBe("foo !~* 'hello'"); 446 | expect(format("foo !~~* 'hello'")).toBe("foo !~~* 'hello'"); 447 | }); 448 | 449 | it("keeps separation between multiple statements", function() { 450 | expect(format("foo;bar;")).toBe("foo;\nbar;"); 451 | expect(format("foo\n;bar;")).toBe("foo;\nbar;"); 452 | expect(format("foo\n\n\n;bar;\n\n")).toBe("foo;\nbar;"); 453 | 454 | const result = format("SELECT count(*),Column1 FROM Table1;\nSELECT count(*),Column1 FROM Table2;"); 455 | expect(result).toBe( 456 | "SELECT\n" + 457 | " count(*),\n" + 458 | " Column1\n" + 459 | "FROM\n" + 460 | " Table1;\n" + 461 | "SELECT\n" + 462 | " count(*),\n" + 463 | " Column1\n" + 464 | "FROM\n" + 465 | " Table2;" 466 | ); 467 | }); 468 | } 469 | -------------------------------------------------------------------------------- /dist/sql-formatter.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports.sqlFormatter=t():e.sqlFormatter=t()}(this,function(){return function(e){function t(n){if(E[n])return E[n].exports;var r=E[n]={exports:{},id:n,loaded:!1};return e[n].call(r.exports,r,r.exports,t),r.loaded=!0,r.exports}var E={};return t.m=e,t.c=E,t.p="",t(0)}([function(e,t,E){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}t.__esModule=!0;var r=E(32),o=n(r),T=E(33),R=n(T),i=E(34),N=n(i),A=E(35),I=n(A);t.default={format:function(e,t){switch(t=t||{},t.language){case"db2":return new o.default(t).format(e);case"n1ql":return new R.default(t).format(e);case"pl/sql":return new N.default(t).format(e);case"sql":case void 0:return new I.default(t).format(e);default:throw Error("Unsupported SQL dialect: "+t.language)}}},e.exports=t.default},function(e,t,E){var n=E(17),r="object"==typeof self&&self&&self.Object===Object&&self,o=n||r||Function("return this")();e.exports=o},function(e,t,E){function n(e){return null==e?void 0===e?i:R:N&&N in Object(e)?o(e):T(e)}var r=E(11),o=E(56),T=E(64),R="[object Null]",i="[object Undefined]",N=r?r.toStringTag:void 0;e.exports=n},function(e,t,E){function n(e,t){var E=o(e,t);return r(E)?E:void 0}var r=E(46),o=E(58);e.exports=n},function(e,t){function E(e){return null!=e&&"object"==typeof e}e.exports=E},function(e,t,E){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var o=E(71),T=n(o),R=E(80),i=n(R),N=E(10),A=n(N),I=E(29),O=n(I),u=E(30),S=n(u),a=E(31),s=n(a),L=function(){function e(t,E){r(this,e),this.cfg=t||{},this.indentation=new O.default(this.cfg.indent),this.inlineBlock=new S.default,this.params=new s.default(this.cfg.params),this.tokenizer=E,this.previousReservedWord={},this.tokens=[],this.index=0}return e.prototype.format=function(e){this.tokens=this.tokenizer.tokenize(e);var t=this.getFormattedQueryFromTokens();return t.trim()},e.prototype.getFormattedQueryFromTokens=function(){var e=this,t="";return this.tokens.forEach(function(E,n){e.index=n,E.type===A.default.WHITESPACE||(E.type===A.default.LINE_COMMENT?t=e.formatLineComment(E,t):E.type===A.default.BLOCK_COMMENT?t=e.formatBlockComment(E,t):E.type===A.default.RESERVED_TOPLEVEL?(t=e.formatToplevelReservedWord(E,t),e.previousReservedWord=E):E.type===A.default.RESERVED_NEWLINE?(t=e.formatNewlineReservedWord(E,t),e.previousReservedWord=E):E.type===A.default.RESERVED?(t=e.formatWithSpaces(E,t),e.previousReservedWord=E):t=E.type===A.default.OPEN_PAREN?e.formatOpeningParentheses(E,t):E.type===A.default.CLOSE_PAREN?e.formatClosingParentheses(E,t):E.type===A.default.PLACEHOLDER?e.formatPlaceholder(E,t):","===E.value?e.formatComma(E,t):":"===E.value?e.formatWithSpaceAfter(E,t):"."===E.value?e.formatWithoutSpaces(E,t):";"===E.value?e.formatQuerySeparator(E,t):e.formatWithSpaces(E,t))}),t},e.prototype.formatLineComment=function(e,t){return this.addNewline(t+e.value)},e.prototype.formatBlockComment=function(e,t){return this.addNewline(this.addNewline(t)+this.indentComment(e.value))},e.prototype.indentComment=function(e){return e.replace(/\n/g,"\n"+this.indentation.getIndent())},e.prototype.formatToplevelReservedWord=function(e,t){return this.indentation.decreaseTopLevel(),t=this.addNewline(t),this.indentation.increaseToplevel(),t+=this.equalizeWhitespace(e.value),this.addNewline(t)},e.prototype.formatNewlineReservedWord=function(e,t){return this.addNewline(t)+this.equalizeWhitespace(e.value)+" "},e.prototype.equalizeWhitespace=function(e){return e.replace(/\s+/g," ")},e.prototype.formatOpeningParentheses=function(e,t){var E=[A.default.WHITESPACE,A.default.OPEN_PAREN,A.default.LINE_COMMENT];return(0,T.default)(E,this.previousToken().type)||(t=(0,i.default)(t)),t+=e.value,this.inlineBlock.beginIfPossible(this.tokens,this.index),this.inlineBlock.isActive()||(this.indentation.increaseBlockLevel(),t=this.addNewline(t)),t},e.prototype.formatClosingParentheses=function(e,t){return this.inlineBlock.isActive()?(this.inlineBlock.end(),this.formatWithSpaceAfter(e,t)):(this.indentation.decreaseBlockLevel(),this.formatWithSpaces(e,this.addNewline(t)))},e.prototype.formatPlaceholder=function(e,t){return t+this.params.get(e)+" "},e.prototype.formatComma=function(e,t){return t=this.trimTrailingWhitespace(t)+e.value+" ",this.inlineBlock.isActive()?t:/^LIMIT$/i.test(this.previousReservedWord.value)?t:this.addNewline(t)},e.prototype.formatWithSpaceAfter=function(e,t){return this.trimTrailingWhitespace(t)+e.value+" "},e.prototype.formatWithoutSpaces=function(e,t){return this.trimTrailingWhitespace(t)+e.value},e.prototype.formatWithSpaces=function(e,t){return t+e.value+" "},e.prototype.formatQuerySeparator=function(e,t){return this.trimTrailingWhitespace(t)+e.value+"\n"},e.prototype.addNewline=function(e){return(0,i.default)(e)+"\n"+this.indentation.getIndent()},e.prototype.trimTrailingWhitespace=function(e){return this.previousNonWhitespaceToken().type===A.default.LINE_COMMENT?(0,i.default)(e)+"\n":(0,i.default)(e)},e.prototype.previousNonWhitespaceToken=function(){for(var e=1;this.previousToken(e).type===A.default.WHITESPACE;)e++;return this.previousToken(e)},e.prototype.previousToken=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:1;return this.tokens[this.index-e]||{}},e}();t.default=L,e.exports=t.default},function(e,t,E){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var o=E(72),T=n(o),R=E(70),i=n(R),N=E(10),A=n(N),I=function(){function e(t){r(this,e),this.WHITESPACE_REGEX=/^(\s+)/,this.NUMBER_REGEX=/^((-\s*)?[0-9]+(\.[0-9]+)?|0x[0-9a-fA-F]+|0b[01]+)\b/,this.OPERATOR_REGEX=/^(!=|<>|==|<=|>=|!<|!>|\|\||::|->>|->|~~\*|~~|!~~\*|!~~|~\*|!~\*|!~|.)/,this.BLOCK_COMMENT_REGEX=/^(\/\*[^]*?(?:\*\/|$))/,this.LINE_COMMENT_REGEX=this.createLineCommentRegex(t.lineCommentTypes),this.RESERVED_TOPLEVEL_REGEX=this.createReservedWordRegex(t.reservedToplevelWords),this.RESERVED_NEWLINE_REGEX=this.createReservedWordRegex(t.reservedNewlineWords),this.RESERVED_PLAIN_REGEX=this.createReservedWordRegex(t.reservedWords),this.WORD_REGEX=this.createWordRegex(t.specialWordChars),this.STRING_REGEX=this.createStringRegex(t.stringTypes),this.OPEN_PAREN_REGEX=this.createParenRegex(t.openParens),this.CLOSE_PAREN_REGEX=this.createParenRegex(t.closeParens),this.INDEXED_PLACEHOLDER_REGEX=this.createPlaceholderRegex(t.indexedPlaceholderTypes,"[0-9]*"),this.IDENT_NAMED_PLACEHOLDER_REGEX=this.createPlaceholderRegex(t.namedPlaceholderTypes,"[a-zA-Z0-9._$]+"),this.STRING_NAMED_PLACEHOLDER_REGEX=this.createPlaceholderRegex(t.namedPlaceholderTypes,this.createStringPattern(t.stringTypes))}return e.prototype.createLineCommentRegex=function(e){return RegExp("^((?:"+e.map(function(e){return(0,i.default)(e)}).join("|")+").*?(?:\n|$))")},e.prototype.createReservedWordRegex=function(e){var t=e.join("|").replace(/ /g,"\\s+");return RegExp("^("+t+")\\b","i")},e.prototype.createWordRegex=function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:[];return RegExp("^([\\w"+e.join("")+"]+)")},e.prototype.createStringRegex=function(e){return RegExp("^("+this.createStringPattern(e)+")")},e.prototype.createStringPattern=function(e){var t={"``":"((`[^`]*($|`))+)","[]":"((\\[[^\\]]*($|\\]))(\\][^\\]]*($|\\]))*)",'""':'(("[^"\\\\]*(?:\\\\.[^"\\\\]*)*("|$))+)',"''":"(('[^'\\\\]*(?:\\\\.[^'\\\\]*)*('|$))+)","N''":"((N'[^N'\\\\]*(?:\\\\.[^N'\\\\]*)*('|$))+)","{{}}":"({{(.*?)}})"};return e.map(function(e){return t[e]}).join("|")},e.prototype.createParenRegex=function(e){var t=this;return RegExp("^("+e.map(function(e){return t.escapeParen(e)}).join("|")+")","i")},e.prototype.escapeParen=function(e){return 1===e.length?(0,i.default)(e):"\\b"+e+"\\b"},e.prototype.createPlaceholderRegex=function(e,t){if((0,T.default)(e))return!1;var E=e.map(i.default).join("|");return RegExp("^((?:"+E+")(?:"+t+"))")},e.prototype.tokenize=function(e){for(var t=[],E=void 0;e.length;)E=this.getNextToken(e,E),e=e.substring(E.value.length),t.push(E);return t},e.prototype.getNextToken=function(e,t){return this.getWhitespaceToken(e)||this.getCommentToken(e)||this.getStringToken(e)||this.getOpenParenToken(e)||this.getCloseParenToken(e)||this.getPlaceholderToken(e)||this.getNumberToken(e)||this.getReservedWordToken(e,t)||this.getWordToken(e)||this.getOperatorToken(e)},e.prototype.getWhitespaceToken=function(e){return this.getTokenOnFirstMatch({input:e,type:A.default.WHITESPACE,regex:this.WHITESPACE_REGEX})},e.prototype.getCommentToken=function(e){return this.getLineCommentToken(e)||this.getBlockCommentToken(e)},e.prototype.getLineCommentToken=function(e){return this.getTokenOnFirstMatch({input:e,type:A.default.LINE_COMMENT,regex:this.LINE_COMMENT_REGEX})},e.prototype.getBlockCommentToken=function(e){return this.getTokenOnFirstMatch({input:e,type:A.default.BLOCK_COMMENT,regex:this.BLOCK_COMMENT_REGEX})},e.prototype.getStringToken=function(e){return this.getTokenOnFirstMatch({input:e,type:A.default.STRING,regex:this.STRING_REGEX})},e.prototype.getOpenParenToken=function(e){return this.getTokenOnFirstMatch({input:e,type:A.default.OPEN_PAREN,regex:this.OPEN_PAREN_REGEX})},e.prototype.getCloseParenToken=function(e){return this.getTokenOnFirstMatch({input:e,type:A.default.CLOSE_PAREN,regex:this.CLOSE_PAREN_REGEX})},e.prototype.getPlaceholderToken=function(e){return this.getIdentNamedPlaceholderToken(e)||this.getStringNamedPlaceholderToken(e)||this.getIndexedPlaceholderToken(e)},e.prototype.getIdentNamedPlaceholderToken=function(e){return this.getPlaceholderTokenWithKey({input:e,regex:this.IDENT_NAMED_PLACEHOLDER_REGEX,parseKey:function(e){return e.slice(1)}})},e.prototype.getStringNamedPlaceholderToken=function(e){var t=this;return this.getPlaceholderTokenWithKey({input:e,regex:this.STRING_NAMED_PLACEHOLDER_REGEX,parseKey:function(e){return t.getEscapedPlaceholderKey({key:e.slice(2,-1),quoteChar:e.slice(-1)})}})},e.prototype.getIndexedPlaceholderToken=function(e){return this.getPlaceholderTokenWithKey({input:e,regex:this.INDEXED_PLACEHOLDER_REGEX,parseKey:function(e){return e.slice(1)}})},e.prototype.getPlaceholderTokenWithKey=function(e){var t=e.input,E=e.regex,n=e.parseKey,r=this.getTokenOnFirstMatch({input:t,regex:E,type:A.default.PLACEHOLDER});return r&&(r.key=n(r.value)),r},e.prototype.getEscapedPlaceholderKey=function(e){var t=e.key,E=e.quoteChar;return t.replace(RegExp((0,i.default)("\\")+E,"g"),E)},e.prototype.getNumberToken=function(e){return this.getTokenOnFirstMatch({input:e,type:A.default.NUMBER,regex:this.NUMBER_REGEX})},e.prototype.getOperatorToken=function(e){return this.getTokenOnFirstMatch({input:e,type:A.default.OPERATOR,regex:this.OPERATOR_REGEX})},e.prototype.getReservedWordToken=function(e,t){if(!t||!t.value||"."!==t.value)return this.getToplevelReservedToken(e)||this.getNewlineReservedToken(e)||this.getPlainReservedToken(e)},e.prototype.getToplevelReservedToken=function(e){return this.getTokenOnFirstMatch({input:e,type:A.default.RESERVED_TOPLEVEL,regex:this.RESERVED_TOPLEVEL_REGEX})},e.prototype.getNewlineReservedToken=function(e){return this.getTokenOnFirstMatch({input:e,type:A.default.RESERVED_NEWLINE,regex:this.RESERVED_NEWLINE_REGEX})},e.prototype.getPlainReservedToken=function(e){return this.getTokenOnFirstMatch({input:e,type:A.default.RESERVED,regex:this.RESERVED_PLAIN_REGEX})},e.prototype.getWordToken=function(e){return this.getTokenOnFirstMatch({input:e,type:A.default.WORD,regex:this.WORD_REGEX})},e.prototype.getTokenOnFirstMatch=function(e){var t=e.input,E=e.type,n=e.regex,r=t.match(n);if(r)return{type:E,value:r[1]}},e}();t.default=I,e.exports=t.default},function(e,t){var E=Array.isArray;e.exports=E},function(e,t,E){function n(e){return null!=e&&o(e.length)&&!r(e)}var r=E(23),o=E(24);e.exports=n},function(e,t){function E(e){var t=typeof e;return null!=e&&("object"==t||"function"==t)}e.exports=E},function(e,t){"use strict";t.__esModule=!0,t.default={WHITESPACE:"whitespace",WORD:"word",STRING:"string",RESERVED:"reserved",RESERVED_TOPLEVEL:"reserved-toplevel",RESERVED_NEWLINE:"reserved-newline",OPERATOR:"operator",OPEN_PAREN:"open-paren",CLOSE_PAREN:"close-paren",LINE_COMMENT:"line-comment",BLOCK_COMMENT:"block-comment",NUMBER:"number",PLACEHOLDER:"placeholder"},e.exports=t.default},function(e,t,E){var n=E(1),r=n.Symbol;e.exports=r},function(e,t,E){function n(e){return null==e?"":r(e)}var r=E(16);e.exports=n},function(e,t){function E(e,t){for(var E=-1,n=null==e?0:e.length,r=Array(n);++E-1&&e%1==0&&t>e}var n=9007199254740991,r=/^(?:0|[1-9]\d*)$/;e.exports=E},function(e,t){function E(e){var t=e&&e.constructor,E="function"==typeof t&&t.prototype||n;return e===E}var n=Object.prototype;e.exports=E},function(e,t){function E(e){if(null!=e){try{return r.call(e)}catch(e){}try{return e+""}catch(e){}}return""}var n=Function.prototype,r=n.toString;e.exports=E},function(e,t,E){var n=E(44),r=E(4),o=Object.prototype,T=o.hasOwnProperty,R=o.propertyIsEnumerable,i=n(function(){return arguments}())?n:function(e){return r(e)&&T.call(e,"callee")&&!R.call(e,"callee")};e.exports=i},function(e,t,E){(function(e){var n=E(1),r=E(77),o="object"==typeof t&&t&&!t.nodeType&&t,T=o&&"object"==typeof e&&e&&!e.nodeType&&e,R=T&&T.exports===o,i=R?n.Buffer:void 0,N=i?i.isBuffer:void 0,A=N||r;e.exports=A}).call(t,E(28)(e))},function(e,t,E){function n(e){if(!o(e))return!1;var t=r(e);return t==R||t==i||t==T||t==N}var r=E(2),o=E(9),T="[object AsyncFunction]",R="[object Function]",i="[object GeneratorFunction]",N="[object Proxy]";e.exports=n},function(e,t){function E(e){return"number"==typeof e&&e>-1&&e%1==0&&n>=e}var n=9007199254740991;e.exports=E},function(e,t,E){function n(e){return"symbol"==typeof e||o(e)&&r(e)==T}var r=E(2),o=E(4),T="[object Symbol]";e.exports=n},function(e,t,E){var n=E(47),r=E(51),o=E(63),T=o&&o.isTypedArray,R=T?r(T):n;e.exports=R},function(e,t,E){function n(e){var t=r(e),E=t%1;return t===t?E?t-E:t:0}var r=E(78);e.exports=n},function(e,t){e.exports=function(e){return e.webpackPolyfill||(e.deprecate=function(){},e.paths=[],e.children=[],e.webpackPolyfill=1),e}},function(e,t,E){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var o=E(76),T=n(o),R=E(75),i=n(R),N="top-level",A="block-level",I=function(){function e(t){r(this,e),this.indent=t||" ",this.indentTypes=[]}return e.prototype.getIndent=function(){return(0,T.default)(this.indent,this.indentTypes.length)},e.prototype.increaseToplevel=function(){this.indentTypes.push(N)},e.prototype.increaseBlockLevel=function(){this.indentTypes.push(A)},e.prototype.decreaseTopLevel=function(){(0,i.default)(this.indentTypes)===N&&this.indentTypes.pop()},e.prototype.decreaseBlockLevel=function(){for(;this.indentTypes.length>0;){var e=this.indentTypes.pop();if(e!==N)break}},e}();t.default=I,e.exports=t.default},function(e,t,E){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var o=E(10),T=n(o),R=50,i=function(){function e(){r(this,e),this.level=0}return e.prototype.beginIfPossible=function(e,t){0===this.level&&this.isInlineBlock(e,t)?this.level=1:this.level>0?this.level++:this.level=0},e.prototype.end=function(){this.level--},e.prototype.isActive=function(){return this.level>0},e.prototype.isInlineBlock=function(e,t){for(var E=0,n=0,r=t;e.length>r;r++){var o=e[r];if(E+=o.value.length,E>R)return!1;if(o.type===T.default.OPEN_PAREN)n++;else if(o.type===T.default.CLOSE_PAREN&&(n--,0===n))return!0;if(this.isForbiddenToken(o))return!1}return!1},e.prototype.isForbiddenToken=function(e){var t=e.type,E=e.value;return t===T.default.RESERVED_TOPLEVEL||t===T.default.RESERVED_NEWLINE||t===T.default.COMMENT||t===T.default.BLOCK_COMMENT||";"===E},e}();t.default=i,e.exports=t.default},function(e,t){"use strict";function E(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var n=function(){function e(t){E(this,e),this.params=t,this.index=0}return e.prototype.get=function(e){var t=e.key,E=e.value;return this.params?t?this.params[t]:this.params[this.index++]:E},e}();t.default=n,e.exports=t.default},function(e,t,E){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var o=E(5),T=n(o),R=E(6),i=n(R),N=["ABS","ACTIVATE","ALIAS","ALL","ALLOCATE","ALLOW","ALTER","ANY","ARE","ARRAY","AS","ASC","ASENSITIVE","ASSOCIATE","ASUTIME","ASYMMETRIC","AT","ATOMIC","ATTRIBUTES","AUDIT","AUTHORIZATION","AUX","AUXILIARY","AVG","BEFORE","BEGIN","BETWEEN","BIGINT","BINARY","BLOB","BOOLEAN","BOTH","BUFFERPOOL","BY","CACHE","CALL","CALLED","CAPTURE","CARDINALITY","CASCADED","CASE","CAST","CCSID","CEIL","CEILING","CHAR","CHARACTER","CHARACTER_LENGTH","CHAR_LENGTH","CHECK","CLOB","CLONE","CLOSE","CLUSTER","COALESCE","COLLATE","COLLECT","COLLECTION","COLLID","COLUMN","COMMENT","COMMIT","CONCAT","CONDITION","CONNECT","CONNECTION","CONSTRAINT","CONTAINS","CONTINUE","CONVERT","CORR","CORRESPONDING","COUNT","COUNT_BIG","COVAR_POP","COVAR_SAMP","CREATE","CROSS","CUBE","CUME_DIST","CURRENT","CURRENT_DATE","CURRENT_DEFAULT_TRANSFORM_GROUP","CURRENT_LC_CTYPE","CURRENT_PATH","CURRENT_ROLE","CURRENT_SCHEMA","CURRENT_SERVER","CURRENT_TIME","CURRENT_TIMESTAMP","CURRENT_TIMEZONE","CURRENT_TRANSFORM_GROUP_FOR_TYPE","CURRENT_USER","CURSOR","CYCLE","DATA","DATABASE","DATAPARTITIONNAME","DATAPARTITIONNUM","DATE","DAY","DAYS","DB2GENERAL","DB2GENRL","DB2SQL","DBINFO","DBPARTITIONNAME","DBPARTITIONNUM","DEALLOCATE","DEC","DECIMAL","DECLARE","DEFAULT","DEFAULTS","DEFINITION","DELETE","DENSERANK","DENSE_RANK","DEREF","DESCRIBE","DESCRIPTOR","DETERMINISTIC","DIAGNOSTICS","DISABLE","DISALLOW","DISCONNECT","DISTINCT","DO","DOCUMENT","DOUBLE","DROP","DSSIZE","DYNAMIC","EACH","EDITPROC","ELEMENT","ELSE","ELSEIF","ENABLE","ENCODING","ENCRYPTION","END","END-EXEC","ENDING","ERASE","ESCAPE","EVERY","EXCEPTION","EXCLUDING","EXCLUSIVE","EXEC","EXECUTE","EXISTS","EXIT","EXP","EXPLAIN","EXTENDED","EXTERNAL","EXTRACT","FALSE","FENCED","FETCH","FIELDPROC","FILE","FILTER","FINAL","FIRST","FLOAT","FLOOR","FOR","FOREIGN","FREE","FULL","FUNCTION","FUSION","GENERAL","GENERATED","GET","GLOBAL","GOTO","GRANT","GRAPHIC","GROUP","GROUPING","HANDLER","HASH","HASHED_VALUE","HINT","HOLD","HOUR","HOURS","IDENTITY","IF","IMMEDIATE","IN","INCLUDING","INCLUSIVE","INCREMENT","INDEX","INDICATOR","INDICATORS","INF","INFINITY","INHERIT","INNER","INOUT","INSENSITIVE","INSERT","INT","INTEGER","INTEGRITY","INTERSECTION","INTERVAL","INTO","IS","ISOBID","ISOLATION","ITERATE","JAR","JAVA","KEEP","KEY","LABEL","LANGUAGE","LARGE","LATERAL","LC_CTYPE","LEADING","LEAVE","LEFT","LIKE","LINKTYPE","LN","LOCAL","LOCALDATE","LOCALE","LOCALTIME","LOCALTIMESTAMP","LOCATOR","LOCATORS","LOCK","LOCKMAX","LOCKSIZE","LONG","LOOP","LOWER","MAINTAINED","MATCH","MATERIALIZED","MAX","MAXVALUE","MEMBER","MERGE","METHOD","MICROSECOND","MICROSECONDS","MIN","MINUTE","MINUTES","MINVALUE","MOD","MODE","MODIFIES","MODULE","MONTH","MONTHS","MULTISET","NAN","NATIONAL","NATURAL","NCHAR","NCLOB","NEW","NEW_TABLE","NEXTVAL","NO","NOCACHE","NOCYCLE","NODENAME","NODENUMBER","NOMAXVALUE","NOMINVALUE","NONE","NOORDER","NORMALIZE","NORMALIZED","NOT","NULL","NULLIF","NULLS","NUMERIC","NUMPARTS","OBID","OCTET_LENGTH","OF","OFFSET","OLD","OLD_TABLE","ON","ONLY","OPEN","OPTIMIZATION","OPTIMIZE","OPTION","ORDER","OUT","OUTER","OVER","OVERLAPS","OVERLAY","OVERRIDING","PACKAGE","PADDED","PAGESIZE","PARAMETER","PART","PARTITION","PARTITIONED","PARTITIONING","PARTITIONS","PASSWORD","PATH","PERCENTILE_CONT","PERCENTILE_DISC","PERCENT_RANK","PIECESIZE","PLAN","POSITION","POWER","PRECISION","PREPARE","PREVVAL","PRIMARY","PRIQTY","PRIVILEGES","PROCEDURE","PROGRAM","PSID","PUBLIC","QUERY","QUERYNO","RANGE","RANK","READ","READS","REAL","RECOVERY","RECURSIVE","REF","REFERENCES","REFERENCING","REFRESH","REGR_AVGX","REGR_AVGY","REGR_COUNT","REGR_INTERCEPT","REGR_R2","REGR_SLOPE","REGR_SXX","REGR_SXY","REGR_SYY","RELEASE","RENAME","REPEAT","RESET","RESIGNAL","RESTART","RESTRICT","RESULT","RESULT_SET_LOCATOR","RETURN","RETURNS","REVOKE","RIGHT","ROLE","ROLLBACK","ROLLUP","ROUND_CEILING","ROUND_DOWN","ROUND_FLOOR","ROUND_HALF_DOWN","ROUND_HALF_EVEN","ROUND_HALF_UP","ROUND_UP","ROUTINE","ROW","ROWNUMBER","ROWS","ROWSET","ROW_NUMBER","RRN","RUN","SAVEPOINT","SCHEMA","SCOPE","SCRATCHPAD","SCROLL","SEARCH","SECOND","SECONDS","SECQTY","SECURITY","SENSITIVE","SEQUENCE","SESSION","SESSION_USER","SIGNAL","SIMILAR","SIMPLE","SMALLINT","SNAN","SOME","SOURCE","SPECIFIC","SPECIFICTYPE","SQL","SQLEXCEPTION","SQLID","SQLSTATE","SQLWARNING","SQRT","STACKED","STANDARD","START","STARTING","STATEMENT","STATIC","STATMENT","STAY","STDDEV_POP","STDDEV_SAMP","STOGROUP","STORES","STYLE","SUBMULTISET","SUBSTRING","SUM","SUMMARY","SYMMETRIC","SYNONYM","SYSFUN","SYSIBM","SYSPROC","SYSTEM","SYSTEM_USER","TABLE","TABLESAMPLE","TABLESPACE","THEN","TIME","TIMESTAMP","TIMEZONE_HOUR","TIMEZONE_MINUTE","TO","TRAILING","TRANSACTION","TRANSLATE","TRANSLATION","TREAT","TRIGGER","TRIM","TRUE","TRUNCATE","TYPE","UESCAPE","UNDO","UNIQUE","UNKNOWN","UNNEST","UNTIL","UPPER","USAGE","USER","USING","VALIDPROC","VALUE","VARCHAR","VARIABLE","VARIANT","VARYING","VAR_POP","VAR_SAMP","VCAT","VERSION","VIEW","VOLATILE","VOLUMES","WHEN","WHENEVER","WHILE","WIDTH_BUCKET","WINDOW","WITH","WITHIN","WITHOUT","WLM","WRITE","XMLELEMENT","XMLEXISTS","XMLNAMESPACES","YEAR","YEARS"],A=["ADD","AFTER","ALTER COLUMN","ALTER TABLE","DELETE FROM","EXCEPT","FETCH FIRST","FROM","GROUP BY","GO","HAVING","INSERT INTO","INTERSECT","LIMIT","ORDER BY","SELECT","SET CURRENT SCHEMA","SET SCHEMA","SET","UNION ALL","UPDATE","VALUES","WHERE"],I=["AND","CROSS JOIN","INNER JOIN","JOIN","LEFT JOIN","LEFT OUTER JOIN","OR","OUTER JOIN","RIGHT JOIN","RIGHT OUTER JOIN"],O=void 0,u=function(){function e(t){r(this,e),this.cfg=t}return e.prototype.format=function(e){return O||(O=new i.default({reservedWords:N,reservedToplevelWords:A,reservedNewlineWords:I,stringTypes:['""',"''","``","[]"],openParens:["("],closeParens:[")"],indexedPlaceholderTypes:["?"],namedPlaceholderTypes:[":"],lineCommentTypes:["--"],specialWordChars:["#","@"]})),new T.default(this.cfg,O).format(e)},e}();t.default=u,e.exports=t.default},function(e,t,E){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var o=E(5),T=n(o),R=E(6),i=n(R),N=["ALL","ALTER","ANALYZE","AND","ANY","ARRAY","AS","ASC","BEGIN","BETWEEN","BINARY","BOOLEAN","BREAK","BUCKET","BUILD","BY","CALL","CASE","CAST","CLUSTER","COLLATE","COLLECTION","COMMIT","CONNECT","CONTINUE","CORRELATE","COVER","CREATE","DATABASE","DATASET","DATASTORE","DECLARE","DECREMENT","DELETE","DERIVED","DESC","DESCRIBE","DISTINCT","DO","DROP","EACH","ELEMENT","ELSE","END","EVERY","EXCEPT","EXCLUDE","EXECUTE","EXISTS","EXPLAIN","FALSE","FETCH","FIRST","FLATTEN","FOR","FORCE","FROM","FUNCTION","GRANT","GROUP","GSI","HAVING","IF","IGNORE","ILIKE","IN","INCLUDE","INCREMENT","INDEX","INFER","INLINE","INNER","INSERT","INTERSECT","INTO","IS","JOIN","KEY","KEYS","KEYSPACE","KNOWN","LAST","LEFT","LET","LETTING","LIKE","LIMIT","LSM","MAP","MAPPING","MATCHED","MATERIALIZED","MERGE","MINUS","MISSING","NAMESPACE","NEST","NOT","NULL","NUMBER","OBJECT","OFFSET","ON","OPTION","OR","ORDER","OUTER","OVER","PARSE","PARTITION","PASSWORD","PATH","POOL","PREPARE","PRIMARY","PRIVATE","PRIVILEGE","PROCEDURE","PUBLIC","RAW","REALM","REDUCE","RENAME","RETURN","RETURNING","REVOKE","RIGHT","ROLE","ROLLBACK","SATISFIES","SCHEMA","SELECT","SELF","SEMI","SET","SHOW","SOME","START","STATISTICS","STRING","SYSTEM","THEN","TO","TRANSACTION","TRIGGER","TRUE","TRUNCATE","UNDER","UNION","UNIQUE","UNKNOWN","UNNEST","UNSET","UPDATE","UPSERT","USE","USER","USING","VALIDATE","VALUE","VALUED","VALUES","VIA","VIEW","WHEN","WHERE","WHILE","WITH","WITHIN","WORK","XOR"],A=["DELETE FROM","EXCEPT ALL","EXCEPT","EXPLAIN DELETE FROM","EXPLAIN UPDATE","EXPLAIN UPSERT","FROM","GROUP BY","HAVING","INFER","INSERT INTO","INTERSECT ALL","INTERSECT","LET","LIMIT","MERGE","NEST","ORDER BY","PREPARE","SELECT","SET CURRENT SCHEMA","SET SCHEMA","SET","UNION ALL","UNION","UNNEST","UPDATE","UPSERT","USE KEYS","VALUES","WHERE"],I=["AND","INNER JOIN","JOIN","LEFT JOIN","LEFT OUTER JOIN","OR","OUTER JOIN","RIGHT JOIN","RIGHT OUTER JOIN","XOR"],O=void 0,u=function(){function e(t){r(this,e),this.cfg=t}return e.prototype.format=function(e){return O||(O=new i.default({reservedWords:N,reservedToplevelWords:A,reservedNewlineWords:I,stringTypes:['""',"''","``"],openParens:["(","[","{"],closeParens:[")","]","}"],namedPlaceholderTypes:["$"],lineCommentTypes:["#","--"]})),new T.default(this.cfg,O).format(e)},e}();t.default=u,e.exports=t.default},function(e,t,E){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var o=E(5),T=n(o),R=E(6),i=n(R),N=["A","ACCESSIBLE","AGENT","AGGREGATE","ALL","ALTER","ANY","ARRAY","AS","ASC","AT","ATTRIBUTE","AUTHID","AVG","BETWEEN","BFILE_BASE","BINARY_INTEGER","BINARY","BLOB_BASE","BLOCK","BODY","BOOLEAN","BOTH","BOUND","BULK","BY","BYTE","C","CALL","CALLING","CASCADE","CASE","CHAR_BASE","CHAR","CHARACTER","CHARSET","CHARSETFORM","CHARSETID","CHECK","CLOB_BASE","CLONE","CLOSE","CLUSTER","CLUSTERS","COALESCE","COLAUTH","COLLECT","COLUMNS","COMMENT","COMMIT","COMMITTED","COMPILED","COMPRESS","CONNECT","CONSTANT","CONSTRUCTOR","CONTEXT","CONTINUE","CONVERT","COUNT","CRASH","CREATE","CREDENTIAL","CURRENT","CURRVAL","CURSOR","CUSTOMDATUM","DANGLING","DATA","DATE_BASE","DATE","DAY","DECIMAL","DEFAULT","DEFINE","DELETE","DESC","DETERMINISTIC","DIRECTORY","DISTINCT","DO","DOUBLE","DROP","DURATION","ELEMENT","ELSIF","EMPTY","ESCAPE","EXCEPTIONS","EXCLUSIVE","EXECUTE","EXISTS","EXIT","EXTENDS","EXTERNAL","EXTRACT","FALSE","FETCH","FINAL","FIRST","FIXED","FLOAT","FOR","FORALL","FORCE","FROM","FUNCTION","GENERAL","GOTO","GRANT","GROUP","HASH","HEAP","HIDDEN","HOUR","IDENTIFIED","IF","IMMEDIATE","IN","INCLUDING","INDEX","INDEXES","INDICATOR","INDICES","INFINITE","INSTANTIABLE","INT","INTEGER","INTERFACE","INTERVAL","INTO","INVALIDATE","IS","ISOLATION","JAVA","LANGUAGE","LARGE","LEADING","LENGTH","LEVEL","LIBRARY","LIKE","LIKE2","LIKE4","LIKEC","LIMITED","LOCAL","LOCK","LONG","MAP","MAX","MAXLEN","MEMBER","MERGE","MIN","MINUS","MINUTE","MLSLABEL","MOD","MODE","MONTH","MULTISET","NAME","NAN","NATIONAL","NATIVE","NATURAL","NATURALN","NCHAR","NEW","NEXTVAL","NOCOMPRESS","NOCOPY","NOT","NOWAIT","NULL","NULLIF","NUMBER_BASE","NUMBER","OBJECT","OCICOLL","OCIDATE","OCIDATETIME","OCIDURATION","OCIINTERVAL","OCILOBLOCATOR","OCINUMBER","OCIRAW","OCIREF","OCIREFCURSOR","OCIROWID","OCISTRING","OCITYPE","OF","OLD","ON","ONLY","OPAQUE","OPEN","OPERATOR","OPTION","ORACLE","ORADATA","ORDER","ORGANIZATION","ORLANY","ORLVARY","OTHERS","OUT","OVERLAPS","OVERRIDING","PACKAGE","PARALLEL_ENABLE","PARAMETER","PARAMETERS","PARENT","PARTITION","PASCAL","PCTFREE","PIPE","PIPELINED","PLS_INTEGER","PLUGGABLE","POSITIVE","POSITIVEN","PRAGMA","PRECISION","PRIOR","PRIVATE","PROCEDURE","PUBLIC","RAISE","RANGE","RAW","READ","REAL","RECORD","REF","REFERENCE","RELEASE","RELIES_ON","REM","REMAINDER","RENAME","RESOURCE","RESULT_CACHE","RESULT","RETURN","RETURNING","REVERSE","REVOKE","ROLLBACK","ROW","ROWID","ROWNUM","ROWTYPE","SAMPLE","SAVE","SAVEPOINT","SB1","SB2","SB4","SECOND","SEGMENT","SELF","SEPARATE","SEQUENCE","SERIALIZABLE","SHARE","SHORT","SIZE_T","SIZE","SMALLINT","SOME","SPACE","SPARSE","SQL","SQLCODE","SQLDATA","SQLERRM","SQLNAME","SQLSTATE","STANDARD","START","STATIC","STDDEV","STORED","STRING","STRUCT","STYLE","SUBMULTISET","SUBPARTITION","SUBSTITUTABLE","SUBTYPE","SUCCESSFUL","SUM","SYNONYM","SYSDATE","TABAUTH","TABLE","TDO","THE","THEN","TIME","TIMESTAMP","TIMEZONE_ABBR","TIMEZONE_HOUR","TIMEZONE_MINUTE","TIMEZONE_REGION","TO","TRAILING","TRANSACTION","TRANSACTIONAL","TRIGGER","TRUE","TRUSTED","TYPE","UB1","UB2","UB4","UID","UNDER","UNIQUE","UNPLUG","UNSIGNED","UNTRUSTED","USE","USER","USING","VALIDATE","VALIST","VALUE","VARCHAR","VARCHAR2","VARIABLE","VARIANCE","VARRAY","VARYING","VIEW","VIEWS","VOID","WHENEVER","WHILE","WITH","WORK","WRAPPED","WRITE","YEAR","ZONE"],A=["ADD","ALTER COLUMN","ALTER TABLE","BEGIN","CONNECT BY","DECLARE","DELETE FROM","DELETE","END","EXCEPT","EXCEPTION","FETCH FIRST","FROM","GROUP BY","HAVING","INSERT INTO","INSERT","INTERSECT","LIMIT","LOOP","MODIFY","ORDER BY","SELECT","SET CURRENT SCHEMA","SET SCHEMA","SET","START WITH","UNION ALL","UNION","UPDATE","VALUES","WHERE"],I=["AND","CROSS APPLY","CROSS JOIN","ELSE","END","INNER JOIN","JOIN","LEFT JOIN","LEFT OUTER JOIN","OR","OUTER APPLY","OUTER JOIN","RIGHT JOIN","RIGHT OUTER JOIN","WHEN","XOR"],O=void 0,u=function(){function e(t){r(this,e),this.cfg=t}return e.prototype.format=function(e){return O||(O=new i.default({reservedWords:N,reservedToplevelWords:A,reservedNewlineWords:I,stringTypes:['""',"N''","''","``"],openParens:["(","CASE"],closeParens:[")","END"],indexedPlaceholderTypes:["?"],namedPlaceholderTypes:[":"],lineCommentTypes:["--"],specialWordChars:["_","$","#",".","@"]})),new T.default(this.cfg,O).format(e)},e}();t.default=u,e.exports=t.default},function(e,t,E){"use strict";function n(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}t.__esModule=!0;var o=E(5),T=n(o),R=E(6),i=n(R),N=["ACCESSIBLE","ACTION","AGAINST","AGGREGATE","ALGORITHM","ALL","ALTER","ANALYSE","ANALYZE","AS","ASC","AUTOCOMMIT","AUTO_INCREMENT","BACKUP","BEGIN","BETWEEN","BINLOG","BOTH","CASCADE","CASE","CHANGE","CHANGED","CHARACTER SET","CHARSET","CHECK","CHECKSUM","COLLATE","COLLATION","COLUMN","COLUMNS","COMMENT","COMMIT","COMMITTED","COMPRESSED","CONCURRENT","CONSTRAINT","CONTAINS","CONVERT","CREATE","CROSS","CURRENT_TIMESTAMP","DATABASE","DATABASES","DAY","DAY_HOUR","DAY_MINUTE","DAY_SECOND","DEFAULT","DEFINER","DELAYED","DELETE","DESC","DESCRIBE","DETERMINISTIC","DISTINCT","DISTINCTROW","DIV","DO","DROP","DUMPFILE","DUPLICATE","DYNAMIC","ELSE","ENCLOSED","END","ENGINE","ENGINES","ENGINE_TYPE","ESCAPE","ESCAPED","EVENTS","EXEC","EXECUTE","EXISTS","EXPLAIN","EXTENDED","FAST","FETCH","FIELDS","FILE","FIRST","FIXED","FLUSH","FOR","FORCE","FOREIGN","FULL","FULLTEXT","FUNCTION","GLOBAL","GRANT","GRANTS","GROUP_CONCAT","HEAP","HIGH_PRIORITY","HOSTS","HOUR","HOUR_MINUTE","HOUR_SECOND","IDENTIFIED","IF","IFNULL","IGNORE","IN","INDEX","INDEXES","INFILE","INSERT","INSERT_ID","INSERT_METHOD","INTERVAL","INTO","INVOKER","IS","ISOLATION","KEY","KEYS","KILL","LAST_INSERT_ID","LEADING","LEVEL","LIKE","LINEAR","LINES","LOAD","LOCAL","LOCK","LOCKS","LOGS","LOW_PRIORITY","MARIA","MASTER","MASTER_CONNECT_RETRY","MASTER_HOST","MASTER_LOG_FILE","MATCH","MAX_CONNECTIONS_PER_HOUR","MAX_QUERIES_PER_HOUR","MAX_ROWS","MAX_UPDATES_PER_HOUR","MAX_USER_CONNECTIONS","MEDIUM","MERGE","MINUTE","MINUTE_SECOND","MIN_ROWS","MODE","MODIFY","MONTH","MRG_MYISAM","MYISAM","NAMES","NATURAL","NOT","NOW()","NULL","OFFSET","ON DELETE","ON UPDATE","ON","ONLY","OPEN","OPTIMIZE","OPTION","OPTIONALLY","OUTFILE","PACK_KEYS","PAGE","PARTIAL","PARTITION","PARTITIONS","PASSWORD","PRIMARY","PRIVILEGES","PROCEDURE","PROCESS","PROCESSLIST","PURGE","QUICK","RAID0","RAID_CHUNKS","RAID_CHUNKSIZE","RAID_TYPE","RANGE","READ","READ_ONLY","READ_WRITE","REFERENCES","REGEXP","RELOAD","RENAME","REPAIR","REPEATABLE","REPLACE","REPLICATION","RESET","RESTORE","RESTRICT","RETURN","RETURNS","REVOKE","RLIKE","ROLLBACK","ROW","ROWS","ROW_FORMAT","SECOND","SECURITY","SEPARATOR","SERIALIZABLE","SESSION","SHARE","SHOW","SHUTDOWN","SLAVE","SONAME","SOUNDS","SQL","SQL_AUTO_IS_NULL","SQL_BIG_RESULT","SQL_BIG_SELECTS","SQL_BIG_TABLES","SQL_BUFFER_RESULT","SQL_CACHE","SQL_CALC_FOUND_ROWS","SQL_LOG_BIN","SQL_LOG_OFF","SQL_LOG_UPDATE","SQL_LOW_PRIORITY_UPDATES","SQL_MAX_JOIN_SIZE","SQL_NO_CACHE","SQL_QUOTE_SHOW_CREATE","SQL_SAFE_UPDATES","SQL_SELECT_LIMIT","SQL_SLAVE_SKIP_COUNTER","SQL_SMALL_RESULT","SQL_WARNINGS","START","STARTING","STATUS","STOP","STORAGE","STRAIGHT_JOIN","STRING","STRIPED","SUPER","TABLE","TABLES","TEMPORARY","TERMINATED","THEN","TO","TRAILING","TRANSACTIONAL","TRUE","TRUNCATE","TYPE","TYPES","UNCOMMITTED","UNIQUE","UNLOCK","UNSIGNED","USAGE","USE","USING","VARIABLES","VIEW","WHEN","WITH","WORK","WRITE","YEAR_MONTH"],A=["ADD","AFTER","ALTER COLUMN","ALTER TABLE","DELETE FROM","EXCEPT","FETCH FIRST","FROM","GROUP BY","GO","HAVING","INSERT INTO","INSERT","INTERSECT","LIMIT","MODIFY","ORDER BY","SELECT","SET CURRENT SCHEMA","SET SCHEMA","SET","UNION ALL","UNION","UPDATE","VALUES","WHERE"],I=["AND","CROSS APPLY","CROSS JOIN","ELSE","INNER JOIN","JOIN","LEFT JOIN","LEFT OUTER JOIN","OR","OUTER APPLY","OUTER JOIN","RIGHT JOIN","RIGHT OUTER JOIN","WHEN","XOR"],O=void 0,u=function(){ 2 | function e(t){r(this,e),this.cfg=t}return e.prototype.format=function(e){return O||(O=new i.default({reservedWords:N,reservedToplevelWords:A,reservedNewlineWords:I,stringTypes:['""',"N''","''","``","[]","{{}}"],openParens:["(","CASE"],closeParens:[")","END"],indexedPlaceholderTypes:["?"],namedPlaceholderTypes:["@",":"],lineCommentTypes:["#","--"]})),new T.default(this.cfg,O).format(e)},e}();t.default=u,e.exports=t.default},function(e,t,E){var n=E(3),r=E(1),o=n(r,"DataView");e.exports=o},function(e,t,E){var n=E(3),r=E(1),o=n(r,"Map");e.exports=o},function(e,t,E){var n=E(3),r=E(1),o=n(r,"Promise");e.exports=o},function(e,t,E){var n=E(3),r=E(1),o=n(r,"Set");e.exports=o},function(e,t,E){var n=E(3),r=E(1),o=n(r,"WeakMap");e.exports=o},function(e,t,E){function n(e,t){var E=T(e),n=!E&&o(e),A=!E&&!n&&R(e),O=!E&&!n&&!A&&N(e),u=E||n||A||O,S=u?r(e.length,String):[],a=S.length;for(var s in e)!t&&!I.call(e,s)||u&&("length"==s||A&&("offset"==s||"parent"==s)||O&&("buffer"==s||"byteLength"==s||"byteOffset"==s)||i(s,a))||S.push(s);return S}var r=E(50),o=E(21),T=E(7),R=E(22),i=E(18),N=E(26),A=Object.prototype,I=A.hasOwnProperty;e.exports=n},function(e,t){function E(e){return e.split("")}e.exports=E},function(e,t){function E(e,t,E,n){for(var r=e.length,o=E+(n?1:-1);n?o--:++ot||t>n)return E;do t%2&&(E+=e),t=r(t/2),t&&(e+=e);while(t);return E}var n=9007199254740991,r=Math.floor;e.exports=E},function(e,t){function E(e,t,E){var n=-1,r=e.length;0>t&&(t=-t>r?0:r+t),E=E>r?r:E,0>E&&(E+=r),r=t>E?0:E-t>>>0,t>>>=0;for(var o=Array(r);++nE?r(e,t,E):e}var r=E(49);e.exports=n},function(e,t,E){function n(e,t){for(var E=e.length;E--&&r(t,e[E],0)>-1;);return E}var r=E(14);e.exports=n},function(e,t,E){var n=E(1),r=n["__core-js_shared__"];e.exports=r},function(e,t,E){function n(e){var t=T.call(e,i),E=e[i];try{e[i]=void 0;var n=!0}catch(e){}var r=R.call(e);return n&&(t?e[i]=E:delete e[i]),r}var r=E(11),o=Object.prototype,T=o.hasOwnProperty,R=o.toString,i=r?r.toStringTag:void 0;e.exports=n},function(e,t,E){var n=E(36),r=E(37),o=E(38),T=E(39),R=E(40),i=E(2),N=E(20),A="[object Map]",I="[object Object]",O="[object Promise]",u="[object Set]",S="[object WeakMap]",a="[object DataView]",s=N(n),L=N(r),c=N(o),f=N(T),C=N(R),p=i;(n&&p(new n(new ArrayBuffer(1)))!=a||r&&p(new r)!=A||o&&p(o.resolve())!=O||T&&p(new T)!=u||R&&p(new R)!=S)&&(p=function(e){var t=i(e),E=t==I?e.constructor:void 0,n=E?N(E):"";if(n)switch(n){case s:return a;case L:return A;case c:return O;case f:return u;case C:return S}return t}),e.exports=p},function(e,t){function E(e,t){return null==e?void 0:e[t]}e.exports=E},function(e,t){function E(e){return A.test(e)}var n="\\ud800-\\udfff",r="\\u0300-\\u036f",o="\\ufe20-\\ufe2f",T="\\u20d0-\\u20ff",R=r+o+T,i="\\ufe0e\\ufe0f",N="\\u200d",A=RegExp("["+N+n+R+i+"]");e.exports=E},function(e,t,E){function n(e,t,E){if(!R(E))return!1;var n=typeof t;return!!("number"==n?o(E)&&T(t,E.length):"string"==n&&t in E)&&r(E[t],e)}var r=E(69),o=E(8),T=E(18),R=E(9);e.exports=n},function(e,t,E){function n(e){return!!o&&o in e}var r=E(55),o=function(){var e=/[^.]+$/.exec(r&&r.keys&&r.keys.IE_PROTO||"");return e?"Symbol(src)_1."+e:""}();e.exports=n},function(e,t,E){var n=E(65),r=n(Object.keys,Object);e.exports=r},function(e,t,E){(function(e){var n=E(17),r="object"==typeof t&&t&&!t.nodeType&&t,o=r&&"object"==typeof e&&e&&!e.nodeType&&e,T=o&&o.exports===r,R=T&&n.process,i=function(){try{var e=o&&o.require&&o.require("util").types;return e?e:R&&R.binding&&R.binding("util")}catch(e){}}();e.exports=i}).call(t,E(28)(e))},function(e,t){function E(e){return r.call(e)}var n=Object.prototype,r=n.toString;e.exports=E},function(e,t){function E(e,t){return function(E){return e(t(E))}}e.exports=E},function(e,t){function E(e,t,E){for(var n=E-1,r=e.length;++nE&&(E=N(A+E,0)),T(e)?A>=E&&e.indexOf(t,E)>-1:!!A&&r(e,t,E)>-1}var r=E(14),o=E(8),T=E(73),R=E(27),i=E(81),N=Math.max;e.exports=n},function(e,t,E){function n(e){if(null==e)return!0;if(i(e)&&(R(e)||"string"==typeof e||"function"==typeof e.splice||N(e)||I(e)||T(e)))return!e.length;var t=o(e);if(t==O||t==u)return!e.size;if(A(e))return!r(e).length;for(var E in e)if(a.call(e,E))return!1;return!0}var r=E(15),o=E(57),T=E(21),R=E(7),i=E(8),N=E(22),A=E(19),I=E(26),O="[object Map]",u="[object Set]",S=Object.prototype,a=S.hasOwnProperty;e.exports=n},function(e,t,E){function n(e){return"string"==typeof e||!o(e)&&T(e)&&r(e)==R}var r=E(2),o=E(7),T=E(4),R="[object String]";e.exports=n},function(e,t,E){function n(e){return T(e)?r(e):o(e)}var r=E(41),o=E(15),T=E(8);e.exports=n},function(e,t){function E(e){var t=null==e?0:e.length;return t?e[t-1]:void 0}e.exports=E},function(e,t,E){function n(e,t,E){return t=(E?o(e,t,E):void 0===t)?1:T(t),r(R(e),t)}var r=E(48),o=E(60),T=E(27),R=E(12);e.exports=n},function(e,t){function E(){return!1}e.exports=E},function(e,t,E){function n(e){if(!e)return 0===e?e:0;if(e=r(e),e===o||e===-o){var t=0>e?-1:1;return t*T}return e===e?e:0}var r=E(79),o=1/0,T=1.7976931348623157e308;e.exports=n},function(e,t,E){function n(e){if("number"==typeof e)return e;if(o(e))return T;if(r(e)){var t="function"==typeof e.valueOf?e.valueOf():e;e=r(t)?t+"":t}if("string"!=typeof e)return 0===e?e:+e;e=e.replace(R,"");var E=N.test(e);return E||A.test(e)?I(e.slice(2),E?2:8):i.test(e)?T:+e}var r=E(9),o=E(25),T=NaN,R=/^\s+|\s+$/g,i=/^[-+]0x[0-9a-f]+$/i,N=/^0b[01]+$/i,A=/^0o[0-7]+$/i,I=parseInt;e.exports=n},function(e,t,E){function n(e,t,E){if(e=i(e),e&&(E||void 0===t))return e.replace(N,"");if(!e||!(t=r(t)))return e;var n=R(e),A=T(n,R(t))+1;return o(n,0,A).join("")}var r=E(16),o=E(53),T=E(54),R=E(67),i=E(12),N=/\s+$/;e.exports=n},function(e,t,E){function n(e){return null==e?[]:r(e,o(e))}var r=E(52),o=E(74);e.exports=n}])}); --------------------------------------------------------------------------------