HHH-18221 Fix for Incomplete list of existing foreign keys - DatabaseMetaData.crossReferences(...) not used
This commit is contained in:
parent
1e70c51b56
commit
753076bb40
|
@ -573,6 +573,26 @@ public class InformixDialect extends Dialect {
|
||||||
return BeforeUseAction.CREATE;
|
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
|
@Override
|
||||||
public UniqueDelegate getUniqueDelegate() {
|
public UniqueDelegate getUniqueDelegate() {
|
||||||
return uniqueDelegate;
|
return uniqueDelegate;
|
||||||
|
|
|
@ -2637,6 +2637,23 @@ public abstract class Dialect implements ConversionContext, TypeContributor, Fun
|
||||||
+ " " + foreignKeyDefinition;
|
+ " " + 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.
|
* The syntax used to add a primary key constraint to a table.
|
||||||
*
|
*
|
||||||
|
|
|
@ -1264,6 +1264,92 @@ public abstract class AbstractInformationExtractorImpl implements InformationExt
|
||||||
ExtractionContext.ResultSetProcessor<T> processor
|
ExtractionContext.ResultSetProcessor<T> processor
|
||||||
) throws SQLException;
|
) throws SQLException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Must do the following:
|
||||||
|
* <ol>
|
||||||
|
* <li>
|
||||||
|
* 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}:
|
||||||
|
* <ul>
|
||||||
|
* <li>
|
||||||
|
* column label {@link #getResultSetForeignKeyLabel} -
|
||||||
|
* foreign key name (may be null)
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* column label {@link #getResultSetPrimaryKeyCatalogLabel} -
|
||||||
|
* primary key table catalog being imported (may be null)
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* column label {@link #getResultSetPrimaryKeySchemaLabel} -
|
||||||
|
* primary key table schema being imported (may be null)
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* column label {@link #getResultSetPrimaryKeyTableLabel} -
|
||||||
|
* primary key table name being imported
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* column label {@link #getResultSetForeignKeyColumnNameLabel} -
|
||||||
|
* foreign key column name
|
||||||
|
* </li>
|
||||||
|
* <li>
|
||||||
|
* column label {@link #getResultSetPrimaryKeyColumnNameLabel} -
|
||||||
|
* primary key column name being imported
|
||||||
|
* </li>
|
||||||
|
* </ul>
|
||||||
|
* The ResultSet must be ordered by the primary key
|
||||||
|
* foreignCatalog/foreignSchema/foreignTable and column position within the key.
|
||||||
|
* </li>
|
||||||
|
* <li> execute {@code processor.process( resultSet )};</li>
|
||||||
|
* <li>
|
||||||
|
* release resources whether {@code processor.process( resultSet )}
|
||||||
|
* executes successfully or not.
|
||||||
|
* </li>
|
||||||
|
* </ol>
|
||||||
|
* <p>
|
||||||
|
* 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 <T> - 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> T processCrossReferenceResultSet(
|
||||||
|
String parentCatalog,
|
||||||
|
String parentSchema,
|
||||||
|
String parentTable,
|
||||||
|
String foreignCatalog,
|
||||||
|
String foreignSchema,
|
||||||
|
String foreignTable,
|
||||||
|
ExtractionContext.ResultSetProcessor<T> processor
|
||||||
|
) throws SQLException;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Iterable<ForeignKeyInformation> getForeignKeys(TableInformation tableInformation) {
|
public Iterable<ForeignKeyInformation> getForeignKeys(TableInformation tableInformation) {
|
||||||
final Map<Identifier, ForeignKeyBuilder> fkBuilders = new HashMap<>();
|
final Map<Identifier, ForeignKeyBuilder> fkBuilders = new HashMap<>();
|
||||||
|
@ -1289,52 +1375,62 @@ public abstract class AbstractInformationExtractorImpl implements InformationExt
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
ExtractionContext.ResultSetProcessor<Void> 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(
|
processImportedKeysResultSet(
|
||||||
catalogFilter,
|
catalogFilter,
|
||||||
schemaFilter,
|
schemaFilter,
|
||||||
tableInformation.getName().getTableName().getText(),
|
tableInformation.getName().getTableName().getText(),
|
||||||
resultSet -> {
|
processor);
|
||||||
// todo : need to account for getCrossReference() as well...
|
final Dialect dialect = extractionContext.getJdbcEnvironment().getDialect();
|
||||||
|
if (dialect.useCrossReferenceForeignKeys()) {
|
||||||
while ( resultSet.next() ) {
|
processCrossReferenceResultSet(
|
||||||
// IMPL NOTE : The builder is mainly used to collect the column reference mappings
|
null,
|
||||||
final Identifier fkIdentifier = DatabaseIdentifier.toIdentifier(
|
null,
|
||||||
resultSet.getString( getResultSetForeignKeyLabel() )
|
dialect.getCrossReferenceParentTableFilter(),
|
||||||
);
|
catalogFilter,
|
||||||
ForeignKeyBuilder fkBuilder = fkBuilders.get( fkIdentifier );
|
schemaFilter,
|
||||||
if ( fkBuilder == null ) {
|
tableInformation.getName().getTableName().getText(),
|
||||||
fkBuilder = generateForeignKeyBuilder( fkIdentifier );
|
processor
|
||||||
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;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
catch (SQLException e) {
|
catch (SQLException e) {
|
||||||
throw convertSQLException(
|
throw convertSQLException(
|
||||||
|
|
|
@ -134,6 +134,26 @@ public class InformationExtractorJdbcDatabaseMetaDataImpl extends AbstractInform
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected <T> T processCrossReferenceResultSet(
|
||||||
|
String parentCatalog,
|
||||||
|
String parentSchema,
|
||||||
|
String parentTable,
|
||||||
|
String foreignCatalog,
|
||||||
|
String foreignSchema,
|
||||||
|
String foreignTable,
|
||||||
|
ExtractionContext.ResultSetProcessor<T> processor) throws SQLException {
|
||||||
|
try (ResultSet resultSet = getExtractionContext().getJdbcDatabaseMetaData().getCrossReference(
|
||||||
|
parentCatalog,
|
||||||
|
parentSchema,
|
||||||
|
parentTable,
|
||||||
|
foreignCatalog,
|
||||||
|
foreignSchema,
|
||||||
|
foreignTable) ) {
|
||||||
|
return processor.process( resultSet );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected void addColumns(TableInformation tableInformation) {
|
protected void addColumns(TableInformation tableInformation) {
|
||||||
final Dialect dialect = getExtractionContext().getJdbcEnvironment().getDialect();
|
final Dialect dialect = getExtractionContext().getJdbcEnvironment().getDialect();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue