SuperX-Kernmodul
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

674 lines
34 KiB

package de.superx.bin;
import static de.superx.servlet.SxSQL_Server.DEFAULT_MANDANTEN_ID;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.JDBCType;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import javax.sql.DataSource;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.HelpFormatter;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.ParseException;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFCellStyle;
import org.apache.poi.xssf.usermodel.XSSFDataFormat;
import org.apache.poi.xssf.usermodel.XSSFFont;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.core.RowMapper;
import de.superx.servlet.SuperXManager;
import de.superx.servlet.SxPools;
import de.superx.spring.AppConfig;
import de.superx.spring.batch.His1DataSources;
import de.superx.spring.cli.config.CLIConfig;
import de.superx.spring.config.DataJdbcConfiguration;
import de.superx.spring.config.ServiceConfig;
/**
* A utility for creating data profiling statistics for tables in a database
* to be used in Data Warehouse Design.
* This can be used as a command line utility or embedded in an application.
*/
public class DataProfiler {
private final static String SQL_COUNT_NULL = "select count(*) from %s where %s is null";
private final static String SQL_PERCENT_UNIQUE = "select\n"
+ " count(distinct %s) as unique_anz,\n"
+ " count(distinct %s)::float / count(*) * 100 as unique_percentage\n"
+ "from\n"
+ " %s;";
private final static String SQL_RANKING = "select %s, count(*) as anz from %s where %s is not null group by %s order by 2 desc limit 10;";
private final static String SQL_MIN_MAX_LEN = "select min(length(%s)) as min_length, max(length(%s)) as max_length\n"
+ "from %s\n"
+ "where %s is not null";
private final static String SQL_COUNT_VALUE = "select count(*) from %s where %s = %s";
private final static String SQL_MIN_MAX_AVG = "select min(%s), max(%s), avg(%s) from %s where %s is not null;";
private final static String SQL_START_END = "select min(%s), max(%s) from %s where %s is not null";
private static GenericApplicationContext APPLICATION_CONTEXT = null;
private static String HELP_STRING = "Use this tool to profile database tables for dwh design. "
+ "It needs the config file 'his1_databases.properties' inside the classpath; "
+ "this file gets written automatically when starting the web application.";
static Logger logger = Logger.getLogger(DataProfiler.class);
private DataSource dataSource;
private String database;
private String schema;
private String[] tables;
/**
* Instantiate a new DataProfiler.
* @param dataSource The DataSource from which to read the table statistics.
* @param schema The schema from which to read. If null public is assumed.
* @param tables An Array of the names of the tables for which statistics should be created.
*/
public DataProfiler(DataSource dataSource, String schema, String[] tables) {
try (Connection con = dataSource.getConnection()) {
this.database = con.getCatalog();
} catch (SQLException e) {
logger.error("Couldn't read catalog", e);
}
this.schema = schema != null ? schema : "public";
List<String> tableList = Arrays.asList(tables);
// TODO: Make sorting configurable?
tableList.sort(null);
this.tables = tableList.toArray(new String[] {});
this.dataSource = dataSource;
}
public static void main(String[] args) {
System.setProperty(SuperXManager.SUPER_X_HISINONE_VERSION, "non-empty-value");
Options options = createOptions();
CommandLine parsedArgs = parseArgs(args, options);
if (parsedArgs.hasOption("h")) {
printHelp(options);
System.exit(0);
}
String database = null;
String schema = "public";
String[] tables = null;
if (parsedArgs.hasOption("d")) {
database = parsedArgs.getOptionValue('d');
}
if (parsedArgs.hasOption("s")) {
schema = parsedArgs.getOptionValue('s');
}
if (parsedArgs.hasOption("t")) {
tables = parsedArgs.getOptionValues('t');
}
if (!parsedArgs.hasOption('d') || !parsedArgs.hasOption('t')) {
printHelp(options);
System.exit(0);
}
try (GenericApplicationContext context = createContext()) {
initSxPools();
DataSource dataSource = context.getBean(His1DataSources.class).get(database);
DataProfiler profiler = new DataProfiler(dataSource, schema, tables);
profiler.outputExcel(profiler.createStatistics(), null);
}
}
/**
* Create the List of TableStatistics.
* @return List of TableStatistics.
*/
public List<TableStatistic> createStatistics() {
List<TableStatistic> tableStats = new ArrayList<>();
try {
JdbcTemplate jt = new JdbcTemplate(dataSource);
logger.info("Database: " + database);
logger.info("Schema: " + schema);
logger.info("Tables: " + Arrays.asList(tables));
try (Connection con = dataSource.getConnection()) {
jt.execute("set search_path to " + schema);
DatabaseMetaData meta = con.getMetaData();
for (String table : tables) {
long rowCount = jt.queryForObject("select count(*) from " + table, Long.class).longValue();
TableStatistic tableStat = new TableStatistic(table, rowCount);
logger.info("Table " + table);
try(ResultSet columns = meta.getColumns(null, schema, table, null);
ResultSet exported = meta.getExportedKeys(null, schema, table);
ResultSet imported = meta.getImportedKeys(null, schema, table);
ResultSet pks = meta.getPrimaryKeys(null, schema, table)) {
while(columns.next()) {
ColumnStatistic columnStat = new ColumnStatistic();
columnStat.name = columns.getString("COLUMN_NAME");
columnStat.size = columns.getInt("COLUMN_SIZE");
columnStat.decimalDigits = columns.getInt("DECIMAL_DIGITS");
columnStat.type = JDBCType.valueOf(columns.getInt("DATA_TYPE"));
columnStat.comment = columns.getString("REMARKS");
columnStat.isNullable = columns.getString("IS_NULLABLE").equalsIgnoreCase("yes");
columnStat.isAutoincrement = columns.getString("IS_AUTOINCREMENT").equalsIgnoreCase("yes");
columnStat.countNull = jt.queryForObject(String.format(SQL_COUNT_NULL, tableStat.name, columnStat.name), Long.class).longValue();
columnStat.percentNull = (double) columnStat.countNull / (double) tableStat.rowCount * 100.0;
jt.query(String.format(SQL_PERCENT_UNIQUE, columnStat.name, columnStat.name, tableStat.name), new RowCallbackHandler() {
@Override
public void processRow(ResultSet rs) throws SQLException {
columnStat.uniqueCount = rs.getLong(1);
columnStat.uniquePercent = rs.getDouble(2);
}
});
columnStat.ranking = jt.query(String.format(SQL_RANKING, columnStat.name, tableStat.name, columnStat.name, columnStat.name), new RowMapper<RankingEntry>() {
@Override
public RankingEntry mapRow(ResultSet rs, int rowNum) throws SQLException {
return new RankingEntry(rs.getString(1), rs.getInt(2));
}
});
switch (columnStat.type) {
case CHAR:
case NCHAR:
case VARCHAR:
case NVARCHAR:
case LONGVARCHAR:
case LONGNVARCHAR:
jt.query(String.format(SQL_MIN_MAX_LEN, columnStat.name, columnStat.name, tableStat.name, columnStat.name), new RowCallbackHandler() {
@Override
public void processRow(ResultSet rs) throws SQLException {
columnStat.minLen = Optional.of(Integer.valueOf(rs.getInt(1)));
columnStat.maxLen = Optional.of(Integer.valueOf(rs.getInt(2)));
}
});
columnStat.min_count = Optional.of(jt.queryForObject(String.format(SQL_COUNT_VALUE, tableStat.name,
"length(" + columnStat.name + ")", columnStat.minLen.get()), Integer.class));
columnStat.max_count = Optional.of(jt.queryForObject(String.format(SQL_COUNT_VALUE, tableStat.name,
"length(" + columnStat.name + ")", columnStat.maxLen.get()), Integer.class));
break;
case BIGINT:
case DECIMAL:
case DOUBLE:
case FLOAT:
case INTEGER:
case REAL:
case NUMERIC:
case SMALLINT:
case TINYINT:
jt.query(String.format(SQL_MIN_MAX_AVG, columnStat.name, columnStat.name, columnStat.name, tableStat.name, columnStat.name), new RowCallbackHandler() {
@Override
public void processRow(ResultSet rs) throws SQLException {
columnStat.min = Optional.of(Double.valueOf(rs.getDouble(1)));
columnStat.max = Optional.of(Double.valueOf(rs.getDouble(2)));
columnStat.avg = Optional.of(Double.valueOf(rs.getDouble(3)));
}
});
columnStat.min_count = Optional.of(jt.queryForObject(String.format(SQL_COUNT_VALUE, tableStat.name, columnStat.name, columnStat.min.get()), Integer.class));
columnStat.max_count = Optional.of(jt.queryForObject(String.format(SQL_COUNT_VALUE, tableStat.name, columnStat.name, columnStat.max.get()), Integer.class));
break;
case DATE:
jt.query(String.format(SQL_START_END, columnStat.name, columnStat.name, tableStat.name, columnStat.name), new RowCallbackHandler() {
@Override
public void processRow(ResultSet rs) throws SQLException {
columnStat.earliestDate = Optional.ofNullable(rs.getDate(1));
columnStat.latestDate = Optional.ofNullable(rs.getDate(2));
}
});
if (columnStat.earliestDate.isPresent()) {
columnStat.min_count = Optional.of(jt.queryForObject(String.format(SQL_COUNT_VALUE, tableStat.name, columnStat.name, quote(columnStat.earliestDate.get())), Integer.class));
}
if (columnStat.latestDate.isPresent()) {
columnStat.max_count = Optional.of(jt.queryForObject(String.format(SQL_COUNT_VALUE, tableStat.name, columnStat.name, quote(columnStat.latestDate.get())), Integer.class));
}
break;
case TIMESTAMP:
case TIMESTAMP_WITH_TIMEZONE:
jt.query(String.format(SQL_START_END, columnStat.name, columnStat.name, tableStat.name, columnStat.name), new RowCallbackHandler() {
@Override
public void processRow(ResultSet rs) throws SQLException {
columnStat.earliestTimestamp = Optional.ofNullable(rs.getTimestamp(1));
columnStat.latestTimestamp = Optional.ofNullable(rs.getTimestamp(2));
}
});
if (columnStat.earliestTimestamp.isPresent()) {
columnStat.min_count = Optional.of(jt.queryForObject(String.format(SQL_COUNT_VALUE, tableStat.name, columnStat.name, quote(columnStat.earliestTimestamp.get())), Integer.class));
}
if (columnStat.latestTimestamp.isPresent()) {
columnStat.max_count = Optional.of(jt.queryForObject(String.format(SQL_COUNT_VALUE, tableStat.name, columnStat.name, quote(columnStat.latestTimestamp.get())), Integer.class));
}
break;
case TIME:
case TIME_WITH_TIMEZONE:
jt.query(String.format(SQL_START_END, columnStat.name, columnStat.name, tableStat.name, columnStat.name), new RowCallbackHandler() {
@Override
public void processRow(ResultSet rs) throws SQLException {
columnStat.earliestTime = Optional.ofNullable(rs.getTime(1));
columnStat.latestTime = Optional.ofNullable(rs.getTime(2));
}
});
if (columnStat.earliestTime.isPresent()) {
columnStat.min_count = Optional.of(jt.queryForObject(String.format(SQL_COUNT_VALUE, tableStat.name, columnStat.name, quote(columnStat.earliestTime.get())), Integer.class));
}
if (columnStat.latestTime.isPresent()) {
columnStat.max_count = Optional.of(jt.queryForObject(String.format(SQL_COUNT_VALUE, tableStat.name, columnStat.name, quote(columnStat.latestTime.get())), Integer.class));
}
break;
default:
}
tableStat.columns.add(columnStat);
}
while (exported.next()) {
String fromColumn = exported.getString("PKCOLUMN_NAME");
String toTable = exported.getString("FKTABLE_NAME");
String toColumn = exported .getString("FKCOLUMN_NAME");
tableStat.exportedKeys.add(new ForeignKey(fromColumn, toTable, toColumn));
}
while (imported.next()) {
String fromColumn = imported.getString("FKCOLUMN_NAME");
String toTable = imported.getString("PKTABLE_NAME");
String toColumn = imported .getString("PKCOLUMN_NAME");
tableStat.importedKeys.add(new ForeignKey(fromColumn, toTable, toColumn));
}
while (pks.next()) {
String column = pks.getString("COLUMN_NAME");
tableStat.primaryKeys.add(column);
}
}
tableStats.add(tableStat);
}
}
} catch (SQLException e) {
logger.error("SQL Fehler", e);
}
return tableStats;
}
/**
* Output statistic for a list of tables to an Excel file.
* The statistics of each table are written to a separate sheet.
* @param tableStats The list of TableStats
* @param outputFile The File to output to. If null output to current dir with a default file name.
*/
public void outputExcel(List<TableStatistic> tableStats, File outputFile) {
XSSFWorkbook workbook = new XSSFWorkbook();
XSSFDataFormat dataFormat = workbook.createDataFormat();
XSSFCellStyle cellStyleDouble = workbook.createCellStyle();
cellStyleDouble.setDataFormat(dataFormat.getFormat("0.##"));
XSSFCellStyle headerStyle = workbook.createCellStyle();
XSSFFont bold = workbook.createFont();
bold.setBold(true);
headerStyle.setFont(bold);
for (TableStatistic tableStat : tableStats) {
XSSFSheet sheet = workbook.createSheet("Table " + tableStat.name);
XSSFRow header = sheet.createRow(0);
XSSFCell cell = header.createCell(0);
cell.setCellValue("Database: " + database);
cell.setCellStyle(headerStyle);
cell = header.createCell(1);
cell.setCellStyle(headerStyle);
cell.setCellValue("Schema: " + schema);
XSSFRow first = sheet.createRow(2);
first.createCell(0).setCellValue("Table");
first.getCell(0).setCellStyle(headerStyle);
first.createCell(1).setCellValue("Row Count");
first.getCell(1).setCellStyle(headerStyle);
first.createCell(2).setCellValue("Column");
first.getCell(2).setCellStyle(headerStyle);
first.createCell(3).setCellValue("Type");
first.getCell(3).setCellStyle(headerStyle);
first.createCell(4).setCellValue("Size");
first.getCell(4).setCellStyle(headerStyle);
first.createCell(5).setCellValue("Not Null");
first.getCell(5).setCellStyle(headerStyle);
first.createCell(6).setCellValue("Autoincrement");
first.getCell(6).setCellStyle(headerStyle);
first.createCell(7).setCellValue("Count NULL");
first.getCell(7).setCellStyle(headerStyle);
first.createCell(8).setCellValue("% NULL");
first.getCell(8).setCellStyle(headerStyle);
first.createCell(9).setCellValue("Count Unique");
first.getCell(9).setCellStyle(headerStyle);
first.createCell(10).setCellValue("% Unique");
first.getCell(10).setCellStyle(headerStyle);
first.createCell(11).setCellValue("Min Len");
first.getCell(11).setCellStyle(headerStyle);
first.createCell(12).setCellValue("Max Len");
first.getCell(12).setCellStyle(headerStyle);
first.createCell(13).setCellValue("Min");
first.getCell(13).setCellStyle(headerStyle);
first.createCell(14).setCellValue("Max");
first.getCell(14).setCellStyle(headerStyle);
first.createCell(15).setCellValue("Avg");
first.getCell(15).setCellStyle(headerStyle);
first.createCell(16).setCellValue("Min Count");
first.getCell(16).setCellStyle(headerStyle);
first.createCell(17).setCellValue("Max Count");
first.getCell(17).setCellStyle(headerStyle);
first.createCell(18).setCellValue("Earliest");
first.getCell(18).setCellStyle(headerStyle);
first.createCell(19).setCellValue("Latest");
first.getCell(19).setCellStyle(headerStyle);
first.createCell(20).setCellValue("Comment");
first.getCell(20).setCellStyle(headerStyle);
int row = 3;
XSSFRow tableRow = sheet.createRow(row);
tableRow.createCell(0).setCellValue(tableStat.name);
tableRow.getCell(0).setCellStyle(headerStyle);
tableRow.createCell(1).setCellValue(tableStat.rowCount);
tableRow.getCell(1).setCellStyle(headerStyle);
for (ColumnStatistic columnStat : tableStat.columns) {
row += 1;
XSSFRow descRow = sheet.createRow(row);
descRow.createCell(2).setCellValue(columnStat.name);
if (tableStat.primaryKeys.contains(columnStat.name)) {
descRow.getCell(2).setCellValue(columnStat.name + " (PK)");
descRow.getCell(2).setCellStyle(headerStyle);
}
descRow.createCell(3).setCellValue(columnStat.type.getName());
descRow.createCell(4).setCellValue(columnStat.size);
if (columnStat.decimalDigits != 0) {
descRow.getCell(4).setCellValue(
Double.valueOf(columnStat.size + "." + columnStat.decimalDigits).doubleValue()
);
descRow.getCell(4).setCellStyle(cellStyleDouble);
}
descRow.createCell(5).setCellValue(!columnStat.isNullable);
descRow.createCell(6).setCellValue(columnStat.isAutoincrement);
descRow.createCell(7).setCellValue(columnStat.countNull);
descRow.createCell(8).setCellValue(columnStat.percentNull);
descRow.getCell(8).setCellStyle(cellStyleDouble);
descRow.createCell(9).setCellValue(columnStat.uniqueCount);
descRow.createCell(10).setCellValue(columnStat.uniquePercent);
descRow.getCell(10).setCellStyle(cellStyleDouble);
if (columnStat.minLen.isPresent()) {
descRow.createCell(11).setCellValue(columnStat.minLen.get().doubleValue());
}
if (columnStat.maxLen.isPresent()) {
descRow.createCell(12).setCellValue(columnStat.maxLen.get().doubleValue());
}
if (columnStat.min.isPresent()) {
descRow.createCell(13).setCellValue(columnStat.min.get().doubleValue());
descRow.getCell(13).setCellStyle(cellStyleDouble);
}
if (columnStat.max.isPresent()) {
descRow.createCell(14).setCellValue(columnStat.max.get().doubleValue());
descRow.getCell(14).setCellStyle(cellStyleDouble);
}
if (columnStat.avg.isPresent()) {
descRow.createCell(15).setCellValue(columnStat.avg.get().doubleValue());
descRow.getCell(15).setCellStyle(cellStyleDouble);
}
if (columnStat.min_count.isPresent()) {
descRow.createCell(16).setCellValue(columnStat.min_count.get().doubleValue());
descRow.getCell(16).setCellStyle(cellStyleDouble);
}
if (columnStat.max_count.isPresent()) {
descRow.createCell(17).setCellValue(columnStat.max_count.get().doubleValue());
descRow.getCell(17).setCellStyle(cellStyleDouble);
}
if (columnStat.earliestDate.isPresent()) {
descRow.createCell(18).setCellValue(columnStat.earliestDate.get().toString());
}
if (columnStat.latestDate.isPresent()) {
descRow.createCell(19).setCellValue(columnStat.latestDate.get().toString());
}
if (columnStat.earliestTime.isPresent()) {
descRow.createCell(18).setCellValue(columnStat.earliestTime.get().toString());
}
if (columnStat.latestTime.isPresent()) {
descRow.createCell(19).setCellValue(columnStat.latestTime.get().toString());
}
if (columnStat.earliestTimestamp.isPresent()) {
descRow.createCell(18).setCellValue(columnStat.earliestTimestamp.get().toString());
}
if (columnStat.latestTimestamp.isPresent()) {
descRow.createCell(19).setCellValue(columnStat.latestTimestamp.get().toString());
}
descRow.createCell(20).setCellValue(columnStat.comment);
}
for (int i = 0; i < 20; i++) {
sheet.autoSizeColumn(i);
}
XSSFRow frequHeader1 = sheet.createRow(row + 2);
XSSFRow frequHeader2 = sheet.createRow(row + 3);
frequHeader1.createCell(0).setCellValue("Frequency");
frequHeader1.getCell(0).setCellStyle(headerStyle);
frequHeader2.createCell(0).setCellValue("Column");
frequHeader2.getCell(0).setCellStyle(headerStyle);
for (int n = 1; n <= 10; n++) {
frequHeader1.createCell(n).setCellValue(n);
frequHeader1.getCell(n).setCellStyle(headerStyle);
}
for (int colNr = 0; colNr < tableStat.columns.size(); colNr++) {
XSSFRow frequRowLabel = sheet.createRow(row + 4 + 2 * colNr);
XSSFRow frequRowCount = sheet.createRow(row + 5 + 2 * colNr);
frequRowLabel.createCell(0).setCellValue(tableStat.columns.get(colNr).name);
for (int rankNr = 0; rankNr < tableStat.columns.get(colNr).ranking.size(); rankNr++) {
frequRowLabel.createCell(rankNr + 1).setCellValue(tableStat.columns.get(colNr).ranking.get(rankNr).label);
frequRowCount.createCell(rankNr + 1).setCellValue(tableStat.columns.get(colNr).ranking.get(rankNr).count);
}
}
row = row + 2 * tableStat.columns.size() + 5;
XSSFRow exHeader1 = sheet.createRow(row);
exHeader1.createCell(0).setCellValue("Exported Keys");
exHeader1.getCell(0).setCellStyle(headerStyle);
XSSFRow exHeader2 = sheet.createRow(row + 1);
exHeader2.createCell(0).setCellValue("From Column");
exHeader2.getCell(0).setCellStyle(headerStyle);
exHeader2.createCell(1).setCellValue("To Table");
exHeader2.getCell(1).setCellStyle(headerStyle);
exHeader2.createCell(2).setCellValue("To Column");
exHeader2.getCell(2).setCellStyle(headerStyle);
for (int fkNr = 0; fkNr < tableStat.exportedKeys.size(); fkNr++) {
XSSFRow fkRow = sheet.createRow(row + 2 + fkNr);
fkRow.createCell(0).setCellValue(tableStat.exportedKeys.get(fkNr).fromColumn);
fkRow.createCell(1).setCellValue(tableStat.exportedKeys.get(fkNr).toTable);
fkRow.createCell(2).setCellValue(tableStat.exportedKeys.get(fkNr).toColumn);
}
row = row + 3 + tableStat.exportedKeys.size();
XSSFRow imHeader1 = sheet.createRow(row);
imHeader1.createCell(0).setCellValue("Imported Keys");
imHeader1.getCell(0).setCellStyle(headerStyle);
XSSFRow imHeader2 = sheet.createRow(row + 1);
imHeader2.createCell(0).setCellValue("From Table");
imHeader2.getCell(0).setCellStyle(headerStyle);
imHeader2.createCell(1).setCellValue("From Column");
imHeader2.getCell(1).setCellStyle(headerStyle);
imHeader2.createCell(2).setCellValue("To Column");
imHeader2.getCell(2).setCellStyle(headerStyle);
for (int fkNr = 0; fkNr < tableStat.importedKeys.size(); fkNr++) {
XSSFRow fkRow = sheet.createRow(row + 2 + fkNr);
fkRow.createCell(0).setCellValue(tableStat.importedKeys.get(fkNr).toTable);
fkRow.createCell(1).setCellValue(tableStat.importedKeys.get(fkNr).toColumn);
fkRow.createCell(2).setCellValue(tableStat.importedKeys.get(fkNr).fromColumn);
}
}
File currDir = new File(".");
String path = currDir.getAbsolutePath();
String fileLocation = path.substring(0, path.length() - 1) + "db_profile_" + database + ".xlsx";
if (outputFile != null) {
fileLocation = outputFile.getAbsolutePath();
}
logger.info("Writing to " + fileLocation);
FileOutputStream outputStream;
try {
outputStream = new FileOutputStream(fileLocation);
workbook.write(outputStream);
workbook.close();
} catch (IOException e) {
logger.error("Couldn't write excel file", e);
}
}
private static Options createOptions() {
Options options = new Options();
Option opt;
opt = new Option("h", "help", false, "get help");
options.addOption(opt);
opt = new Option("t", "tables", true, "tables");
opt.setArgs(Option.UNLIMITED_VALUES);
// opt.setRequired(true);
options.addOption(opt);
opt = new Option("s", "schema", true, "schema");
options.addOption(opt);
opt = new Option("d", "database", true, "database");
// opt.setRequired(true);
options.addOption(opt);
return options;
}
private static CommandLine parseArgs(String[] args, Options options) {
CommandLineParser parser = new GnuParser();
try {
return parser.parse(options, args, false);
} catch (ParseException e) {
System.out.println("error while reading the command line parameters:");
e.printStackTrace();
System.exit(1);
}
return null;
}
private static void initSxPools() {
try {
List<String> mandantenNamen = new LinkedList<String>();
mandantenNamen.add(DEFAULT_MANDANTEN_ID);
SxPools.closeAll();
SxPools.init(mandantenNamen);
SxPools.get(DEFAULT_MANDANTEN_ID).init();
SxPools.get(DEFAULT_MANDANTEN_ID).initLogging(true, Level.DEBUG);
// also init kettle env, set plugin dir
SuperXManager.initKettleEnv(APPLICATION_CONTEXT);
} catch (Exception e) {
System.out.println("error while initialising the SuperX pools:");
e.printStackTrace();
System.exit(1);
}
}
private static void printHelp(Options options) {
HelpFormatter help = new HelpFormatter();
help.printHelp(HELP_STRING, options);
}
private static GenericApplicationContext createContext() {
/*
* https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/AnnotationConfigApplicationContext.html
* quote:
* "In case of multiple @Configuration classes, @Bean methods defined in later classes will override those defined in earlier classes.
* This can be leveraged to deliberately override certain bean definitions via an extra @Configuration class."
* - so it's alright to override some beans via "CLIConfig"
*/
if (APPLICATION_CONTEXT == null) {
APPLICATION_CONTEXT = new AnnotationConfigApplicationContext(AppConfig.class, DataJdbcConfiguration.class, CLIConfig.class, ServiceConfig.class);
}
return APPLICATION_CONTEXT;
}
private static String quote(Object o) {
return "'" + o + "'";
}
}
class TableStatistic {
public String name;
public long rowCount;
public List<ColumnStatistic> columns;
public List<ForeignKey> exportedKeys;
public List<ForeignKey> importedKeys;
public List<String> primaryKeys;
public TableStatistic(String name, long rowCount) {
this.name = name;
this.rowCount = rowCount;
this.columns = new ArrayList<>();
this.exportedKeys = new ArrayList<>();
this.importedKeys = new ArrayList<>();
this.primaryKeys = new ArrayList<>();
}
}
class ColumnStatistic {
public String name;
public JDBCType type;
public int size;
public int decimalDigits;
public boolean isNullable;
public boolean isAutoincrement;
public String comment;
public long countNull;
public double percentNull;
public long uniqueCount;
public double uniquePercent;
public Optional<Integer> max_count = Optional.empty();
public Optional<Integer> min_count = Optional.empty();
public List<RankingEntry> ranking;
public Optional<Integer> minLen = Optional.empty();
public Optional<Integer> maxLen = Optional.empty();
public Optional<Double> min = Optional.empty();
public Optional<Double> max = Optional.empty();
public Optional<Double> avg = Optional.empty();
public Optional<Date> earliestDate = Optional.empty();
public Optional<Date> latestDate = Optional.empty();
public Optional<Time> earliestTime = Optional.empty();
public Optional<Time> latestTime = Optional.empty();
public Optional<Timestamp> earliestTimestamp = Optional.empty();
public Optional<Timestamp> latestTimestamp = Optional.empty();
}
class RankingEntry {
public RankingEntry(String label, int count) {
this.label = label;
this.count = count;
}
public String label;
public int count;
}
class ForeignKey {
public ForeignKey(String fromColumn, String toTable, String toColumn) {
this.fromColumn = fromColumn;
this.toTable = toTable;
this.toColumn = toColumn;
}
public String fromColumn;
public String toTable;
public String toColumn;
}