Skip to content

Commit

Permalink
[536] Encapsulate extraction methods into ReactiveExtractionTool
Browse files Browse the repository at this point in the history
[536] : Added Javodoc, made minor changes to sych up with ORM, changed more SQL keywords that were upper-case to lower-case
  • Loading branch information
gbadner committed Aug 4, 2021
1 parent ba2e5f9 commit 7552fa8
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 97 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ org.gradle.java.installations.auto-download=false
enableMavenLocalRepo = true

# Override default Hibernate ORM version
hibernateOrmVersion = 5.5.5-SNAPSHOT
hibernateOrmVersion = 5.5.6-SNAPSHOT

# If set to true, skip Hibernate ORM version parsing (default is true, if set to null)
# this is required when using intervals or weird versions or the build will fail
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,21 @@ interface Expectation {
CompletionStage<Result> select(String sql);
CompletionStage<Result> select(String sql, Object[] paramValues);
CompletionStage<ResultSet> selectJdbc(String sql, Object[] paramValues);

/**
* This method is intended to be used only for queries returning
* a ResultSet that must be executed outside of any "current"
* transaction (i.e with autocommit=true).
* <p/>
* For example, it would be appropriate to use this method when
* performing queries on information_schema or system tables in
* order to obtain metadata information about catalogs, schemas,
* tables, etc.
*
* @param sql - the query to execute outside of a transaction
* @param paramValues - a non-null array of parameter values
* @return the CompletionStage<ResultSet> from executing the query.
*/
CompletionStage<ResultSet> selectJdbcOutsideTransaction(String sql, Object[] paramValues);

CompletionStage<Long> insertAndSelectIdentifier(String sql, Object[] paramValues);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,13 @@
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.tool.schema.extract.internal.AbstractInformationExtractorImpl;
import org.hibernate.tool.schema.extract.spi.ExtractionContext;
import org.hibernate.tool.schema.extract.spi.InformationExtractor;

/**
* @Author Gail Badner
* An implementation of {@link InformationExtractor} that obtains metadata
* information from a database's information_schema.
*
* @author Gail Badner
*/
public abstract class AbstractReactiveInformationSchemaBasedExtractorImpl extends AbstractInformationExtractorImpl {

Expand Down Expand Up @@ -130,7 +134,7 @@ private String normalizeLabel(String columnLabel) {
protected <T> T processCatalogsResultSet(ExtractionContext.ResultSetProcessor<T> processor) throws SQLException {
return getExtractionContext().getQueryResults(
String.format(
"SELECT catalog_name AS %s FROM information_schema.information_schema_catalog_name",
"select catalog_name as %s from information_schema.information_schema_catalog_name",
getResultSetCatalogLabel()
),
null,
Expand All @@ -140,16 +144,16 @@ protected <T> T processCatalogsResultSet(ExtractionContext.ResultSetProcessor<T>

@Override
protected <T> T processSchemaResultSet(
String catalogFilter,
String schemaFilter,
String catalog,
String schemaPattern,
ExtractionContext.ResultSetProcessor<T> processor) throws SQLException {
final StringBuilder sb = new StringBuilder()
.append( "SELECT catalog_name AS " ).append( getResultSetCatalogLabel() )
.append( " , schema_name AS " ).append( getResultSetSchemaLabel() )
.append( " FROM information_schema.schemata WHERE true" );
.append( "select catalog_name as " ).append( getResultSetCatalogLabel() )
.append( " , schema_name as " ).append( getResultSetSchemaLabel() )
.append( " from information_schema.schemata where true" );
final List<Object> parameters = new ArrayList<>();
appendClauseAndParameterIfNotNull( " AND catalog_name = ", catalogFilter, sb, parameters );
appendClauseAndParameterIfNotNull( " AND schema_name = ", schemaFilter, sb, parameters );
appendClauseAndParameterIfNotNull( " and catalog_name = ", catalog, sb, parameters );
appendClauseAndParameterIfNotNull( " and schema_name like ", schemaPattern, sb, parameters );
return getExtractionContext().getQueryResults( sb.toString(), parameters.toArray(), processor );
}

Expand All @@ -170,39 +174,39 @@ protected void appendClauseAndParameterIfNotNull(

@Override
protected <T> T processTableResultSet(
String catalogFilter,
String schemaFilter,
String tableNameFilter,
String[] tableTypes,
String catalog,
String schemaPattern,
String tableNamePattern,
String[] types,
ExtractionContext.ResultSetProcessor<T> processor
) throws SQLException {

final StringBuilder sb = new StringBuilder()
.append( "SELECT table_catalog AS " ).append( getResultSetCatalogLabel() )
.append( " , table_schema AS " ).append( getResultSetSchemaLabel() )
.append( " , table_name AS " ).append( getResultSetTableNameLabel() )
.append( " , table_type AS " ).append( getResultSetTableTypeLabel() )
.append( " , null AS " ).append( getResultSetRemarksLabel() )
.append( "select table_catalog as " ).append( getResultSetCatalogLabel() )
.append( " , table_schema as " ).append( getResultSetSchemaLabel() )
.append( " , table_name as " ).append( getResultSetTableNameLabel() )
.append( " , table_type as " ).append( getResultSetTableTypeLabel() )
.append( " , null as " ).append( getResultSetRemarksLabel() )
// Remarks are not available from information_schema.
// Hibernate ORM does not currently do anything with remarks,
// so just return null for now.
.append( " FROM information_schema.tables WHERE true" );
.append( " from information_schema.tables where true" );
List<Object> parameterValues = new ArrayList<>();
appendClauseAndParameterIfNotNull( " AND table_catalog = ", catalogFilter, sb, parameterValues );
appendClauseAndParameterIfNotNull( " AND table_schema = ", schemaFilter, sb, parameterValues );
appendClauseAndParameterIfNotNull( " AND table_name = ", tableNameFilter, sb, parameterValues );
appendClauseAndParameterIfNotNull( " and table_catalog = ", catalog, sb, parameterValues );
appendClauseAndParameterIfNotNull( " and table_schema like ", schemaPattern, sb, parameterValues );
appendClauseAndParameterIfNotNull( " and table_name like ", tableNamePattern, sb, parameterValues );

if ( tableTypes != null && tableTypes.length > 0 ) {
if ( types != null && types.length > 0 ) {
appendClauseAndParameterIfNotNull(
" AND table_type IN ( ",
tableTypes[0].equals( "TABLE" ) ? getResultSetTableTypesPhysicalTableConstant() : tableTypes[0],
" and table_type in ( ",
types[0].equals( "TABLE" ) ? getResultSetTableTypesPhysicalTableConstant() : types[0],
sb,
parameterValues
);
for ( int i = 1 ; i < tableTypes.length ; i++ ) {
for ( int i = 1 ; i < types.length ; i++ ) {
appendClauseAndParameterIfNotNull(
", ",
tableTypes[i].equals( "TABLE" ) ? getResultSetTableTypesPhysicalTableConstant() : tableTypes[i],
types[i].equals( "TABLE" ) ? getResultSetTableTypesPhysicalTableConstant() : types[i],
sb,
parameterValues
);
Expand All @@ -214,25 +218,26 @@ protected <T> T processTableResultSet(

@Override
protected <T> T processColumnsResultSet(
String catalogFilter,
String schemaFilter,
String tableName,
String catalog,
String schemaPattern,
String tableNamePattern,
String columnNamePattern,
ExtractionContext.ResultSetProcessor<T> processor) throws SQLException {

final StringBuilder sb = new StringBuilder()
.append( "SELECT table_name AS " ).append( getResultSetTableNameLabel() )
.append( ", column_name AS " ).append( getResultSetColumnNameLabel() )
.append( ", udt_name AS " ).append( getResultSetTypeNameLabel() )
.append( ", null AS " ).append( getResultSetColumnSizeLabel() )
.append( "select table_name as " ).append( getResultSetTableNameLabel() )
.append( ", column_name as " ).append( getResultSetColumnNameLabel() )
.append( ", udt_name as " ).append( getResultSetTypeNameLabel() )
.append( ", null as " ).append( getResultSetColumnSizeLabel() )
// Column size is fairly complicated to get out of information_schema
// and likely to be DB-dependent. Currently, Hibernate ORM does not use
// column size for anything, so for now, just return null.
.append( ", null AS " ) .append( getResultSetDecimalDigitsLabel() )
.append( ", null as " ) .append( getResultSetDecimalDigitsLabel() )
// Decimal digits is fairly complicated to get out of information_schema
// and likely to be DB-dependent. Currently, Hibernate ORM does not use
// decimal digits for anything, so for now, just return null.
.append( ", is_nullable AS " ).append( getResultSetIsNullableLabel() )
.append( ", null AS " ).append( getResultSetSqlTypeCodeLabel() )
.append( ", is_nullable as " ).append( getResultSetIsNullableLabel() )
.append( ", null as " ).append( getResultSetSqlTypeCodeLabel() )
// SQL type code is not available from information_schema,
// and, for PostgreSQL at least, it appears to be hard-coded
// into the JDBC driver. Currently, Hibernate ORM only uses
Expand All @@ -241,14 +246,14 @@ protected <T> T processColumnsResultSet(
// Hibernate's metadata for the column. ORM also considers
// the same column type name as a match, so the SQL code is
// optional. For now, just return null for the SQL type code.
.append( " FROM information_schema.columns WHERE true" );
.append( " from information_schema.columns where true" );

final List<Object> parameterValues = new ArrayList<>();
appendClauseAndParameterIfNotNull( " AND table_catalog = " , catalogFilter, sb, parameterValues );
appendClauseAndParameterIfNotNull( " AND table_schema = " , schemaFilter, sb, parameterValues );
appendClauseAndParameterIfNotNull( " AND table_name = " , tableName, sb, parameterValues );
appendClauseAndParameterIfNotNull( " and table_catalog = " , catalog, sb, parameterValues );
appendClauseAndParameterIfNotNull( " and table_schema like " , schemaPattern, sb, parameterValues );
appendClauseAndParameterIfNotNull( " and table_name like " , tableNamePattern, sb, parameterValues );

sb.append( "ORDER BY table_name, column_name, ordinal_position" );
sb.append( " order by table_catalog, table_schema, table_name, column_name, ordinal_position" );

return getExtractionContext().getQueryResults( sb.toString(), parameterValues.toArray(), processor );
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl;
import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider;
import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.tool.schema.spi.SchemaManagementTool;

import java.util.Map;

Expand All @@ -29,10 +28,6 @@ public class NoJdbcConnectionProviderInitiator implements StandardServiceInitiat
public ConnectionProvider initiateService(Map configurationValues, ServiceRegistryImplementor registry) {
ConnectionProvider provider = ConnectionProviderInitiator.INSTANCE.initiateService(configurationValues, registry);
if (provider instanceof DriverManagerConnectionProviderImpl) {
final SchemaManagementTool schemaManagementTool = registry.getService( SchemaManagementTool.class );
final ReactiveGenerationTarget reactiveGenerationTarget = new ReactiveGenerationTarget( registry );
schemaManagementTool.setCustomDatabaseGenerationTarget( reactiveGenerationTarget );

return NoJdbcConnectionProvider.INSTANCE;
}
return provider;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import org.hibernate.boot.registry.StandardServiceInitiator;
import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider;
import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.tool.schema.spi.SchemaManagementTool;

import java.util.Map;

Expand All @@ -31,9 +30,6 @@ public MultiTenantConnectionProvider initiateService(Map configurationValues, Se
return null;
}

final SchemaManagementTool schemaManagementTool = registry.getService( SchemaManagementTool.class );
schemaManagementTool.setCustomDatabaseGenerationTarget( new ReactiveGenerationTarget(registry) );

return new NoJdbcMultiTenantConnectionProvider();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@
import org.hibernate.tool.schema.extract.spi.ExtractionContext;

/**
* @Author Gail Badner
* An implementation of {@link AbstractReactiveInformationSchemaBasedExtractorImpl}
* specifically for PostgreSQL that obtains metadata from PostgreSQL's system
* tables, when it is not available from PosgreSQL's information_schema.
*
* @author Gail Badner
*/
public class PostgreSqlReactiveInformationExtractorImpl extends AbstractReactiveInformationSchemaBasedExtractorImpl {

Expand All @@ -44,12 +48,21 @@ protected <T> T processPrimaryKeysResultSet(

@Override
protected <T> T processIndexInfoResultSet(
String catalogFilter,
String schemaFilter,
Identifier tableName,
String catalog,
String schema,
String table,
boolean unique,
boolean approximate,
ExtractionContext.ResultSetProcessor<T> processor) throws SQLException {

// This implementation is based on org.postgresql.jdbc.PgDatabaseMetaData#getIndexInfo.
// It excludes columns that are specified by DatabaseMetaData#getIndexInfo, but
// not specified by AbstractInformationExtractorImpl#processIndexInfoResultSet.

// TODO: How should the "approximate" parameter be used?
// org.postgresql.jdbc.PgDatabaseMetaData#getIndexInfo
// does not use that argument. It is currently ignored here as well.

// Generate the inner query first.
final StringBuilder innerQuery = new StringBuilder()
.append( "select ci.relname as index_name" )
Expand All @@ -68,8 +81,12 @@ protected <T> T processIndexInfoResultSet(

final List<Object> parameterValues = new ArrayList<>();

appendClauseAndParameterIfNotNull( " and n.nspname = ", schemaFilter, innerQuery, parameterValues );
appendClauseAndParameterIfNotNull( " and ct.relname = ", tableName.getText(), innerQuery, parameterValues );
appendClauseAndParameterIfNotNull( " and n.nspname = ", schema, innerQuery, parameterValues );
appendClauseAndParameterIfNotNull( " and ct.relname = ", table, innerQuery, parameterValues );

if (unique) {
innerQuery.append( " AND i.indisunique = true" );
}

return getExtractionContext().getQueryResults(
"select tmp.index_name as " + getResultSetIndexNameLabel() +
Expand All @@ -84,11 +101,16 @@ protected <T> T processIndexInfoResultSet(

@Override
protected <T> T processImportedKeysResultSet(
String catalogFilter,
String schemaFilter,
String tableName,
String catalog,
String schema,
String table,
ExtractionContext.ResultSetProcessor<T> processor
) throws SQLException {

// This implementation is based on org.postgresql.jdbc.PgDatabaseMetaData#getImportedExportedKeys.
// It excludes columns that are specified by DatabaseMetaData#getImportedKeys, but
// not specified by AbstractInformationExtractorImpl#processImportedKeysResultSet.

final StringBuilder sb = new StringBuilder()
.append( "select null as " ).append( getResultSetPrimaryKeyCatalogLabel() )
.append( ", pkn.nspname as " ).append( getResultSetPrimaryKeySchemaLabel() )
Expand All @@ -107,10 +129,11 @@ protected <T> T processImportedKeysResultSet(

final List<Object> parameterValues = new ArrayList<>();

appendClauseAndParameterIfNotNull( " and fkn.nspname = ", schemaFilter, sb, parameterValues );
appendClauseAndParameterIfNotNull( " and fkc.relname = ", tableName, sb, parameterValues );
appendClauseAndParameterIfNotNull( " and fkn.nspname = ", schema, sb, parameterValues );
appendClauseAndParameterIfNotNull( " and fkc.relname = ", table, sb, parameterValues );

sb.append( " order by pkn.nspname,pkc.relname, con.conname, pos.n");
// No need to order by catalog since it is always null.
sb.append( " order by pkn.nspname, pkc.relname, con.conname, pos.n");

return getExtractionContext().getQueryResults( sb.toString(), parameterValues.toArray(), processor );
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,36 +12,57 @@
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import org.hibernate.resource.transaction.spi.DdlTransactionIsolator;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.spi.ServiceRegistryImplementor;
import org.hibernate.tool.schema.extract.spi.ExtractionContext;
import org.hibernate.tool.schema.extract.spi.InformationExtractor;
import org.hibernate.tool.schema.internal.HibernateSchemaManagementTool;
import org.hibernate.tool.schema.spi.ExtractionTool;

public class ReactiveSchemaManagementTool extends HibernateSchemaManagementTool {

public ExtractionContext createExtractionContext(
ServiceRegistry serviceRegistry,
JdbcEnvironment jdbcEnvironment,
DdlTransactionIsolator ddlTransactionIsolator,
Identifier defaultCatalog,
Identifier defaultSchema,
ExtractionContext.DatabaseObjectAccess databaseObjectAccess) {
return new ReactiveImprovedExtractionContextImpl(
serviceRegistry,
defaultCatalog,
defaultSchema,
databaseObjectAccess
);
@Override
public void injectServices(ServiceRegistryImplementor serviceRegistry) {
super.injectServices( serviceRegistry );
setCustomDatabaseGenerationTarget( new ReactiveGenerationTarget( serviceRegistry ) );
}

public InformationExtractor createInformationExtractor(ExtractionContext extractionContext) {
final Dialect dialect = getServiceRegistry().getService( JdbcEnvironment.class ).getDialect();
if ( dialect instanceof PostgreSQL10Dialect ) {
return new PostgreSqlReactiveInformationExtractorImpl( extractionContext );
@Override
public ExtractionTool getExtractionTool() {
return ReactiveExtractionTool.INSTANCE;
}

private static class ReactiveExtractionTool implements ExtractionTool {

private static final ReactiveExtractionTool INSTANCE = new ReactiveExtractionTool();

private ReactiveExtractionTool() {
}
else {
throw new NotYetImplementedException(
"No InformationExtractor for Dialect [" + dialect + "] is implemented yet"

public ExtractionContext createExtractionContext(
ServiceRegistry serviceRegistry,
JdbcEnvironment jdbcEnvironment,
DdlTransactionIsolator ddlTransactionIsolator,
Identifier defaultCatalog,
Identifier defaultSchema,
ExtractionContext.DatabaseObjectAccess databaseObjectAccess) {
return new ReactiveImprovedExtractionContextImpl(
serviceRegistry,
defaultCatalog,
defaultSchema,
databaseObjectAccess
);
}

public InformationExtractor createInformationExtractor(ExtractionContext extractionContext) {
final Dialect dialect = extractionContext.getJdbcEnvironment().getDialect();
if ( dialect instanceof PostgreSQL10Dialect ) {
return new PostgreSqlReactiveInformationExtractorImpl( extractionContext );
}
else {
throw new NotYetImplementedException(
"No InformationExtractor for Dialect [" + dialect + "] is implemented yet"
);
}
}
}
}
Loading

0 comments on commit 7552fa8

Please sign in to comment.