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