Skip to content

Commit 21c8924

Browse files
committed
HHH-14435 Improve table information extraction (especially for Oracle with enableSynonyms)
Previously Oracle with enableSynonyms ran into a timeout on CI because the JDBC driver issues a rather expensive query. The new implementation issues a dummy query and uses the result set metadata as suggested in various online articles about schema introspection
1 parent 62d727d commit 21c8924

File tree

2 files changed

+56
-47
lines changed

2 files changed

+56
-47
lines changed

hibernate-core/src/main/java/org/hibernate/boot/model/relational/QualifiedTableName.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,4 +23,20 @@ public QualifiedTableName(Namespace.Name schemaName, Identifier tableName) {
2323
public Identifier getTableName() {
2424
return getObjectName();
2525
}
26+
27+
public QualifiedTableName quote() {
28+
Identifier catalogName = getCatalogName();
29+
if ( catalogName != null ) {
30+
catalogName = new Identifier( catalogName.getText(), true );
31+
}
32+
Identifier schemaName = getSchemaName();
33+
if ( schemaName != null ) {
34+
schemaName = new Identifier( schemaName.getText(), true );
35+
}
36+
Identifier tableName = getTableName();
37+
if ( tableName != null ) {
38+
tableName = new Identifier( tableName.getText(), true );
39+
}
40+
return new QualifiedTableName( catalogName, schemaName, tableName );
41+
}
2642
}

hibernate-core/src/main/java/org/hibernate/tool/schema/extract/internal/InformationExtractorJdbcDatabaseMetaDataImpl.java

Lines changed: 40 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88

99
import java.sql.DatabaseMetaData;
1010
import java.sql.ResultSet;
11+
import java.sql.ResultSetMetaData;
1112
import java.sql.SQLException;
13+
import java.sql.Statement;
1214
import java.util.ArrayList;
1315
import java.util.Collections;
1416
import java.util.HashMap;
@@ -540,62 +542,53 @@ protected boolean isPhysicalTableType(String tableType) {
540542
}
541543

542544
private void addColumns(TableInformation tableInformation) {
543-
final QualifiedTableName tableName = tableInformation.getName();
544-
final Identifier catalog = tableName.getCatalogName();
545-
final Identifier schema = tableName.getSchemaName();
546-
547-
final String catalogFilter;
548-
final String schemaFilter;
549-
550-
if ( catalog == null ) {
551-
catalogFilter = "";
552-
}
553-
else {
554-
catalogFilter = catalog.getText();
555-
}
556-
557-
if ( schema == null ) {
558-
schemaFilter = "";
559-
}
560-
else {
561-
schemaFilter = schema.getText();
562-
}
563-
564-
try {
565-
ResultSet resultSet = extractionContext.getJdbcDatabaseMetaData().getColumns(
566-
catalogFilter,
567-
schemaFilter,
568-
tableName.getTableName().getText(),
569-
"%"
570-
);
571-
572-
try {
573-
while ( resultSet.next() ) {
574-
final String columnName = resultSet.getString( "COLUMN_NAME" );
575-
final ColumnInformationImpl columnInformation = new ColumnInformationImpl(
576-
tableInformation,
577-
DatabaseIdentifier.toIdentifier( columnName ),
578-
resultSet.getInt( "DATA_TYPE" ),
579-
new StringTokenizer( resultSet.getString( "TYPE_NAME" ), "() " ).nextToken(),
580-
resultSet.getInt( "COLUMN_SIZE" ),
581-
resultSet.getInt( "DECIMAL_DIGITS" ),
582-
interpretTruthValue( resultSet.getString( "IS_NULLABLE" ) )
583-
);
584-
tableInformation.addColumn( columnInformation );
585-
}
586-
}
587-
finally {
588-
resultSet.close();
545+
// We use this dummy query to retrieve the table information through the ResultSetMetaData
546+
// This is significantly better than to use the DatabaseMetaData especially on Oracle with synonyms enabled
547+
final String tableName = extractionContext.getJdbcEnvironment().getQualifiedObjectNameFormatter().format(
548+
// The name comes from the database, so the case is correct
549+
// But we quote here to avoid issues with reserved words
550+
tableInformation.getName().quote(),
551+
extractionContext.getJdbcEnvironment().getDialect()
552+
);
553+
final String query = "select * from " + tableName + " where 1=0";
554+
try (Statement statement = extractionContext.getJdbcConnection()
555+
.createStatement(); ResultSet resultSet = statement.executeQuery( query )) {
556+
final ResultSetMetaData metaData = resultSet.getMetaData();
557+
final int columnCount = metaData.getColumnCount();
558+
559+
for ( int i = 1; i <= columnCount; i++ ) {
560+
final String columnName = metaData.getColumnName( i );
561+
final ColumnInformationImpl columnInformation = new ColumnInformationImpl(
562+
tableInformation,
563+
DatabaseIdentifier.toIdentifier( columnName ),
564+
metaData.getColumnType( i ),
565+
new StringTokenizer( metaData.getColumnTypeName( i ), "() " ).nextToken(),
566+
metaData.getPrecision( i ),
567+
metaData.getScale( i ),
568+
interpretNullable( metaData.isNullable( i ) )
569+
);
570+
tableInformation.addColumn( columnInformation );
589571
}
590572
}
591573
catch (SQLException e) {
592574
throw convertSQLException(
593575
e,
594-
"Error accessing column metadata: " + tableName.toString()
576+
"Error accessing column metadata: " + tableInformation.getName().toString()
595577
);
596578
}
597579
}
598580

581+
private TruthValue interpretNullable(int nullable) {
582+
switch ( nullable ) {
583+
case ResultSetMetaData.columnNullable:
584+
return TruthValue.TRUE;
585+
case ResultSetMetaData.columnNoNulls:
586+
return TruthValue.FALSE;
587+
default:
588+
return TruthValue.UNKNOWN;
589+
}
590+
}
591+
599592
private TruthValue interpretTruthValue(String nullable) {
600593
if ( "yes".equalsIgnoreCase( nullable ) ) {
601594
return TruthValue.TRUE;

0 commit comments

Comments
 (0)