@@ -4,7 +4,11 @@ import { EOF_TOKEN, Token, TokenType } from '../../lexer/token.js';
44import { functions } from './clickhouse.functions.js' ;
55import { dataTypes , keywords } from './clickhouse.keywords.js' ;
66
7- const reservedSelect = expandPhrases ( [ 'SELECT [DISTINCT]' ] ) ;
7+ const reservedSelect = expandPhrases ( [
8+ 'SELECT [DISTINCT]' ,
9+ // https://clickhouse.com/docs/sql-reference/statements/alter/view
10+ 'MODIFY QUERY SELECT [DISTINCT]' ,
11+ ] ) ;
812
913const reservedClauses = expandPhrases ( [
1014 'SET' ,
@@ -28,17 +32,35 @@ const reservedClauses = expandPhrases([
2832 // https://clickhouse.com/docs/sql-reference/statements/insert-into
2933 'INSERT INTO' ,
3034 'VALUES' ,
35+ 'DEPENDS ON' ,
36+ // https://clickhouse.com/docs/sql-reference/statements/move
37+ 'MOVE {USER | ROLE | QUOTA | SETTINGS PROFILE | ROW POLICY}' ,
38+ // https://clickhouse.com/docs/sql-reference/statements/grant
39+ 'GRANT' ,
40+ // https://clickhouse.com/docs/sql-reference/statements/revoke
41+ 'REVOKE' ,
42+ // https://clickhouse.com/docs/sql-reference/statements/check-grant
43+ 'CHECK GRANT' ,
44+ // https://clickhouse.com/docs/sql-reference/statements/set-role
45+ 'SET [DEFAULT] ROLE [NONE | ALL | ALL EXCEPT]' ,
46+ // https://clickhouse.com/docs/sql-reference/statements/optimize
47+ 'DEDUPLICATE BY' ,
48+ // https://clickhouse.com/docs/sql-reference/statements/alter/statistics
49+ 'MODIFY STATISTICS' ,
50+ 'TYPE' ,
3151] ) ;
3252
3353const standardOnelineClauses = expandPhrases ( [
3454 // https://clickhouse.com/docs/sql-reference/statements/create
3555 'CREATE [OR REPLACE] [TEMPORARY] TABLE [IF NOT EXISTS]' ,
3656] ) ;
3757const tabularOnelineClauses = expandPhrases ( [
58+ 'ALL EXCEPT' ,
59+ 'ON CLUSTER' ,
3860 // https://clickhouse.com/docs/sql-reference/statements/update
3961 'UPDATE' ,
4062 // https://clickhouse.com/docs/sql-reference/statements/system
41- 'SYSTEM RELOAD {DICTIONARIES | DICTIONARY | FUNCTIONS | FUNCTION | ASYNCHRONOUS METRICS} [ON CLUSTER] ' ,
63+ 'SYSTEM RELOAD {DICTIONARIES | DICTIONARY | FUNCTIONS | FUNCTION | ASYNCHRONOUS METRICS}' ,
4264 'SYSTEM DROP {DNS CACHE | MARK CACHE | ICEBERG METADATA CACHE | TEXT INDEX DICTIONARY CACHE | TEXT INDEX HEADER CACHE | TEXT INDEX POSTINGS CACHE | REPLICA | DATABASE REPLICA | UNCOMPRESSED CACHE | COMPILED EXPRESSION CACHE | QUERY CONDITION CACHE | QUERY CACHE | FORMAT SCHEMA CACHE | FILESYSTEM CACHE}' ,
4365 'SYSTEM FLUSH LOGS' ,
4466 'SYSTEM RELOAD {CONFIG | USERS}' ,
@@ -56,6 +78,7 @@ const tabularOnelineClauses = expandPhrases([
5678 'SYSTEM {STOP | START} [REPLICATED] VIEW' ,
5779 'SYSTEM {STOP | START} VIEWS' ,
5880 'SYSTEM {REFRESH | CANCEL | WAIT} VIEW' ,
81+ 'WITH NAME' ,
5982 // https://clickhouse.com/docs/sql-reference/statements/show
6083 'SHOW [CREATE] {TABLE | TEMPORARY TABLE | DICTIONARY | VIEW | DATABASE}' ,
6184 'SHOW DATABASES [[NOT] {LIKE | ILIKE}]' ,
@@ -65,32 +88,28 @@ const tabularOnelineClauses = expandPhrases([
6588 'ATTACH {TABLE | DICTIONARY | DATABASE} [IF NOT EXISTS]' ,
6689 // https://clickhouse.com/docs/sql-reference/statements/detach
6790 'DETACH {TABLE | DICTIONARY | DATABASE} [IF EXISTS]' ,
91+ 'PERMANENTLY' ,
92+ 'SYNC' ,
6893 // https://clickhouse.com/docs/sql-reference/statements/drop
6994 'DROP {DICTIONARY | DATABASE | USER | ROLE | QUOTA | PROFILE | SETTINGS PROFILE | VIEW | FUNCTION | NAMED COLLECTION | ROW POLICY | POLICY} [IF EXISTS]' ,
7095 'DROP [TEMPORARY] TABLE [IF EXISTS] [IF EMPTY]' ,
7196 // https://clickhouse.com/docs/sql-reference/statements/exists
7297 'EXISTS [TEMPORARY] {TABLE | DICTIONARY | DATABASE}' ,
7398 // https://clickhouse.com/docs/sql-reference/statements/kill
74- 'KILL QUERY [ON CLUSTER] ' ,
99+ 'KILL QUERY' ,
75100 // https://clickhouse.com/docs/sql-reference/statements/optimize
76101 'OPTIMIZE TABLE' ,
77102 // https://clickhouse.com/docs/sql-reference/statements/rename
78103 'RENAME [TABLE | DICTIONARY | DATABASE]' ,
79104 // https://clickhouse.com/docs/sql-reference/statements/exchange
80105 'EXCHANGE {TABLES | DICTIONARIES}' ,
81- // https://clickhouse.com/docs/sql-reference/statements/set-role
82- 'SET ROLE [DEFAULT | NONE | ALL | ALL EXCEPT]' ,
83- 'SET DEFAULT ROLE [NONE]' ,
84106 // https://clickhouse.com/docs/sql-reference/statements/truncate
85107 'TRUNCATE TABLE [IF EXISTS]' ,
86108 // https://clickhouse.com/docs/sql-reference/statements/execute_as
87109 'EXECUTE AS' ,
88110 // https://clickhouse.com/docs/sql-reference/statements/use
89111 'USE' ,
90- // https://clickhouse.com/docs/sql-reference/statements/move
91- 'MOVE {USER | ROLE | QUOTA | SETTINGS PROFILE | ROW POLICY}' ,
92- // https://clickhouse.com/docs/sql-reference/statements/check-grant
93- 'CHECK GRANT' ,
112+ 'TO' ,
94113 // https://clickhouse.com/docs/sql-reference/statements/undrop
95114 'UNDROP TABLE' ,
96115 // https://clickhouse.com/docs/sql-reference/statements/create
@@ -109,7 +128,6 @@ const tabularOnelineClauses = expandPhrases([
109128 'ALTER NAMED COLLECTION [IF EXISTS]' ,
110129 // https://clickhouse.com/docs/sql-reference/statements/alter/user
111130 'RENAME TO' ,
112- 'DEFAULT ROLE [ALL [EXCEPT]]' ,
113131 'GRANTEES' ,
114132 'NOT IDENTIFIED' ,
115133 'RESET AUTHENTICATION METHODS TO NEW' ,
@@ -120,7 +138,8 @@ const tabularOnelineClauses = expandPhrases([
120138 '{ADD | MODIFY} SETTINGS' ,
121139 'ADD PROFILES' ,
122140 // https://clickhouse.com/docs/sql-reference/statements/alter/apply-deleted-mask
123- 'APPLY DELETED MASK [IN PARTITION]' ,
141+ 'APPLY DELETED MASK' ,
142+ 'IN PARTITION' ,
124143 // https://clickhouse.com/docs/sql-reference/statements/alter/column
125144 '{ADD | DROP | RENAME | CLEAR | COMMENT | MODIFY | ALTER | MATERIALIZE} COLUMN' ,
126145 // https://clickhouse.com/docs/sql-reference/statements/alter/partition
@@ -142,6 +161,11 @@ const tabularOnelineClauses = expandPhrases([
142161 '{MODIFY | REMOVE} SAMPLE BY' ,
143162 // https://clickhouse.com/docs/sql-reference/statements/alter/skipping-index
144163 '{ADD | MATERIALIZE | CLEAR} INDEX [IF NOT EXISTS]' ,
164+ 'DROP INDEX [IF EXISTS]' ,
165+ 'GRANULARITY' ,
166+ 'AFTER' ,
167+ 'FIRST' ,
168+
145169 // https://clickhouse.com/docs/sql-reference/statements/alter/constraint
146170 'ADD CONSTRAINT [IF NOT EXISTS]' ,
147171 'DROP CONSTRAINT [IF EXISTS]' ,
@@ -150,7 +174,6 @@ const tabularOnelineClauses = expandPhrases([
150174 'REMOVE TTL' ,
151175 // https://clickhouse.com/docs/sql-reference/statements/alter/statistics
152176 'ADD STATISTICS [IF NOT EXISTS]' ,
153- 'MODIFY STATISTICS' ,
154177 '{DROP | CLEAR} STATISTICS [IF EXISTS]' ,
155178 'MATERIALIZE STATISTICS [ALL | IF EXISTS]' ,
156179 // https://clickhouse.com/docs/sql-reference/statements/alter/quota
@@ -163,23 +186,25 @@ const tabularOnelineClauses = expandPhrases([
163186 // https://clickhouse.com/docs/sql-reference/statements/alter/projection
164187 'ADD PROJECTION [IF NOT EXISTS]' ,
165188 '{DROP | MATERIALIZE | CLEAR} PROJECTION [IF EXISTS]' ,
166- // https://clickhouse.com/docs/sql-reference/statements/alter/view
167- 'MODIFY QUERY' ,
168189 // https://clickhouse.com/docs/sql-reference/statements/create/view#refreshable-materialized-view
169190 'REFRESH {EVERY | AFTER}' ,
170191 'RANDOMIZE FOR' ,
171- 'DEPENDS ON ' ,
192+ 'APPEND ' ,
172193 'APPEND TO' ,
173194 // https://clickhouse.com/docs/sql-reference/statements/delete
174195 'DELETE FROM' ,
175196 // https://clickhouse.com/docs/sql-reference/statements/explain
176197 'EXPLAIN [AST | SYNTAX | QUERY TREE | PLAN | PIPELINE | ESTIMATE | TABLE OVERRIDE]' ,
177198 // https://clickhouse.com/docs/sql-reference/statements/grant
178- 'GRANT [ON CLUSTER]' ,
199+ 'GRANT ON CLUSTER' ,
200+ 'GRANT CURRENT GRANTS' ,
201+ 'WITH GRANT OPTION' ,
179202 // https://clickhouse.com/docs/sql-reference/statements/revoke
180- 'REVOKE [ON CLUSTER]' ,
203+ 'REVOKE ON CLUSTER' ,
204+ 'ADMIN OPTION FOR' ,
181205 // https://clickhouse.com/docs/sql-reference/statements/check-table
182206 'CHECK TABLE' ,
207+ 'PARTITION ID' ,
183208 // https://clickhouse.com/docs/sql-reference/statements/describe-table
184209 '{DESC | DESCRIBE} TABLE' ,
185210] ) ;
@@ -196,7 +221,10 @@ const reservedJoins = expandPhrases([
196221 '[GLOBAL] [INNER|LEFT|RIGHT|FULL|CROSS] [OUTER|SEMI|ANTI|ANY|ALL|ASOF] JOIN' ,
197222] ) ;
198223
199- const reservedKeywordPhrases = expandPhrases ( [ '{ROWS | RANGE} BETWEEN' ] ) ;
224+ const reservedKeywordPhrases = expandPhrases ( [
225+ '{ROWS | RANGE} BETWEEN' ,
226+ 'ALTER MATERIALIZE STATISTICS' ,
227+ ] ) ;
200228
201229// https://clickhouse.com/docs/sql-reference/syntax
202230export const clickhouse : DialectOptions = {
@@ -260,15 +288,15 @@ export const clickhouse: DialectOptions = {
260288 */
261289function postProcess ( tokens : Token [ ] ) : Token [ ] {
262290 return tokens . map ( ( token , i ) => {
291+ const nextToken = tokens [ i + 1 ] || EOF_TOKEN ;
292+ const prevToken = tokens [ i - 1 ] || EOF_TOKEN ;
293+
263294 // Only process IN and ANY that are currently RESERVED_FUNCTION_NAME
264295 // Check text (uppercase canonical form) for matching, but preserve raw (original casing)
265296 if (
266297 token . type === TokenType . RESERVED_FUNCTION_NAME &&
267298 ( token . text === 'IN' || token . text === 'ANY' )
268299 ) {
269- const nextToken = tokens [ i + 1 ] || EOF_TOKEN ;
270- const prevToken = tokens [ i - 1 ] || EOF_TOKEN ;
271-
272300 // Must be followed by ( to be a function
273301 if ( nextToken . text !== '(' ) {
274302 // Not followed by ( means it's an operator/keyword, convert to uppercase
@@ -293,6 +321,37 @@ function postProcess(tokens: Token[]): Token[] {
293321 // Otherwise, keep as RESERVED_FUNCTION_NAME to preserve original casing via functionCase option
294322 }
295323
324+ // If we have queries like
325+ // > GRANT SELECT, INSERT ON db.table TO john
326+ // > GRANT SELECT(a, b), SELECT(c) ON db.table TO john
327+ // we want to format them as
328+ // > GRANT
329+ // > SELECT,
330+ // > INSERT ON db.table
331+ // > TO john
332+ // > GRANT
333+ // > SELECT(a, b),
334+ // > SELECT(c) ON db.table
335+ // > TO john
336+ // To do this we need to convert the SELECT keyword to a RESERVED_KEYWORD.
337+ if (
338+ token . type === TokenType . RESERVED_SELECT &&
339+ ( nextToken . type === TokenType . COMMA ||
340+ prevToken . type === TokenType . RESERVED_CLAUSE ||
341+ prevToken . type === TokenType . COMMA )
342+ ) {
343+ return { ...token , type : TokenType . RESERVED_KEYWORD } ;
344+ }
345+
346+ // We should format `set(100)` as-is rather than `SET (100)`
347+ if (
348+ token . type === TokenType . RESERVED_CLAUSE &&
349+ token . text === 'SET' &&
350+ nextToken . type === TokenType . OPEN_PAREN
351+ ) {
352+ return { ...token , type : TokenType . RESERVED_FUNCTION_NAME , text : token . raw } ;
353+ }
354+
296355 return token ;
297356 } ) ;
298357}
0 commit comments