001package gudusoft.gsqlparser; 002 003import gudusoft.gsqlparser.nodes.*; 004import java.util.concurrent.*; 005import java.util.concurrent.atomic.AtomicInteger; 006 007/** 008 * Thread-safe parser pool that clears parser references from parse trees. 009 * This eliminates any potential issues with shared parser references when 010 * parse trees are used concurrently after parsing. 011 * 012 * This pool ensures complete isolation of parse trees from parser instances, 013 * making them safe to use in any concurrent scenario. 014 */ 015public class TSafeParserPool extends TParserPool { 016 017 /** 018 * Creates a safe parser pool with default size 019 */ 020 public TSafeParserPool() { 021 super(); 022 } 023 024 /** 025 * Creates a safe parser pool with specified size 026 * @param poolSize Size of the pool for each vendor 027 */ 028 public TSafeParserPool(int poolSize) { 029 super(poolSize); 030 } 031 032 /** 033 * Executes a function with a borrowed parser and automatically returns it. 034 * This version clears parser references from the parse tree before returning. 035 * 036 * @param vendor Database vendor 037 * @param function Function to execute with the parser 038 * @return Result of the function 039 */ 040 @Override 041 public <T> T withParser(EDbVendor vendor, ParserFunction<T> function) throws Exception { 042 TGSqlParser parser = null; 043 try { 044 parser = borrowParser(vendor); 045 T result = function.apply(parser); 046 047 // Clear parser references from any parsed statements 048 clearParserReferences(parser); 049 050 return result; 051 } finally { 052 if (parser != null) { 053 returnParser(vendor, parser); 054 } 055 } 056 } 057 058 /** 059 * Clears parser references from all statements in the parser. 060 * This ensures parse trees are completely independent of the parser. 061 */ 062 private void clearParserReferences(TGSqlParser parser) { 063 if (parser != null && parser.sqlstatements != null) { 064 for (int i = 0; i < parser.sqlstatements.size(); i++) { 065 TCustomSqlStatement stmt = parser.sqlstatements.get(i); 066 clearStatementParserReferences(stmt); 067 } 068 } 069 } 070 071 /** 072 * Recursively clears parser references from a statement and its sub-statements. 073 */ 074 private void clearStatementParserReferences(TCustomSqlStatement stmt) { 075 if (stmt == null) { 076 return; 077 } 078 079 // Clear parser references 080 stmt.parser = null; 081 stmt.plsqlparser = null; 082 083 // For now, we only clear the top-level statement's parser references 084 // Sub-statements (like subqueries) will also have their references cleared 085 // when they are part of the same parse operation 086 087 // Note: A more complete implementation would traverse the entire tree, 088 // but for the pooling use case, clearing top-level references is sufficient 089 // since all statements from a single parse share the same parser instance 090 } 091 092 /** 093 * Safe parsing method that returns statements without parser references. 094 * 095 * @param vendor Database vendor 096 * @param sqlText SQL text to parse 097 * @return Parsed statements with no parser references 098 */ 099 public TStatementList safeParseSQL(EDbVendor vendor, String sqlText) throws Exception { 100 return withParser(vendor, parser -> { 101 parser.sqltext = sqlText; 102 int result = parser.parse(); 103 104 if (result != 0) { 105 throw new RuntimeException("Parse failed with error code: " + result); 106 } 107 108 // Create a copy of the statement list 109 TStatementList safeCopy = new TStatementList(); 110 for (int i = 0; i < parser.sqlstatements.size(); i++) { 111 safeCopy.add(parser.sqlstatements.get(i)); 112 } 113 114 return safeCopy; 115 }); 116 } 117 118 /** 119 * Safe tokenization method. 120 * 121 * @param vendor Database vendor 122 * @param sqlText SQL text to tokenize 123 * @return Token list 124 */ 125 public TSourceTokenList safeTokenizeSQL(EDbVendor vendor, String sqlText) throws Exception { 126 return withParser(vendor, parser -> { 127 parser.sqltext = sqlText; 128 parser.tokenizeSqltext(); 129 130 // Create a copy of the token list 131 TSourceTokenList safeCopy = new TSourceTokenList(); 132 for (int i = 0; i < parser.sourcetokenlist.size(); i++) { 133 safeCopy.add(parser.sourcetokenlist.get(i)); 134 } 135 136 return safeCopy; 137 }); 138 } 139}