diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java index bb709307d..2c918f639 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java @@ -6988,6 +6988,35 @@ final short peekStatusFlag() { return 0; } + final int peekReturnValueStatus() throws SQLServerException { + // Ensure that we have a packet to read from. + if (!ensurePayload()) { + throwInvalidTDS(); + } + + // In order to parse the 'status' value, we need to skip over the following properties in the TDS packet + // payload: TDS token type (1 byte value), ordinal/length (2 byte value), parameter name length value (1 byte value) and + // the number of bytes that make the parameter name (need to be calculated). + // + // 'offset' starts at 4 because tdsTokenType + ordinal/length + parameter name length value is 4 bytes. So, we + // skip 4 bytes immediateley. + int offset = 4; + int paramNameLength = currentPacket.payload[payloadOffset + 3]; + + // Check if parameter name is set. If it's set, it should be > 0. In which case, we add the + // additional bytes to skip. + if (paramNameLength > 0) { + // Each character in unicode is 2 bytes + offset += 2 * paramNameLength; + } + + if (payloadOffset + offset <= currentPacket.payloadLength) { + return currentPacket.payload[payloadOffset + offset] & 0xFF; + } + + return -1; + } + final int readUnsignedByte() throws SQLServerException { // Ensure that we have a packet to read from. if (!ensurePayload()) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java index 5e278406d..5c1393f9a 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java @@ -192,7 +192,7 @@ private Parameter getOutParameter(int i) throws SQLServerException { return inOutParam[i - 1]; } - if (inOutParam[i - 1].isReturnValue() && bReturnValueSyntax && !isCursorable(executeMethod) && !isTVPType) { + if (inOutParam[i - 1].isReturnValue() && bReturnValueSyntax && !isCursorable(executeMethod) && !isTVPType && returnValueStatus != 2) { return inOutParam[i - 1]; } @@ -341,7 +341,7 @@ boolean onRetValue(TDSReader tdsReader) throws SQLServerException { OutParamHandler outParamHandler = new OutParamHandler(); if (bReturnValueSyntax && (nOutParamsAssigned == 0) && !isCursorable(executeMethod) && !isTVPType - && SQLServerConnection.isCallRemoteProcDirectValid(userSQL, inOutParam.length, bReturnValueSyntax)) { + && SQLServerConnection.isCallRemoteProcDirectValid(userSQL, inOutParam.length, bReturnValueSyntax) && returnValueStatus != 2) { nOutParamsAssigned++; } @@ -389,7 +389,7 @@ boolean onRetValue(TDSReader tdsReader) throws SQLServerException { outParamIndex = outParamHandler.srv.getOrdinalOrLength(); if (bReturnValueSyntax && !isCursorable(executeMethod) && !isTVPType && SQLServerConnection - .isCallRemoteProcDirectValid(userSQL, inOutParam.length, bReturnValueSyntax)) { + .isCallRemoteProcDirectValid(userSQL, inOutParam.length, bReturnValueSyntax) && returnValueStatus != 2) { outParamIndex++; } else { // Statements need to have their out param indices adjusted by the number diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java index 2035e1fd1..3559fc99f 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java @@ -56,6 +56,7 @@ public class SQLServerStatement implements ISQLServerStatement { /** * Always update serialVersionUID when prompted. */ + private static final long serialVersionUID = -4421134713913331507L; final static char LEFT_CURLY_BRACKET = 123; @@ -66,6 +67,9 @@ public class SQLServerStatement implements ISQLServerStatement { /** response buffer adaptive flag */ boolean isResponseBufferingAdaptive = false; + /** TDS token return value status **/ + int returnValueStatus; + final boolean getIsResponseBufferingAdaptive() { return isResponseBufferingAdaptive; } @@ -1627,11 +1631,21 @@ boolean onRetStatus(TDSReader tdsReader) throws SQLServerException { @Override boolean onRetValue(TDSReader tdsReader) throws SQLServerException { + // Status: A value of 0x01 means the return value corresponds to an output parameter from + // a stored procedure. If it's 0x02 then the value corresponds to a return value from a + // user defined function. + // + // If it's a return value from a user defined function, we need to return false from this method + // so that the return value is not skipped. + int status = tdsReader.peekReturnValueStatus(); + + SQLServerStatement.this.returnValueStatus = status; + // We are only interested in return values that are statement OUT parameters, // in which case we need to stop parsing and let CallableStatement take over. // A RETVALUE token appearing in the execution results, but before any RETSTATUS // token, is a TEXTPTR return value that should be ignored. - if (moreResults && null == procedureRetStatToken) { + if (moreResults && null == procedureRetStatToken && status != 2) { Parameter p = new Parameter( Util.shouldHonorAEForParameters(stmtColumnEncriptionSetting, connection)); p.skipRetValStatus(tdsReader);