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 * MySQL 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 MySQL-specific logic incrementally</li> 016 * </ul> 017 * 018 * @see SqlParser 019 * @see SqlParserFactory 020 * @since 3.2.0.0 021 */ 022public class MySqlParser implements SqlParser { 023 024 private final EDbVendor vendor = EDbVendor.dbvmysql; 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 TGSqlParser legacyParser = new TGSqlParser(vendor); 046 transferContext(legacyParser, context); 047 048 // Exactly ONE pass over the input. This used to tokenize and then 049 // parse (or raw-split) the same legacy parser, which reads the 050 // source twice -- fatal for a stream, which is not repeatable: the 051 // second pass saw EOF and reported success with zero statements. 052 long opStart = System.currentTimeMillis(); 053 int result; 054 String failurePrefix; 055 if (fullParse) { 056 result = legacyParser.parse(); 057 failurePrefix = "Parsing failed: "; 058 resultBuilder.parsingTimeMs(System.currentTimeMillis() - opStart); 059 } else { 060 result = legacyParser.getrawsqlstatements(); 061 failurePrefix = "Tokenization failed: "; 062 resultBuilder.tokenizationTimeMs(System.currentTimeMillis() - opStart); 063 } 064 if (result != 0) { 065 resultBuilder.errorCode(result); 066 String legacyMessage = legacyParser.getErrormessage(); 067 // Keep a refusal message verbatim so callers matching on it 068 // still recognise it; only decorate ordinary failures. 069 resultBuilder.errorMessage(TBaseType.isTrialSizeMessage(legacyMessage) 070 ? legacyMessage : failurePrefix + legacyMessage); 071 } else { 072 resultBuilder.errorCode(0); 073 resultBuilder.errorMessage(""); 074 } 075 076 resultBuilder.sourceTokenList(legacyParser.sourcetokenlist); 077 resultBuilder.sqlStatements(legacyParser.sqlstatements); 078 resultBuilder.lexer(legacyParser.getFlexer()); 079 080 } catch (Exception e) { 081 resultBuilder.errorCode(1); 082 resultBuilder.errorMessage("Operation failed: " + e.getMessage()); 083 } 084 085 return resultBuilder.build(); 086 } 087 088 private void transferContext(TGSqlParser legacyParser, ParserContext context) { 089 if (context.getSqlText() != null && !context.getSqlText().isEmpty()) { 090 legacyParser.sqltext = context.getSqlText(); 091 } 092 if (context.getSqlFilename() != null && !context.getSqlFilename().isEmpty()) { 093 legacyParser.sqlfilename = context.getSqlFilename(); 094 } 095 if (context.getSqlCharset() != null && !context.getSqlCharset().isEmpty()) { 096 legacyParser.setSqlCharset(context.getSqlCharset()); 097 } 098 if (context.getSqlInputStream() != null) { 099 // Forward the stream itself: BOM and encoding detection belong to 100 // readsql(), not here. Safe now that this adapter makes exactly one 101 // pass over the source. Without this the stream was dropped and the 102 // adapter silently parsed EMPTY input in every edition. 103 legacyParser.setSqlInputStream(context.getSqlInputStream()); 104 } 105 106 legacyParser.setEnablePartialParsing(context.isEnablePartialParsing()); 107 legacyParser.setSinglePLBlock(context.isSinglePLBlock()); 108 legacyParser.setOnlyNeedRawParseTree(context.isOnlyNeedRawParseTree()); 109 110 if (context.getTeradataUtilityType() != null) { 111 legacyParser.setTeradataUtilityType(context.getTeradataUtilityType()); 112 } 113 if (context.getMetaDatabase() != null) { 114 legacyParser.setMetaDatabase(context.getMetaDatabase()); 115 } 116 if (context.getSqlEnv() != null) { 117 legacyParser.setSqlEnv(context.getSqlEnv()); 118 } 119 if (context.isDumpResolverLog()) { 120 TBaseType.DUMP_RESOLVER_LOG_TO_CONSOLE = true; 121 } 122 } 123 124 @Override 125 public String toString() { 126 return "MySqlParser{vendor=" + vendor + "}"; 127 } 128}