Skip to content

Commit ae7ee87

Browse files
committed
Clean up keyword vs operator for in/any
1 parent 28dd0f0 commit ae7ee87

File tree

5 files changed

+24
-70
lines changed

5 files changed

+24
-70
lines changed

src/languages/clickhouse/clickhouse.formatter.ts

Lines changed: 17 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { DialectOptions } from '../../dialect.js';
22
import { expandPhrases } from '../../expandPhrases.js';
3-
import { EOF_TOKEN, Token, TokenType } from '../../lexer/token.js';
3+
import { EOF_TOKEN, isToken, Token, TokenType } from '../../lexer/token.js';
44
import { functions } from './clickhouse.functions.js';
55
import { dataTypes, keywords } from './clickhouse.keywords.js';
66

@@ -298,34 +298,22 @@ function postProcess(tokens: Token[]): Token[] {
298298
const nextToken = tokens[i + 1] || EOF_TOKEN;
299299
const prevToken = tokens[i - 1] || EOF_TOKEN;
300300

301-
// Only process IN and ANY that are currently RESERVED_FUNCTION_NAME
302-
// Check text (uppercase canonical form) for matching, but preserve raw (original casing)
301+
// We want to convert IN from RESERVED_KEYWORD to RESERVED_FUNCTION_NAME
302+
// when it's used as a function. Whether it's used as an operator depends on
303+
// the previous token and whether the next token is an opening parenthesis.
303304
if (
304-
token.type === TokenType.RESERVED_FUNCTION_NAME &&
305-
(token.text === 'IN' || token.text === 'ANY')
305+
isToken.IN(token) &&
306+
!(
307+
prevToken.type === TokenType.IDENTIFIER ||
308+
prevToken.type === TokenType.QUOTED_IDENTIFIER ||
309+
prevToken.type === TokenType.NUMBER ||
310+
prevToken.type === TokenType.STRING ||
311+
prevToken.type === TokenType.CLOSE_PAREN ||
312+
prevToken.type === TokenType.ASTERISK
313+
) &&
314+
nextToken.text === '('
306315
) {
307-
// Must be followed by ( to be a function
308-
if (nextToken.text !== '(') {
309-
// Not followed by ( means it's an operator/keyword, convert to uppercase
310-
return { ...token, type: TokenType.RESERVED_KEYWORD, raw: token.text };
311-
}
312-
313-
// For IN: convert to keyword if previous token is an expression token
314-
// For ANY: convert to keyword if previous token is an operator
315-
if (
316-
(token.text === 'IN' &&
317-
(prevToken.type === TokenType.IDENTIFIER ||
318-
prevToken.type === TokenType.QUOTED_IDENTIFIER ||
319-
prevToken.type === TokenType.NUMBER ||
320-
prevToken.type === TokenType.STRING ||
321-
prevToken.type === TokenType.CLOSE_PAREN ||
322-
prevToken.type === TokenType.ASTERISK)) ||
323-
(token.text === 'ANY' && prevToken.type === TokenType.OPERATOR)
324-
) {
325-
// Convert to keyword (operator) - use uppercase for display
326-
return { ...token, type: TokenType.RESERVED_KEYWORD, raw: token.text };
327-
}
328-
// Otherwise, keep as RESERVED_FUNCTION_NAME to preserve original casing via functionCase option
316+
return { ...token, type: TokenType.RESERVED_FUNCTION_NAME };
329317
}
330318

331319
// If we have queries like
@@ -351,12 +339,8 @@ function postProcess(tokens: Token[]): Token[] {
351339
}
352340

353341
// We should format `set(100)` as-is rather than `SET (100)`
354-
if (
355-
token.type === TokenType.RESERVED_CLAUSE &&
356-
token.text === 'SET' &&
357-
nextToken.type === TokenType.OPEN_PAREN
358-
) {
359-
return { ...token, type: TokenType.RESERVED_FUNCTION_NAME, text: token.raw };
342+
if (isToken.SET(token) && nextToken.type === TokenType.OPEN_PAREN) {
343+
return { ...token, type: TokenType.RESERVED_FUNCTION_NAME };
360344
}
361345

362346
return token;

src/languages/clickhouse/clickhouse.functions.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -767,7 +767,6 @@ export const functions: string[] = [
767767
'ifNull',
768768
'ignore',
769769
'ilike',
770-
'in',
771770
'inIgnoreSet',
772771
'indexHint',
773772
'indexOf',

src/languages/clickhouse/clickhouse.keywords.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ export const keywords: string[] = [
1212
'ALTER',
1313
'AND',
1414
'ANTI',
15-
'ANY',
1615
'APPEND',
1716
'APPLY',
1817
'ARRAY',

src/lexer/token.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ export const testToken =
8080
export const isToken = {
8181
ARRAY: testToken({ text: 'ARRAY', type: TokenType.RESERVED_DATA_TYPE }),
8282
BY: testToken({ text: 'BY', type: TokenType.RESERVED_KEYWORD }),
83+
IN: testToken({ text: 'IN', type: TokenType.RESERVED_KEYWORD }),
8384
SET: testToken({ text: 'SET', type: TokenType.RESERVED_CLAUSE }),
8485
STRUCT: testToken({ text: 'STRUCT', type: TokenType.RESERVED_DATA_TYPE }),
8586
WINDOW: testToken({ text: 'WINDOW', type: TokenType.RESERVED_CLAUSE }),

test/clickhouse.test.ts

Lines changed: 6 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ describe('ClickhouseFormatter', () => {
160160
],
161161
});
162162
supportsSetOperations(format, ['UNION', 'UNION ALL', 'UNION DISTINCT', 'PARALLEL WITH']);
163-
supportsOperators(format, ['%'], { any: true });
163+
supportsOperators(format, ['%'], { any: false });
164164
supportsWindow(format);
165165
supportsLimiting(format, { limit: true, offset: false });
166166
supportsArrayAndMapAccessors(format);
@@ -180,43 +180,24 @@ describe('ClickhouseFormatter', () => {
180180
);
181181
});
182182

183-
describe('in/any set operators', () => {
183+
describe('IN set operator vs function', () => {
184184
it('should respect the IN operator as a keyword when used as an operator', () => {
185185
expect(format('SELECT 1 in foo;')).toBe(dedent`
186186
SELECT
187-
1 IN foo;
187+
1 in foo;
188188
`);
189189

190190
expect(format('SELECT foo in (1,2,3);')).toBe(dedent`
191191
SELECT
192-
foo IN (1, 2, 3);
192+
foo in (1, 2, 3);
193193
`);
194194
expect(format('SELECT "foo" in (1,2,3);')).toBe(dedent`
195195
SELECT
196-
"foo" IN (1, 2, 3);
196+
"foo" in (1, 2, 3);
197197
`);
198198
expect(format('SELECT 1 in (1,2,3);')).toBe(dedent`
199199
SELECT
200-
1 IN (1, 2, 3);
201-
`);
202-
});
203-
it('should respect the ANY operator as a keyword when used as an operator', () => {
204-
expect(format('SELECT 1 = any foo;')).toBe(dedent`
205-
SELECT
206-
1 = ANY foo;
207-
`);
208-
209-
expect(format('SELECT foo = any (1,2,3);')).toBe(dedent`
210-
SELECT
211-
foo = ANY (1, 2, 3);
212-
`);
213-
expect(format('SELECT "foo" = any (1,2,3);')).toBe(dedent`
214-
SELECT
215-
"foo" = ANY (1, 2, 3);
216-
`);
217-
expect(format('SELECT 1 = any (1,2,3);')).toBe(dedent`
218-
SELECT
219-
1 = ANY (1, 2, 3);
200+
1 in (1, 2, 3);
220201
`);
221202
});
222203

@@ -230,16 +211,6 @@ describe('ClickhouseFormatter', () => {
230211
in("foo", "bar");
231212
`);
232213
});
233-
it('should respect the ANY operator as a keyword when used as a function', () => {
234-
expect(format('SELECT any(foo);')).toBe(dedent`
235-
SELECT
236-
any(foo);
237-
`);
238-
expect(format('SELECT any("foo");')).toBe(dedent`
239-
SELECT
240-
any("foo");
241-
`);
242-
});
243214
});
244215

245216
it('should support parameters', () => {

0 commit comments

Comments
 (0)