001package gudusoft.gsqlparser.parser; 002 003import gudusoft.gsqlparser.EDbVendor; 004import gudusoft.gsqlparser.EErrorType; 005import gudusoft.gsqlparser.EFindSqlStateType; 006import gudusoft.gsqlparser.ESqlStatementType; 007import gudusoft.gsqlparser.ETokenStatus; 008import gudusoft.gsqlparser.ETokenType; 009import gudusoft.gsqlparser.TBaseType; 010import gudusoft.gsqlparser.TCustomLexer; 011import gudusoft.gsqlparser.TCustomParser; 012import gudusoft.gsqlparser.TCustomSqlStatement; 013import gudusoft.gsqlparser.TLexerAthena; 014import gudusoft.gsqlparser.TParserAthena; 015import gudusoft.gsqlparser.TSourceToken; 016import gudusoft.gsqlparser.TSourceTokenList; 017import gudusoft.gsqlparser.TStatementList; 018import gudusoft.gsqlparser.TSyntaxError; 019import gudusoft.gsqlparser.compiler.TASTEvaluator; 020import gudusoft.gsqlparser.compiler.TContext; 021import gudusoft.gsqlparser.compiler.TFrame; 022import gudusoft.gsqlparser.compiler.TGlobalScope; 023import gudusoft.gsqlparser.resolver.TSQLResolver; 024import gudusoft.gsqlparser.sqlcmds.ISqlCmds; 025import gudusoft.gsqlparser.sqlcmds.SqlCmdsFactory; 026import gudusoft.gsqlparser.sqlenv.TSQLEnv; 027import gudusoft.gsqlparser.stmt.TCreateTableSqlStatement; 028import gudusoft.gsqlparser.stmt.TUnknownSqlStatement; 029import gudusoft.gsqlparser.stmt.oracle.TSqlplusCmdStatement; 030 031import java.util.Stack; 032 033/** 034 * AWS Athena SQL parser implementation. 035 * 036 * <p>This parser handles Athena-specific SQL syntax including: 037 * <ul> 038 * <li>Presto/Trino-based SQL syntax</li> 039 * <li>MySQL-style comment handling</li> 040 * <li>PL/SQL-like procedural blocks (BEGIN/END)</li> 041 * <li>Dynamic delimiter support</li> 042 * </ul> 043 * 044 * <p><b>Implementation Status:</b> MIGRATED 045 * <ul> 046 * <li><b>Completed:</b> Migrated from TGSqlParser to AbstractSqlParser</li> 047 * <li><b>Current:</b> Fully self-contained Athena parser</li> 048 * </ul> 049 * 050 * <p><b>Design Notes:</b> 051 * <ul> 052 * <li>Extends {@link AbstractSqlParser} using template method pattern</li> 053 * <li>Uses single parser: {@link TParserAthena}</li> 054 * <li>Primary delimiter: semicolon (;)</li> 055 * <li>Supports PL/SQL-like blocks with BEGIN/END</li> 056 * </ul> 057 * 058 * @see SqlParser 059 * @see AbstractSqlParser 060 * @see TLexerAthena 061 * @see TParserAthena 062 * @since 3.2.0.0 063 */ 064public class AthenaSqlParser extends AbstractSqlParser { 065 066 // ========== Lexer and Parser ========== 067 068 /** The Athena lexer used for tokenization (note: lowercase 'a' in generated class name) */ 069 public TLexerAthena flexer; 070 071 /** The Athena parser used for parsing */ 072 private TParserAthena fparser; 073 074 // ========== Statement Extraction State ========== 075 076 /** Current statement being built during raw extraction */ 077 private TCustomSqlStatement gcurrentsqlstatement; 078 079 /** 080 * Construct Athena SQL parser. 081 * <p> 082 * Configures the parser for Athena database with semicolon (;) as the default delimiter. 083 */ 084 public AthenaSqlParser() { 085 super(EDbVendor.dbvathena); 086 this.delimiterChar = ';'; 087 this.defaultDelimiterStr = ";"; 088 089 // Create lexer once - will be reused for all parsing operations 090 this.flexer = new TLexerAthena(); 091 this.flexer.delimiterchar = this.delimiterChar; 092 this.flexer.defaultDelimiterStr = this.defaultDelimiterStr; 093 094 // Set parent's lexer reference for shared tokenization logic 095 this.lexer = this.flexer; 096 097 // Create parser once - will be reused for all parsing operations 098 this.fparser = new TParserAthena(null); 099 this.fparser.lexer = this.flexer; 100 } 101 102 // ========== Abstract Method Implementations ========== 103 104 @Override 105 protected TCustomLexer getLexer(ParserContext context) { 106 return this.flexer; 107 } 108 109 @Override 110 protected TCustomParser getParser(ParserContext context, TSourceTokenList tokens) { 111 this.fparser.sourcetokenlist = tokens; 112 return this.fparser; 113 } 114 115 @Override 116 protected TCustomParser getSecondaryParser(ParserContext context, TSourceTokenList tokens) { 117 // Athena does not have a secondary parser 118 return null; 119 } 120 121 // ========== Phase 2: Tokenization (Hook Pattern) ========== 122 123 @Override 124 protected void tokenizeVendorSql() { 125 doathenatexttotokenlist(); 126 } 127 128 /** 129 * Tokenize Athena SQL text into source tokens. 130 * <p> 131 * Migrated from TGSqlParser.doathenatexttotokenlist() (lines 4664-4694). 132 * <p> 133 * Handles: 134 * <ul> 135 * <li>MySQL-style comment validation</li> 136 * <li>Dynamic delimiter changes (like MySQL DELIMITER command)</li> 137 * </ul> 138 */ 139 private void doathenatexttotokenlist() { 140 TSourceToken asourcetoken; 141 int yychar; 142 boolean startDelimiter = false; 143 144 flexer.tmpDelimiter = ""; 145 146 asourcetoken = getanewsourcetoken(); 147 if (asourcetoken == null) return; 148 yychar = asourcetoken.tokencode; 149 150 while (yychar > 0) { 151 sourcetokenlist.add(asourcetoken); 152 asourcetoken = getanewsourcetoken(); 153 if (asourcetoken == null) break; 154 checkMySQLCommentToken(asourcetoken); 155 156 if ((asourcetoken.tokencode == TBaseType.lexnewline) && (startDelimiter)) { 157 startDelimiter = false; 158 flexer.tmpDelimiter = sourcetokenlist.get(sourcetokenlist.size() - 1).getAstext(); 159 } 160 161 yychar = asourcetoken.tokencode; 162 } 163 } 164 165 /** 166 * Check MySQL-style comment tokens. 167 * <p> 168 * Migrated from TGSqlParser.checkMySQLCommentToken() (lines 4604-4619). 169 * <p> 170 * Note: This method is mostly a no-op as the validation logic is commented out 171 * in the original implementation. 172 */ 173 private void checkMySQLCommentToken(TSourceToken cmtToken) { 174 // Original implementation is commented out - keeping as no-op 175 // This matches the original TGSqlParser behavior 176 } 177 178 // ========== Phase 3: Raw Statement Extraction (Hook Pattern) ========== 179 180 @Override 181 protected void setupVendorParsersForExtraction() { 182 this.fparser.sqlcmds = this.sqlcmds; 183 this.fparser.sourcetokenlist = this.sourcetokenlist; 184 } 185 186 @Override 187 protected void extractVendorRawStatements(SqlParseResult.Builder builder) { 188 doathenagetrawsqlstatements(builder); 189 } 190 191 /** 192 * Extract raw SQL statements from token list. 193 * <p> 194 * Migrated from TGSqlParser.doathenagetrawsqlstatements() (lines 6724+). 195 * <p> 196 * This method implements a state machine to identify statement boundaries: 197 * <ul> 198 * <li>Regular SQL statements terminated by semicolon</li> 199 * <li>PL/SQL-like blocks with BEGIN/END pairs</li> 200 * <li>Slash (/) and period (.) terminators for procedural blocks</li> 201 * </ul> 202 */ 203 private void doathenagetrawsqlstatements(SqlParseResult.Builder builder) { 204 int waitingEnd = 0; 205 boolean foundEnd = false; 206 207 if (TBaseType.assigned(sqlstatements)) sqlstatements.clear(); 208 if (!TBaseType.assigned(sourcetokenlist)) { 209 builder.errorCode(-1); 210 return; 211 } 212 213 gcurrentsqlstatement = null; 214 EFindSqlStateType gst = EFindSqlStateType.stnormal; 215 TSourceToken lcprevsolidtoken = null; 216 TSourceToken ast = null; 217 218 for (int i = 0; i < sourcetokenlist.size(); i++) { 219 220 if ((ast != null) && (ast.issolidtoken())) 221 lcprevsolidtoken = ast; 222 223 ast = sourcetokenlist.get(i); 224 sourcetokenlist.curpos = i; 225 226 switch (gst) { 227 case sterror: { 228 if (ast.tokentype == ETokenType.ttsemicolon) { 229 appendToken(gcurrentsqlstatement, ast); 230 onRawStatementComplete(this.parserContext, gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder); 231 gst = EFindSqlStateType.stnormal; 232 } else { 233 appendToken(gcurrentsqlstatement, ast); 234 } 235 break; 236 } // sterror 237 238 case stnormal: { 239 if ((ast.tokencode == TBaseType.cmtdoublehyphen) 240 || (ast.tokencode == TBaseType.cmtslashstar) 241 || (ast.tokencode == TBaseType.lexspace) 242 || (ast.tokencode == TBaseType.lexnewline) 243 || (ast.tokentype == ETokenType.ttsemicolon)) { 244 if (gcurrentsqlstatement != null) { 245 appendToken(gcurrentsqlstatement, ast); 246 } 247 248 if ((lcprevsolidtoken != null) && (ast.tokentype == ETokenType.ttsemicolon)) { 249 if (lcprevsolidtoken.tokentype == ETokenType.ttsemicolon) { 250 // ;;;; continuous semicolon, treat it as comment 251 ast.tokentype = ETokenType.ttsimplecomment; 252 ast.tokencode = TBaseType.cmtdoublehyphen; 253 } 254 } 255 256 continue; 257 } 258 259 // find a tokentext to start sql or plsql mode 260 gcurrentsqlstatement = sqlcmds.issql(ast, gst, gcurrentsqlstatement); 261 262 if (gcurrentsqlstatement != null) { 263 if (gcurrentsqlstatement.isathenaplsql()) { 264 gst = EFindSqlStateType.ststoredprocedure; 265 appendToken(gcurrentsqlstatement, ast); 266 foundEnd = false; 267 if ((ast.tokencode == TBaseType.rrw_begin) 268 || (ast.tokencode == TBaseType.rrw_package) 269 || (ast.searchToken(TBaseType.rrw_package, 4) != null)) { 270 waitingEnd = 1; 271 } 272 } else { 273 gst = EFindSqlStateType.stsql; 274 appendToken(gcurrentsqlstatement, ast); 275 } 276 } else { 277 // error tokentext found 278 this.syntaxErrors.add(new TSyntaxError(ast.getAstext(), ast.lineNo, (ast.columnNo < 0 ? 0 : ast.columnNo), 279 "Error when tokenlize", EErrorType.spwarning, TBaseType.MSG_WARNING_ERROR_WHEN_TOKENIZE, null, ast.posinlist)); 280 281 ast.tokentype = ETokenType.tttokenlizererrortoken; 282 gst = EFindSqlStateType.sterror; 283 284 gcurrentsqlstatement = new TUnknownSqlStatement(vendor); 285 gcurrentsqlstatement.sqlstatementtype = ESqlStatementType.sstinvalid; 286 appendToken(gcurrentsqlstatement, ast); 287 } 288 289 break; 290 } // stnormal 291 292 case stsql: { 293 if (ast.tokentype == ETokenType.ttsemicolon) { 294 gst = EFindSqlStateType.stnormal; 295 appendToken(gcurrentsqlstatement, ast); 296 gcurrentsqlstatement.semicolonended = ast; 297 if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE) { 298 System.out.println(" [RAW] Found semicolon, completing statement. Token count=" + gcurrentsqlstatement.sourcetokenlist.size()); 299 } 300 onRawStatementComplete(this.parserContext, gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder); 301 continue; 302 } 303 304 if (sourcetokenlist.sqlplusaftercurtoken()) { // most probably is / cmd 305 gst = EFindSqlStateType.stnormal; 306 appendToken(gcurrentsqlstatement, ast); 307 onRawStatementComplete(this.parserContext, gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder); 308 continue; 309 } 310 appendToken(gcurrentsqlstatement, ast); 311 break; 312 } // case stsql 313 314 case ststoredprocedure: { 315 if (ast.tokencode == TBaseType.rrw_begin) { 316 waitingEnd++; 317 } else if (ast.tokencode == TBaseType.rrw_if) { 318 if (ast.searchToken(TBaseType.rrw_end, -1) == null) { 319 // this is not if after END 320 waitingEnd++; 321 } 322 } else if (ast.tokencode == TBaseType.rrw_case) { 323 if (ast.searchToken(TBaseType.rrw_end, -1) == null) { 324 // this is not case after END 325 waitingEnd++; 326 } 327 } else if (ast.tokencode == TBaseType.rrw_loop) { 328 if (ast.searchToken(TBaseType.rrw_end, -1) == null) { 329 // this is not loop after END 330 waitingEnd++; 331 } 332 } else if (ast.tokencode == TBaseType.rrw_end) { 333 foundEnd = true; 334 waitingEnd--; 335 if (waitingEnd < 0) { 336 waitingEnd = 0; 337 } 338 } 339 340 if ((ast.tokentype == ETokenType.ttslash) && (ast.tokencode == TBaseType.sqlpluscmd)) { 341 // Slash terminator 342 ast.tokenstatus = ETokenStatus.tsignorebyyacc; 343 gst = EFindSqlStateType.stnormal; 344 onRawStatementComplete(this.parserContext, gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder); 345 346 // make / a sqlplus cmd 347 gcurrentsqlstatement = new TSqlplusCmdStatement(vendor); 348 appendToken(gcurrentsqlstatement, ast); 349 onRawStatementComplete(this.parserContext, gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder); 350 } else if ((ast.tokentype == ETokenType.ttperiod) && (sourcetokenlist.returnaftercurtoken(false)) && (sourcetokenlist.returnbeforecurtoken(false))) { 351 // single dot at a separate line 352 ast.tokenstatus = ETokenStatus.tsignorebyyacc; 353 gst = EFindSqlStateType.stnormal; 354 onRawStatementComplete(this.parserContext, gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder); 355 356 // make ttperiod a sqlplus cmd 357 gcurrentsqlstatement = new TSqlplusCmdStatement(vendor); 358 appendToken(gcurrentsqlstatement, ast); 359 onRawStatementComplete(this.parserContext, gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder); 360 } else { 361 appendToken(gcurrentsqlstatement, ast); 362 if ((ast.tokentype == ETokenType.ttsemicolon) && (waitingEnd == 0) && (foundEnd)) { 363 gst = EFindSqlStateType.stnormal; 364 onRawStatementComplete(this.parserContext, gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder); 365 } 366 } 367 368 if (ast.tokencode == TBaseType.sqlpluscmd) { 369 // change tokencode back to keyword or ident, because sqlplus cmd 370 // in a sql statement (almost is plsql block) is not really a sqlplus cmd 371 int m = flexer.getkeywordvalue(ast.getAstext()); 372 if (m != 0) { 373 ast.tokencode = m; 374 } else { 375 ast.tokencode = TBaseType.ident; 376 } 377 } 378 379 break; 380 } // case ststoredprocedure 381 } 382 } 383 384 // Handle incomplete statement at end of file 385 if ((gcurrentsqlstatement != null) && 386 ((gst == EFindSqlStateType.stsql) || (gst == EFindSqlStateType.ststoredprocedure) || (gst == EFindSqlStateType.sterror))) { 387 if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE) { 388 System.out.println(" [RAW] Incomplete statement at EOF. Token count=" + gcurrentsqlstatement.sourcetokenlist.size()); 389 } 390 onRawStatementComplete(this.parserContext, gcurrentsqlstatement, this.fparser, null, this.sqlstatements, false, builder); 391 } 392 393 // Populate builder with extracted statements (CRITICAL: this was missing!) 394 builder.sqlStatements(this.sqlstatements); 395 builder.errorCode(0); 396 builder.errorMessage(""); 397 } 398 399 /** 400 * Helper method to append a token to a statement. 401 * <p> 402 * Sets the token's statement reference and adds it to the statement's token list. 403 */ 404 private void appendToken(TCustomSqlStatement statement, TSourceToken token) { 405 if (statement == null || token == null) { 406 return; 407 } 408 token.stmt = statement; 409 statement.sourcetokenlist.add(token); 410 } 411 412 // ========== Phase 4: Statement Parsing ========== 413 414 @Override 415 protected TStatementList performParsing(ParserContext context, TCustomParser parser, 416 TCustomParser secondaryParser, TSourceTokenList tokens, 417 TStatementList rawStatements) { 418 if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE) { 419 System.out.println("AthenaSqlParser.performParsing() CALLED with " + 420 (rawStatements != null ? rawStatements.size() : 0) + " statements"); 421 } 422 423 // Store references 424 this.fparser = (TParserAthena) parser; 425 this.sourcetokenlist = tokens; 426 this.parserContext = context; 427 428 // Initialize sqlcmds 429 this.sqlcmds = SqlCmdsFactory.get(vendor); 430 this.fparser.sqlcmds = this.sqlcmds; 431 432 // Initialize global context using inherited method 433 initializeGlobalContext(); 434 435 // Parse each statement 436 for (int i = 0; i < sqlstatements.size(); i++) { 437 TCustomSqlStatement stmt = sqlstatements.getRawSql(i); 438 try { 439 stmt.setFrameStack(frameStack); 440 int parseResult = stmt.parsestatement(null, false, context.isOnlyNeedRawParseTree()); 441 442 // Error recovery 443 boolean doRecover = TBaseType.ENABLE_ERROR_RECOVER_IN_CREATE_TABLE; 444 if (doRecover && ((parseResult != 0) || (stmt.getErrorCount() > 0))) { 445 if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE) { 446 System.out.println("AthenaSqlParser: Triggering error recovery for " + stmt.sqlstatementtype); 447 System.out.println(" parseResult=" + parseResult + ", errorCount=" + stmt.getErrorCount()); 448 if (stmt.sqlstatementtype == ESqlStatementType.sstcreatetable) { 449 TCreateTableSqlStatement ct = (TCreateTableSqlStatement)stmt; 450 System.out.println(" columns before recovery: " + ct.getColumnList().size()); 451 } 452 } 453 handleCreateTableErrorRecovery(stmt); 454 if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE && stmt.sqlstatementtype == ESqlStatementType.sstcreatetable) { 455 TCreateTableSqlStatement ct = (TCreateTableSqlStatement)stmt; 456 System.out.println(" columns after recovery: " + ct.getColumnList().size()); 457 } 458 } 459 460 // Collect errors 461 if ((parseResult != 0) || (stmt.getErrorCount() > 0)) { 462 copyErrorsFromStatement(stmt); 463 } 464 } catch (Exception ex) { 465 // Use inherited exception handler 466 handleStatementParsingException(stmt, i, ex); 467 continue; 468 } 469 } 470 471 // Clean up frame stack 472 if (globalFrame != null) { 473 globalFrame.popMeFromStack(frameStack); 474 } 475 476 // Final debug check before returning 477 if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE) { 478 System.out.println("AthenaSqlParser: total statements = " + sqlstatements.size()); 479 for (int i = 0; i < sqlstatements.size(); i++) { 480 TCustomSqlStatement stmt = sqlstatements.get(i); 481 System.out.println(" Statement[" + i + "]: " + stmt.sqlstatementtype + " @ " + System.identityHashCode(stmt)); 482 if (stmt.sqlstatementtype == ESqlStatementType.sstcreatetable) { 483 TCreateTableSqlStatement ct = (TCreateTableSqlStatement)stmt; 484 System.out.println(" columns = " + ct.getColumnList().size()); 485 } 486 } 487 } 488 489 return sqlstatements; 490 } 491 492 /** 493 * Handle CREATE TABLE error recovery. 494 * <p> 495 * Migrated from TGSqlParser.doparse() (lines 16914-16971). 496 * <p> 497 * Attempts to recover from parsing errors in CREATE TABLE statements by 498 * marking unparseable table properties as sqlpluscmd and retrying. 499 * This allows parsing CREATE TABLE statements with vendor-specific extensions 500 * that may not be in the grammar. 501 */ 502 private void handleCreateTableErrorRecovery(TCustomSqlStatement stmt) { 503 // Check if this is a CREATE TABLE or CREATE INDEX statement 504 if (!(stmt.sqlstatementtype == ESqlStatementType.sstcreatetable || 505 stmt.sqlstatementtype == ESqlStatementType.sstcreateindex)) { 506 return; 507 } 508 509 // Check if strict parsing is disabled 510 if (TBaseType.c_createTableStrictParsing) { 511 return; 512 } 513 514 TCustomSqlStatement errorSqlStatement = stmt; 515 516 int nested = 0; 517 boolean isIgnore = false; 518 boolean isFoundIgnoreToken = false; 519 TSourceToken firstIgnoreToken = null; 520 521 // Iterate through tokens to find the closing parenthesis of table definition 522 for (int k = 0; k < errorSqlStatement.sourcetokenlist.size(); k++) { 523 TSourceToken st = errorSqlStatement.sourcetokenlist.get(k); 524 525 if (isIgnore) { 526 // We're past the table definition, mark tokens as ignoreable 527 if (st.issolidtoken() && (st.tokencode != ';')) { 528 isFoundIgnoreToken = true; 529 if (firstIgnoreToken == null) { 530 firstIgnoreToken = st; 531 } 532 } 533 // Mark all tokens (except semicolon) as sqlpluscmd to ignore them 534 if (st.tokencode != ';') { 535 st.tokencode = TBaseType.sqlpluscmd; 536 } 537 continue; 538 } 539 540 // Track closing parentheses 541 if (st.tokencode == (int) ')') { 542 nested--; 543 if (nested == 0) { 544 // Found the closing parenthesis of table definition 545 // Check if next tokens are "AS ( SELECT" (CTAS pattern) 546 boolean isSelect = false; 547 TSourceToken st1 = st.searchToken(TBaseType.rrw_as, 1); 548 if (st1 != null) { 549 TSourceToken st2 = st.searchToken((int) '(', 2); 550 if (st2 != null) { 551 TSourceToken st3 = st.searchToken(TBaseType.rrw_select, 3); 552 isSelect = (st3 != null); 553 } 554 } 555 // If not a CTAS, start ignoring subsequent tokens 556 if (!isSelect) { 557 isIgnore = true; 558 } 559 } 560 } 561 562 // Track opening parentheses 563 if ((st.tokencode == (int) '(') || (st.tokencode == TBaseType.left_parenthesis_2)) { 564 nested++; 565 } 566 } 567 568 // For Oracle, validate that the first ignored token is a valid table property 569 // For Athena, we don't have this validation, so skip this check 570 // (Athena doesn't use searchOracleTablePros) 571 572 // If we found ignoreable tokens, clear errors and retry parsing 573 if (isFoundIgnoreToken) { 574 if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE) { 575 System.out.println(" Found ignoreable tokens, clearing errors and re-parsing..."); 576 } 577 errorSqlStatement.clearError(); 578 int retryResult = errorSqlStatement.parsestatement(null, false, parserContext.isOnlyNeedRawParseTree()); 579 if (TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE) { 580 System.out.println(" Retry parse result: " + retryResult); 581 if (stmt.sqlstatementtype == ESqlStatementType.sstcreatetable) { 582 TCreateTableSqlStatement ct = (TCreateTableSqlStatement)stmt; 583 System.out.println(" Final column count after retry: " + ct.getColumnList().size()); 584 } 585 } 586 } 587 } 588 589 // ========== Phase 5: Semantic Analysis & Interpretation ========== 590 591 @Override 592 protected void performSemanticAnalysis(ParserContext context, TStatementList statements) { 593 if (!TBaseType.isEnableResolver()) { 594 return; 595 } 596 597 if (getSyntaxErrors().isEmpty()) { 598 TSQLResolver resolver = new TSQLResolver(this.globalContext, statements); 599 resolver.resolve(); 600 } 601 } 602 603 @Override 604 protected void performInterpreter(ParserContext context, TStatementList statements) { 605 if (!TBaseType.ENABLE_INTERPRETER) { 606 return; 607 } 608 609 if (getSyntaxErrors().isEmpty()) { 610 TGlobalScope interpreterScope = new TGlobalScope(sqlEnv); 611 TASTEvaluator astEvaluator = new TASTEvaluator(statements, interpreterScope); 612 astEvaluator.eval(); 613 } 614 } 615 616 @Override 617 public EDbVendor getVendor() { 618 return vendor; 619 } 620 621 @Override 622 public String toString() { 623 return "AthenaSqlParser{vendor=" + vendor + "}"; 624 } 625}