live.document.mavenplugin.sql.TableWriteStatementExporter Maven / Gradle / Ivy
package live.document.mavenplugin.sql;
import com.github.vertical_blank.sqlformatter.SqlFormatter;
import com.github.vertical_blank.sqlformatter.core.FormatConfig;
import com.github.vertical_blank.sqlformatter.languages.Dialect;
import live.document.generator.model.CallNode;
import live.document.generator.model.CallTree;
import live.document.plsqlscanner.PlSqlExplained;
import live.document.scanner.CallGraph;
import live.document.scanner.CallProcedureObject;
import live.document.scanner.DbObjectTypeEnum;
import live.document.scanner.MethodObject;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class TableWriteStatementExporter {
private static final String[] STATEMENT_STARTS = new String[]{"INSERT INTO ", "UPDATE ", "DELETE FROM ", "SELECT "};
public static final String SEPERATOR = "-----------------------------------------------------------------";
private Set tables = new HashSet<>();
private PlSqlExplained plSqlExplained;
private CallGraph callGraph;
public TableWriteStatementExporter(String[] tables, PlSqlExplained plSqlExplained, CallGraph callGraph) {
if (tables != null) {
this.tables.addAll(Arrays.asList(tables));
}
this.plSqlExplained = plSqlExplained;
this.callGraph = callGraph;
}
public String export() {
return getExportResult(new HashSet<>(), new HashSet<>());
}
public String export(CallTree callTree) {
Set allMethodsOfCallTree = getAllMethodsOfCallTree(callTree);
Set allPlsqlObjectNamesOfCallTree = getAllPlsqlObjectNamesOfCallTree(callTree);
return getExportResult(allMethodsOfCallTree, allPlsqlObjectNamesOfCallTree);
}
@NotNull
private String getExportResult(Set allMethodsOfCallTree, Set allPlsqlObjectNamesOfCallTree) {
Map> results = new HashMap<>();
if (tables.isEmpty()) {
tables = getDefaultAllTables(allMethodsOfCallTree, allPlsqlObjectNamesOfCallTree);
}
for (String table : tables) {
results.put(table, getTableStatements(table, allMethodsOfCallTree, allPlsqlObjectNamesOfCallTree));
}
Map> tableWriteFunctions = findTableWriteFunctions(allMethodsOfCallTree, allPlsqlObjectNamesOfCallTree);
StringBuilder stringBuilder = new StringBuilder();
for (Map.Entry> entry : results.entrySet()) {
stringBuilder
.append(SEPERATOR).append(System.lineSeparator())
.append("-------- TABLE: ").append(entry.getKey()).append(" --------").append(System.lineSeparator())
.append(SEPERATOR).append(System.lineSeparator()).append(System.lineSeparator());
List procedures = tableWriteFunctions.get(entry.getKey());
stringBuilder.append("-- Total procedure/Functions/Java Method which modify this table directly: ").append(procedures.size()).append(System.lineSeparator());
procedures.stream()
.forEach(p -> stringBuilder.append("-- ").append(p).append(System.lineSeparator()));
stringBuilder.append(System.lineSeparator());
entry.getValue().stream().forEach(s -> stringBuilder.append(s)
.append(System.lineSeparator()).append(System.lineSeparator()));
}
return stringBuilder.toString();
}
private Set getAllPlsqlObjectNamesOfCallTree(CallTree callTree) {
return callTree.getNodes()
.stream()
.flatMap(n -> getAllPlsqlObjectNamesOfCallNode(n).stream())
.collect(Collectors.toSet());
}
private List getAllPlsqlObjectNamesOfCallNode(CallNode node) {
List results = new ArrayList<>();
if (node.isLink() || !node.isVisible()) {
return results;
}
if (node.getMethodObject() != null) {
List collect = node.getMethodObject().calleeStream(CallProcedureObject.class)
.map(CallProcedureObject::getProcedure)
.collect(Collectors.toList());
results.addAll(collect);
}
if (node.getDbOperation() != null) {
if (node.getDbOperation().getObjectType() == DbObjectTypeEnum.PROCEDURE_FUNCTION) {
results.add(node.getDbOperation().getName());
}
}
List collect = node.getChildren().stream()
.flatMap(n -> getAllPlsqlObjectNamesOfCallNode(n).stream())
.collect(Collectors.toList());
results.addAll(collect);
return results;
}
private Set getAllMethodsOfCallTree(CallTree callTree) {
return callTree.getNodes()
.stream()
.flatMap(n -> getAllMethodsOfCallNode(n).stream())
.collect(Collectors.toSet());
}
private List getAllMethodsOfCallNode(CallNode node) {
List results = new ArrayList<>();
if (node.getMethodObject() != null) {
results.add(node.getMethodObject());
List collect = node.getChildren().stream()
.filter(n -> ! n.isLink() && n.isVisible())
.flatMap(c -> getAllMethodsOfCallNode(c).stream())
.collect(Collectors.toList());
results.addAll(collect);
}
return results;
}
/**
* 找到对Table写操作存储过程或函数,以及Java方法(在方法中调用sql直接修改表)
* @return
* @param methodScope
* @param plsqlObjectNameScope
*/
private Map> findTableWriteFunctions(Set methodScope, Set plsqlObjectNameScope) {
Map> results = new HashMap<>();
for (String table : tables) {
results.put(table,
plSqlExplained.getPlSqlObjects()
.stream()
.filter(p -> plsqlObjectNameScope.isEmpty() || plsqlObjectNameScope.contains(p.getFullName()))
.filter(p -> p.getTableWriteStatements().containsKey(table))
.map(p -> p.getFullName())
.sorted()
.collect(Collectors.toList())
);
}
Stream allMethods = callGraph.getClasses()
.stream()
.flatMap(c -> c.getMethods().stream());
allMethods
.filter(m -> methodScope.isEmpty() || methodScope.contains(m))
.forEach(method -> {
for (String table : tables) {
if (method.getTableWriteStatements().containsKey(table)) {
results.get(table).add(method.getFullName());
}
}
});
for (String table : tables) {
List sorted = results.get(table).stream().sorted().collect(Collectors.toList());
results.put(table, sorted);
}
return results;
}
private Set getDefaultAllTables(Set methodScope, Set plsqlObjectNameScope) {
List results = new ArrayList<>();
callGraph.getClasses().stream()
.flatMap(c -> c.getMethods().stream())
.filter(m -> methodScope.isEmpty() || methodScope.contains(m))
.forEach(m -> results.addAll(m.getTableWriteStatements().keySet()));
plSqlExplained.getPlSqlObjects()
.stream()
.filter(p -> plsqlObjectNameScope.isEmpty() || plsqlObjectNameScope.contains(p.getFullName()))
.forEach(o -> results.addAll(o.getTableWriteStatements().keySet()));
return new HashSet<>(results);
}
private List getTableStatements(String table, Set methodScope, Set plsqlObjectNameScope) {
List results = callGraph.getClasses().stream()
.flatMap(c -> c.getMethods().stream())
.filter(m -> methodScope.isEmpty() || methodScope.contains(m))
.flatMap(m -> {
if (m.getTableWriteStatements().containsKey(table)) {
return m.getTableWriteStatements().get(table)
.stream()
.map(s -> String.format("%s;%s---%s", s, System.lineSeparator(), m.getFullName()));
} else {
return new ArrayList().stream();
}
})
.collect(Collectors.toList());
results.addAll(plSqlExplained.getPlSqlObjects()
.stream()
.filter(p -> plsqlObjectNameScope.isEmpty() || plsqlObjectNameScope.contains(p.getFullName()))
.flatMap(o -> {
if (o.getTableWriteStatements().containsKey(table)) {
return o.getTableWriteStatements().get(table)
.stream()
.map(s -> String.format("%s;%s---%s", s, System.lineSeparator(), o.getFullName()))
;
} else {
return new ArrayList().stream();
}
})
.collect(Collectors.toList()));
return sortAndFormatStatements(results);
}
List sortAndFormatStatements(List statements) {
Set sqls = new HashSet<>(statements);
FormatConfig build = FormatConfig.builder()
.indent(" ") // Defaults to two spaces
.uppercase(true) // Defaults to false (not safe to use when SQL dialect has case-sensitive identifiers)
.maxColumnLength(140) // Defaults to 50
.build();
SqlFormatter.Formatter formatter = SqlFormatter.of(Dialect.PlSql);
return sqls.stream().map(s -> formatter.format(s, build))
.sorted()
.collect(Collectors.toList());
}
}