001package gudusoft.gsqlparser.sqlcmds;
002
003import gudusoft.gsqlparser.*;
004import gudusoft.gsqlparser.stmt.*;
005import gudusoft.gsqlparser.stmt.mysql.*;
006
007/**
008 * ClickHouse SQL command resolver.
009 * Handles ClickHouse-specific statement detection and command resolution.
010 * Based on MySQL command resolver since ClickHouse grammar is derived from MySQL.
011 *
012 * @since 3.2.0.0
013 */
014public class TSqlCmdsClickhouse extends AbstractSqlCmds {
015
016    public TSqlCmdsClickhouse() {
017        super(EDbVendor.dbvclickhouse);
018    }
019
020    @Override
021    protected void initializeCommands() {
022        // ClickHouse commands - based on MySQL with ClickHouse-specific adaptations
023
024        // ALTER commands
025        addCmd(TBaseType.rrw_alter, "database", " ", " ", " ", " ", " ", ESqlStatementType.sstmysqlalterdatabase);
026        addCmd(TBaseType.rrw_alter, "table", " ", " ", " ", " ", " ", ESqlStatementType.sstaltertable);
027        addCmd(TBaseType.rrw_alter, "view", " ", " ", " ", " ", " ", ESqlStatementType.sstmysqlalterview);
028
029        // CREATE commands
030        addCmd(TBaseType.rrw_create, "database", " ", " ", " ", " ", " ", ESqlStatementType.sstmysqlcreatedatabase);
031        addCmd(TBaseType.rrw_create, "function", " ", " ", " ", " ", " ", ESqlStatementType.sstmysqlcreatefunction);
032        addCmd(TBaseType.rrw_create, "index", " ", " ", " ", " ", " ", ESqlStatementType.sstmysqlcreateindex);
033        addCmd(TBaseType.rrw_create, "or", "replace", "table", " ", " ", " ", ESqlStatementType.sstcreatetable);
034        addCmd(TBaseType.rrw_create, "or", "replace", "*", "*", "*", "view", ESqlStatementType.sstcreateview);
035        addCmd(TBaseType.rrw_create, "or", "replace", "view", " ", " ", " ", ESqlStatementType.sstcreateview);
036        addCmd(TBaseType.rrw_create, "materialized", "view", " ", " ", " ", " ", ESqlStatementType.sstcreatematerializedview);
037        addCmd(TBaseType.rrw_create, "window", "view", " ", " ", " ", " ", ESqlStatementType.sstClickhouseCreateWindowView);
038        addCmd(TBaseType.rrw_create, "temporary", "table", " ", " ", " ", " ", ESqlStatementType.sstcreatetable);
039        addCmd(TBaseType.rrw_create, "temp", "table", " ", " ", " ", " ", ESqlStatementType.sstcreatetable);
040        addCmd(TBaseType.rrw_create, "table", " ", " ", " ", " ", " ", ESqlStatementType.sstcreatetable);
041        addCmd(TBaseType.rrw_create, "*", "*", "*", "*", "*", "view", ESqlStatementType.sstcreateview);
042        addCmd(TBaseType.rrw_create, "view", " ", " ", " ", " ", " ", ESqlStatementType.sstcreateview);
043
044        // DELETE
045        addCmd(TBaseType.rrw_delete, " ", " ", " ", " ", " ", " ", ESqlStatementType.sstdelete);
046
047        // DESCRIBE / DESC
048        addCmd(TBaseType.rrw_describe, " ", " ", " ", " ", " ", " ", ESqlStatementType.sstdescribe);
049        addCmd(TBaseType.rrw_mysql_desc, " ", " ", " ", " ", " ", " ", ESqlStatementType.sstdescribe);
050
051        // DROP commands
052        addCmd(TBaseType.rrw_drop, "database", " ", " ", " ", " ", " ", ESqlStatementType.sstmysqldropdatabase);
053        addCmd(TBaseType.rrw_drop, "function", " ", " ", " ", " ", " ", ESqlStatementType.sstmysqldropfunction);
054        addCmd(TBaseType.rrw_drop, "index", " ", " ", " ", " ", " ", ESqlStatementType.sstmysqldropindex);
055        addCmd(TBaseType.rrw_drop, "table", " ", " ", " ", " ", " ", ESqlStatementType.sstmysqldroptable);
056        addCmd(TBaseType.rrw_drop, "temporary", "table", " ", " ", " ", " ", ESqlStatementType.sstmysqldroptable);
057        addCmd(TBaseType.rrw_drop, "view", " ", " ", " ", " ", " ", ESqlStatementType.sstdropview);
058
059        // EXPLAIN
060        addCmd(TBaseType.rrw_explain, " ", " ", " ", " ", " ", " ", ESqlStatementType.sstExplain);
061
062        // GRANT / REVOKE
063        addCmd(TBaseType.rrw_grant, " ", " ", " ", " ", " ", " ", ESqlStatementType.sstmysqlgrant);
064        addCmd(TBaseType.rrw_revoke, " ", " ", " ", " ", " ", " ", ESqlStatementType.sstmysqlrevoke);
065
066        // INSERT
067        addCmd(TBaseType.rrw_insert, " ", " ", " ", " ", " ", " ", ESqlStatementType.sstinsert);
068
069        // OPTIMIZE
070        addCmd(TBaseType.rrw_optimize, "table", " ", " ", " ", " ", " ", ESqlStatementType.sstmysqloptimizetable);
071
072        // RENAME
073        addCmd(TBaseType.rrw_rename, "table", " ", " ", " ", " ", " ", ESqlStatementType.sstmysqlrenametable);
074
075        // SELECT
076        addCmd(TBaseType.rrw_select, " ", " ", " ", " ", " ", " ", ESqlStatementType.sstselect);
077
078        // SET
079        addCmd(TBaseType.rrw_set, " ", " ", " ", " ", " ", " ", ESqlStatementType.sstmysqlset);
080
081        // SHOW commands (ClickHouse supports a subset of MySQL SHOW)
082        addCmd(TBaseType.rrw_show, "columns", " ", " ", " ", " ", " ", ESqlStatementType.sstmysqlshowcolumns);
083        addCmd(TBaseType.rrw_show, "create", "database", " ", " ", " ", " ", ESqlStatementType.sstmysqlshowcreatedatabase);
084        addCmd(TBaseType.rrw_show, "create", "table", " ", " ", " ", " ", ESqlStatementType.sstmysqlshowcreatetable);
085        addCmd(TBaseType.rrw_show, "create", "view", " ", " ", " ", " ", ESqlStatementType.sstmysqlshowcreateview);
086        addCmd(TBaseType.rrw_show, "databases", " ", " ", " ", " ", " ", ESqlStatementType.sstmysqlshowdatabases);
087        addCmd(TBaseType.rrw_show, "processlist", " ", " ", " ", " ", " ", ESqlStatementType.sstmysqlshowprocesslist);
088        addCmd(TBaseType.rrw_show, "tables", " ", " ", " ", " ", " ", ESqlStatementType.sstmysqlshowtables);
089
090        // TRUNCATE
091        addCmd(TBaseType.rrw_truncate, " ", " ", " ", " ", " ", " ", ESqlStatementType.sstmysqltruncate);
092
093        // UPDATE
094        addCmd(TBaseType.rrw_update, " ", " ", " ", " ", " ", " ", ESqlStatementType.sstupdate);
095
096        // USE
097        addCmd(TBaseType.rrw_use, " ", " ", " ", " ", " ", " ", ESqlStatementType.sstmysqluse);
098    }
099
100    @Override
101    protected String getToken1Str(int token1) {
102        // Handle ClickHouse vendor-specific reserved words
103        switch (token1) {
104            case TBaseType.rrw_mysql_desc:
105                return "desc";
106            default:
107                return null;
108        }
109    }
110
111    @Override
112    public TCustomSqlStatement issql(TSourceToken pcst, EFindSqlStateType pstate, TCustomSqlStatement psqlstatement) {
113        TCustomSqlStatement ret = null;
114        int k;
115        boolean lcisnewsql;
116        TSourceToken lcpprevsolidtoken, lcnextsolidtoken;
117
118        gnewsqlstatementtype = ESqlStatementType.sstinvalid;
119
120        if ((pcst.tokencode == TBaseType.cmtdoublehyphen)
121                || (pcst.tokencode == TBaseType.cmtslashstar)
122                || (pcst.tokencode == TBaseType.lexspace)
123                || (pcst.tokencode == TBaseType.lexnewline)
124                || (pcst.tokentype == ETokenType.ttsemicolon)) {
125            return ret;
126        }
127
128        int lcpos = pcst.posinlist;
129        TSourceTokenList lcsourcetokenlist = pcst.container;
130        TCustomSqlStatement lccurrentsqlstatement = psqlstatement;
131
132        // subquery after semicolon || at first line
133        if ((pstate == EFindSqlStateType.stnormal) && (pcst.tokentype == ETokenType.ttleftparenthesis)) {
134            k = lcsourcetokenlist.solidtokenafterpos(lcpos, TBaseType.rrw_select, 1, "(");
135            if (k > 0) {
136                ret = new TSelectSqlStatement(vendor);
137            }
138            return ret;
139        }
140
141        // cte
142        if ((pstate == EFindSqlStateType.stnormal) && (pcst.tokencode == TBaseType.rrw_with)) {
143            ret = findcte(pcst);
144            if ((ret != null)) return ret;
145        }
146
147        gnewsqlstatementtype = getStatementTypeForToken(pcst);
148
149        TSourceToken lcprevsolidtoken = lcsourcetokenlist.solidtokenbefore(lcpos);
150
151        if (pcst.tokencode == TBaseType.rrw_create) {
152            TSourceToken nextToken = lcsourcetokenlist.nextsolidtoken(lcpos, 1, false);
153            if (TBaseType.assigned(nextToken) && nextToken.toString().equalsIgnoreCase("window")) {
154                TSourceToken viewAfterWindow = lcsourcetokenlist.nextsolidtoken(nextToken.posinlist, 1, false);
155                if (TBaseType.assigned(viewAfterWindow) && viewAfterWindow.toString().equalsIgnoreCase("view")) {
156                    gnewsqlstatementtype = ESqlStatementType.sstClickhouseCreateWindowView;
157                }
158            }
159        }
160
161        if (pcst.toString().equalsIgnoreCase("SYSTEM")) {
162            gnewsqlstatementtype = ESqlStatementType.sstClickhouseSystem;
163        }
164
165        if (pcst.toString().equalsIgnoreCase("DETACH")) {
166            gnewsqlstatementtype = ESqlStatementType.sstClickhouseDetach;
167        }
168
169        if (pcst.toString().equalsIgnoreCase("ATTACH")) {
170            gnewsqlstatementtype = ESqlStatementType.sstClickhouseAttach;
171        }
172
173        if (pcst.tokencode == TBaseType.rrw_kill) {
174            gnewsqlstatementtype = ESqlStatementType.sstClickhouseKill;
175        }
176
177        if (pcst.toString().equalsIgnoreCase("WATCH")) {
178            gnewsqlstatementtype = ESqlStatementType.sstClickhouseWatch;
179        }
180
181        if ((gnewsqlstatementtype == ESqlStatementType.sstinvalid) && (pcst.tokencode == TBaseType.rrw_create)) {
182            TSourceToken viewToken = pcst.container.searchToken(TBaseType.rrw_view, "", pcst, 15);
183            if (viewToken != null) {
184                gnewsqlstatementtype = ESqlStatementType.sstcreateview;
185            }
186        }
187
188        switch (gnewsqlstatementtype) {
189            case sstinvalid: {
190                ret = null;
191                break;
192            }
193            case sstselect: {
194                lcisnewsql = true;
195
196                if (pstate != EFindSqlStateType.stnormal) {
197                    if (TBaseType.assigned(lcprevsolidtoken)) {
198                        if (lcprevsolidtoken.tokentype == ETokenType.ttleftparenthesis)
199                            lcisnewsql = false;
200                        else if (lcprevsolidtoken.tokencode == TBaseType.rrw_union)
201                            lcisnewsql = false;
202                        else if (lcprevsolidtoken.tokencode == TBaseType.rrw_intersect)
203                            lcisnewsql = false;
204                        else if (lcprevsolidtoken.tokencode == TBaseType.rrw_except)
205                            lcisnewsql = false;
206                        else if (lcprevsolidtoken.tokencode == TBaseType.rrw_as) {
207                            if (lccurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstcreatetable)
208                                lcisnewsql = false;
209                            if (lccurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstcreateview)
210                                lcisnewsql = false;
211                            if (lccurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstcreatematerializedview)
212                                lcisnewsql = false;
213                            if (lccurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstClickhouseCreateWindowView)
214                                lcisnewsql = false;
215                        }
216
217                        if (lcisnewsql && (lcprevsolidtoken.tokencode == TBaseType.rrw_all)) {
218                            lcpprevsolidtoken = lcsourcetokenlist.solidtokenbefore(lcprevsolidtoken.posinlist);
219                            if (TBaseType.assigned(lcpprevsolidtoken)) {
220                                if (lcpprevsolidtoken.tokencode == TBaseType.rrw_union)
221                                    lcisnewsql = false;
222                            }
223                        }
224                    }
225
226                    if (TBaseType.assigned(lccurrentsqlstatement)) {
227                        if (lccurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstinsert)
228                            lcisnewsql = false;
229                    }
230                }
231
232                if (lcisnewsql)
233                    ret = new TSelectSqlStatement(vendor);
234                break;
235            }
236            case sstinsert: {
237                lcisnewsql = true;
238                if (pstate != EFindSqlStateType.stnormal) {
239                    if (TBaseType.assigned(lccurrentsqlstatement)) {
240                        // ClickHouse doesn't have stored procedures
241                    }
242                }
243
244                if (lcisnewsql)
245                    ret = new TInsertSqlStatement(vendor);
246                ret.sqlstatementtype = gnewsqlstatementtype;
247                break;
248            }
249            case sstupdate: {
250                lcisnewsql = true;
251                if (pstate != EFindSqlStateType.stnormal) {
252                    lcprevsolidtoken = lcsourcetokenlist.solidtokenbefore(lcpos);
253                    if (TBaseType.assigned(lcprevsolidtoken)) {
254                        if (lcprevsolidtoken.tokencode == TBaseType.rrw_on)
255                            lcisnewsql = false;
256                        else if (lcprevsolidtoken.tokencode == TBaseType.rrw_for)
257                            lcisnewsql = false;
258                    }
259
260                    lcnextsolidtoken = lcsourcetokenlist.nextsolidtoken(lcpos, 1, false);
261                    if (TBaseType.assigned(lcnextsolidtoken)) {
262                        if (lcnextsolidtoken.tokentype == ETokenType.ttleftparenthesis) {
263                            k = lcsourcetokenlist.solidtokenafterpos(lcnextsolidtoken.posinlist, TBaseType.rrw_select, 1, "(");
264                            if (k == 0) lcisnewsql = false;
265                        }
266                    }
267                }
268
269                if (lcisnewsql) {
270                    ret = new TUpdateSqlStatement(vendor);
271                    ret.dummytag = 1;
272                }
273                break;
274            }
275            case sstdelete: {
276                lcisnewsql = true;
277
278                if (pstate != EFindSqlStateType.stnormal) {
279                    lcprevsolidtoken = lcsourcetokenlist.solidtokenbefore(lcpos);
280                    if (TBaseType.assigned(lcprevsolidtoken)) {
281                        if (lcprevsolidtoken.tokencode == TBaseType.rrw_on)
282                            lcisnewsql = false;
283                    }
284                }
285
286                if (lcisnewsql)
287                    ret = new TDeleteSqlStatement(vendor);
288                break;
289            }
290            case sstcreatetable: {
291                ret = new TCreateTableSqlStatement(vendor);
292                break;
293            }
294            case sstcreateview: {
295                ret = new TCreateViewSqlStatement(vendor);
296                break;
297            }
298            case sstcreatematerializedview: {
299                ret = new TCreateMaterializedSqlStatement(vendor);
300                break;
301            }
302            case sstClickhouseCreateWindowView: {
303                ret = new TCreateViewSqlStatement(vendor);
304                ret.sqlstatementtype = ESqlStatementType.sstClickhouseCreateWindowView;
305                break;
306            }
307            case sstmysqlcreateindex: {
308                ret = new TCreateIndexSqlStatement(vendor);
309                break;
310            }
311            case sstcreatedatabase:
312            case sstmysqlcreatedatabase: {
313                ret = new TCreateDatabaseSqlStatement(vendor);
314                break;
315            }
316            case sstmysqldroptable:
317            case sstdroptable: {
318                ret = new TDropTableSqlStatement(vendor);
319                break;
320            }
321            case sstdropview: {
322                ret = new TDropViewSqlStatement(vendor);
323                break;
324            }
325            case sstdropindex:
326            case sstmysqldropindex: {
327                ret = new TDropIndexSqlStatement(vendor);
328                break;
329            }
330            case sstaltertable: {
331                ret = new TAlterTableStatement(vendor);
332                break;
333            }
334            case sstmysqlset: {
335                ret = new TSetStmt(vendor);
336                break;
337            }
338            case sstmysqlcreatefunction: {
339                ret = new TCreateFunctionStmt(vendor);
340                break;
341            }
342            case sstmysqltruncate: {
343                ret = new TTruncateStatement(vendor);
344                break;
345            }
346            case sstdescribe: {
347                ret = new TDescribeStmt(vendor);
348                break;
349            }
350            case sstExplain: {
351                ret = new TExplainPlan(vendor);
352                break;
353            }
354            case sstClickhouseSystem: {
355                ret = new TUnknownSqlStatement(vendor);
356                ret.sqlstatementtype = ESqlStatementType.sstClickhouseSystem;
357                break;
358            }
359            case sstClickhouseDetach: {
360                ret = new TUnknownSqlStatement(vendor);
361                ret.sqlstatementtype = ESqlStatementType.sstClickhouseDetach;
362                break;
363            }
364            case sstClickhouseAttach: {
365                ret = new TUnknownSqlStatement(vendor);
366                ret.sqlstatementtype = ESqlStatementType.sstClickhouseAttach;
367                break;
368            }
369            case sstClickhouseKill: {
370                ret = new TUnknownSqlStatement(vendor);
371                ret.sqlstatementtype = ESqlStatementType.sstClickhouseKill;
372                break;
373            }
374            case sstClickhouseWatch: {
375                ret = new TUnknownSqlStatement(vendor);
376                ret.sqlstatementtype = ESqlStatementType.sstClickhouseWatch;
377                break;
378            }
379            case sstmysqloptimizetable: {
380                ret = new TMySQLOptimizeTableStmt(vendor);
381                break;
382            }
383            case sstmysqlalterview: {
384                ret = new TAlterViewStatement(vendor);
385                break;
386            }
387            case sstmysqldropfunction: {
388                ret = new TDropFunctionStmt(vendor);
389                break;
390            }
391            case sstmysqlrenametable: {
392                ret = new TRenameStmt(vendor);
393                break;
394            }
395            case sstmysqluse: {
396                ret = new TUseDatabase(vendor);
397                break;
398            }
399            case sstmysqlshowcolumns:
400            case sstmysqlshowcreatedatabase:
401            case sstmysqlshowcreatetable:
402            case sstmysqlshowcreateview:
403            case sstmysqlshowdatabases:
404            case sstmysqlshowprocesslist:
405            case sstmysqlshowtables:
406                ret = new TMySQLShowStmt(vendor);
407                ret.sqlstatementtype = gnewsqlstatementtype;
408                break;
409            default: {
410                ret = new TUnknownSqlStatement(vendor);
411                ret.sqlstatementtype = gnewsqlstatementtype;
412                break;
413            }
414        }
415
416        return ret;
417    }
418}