From 431c5f466523acb1bf2b188cf441bb6f36804b02 Mon Sep 17 00:00:00 2001 From: "Justin Dane D. Vallar" Date: Mon, 24 Jan 2022 16:56:14 +0800 Subject: [PATCH 1/4] Implement failing tests for floats with trailing point --- test/behavesLikeSqlFormatter.js | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/test/behavesLikeSqlFormatter.js b/test/behavesLikeSqlFormatter.js index 38cd57156d..8e6bdd7bfc 100644 --- a/test/behavesLikeSqlFormatter.js +++ b/test/behavesLikeSqlFormatter.js @@ -121,7 +121,7 @@ export default function behavesLikeSqlFormatter(format) { expect(result).toBe(dedent` LIMIT 5; - + SELECT foo, bar; @@ -378,7 +378,7 @@ export default function behavesLikeSqlFormatter(format) { Column1 FROM Table1; - + SELECT COUNT(*), Column1 @@ -408,7 +408,7 @@ export default function behavesLikeSqlFormatter(format) { * FROM test; - + CREATE TABLE test( id NUMBER NOT NULL, @@ -429,6 +429,22 @@ export default function behavesLikeSqlFormatter(format) { `); }); + it('correctly handles floats with trailing point', () => { + let result = format('SELECT 1000. AS a;'); + expect(result).toBe(dedent` + SELECT + 1000. AS a; + `); + + result = format('SELECT a, b / 1000. AS a_s, 100. * b / SUM(a_s);'); + expect(result).toBe(dedent` + SELECT + a, + b / 1000. AS a_s, + 100.* b / SUM(a_s); + `); + }); + it('does not split UNION ALL in half', () => { const result = format(` SELECT * FROM tbl1 From c0c1dcb2093583003d1c492bf5751da1a6ec2680 Mon Sep 17 00:00:00 2001 From: "Justin Dane D. Vallar" Date: Mon, 24 Jan 2022 17:14:26 +0800 Subject: [PATCH 2/4] Update tests --- test/behavesLikeSqlFormatter.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/behavesLikeSqlFormatter.js b/test/behavesLikeSqlFormatter.js index 8e6bdd7bfc..a1af7b20f5 100644 --- a/test/behavesLikeSqlFormatter.js +++ b/test/behavesLikeSqlFormatter.js @@ -439,10 +439,10 @@ export default function behavesLikeSqlFormatter(format) { result = format('SELECT a, b / 1000. AS a_s, 100. * b / SUM(a_s);'); expect(result).toBe(dedent` SELECT - a, - b / 1000. AS a_s, - 100.* b / SUM(a_s); - `); + a, + b / 1000. AS a_s, + 100. * b / SUM(a_s); + `); }); it('does not split UNION ALL in half', () => { From 71ab1723003164fe7c0ebc9ed4bc1005f5812230 Mon Sep 17 00:00:00 2001 From: "Justin Dane D. Vallar" Date: Mon, 24 Jan 2022 17:15:51 +0800 Subject: [PATCH 3/4] Fix parsing bug with trailing floating-point --- src/core/Tokenizer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/Tokenizer.ts b/src/core/Tokenizer.ts index 729899ff41..973cafda9a 100644 --- a/src/core/Tokenizer.ts +++ b/src/core/Tokenizer.ts @@ -83,7 +83,7 @@ export default class Tokenizer { [TokenType.LINE_COMMENT]: regexFactory.createLineCommentRegex(cfg.lineCommentTypes), [TokenType.BLOCK_COMMENT]: /^(\/\*[^]*?(?:\*\/|$))/u, [TokenType.NUMBER]: - /^((-\s*)?[0-9]+(\.[0-9]+)?([eE][-+]?[0-9]+(\.[0-9]+)?)?|0x[0-9a-fA-F]+|0b[01]+)\b/u, + /^((-\s*)?[0-9]+(\.[0-9]*)?([eE][-+]?[0-9]+(\.[0-9]+)?)?|0x[0-9a-fA-F]+|0b[01]+)/u, [TokenType.PLACEHOLDER]: NULL_REGEX, // matches nothing }; From 85bdc5ddde6b6a5a9c9f4bba47fa6e03363f4924 Mon Sep 17 00:00:00 2001 From: Giorgos Petrou Date: Thu, 24 Mar 2022 11:45:21 +0200 Subject: [PATCH 4/4] Support DELIMITER for MariaDB and MySQL --- src/core/token.ts | 4 +++- src/languages/mariadb.formatter.ts | 16 ++++++++++++++++ src/languages/mysql.formatter.ts | 16 ++++++++++++++++ test/mariadb.test.js | 13 +++++++++++++ test/mysql.test.js | 11 +++++++++++ 5 files changed, 59 insertions(+), 1 deletion(-) diff --git a/src/core/token.ts b/src/core/token.ts index 722cc256f6..0cca71e822 100644 --- a/src/core/token.ts +++ b/src/core/token.ts @@ -33,9 +33,11 @@ export const testToken = (compareToken: Token) => (token: Token) => export const isToken = { AS: testToken({ value: 'AS', type: TokenType.RESERVED_KEYWORD }), AND: testToken({ value: 'AND', type: TokenType.RESERVED_LOGICAL_OPERATOR }), + BEGIN: testToken({ value: 'BEGIN', type: TokenType.RESERVED_COMMAND }), BETWEEN: testToken({ value: 'BETWEEN', type: TokenType.RESERVED_KEYWORD }), - CASE: testToken({ value: 'CASE', type: TokenType.BLOCK_START }), BY: testToken({ value: 'BY', type: TokenType.RESERVED_KEYWORD }), + CASE: testToken({ value: 'CASE', type: TokenType.BLOCK_START }), + DELIMITER: testToken({ value: 'DELIMITER', type: TokenType.WORD }), END: testToken({ value: 'END', type: TokenType.BLOCK_END }), FROM: testToken({ value: 'FROM', type: TokenType.RESERVED_COMMAND }), LATERAL: testToken({ value: 'LATERAL', type: TokenType.RESERVED_DEPENDENT_CLAUSE }), diff --git a/src/languages/mariadb.formatter.ts b/src/languages/mariadb.formatter.ts index 3c63f8829e..5ca20ebc0a 100644 --- a/src/languages/mariadb.formatter.ts +++ b/src/languages/mariadb.formatter.ts @@ -1185,6 +1185,22 @@ export default class MariaDbFormatter extends Formatter { } tokenOverride(token: Token) { + if (this.tokenLookBehind()?.type === TokenType.BLOCK_END && isToken.BEGIN(token)) { + return { type: TokenType.BLOCK_START, value: '\n' + token.value, whitespaceBefore: '' }; + } + + if (isToken.DELIMITER(token) && this.tokenLookBehind()?.type === TokenType.OPERATOR) { + return { type: TokenType.RESERVED_KEYWORD, value: '\n' + token.value }; + } + + if ( + token.type === TokenType.OPERATOR && + isToken.DELIMITER(this.tokenLookBehind()) && + this.tokenLookAhead()?.type !== TokenType.OPERATOR + ) { + return { type: TokenType.STRING, value: token.value }; + } + // [SET] ( ... if (isToken.SET(token) && this.tokenLookAhead()?.value === '(') { // This is SET datatype, not SET statement diff --git a/src/languages/mysql.formatter.ts b/src/languages/mysql.formatter.ts index 02263eff7a..f643f2395e 100644 --- a/src/languages/mysql.formatter.ts +++ b/src/languages/mysql.formatter.ts @@ -1348,6 +1348,22 @@ export default class MySqlFormatter extends Formatter { } tokenOverride(token: Token) { + if (this.tokenLookBehind()?.type === TokenType.BLOCK_END && isToken.BEGIN(token)) { + return { type: TokenType.BLOCK_START, value: '\n' + token.value, whitespaceBefore: '' }; + } + + if (isToken.DELIMITER(token) && this.tokenLookBehind()?.type === TokenType.OPERATOR) { + return { type: TokenType.RESERVED_KEYWORD, value: '\n' + token.value }; + } + + if ( + token.type === TokenType.OPERATOR && + isToken.DELIMITER(this.tokenLookBehind()) && + this.tokenLookAhead()?.type !== TokenType.OPERATOR + ) { + return { type: TokenType.STRING, value: token.value }; + } + // [LATERAL] ( ... if (isToken.LATERAL(token) && this.tokenLookAhead()?.type === TokenType.BLOCK_START) { // This is a subquery, treat it like a join diff --git a/test/mariadb.test.js b/test/mariadb.test.js index fb720fdd0e..3358747366 100644 --- a/test/mariadb.test.js +++ b/test/mariadb.test.js @@ -1,3 +1,4 @@ +import dedent from 'dedent-js'; import * as sqlFormatter from '../src/sqlFormatter'; import MariaDbFormatter from '../src/languages/mariadb.formatter'; import behavesLikeMariaDbFormatter from './behavesLikeMariaDbFormatter'; @@ -12,4 +13,16 @@ describe('MariaDbFormatter', () => { supportsStrings(format, MariaDbFormatter.stringTypes); supportsOperators(format, MariaDbFormatter.operators, MariaDbFormatter.reservedLogicalOperators); + + it('supports DELIMITER', () => { + const result = format('DELIMITER $$ CREATE PROCEDURE sp_name() BEGIN END $$ DELIMITER ;'); + expect(result).toBe(dedent` + DELIMITER $$ + CREATE PROCEDURE + sp_name() + BEGIN + END $$ + DELIMITER ; + `); + }); }); diff --git a/test/mysql.test.js b/test/mysql.test.js index ca7d100887..2e7d96a09e 100644 --- a/test/mysql.test.js +++ b/test/mysql.test.js @@ -25,4 +25,15 @@ describe('MySqlFormatter', () => { foo; `); }); + + it('supports DELIMITER', () => { + const result = format('DELIMITER $$ CREATE PROCEDURE sp_name() BEGIN END $$ DELIMITER ;'); + expect(result).toBe(dedent` + DELIMITER $$ + CREATE PROCEDURE + sp_name() BEGIN + END $$ + DELIMITER ; + `); + }); });