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