001package gudusoft.gsqlparser.dlineage.util; 002 003import gudusoft.gsqlparser.EDbVendor; 004import gudusoft.gsqlparser.dlineage.DataFlowAnalyzer; 005import gudusoft.gsqlparser.dlineage.ParallelDataFlowAnalyzer; 006import gudusoft.gsqlparser.dlineage.dataflow.metadata.MetadataReader; 007import gudusoft.gsqlparser.dlineage.dataflow.metadata.sqldep.SQLDepMetadataAnalyzer; 008import gudusoft.gsqlparser.dlineage.dataflow.model.ModelBindingManager; 009import gudusoft.gsqlparser.dlineage.dataflow.model.Option; 010import gudusoft.gsqlparser.dlineage.dataflow.model.RelationshipType; 011import gudusoft.gsqlparser.dlineage.dataflow.model.json.Error; 012import gudusoft.gsqlparser.dlineage.dataflow.model.json.Process; 013import gudusoft.gsqlparser.dlineage.dataflow.model.json.*; 014import gudusoft.gsqlparser.dlineage.dataflow.model.xml.*; 015import gudusoft.gsqlparser.dlineage.metadata.Sqlflow; 016import gudusoft.gsqlparser.util.Logger; 017import gudusoft.gsqlparser.util.LoggerFactory; 018import gudusoft.gsqlparser.util.SQLUtil; 019 020import java.io.File; 021import java.util.*; 022import java.util.concurrent.atomic.AtomicLong; 023import java.util.stream.Collectors; 024 025public class DataflowUtility { 026 027 private static final Logger logger = LoggerFactory.getLogger(FunctionUtility.class); 028 029 public static dataflow mergeFunctionCallDataflow(dataflow dataflow, EDbVendor dbVendor) { 030 if (ModelBindingManager.getGlobalVendor() == null) { 031 ModelBindingManager.setGlobalVendor(dbVendor); 032 } 033 dataflow instance = cloneDataflow(dataflow); 034 List<procedure> procedures = new ArrayList<>(instance.getProcedures()); 035 if (instance.getPackages() != null) { 036 for (oraclePackage pkg : instance.getPackages()) { 037 if (pkg.getProcedures() != null) { 038 for (procedure procedure : pkg.getProcedures()) { 039 procedure.setOraclePackage(pkg); 040 procedures.add(procedure); 041 } 042 } 043 } 044 } 045 046 Map<String, procedure> procedureIdMap = procedures.stream().collect(Collectors.toMap( 047 t -> t.getId(), 048 t -> t, 049 (existingValue, newValue) -> newValue 050 )); 051 Map<String, table> functionIdMap = dataflow.getResultsets().stream().collect(Collectors.toMap( 052 t -> t.getId(), 053 t -> t, 054 (existingValue, newValue) -> newValue 055 )); 056 057 Map<String, table> functionMap = new HashMap<>(); 058 Map<String, procedure> procedureMap = new HashMap<>(); 059 Map<String, oraclePackage> oraclePackageMap = new HashMap<>(); 060 Map<String, Set<String>> oraclePackageProcedureMap = new HashMap<>(); 061 if (instance.getPackages() != null) { 062 for (oraclePackage pkg : instance.getPackages()) { 063 String qualifiedPackageName = DlineageUtil.getIdentifierOraclePackageNameWithArgNum(pkg); 064 if (!oraclePackageMap.containsKey(qualifiedPackageName)) { 065 oraclePackageMap.put(qualifiedPackageName, pkg); 066 } 067 for (procedure procedure : pkg.getProcedures()) { 068 String qualifiedProcedureName = qualifiedPackageName + "." + DlineageUtil.getIdentifierProcedureNameWithArgNum(procedure); 069 if (!oraclePackageProcedureMap.containsKey(qualifiedPackageName)) { 070 oraclePackageProcedureMap.put(qualifiedPackageName, new HashSet<>()); 071 } 072 oraclePackageProcedureMap.get(qualifiedPackageName).add(qualifiedProcedureName); 073 } 074 } 075 } 076 077 078 079 for (procedure procedure : procedures) { 080 String qualifiedProcedureName = DlineageUtil.getIdentifierProcedureNameWithArgNum(procedure); 081 if (procedure.getOraclePackage() != null) { 082 String qualifiedPackageName = DlineageUtil.getIdentifierOraclePackageNameWithArgNum(procedure.getOraclePackage()); 083 qualifiedProcedureName = qualifiedPackageName + "." + DlineageUtil.getIdentifierProcedureNameWithArgNum(procedure); 084 } 085 if (!procedureMap.containsKey(qualifiedProcedureName)) { 086 procedureMap.put(qualifiedProcedureName, procedure); 087 } 088 } 089 090 for (table function : instance.getResultsets()) { 091 String qualifiedFunctionName = DlineageUtil.getIdentifierFunctionName(function); 092 if (!functionMap.containsKey(qualifiedFunctionName)) { 093 functionMap.put(qualifiedFunctionName, function); 094 } 095 } 096 097 Set<oraclePackage> oraclePackages = new LinkedHashSet<>(); 098 for (String key : oraclePackageMap.keySet()) { 099 oraclePackage standardPackage = oraclePackageMap.get(key); 100 oraclePackages.add(standardPackage); 101 Set<String> procedureNames = oraclePackageProcedureMap.get(key); 102 if (procedureNames != null) { 103 for (String procedureName : procedureNames) { 104 procedureName = key + "." + procedureName; 105 if (procedureMap.containsKey(procedureName)) { 106 procedure standardProcedure = procedureMap.get(procedureName); 107 if (!standardPackage.getProcedures().contains(standardProcedure)) { 108 standardPackage.getProcedures().add(standardProcedure); 109 } 110 procedureMap.remove(procedureName); 111 } 112 } 113 } 114 } 115 116 instance.setPackages(new ArrayList<>(oraclePackages)); 117 instance.setProcedures(new ArrayList<>(procedureMap.values())); 118 instance.setResultsets(new ArrayList<>(functionMap.values())); 119 120 Map<String, relationship> mergeRelations = new LinkedHashMap<String, relationship>(); 121 122 for (relationship relationship : instance.getRelationships()) { 123 if (relationship.getCaller() == null || relationship.getCallees() == null || relationship.getCallees().size() == 0) { 124 continue; 125 } 126 127 String targetId = relationship.getCaller().getId(); 128 if (procedureIdMap.containsKey(targetId)) { 129 procedure procedure = procedureIdMap.get(targetId); 130 String procedureName = DlineageUtil.getIdentifierProcedureNameWithArgNum(procedure); 131 if (procedure.getOraclePackage() != null) { 132 String qualifiedPackageName = DlineageUtil.getIdentifierOraclePackageNameWithArgNum(procedure.getOraclePackage()); 133 procedureName = qualifiedPackageName + "." + procedureName; 134 } 135 procedure standardProcedure = procedureMap.get(procedureName); 136 relationship.getCaller().setId(standardProcedure.getId()); 137 } else if (functionIdMap.containsKey(targetId)) { 138 table function = functionIdMap.get(targetId); 139 String functionName = DlineageUtil.getIdentifierFunctionName(function); 140 table standardFunction = functionMap.get(functionName); 141 relationship.getCaller().setId(standardFunction.getId()); 142 } 143 144 for (sourceColumn sourceColumn : relationship.getCallees()) { 145 String sourceId = sourceColumn.getId(); 146 if (procedureIdMap.containsKey(sourceId)) { 147 procedure procedure = procedureIdMap.get(sourceId); 148 String procedureName = DlineageUtil.getIdentifierProcedureNameWithArgNum(procedure); 149 if (procedure.getOraclePackage() != null) { 150 String qualifiedPackageName = DlineageUtil.getIdentifierOraclePackageNameWithArgNum(procedure.getOraclePackage()); 151 procedureName = qualifiedPackageName + "." + procedureName; 152 } 153 procedure standardProcedure = procedureMap.get(procedureName); 154 sourceColumn.setId(standardProcedure.getId()); 155 } else if (functionIdMap.containsKey(sourceId)) { 156 table function = functionIdMap.get(sourceId); 157 String functionName = DlineageUtil.getIdentifierFunctionName(function); 158 table standardFunction = functionMap.get(functionName); 159 sourceColumn.setId(standardFunction.getId()); 160 } 161 } 162 163 String key = relationship.toDedupKey(); 164 if (!mergeRelations.containsKey(key)) { 165 mergeRelations.put(key, relationship); 166 } 167 } 168 169 instance.setRelationships(new ArrayList<relationship>(mergeRelations.values())); 170 171 if (instance.getPackages() != null) { 172 for (oraclePackage pkg : instance.getPackages()) { 173 if (pkg.getProcedures() != null) { 174 for (procedure procedure : pkg.getProcedures()) { 175 procedure.setOraclePackage(null); 176 } 177 } 178 } 179 } 180 181 return instance; 182 } 183 184 public static dataflow convertTableLevelToFunctionCallDataflow(dataflow dataflow, boolean showBuiltIn, EDbVendor dbVendor) { 185 if (ModelBindingManager.getGlobalVendor() == null) { 186 ModelBindingManager.setGlobalVendor(dbVendor); 187 } 188 189 dataflow instance = cloneDataflow(dataflow); 190 191 if (instance.getRelationships() == null) { 192 return instance; 193 } 194 195 List<relationship> callRelationships = instance.getRelationships().stream() 196 .filter(t -> RelationshipType.call.name().equals(t.getType())) 197 .filter(t -> !showBuiltIn ? !Boolean.TRUE.equals(t.getBuiltIn()): true).collect(Collectors.toList()); 198 instance.setRelationships(callRelationships); 199 200 Set<String> ids = new HashSet<>(); 201 202 203 callRelationships.stream().forEach(t -> { 204 ids.add(t.getCaller().getId()); 205 t.getCallees().stream().forEach(t1 -> ids.add(t1.getId())); 206 }); 207 208 Iterator<table> iterator = instance.getTables().iterator(); 209 while (iterator.hasNext()) { 210 table t = iterator.next(); 211 if (!ids.contains(t.getId())) { 212 iterator.remove(); 213 } 214 } 215 216 iterator = instance.getResultsets().iterator(); 217 while (iterator.hasNext()) { 218 table t = iterator.next(); 219 if (!ids.contains(t.getId())) { 220 iterator.remove(); 221 } 222 } 223 224 iterator = instance.getViews().iterator(); 225 while (iterator.hasNext()) { 226 table t = iterator.next(); 227 if (!ids.contains(t.getId())) { 228 iterator.remove(); 229 } 230 } 231 232 iterator = instance.getStages().iterator(); 233 while (iterator.hasNext()) { 234 table t = iterator.next(); 235 if (!ids.contains(t.getId())) { 236 iterator.remove(); 237 } 238 } 239 240 iterator = instance.getStreams().iterator(); 241 while (iterator.hasNext()) { 242 table t = iterator.next(); 243 if (!ids.contains(t.getId())) { 244 iterator.remove(); 245 } 246 } 247 248 iterator = instance.getVariables().iterator(); 249 while (iterator.hasNext()) { 250 table t = iterator.next(); 251 if (!ids.contains(t.getId())) { 252 iterator.remove(); 253 } 254 } 255 256 iterator = instance.getPaths().iterator(); 257 while (iterator.hasNext()) { 258 table t = iterator.next(); 259 if (!ids.contains(t.getId())) { 260 iterator.remove(); 261 } 262 } 263 264 iterator = instance.getDatasources().iterator(); 265 while (iterator.hasNext()) { 266 table t = iterator.next(); 267 if (!ids.contains(t.getId())) { 268 iterator.remove(); 269 } 270 } 271 272 iterator = instance.getDatabases().iterator(); 273 while (iterator.hasNext()) { 274 table t = iterator.next(); 275 if (!ids.contains(t.getId())) { 276 iterator.remove(); 277 } 278 } 279 280 iterator = instance.getSchemas().iterator(); 281 while (iterator.hasNext()) { 282 table t = iterator.next(); 283 if (!ids.contains(t.getId())) { 284 iterator.remove(); 285 } 286 } 287 288 iterator = instance.getSequences().iterator(); 289 while (iterator.hasNext()) { 290 table t = iterator.next(); 291 if (!ids.contains(t.getId())) { 292 iterator.remove(); 293 } 294 } 295 296 return mergeFunctionCallDataflow(instance, dbVendor); 297 } 298 299 public static dataflow convertToTableLevelDataflow(dataflow dataflow) { 300 dataflow instance = cloneDataflow(dataflow); 301 302 if (instance.getRelationships() == null) { 303 return instance; 304 } 305 306 Map<String, LinkedHashSet<Pair<String, String>>> relationMap = new HashMap<String, LinkedHashSet<Pair<String, String>>>(); 307 Map<String, LinkedHashSet<Pair3<String, String, relationship>>> callRelationMap = new HashMap<String, LinkedHashSet<Pair3<String, String, relationship>>>(); 308 for (RelationshipType type : RelationshipType.values()) { 309 relationMap.put(type.name(), new LinkedHashSet<Pair<String, String>>()); 310 callRelationMap.put(type.name(), new LinkedHashSet<Pair3<String, String, relationship>>()); 311 } 312 for (relationship relationship : instance.getRelationships()) { 313 if (RelationshipType.call.name().equals(relationship.getType())) { 314 String targetId = relationship.getCaller().getId(); 315 for (sourceColumn sourceColumn : relationship.getCallees()) { 316 String sourceId = sourceColumn.getId(); 317 callRelationMap.get(relationship.getType()).add(new Pair3<>(targetId, sourceId, relationship)); 318 } 319 } else { 320 String targetId = relationship.getTarget().getParent_id(); 321 for (sourceColumn sourceColumn : relationship.getSources()) { 322 String sourceId = sourceColumn.getParent_id(); 323 relationMap.get(relationship.getType()).add(new Pair<>(targetId, sourceId)); 324 } 325 } 326 } 327 328 long maxId = 0; 329 Map<String, table> dbObjMap = getDataflowDbObjMap(instance); 330 331 List<procedure> procedures = new ArrayList<>(instance.getProcedures()); 332 if (instance.getPackages() != null) { 333 for (oraclePackage pkg : instance.getPackages()) { 334 procedures.addAll(pkg.getProcedures()); 335 } 336 } 337 338 Map<String, procedure> procedureMap = procedures.stream().collect(Collectors.toMap( 339 t -> t.getId(), 340 t -> t, 341 (existingValue, newValue) -> newValue 342 )); 343 344 for (table table : dbObjMap.values()) { 345 if (Long.valueOf(table.getId()) > maxId) { 346 maxId = Long.valueOf(table.getId()); 347 } 348 } 349 350 AtomicLong id = new AtomicLong(maxId + 10000000); 351 352 for (table table : dbObjMap.values()) { 353 column column = new column(); 354 column.setId(String.valueOf(id.incrementAndGet())); 355 column.setName(table.getType()); 356 table.setColumns(Arrays.asList(column)); 357 } 358 359 List<relationship> relations = new ArrayList<relationship>(); 360 for (RelationshipType type : RelationshipType.values()) { 361 LinkedHashSet<Pair<String, String>> relationSet = relationMap.get(type.name()); 362 LinkedHashSet<Pair3<String, String, relationship>> callRelationSet = callRelationMap.get(type.name()); 363 if (RelationshipType.call.equals(type)) { 364 for (Pair3<String, String, relationship> pair : callRelationSet) { 365 if ((dbObjMap.get(pair.first) == null && procedureMap.get(pair.first) == null) 366 || (dbObjMap.get(pair.second) == null && procedureMap.get(pair.second) == null)) { 367 continue; 368 } 369 370 relationship relationship = new relationship(); 371 relationship.setType(type.name()); 372 relationship.setId(String.valueOf(id.incrementAndGet())); 373 relationship.setCallStmt(pair.third.getCallStmt()); 374 relationship.setCallCoordinate(pair.third.getCallCoordinate()); 375 376 targetColumn targetColumn = new targetColumn(); 377 if (dbObjMap.containsKey(pair.first)) { 378 targetColumn.setId(dbObjMap.get(pair.first).getId()); 379 targetColumn.setName(dbObjMap.get(pair.first).getName()); 380 targetColumn.setCoordinate(dbObjMap.get(pair.first).getCoordinate()); 381 } else { 382 targetColumn.setId(procedureMap.get(pair.first).getId()); 383 targetColumn.setName(procedureMap.get(pair.first).getName()); 384 targetColumn.setCoordinate(procedureMap.get(pair.first).getCoordinate()); 385 } 386 387 sourceColumn sourceColumn = new sourceColumn(); 388 if (dbObjMap.containsKey(pair.second)) { 389 sourceColumn.setId(dbObjMap.get(pair.second).getId()); 390 sourceColumn.setName(dbObjMap.get(pair.second).getName()); 391 sourceColumn.setCoordinate(dbObjMap.get(pair.second).getCoordinate()); 392 } else { 393 sourceColumn.setId(procedureMap.get(pair.second).getId()); 394 sourceColumn.setName(procedureMap.get(pair.second).getName()); 395 sourceColumn.setCoordinate(procedureMap.get(pair.second).getCoordinate()); 396 } 397 398 relationship.setCaller(targetColumn); 399 relationship.setCallees(Arrays.asList(sourceColumn)); 400 relationship.setBuiltIn(pair.third.getBuiltIn()); 401 relations.add(relationship); 402 } 403 } else { 404 for (Pair<String, String> pair : relationSet) { 405 if (dbObjMap.get(pair.first) == null || dbObjMap.get(pair.second) == null) { 406 continue; 407 } 408 relationship relationship = new relationship(); 409 relationship.setType(type.name()); 410 relationship.setId(String.valueOf(id.incrementAndGet())); 411 targetColumn targetColumn = new targetColumn(); 412 targetColumn.setId(dbObjMap.get(pair.first).getColumns().get(0).getId()); 413 targetColumn.setColumn(dbObjMap.get(pair.first).getColumns().get(0).getName()); 414 targetColumn.setParent_id(pair.first); 415 targetColumn.setParent_name(dbObjMap.get(pair.first).getName()); 416 sourceColumn sourceColumn = new sourceColumn(); 417 sourceColumn.setId(dbObjMap.get(pair.second).getColumns().get(0).getId()); 418 sourceColumn.setColumn(dbObjMap.get(pair.second).getColumns().get(0).getName()); 419 sourceColumn.setParent_id(pair.second); 420 sourceColumn.setParent_name(dbObjMap.get(pair.second).getName()); 421 relationship.setTarget(targetColumn); 422 relationship.setSources(Arrays.asList(sourceColumn)); 423 relations.add(relationship); 424 } 425 } 426 } 427 instance.setRelationships(relations); 428 return instance; 429 } 430 431 public static dataflow convertToSchemaLevelDataflow(dataflow dataflow, EDbVendor dbVendor) throws Exception { 432 return convertToSchemaLevelDataflow(dataflow, dbVendor, false); 433 } 434 435 public static dataflow convertToSchemaLevelDataflow(dataflow dataflow, EDbVendor dbVendor, boolean isSimple) throws Exception { 436 dataflow instance = cloneDataflow(dataflow); 437 438 if (!isSimple) { 439 DataFlowAnalyzer analyzer = new DataFlowAnalyzer("", dbVendor, true); 440 instance = analyzer.getSimpleDataflow(instance, true, Arrays.asList(new String[]{"fdd", "fdr"})); 441 } 442 443 List<table> allTables = new ArrayList<table>(); 444 allTables.addAll(instance.getTables()); 445 allTables.addAll(instance.getViews()); 446 447 ModelBindingManager.setGlobalVendor(dbVendor); 448 449 Map<String, String> tableIdSchameNameMap = allTables.stream().collect(Collectors.toMap(table -> table.getId(), table -> table.getFullSchemaName(), (existingValue, newValue) -> newValue)); 450 451 if (instance.getRelationships() == null) { 452 return instance; 453 } 454 455 Map<String, LinkedHashSet<Pair<String, String>>> relationMap = new HashMap<String, LinkedHashSet<Pair<String, String>>>(); 456 for (RelationshipType type : RelationshipType.values()) { 457 relationMap.put(type.name(), new LinkedHashSet<Pair<String, String>>()); 458 } 459 for (relationship relationship : instance.getRelationships()) { 460 String targetId = relationship.getTarget().getParent_id(); 461 for (sourceColumn sourceColumn : relationship.getSources()) { 462 String sourceId = sourceColumn.getParent_id(); 463 relationMap.get(relationship.getType()).add(new Pair<>(tableIdSchameNameMap.get(targetId), tableIdSchameNameMap.get(sourceId))); 464 } 465 } 466 467 Map<String, table> dbObjMap = getDataflowDbObjMap(instance); 468 LinkedHashSet<String> schemaNameSet = new LinkedHashSet<String>(); 469 schemaNameSet.addAll(tableIdSchameNameMap.values()); 470 List<table> schemaTables = new ArrayList<table>(); 471 472 AtomicLong id = new AtomicLong(0); 473 474 for (String schemaName : schemaNameSet) { 475 table table = new table(); 476 table.setId(String.valueOf(id.incrementAndGet())); 477 table.setName(schemaName); 478 table.setType("schema"); 479 schemaTables.add(table); 480 dbObjMap.put(schemaName, table); 481 } 482 483 for (table table : schemaTables) { 484 column column = new column(); 485 column.setId(String.valueOf(id.incrementAndGet())); 486 column.setName(table.getType()); 487 table.setColumns(Arrays.asList(column)); 488 } 489 490 List<relationship> relations = new ArrayList<relationship>(); 491 for (RelationshipType type : RelationshipType.values()) { 492 LinkedHashSet<Pair<String, String>> relationSet = relationMap.get(type.name()); 493 494 for (Pair<String, String> pair : relationSet) { 495 if (dbObjMap.get(pair.first) == null || dbObjMap.get(pair.second) == null) { 496 continue; 497 } 498 relationship relationship = new relationship(); 499 relationship.setType(type.name()); 500 relationship.setId(String.valueOf(id.incrementAndGet())); 501 targetColumn targetColumn = new targetColumn(); 502 targetColumn.setId(dbObjMap.get(pair.first).getColumns().get(0).getId()); 503 targetColumn.setColumn(dbObjMap.get(pair.first).getColumns().get(0).getName()); 504 targetColumn.setParent_id(dbObjMap.get(pair.first).getId()); 505 targetColumn.setParent_name(dbObjMap.get(pair.first).getName()); 506 sourceColumn sourceColumn = new sourceColumn(); 507 sourceColumn.setId(dbObjMap.get(pair.second).getColumns().get(0).getId()); 508 sourceColumn.setColumn(dbObjMap.get(pair.second).getColumns().get(0).getName()); 509 sourceColumn.setParent_id(dbObjMap.get(pair.second).getId()); 510 sourceColumn.setParent_name(dbObjMap.get(pair.second).getName()); 511 relationship.setTarget(targetColumn); 512 relationship.setSources(Arrays.asList(sourceColumn)); 513 relations.add(relationship); 514 } 515 } 516 517 dataflow schemaDataflow = new dataflow(); 518 schemaDataflow.setTables(schemaTables); 519 schemaDataflow.setRelationships(relations); 520 return schemaDataflow; 521 } 522 523 private static final int PARALLEL_CLONE_THRESHOLD = 1000; 524 525 public static dataflow cloneDataflow(dataflow dataflow) { 526 dataflow dataflowCopy = new dataflow(); 527 if (dataflow.getTables() != null) { 528 dataflowCopy.setTables(cloneTables(dataflow.getTables())); 529 } 530 if (dataflow.getViews() != null) { 531 dataflowCopy.setViews(cloneTables(dataflow.getViews())); 532 } 533 if (dataflow.getRelationships() != null) { 534 dataflowCopy.setRelationships(new ArrayList<>(dataflow.getRelationships())); 535 } 536 if (dataflow.getErrors() != null) { 537 dataflowCopy.setErrors(new ArrayList<>(dataflow.getErrors())); 538 } 539 if (dataflow.getPaths() != null) { 540 dataflowCopy.setPaths(cloneTables(dataflow.getPaths())); 541 } 542 if (dataflow.getPackages() != null) { 543 dataflowCopy.setPackages(new ArrayList<>(dataflow.getPackages())); 544 } 545 if (dataflow.getProcedures() != null) { 546 dataflowCopy.setProcedures(new ArrayList<>(dataflow.getProcedures())); 547 } 548 if (dataflow.getProcesses() != null) { 549 dataflowCopy.setProcesses(new ArrayList<>(dataflow.getProcesses())); 550 } 551 if (dataflow.getResultsets() != null) { 552 dataflowCopy.setResultsets(cloneTables(dataflow.getResultsets())); 553 } 554 if (dataflow.getVariables() != null) { 555 dataflowCopy.setVariables(cloneTables(dataflow.getVariables())); 556 } 557 if (dataflow.getStages() != null) { 558 dataflowCopy.setStages(cloneTables(dataflow.getStages())); 559 } 560 if (dataflow.getSequences() != null) { 561 dataflowCopy.setSequences(cloneTables(dataflow.getSequences())); 562 } 563 if (dataflow.getDatasources() != null) { 564 dataflowCopy.setDatasources(cloneTables(dataflow.getDatasources())); 565 } 566 if (dataflow.getDatabases() != null) { 567 dataflowCopy.setDatabases(cloneTables(dataflow.getDatabases())); 568 } 569 if (dataflow.getSchemas() != null) { 570 dataflowCopy.setSchemas(cloneTables(dataflow.getSchemas())); 571 } 572 if (dataflow.getStreams() != null) { 573 dataflowCopy.setStreams(cloneTables(dataflow.getStreams())); 574 } 575 dataflowCopy.setOrientation(dataflow.getOrientation()); 576 return dataflowCopy; 577 } 578 579 private static List<table> cloneTables(List<table> tableList) { 580 if (tableList == null) { 581 return null; 582 } 583 int size = tableList.size(); 584 if (size >= PARALLEL_CLONE_THRESHOLD) { 585 return tableList.parallelStream() 586 .map(DataflowUtility::cloneTable) 587 .collect(Collectors.toCollection(() -> new ArrayList<>(size))); 588 } else { 589 List<table> tables = new ArrayList<>(size); 590 for (int i = 0; i < size; i++) { 591 tables.add(cloneTable(tableList.get(i))); 592 } 593 return tables; 594 } 595 } 596 597 private static table cloneTable(table item) { 598 try { 599 table cloned = (table) item.clone(); 600 List<column> columns = item.getColumns(); 601 if (columns != null) { 602 cloned.setColumns(new ArrayList<>(columns)); 603 } 604 return cloned; 605 } catch (CloneNotSupportedException e) { 606 throw new RuntimeException("Clone table failed.", e); 607 } 608 } 609 610 public static Map<String, table> getDataflowDbObjMap(dataflow dataflow) { 611 List<table> tables = new ArrayList<table>(); 612 if (dataflow.getTables() != null) { 613 tables.addAll(dataflow.getTables()); 614 } 615 if (dataflow.getViews() != null) { 616 tables.addAll(dataflow.getViews()); 617 } 618 if (dataflow.getPaths() != null) { 619 tables.addAll(dataflow.getPaths()); 620 } 621 if (dataflow.getResultsets() != null) { 622 tables.addAll(dataflow.getResultsets()); 623 } 624 if (dataflow.getVariables() != null) { 625 tables.addAll(dataflow.getVariables()); 626 } 627 if (dataflow.getStages() != null) { 628 tables.addAll(dataflow.getStages()); 629 } 630 if (dataflow.getSequences() != null) { 631 tables.addAll(dataflow.getSequences()); 632 } 633 if (dataflow.getDatasources() != null) { 634 tables.addAll(dataflow.getDatasources()); 635 } 636 if (dataflow.getDatabases() != null) { 637 tables.addAll(dataflow.getDatabases()); 638 } 639 if (dataflow.getSchemas() != null) { 640 tables.addAll(dataflow.getSchemas()); 641 } 642 if (dataflow.getStreams() != null) { 643 tables.addAll(dataflow.getStreams()); 644 } 645 646 Map<String, table> dbObjMap = new HashMap<>(); 647 for (table table : tables) { 648 dbObjMap.put(table.getId(), table); 649 } 650 return dbObjMap; 651 } 652 public static Map<String, table> getDataflowDbObjNameMap(dataflow dataflow) { 653 List<table> tables = new ArrayList<table>(); 654 if (dataflow.getTables() != null) { 655 tables.addAll(dataflow.getTables()); 656 } 657 if (dataflow.getViews() != null) { 658 tables.addAll(dataflow.getViews()); 659 } 660 if (dataflow.getPaths() != null) { 661 tables.addAll(dataflow.getPaths()); 662 } 663 if (dataflow.getResultsets() != null) { 664 tables.addAll(dataflow.getResultsets()); 665 } 666 if (dataflow.getVariables() != null) { 667 tables.addAll(dataflow.getVariables()); 668 } 669 if (dataflow.getStages() != null) { 670 tables.addAll(dataflow.getStages()); 671 } 672 if (dataflow.getSequences() != null) { 673 tables.addAll(dataflow.getSequences()); 674 } 675 if (dataflow.getDatasources() != null) { 676 tables.addAll(dataflow.getDatasources()); 677 } 678 if (dataflow.getDatabases() != null) { 679 tables.addAll(dataflow.getDatabases()); 680 } 681 if (dataflow.getSchemas() != null) { 682 tables.addAll(dataflow.getSchemas()); 683 } 684 if (dataflow.getStreams() != null) { 685 tables.addAll(dataflow.getStreams()); 686 } 687 688 Map<String, table> dbObjMap = new HashMap<>(); 689 for(table table: tables){ 690 dbObjMap.put(table.getFullName(), table); 691 } 692 return dbObjMap; 693 } 694 public static dataflow mergeDataflowsWithDifferentStartId(Collection<dataflow> dataflows, EDbVendor vendor) { 695 ModelBindingManager.setGlobalVendor(vendor); 696 try { 697 return mergeDataflowsByStartId(dataflows, DATAFLOW_ID_RANGE * dataflows.size()); 698 } finally { 699 ModelBindingManager.removeGlobalVendor(); 700 } 701 } 702 703 /** 704 * 内部合并实现:调用方需保证所有输入 dataflow 的 ID 空间不冲突,并且已提前设置好 ModelBindingManager。 705 * <p> 706 * 该方法代替之前位于 {@code ParallelDataFlowAnalyzer} 的同名实现,外部调用者应使用 707 * {@link #mergeDataflowsWithDifferentStartId(Collection, EDbVendor)} 或 708 * {@link #mergeDataflows(Collection, EDbVendor)}。 709 */ 710 public static dataflow mergeDataflowsByStartId(Collection<dataflow> dataflows, long startId) { 711 dataflow mergeDataflow = new dataflow(); 712 713 List<table> tableCopy = new ArrayList<table>(); 714 List<table> viewCopy = new ArrayList<table>(); 715 List<table> databaseCopy = new ArrayList<table>(); 716 List<table> schemaCopy = new ArrayList<table>(); 717 List<table> stageCopy = new ArrayList<table>(); 718 List<table> dataSourceCopy = new ArrayList<table>(); 719 List<table> streamCopy = new ArrayList<table>(); 720 List<table> fileCopy = new ArrayList<table>(); 721 List<table> variableCopy = new ArrayList<table>(); 722 List<table> resultSetCopy = new ArrayList<table>(); 723 List<table> sequenceCopy = new ArrayList<table>(); 724 725 List<process> processCopy = new ArrayList<process>(); 726 List<relationship> relationshipCopy = new ArrayList<relationship>(); 727 List<procedure> procedureCopy = new ArrayList<procedure>(); 728 List<oraclePackage> packageCopy = new ArrayList<oraclePackage>(); 729 List<error> errorCopy = new ArrayList<error>(); 730 731 List<table> tables = new ArrayList<table>(); 732 int dfIdx = 0; 733 for (dataflow dataflow : dataflows) { 734 if (dataflow == null) { 735 continue; 736 } 737 738 if (dataflow.getTables() != null) { 739 tableCopy.addAll(dataflow.getTables()); 740 } 741 mergeDataflow.setTables(tableCopy); 742 if (dataflow.getViews() != null) { 743 viewCopy.addAll(dataflow.getViews()); 744 } 745 mergeDataflow.setViews(viewCopy); 746 if (dataflow.getDatabases() != null) { 747 databaseCopy.addAll(dataflow.getDatabases()); 748 } 749 mergeDataflow.setDatabases(databaseCopy); 750 if (dataflow.getSchemas() != null) { 751 schemaCopy.addAll(dataflow.getSchemas()); 752 } 753 mergeDataflow.setSchemas(schemaCopy); 754 if (dataflow.getStages() != null) { 755 stageCopy.addAll(dataflow.getStages()); 756 } 757 mergeDataflow.setStages(stageCopy); 758 if (dataflow.getDatasources() != null) { 759 dataSourceCopy.addAll(dataflow.getDatasources()); 760 } 761 mergeDataflow.setDatasources(dataSourceCopy); 762 if (dataflow.getStreams() != null) { 763 streamCopy.addAll(dataflow.getStreams()); 764 } 765 mergeDataflow.setStreams(streamCopy); 766 if (dataflow.getPaths() != null) { 767 fileCopy.addAll(dataflow.getPaths()); 768 } 769 mergeDataflow.setPaths(fileCopy); 770 if (dataflow.getVariables() != null) { 771 variableCopy.addAll(dataflow.getVariables()); 772 } 773 mergeDataflow.setVariables(variableCopy); 774 if (dataflow.getResultsets() != null) { 775 resultSetCopy.addAll(dataflow.getResultsets()); 776 } 777 mergeDataflow.setResultsets(resultSetCopy); 778 if (dataflow.getSequences() != null) { 779 sequenceCopy.addAll(dataflow.getSequences()); 780 } 781 mergeDataflow.setSequences(sequenceCopy); 782 783 dfIdx++; 784 785 if (dataflow.getProcesses() != null) { 786 processCopy.addAll(dataflow.getProcesses()); 787 } 788 mergeDataflow.setProcesses(processCopy); 789 if (dataflow.getRelationships() != null) { 790 relationshipCopy.addAll(dataflow.getRelationships()); 791 } 792 mergeDataflow.setRelationships(relationshipCopy); 793 if (dataflow.getProcedures() != null) { 794 procedureCopy.addAll(dataflow.getProcedures()); 795 } 796 mergeDataflow.setProcedures(procedureCopy); 797 if (dataflow.getPackages() != null) { 798 packageCopy.addAll(dataflow.getPackages()); 799 } 800 mergeDataflow.setPackages(packageCopy); 801 if (dataflow.getErrors() != null) { 802 errorCopy.addAll(dataflow.getErrors()); 803 } 804 if (errorCopy.size() > 10000) { 805 errorCopy = errorCopy.subList(0, 10000); 806 } 807 mergeDataflow.setErrors(errorCopy); 808 809 tables.addAll(dataflow.getTables()); 810 tables.addAll(dataflow.getViews()); 811 tables.addAll(dataflow.getDatabases()); 812 tables.addAll(dataflow.getSchemas()); 813 tables.addAll(dataflow.getStages()); 814 tables.addAll(dataflow.getDatasources()); 815 tables.addAll(dataflow.getStreams()); 816 tables.addAll(dataflow.getPaths()); 817 tables.addAll(dataflow.getResultsets()); 818 tables.addAll(dataflow.getVariables()); 819 if (dataflow.getSequences() != null) { 820 tables.addAll(dataflow.getSequences()); 821 } 822 } 823 824 Map<String, List<table>> tableMap = new HashMap<String, List<table>>(); 825 Map<String, String> tableTypeMap = new HashMap<String, String>(); 826 Map<String, String> tableIdMap = new HashMap<String, String>(); 827 828 Map<String, List<column>> columnMap = new HashMap<String, List<column>>(); 829 Map<String, Set<String>> tableColumnMap = new HashMap<String, Set<String>>(); 830 Map<String, String> columnIdMap = new HashMap<String, String>(); 831 Map<String, column> columnMergeIdMap = new HashMap<String, column>(); 832 833 List<procedure> procedures = new ArrayList<>(mergeDataflow.getProcedures()); 834 if (mergeDataflow.getPackages() != null) { 835 for (oraclePackage pkg : mergeDataflow.getPackages()) { 836 procedures.addAll(pkg.getProcedures()); 837 } 838 } 839 840 Set<String> procedureIdSet = procedures.stream().map(t -> t.getId()).collect(Collectors.toSet()); 841 842 for (table table : tables) { 843 String qualifiedTableName = DlineageUtil.getQualifiedTableName(table); 844 String tableFullName = DlineageUtil.getIdentifierNormalTableName(qualifiedTableName); 845 if ("variable".endsWith(table.getType()) && !SQLUtil.isEmpty(table.getParent())) { 846 tableFullName = table.getParent() + "." + tableFullName; 847 } 848 849 if (!tableMap.containsKey(tableFullName)) { 850 tableMap.put(tableFullName, new ArrayList<table>()); 851 } 852 853 tableMap.get(tableFullName).add(table); 854 855 if (!tableTypeMap.containsKey(tableFullName)) { 856 tableTypeMap.put(tableFullName, table.getType()); 857 } else if ("view".equals(table.getSubType())) { 858 tableTypeMap.put(tableFullName, table.getType()); 859 } else if ("database".equals(table.getSubType())) { 860 tableTypeMap.put(tableFullName, table.getType()); 861 } else if ("schema".equals(table.getSubType())) { 862 tableTypeMap.put(tableFullName, table.getType()); 863 } else if ("stage".equals(table.getSubType())) { 864 tableTypeMap.put(tableFullName, table.getType()); 865 } else if ("datasource".equals(table.getSubType())) { 866 tableTypeMap.put(tableFullName, table.getType()); 867 } else if ("stream".equals(table.getSubType())) { 868 tableTypeMap.put(tableFullName, table.getType()); 869 } else if ("file".equals(table.getSubType())) { 870 tableTypeMap.put(tableFullName, table.getType()); 871 } else if ("sequence".equals(table.getSubType())) { 872 tableTypeMap.put(tableFullName, table.getType()); 873 } else if ("table".equals(tableTypeMap.get(tableFullName))) { 874 tableTypeMap.put(tableFullName, table.getType()); 875 } else if ("variable".equals(tableTypeMap.get(tableFullName))) { 876 tableTypeMap.put(tableFullName, table.getType()); 877 } 878 879 if (table.getColumns() != null) { 880 if (!tableColumnMap.containsKey(tableFullName)) { 881 tableColumnMap.put(tableFullName, new LinkedHashSet<String>()); 882 } 883 for (column column : table.getColumns()) { 884 String columnFullName = tableFullName + "." 885 + DlineageUtil.getIdentifierNormalColumnName(column.getName()); 886 887 if (!columnMap.containsKey(columnFullName)) { 888 columnMap.put(columnFullName, new ArrayList<column>()); 889 tableColumnMap.get(tableFullName).add(columnFullName); 890 } 891 892 columnMap.get(columnFullName).add(column); 893 } 894 } 895 } 896 897 Iterator<String> tableNameIter = tableMap.keySet().iterator(); 898 while (tableNameIter.hasNext()) { 899 String tableName = tableNameIter.next(); 900 List<table> tableList = tableMap.get(tableName); 901 table table; 902 if (tableList.size() > 1) { 903 table standardTable = tableList.get(0); 904 //Function允许重名,不做合并处理 905 //检查列表中任意一个表是否为function,防止function表被合并丢失 906 boolean hasFunction = false; 907 for (table t : tableList) { 908 if (t.isFunction()) { 909 hasFunction = true; 910 break; 911 } 912 } 913 if (hasFunction) { 914 continue; 915 } 916 917 String type = tableTypeMap.get(tableName); 918 table = new table(); 919 table.setId(String.valueOf(++startId)); 920 table.setServer(standardTable.getServer()); 921 table.setDatabase(standardTable.getDatabase()); 922 table.setSchema(standardTable.getSchema()); 923 table.setName(standardTable.getName()); 924 table.setDisplayName(standardTable.getDisplayName()); 925 table.setParent(standardTable.getParent()); 926 table.setColumns(new ArrayList<column>()); 927 String subType = null; 928 for(table item: tableList){ 929 if (item.getSubType() != null) { 930 subType = item.getSubType(); 931 break; 932 } 933 } 934 if (subType != null) { 935 table.setSubType(subType); 936 } else { 937 table.setSubType(standardTable.getSubType()); 938 } 939 mergeEndpointClassification(table, tableList); 940 Set<String> processIds = new LinkedHashSet<String>(); 941 for (int k = 0; k < tableList.size(); k++) { 942 if (tableList.get(k).getProcessIds() != null) { 943 processIds.addAll(tableList.get(k).getProcessIds()); 944 } 945 } 946 if (!processIds.isEmpty()) { 947 table.setProcessIds(new ArrayList<String>(processIds)); 948 } 949 table.setType(type); 950 for (table item : tableList) { 951 if (!SQLUtil.isEmpty(table.getCoordinate()) && !SQLUtil.isEmpty(item.getCoordinate())) { 952 if (table.getCoordinate().indexOf(item.getCoordinate()) == -1) { 953 table.appendCoordinate(item.getCoordinate()); 954 } 955 } else if (!SQLUtil.isEmpty(item.getCoordinate())) { 956 table.setCoordinate(item.getCoordinate()); 957 } 958 959 if (!SQLUtil.isEmpty(table.getAlias()) && !SQLUtil.isEmpty(item.getAlias())) { 960 table.setAlias(table.getAlias() + "," + item.getAlias()); 961 } else if (!SQLUtil.isEmpty(item.getAlias())) { 962 table.setAlias(item.getAlias()); 963 } 964 965 tableIdMap.put(item.getId(), table.getId()); 966 967 if (item.isView()) { 968 mergeDataflow.getViews().remove(item); 969 } else if (item.isDatabaseType()) { 970 mergeDataflow.getDatabases().remove(item); 971 } else if (item.isSchemaType()) { 972 mergeDataflow.getSchemas().remove(item); 973 } else if (item.isStage()) { 974 mergeDataflow.getStages().remove(item); 975 } else if (item.isDataSource()) { 976 mergeDataflow.getDatasources().remove(item); 977 } else if (item.isStream()) { 978 mergeDataflow.getStreams().remove(item); 979 } else if (item.isFile()) { 980 mergeDataflow.getPaths().remove(item); 981 } else if (item.isVariable()) { 982 mergeDataflow.getVariables().remove(item); 983 } else if (item.isTable()) { 984 mergeDataflow.getTables().remove(item); 985 } else if (item.isResultSet()) { 986 mergeDataflow.getResultsets().remove(item); 987 } else if (item.isSequence()) { 988 mergeDataflow.getSequences().remove(item); 989 } 990 } 991 992 if (table.isView()) { 993 mergeDataflow.getViews().add(table); 994 } else if (table.isDatabaseType()) { 995 mergeDataflow.getDatabases().add(table); 996 } else if (table.isSchemaType()) { 997 mergeDataflow.getSchemas().add(table); 998 } else if (table.isStage()) { 999 mergeDataflow.getStages().add(table); 1000 } else if (table.isDataSource()) { 1001 mergeDataflow.getDatasources().add(table); 1002 } else if (table.isStream()) { 1003 mergeDataflow.getStreams().add(table); 1004 } else if (table.isFile()) { 1005 mergeDataflow.getPaths().add(table); 1006 } else if (table.isVariable()) { 1007 mergeDataflow.getVariables().add(table); 1008 } else if (table.isResultSet()) { 1009 mergeDataflow.getResultsets().add(table); 1010 } else if (table.isSequence()) { 1011 mergeDataflow.getSequences().add(table); 1012 } else { 1013 mergeDataflow.getTables().add(table); 1014 } 1015 } else { 1016 table = tableList.get(0); 1017 } 1018 1019 Set<String> columns = tableColumnMap.get(tableName); 1020 Iterator<String> columnIter = columns.iterator(); 1021 List<column> mergeColumns = new ArrayList<column>(); 1022 while (columnIter.hasNext()) { 1023 String columnName = columnIter.next(); 1024 List<column> columnList = columnMap.get(columnName); 1025 List<column> functions = new ArrayList<column>(); 1026 for (column t : columnList) { 1027 if (Boolean.TRUE.toString().equals(t.getIsFunction())) { 1028 functions.add(t); 1029 } 1030 } 1031 if (functions != null && !functions.isEmpty()) { 1032 for (column function : functions) { 1033 mergeColumns.add(function); 1034 columnIdMap.put(function.getId(), function.getId()); 1035 columnMergeIdMap.put(function.getId(), function); 1036 } 1037 1038 columnList.removeAll(functions); 1039 } 1040 if (!columnList.isEmpty()) { 1041 column firstColumn = columnList.iterator().next(); 1042 if (columnList.size() > 1) { 1043 column mergeColumn = new column(); 1044 mergeColumn.setId(String.valueOf(++startId)); 1045 mergeColumn.setName(firstColumn.getName()); 1046 mergeColumn.setDisplayName(firstColumn.getDisplayName()); 1047 mergeColumn.setSource(firstColumn.getSource()); 1048 mergeColumn.setQualifiedTable(firstColumn.getQualifiedTable()); 1049 mergeColumns.add(mergeColumn); 1050 for (column item : columnList) { 1051 mergeColumn.appendCoordinate(item.getCoordinate()); 1052 columnIdMap.put(item.getId(), mergeColumn.getId()); 1053 //add by grq 2023.02.06 issue=I6DB5S 1054 if (item.getDataType() != null) { 1055 mergeColumn.setDataType(item.getDataType()); 1056 } 1057 if (item.isForeignKey() != null) { 1058 mergeColumn.setForeignKey(item.isForeignKey()); 1059 } 1060 if (item.isUnqiueKey() != null) { 1061 mergeColumn.setUnqiueKey(item.isUnqiueKey()); 1062 } 1063 if (item.isIndexKey() != null) { 1064 mergeColumn.setIndexKey(item.isIndexKey()); 1065 } 1066 if (item.isPrimaryKey() != null) { 1067 mergeColumn.setPrimaryKey(item.isPrimaryKey()); 1068 } 1069 //end by grq 1070 } 1071 columnMergeIdMap.put(mergeColumn.getId(), mergeColumn); 1072 } else { 1073 mergeColumns.add(firstColumn); 1074 columnIdMap.put(firstColumn.getId(), firstColumn.getId()); 1075 columnMergeIdMap.put(firstColumn.getId(), firstColumn); 1076 } 1077 } 1078 } 1079 table.setColumns(mergeColumns); 1080 } 1081 1082 Map<String, String> procedureNameToResultSetId = new HashMap<>(); 1083 for (dataflow df : dataflows) { 1084 if (df == null || df.getProcedures() == null) { 1085 continue; 1086 } 1087 1088 Map<String, String> rsIdToProcId = new HashMap<>(); 1089 if (df.getResultsets() != null) { 1090 for (table t : df.getResultsets()) { 1091 if (!t.isFunction() && t.getProcedureId() != null) { 1092 rsIdToProcId.put(t.getId(), t.getProcedureId()); 1093 } 1094 } 1095 } 1096 if (df.getTables() != null) { 1097 for (table t : df.getTables()) { 1098 if (t.getProcedureId() != null) { 1099 rsIdToProcId.put(t.getId(), t.getProcedureId()); 1100 } 1101 } 1102 } 1103 if (df.getVariables() != null) { 1104 for (table t : df.getVariables()) { 1105 if (t.getProcedureId() != null) { 1106 rsIdToProcId.put(t.getId(), t.getProcedureId()); 1107 } 1108 } 1109 } 1110 if (rsIdToProcId.isEmpty()) { 1111 continue; 1112 } 1113 1114 Map<String, String> procIdToName = new HashMap<>(); 1115 for (procedure proc : df.getProcedures()) { 1116 procIdToName.put(proc.getId(), DlineageUtil.getIdentifierNormalTableName(proc.getName())); 1117 } 1118 1119 for (Map.Entry<String, String> entry : rsIdToProcId.entrySet()) { 1120 String procName = procIdToName.get(entry.getValue()); 1121 if (procName != null) { 1122 String mergedRsId = tableIdMap.get(entry.getKey()); 1123 String resultSetId = mergedRsId != null ? mergedRsId : entry.getKey(); 1124 procedureNameToResultSetId.put(procName, resultSetId); 1125 } 1126 } 1127 } 1128 1129 List<table> allTables = dataflow.getAllTables(mergeDataflow); 1130 Map<String, table> allTableIdMap = new HashMap<>(allTables.size() * 4 / 3 + 1); 1131 for (table t : allTables) { 1132 allTableIdMap.put(t.getId(), t); 1133 } 1134 1135 Map<String, String> functionIdToResultSetId = new HashMap<>(); 1136 Map<String, column> funcColIdToResultSetCol = new HashMap<>(); 1137 for (table t : allTableIdMap.values()) { 1138 if (t.isFunction()) { 1139 String functionName = DlineageUtil.getIdentifierNormalTableName( 1140 DlineageUtil.getTableFullName(t.getName())); 1141 String resultSetId = procedureNameToResultSetId.get(functionName); 1142 if (resultSetId != null) { 1143 functionIdToResultSetId.put(t.getId(), resultSetId); 1144 table resultSet = allTableIdMap.get(resultSetId); 1145 if (resultSet != null && t.getColumns() != null && resultSet.getColumns() != null) { 1146 List<column> funcCols = t.getColumns(); 1147 List<column> rsCols = resultSet.getColumns(); 1148 int minSize = Math.min(funcCols.size(), rsCols.size()); 1149 for (int i = 0; i < minSize; i++) { 1150 funcColIdToResultSetCol.put(funcCols.get(i).getId(), rsCols.get(i)); 1151 } 1152 } 1153 } 1154 } 1155 } 1156 1157 if (mergeDataflow.getRelationships() != null) { 1158 Map<String, relationship> mergeRelations = new LinkedHashMap<String, relationship>(); 1159 for (int i = 0; i < mergeDataflow.getRelationships().size(); i++) { 1160 relationship relation = mergeDataflow.getRelationships().get(i); 1161 if (RelationshipType.call.name().equals(relation.getType())) { 1162 targetColumn target = relation.getCaller(); 1163 List<sourceColumn> sources = relation.getCallees(); 1164 if (target == null) { 1165 continue; 1166 } 1167 String mappedTargetId = tableIdMap.get(target.getId()); 1168 if (mappedTargetId != null) { 1169 target.setId(mappedTargetId); 1170 } 1171 1172 Set<sourceColumn> sourceSet = new LinkedHashSet<sourceColumn>(); 1173 if (sources != null) { 1174 for (sourceColumn source : sources) { 1175 String mappedSourceId = tableIdMap.get(source.getId()); 1176 if (mappedSourceId != null) { 1177 source.setId(mappedSourceId); 1178 } 1179 } 1180 sourceSet.addAll(sources); 1181 relation.setCallees(new ArrayList<sourceColumn>(sourceSet)); 1182 } 1183 1184 String key = relation.toDedupKey(); 1185 mergeRelations.putIfAbsent(key, relation); 1186 } else { 1187 targetColumn target = relation.getTarget(); 1188 if (target == null) { 1189 continue; 1190 } 1191 String mappedTargetParentId = tableIdMap.get(target.getParent_id()); 1192 if (mappedTargetParentId != null) { 1193 target.setParent_id(mappedTargetParentId); 1194 } 1195 1196 String mappedTargetColId = columnIdMap.get(target.getId()); 1197 if (mappedTargetColId != null) { 1198 target.setId(mappedTargetColId); 1199 column mergedCol = columnMergeIdMap.get(mappedTargetColId); 1200 if (mergedCol != null) { 1201 target.setCoordinate(mergedCol.getCoordinate()); 1202 } 1203 } 1204 1205 List<sourceColumn> sources = relation.getSources(); 1206 Set<sourceColumn> sourceSet = new LinkedHashSet<sourceColumn>(); 1207 // Same contract as DataFlowAnalyzer.mergeTables: dedup runs 1208 // under the merged column's stamped coordinate, but a source's 1209 // own usable reference-site coordinate is restored on the 1210 // surviving relation. 1211 Map<sourceColumn, String> referenceCoordinates = new IdentityHashMap<sourceColumn, String>(); 1212 if (sources != null) { 1213 for (sourceColumn source : sources) { 1214 String mappedParentId = tableIdMap.get(source.getParent_id()); 1215 if (mappedParentId != null) { 1216 source.setParent_id(mappedParentId); 1217 } 1218 String mappedSourceId = tableIdMap.get(source.getSource_id()); 1219 if (mappedSourceId != null) { 1220 source.setSource_id(mappedSourceId); 1221 } 1222 String originalParentId = source.getParent_id(); 1223 if (!functionIdToResultSetId.isEmpty()) { 1224 String resultSetId = functionIdToResultSetId.get(originalParentId); 1225 if (resultSetId != null) { 1226 source.setParent_id(resultSetId); 1227 column matched = funcColIdToResultSetCol.get(source.getId()); 1228 if (matched != null) { 1229 source.setId(matched.getId()); 1230 source.setColumn(matched.getName()); 1231 } 1232 table resultSetTable = allTableIdMap.get(source.getParent_id()); 1233 if (resultSetTable != null) { 1234 source.setParent_name(resultSetTable.getName()); 1235 } 1236 } 1237 } 1238 String mappedColId = columnIdMap.get(source.getId()); 1239 if (mappedColId != null) { 1240 source.setId(mappedColId); 1241 column mergedCol = columnMergeIdMap.get(mappedColId); 1242 if (mergedCol != null) { 1243 String ownCoordinate = source.getCoordinate(); 1244 source.setCoordinate(mergedCol.getCoordinate()); 1245 if (DlineageUtil.hasUsableCoordinate(ownCoordinate)) { 1246 referenceCoordinates.put(source, ownCoordinate); 1247 } 1248 } 1249 } 1250 } 1251 1252 sourceSet.addAll(sources); 1253 relation.setSources(new ArrayList<sourceColumn>(sourceSet)); 1254 } 1255 1256 String key = relation.toDedupKey(); 1257 if (mergeRelations.putIfAbsent(key, relation) == null) { 1258 for (Map.Entry<sourceColumn, String> entry : referenceCoordinates.entrySet()) { 1259 entry.getKey().setCoordinate(entry.getValue()); 1260 } 1261 } 1262 } 1263 } 1264 1265 mergeDataflow.setRelationships(new ArrayList<relationship>(mergeRelations.values())); 1266 } 1267 1268 startId = addStarColumnBridgingRelations(mergeDataflow, startId); 1269 1270 tableMap.clear(); 1271 tableTypeMap.clear(); 1272 tableIdMap.clear(); 1273 columnMap.clear(); 1274 tableColumnMap.clear(); 1275 columnIdMap.clear(); 1276 columnMergeIdMap.clear(); 1277 tables.clear(); 1278 1279 return mergeDataflow; 1280 } 1281 1282 /** 1283 * 合并后同一张表可能同时拥有 * 列(来自只做 SELECT * 的来源)和具名列(来自显式引用列名的来源), 1284 * 但缺少 * -> 具名列 的 fdd 关系,会导致 上游.* -> 表.* 与 表.具名列 -> 下游.具名列 之间链路断开。 1285 * 本方法为这类表补齐 * -> 每个具名列 的 fdd 桥接(已存在则跳过;忽略 system / function 列)。 1286 */ 1287 private static long addStarColumnBridgingRelations(dataflow df, long startId) { 1288 if (df == null) { 1289 return startId; 1290 } 1291 Map<String, Set<String>> bridgeIndex = new HashMap<>(); 1292 if (df.getRelationships() != null) { 1293 for (relationship rel : df.getRelationships()) { 1294 if (!RelationshipType.fdd.name().equals(rel.getType())) { 1295 continue; 1296 } 1297 if (rel.getTarget() == null || rel.getSources() == null) { 1298 continue; 1299 } 1300 String parentId = rel.getTarget().getParent_id(); 1301 if (parentId == null) { 1302 continue; 1303 } 1304 for (sourceColumn sc : rel.getSources()) { 1305 if (parentId.equals(sc.getParent_id())) { 1306 bridgeIndex.computeIfAbsent(parentId, k -> new HashSet<>()) 1307 .add(rel.getTarget().getId() + "<-" + sc.getId()); 1308 } 1309 } 1310 } 1311 } 1312 1313 List<relationship> newRelations = new ArrayList<>(); 1314 for (table t : dataflow.getAllTables(df)) { 1315 if (t.getColumns() == null || t.getColumns().size() < 2) { 1316 continue; 1317 } 1318 column starColumn = null; 1319 List<column> regularColumns = new ArrayList<>(); 1320 for (column c : t.getColumns()) { 1321 if ("system".equals(c.getSource())) { 1322 continue; 1323 } 1324 if (Boolean.TRUE.toString().equals(c.getIsFunction())) { 1325 continue; 1326 } 1327 if ("*".equals(c.getName())) { 1328 if (starColumn == null) { 1329 starColumn = c; 1330 } 1331 } else { 1332 regularColumns.add(c); 1333 } 1334 } 1335 if (starColumn == null || regularColumns.isEmpty()) { 1336 continue; 1337 } 1338 1339 Set<String> existingBridges = bridgeIndex.getOrDefault(t.getId(), Collections.emptySet()); 1340 1341 String starId = starColumn.getId(); 1342 for (column target : regularColumns) { 1343 if (existingBridges.contains(target.getId() + "<-" + starId)) { 1344 continue; 1345 } 1346 relationship rel = new relationship(); 1347 rel.setId(String.valueOf(++startId)); 1348 rel.setType(RelationshipType.fdd.name()); 1349 rel.setEffectType("expand_star"); 1350 1351 targetColumn tc = new targetColumn(); 1352 tc.setId(target.getId()); 1353 tc.setColumn(target.getName()); 1354 tc.setParent_id(t.getId()); 1355 tc.setParent_name(t.getName()); 1356 rel.setTarget(tc); 1357 1358 sourceColumn sc = new sourceColumn(); 1359 sc.setId(starId); 1360 sc.setColumn(starColumn.getName()); 1361 sc.setParent_id(t.getId()); 1362 sc.setParent_name(t.getName()); 1363 rel.setSources(Arrays.asList(sc)); 1364 1365 newRelations.add(rel); 1366 } 1367 } 1368 if (!newRelations.isEmpty()) { 1369 List<relationship> all = df.getRelationships() != null 1370 ? new ArrayList<>(df.getRelationships()) 1371 : new ArrayList<>(); 1372 all.addAll(newRelations); 1373 df.setRelationships(all); 1374 } 1375 return startId; 1376 } 1377 1378 /** 1379 * 单个 dataflow 的 ID 跨度(与 ParallelDataFlowAnalyzer 中的常量保持一致)。 1380 */ 1381 private static final long DATAFLOW_ID_RANGE = 5000000L; 1382 1383 /** 1384 * 合并一组 startId 都为 0(即 ID 空间相互冲突)的 dataflow。 1385 * <p> 1386 * 标准合并 {@link #mergeDataflowsWithDifferentStartId(Collection, EDbVendor)} 的前提是 1387 * 每个 dataflow 在生成时已通过 {@code optionCopy.setStartId(5000000L * i)} 占用了 1388 * 不重叠的 ID 段;当外部调用方拿到的 dataflow 都是用默认 startId=0 生成时,直接合并 1389 * 会因为 ID 冲突而错乱。本方法会先按下标把每个 dataflow 的全部 ID 偏移 1390 * {@link #DATAFLOW_ID_RANGE} * index,再委托给标准合并逻辑。 1391 * <p> 1392 * 注意:此方法会原地修改入参 dataflow 的 ID(与标准合并行为一致)。 1393 */ 1394 public static dataflow mergeDataflows(Collection<dataflow> dataflows, EDbVendor vendor) { 1395 if (dataflows == null || dataflows.isEmpty()) { 1396 return null; 1397 } 1398 ModelBindingManager.setGlobalVendor(vendor); 1399 boolean optionSet = false; 1400 if (ModelBindingManager.getGlobalOption() == null) { 1401 Option option = new Option(); 1402 option.setVendor(vendor); 1403 ModelBindingManager.setGlobalOption(option); 1404 optionSet = true; 1405 } 1406 try { 1407 // ID 按下标 * DATAFLOW_ID_RANGE 偏移,避免 ID 冲突; 1408 // coordinate 第三维 fileIdx 按前面所有 dataflow 累积的源文件数偏移, 1409 // 使得合并后每个来源的 fileIdx 保持连续且不重叠。 1410 long cumulativeFileIdx = 0L; 1411 int i = 0; 1412 for (dataflow df : dataflows) { 1413 if (df != null) { 1414 int maxFileIdx = findMaxFileIdx(df); 1415 offsetDataflowIds(df, DATAFLOW_ID_RANGE * i, cumulativeFileIdx); 1416 if (maxFileIdx >= 0) { 1417 cumulativeFileIdx += (maxFileIdx + 1L); 1418 } 1419 } 1420 i++; 1421 } 1422 return mergeDataflowsWithDifferentStartId(dataflows, vendor); 1423 } finally { 1424 ModelBindingManager.removeGlobalVendor(); 1425 if (optionSet) { 1426 ModelBindingManager.removeGlobalOption(); 1427 } 1428 } 1429 } 1430 1431 /** 1432 * 从一组临时 XML 文件迭代加载并合并 dataflow,节约内存。 1433 * <p> 1434 * 与 {@link #iterativeMergeDataflowsFromFilesByStartId(List, long)} 不同的是,本方法假定文件中的 1435 * dataflow 是用默认 startId=0 生成的(ID 空间互相冲突),因此会按下标先做 ID 偏移再合并。 1436 * <p> 1437 * 注意:本方法按入参 {@code tempFiles} 顺序迭代合并,不做任何排序。 1438 * coordinate 第三维 fileIdx 的偏移也是严格按入参顺序累积的, 1439 * 调用方传入的顺序应与原始源文件的语义顺序保持一致。 1440 */ 1441 public static dataflow iterativeMergeDataflowsFromFiles(List<File> tempFiles, EDbVendor vendor) { 1442 if (tempFiles == null || tempFiles.isEmpty()) { 1443 return null; 1444 } 1445 ModelBindingManager.setGlobalVendor(vendor); 1446 boolean optionSet = false; 1447 if (ModelBindingManager.getGlobalOption() == null) { 1448 Option option = new Option(); 1449 option.setVendor(vendor); 1450 ModelBindingManager.setGlobalOption(option); 1451 optionSet = true; 1452 } 1453 try { 1454 return iterativeMergeFromFiles(tempFiles, DATAFLOW_ID_RANGE * tempFiles.size(), true); 1455 } finally { 1456 ModelBindingManager.removeGlobalVendor(); 1457 if (optionSet) { 1458 ModelBindingManager.removeGlobalOption(); 1459 } 1460 } 1461 } 1462 1463 /** 1464 * 从一组临时 XML 文件迭代加载并合并 dataflow,调用方需保证文件中的 dataflow ID 空间互不冲突, 1465 * 且已设置好 ModelBindingManager。 1466 * <p> 1467 * 主要供 {@code ParallelDataFlowAnalyzer} 等内部生成方使用:每个临时文件中的 dataflow 已通过 1468 * {@code optionCopy.setStartId(DATAFLOW_ID_RANGE * i)} 占用了不重叠的 ID 段。 1469 * <p> 1470 * 注意:本方法按入参 {@code tempFiles} 顺序迭代合并,不做任何排序。 1471 * coordinate 第三维 fileIdx 的偏移严格按入参顺序累积。 1472 */ 1473 public static dataflow iterativeMergeDataflowsFromFilesByStartId(List<File> tempFiles, long startId) { 1474 return iterativeMergeFromFiles(tempFiles, startId, false); 1475 } 1476 1477 private static dataflow iterativeMergeFromFiles(List<File> tempFiles, long startId, boolean offsetIds) { 1478 if (tempFiles == null || tempFiles.isEmpty()) { 1479 logger.warn("iterativeMergeDataflowsFromFiles: tempFiles is null or empty"); 1480 return null; 1481 } 1482 1483 logger.info("iterativeMergeDataflowsFromFiles: total dataflows: " + tempFiles.size()); 1484 1485 long startTime = System.currentTimeMillis(); 1486 int totalIterations = tempFiles.size() - 1; 1487 logger.info("iterativeMergeDataflowsFromFiles: start merging, total iterations: " + totalIterations); 1488 1489 long loadStartTime = System.currentTimeMillis(); 1490 dataflow mergedDataflow = XML2Model.loadXML(dataflow.class, tempFiles.get(0)); 1491 long cumulativeFileIdx = 0L; 1492 if (mergedDataflow != null) { 1493 long idOffset = offsetIds ? 0L : 0L; 1494 offsetDataflowIds(mergedDataflow, idOffset, 0L); 1495 int maxFileIdx = findMaxFileIdx(mergedDataflow); 1496 if (maxFileIdx >= 0) { 1497 cumulativeFileIdx = maxFileIdx + 1L; 1498 } 1499 } 1500 long loadEndTime = System.currentTimeMillis(); 1501 int initialRelationCount = mergedDataflow != null && mergedDataflow.getRelationships() != null ? mergedDataflow.getRelationships().size() : 0; 1502 logger.info("iterativeMergeDataflowsFromFiles: loaded initial dataflow, relation count: " + initialRelationCount + ", time: " + (loadEndTime - loadStartTime) + "ms"); 1503 1504 Set<Long> mergedDedupKeys = new HashSet<>(Math.max(initialRelationCount, 16)); 1505 if (mergedDataflow != null && mergedDataflow.getRelationships() != null) { 1506 for (relationship rel : mergedDataflow.getRelationships()) { 1507 mergedDedupKeys.add(rel.toDedupHash()); 1508 } 1509 } 1510 1511 long currentMaxId = startId; 1512 for (int i = 1; i < tempFiles.size(); i++) { 1513 long iterationStartTime = System.currentTimeMillis(); 1514 int currentIteration = i; 1515 int remainingIterations = totalIterations - currentIteration + 1; 1516 1517 logger.info("iterativeMergeDataflowsFromFiles: iteration " + currentIteration + "/" + totalIterations + ", remaining: " + remainingIterations); 1518 1519 long loadCurrentStartTime = System.currentTimeMillis(); 1520 dataflow currentDataflow = XML2Model.loadXML(dataflow.class, tempFiles.get(i)); 1521 if (currentDataflow != null) { 1522 int maxFileIdx = findMaxFileIdx(currentDataflow); 1523 long idOffset = offsetIds ? (DATAFLOW_ID_RANGE * i) : 0L; 1524 offsetDataflowIds(currentDataflow, idOffset, cumulativeFileIdx); 1525 if (maxFileIdx >= 0) { 1526 cumulativeFileIdx += (maxFileIdx + 1L); 1527 } 1528 } 1529 long loadCurrentEndTime = System.currentTimeMillis(); 1530 int currentRelationCount = currentDataflow != null && currentDataflow.getRelationships() != null ? currentDataflow.getRelationships().size() : 0; 1531 logger.info("iterativeMergeDataflowsFromFiles: loaded dataflow[" + i + "], relation count: " + currentRelationCount + ", time: " + (loadCurrentEndTime - loadCurrentStartTime) + "ms"); 1532 1533 long mergeStartTime = System.currentTimeMillis(); 1534 //autogenerate 首个 dataflow 加载失败(mergedDataflow==null)时,以首个可用 dataflow 作为合并基准并初始化去重索引 1535 if (mergedDataflow == null && currentDataflow != null) { 1536 mergedDataflow = currentDataflow; 1537 if (mergedDataflow.getRelationships() != null) { 1538 for (relationship rel : mergedDataflow.getRelationships()) { 1539 mergedDedupKeys.add(rel.toDedupHash()); 1540 } 1541 } 1542 } else { 1543 currentMaxId = incrementalMergeDataflow(mergedDataflow, currentDataflow, currentMaxId, mergedDedupKeys); 1544 } 1545 long mergeEndTime = System.currentTimeMillis(); 1546 int mergedRelationCount = mergedDataflow != null && mergedDataflow.getRelationships() != null ? mergedDataflow.getRelationships().size() : 0; 1547 1548 long iterationEndTime = System.currentTimeMillis(); 1549 long iterationTime = iterationEndTime - iterationStartTime; 1550 long mergeTime = mergeEndTime - mergeStartTime; 1551 logger.info("iterativeMergeDataflowsFromFiles: iteration " + currentIteration + " completed, merged relation count: " + mergedRelationCount + ", merge time: " + mergeTime + "ms, total iteration time: " + iterationTime + "ms"); 1552 1553 currentDataflow = null; 1554 } 1555 1556 long endTime = System.currentTimeMillis(); 1557 long totalTime = endTime - startTime; 1558 int finalRelationCount = mergedDataflow != null && mergedDataflow.getRelationships() != null ? mergedDataflow.getRelationships().size() : 0; 1559 logger.info("iterativeMergeDataflowsFromFiles: all iterations completed, final relation count: " + finalRelationCount + ", total time: " + totalTime + "ms (" + (totalTime / 1000.0) + "s)"); 1560 1561 return mergedDataflow; 1562 } 1563 1564 /** 1565 * 增量合并:将 {@code currentDataflow} 合并到 {@code mergedDataflow} 中,原地修改 mergedDataflow。 1566 * <p> 1567 * 与 {@link #mergeDataflowsByStartId} 不同,本方法保持 mergedDataflow 中已有表/列的 ID 不变, 1568 * 只将 currentDataflow 中同名表/列的属性追加到已有表/列上,已有关系不做任何处理。 1569 * 仅对 currentDataflow 的关系做 ID 重映射和去重后追加到 mergedDataflow。 1570 * 1571 * @param mergedDataflow 已合并的 dataflow(原地修改) 1572 * @param currentDataflow 新加载的 dataflow 1573 * @param currentMaxId 当前最大 ID,用于生成新 ID 1574 * @param mergedDedupKeys 已有关系的去重 hash 集合(原地修改) 1575 * @return 更新后的最大 ID 1576 */ 1577 private static long incrementalMergeDataflow(dataflow mergedDataflow, dataflow currentDataflow, 1578 long currentMaxId, Set<Long> mergedDedupKeys) { 1579 //autogenerate mergedDataflow 为 null 时不可解引用,避免首个 dataflow 加载失败引发 NPE 1580 if (currentDataflow == null || mergedDataflow == null) { 1581 return currentMaxId; 1582 } 1583 1584 // 1. 构建 mergedDataflow 已有表的名称索引 1585 Map<String, table> existingTableMap = new LinkedHashMap<>(); 1586 for (table t : dataflow.getAllTables(mergedDataflow)) { 1587 String tableFullName = getTableFullNameKey(t); 1588 if (!existingTableMap.containsKey(tableFullName)) { 1589 existingTableMap.put(tableFullName, t); 1590 } 1591 } 1592 1593 // 2. 处理 currentDataflow 的表和列,构建 ID 映射(只映射新ID → 已有ID) 1594 Map<String, String> tableIdMap = new HashMap<>(); 1595 Map<String, String> columnIdMap = new HashMap<>(); 1596 Map<String, column> columnMergeIdMap = new HashMap<>(); 1597 Set<String> newOrModifiedTableIds = new HashSet<>(); 1598 1599 for (table t : dataflow.getAllTables(currentDataflow)) { 1600 String tableFullName = getTableFullNameKey(t); 1601 table existingTable = existingTableMap.get(tableFullName); 1602 1603 if (existingTable != null && !t.isFunction()) { 1604 // 同名表:合并到已有表,已有表 ID 保持不变 1605 tableIdMap.put(t.getId(), existingTable.getId()); 1606 mergeTableProperties(existingTable, t); 1607 mergeColumnsIncremental(existingTable, t, columnIdMap, columnMergeIdMap); 1608 newOrModifiedTableIds.add(existingTable.getId()); 1609 } else { 1610 // 新表:直接追加到 mergedDataflow 1611 addTableToMergedDataflow(mergedDataflow, t); 1612 existingTableMap.put(tableFullName, t); 1613 tableIdMap.put(t.getId(), t.getId()); 1614 newOrModifiedTableIds.add(t.getId()); 1615 if (t.getColumns() != null) { 1616 for (column c : t.getColumns()) { 1617 columnIdMap.put(c.getId(), c.getId()); 1618 columnMergeIdMap.put(c.getId(), c); 1619 } 1620 } 1621 } 1622 } 1623 1624 // 3. 追加其他实体 1625 appendOtherEntities(mergedDataflow, currentDataflow); 1626 1627 // 4. 构建 function→resultSet 映射(基于合并后的完整数据) 1628 Map<String, String> functionIdToResultSetId = new HashMap<>(); 1629 Map<String, column> funcColIdToResultSetCol = new HashMap<>(); 1630 Map<String, table> allTableIdMap = new HashMap<>(); 1631 buildFunctionResultSetMapping(mergedDataflow, functionIdToResultSetId, 1632 funcColIdToResultSetCol, allTableIdMap); 1633 1634 // 5. 只处理 currentDataflow 的关系 1635 if (currentDataflow.getRelationships() != null) { 1636 List<relationship> newRelations = new ArrayList<>(); 1637 for (relationship rel : currentDataflow.getRelationships()) { 1638 remapAndDedupRelation(rel, tableIdMap, columnIdMap, columnMergeIdMap, 1639 functionIdToResultSetId, funcColIdToResultSetCol, allTableIdMap, 1640 mergedDedupKeys, newRelations); 1641 } 1642 if (!newRelations.isEmpty()) { 1643 if (mergedDataflow.getRelationships() == null) { 1644 mergedDataflow.setRelationships(new ArrayList<relationship>()); 1645 } 1646 mergedDataflow.getRelationships().addAll(newRelations); 1647 } 1648 } 1649 1650 // 6. 为新表或修改过的表补齐 * 列桥接关系 1651 if (!newOrModifiedTableIds.isEmpty()) { 1652 currentMaxId = addStarColumnBridgingRelationsForTables(mergedDataflow, currentMaxId, 1653 newOrModifiedTableIds); 1654 } 1655 1656 return currentMaxId; 1657 } 1658 1659 /** 1660 * 获取表的全名 key,用于同名表匹配。 1661 */ 1662 private static String getTableFullNameKey(table table) { 1663 String qualifiedTableName = DlineageUtil.getQualifiedTableName(table); 1664 String tableFullName = DlineageUtil.getIdentifierNormalTableName(qualifiedTableName); 1665 if ("variable".endsWith(table.getType()) && !SQLUtil.isEmpty(table.getParent())) { 1666 tableFullName = table.getParent() + "." + tableFullName; 1667 } 1668 return tableFullName; 1669 } 1670 1671 /** 1672 * 将 newTable 的属性合并到 existingTable 上(已有表 ID 保持不变)。 1673 */ 1674 private static void mergeTableProperties(table existingTable, table newTable) { 1675 if (!SQLUtil.isEmpty(newTable.getCoordinate())) { 1676 if (SQLUtil.isEmpty(existingTable.getCoordinate())) { 1677 existingTable.setCoordinate(newTable.getCoordinate()); 1678 } else if (existingTable.getCoordinate().indexOf(newTable.getCoordinate()) == -1) { 1679 existingTable.appendCoordinate(newTable.getCoordinate()); 1680 } 1681 } 1682 if (!SQLUtil.isEmpty(newTable.getAlias())) { 1683 if (SQLUtil.isEmpty(existingTable.getAlias())) { 1684 existingTable.setAlias(newTable.getAlias()); 1685 } else if (!existingTable.getAlias().contains(newTable.getAlias())) { 1686 existingTable.setAlias(existingTable.getAlias() + "," + newTable.getAlias()); 1687 } 1688 } 1689 if (newTable.getProcessIds() != null) { 1690 if (existingTable.getProcessIds() == null) { 1691 existingTable.setProcessIds(new ArrayList<>(newTable.getProcessIds())); 1692 } else { 1693 Set<String> merged = new LinkedHashSet<>(existingTable.getProcessIds()); 1694 merged.addAll(newTable.getProcessIds()); 1695 existingTable.setProcessIds(new ArrayList<>(merged)); 1696 } 1697 } 1698 if (newTable.getProcedureId() != null && existingTable.getProcedureId() == null) { 1699 existingTable.setProcedureId(newTable.getProcedureId()); 1700 } 1701 mergeEndpointClassificationPair(existingTable, newTable); 1702 } 1703 1704 /** Canonical outermost-first order of {@code templatedParts} labels. */ 1705 private static final String[] TEMPLATED_PART_ORDER = { "server", "db", "schema", "name" }; 1706 1707 private static final String DYNAMIC_TEMPLATE_KIND = "DYNAMIC_TEMPLATE"; 1708 1709 /** 1710 * Carries the authoritative endpoint classification through a same-name merge instead of 1711 * silently dropping it (docs/tmp/dynamic-template-endpoint-provenance.md, merge finding). 1712 * The name-derived facts (kind, normalizedName) and the create-effect facts 1713 * (createdInSql, endpointIntroduction) survive as "any occurrence states it". 1714 * 1715 * <p>Template provenance is stricter, and "templated" means {@code templatedParts} is 1716 * present — NOT {@code endpointKind == DYNAMIC_TEMPLATE}, because a templated literal-temp 1717 * ({@code [#t_' + @X + ']}) keeps its temp kind while carrying parts. Parts survive only 1718 * when EVERY occurrence is templated; one plain occurrence means a STATIC statement also 1719 * references this name, so the merged node keeps the PLAIN occurrence's kind (making the 1720 * outcome independent of merge order and merge API) and drops the template claim — the 1721 * shared-node contract. 1722 */ 1723 private static void mergeEndpointClassification(table merged, List<table> occurrences) { 1724 boolean created = false; 1725 String introduction = null; 1726 String normalized = null; 1727 String templatedKind = null; 1728 String plainKind = null; 1729 boolean anyTemplate = false; 1730 boolean allTemplate = true; 1731 Set<String> parts = new LinkedHashSet<String>(); 1732 for (table occurrence : occurrences) { 1733 // non-identifier-compare: fixed enum-name/flag wire values, not DB object names 1734 if ("true".equals(occurrence.getCreatedInSql())) { 1735 created = true; 1736 } 1737 if (introduction == null) { 1738 introduction = occurrence.getEndpointIntroduction(); 1739 } 1740 if (normalized == null) { 1741 normalized = occurrence.getNormalizedName(); 1742 } 1743 if (occurrence.getTemplatedParts() != null) { 1744 anyTemplate = true; 1745 addTemplatedParts(parts, occurrence.getTemplatedParts()); 1746 if (templatedKind == null) { 1747 templatedKind = occurrence.getEndpointKind(); 1748 } 1749 } else { 1750 allTemplate = false; 1751 if (plainKind == null) { 1752 plainKind = occurrence.getEndpointKind(); 1753 } 1754 } 1755 } 1756 if (created) { 1757 merged.setCreatedInSql("true"); 1758 } 1759 if (introduction != null) { 1760 merged.setEndpointIntroduction(introduction); 1761 } 1762 if (normalized != null) { 1763 merged.setNormalizedName(normalized); 1764 } 1765 if (anyTemplate && allTemplate) { 1766 merged.setTemplatedParts(joinTemplatedParts(parts)); 1767 if (templatedKind != null) { 1768 merged.setEndpointKind(templatedKind); 1769 } 1770 } else if (plainKind != null) { 1771 // No template, or mixed static/templated: the plain occurrence's kind stands. 1772 merged.setEndpointKind(plainKind); 1773 } 1774 } 1775 1776 /** Pairwise form of {@link #mergeEndpointClassification} for the incremental merge. */ 1777 private static void mergeEndpointClassificationPair(table existingTable, table newTable) { 1778 // non-identifier-compare: fixed enum-name/flag wire values, not DB object names 1779 if ("true".equals(newTable.getCreatedInSql()) && existingTable.getCreatedInSql() == null) { 1780 existingTable.setCreatedInSql("true"); 1781 } 1782 if (existingTable.getEndpointIntroduction() == null) { 1783 existingTable.setEndpointIntroduction(newTable.getEndpointIntroduction()); 1784 } 1785 if (existingTable.getNormalizedName() == null) { 1786 existingTable.setNormalizedName(newTable.getNormalizedName()); 1787 } 1788 boolean existingTemplate = existingTable.getTemplatedParts() != null; 1789 boolean incomingTemplate = newTable.getTemplatedParts() != null; 1790 if (existingTemplate && incomingTemplate) { 1791 Set<String> parts = new LinkedHashSet<String>(); 1792 addTemplatedParts(parts, existingTable.getTemplatedParts()); 1793 addTemplatedParts(parts, newTable.getTemplatedParts()); 1794 existingTable.setTemplatedParts(joinTemplatedParts(parts)); 1795 if (existingTable.getEndpointKind() == null) { 1796 existingTable.setEndpointKind(newTable.getEndpointKind()); 1797 } 1798 } else if (existingTemplate) { 1799 // A plain occurrence of the same name arrived: mixed static/templated. The 1800 // template claim is withdrawn and the PLAIN side's kind stands — the same 1801 // outcome the reverse arrival order and the batch merge produce. Monotone: 1802 // once withdrawn, a later templated occurrence stays out. 1803 existingTable.setTemplatedParts(null); 1804 existingTable.setEndpointKind(newTable.getEndpointKind()); 1805 } else if (incomingTemplate) { 1806 // Mixed, other order: keep the existing (plain) side's kind; never adopt the 1807 // templated side's kind or parts. 1808 } else if (existingTable.getEndpointKind() == null) { 1809 existingTable.setEndpointKind(newTable.getEndpointKind()); 1810 } 1811 } 1812 1813 private static void addTemplatedParts(Set<String> parts, String joined) { 1814 if (SQLUtil.isEmpty(joined)) { 1815 return; 1816 } 1817 for (String part : joined.split("\\|")) { 1818 if (!part.isEmpty()) { 1819 parts.add(part); 1820 } 1821 } 1822 } 1823 1824 private static String joinTemplatedParts(Set<String> parts) { 1825 StringBuilder sb = new StringBuilder(); 1826 for (String part : TEMPLATED_PART_ORDER) { 1827 if (parts.contains(part)) { 1828 if (sb.length() > 0) { 1829 sb.append('|'); 1830 } 1831 sb.append(part); 1832 } 1833 } 1834 return sb.length() == 0 ? null : sb.toString(); 1835 } 1836 1837 /** 1838 * 将 newTable 的列合并到 existingTable 上(已有列 ID 保持不变)。 1839 */ 1840 private static void mergeColumnsIncremental(table existingTable, table newTable, 1841 Map<String, String> columnIdMap, 1842 Map<String, column> columnMergeIdMap) { 1843 if (newTable.getColumns() == null) { 1844 return; 1845 } 1846 if (existingTable.getColumns() == null) { 1847 existingTable.setColumns(new ArrayList<column>()); 1848 } 1849 1850 Map<String, column> existingColMap = new LinkedHashMap<>(); 1851 for (column c : existingTable.getColumns()) { 1852 existingColMap.put(c.getName(), c); 1853 } 1854 1855 for (column newCol : newTable.getColumns()) { 1856 if (Boolean.TRUE.toString().equals(newCol.getIsFunction())) { 1857 // function 列直接追加,不做合并 1858 existingTable.getColumns().add(newCol); 1859 columnIdMap.put(newCol.getId(), newCol.getId()); 1860 columnMergeIdMap.put(newCol.getId(), newCol); 1861 continue; 1862 } 1863 1864 column existingCol = existingColMap.get(newCol.getName()); 1865 if (existingCol != null) { 1866 // 同名列:合并到已有列,已有列 ID 保持不变 1867 columnIdMap.put(newCol.getId(), existingCol.getId()); 1868 if (!SQLUtil.isEmpty(newCol.getCoordinate())) { 1869 if (SQLUtil.isEmpty(existingCol.getCoordinate())) { 1870 existingCol.setCoordinate(newCol.getCoordinate()); 1871 } else if (existingCol.getCoordinate().indexOf(newCol.getCoordinate()) == -1) { 1872 existingCol.appendCoordinate(newCol.getCoordinate()); 1873 } 1874 } 1875 if (newCol.getDataType() != null) { 1876 existingCol.setDataType(newCol.getDataType()); 1877 } 1878 if (newCol.isForeignKey() != null) { 1879 existingCol.setForeignKey(newCol.isForeignKey()); 1880 } 1881 if (newCol.isUnqiueKey() != null) { 1882 existingCol.setUnqiueKey(newCol.isUnqiueKey()); 1883 } 1884 if (newCol.isIndexKey() != null) { 1885 existingCol.setIndexKey(newCol.isIndexKey()); 1886 } 1887 if (newCol.isPrimaryKey() != null) { 1888 existingCol.setPrimaryKey(newCol.isPrimaryKey()); 1889 } 1890 } else { 1891 // 新列:直接追加 1892 existingTable.getColumns().add(newCol); 1893 existingColMap.put(newCol.getName(), newCol); 1894 columnIdMap.put(newCol.getId(), newCol.getId()); 1895 columnMergeIdMap.put(newCol.getId(), newCol); 1896 } 1897 } 1898 } 1899 1900 /** 1901 * 将 table 按类型追加到 mergedDataflow 对应的列表中。 1902 */ 1903 private static void addTableToMergedDataflow(dataflow df, table t) { 1904 if (t.isView()) { 1905 if (df.getViews() == null) df.setViews(new ArrayList<table>()); 1906 df.getViews().add(t); 1907 } else if (t.isDatabaseType()) { 1908 if (df.getDatabases() == null) df.setDatabases(new ArrayList<table>()); 1909 df.getDatabases().add(t); 1910 } else if (t.isSchemaType()) { 1911 if (df.getSchemas() == null) df.setSchemas(new ArrayList<table>()); 1912 df.getSchemas().add(t); 1913 } else if (t.isStage()) { 1914 if (df.getStages() == null) df.setStages(new ArrayList<table>()); 1915 df.getStages().add(t); 1916 } else if (t.isDataSource()) { 1917 if (df.getDatasources() == null) df.setDatasources(new ArrayList<table>()); 1918 df.getDatasources().add(t); 1919 } else if (t.isStream()) { 1920 if (df.getStreams() == null) df.setStreams(new ArrayList<table>()); 1921 df.getStreams().add(t); 1922 } else if (t.isFile()) { 1923 if (df.getPaths() == null) df.setPaths(new ArrayList<table>()); 1924 df.getPaths().add(t); 1925 } else if (t.isVariable()) { 1926 if (df.getVariables() == null) df.setVariables(new ArrayList<table>()); 1927 df.getVariables().add(t); 1928 } else if (t.isResultSet()) { 1929 if (df.getResultsets() == null) df.setResultsets(new ArrayList<table>()); 1930 df.getResultsets().add(t); 1931 } else if (t.isSequence()) { 1932 if (df.getSequences() == null) df.setSequences(new ArrayList<table>()); 1933 df.getSequences().add(t); 1934 } else { 1935 if (df.getTables() == null) df.setTables(new ArrayList<table>()); 1936 df.getTables().add(t); 1937 } 1938 } 1939 1940 /** 1941 * 将 currentDataflow 的非表实体追加到 mergedDataflow。 1942 */ 1943 private static void appendOtherEntities(dataflow mergedDataflow, dataflow currentDataflow) { 1944 if (currentDataflow.getProcesses() != null) { 1945 if (mergedDataflow.getProcesses() == null) { 1946 mergedDataflow.setProcesses(new ArrayList<process>()); 1947 } 1948 mergedDataflow.getProcesses().addAll(currentDataflow.getProcesses()); 1949 } 1950 if (currentDataflow.getProcedures() != null) { 1951 if (mergedDataflow.getProcedures() == null) { 1952 mergedDataflow.setProcedures(new ArrayList<procedure>()); 1953 } 1954 mergedDataflow.getProcedures().addAll(currentDataflow.getProcedures()); 1955 } 1956 if (currentDataflow.getPackages() != null) { 1957 if (mergedDataflow.getPackages() == null) { 1958 mergedDataflow.setPackages(new ArrayList<oraclePackage>()); 1959 } 1960 mergedDataflow.getPackages().addAll(currentDataflow.getPackages()); 1961 } 1962 if (currentDataflow.getErrors() != null) { 1963 if (mergedDataflow.getErrors() == null) { 1964 mergedDataflow.setErrors(new ArrayList<error>()); 1965 } 1966 mergedDataflow.getErrors().addAll(currentDataflow.getErrors()); 1967 if (mergedDataflow.getErrors().size() > 10000) { 1968 mergedDataflow.setErrors(mergedDataflow.getErrors().subList(0, 10000)); 1969 } 1970 } 1971 } 1972 1973 /** 1974 * 基于合并后的 dataflow 构建 function→resultSet 映射。 1975 */ 1976 private static void buildFunctionResultSetMapping(dataflow df, 1977 Map<String, String> functionIdToResultSetId, 1978 Map<String, column> funcColIdToResultSetCol, 1979 Map<String, table> allTableIdMap) { 1980 // 收集所有 procedure 1981 List<procedure> allProcedures = new ArrayList<>(); 1982 if (df.getProcedures() != null) { 1983 allProcedures.addAll(df.getProcedures()); 1984 } 1985 if (df.getPackages() != null) { 1986 for (oraclePackage pkg : df.getPackages()) { 1987 if (pkg.getProcedures() != null) { 1988 allProcedures.addAll(pkg.getProcedures()); 1989 } 1990 } 1991 } 1992 1993 // 构建 procedure name → id 映射 1994 Map<String, String> procNameToId = new HashMap<>(); 1995 for (procedure proc : allProcedures) { 1996 procNameToId.put(DlineageUtil.getIdentifierNormalTableName(proc.getName()), proc.getId()); 1997 } 1998 1999 // 构建 resultSet/procedureId → procedure name 映射 2000 Map<String, String> procedureNameToResultSetId = new HashMap<>(); 2001 List<table> allTables = dataflow.getAllTables(df); 2002 for (table t : allTables) { 2003 if (t.getProcedureId() != null && !t.isFunction()) { 2004 String procName = null; 2005 for (procedure proc : allProcedures) { 2006 if (proc.getId().equals(t.getProcedureId())) { 2007 procName = DlineageUtil.getIdentifierNormalTableName(proc.getName()); 2008 break; 2009 } 2010 } 2011 if (procName != null) { 2012 procedureNameToResultSetId.put(procName, t.getId()); 2013 } 2014 } 2015 } 2016 2017 // 构建 allTableIdMap 2018 for (table t : allTables) { 2019 allTableIdMap.put(t.getId(), t); 2020 } 2021 2022 // 构建 functionId → resultSetId 映射 2023 for (table t : allTables) { 2024 if (t.isFunction()) { 2025 String functionName = DlineageUtil.getIdentifierNormalTableName( 2026 DlineageUtil.getTableFullName(t.getName())); 2027 String resultSetId = procedureNameToResultSetId.get(functionName); 2028 if (resultSetId != null) { 2029 functionIdToResultSetId.put(t.getId(), resultSetId); 2030 table resultSet = allTableIdMap.get(resultSetId); 2031 if (resultSet != null && t.getColumns() != null && resultSet.getColumns() != null) { 2032 List<column> funcCols = t.getColumns(); 2033 List<column> rsCols = resultSet.getColumns(); 2034 int minSize = Math.min(funcCols.size(), rsCols.size()); 2035 for (int j = 0; j < minSize; j++) { 2036 funcColIdToResultSetCol.put(funcCols.get(j).getId(), rsCols.get(j)); 2037 } 2038 } 2039 } 2040 } 2041 } 2042 } 2043 2044 /** 2045 * 对单条关系做 ID 重映射和去重,如果未重复则加入 newRelations 列表。 2046 */ 2047 private static void remapAndDedupRelation(relationship rel, 2048 Map<String, String> tableIdMap, 2049 Map<String, String> columnIdMap, 2050 Map<String, column> columnMergeIdMap, 2051 Map<String, String> functionIdToResultSetId, 2052 Map<String, column> funcColIdToResultSetCol, 2053 Map<String, table> allTableIdMap, 2054 Set<Long> mergedDedupKeys, 2055 List<relationship> newRelations) { 2056 if (RelationshipType.call.name().equals(rel.getType())) { 2057 // call 类型关系 2058 targetColumn caller = rel.getCaller(); 2059 if (caller == null) { 2060 return; 2061 } 2062 String mappedCallerId = tableIdMap.get(caller.getId()); 2063 if (mappedCallerId != null) { 2064 caller.setId(mappedCallerId); 2065 } 2066 2067 List<sourceColumn> callees = rel.getCallees(); 2068 if (callees != null) { 2069 Set<sourceColumn> calleeSet = new LinkedHashSet<>(); 2070 for (sourceColumn callee : callees) { 2071 String mappedSourceId = tableIdMap.get(callee.getId()); 2072 if (mappedSourceId != null) { 2073 callee.setId(mappedSourceId); 2074 } 2075 } 2076 calleeSet.addAll(callees); 2077 rel.setCallees(new ArrayList<>(calleeSet)); 2078 } 2079 2080 if (mergedDedupKeys.add(rel.toDedupHash())) { 2081 newRelations.add(rel); 2082 } 2083 } else { 2084 // fdd/frd 等类型关系 2085 targetColumn target = rel.getTarget(); 2086 if (target == null) { 2087 return; 2088 } 2089 String mappedTargetParentId = tableIdMap.get(target.getParent_id()); 2090 if (mappedTargetParentId != null) { 2091 target.setParent_id(mappedTargetParentId); 2092 } 2093 String mappedTargetColId = columnIdMap.get(target.getId()); 2094 if (mappedTargetColId != null) { 2095 target.setId(mappedTargetColId); 2096 column mergedCol = columnMergeIdMap.get(mappedTargetColId); 2097 if (mergedCol != null) { 2098 target.setCoordinate(mergedCol.getCoordinate()); 2099 } 2100 } 2101 2102 List<sourceColumn> sources = rel.getSources(); 2103 // Same contract as DataFlowAnalyzer.mergeTables: dedup runs under the 2104 // merged column's stamped coordinate, but a source's own usable 2105 // reference-site coordinate is restored on the surviving relation. 2106 Map<sourceColumn, String> referenceCoordinates = new IdentityHashMap<>(); 2107 if (sources != null) { 2108 Set<sourceColumn> sourceSet = new LinkedHashSet<>(); 2109 for (sourceColumn source : sources) { 2110 String mappedParentId = tableIdMap.get(source.getParent_id()); 2111 if (mappedParentId != null) { 2112 source.setParent_id(mappedParentId); 2113 } 2114 String mappedSourceId = tableIdMap.get(source.getSource_id()); 2115 if (mappedSourceId != null) { 2116 source.setSource_id(mappedSourceId); 2117 } 2118 String originalParentId = source.getParent_id(); 2119 if (!functionIdToResultSetId.isEmpty()) { 2120 String resultSetId = functionIdToResultSetId.get(originalParentId); 2121 if (resultSetId != null) { 2122 source.setParent_id(resultSetId); 2123 column matched = funcColIdToResultSetCol.get(source.getId()); 2124 if (matched != null) { 2125 source.setId(matched.getId()); 2126 source.setColumn(matched.getName()); 2127 } 2128 table resultSetTable = allTableIdMap.get(source.getParent_id()); 2129 if (resultSetTable != null) { 2130 source.setParent_name(resultSetTable.getName()); 2131 } 2132 } 2133 } 2134 String mappedColId = columnIdMap.get(source.getId()); 2135 if (mappedColId != null) { 2136 source.setId(mappedColId); 2137 column mergedCol = columnMergeIdMap.get(mappedColId); 2138 if (mergedCol != null) { 2139 String ownCoordinate = source.getCoordinate(); 2140 source.setCoordinate(mergedCol.getCoordinate()); 2141 if (DlineageUtil.hasUsableCoordinate(ownCoordinate)) { 2142 referenceCoordinates.put(source, ownCoordinate); 2143 } 2144 } 2145 } 2146 } 2147 sourceSet.addAll(sources); 2148 rel.setSources(new ArrayList<>(sourceSet)); 2149 } 2150 2151 if (mergedDedupKeys.add(rel.toDedupHash())) { 2152 newRelations.add(rel); 2153 for (Map.Entry<sourceColumn, String> entry : referenceCoordinates.entrySet()) { 2154 entry.getKey().setCoordinate(entry.getValue()); 2155 } 2156 } 2157 } 2158 } 2159 2160 /** 2161 * 只对指定表做 * 列桥接,避免全量扫描。 2162 */ 2163 private static long addStarColumnBridgingRelationsForTables(dataflow df, long startId, 2164 Set<String> targetTableIds) { 2165 if (df == null || targetTableIds.isEmpty()) { 2166 return startId; 2167 } 2168 2169 // 构建已有桥接关系索引 2170 Map<String, Set<String>> bridgeIndex = new HashMap<>(); 2171 if (df.getRelationships() != null) { 2172 for (relationship rel : df.getRelationships()) { 2173 if (!RelationshipType.fdd.name().equals(rel.getType())) { 2174 continue; 2175 } 2176 if (rel.getTarget() == null || rel.getSources() == null) { 2177 continue; 2178 } 2179 String parentId = rel.getTarget().getParent_id(); 2180 if (parentId == null) { 2181 continue; 2182 } 2183 for (sourceColumn sc : rel.getSources()) { 2184 if (parentId.equals(sc.getParent_id())) { 2185 bridgeIndex.computeIfAbsent(parentId, k -> new HashSet<>()) 2186 .add(rel.getTarget().getId() + "<-" + sc.getId()); 2187 } 2188 } 2189 } 2190 } 2191 2192 List<relationship> newRelations = new ArrayList<>(); 2193 for (table t : dataflow.getAllTables(df)) { 2194 if (!targetTableIds.contains(t.getId())) { 2195 continue; 2196 } 2197 if (t.getColumns() == null || t.getColumns().size() < 2) { 2198 continue; 2199 } 2200 column starColumn = null; 2201 List<column> regularColumns = new ArrayList<>(); 2202 for (column c : t.getColumns()) { 2203 if ("system".equals(c.getSource())) { 2204 continue; 2205 } 2206 if (Boolean.TRUE.toString().equals(c.getIsFunction())) { 2207 continue; 2208 } 2209 if ("*".equals(c.getName())) { 2210 if (starColumn == null) { 2211 starColumn = c; 2212 } 2213 } else { 2214 regularColumns.add(c); 2215 } 2216 } 2217 if (starColumn == null || regularColumns.isEmpty()) { 2218 continue; 2219 } 2220 2221 Set<String> existingBridges = bridgeIndex.getOrDefault(t.getId(), Collections.emptySet()); 2222 String starId = starColumn.getId(); 2223 for (column target : regularColumns) { 2224 if (existingBridges.contains(target.getId() + "<-" + starId)) { 2225 continue; 2226 } 2227 relationship rel = new relationship(); 2228 rel.setId(String.valueOf(++startId)); 2229 rel.setType(RelationshipType.fdd.name()); 2230 rel.setEffectType("expand_star"); 2231 2232 targetColumn tc = new targetColumn(); 2233 tc.setId(target.getId()); 2234 tc.setColumn(target.getName()); 2235 tc.setParent_id(t.getId()); 2236 tc.setParent_name(t.getName()); 2237 rel.setTarget(tc); 2238 2239 sourceColumn sc = new sourceColumn(); 2240 sc.setId(starId); 2241 sc.setColumn(starColumn.getName()); 2242 sc.setParent_id(t.getId()); 2243 sc.setParent_name(t.getName()); 2244 rel.setSources(Arrays.asList(sc)); 2245 2246 newRelations.add(rel); 2247 } 2248 } 2249 2250 if (!newRelations.isEmpty()) { 2251 if (df.getRelationships() == null) { 2252 df.setRelationships(new ArrayList<relationship>()); 2253 } 2254 df.getRelationships().addAll(newRelations); 2255 } 2256 return startId; 2257 } 2258 2259 private static String offsetId(String id, long offset) { 2260 if (id == null || id.isEmpty()) { 2261 return id; 2262 } 2263 try { 2264 return String.valueOf(Long.parseLong(id) + offset); 2265 } catch (NumberFormatException ignore) { 2266 return id; 2267 } 2268 } 2269 2270 /** 2271 * 偏移 coordinate 第三维(fileIdx)。 2272 * <p> 2273 * coordinate 字符串格式为 {@code [line,col,fileIdx],[line,col,fileIdx]}, 2274 * 合并多个独立生成的 dataflow 时,它们的 fileIdx 都从 0 起始会互相冲突, 2275 * 通过给第三维加上 offset 来区分不同的来源。 2276 */ 2277 private static String offsetCoordinate(String coordinate, long offset) { 2278 if (coordinate == null || coordinate.isEmpty() || offset == 0L) { 2279 return coordinate; 2280 } 2281 StringBuilder sb = new StringBuilder(coordinate.length() + 8); 2282 int i = 0; 2283 int len = coordinate.length(); 2284 while (i < len) { 2285 int open = coordinate.indexOf('[', i); 2286 if (open < 0) { 2287 sb.append(coordinate, i, len); 2288 break; 2289 } 2290 int close = coordinate.indexOf(']', open); 2291 if (close < 0) { 2292 sb.append(coordinate, i, len); 2293 break; 2294 } 2295 sb.append(coordinate, i, open + 1); 2296 String inner = coordinate.substring(open + 1, close); 2297 String[] parts = inner.split(",", -1); 2298 for (int k = 0; k < parts.length; k++) { 2299 if (k > 0) sb.append(','); 2300 if (k == 2) { 2301 String p = parts[k].trim(); 2302 try { 2303 sb.append(Long.parseLong(p) + offset); 2304 } catch (NumberFormatException ignore) { 2305 sb.append(parts[k]); 2306 } 2307 } else { 2308 sb.append(parts[k]); 2309 } 2310 } 2311 sb.append(']'); 2312 i = close + 1; 2313 } 2314 return sb.toString(); 2315 } 2316 2317 /** 2318 * 偏移 dataflow 中所有节点的 ID 和 coordinate 第三维 fileIdx。 2319 * 2320 * @param df 待偏移的 dataflow(原地修改) 2321 * @param idOffset 给所有 id / xxxId 字段加上的偏移量 2322 * @param fileIdxOffset 给所有 coordinate 第三维 (fileIdx) 加上的偏移量, 2323 * 通常为合并时前面所有 dataflow 累积的源文件数, 2324 * 以便合并后不同来源的 fileIdx 保持连续且不重叠。 2325 */ 2326 private static void offsetDataflowIds(dataflow df, long idOffset, long fileIdxOffset) { 2327 if (idOffset == 0L && fileIdxOffset == 0L) { 2328 return; 2329 } 2330 2331 // tables / views / databases / schemas / stages / datasources / streams / paths / variables / resultsets / sequences 2332 for (table t : dataflow.getAllTables(df)) { 2333 offsetTableIds(t, idOffset); 2334 t.setCoordinate(offsetCoordinate(t.getCoordinate(), fileIdxOffset)); 2335 if (t.getColumns() != null) { 2336 for (column c : t.getColumns()) { 2337 c.setCoordinate(offsetCoordinate(c.getCoordinate(), fileIdxOffset)); 2338 } 2339 } 2340 } 2341 2342 // processes 2343 if (df.getProcesses() != null) { 2344 for (process p : df.getProcesses()) { 2345 p.setId(offsetId(p.getId(), idOffset)); 2346 p.setProcedureId(offsetId(p.getProcedureId(), idOffset)); 2347 p.setCoordinate(offsetCoordinate(p.getCoordinate(), fileIdxOffset)); 2348 } 2349 } 2350 2351 // procedures 2352 if (df.getProcedures() != null) { 2353 for (procedure p : df.getProcedures()) { 2354 p.setId(offsetId(p.getId(), idOffset)); 2355 p.setCoordinate(offsetCoordinate(p.getCoordinate(), fileIdxOffset)); 2356 if (p.getArguments() != null) { 2357 for (argument arg : p.getArguments()) { 2358 arg.setId(offsetId(arg.getId(), idOffset)); 2359 arg.setCoordinate(offsetCoordinate(arg.getCoordinate(), fileIdxOffset)); 2360 } 2361 } 2362 } 2363 } 2364 2365 // oracle packages 2366 if (df.getPackages() != null) { 2367 for (oraclePackage pkg : df.getPackages()) { 2368 pkg.setId(offsetId(pkg.getId(), idOffset)); 2369 if (pkg.getProcedures() != null) { 2370 for (procedure pp : pkg.getProcedures()) { 2371 pp.setId(offsetId(pp.getId(), idOffset)); 2372 pp.setCoordinate(offsetCoordinate(pp.getCoordinate(), fileIdxOffset)); 2373 if (pp.getArguments() != null) { 2374 for (argument arg : pp.getArguments()) { 2375 arg.setId(offsetId(arg.getId(), idOffset)); 2376 arg.setCoordinate(offsetCoordinate(arg.getCoordinate(), fileIdxOffset)); 2377 } 2378 } 2379 } 2380 } 2381 } 2382 } 2383 2384 // relationships 2385 if (df.getRelationships() != null) { 2386 for (relationship rel : df.getRelationships()) { 2387 rel.setId(offsetId(rel.getId(), idOffset)); 2388 rel.setProcessId(offsetId(rel.getProcessId(), idOffset)); 2389 rel.setProcedureId(offsetId(rel.getProcedureId(), idOffset)); 2390 if (rel.getTarget() != null) { 2391 offsetTargetColumnIds(rel.getTarget(), idOffset); 2392 } 2393 if (rel.getCaller() != null) { 2394 offsetTargetColumnIds(rel.getCaller(), idOffset); 2395 } 2396 if (rel.getSources() != null) { 2397 for (sourceColumn sc : rel.getSources()) { 2398 offsetSourceColumnIds(sc, idOffset); 2399 } 2400 } 2401 if (rel.getCallees() != null) { 2402 for (sourceColumn sc : rel.getCallees()) { 2403 offsetSourceColumnIds(sc, idOffset); 2404 } 2405 } 2406 } 2407 } 2408 } 2409 2410 /** 2411 * 扫描 dataflow 中所有 coordinate,返回最大的第三维 (fileIdx) 值; 2412 * 若没有任何 coordinate 则返回 -1。 2413 */ 2414 private static int findMaxFileIdx(dataflow df) { 2415 if (df == null) { 2416 return -1; 2417 } 2418 int max = -1; 2419 for (table t : dataflow.getAllTables(df)) { 2420 max = Math.max(max, maxFileIdxInCoordinate(t.getCoordinate())); 2421 if (t.getColumns() != null) { 2422 for (column c : t.getColumns()) { 2423 max = Math.max(max, maxFileIdxInCoordinate(c.getCoordinate())); 2424 } 2425 } 2426 } 2427 if (df.getProcesses() != null) { 2428 for (process p : df.getProcesses()) { 2429 max = Math.max(max, maxFileIdxInCoordinate(p.getCoordinate())); 2430 } 2431 } 2432 if (df.getProcedures() != null) { 2433 for (procedure p : df.getProcedures()) { 2434 max = Math.max(max, maxFileIdxInCoordinate(p.getCoordinate())); 2435 if (p.getArguments() != null) { 2436 for (argument arg : p.getArguments()) { 2437 max = Math.max(max, maxFileIdxInCoordinate(arg.getCoordinate())); 2438 } 2439 } 2440 } 2441 } 2442 if (df.getPackages() != null) { 2443 for (oraclePackage pkg : df.getPackages()) { 2444 if (pkg.getProcedures() != null) { 2445 for (procedure pp : pkg.getProcedures()) { 2446 max = Math.max(max, maxFileIdxInCoordinate(pp.getCoordinate())); 2447 if (pp.getArguments() != null) { 2448 for (argument arg : pp.getArguments()) { 2449 max = Math.max(max, maxFileIdxInCoordinate(arg.getCoordinate())); 2450 } 2451 } 2452 } 2453 } 2454 } 2455 } 2456 return max; 2457 } 2458 2459 /** 2460 * 从 coordinate 字符串中解析出最大的第三维 (fileIdx) 值; 2461 * 若 coordinate 为空或没有任何可解析的数值则返回 -1。 2462 */ 2463 private static int maxFileIdxInCoordinate(String coordinate) { 2464 if (coordinate == null || coordinate.isEmpty()) { 2465 return -1; 2466 } 2467 int max = -1; 2468 int i = 0; 2469 int len = coordinate.length(); 2470 while (i < len) { 2471 int open = coordinate.indexOf('[', i); 2472 if (open < 0) break; 2473 int close = coordinate.indexOf(']', open); 2474 if (close < 0) break; 2475 String inner = coordinate.substring(open + 1, close); 2476 String[] parts = inner.split(",", -1); 2477 if (parts.length >= 3) { 2478 try { 2479 int v = Integer.parseInt(parts[2].trim()); 2480 if (v > max) max = v; 2481 } catch (NumberFormatException ignore) { 2482 // 不是数字,跳过 2483 } 2484 } 2485 i = close + 1; 2486 } 2487 return max; 2488 } 2489 2490 private static void offsetTableIds(table t, long offset) { 2491 if (t == null) { 2492 return; 2493 } 2494 t.setId(offsetId(t.getId(), offset)); 2495 if (t.getProcessIds() != null) { 2496 List<String> newProcessIds = new ArrayList<>(t.getProcessIds().size()); 2497 for (String pid : t.getProcessIds()) { 2498 newProcessIds.add(offsetId(pid, offset)); 2499 } 2500 t.setProcessIds(newProcessIds); 2501 } 2502 if (t.getColumns() != null) { 2503 for (column c : t.getColumns()) { 2504 c.setId(offsetId(c.getId(), offset)); 2505 } 2506 } 2507 } 2508 2509 private static void offsetTargetColumnIds(targetColumn col, long offset) { 2510 col.setId(offsetId(col.getId(), offset)); 2511 col.setParent_id(offsetId(col.getParent_id(), offset)); 2512 col.setTarget_id(offsetId(col.getTarget_id(), offset)); 2513 } 2514 2515 private static void offsetSourceColumnIds(sourceColumn col, long offset) { 2516 col.setId(offsetId(col.getId(), offset)); 2517 col.setParent_id(offsetId(col.getParent_id(), offset)); 2518 col.setSource_id(offsetId(col.getSource_id(), offset)); 2519 } 2520 2521 public static dataflow readDataflowFromCsvMetadata(String csvMetadata, EDbVendor vendor) { 2522 if (!MetadataReader.isMetadata(csvMetadata)) { 2523 throw new IllegalArgumentException("Illegal csv metadata."); 2524 } 2525 return new SQLDepMetadataAnalyzer().analyzeMetadata(vendor, csvMetadata); 2526 } 2527 2528 public static Dataflow mergeDataflowsAndCsv(List<Pair<EDbVendor,dataflow>> pairs, String csvMetadata) { 2529 //重置ID防止重复 2530 Long index = 0L; 2531 for(int i=0; i<pairs.size(); i++){ 2532 dataflow dataflow = pairs.get(i).second; 2533 Map<String, table> objIDMap = getDataflowDbObjMap(dataflow); 2534 Map<String, String> idMaps = new HashMap<>(); 2535 for (Map.Entry<String, table> entry : objIDMap.entrySet()) { 2536 index = index + 1; 2537 String id = index.toString(); 2538 idMaps.put(entry.getKey(), id); 2539 table table = entry.getValue(); 2540 table.setId(id); 2541 if(table.getColumns() != null){ 2542 for(column col: table.getColumns()){ 2543 index = index + 1; 2544 id = index.toString(); 2545 idMaps.put(col.getId(), id); 2546 col.setId(id); 2547 } 2548 } 2549 } 2550 if(dataflow.getProcedures() != null){ 2551 for(procedure procedure: dataflow.getProcedures()){ 2552 index = index + 1; 2553 String id = index.toString(); 2554 idMaps.put(procedure.getId(), id); 2555 procedure.setId(id); 2556 } 2557 } 2558 if(dataflow.getProcesses() != null){ 2559 for(process process: dataflow.getProcesses()){ 2560 index = index + 1; 2561 String id = index.toString(); 2562 idMaps.put(process.getId(), id); 2563 process.setId(id); 2564 } 2565 } 2566 if(dataflow.getPackages() != null){ 2567 for(oraclePackage oraclePackage: dataflow.getPackages()){ 2568 index = index + 1; 2569 String id = index.toString(); 2570 idMaps.put(oraclePackage.getId(), id); 2571 oraclePackage.setId(id); 2572 if(oraclePackage.getProcedures() != null){ 2573 for(procedure procedure: oraclePackage.getProcedures()){ 2574 index = index + 1; 2575 id = index.toString(); 2576 idMaps.put(procedure.getId(), id); 2577 procedure.setId(id); 2578 } 2579 } 2580 } 2581 } 2582 if(dataflow.getRelationships() != null && dataflow.getRelationships().size()>0){ 2583 for(relationship rel: dataflow.getRelationships()){ 2584 index = index + 1; 2585 String id = index.toString(); 2586 rel.setId(id); 2587 rel.setProcedureId(idMaps.get(rel.getProcedureId())); 2588 rel.setProcessId(idMaps.get(rel.getProcessId())); 2589 rel.getTarget().setParent_id(idMaps.get(rel.getTarget().getParent_id())); 2590 rel.getTarget().setTarget_id(idMaps.get(rel.getTarget().getTarget_id())); 2591 rel.getTarget().setId(idMaps.get(rel.getTarget().getId())); 2592 for(sourceColumn column: rel.getSources()){ 2593 column.setParent_id(idMaps.get(column.getParent_id())); 2594 column.setId(idMaps.get(column.getId())); 2595 } 2596 } 2597 } 2598 } 2599 Dataflow mDataflow = DataFlowAnalyzer.getSqlflowJSONModel(pairs.get(0).first, pairs.get(0).second, false); 2600 Map<String, table> objNameMap = getDataflowDbObjNameMap(pairs.get(0).second); 2601 Sqlflow dbobjs = mDataflow.getDbobjs(); 2602 List<Relationship> mRelationshipList = new ArrayList<>(); 2603 if(mDataflow.getRelationships() != null && mDataflow.getRelationships().length>0){ 2604 mRelationshipList = new LinkedList<>(Arrays.asList(mDataflow.getRelationships())); 2605 } 2606 2607 List<Error> mErrorList = new ArrayList<>(); 2608 if(mDataflow.getErrors() != null && mDataflow.getErrors().length>0){ 2609 mErrorList = new LinkedList<>(Arrays.asList(mDataflow.getErrors())); 2610 } 2611 2612 List<Process> mProcessList = new ArrayList<>(); 2613 if(mDataflow.getProcesses() != null && mDataflow.getProcesses().length>0){ 2614 mProcessList = new LinkedList<>(Arrays.asList(mDataflow.getProcesses())); 2615 } 2616 2617 for(int i=1; i<pairs.size(); i++){ 2618 objNameMap.putAll(getDataflowDbObjNameMap(pairs.get(i).second)); 2619 Dataflow dataflow = DataFlowAnalyzer.getSqlflowJSONModel(pairs.get(i).first, pairs.get(i).second, false); 2620 if(dataflow.getDbobjs().getServers() != null && dataflow.getDbobjs().getServers().size()>0){ 2621 if(dbobjs.getServers() == null){ 2622 dbobjs.setServers(new ArrayList<>()); 2623 } 2624 dbobjs.getServers().addAll(dataflow.getDbobjs().getServers()); 2625 } 2626 2627 if(dataflow.getDbobjs().getErrorMessages() != null && dataflow.getDbobjs().getErrorMessages().size()>0){ 2628 if(dbobjs.getErrorMessages() == null){ 2629 dbobjs.setErrorMessages(new ArrayList<>()); 2630 } 2631 dbobjs.getErrorMessages().addAll(dataflow.getDbobjs().getErrorMessages()); 2632 } 2633 2634 if(dataflow.getRelationships() != null && dataflow.getRelationships().length>0){ 2635 List<Relationship> relationshipList = new LinkedList<>(Arrays.asList(dataflow.getRelationships())); 2636 mRelationshipList.addAll(relationshipList); 2637 } 2638 2639 if(dataflow.getErrors() != null && dataflow.getErrors().length>0){ 2640 List<Error> errorList = new LinkedList<>(Arrays.asList(dataflow.getErrors())); 2641 mErrorList.addAll(errorList); 2642 } 2643 2644 if(dataflow.getProcesses() != null && dataflow.getProcesses().length>0){ 2645 List<Process> processList = new LinkedList<>(Arrays.asList(dataflow.getProcesses())); 2646 mProcessList.addAll(processList); 2647 } 2648 2649 } 2650 if(!SQLUtil.isEmpty(csvMetadata)){ 2651 /** 2652 * excel内的血缘 只合并关系 就是只做关联,如果找不到obj 就算了 2653 */ 2654 dataflow df = readDataflowFromCsvMetadata(csvMetadata); 2655 Map<String, table> objIDMap = getDataflowDbObjMap(df); 2656 if(df.getRelationships() != null && df.getRelationships().size()>0){ 2657 for(relationship rel: df.getRelationships()){ 2658 rel.setId("m"+rel.getId()); 2659 table sTable = objIDMap.get(rel.getTarget().getParent_id()); 2660 table tTable = objNameMap.get(sTable.getFullName()); 2661 rel.getTarget().setParent_id(tTable.getId()); 2662 for(column col: tTable.getColumns()){ 2663 if(col.getName().equalsIgnoreCase(rel.getTarget().getColumn())){ 2664 rel.getTarget().setId(col.getId()); 2665 break; 2666 } 2667 } 2668 for(sourceColumn column: rel.getSources()){ 2669 sTable = objIDMap.get(column.getParent_id()); 2670 tTable = objNameMap.get(sTable.getFullName()); 2671 column.setParent_id(tTable.getId()); 2672 for(column col: tTable.getColumns()){ 2673 if(col.getName().equalsIgnoreCase(column.getColumn())){ 2674 column.setId(col.getId()); 2675 break; 2676 } 2677 } 2678 } 2679 mRelationshipList.add(toRelationship(rel)); 2680 } 2681 } 2682 } 2683 2684 mDataflow.setDbobjs(dbobjs); 2685 mDataflow.setRelationships(mRelationshipList.toArray(new Relationship[mRelationshipList.size()])); 2686 mDataflow.setProcesses(mProcessList.toArray(new Process[mProcessList.size()])); 2687 mDataflow.setErrors(mErrorList.toArray(new Error[mErrorList.size()])); 2688 return mDataflow; 2689 } 2690 2691 public static dataflow readDataflowFromCsvMetadata(String csvMetadata) { 2692 if (!MetadataReader.isMetadata(csvMetadata)) { 2693 throw new IllegalArgumentException("Illegal csv metadata."); 2694 } 2695 return new SQLDepMetadataAnalyzer().analyzeMetadata(null, csvMetadata); 2696 } 2697 2698 private static Relationship toRelationship(relationship relation){ 2699 Relationship relationModel; 2700 if (relation.getType().equals("join")) { 2701 JoinRelationship joinRelationModel = new JoinRelationship(); 2702 joinRelationModel.setCondition(relation.getCondition()); 2703 joinRelationModel.setJoinType(relation.getJoinType()); 2704 joinRelationModel.setClause(relation.getClause()); 2705 relationModel = joinRelationModel; 2706 } else { 2707 relationModel = new Relationship(); 2708 } 2709 relationModel.setId(relation.getId()); 2710 relationModel.setProcessId(relation.getProcessId()); 2711 relationModel.setProcessType(relation.getProcessType()); 2712 relationModel.setType(relation.getType()); 2713 relationModel.setEffectType(relation.getEffectType()); 2714 relationModel.setPartition(relation.getPartition()); 2715 relationModel.setFunction(relation.getFunction()); 2716 relationModel.setProcedureId(relation.getProcedureId()); 2717 relationModel.setSqlHash(relation.getSqlHash()); 2718 relationModel.setCondition(relation.getCondition()); 2719 relationModel.setSqlComment(relation.getSqlComment()); 2720 relationModel.setTimestampMax(relation.getTimestampMax()); 2721 relationModel.setTimestampMin(relation.getTimestampMin()); 2722 2723 if (relation.getTarget() != null && relation.getSources() != null && !relation.getSources().isEmpty()) { 2724 RelationshipElement targetModel = new RelationshipElement(); 2725 targetColumn target = relation.getTarget(); 2726 targetModel.setColumn(target.getColumn()); 2727 targetModel.setParentName(target.getParent_name()); 2728 targetModel.setTargetName(target.getTarget_name()); 2729 targetModel.setId(target.getId()); 2730 targetModel.setTargetId(target.getTarget_id()); 2731 targetModel.setParentId(target.getParent_id()); 2732 targetModel.setCoordinates(Coordinate.parse(target.getCoordinate())); 2733 targetModel.setFunction(target.getFunction()); 2734 targetModel.setType(target.getType()); 2735 relationModel.setTarget(targetModel); 2736 2737 List<RelationshipElement> sourceModels = new ArrayList<>(); 2738 for (sourceColumn source : relation.getSources()) { 2739 RelationshipElement sourceModel = new RelationshipElement(); 2740 sourceModel.setColumn(source.getColumn()); 2741 sourceModel.setParentName(source.getParent_name()); 2742 sourceModel.setSourceName(source.getSource_name()); 2743 sourceModel.setColumnType(source.getColumn_type()); 2744 sourceModel.setId(source.getId()); 2745 sourceModel.setParentId(source.getParent_id()); 2746 sourceModel.setSourceId(source.getSource_id()); 2747 sourceModel.setCoordinates(Coordinate.parse(source.getCoordinate())); 2748 sourceModel.setClauseType(source.getClauseType()); 2749 sourceModel.setType(source.getType()); 2750 sourceModels.add(sourceModel); 2751 if (source.getTransforms() != null && !source.getTransforms().isEmpty()) { 2752 List<Transform> transforms = new ArrayList<gudusoft.gsqlparser.dlineage.dataflow.model.json.Transform>(); 2753 for (transform transform : source.getTransforms()) { 2754 Transform item = new Transform(); 2755 item.setCode(transform.getCode()); 2756 item.setType(transform.getType()); 2757 item.setCoordinate(transform.getCoordinate(true)); 2758 transforms.add(item); 2759 } 2760 sourceModel.setTransforms(transforms.toArray(new Transform[0])); 2761 } 2762 } 2763 relationModel.setSources(sourceModels.toArray(new RelationshipElement[0])); 2764 } else if (relation.getCaller() != null && relation.getCallees() != null 2765 && !relation.getCallees().isEmpty()) { 2766 RelationshipElement targetModel = new RelationshipElement(); 2767 targetColumn target = relation.getCaller(); 2768 targetModel.setName(target.getName()); 2769 targetModel.setId(target.getId()); 2770 targetModel.setCoordinates(Coordinate.parse(target.getCoordinate())); 2771 targetModel.setType(target.getType()); 2772 relationModel.setCaller(targetModel); 2773 List<RelationshipElement> sourceModels = new ArrayList<gudusoft.gsqlparser.dlineage.dataflow.model.json.RelationshipElement>(); 2774 for (sourceColumn source : relation.getCallees()) { 2775 RelationshipElement sourceModel = new RelationshipElement(); 2776 sourceModel.setName(source.getName()); 2777 sourceModel.setId(source.getId()); 2778 sourceModel.setCoordinates(Coordinate.parse(source.getCoordinate())); 2779 sourceModel.setType(source.getType()); 2780 sourceModels.add(sourceModel); 2781 } 2782 relationModel.setCallees(sourceModels.toArray(new RelationshipElement[0])); 2783 } 2784 return relationModel; 2785 } 2786 2787// public static void main(String[] args) throws Exception { 2788// // 基于文件的迭代合并(节约内存,适合大数据量) 2789// dataflow dataflow = iterativeMergeDataflowsFromFiles(Arrays.asList(new File("C:\\Users\\KK\\xwechat_files\\wxid_z9ci6s8b7g0d21_9365\\msg\\file\\2026-06\\dataflow_item").listFiles()), EDbVendor.dbvoracle); 2790// System.out.println(XML2Model.saveXML(dataflow)); 2791// } 2792// 2793// public static void main(String[] args) throws Exception { 2794// // 基于内存的合并(一次性加载所有文件到内存,适合小数据量) 2795// File[] files = new File("C:\\Users\\KK\\xwechat_files\\wxid_z9ci6s8b7g0d21_9365\\msg\\file\\2026-06\\dataflow_item").listFiles(); 2796// if (files == null || files.length == 0) { 2797// System.out.println("No dataflow files found."); 2798// return; 2799// } 2800// 2801// List<dataflow> dataflows = new ArrayList<>(); 2802// for (File file : files) { 2803// dataflow df = XML2Model.loadXML(dataflow.class, file); 2804// if (df != null) { 2805// dataflows.add(df); 2806// } 2807// } 2808// 2809// dataflow mergedDataflow = mergeDataflows(dataflows, EDbVendor.dbvoracle); 2810// System.out.println(XML2Model.saveXML(mergedDataflow)); 2811// } 2812// 2813// public static void main(String[] args) throws Exception { 2814// String sqlDirPath = "C:\\Users\\KK\\Desktop\\同义词验证"; 2815// File sqlDir = new File(sqlDirPath); 2816// 2817// if (!sqlDir.exists() || !sqlDir.isDirectory()) { 2818// System.err.println("目录不存在或不是目录: " + sqlDirPath); 2819// return; 2820// } 2821// 2822// File[] sqlFiles = sqlDir.listFiles((dir, name) -> name.toLowerCase().endsWith(".sql")); 2823// 2824// if (sqlFiles == null || sqlFiles.length == 0) { 2825// System.err.println("目录下没有找到 SQL 文件: " + sqlDirPath); 2826// return; 2827// } 2828// 2829// System.out.println("找到 " + sqlFiles.length + " 个 SQL 文件"); 2830// for (File file : sqlFiles) { 2831// System.out.println(" - " + file.getName()); 2832// } 2833// 2834// Option option = new Option(); 2835// option.setVendor(EDbVendor.dbvoracle); 2836// option.setOutput(true); 2837// option.setSimpleOutput(true); 2838// option.setSimpleShowSynonym(true); 2839// option.setParallel(4); 2840// 2841// ParallelDataFlowAnalyzer analyzer = new ParallelDataFlowAnalyzer( 2842// sqlFiles, 2843// option 2844// ); 2845// 2846// System.out.println("\n开始分析..."); 2847// long startTime = System.currentTimeMillis(); 2848// analyzer.generateDataFlow(false, true); 2849// long endTime = System.currentTimeMillis(); 2850// 2851// dataflow dataflow = analyzer.getDataFlow(); 2852// 2853// analyzer.dispose(); 2854// 2855// if (dataflow == null) { 2856// System.err.println("分析失败,未生成数据流"); 2857// return; 2858// } 2859// 2860// Option option1 = new Option(); 2861// option1.setVendor(EDbVendor.dbvoracle); 2862// option1.setSimpleOutput(true); 2863// dataflow simpleDataflow = new DataFlowAnalyzer("", option1).getSimpleDataflow(dataflow, true); 2864// 2865// 2866// System.out.println("分析完成,耗时: " + (endTime - startTime) + " ms"); 2867// System.out.println("表数量: " + (simpleDataflow.getTables() != null ? simpleDataflow.getTables().size() : 0)); 2868// System.out.println("视图数量: " + (simpleDataflow.getViews() != null ? simpleDataflow.getViews().size() : 0)); 2869// System.out.println("关系数量: " + (simpleDataflow.getRelationships() != null ? simpleDataflow.getRelationships().size() : 0)); 2870// System.out.println("错误数量: " + (simpleDataflow.getErrors() != null ? simpleDataflow.getErrors().size() : 0)); 2871// 2872// System.out.println(XML2Model.saveXML(simpleDataflow)); 2873// 2874// } 2875}