HHH-11600 - Sap HANA PreparedStatement implements CallableStatement and is treated as such by Hibernate

This commit is contained in:
Vlad Mihalcea 2017-03-30 17:21:23 +03:00
parent 1c34914455
commit d80c6ac0f9
2 changed files with 85 additions and 42 deletions

View File

@ -31,8 +31,6 @@ public class ResultSetReturnImpl implements ResultSetReturn {
private final SqlStatementLogger sqlStatementLogger; private final SqlStatementLogger sqlStatementLogger;
private final SqlExceptionHelper sqlExceptionHelper; private final SqlExceptionHelper sqlExceptionHelper;
private boolean isJdbc4 = true;
/** /**
* Constructs a ResultSetReturnImpl * Constructs a ResultSetReturnImpl
* *
@ -56,15 +54,6 @@ public class ResultSetReturnImpl implements ResultSetReturn {
public ResultSet extract(PreparedStatement statement) { public ResultSet extract(PreparedStatement statement) {
// IMPL NOTE : SQL logged by caller // IMPL NOTE : SQL logged by caller
try { try {
if ( isTypeOf( statement, CallableStatement.class ) ) {
// We actually need to extract from Callable statement. Although
// this seems needless, Oracle can return an
// OracleCallableStatementWrapper that finds its way to this method,
// rather than extract(CallableStatement). See HHH-8022.
final CallableStatement callableStatement = statement.unwrap( CallableStatement.class );
return extract( callableStatement );
}
final ResultSet rs; final ResultSet rs;
try { try {
jdbcExecuteStatementStart(); jdbcExecuteStatementStart();
@ -89,25 +78,6 @@ public class ResultSetReturnImpl implements ResultSetReturn {
jdbcCoordinator.getJdbcSessionOwner().getJdbcSessionContext().getObserver().jdbcExecuteStatementStart(); jdbcCoordinator.getJdbcSessionOwner().getJdbcSessionContext().getObserver().jdbcExecuteStatementStart();
} }
private boolean isTypeOf(final Statement statement, final Class<? extends Statement> type) {
if ( isJdbc4 ) {
try {
// This is "more correct" than #isInstance, but not always supported.
return statement.isWrapperFor( type );
}
catch (SQLException e) {
// No operation
}
catch (Throwable e) {
// No operation. Note that this catches more than just SQLException to
// cover edge cases where a driver might throw an UnsupportedOperationException, AbstractMethodError,
// etc. If so, skip permanently.
isJdbc4 = false;
}
}
return type.isInstance( statement );
}
@Override @Override
public ResultSet extract(CallableStatement callableStatement) { public ResultSet extract(CallableStatement callableStatement) {
// IMPL NOTE : SQL logged by caller // IMPL NOTE : SQL logged by caller

View File

@ -108,6 +108,8 @@ public abstract class Loader {
private final boolean referenceCachingEnabled; private final boolean referenceCachingEnabled;
private boolean isJdbc4 = true;
public Loader(SessionFactoryImplementor factory) { public Loader(SessionFactoryImplementor factory) {
this.factory = factory; this.factory = factory;
this.referenceCachingEnabled = factory.getSessionFactoryOptions().isDirectReferenceCacheEntriesEnabled(); this.referenceCachingEnabled = factory.getSessionFactoryOptions().isDirectReferenceCacheEntriesEnabled();
@ -1907,15 +1909,54 @@ public abstract class Loader {
sql = preprocessSQL( sql, queryParameters, getFactory().getDialect(), afterLoadActions ); sql = preprocessSQL( sql, queryParameters, getFactory().getDialect(), afterLoadActions );
final PreparedStatement st = prepareQueryStatement( sql, queryParameters, limitHandler, scroll, session ); final PreparedStatement st = prepareQueryStatement( sql, queryParameters, limitHandler, scroll, session );
return new SqlStatementWrapper(
st, getResultSet( final ResultSet rs;
if( queryParameters.isCallable() && isTypeOf( st, CallableStatement.class ) ) {
final CallableStatement cs = st.unwrap( CallableStatement.class );
rs = getResultSet(
cs,
queryParameters.getRowSelection(),
limitHandler,
queryParameters.hasAutoDiscoverScalarTypes(),
session
);
}
else {
rs = getResultSet(
st, st,
queryParameters.getRowSelection(), queryParameters.getRowSelection(),
limitHandler, limitHandler,
queryParameters.hasAutoDiscoverScalarTypes(), queryParameters.hasAutoDiscoverScalarTypes(),
session session
) );
}
return new SqlStatementWrapper(
st,
rs
); );
}
private boolean isTypeOf(final Statement statement, final Class<? extends Statement> type) {
if ( isJdbc4 ) {
try {
// This is "more correct" than #isInstance, but not always supported.
return statement.isWrapperFor( type );
}
catch (SQLException e) {
// No operation
}
catch (Throwable e) {
// No operation. Note that this catches more than just SQLException to
// cover edge cases where a driver might throw an UnsupportedOperationException, AbstractMethodError,
// etc. If so, skip permanently.
isJdbc4 = false;
}
}
return type.isInstance( statement );
} }
/** /**
@ -2121,16 +2162,8 @@ public abstract class Loader {
final SharedSessionContractImplementor session) throws SQLException, HibernateException { final SharedSessionContractImplementor session) throws SQLException, HibernateException {
try { try {
ResultSet rs = session.getJdbcCoordinator().getResultSetReturn().extract( st ); ResultSet rs = session.getJdbcCoordinator().getResultSetReturn().extract( st );
rs = wrapResultSetIfEnabled( rs, session );
if ( !limitHandler.supportsLimitOffset() || !LimitHelper.useLimit( limitHandler, selection ) ) { return processResultSet(rs, selection, limitHandler, autodiscovertypes, session);
advance( rs, selection );
}
if ( autodiscovertypes ) {
autoDiscoverTypes( rs );
}
return rs;
} }
catch (SQLException | HibernateException e) { catch (SQLException | HibernateException e) {
session.getJdbcCoordinator().getLogicalConnection().getResourceRegistry().release( st ); session.getJdbcCoordinator().getLogicalConnection().getResourceRegistry().release( st );
@ -2139,6 +2172,46 @@ public abstract class Loader {
} }
} }
/**
* Execute given <tt>CallableStatement</tt>, advance to the first result and return SQL <tt>ResultSet</tt>.
*/
protected final ResultSet getResultSet(
final CallableStatement st,
final RowSelection selection,
final LimitHandler limitHandler,
final boolean autodiscovertypes,
final SharedSessionContractImplementor session) throws SQLException, HibernateException {
try {
ResultSet rs = session.getJdbcCoordinator().getResultSetReturn().extract( st );
return processResultSet(rs, selection, limitHandler, autodiscovertypes, session);
}
catch (SQLException | HibernateException e) {
session.getJdbcCoordinator().getLogicalConnection().getResourceRegistry().release( st );
session.getJdbcCoordinator().afterStatementExecution();
throw e;
}
}
private ResultSet processResultSet(
ResultSet rs,
final RowSelection selection,
final LimitHandler limitHandler,
final boolean autodiscovertypes,
final SharedSessionContractImplementor session
) throws SQLException, HibernateException {
rs = wrapResultSetIfEnabled( rs, session );
if ( !limitHandler.supportsLimitOffset() || !LimitHelper.useLimit( limitHandler, selection ) ) {
advance( rs, selection );
}
if ( autodiscovertypes ) {
autoDiscoverTypes( rs );
}
return rs;
}
protected void autoDiscoverTypes(ResultSet rs) { protected void autoDiscoverTypes(ResultSet rs) {
throw new AssertionFailure( "Auto discover types not supported in this loader" ); throw new AssertionFailure( "Auto discover types not supported in this loader" );