HHH-15403 Likely Statement leak on invoking a stored procedure

This commit is contained in:
Andrea Boriero 2022-07-20 10:32:45 +02:00 committed by Sanne Grinovero
parent a4e52f91f8
commit 1f31284f33
6 changed files with 113 additions and 86 deletions

View File

@ -613,7 +613,7 @@ public class ProcedureCallImpl<R>
.getJdbcCoordinator()
.getStatementPreparer()
.prepareStatement( call.getSql(), true );
try {
// Register the parameter mode and type
callableStatementSupport.registerParameters(
procedureName,
@ -648,8 +648,6 @@ public class ProcedureCallImpl<R>
}
}
final JdbcCallRefCursorExtractor[] extractors = refCursorExtractors.toArray( new JdbcCallRefCursorExtractor[0] );
final ExecutionContext executionContext = new ExecutionContext() {
private final Callback callback = new CallbackImpl();
@ -687,7 +685,6 @@ public class ProcedureCallImpl<R>
// Note that this should actually happen in an executor
try {
int paramBindingPosition = call.getFunctionReturn() == null ? 1 : 2;
for ( JdbcParameterBinder parameterBinder : call.getParameterBinders() ) {
parameterBinder.bindParameterValue(
@ -700,13 +697,21 @@ public class ProcedureCallImpl<R>
}
}
catch (SQLException e) {
getSession().getJdbcCoordinator().getLogicalConnection().getResourceRegistry().release( statement );
throw getSession().getJdbcServices().getSqlExceptionHelper().convert(
e,
"Error registering CallableStatement parameters",
procedureName
);
}
return new ProcedureOutputsImpl( this, parameterRegistrations, extractors, statement );
return new ProcedureOutputsImpl(
this,
parameterRegistrations,
refCursorExtractors.toArray( new JdbcCallRefCursorExtractor[0] ),
statement
);
}
@Override

View File

@ -148,4 +148,9 @@ public class ProcedureOutputsImpl extends OutputsImpl implements ProcedureOutput
}
}
@Override
public void release() {
super.release();
getResultContext().getSession().getJdbcCoordinator().getLogicalConnection().getResourceRegistry().release( callableStatement );
}
}

View File

@ -133,9 +133,16 @@ public final class ResourceRegistryStandardImpl implements ResourceRegistry {
else {
resultSets.remove( resultSet );
if ( resultSets.isEmpty() ) {
try {
if ( statement.isClosed() ) {
xref.remove( statement );
}
}
catch (SQLException e) {
log.debugf( "Unable to release JDBC statement [%s]", e.getMessage() );
}
}
}
}
else {
final Object removed = unassociatedResultSets == null ? null : unassociatedResultSets.remove( resultSet );

View File

@ -67,6 +67,10 @@ public class OutputsImpl implements Outputs {
}
protected ResultContext getResultContext(){
return context;
}
protected void executeStatement() {
try {
final boolean isResultSet = jdbcStatement.execute();
@ -134,12 +138,7 @@ public class OutputsImpl implements Outputs {
@Override
public void release() {
try {
jdbcStatement.close();
}
catch (SQLException e) {
log.debug( "Unable to close PreparedStatement", e );
}
context.getSession().getJdbcCoordinator().getLogicalConnection().getResourceRegistry().release( jdbcStatement );
}
private List extractCurrentResults() {
@ -281,9 +280,6 @@ public class OutputsImpl implements Outputs {
}
return results;
}
// catch (SQLException e) {
// throw context.getSession().getExceptionConverter().convert( e, "Error processing return rows" );
// }
finally {
rowReader.finishUp( jdbcValuesSourceProcessingState );
jdbcValuesSourceProcessingState.finishUp();

View File

@ -10,6 +10,7 @@ import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;
import org.hibernate.Session;
import org.hibernate.dialect.OracleDialect;
@ -18,6 +19,7 @@ import org.hibernate.engine.jdbc.spi.ResultSetReturn;
import org.hibernate.engine.jdbc.spi.StatementPreparer;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.jdbc.Work;
import org.hibernate.procedure.ProcedureCall;
import org.hibernate.testing.RequiresDialect;
import org.hibernate.testing.TestForIssue;
@ -84,15 +86,19 @@ public class CursorFromCallableTest extends BaseCoreFunctionalTestCase {
// Verify statement closing with JdbcCoordinator#hasRegisteredResources() instead.
// BigDecimal maxCursors = (BigDecimal) session.createSQLQuery( "SELECT value FROM v$parameter WHERE name = 'open_cursors'" ).uniqueResult();
// for ( int i = 0; i < maxCursors + 10; ++i ) { named_query_execution }
ProcedureCall namedStoredProcedureQuery = session.createNamedStoredProcedureQuery( "NumValue.getSomeValues" );
List resultList = namedStoredProcedureQuery.getResultList();
Assert.assertEquals(
Arrays.asList( new NumValue( 1, "Line 1" ), new NumValue( 2, "Line 2" ) ),
session.createNamedStoredProcedureQuery( "NumValue.getSomeValues" ).getResultList()
resultList
);
namedStoredProcedureQuery.close();
JdbcCoordinator jdbcCoordinator = ( (SessionImplementor) session ).getJdbcCoordinator();
Assert.assertFalse(
"Prepared statement and result set should be released after query execution.",
jdbcCoordinator.getLogicalConnection().getResourceRegistry().hasRegisteredResources()
);
session.getTransaction().commit();
session.close();
}

View File

@ -119,6 +119,14 @@ public class PreparedStatementSpyConnectionProvider extends ConnectionProviderDe
return statementSpy;
} ).when( connectionSpy ).prepareStatement( ArgumentMatchers.anyString() );
Mockito.doAnswer( invocation -> {
PreparedStatement statement = (PreparedStatement) invocation.callRealMethod();
PreparedStatement statementSpy = spy( statement, settingsForStatements );
String sql = (String) invocation.getArguments()[0];
preparedStatementMap.put( statementSpy, sql );
return statementSpy;
} ).when( connectionSpy ).prepareCall( ArgumentMatchers.anyString() );
Mockito.doAnswer( invocation -> {
Statement statement = (Statement) invocation.callRealMethod();
Statement statementSpy = spy( statement, settingsForStatements );