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