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