Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.nuiton.db.meta.DatabaseMetaBuilder Maven / Gradle / Ivy
package org.nuiton.db.meta;
/*-
* #%L
* Nuiton DB Meta
* %%
* Copyright (C) 2019 Nuiton
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* .
* #L%
*/
import com.google.common.base.CaseFormat;
import com.google.common.base.Converter;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
import com.google.common.collect.Streams;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.reflections.Reflections;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author Arnaud Thimel (Code Lutin)
* @since 0.1
*/
public class DatabaseMetaBuilder {
private static final Log log = LogFactory.getLog(DatabaseMetaBuilder.class);
public static final Pattern POSSIBLE_VALUES_PATTERN = Pattern.compile("(Valeurs possibles :){1} (.*)");
protected static Cache metaCountCache;
protected final Consumer worker;
protected final String packageForEnumResolver;
public DatabaseMetaBuilder(Consumer worker, String packageForEnumResolver) {
this.worker = worker;
this.packageForEnumResolver = packageForEnumResolver;
}
public DatabaseMetaBuilder(Connection sqlConnection, String packageForEnumResolver) {
this(sqlWork -> {
try {
sqlWork.execute(sqlConnection);
} catch (SQLException e) {
log.error("Unable to do SqlWork", e);
}
}, packageForEnumResolver);
}
public DatabaseMetaBuilder(Connection sqlConnection) {
this(sqlConnection, null);
}
protected Cache getMetaCountCache() {
if (metaCountCache == null) {
metaCountCache = CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.HOURS).build();
}
return metaCountCache;
}
protected void doWork(SqlWork work) {
worker.accept(work);
}
protected ImmutableSet getTableNames(String schema, String type) {
ImmutableSet.Builder builder = ImmutableSet.builder();
doWork(connection -> {
DatabaseMetaData metaData = connection.getMetaData();
try (ResultSet resultSet = metaData.getTables(null, schema, null, new String[]{type})) {
while (resultSet.next()) {
// Les colonnes (PG) : table_cat, table_schem, table_name, table_type, remarks
String name = resultSet.getString("TABLE_NAME");
builder.add(name);
}
}
});
return builder.build();
}
protected ImmutableSet getColumnNames(String schema, String tableName) {
ImmutableSet.Builder builder = ImmutableSet.builder();
doWork(connection -> {
DatabaseMetaData metaData = connection.getMetaData();
try (ResultSet resultSet = metaData.getColumns(null, schema, tableName, null)) {
// Les colonnes (PG) : TABLE_CAT, TABLE_SCHEM, TABLE_NAME, COLUMN_NAME, DATA_TYPE, TYPE_NAME, COLUMN_SIZE, BUFFER_LENGTH, DECIMAL_DIGITS, NUM_PREC_RADIX, NULLABLE, REMARKS, COLUMN_DEF, SQL_DATA_TYPE, SQL_DATETIME_SUB, CHAR_OCTET_LENGTH, ORDINAL_POSITION, IS_NULLABLE, SCOPE_CATLOG, SCOPE_SCHEMA, SCOPE_TABLE, SOURCE_DATA_TYPE, IS_AUTOINCREMENT
// ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
// int columnCount = resultSetMetaData.getColumnCount();
// String[] columns = new String[columnCount];
// for (int i = 0; i readTableMetas(String type,
String schema,
Map comments,
Set primaryKeys,
Set uniqueColumns,
Multimap foreignKeys,
ImmutableList customTypes) {
// doWork(connection -> {
// DatabaseMetaData metaData = connection.getMetaData();
// ResultSet resultSet = metaData.getTables(null, schema, null, null);
// ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
// int columnCount = resultSetMetaData.getColumnCount();
// String[] columns = new String[columnCount];
// for (int i = 0; i tableMetas = ImmutableList.builder();
for (String tableName : getTableNames(schema, type)) {
ImmutableSet columnNames = getColumnNames(schema, tableName);
doWork(connection -> {
List columns = Lists.newArrayList();
for (String tableColumnName : columnNames) {
DatabaseMetaData metaData = connection.getMetaData();
try (ResultSet resultSet = metaData.getColumns(null, schema, tableName, tableColumnName)) {
// Les colonnes (PG) : TABLE_CAT, TABLE_SCHEM, TABLE_NAME, COLUMN_NAME, DATA_TYPE, TYPE_NAME, COLUMN_SIZE, BUFFER_LENGTH, DECIMAL_DIGITS, NUM_PREC_RADIX, NULLABLE, REMARKS, COLUMN_DEF, SQL_DATA_TYPE, SQL_DATETIME_SUB, CHAR_OCTET_LENGTH, ORDINAL_POSITION, IS_NULLABLE, SCOPE_CATLOG, SCOPE_SCHEMA, SCOPE_TABLE, SOURCE_DATA_TYPE, IS_AUTOINCREMENT
// ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
// int columnCount = resultSetMetaData.getColumnCount();
// String[] metas = new String[columnCount];
// for (int i = 0; i < columnCount; i++) {
// String metaName = resultSetMetaData.getColumnName(i + 1);
// // System.out.println(metaName);
// metas[i] = metaName;
// }
while (resultSet.next()) {
String dataType = resultSet.getString("TYPE_NAME");
Optional size = Optional.empty();
Optional customTypeMeta = customTypes.stream().filter(customType -> customType.getName().equals(dataType)).findAny();
if (!customTypeMeta.isPresent()) {
size = Optional.of(resultSet.getInt("COLUMN_SIZE"));
}
boolean nullable = resultSet.getInt("NULLABLE") == 1;
ColumnRef columnRef = asCommentKey(tableName, tableColumnName);
Optional comment = Optional.ofNullable(comments.get(columnRef));
boolean primaryKey = primaryKeys.contains(columnRef);
boolean unique = uniqueColumns.contains(columnRef);
if (!comment.isPresent() && log.isWarnEnabled()) {
log.warn(String.format("Pas de commentaire pour la colonne %s", columnRef.pretty()));
}
ImmutableSet keys = getForeignKeys(foreignKeys, columnRef);
Optional> possibleValues = Optional.empty();
if (comment.isPresent()) {
Matcher matcher = POSSIBLE_VALUES_PATTERN.matcher(comment.get());
if (matcher.find()) {
String values = matcher.group(2);
Map map;
if (values.contains("=")) {
map = Splitter.on(",")
.omitEmptyStrings()
.trimResults()
.withKeyValueSeparator("=")
.split(values);
if (log.isWarnEnabled()) {
for (Map.Entry entry : map.entrySet()) {
if (entry.getKey().equals(entry.getValue())) {
String message = String.format(
"Valeur '%s' suspecte (identique clé=valeur) pour la colonne %s",
entry.getKey(),
columnRef.pretty());
log.warn(message);
}
}
}
} else {
Iterable tabPossibleValues = Splitter.on(",")
.omitEmptyStrings()
.trimResults()
.split(values);
map = Maps.newHashMap();
for (String possibleValue:tabPossibleValues) {
map.put(possibleValue, null);
}
}
possibleValues = Optional.of(map);
}
if (customTypeMeta.isPresent() && log.isWarnEnabled()) {
if (possibleValues.isPresent()) {
for (String value : possibleValues.get().keySet()) {
if (!customTypeMeta.get().getValues().contains(value)) {
String message = String.format(
"La valeur '%s' n'est pas attendue selon le type %s pour la colonne %s",
value,
customTypeMeta.get().getName(),
columnRef.pretty());
log.warn(message);
}
}
} else {
String message = String.format(
"La liste des valeurs n'est pas détaillée pour la colonne %s de type %s",
columnRef.pretty(),
customTypeMeta.get().getName());
log.warn(message);
}
}
}
ColumnMeta column = ImmutableColumnMeta.builder()
.name(tableColumnName)
.type(dataType)
.customType(customTypeMeta)
.length(size)
.nullable(nullable)
.comment(comment)
.isPrimaryKey(primaryKey)
.isUnique(unique)
.isForeignKey(!keys.isEmpty())
.foreignKeyColumns(keys)
.possibleValues(possibleValues)
.build();
columns.add(column);
// System.out.println(String.format("*********** %s %s (%d) %s ***********", tableColumnName, dataType, size, nullable));
// for (int i = 0; i comment = Optional.ofNullable(comments.get(commentKey));
if (!comment.isPresent() && log.isWarnEnabled()) {
log.warn(String.format("Pas de commentaire pour la table/vue %s", tableName));
}
TableMeta tableMeta = ImmutableTableMeta.builder()
.name(tableName)
.isView("VIEW".equals(type))
.columns(columns)
.count(Optional.ofNullable(count))
.comment(comment)
.build();
tableMetas.add(tableMeta);
});
}
ImmutableList result = tableMetas.build();
return result;
}
protected ImmutableSet getForeignKeys(Multimap foreignKeys, ColumnRef columnRef) {
Collection keys = foreignKeys.get(columnRef);
if (CollectionUtils.isEmpty(keys)) {
return ImmutableSet.of();
}
ImmutableSet result = keys.stream()
.map(ColumnRef::pretty)
.collect(ImmutableSet.toImmutableSet());
return result;
}
protected ColumnRef asCommentKey(String tableName, String columnName) {
Optional right = Optional.ofNullable(StringUtils.trimToNull(columnName));
if (right.isPresent()) {
return ColumnRef.forColumn(tableName, columnName);
} else {
return ColumnRef.forTable(tableName);
}
}
protected ColumnRef asProcedureKey(String procedureName, String argTypesString) {
Optional args = Optional.ofNullable(StringUtils.trimToNull(argTypesString));
String procedureKey = procedureName;
if (args.isPresent()) {
procedureKey += String.format(" (%s)", args.get());
}
ColumnRef procedureRef = asCommentKey(procedureKey, null);
return procedureRef;
}
/**
* Dans la Map renvoyée, la clé est constituée d'une Pair de TABLE_NAME, COLUMN_NAME. Sachant que COLUMN_NAME peut
* être null s'il s'agit d'un commentaire sur la table.
*/
protected Map readTableAndViewComments(String schema) {
ImmutableMap.Builder builder = ImmutableMap.builder();
doWork(connection -> {
String sql = String.format("SELECT " +
" t.table_type AS table_type, " +
" t.table_name AS table, " +
" c.column_name AS colonne, " +
" d.description AS commentaire " +
"FROM ( " +
" SELECT " +
" table_schema, " +
" table_name, " +
" (table_schema || '.' || table_name)::regclass::oid AS objoid, " +
" table_type " +
" FROM " +
" information_schema.tables " +
" WHERE " +
" table_schema = '%s') t " +
" INNER JOIN pg_catalog.pg_description d " +
" ON d.objoid = t.objoid " +
" LEFT JOIN information_schema.columns c " +
" ON c.table_schema = t.table_schema " +
" AND c.table_name = t.table_name " +
" AND c.ordinal_position = d.objsubid " +
" ORDER BY " +
" t.table_name, " +
" d.objsubid; ", schema);
try (PreparedStatement statement = connection.prepareStatement(sql)) {
try (ResultSet resultSet = statement.executeQuery()) {
while (resultSet.next()) {
// String tableType = resultSet.getString("table_type");
String table = resultSet.getString("table");
String colonne = resultSet.getString("colonne");
String commentaire = resultSet.getString("commentaire");
ColumnRef key = asCommentKey(table, colonne);
builder.put(key, commentaire);
}
}
}
});
ImmutableMap result = builder.build();
return result;
}
/**
* Dans la Map renvoyée, la clé est constituée d'une Pair de PROCEDURE_NAME, ATTRIBUTE_NAME. Sachant que
* ATTRIBUTE_NAME peut être null s'il s'agit d'un commentaire sur la procédure.
*/
protected Map readProcedureComments(String schema) {
ImmutableMap.Builder builder = ImmutableMap.builder();
doWork(connection -> {
String sql = String.format(
"SELECT" +
" p.proname AS nom, " +
" p.proargtypes AS arguments, " +
" d.description AS commentaire " +
"FROM " +
" pg_catalog.pg_namespace n " +
" JOIN pg_catalog.pg_proc p ON p.pronamespace = n.oid " +
" INNER JOIN pg_catalog.pg_description d ON d.objoid = p.oid " +
"WHERE " +
" n.nspname = '%s' " +
" ; "
, schema);
try (PreparedStatement statement = connection.prepareStatement(sql)) {
try (ResultSet resultSet = statement.executeQuery()) {
while (resultSet.next()) {
String table = resultSet.getString("nom");
String argTypesString = resultSet.getString("arguments");
String commentaire = resultSet.getString("commentaire");
ColumnRef key = asProcedureKey(table, argTypesString);
builder.put(key, commentaire);
}
}
}
});
ImmutableMap result = builder.build();
return result;
}
protected ImmutableList readProcedureMetas(String schema) {
Map comments = readProcedureComments(schema);
ImmutableList.Builder builder = ImmutableList.builder();
Map types = new HashMap<>();
doWork(connection -> {
String sql = " select oid, t.typname from pg_type t ";
try (PreparedStatement statement = connection.prepareStatement(sql)) {
try (ResultSet resultSet = statement.executeQuery()) {
while (resultSet.next()) {
int id = resultSet.getInt("oid");
String type = resultSet.getString("typname");
types.put(id, type);
}
}
}
});
doWork(connection -> {
String sql = String.format("select p.proname, p.proargnames, p.proargtypes " +
" from pg_proc p " +
" inner join pg_namespace n " +
" on n.oid = p.pronamespace " +
" where " +
// On limite à l'utilisateur autre que l'utilisateur système (> 10), à moins que ce soit l'utilisateur courant
" (p.proowner > 10 or p.proowner = (select usesysid FROM pg_user u where u.usename = (select * from current_user))) " +
" and n.nspname='%s'", schema);
try (PreparedStatement statement = connection.prepareStatement(sql)) {
try (ResultSet resultSet = statement.executeQuery()) {
while (resultSet.next()) {
String name = resultSet.getString("proname");
String argTypesString = resultSet.getString("proargtypes");
List args = new LinkedList<>();
if (StringUtils.isNotEmpty(argTypesString)) {
ImmutableList argTypes = Streams.stream(Splitter.on(' ').split(argTypesString))
.map(Integer::valueOf)
.map(types::get)
.collect(ImmutableList.toImmutableList());
String proArgs = Strings.nullToEmpty(resultSet.getString("proargnames"));
// {source_exercise_id,destination_exercise_id}
if (proArgs.startsWith("{") && proArgs.endsWith("}")) {
proArgs = proArgs.substring(0, proArgs.length() - 1).substring(1);
}
ImmutableList argNames = ImmutableList.copyOf(Splitter.on(',').omitEmptyStrings().split(proArgs));
for (int i=0; i comment = Optional.ofNullable(comments.get(commentKey));
ProcedureMeta procedureMeta = ImmutableProcedureMeta.builder()
.name(name)
.comment(comment)
.args(args)
.build();
builder.add(procedureMeta);
}
}
}
});
ImmutableList result = builder.build();
return result;
}
/**
* Dans la Multimap renvoyée, la clé est constituée d'une Pair de TABLE_NAME, COLUMN_NAME. Les valeurs sont
* également des Pair de TABLE_NAME, COLUMN_NAME représentant la colonne référencée par la FK
*/
protected Multimap readForeignKeys(String schema) {
LinkedHashMultimap result = LinkedHashMultimap.create();
doWork(connection -> {
String sql = String.format("select " +
" contrainte.conname as foreign_key_name, " +
" local_class.relname as table, " +
" local_attribute.attname as column, " +
" foreign_class.relname as foreign_table, " +
" foreign_attribute.attname as foreign_column " +
" from " +
" (select " +
" unnest(con1.conkey) as \"parent\", " +
" unnest(con1.confkey) as \"child\", " +
" con1.confrelid, " +
" con1.conrelid, " +
" con1.conname " +
" from " +
" pg_class cl " +
" join pg_namespace ns " +
" on cl.relnamespace = ns.oid " +
" join pg_constraint con1 " +
" on con1.conrelid = cl.oid " +
" where " +
" ns.nspname = '%s' " +
" and con1.contype = 'f' " +
" ) contrainte " +
" join pg_class foreign_class " +
" on foreign_class.oid = contrainte.confrelid " +
" join pg_attribute foreign_attribute " +
" on foreign_attribute.attrelid = contrainte.confrelid " +
" and foreign_attribute.attnum = contrainte.child " +
" join pg_class local_class " +
" \t on local_class.oid = contrainte.conrelid " +
" join pg_attribute local_attribute " +
" on local_attribute.attrelid = contrainte.conrelid " +
" and local_attribute.attnum = contrainte.parent " +
" order by local_class.relname asc, " +
" local_attribute.attname asc " +
" ;\n", schema);
try (PreparedStatement statement = connection.prepareStatement(sql)) {
try (ResultSet resultSet = statement.executeQuery()) {
while (resultSet.next()) {
String table = resultSet.getString("table");
String colonne = resultSet.getString("column");
String foreignTable = resultSet.getString("foreign_table");
String foreignColonne = resultSet.getString("foreign_column");
ColumnRef source = ColumnRef.forColumn(table, colonne);
ColumnRef reference = ColumnRef.forColumn(foreignTable, foreignColonne);
result.put(source, reference);
}
}
}
});
return result;
}
/**
* Renvoie un ensemble de Pair de TABLE_NAME, COLUMN_NAME représentant les clés primaires de la base.
*/
protected ImmutableSet readPrimaryKeys(String schema) {
ImmutableSet.Builder builder = ImmutableSet.builder();
doWork(connection -> {
String sql = String.format("select " +
" con.conname as primary_key_name, " +
" table_class.relname as table, " +
" column_attribute.attname as column " +
"from " +
" (select " +
" unnest(con1.conkey) as \"parent\", " +
" con1.conrelid, " +
" con1.conname " +
" from " +
" pg_class cl " +
" join pg_namespace ns " +
" on cl.relnamespace = ns.oid " +
" join pg_constraint con1 " +
" on con1.conrelid = cl.oid " +
" where " +
" ns.nspname = '%s' " +
" and con1.contype = 'p' " +
" ) con " +
" join pg_class table_class " +
" on table_class.oid = con.conrelid " +
" join pg_attribute column_attribute " +
" on column_attribute.attrelid = con.conrelid " +
" and column_attribute.attnum = con.parent " +
" ; ", schema);
try (PreparedStatement statement = connection.prepareStatement(sql)) {
try (ResultSet resultSet = statement.executeQuery()) {
while (resultSet.next()) {
String table = resultSet.getString("table");
String colonne = resultSet.getString("column");
ColumnRef source = ColumnRef.forColumn(table, colonne);
builder.add(source);
}
}
}
});
ImmutableSet result = builder.build();
return result;
}
/**
* Renvoie un ensemble de Pair de TABLE_NAME, COLUMN_NAME représentant les colonnes avec une contrainte d'unicité.
*/
protected ImmutableSet readUniqueColumns(String schema) {
ImmutableSet.Builder builder = ImmutableSet.builder();
doWork(connection -> {
String sql = String.format("select " +
" con.conname as primary_key_name, " +
" table_class.relname as table, " +
" column_attribute.attname as column " +
"from " +
" (select " +
" unnest(con1.conkey) as \"parent\", " +
" con1.conrelid, " +
" con1.conname " +
" from " +
" pg_class cl " +
" join pg_namespace ns " +
" on cl.relnamespace = ns.oid " +
" join pg_constraint con1 " +
" on con1.conrelid = cl.oid " +
" where " +
" ns.nspname = '%s' " +
" and con1.contype = 'u' " +
" ) con " +
" join pg_class table_class " +
" on table_class.oid = con.conrelid " +
" join pg_attribute column_attribute " +
" on column_attribute.attrelid = con.conrelid " +
" and column_attribute.attnum = con.parent " +
" ; ", schema);
try (PreparedStatement statement = connection.prepareStatement(sql)) {
try (ResultSet resultSet = statement.executeQuery()) {
while (resultSet.next()) {
String table = resultSet.getString("table");
String colonne = resultSet.getString("column");
ColumnRef source = ColumnRef.forColumn(table, colonne);
builder.add(source);
}
}
}
});
ImmutableSet result = builder.build();
return result;
}
protected ImmutableList readTableMetas(String schema, ImmutableList customTypes) {
Map comments = readTableAndViewComments(schema);
Set primaryKeys = readPrimaryKeys(schema);
Set uniqueColumns = readUniqueColumns(schema);
Multimap foreignKeys = readForeignKeys(schema);
ImmutableList result = readTableMetas("TABLE", schema, comments, primaryKeys, uniqueColumns, foreignKeys, customTypes);
return result;
}
protected ImmutableList readViewMetas(String schema, ImmutableList customTypes) {
Map comments = readTableAndViewComments(schema);
Set primaryKeys = readPrimaryKeys(schema);
Set uniqueColumns = readUniqueColumns(schema);
Multimap foreignKeys = readForeignKeys(schema);
ImmutableList result = readTableMetas("VIEW", schema, comments, primaryKeys, uniqueColumns, foreignKeys, customTypes);
return result;
}
protected Map readCustomTypesComments(String schema) {
Map result = new HashMap<>();
doWork(connection -> {
String sql = String.format(
" SELECT t.typname AS type," +
" d.description AS comment" +
" FROM pg_type t " +
" INNER JOIN pg_catalog.pg_namespace n " +
" ON n.oid = t.typnamespace" +
" AND n.nspname = '%s'" +
" INNER JOIN pg_catalog.pg_description d" +
" ON d.objoid = (n.nspname || '.' || t.typname)::regtype::oid" +
" AND d.objsubid = 0 -- Pour ne pas avoir les commentaires sur les valeurs des types" +
" WHERE (t.typrelid = 0 " +
" OR " +
" (SELECT c.relkind = 'c' " +
" FROM pg_catalog.pg_class c " +
" WHERE c.oid = t.typrelid)) " +
" AND NOT EXISTS " +
" (SELECT 1 " +
" FROM pg_catalog.pg_type el " +
" WHERE el.oid = t.typelem " +
" AND el.typarray = t.oid) ; ", schema);
try (PreparedStatement statement = connection.prepareStatement(sql)) {
try (ResultSet resultSet = statement.executeQuery()) {
while (resultSet.next()) {
String name = resultSet.getString("type");
String comment = resultSet.getString("comment");
if (StringUtils.isNotEmpty(comment)) {
result.put(name, comment);
}
}
}
}
});
return result;
}
protected ImmutableList readCustomTypes(String schema, Function> enumResolver) {
Map comments = readCustomTypesComments(schema);
ImmutableList.Builder builder = ImmutableList.builder();
doWork(connection -> {
String sql = String.format(
" SELECT t.typname AS type " +
" FROM pg_type t " +
" LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace " +
" WHERE (t.typrelid = 0 " +
" OR " +
" (SELECT c.relkind = 'c' " +
" FROM pg_catalog.pg_class c " +
" WHERE c.oid = t.typrelid)) " +
" AND NOT EXISTS " +
" (SELECT 1 " +
" FROM pg_catalog.pg_type el " +
" WHERE el.oid = t.typelem " +
" AND el.typarray = t.oid) " +
" AND n.nspname = '%s';", schema);
try (PreparedStatement statement = connection.prepareStatement(sql)) {
try (ResultSet resultSet = statement.executeQuery()) {
while (resultSet.next()) {
String name = resultSet.getString("type");
Optional comment = Optional.ofNullable(comments.get(name));
Class extends Enum> foundClass = enumResolver.apply(name);
Optional> enumMatching = Optional.ofNullable(foundClass);
List values = new LinkedList<>();
if (enumMatching.isPresent()) {
Class extends Enum> aClass = enumMatching.get();
try {
Method valuesMethod = aClass.getMethod("values");
Object invokeResult = valuesMethod.invoke(null);
Enum[] result = (Enum[]) invokeResult;
Arrays.stream(result)
.map(Enum::name)
.forEach(values::add);
} catch (Exception eee) {
log.error("Unable to read enum values", eee);
}
} else if (log.isWarnEnabled()) {
log.warn("No enum found for name: " + name);
}
ImmutableCustomTypeMeta customType = ImmutableCustomTypeMeta.builder()
.name(name)
.enumEquivalence(enumMatching)
.values(values)
.comment(comment)
.build();
builder.add(customType);
}
}
}
});
ImmutableList result = builder.build();
return result;
}
protected Function> packageEnumResolver() {
Set> enums;
if (StringUtils.isNotEmpty(packageForEnumResolver)) {
enums = new Reflections(packageForEnumResolver).getSubTypesOf(Enum.class);
} else {
enums = ImmutableSet.of();
}
Converter camelToUnderscore = CaseFormat.UPPER_CAMEL.converterTo(CaseFormat.LOWER_UNDERSCORE);
ImmutableMap> enumsIndex =
Maps.uniqueIndex(enums, e -> camelToUnderscore.convert(e.getSimpleName()));
Function> result = s -> enumsIndex.get(s.toLowerCase());
return result;
}
public DatabaseMeta buildMeta(String schema) {
ImmutableList customTypes = readCustomTypes(schema, packageEnumResolver());
ImmutableList tables = readTableMetas(schema, customTypes);
ImmutableList views = readViewMetas(schema, customTypes);
ImmutableList procedures = readProcedureMetas(schema);
DatabaseMeta result = ImmutableDatabaseMeta.builder()
.schema(schema)
.customTypes(customTypes)
.tables(tables)
.views(views)
.procedures(procedures)
.build();
return result;
}
}