001package gudusoft.gsqlparser.dlineage.util; 002 003import java.io.File; 004import java.util.ArrayList; 005import java.util.Arrays; 006import java.util.Comparator; 007import java.util.HashMap; 008import java.util.Iterator; 009import java.util.LinkedHashMap; 010import java.util.LinkedHashSet; 011import java.util.List; 012import java.util.Map; 013import java.util.Set; 014import java.util.TreeSet; 015 016import gudusoft.gsqlparser.EDbVendor; 017import gudusoft.gsqlparser.sqlenv.CollatorProvider; 018import gudusoft.gsqlparser.sqlenv.ESQLDataObjectType; 019import gudusoft.gsqlparser.sqlenv.IdentifierProfile; 020import gudusoft.gsqlparser.sqlenv.IdentifierService; 021import gudusoft.gsqlparser.dlineage.DataFlowAnalyzer; 022import gudusoft.gsqlparser.dlineage.dataflow.model.xml.dataflow; 023import gudusoft.gsqlparser.dlineage.dataflow.model.xml.process; 024import gudusoft.gsqlparser.dlineage.dataflow.model.xml.relationship; 025import gudusoft.gsqlparser.dlineage.dataflow.model.xml.sourceColumn; 026import gudusoft.gsqlparser.dlineage.dataflow.model.xml.table; 027import gudusoft.gsqlparser.dlineage.dataflow.model.xml.targetColumn; 028import gudusoft.gsqlparser.util.Logger; 029import gudusoft.gsqlparser.util.LoggerFactory; 030import gudusoft.gsqlparser.util.SQLUtil; 031 032public class ProcessUtility { 033 034 private static final Logger logger = LoggerFactory.getLogger(ProcessUtility.class); 035 036 public static dataflow generateTableLevelLineage(DataFlowAnalyzer analyzer, dataflow instance) { 037 return generateTableLevelLineage(analyzer, instance, true, false); 038 } 039 040 public static String generateTableLevelLineageCsv(DataFlowAnalyzer analyzer, dataflow instance) { 041 return generateTableLevelLineageCsv(analyzer, instance, true, false, ","); 042 } 043 044 public static String generateTableLevelLineageCsv(DataFlowAnalyzer analyzer, dataflow instance, String delimiter) { 045 return generateTableLevelLineageCsv(analyzer, instance, true, false, delimiter); 046 } 047 048 private static String generateTableLevelLineageCsv(DataFlowAnalyzer analyzer, dataflow instance, boolean ignoreSelfLineage, boolean isSimple, String delimiter) { 049 StringBuilder buffer = new StringBuilder(); 050 buffer.append("source_db"+delimiter+"source_schema"+delimiter+"source_table"+delimiter+"source_column"+delimiter+"target_db"+delimiter+"target_schema"+delimiter+"target_table"+delimiter+"target_column"+delimiter+"process_type"+delimiter+"process_hashid"+delimiter+"coordinate"+delimiter+"procedure_names\n"); 051 try { 052 dataflow simple = instance; 053 if (!isSimple) { 054 simple = analyzer.getSimpleDataflow(instance, true); 055 } 056 if (simple.getTables() == null) { 057 return null; 058 } 059 List<relationship> relations = instance.getRelationships(); 060 if (relations == null || relations.size() == 0) { 061 return null; 062 } 063 long maxId = Long.parseLong(relations.get(relations.size() - 1).getId().split("\\-")[0].replace("_", "")) 064 * 100; 065 066 Iterator<relationship> iter = simple.getRelationships().iterator(); 067 while (iter.hasNext()) { 068 relationship relation = iter.next(); 069 if (!"fdd".equals(relation.getType())) { 070 iter.remove(); 071 } 072 } 073 074 Map<Pair3<String, String, String>, Set<relationship>> tableRelationMap = new LinkedHashMap<Pair3<String, String, String>, Set<relationship>>(); 075 List<relationship> tableRelations = new ArrayList<relationship>(); 076 iter = simple.getRelationships().iterator(); 077 while (iter.hasNext()) { 078 relationship relation = iter.next(); 079 if (relation.getSources() != null && relation.getTarget() != null && relation.getProcessId() != null) { 080 String targetId = relation.getTarget().getParent_id(); 081 if (SQLUtil.isEmpty(targetId)) 082 continue; 083 for (sourceColumn sourceColumn : relation.getSources()) { 084 String sourceId = sourceColumn.getParent_id(); 085 if (SQLUtil.isEmpty(sourceId)) 086 continue; 087 088 if (ignoreSelfLineage && sourceId.equals(targetId)) { 089 continue; 090 } 091 092 Pair3<String, String, String> tableRelationPair = new Pair3<String, String, String>(sourceId, 093 targetId, relation.getProcessId()); 094 if (!tableRelationMap.containsKey(tableRelationPair)) { 095 relationship tableRelation = new relationship(); 096 097 targetColumn targetTable = new targetColumn(); 098 targetTable.setId(String.valueOf(++maxId)); 099 targetTable.setTarget_id(targetId); 100 targetTable.setTarget_name(relation.getTarget().getParent_name()); 101 tableRelation.setTarget(targetTable); 102 103 sourceColumn sourceTale = new sourceColumn(); 104 sourceTale.setId(String.valueOf(++maxId)); 105 sourceTale.setSource_id(sourceId); 106 sourceTale.setSource_name(sourceColumn.getParent_name()); 107 108 tableRelation.setSources(Arrays.asList(sourceTale)); 109 tableRelation.setType("fdd"); 110 tableRelation.setId(String.valueOf(++maxId)); 111 tableRelation.setProcessId(relation.getProcessId()); 112 tableRelations.add(tableRelation); 113 tableRelationMap.put(tableRelationPair, new LinkedHashSet<relationship>()); 114 } 115 tableRelationMap.get(tableRelationPair).add(relation); 116 } 117 } 118 } 119 120 List<table> tables = new ArrayList<>(); 121 if (simple.getTables() != null) { 122 tables.addAll(simple.getTables()); 123 } 124 if (simple.getPaths() != null) { 125 tables.addAll(simple.getPaths()); 126 } 127 if (simple.getStages() != null) { 128 tables.addAll(simple.getStages()); 129 } 130 if (simple.getDatasources() != null) { 131 tables.addAll(simple.getDatasources()); 132 } 133 if (simple.getStreams() != null) { 134 tables.addAll(simple.getStreams()); 135 } 136 if (simple.getDatabases() != null) { 137 tables.addAll(simple.getDatabases()); 138 } 139 if (simple.getSchemas() != null) { 140 tables.addAll(simple.getSchemas()); 141 } 142 if (simple.getVariables() != null) { 143 tables.addAll(simple.getVariables()); 144 } 145 if (simple.getViews() != null) { 146 tables.addAll(simple.getViews()); 147 } 148 Map<String, table> tableMap = new HashMap<String, table>(); 149 Iterator<table> tableIter = tables.iterator(); 150 while (tableIter.hasNext()) { 151 table table = tableIter.next(); 152 tableMap.put(table.getId(), table); 153 } 154 155 Map<String, process> processMap = new HashMap<String, process>(); 156 Iterator<process> processIter = simple.getProcesses().iterator(); 157 while (processIter.hasNext()) { 158 process process = processIter.next(); 159 processMap.put(process.getId(), process); 160 } 161 162 Set<String> lineSet = new TreeSet<String>(new Comparator<String>() { 163 @Override 164 public int compare(String o1, String o2) { 165 return o1.toLowerCase().compareTo(o2.toLowerCase()); 166 } 167 }); 168 for (Pair3<String, String, String> key : tableRelationMap.keySet()) { 169 String sourceId = key.first; 170 String targetId = key.second; 171 String processId = key.third; 172 table sourceTable = tableMap.get(sourceId); 173 table targetTable = tableMap.get(targetId); 174 process process = processMap.get(processId); 175 176 String source_db = "default"; 177 if(!SQLUtil.isEmpty(sourceTable.getDatabase())){ 178 source_db = sourceTable.getDatabase(); 179 } 180 String source_schema = "default"; 181 if(!SQLUtil.isEmpty(sourceTable.getSchema())){ 182 source_schema = sourceTable.getSchema(); 183 } 184 String source_table = sourceTable.getTableNameOnly(); 185 Set<relationship> processRelations = tableRelationMap.get(key); 186 Set<String> sourceColumns = new LinkedHashSet<String>(); 187 Set<String> targetColumns = new LinkedHashSet<String>(); 188 for (relationship relation : processRelations) { 189 for (sourceColumn column : relation.getSources()) { 190 sourceColumns.add(column.getColumn()); 191 } 192 targetColumns.add(relation.getTarget().getColumn()); 193 } 194 195 String target_db = "default"; 196 if(!SQLUtil.isEmpty(targetTable.getDatabase())){ 197 target_db = targetTable.getDatabase(); 198 } 199 String target_schema = "default"; 200 if(!SQLUtil.isEmpty(targetTable.getSchema())){ 201 target_schema = targetTable.getSchema(); 202 } 203 String target_table = targetTable.getTableNameOnly(); 204 String process_type = process.getType(); 205 String process_hashid = process.getQueryHashId(); 206 String process_coordinate = process.getCoordinate(); 207 String procedure_names = process.getProcedureName(); 208 if("batchQueries".equals(procedure_names)) { 209 procedure_names = ""; 210 } 211 212 StringBuilder temp = new StringBuilder(); 213 temp.append(source_db).append(delimiter).append(source_schema).append(delimiter).append(source_table).append(delimiter) 214 .append(String.join(";", sourceColumns)).append(delimiter).append(target_db).append(delimiter) 215 .append(target_schema).append(delimiter).append(target_table).append(delimiter) 216 .append(String.join(";", targetColumns)).append(delimiter).append(process_type).append(delimiter) 217 .append(process_hashid).append(delimiter).append("\"").append(process_coordinate).append("\"").append(delimiter).append(procedure_names).append("\n"); 218 lineSet.add(temp.toString()); 219 } 220 221 for(String lineKey: lineSet) { 222 buffer.append(lineKey); 223 } 224 225 } catch (Exception e) { 226 logger.error("Generate table level csv failed.", e); 227 } 228 return buffer.toString(); 229 } 230 231 public static dataflow generateTableLevelLineage(DataFlowAnalyzer analyzer, dataflow instance, 232 boolean ignoreSelfLineage, boolean isSimple) { 233 try { 234 dataflow simple = instance; 235 if (!isSimple) { 236 simple = analyzer.getSimpleDataflow(instance, true); 237 } 238 if (simple.getTables() == null) { 239 return simple; 240 } 241 242 List<relationship> relations = instance.getRelationships(); 243 if (relations == null || relations.size() == 0) { 244 return simple; 245 } 246 247 String id = relations.get(relations.size() - 1).getId().split("\\-")[0].replace("_", ""); 248 if(id.trim().length() == 0){ 249 System.out.println(relations.get(relations.size() - 1).getId()); 250 } 251 long maxId = Long.parseLong(id) 252 * 100; 253 254 Iterator<relationship> iter = simple.getRelationships().iterator(); 255 while (iter.hasNext()) { 256 relationship relation = iter.next(); 257 if (!"fdd".equals(relation.getType())) { 258 iter.remove(); 259 } 260 } 261 262 Map<Pair3<String, String, String>, relationship> tableRelationMap = new LinkedHashMap<Pair3<String, String, String>, relationship>(); 263 List<relationship> tableRelations = new ArrayList<relationship>(); 264 iter = simple.getRelationships().iterator(); 265 while (iter.hasNext()) { 266 relationship relation = iter.next(); 267 if (relation.getSources() != null && relation.getTarget() != null && relation.getProcessId() != null) { 268 String targetId = relation.getTarget().getParent_id(); 269 if (SQLUtil.isEmpty(targetId)) 270 continue; 271 for (sourceColumn sourceColumn : relation.getSources()) { 272 String sourceId = sourceColumn.getParent_id(); 273 if (SQLUtil.isEmpty(sourceId)) 274 continue; 275 276 if (ignoreSelfLineage && sourceId.equals(targetId)) { 277 continue; 278 } 279 280 Pair3<String, String, String> tableRelationPair = new Pair3<String, String, String>(sourceId, 281 targetId, relation.getProcessId()); 282 if (!tableRelationMap.containsKey(tableRelationPair)) { 283 relationship tableRelation = new relationship(); 284 285 targetColumn targetTable = new targetColumn(); 286 targetTable.setId(String.valueOf(++maxId)); 287 targetTable.setTarget_id(targetId); 288 targetTable.setTarget_name(relation.getTarget().getParent_name()); 289 tableRelation.setTarget(targetTable); 290 291 sourceColumn sourceTale = new sourceColumn(); 292 sourceTale.setId(String.valueOf(++maxId)); 293 sourceTale.setSource_id(sourceId); 294 sourceTale.setSource_name(sourceColumn.getParent_name()); 295 296 tableRelation.setSources(Arrays.asList(sourceTale)); 297 tableRelation.setType("fdd"); 298 tableRelation.setId(String.valueOf(++maxId)); 299 tableRelation.setProcessId(relation.getProcessId()); 300 tableRelations.add(tableRelation); 301 tableRelationMap.put(tableRelationPair, tableRelation); 302 } 303 } 304 } 305 } 306 307 simple.getRelationships().clear(); 308 simple.getRelationships().addAll(tableRelations); 309 310 List<table> tables = new ArrayList<>(); 311 if (simple.getTables() != null) { 312 tables.addAll(simple.getTables()); 313 } 314 if (simple.getPaths() != null) { 315 tables.addAll(simple.getPaths()); 316 } 317 if (simple.getStages() != null) { 318 tables.addAll(simple.getStages()); 319 } 320 if (simple.getDatasources() != null) { 321 tables.addAll(simple.getDatasources()); 322 } 323 if (simple.getDatabases() != null) { 324 tables.addAll(simple.getDatabases()); 325 } 326 if (simple.getSchemas() != null) { 327 tables.addAll(simple.getSchemas()); 328 } 329 if (simple.getVariables() != null) { 330 tables.addAll(simple.getVariables()); 331 } 332 if (simple.getViews() != null) { 333 tables.addAll(simple.getViews()); 334 } 335 336 for (table table : tables) { 337 table.getColumns().clear(); 338 } 339 340 if (simple.getProcesses() == null) { 341 return simple; 342 } 343 344 Map<String, table> tableMap = new HashMap<String, table>(); 345 Iterator<table> tableIter = tables.iterator(); 346 while (tableIter.hasNext()) { 347 table table = tableIter.next(); 348 tableMap.put(table.getId(), table); 349 } 350 351 Map<String, process> processMap = new HashMap<String, process>(); 352 Iterator<process> processIter = simple.getProcesses().iterator(); 353 while (processIter.hasNext()) { 354 process process = processIter.next(); 355 processMap.put(process.getId(), process); 356 } 357 358 Map<String, relationship> processRelations = new LinkedHashMap<String, relationship>(); 359 iter = simple.getRelationships().iterator(); 360 while (iter.hasNext()) { 361 relationship relation = iter.next(); 362 relationship beforeRelation = new relationship(); 363 beforeRelation.setSources(relation.getSources()); 364 beforeRelation.setType("fdd"); 365 beforeRelation.setId(String.valueOf(++maxId)); 366 367 targetColumn targetProcess = new targetColumn(); 368 targetProcess.setId(String.valueOf(++maxId)); 369 targetProcess.setTarget_id(relation.getProcessId()); 370 targetProcess.setTarget_name(processMap.get(relation.getProcessId()).getName()); 371 beforeRelation.setTarget(targetProcess); 372 373 String key = targetProcess.getTarget_id(); 374 if (beforeRelation.getSources() != null) { 375 for (sourceColumn sourceColumn : beforeRelation.getSources()) { 376 key += (":" + sourceColumn.getSource_id() + ":" + relation.getProcessId()); 377 } 378 } 379 if (!processRelations.containsKey(key)) { 380 beforeRelation.getTarget().setId(beforeRelation.getTarget().getTarget_id()); 381 if (beforeRelation.getSources() != null) { 382 for (sourceColumn sourceColumn : beforeRelation.getSources()) { 383 sourceColumn.setId(sourceColumn.getSource_id()); 384 } 385 } 386 processRelations.put(key, beforeRelation); 387 } 388 389 relationship afterRelation = new relationship(); 390 afterRelation.setTarget(relation.getTarget()); 391 afterRelation.setType("fdd"); 392 afterRelation.setId(String.valueOf(++maxId)); 393 394 sourceColumn sourceProcess = new sourceColumn(); 395 sourceProcess.setId(String.valueOf(++maxId)); 396 sourceProcess.setSource_id(relation.getProcessId()); 397 sourceProcess.setSource_name(processMap.get(relation.getProcessId()).getName()); 398 afterRelation.setSources(Arrays.asList(new sourceColumn[] { sourceProcess })); 399 400 key = relation.getTarget().getTarget_id(); 401 key += (":" + sourceProcess.getSource_id() + ":" + relation.getProcessId()); 402 if (!processRelations.containsKey(key)) { 403 afterRelation.getTarget().setId(afterRelation.getTarget().getTarget_id()); 404 sourceProcess.setId(sourceProcess.getSource_id()); 405 processRelations.put(key, afterRelation); 406 } 407 408 iter.remove(); 409 } 410 411 simple.getRelationships().addAll(processRelations.values()); 412 413 return simple; 414 } catch (Exception e) { 415 logger.error("Generate table level process failed.", e); 416 } 417 return instance; 418 } 419 420 public static void main(String[] args) throws Exception { 421 dataflow dataflow = XML2Model.loadXML(dataflow.class, new File( 422 "C:/Users/KK/Desktop/e091bfbe0f4178ffbcff4711751a119ec06087c9723be51a26c3a1b6b120acf8.dataflow.zip")); 423 DataFlowAnalyzer dataFlowAnalyzer = new DataFlowAnalyzer("", EDbVendor.dbvoracle, true); 424 dataflow dataflow1 = ProcessUtility.generateTableLevelLineage(dataFlowAnalyzer, dataflow); 425 XML2Model.saveXML(dataflow1, new File("D:\\1.zip")); 426 } 427 428 public static String generateColumnLevelLineageCsv(DataFlowAnalyzer analyzer, dataflow instance) { 429 return generateColumnLevelLineageCsv(analyzer, instance, false, ","); 430 } 431 432 public static String generateColumnLevelLineageCsv(DataFlowAnalyzer analyzer, dataflow instance, String delimiter) { 433 return generateColumnLevelLineageCsv(analyzer, instance, false, delimiter); 434 } 435 436 public static String generateColumnLevelLineageCsv(DataFlowAnalyzer analyzer, dataflow instance, boolean isSimple, String delimiter) { 437 StringBuilder buffer = new StringBuilder(); 438 buffer.append("SOURCE_DB"+delimiter+"SOURCE_SCHEMA"+delimiter+"SOURCE_TABLE_ID"+delimiter+"SOURCE_TABLE"+delimiter+"SOURCE_COLUMN_ID"+delimiter+"SOURCE_COLUMN"+delimiter+"TARGET_DB"+delimiter+"TARGET_SCHEMA"+delimiter+"TARGET_TABLE_ID"+delimiter+"TARGET_TABLE"+delimiter+"TARGET_COLUMN_ID"+delimiter+"TARGET_COLUMN"+delimiter+"RELATION_TYPE"+delimiter+"EFFECTTYPE\n"); 439 try { 440 dataflow simple = instance; 441// if (!isSimple) { 442// simple = analyzer.getSimpleDataflow(instance, true); 443// } 444 List<relationship> relations = simple.getRelationships(); 445 if (relations == null || relations.size() == 0) 446 return buffer.toString(); 447 448 List<table> tables = new ArrayList<>(); 449 if (simple.getTables() != null) { 450 tables.addAll(simple.getTables()); 451 } 452 if (simple.getPaths() != null) { 453 tables.addAll(simple.getPaths()); 454 } 455 if (simple.getStages() != null) { 456 tables.addAll(simple.getStages()); 457 } 458 if (simple.getDatasources() != null) { 459 tables.addAll(simple.getDatasources()); 460 } 461 if (simple.getStreams() != null) { 462 tables.addAll(simple.getStreams()); 463 } 464 if (simple.getDatabases() != null) { 465 tables.addAll(simple.getDatabases()); 466 } 467 if (simple.getSchemas() != null) { 468 tables.addAll(simple.getSchemas()); 469 } 470 if (simple.getVariables() != null) { 471 tables.addAll(simple.getVariables()); 472 } 473 if (simple.getViews() != null) { 474 tables.addAll(simple.getViews()); 475 } 476 if (simple.getResultsets() != null) { 477 tables.addAll(simple.getResultsets()); 478 } 479 480 Map<String, table> tableMap = new HashMap<String, table>(); 481 Iterator<table> tableIter = tables.iterator(); 482 while (tableIter.hasNext()) { 483 table table = tableIter.next(); 484 tableMap.put(table.getId(), table); 485 } 486 487 TreeSet<String> lines = new TreeSet<String>(new Comparator<String>() { 488 @Override 489 public int compare(String o1, String o2) { 490 return o1.toLowerCase().compareTo(o2.toLowerCase()); 491 } 492 }); 493 for(relationship relation: relations) { 494 if(relation.getSources()==null || relation.getTarget() == null) 495 continue; 496 497 String relation_type = relation.getType(); 498 if("fdd".equals(relation_type)) { 499 relation_type = "direct"; 500 } 501 else if("fdr".equals(relation_type) || "frd".equals(relation_type) || "fddi".equals(relation_type)) { 502 relation_type = "indirect"; 503 } 504 505 String effect_type = relation.getEffectType(); 506 507 targetColumn targetColumn = relation.getTarget(); 508 table targetTable = tableMap.get(targetColumn.getParent_id()); 509 String target_db = targetTable.getDatabase(); 510 target_db = target_db == null ? "default" : target_db; 511 String target_schema = targetTable.getSchema(); 512 target_schema = target_schema == null ? "default" : target_schema; 513 String target_table = targetTable.getName(); 514 String target_table_id = targetTable.getId(); 515 String target_column = targetColumn.getColumn(); 516 String target_column_id = targetColumn.getId(); 517 518 for (sourceColumn sourceColumn : relation.getSources()) { 519 StringBuilder temp = new StringBuilder(); 520 table sourceTable = tableMap.get(sourceColumn.getParent_id()); 521 String source_db = sourceTable.getDatabase(); 522 source_db = source_db == null ? "default" : source_db; 523 String source_schema = sourceTable.getSchema(); 524 source_schema = source_schema == null ? "default" : source_schema; 525 String source_table = sourceTable.getName(); 526 String source_table_id = sourceTable.getId(); 527 String source_column = sourceColumn.getColumn(); 528 String source_column_id = sourceColumn.getId(); 529 530 temp.append(source_db).append(delimiter).append(source_schema).append(delimiter).append(source_table_id).append(delimiter).append(source_table) 531 .append(delimiter).append(source_column_id).append(delimiter).append(source_column).append(delimiter).append(target_db) 532 .append(delimiter).append(target_schema).append(delimiter).append(target_table_id).append(delimiter).append(target_table).append(delimiter) 533 .append(target_column_id).append(delimiter).append(target_column).append(delimiter).append(relation_type).append(delimiter) 534 .append(effect_type).append("\n"); 535 lines.add(temp.toString()); 536 } 537 } 538 for(String line: lines) { 539 buffer.append(line); 540 } 541 } catch (Exception e) { 542 logger.error("Generate column level csv failed.", e); 543 } 544 return buffer.toString(); 545 } 546 547 /** 548 * Simplified column level lineage output. Each line has the form: 549 * 550 * <source schema.table.column><delimiter><target schema.table.column><delimiter><relation type> 551 * 552 * e.g. {@code scott.emp.deptno, default.vsal.salary, fdr} 553 * 554 * The raw relation type (fdd/fdr) is emitted rather than direct/indirect, and 555 * any record whose source or target column is the synthetic {@code RelationRows} 556 * column is dropped. 557 */ 558 public static String generateColumnLevelLineageCsvSimple(DataFlowAnalyzer analyzer, dataflow instance, String delimiter) { 559 StringBuilder buffer = new StringBuilder(); 560 try { 561 EDbVendor vendor = analyzer.getOption().getVendor(); 562 IdentifierService identifierService = identifierServiceFor(vendor); 563 dataflow simple = instance; 564 List<relationship> relations = simple.getRelationships(); 565 if (relations == null || relations.size() == 0) 566 return buffer.toString(); 567 568 List<table> tables = new ArrayList<>(); 569 if (simple.getTables() != null) { 570 tables.addAll(simple.getTables()); 571 } 572 if (simple.getPaths() != null) { 573 tables.addAll(simple.getPaths()); 574 } 575 if (simple.getStages() != null) { 576 tables.addAll(simple.getStages()); 577 } 578 if (simple.getDatasources() != null) { 579 tables.addAll(simple.getDatasources()); 580 } 581 if (simple.getStreams() != null) { 582 tables.addAll(simple.getStreams()); 583 } 584 if (simple.getDatabases() != null) { 585 tables.addAll(simple.getDatabases()); 586 } 587 if (simple.getSchemas() != null) { 588 tables.addAll(simple.getSchemas()); 589 } 590 if (simple.getVariables() != null) { 591 tables.addAll(simple.getVariables()); 592 } 593 if (simple.getViews() != null) { 594 tables.addAll(simple.getViews()); 595 } 596 if (simple.getResultsets() != null) { 597 tables.addAll(simple.getResultsets()); 598 } 599 600 Map<String, table> tableMap = new HashMap<String, table>(); 601 Iterator<table> tableIter = tables.iterator(); 602 while (tableIter.hasNext()) { 603 table table = tableIter.next(); 604 tableMap.put(table.getId(), table); 605 } 606 607 TreeSet<String> lines = new TreeSet<String>(new Comparator<String>() { 608 @Override 609 public int compare(String o1, String o2) { 610 return o1.toLowerCase().compareTo(o2.toLowerCase()); 611 } 612 }); 613 for (relationship relation : relations) { 614 if (relation.getSources() == null || relation.getTarget() == null) 615 continue; 616 617 String relation_type = relation.getType(); 618 619 targetColumn targetColumn = relation.getTarget(); 620 String target_column = targetColumn.getColumn(); 621 if (isRelationRowsColumn(target_column)) 622 continue; 623 table targetTable = tableMap.get(targetColumn.getParent_id()); 624 String targetName = qualifiedColumnName(identifierService, targetTable, target_column); 625 626 for (sourceColumn sourceColumn : relation.getSources()) { 627 String source_column = sourceColumn.getColumn(); 628 if (isRelationRowsColumn(source_column)) 629 continue; 630 table sourceTable = tableMap.get(sourceColumn.getParent_id()); 631 String sourceName = qualifiedColumnName(identifierService, sourceTable, source_column); 632 633 lines.add(sourceName + delimiter + targetName + delimiter + relation_type + "\n"); 634 } 635 } 636 for (String line : lines) { 637 buffer.append(line); 638 } 639 } catch (Exception e) { 640 logger.error("Generate simple column level csv failed.", e); 641 } 642 return buffer.toString(); 643 } 644 645 private static boolean isRelationRowsColumn(String column) { 646 return "RelationRows".equalsIgnoreCase(column); 647 } 648 649 /** 650 * Build a {@code schema.table.column} qualified name, normalizing each real 651 * identifier segment according to the database vendor's case-folding and 652 * quoting rules via {@link IdentifierService#normalizeQualifiedName}. The 653 * synthetic {@code default} placeholder (used when a table has no schema) is 654 * left as-is rather than being treated as a vendor identifier. 655 */ 656 private static String qualifiedColumnName(IdentifierService identifierService, table table, String column) { 657 if (table == null) { 658 return identifierService.normalizeQualifiedName(column, ESQLDataObjectType.dotColumn); 659 } 660 String schema = table.getSchema(); 661 String name = table.getName(); 662 663 String qualified; 664 String schemaPlaceholder = null; 665 if (name != null && schema != null 666 && name.toLowerCase().startsWith((schema + ".").toLowerCase())) { 667 // table name already carries the schema prefix (e.g. "scott.emp") 668 qualified = name + "." + column; 669 } else if (schema != null) { 670 qualified = schema + "." + name + "." + column; 671 } else { 672 // no real schema; keep the synthetic "default" placeholder out of 673 // vendor normalization and prepend it afterwards 674 schemaPlaceholder = "default"; 675 qualified = name + "." + column; 676 } 677 678 // normalize the whole dotted path; each segment is folded per its object 679 // type (schema/table/column) using the vendor's identifier rules 680 String normalized = identifierService.normalizeQualifiedName(qualified, ESQLDataObjectType.dotColumn); 681 return schemaPlaceholder == null ? normalized : schemaPlaceholder + "." + normalized; 682 } 683 684 /** 685 * Build a vendor-aware {@link IdentifierService}, mirroring the wiring used by 686 * {@link IdentifierService#normalizeStatic} (a collator is only attached for 687 * the collation-based SQL Server family). 688 */ 689 private static IdentifierService identifierServiceFor(EDbVendor vendor) { 690 IdentifierProfile profile = IdentifierProfile.forVendor(vendor, IdentifierProfile.VendorFlags.defaults()); 691 CollatorProvider collatorProvider = (vendor == EDbVendor.dbvmssql || vendor == EDbVendor.dbvazuresql) 692 ? new CollatorProvider() 693 : null; 694 return new IdentifierService(profile, collatorProvider); 695 } 696}