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