001package gudusoft.gsqlparser.parser; 002 003import gudusoft.gsqlparser.EDbVendor; 004import gudusoft.gsqlparser.TBaseType; 005import gudusoft.gsqlparser.TGSqlParser; 006import gudusoft.gsqlparser.TSourceTokenList; 007import gudusoft.gsqlparser.TStatementList; 008 009/** 010 * Trino SQL parser implementation. 011 * 012 * <p><b>Implementation Status:</b> DELEGATION PHASE 013 * <ul> 014 * <li><b>Current:</b> Delegates to legacy TGSqlParser</li> 015 * <li><b>Future:</b> Will extract Trino-specific logic incrementally</li> 016 * </ul> 017 * 018 * @see SqlParser 019 * @see SqlParserFactory 020 * @since 3.2.0.0 021 */ 022public class TrinoSqlParser implements SqlParser { 023 024 private final EDbVendor vendor = EDbVendor.dbvtrino; 025 026 @Override 027 public EDbVendor getVendor() { 028 return vendor; 029 } 030 031 @Override 032 public SqlParseResult parse(ParserContext context) { 033 return delegateToLegacy(context, true); 034 } 035 036 @Override 037 public SqlParseResult tokenize(ParserContext context) { 038 return delegateToLegacy(context, false); 039 } 040 041 private SqlParseResult delegateToLegacy(ParserContext context, boolean fullParse) { 042 SqlParseResult.Builder resultBuilder = new SqlParseResult.Builder(); 043 044 try { 045 // CRITICAL: Use dbvpresto (not dbvtrino) to avoid infinite recursion. 046 // TGSqlParser(dbvtrino) -> TrinoSqlParser.tokenize() -> delegateToLegacy() 047 // would create another TGSqlParser(dbvtrino), causing StackOverflowError. 048 // Presto has the actual parser implementation that Trino delegates to. 049 TGSqlParser legacyParser = new TGSqlParser(EDbVendor.dbvpresto); 050 transferContext(legacyParser, context); 051 052 // Exactly ONE pass over the input. This used to tokenize and then 053 // parse (or raw-split) the same legacy parser, which reads the 054 // source twice -- fatal for a stream, which is not repeatable: the 055 // second pass saw EOF and reported success with zero statements. 056 long opStart = System.currentTimeMillis(); 057 int result; 058 String failurePrefix; 059 if (fullParse) { 060 result = legacyParser.parse(); 061 failurePrefix = "Parsing failed: "; 062 resultBuilder.parsingTimeMs(System.currentTimeMillis() - opStart); 063 } else { 064 result = legacyParser.getrawsqlstatements(); 065 failurePrefix = "Tokenization failed: "; 066 resultBuilder.tokenizationTimeMs(System.currentTimeMillis() - opStart); 067 } 068 if (result != 0) { 069 resultBuilder.errorCode(result); 070 String legacyMessage = legacyParser.getErrormessage(); 071 // Keep a refusal message verbatim so callers matching on it 072 // still recognise it; only decorate ordinary failures. 073 resultBuilder.errorMessage(TBaseType.isTrialSizeMessage(legacyMessage) 074 ? legacyMessage : failurePrefix + legacyMessage); 075 } else { 076 resultBuilder.errorCode(0); 077 resultBuilder.errorMessage(""); 078 } 079 080 resultBuilder.sourceTokenList(legacyParser.sourcetokenlist); 081 resultBuilder.sqlStatements(legacyParser.sqlstatements); 082 resultBuilder.lexer(legacyParser.getFlexer()); 083 084 } catch (Exception e) { 085 resultBuilder.errorCode(1); 086 resultBuilder.errorMessage("Operation failed: " + e.getMessage()); 087 } 088 089 return resultBuilder.build(); 090 } 091 092 private void transferContext(TGSqlParser legacyParser, ParserContext context) { 093 if (context.getSqlText() != null && !context.getSqlText().isEmpty()) { 094 legacyParser.sqltext = context.getSqlText(); 095 } 096 if (context.getSqlFilename() != null && !context.getSqlFilename().isEmpty()) { 097 legacyParser.sqlfilename = context.getSqlFilename(); 098 } 099 if (context.getSqlCharset() != null && !context.getSqlCharset().isEmpty()) { 100 legacyParser.setSqlCharset(context.getSqlCharset()); 101 } 102 if (context.getSqlInputStream() != null) { 103 // Forward the stream itself: BOM and encoding detection belong to 104 // readsql(), not here. Safe now that this adapter makes exactly one 105 // pass over the source. Without this the stream was dropped and the 106 // adapter silently parsed EMPTY input in every edition. 107 legacyParser.setSqlInputStream(context.getSqlInputStream()); 108 } 109 110 legacyParser.setEnablePartialParsing(context.isEnablePartialParsing()); 111 legacyParser.setSinglePLBlock(context.isSinglePLBlock()); 112 legacyParser.setOnlyNeedRawParseTree(context.isOnlyNeedRawParseTree()); 113 114 if (context.getTeradataUtilityType() != null) { 115 legacyParser.setTeradataUtilityType(context.getTeradataUtilityType()); 116 } 117 if (context.getMetaDatabase() != null) { 118 legacyParser.setMetaDatabase(context.getMetaDatabase()); 119 } 120 if (context.getSqlEnv() != null) { 121 legacyParser.setSqlEnv(context.getSqlEnv()); 122 } 123 if (context.isDumpResolverLog()) { 124 TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE = true; 125 } 126 } 127 128 @Override 129 public String toString() { 130 return "TrinoSqlParser{vendor=" + vendor + "}"; 131 } 132}