HHH-17733 Oracle - Stream has already been closed when querying an entity with a LONG RAW column and a relation

This commit is contained in:
Andrea Boriero 2024-03-06 12:57:58 +01:00
parent bffd7683e0
commit 27d17eb2cf
1 changed files with 57 additions and 2 deletions

View File

@ -7,6 +7,7 @@
package org.hibernate.sql.results.jdbc.internal;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.BitSet;
@ -15,6 +16,7 @@ import org.hibernate.JDBCException;
import org.hibernate.QueryTimeoutException;
import org.hibernate.cache.spi.QueryKey;
import org.hibernate.cache.spi.QueryResultsCache;
import org.hibernate.dialect.OracleDialect;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.exception.DataException;
@ -44,6 +46,7 @@ public class JdbcValuesResultSetImpl extends AbstractJdbcValues {
private final SqlSelection[] sqlSelections;
private final BitSet initializedIndexes;
private BitSet forceInitializedIndexes;
private final Object[] currentRowJdbcValues;
private final int[] valueIndexesToCacheIndexes;
// Is only meaningful if valueIndexesToCacheIndexes is not null
@ -72,9 +75,36 @@ public class JdbcValuesResultSetImpl extends AbstractJdbcValues {
final int rowSize = valuesMapping.getRowSize();
this.sqlSelections = new SqlSelection[rowSize];
for ( SqlSelection selection : valuesMapping.getSqlSelections() ) {
this.sqlSelections[selection.getValuesArrayPosition()] = selection;
final boolean isOracleDialect = executionContext.getSession().getJdbcServices().getDialect() instanceof OracleDialect;
if ( isOracleDialect ) {
// When LONG RAW and LONG type columns are not read sequentially (e.g. a following column in the ResultSet is read first)
// Oracle raises a `ORA-17027: Stream has already been closed` exception , in order to avoid this
// Hibernate needs force their initialization
final ResultSetMetaData metaData = ( (AbstractResultSetAccess) resultSetAccess ).getMetaData();
for ( SqlSelection selection : valuesMapping.getSqlSelections() ) {
this.sqlSelections[selection.getValuesArrayPosition()] = selection;
if ( isOracleDialect ) {
try {
int columnType = metaData.getColumnType( selection.getJdbcResultSetIndex() );
if ( columnType == -4 || columnType == -1 ) {
if ( forceInitializedIndexes == null ) {
forceInitializedIndexes = new BitSet();
}
forceInitializedIndexes.set( selection.getValuesArrayPosition() );
}
}
catch (SQLException e) {
// ignore
}
}
}
}
else {
for ( SqlSelection selection : valuesMapping.getSqlSelections() ) {
this.sqlSelections[selection.getValuesArrayPosition()] = selection;
}
}
this.initializedIndexes = new BitSet( rowSize );
this.currentRowJdbcValues = new Object[rowSize];
if ( queryCachePutManager == null ) {
@ -324,6 +354,31 @@ public class JdbcValuesResultSetImpl extends AbstractJdbcValues {
private void readCurrentRowValues() {
initializedIndexes.clear();
if ( forceInitializedIndexes != null ) {
final ResultSet resultSet = resultSetAccess.getResultSet();
final SharedSessionContractImplementor session = executionContext.getSession();
for ( final SqlSelection sqlSelection : sqlSelections ) {
try {
final int valuesArrayPosition = sqlSelection.getValuesArrayPosition();
if ( forceInitializedIndexes.get( valuesArrayPosition ) ) {
currentRowJdbcValues[valuesArrayPosition] = sqlSelection.getJdbcValueExtractor().extract(
resultSet,
sqlSelection.getJdbcResultSetIndex(),
session
);
initializedIndexes.set( valuesArrayPosition );
}
}
catch (SQLException e) {
// do not want to wrap in ExecutionException here
throw executionContext.getSession().getJdbcServices().getSqlExceptionHelper().convert(
e,
"Could not extract column [" + sqlSelection.getJdbcResultSetIndex() + "] from JDBC ResultSet"
);
}
}
}
}
@Override