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 }; 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/behavesLikeSqlFormatter.js b/test/behavesLikeSqlFormatter.js index 38cd57156d..a1af7b20f5 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 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 ; + `); + }); });