001package gudusoft.gsqlparser.dlineage; 002 003import gudusoft.gsqlparser.EDbVendor; 004import gudusoft.gsqlparser.TBaseType; 005import gudusoft.gsqlparser.TGSqlParser; 006import gudusoft.gsqlparser.dlineage.dataflow.listener.DataFlowHandleListener; 007import gudusoft.gsqlparser.dlineage.dataflow.metadata.MetadataReader; 008import gudusoft.gsqlparser.dlineage.dataflow.model.*; 009import gudusoft.gsqlparser.dlineage.dataflow.model.json.Coordinate; 010import gudusoft.gsqlparser.dlineage.dataflow.model.xml.*; 011import gudusoft.gsqlparser.dlineage.dataflow.sqlenv.SQLEnvParser; 012import gudusoft.gsqlparser.dlineage.util.*; 013import gudusoft.gsqlparser.sqlenv.TSQLEnv; 014import gudusoft.gsqlparser.sqlenv.parser.TJSONSQLEnvParser; 015import gudusoft.gsqlparser.util.IndexedLinkedHashMap; 016import gudusoft.gsqlparser.util.Logger; 017import gudusoft.gsqlparser.util.LoggerFactory; 018import gudusoft.gsqlparser.util.SQLUtil; 019import gudusoft.gsqlparser.util.json.JSON; 020 021import java.io.File; 022import java.io.FileInputStream; 023import java.io.IOException; 024import java.io.BufferedReader; 025import java.io.InputStreamReader; 026import java.util.zip.GZIPInputStream; 027import java.util.*; 028import java.util.concurrent.*; 029import java.util.concurrent.atomic.AtomicInteger; 030import java.util.stream.Collectors; 031 032public class ParallelDataFlowAnalyzer implements IDataFlowAnalyzer { 033 private static final Logger logger = LoggerFactory.getLogger(ParallelDataFlowAnalyzer.class); 034 private SqlInfo[] sqlInfos; 035 private Option option = new Option(); 036 private TSQLEnv sqlenv = null; 037 private dataflow dataflow; 038 private String dataflowString; 039 private List<ErrorInfo> errorInfos = new CopyOnWriteArrayList<ErrorInfo>(); 040 private IndexedLinkedHashMap<String, List<SqlInfo>> sqlInfoMap = new IndexedLinkedHashMap<String, List<SqlInfo>>(); 041 private final Map<String, String> hashSQLMap = new HashMap(); 042 043 public ParallelDataFlowAnalyzer(SqlInfo[] sqlInfos, EDbVendor dbVendor, boolean simpleOutput) { 044 this.sqlInfos = sqlInfos; 045 option.setVendor(dbVendor); 046 option.setSimpleOutput(simpleOutput); 047 ModelBindingManager.setGlobalOption(option); 048 } 049 050 public ParallelDataFlowAnalyzer(String[] sqlContents, EDbVendor dbVendor, boolean simpleOutput, String defaultServer, String defaultDatabase, String defaltSchema) { 051 SqlInfo[] sqlInfos = new SqlInfo[sqlContents.length]; 052 for (int i = 0; i < sqlContents.length; i++) { 053 SqlInfo info = new SqlInfo(); 054 info.setSql(sqlContents[i]); 055 info.setOriginIndex(0); 056 sqlInfos[i] = info; 057 } 058 option.setVendor(dbVendor); 059 option.setSimpleOutput(simpleOutput); 060 option.setDefaultServer(defaultServer); 061 option.setDefaultDatabase(defaultDatabase); 062 option.setDefaultSchema(defaltSchema); 063 this.sqlInfos = convertSQL(dbVendor, JSON.toJSONString(sqlInfos)).toArray(new SqlInfo[0]); 064 } 065 066 public ParallelDataFlowAnalyzer(String[] sqlContents, EDbVendor dbVendor, boolean simpleOutput) { 067 SqlInfo[] sqlInfos = new SqlInfo[sqlContents.length]; 068 for (int i = 0; i < sqlContents.length; i++) { 069 SqlInfo info = new SqlInfo(); 070 info.setSql(sqlContents[i]); 071 info.setOriginIndex(0); 072 sqlInfos[i] = info; 073 } 074 option.setVendor(dbVendor); 075 option.setSimpleOutput(simpleOutput); 076 this.sqlInfos = convertSQL(dbVendor, JSON.toJSONString(sqlInfos)).toArray(new SqlInfo[0]); 077 } 078 079 public ParallelDataFlowAnalyzer(SqlInfo[] sqlInfos, Option option) { 080 this.sqlInfos = sqlInfos; 081 this.option = option; 082 ModelBindingManager.setGlobalOption(option); 083 } 084 085 public ParallelDataFlowAnalyzer(File[] sqlFiles, Option option) { 086 SqlInfo[] sqlInfos = new SqlInfo[sqlFiles.length]; 087 for (int i = 0; i < sqlFiles.length; i++) { 088 SqlInfo info = new SqlInfo(); 089 info.setSql(SQLUtil.getFileContent(sqlFiles[i])); 090 info.setFileName(sqlFiles[i].getName()); 091 info.setFilePath(sqlFiles[i].getAbsolutePath()); 092 info.setOriginIndex(0); 093 sqlInfos[i] = info; 094 } 095 this.sqlInfos = sqlInfos; 096 this.option = option; 097 ModelBindingManager.setGlobalOption(option); 098 } 099 100 public ParallelDataFlowAnalyzer(File[] sqlFiles, Option option, int splitSizeMB, File splitDir) { 101 List<SqlInfo> sqlInfoList = new ArrayList<SqlInfo>(); 102 for (int i = 0; i < sqlFiles.length; i++) { 103 try { 104 // Ensure split directory exists 105 if (!splitDir.exists() && !splitDir.mkdirs()) { 106 throw new IOException("Failed to create split directory: " + splitDir.getAbsolutePath()); 107 } 108 109 // Split the file if it's large 110 List<File> splitFiles = gudusoft.gsqlparser.util.FileSplitter.splitFile(sqlFiles[i], splitDir, splitSizeMB, option.getVendor()); 111 112 if (splitFiles.isEmpty()) { 113 // If no split files were created, process the original file 114 SqlInfo info = new SqlInfo(); 115 info.setSql(SQLUtil.getFileContent(sqlFiles[i])); 116 info.setFileName(sqlFiles[i].getName()); 117 info.setFilePath(sqlFiles[i].getAbsolutePath()); 118 info.setOriginIndex(0); 119 sqlInfoList.add(info); 120 } else { 121 // Process each split file 122 for (File splitFile : splitFiles) { 123 SqlInfo info = new SqlInfo(); 124 info.setSql(SQLUtil.getFileContent(splitFile)); 125 info.setFileName(splitFile.getName()); 126 info.setFilePath(splitFile.getAbsolutePath()); 127 info.setOriginIndex(0); 128 sqlInfoList.add(info); 129 } 130 } 131 } catch (Exception e) { 132 sqlInfoList.clear(); 133 logger.error("Error splitting file: " + sqlFiles[i].getAbsolutePath(), e); 134 // Fallback to original file if splitting fails 135 SqlInfo info = new SqlInfo(); 136 info.setSql(SQLUtil.getFileContent(sqlFiles[i])); 137 info.setFileName(sqlFiles[i].getName()); 138 info.setFilePath(sqlFiles[i].getAbsolutePath()); 139 info.setOriginIndex(0); 140 sqlInfoList.add(info); 141 } 142 } 143 this.sqlInfos = sqlInfoList.toArray(new SqlInfo[0]); 144 this.option = option; 145 ModelBindingManager.setGlobalOption(option); 146 } 147 148 public ParallelDataFlowAnalyzer(File[] sqlFiles, Option option, int splitSizeMB, String splitDirPath) { 149 this(sqlFiles, option, splitSizeMB, new File(splitDirPath)); 150 } 151 152 protected List<SqlInfo> convertSQL(EDbVendor vendor, String json) { 153 List<SqlInfo> sqlInfos = new ArrayList<SqlInfo>(); 154 List sqlContents = (List) JSON.parseObject(json); 155 for (int j = 0; j < sqlContents.size(); j++) { 156 Map sqlContent = (Map) sqlContents.get(j); 157 String sql = (String) sqlContent.get("sql"); 158 String fileName = (String) sqlContent.get("fileName"); 159 String filePath = (String) sqlContent.get("filePath"); 160 if (sql != null && sql.trim().startsWith("{")) { 161 if (sql.indexOf("createdBy") != -1) { 162 if (this.sqlenv == null) { 163 TSQLEnv[] sqlenvs = new TJSONSQLEnvParser(option.getDefaultServer(), option.getDefaultDatabase(), option.getDefaultSchema()).parseSQLEnv(vendor, sql); 164 if (sqlenvs != null) { 165 this.sqlenv = sqlenvs[0]; 166 } 167 } 168 if (sql.toLowerCase().indexOf("sqldep") != -1 || sql.toLowerCase().indexOf("grabit") != -1 ) { 169 Map queryObject = (Map) JSON.parseObject(sql); 170 List querys = (List) queryObject.get("queries"); 171 if (querys != null) { 172 for (int i = 0; i < querys.size(); i++) { 173 Map object = (Map) querys.get(i); 174 SqlInfo info = new SqlInfo(); 175 info.setSql(JSON.toJSONString(object)); 176 info.setFileName(fileName); 177 info.setFilePath(filePath); 178 info.setOriginIndex(i); 179 sqlInfos.add(info); 180 } 181 queryObject.remove("queries"); 182 SqlInfo info = new SqlInfo(); 183 info.setSql(JSON.toJSONString(queryObject)); 184 info.setFileName(fileName); 185 info.setFilePath(filePath); 186 info.setOriginIndex(querys.size()); 187 sqlInfos.add(info); 188 } else { 189 SqlInfo info = new SqlInfo(); 190 info.setSql(JSON.toJSONString(queryObject)); 191 info.setFileName(fileName); 192 info.setFilePath(filePath); 193 info.setOriginIndex(0); 194 sqlInfos.add(info); 195 } 196 } 197 else if (sql.toLowerCase().indexOf("sqlflow") != -1) { 198 Map sqlflow = (Map) JSON.parseObject(sql); 199 if ("sqlflow-sharded".equals(sqlflow.get("format"))) { 200 String baseDir = null; 201 if (filePath != null) { 202 baseDir = new File(filePath).getParent(); 203 } 204 String sourceCompression = (String) sqlflow.get("sourceCompression"); 205 List<Map> servers = (List<Map>) sqlflow.get("servers"); 206 if (servers != null) { 207 for (Map serverObject : servers) { 208 String name = (String) serverObject.get("name"); 209 String dbVendor = (String) serverObject.get("dbVendor"); 210 List<Map> databases = (List<Map>) serverObject.get("databases"); 211 if (databases != null) { 212 for (Map database : databases) { 213 Map source = (Map) database.get("source"); 214 if (source != null) { 215 String sourcePath = (String) source.get("path"); 216 if (sourcePath != null && baseDir != null) { 217 File sourceFile = new File(baseDir, sourcePath); 218 String fullPath = sourceFile.getAbsolutePath(); 219 if ("block".equals(sourceCompression)) { 220 readGzipBlockSource(fullPath, fileName, filePath, name, dbVendor, sqlInfos); 221 } else { 222 String sourceContent = SQLUtil.getFileContent(fullPath); 223 if (sourceContent != null) { 224 String[] lines = sourceContent.split("\\r?\\n"); 225 for (int i = 0; i < lines.length; i++) { 226 String line = lines[i].trim(); 227 if (line.isEmpty()) { 228 continue; 229 } 230 try { 231 Map sourceObject = (Map) JSON.parseObject(line); 232 String sourceCode = (String) sourceObject.get("sourceCode"); 233 if (sourceCode != null && !sourceCode.isEmpty()) { 234 SqlInfo info = new SqlInfo(); 235 info.setSql(sourceCode); 236 info.setFileName(sourceFile.getName()); 237 info.setFilePath(sourceFile.getAbsolutePath()); 238 info.setOriginIndex(i); 239 info.setDbVendor(dbVendor); 240 info.setServer(name); 241 sqlInfos.add(info); 242 } 243 } catch (Exception e) { 244 logger.warn("Parse source jsonl line failed.", e); 245 } 246 } 247 } 248 } 249 } 250 } 251 } 252 } 253 SqlInfo serverInfo = new SqlInfo(); 254 serverInfo.setSql(JSON.toJSONString(serverObject)); 255 serverInfo.setFileName(fileName); 256 serverInfo.setFilePath(filePath); 257 serverInfo.setDbVendor(dbVendor); 258 serverInfo.setServer(name); 259 sqlInfos.add(serverInfo); 260 } 261 } 262 } else { 263 List<Map> servers = (List<Map>) sqlflow.get("servers"); 264 if (servers != null) { 265 for (Map queryObject : servers) { 266 String name = (String) queryObject.get("name"); 267 String dbVendor = (String) queryObject.get("dbVendor"); 268 List querys = (List) queryObject.get("queries"); 269 if (querys != null) { 270 for (int i = 0; i < querys.size(); i++) { 271 Map object = (Map) querys.get(i); 272 SqlInfo info = new SqlInfo(); 273 info.setSql(JSON.toJSONString(object)); 274 info.setFileName(fileName); 275 info.setFilePath(filePath); 276 info.setOriginIndex(i); 277 info.setDbVendor(dbVendor); 278 info.setServer(name); 279 sqlInfos.add(info); 280 } 281 queryObject.remove("queries"); 282 SqlInfo info = new SqlInfo(); 283 info.setSql(JSON.toJSONString(queryObject)); 284 info.setFileName(fileName); 285 info.setFilePath(filePath); 286 info.setOriginIndex(querys.size()); 287 info.setDbVendor(dbVendor); 288 info.setServer(filePath); 289 sqlInfos.add(info); 290 } else { 291 SqlInfo info = new SqlInfo(); 292 info.setSql(JSON.toJSONString(queryObject)); 293 info.setFileName(fileName); 294 info.setFilePath(filePath); 295 info.setOriginIndex(0); 296 sqlInfos.add(info); 297 } 298 } 299 } 300 } 301 } 302 } 303 } else if (sql != null) { 304 SqlInfo info = new SqlInfo(); 305 info.setSql(sql); 306 info.setFileName(fileName); 307 info.setFilePath(filePath); 308 info.setOriginIndex(0); 309 sqlInfos.add(info); 310 } 311 } 312 return sqlInfos; 313 } 314 315 private void readGzipBlockSource(String fullPath, String fileName, String filePath, String serverName, String dbVendor, List<SqlInfo> sqlInfos) { 316 try (FileInputStream fis = new FileInputStream(fullPath); 317 GZIPInputStream gzis = new GZIPInputStream(fis); 318 BufferedReader reader = new BufferedReader(new InputStreamReader(gzis, "UTF-8"))) { 319 String line; 320 int j = 0; 321 while ((line = reader.readLine()) != null) { 322 line = line.trim(); 323 if (line.isEmpty()) { 324 continue; 325 } 326 try { 327 Map sourceObject = (Map) JSON.parseObject(line); 328 String sourceCode = (String) sourceObject.get("sourceCode"); 329 if (sourceCode != null && !sourceCode.isEmpty()) { 330 SqlInfo info = new SqlInfo(); 331 info.setSql(sourceCode); 332 info.setFileName(fileName); 333 info.setFilePath(filePath); 334 info.setOriginIndex(j); 335 info.setDbVendor(dbVendor); 336 info.setServer(serverName); 337 sqlInfos.add(info); 338 } 339 } catch (Exception e) { 340 logger.warn("Parse gzip source jsonl line failed.", e); 341 } 342 j++; 343 } 344 } catch (Exception e) { 345 logger.warn("Read gzip source file failed: " + fullPath, e); 346 } 347 } 348 349 @Override 350 public boolean isIgnoreRecordSet() { 351 return option.isIgnoreRecordSet(); 352 } 353 354 @Override 355 public void setIgnoreRecordSet(boolean ignoreRecordSet) { 356 option.setIgnoreRecordSet(ignoreRecordSet); 357 } 358 359 @Override 360 public boolean isSimpleShowTopSelectResultSet() { 361 return option.isSimpleShowTopSelectResultSet(); 362 } 363 364 @Override 365 public void setSimpleShowTopSelectResultSet(boolean simpleShowTopSelectResultSet) { 366 option.setSimpleShowTopSelectResultSet(simpleShowTopSelectResultSet); 367 } 368 369 @Override 370 public boolean isSimpleShowFunction() { 371 return option.isSimpleShowFunction(); 372 } 373 374 @Override 375 public void setSimpleShowFunction(boolean simpleShowFunction) { 376 option.setSimpleShowFunction(simpleShowFunction); 377 } 378 379 @Override 380 public boolean isShowJoin() { 381 return option.isShowJoin(); 382 } 383 384 @Override 385 public void setShowJoin(boolean showJoin) { 386 option.setShowJoin(showJoin); 387 } 388 389 @Override 390 public boolean isShowImplicitSchema() { 391 return option.isShowImplicitSchema(); 392 } 393 394 @Override 395 public void setShowImplicitSchema(boolean showImplicitSchema) { 396 option.setShowImplicitSchema(showImplicitSchema); 397 } 398 399 @Override 400 public boolean isShowConstantTable() { 401 return option.isShowConstantTable(); 402 } 403 404 @Override 405 public void setShowConstantTable(boolean showConstantTable) { 406 option.setShowConstantTable(showConstantTable); 407 } 408 409 @Override 410 public boolean isShowCountTableColumn() { 411 return option.isShowCountTableColumn(); 412 } 413 414 @Override 415 public void setShowCountTableColumn(boolean showCountTableColumn) { 416 option.setShowCountTableColumn(showCountTableColumn); 417 } 418 419 @Override 420 public boolean isTransform() { 421 return option.isTransform(); 422 } 423 424 @Override 425 public void setTransform(boolean transform) { 426 option.setTransform(transform); 427 if (option.isTransformCoordinate()) { 428 option.setTransform(true); 429 } 430 } 431 432 @Override 433 public boolean isTransformCoordinate() { 434 return option.isTransformCoordinate(); 435 } 436 437 @Override 438 public void setTransformCoordinate(boolean transformCoordinate) { 439 option.setTransformCoordinate(transformCoordinate); 440 if (transformCoordinate) { 441 option.setTransform(true); 442 } 443 } 444 445 @Override 446 public boolean isLinkOrphanColumnToFirstTable() { 447 return option.isLinkOrphanColumnToFirstTable(); 448 } 449 450 @Override 451 public void setLinkOrphanColumnToFirstTable(boolean linkOrphanColumnToFirstTable) { 452 option.setLinkOrphanColumnToFirstTable(linkOrphanColumnToFirstTable); 453 } 454 455 @Override 456 public boolean isIgnoreCoordinate() { 457 return option.isIgnoreCoordinate(); 458 } 459 460 @Override 461 public void setIgnoreCoordinate(boolean ignoreCoordinate) { 462 option.setIgnoreCoordinate(ignoreCoordinate); 463 } 464 465 @Override 466 public void setHandleListener(DataFlowHandleListener listener) { 467 option.setHandleListener(listener); 468 } 469 470 @Override 471 public void setSqlEnv(TSQLEnv sqlenv) { 472 this.sqlenv = sqlenv; 473 } 474 475 @Override 476 public void setOption(Option option) { 477 this.option = option; 478 } 479 480 @Override 481 public Option getOption() { 482 return option; 483 } 484 485 @Override 486 public List<ErrorInfo> getErrorMessages() { 487 return errorInfos; 488 } 489 490 @Override 491 public synchronized String generateSqlInfos() { 492 return JSON.toJSONString(sqlInfoMap); 493 } 494 495 @Override 496 public synchronized String generateDataFlow() { 497 return generateDataFlow(false); 498 } 499 500 @Override 501 public Map<String, List<SqlInfo>> getSqlInfos() { 502 return sqlInfoMap; 503 } 504 505 @Override 506 /** 507 * @deprecated please use SqlInfoHelper.getSelectedDbObjectInfo 508 */ 509 public DbObjectPosition getSelectedDbObjectInfo(Coordinate start, Coordinate end) { 510 if (start == null || end == null) { 511 throw new IllegalArgumentException("Coordinate can't be null."); 512 } 513 514 String hashCode = start.getHashCode(); 515 516 if (hashCode == null) { 517 throw new IllegalArgumentException("Coordinate hashcode can't be null."); 518 } 519 520 int dbObjectStartLine = (int) start.getX() - 1; 521 int dbObjectStarColumn = (int) start.getY() - 1; 522 int dbObjectEndLine = (int) end.getX() - 1; 523 int dbObjectEndColumn = (int) end.getY() - 1; 524 List<SqlInfo> sqlInfoList; 525 if (hashCode.matches("\\d+")) { 526 sqlInfoList = sqlInfoMap.getValueAtIndex(Integer.valueOf(hashCode)); 527 } else { 528 sqlInfoList = sqlInfoMap.get(hashCode); 529 } 530 for (int j = 0; j < sqlInfoList.size(); j++) { 531 SqlInfo sqlInfo = sqlInfoList.get(j); 532 int startLine = sqlInfo.getLineStart(); 533 int endLine = sqlInfo.getLineEnd(); 534 if (dbObjectStartLine >= startLine && dbObjectStartLine <= endLine) { 535 DbObjectPosition position = new DbObjectPosition(); 536 position.setFile(sqlInfo.getFileName()); 537 position.setFilePath(sqlInfo.getFilePath()); 538 position.setSql(sqlInfo.getSql()); 539 position.setIndex(sqlInfo.getOriginIndex()); 540 List<Pair<Integer, Integer>> positions = position.getPositions(); 541 positions.add(new Pair<Integer, Integer>( 542 dbObjectStartLine - startLine + sqlInfo.getOriginLineStart() + 1, dbObjectStarColumn + 1)); 543 positions.add(new Pair<Integer, Integer>(dbObjectEndLine - startLine + sqlInfo.getOriginLineStart() + 1, 544 dbObjectEndColumn + 1)); 545 return position; 546 } 547 } 548 return null; 549 } 550 551 @Override 552 public synchronized String generateDataFlow(final boolean withExtraInfo) { 553 return generateDataFlow(withExtraInfo, true); 554 } 555 556 public synchronized String generateDataFlow(final boolean withExtraInfo, boolean useSaveMemoryMode) { 557 sqlInfoMap.clear(); 558 errorInfos.clear(); 559 Map<String, Pair3<StringBuilder, AtomicInteger, String>> databaseMap = new LinkedHashMap<String, Pair3<StringBuilder, AtomicInteger, String>>(); 560 for (int i = 0; i < sqlInfos.length; i++) { 561 SqlInfo sqlInfo = sqlInfos[i]; 562 if (sqlInfo != null && sqlInfo.getSql() == null && sqlInfo.getFilePath() != null) { 563 sqlInfo.setSql(SQLUtil.getFileContent(sqlInfo.getFilePath())); 564 } 565 if (sqlInfo != null && sqlInfo.getSql() == null && sqlInfo.getFileName() != null) { 566 sqlInfo.setSql(SQLUtil.getFileContent(sqlInfo.getFileName())); 567 } 568 if (sqlInfo == null || sqlInfo.getSql() == null) { 569 sqlInfoMap.put(String.valueOf(i), new ArrayList<SqlInfo>()); 570 continue; 571 } 572 String sql = sqlInfo.getSql(); 573 if (sql != null && sql.trim().startsWith("{")) { 574 if (MetadataReader.isGrabit(sql) || MetadataReader.isSqlflow(sql) || MetadataReader.isSqlflowSharded(sql)) { 575 String hash = SHA256.getMd5(sql); 576 String fileHash = SHA256.getMd5(hash); 577 if (!sqlInfoMap.containsKey(fileHash)) { 578 sqlInfoMap.put(fileHash, new ArrayList<SqlInfo>()); 579 sqlInfoMap.get(fileHash).add(sqlInfo); 580 } 581 } else { 582 Map queryObject = (Map) JSON.parseObject(sql); 583 appendSqlInfo(databaseMap, i, sqlInfo, queryObject); 584 } 585 } else { 586 String content = sql; 587 String hash = SHA256.getMd5(content); 588 String fileHash = SHA256.getMd5(hash); 589 if (!sqlInfoMap.containsKey(fileHash)) { 590 sqlInfoMap.put(fileHash, new ArrayList<SqlInfo>()); 591 592 String database = TSQLEnv.DEFAULT_DB_NAME; 593 String schema = TSQLEnv.DEFAULT_SCHEMA_NAME; 594 if(sqlenv!=null) { 595 // Prefer the per-analysis Option default database over the attached 596 // env's default catalog (mirrors DataFlowAnalyzer), so a shared 597 // multi-catalog TSQLEnv can be reused across analyses with different 598 // default databases. 599 if (!SQLUtil.isEmpty(option.getDefaultDatabase()) 600 && !TSQLEnv.DEFAULT_DB_NAME.equals(option.getDefaultDatabase())) { 601 database = option.getDefaultDatabase(); 602 } else { 603 database = sqlenv.getDefaultCatalogName(); 604 } 605 if(database == null) { 606 database = TSQLEnv.DEFAULT_DB_NAME; 607 } 608 schema = sqlenv.getDefaultSchemaName(); 609 if(schema == null) { 610 schema = TSQLEnv.DEFAULT_SCHEMA_NAME; 611 } 612 } 613 boolean supportCatalog = TSQLEnv.supportCatalog(option.getVendor()); 614 boolean supportSchema = TSQLEnv.supportSchema(option.getVendor()); 615 StringBuilder builder = new StringBuilder(); 616 if (supportCatalog) { 617 builder.append(database); 618 } 619 if (supportSchema) { 620 if (builder.length() > 0) { 621 builder.append("."); 622 } 623 builder.append(schema); 624 } 625 String group = builder.toString(); 626 SqlInfo sqlInfoItem = new SqlInfo(); 627 sqlInfoItem.setFileName(sqlInfo.getFileName()); 628 sqlInfoItem.setFilePath(sqlInfo.getFilePath()); 629 sqlInfoItem.setSql(sqlInfo.getSql()); 630 sqlInfoItem.setOriginIndex(0); 631 sqlInfoItem.setOriginLineStart(0); 632 sqlInfoItem.setOriginLineEnd(sqlInfo.getSql().split("\n").length - 1); 633 sqlInfoItem.setIndex(0); 634 sqlInfoItem.setLineStart(0); 635 sqlInfoItem.setLineEnd(sqlInfo.getSql().split("\n").length - 1); 636 sqlInfoItem.setHash(fileHash); 637 sqlInfoItem.setGroup(group); 638 639 sqlInfoMap.get(fileHash).add(sqlInfoItem); 640 } 641 } 642 } 643 644 final TSQLEnv[] env = new TSQLEnv[]{sqlenv}; 645 if (sqlenv == null) { 646 TSQLEnv[] envs = new SQLEnvParser(option.getDefaultServer(), option.getDefaultDatabase(), option.getDefaultSchema()).parseSQLEnv(option.getVendor(), sqlInfos); 647 if (envs != null && envs.length > 0) { 648 env[0] = envs[0]; 649 } 650 } 651 ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors 652 .newFixedThreadPool(option.getParallel() < sqlInfos.length 653 ? option.getParallel() 654 : sqlInfos.length); 655 656 List<File> tempFiles = Collections.synchronizedList(new ArrayList<File>()); 657 final Map<SqlInfo, dataflow> dataflowMap = useSaveMemoryMode ? null : new ConcurrentHashMap<>(); 658 659 try { 660 final CountDownLatch latch = new CountDownLatch(sqlInfos.length); 661 662 logger.info("start parallel analyze " + sqlInfos.length + " sqlinfos"); 663 664 for (int i = 0; i < sqlInfos.length; i++) { 665 final SqlInfo[] sqlInfoCopy = new SqlInfo[sqlInfos.length]; 666 final SqlInfo item = sqlInfos[i]; 667 sqlInfoCopy[i] = item; 668 final Option optionCopy = (Option) option.clone(); 669 optionCopy.setStartId(5000000L * i); 670 optionCopy.setOutput(false); 671 final int index = i; 672 Runnable task = new Runnable() { 673 @Override 674 public void run() { 675 try { 676 logger.info("start analyze sqlinfo[" + index + "]" + (sqlInfos[index].getFilePath() != null ? ", file name = " + new File(sqlInfos[index].getFilePath()).getName() : "")); 677 DataFlowAnalyzer analyzer = new DataFlowAnalyzer(sqlInfoCopy, optionCopy); 678 analyzer.setSqlEnv(env[0]); 679 analyzer.generateDataFlow(withExtraInfo); 680 dataflow dataflow = analyzer.getDataFlow(); 681 if (dataflow == null) { 682 logger.warn("analyze sqlinfo[" + index + "] done, but dataflow is null" + (sqlInfos[index].getFilePath() != null ? ", file name = " + new File(sqlInfos[index].getFilePath()).getName() : "")); 683 errorInfos.addAll(analyzer.getErrorMessages()); 684 analyzer.dispose(); 685 return; 686 } 687 logger.info("analyze sqlinfo[" + index + "] done, relation count: " + dataflow.getRelationships().size()+", error count: "+ analyzer.getErrorMessages().size()); 688 if (analyzer.getErrorMessages().size() > 10000) { 689 dataflow.setErrors(new ArrayList<>(dataflow.getErrors().subList(0, 10000))); 690 errorInfos.addAll(analyzer.getErrorMessages().subList(0, 10000)); 691 logger.warn("Too many errors in dataflow ("+dataflow.getErrors().size()+"), truncating to first 10000 errors" + (sqlInfos[index].getFileName() != null ? ", file name = " + sqlInfos[index].getFileName() : "")); 692 } 693 else{ 694 errorInfos.addAll(analyzer.getErrorMessages()); 695 } 696 697 if (useSaveMemoryMode) { 698 File tempFile = File.createTempFile("dataflow_" + index + "_" + System.currentTimeMillis() + "_", ".xml.zip"); 699 XML2Model.saveXML(dataflow, tempFile); 700 tempFiles.add(tempFile); 701 } else { 702 dataflowMap.put(item, dataflow); 703 } 704 705 hashSQLMap.putAll(analyzer.getHashSQLMap()); 706 analyzer.dispose(); 707 } 708 catch (Exception e) { 709 logger.error("analyze sqlinfo[" + index + "] failed.", e); 710 } 711 finally { 712 latch.countDown(); 713 } 714 } 715 }; 716 executor.submit(task); 717 } 718 latch.await(); 719 } catch (Exception e) { 720 logger.error("execute task failed.", e); 721 } 722 executor.shutdown(); 723 724 ModelBindingManager modelManager = new ModelBindingManager(); 725 modelManager.setGlobalVendor(option.getVendor()); 726 modelManager.setGlobalOption(option); 727 ModelBindingManager.set(modelManager); 728 729 if (useSaveMemoryMode) { 730 // 使用节约内存模式,迭代合并 731 logger.info("start merge dataflow, dataflow count: " + tempFiles.size()+", useSaveMemoryMode: "+ useSaveMemoryMode+", gsp version: "+ TBaseType.versionid); 732 this.dataflow = iterativeMergeDataFlows(tempFiles, 5000000L * sqlInfos.length); 733 if (this.dataflow != null && this.dataflow.getRelationships() != null) { 734 logger.info("merge dataflow done, dataflow count: " + tempFiles.size() + ", relation count: " + this.dataflow.getRelationships().size()); 735 } 736 for (File tempFile : tempFiles) { 737 tempFile.delete(); 738 } 739 } else { 740 // 保持原有逻辑 741 logger.info("start merge dataflow, dataflow count: " + dataflowMap.size()+", useSaveMemoryMode: "+ useSaveMemoryMode+", gsp version: "+ TBaseType.versionid); 742 this.dataflow = mergeDataFlows(dataflowMap, 5000000L * sqlInfos.length); 743 if (this.dataflow != null && this.dataflow.getRelationships() != null) { 744 logger.info("merge dataflow done, dataflow count: " + tempFiles.size() + ", relation count: " + this.dataflow.getRelationships().size()); 745 } 746 dataflowMap.clear(); 747 } 748 749 if (this.dataflow != null) { 750 logger.info("merge done, relation count: " + this.dataflow.getRelationships().size()); 751 } 752 753 if (dataflow != null && option.isOutput()) { 754 if (option.isTextFormat()) { 755 dataflowString = DataFlowAnalyzer.getTextOutput(dataflow); 756 } else { 757 try { 758 dataflowString = XML2Model.saveXML(dataflow); 759 }catch (Exception e){ 760 logger.error("save dataflow as xml failed.", e); 761 dataflowString = null; 762 } 763 } 764 } 765 ModelBindingManager.remove(); 766 return dataflowString; 767 } 768 769 private dataflow iterativeMergeDataFlows(List<File> tempFiles, long startId) { 770 return DataflowUtility.iterativeMergeDataflowsFromFilesByStartId(tempFiles, startId); 771 } 772 773 private void appendSqlInfo(Map<String, Pair3<StringBuilder, AtomicInteger, String>> databaseMap, int index, 774 SqlInfo sqlInfo, Map queryObject) { 775 EDbVendor vendor = option.getVendor(); 776 if(!SQLUtil.isEmpty(sqlInfo.getDbVendor())){ 777 vendor = EDbVendor.valueOf(sqlInfo.getDbVendor()); 778 } 779 780 boolean supportCatalog = TSQLEnv.supportCatalog(vendor); 781 boolean supportSchema = TSQLEnv.supportSchema(vendor); 782 783 String groupName = (String) queryObject.get("groupName"); 784 if (DlineageUtil.isProcedureExcluded(groupName)) { 785 return; 786 } 787 788 String content = (String) queryObject.get("sourceCode"); 789 if (SQLUtil.isEmpty(content)) { 790 return; 791 } 792 793 StringBuilder builder = new StringBuilder(); 794 if (supportCatalog) { 795 String database = (String) queryObject.get("database"); 796 if (database.indexOf(".") != -1) { 797 String delimitedChar = TSQLEnv.delimitedChar(vendor); 798 database = delimitedChar + SQLUtil.trimColumnStringQuote(database) + delimitedChar; 799 } 800 builder.append(database); 801 } 802 if (supportSchema) { 803 String schema = (String) queryObject.get("schema"); 804 if (schema.indexOf(".") != -1) { 805 String delimitedChar = TSQLEnv.delimitedChar(vendor); 806 schema = delimitedChar + SQLUtil.trimColumnStringQuote(schema) + delimitedChar; 807 } 808 if (builder.length() > 0) { 809 builder.append("."); 810 } 811 builder.append(schema); 812 } 813 String group = builder.toString(); 814 String sqlHash = SHA256.getMd5(content); 815 String hash = SHA256.getMd5(sqlHash); 816 if (!databaseMap.containsKey(sqlHash)) { 817 databaseMap.put(sqlHash, 818 new Pair3<StringBuilder, AtomicInteger, String>(new StringBuilder(), new AtomicInteger(), group)); 819 } 820 String delimiterChar = String.valueOf(TGSqlParser.getDelimiterChar(option.getVendor())); 821 StringBuilder buffer = new StringBuilder(content); 822 if (content.trim().endsWith(delimiterChar) || content.trim().endsWith(";")) { 823 buffer.append("\n"); 824 } else if(vendor == EDbVendor.dbvredshift 825 || vendor == EDbVendor.dbvgaussdb 826 || vendor == EDbVendor.dbvedb 827 || vendor == EDbVendor.dbvpostgresql 828 || vendor == EDbVendor.dbvmysql 829 || vendor == EDbVendor.dbvteradata){ 830 buffer.append("\n\n-- " + TBaseType.sqlflow_stmt_delimiter_str + "\n\n"); 831 } else{ 832 SQLUtil.endTrim(buffer); 833 buffer.append(";").append("\n"); 834 } 835 836 int lineStart = databaseMap.get(sqlHash).first.toString().split("\n", -1).length - 1; 837 if (databaseMap.get(sqlHash).first.toString().length() == 0) { 838 lineStart = 0; 839 } 840 databaseMap.get(sqlHash).first.append(buffer.toString()); 841 SqlInfo sqlInfoItem = new SqlInfo(); 842 sqlInfoItem.setFileName(sqlInfo.getFileName()); 843 sqlInfoItem.setFilePath(sqlInfo.getFilePath()); 844 sqlInfoItem.setSql(buffer.toString()); 845 sqlInfoItem.setOriginIndex(index); 846 sqlInfoItem.setOriginLineStart(0); 847 sqlInfoItem.setOriginLineEnd(buffer.toString().split("\n", -1).length - 1); 848 sqlInfoItem.setIndex(databaseMap.get(sqlHash).second.getAndIncrement()); 849 sqlInfoItem.setLineStart(lineStart); 850 sqlInfoItem.setLineEnd(databaseMap.get(sqlHash).first.toString().split("\n", -1).length - 1); 851 sqlInfoItem.setGroup(group); 852 sqlInfoItem.setHash(hash); 853 854 if (!sqlInfoMap.containsKey(hash)) { 855 sqlInfoMap.put(hash, new ArrayList<SqlInfo>()); 856 } 857 sqlInfoMap.get(hash).add(sqlInfoItem); 858 } 859 860 @Override 861 public void dispose() { 862 ModelBindingManager.remove(); 863 } 864 865 private dataflow mergeDataFlows(Map<SqlInfo, dataflow> dataflowMap, long startId) { 866 return DataflowUtility.mergeDataflowsByStartId(dataflowMap.values(), startId); 867 } 868 869 @Override 870 public synchronized dataflow getDataFlow() { 871 if (dataflow != null) { 872 return dataflow; 873 } else if (dataflowString != null) { 874 return XML2Model.loadXML(dataflow.class, dataflowString); 875 } 876 return null; 877 } 878 879 @Override 880 public Map<String, String> getHashSQLMap() { 881 return hashSQLMap; 882 } 883 884 public static void main(String[] args) throws Exception { 885 Option option = new Option(); 886 option.setVendor(EDbVendor.dbvmssql); 887 option.setOutput(false); 888// option.setSimpleOutput(true); 889 File parentDir = new File("C:\\Users\\KK\\Desktop\\sql"); 890 ParallelDataFlowAnalyzer analyzer = new ParallelDataFlowAnalyzer(new File[]{new File("C:\\Users\\KK\\Desktop\\metadata_with_query\\客户原始sql.json")}, option, 5, new File("C:\\Users\\KK\\Desktop\\metadata_with_query")); 891 analyzer.generateDataFlow(false, true); 892 dataflow dataflow = analyzer.getDataFlow(); 893// System.out.println(XML2Model.saveXML(dataflow)); 894// dataflow dataflow = XML2Model.loadXML(gudusoft.gsqlparser.dlineage.dataflow.model.xml.dataflow.class,new File("D:\\dataflow.xml.zip")); 895 XML2Model.saveXML(dataflow, new File("D:\\dataflow.xml.zip")); 896// dataflow = DataflowUtility.convertToTableLevelDataflow(dataflow); 897// dataflow = DataflowUtility.convertTableLevelToFunctionCallDataflow(dataflow, true, EDbVendor.dbvoracle); 898// System.out.println(XML2Model.saveXML(dataflow)); 899 } 900 901}