001package gudusoft.gsqlparser.dlineage.util; 002 003import gudusoft.gsqlparser.EDbVendor; 004import gudusoft.gsqlparser.util.Logger; 005import gudusoft.gsqlparser.util.LoggerFactory; 006import gudusoft.gsqlparser.util.SQLUtil; 007 008import java.io.InputStream; 009import java.util.HashMap; 010import java.util.HashSet; 011import java.util.Map; 012import java.util.Set; 013 014public final class FunctionUtility { 015 private static final Logger logger = LoggerFactory.getLogger(FunctionUtility.class); 016 017 static Map<EDbVendor, Map<String, Set<Integer>>> dbFunctionDirectRelationMap = new HashMap<>(); 018 static Map<EDbVendor, Map<String, Set<Integer>>> dbFunctionIndirectRelationMap = new HashMap<>(); 019 020 public static boolean isDirectRelation(EDbVendor vendor, String function, int argCount, int argIndex) { 021 if (!dbFunctionDirectRelationMap.containsKey(vendor)) { 022 initFunctionMap(vendor); 023 } 024 Map<String, Set<Integer>> functionMap = dbFunctionDirectRelationMap.get(vendor); 025 Set<Integer> relationsArgs = functionMap.get(function.toUpperCase() + "_" + argCount); 026 if (relationsArgs != null) { 027 return relationsArgs.contains(argIndex); 028 } 029 // Variadic fallback: if any entry for this function name exists with a different 030 // arity, use the largest arity entry's pattern, defaulting to direct for overflow args. 031 String prefix = function.toUpperCase() + "_"; 032 Set<Integer> bestMatch = null; 033 int bestArity = 0; 034 for (Map.Entry<String, Set<Integer>> entry : functionMap.entrySet()) { 035 if (entry.getKey().startsWith(prefix)) { 036 int arity = Integer.parseInt(entry.getKey().substring(prefix.length())); 037 if (arity > bestArity) { 038 bestArity = arity; 039 bestMatch = entry.getValue(); 040 } 041 } 042 } 043 if (bestMatch != null) { 044 if (argIndex < bestArity) { 045 return bestMatch.contains(argIndex); 046 } 047 return true; // overflow args default to direct 048 } 049 return true; 050 } 051 052 public static boolean isIndirectRelation(EDbVendor vendor, String function, int argCount, int argIndex) { 053 if (!dbFunctionIndirectRelationMap.containsKey(vendor)) { 054 initFunctionMap(vendor); 055 } 056 Map<String, Set<Integer>> functionMap = dbFunctionIndirectRelationMap.get(vendor); 057 Set<Integer> relationsArgs = functionMap.get(function.toUpperCase() + "_" + argCount); 058 if (relationsArgs != null) { 059 return relationsArgs.contains(argIndex); 060 } 061 // Variadic fallback: if any entry for this function name exists with a different 062 // arity, use the largest arity entry's pattern, defaulting to not-indirect for overflow args. 063 String prefix = function.toUpperCase() + "_"; 064 Set<Integer> bestMatch = null; 065 int bestArity = 0; 066 for (Map.Entry<String, Set<Integer>> entry : functionMap.entrySet()) { 067 if (entry.getKey().startsWith(prefix)) { 068 int arity = Integer.parseInt(entry.getKey().substring(prefix.length())); 069 if (arity > bestArity) { 070 bestArity = arity; 071 bestMatch = entry.getValue(); 072 } 073 } 074 } 075 if (bestMatch != null) { 076 if (argIndex < bestArity) { 077 return bestMatch.contains(argIndex); 078 } 079 return false; // overflow args default to not-indirect 080 } 081 return false; 082 } 083 084 private static void initFunctionMap(EDbVendor vendor) { 085 Map<String, Set<Integer>> functionDirectRelationMap = new HashMap<>(); 086 Map<String, Set<Integer>> functionIndirectRelationMap = new HashMap<>(); 087 try { 088 InputStream is = FunctionUtility.class.getResourceAsStream("/gudusoft/gsqlparser/dataflow/" 089 + vendor.name().replace("dbv", "") + "/function.properties"); 090 if (is != null) { 091 String[] lines = SQLUtil.getInputStreamContent(is, false).split("\n"); 092 for (String line : lines) { 093 String[] function = line.trim().replace("(", ",").replace(")", "").split("\\s*,\\s*", -1); 094 if (function.length <= 1) { 095 continue; 096 } 097 String functionName = function[0].toUpperCase(); 098 Integer argCount = function.length - 1; 099 Set<Integer> directRelationArgs = new HashSet<>(); 100 Set<Integer> indirectRelationArgs = new HashSet<>(); 101 for (int i = 1; i <= argCount; i++) { 102 if ("fdd".equalsIgnoreCase(function[i])) { 103 directRelationArgs.add(i - 1); 104 } 105 if ("fdr".equalsIgnoreCase(function[i])) { 106 indirectRelationArgs.add(i - 1); 107 } 108 } 109 functionDirectRelationMap.put(functionName + "_" + argCount, directRelationArgs); 110 functionIndirectRelationMap.put(functionName + "_" + argCount, indirectRelationArgs); 111 } 112 } 113 } catch (Exception e) { 114 logger.error("init function map failed.", e); 115 } 116 dbFunctionDirectRelationMap.put(vendor, functionDirectRelationMap); 117 dbFunctionIndirectRelationMap.put(vendor, functionIndirectRelationMap); 118 } 119}