001package gudusoft.gsqlparser.parser;
002
003import gudusoft.gsqlparser.EDbVendor;
004import gudusoft.gsqlparser.TBaseType;
005import gudusoft.gsqlparser.TGSqlParser;
006import gudusoft.gsqlparser.TCustomLexer;
007import gudusoft.gsqlparser.TCustomParser;
008import gudusoft.gsqlparser.TCustomSqlStatement;
009import gudusoft.gsqlparser.TLexerSybase;
010import gudusoft.gsqlparser.TParserSybase;
011import gudusoft.gsqlparser.TSourceToken;
012import gudusoft.gsqlparser.TSourceTokenList;
013import gudusoft.gsqlparser.TStatementList;
014import gudusoft.gsqlparser.TSyntaxError;
015import gudusoft.gsqlparser.EFindSqlStateType;
016import gudusoft.gsqlparser.ETokenType;
017import gudusoft.gsqlparser.ETokenStatus;
018import gudusoft.gsqlparser.ESqlStatementType;
019import gudusoft.gsqlparser.EErrorType;
020import gudusoft.gsqlparser.stmt.TUnknownSqlStatement;
021import gudusoft.gsqlparser.stmt.mssql.TMssqlBlock;
022import gudusoft.gsqlparser.stmt.mssql.TMssqlExecute;
023import gudusoft.gsqlparser.sqlcmds.ISqlCmds;
024import gudusoft.gsqlparser.sqlcmds.SqlCmdsFactory;
025import gudusoft.gsqlparser.compiler.TContext;
026import gudusoft.gsqlparser.sqlenv.TSQLEnv;
027import gudusoft.gsqlparser.compiler.TGlobalScope;
028import gudusoft.gsqlparser.compiler.TFrame;
029import gudusoft.gsqlparser.resolver.TSQLResolver;
030import gudusoft.gsqlparser.resolver2.TSQLResolver2;
031import gudusoft.gsqlparser.resolver2.TSQLResolverConfig;
032import gudusoft.gsqlparser.TLog;
033import gudusoft.gsqlparser.compiler.TASTEvaluator;
034
035import java.io.BufferedReader;
036import java.util.ArrayList;
037import java.util.List;
038import java.util.Stack;
039
040/**
041 * Sybase SQL parser implementation.
042 *
043 * <p>This parser handles Sybase-specific SQL syntax including:
044 * <ul>
045 *   <li>Sybase stored procedures with GO statement boundaries</li>
046 *   <li>Sybase BEGIN/END blocks</li>
047 *   <li>Sybase TRY/CATCH blocks</li>
048 *   <li>Sybase-specific statement types</li>
049 * </ul>
050 *
051 * <p><b>Design Notes:</b>
052 * <ul>
053 *   <li>Extends {@link AbstractSqlParser} using the template method pattern</li>
054 *   <li>Uses {@link TLexerSybase} for tokenization</li>
055 *   <li>Uses {@link TParserSybase} for parsing</li>
056 *   <li>Tokenization and statement extraction similar to MSSQL</li>
057 *   <li>Delimiter character: ';' for Sybase statements</li>
058 * </ul>
059 *
060 * @see SqlParser
061 * @see AbstractSqlParser
062 * @see TLexerSybase
063 * @see TParserSybase
064 * @since 3.2.0.0
065 */
066public class SybaseSqlParser extends AbstractSqlParser {
067
068    /**
069     * Construct Sybase SQL parser.
070     * <p>
071     * Configures the parser for Sybase with default delimiter (;).
072     */
073    public SybaseSqlParser() {
074        super(EDbVendor.dbvsybase);
075        this.delimiterChar = ';';
076        this.defaultDelimiterStr = ";";
077
078        // Create lexer once - will be reused for all parsing operations
079        this.flexer = new TLexerSybase();
080        this.flexer.delimiterchar = this.delimiterChar;
081        this.flexer.defaultDelimiterStr = this.defaultDelimiterStr;
082
083        // Set parent's lexer reference for shared tokenization logic
084        this.lexer = this.flexer;
085
086        // Create parser once - will be reused for all parsing operations
087        this.fparser = new TParserSybase(null);
088        this.fparser.lexer = this.flexer;
089    }
090
091    // ========== Parser Components ==========
092
093    /** The Sybase lexer used for tokenization */
094    public TLexerSybase flexer;
095
096    /** Sybase parser (for Sybase statements) */
097    private TParserSybase fparser;
098
099    /** Current statement being built during extraction */
100    private TCustomSqlStatement gcurrentsqlstatement;
101
102    // ========== AbstractSqlParser Abstract Methods Implementation ==========
103
104    /**
105     * Return the Sybase lexer instance.
106     */
107    @Override
108    protected TCustomLexer getLexer(ParserContext context) {
109        return this.flexer;
110    }
111
112    /**
113     * Return the Sybase parser instance with updated token list.
114     */
115    @Override
116    protected TCustomParser getParser(ParserContext context, TSourceTokenList tokens) {
117        this.fparser.sourcetokenlist = tokens;
118        return this.fparser;
119    }
120
121    /**
122     * Call Sybase-specific tokenization logic.
123     * <p>
124     * Sybase uses MSSQL-like tokenization.
125     */
126    @Override
127    protected void tokenizeVendorSql() {
128        dosybasesqltexttotokenlist();
129    }
130
131    /**
132     * Setup Sybase parser for raw statement extraction.
133     */
134    @Override
135    protected void setupVendorParsersForExtraction() {
136        this.fparser.sqlcmds = this.sqlcmds;
137        this.fparser.sourcetokenlist = this.sourcetokenlist;
138    }
139
140    /**
141     * Call Sybase-specific raw statement extraction logic.
142     */
143    @Override
144    protected void extractVendorRawStatements(SqlParseResult.Builder builder) {
145        dosybasegetrawsqlstatements(builder);
146    }
147
148    /**
149     * Perform full parsing of statements with syntax checking.
150     */
151    @Override
152    protected TStatementList performParsing(ParserContext context,
153                                           TCustomParser parser,
154                                           TCustomParser secondaryParser,
155                                           TSourceTokenList tokens,
156                                           TStatementList rawStatements) {
157        this.fparser = (TParserSybase) parser;
158        this.sourcetokenlist = tokens;
159        this.parserContext = context;
160        this.sqlstatements = rawStatements;
161
162        this.sqlcmds = SqlCmdsFactory.get(vendor);
163        this.fparser.sqlcmds = this.sqlcmds;
164
165        if (context != null && context.getGsqlparser() != null) {
166            TGSqlParser gsqlparser = (TGSqlParser) context.getGsqlparser();
167            this.frameStack = gsqlparser.getFrameStack();
168            this.fparser.getNf().setGsqlParser(gsqlparser);
169            this.globalContext = new TContext();
170            this.sqlEnv = new TSQLEnv(this.vendor) {
171                @Override
172                public void initSQLEnv() {
173                }
174            };
175            this.globalContext.setSqlEnv(this.sqlEnv, this.sqlstatements);
176        } else {
177            initializeGlobalContext();
178        }
179
180        for (int i = 0; i < sqlstatements.size(); i++) {
181            TCustomSqlStatement stmt = sqlstatements.getRawSql(i);
182
183            try {
184                stmt.setFrameStack(frameStack);
185                int parseResult = stmt.parsestatement(null, false, context.isOnlyNeedRawParseTree());
186
187                boolean doRecover = TBaseType.ENABLE_ERROR_RECOVER_IN_CREATE_TABLE;
188                if (doRecover && ((parseResult != 0) || (stmt.getErrorCount() > 0))) {
189                    handleCreateTableErrorRecovery(stmt);
190                }
191
192                if ((parseResult != 0) || (stmt.getErrorCount() > 0)) {
193                    copyErrorsFromStatement(stmt);
194                }
195
196            } catch (Exception ex) {
197                handleStatementParsingException(stmt, i, ex);
198                continue;
199            }
200        }
201
202        if (globalFrame != null) {
203            globalFrame.popMeFromStack(frameStack);
204        }
205
206        return this.sqlstatements;
207    }
208
209    private void handleCreateTableErrorRecovery(TCustomSqlStatement stmt) {
210        if (((stmt.sqlstatementtype == ESqlStatementType.sstcreatetable)
211                || (stmt.sqlstatementtype == ESqlStatementType.sstcreateindex))
212                && (!TBaseType.c_createTableStrictParsing)) {
213
214            int nested = 0;
215            boolean isIgnore = false, isFoundIgnoreToken = false;
216            TSourceToken firstIgnoreToken = null;
217
218            for (int k = 0; k < stmt.sourcetokenlist.size(); k++) {
219                TSourceToken st = stmt.sourcetokenlist.get(k);
220                if (isIgnore) {
221                    if (st.issolidtoken() && (st.tokencode != ';')) {
222                        isFoundIgnoreToken = true;
223                        if (firstIgnoreToken == null) {
224                            firstIgnoreToken = st;
225                        }
226                    }
227                    if (st.tokencode != ';') {
228                        st.tokencode = TBaseType.sqlpluscmd;
229                    }
230                    continue;
231                }
232                if (st.tokencode == (int) ')') {
233                    nested--;
234                    if (nested == 0) {
235                        boolean isSelect = false;
236                        TSourceToken st1 = st.searchToken(TBaseType.rrw_as, 1);
237                        if (st1 != null) {
238                            TSourceToken st2 = st.searchToken((int) '(', 2);
239                            if (st2 != null) {
240                                TSourceToken st3 = st.searchToken(TBaseType.rrw_select, 3);
241                                isSelect = (st3 != null);
242                            }
243                        }
244                        if (!isSelect) isIgnore = true;
245                    }
246                } else if (st.tokencode == (int) '(') {
247                    nested++;
248                }
249            }
250
251            if (isFoundIgnoreToken) {
252                stmt.clearError();
253                stmt.parsestatement(null, false);
254            }
255        }
256    }
257
258    @Override
259    protected void performSemanticAnalysis(ParserContext context, TStatementList statements) {
260        if (context != null && context.getGsqlparser() != null) {
261            return;
262        }
263
264        if (getSyntaxErrors().isEmpty()) {
265            if (TBaseType.isEnableResolver2()) {
266                TSQLResolverConfig config = new TSQLResolverConfig();
267                config.setVendor(vendor);
268                TSQLResolver2 resolver2 = new TSQLResolver2(null, statements, config);
269                if (this.sqlEnv != null) {
270                    resolver2.setSqlEnv(this.sqlEnv);
271                }
272                resolver2.resolve();
273            } else if (TBaseType.isEnableResolver()) {
274                TSQLResolver resolver = new TSQLResolver(globalContext, statements);
275                resolver.resolve();
276            }
277        }
278    }
279
280    @Override
281    protected void performInterpreter(ParserContext context, TStatementList statements) {
282        if (TBaseType.ENABLE_INTERPRETER && getSyntaxErrors().isEmpty()) {
283            TLog.clearLogs();
284            TGlobalScope interpreterScope = new TGlobalScope(sqlEnv);
285            TLog.enableInterpreterLogOnly();
286            TASTEvaluator astEvaluator = new TASTEvaluator(statements, interpreterScope);
287            astEvaluator.eval();
288        }
289    }
290
291    // ========== Helper Methods ==========
292
293    /**
294     * Add token to current statement with proper statement linkage.
295     * This replicates TCustomSqlStatement.addtokentolist() behavior
296     * which sets st.stmt = this before adding.
297     */
298    private void addTokenToStatement(TSourceToken st) {
299        st.stmt = gcurrentsqlstatement;
300        gcurrentsqlstatement.sourcetokenlist.add(st);
301    }
302
303    // ========== Sybase-Specific Tokenization ==========
304
305    /**
306     * Sybase-specific tokenization logic.
307     * <p>
308     * Sybase uses MSSQL-like tokenization.
309     * Migrated from TGSqlParser.dosybasesqltexttotokenlist()
310     */
311    private void dosybasesqltexttotokenlist() {
312        TSourceToken lcprevtoken = null;
313        int lcsteps = 0;
314        TSourceToken asourcetoken, lctoken, lctoken2, lctoken3;
315        int yychar;
316        boolean iskeywordgo;
317
318        asourcetoken = getanewsourcetoken();
319
320        if (asourcetoken == null) return;
321
322        yychar = asourcetoken.tokencode;
323
324        boolean lcinopenrowset = false;
325        int lcnested = 0;
326
327        while (yychar > 0) {
328
329            if (asourcetoken.tokencode == TBaseType.rrw_openrowset) {
330                lcinopenrowset = true;
331                lcnested = 0;
332            } else if (asourcetoken.tokentype == ETokenType.ttleftparenthesis) {
333                if ((lcsteps > 0) && TBaseType.assigned(lcprevtoken)) {
334                    if (lcprevtoken.tokencode == TBaseType.rrw_primary) {
335                        lcprevtoken.tokencode = TBaseType.rrw_select - 2;
336                        checkconstarinttoken(lcprevtoken);
337                    } else if (lcprevtoken.tokencode == TBaseType.rrw_foreign) {
338                        lcprevtoken.tokencode = TBaseType.rrw_select - 4;
339                        checkconstarinttoken(lcprevtoken);
340                    } else if (lcprevtoken.tokencode == TBaseType.rrw_unique) {
341                        lcprevtoken.tokencode = TBaseType.rrw_select - 1;
342                        checkconstarinttoken(lcprevtoken);
343                    }
344                    lcprevtoken = null;
345                    lcsteps = 0;
346                }
347
348                if (lcinopenrowset)
349                    lcnested++;
350            } else if (asourcetoken.tokentype == ETokenType.ttrightparenthesis) {
351                if (lcinopenrowset) {
352                    if ((lcnested > 0))
353                        lcnested--;
354                    if (lcnested == 0)
355                        lcinopenrowset = false;
356                }
357            } else if (asourcetoken.tokentype == ETokenType.ttsemicolon) {
358                if (lcinopenrowset) {
359                    asourcetoken.tokentype = ETokenType.ttsemicolon2;
360                } else {
361                    lctoken2 = asourcetoken.searchToken(TBaseType.rrw_begin, -1);
362                    if (lctoken2 != null) {
363                        asourcetoken.tokencode = TBaseType.SEMI_COLON_AFTER_BEGIN;
364                        asourcetoken.tokentype = ETokenType.ttsemicolon3;
365                    } else {
366                        lctoken2 = asourcetoken.searchToken(TBaseType.rrw_try, -1);
367                        if (lctoken2 == null) {
368                            lctoken2 = asourcetoken.searchToken(TBaseType.rrw_catch, -1);
369                        }
370                        if (lctoken2 != null) {
371                            lctoken3 = asourcetoken.searchToken(TBaseType.rrw_begin, -2);
372                            if (lctoken3 != null) {
373                                asourcetoken.tokencode = TBaseType.SEMI_COLON_AFTER_BEGIN;
374                                asourcetoken.tokentype = ETokenType.ttsemicolon3;
375                            }
376                        }
377                    }
378                }
379            } else if (asourcetoken.tokentype == ETokenType.ttperiod) {
380                lctoken = getprevtoken(asourcetoken);
381                if (TBaseType.assigned(lctoken)) {
382                    if (lctoken.tokencode == TBaseType.rrw_go) {
383                        lctoken.tokencode = TBaseType.ident;
384                        lctoken.tokentype = ETokenType.ttidentifier;
385                    }
386                }
387            } else if (asourcetoken.tokencode == TBaseType.rrw_table) {
388                lctoken = getprevtoken(asourcetoken);
389                if (TBaseType.assigned(lctoken)) {
390                    if (lctoken.tokencode == TBaseType.rrw_lock) {
391                        lctoken.tokencode = TBaseType.rw_locktable;
392                    }
393                }
394            } else if (asourcetoken.tokencode == TBaseType.rrw_sybase_isolation) {
395                // AT ISOLATION clause - change 'at' token code for grammar recognition
396                lctoken = getprevtoken(asourcetoken);
397                if (TBaseType.assigned(lctoken)) {
398                    if (lctoken.tokencode == TBaseType.rrw_sybase_at) {
399                        lctoken.tokencode = TBaseType.rw_sybase_at1;
400                    }
401                }
402            } else if (asourcetoken.tokencode == TBaseType.rrw_update) {
403                // UPDATE in create trigger context
404                lctoken = getprevtoken(asourcetoken);
405                if (TBaseType.assigned(lctoken)) {
406                    if ((lctoken.tokencode == TBaseType.rrw_not)
407                            || (lctoken.tokencode == TBaseType.rrw_and)
408                            || (lctoken.tokencode == TBaseType.rrw_or)
409                            || (lctoken.tokencode == TBaseType.rrw_if)
410                    ) {
411                        // this is update(column) in create trigger
412                        asourcetoken.tokencode = TBaseType.rw_sybase_update1;
413                    }
414                }
415            } else if (asourcetoken.tokencode == TBaseType.rrw_go) {
416                iskeywordgo = true;
417                lctoken = getprevtoken(asourcetoken);
418                if (TBaseType.assigned(lctoken)) {
419                    if ((lctoken.lineNo == asourcetoken.lineNo) && (lctoken.tokencode != ';')) {
420                        iskeywordgo = false;
421                    }
422                }
423
424                if (iskeywordgo) {
425                    lcinopenrowset = false;
426                    lcnested = 0;
427                    lcprevtoken = asourcetoken;
428                } else {
429                    asourcetoken.tokencode = TBaseType.ident;
430                    asourcetoken.tokentype = ETokenType.ttidentifier;
431                }
432            } else if (asourcetoken.tokencode == TBaseType.rrw_primary) {
433                lcsteps = 2;
434                lcprevtoken = asourcetoken;
435            } else if (asourcetoken.tokencode == TBaseType.rrw_foreign) {
436                lcsteps = 2;
437                lcprevtoken = asourcetoken;
438            } else if (asourcetoken.tokencode == TBaseType.rrw_unique) {
439                lcsteps = 1;
440                lcprevtoken = asourcetoken;
441            } else if (asourcetoken.issolidtoken()) {
442                if (lcsteps > 0) {
443                    if (!(TBaseType.mysametext("clustered", asourcetoken.toString())
444                            || TBaseType.mysametext("nonclustered", asourcetoken.toString())
445                            || TBaseType.mysametext("hash", asourcetoken.toString())
446                    ))
447                        lcsteps--;
448                }
449            }
450
451            sourcetokenlist.add(asourcetoken);
452
453            asourcetoken = getanewsourcetoken();
454            if (asourcetoken == null) break;
455            yychar = asourcetoken.tokencode;
456        }
457    }
458
459    private TSourceToken getprevtoken(TSourceToken ptoken) {
460        TSourceTokenList lcstlist = ptoken.container;
461        if (TBaseType.assigned(lcstlist)) {
462            if ((ptoken.posinlist > 0) && (lcstlist.size() > ptoken.posinlist - 1)) {
463                if (!((lcstlist.get(ptoken.posinlist - 1).tokentype == ETokenType.ttwhitespace)
464                        || (lcstlist.get(ptoken.posinlist - 1).tokentype == ETokenType.ttreturn)
465                        || (lcstlist.get(ptoken.posinlist - 1).tokentype == ETokenType.ttsimplecomment)
466                        || (lcstlist.get(ptoken.posinlist - 1).tokentype == ETokenType.ttbracketedcomment)
467                )) {
468                    return lcstlist.get(ptoken.posinlist - 1);
469                } else {
470                    return lcstlist.nextsolidtoken(ptoken.posinlist - 1, -1, false);
471                }
472            }
473        }
474        return null;
475    }
476
477    private void checkconstarinttoken(TSourceToken token) {
478        TSourceTokenList lcStList = token.container;
479        if (TBaseType.assigned(lcStList)) {
480            TSourceToken lcPPToken = lcStList.nextsolidtoken(token.posinlist, -2, false);
481            if (TBaseType.assigned(lcPPToken)) {
482                if (lcPPToken.tokencode == flexer.getkeywordvalue("constraint")) {
483                    lcPPToken.tokencode = TBaseType.rw_constraint2;
484                }
485            }
486        }
487    }
488
489    // ========== Sybase-Specific Raw Statement Extraction ==========
490
491    /**
492     * Sybase-specific raw statement extraction logic.
493     * <p>
494     * Migrated from TGSqlParser.dosybasegetrawsqlstatements()
495     */
496    private void dosybasegetrawsqlstatements(SqlParseResult.Builder builder) {
497        int errorcount = 0;
498        int case_end_nest = 0;
499        // Watcom / SAP IQ / SQL Anywhere `IF cond THEN ... END IF` tracking.
500        // `lcwatcomIfNest` counts open IF...THEN blocks awaiting END IF or
501        // ENDIF inside a BEGIN/END block. `lcwhenPending` counts WHEN tokens
502        // awaiting their matching THEN so that CASE WHEN ... THEN, MERGE
503        // WHEN ... THEN and EXCEPTION WHEN ... THEN do not look like Watcom
504        // IF openers. The counters are only used in the stblock state — at
505        // top level we still rely on the standard statement-splitter path.
506        int lcwatcomIfNest = 0;
507        int lcwhenPending = 0;
508
509        if (TBaseType.assigned(sqlstatements)) sqlstatements.clear();
510        if (!TBaseType.assigned(sourcetokenlist)) {
511            builder.errorCode(-1);
512            builder.errorMessage("Source token list is null");
513            return;
514        }
515
516        gcurrentsqlstatement = null;
517        EFindSqlStateType gst = EFindSqlStateType.stnormal;
518        int lcblocklevel = 0;
519        int lctrycatchlevel = 0;
520        TSourceToken lcprevsolidtoken = null, lcnextsolidtoken, lcnnextsolidtoken;
521        TSourceToken ast = null;
522        int i;
523        boolean lcisendconversation, lcstillinsql;
524
525        for (i = 0; i < sourcetokenlist.size(); i++) {
526
527            if ((ast != null) && (ast.issolidtoken()))
528                lcprevsolidtoken = ast;
529
530            ast = sourcetokenlist.get(i);
531            sourcetokenlist.curpos = i;
532            if (ast.tokenstatus == ETokenStatus.tsignoredbygetrawstatement) {
533                addTokenToStatement(ast);
534                continue;
535            }
536
537            if (gst == EFindSqlStateType.ststoredprocedurebody) {
538                if (!((ast.tokencode == TBaseType.rrw_go)
539                        || (ast.tokencode == TBaseType.rrw_create)
540                        || (ast.tokencode == TBaseType.rrw_alter))) {
541                    addTokenToStatement(ast);
542                    continue;
543                }
544            }
545
546            TCustomSqlStatement lcnextsqlstatement = sqlcmds.issql(ast, gst, gcurrentsqlstatement);
547
548            switch (gst) {
549                case sterror: {
550                    if (TBaseType.assigned(lcnextsqlstatement)) {
551                        onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
552                        gcurrentsqlstatement = lcnextsqlstatement;
553                        addTokenToStatement(ast);
554                        gst = EFindSqlStateType.stsql;
555                    } else if ((ast.tokentype == ETokenType.ttsemicolon)) {
556                        addTokenToStatement(ast);
557                        onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
558                        gst = EFindSqlStateType.stnormal;
559                    } else {
560                        addTokenToStatement(ast);
561                    }
562                    break;
563                }
564                case stnormal: {
565                    if ((ast.tokencode == TBaseType.cmtdoublehyphen)
566                            || (ast.tokencode == TBaseType.cmtslashstar)
567                            || (ast.tokencode == TBaseType.lexspace)
568                            || (ast.tokencode == TBaseType.lexnewline)
569                            || (ast.tokentype == ETokenType.ttsemicolon)) {
570                        if (TBaseType.assigned(gcurrentsqlstatement)) {
571                            addTokenToStatement(ast);
572                        }
573
574                        if (TBaseType.assigned(lcprevsolidtoken) && (ast.tokentype == ETokenType.ttsemicolon)) {
575                            if (lcprevsolidtoken.tokentype == ETokenType.ttsemicolon) {
576                                ast.tokentype = ETokenType.ttsimplecomment;
577                                ast.tokencode = TBaseType.cmtdoublehyphen;
578                            }
579                        }
580
581                        continue;
582                    }
583
584                    gcurrentsqlstatement = lcnextsqlstatement;
585
586                    if (TBaseType.assigned(gcurrentsqlstatement)) {
587                        switch (gcurrentsqlstatement.sqlstatementtype) {
588                            case sstmssqlcreateprocedure:
589                            case sstmssqlcreatefunction:
590                            case sstcreatetrigger:
591                            case sstmssqlalterprocedure:
592                            case sstmssqlalterfunction:
593                            case sstmssqlaltertrigger: {
594                                addTokenToStatement(ast);
595                                gst = EFindSqlStateType.ststoredprocedure;
596                                break;
597                            }
598                            case sstmssqlbegintry:
599                            case sstmssqlbegincatch: {
600                                addTokenToStatement(ast);
601                                gst = EFindSqlStateType.sttrycatch;
602                                lctrycatchlevel = 0;
603                                break;
604                            }
605                            case sstmssqlgo: {
606                                addTokenToStatement(ast);
607                                onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
608                                gst = EFindSqlStateType.stnormal;
609                                break;
610                            }
611                            default: {
612                                addTokenToStatement(ast);
613                                if (gcurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqllabel) {
614                                    onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
615                                    gst = EFindSqlStateType.stnormal;
616                                } else {
617                                    gst = EFindSqlStateType.stsql;
618                                }
619                                break;
620                            }
621                        }
622                    } else {
623                        if (ast.tokencode == TBaseType.rrw_begin) {
624                            gcurrentsqlstatement = new TMssqlBlock(vendor);
625                            gcurrentsqlstatement.sqlstatementtype = ESqlStatementType.sstmssqlblock;
626                            addTokenToStatement(ast);
627                            gst = EFindSqlStateType.stblock;
628                        } else {
629                            if (sqlstatements.size() == 0) {
630                                gst = EFindSqlStateType.stsql;
631                                gcurrentsqlstatement = new TMssqlExecute(vendor);
632                                ast.tokencode = TBaseType.rrw_sybase_exce_proc_name;
633                                addTokenToStatement(ast);
634                            } else if (sqlstatements.get(sqlstatements.size() - 1).sqlstatementtype == ESqlStatementType.sstmssqlgo) {
635                                gst = EFindSqlStateType.stsql;
636                                gcurrentsqlstatement = new TMssqlExecute(vendor);
637                                ast.tokencode = TBaseType.rrw_sybase_exce_proc_name;
638                                addTokenToStatement(ast);
639                            }
640                        }
641                    }
642
643                    if (!TBaseType.assigned(gcurrentsqlstatement)) {
644                        this.syntaxErrors.add(new TSyntaxError(ast.getAstext(), ast.lineNo, (ast.columnNo < 0 ? 0 : ast.columnNo)
645                                , "Error when tokenlize", EErrorType.spwarning, TBaseType.MSG_WARNING_ERROR_WHEN_TOKENIZE, null, ast.posinlist));
646
647                        ast.tokentype = ETokenType.tttokenlizererrortoken;
648                        gst = EFindSqlStateType.sterror;
649
650                        gcurrentsqlstatement = new TUnknownSqlStatement(vendor);
651                        gcurrentsqlstatement.sqlstatementtype = ESqlStatementType.sstinvalid;
652                        addTokenToStatement(ast);
653                    }
654                    break;
655                }
656                case stblock: {
657                    if (TBaseType.assigned(lcnextsqlstatement)) {
658                        if (lcnextsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlgo) {
659                            onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
660                            gcurrentsqlstatement = lcnextsqlstatement;
661                            addTokenToStatement(ast);
662                            onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
663                            gst = EFindSqlStateType.stnormal;
664                        } else {
665                            lcnextsqlstatement = null;
666                        }
667                    }
668
669                    if (gst == EFindSqlStateType.stblock) {
670                        addTokenToStatement(ast);
671                        if (ast.tokencode == TBaseType.rrw_begin) {
672                            lcnextsolidtoken = sourcetokenlist.nextsolidtoken(i, 1, false);
673                            if (TBaseType.assigned(lcnextsolidtoken)) {
674                                if (!(TBaseType.mysametext(lcnextsolidtoken.getAstext(), "tran")
675                                        || TBaseType.mysametext(lcnextsolidtoken.getAstext(), "transaction")
676                                        || TBaseType.mysametext(lcnextsolidtoken.getAstext(), "distributed")
677                                        || TBaseType.mysametext(lcnextsolidtoken.getAstext(), "dialog")
678                                        || TBaseType.mysametext(lcnextsolidtoken.getAstext(), "conversation")
679                                ))
680                                    lcblocklevel++;
681                            } else
682                                lcblocklevel++;
683
684                        } else if (ast.tokencode == TBaseType.rrw_case)
685                            lcblocklevel++;
686                        else if (ast.tokencode == TBaseType.rrw_when) {
687                            // Each WHEN expects a matching THEN (CASE, MERGE,
688                            // EXCEPTION). Track so that THEN does not get
689                            // confused with the Watcom IF...THEN opener.
690                            lcwhenPending++;
691                        } else if (ast.tokencode == TBaseType.rrw_then) {
692                            if (lcwhenPending > 0)
693                                lcwhenPending--;
694                            else
695                                lcwatcomIfNest++;
696                        } else if (ast.tokencode == flexer.getkeywordvalue("ENDIF")) {
697                            // One-word ENDIF closes a Watcom IF if one is open.
698                            if (lcwatcomIfNest > 0)
699                                lcwatcomIfNest--;
700                        } else if (ast.tokencode == TBaseType.rrw_end) {
701
702                            lcisendconversation = false;
703                            // Watcom/SQL Anywhere/Sybase IQ: `END IF` closes a
704                            // Watcom IF statement only when there is an open
705                            // IF...THEN to match. Without that match, `END`
706                            // followed by `IF` is just a block-close followed
707                            // by a new T-SQL `IF` statement (semicolons are
708                            // optional in Sybase/T-SQL), so we must still
709                            // decrement the block level normally.
710                            boolean lcisendif = false;
711
712                            lcnextsolidtoken = sourcetokenlist.nextsolidtoken(i, 1, false);
713                            if (TBaseType.assigned(lcnextsolidtoken)) {
714                                if (lcnextsolidtoken.tokencode == flexer.getkeywordvalue("CONVERSATION"))
715                                    lcisendconversation = true;
716                                else if (lcnextsolidtoken.tokencode == TBaseType.rrw_if
717                                        && lcwatcomIfNest > 0) {
718                                    lcisendif = true;
719                                    lcwatcomIfNest--;
720                                }
721                            }
722
723                            if (!lcisendconversation && !lcisendif) {
724
725                                if (lcblocklevel == 0) {
726                                    if (gcurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlif) {
727                                        if (TBaseType.assigned(lcnextsolidtoken)) {
728                                            if (lcnextsolidtoken.tokencode == TBaseType.rrw_else) {
729                                                gst = EFindSqlStateType.stsql;
730                                            } else if (lcnextsolidtoken.tokentype == ETokenType.ttsemicolon) {
731                                                lcnnextsolidtoken = sourcetokenlist.nextsolidtoken(lcnextsolidtoken.posinlist, 1, false);
732                                                if (TBaseType.assigned(lcnnextsolidtoken)) {
733                                                    if (lcnnextsolidtoken.tokencode == TBaseType.rrw_else) {
734                                                        gst = EFindSqlStateType.stsql;
735                                                    }
736                                                }
737                                            }
738                                        }
739                                    }
740
741                                    if (gst != EFindSqlStateType.stsql) {
742                                        onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
743                                        gst = EFindSqlStateType.stnormal;
744                                    }
745
746                                } else {
747                                    lcblocklevel--;
748                                }
749
750                            }
751                        }
752                    }
753                    break;
754                }
755                case sttrycatch: {
756
757                    if (TBaseType.assigned(lcnextsqlstatement)) {
758                        if (lcnextsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlgo) {
759                            onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
760                            gcurrentsqlstatement = lcnextsqlstatement;
761                            addTokenToStatement(ast);
762                            onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
763                            gst = EFindSqlStateType.stnormal;
764                        } else {
765                            if (
766                                    (lcnextsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlbegintry)
767                                            || (lcnextsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlbegincatch)
768                            )
769                                lctrycatchlevel++;
770                            lcnextsqlstatement = null;
771                        }
772                    }
773
774                    if (gst == EFindSqlStateType.sttrycatch) {
775                        addTokenToStatement(ast);
776                        if ((ast.tokencode == TBaseType.rrw_try) ||
777                                (ast.tokencode == TBaseType.rrw_catch)) {
778                            if (TBaseType.assigned(lcprevsolidtoken)) {
779                                if (lcprevsolidtoken.tokencode == TBaseType.rrw_end) {
780                                    if (lctrycatchlevel == 0) {
781                                        onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
782                                        gst = EFindSqlStateType.stnormal;
783                                    } else
784                                        lctrycatchlevel--;
785                                }
786                            }
787                        }
788                    }
789                    break;
790                }
791                case stsql: {
792                    if ((ast.tokentype == ETokenType.ttsemicolon)) {
793                        lcstillinsql = false;
794                        if (gcurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlif) {
795                            lcnextsolidtoken = sourcetokenlist.nextsolidtoken(i, 1, false);
796                            if (TBaseType.assigned(lcnextsolidtoken)) {
797                                if (lcnextsolidtoken.tokencode == TBaseType.rrw_else) {
798                                    addTokenToStatement(ast);
799                                    lcstillinsql = true;
800                                }
801
802                            }
803                        }
804
805                        if (!lcstillinsql) {
806                            gst = EFindSqlStateType.stnormal;
807                            addTokenToStatement(ast);
808                            gcurrentsqlstatement.semicolonended = ast;
809                            onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
810                        }
811
812                    } else if (TBaseType.assigned(lcnextsqlstatement)) {
813
814                        if (lcnextsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlgo) {
815                            onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
816                            gcurrentsqlstatement = lcnextsqlstatement;
817                            addTokenToStatement(ast);
818                            onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
819                            gst = EFindSqlStateType.stnormal;
820                            continue;
821                        }
822
823                        switch (gcurrentsqlstatement.sqlstatementtype) {
824                            case sstmssqlif:
825                            case sstmssqlwhile: {
826                                if ((lcnextsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlbegincatch)
827                                        || (lcnextsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlbegintry)) {
828                                    addTokenToStatement(ast);
829                                    gst = EFindSqlStateType.stblock;
830                                    lcblocklevel = 1;
831                                    lcnextsqlstatement = null;
832                                    continue;
833
834                                } else if (gcurrentsqlstatement.dummytag == 1) {
835                                    addTokenToStatement(ast);
836
837                                    if ((lcnextsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlif)
838                                            || (lcnextsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlwhile))
839                                        gcurrentsqlstatement.dummytag = 1;
840                                    else
841                                        gcurrentsqlstatement.dummytag = 0;
842
843
844                                    lcnextsqlstatement = null;
845                                    continue;
846                                }
847                                break;
848                            }
849                            case sstmssqlalterqueue: {
850                                if (lcnextsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlexec) {
851                                    addTokenToStatement(ast);
852
853                                    lcnextsqlstatement = null;
854                                    continue;
855
856                                }
857                                break;
858                            }
859                            case sstmssqlcreateschema: {
860                                addTokenToStatement(ast);
861                                lcnextsqlstatement = null;
862                                continue;
863                            }
864                        }
865
866                        onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
867                        gcurrentsqlstatement = lcnextsqlstatement;
868                        addTokenToStatement(ast);
869
870                        switch (gcurrentsqlstatement.sqlstatementtype) {
871                            case sstmssqlcreateprocedure:
872                            case sstmssqlcreatefunction:
873                            case sstcreatetrigger:
874                            case sstmssqlalterprocedure:
875                            case sstmssqlalterfunction:
876                            case sstmssqlaltertrigger: {
877                                gst = EFindSqlStateType.ststoredprocedure;
878                                break;
879                            }
880                            case sstmssqlbegintry:
881                            case sstmssqlbegincatch: {
882                                gst = EFindSqlStateType.sttrycatch;
883                                lctrycatchlevel = 0;
884                                break;
885                            }
886                            case sstmssqlgo: {
887                                gst = EFindSqlStateType.stnormal;
888                                break;
889                            }
890                            default: {
891                                gst = EFindSqlStateType.stsql;
892                                break;
893                            }
894                        }
895
896                    } else if ((ast.tokencode == TBaseType.rrw_begin)) {
897                        if ((gcurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlif)
898                                || (gcurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlwhile)) {
899                            gst = EFindSqlStateType.stblock;
900                            lcblocklevel = 0;
901                            addTokenToStatement(ast);
902                        } else if (gcurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqldeclare) {
903                            onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
904                            gcurrentsqlstatement = new TMssqlBlock(vendor);
905                            gcurrentsqlstatement.sqlstatementtype = ESqlStatementType.sstmssqlblock;
906                            addTokenToStatement(ast);
907                            gst = EFindSqlStateType.stblock;
908                        } else {
909                            addTokenToStatement(ast);
910                        }
911                    } else if ((ast.tokencode == TBaseType.rrw_case)) {
912                        case_end_nest++;
913                        addTokenToStatement(ast);
914                    } else if ((ast.tokencode == TBaseType.rrw_end)) {
915                        if (case_end_nest > 0) {
916                            case_end_nest--;
917                        }
918                        addTokenToStatement(ast);
919                    } else if ((ast.tokencode == TBaseType.rrw_else)) {
920                        addTokenToStatement(ast);
921                        if (((gcurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlif)
922                                || (gcurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlwhile)
923                        ) && (case_end_nest == 0))
924                            gcurrentsqlstatement.dummytag = 1;
925                    } else {
926                        addTokenToStatement(ast);
927                        if (gcurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlset) {
928                            if (ast.tokencode == TBaseType.rrw_on) {
929                                gst = EFindSqlStateType.stnormal;
930                                onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
931                            }
932                        }
933                    }
934                    break;
935                }
936                case ststoredprocedure: {
937                    if (TBaseType.assigned(lcnextsqlstatement)) {
938                        if (lcnextsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlgo) {
939                            onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
940                            gcurrentsqlstatement = lcnextsqlstatement;
941                            addTokenToStatement(ast);
942                            onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
943                            gst = EFindSqlStateType.stnormal;
944                        } else {
945                            gst = EFindSqlStateType.ststoredprocedurebody;
946                            addTokenToStatement(ast);
947
948                            lcnextsqlstatement = null;
949                        }
950                    }
951
952                    if (gst == EFindSqlStateType.ststoredprocedure) {
953                        addTokenToStatement(ast);
954                        if (ast.tokencode == TBaseType.rrw_begin) {
955                            gst = EFindSqlStateType.stblock;
956                        }
957                    }
958                    break;
959                }
960                case ststoredprocedurebody: {
961                    if (TBaseType.assigned(lcnextsqlstatement)) {
962                        switch (lcnextsqlstatement.sqlstatementtype) {
963                            case sstmssqlgo: {
964                                onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
965                                gcurrentsqlstatement = lcnextsqlstatement;
966                                addTokenToStatement(ast);
967                                onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
968                                gst = EFindSqlStateType.stnormal;
969                                break;
970                            }
971                            case sstmssqlcreateprocedure:
972                            case sstmssqlcreatefunction:
973                            case sstcreatetrigger:
974                            case sstmssqlalterprocedure:
975                            case sstmssqlalterfunction:
976                            case sstmssqlaltertrigger: {
977                                onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
978                                gcurrentsqlstatement = lcnextsqlstatement;
979                                addTokenToStatement(ast);
980                                gst = EFindSqlStateType.ststoredprocedure;
981                                break;
982                            }
983                            default: {
984                                lcnextsqlstatement = null;
985                                break;
986                            }
987                        }
988                    }
989
990                    if (gst == EFindSqlStateType.ststoredprocedurebody)
991                        addTokenToStatement(ast);
992                }
993                break;
994            }
995        }
996
997        // last statement
998        if (TBaseType.assigned(gcurrentsqlstatement) && (gst != EFindSqlStateType.stnormal)) {
999            onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, true, builder);
1000        }
1001
1002        builder.sqlStatements(this.sqlstatements);
1003        builder.errorCode(errorcount);
1004        builder.errorMessage(errorcount == 0 ? "" : String.format("Extraction completed with %d error(s)", errorcount));
1005    }
1006}