001package gudusoft.gsqlparser.sqlenv.parser; 002 003import gudusoft.gsqlparser.EDbVendor; 004import gudusoft.gsqlparser.sqlenv.TSQLEnv; 005import gudusoft.gsqlparser.sqlenv.parser.grabit.GrabitSQLEnv; 006import gudusoft.gsqlparser.sqlenv.parser.grabit.MultipleGrabitSQLEnv; 007import gudusoft.gsqlparser.sqlenv.parser.sharded.SqlflowShardedSQLEnv; 008import gudusoft.gsqlparser.sqlenv.parser.sqldep.MultipleSQLDepSQLEnv; 009import gudusoft.gsqlparser.sqlenv.parser.sqldep.SQLDepSQLEnv; 010import gudusoft.gsqlparser.sqlenv.parser.sqlflow.SqlflowSQLEnv; 011import gudusoft.gsqlparser.dlineage.dataflow.metadata.MetadataReader; 012import gudusoft.gsqlparser.util.SQLUtil; 013import gudusoft.gsqlparser.util.json.JSON; 014 015import java.io.File; 016import java.io.IOException; 017import java.nio.charset.StandardCharsets; 018import java.nio.file.Files; 019import java.nio.file.Path; 020import java.nio.file.Paths; 021import java.util.ArrayList; 022import java.util.List; 023import java.util.Map; 024import java.util.logging.Level; 025import java.util.logging.Logger; 026 027@SuppressWarnings("rawtypes") 028public class TJSONSQLEnvParser implements TSQLEnvParser { 029 030 private String defaultServer; 031 private String defaultDatabase; 032 private String defaultSchema; 033 034 public TJSONSQLEnvParser(String defaultServer, String defaultDatabase, String defaultSchema) { 035 this.defaultServer = defaultServer; 036 this.defaultDatabase = defaultDatabase; 037 this.defaultSchema = defaultSchema; 038 } 039 040 public TSQLEnv[] parseSQLEnv(EDbVendor vendor, String sql) { 041 return parseSQLEnv(vendor, sql, null); 042 } 043 044 /** 045 * @param explicitBaseDir directory to resolve a sharded manifest's relative 046 * catalog/source paths against, used when {@code sql} is inline manifest 047 * CONTENT (which carries no location of its own). Ignored when {@code sql} 048 * is a file path, since the path already yields the base directory. 049 */ 050 public TSQLEnv[] parseSQLEnv(EDbVendor vendor, String sql, String explicitBaseDir) { 051 if (SQLUtil.isEmpty(sql)) 052 return null; 053 054 String trimSQL = sql.trim(); 055 056 // Check if input is a file path (for sqlflow-sharded format). For inline 057 // content, fall back to the caller-supplied base directory so relative 058 // catalog paths still resolve. 059 String baseDir = explicitBaseDir; 060 String jsonContent = trimSQL; 061 062 if (!trimSQL.startsWith("{") && !trimSQL.startsWith("[")) { 063 // Try to interpret as a file path 064 File file = new File(trimSQL); 065 if (file.exists() && file.isFile()) { 066 Path filePath = file.toPath(); 067 Path parentDir = filePath.getParent(); 068 baseDir = (parentDir != null) ? parentDir.toString() : ""; 069 070 try { 071 jsonContent = new String(Files.readAllBytes(filePath), StandardCharsets.UTF_8).trim(); 072 } catch (IOException e) { 073 Logger.getLogger(TJSONSQLEnvParser.class.getName()) 074 .log(Level.WARNING, "Failed to read file: " + trimSQL, e); 075 return null; 076 } 077 } 078 } 079 080 if (jsonContent.startsWith("{") && jsonContent.endsWith("}")) { 081 return getJSONSQLEnv(vendor, jsonContent, baseDir); 082 } 083 084 if (jsonContent.startsWith("[") && jsonContent.endsWith("]")) { 085 return getMultipleJSONSQLEnv(vendor, jsonContent); 086 } 087 088 return null; 089 } 090 091 private TSQLEnv[] getMultipleJSONSQLEnv(EDbVendor vendor, String trimSQL) { 092 try { 093 List json = (List) JSON.parseObject(trimSQL); 094 if (trimSQL.indexOf("createdBy") != -1) { 095 if (trimSQL.toLowerCase().indexOf("sqldep") != -1) { 096 return new TSQLEnv[] { new MultipleSQLDepSQLEnv(defaultServer, defaultDatabase, defaultSchema, vendor, json) }; 097 } 098 if (trimSQL.toLowerCase().indexOf("grabit") != -1) { 099 return new TSQLEnv[] { new MultipleGrabitSQLEnv(defaultServer, defaultDatabase, defaultSchema, vendor, json) }; 100 } 101 } 102 } catch (Exception e) { 103 Logger.getLogger(TJSONSQLEnvParser.class.getName()).log(Level.WARNING, "Parse json failed.", e); 104 } 105 return null; 106 } 107 108 private TSQLEnv[] getJSONSQLEnv(EDbVendor vendor, String trimSQL, String baseDir) { 109 try { 110 Map json = (Map) JSON.parseObject(trimSQL); 111 // Route on the authoritative `format` field BEFORE the createdBy 112 // product brand. A sqlflow-sharded manifest carries the exporter 113 // brand (SQLdep/grabit/sqlflow) in createdBy, so a brand-first check 114 // sent a SQLdep-branded sharded manifest to the legacy SQLDepSQLEnv 115 // and silently lost the external catalog used for column resolution. 116 if (MetadataReader.isSqlflowSharded(trimSQL)) { 117 if (!MetadataReader.isSupportedSqlflowSharded(trimSQL)) { 118 // Unknown future formatVersion: do not load a catalog we may 119 // mis-read. No lineage catalog is better than a wrong one. 120 Logger.getLogger(TJSONSQLEnvParser.class.getName()).log(Level.WARNING, 121 "Unsupported sqlflow-sharded formatVersion " 122 + MetadataReader.shardedFormatVersion(trimSQL) 123 + "; this build supports up to " 124 + MetadataReader.SUPPORTED_SHARDED_FORMAT_VERSION 125 + ". SQLEnv catalog not loaded."); 126 return null; 127 } 128 List servers = (List) json.get("servers"); 129 if (servers == null) 130 return null; 131 List<TSQLEnv> sqlEnvs = new ArrayList<TSQLEnv>(); 132 for (int i = 0; i < servers.size(); i++) { 133 Map server = (Map) servers.get(i); 134 String dbVendor = (String) server.get("dbVendor"); 135 if (SQLUtil.isEmpty(dbVendor)) { 136 sqlEnvs.add(new SqlflowShardedSQLEnv(defaultServer, defaultDatabase, defaultSchema, vendor, server, baseDir)); 137 } else { 138 sqlEnvs.add(new SqlflowShardedSQLEnv(defaultServer, defaultDatabase, defaultSchema, EDbVendor.valueOf(dbVendor), server, baseDir)); 139 } 140 } 141 return sqlEnvs.toArray(new TSQLEnv[0]); 142 } 143 if (json.containsKey("createdBy")) { 144 String createdBy = (String) json.get("createdBy"); 145 if (createdBy.toLowerCase().indexOf("sqldep") != -1) { 146 return new TSQLEnv[] { new SQLDepSQLEnv(defaultServer, defaultDatabase, defaultSchema, vendor, json) }; 147 } 148 if (createdBy.toLowerCase().indexOf("grabit") != -1) { 149 return new TSQLEnv[] { new GrabitSQLEnv(defaultServer, defaultDatabase, defaultSchema, vendor, json) }; 150 } 151 if (createdBy.toLowerCase().indexOf("sqlflow") != -1) { 152 { 153 List servers = (List) json.get("servers"); 154 if (servers == null) 155 return null; 156 List<TSQLEnv> sqlEnvs = new ArrayList<TSQLEnv>(); 157 for (int i = 0; i < servers.size(); i++) { 158 Map server = (Map) servers.get(i); 159 String dbVendor = (String) server.get("dbVendor"); 160 if (SQLUtil.isEmpty(dbVendor)) { 161 sqlEnvs.add(new SqlflowSQLEnv(defaultServer, defaultDatabase, defaultSchema, vendor, server)); 162 } else { 163 sqlEnvs.add(new SqlflowSQLEnv(defaultServer, defaultDatabase, defaultSchema, EDbVendor.valueOf(dbVendor), server)); 164 } 165 } 166 return sqlEnvs.toArray(new TSQLEnv[0]); 167 } 168 } 169 } 170 } catch (Exception e) { 171 Logger.getLogger(TJSONSQLEnvParser.class.getName()).log(Level.WARNING, "Parse json failed.", e); 172 } 173 return null; 174 } 175}