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.TLexerMssql;
010import gudusoft.gsqlparser.TParserMssqlSql;
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
040import static gudusoft.gsqlparser.ESqlStatementType.sstmssqlreturn;
041
042/**
043 * Microsoft SQL Server database SQL parser implementation.
044 *
045 * <p>This parser handles SQL Server-specific SQL syntax including:
046 * <ul>
047 *   <li>T-SQL blocks (stored procedures, functions, triggers)</li>
048 *   <li>BEGIN/END blocks</li>
049 *   <li>TRY/CATCH error handling</li>
050 *   <li>GO batch separator</li>
051 *   <li>SQL Server-specific DML/DDL (MERGE, OPENROWSET, etc.)</li>
052 *   <li>Special token handling (LOCK TABLE, COPY INTO, etc.)</li>
053 * </ul>
054 *
055 * <p><b>Design Notes:</b>
056 * <ul>
057 *   <li>Extends {@link AbstractSqlParser} using the template method pattern</li>
058 *   <li>Uses {@link TLexerMssql} for tokenization</li>
059 *   <li>Uses {@link TParserMssqlSql} for parsing</li>
060 *   <li>Delimiter character: ';' for SQL statements</li>
061 * </ul>
062 *
063 * <p><b>Usage Example:</b>
064 * <pre>
065 * // Get SQL Server parser from factory
066 * SqlParser parser = SqlParserFactory.get(EDbVendor.dbvmssql);
067 *
068 * // Build context
069 * ParserContext context = new ParserContext.Builder(EDbVendor.dbvmssql)
070 *     .sqlText("SELECT * FROM Employees WHERE DepartmentID = 10")
071 *     .build();
072 *
073 * // Parse
074 * SqlParseResult result = parser.parse(context);
075 *
076 * // Access statements
077 * TStatementList statements = result.getSqlStatements();
078 * </pre>
079 *
080 * @see SqlParser
081 * @see AbstractSqlParser
082 * @see TLexerMssql
083 * @see TParserMssqlSql
084 * @since 3.2.0.0
085 */
086public class MssqlSqlParser extends AbstractSqlParser {
087
088    /**
089     * Construct SQL Server SQL parser.
090     * <p>
091     * Configures the parser for SQL Server database with default delimiter (;).
092     * <p>
093     * Following the original TGSqlParser pattern, the lexer and parser are
094     * created once in the constructor and reused for all parsing operations.
095     */
096    public MssqlSqlParser() {
097        super(EDbVendor.dbvmssql);
098        this.delimiterChar = ';';
099        this.defaultDelimiterStr = ";";
100
101        // Create lexer once - will be reused for all parsing operations
102        this.flexer = new TLexerMssql();
103        this.flexer.delimiterchar = this.delimiterChar;
104        this.flexer.defaultDelimiterStr = this.defaultDelimiterStr;
105
106        // Set parent's lexer reference for shared tokenization logic
107        this.lexer = this.flexer;
108
109        // Create parser once - will be reused for all parsing operations
110        this.fparser = new TParserMssqlSql(null);
111        this.fparser.lexer = this.flexer;
112    }
113
114    // ========== Parser Components ==========
115
116    /** The SQL Server lexer used for tokenization */
117    public TLexerMssql flexer;
118
119    /** SQL parser (for SQL Server statements) */
120    private TParserMssqlSql fparser;
121
122    /** Current statement being built during extraction */
123    private TCustomSqlStatement gcurrentsqlstatement;
124
125    // Note: Global context and frame stack fields inherited from AbstractSqlParser:
126    // - protected TContext globalContext
127    // - protected TSQLEnv sqlEnv
128    // - protected Stack<TFrame> frameStack
129    // - protected TFrame globalFrame
130
131    // ========== AbstractSqlParser Abstract Methods Implementation ==========
132
133    /**
134     * Return the SQL Server lexer instance.
135     */
136    @Override
137    protected TCustomLexer getLexer(ParserContext context) {
138        return this.flexer;
139    }
140
141    /**
142     * Return the SQL Server SQL parser instance with updated token list.
143     */
144    @Override
145    protected TCustomParser getParser(ParserContext context, TSourceTokenList tokens) {
146        this.fparser.sourcetokenlist = tokens;
147        return this.fparser;
148    }
149
150    /**
151     * Call MSSQL-specific tokenization logic.
152     * <p>
153     * Delegates to domssqlsqltexttotokenlist which handles SQL Server's
154     * specific keyword recognition, bracket identifiers, and token generation.
155     */
156    @Override
157    protected void tokenizeVendorSql() {
158        domssqlsqltexttotokenlist();
159    }
160
161    /**
162     * Setup MSSQL parser for raw statement extraction.
163     * <p>
164     * MSSQL uses a single parser, so we inject sqlcmds and update
165     * the token list for the main parser only.
166     */
167    @Override
168    protected void setupVendorParsersForExtraction() {
169        // Inject sqlcmds into parser (required for make_stmt)
170        this.fparser.sqlcmds = this.sqlcmds;
171
172        // Update token list for parser
173        this.fparser.sourcetokenlist = this.sourcetokenlist;
174    }
175
176    /**
177     * Call MSSQL-specific raw statement extraction logic.
178     * <p>
179     * Delegates to domssqlgetrawsqlstatements which handles SQL Server's
180     * statement delimiters (semicolon and GO command).
181     */
182    @Override
183    protected void extractVendorRawStatements(SqlParseResult.Builder builder) {
184        domssqlgetrawsqlstatements(builder);
185    }
186
187    /**
188     * Perform full parsing of statements with syntax checking.
189     * <p>
190     * This method orchestrates the parsing of all statements.
191     */
192    @Override
193    protected TStatementList performParsing(ParserContext context,
194                                           TCustomParser parser,
195                                           TCustomParser secondaryParser,
196                                           TSourceTokenList tokens,
197                                           TStatementList rawStatements) {
198        // Store references
199        this.fparser = (TParserMssqlSql) parser;
200        this.sourcetokenlist = tokens;
201        this.parserContext = context;
202
203        // Use the raw statements passed from AbstractSqlParser.parse()
204        this.sqlstatements = rawStatements;
205
206        // Initialize statement parsing infrastructure
207        this.sqlcmds = SqlCmdsFactory.get(vendor);
208
209        // Inject sqlcmds into parser (required for make_stmt and other methods)
210        this.fparser.sqlcmds = this.sqlcmds;
211
212        // Initialize global context for semantic analysis
213        // CRITICAL: When delegated from TGSqlParser, use TGSqlParser's frameStack
214        // so that variables set in SET statements can be found by EXECUTE statements
215        if (context != null && context.getGsqlparser() != null) {
216            TGSqlParser gsqlparser = (TGSqlParser) context.getGsqlparser();
217            this.frameStack = gsqlparser.getFrameStack();
218
219            // CRITICAL: Set gsqlparser on the NodeFactory - matches TGSqlParser behavior
220            // This is needed for proper AST node creation during parsing
221            this.fparser.getNf().setGsqlParser(gsqlparser);
222
223            // Create global context if needed
224            this.globalContext = new TContext();
225            this.sqlEnv = new TSQLEnv(this.vendor) {
226                @Override
227                public void initSQLEnv() {
228                }
229            };
230            this.globalContext.setSqlEnv(this.sqlEnv, this.sqlstatements);
231        } else {
232            initializeGlobalContext();
233        }
234
235        // Parse each statement with exception handling for robustness
236        for (int i = 0; i < sqlstatements.size(); i++) {
237            TCustomSqlStatement stmt = sqlstatements.getRawSql(i);
238
239            try {
240                stmt.setFrameStack(frameStack);
241
242                // Parse the statement
243                int parseResult = stmt.parsestatement(null, false, context.isOnlyNeedRawParseTree());
244
245                // Handle error recovery for CREATE TABLE/INDEX
246                boolean doRecover = TBaseType.ENABLE_ERROR_RECOVER_IN_CREATE_TABLE;
247                if (doRecover && ((parseResult != 0) || (stmt.getErrorCount() > 0))) {
248                    handleCreateTableErrorRecovery(stmt);
249                }
250
251                // Collect syntax errors
252                if ((parseResult != 0) || (stmt.getErrorCount() > 0)) {
253                    copyErrorsFromStatement(stmt);
254                }
255
256            } catch (Exception ex) {
257                // Use inherited exception handler from AbstractSqlParser
258                // This provides consistent error handling across all database parsers
259               // ex.printStackTrace();
260                handleStatementParsingException(stmt, i, ex);
261                continue;
262            }
263        }
264
265        // Clean up frame stack
266        if (globalFrame != null) {
267            globalFrame.popMeFromStack(frameStack);
268        }
269
270        return this.sqlstatements;
271    }
272
273    // Note: initializeGlobalContext() inherited from AbstractSqlParser
274    // Note: No override of afterStatementParsed() needed - default (no-op) is appropriate for MSSQL
275
276    /**
277     * Handle error recovery for CREATE TABLE/INDEX statements.
278     */
279    private void handleCreateTableErrorRecovery(TCustomSqlStatement stmt) {
280        if (((stmt.sqlstatementtype == ESqlStatementType.sstcreatetable)
281                || (stmt.sqlstatementtype == ESqlStatementType.sstcreateindex))
282                && (!TBaseType.c_createTableStrictParsing)) {
283
284            int nested = 0;
285            boolean isIgnore = false, isFoundIgnoreToken = false;
286            TSourceToken firstIgnoreToken = null;
287
288            for (int k = 0; k < stmt.sourcetokenlist.size(); k++) {
289                TSourceToken st = stmt.sourcetokenlist.get(k);
290                if (isIgnore) {
291                    if (st.issolidtoken() && (st.tokencode != ';')) {
292                        isFoundIgnoreToken = true;
293                        if (firstIgnoreToken == null) {
294                            firstIgnoreToken = st;
295                        }
296                    }
297                    if (st.tokencode != ';') {
298                        st.tokencode = TBaseType.sqlpluscmd;
299                    }
300                    continue;
301                }
302                if (st.tokencode == (int) ')') {
303                    nested--;
304                    if (nested == 0) {
305                        boolean isSelect = false;
306                        TSourceToken st1 = st.searchToken(TBaseType.rrw_as, 1);
307                        if (st1 != null) {
308                            TSourceToken st2 = st.searchToken((int) '(', 2);
309                            if (st2 != null) {
310                                TSourceToken st3 = st.searchToken(TBaseType.rrw_select, 3);
311                                isSelect = (st3 != null);
312                            }
313                        }
314                        if (!isSelect) isIgnore = true;
315                    }
316                } else if (st.tokencode == (int) '(') {
317                    nested++;
318                }
319            }
320
321            if (isFoundIgnoreToken) {
322                stmt.clearError();
323                stmt.parsestatement(null, false);
324            }
325        }
326    }
327
328    /**
329     * Perform SQL Server-specific semantic analysis.
330     * <p>
331     * When called via delegation from TGSqlParser (context.getGsqlparser() != null),
332     * semantic analysis is skipped here and handled by TGSqlParser.doDelegatedParse()
333     * to ensure resolver2 is stored in TGSqlParser where tests expect to find it.
334     * <p>
335     * When called directly on MssqlSqlParser, uses TBaseType flags to determine
336     * which resolver to use:
337     * <ul>
338     *   <li>TBaseType.isEnableResolver2() -> use TSQLResolver2</li>
339     *   <li>TBaseType.isEnableResolver() -> use TSQLResolver</li>
340     * </ul>
341     */
342    @Override
343    protected void performSemanticAnalysis(ParserContext context, TStatementList statements) {
344        // Skip semantic analysis when delegated from TGSqlParser
345        // TGSqlParser.doDelegatedParse() will handle it to store resolver2 properly
346        if (context != null && context.getGsqlparser() != null) {
347            return;
348        }
349
350        if (getSyntaxErrors().isEmpty()) {
351            if (TBaseType.isEnableResolver2()) {
352                // Use TSQLResolver2
353                TSQLResolverConfig config = new TSQLResolverConfig();
354                config.setVendor(vendor);
355                TSQLResolver2 resolver2 = new TSQLResolver2(null, statements, config);
356                if (this.sqlEnv != null) {
357                    resolver2.setSqlEnv(this.sqlEnv);
358                }
359                resolver2.resolve();
360            } else if (TBaseType.isEnableResolver()) {
361                // Use TSQLResolver
362                TSQLResolver resolver = new TSQLResolver(globalContext, statements);
363                resolver.resolve();
364            }
365            // If neither resolver is enabled, skip semantic analysis
366        }
367    }
368
369    /**
370     * Perform interpretation/evaluation on parsed statements.
371     */
372    @Override
373    protected void performInterpreter(ParserContext context, TStatementList statements) {
374        if (TBaseType.ENABLE_INTERPRETER && getSyntaxErrors().isEmpty()) {
375            TLog.clearLogs();
376            TGlobalScope interpreterScope = new TGlobalScope(sqlEnv);
377            TLog.enableInterpreterLogOnly();
378            TASTEvaluator astEvaluator = new TASTEvaluator(statements, interpreterScope);
379            astEvaluator.eval();
380        }
381    }
382
383    // ========== SQL Server-Specific Tokenization ==========
384
385    /**
386     * SQL Server-specific tokenization logic.
387     * <p>
388     * Extracted from: TGSqlParser.domssqlsqltexttotokenlist() (lines 2225-2425)
389     */
390    private void domssqlsqltexttotokenlist() {
391        TSourceToken lcprevtoken = null;
392        int lcsteps = 0;
393        TSourceToken asourcetoken, lctoken, lctoken2, lctoken3;
394        int yychar;
395        boolean iskeywordgo;
396
397        asourcetoken = getanewsourcetoken();
398
399        if (asourcetoken == null) return;
400
401        yychar = asourcetoken.tokencode;
402
403        boolean lcinopenrowset = false;
404        int lcnested = 0;
405
406        while (yychar > 0) {
407
408            if (asourcetoken.tokencode == TBaseType.rrw_openrowset) {
409                // 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;  //rw_primary2
416                        checkconstarinttoken(lcprevtoken);
417                    } else if (lcprevtoken.tokencode == TBaseType.rrw_foreign) {
418                        lcprevtoken.tokencode = TBaseType.rrw_select - 4;  //rw_foreign2
419                        checkconstarinttoken(lcprevtoken);
420                    } else if (lcprevtoken.tokencode == TBaseType.rrw_unique) {
421                        lcprevtoken.tokencode = TBaseType.rrw_select - 1;  //rw_unique2
422                        checkconstarinttoken(lcprevtoken);
423                    }
424                    lcprevtoken = null;
425                    lcsteps = 0;
426                }
427
428                // openrowset(....)
429                if (lcinopenrowset)
430                    lcnested++;
431            } else if (asourcetoken.tokentype == ETokenType.ttrightparenthesis) {
432                // openrowset(....)
433                if (lcinopenrowset) {
434                    if ((lcnested > 0))
435                        lcnested--;
436                    if (lcnested == 0)
437                        lcinopenrowset = false;
438                }
439            } else if (asourcetoken.tokentype == ETokenType.ttsemicolon) {
440                if (lcinopenrowset) {
441                    asourcetoken.tokentype = ETokenType.ttsemicolon2;
442                } else {
443                    lctoken2 = asourcetoken.searchToken(TBaseType.rrw_begin, -1);
444                    if (lctoken2 != null) {
445                        asourcetoken.tokencode = TBaseType.SEMI_COLON_AFTER_BEGIN;
446                        asourcetoken.tokentype = ETokenType.ttsemicolon3;
447                    } else {
448                        lctoken2 = asourcetoken.searchToken(TBaseType.rrw_try, -1);
449                        if (lctoken2 == null) {
450                            lctoken2 = asourcetoken.searchToken(TBaseType.rrw_catch, -1);
451                        }
452                        if (lctoken2 != null) {
453                            lctoken3 = asourcetoken.searchToken(TBaseType.rrw_begin, -2);
454                            if (lctoken3 != null) {
455                                asourcetoken.tokencode = TBaseType.SEMI_COLON_AFTER_BEGIN;
456                                asourcetoken.tokentype = ETokenType.ttsemicolon3;
457                            }
458                        }
459                    }
460                }
461
462                lctoken = getprevtoken(asourcetoken);
463                if ((lctoken != null)) {
464                    if (lctoken.tokentype == ETokenType.ttsemicolon) {
465                        // treat this semicolon as a whitespace
466                        asourcetoken.tokencode = TBaseType.lexspace;
467                    }
468                }
469            } else if (asourcetoken.tokentype == ETokenType.ttperiod) {
470                lctoken = getprevtoken(asourcetoken);
471                if (TBaseType.assigned(lctoken)) {
472                    // go.fieldname, go is a table alias, not a go statement
473                    if (lctoken.tokencode == TBaseType.rrw_go) {
474                        lctoken.tokencode = TBaseType.ident;
475                        lctoken.tokentype = ETokenType.ttidentifier;
476                    }
477                }
478            } else if (asourcetoken.tokencode == TBaseType.rrw_table) {
479                lctoken = getprevtoken(asourcetoken);
480                if (TBaseType.assigned(lctoken)) {
481                    if (lctoken.tokencode == TBaseType.rrw_lock) {
482                        lctoken.tokencode = TBaseType.rw_locktable;
483                    }
484                }
485            } else if (asourcetoken.tokencode == TBaseType.rrw_into) {
486                lctoken = getprevtoken(asourcetoken);
487                if (TBaseType.assigned(lctoken)) {
488                    if (lctoken.tokencode == TBaseType.rrw_sqlserver_copy) {
489                        lctoken.tokencode = TBaseType.rrw_sqlserver_copyinto;
490                    }
491                }
492            } else if (asourcetoken.tokencode == TBaseType.rrw_sqlserver_column) {
493                lctoken = getprevtoken(asourcetoken);
494                if (TBaseType.assigned(lctoken)) {
495                    if (lctoken.tokencode == TBaseType.rrw_drop) {
496                        asourcetoken.tokencode = TBaseType.rrw_sqlserver_drop_column;
497                    }
498                }
499            } else if (asourcetoken.tokencode == TBaseType.rrw_go) {
500                iskeywordgo = true;
501                lctoken = getprevtoken(asourcetoken);
502                if (TBaseType.assigned(lctoken)) {
503                    // go should not at same line as other sql statement
504                    if ((lctoken.lineNo == asourcetoken.lineNo) && (lctoken.tokencode != ';')) {
505                        iskeywordgo = false;
506                    }
507                }
508
509                if (iskeywordgo) {
510                    lcinopenrowset = false;
511                    lcnested = 0;
512                    lcprevtoken = asourcetoken;
513                } else {
514                    asourcetoken.tokencode = TBaseType.ident;
515                    asourcetoken.tokentype = ETokenType.ttidentifier;
516                }
517            } else if (asourcetoken.tokencode == TBaseType.rrw_primary) {
518                // primary key [clustered | nonclustered ] [hash] ( column list)
519                lcsteps = 2;
520                lcprevtoken = asourcetoken;
521            } else if (asourcetoken.tokencode == TBaseType.rrw_foreign) {
522                // foreign key [clustered | nonclustered ] ( column list)
523                lcsteps = 2;
524                lcprevtoken = asourcetoken;
525            } else if (asourcetoken.tokencode == TBaseType.rrw_unique) {
526                // unique [clustered | nonclustered ] [hash] ( column list)
527                lcsteps = 1;
528                lcprevtoken = asourcetoken;
529            } else if (asourcetoken.issolidtoken()) {
530                if (lcsteps > 0) {
531                    if (!(TBaseType.mysametext("clustered", asourcetoken.toString())
532                            || TBaseType.mysametext("nonclustered", asourcetoken.toString())
533                            || TBaseType.mysametext("hash", asourcetoken.toString())
534                    ))
535                        lcsteps--;
536                }
537            }
538
539            sourcetokenlist.add(asourcetoken);
540
541            asourcetoken = getanewsourcetoken();
542            if (asourcetoken == null) break;
543            yychar = asourcetoken.tokencode;
544        }
545    }
546
547    /**
548     * Get previous token in token list.
549     * <p>
550     * Matches TGSqlParser.getprevtoken() implementation exactly to ensure
551     * consistent tokenization behavior.
552     */
553    private TSourceToken getprevtoken(TSourceToken ptoken) {
554        TSourceTokenList lcstlist = ptoken.container;
555        if (TBaseType.assigned(lcstlist)) {
556            if ((ptoken.posinlist > 0) && (lcstlist.size() > ptoken.posinlist - 1)) {
557                if (!((lcstlist.get(ptoken.posinlist - 1).tokentype == ETokenType.ttwhitespace)
558                        || (lcstlist.get(ptoken.posinlist - 1).tokentype == ETokenType.ttreturn)
559                        || (lcstlist.get(ptoken.posinlist - 1).tokentype == ETokenType.ttsimplecomment)
560                        || (lcstlist.get(ptoken.posinlist - 1).tokentype == ETokenType.ttbracketedcomment)
561                )) {
562                    return lcstlist.get(ptoken.posinlist - 1);
563                } else {
564                    return lcstlist.nextsolidtoken(ptoken.posinlist - 1, -1, false);
565                }
566            }
567        }
568        return null;
569    }
570
571    /**
572     * Check and adjust constraint token codes.
573     * <p>
574     * When PRIMARY KEY, FOREIGN KEY, or UNIQUE is followed by '(', we need to
575     * look back to see if there's a CONSTRAINT keyword. If found, change its
576     * token code from rrw_constraint (515) to rw_constraint2 (298).
577     * <p>
578     * Extracted from: TGSqlParser.checkconstarinttoken() (lines 1989-2005)
579     *
580     * @param token The token after which to search (PRIMARY/FOREIGN/UNIQUE)
581     */
582    private void checkconstarinttoken(TSourceToken token) {
583        TSourceTokenList lcStList = token.container;
584        if (TBaseType.assigned(lcStList)) {
585            // Look back 2 solid tokens to find CONSTRAINT keyword
586            TSourceToken lcPPToken = lcStList.nextsolidtoken(token.posinlist, -2, false);
587            if (TBaseType.assigned(lcPPToken)) {
588                if (lcPPToken.tokencode == flexer.getkeywordvalue("constraint")) {
589                    // Change CONSTRAINT to special constraint2 token code
590                    lcPPToken.tokencode = TBaseType.rw_constraint2;
591                }
592            }
593        }
594    }
595
596    // ========== SQL Server-Specific Raw Statement Extraction ==========
597
598    /**
599     * SQL Server-specific raw statement extraction logic.
600     * <p>
601     * Extracted from: TGSqlParser.domssqlgetrawsqlstatements() (lines 13081-13904)
602     */
603    private void domssqlgetrawsqlstatements(SqlParseResult.Builder builder) {
604        int errorcount = 0;
605        int case_end_nest = 0;
606
607        if (TBaseType.assigned(sqlstatements)) sqlstatements.clear();
608        if (!TBaseType.assigned(sourcetokenlist)) {
609            builder.errorCode(-1);
610            builder.errorMessage("Source token list is null");
611            return;
612        }
613
614        gcurrentsqlstatement = null;
615        EFindSqlStateType gst = EFindSqlStateType.stnormal;
616        int lcblocklevel = 0;
617        int lctrycatchlevel = 0;
618        TSourceToken lcprevsolidtoken = null, lcnextsolidtoken, lcnnextsolidtoken;
619        TSourceToken ast = null;
620        int i, lcMergeInSelectNested = 0;
621        boolean lcisendconversation, lcstillinsql, lcMergeInSelect = false;
622
623        for (i = 0; i < sourcetokenlist.size(); i++) {
624
625            if ((ast != null) && (ast.issolidtoken()))
626                lcprevsolidtoken = ast;
627
628            ast = sourcetokenlist.get(i);
629            sourcetokenlist.curpos = i;
630
631            if (ast.tokencode == TBaseType.rrw_for) {
632                TSourceToken st1 = ast.nextSolidToken();
633                if ((st1 != null) && (st1.tokencode == TBaseType.rrw_system_time)) {
634                    ast.tokencode = TBaseType.rw_for_system_time;
635                } else if ((st1 != null) && TBaseType.mycomparetext(st1.getAstext(), "PATH") == 0) {
636                    ast.tokencode = TBaseType.rw_for_path;
637                }
638            }
639
640            if (lcMergeInSelect) {
641                if (ast.tokencode == '(') lcMergeInSelectNested++;
642                if (ast.tokencode == ')') {
643                    lcMergeInSelectNested--;
644                    if (lcMergeInSelectNested == 0) {
645                        lcMergeInSelect = false;
646                    }
647                }
648                gcurrentsqlstatement.sourcetokenlist.add(ast);
649                continue;
650            }
651
652            if (ast.tokenstatus == ETokenStatus.tsignoredbygetrawstatement) {
653                gcurrentsqlstatement.sourcetokenlist.add(ast);
654                continue;
655            }
656
657            if (ast.tokencode == TBaseType.rrw_minus) {
658                TSourceToken st1 = ast.searchToken('(', 1);
659                if (st1 == null) {
660                    st1 = ast.searchToken(TBaseType.rrw_select, 1);
661                    if (st1 == null) {
662                        ast.tokencode = TBaseType.ident;
663                    }
664                }
665            } else if (ast.tokencode == TBaseType.rrw_merge) {
666                TSourceToken st1 = ast.nextSolidToken();
667                if (st1.tokencode == TBaseType.rrw_join) {
668                    ast.tokencode = TBaseType.rrw_merge2_sqlserver;
669                }
670                if ((lcprevsolidtoken != null) && (lcprevsolidtoken.tokencode == '(')) {
671                    lcMergeInSelect = true;
672                    lcMergeInSelectNested++;
673                    gcurrentsqlstatement.sourcetokenlist.add(ast);
674                    continue;
675                }
676            } else if (ast.tokencode == TBaseType.rrw_sqlserver_value) {
677                if ((lcprevsolidtoken != null) && (lcprevsolidtoken.tokencode == '.')) {
678                    TSourceToken st1 = ast.searchToken('(', 1);
679                    if (st1 != null) {
680                        ast.tokencode = TBaseType.rrw_xml_value;
681                    }
682                }
683            } else if (ast.tokencode == TBaseType.rrw_sqlserver_modify) {
684                if ((lcprevsolidtoken != null) && (lcprevsolidtoken.tokencode == '.')) {
685                    TSourceToken st1 = ast.searchToken('(', 1);
686                    if (st1 != null) {
687                        ast.tokencode = TBaseType.rrw_xml_modify;
688                    }
689                }
690            } else if (ast.tokencode == TBaseType.rrw_sqlserver_query) {
691                if ((lcprevsolidtoken != null) && (lcprevsolidtoken.tokencode == '.')) {
692                    TSourceToken st1 = ast.searchToken('(', 1);
693                    if (st1 != null) {
694                        ast.tokencode = TBaseType.rrw_xml_query;
695                    }
696                }
697            } else if (ast.tokencode == TBaseType.rrw_sqlserver_exist) {
698                if ((lcprevsolidtoken != null) && (lcprevsolidtoken.tokencode == '.')) {
699                    TSourceToken st1 = ast.searchToken('(', 1);
700                    if (st1 != null) {
701                        ast.tokencode = TBaseType.rrw_xml_exist;
702                    }
703                }
704            } else if (ast.tokencode == TBaseType.rrw_sqlserver_nodes) {
705                if ((lcprevsolidtoken != null) && (lcprevsolidtoken.tokencode == '.')) {
706                    TSourceToken st1 = ast.searchToken('(', 1);
707                    if (st1 != null) {
708                        ast.tokencode = TBaseType.rrw_xml_nodes;
709                    }
710                }
711            } else if (ast.tokencode == TBaseType.ident && ast.tokentype == ETokenType.ttdbstring) {
712                // Handle bracket-quoted XML method names: .[value](), .[nodes](), etc.
713                if ((lcprevsolidtoken != null) && (lcprevsolidtoken.tokencode == '.')) {
714                    TSourceToken st1 = ast.searchToken('(', 1);
715                    if (st1 != null) {
716                        String text = ast.toString();
717                        if (text.length() > 2) {
718                            String inner = text.substring(1, text.length() - 1).toLowerCase();
719                            if (inner.equals("value")) {
720                                ast.tokencode = TBaseType.rrw_xml_value;
721                            } else if (inner.equals("modify")) {
722                                ast.tokencode = TBaseType.rrw_xml_modify;
723                            } else if (inner.equals("query")) {
724                                ast.tokencode = TBaseType.rrw_xml_query;
725                            } else if (inner.equals("exist")) {
726                                ast.tokencode = TBaseType.rrw_xml_exist;
727                            } else if (inner.equals("nodes")) {
728                                ast.tokencode = TBaseType.rrw_xml_nodes;
729                            }
730                        }
731                    }
732                }
733            } else if (ast.tokencode == TBaseType.rrw_check) {
734                if ((lcprevsolidtoken != null) && (lcprevsolidtoken.tokencode == TBaseType.rrw_with)) {
735                    lcprevsolidtoken.tokencode = TBaseType.rrw_sqlserver_check_with;
736                }
737            } else if (ast.tokencode == TBaseType.rrw_sqlserver_next) {
738                TSourceToken st1 = ast.nextSolidToken();
739                if ((st1 != null) && (st1.tokencode == '.')) {
740                    ast.tokencode = TBaseType.ident;
741                }
742            } else if (ast.tokencode == TBaseType.rrw_fetch) {
743                if ((lcprevsolidtoken != null) && ((lcprevsolidtoken.tokencode == TBaseType.rrw_sqlserver_row) || (lcprevsolidtoken.tokencode == TBaseType.rrw_sqlserver_rows))) {
744                    TSourceToken prev2 = lcprevsolidtoken.searchToken(TBaseType.rrw_open, -1);
745                    if (prev2 == null) {
746                        ast.tokencode = TBaseType.rrw_sqlserver_offset_fetch;
747                    }
748                }
749            } else if ((ast.tokencode == TBaseType.rrw_exec) || (ast.tokencode == TBaseType.rrw_execute)) {
750                // search ;2 after execute
751                // EXECUTE ApxSQL_Test_Triggers_Add;2  @TableName, @AddFlag
752                int searchRange = 4;
753                TSourceToken endTokenInSameLine = ast.searchTokenAtTheEndOfSameLine();
754                if (endTokenInSameLine != null) {
755                    searchRange = endTokenInSameLine.posinlist - ast.posinlist;
756                }
757                TSourceToken st1 = ast.searchToken(';', searchRange);
758                if (st1 != null) {
759                    TSourceToken numSt = st1.nextSolidToken();
760                    if ((numSt != null) && (numSt.tokentype == ETokenType.ttnumber)) {
761                        st1.tokencode = TBaseType.rrw_sqlserver_semicolon_module_number;
762                        st1.tokentype = ETokenType.ttidentifier;
763                    }
764                }
765            } else if (ast.tokencode == TBaseType.rrw_sqlserver_trim) {
766                TSourceToken st1 = ast.nextSolidToken();
767                if (st1 != null) {
768                    if (st1.tokencode == '(') {
769                        // keep as trim keyword
770                    } else {
771                        ast.tokencode = TBaseType.ident;
772                    }
773                }
774            }
775
776            if (vendor == EDbVendor.dbvopenedge){
777                if (ast.tokencode == TBaseType.rrw_order){
778                        TSourceToken st1 = ast.searchToken(TBaseType.rrw_by,1);
779                        if (st1 == null) {
780                            ast.tokencode = TBaseType.ident;
781                        }
782                }else if (ast.tokencode == TBaseType.rrw_with){
783                        TSourceToken st1 = ast.searchToken(TBaseType.rrw_check,1);
784                        if (st1 != null) {
785                            ast.tokencode = TBaseType.rrw_openedge_with_check;
786                        }
787                }
788            }
789
790            if (gst == EFindSqlStateType.ststoredprocedurebody) {
791                if (!((ast.tokencode == TBaseType.rrw_go)
792                        || (ast.tokencode == TBaseType.rrw_create)
793                        || (ast.tokencode == TBaseType.rrw_alter))) {
794                    gcurrentsqlstatement.sourcetokenlist.add(ast);
795                    continue;
796                }
797            }
798
799            TCustomSqlStatement lcnextsqlstatement = sqlcmds.issql(ast, gst, gcurrentsqlstatement);
800
801            switch (gst) {
802                case sterror: {
803                    if (TBaseType.assigned(lcnextsqlstatement)) {
804                        onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
805                        gcurrentsqlstatement = lcnextsqlstatement;
806                        gcurrentsqlstatement.sourcetokenlist.add(ast);
807                        gst = EFindSqlStateType.stsql;
808                    } else if ((ast.tokentype == ETokenType.ttsemicolon)) {
809                        gcurrentsqlstatement.sourcetokenlist.add(ast);
810                        onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
811                        gst = EFindSqlStateType.stnormal;
812                    } else {
813                        gcurrentsqlstatement.sourcetokenlist.add(ast);
814                    }
815                    break;
816                }
817                case stnormal: {
818                    if ((ast.tokencode == TBaseType.cmtdoublehyphen)
819                            || (ast.tokencode == TBaseType.cmtslashstar)
820                            || (ast.tokencode == TBaseType.lexspace)
821                            || (ast.tokencode == TBaseType.lexnewline)
822                            || (ast.tokentype == ETokenType.ttsemicolon)) {
823                        if (TBaseType.assigned(gcurrentsqlstatement)) {
824                            gcurrentsqlstatement.sourcetokenlist.add(ast);
825                        }
826
827                        if (TBaseType.assigned(lcprevsolidtoken) && (ast.tokentype == ETokenType.ttsemicolon)) {
828                            if (lcprevsolidtoken.tokentype == ETokenType.ttsemicolon) {
829                                // ;;;; continuous semicolon,treat it as comment
830                                ast.tokentype = ETokenType.ttsimplecomment;
831                                ast.tokencode = TBaseType.cmtdoublehyphen;
832                            }
833                        }
834
835                        continue;
836                    }
837
838                    gcurrentsqlstatement = lcnextsqlstatement;
839
840                    if (TBaseType.assigned(gcurrentsqlstatement)) {
841                        switch (gcurrentsqlstatement.sqlstatementtype) {
842                            case sstmssqlcreateprocedure:
843                            case sstmssqlcreatefunction:
844                            case sstcreatetrigger:
845                            case sstmssqlalterprocedure:
846                            case sstmssqlalterfunction:
847                            case sstmssqlaltertrigger: {
848                                gcurrentsqlstatement.sourcetokenlist.add(ast);
849                                gst = EFindSqlStateType.ststoredprocedure;
850                                break;
851                            }
852                            case sstmssqlbegintry:
853                            case sstmssqlbegincatch: {
854                                gcurrentsqlstatement.sourcetokenlist.add(ast);
855                                gst = EFindSqlStateType.sttrycatch;
856                                lctrycatchlevel = 0;
857                                break;
858                            }
859                            case sstmssqlgo: {
860                                gcurrentsqlstatement.sourcetokenlist.add(ast);
861                                onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
862                                gst = EFindSqlStateType.stnormal;
863                                break;
864                            }
865                            default: {
866                                gcurrentsqlstatement.sourcetokenlist.add(ast);
867                                gst = EFindSqlStateType.stsql;
868                                break;
869                            }
870                        }
871                    } else {
872                        if (ast.tokencode == TBaseType.rrw_begin) {
873                            gcurrentsqlstatement = new TMssqlBlock(vendor);
874                            gcurrentsqlstatement.sqlstatementtype = ESqlStatementType.sstmssqlblock;
875                            gcurrentsqlstatement.sourcetokenlist.add(ast);
876                            gst = EFindSqlStateType.stblock;
877                        } else {
878                            if (sqlstatements.size() == 0) {
879                                //first statement of mssql batch, treat it as exec sp
880                                gst = EFindSqlStateType.stsql;
881                                gcurrentsqlstatement = new TMssqlExecute(vendor);
882                                gcurrentsqlstatement.sourcetokenlist.add(ast);
883                            } else if (sqlstatements.get(sqlstatements.size() - 1).sqlstatementtype == ESqlStatementType.sstmssqlgo) {
884                                // prev sql is go, treat it as exec sp
885                                gst = EFindSqlStateType.stsql;
886                                gcurrentsqlstatement = new TMssqlExecute(vendor);
887                                gcurrentsqlstatement.sourcetokenlist.add(ast);
888                            }
889                        }
890                    }
891
892                    if (!TBaseType.assigned(gcurrentsqlstatement)) //error tokentext found
893                    {
894                        this.syntaxErrors.add(new TSyntaxError(ast.getAstext(), ast.lineNo, (ast.columnNo < 0 ? 0 : ast.columnNo)
895                                , "Error when tokenlize", EErrorType.spwarning, TBaseType.MSG_WARNING_ERROR_WHEN_TOKENIZE, null, ast.posinlist));
896
897                        ast.tokentype = ETokenType.tttokenlizererrortoken;
898                        gst = EFindSqlStateType.sterror;
899
900                        gcurrentsqlstatement = new TUnknownSqlStatement(vendor);
901                        gcurrentsqlstatement.sqlstatementtype = ESqlStatementType.sstinvalid;
902                        gcurrentsqlstatement.sourcetokenlist.add(ast);
903                    }
904                    break;
905                }
906                case stblock: {
907                    if (TBaseType.assigned(lcnextsqlstatement)) {
908                        if (lcnextsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlgo) {
909                            onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
910                            gcurrentsqlstatement = lcnextsqlstatement;
911                            gcurrentsqlstatement.sourcetokenlist.add(ast);
912                            onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
913                            gst = EFindSqlStateType.stnormal;
914                        } else {
915                            lcnextsqlstatement = null;
916                        }
917                    }
918
919                    if (gst == EFindSqlStateType.stblock) {
920                        gcurrentsqlstatement.sourcetokenlist.add(ast);
921                        if (ast.tokencode == TBaseType.rrw_begin) {
922                            lcnextsolidtoken = sourcetokenlist.nextsolidtoken(i, 1, false);
923                            if (TBaseType.assigned(lcnextsolidtoken)) {
924                                if (!((lcnextsolidtoken.tokencode == TBaseType.rrw_sqlserver_tran)
925                                        || (lcnextsolidtoken.tokencode == TBaseType.rrw_sqlserver_transaction)
926                                        || (lcnextsolidtoken.tokencode == TBaseType.rrw_sqlserver_distributed)
927                                        || (lcnextsolidtoken.tokencode == TBaseType.rrw_sqlserver_dialog)
928                                        || (lcnextsolidtoken.tokencode == TBaseType.rrw_sqlserver_conversation)
929                                ))
930                                    lcblocklevel++;
931                            } else
932                                lcblocklevel++;
933
934                        } else if (ast.tokencode == TBaseType.rrw_case)
935                            lcblocklevel++;
936                        else if (ast.tokencode == TBaseType.rrw_end) {
937
938                            lcisendconversation = false;
939
940                            lcnextsolidtoken = sourcetokenlist.nextsolidtoken(i, 1, false);
941                            if (TBaseType.assigned(lcnextsolidtoken)) {
942                                if (lcnextsolidtoken.tokencode == TBaseType.rrw_sqlserver_conversation)
943                                    lcisendconversation = true;
944                            }
945
946                            if (!lcisendconversation) {
947
948                                if (lcblocklevel == 0) {
949                                    if (gcurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlif) {
950                                        if (TBaseType.assigned(lcnextsolidtoken)) {
951                                            if (lcnextsolidtoken.tokencode == TBaseType.rrw_else) {
952                                                gst = EFindSqlStateType.stsql;
953                                            } else if (lcnextsolidtoken.tokentype == ETokenType.ttsemicolon) {
954                                                lcnnextsolidtoken = sourcetokenlist.nextsolidtoken(lcnextsolidtoken.posinlist, 1, false);
955                                                if (TBaseType.assigned(lcnnextsolidtoken)) {
956                                                    if (lcnnextsolidtoken.tokencode == TBaseType.rrw_else) {
957                                                        gst = EFindSqlStateType.stsql;
958                                                    }
959                                                }
960                                            }
961                                        }
962                                    }
963
964                                    if (gst != EFindSqlStateType.stsql) {
965                                        onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
966                                        gst = EFindSqlStateType.stnormal;
967                                    }
968
969                                } else {
970                                    lcblocklevel--;
971                                }
972
973                            }
974                        }
975                    }
976                    break;
977                }
978                case sttrycatch: {
979
980                    if (TBaseType.assigned(lcnextsqlstatement)) {
981                        if (lcnextsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlgo) {
982                            onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
983                            gcurrentsqlstatement = lcnextsqlstatement;
984                            gcurrentsqlstatement.sourcetokenlist.add(ast);
985                            onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
986                            gst = EFindSqlStateType.stnormal;
987                        } else {
988                            if (
989                                    (lcnextsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlbegintry)
990                                            || (lcnextsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlbegincatch)
991                            )
992                                lctrycatchlevel++;
993                            lcnextsqlstatement = null;
994                        }
995                    }
996
997                    if (gst == EFindSqlStateType.sttrycatch) {
998                        gcurrentsqlstatement.sourcetokenlist.add(ast);
999                        if ((ast.tokencode == TBaseType.rrw_try) ||
1000                                (ast.tokencode == TBaseType.rrw_catch)) {
1001                            if (TBaseType.assigned(lcprevsolidtoken)) {
1002                                if (lcprevsolidtoken.tokencode == TBaseType.rrw_end) {
1003                                    if (lctrycatchlevel == 0) {
1004                                        onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
1005                                        gst = EFindSqlStateType.stnormal;
1006                                    } else
1007                                        lctrycatchlevel--;
1008                                }
1009                            }
1010                        }
1011                    }
1012                    break;
1013                }
1014                case stprocedureWithReturn: {
1015                    // found return statement in create procedure/function
1016
1017                    if (TBaseType.assigned(lcnextsqlstatement)) {
1018                        if (lcnextsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlgo) {
1019                            onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
1020                            gcurrentsqlstatement = lcnextsqlstatement;
1021                            gcurrentsqlstatement.sourcetokenlist.add(ast);
1022                            onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
1023                            gst = EFindSqlStateType.stnormal;
1024                            break;
1025                        }
1026                    }
1027
1028                    gcurrentsqlstatement.sourcetokenlist.add(ast);
1029                    if ((gst == EFindSqlStateType.stprocedureWithReturn) && (ast.tokentype == ETokenType.ttsemicolon)) {
1030                        onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
1031                        gst = EFindSqlStateType.stnormal;
1032                    }
1033                    break;
1034                }
1035                case stsql: {
1036                    if ((ast.tokentype == ETokenType.ttsemicolon)) {
1037                        lcstillinsql = false;
1038                        if (gcurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlif) {
1039                            lcnextsolidtoken = sourcetokenlist.nextsolidtoken(i, 1, false);
1040                            if (TBaseType.assigned(lcnextsolidtoken)) {
1041                                if (lcnextsolidtoken.tokencode == TBaseType.rrw_else) {
1042                                    // if ( expr stmt; else
1043                                    gcurrentsqlstatement.sourcetokenlist.add(ast);
1044                                    lcstillinsql = true;
1045                                }
1046
1047                            }
1048                        }
1049
1050                        if (!lcstillinsql) {
1051                            gst = EFindSqlStateType.stnormal;
1052                            gcurrentsqlstatement.sourcetokenlist.add(ast);
1053                            gcurrentsqlstatement.semicolonended = ast;
1054                            onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
1055                        }
1056
1057                    } else if (TBaseType.assigned(lcnextsqlstatement)) {
1058
1059                        if (lcnextsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlgo) {
1060                            onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
1061                            gcurrentsqlstatement = lcnextsqlstatement;
1062                            gcurrentsqlstatement.sourcetokenlist.add(ast);
1063                            onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
1064                            gst = EFindSqlStateType.stnormal;
1065                            continue;
1066                        }
1067
1068                        switch (gcurrentsqlstatement.sqlstatementtype) {
1069                            case sstmssqlif:
1070                            case sstmssqlwhile: {
1071                                if ((lcnextsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlbegincatch)
1072                                        || (lcnextsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlbegintry)) {
1073                                    gcurrentsqlstatement.sourcetokenlist.add(ast);
1074                                    gst = EFindSqlStateType.stblock;
1075                                    lcblocklevel = 1;
1076                                    lcnextsqlstatement = null;
1077                                    continue;
1078
1079                                } else if (gcurrentsqlstatement.dummytag == 1) {
1080                                    // if ( cond ^stmt nextstmt (^ stands for current pos)
1081                                    gcurrentsqlstatement.sourcetokenlist.add(ast);
1082
1083                                    if ((lcnextsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlif)
1084                                            || (lcnextsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlwhile))
1085                                        gcurrentsqlstatement.dummytag = 1;
1086                                    else
1087                                        gcurrentsqlstatement.dummytag = 0;
1088
1089                                    lcnextsqlstatement = null;
1090                                    continue;
1091                                } else {
1092                                    if (lcnextsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlif) {
1093                                        if ((ast.nextSolidToken() != null) && (ast.nextSolidToken().tokencode == TBaseType.rrw_sqlserver_exists)) {
1094                                            gcurrentsqlstatement.sourcetokenlist.add(ast);
1095                                            lcnextsqlstatement = null;
1096                                            continue;
1097                                        }
1098                                    }
1099                                }
1100                                break;
1101                            }
1102                            case sstmssqlalterqueue: {
1103                                if (lcnextsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlexec) {
1104                                    // execute can't be used to delimite alter queue
1105                                    gcurrentsqlstatement.sourcetokenlist.add(ast);
1106                                    lcnextsqlstatement = null;
1107                                    continue;
1108
1109                                }
1110                                break;
1111                            }
1112                            case sstmssqlcreateschema: {
1113                                gcurrentsqlstatement.sourcetokenlist.add(ast);
1114                                lcnextsqlstatement = null;
1115                                continue;
1116                            }
1117                        }
1118
1119                        onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
1120                        gcurrentsqlstatement = lcnextsqlstatement;
1121                        gcurrentsqlstatement.sourcetokenlist.add(ast);
1122
1123                        switch (gcurrentsqlstatement.sqlstatementtype) {
1124                            case sstmssqlcreateprocedure:
1125                            case sstmssqlcreatefunction:
1126                            case sstcreatetrigger:
1127                            case sstmssqlalterprocedure:
1128                            case sstmssqlalterfunction:
1129                            case sstmssqlaltertrigger: {
1130                                gst = EFindSqlStateType.ststoredprocedure;
1131                                break;
1132                            }
1133                            case sstmssqlbegintry:
1134                            case sstmssqlbegincatch: {
1135                                gst = EFindSqlStateType.sttrycatch;
1136                                lctrycatchlevel = 0;
1137                                break;
1138                            }
1139                            case sstmssqlgo: {
1140                                gst = EFindSqlStateType.stnormal;
1141                                break;
1142                            }
1143                            default: {
1144                                gst = EFindSqlStateType.stsql;
1145                                break;
1146                            }
1147                        }
1148
1149                    } else if ((ast.tokencode == TBaseType.rrw_begin)) {
1150                        if ((gcurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlif)
1151                                || (gcurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlwhile)) {
1152                            gst = EFindSqlStateType.stblock;
1153                            lcblocklevel = 0;
1154                            gcurrentsqlstatement.sourcetokenlist.add(ast);
1155                        } else if (gcurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqldeclare) {
1156                            onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
1157                            gcurrentsqlstatement = new TMssqlBlock(vendor);
1158                            gcurrentsqlstatement.sqlstatementtype = ESqlStatementType.sstmssqlblock;
1159                            gcurrentsqlstatement.sourcetokenlist.add(ast);
1160                            gst = EFindSqlStateType.stblock;
1161                        } else {
1162                            gcurrentsqlstatement.sourcetokenlist.add(ast);
1163                        }
1164                    } else if ((ast.tokencode == TBaseType.rrw_case)) {
1165                        case_end_nest++;
1166                        gcurrentsqlstatement.sourcetokenlist.add(ast);
1167                    } else if ((ast.tokencode == TBaseType.rrw_end)) {
1168                        if (case_end_nest > 0) {
1169                            case_end_nest--;
1170                        }
1171                        gcurrentsqlstatement.sourcetokenlist.add(ast);
1172                    } else if ((ast.tokencode == TBaseType.rrw_else)) {
1173                        gcurrentsqlstatement.sourcetokenlist.add(ast);
1174                        if (((gcurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlif)
1175                                || (gcurrentsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlwhile)
1176                        ) && (case_end_nest == 0))
1177                            gcurrentsqlstatement.dummytag = 1;
1178                    } else {
1179                        gcurrentsqlstatement.sourcetokenlist.add(ast);
1180                    }
1181                    break;
1182                }
1183                case ststoredprocedure: {
1184                    if (TBaseType.assigned(lcnextsqlstatement)) {
1185                        if (lcnextsqlstatement.sqlstatementtype == ESqlStatementType.sstmssqlgo) {
1186                            onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
1187                            gcurrentsqlstatement = lcnextsqlstatement;
1188                            gcurrentsqlstatement.sourcetokenlist.add(ast);
1189                            onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
1190                            gst = EFindSqlStateType.stnormal;
1191                        } else if (lcnextsqlstatement.sqlstatementtype == sstmssqlreturn) {
1192                            gst = EFindSqlStateType.stprocedureWithReturn;
1193                            gcurrentsqlstatement.sourcetokenlist.add(ast);
1194                            lcnextsqlstatement = null;
1195                        } else {
1196                            gst = EFindSqlStateType.ststoredprocedurebody;
1197                            gcurrentsqlstatement.sourcetokenlist.add(ast);
1198
1199                            lcnextsqlstatement = null;
1200                        }
1201                    }
1202
1203                    if (gst == EFindSqlStateType.ststoredprocedure) {
1204                        gcurrentsqlstatement.sourcetokenlist.add(ast);
1205                        if (ast.tokencode == TBaseType.rrw_begin) {
1206                            gst = EFindSqlStateType.stblock;
1207                        }
1208                    }
1209                    break;
1210                }
1211                case ststoredprocedurebody: {
1212                    if (TBaseType.assigned(lcnextsqlstatement)) {
1213                        switch (lcnextsqlstatement.sqlstatementtype) {
1214                            case sstmssqlgo: {
1215                                onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
1216                                gcurrentsqlstatement = lcnextsqlstatement;
1217                                gcurrentsqlstatement.sourcetokenlist.add(ast);
1218                                onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
1219                                gst = EFindSqlStateType.stnormal;
1220                                break;
1221                            }
1222                            case sstmssqlcreateprocedure:
1223                            case sstmssqlcreatefunction:
1224                            case sstcreatetrigger:
1225                            case sstmssqlalterprocedure:
1226                            case sstmssqlalterfunction:
1227                            case sstmssqlaltertrigger: {
1228                                onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
1229                                gcurrentsqlstatement = lcnextsqlstatement;
1230                                gcurrentsqlstatement.sourcetokenlist.add(ast);
1231                                gst = EFindSqlStateType.ststoredprocedure;
1232                                break;
1233                            }
1234                            case sstcreateview:
1235                            case sstcreatetable: {
1236
1237                                boolean readForNewStmt = false;
1238                                TSourceToken st1 = ast.searchToken(';', -1);
1239                                if (st1 != null) {
1240                                    TSourceToken st2 = ast.searchToken(TBaseType.rrw_end, -2);
1241                                    if (st2 != null) {
1242                                        // Verify this END is at block level 0 (not inside a nested
1243                                        // BEGIN...END or BEGIN TRY...END TRY block) by counting
1244                                        // BEGINs and ENDs in the current proc's token list.
1245                                        int blockDepth = 0;
1246                                        for (int k = 0; k < gcurrentsqlstatement.sourcetokenlist.size(); k++) {
1247                                            TSourceToken tk = gcurrentsqlstatement.sourcetokenlist.get(k);
1248                                            if (tk.tokencode == TBaseType.rrw_begin) {
1249                                                TSourceToken tkNext = tk.nextSolidToken();
1250                                                if (tkNext == null || (tkNext.tokencode != TBaseType.rrw_sqlserver_tran
1251                                                        && tkNext.tokencode != TBaseType.rrw_sqlserver_transaction
1252                                                        && tkNext.tokencode != TBaseType.rrw_sqlserver_distributed
1253                                                        && tkNext.tokencode != TBaseType.rrw_sqlserver_dialog
1254                                                        && tkNext.tokencode != TBaseType.rrw_sqlserver_conversation)) {
1255                                                    blockDepth++;
1256                                                }
1257                                            } else if (tk.tokencode == TBaseType.rrw_end) {
1258                                                TSourceToken tkNext = tk.nextSolidToken();
1259                                                if (tkNext == null || tkNext.tokencode != TBaseType.rrw_sqlserver_conversation) {
1260                                                    blockDepth--;
1261                                                }
1262                                            }
1263                                        }
1264                                        // blockDepth == 0 means all BEGIN/END pairs are balanced
1265                                        // so the END; is at the top level (proc boundary)
1266                                        if (blockDepth == 0) {
1267                                            readForNewStmt = true;
1268                                        }
1269                                    }
1270                                }
1271
1272                                if (readForNewStmt) {
1273                                    onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
1274                                    gcurrentsqlstatement = lcnextsqlstatement;
1275                                    gcurrentsqlstatement.sourcetokenlist.add(ast);
1276                                    gst = EFindSqlStateType.stsql;
1277                                } else {
1278                                    lcnextsqlstatement = null;
1279                                }
1280                                break;
1281                            }
1282                            case sstmssqlDropSecurityPolicy:
1283                            case sstmssqlAlterSecurityPolicy:
1284                            case sstmssqlCreateSecurityPolicy:
1285                                onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, false, builder);
1286                                gcurrentsqlstatement = lcnextsqlstatement;
1287                                gcurrentsqlstatement.sourcetokenlist.add(ast);
1288                                gst = EFindSqlStateType.stsql;
1289
1290                                break;
1291                            default: {
1292                                lcnextsqlstatement = null;
1293                                break;
1294                            }
1295                        }
1296                    }
1297
1298                    if (gst == EFindSqlStateType.ststoredprocedurebody)
1299                        gcurrentsqlstatement.sourcetokenlist.add(ast);
1300                }
1301                break;
1302            }
1303        }
1304
1305        //last statement
1306        if (TBaseType.assigned(gcurrentsqlstatement) && (gst != EFindSqlStateType.stnormal)) {
1307            onRawStatementComplete(parserContext, gcurrentsqlstatement, fparser, null, sqlstatements, true, builder);
1308        }
1309
1310        // Set results in builder
1311        builder.sqlStatements(this.sqlstatements);
1312        builder.errorCode(errorcount);
1313        builder.errorMessage(errorcount == 0 ? "" : String.format("Extraction completed with %d error(s)", errorcount));
1314    }
1315}