diff --git a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixDialect.java b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixDialect.java index c0210b6a31..89a4ae7bc3 100644 --- a/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixDialect.java +++ b/hibernate-community-dialects/src/main/java/org/hibernate/community/dialect/InformixDialect.java @@ -573,6 +573,26 @@ public class InformixDialect extends Dialect { return BeforeUseAction.CREATE; } + @Override + public String[] getCreateSchemaCommand(String schemaName) { + return new String[] { "create schema authorization " + schemaName }; + } + + @Override + public String[] getDropSchemaCommand(String schemaName) { + return new String[] { "" }; + } + + @Override + public boolean useCrossReferenceForeignKeys(){ + return true; + } + + @Override + public String getCrossReferenceParentTableFilter(){ + return "%"; + } + @Override public UniqueDelegate getUniqueDelegate() { return uniqueDelegate; diff --git a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java index e1662ba3bc..22be5deeba 100644 --- a/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java +++ b/hibernate-core/src/main/java/org/hibernate/dialect/Dialect.java @@ -2612,6 +2612,23 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun + " " + foreignKeyDefinition; } + /** + * Does the dialect also need cross-references to get a complete + * list of foreign keys? + */ + public boolean useCrossReferenceForeignKeys(){ + return false; + } + + /** + * Some dialects require a not null primaryTable filter. + * Sometimes a wildcard entry is sufficient for the like condition. + * @return + */ + public String getCrossReferenceParentTableFilter(){ + return null; + } + /** * The syntax used to add a primary key constraint to a table. * diff --git a/hibernate-core/src/main/java/org/hibernate/tool/schema/extract/internal/AbstractInformationExtractorImpl.java b/hibernate-core/src/main/java/org/hibernate/tool/schema/extract/internal/AbstractInformationExtractorImpl.java index 6dd4d04962..a62a78ddb3 100644 --- a/hibernate-core/src/main/java/org/hibernate/tool/schema/extract/internal/AbstractInformationExtractorImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/tool/schema/extract/internal/AbstractInformationExtractorImpl.java @@ -1264,6 +1264,92 @@ public abstract class AbstractInformationExtractorImpl implements InformationExt ExtractionContext.ResultSetProcessor processor ) throws SQLException; + /** + * Must do the following: + *
    + *
  1. + * obtain a {@link ResultSet} containing a row for each foreign key + * column making up a foreign key for any existing + * foreignCatalog/foreignSchema/foreignTable combination as specified by + * parameters described below. + * The {@link ResultSet} must contain the following, consistent + * with the corresponding columns returned by {@link DatabaseMetaData#getCrossReference}: + *
      + *
    • + * column label {@link #getResultSetForeignKeyLabel} - + * foreign key name (may be null) + *
    • + *
    • + * column label {@link #getResultSetPrimaryKeyCatalogLabel} - + * primary key table catalog being imported (may be null) + *
    • + *
    • + * column label {@link #getResultSetPrimaryKeySchemaLabel} - + * primary key table schema being imported (may be null) + *
    • + *
    • + * column label {@link #getResultSetPrimaryKeyTableLabel} - + * primary key table name being imported + *
    • + *
    • + * column label {@link #getResultSetForeignKeyColumnNameLabel} - + * foreign key column name + *
    • + *
    • + * column label {@link #getResultSetPrimaryKeyColumnNameLabel} - + * primary key column name being imported + *
    • + *
    + * The ResultSet must be ordered by the primary key + * foreignCatalog/foreignSchema/foreignTable and column position within the key. + *
  2. + *
  3. execute {@code processor.process( resultSet )};
  4. + *
  5. + * release resources whether {@code processor.process( resultSet )} + * executes successfully or not. + *
  6. + *
+ *

+ * The {@code parentCatalog}, {@code parentSchema}, {@code parentTable}, + * {@code foreignCatalog}, {@code foreignSchema}, {@code foreignTable} + * parameters are as specified by {@link DatabaseMetaData#getCrossReference( + * String, String, String, String, String, String)} + * and are copied here: + * + * @param parentCatalog a catalog name; must match the catalog name + * as it is stored in the database; "" retrieves those without a + * catalog; {@code null} means drop catalog name from the selection criteria + * @param parentSchema a schema name; must match the schema name as + * it is stored in the database; "" retrieves those without a schema; + * {@code null} means drop schema name from the selection criteria + * @param parentTable the name of the table that exports the key; must match + * the table name as it is stored in the database + * @param foreignCatalog a catalog name; must match the catalog name as + * it is stored in the database; "" retrieves those without a + * catalog; {@code null} means drop catalog name from the selection criteria + * @param foreignSchema a schema name; must match the schema name as it + * is stored in the database; "" retrieves those without a schema; + * {@code null} means drop schema name from the selection criteria + * @param foreignTable the name of the table that imports the key; must match + * the table name as it is stored in the database + * @param processor - the provided ResultSetProcessor. + * @param - defined by {@code processor} + * @return - defined by {@code processor} + * @throws SQLException - if a database error occurs + * @see #processImportedKeysResultSet(String, String, String, + * ExtractionContext.ResultSetProcessor) + */ + protected abstract T processCrossReferenceResultSet( + String parentCatalog, + String parentSchema, + String parentTable, + String foreignCatalog, + String foreignSchema, + String foreignTable, + ExtractionContext.ResultSetProcessor processor + ) throws SQLException; + + @Override public Iterable getForeignKeys(TableInformation tableInformation) { final Map fkBuilders = new HashMap<>(); @@ -1289,52 +1375,62 @@ public abstract class AbstractInformationExtractorImpl implements InformationExt } try { + ExtractionContext.ResultSetProcessor processor = resultSet -> { + while ( resultSet.next() ) { + // IMPL NOTE : The builder is mainly used to collect the column reference mappings + final Identifier fkIdentifier = DatabaseIdentifier.toIdentifier( + resultSet.getString( getResultSetForeignKeyLabel() ) + ); + ForeignKeyBuilder fkBuilder = fkBuilders.get( fkIdentifier ); + if ( fkBuilder == null ) { + fkBuilder = generateForeignKeyBuilder( fkIdentifier ); + fkBuilders.put( fkIdentifier, fkBuilder ); + } + + final QualifiedTableName incomingPkTableName = extractPrimaryKeyTableName( resultSet ); + + final TableInformation pkTableInformation = extractionContext.getDatabaseObjectAccess() + .locateTableInformation( incomingPkTableName ); + + if ( pkTableInformation == null ) { + // the assumption here is that we have not seen this table already based on fully-qualified name + // during previous step of building all table metadata so most likely this is + // not a match based solely on schema/catalog and that another row in this result set + // should match. + continue; + } + + final Identifier fkColumnIdentifier = DatabaseIdentifier.toIdentifier( + resultSet.getString( getResultSetForeignKeyColumnNameLabel() ) + ); + final Identifier pkColumnIdentifier = DatabaseIdentifier.toIdentifier( + resultSet.getString( getResultSetPrimaryKeyColumnNameLabel() ) + ); + + fkBuilder.addColumnMapping( + tableInformation.getColumn( fkColumnIdentifier ), + pkTableInformation.getColumn( pkColumnIdentifier ) + ); + } + return null; + }; processImportedKeysResultSet( catalogFilter, schemaFilter, tableInformation.getName().getTableName().getText(), - resultSet -> { - // todo : need to account for getCrossReference() as well... - - while ( resultSet.next() ) { - // IMPL NOTE : The builder is mainly used to collect the column reference mappings - final Identifier fkIdentifier = DatabaseIdentifier.toIdentifier( - resultSet.getString( getResultSetForeignKeyLabel() ) - ); - ForeignKeyBuilder fkBuilder = fkBuilders.get( fkIdentifier ); - if ( fkBuilder == null ) { - fkBuilder = generateForeignKeyBuilder( fkIdentifier ); - fkBuilders.put( fkIdentifier, fkBuilder ); - } - - final QualifiedTableName incomingPkTableName = extractPrimaryKeyTableName( resultSet ); - - final TableInformation pkTableInformation = extractionContext.getDatabaseObjectAccess() - .locateTableInformation( incomingPkTableName ); - - if ( pkTableInformation == null ) { - // the assumption here is that we have not seen this table already based on fully-qualified name - // during previous step of building all table metadata so most likely this is - // not a match based solely on schema/catalog and that another row in this result set - // should match. - continue; - } - - final Identifier fkColumnIdentifier = DatabaseIdentifier.toIdentifier( - resultSet.getString( getResultSetForeignKeyColumnNameLabel() ) - ); - final Identifier pkColumnIdentifier = DatabaseIdentifier.toIdentifier( - resultSet.getString( getResultSetPrimaryKeyColumnNameLabel() ) - ); - - fkBuilder.addColumnMapping( - tableInformation.getColumn( fkColumnIdentifier ), - pkTableInformation.getColumn( pkColumnIdentifier ) - ); - } - return null; - } - ); + processor); + final Dialect dialect = extractionContext.getJdbcEnvironment().getDialect(); + if (dialect.useCrossReferenceForeignKeys()) { + processCrossReferenceResultSet( + null, + null, + dialect.getCrossReferenceParentTableFilter(), + catalogFilter, + schemaFilter, + tableInformation.getName().getTableName().getText(), + processor + ); + } } catch (SQLException e) { throw convertSQLException( diff --git a/hibernate-core/src/main/java/org/hibernate/tool/schema/extract/internal/InformationExtractorJdbcDatabaseMetaDataImpl.java b/hibernate-core/src/main/java/org/hibernate/tool/schema/extract/internal/InformationExtractorJdbcDatabaseMetaDataImpl.java index 1c33291926..456fb1a9b4 100644 --- a/hibernate-core/src/main/java/org/hibernate/tool/schema/extract/internal/InformationExtractorJdbcDatabaseMetaDataImpl.java +++ b/hibernate-core/src/main/java/org/hibernate/tool/schema/extract/internal/InformationExtractorJdbcDatabaseMetaDataImpl.java @@ -134,6 +134,26 @@ public class InformationExtractorJdbcDatabaseMetaDataImpl extends AbstractInform } } + @Override + protected T processCrossReferenceResultSet( + String parentCatalog, + String parentSchema, + String parentTable, + String foreignCatalog, + String foreignSchema, + String foreignTable, + ExtractionContext.ResultSetProcessor processor) throws SQLException { + try (ResultSet resultSet = getExtractionContext().getJdbcDatabaseMetaData().getCrossReference( + parentCatalog, + parentSchema, + parentTable, + foreignCatalog, + foreignSchema, + foreignTable) ) { + return processor.process( resultSet ); + } + } + protected void addColumns(TableInformation tableInformation) { final Dialect dialect = getExtractionContext().getJdbcEnvironment().getDialect();