From 4427868fc2a9eacf1910859a78124be4ef0c3230 Mon Sep 17 00:00:00 2001 From: emirmrkaljevic <93012621+emirmrkaljevic@users.noreply.github.com> Date: Thu, 15 Oct 2026 10:54:25 +0200 Subject: [PATCH] fix(mysql): apply keywordCase to INTERVAL duration expressions INTERVAL is listed as both a function and a keyword in MySQL-like dialects. Without a following '(', it was demoted to IDENTIFIER, so keywordCase left `interval 30 day` unchanged while MariaDB uppercased it. Keep the non-call form as RESERVED_KEYWORD in the shared MariaDB post-process path. --- src/languages/mariadb/likeMariaDb.ts | 9 +++++++++ test/mysql.test.ts | 21 +++++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/src/languages/mariadb/likeMariaDb.ts b/src/languages/mariadb/likeMariaDb.ts index 63c7cba1f7..1c43361417 100644 --- a/src/languages/mariadb/likeMariaDb.ts +++ b/src/languages/mariadb/likeMariaDb.ts @@ -14,6 +14,15 @@ export function postProcess(tokens: Token[]) { // This is VALUES() function, not VALUES clause return { ...token, type: TokenType.RESERVED_FUNCTION_NAME }; } + // INTERVAL is both a comparison function (INTERVAL(N, ...)) and a + // duration keyword (INTERVAL n DAY). Without '(', keep it as a keyword + // so keywordCase applies; otherwise disambiguateTokens would demote it + // to IDENTIFIER (see #985). + if (token.type === TokenType.RESERVED_FUNCTION_NAME && token.text === 'INTERVAL') { + if (nextToken.text !== '(') { + return { ...token, type: TokenType.RESERVED_KEYWORD }; + } + } return token; }); } diff --git a/test/mysql.test.ts b/test/mysql.test.ts index e6bcd37af9..0b654d4d6e 100644 --- a/test/mysql.test.ts +++ b/test/mysql.test.ts @@ -98,6 +98,27 @@ describe('MySqlFormatter', () => { `); }); + it('applies keywordCase to INTERVAL duration expressions (#985)', () => { + expect( + format(`select now() - interval 30 day; select date_sub(now(), interval 30 day);`, { + keywordCase: 'upper', + }) + ).toBe(dedent` + SELECT + now() - INTERVAL 30 day; + + SELECT + date_sub(now(), INTERVAL 30 day); + `); + }); + + it('still treats INTERVAL(...) as a function name', () => { + expect(format(`select interval(55, 0, 50, 100);`, { functionCase: 'upper' })).toBe(dedent` + select + INTERVAL(55, 0, 50, 100); + `); + }); + it('formats ALTER TABLE ... ALTER COLUMN', () => { expect( format(