cleanups to InformationExtractorJdbcDatabaseMetaDataImpl

and more to AbstractInformationExtractorImpl
This commit is contained in:
Gavin King 2024-11-14 11:43:07 +01:00
parent 5d5ce3feda
commit d20de1f5d7
3 changed files with 146 additions and 162 deletions

View File

@ -6,7 +6,6 @@ package org.hibernate.tool.schema.extract.internal;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
@ -42,6 +41,7 @@ import org.hibernate.tool.schema.spi.SchemaManagementException;
import static java.util.Collections.addAll;
import static org.hibernate.boot.model.naming.DatabaseIdentifier.toIdentifier;
import static org.hibernate.internal.util.StringHelper.EMPTY_STRINGS;
import static org.hibernate.internal.util.StringHelper.isBlank;
import static org.hibernate.internal.util.StringHelper.splitTrimmingTokens;
import static org.hibernate.internal.util.config.ConfigurationHelper.getBoolean;
@ -85,31 +85,37 @@ public abstract class AbstractInformationExtractorImpl implements InformationExt
""
)
);
final Dialect dialect = extractionContext.getJdbcEnvironment().getDialect();
this.extraPhysicalTableTypes = getPhysicalTableTypes( extraPhysicalTableTypesConfig, dialect );
this.tableTypes = getTableTypes( configService, dialect );
}
private String[] getPhysicalTableTypes(String extraPhysicalTableTypesConfig, Dialect dialect) {
final List<String> physicalTableTypesList = new ArrayList<>();
if ( !isBlank( extraPhysicalTableTypesConfig ) ) {
addAll( physicalTableTypesList, splitTrimmingTokens( ",;", extraPhysicalTableTypesConfig, false ) );
}
final Dialect dialect = extractionContext.getJdbcEnvironment().getDialect();
dialect.augmentPhysicalTableTypes( physicalTableTypesList );
this.extraPhysicalTableTypes = physicalTableTypesList.toArray( new String[0] );
return physicalTableTypesList.toArray( EMPTY_STRINGS );
}
private String[] getTableTypes(ConfigurationService configService, Dialect dialect) {
final List<String> tableTypesList = new ArrayList<>();
tableTypesList.add( "TABLE" );
tableTypesList.add( "VIEW" );
if ( getBoolean( AvailableSettings.ENABLE_SYNONYMS, configService.getSettings() ) ) {
if ( dialect instanceof DB2Dialect ) {
if ( dialect instanceof DB2Dialect ) { //TODO: should not use Dialect types directly!
tableTypesList.add( "ALIAS" );
}
tableTypesList.add( "SYNONYM" );
}
addAll( tableTypesList, extraPhysicalTableTypes );
dialect.augmentRecognizedTableTypes( tableTypesList );
this.tableTypes = tableTypesList.toArray( new String[0] );
return tableTypesList.toArray( EMPTY_STRINGS );
}
protected IdentifierHelper identifierHelper() {
return getIdentifierHelper();
private IdentifierHelper getIdentifierHelper() {
return getJdbcEnvironment().getIdentifierHelper();
}
protected JDBCException convertSQLException(SQLException sqlException, String message) {
@ -272,13 +278,17 @@ public abstract class AbstractInformationExtractorImpl implements InformationExt
protected abstract <T> T processSchemaResultSet(
String catalog,
String schemaPattern,
ExtractionContext.ResultSetProcessor<T> processor) throws SQLException;
ExtractionContext.ResultSetProcessor<T> processor)
throws SQLException;
@Override
public boolean schemaExists(Identifier catalog, Identifier schema) {
final String catalogFilter = determineCatalogFilter( catalog );
final String schemaFilter = determineSchemaFilter( schema );
final String catalogFilter =
getIdentifierHelper()
.toMetaDataCatalogName( catalog == null ? extractionContext.getDefaultCatalog() : catalog );
final String schemaFilter =
getIdentifierHelper()
.toMetaDataSchemaName( schema == null ? extractionContext.getDefaultSchema() : schema );
try {
return processSchemaResultSet(
catalogFilter,
@ -308,32 +318,10 @@ public abstract class AbstractInformationExtractorImpl implements InformationExt
}
}
private IdentifierHelper getIdentifierHelper() {
return getJdbcEnvironment().getIdentifierHelper();
}
protected String determineCatalogFilter(Identifier catalog) {
Identifier identifierToUse = catalog;
if ( identifierToUse == null ) {
identifierToUse = extractionContext.getDefaultCatalog();
}
return getIdentifierHelper().toMetaDataCatalogName( identifierToUse );
}
protected String determineSchemaFilter(Identifier schema) {
Identifier identifierToUse = schema;
if ( identifierToUse == null ) {
identifierToUse = extractionContext.getDefaultSchema();
}
return getIdentifierHelper().toMetaDataSchemaName( identifierToUse );
}
private TableInformation extractTableInformation(ResultSet resultSet) throws SQLException {
return new TableInformationImpl(
this,
identifierHelper(),
getIdentifierHelper(),
extractTableName( resultSet ),
isPhysicalTableType( resultSet.getString( getResultSetTableTypeLabel() ) ),
resultSet.getString( getResultSetRemarksLabel() )
@ -616,7 +604,8 @@ public abstract class AbstractInformationExtractorImpl implements InformationExt
String schemaPattern,
String tableNamePattern,
String columnNamePattern,
ExtractionContext.ResultSetProcessor<T> processor) throws SQLException;
ExtractionContext.ResultSetProcessor<T> processor)
throws SQLException;
private void populateTablesWithColumns(
String catalogFilter,
@ -637,7 +626,7 @@ public abstract class AbstractInformationExtractorImpl implements InformationExt
currentTable = tables.getTableInformation( currentTableName );
}
if ( currentTable != null ) {
addExtractedColumnInformation( currentTable, resultSet );
currentTable.addColumn( columnInformation( currentTable, resultSet ) );
}
}
return null;
@ -649,9 +638,9 @@ public abstract class AbstractInformationExtractorImpl implements InformationExt
}
}
protected void addExtractedColumnInformation(TableInformation tableInformation, ResultSet resultSet)
private ColumnInformationImpl columnInformation(TableInformation tableInformation, ResultSet resultSet)
throws SQLException {
final ColumnInformation columnInformation = new ColumnInformationImpl(
return new ColumnInformationImpl(
tableInformation,
toIdentifier( resultSet.getString( getResultSetColumnNameLabel() ) ),
resultSet.getInt( getResultSetSqlTypeCodeLabel() ),
@ -660,14 +649,13 @@ public abstract class AbstractInformationExtractorImpl implements InformationExt
resultSet.getInt( getResultSetDecimalDigitsLabel() ),
interpretTruthValue( resultSet.getString( getResultSetIsNullableLabel() ) )
);
tableInformation.addColumn( columnInformation );
}
private NameSpaceTablesInformation extractNameSpaceTablesInformation(ResultSet resultSet) throws SQLException {
final NameSpaceTablesInformation tables = new NameSpaceTablesInformation( identifierHelper() );
private NameSpaceTablesInformation extractNameSpaceTablesInformation(ResultSet resultSet)
throws SQLException {
final NameSpaceTablesInformation tables = new NameSpaceTablesInformation( getIdentifierHelper() );
while ( resultSet.next() ) {
final TableInformation tableInformation = extractTableInformation( resultSet );
tables.addTableInformation( tableInformation );
tables.addTableInformation( extractTableInformation( resultSet ) );
}
return tables;
}
@ -721,8 +709,8 @@ public abstract class AbstractInformationExtractorImpl implements InformationExt
String schemaPattern,
String tableNamePattern,
String[] types,
ExtractionContext.ResultSetProcessor<T> processor
) throws SQLException;
ExtractionContext.ResultSetProcessor<T> processor)
throws SQLException;
private TableInformation locateTableInNamespace(
Identifier catalog,
@ -735,6 +723,7 @@ public abstract class AbstractInformationExtractorImpl implements InformationExt
final String schemaFilter;
final NameQualifierSupport nameQualifierSupport = getNameQualifierSupport();
if ( nameQualifierSupport.supportsCatalogs() ) {
if ( catalog == null ) {
String defaultCatalog;
@ -780,12 +769,7 @@ public abstract class AbstractInformationExtractorImpl implements InformationExt
schemaFilter,
tableNameFilter,
tableTypes,
resultSet -> extractTableInformation(
catalogToUse,
schemaToUse,
tableName,
resultSet
)
resultSet -> extractTableInformation( catalogToUse, schemaToUse, tableName, resultSet )
);
}
@ -802,15 +786,16 @@ public abstract class AbstractInformationExtractorImpl implements InformationExt
Identifier catalog,
Identifier schema,
Identifier tableName,
ResultSet resultSet) throws SQLException {
ResultSet resultSet)
throws SQLException {
boolean found = false;
TableInformation tableInformation = null;
while ( resultSet.next() ) {
if ( tableName.equals( Identifier.toIdentifier(
resultSet.getString( getResultSetTableNameLabel() ),
tableName.isQuoted()
) ) ) {
final Identifier identifier =
toIdentifier( resultSet.getString( getResultSetTableNameLabel() ),
tableName.isQuoted() );
if ( tableName.equals( identifier ) ) {
if ( found ) {
LOG.multipleTablesFound( tableName.render() );
throw new SchemaExtractionException(
@ -867,7 +852,7 @@ public abstract class AbstractInformationExtractorImpl implements InformationExt
"%",
resultSet -> {
while ( resultSet.next() ) {
addExtractedColumnInformation( tableInformation, resultSet );
tableInformation.addColumn( columnInformation( tableInformation, resultSet ) );
}
return null;
}
@ -879,14 +864,6 @@ public abstract class AbstractInformationExtractorImpl implements InformationExt
}
}
protected Boolean interpretNullable(int nullable) {
return switch ( nullable ) {
case ResultSetMetaData.columnNullable -> Boolean.TRUE;
case ResultSetMetaData.columnNoNulls -> Boolean.FALSE;
default -> null;
};
}
private Boolean interpretTruthValue(String nullable) {
if ( "yes".equalsIgnoreCase( nullable ) ) {
return Boolean.TRUE;
@ -904,7 +881,8 @@ public abstract class AbstractInformationExtractorImpl implements InformationExt
String catalogFilter,
String schemaFilter,
Identifier tableName,
ExtractionContext.ResultSetProcessor<T> processor) throws SQLException;
ExtractionContext.ResultSetProcessor<T> processor)
throws SQLException;
@Override
public PrimaryKeyInformation getPrimaryKey(TableInformationImpl tableInformation) {
@ -926,10 +904,8 @@ public abstract class AbstractInformationExtractorImpl implements InformationExt
}
}
private PrimaryKeyInformation extractPrimaryKeyInformation(
TableInformation tableInformation,
ResultSet resultSet
) throws SQLException {
private PrimaryKeyInformation extractPrimaryKeyInformation(TableInformation tableInformation, ResultSet resultSet)
throws SQLException {
final List<ColumnInformation> pkColumns = new ArrayList<>();
boolean firstPass = true;
@ -937,19 +913,16 @@ public abstract class AbstractInformationExtractorImpl implements InformationExt
while ( resultSet.next() ) {
final String currentPkName = resultSet.getString( getResultSetPrimaryKeyNameLabel() );
final Identifier currentPkIdentifier = currentPkName == null ? null : toIdentifier( currentPkName );
final Identifier currentPkIdentifier =
currentPkName == null ? null : toIdentifier( currentPkName );
if ( firstPass ) {
pkIdentifier = currentPkIdentifier;
firstPass = false;
}
else {
if ( !Objects.equals( pkIdentifier, currentPkIdentifier ) ) {
throw new SchemaExtractionException(
String.format(
"Encountered primary keys differing name on table %s",
tableInformation.getName().toString()
)
);
throw new SchemaExtractionException( "Encountered primary keys differing name on table "
+ tableInformation.getName().toString() );
}
}
@ -1054,7 +1027,8 @@ public abstract class AbstractInformationExtractorImpl implements InformationExt
String table,
boolean unique,
boolean approximate,
ExtractionContext.ResultSetProcessor<T> processor) throws SQLException;
ExtractionContext.ResultSetProcessor<T> processor)
throws SQLException;
@Override
public Iterable<IndexInformation> getIndexes(TableInformation tableInformation) {
@ -1186,8 +1160,8 @@ public abstract class AbstractInformationExtractorImpl implements InformationExt
String catalog,
String schema,
String table,
ExtractionContext.ResultSetProcessor<T> processor
) throws SQLException;
ExtractionContext.ResultSetProcessor<T> processor)
throws SQLException;
/**
* Must do the following:
@ -1271,8 +1245,8 @@ public abstract class AbstractInformationExtractorImpl implements InformationExt
String foreignCatalog,
String foreignSchema,
String foreignTable,
ExtractionContext.ResultSetProcessor<T> processor
) throws SQLException;
ExtractionContext.ResultSetProcessor<T> processor)
throws SQLException;
@Override
@ -1322,8 +1296,11 @@ public abstract class AbstractInformationExtractorImpl implements InformationExt
return fks;
}
private void process(TableInformation tableInformation, ResultSet resultSet, Map<Identifier, ForeignKeyBuilder> fkBuilders)
throws SQLException {
private void process(
TableInformation tableInformation,
ResultSet resultSet,
Map<Identifier, ForeignKeyBuilder> fkBuilders)
throws SQLException {
while ( resultSet.next() ) {
// IMPL NOTE : The builder is mainly used to collect the column reference mappings
final Identifier fkIdentifier = toIdentifier( resultSet.getString( getResultSetForeignKeyLabel() ) );

View File

@ -39,8 +39,9 @@ public class InformationExtractorJdbcDatabaseMetaDataImpl extends AbstractInform
}
@Override
public <T> T processCatalogsResultSet(ExtractionContext.ResultSetProcessor<T> processor) throws SQLException {
try (ResultSet resultSet = getJdbcDatabaseMetaData().getCatalogs() ) {
public <T> T processCatalogsResultSet(ExtractionContext.ResultSetProcessor<T> processor)
throws SQLException {
try ( ResultSet resultSet = getJdbcDatabaseMetaData().getCatalogs() ) {
return processor.process( resultSet );
}
}
@ -49,10 +50,11 @@ public class InformationExtractorJdbcDatabaseMetaDataImpl extends AbstractInform
protected <T> T processSchemaResultSet(
String catalog,
String schemaPattern,
ExtractionContext.ResultSetProcessor<T> processor) throws SQLException {
try (ResultSet resultSet = getJdbcDatabaseMetaData().getSchemas(
catalog,
schemaPattern ) ) {
ExtractionContext.ResultSetProcessor<T> processor)
throws SQLException {
try ( ResultSet resultSet =
getJdbcDatabaseMetaData()
.getSchemas( catalog, schemaPattern ) ) {
return processor.process( resultSet );
}
}
@ -63,13 +65,11 @@ public class InformationExtractorJdbcDatabaseMetaDataImpl extends AbstractInform
String schemaPattern,
String tableNamePattern,
String[] types,
ExtractionContext.ResultSetProcessor<T> processor
) throws SQLException {
try (ResultSet resultSet = getJdbcDatabaseMetaData().getTables(
catalog,
schemaPattern,
tableNamePattern,
types)) {
ExtractionContext.ResultSetProcessor<T> processor)
throws SQLException {
try ( ResultSet resultSet =
getJdbcDatabaseMetaData()
.getTables( catalog, schemaPattern, tableNamePattern, types) ) {
return processor.process( resultSet );
}
}
@ -80,12 +80,11 @@ public class InformationExtractorJdbcDatabaseMetaDataImpl extends AbstractInform
String schemaPattern,
String tableNamePattern,
String columnNamePattern,
ExtractionContext.ResultSetProcessor<T> processor) throws SQLException {
try (ResultSet resultSet = getJdbcDatabaseMetaData().getColumns(
catalog,
schemaPattern,
tableNamePattern,
columnNamePattern )) {
ExtractionContext.ResultSetProcessor<T> processor)
throws SQLException {
try ( ResultSet resultSet =
getJdbcDatabaseMetaData()
.getColumns( catalog, schemaPattern, tableNamePattern, columnNamePattern ) ) {
return processor.process( resultSet );
}
}
@ -95,11 +94,11 @@ public class InformationExtractorJdbcDatabaseMetaDataImpl extends AbstractInform
String catalogFilter,
String schemaFilter,
Identifier tableName,
ExtractionContext.ResultSetProcessor<T> processor) throws SQLException {
try( ResultSet resultSet = getJdbcDatabaseMetaData().getPrimaryKeys(
catalogFilter,
schemaFilter,
tableName.getText() ) ) {
ExtractionContext.ResultSetProcessor<T> processor)
throws SQLException {
try ( ResultSet resultSet =
getJdbcDatabaseMetaData()
.getPrimaryKeys( catalogFilter, schemaFilter, tableName.getText() ) ) {
return processor.process( resultSet );
}
}
@ -111,14 +110,11 @@ public class InformationExtractorJdbcDatabaseMetaDataImpl extends AbstractInform
String table,
boolean unique,
boolean approximate,
ExtractionContext.ResultSetProcessor<T> processor) throws SQLException {
try (ResultSet resultSet = getJdbcDatabaseMetaData().getIndexInfo(
catalog,
schema,
table,
unique,
approximate ) ) {
ExtractionContext.ResultSetProcessor<T> processor)
throws SQLException {
try ( ResultSet resultSet =
getJdbcDatabaseMetaData()
.getIndexInfo( catalog, schema, table, unique, approximate ) ) {
return processor.process( resultSet );
}
}
@ -128,11 +124,11 @@ public class InformationExtractorJdbcDatabaseMetaDataImpl extends AbstractInform
String catalog,
String schema,
String table,
ExtractionContext.ResultSetProcessor<T> processor) throws SQLException {
try (ResultSet resultSet = getJdbcDatabaseMetaData().getImportedKeys(
catalog,
schema,
table ) ) {
ExtractionContext.ResultSetProcessor<T> processor)
throws SQLException {
try ( ResultSet resultSet =
getJdbcDatabaseMetaData()
.getImportedKeys( catalog, schema, table ) ) {
return processor.process( resultSet );
}
}
@ -145,14 +141,12 @@ public class InformationExtractorJdbcDatabaseMetaDataImpl extends AbstractInform
String foreignCatalog,
String foreignSchema,
String foreignTable,
ExtractionContext.ResultSetProcessor<T> processor) throws SQLException {
try (ResultSet resultSet = getJdbcDatabaseMetaData().getCrossReference(
parentCatalog,
parentSchema,
parentTable,
foreignCatalog,
foreignSchema,
foreignTable) ) {
ExtractionContext.ResultSetProcessor<T> processor)
throws SQLException {
try ( ResultSet resultSet =
getJdbcDatabaseMetaData()
.getCrossReference( parentCatalog, parentSchema, parentTable,
foreignCatalog, foreignSchema, foreignTable) ) {
return processor.process( resultSet );
}
}
@ -162,12 +156,12 @@ public class InformationExtractorJdbcDatabaseMetaDataImpl extends AbstractInform
final ExtractionContext extractionContext = getExtractionContext();
// We use this dummy query to retrieve the table information through the ResultSetMetaData
// This is significantly better than to use the DatabaseMetaData especially on Oracle with synonyms enabled
final String tableName = extractionContext.getSqlStringGenerationContext().format(
// The name comes from the database, so the case is correct
// But we quote here to avoid issues with reserved words
tableInformation.getName().quote()
);
// Significantly better than using DatabaseMetaData especially on Oracle with synonyms enabled
final String tableName =
extractionContext.getSqlStringGenerationContext()
// The name comes from the database, so the case is correct
// But we quote here to avoid issues with reserved words
.format( tableInformation.getName().quote() );
try {
extractionContext.getQueryResults(
@ -176,38 +170,51 @@ public class InformationExtractorJdbcDatabaseMetaDataImpl extends AbstractInform
resultSet -> {
final ResultSetMetaData metaData = resultSet.getMetaData();
final int columnCount = metaData.getColumnCount();
for ( int i = 1; i <= columnCount; i++ ) {
final String columnName = metaData.getColumnName( i );
final int columnType = metaData.getColumnType( i );
final String typeName = new StringTokenizer( metaData.getColumnTypeName( i ), "()" ).nextToken();
final int scale = metaData.getScale( i );
final ColumnInformationImpl columnInformation = new ColumnInformationImpl(
tableInformation,
DatabaseIdentifier.toIdentifier( columnName ),
columnType,
typeName,
dialect.resolveSqlTypeLength(
typeName,
columnType,
metaData.getPrecision( i ),
scale,
metaData.getColumnDisplaySize( i )
),
scale,
interpretNullable( metaData.isNullable( i ) )
);
tableInformation.addColumn( columnInformation );
tableInformation.addColumn( columnInformation( tableInformation, metaData, i, dialect ) );
}
return null;
}
);
}
catch (SQLException e) {
throw convertSQLException(
e,
"Error accessing column metadata: " + tableInformation.getName().toString()
);
throw convertSQLException( e,
"Error accessing column metadata: "
+ tableInformation.getName().toString() );
}
}
private static Boolean interpretNullable(int nullable) {
return switch ( nullable ) {
case ResultSetMetaData.columnNullable -> Boolean.TRUE;
case ResultSetMetaData.columnNoNulls -> Boolean.FALSE;
default -> null;
};
}
private static ColumnInformationImpl columnInformation(
TableInformation tableInformation, ResultSetMetaData metaData, int i, Dialect dialect)
throws SQLException {
final String columnName = metaData.getColumnName( i );
final int columnType = metaData.getColumnType( i );
final String typeName =
new StringTokenizer( metaData.getColumnTypeName( i ), "()" )
.nextToken();
final int scale = metaData.getScale( i );
return new ColumnInformationImpl(
tableInformation,
DatabaseIdentifier.toIdentifier( columnName ),
columnType,
typeName,
dialect.resolveSqlTypeLength(
typeName,
columnType,
metaData.getPrecision( i ),
scale,
metaData.getColumnDisplaySize( i )
),
scale,
interpretNullable( metaData.isNullable( i ) )
);
}
}

View File

@ -35,7 +35,7 @@ public class TableInformationImpl implements TableInformation {
private PrimaryKeyInformation primaryKey;
private Map<Identifier, ForeignKeyInformation> foreignKeys;
private Map<Identifier, IndexInformation> indexes;
private Map<Identifier, ColumnInformation> columns = new HashMap<>( );
private final Map<Identifier, ColumnInformation> columns = new HashMap<>();
private boolean wasPrimaryKeyLoaded = false; // to avoid multiple db reads since primary key can be null.