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