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