From 42dc48dc634ea30a4dc5dda830880095ee7b57c5 Mon Sep 17 00:00:00 2001 From: Terry Chow Date: Fri, 23 Aug 2024 11:06:29 -0700 Subject: [PATCH] Exec cstmt directly --- .../microsoft/sqlserver/jdbc/IOBuffer.java | 44 +- .../sqlserver/jdbc/ISQLServerDataSource.java | 42 +- .../microsoft/sqlserver/jdbc/Parameter.java | 76 +- .../sqlserver/jdbc/SQLServerBulkCopy.java | 11 +- .../jdbc/SQLServerCallableStatement.java | 903 ++++++++++++------ .../sqlserver/jdbc/SQLServerConnection.java | 29 +- .../sqlserver/jdbc/SQLServerDataSource.java | 30 +- .../sqlserver/jdbc/SQLServerDriver.java | 8 +- .../jdbc/SQLServerPreparedStatement.java | 219 ++++- .../sqlserver/jdbc/SQLServerResource.java | 3 + .../sqlserver/jdbc/SQLServerStatement.java | 49 +- .../sqlserver/jdbc/SQLServerXAResource.java | 28 +- .../com/microsoft/sqlserver/jdbc/dtv.java | 58 +- .../microsoft/sqlserver/jdbc/tdsparser.java | 11 + .../jdbc/SQLServerConnectionTest.java | 3 + .../sqlserver/jdbc/TestResource.java | 8 +- .../CallableStatementTest.java | 766 ++++++++++++++- .../RequestBoundaryMethodsTest.java | 6 +- .../jdbc/datatypes/DateAndTimeTypeTest.java | 138 ++- .../unit/statement/BatchExecutionTest.java | 11 +- .../jdbc/unit/statement/StatementTest.java | 7 +- 21 files changed, 1984 insertions(+), 466 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java index f51a5430c..4673a9e7c 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java @@ -467,6 +467,7 @@ static final String getEncryptionLevel(int level) { final static int COLINFO_STATUS_DIFFERENT_NAME = 0x20; final static int MAX_FRACTIONAL_SECONDS_SCALE = 7; + final static int DEFAULT_FRACTIONAL_SECONDS_SCALE = 3; final static Timestamp MAX_TIMESTAMP = Timestamp.valueOf("2079-06-06 23:59:59"); final static Timestamp MIN_TIMESTAMP = Timestamp.valueOf("1900-01-01 00:00:00"); @@ -4853,7 +4854,7 @@ void writeVMaxHeader(long headerLength, boolean isNull, SQLCollation collation) * Utility for internal writeRPCString calls */ void writeRPCStringUnicode(String sValue) throws SQLServerException { - writeRPCStringUnicode(null, sValue, false, null); + writeRPCStringUnicode(null, sValue, false, null, false); } /** @@ -4868,8 +4869,8 @@ void writeRPCStringUnicode(String sValue) throws SQLServerException { * @param collation * the collation of the data value */ - void writeRPCStringUnicode(String sName, String sValue, boolean bOut, - SQLCollation collation) throws SQLServerException { + void writeRPCStringUnicode(String sName, String sValue, boolean bOut, SQLCollation collation, + boolean isNonPLP) throws SQLServerException { boolean bValueNull = (sValue == null); int nValueLen = bValueNull ? 0 : (2 * sValue.length()); // Textual RPC requires a collation. If none is provided, as is the case when @@ -4881,7 +4882,7 @@ void writeRPCStringUnicode(String sName, String sValue, boolean bOut, * Use PLP encoding if either OUT params were specified or if the user query exceeds * DataTypes.SHORT_VARTYPE_MAX_BYTES */ - if (nValueLen > DataTypes.SHORT_VARTYPE_MAX_BYTES || bOut) { + if ((nValueLen > DataTypes.SHORT_VARTYPE_MAX_BYTES || bOut) && !isNonPLP) { writeRPCNameValType(sName, bOut, TDSType.NVARCHAR); writeVMaxHeader(nValueLen, // Length @@ -5632,8 +5633,8 @@ void writeCryptoMetaData() throws SQLServerException { writeByte(cryptoMeta.normalizationRuleVersion); } - void writeRPCByteArray(String sName, byte[] bValue, boolean bOut, JDBCType jdbcType, - SQLCollation collation) throws SQLServerException { + void writeRPCByteArray(String sName, byte[] bValue, boolean bOut, JDBCType jdbcType, SQLCollation collation, + boolean isNonPLP) throws SQLServerException { boolean bValueNull = (bValue == null); int nValueLen = bValueNull ? 0 : bValue.length; boolean isShortValue = (nValueLen <= DataTypes.SHORT_VARTYPE_MAX_BYTES); @@ -5679,7 +5680,7 @@ void writeRPCByteArray(String sName, byte[] bValue, boolean bOut, JDBCType jdbcT writeRPCNameValType(sName, bOut, tdsType); - if (usePLP) { + if (usePLP && !isNonPLP) { writeVMaxHeader(nValueLen, bValueNull, collation); // Send the data. @@ -7058,6 +7059,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/ISQLServerDataSource.java b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerDataSource.java index b9ab8027f..0792ebd9c 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerDataSource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerDataSource.java @@ -609,6 +609,25 @@ public interface ISQLServerDataSource extends javax.sql.CommonDataSource { */ boolean getUseDefaultGSSCredential(); + /** + * Sets whether or not sp_sproc_columns will be used for parameter name lookup. + * + * @param useFlexibleCallableStatements + * When set to false, sp_sproc_columns is not used for parameter name lookup + * in callable statements. This eliminates a round trip to the server but imposes limitations + * on how parameters are set. When set to false, applications must either reference + * parameters by name or by index, not both. Parameters must also be set in the same + * order as the stored procedure definition. + */ + void setUseFlexibleCallableStatements(boolean useFlexibleCallableStatements); + + /** + * Returns whether or not sp_sproc_columns is being used for parameter name lookup. + * + * @return useFlexibleCallableStatements + */ + boolean getUseFlexibleCallableStatements(); + /** * Sets the GSSCredential. * @@ -1346,27 +1365,4 @@ public interface ISQLServerDataSource extends javax.sql.CommonDataSource { * @return cacheBulkCopyMetadata boolean value */ boolean getcacheBulkCopyMetadata(); - - /** - * useFlexibleCallableStatements is temporarily removed. This is meant as a no-op. - * - * Sets whether or not sp_sproc_columns will be used for parameter name lookup. - * - * @param useFlexibleCallableStatements - * When set to false, sp_sproc_columns is not used for parameter name lookup - * in callable statements. This eliminates a round trip to the server but imposes limitations - * on how parameters are set. When set to false, applications must either reference - * parameters by name or by index, not both. Parameters must also be set in the same - * order as the stored procedure definition. - */ - void setUseFlexibleCallableStatements(boolean useFlexibleCallableStatements); - - /** - * useFlexibleCallableStatements is temporarily removed. This is meant as a no-op. - * - * Returns whether or not sp_sproc_columns is being used for parameter name lookup. - * - * @return useFlexibleCallableStatements - */ - boolean getUseFlexibleCallableStatements(); } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java b/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java index 807bf3250..6b61cf53e 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java @@ -38,6 +38,8 @@ final class Parameter { // For unencrypted parameters cryptometa will be null. For encrypted parameters it will hold encryption metadata. CryptoMetadata cryptoMeta = null; + boolean isNonPLP = false; + TypeInfo getTypeInfo() { return typeInfo; } @@ -49,6 +51,7 @@ final CryptoMetadata getCryptoMetadata() { private boolean shouldHonorAEForParameter = false; private boolean userProvidesPrecision = false; private boolean userProvidesScale = false; + private boolean isReturnValue = false; // The parameter type definition private String typeDefinition = null; @@ -71,11 +74,49 @@ boolean isOutput() { return null != registeredOutDTV; } - // Since a parameter can have only one type definition for both sending its value to the server (IN) - // and getting its value from the server (OUT), we use the JDBC type of the IN parameter value if there - // is one; otherwise we use the registered OUT param JDBC type. - JDBCType getJdbcType() { - return (null != inputDTV) ? inputDTV.getJdbcType() : JDBCType.UNKNOWN; + /** + * Returns true/false if the parameter is of return type + * + * @return isReturnValue + */ + boolean isReturnValue() { + return isReturnValue; + } + + /** + * Sets the parameter to be of return type + * + * @param isReturnValue + */ + void setReturnValue(boolean isReturnValue) { + this.isReturnValue = isReturnValue; + } + + /** + * Sets the name of the parameter + * + * @param name + */ + void setName(String name) { + this.name = name; + } + + /** + * Retrieve the name of the parameter + * + * @return + */ + String getName() { + return this.name; + } + + /** + * Returns the `registeredOutDTV` instance of the parameter + * + * @return registeredOutDTV + */ + DTV getRegisteredOutDTV() { + return this.registeredOutDTV; } /** @@ -87,6 +128,13 @@ DTV getInputDTV() { return this.inputDTV; } + // Since a parameter can have only one type definition for both sending its value to the server (IN) + // and getting its value from the server (OUT), we use the JDBC type of the IN parameter value if there + // is one; otherwise we use the registered OUT param JDBC type. + JDBCType getJdbcType() { + return (null != inputDTV) ? inputDTV.getJdbcType() : JDBCType.UNKNOWN; + } + /** * Used when sendStringParametersAsUnicode=true to derive the appropriate National Character Set JDBC type * corresponding to the specified JDBC type. @@ -256,7 +304,7 @@ void setFromReturnStatus(int returnStatus, SQLServerConnection con) throws SQLSe if (null == getterDTV) getterDTV = new DTV(); - getterDTV.setValue(null, JDBCType.INTEGER, returnStatus, JavaType.INTEGER, null, null, null, con, + getterDTV.setValue(null, this.getJdbcType(), returnStatus, JavaType.INTEGER, null, null, null, con, getForceEncryption()); } @@ -397,10 +445,14 @@ boolean isValueGotten() { Object getValue(JDBCType jdbcType, InputStreamGetterArgs getterArgs, Calendar cal, TDSReader tdsReader, SQLServerStatement statement) throws SQLServerException { - if (null == getterDTV) + if (null == getterDTV) { getterDTV = new DTV(); + } + + if (null != tdsReader) { + deriveTypeInfo(tdsReader); + } - deriveTypeInfo(tdsReader); // If the parameter is not encrypted or column encryption is turned off (either at connection or // statement level), cryptoMeta would be null. return getterDTV.getValue(jdbcType, outScale, getterArgs, cal, typeInfo, cryptoMeta, tdsReader, statement); @@ -1220,15 +1272,17 @@ String getTypeDefinition(SQLServerConnection con, TDSReader tdsReader) throws SQ return typeDefinition; } - void sendByRPC(TDSWriter tdsWriter, SQLServerStatement statement) throws SQLServerException { + void sendByRPC(TDSWriter tdsWriter, boolean callRPCDirectly, + SQLServerStatement statement) throws SQLServerException { assert null != inputDTV : "Parameter was neither set nor registered"; SQLServerConnection conn = statement.connection; try { + inputDTV.isNonPLP = isNonPLP; inputDTV.sendCryptoMetaData(this.cryptoMeta, tdsWriter); inputDTV.setJdbcTypeSetByUser(getJdbcTypeSetByUser(), getValueLength()); - inputDTV.sendByRPC(name, null, conn.getDatabaseCollation(), valueLength, isOutput() ? outScale : scale, - isOutput(), tdsWriter, statement); + inputDTV.sendByRPC(callRPCDirectly ? name : null, null, conn.getDatabaseCollation(), valueLength, + isOutput() ? outScale : scale, isOutput(), tdsWriter, statement); } finally { // reset the cryptoMeta in IOBuffer inputDTV.sendCryptoMetaData(null, tdsWriter); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java index 35f239608..74619a893 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java @@ -2129,7 +2129,8 @@ private void writeNullToTdsWriter(TDSWriter tdsWriter, int srcJdbcType, private void writeColumnToTdsWriter(TDSWriter tdsWriter, int bulkPrecision, int bulkScale, int bulkJdbcType, boolean bulkNullable, // should it be destNullable instead? - int srcColOrdinal, int destColOrdinal, boolean isStreaming, Object colValue, Calendar cal) throws SQLServerException { + int srcColOrdinal, int destColOrdinal, boolean isStreaming, Object colValue, + Calendar cal) throws SQLServerException { SSType destSSType = destColumnMetadata.get(destColOrdinal).ssType; bulkPrecision = validateSourcePrecision(bulkPrecision, bulkJdbcType, @@ -3046,8 +3047,8 @@ private Object readColumnFromResultSet(int srcColOrdinal, int srcJdbcType, boole /** * Reads the given column from the result set current row and writes the data to tdsWriter. */ - private void writeColumn(TDSWriter tdsWriter, int srcColOrdinal, int destColOrdinal, - Object colValue, Calendar cal) throws SQLServerException { + private void writeColumn(TDSWriter tdsWriter, int srcColOrdinal, int destColOrdinal, Object colValue, + Calendar cal) throws SQLServerException { String destName = destColumnMetadata.get(destColOrdinal).columnName; int srcPrecision, srcScale, destPrecision, srcJdbcType; SSType destSSType = null; @@ -3699,8 +3700,8 @@ private boolean writeBatchData(TDSWriter tdsWriter, TDSCommand command, // Loop for each destination column. The mappings is a many to one mapping // where multiple source columns can be mapped to one destination column. for (ColumnMapping columnMapping : columnMappings) { - writeColumn(tdsWriter, columnMapping.sourceColumnOrdinal, columnMapping.destinationColumnOrdinal, null, - null // cell + writeColumn(tdsWriter, columnMapping.sourceColumnOrdinal, columnMapping.destinationColumnOrdinal, + null, null // cell // value is // retrieved // inside diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java index e6e42ada6..270df7ee7 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java @@ -73,12 +73,25 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple /** Currently active Stream Note only one stream can be active at a time */ private transient Closeable activeStream; + /** Checks if return values is already accessed in stored procedure */ + private boolean isReturnValueAccessed = false; + /** map */ private Map map = new ConcurrentHashMap<>(); /** atomic integer */ AtomicInteger ai = new AtomicInteger(0); + /** + * Enum to check if the callablestatement is a setter or getter method. This enum is used in method findColumn where + * we get the parameter/column by name + * + */ + enum CallableStatementGetterSetterMethod { + IS_SETTER_METHOD, + IS_GETTER_METHOD + } + /** * Create a new callable statement. * @@ -97,10 +110,31 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple SQLServerCallableStatement(SQLServerConnection connection, String sql, int nRSType, int nRSConcur, SQLServerStatementColumnEncryptionSetting stmtColEncSetting) throws SQLServerException { super(connection, sql, nRSType, nRSConcur, stmtColEncSetting); + isReturnValueAccessed = false; } @Override public void registerOutParameter(int index, int sqlType) throws SQLServerException { + // Register output parameter by index + isSetByIndex = true; + if (!connection.getUseFlexibleCallableStatements() && isSetByName && isSetByIndex) { + SQLServerException.makeFromDriverError(connection, this, + SQLServerException.getErrString("R_noNamedAndIndexedParameters"), null, false); + } + registerOutputParameter(index, sqlType); + } + + private void registerOutParameterByName(int index, int sqlType) throws SQLServerException { + // Register output parameter by name -- findColumn() sets the 'setByName' flag + registerOutputParameter(index, sqlType); + } + + void registerOutParameterNonPLP(int index, int sqlType) throws SQLServerException { + registerOutParameter(index, sqlType); + inOutParam[index - 1].isNonPLP = true; + } + + private void registerOutputParameter(int index, int sqlType) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {index, sqlType}); checkClosed(); @@ -169,6 +203,20 @@ private Parameter getOutParameter(int i) throws SQLServerException { // if this item has been indexed already leave! if (inOutParam[i - 1] == lastParamAccessed || inOutParam[i - 1].isValueGotten()) { + // if it is a return value, increment the nOutParamsAssigned. Checking for isCursorable here is because the + // driver is executing + // the stored procedure for cursorable ones differently ( calling sp_cursorexecute r sp_cursorprepexec. + if (bReturnValueSyntax && inOutParam[i - 1].isValueGotten() && inOutParam[i - 1].isReturnValue() + && !isReturnValueAccessed && !isCursorable(executeMethod) && !isTVPType + && callRPCDirectly(inOutParam)) { + nOutParamsAssigned++; + isReturnValueAccessed = true; + } + return inOutParam[i - 1]; + } + + if (inOutParam[i - 1].isReturnValue() && bReturnValueSyntax && !isCursorable(executeMethod) && !isTVPType + && returnValueStatus != USER_DEFINED_FUNCTION_RETURN_STATUS) { return inOutParam[i - 1]; } @@ -227,18 +275,24 @@ final void processOutParameters() throws SQLServerException { } // Next, if there are any unindexed parameters left then discard them too. - assert nOutParamsAssigned <= nOutParams; - if (nOutParamsAssigned < nOutParams) - skipOutParameters(nOutParams - nOutParamsAssigned, true); - - // Finally, skip the last-indexed parameter. If there were no unindexed parameters - // in the previous step, then this is the last-indexed parameter left from the first - // step. If we skipped unindexed parameters in the previous step, then this is the - // last-indexed parameter left at the end of that step. - if (outParamIndex >= 0) { - inOutParam[outParamIndex].skipValue(resultsReader(), true); - inOutParam[outParamIndex].resetOutputValue(); - outParamIndex = -1; + if (nOutParamsAssigned <= nOutParams) { + if (bReturnValueSyntax && (nOutParamsAssigned == 0) && !isCursorable(executeMethod)) { + nOutParamsAssigned++; + } + + if (nOutParamsAssigned < nOutParams) { + skipOutParameters(nOutParams - nOutParamsAssigned, true); + } + + // Finally, skip the last-indexed parameter. If there were no unindexed parameters + // in the previous step, then this is the last-indexed parameter left from the first + // step. If we skipped unindexed parameters in the previous step, then this is the + // last-indexed parameter left at the end of that step. + if (outParamIndex >= 0) { + inOutParam[outParamIndex].skipValue(resultsReader(), true); + inOutParam[outParamIndex].resetOutputValue(); + outParamIndex = -1; + } } } @@ -309,60 +363,84 @@ boolean onRetValue(TDSReader tdsReader) throws SQLServerException { OutParamHandler outParamHandler = new OutParamHandler(); + if (bReturnValueSyntax && (nOutParamsAssigned == 0) && !isCursorable(executeMethod) && !isTVPType + && callRPCDirectly(inOutParam) && returnValueStatus != USER_DEFINED_FUNCTION_RETURN_STATUS) { + nOutParamsAssigned++; + } + // Index the application OUT parameters - assert numParamsToSkip <= nOutParams - nOutParamsAssigned; - for (int paramsSkipped = 0; paramsSkipped < numParamsToSkip; ++paramsSkipped) { - // Discard the last-indexed parameter by skipping over it and - // discarding the value if it is no longer needed. - if (-1 != outParamIndex) { - inOutParam[outParamIndex].skipValue(resultsReader(), discardValues); - if (discardValues) - inOutParam[outParamIndex].resetOutputValue(); - } + if (numParamsToSkip <= nOutParams - nOutParamsAssigned) { + for (int paramsSkipped = 0; paramsSkipped < numParamsToSkip; ++paramsSkipped) { + // Discard the last-indexed parameter by skipping over it and + // discarding the value if it is no longer needed. + if (-1 != outParamIndex) { + inOutParam[outParamIndex].skipValue(resultsReader(), discardValues); + if (discardValues) { + inOutParam[outParamIndex].resetOutputValue(); + } + } - // Look for the next parameter value in the response. - outParamHandler.reset(); - TDSParser.parse(resultsReader(), outParamHandler); - - // If we don't find it, then most likely the server encountered some error that - // was bad enough to halt statement execution before returning OUT params, but - // not necessarily bad enough to close the connection. - if (!outParamHandler.foundParam()) { - // If we were just going to discard the OUT parameters we found anyway, - // then it's no problem that we didn't find any of them. For exmaple, - // when we are closing or reexecuting this CallableStatement (that is, - // calling in through processResponse), we don't care that execution - // failed to return the OUT parameters. - if (discardValues) - break; - - // If we were asked to retain the OUT parameters as we skip past them, - // then report an error if we did not find any. - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_valueNotSetForParameter")); - Object[] msgArgs = {outParamIndex + 1}; - SQLServerException.makeFromDriverError(connection, this, form.format(msgArgs), null, false); - } + // Look for the next parameter value in the response. + outParamHandler.reset(); + TDSParser.parse(resultsReader(), outParamHandler); + + // If we don't find it, then most likely the server encountered some error that + // was bad enough to halt statement execution before returning OUT params, but + // not necessarily bad enough to close the connection. + if (!outParamHandler.foundParam()) { + // If we were just going to discard the OUT parameters we found anyway, + // then it's no problem that we didn't find any of them. For exmaple, + // when we are closing or reexecuting this CallableStatement (that is, + // calling in through processResponse), we don't care that execution + // failed to return the OUT parameters. + if (discardValues) + break; + + // If we were asked to retain the OUT parameters as we skip past them, + // then report an error if we did not find any. + MessageFormat form = new MessageFormat( + SQLServerException.getErrString("R_valueNotSetForParameter")); + Object[] msgArgs = {outParamIndex + 1}; + SQLServerException.makeFromDriverError(connection, this, form.format(msgArgs), null, false); + } + + // In Yukon and later, large Object output parameters are reordered to appear at + // the end of the stream. First group of small parameters is sent, followed by + // group of large output parameters. There is no reordering within the groups. + + // Note that parameter ordinals are 0-indexed and that the return status is not + // considered to be an output parameter. + outParamIndex = outParamHandler.srv.getOrdinalOrLength(); - // In Yukon and later, large Object output parameters are reordered to appear at - // the end of the stream. First group of small parameters is sent, followed by - // group of large output parameters. There is no reordering within the groups. - - // Note that parameter ordinals are 0-indexed and that the return status is not - // considered to be an output parameter. - outParamIndex = outParamHandler.srv.getOrdinalOrLength(); - - // Statements need to have their out param indices adjusted by the number - // of sp_[cursor][prep]exec params. - outParamIndex -= outParamIndexAdjustment; - if ((outParamIndex < 0 || outParamIndex >= inOutParam.length) || (!inOutParam[outParamIndex].isOutput())) { - if (getStatementLogger().isLoggable(java.util.logging.Level.INFO)) { - getStatementLogger().info(toString() + " Unexpected outParamIndex: " + outParamIndex - + "; adjustment: " + outParamIndexAdjustment); + if (bReturnValueSyntax && !isCursorable(executeMethod) && !isTVPType && callRPCDirectly(inOutParam) + && returnValueStatus != USER_DEFINED_FUNCTION_RETURN_STATUS) { + outParamIndex++; + } else { + // Statements need to have their out param indices adjusted by the number + // of sp_[cursor][prep]exec params. + outParamIndex -= outParamIndexAdjustment; + } + + if ((outParamIndex < 0 || outParamIndex >= inOutParam.length) + || (!inOutParam[outParamIndex].isOutput())) { + + // For RPC calls with out parameters, the initial return value token will indicate + // it being a RPC. In such case, consume the token as it does not contain the out parameter + // value. The subsequent token will have the value. + if (outParamHandler.srv.getStatus() == USER_DEFINED_FUNCTION_RETURN_STATUS) { + continue; + } + + if (getStatementLogger().isLoggable(java.util.logging.Level.INFO)) { + getStatementLogger().info(toString() + " Unexpected outParamIndex: " + outParamIndex + + "; adjustment: " + outParamIndexAdjustment); + } + + connection.throwInvalidTDS(); } - connection.throwInvalidTDS(); - } - ++nOutParamsAssigned; + ++nOutParamsAssigned; + } } } @@ -415,6 +493,10 @@ private Parameter getterGetParam(int index) throws SQLServerException { // Check for valid index if (index < 1 || index > inOutParam.length) { + if (!connection.getUseFlexibleCallableStatements()) { + SQLServerException.makeFromDriverError(connection, this, + SQLServerException.getErrString("R_unknownOutputParameter"), SQLSTATE_07009, false); + } MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidOutputParameter")); Object[] msgArgs = {index}; SQLServerException.makeFromDriverError(connection, this, form.format(msgArgs), SQLSTATE_07009, false); @@ -445,7 +527,13 @@ private Parameter getterGetParam(int index) throws SQLServerException { } private Object getValue(int parameterIndex, JDBCType jdbcType) throws SQLServerException { - return getterGetParam(parameterIndex).getValue(jdbcType, null, null, resultsReader(), this); + Parameter param = getterGetParam(parameterIndex); + if (!param.isValueGotten() || !param.isReturnValue()) { + return param.getValue(jdbcType, null, null, resultsReader(), this); + } else { + // if we have already retrieved the value, we have the typeInfo and we do not need to get it again + return param.getValue(param.getJdbcType(), null, null, null, this); + } } private Object getValue(int parameterIndex, JDBCType jdbcType, Calendar cal) throws SQLServerException { @@ -477,6 +565,7 @@ private Object getSQLXMLInternal(int parameterIndex) throws SQLServerException { @Override public int getInt(int index) throws SQLServerException { + setByIndex(); loggerExternal.entering(getClassNameLogging(), "getInt", index); checkClosed(); Integer value = (Integer) getValue(index, JDBCType.INTEGER); @@ -488,13 +577,15 @@ public int getInt(int index) throws SQLServerException { public int getInt(String parameterName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getInt", parameterName); checkClosed(); - Integer value = (Integer) getValue(findColumn(parameterName), JDBCType.INTEGER); + Integer value = (Integer) getValue( + findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD), JDBCType.INTEGER); loggerExternal.exiting(getClassNameLogging(), "getInt", value); return null != value ? value : 0; } @Override public String getString(int index) throws SQLServerException { + setByIndex(); loggerExternal.entering(getClassNameLogging(), "getString", index); checkClosed(); String value = null; @@ -511,7 +602,8 @@ public String getString(String parameterName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getString", parameterName); checkClosed(); String value = null; - Object objectValue = getValue(findColumn(parameterName), JDBCType.CHAR); + Object objectValue = getValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD), + JDBCType.CHAR); if (null != objectValue) { value = objectValue.toString(); } @@ -521,6 +613,7 @@ public String getString(String parameterName) throws SQLServerException { @Override public final String getNString(int parameterIndex) throws SQLException { + setByIndex(); loggerExternal.entering(getClassNameLogging(), "getNString", parameterIndex); checkClosed(); String value = (String) getValue(parameterIndex, JDBCType.NCHAR); @@ -532,7 +625,8 @@ public final String getNString(int parameterIndex) throws SQLException { public final String getNString(String parameterName) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getNString", parameterName); checkClosed(); - String value = (String) getValue(findColumn(parameterName), JDBCType.NCHAR); + String value = (String) getValue( + findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD), JDBCType.NCHAR); loggerExternal.exiting(getClassNameLogging(), "getNString", value); return value; } @@ -543,6 +637,7 @@ public final String getNString(String parameterName) throws SQLException { @Deprecated(since = "6.5.4") @Override public BigDecimal getBigDecimal(int parameterIndex, int scale) throws SQLException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "getBigDecimal", new Object[] {parameterIndex, scale}); checkClosed(); @@ -562,7 +657,8 @@ public BigDecimal getBigDecimal(String parameterName, int scale) throws SQLServe if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "getBigDecimal", new Object[] {parameterName, scale}); checkClosed(); - BigDecimal value = (BigDecimal) getValue(findColumn(parameterName), JDBCType.DECIMAL); + BigDecimal value = (BigDecimal) getValue( + findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD), JDBCType.DECIMAL); if (null != value) value = value.setScale(scale, BigDecimal.ROUND_DOWN); loggerExternal.exiting(getClassNameLogging(), "getBigDecimal", value); @@ -571,6 +667,7 @@ public BigDecimal getBigDecimal(String parameterName, int scale) throws SQLServe @Override public boolean getBoolean(int index) throws SQLServerException { + setByIndex(); loggerExternal.entering(getClassNameLogging(), "getBoolean", index); checkClosed(); Boolean value = (Boolean) getValue(index, JDBCType.BIT); @@ -582,13 +679,15 @@ public boolean getBoolean(int index) throws SQLServerException { public boolean getBoolean(String parameterName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getBoolean", parameterName); checkClosed(); - Boolean value = (Boolean) getValue(findColumn(parameterName), JDBCType.BIT); + Boolean value = (Boolean) getValue( + findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD), JDBCType.BIT); loggerExternal.exiting(getClassNameLogging(), "getBoolean", value); return null != value ? value : false; } @Override public byte getByte(int index) throws SQLServerException { + setByIndex(); loggerExternal.entering(getClassNameLogging(), "getByte", index); checkClosed(); Short shortValue = (Short) getValue(index, JDBCType.TINYINT); @@ -601,7 +700,8 @@ public byte getByte(int index) throws SQLServerException { public byte getByte(String parameterName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getByte", parameterName); checkClosed(); - Short shortValue = (Short) getValue(findColumn(parameterName), JDBCType.TINYINT); + Short shortValue = (Short) getValue( + findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD), JDBCType.TINYINT); byte byteValue = (null != shortValue) ? shortValue.byteValue() : 0; loggerExternal.exiting(getClassNameLogging(), "getByte", byteValue); return byteValue; @@ -609,6 +709,7 @@ public byte getByte(String parameterName) throws SQLServerException { @Override public byte[] getBytes(int index) throws SQLServerException { + setByIndex(); loggerExternal.entering(getClassNameLogging(), "getBytes", index); checkClosed(); byte[] value = (byte[]) getValue(index, JDBCType.BINARY); @@ -620,13 +721,15 @@ public byte[] getBytes(int index) throws SQLServerException { public byte[] getBytes(String parameterName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getBytes", parameterName); checkClosed(); - byte[] value = (byte[]) getValue(findColumn(parameterName), JDBCType.BINARY); + byte[] value = (byte[]) getValue( + findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD), JDBCType.BINARY); loggerExternal.exiting(getClassNameLogging(), "getBytes", value); return value; } @Override public Date getDate(int index) throws SQLServerException { + setByIndex(); loggerExternal.entering(getClassNameLogging(), "getDate", index); checkClosed(); java.sql.Date value = (java.sql.Date) getValue(index, JDBCType.DATE); @@ -638,13 +741,15 @@ public Date getDate(int index) throws SQLServerException { public Date getDate(String parameterName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getDate", parameterName); checkClosed(); - java.sql.Date value = (java.sql.Date) getValue(findColumn(parameterName), JDBCType.DATE); + java.sql.Date value = (java.sql.Date) getValue( + findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD), JDBCType.DATE); loggerExternal.exiting(getClassNameLogging(), "getDate", value); return value; } @Override public Date getDate(int index, Calendar cal) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "getDate", new Object[] {index, cal}); checkClosed(); @@ -658,13 +763,15 @@ public Date getDate(String parameterName, Calendar cal) throws SQLServerExceptio if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "getDate", new Object[] {parameterName, cal}); checkClosed(); - java.sql.Date value = (java.sql.Date) getValue(findColumn(parameterName), JDBCType.DATE, cal); + java.sql.Date value = (java.sql.Date) getValue( + findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD), JDBCType.DATE); loggerExternal.exiting(getClassNameLogging(), "getDate", value); return value; } @Override public double getDouble(int index) throws SQLServerException { + setByIndex(); loggerExternal.entering(getClassNameLogging(), "getDouble", index); checkClosed(); Double value = (Double) getValue(index, JDBCType.DOUBLE); @@ -676,13 +783,15 @@ public double getDouble(int index) throws SQLServerException { public double getDouble(String parameterName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getDouble", parameterName); checkClosed(); - Double value = (Double) getValue(findColumn(parameterName), JDBCType.DOUBLE); + Double value = (Double) getValue( + findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD), JDBCType.DOUBLE); loggerExternal.exiting(getClassNameLogging(), "getDouble", value); return null != value ? value : 0; } @Override public float getFloat(int index) throws SQLServerException { + setByIndex(); loggerExternal.entering(getClassNameLogging(), "getFloat", index); checkClosed(); Float value = (Float) getValue(index, JDBCType.REAL); @@ -692,17 +801,17 @@ public float getFloat(int index) throws SQLServerException { @Override public float getFloat(String parameterName) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "getFloat", parameterName); checkClosed(); - Float value = (Float) getValue(findColumn(parameterName), JDBCType.REAL); + Float value = (Float) getValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD), + JDBCType.REAL); loggerExternal.exiting(getClassNameLogging(), "getFloat", value); return null != value ? value : 0; } @Override public long getLong(int index) throws SQLServerException { - + setByIndex(); loggerExternal.entering(getClassNameLogging(), "getLong", index); checkClosed(); Long value = (Long) getValue(index, JDBCType.BIGINT); @@ -714,14 +823,15 @@ public long getLong(int index) throws SQLServerException { public long getLong(String parameterName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getLong", parameterName); checkClosed(); - Long value = (Long) getValue(findColumn(parameterName), JDBCType.BIGINT); + Long value = (Long) getValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD), + JDBCType.BIGINT); loggerExternal.exiting(getClassNameLogging(), "getLong", value); return null != value ? value : 0; } @Override public Object getObject(int index) throws SQLServerException { - + setByIndex(); loggerExternal.entering(getClassNameLogging(), "getObject", index); checkClosed(); Object value = getValue(index, @@ -733,6 +843,7 @@ public Object getObject(int index) throws SQLServerException { @Override public T getObject(int index, Class type) throws SQLException { + setByIndex(); loggerExternal.entering(getClassNameLogging(), "getObject", index); checkClosed(); Object returnValue; @@ -827,7 +938,7 @@ public T getObject(int index, Class type) throws SQLException { public Object getObject(String parameterName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getObject", parameterName); checkClosed(); - int parameterIndex = findColumn(parameterName); + int parameterIndex = findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD); Object value = getValue(parameterIndex, null != getterGetParam(parameterIndex).getJdbcTypeSetByUser() ? getterGetParam(parameterIndex) .getJdbcTypeSetByUser() : getterGetParam(parameterIndex).getJdbcType()); @@ -839,7 +950,7 @@ public Object getObject(String parameterName) throws SQLServerException { public T getObject(String parameterName, Class type) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getObject", parameterName); checkClosed(); - int parameterIndex = findColumn(parameterName); + int parameterIndex = findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD); T value = getObject(parameterIndex, type); loggerExternal.exiting(getClassNameLogging(), "getObject", value); return value; @@ -847,6 +958,7 @@ public T getObject(String parameterName, Class type) throws SQLException @Override public short getShort(int index) throws SQLServerException { + setByIndex(); loggerExternal.entering(getClassNameLogging(), "getShort", index); checkClosed(); Short value = (Short) getValue(index, JDBCType.SMALLINT); @@ -858,14 +970,15 @@ public short getShort(int index) throws SQLServerException { public short getShort(String parameterName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getShort", parameterName); checkClosed(); - Short value = (Short) getValue(findColumn(parameterName), JDBCType.SMALLINT); + Short value = (Short) getValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD), + JDBCType.SMALLINT); loggerExternal.exiting(getClassNameLogging(), "getShort", value); return null != value ? value : 0; } @Override public Time getTime(int index) throws SQLServerException { - + setByIndex(); loggerExternal.entering(getClassNameLogging(), "getTime", index); checkClosed(); java.sql.Time value = (java.sql.Time) getValue(index, JDBCType.TIME); @@ -877,13 +990,15 @@ public Time getTime(int index) throws SQLServerException { public Time getTime(String parameterName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getTime", parameterName); checkClosed(); - java.sql.Time value = (java.sql.Time) getValue(findColumn(parameterName), JDBCType.TIME); + java.sql.Time value = (java.sql.Time) getValue( + findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD), JDBCType.TIME); loggerExternal.exiting(getClassNameLogging(), "getTime", value); return value; } @Override public Time getTime(int index, Calendar cal) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "getTime", new Object[] {index, cal}); checkClosed(); @@ -897,13 +1012,15 @@ public Time getTime(String parameterName, Calendar cal) throws SQLServerExceptio if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "getTime", new Object[] {parameterName, cal}); checkClosed(); - java.sql.Time value = (java.sql.Time) getValue(findColumn(parameterName), JDBCType.TIME, cal); + java.sql.Time value = (java.sql.Time) getValue( + findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD), JDBCType.TIME, cal); loggerExternal.exiting(getClassNameLogging(), "getTime", value); return value; } @Override public Timestamp getTimestamp(int index) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), GET_TIMESTAMP, index); checkClosed(); @@ -916,13 +1033,15 @@ public Timestamp getTimestamp(int index) throws SQLServerException { public Timestamp getTimestamp(String parameterName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), GET_TIMESTAMP, parameterName); checkClosed(); - java.sql.Timestamp value = (java.sql.Timestamp) getValue(findColumn(parameterName), JDBCType.TIMESTAMP); + java.sql.Timestamp value = (java.sql.Timestamp) getValue( + findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD), JDBCType.TIMESTAMP); loggerExternal.exiting(getClassNameLogging(), GET_TIMESTAMP, value); return value; } @Override public Timestamp getTimestamp(int index, Calendar cal) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), GET_TIMESTAMP, new Object[] {index, cal}); checkClosed(); @@ -932,16 +1051,19 @@ public Timestamp getTimestamp(int index, Calendar cal) throws SQLServerException } @Override - public Timestamp getTimestamp(String name, Calendar cal) throws SQLServerException { + public Timestamp getTimestamp(String parameterName, Calendar cal) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), GET_TIMESTAMP, new Object[] {name, cal}); + loggerExternal.entering(getClassNameLogging(), GET_TIMESTAMP, new Object[] {parameterName, cal}); checkClosed(); - java.sql.Timestamp value = (java.sql.Timestamp) getValue(findColumn(name), JDBCType.TIMESTAMP, cal); + java.sql.Timestamp value = (java.sql.Timestamp) getValue( + findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD), JDBCType.TIMESTAMP, + cal); loggerExternal.exiting(getClassNameLogging(), GET_TIMESTAMP, value); return value; } LocalDateTime getLocalDateTime(int columnIndex) throws SQLServerException { + setByIndex(); loggerExternal.entering(getClassNameLogging(), "getLocalDateTime", columnIndex); checkClosed(); LocalDateTime value = (LocalDateTime) getValue(columnIndex, JDBCType.LOCALDATETIME); @@ -951,6 +1073,7 @@ LocalDateTime getLocalDateTime(int columnIndex) throws SQLServerException { @Override public Timestamp getDateTime(int index) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "getDateTime", index); checkClosed(); @@ -963,13 +1086,15 @@ public Timestamp getDateTime(int index) throws SQLServerException { public Timestamp getDateTime(String parameterName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getDateTime", parameterName); checkClosed(); - java.sql.Timestamp value = (java.sql.Timestamp) getValue(findColumn(parameterName), JDBCType.DATETIME); + java.sql.Timestamp value = (java.sql.Timestamp) getValue( + findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD), JDBCType.DATETIME); loggerExternal.exiting(getClassNameLogging(), "getDateTime", value); return value; } @Override public Timestamp getDateTime(int index, Calendar cal) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "getDateTime", new Object[] {index, cal}); checkClosed(); @@ -979,17 +1104,20 @@ public Timestamp getDateTime(int index, Calendar cal) throws SQLServerException } @Override - public Timestamp getDateTime(String name, Calendar cal) throws SQLServerException { + public Timestamp getDateTime(String parameterName, Calendar cal) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "getDateTime", new Object[] {name, cal}); + loggerExternal.entering(getClassNameLogging(), "getDateTime", new Object[] {parameterName, cal}); checkClosed(); - java.sql.Timestamp value = (java.sql.Timestamp) getValue(findColumn(name), JDBCType.DATETIME, cal); + java.sql.Timestamp value = (java.sql.Timestamp) getValue( + findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD), JDBCType.DATETIME, + cal); loggerExternal.exiting(getClassNameLogging(), "getDateTime", value); return value; } @Override public Timestamp getSmallDateTime(int index) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "getSmallDateTime", index); checkClosed(); @@ -1002,13 +1130,16 @@ public Timestamp getSmallDateTime(int index) throws SQLServerException { public Timestamp getSmallDateTime(String parameterName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getSmallDateTime", parameterName); checkClosed(); - java.sql.Timestamp value = (java.sql.Timestamp) getValue(findColumn(parameterName), JDBCType.SMALLDATETIME); + java.sql.Timestamp value = (java.sql.Timestamp) getValue( + findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD), + JDBCType.SMALLDATETIME); loggerExternal.exiting(getClassNameLogging(), "getSmallDateTime", value); return value; } @Override public Timestamp getSmallDateTime(int index, Calendar cal) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "getSmallDateTime", new Object[] {index, cal}); checkClosed(); @@ -1018,17 +1149,20 @@ public Timestamp getSmallDateTime(int index, Calendar cal) throws SQLServerExcep } @Override - public Timestamp getSmallDateTime(String name, Calendar cal) throws SQLServerException { + public Timestamp getSmallDateTime(String parameterName, Calendar cal) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "getSmallDateTime", new Object[] {name, cal}); + loggerExternal.entering(getClassNameLogging(), "getSmallDateTime", new Object[] {parameterName, cal}); checkClosed(); - java.sql.Timestamp value = (java.sql.Timestamp) getValue(findColumn(name), JDBCType.SMALLDATETIME, cal); + java.sql.Timestamp value = (java.sql.Timestamp) getValue( + findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD), JDBCType.SMALLDATETIME, + cal); loggerExternal.exiting(getClassNameLogging(), "getSmallDateTime", value); return value; } @Override public microsoft.sql.DateTimeOffset getDateTimeOffset(int index) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "getDateTimeOffset", index); checkClosed(); @@ -1053,7 +1187,8 @@ public microsoft.sql.DateTimeOffset getDateTimeOffset(String parameterName) thro throw new SQLServerException(SQLServerException.getErrString("R_notSupported"), SQLState.DATA_EXCEPTION_NOT_SPECIFIC, DriverError.NOT_SET, null); - microsoft.sql.DateTimeOffset value = (microsoft.sql.DateTimeOffset) getValue(findColumn(parameterName), + microsoft.sql.DateTimeOffset value = (microsoft.sql.DateTimeOffset) getValue( + findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD), JDBCType.DATETIMEOFFSET); loggerExternal.exiting(getClassNameLogging(), "getDateTimeOffset", value); return value; @@ -1073,6 +1208,7 @@ public boolean wasNull() throws SQLServerException { @Override public final java.io.InputStream getAsciiStream(int parameterIndex) throws SQLServerException { + setByIndex(); loggerExternal.entering(getClassNameLogging(), "getAsciiStream", parameterIndex); checkClosed(); InputStream value = (InputStream) getStream(parameterIndex, StreamType.ASCII); @@ -1084,13 +1220,15 @@ public final java.io.InputStream getAsciiStream(int parameterIndex) throws SQLSe public final java.io.InputStream getAsciiStream(String parameterName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getAsciiStream", parameterName); checkClosed(); - InputStream value = (InputStream) getStream(findColumn(parameterName), StreamType.ASCII); + InputStream value = (InputStream) getStream( + findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD), StreamType.ASCII); loggerExternal.exiting(getClassNameLogging(), "getAsciiStream", value); return value; } @Override public BigDecimal getBigDecimal(int parameterIndex) throws SQLServerException { + setByIndex(); loggerExternal.entering(getClassNameLogging(), "getBigDecimal", parameterIndex); checkClosed(); BigDecimal value = (BigDecimal) getValue(parameterIndex, JDBCType.DECIMAL); @@ -1102,13 +1240,15 @@ public BigDecimal getBigDecimal(int parameterIndex) throws SQLServerException { public BigDecimal getBigDecimal(String parameterName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getBigDecimal", parameterName); checkClosed(); - BigDecimal value = (BigDecimal) getValue(findColumn(parameterName), JDBCType.DECIMAL); + BigDecimal value = (BigDecimal) getValue( + findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD), JDBCType.DECIMAL); loggerExternal.exiting(getClassNameLogging(), "getBigDecimal", value); return value; } @Override public BigDecimal getMoney(int parameterIndex) throws SQLServerException { + setByIndex(); loggerExternal.entering(getClassNameLogging(), "getMoney", parameterIndex); checkClosed(); BigDecimal value = (BigDecimal) getValue(parameterIndex, JDBCType.MONEY); @@ -1120,13 +1260,15 @@ public BigDecimal getMoney(int parameterIndex) throws SQLServerException { public BigDecimal getMoney(String parameterName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getMoney", parameterName); checkClosed(); - BigDecimal value = (BigDecimal) getValue(findColumn(parameterName), JDBCType.MONEY); + BigDecimal value = (BigDecimal) getValue( + findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD), JDBCType.MONEY); loggerExternal.exiting(getClassNameLogging(), "getMoney", value); return value; } @Override public BigDecimal getSmallMoney(int parameterIndex) throws SQLServerException { + setByIndex(); loggerExternal.entering(getClassNameLogging(), "getSmallMoney", parameterIndex); checkClosed(); BigDecimal value = (BigDecimal) getValue(parameterIndex, JDBCType.SMALLMONEY); @@ -1138,13 +1280,15 @@ public BigDecimal getSmallMoney(int parameterIndex) throws SQLServerException { public BigDecimal getSmallMoney(String parameterName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getSmallMoney", parameterName); checkClosed(); - BigDecimal value = (BigDecimal) getValue(findColumn(parameterName), JDBCType.SMALLMONEY); + BigDecimal value = (BigDecimal) getValue( + findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD), JDBCType.SMALLMONEY); loggerExternal.exiting(getClassNameLogging(), "getSmallMoney", value); return value; } @Override public final java.io.InputStream getBinaryStream(int parameterIndex) throws SQLServerException { + setByIndex(); loggerExternal.entering(getClassNameLogging(), "getBinaryStream", parameterIndex); checkClosed(); InputStream value = (InputStream) getStream(parameterIndex, StreamType.BINARY); @@ -1156,13 +1300,15 @@ public final java.io.InputStream getBinaryStream(int parameterIndex) throws SQLS public final java.io.InputStream getBinaryStream(String parameterName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getBinaryStream", parameterName); checkClosed(); - InputStream value = (InputStream) getStream(findColumn(parameterName), StreamType.BINARY); + InputStream value = (InputStream) getStream( + findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD), StreamType.BINARY); loggerExternal.exiting(getClassNameLogging(), "getBinaryStream", value); return value; } @Override public Blob getBlob(int parameterIndex) throws SQLServerException { + setByIndex(); loggerExternal.entering(getClassNameLogging(), "getBlob", parameterIndex); checkClosed(); Blob value = (Blob) getValue(parameterIndex, JDBCType.BLOB); @@ -1174,13 +1320,15 @@ public Blob getBlob(int parameterIndex) throws SQLServerException { public Blob getBlob(String parameterName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getBlob", parameterName); checkClosed(); - Blob value = (Blob) getValue(findColumn(parameterName), JDBCType.BLOB); + Blob value = (Blob) getValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD), + JDBCType.BLOB); loggerExternal.exiting(getClassNameLogging(), "getBlob", value); return value; } @Override public final java.io.Reader getCharacterStream(int parameterIndex) throws SQLServerException { + setByIndex(); loggerExternal.entering(getClassNameLogging(), "getCharacterStream", parameterIndex); checkClosed(); Reader reader = (Reader) getStream(parameterIndex, StreamType.CHARACTER); @@ -1192,13 +1340,15 @@ public final java.io.Reader getCharacterStream(int parameterIndex) throws SQLSer public final java.io.Reader getCharacterStream(String parameterName) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getCharacterStream", parameterName); checkClosed(); - Reader reader = (Reader) getStream(findColumn(parameterName), StreamType.CHARACTER); + Reader reader = (Reader) getStream( + findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD), StreamType.CHARACTER); loggerExternal.exiting(getClassNameLogging(), "getCharacterSream", reader); return reader; } @Override public final java.io.Reader getNCharacterStream(int parameterIndex) throws SQLException { + setByIndex(); loggerExternal.entering(getClassNameLogging(), "getNCharacterStream", parameterIndex); checkClosed(); Reader reader = (Reader) getStream(parameterIndex, StreamType.NCHARACTER); @@ -1210,7 +1360,8 @@ public final java.io.Reader getNCharacterStream(int parameterIndex) throws SQLEx public final java.io.Reader getNCharacterStream(String parameterName) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getNCharacterStream", parameterName); checkClosed(); - Reader reader = (Reader) getStream(findColumn(parameterName), StreamType.NCHARACTER); + Reader reader = (Reader) getStream( + findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD), StreamType.NCHARACTER); loggerExternal.exiting(getClassNameLogging(), "getNCharacterStream", reader); return reader; } @@ -1229,6 +1380,7 @@ void closeActiveStream() throws SQLServerException { @Override public Clob getClob(int parameterIndex) throws SQLServerException { + setByIndex(); loggerExternal.entering(getClassNameLogging(), "getClob", parameterIndex); checkClosed(); Clob clob = (Clob) getValue(parameterIndex, JDBCType.CLOB); @@ -1240,13 +1392,15 @@ public Clob getClob(int parameterIndex) throws SQLServerException { public Clob getClob(String parameterName) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getClob", parameterName); checkClosed(); - Clob clob = (Clob) getValue(findColumn(parameterName), JDBCType.CLOB); + Clob clob = (Clob) getValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD), + JDBCType.CLOB); loggerExternal.exiting(getClassNameLogging(), "getClob", clob); return clob; } @Override public NClob getNClob(int parameterIndex) throws SQLException { + setByIndex(); loggerExternal.entering(getClassNameLogging(), "getNClob", parameterIndex); checkClosed(); NClob nClob = (NClob) getValue(parameterIndex, JDBCType.NCLOB); @@ -1258,7 +1412,8 @@ public NClob getNClob(int parameterIndex) throws SQLException { public NClob getNClob(String parameterName) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getNClob", parameterName); checkClosed(); - NClob nClob = (NClob) getValue(findColumn(parameterName), JDBCType.NCLOB); + NClob nClob = (NClob) getValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD), + JDBCType.NCLOB); loggerExternal.exiting(getClassNameLogging(), "getNClob", nClob); return nClob; } @@ -1272,7 +1427,7 @@ public Object getObject(int parameterIndex, java.util.Map> map) @Override public Object getObject(String parameterName, java.util.Map> m) throws SQLException { checkClosed(); - return getObject(findColumn(parameterName), m); + return getObject(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD), m); } @Override @@ -1284,7 +1439,7 @@ public Ref getRef(int parameterIndex) throws SQLException { @Override public Ref getRef(String parameterName) throws SQLException { checkClosed(); - return getRef(findColumn(parameterName)); + return getRef(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD)); } @Override @@ -1296,107 +1451,152 @@ public java.sql.Array getArray(int parameterIndex) throws SQLException { @Override public java.sql.Array getArray(String parameterName) throws SQLException { checkClosed(); - return getArray(findColumn(parameterName)); + return getArray(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD)); } - /* JDBC 3.0 */ + /** + * handle `@name` as well as `name`, since `@name` is what's returned by DatabaseMetaData#getProcedureColumns + * + * @param columnName + * @return + */ + private String stripLeadingAtSign(String columnName) { + return (columnName.startsWith("@") ? columnName.substring(1, columnName.length()) : columnName); + } + /* JDBC 3.0 */ /** * Find a column's index given its name. - * + * * @param columnName * the name * @throws SQLServerException * when an error occurs * @return the index */ - private int findColumn(String columnName) throws SQLServerException { - if (null == parameterNames) { - try (SQLServerStatement s = (SQLServerStatement) connection.createStatement()) { - // Note we are concatenating the information from the passed in sql, not any arguments provided by the - // user - // if the user can execute the sql, any fragments of it is potentially executed via the meta data call - // through injection - // is not a security issue. - - ThreePartName threePartName = ThreePartName.parse(procedureName); - StringBuilder metaQuery = new StringBuilder("exec sp_sproc_columns "); - if (null != threePartName.getDatabasePart()) { - metaQuery.append("@procedure_qualifier="); - metaQuery.append(threePartName.getDatabasePart()); - metaQuery.append(", "); - } + private int findColumn(String columnName, CallableStatementGetterSetterMethod method) throws SQLServerException { + isSetByName = true; + if (!connection.getUseFlexibleCallableStatements() && isSetByName && isSetByIndex) { + SQLServerException.makeFromDriverError(connection, this, + SQLServerException.getErrString("R_noNamedAndIndexedParameters"), null, false); + } - if (null != threePartName.getOwnerPart()) { - metaQuery.append("@procedure_owner="); - metaQuery.append(threePartName.getOwnerPart()); - metaQuery.append(", "); - } - if (null != threePartName.getProcedurePart()) { - // we should always have a procedure name part - metaQuery.append("@procedure_name="); - metaQuery.append(threePartName.getProcedurePart()); - metaQuery.append(" , @ODBCVer=3, @fUsePattern=0"); - } else { - // This should rarely happen, this will only happen if we can't find the stored procedure name - // invalidly formatted call syntax. - MessageFormat form = new MessageFormat( - SQLServerException.getErrString("R_parameterNotDefinedForProcedure")); - Object[] msgArgs = {columnName, ""}; - SQLServerException.makeFromDriverError(connection, this, form.format(msgArgs), SQLSTATE_07009, - false); - } + // If inOutParam is null, likely the statement was closed beforehand. + if (null == inOutParam) { + SQLServerException.makeFromDriverError(connection, this, + SQLServerException.getErrString("R_statementIsClosed"), null, false); + } - try (ResultSet rs = s.executeQueryInternal(metaQuery.toString())) { - parameterNames = new HashMap<>(); - insensitiveParameterNames = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - int columnIndex = 0; - while (rs.next()) { - String p = rs.getString(4).trim(); - parameterNames.put(p, columnIndex); - insensitiveParameterNames.put(p, columnIndex++); + if (connection.getUseFlexibleCallableStatements() || isCursorable(executeMethod)) { + // Stored procedures with cursorable methods are not called directly, so we have to get the metadata + if (parameterNames == null) { + try (SQLServerStatement s = (SQLServerStatement) connection.createStatement()) { + // Note we are concatenating the information from the passed in sql, not any arguments provided by the + // user + // if the user can execute the sql, any fragments of it is potentially executed via the meta data call + // through injection + // is not a security issue. + ThreePartName threePartName = ThreePartName.parse(procedureName); + StringBuilder metaQuery = new StringBuilder("exec sp_sproc_columns "); + if (null != threePartName.getDatabasePart()) { + metaQuery.append("@procedure_qualifier="); + metaQuery.append(threePartName.getDatabasePart()); + metaQuery.append(", "); + } + if (null != threePartName.getOwnerPart()) { + metaQuery.append("@procedure_owner="); + metaQuery.append(threePartName.getOwnerPart()); + metaQuery.append(", "); } + if (null != threePartName.getProcedurePart()) { + // we should always have a procedure name part + metaQuery.append("@procedure_name="); + metaQuery.append(threePartName.getProcedurePart()); + metaQuery.append(" , @ODBCVer=3, @fUsePattern=0"); + } else { + // This should rarely happen, this will only happen if we can't find the stored procedure name + // invalidly formatted call syntax. + MessageFormat form = new MessageFormat( + SQLServerException.getErrString("R_parameterNotDefinedForProcedure")); + Object[] msgArgs = {columnName, ""}; + SQLServerException.makeFromDriverError(connection, this, form.format(msgArgs), SQLSTATE_07009, + false); + } + + try (ResultSet rs = s.executeQueryInternal(metaQuery.toString())) { + parameterNames = new HashMap<>(); + insensitiveParameterNames = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + int columnIndex = 0; + while (rs.next()) { + String p = rs.getString(4).trim(); + parameterNames.put(p, columnIndex); + insensitiveParameterNames.put(p, columnIndex++); + } + } + } catch (SQLException e) { + SQLServerException.makeFromDriverError(connection, this, e.toString(), null, false); } - } catch (SQLException e) { - SQLServerException.makeFromDriverError(connection, this, e.toString(), null, false); } - } + // If the server didn't return anything (eg. the param names for the sp_sproc_columns), user might not + // have required permissions to view all the parameterNames. And, there's also the case depending on the permissions, + // @RETURN_VALUE may or may not be present. So, the parameterNames list might have an additional +1 parameter. + if (null != parameterNames && parameterNames.size() <= 1) { + return map.computeIfAbsent(columnName, ifAbsent -> ai.incrementAndGet()); + } - // If the server didn't return anything (eg. the param names for the sp_sproc_columns), user might not - // have required permissions to view all the parameterNames. And, there's also the case depending on the permissions, - // @RETURN_VALUE may or may not be present. So, the parameterNames list might have an additional +1 parameter. - if (null != parameterNames && parameterNames.size() <= 1) { - return map.computeIfAbsent(columnName, ifAbsent -> ai.incrementAndGet()); - } + // handle `@name` as well as `name`, since `@name` is what's returned + // by DatabaseMetaData#getProcedureColumns + String columnNameWithSign = columnName.startsWith("@") ? columnName : "@" + columnName; + + // In order to be as accurate as possible when locating parameter name + // indexes, as well as be deterministic when running on various client + // locales, we search for parameter names using the following scheme: - // handle `@name` as well as `name`, since `@name` is what's returned - // by DatabaseMetaData#getProcedureColumns - String columnNameWithSign = columnName.startsWith("@") ? columnName : "@" + columnName; + // 1. Search using case-sensitive non-locale specific (binary) compare first. + // 2. Search using case-insensitive, non-locale specific (binary) compare last. + int matchPos = (parameterNames != null && parameterNames.containsKey(columnNameWithSign)) ? parameterNames + .get(columnNameWithSign) : -1; + if (matchPos == -1 && insensitiveParameterNames.containsKey(columnNameWithSign)) { + matchPos = insensitiveParameterNames.get(columnNameWithSign); + } - // In order to be as accurate as possible when locating parameter name - // indexes, as well as be deterministic when running on various client - // locales, we search for parameter names using the following scheme: + if (matchPos == -1) { + MessageFormat form = new MessageFormat( + SQLServerException.getErrString("R_parameterNotDefinedForProcedure")); + Object[] msgArgs = {columnName, procedureName}; + SQLServerException.makeFromDriverError(connection, this, form.format(msgArgs), SQLSTATE_07009, false); + } - // 1. Search using case-sensitive non-locale specific (binary) compare first. - // 2. Search using case-insensitive, non-locale specific (binary) compare last. - Integer matchPos = (parameterNames != null) ? parameterNames.get(columnNameWithSign) : null; - if (null == matchPos) { - matchPos = insensitiveParameterNames.get(columnNameWithSign); + // @RETURN_VALUE is always in the list. If the user uses return value ?=call(@p1) syntax then + // @p1 is index 2 otherwise its index 1. + if (bReturnValueSyntax) // 3.2717 + return matchPos + 1; + else + return matchPos; } - if (null == matchPos) { - MessageFormat form = new MessageFormat( - SQLServerException.getErrString("R_parameterNotDefinedForProcedure")); - Object[] msgArgs = {columnName, procedureName}; - SQLServerException.makeFromDriverError(connection, this, form.format(msgArgs), SQLSTATE_07009, false); + + String columnNameWithoutAtSign = stripLeadingAtSign(columnName); + + for (int i = 0; i < inOutParam.length; i++) { + if (null != inOutParam[i].getName() && inOutParam[i].getName().equalsIgnoreCase(columnNameWithoutAtSign)) { + return i + 1; + } + } + + if (method == CallableStatementGetterSetterMethod.IS_SETTER_METHOD) { + for (int i = 0; i < inOutParam.length; i++) { + // if it is not already registered as output param or the parameter is not an input parameter, then + // set the param name and return index. + if (null == inOutParam[i].getName() && !inOutParam[i].isReturnValue() + && null == inOutParam[i].getInputDTV() && null == inOutParam[i].getRegisteredOutDTV()) { + inOutParam[i].setName(columnNameWithoutAtSign); + return i + 1; + } + } } - // @RETURN_VALUE is always in the list. If the user uses return value ?=call(@p1) syntax then - // @p1 is index 2 otherwise its index 1. - if (bReturnValueSyntax) // 3.2717 - return matchPos + 1; - else - return matchPos; + return -1; } @Override @@ -1406,7 +1606,8 @@ public void setTimestamp(String parameterName, java.sql.Timestamp value, loggerExternal.entering(getClassNameLogging(), "setTimeStamp", new Object[] {parameterName, value, calendar}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.TIMESTAMP, value, JavaType.TIMESTAMP, calendar, false); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.TIMESTAMP, + value, JavaType.TIMESTAMP, calendar, false); loggerExternal.exiting(getClassNameLogging(), "setTimeStamp"); } @@ -1417,7 +1618,8 @@ public void setTimestamp(String parameterName, java.sql.Timestamp value, Calenda loggerExternal.entering(getClassNameLogging(), "setTimeStamp", new Object[] {parameterName, value, calendar, forceEncrypt}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.TIMESTAMP, value, JavaType.TIMESTAMP, calendar, forceEncrypt); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.TIMESTAMP, + value, JavaType.TIMESTAMP, calendar, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setTimeStamp"); } @@ -1426,7 +1628,8 @@ public void setTime(String parameterName, java.sql.Time value, Calendar calendar if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setTime", new Object[] {parameterName, value, calendar}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.TIME, value, JavaType.TIME, calendar, false); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.TIME, value, + JavaType.TIME, calendar, false); loggerExternal.exiting(getClassNameLogging(), "setTime"); } @@ -1437,7 +1640,8 @@ public void setTime(String parameterName, java.sql.Time value, Calendar calendar loggerExternal.entering(getClassNameLogging(), "setTime", new Object[] {parameterName, value, calendar, forceEncrypt}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.TIME, value, JavaType.TIME, calendar, forceEncrypt); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.TIME, value, + JavaType.TIME, calendar, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setTime"); } @@ -1446,7 +1650,8 @@ public void setDate(String parameterName, java.sql.Date value, Calendar calendar if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setDate", new Object[] {parameterName, value, calendar}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.DATE, value, JavaType.DATE, calendar, false); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.DATE, value, + JavaType.DATE, calendar, false); loggerExternal.exiting(getClassNameLogging(), "setDate"); } @@ -1457,17 +1662,18 @@ public void setDate(String parameterName, java.sql.Date value, Calendar calendar loggerExternal.entering(getClassNameLogging(), "setDate", new Object[] {parameterName, value, calendar, forceEncrypt}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.DATE, value, JavaType.DATE, calendar, forceEncrypt); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.DATE, value, + JavaType.DATE, calendar, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setDate"); } @Override - public final void setCharacterStream(String parameterName, Reader reader) throws SQLException { + public final void setCharacterStream(String parameterName, Reader value) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setCharacterStream", new Object[] {parameterName, reader}); + loggerExternal.entering(getClassNameLogging(), "setCharacterStream", new Object[] {parameterName, value}); checkClosed(); - setStream(findColumn(parameterName), StreamType.CHARACTER, reader, JavaType.READER, - DataTypes.UNKNOWN_STREAM_LENGTH); + setStream(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), StreamType.CHARACTER, + value, JavaType.READER, DataTypes.UNKNOWN_STREAM_LENGTH); loggerExternal.exiting(getClassNameLogging(), "setCharacterStream"); } @@ -1477,18 +1683,19 @@ public final void setCharacterStream(String parameterName, Reader value, int len loggerExternal.entering(getClassNameLogging(), "setCharacterStream", new Object[] {parameterName, value, length}); checkClosed(); - setStream(findColumn(parameterName), StreamType.CHARACTER, value, JavaType.READER, length); + setStream(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), StreamType.CHARACTER, + value, JavaType.READER, length); loggerExternal.exiting(getClassNameLogging(), "setCharacterStream"); } @Override - public final void setCharacterStream(String parameterName, Reader reader, long length) throws SQLException { - + public final void setCharacterStream(String parameterName, Reader value, long length) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setCharacterStream", - new Object[] {parameterName, reader, length}); + new Object[] {parameterName, value, length}); checkClosed(); - setStream(findColumn(parameterName), StreamType.CHARACTER, reader, JavaType.READER, length); + setStream(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), StreamType.CHARACTER, + value, JavaType.READER, length); loggerExternal.exiting(getClassNameLogging(), "setCharacterStream"); } @@ -1497,8 +1704,8 @@ public final void setNCharacterStream(String parameterName, Reader value) throws if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setNCharacterStream", new Object[] {parameterName, value}); checkClosed(); - setStream(findColumn(parameterName), StreamType.NCHARACTER, value, JavaType.READER, - DataTypes.UNKNOWN_STREAM_LENGTH); + setStream(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), + StreamType.NCHARACTER, value, JavaType.READER, DataTypes.UNKNOWN_STREAM_LENGTH); loggerExternal.exiting(getClassNameLogging(), "setNCharacterStream"); } @@ -1508,7 +1715,8 @@ public final void setNCharacterStream(String parameterName, Reader value, long l loggerExternal.entering(getClassNameLogging(), "setNCharacterStream", new Object[] {parameterName, value, length}); checkClosed(); - setStream(findColumn(parameterName), StreamType.NCHARACTER, value, JavaType.READER, length); + setStream(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), + StreamType.NCHARACTER, value, JavaType.READER, length); loggerExternal.exiting(getClassNameLogging(), "setNCharacterStream"); } @@ -1517,17 +1725,18 @@ public final void setClob(String parameterName, Clob value) throws SQLException if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setClob", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.CLOB, value, JavaType.CLOB, false); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.CLOB, value, + JavaType.CLOB, false); loggerExternal.exiting(getClassNameLogging(), "setClob"); } @Override - public final void setClob(String parameterName, Reader reader) throws SQLException { + public final void setClob(String parameterName, Reader value) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setClob", new Object[] {parameterName, reader}); + loggerExternal.entering(getClassNameLogging(), "setClob", new Object[] {parameterName, value}); checkClosed(); - setStream(findColumn(parameterName), StreamType.CHARACTER, reader, JavaType.READER, - DataTypes.UNKNOWN_STREAM_LENGTH); + setStream(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), StreamType.CHARACTER, + value, JavaType.READER, DataTypes.UNKNOWN_STREAM_LENGTH); loggerExternal.exiting(getClassNameLogging(), "setClob"); } @@ -1536,7 +1745,8 @@ public final void setClob(String parameterName, Reader value, long length) throw if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setClob", new Object[] {parameterName, value, length}); checkClosed(); - setStream(findColumn(parameterName), StreamType.CHARACTER, value, JavaType.READER, length); + setStream(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), StreamType.CHARACTER, + value, JavaType.READER, length); loggerExternal.exiting(getClassNameLogging(), "setClob"); } @@ -1545,26 +1755,28 @@ public final void setNClob(String parameterName, NClob value) throws SQLExceptio if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setNClob", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.NCLOB, value, JavaType.NCLOB, false); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.NCLOB, value, + JavaType.NCLOB, false); loggerExternal.exiting(getClassNameLogging(), "setNClob"); } @Override - public final void setNClob(String parameterName, Reader reader) throws SQLException { + public final void setNClob(String parameterName, Reader value) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setNClob", new Object[] {parameterName, reader}); + loggerExternal.entering(getClassNameLogging(), "setNClob", new Object[] {parameterName, value}); checkClosed(); - setStream(findColumn(parameterName), StreamType.NCHARACTER, reader, JavaType.READER, - DataTypes.UNKNOWN_STREAM_LENGTH); + setStream(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), + StreamType.NCHARACTER, value, JavaType.READER, DataTypes.UNKNOWN_STREAM_LENGTH); loggerExternal.exiting(getClassNameLogging(), "setNClob"); } @Override - public final void setNClob(String parameterName, Reader reader, long length) throws SQLException { + public final void setNClob(String parameterName, Reader value, long length) throws SQLException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) - loggerExternal.entering(getClassNameLogging(), "setNClob", new Object[] {parameterName, reader, length}); + loggerExternal.entering(getClassNameLogging(), "setNClob", new Object[] {parameterName, value, length}); checkClosed(); - setStream(findColumn(parameterName), StreamType.NCHARACTER, reader, JavaType.READER, length); + setStream(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), + StreamType.NCHARACTER, value, JavaType.READER, length); loggerExternal.exiting(getClassNameLogging(), "setNClob"); } @@ -1573,7 +1785,8 @@ public final void setNString(String parameterName, String value) throws SQLExcep if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setNString", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.NVARCHAR, value, JavaType.STRING, false); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.NVARCHAR, + value, JavaType.STRING, false); loggerExternal.exiting(getClassNameLogging(), "setNString"); } @@ -1583,7 +1796,8 @@ public final void setNString(String parameterName, String value, boolean forceEn loggerExternal.entering(getClassNameLogging(), "setNString", new Object[] {parameterName, value, forceEncrypt}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.NVARCHAR, value, JavaType.STRING, forceEncrypt); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.NVARCHAR, + value, JavaType.STRING, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setNString"); } @@ -1592,7 +1806,7 @@ public void setObject(String parameterName, Object value) throws SQLServerExcept if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setObject", new Object[] {parameterName, value}); checkClosed(); - setObjectNoType(findColumn(parameterName), value, false); + setObjectNoType(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), value, false); loggerExternal.exiting(getClassNameLogging(), "setObject"); } @@ -1603,12 +1817,15 @@ public void setObject(String parameterName, Object value, int sqlType) throws SQ loggerExternal.entering(getClassNameLogging(), "setObject", new Object[] {parameterName, value, sqlType}); checkClosed(); if (microsoft.sql.Types.STRUCTURED == sqlType) { - tvpName = getTVPNameFromObject(findColumn(parameterName), value); - setObject(setterGetParam(findColumn(parameterName)), value, JavaType.TVP, JDBCType.TVP, null, null, false, - findColumn(parameterName), tvpName); + tvpName = getTVPNameFromObject( + findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), value); + setObject(setterGetParam(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD)), + value, JavaType.TVP, JDBCType.TVP, null, null, false, + findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), tvpName); } else - setObject(setterGetParam(findColumn(parameterName)), value, JavaType.of(value), JDBCType.of(sqlType), null, - null, false, findColumn(parameterName), tvpName); + setObject(setterGetParam(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD)), + value, JavaType.of(value), JDBCType.of(sqlType), null, null, false, + findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), tvpName); loggerExternal.exiting(getClassNameLogging(), "setObject"); } @@ -1618,8 +1835,9 @@ public void setObject(String parameterName, Object value, int sqlType, int decim loggerExternal.entering(getClassNameLogging(), "setObject", new Object[] {parameterName, value, sqlType, decimals}); checkClosed(); - setObject(setterGetParam(findColumn(parameterName)), value, JavaType.of(value), JDBCType.of(sqlType), decimals, - null, false, findColumn(parameterName), null); + setObject(setterGetParam(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD)), + value, JavaType.of(value), JDBCType.of(sqlType), decimals, null, false, + findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), null); loggerExternal.exiting(getClassNameLogging(), "setObject"); } @@ -1634,9 +1852,11 @@ public void setObject(String parameterName, Object value, int sqlType, int decim // scale - for java.sql.Types.DECIMAL or java.sql.Types.NUMERIC types, // this is the number of digits after the decimal point. // For all other types, this value will be ignored. - setObject(setterGetParam(findColumn(parameterName)), value, JavaType.of(value), JDBCType.of(sqlType), + + setObject(setterGetParam(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD)), + value, JavaType.of(value), JDBCType.of(sqlType), (java.sql.Types.NUMERIC == sqlType || java.sql.Types.DECIMAL == sqlType) ? decimals : null, null, - forceEncrypt, findColumn(parameterName), null); + forceEncrypt, findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), null); loggerExternal.exiting(getClassNameLogging(), "setObject"); } @@ -1654,10 +1874,12 @@ public final void setObject(String parameterName, Object value, int targetSqlTyp // InputStream and Reader, this is the length of the data in the stream or reader. // For all other types, this value will be ignored. - setObject(setterGetParam(findColumn(parameterName)), value, JavaType.of(value), JDBCType.of(targetSqlType), + setObject(setterGetParam(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD)), + value, JavaType.of(value), JDBCType.of(targetSqlType), (java.sql.Types.NUMERIC == targetSqlType || java.sql.Types.DECIMAL == targetSqlType || InputStream.class.isInstance(value) || Reader.class.isInstance(value)) ? scale : null, - precision, false, findColumn(parameterName), null); + precision, false, findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), + null); loggerExternal.exiting(getClassNameLogging(), "setObject"); } @@ -1667,8 +1889,8 @@ public final void setAsciiStream(String parameterName, InputStream value) throws if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setAsciiStream", new Object[] {parameterName, value}); checkClosed(); - setStream(findColumn(parameterName), StreamType.ASCII, value, JavaType.INPUTSTREAM, - DataTypes.UNKNOWN_STREAM_LENGTH); + setStream(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), StreamType.ASCII, + value, JavaType.INPUTSTREAM, DataTypes.UNKNOWN_STREAM_LENGTH); loggerExternal.exiting(getClassNameLogging(), "setAsciiStream"); } @@ -1678,7 +1900,8 @@ public final void setAsciiStream(String parameterName, InputStream value, int le loggerExternal.entering(getClassNameLogging(), "setAsciiStream", new Object[] {parameterName, value, length}); checkClosed(); - setStream(findColumn(parameterName), StreamType.ASCII, value, JavaType.INPUTSTREAM, length); + setStream(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), StreamType.ASCII, + value, JavaType.INPUTSTREAM, length); loggerExternal.exiting(getClassNameLogging(), "setAsciiStream"); } @@ -1688,7 +1911,8 @@ public final void setAsciiStream(String parameterName, InputStream value, long l loggerExternal.entering(getClassNameLogging(), "setAsciiStream", new Object[] {parameterName, value, length}); checkClosed(); - setStream(findColumn(parameterName), StreamType.ASCII, value, JavaType.INPUTSTREAM, length); + setStream(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), StreamType.ASCII, + value, JavaType.INPUTSTREAM, length); loggerExternal.exiting(getClassNameLogging(), "setAsciiStream"); } @@ -1697,8 +1921,8 @@ public final void setBinaryStream(String parameterName, InputStream value) throw if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setBinaryStream", new Object[] {parameterName, value}); checkClosed(); - setStream(findColumn(parameterName), StreamType.BINARY, value, JavaType.INPUTSTREAM, - DataTypes.UNKNOWN_STREAM_LENGTH); + setStream(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), StreamType.BINARY, + value, JavaType.INPUTSTREAM, DataTypes.UNKNOWN_STREAM_LENGTH); loggerExternal.exiting(getClassNameLogging(), "setBinaryStream"); } @@ -1708,7 +1932,8 @@ public final void setBinaryStream(String parameterName, InputStream value, int l loggerExternal.entering(getClassNameLogging(), "setBinaryStream", new Object[] {parameterName, value, length}); checkClosed(); - setStream(findColumn(parameterName), StreamType.BINARY, value, JavaType.INPUTSTREAM, length); + setStream(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), StreamType.BINARY, + value, JavaType.INPUTSTREAM, length); loggerExternal.exiting(getClassNameLogging(), "setBinaryStream"); } @@ -1718,7 +1943,8 @@ public final void setBinaryStream(String parameterName, InputStream value, long loggerExternal.entering(getClassNameLogging(), "setBinaryStream", new Object[] {parameterName, value, length}); checkClosed(); - setStream(findColumn(parameterName), StreamType.BINARY, value, JavaType.INPUTSTREAM, length); + setStream(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), StreamType.BINARY, + value, JavaType.INPUTSTREAM, length); loggerExternal.exiting(getClassNameLogging(), "setBinaryStream"); } @@ -1727,7 +1953,8 @@ public final void setBlob(String parameterName, Blob inputStream) throws SQLExce if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setBlob", new Object[] {parameterName, inputStream}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.BLOB, inputStream, JavaType.BLOB, false); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.BLOB, + inputStream, JavaType.BLOB, false); loggerExternal.exiting(getClassNameLogging(), "setBlob"); } @@ -1736,8 +1963,8 @@ public final void setBlob(String parameterName, InputStream value) throws SQLExc if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setBlob", new Object[] {parameterName, value}); checkClosed(); - setStream(findColumn(parameterName), StreamType.BINARY, value, JavaType.INPUTSTREAM, - DataTypes.UNKNOWN_STREAM_LENGTH); + setStream(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), StreamType.BINARY, + value, JavaType.INPUTSTREAM, DataTypes.UNKNOWN_STREAM_LENGTH); loggerExternal.exiting(getClassNameLogging(), "setBlob"); } @@ -1747,7 +1974,8 @@ public final void setBlob(String parameterName, InputStream inputStream, long le loggerExternal.entering(getClassNameLogging(), "setBlob", new Object[] {parameterName, inputStream, length}); checkClosed(); - setStream(findColumn(parameterName), StreamType.BINARY, inputStream, JavaType.INPUTSTREAM, length); + setStream(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), StreamType.BINARY, + inputStream, JavaType.INPUTSTREAM, length); loggerExternal.exiting(getClassNameLogging(), "setBlob"); } @@ -1756,7 +1984,8 @@ public void setTimestamp(String parameterName, java.sql.Timestamp value) throws if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setTimestamp", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.TIMESTAMP, value, JavaType.TIMESTAMP, false); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.TIMESTAMP, + value, JavaType.TIMESTAMP, false); loggerExternal.exiting(getClassNameLogging(), "setTimestamp"); } @@ -1765,7 +1994,8 @@ public void setTimestamp(String parameterName, java.sql.Timestamp value, int sca if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setTimestamp", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.TIMESTAMP, value, JavaType.TIMESTAMP, null, scale, false); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.TIMESTAMP, + value, JavaType.TIMESTAMP, null, scale, false); loggerExternal.exiting(getClassNameLogging(), "setTimestamp"); } @@ -1776,7 +2006,8 @@ public void setTimestamp(String parameterName, java.sql.Timestamp value, int sca loggerExternal.entering(getClassNameLogging(), "setTimestamp", new Object[] {parameterName, value, forceEncrypt}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.TIMESTAMP, value, JavaType.TIMESTAMP, null, scale, forceEncrypt); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.TIMESTAMP, + value, JavaType.TIMESTAMP, null, scale, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setTimestamp"); } @@ -1785,7 +2016,8 @@ public void setDateTimeOffset(String parameterName, microsoft.sql.DateTimeOffset if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setDateTimeOffset", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.DATETIMEOFFSET, value, JavaType.DATETIMEOFFSET, false); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), + JDBCType.DATETIMEOFFSET, value, JavaType.DATETIMEOFFSET, false); loggerExternal.exiting(getClassNameLogging(), "setDateTimeOffset"); } @@ -1795,8 +2027,8 @@ public void setDateTimeOffset(String parameterName, microsoft.sql.DateTimeOffset if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setDateTimeOffset", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.DATETIMEOFFSET, value, JavaType.DATETIMEOFFSET, null, scale, - false); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), + JDBCType.DATETIMEOFFSET, value, JavaType.DATETIMEOFFSET, null, scale, false); loggerExternal.exiting(getClassNameLogging(), "setDateTimeOffset"); } @@ -1807,8 +2039,8 @@ public void setDateTimeOffset(String parameterName, microsoft.sql.DateTimeOffset loggerExternal.entering(getClassNameLogging(), "setDateTimeOffset", new Object[] {parameterName, value, forceEncrypt}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.DATETIMEOFFSET, value, JavaType.DATETIMEOFFSET, null, scale, - forceEncrypt); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), + JDBCType.DATETIMEOFFSET, value, JavaType.DATETIMEOFFSET, null, scale, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setDateTimeOffset"); } @@ -1817,7 +2049,8 @@ public void setDate(String parameterName, java.sql.Date value) throws SQLServerE if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setDate", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.DATE, value, JavaType.DATE, false); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.DATE, value, + JavaType.DATE, false); loggerExternal.exiting(getClassNameLogging(), "setDate"); } @@ -1826,7 +2059,8 @@ public void setTime(String parameterName, java.sql.Time value) throws SQLServerE if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setTime", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.TIME, value, JavaType.TIME, false); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.TIME, value, + JavaType.TIME, false); loggerExternal.exiting(getClassNameLogging(), "setTime"); } @@ -1835,7 +2069,8 @@ public void setTime(String parameterName, java.sql.Time value, int scale) throws if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setTime", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.TIME, value, JavaType.TIME, null, scale, false); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.TIME, value, + JavaType.TIME, null, scale, false); loggerExternal.exiting(getClassNameLogging(), "setTime"); } @@ -1846,7 +2081,8 @@ public void setTime(String parameterName, java.sql.Time value, int scale, loggerExternal.entering(getClassNameLogging(), "setTime", new Object[] {parameterName, value, forceEncrypt}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.TIME, value, JavaType.TIME, null, scale, forceEncrypt); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.TIME, value, + JavaType.TIME, null, scale, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setTime"); } @@ -1855,7 +2091,8 @@ public void setDateTime(String parameterName, java.sql.Timestamp value) throws S if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setDateTime", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.DATETIME, value, JavaType.TIMESTAMP, false); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.DATETIME, + value, JavaType.TIMESTAMP, false); loggerExternal.exiting(getClassNameLogging(), "setDateTime"); } @@ -1866,7 +2103,8 @@ public void setDateTime(String parameterName, java.sql.Timestamp value, loggerExternal.entering(getClassNameLogging(), "setDateTime", new Object[] {parameterName, value, forceEncrypt}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.DATETIME, value, JavaType.TIMESTAMP, forceEncrypt); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.DATETIME, + value, JavaType.TIMESTAMP, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setDateTime"); } @@ -1875,7 +2113,8 @@ public void setSmallDateTime(String parameterName, java.sql.Timestamp value) thr if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setSmallDateTime", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.SMALLDATETIME, value, JavaType.TIMESTAMP, false); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), + JDBCType.SMALLDATETIME, value, JavaType.TIMESTAMP, false); loggerExternal.exiting(getClassNameLogging(), "setSmallDateTime"); } @@ -1886,8 +2125,8 @@ public void setSmallDateTime(String parameterName, java.sql.Timestamp value, loggerExternal.entering(getClassNameLogging(), "setSmallDateTime", new Object[] {parameterName, value, forceEncrypt}); checkClosed(); - - setValue(findColumn(parameterName), JDBCType.SMALLDATETIME, value, JavaType.TIMESTAMP, forceEncrypt); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), + JDBCType.SMALLDATETIME, value, JavaType.TIMESTAMP, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setSmallDateTime"); } @@ -1896,7 +2135,8 @@ public void setUniqueIdentifier(String parameterName, String guid) throws SQLSer if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setUniqueIdentifier", new Object[] {parameterName, guid}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.GUID, guid, JavaType.STRING, false); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.GUID, guid, + JavaType.STRING, false); loggerExternal.exiting(getClassNameLogging(), "setUniqueIdentifier"); } @@ -1906,7 +2146,8 @@ public void setUniqueIdentifier(String parameterName, String guid, boolean force loggerExternal.entering(getClassNameLogging(), "setUniqueIdentifier", new Object[] {parameterName, guid, forceEncrypt}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.GUID, guid, JavaType.STRING, forceEncrypt); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.GUID, guid, + JavaType.STRING, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setUniqueIdentifier"); } @@ -1915,7 +2156,8 @@ public void setBytes(String parameterName, byte[] value) throws SQLServerExcepti if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setBytes", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.BINARY, value, JavaType.BYTEARRAY, false); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.BINARY, + value, JavaType.BYTEARRAY, false); loggerExternal.exiting(getClassNameLogging(), "setBytes"); } @@ -1925,7 +2167,8 @@ public void setBytes(String parameterName, byte[] value, boolean forceEncrypt) t loggerExternal.entering(getClassNameLogging(), "setBytes", new Object[] {parameterName, value, forceEncrypt}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.BINARY, value, JavaType.BYTEARRAY, forceEncrypt); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.BINARY, + value, JavaType.BYTEARRAY, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setBytes"); } @@ -1934,7 +2177,8 @@ public void setByte(String parameterName, byte value) throws SQLServerException if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setByte", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.TINYINT, value, JavaType.BYTE, false); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.TINYINT, + value, JavaType.BYTE, false); loggerExternal.exiting(getClassNameLogging(), "setByte"); } @@ -1944,7 +2188,8 @@ public void setByte(String parameterName, byte value, boolean forceEncrypt) thro loggerExternal.entering(getClassNameLogging(), "setByte", new Object[] {parameterName, value, forceEncrypt}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.TINYINT, value, JavaType.BYTE, forceEncrypt); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.TINYINT, + value, JavaType.BYTE, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setByte"); } @@ -1953,7 +2198,8 @@ public void setString(String parameterName, String value) throws SQLServerExcept if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setString", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.VARCHAR, value, JavaType.STRING, false); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.VARCHAR, + value, JavaType.STRING, false); loggerExternal.exiting(getClassNameLogging(), "setString"); } @@ -1963,7 +2209,8 @@ public void setString(String parameterName, String value, boolean forceEncrypt) loggerExternal.entering(getClassNameLogging(), "setString", new Object[] {parameterName, value, forceEncrypt}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.VARCHAR, value, JavaType.STRING, forceEncrypt); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.VARCHAR, + value, JavaType.STRING, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setString"); } @@ -1972,7 +2219,8 @@ public void setMoney(String parameterName, BigDecimal value) throws SQLServerExc if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setMoney", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.MONEY, value, JavaType.BIGDECIMAL, false); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.MONEY, value, + JavaType.BIGDECIMAL, false); loggerExternal.exiting(getClassNameLogging(), "setMoney"); } @@ -1982,7 +2230,8 @@ public void setMoney(String parameterName, BigDecimal value, boolean forceEncryp loggerExternal.entering(getClassNameLogging(), "setMoney", new Object[] {parameterName, value, forceEncrypt}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.MONEY, value, JavaType.BIGDECIMAL, forceEncrypt); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.MONEY, value, + JavaType.BIGDECIMAL, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setMoney"); } @@ -1991,7 +2240,8 @@ public void setSmallMoney(String parameterName, BigDecimal value) throws SQLServ if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setSmallMoney", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.SMALLMONEY, value, JavaType.BIGDECIMAL, false); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.SMALLMONEY, + value, JavaType.BIGDECIMAL, false); loggerExternal.exiting(getClassNameLogging(), "setSmallMoney"); } @@ -2001,7 +2251,8 @@ public void setSmallMoney(String parameterName, BigDecimal value, boolean forceE loggerExternal.entering(getClassNameLogging(), "setSmallMoney", new Object[] {parameterName, value, forceEncrypt}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.SMALLMONEY, value, JavaType.BIGDECIMAL, forceEncrypt); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.SMALLMONEY, + value, JavaType.BIGDECIMAL, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setSmallMoney"); } @@ -2010,7 +2261,8 @@ public void setBigDecimal(String parameterName, BigDecimal value) throws SQLServ if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setBigDecimal", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.DECIMAL, value, JavaType.BIGDECIMAL, false); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.DECIMAL, + value, JavaType.BIGDECIMAL, false); loggerExternal.exiting(getClassNameLogging(), "setBigDecimal"); } @@ -2021,7 +2273,8 @@ public void setBigDecimal(String parameterName, BigDecimal value, int precision, loggerExternal.entering(getClassNameLogging(), "setBigDecimal", new Object[] {parameterName, value, precision, scale}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.DECIMAL, value, JavaType.BIGDECIMAL, precision, scale, false); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.DECIMAL, + value, JavaType.BIGDECIMAL, precision, scale, false); loggerExternal.exiting(getClassNameLogging(), "setBigDecimal"); } @@ -2032,8 +2285,8 @@ public void setBigDecimal(String parameterName, BigDecimal value, int precision, loggerExternal.entering(getClassNameLogging(), "setBigDecimal", new Object[] {parameterName, value, precision, scale, forceEncrypt}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.DECIMAL, value, JavaType.BIGDECIMAL, precision, scale, - forceEncrypt); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.DECIMAL, + value, JavaType.BIGDECIMAL, precision, scale, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setBigDecimal"); } @@ -2042,7 +2295,8 @@ public void setDouble(String parameterName, double value) throws SQLServerExcept if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setDouble", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.DOUBLE, value, JavaType.DOUBLE, false); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.DOUBLE, + value, JavaType.DOUBLE, false); loggerExternal.exiting(getClassNameLogging(), "setDouble"); } @@ -2052,7 +2306,8 @@ public void setDouble(String parameterName, double value, boolean forceEncrypt) loggerExternal.entering(getClassNameLogging(), "setDouble", new Object[] {parameterName, value, forceEncrypt}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.DOUBLE, value, JavaType.DOUBLE, forceEncrypt); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.DOUBLE, + value, JavaType.DOUBLE, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setDouble"); } @@ -2061,7 +2316,8 @@ public void setFloat(String parameterName, float value) throws SQLServerExceptio if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setFloat", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.REAL, value, JavaType.FLOAT, false); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.REAL, value, + JavaType.FLOAT, false); loggerExternal.exiting(getClassNameLogging(), "setFloat"); } @@ -2071,7 +2327,8 @@ public void setFloat(String parameterName, float value, boolean forceEncrypt) th loggerExternal.entering(getClassNameLogging(), "setFloat", new Object[] {parameterName, value, forceEncrypt}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.REAL, value, JavaType.FLOAT, forceEncrypt); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.REAL, value, + JavaType.FLOAT, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setFloat"); } @@ -2080,7 +2337,8 @@ public void setInt(String parameterName, int value) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setInt", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.INTEGER, value, JavaType.INTEGER, false); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.INTEGER, + value, JavaType.INTEGER, false); loggerExternal.exiting(getClassNameLogging(), "setInt"); } @@ -2089,7 +2347,8 @@ public void setInt(String parameterName, int value, boolean forceEncrypt) throws if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setInt", new Object[] {parameterName, value, forceEncrypt}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.INTEGER, value, JavaType.INTEGER, forceEncrypt); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.INTEGER, + value, JavaType.INTEGER, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setInt"); } @@ -2098,7 +2357,8 @@ public void setLong(String parameterName, long value) throws SQLServerException if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setLong", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.BIGINT, value, JavaType.LONG, false); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.BIGINT, + value, JavaType.LONG, false); loggerExternal.exiting(getClassNameLogging(), "setLong"); } @@ -2108,7 +2368,8 @@ public void setLong(String parameterName, long value, boolean forceEncrypt) thro loggerExternal.entering(getClassNameLogging(), "setLong", new Object[] {parameterName, value, forceEncrypt}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.BIGINT, value, JavaType.LONG, forceEncrypt); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.BIGINT, + value, JavaType.LONG, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setLong"); } @@ -2117,7 +2378,8 @@ public void setShort(String parameterName, short value) throws SQLServerExceptio if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setShort", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.SMALLINT, value, JavaType.SHORT, false); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.SMALLINT, + value, JavaType.SHORT, false); loggerExternal.exiting(getClassNameLogging(), "setShort"); } @@ -2127,7 +2389,8 @@ public void setShort(String parameterName, short value, boolean forceEncrypt) th loggerExternal.entering(getClassNameLogging(), "setShort", new Object[] {parameterName, value, forceEncrypt}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.SMALLINT, value, JavaType.SHORT, forceEncrypt); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.SMALLINT, + value, JavaType.SHORT, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setShort"); } @@ -2136,7 +2399,8 @@ public void setBoolean(String parameterName, boolean value) throws SQLServerExce if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setBoolean", new Object[] {parameterName, value}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.BIT, value, JavaType.BOOLEAN, false); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.BIT, value, + JavaType.BOOLEAN, false); loggerExternal.exiting(getClassNameLogging(), "setBoolean"); } @@ -2146,7 +2410,8 @@ public void setBoolean(String parameterName, boolean value, boolean forceEncrypt loggerExternal.entering(getClassNameLogging(), "setBoolean", new Object[] {parameterName, value, forceEncrypt}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.BIT, value, JavaType.BOOLEAN, forceEncrypt); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.BIT, value, + JavaType.BOOLEAN, forceEncrypt); loggerExternal.exiting(getClassNameLogging(), "setBoolean"); } @@ -2155,8 +2420,9 @@ public void setNull(String parameterName, int nType) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setNull", new Object[] {parameterName, nType}); checkClosed(); - setObject(setterGetParam(findColumn(parameterName)), null, JavaType.OBJECT, JDBCType.of(nType), null, null, - false, findColumn(parameterName), null); + setObject(setterGetParam(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD)), null, + JavaType.OBJECT, JDBCType.of(nType), null, null, false, + findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), null); loggerExternal.exiting(getClassNameLogging(), "setNull"); } @@ -2165,8 +2431,9 @@ public void setNull(String parameterName, int nType, String sTypeName) throws SQ if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setNull", new Object[] {parameterName, nType, sTypeName}); checkClosed(); - setObject(setterGetParam(findColumn(parameterName)), null, JavaType.OBJECT, JDBCType.of(nType), null, null, - false, findColumn(parameterName), sTypeName); + setObject(setterGetParam(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD)), null, + JavaType.OBJECT, JDBCType.of(nType), null, null, false, + findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), sTypeName); loggerExternal.exiting(getClassNameLogging(), "setNull"); } @@ -2174,43 +2441,49 @@ public void setNull(String parameterName, int nType, String sTypeName) throws SQ public void setURL(String parameterName, URL url) throws SQLException { loggerExternal.entering(getClassNameLogging(), "setURL", parameterName); checkClosed(); - setURL(findColumn(parameterName), url); + setURL(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), url); loggerExternal.exiting(getClassNameLogging(), "setURL"); } @Override public final void setStructured(String parameterName, String tvpName, SQLServerDataTable tvpDataTable) throws SQLServerException { - tvpName = getTVPNameIfNull(findColumn(parameterName), tvpName); + tvpName = getTVPNameIfNull(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), + tvpName); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setStructured", new Object[] {parameterName, tvpName, tvpDataTable}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.TVP, tvpDataTable, JavaType.TVP, tvpName); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.TVP, + tvpDataTable, JavaType.TVP, tvpName); loggerExternal.exiting(getClassNameLogging(), "setStructured"); } @Override public final void setStructured(String parameterName, String tvpName, ResultSet tvpResultSet) throws SQLServerException { - tvpName = getTVPNameIfNull(findColumn(parameterName), tvpName); + tvpName = getTVPNameIfNull(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), + tvpName); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setStructured", new Object[] {parameterName, tvpName, tvpResultSet}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.TVP, tvpResultSet, JavaType.TVP, tvpName); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.TVP, + tvpResultSet, JavaType.TVP, tvpName); loggerExternal.exiting(getClassNameLogging(), "setStructured"); } @Override public final void setStructured(String parameterName, String tvpName, ISQLServerDataRecord tvpDataRecord) throws SQLServerException { - tvpName = getTVPNameIfNull(findColumn(parameterName), tvpName); + tvpName = getTVPNameIfNull(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), + tvpName); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setStructured", new Object[] {parameterName, tvpName, tvpDataRecord}); checkClosed(); - setValue(findColumn(parameterName), JDBCType.TVP, tvpDataRecord, JavaType.TVP, tvpName); + setValue(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), JDBCType.TVP, + tvpDataRecord, JavaType.TVP, tvpName); loggerExternal.exiting(getClassNameLogging(), "setStructured"); } @@ -2231,12 +2504,13 @@ public final void setSQLXML(String parameterName, SQLXML xmlObject) throws SQLEx if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setSQLXML", new Object[] {parameterName, xmlObject}); checkClosed(); - setSQLXMLInternal(findColumn(parameterName), xmlObject); + setSQLXMLInternal(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), xmlObject); loggerExternal.exiting(getClassNameLogging(), "setSQLXML"); } @Override public final SQLXML getSQLXML(int parameterIndex) throws SQLException { + setByIndex(); loggerExternal.entering(getClassNameLogging(), "getSQLXML", parameterIndex); checkClosed(); SQLServerSQLXML value = (SQLServerSQLXML) getSQLXMLInternal(parameterIndex); @@ -2248,7 +2522,8 @@ public final SQLXML getSQLXML(int parameterIndex) throws SQLException { public final SQLXML getSQLXML(String parameterName) throws SQLException { loggerExternal.entering(getClassNameLogging(), "getSQLXML", parameterName); checkClosed(); - SQLServerSQLXML value = (SQLServerSQLXML) getSQLXMLInternal(findColumn(parameterName)); + SQLServerSQLXML value = (SQLServerSQLXML) getSQLXMLInternal( + findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD)); loggerExternal.exiting(getClassNameLogging(), "getSQLXML", value); return value; } @@ -2276,7 +2551,11 @@ public void registerOutParameter(String parameterName, int sqlType, String typeN loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {parameterName, sqlType, typeName}); checkClosed(); - registerOutParameter(findColumn(parameterName), sqlType, typeName); + + int index = findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD); + + registerOutParameterByName(index, sqlType); + loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); } @@ -2286,7 +2565,12 @@ public void registerOutParameter(String parameterName, int sqlType, int scale) t loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {parameterName, sqlType, scale}); checkClosed(); - registerOutParameter(findColumn(parameterName), sqlType, scale); + + int index = findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD); + + registerOutParameterByName(index, sqlType); + inOutParam[index - 1].setOutScale(scale); + loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); } @@ -2295,9 +2579,15 @@ public void registerOutParameter(String parameterName, int sqlType, int precisio int scale) throws SQLServerException { if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "registerOutParameter", - new Object[] {parameterName, sqlType, scale}); + new Object[] {parameterName, sqlType, scale, precision}); checkClosed(); - registerOutParameter(findColumn(parameterName), sqlType, precision, scale); + + int index = findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD); + + registerOutParameterByName(index, sqlType); + inOutParam[index - 1].setValueLength(precision); + inOutParam[index - 1].setOutScale(scale); + loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); } @@ -2307,7 +2597,8 @@ public void registerOutParameter(String parameterName, int sqlType) throws SQLSe loggerExternal.entering(getClassNameLogging(), "registerOutParameter", new Object[] {parameterName, sqlType}); checkClosed(); - registerOutParameter(findColumn(parameterName), sqlType); + registerOutParameterByName(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), + sqlType); loggerExternal.exiting(getClassNameLogging(), "registerOutParameter"); } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index d81e3da9e..492dd360f 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -167,6 +167,10 @@ public class SQLServerConnection implements ISQLServerConnection, java.io.Serial /** Flag that determines whether the accessToken callback was set **/ private transient SQLServerAccessTokenCallback accessTokenCallback = null; + /** Flag indicating whether to use sp_sproc_columns for parameter name lookup */ + private boolean useFlexibleCallableStatements = SQLServerDriverBooleanProperty.USE_FLEXIBLE_CALLABLE_STATEMENTS + .getDefaultValue(); + /** * Keep this distinct from _federatedAuthenticationRequested, since some fedauth library types may not need more * info @@ -558,6 +562,10 @@ static ParsedSQLCacheItem parseAndCacheSQL(CityHash128Key key, String sql) throw return cacheItem; } + static int countParams(String sql) { + return locateParams(sql).length; + } + /** Default size for prepared statement caches */ static final int DEFAULT_STATEMENT_POOLING_CACHE_SIZE = 0; @@ -2500,6 +2508,15 @@ Connection connectInternal(Properties propsIn, } trustServerCertificate = isBooleanPropertyOn(sPropKey, sPropValue); + sPropKey = SQLServerDriverBooleanProperty.USE_FLEXIBLE_CALLABLE_STATEMENTS.toString(); + sPropValue = activeConnectionProperties.getProperty(sPropKey); + if (null == sPropValue) { + sPropValue = Boolean.toString( + SQLServerDriverBooleanProperty.USE_FLEXIBLE_CALLABLE_STATEMENTS.getDefaultValue()); + activeConnectionProperties.setProperty(sPropKey, sPropValue); + } + useFlexibleCallableStatements = isBooleanPropertyOn(sPropKey, sPropValue); + // Set requestedEncryptionLevel according to the value of the encrypt connection property if (encryptOption.compareToIgnoreCase(EncryptOption.FALSE.toString()) == 0) { requestedEncryptionLevel = TDS.ENCRYPT_OFF; @@ -7680,7 +7697,7 @@ String replaceParameterMarkers(String sqlSrc, int[] paramPositions, Parameter[] int paramIndex = 0; while (true) { - int srcEnd = (paramIndex >= paramPositions.length) ? sqlSrc.length() : paramPositions[paramIndex]; + int srcEnd = ParameterUtils.scanSQLForChar('?', sqlSrc, srcBegin); sqlSrc.getChars(srcBegin, srcEnd, sqlDst, dstBegin); dstBegin += srcEnd - srcBegin; @@ -8114,19 +8131,15 @@ public void setAccessTokenCallbackClass(String accessTokenCallbackClass) { } /** - * useFlexibleCallableStatements is temporarily removed. This is meant as a no-op. - * * Returns whether or not sp_sproc_columns is being used for parameter name lookup. * * @return useFlexibleCallableStatements */ public boolean getUseFlexibleCallableStatements() { - return true; + return this.useFlexibleCallableStatements; } /** - * useFlexibleCallableStatements is temporarily removed. This is meant as a no-op. - * * Sets whether or not sp_sproc_columns will be used for parameter name lookup. * * @param useFlexibleCallableStatements @@ -8136,7 +8149,9 @@ public boolean getUseFlexibleCallableStatements() { * parameters by name or by index, not both. Parameters must also be set in the same * order as the stored procedure definition. */ - public void setUseFlexibleCallableStatements(boolean useFlexibleCallableStatements) {} + public void setUseFlexibleCallableStatements(boolean useFlexibleCallableStatements) { + this.useFlexibleCallableStatements = useFlexibleCallableStatements; + } /** * Cleans up discarded prepared statement handles on the server using batched un-prepare actions if the batching diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java index e27845a0d..7d122c1e1 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java @@ -251,6 +251,19 @@ public boolean getUseDefaultGSSCredential() { SQLServerDriverBooleanProperty.USE_DEFAULT_GSS_CREDENTIAL.getDefaultValue()); } + @Override + public void setUseFlexibleCallableStatements(boolean enable) { + setBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.USE_FLEXIBLE_CALLABLE_STATEMENTS.toString(), + enable); + } + + @Override + public boolean getUseFlexibleCallableStatements() { + return getBooleanProperty(connectionProps, + SQLServerDriverBooleanProperty.USE_FLEXIBLE_CALLABLE_STATEMENTS.toString(), + SQLServerDriverBooleanProperty.USE_FLEXIBLE_CALLABLE_STATEMENTS.getDefaultValue()); + } + @Override public void setAccessToken(String accessToken) { setStringProperty(connectionProps, SQLServerDriverStringProperty.ACCESS_TOKEN.toString(), accessToken); @@ -1295,23 +1308,6 @@ public int getMsiTokenCacheTtl() { return 0; } - /** - * useFlexibleCallableStatements is temporarily removed. - * This method is a no-op for backwards compatibility only. - */ - @Override - public void setUseFlexibleCallableStatements(boolean enable) {} - - - /** - * useFlexibleCallableStatements is temporarily removed. - * This method is a no-op for backwards compatibility only. - */ - @Override - public boolean getUseFlexibleCallableStatements() { - return true; - } - /** * Sets the {@link SQLServerAccessTokenCallback} delegate. * diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java index 0e9689216..4c4c0724b 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java @@ -698,6 +698,7 @@ enum SQLServerDriverBooleanProperty { IGNORE_OFFSET_ON_DATE_TIME_OFFSET_CONVERSION("ignoreOffsetOnDateTimeOffsetConversion", false), USE_DEFAULT_JAAS_CONFIG("useDefaultJaasConfig", false), USE_DEFAULT_GSS_CREDENTIAL("useDefaultGSSCredential", false), + USE_FLEXIBLE_CALLABLE_STATEMENTS("useFlexibleCallableStatements", true), CALC_BIG_DECIMAL_PRECISION("calcBigDecimalPrecision", false); private final String name; @@ -773,8 +774,11 @@ public final class SQLServerDriver implements java.sql.Driver { Boolean.toString(SQLServerDriverBooleanProperty.INTEGRATED_SECURITY.getDefaultValue()), false, TRUE_FALSE), new SQLServerDriverPropertyInfo(SQLServerDriverBooleanProperty.USE_DEFAULT_GSS_CREDENTIAL.toString(), - Boolean.toString(SQLServerDriverBooleanProperty.USE_DEFAULT_GSS_CREDENTIAL.getDefaultValue()), false, - TRUE_FALSE), + Boolean.toString(SQLServerDriverBooleanProperty.USE_DEFAULT_GSS_CREDENTIAL.getDefaultValue()), + false, TRUE_FALSE), + new SQLServerDriverPropertyInfo(SQLServerDriverBooleanProperty.USE_FLEXIBLE_CALLABLE_STATEMENTS.toString(), + Boolean.toString(SQLServerDriverBooleanProperty.USE_FLEXIBLE_CALLABLE_STATEMENTS.getDefaultValue()), + false, TRUE_FALSE), new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.KEY_STORE_AUTHENTICATION.toString(), SQLServerDriverStringProperty.KEY_STORE_AUTHENTICATION.getDefaultValue(), false, new String[] {KeyStoreAuthentication.JAVA_KEYSTORE_PASSWORD.toString()}), diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index a5f50b14a..d32d75ac8 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -29,6 +29,7 @@ import java.util.Map.Entry; import java.util.Vector; import java.util.logging.Level; +import java.util.regex.Pattern; import com.microsoft.sqlserver.jdbc.SQLServerConnection.CityHash128Key; import com.microsoft.sqlserver.jdbc.SQLServerConnection.PreparedStatementHandle; @@ -70,6 +71,12 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS /** Processed SQL statement text, may not be same as what user initially passed. */ final String userSQL; + // flag whether is exec escape syntax + private boolean isExecEscapeSyntax; + + // flag whether is call escape syntax + private boolean isCallEscapeSyntax; + /** Parameter positions in processed SQL statement text. */ final int[] userSQLParamPositions; @@ -128,6 +135,20 @@ private void setPreparedStatementHandle(int handle) { */ private boolean useBulkCopyForBatchInsert; + /** + * Regex for JDBC 'call' escape syntax + * + * Matches {[? =] call sproc ([@arg =] ?, [@arg =] ?, [@arg =] ? ...)} + */ + private static final Pattern callEscapePattern = Pattern + .compile("^\\s*(?i)\\{(\\s*\\??\\s*=?\\s*)call [^\\(\\)]+\\s*" + + "((\\(\\s*(.+\\s*=\\s*)?\\?\\s*(,\\s*\\?\\s*)*\\))?|\\(\\))\\s*}"); + + /** + * Regex for 'exec' escape syntax + */ + private static final Pattern execEscapePattern = Pattern.compile("^\\s*(?i)(?:exec|execute)\\b"); + /** * For caching data related to batch insert with bulkcopy */ @@ -270,6 +291,8 @@ private boolean resetPrepStmtHandle(boolean discardCurrentCacheItem) { procedureName = parsedSQL.procedureName; bReturnValueSyntax = parsedSQL.bReturnValueSyntax; userSQL = parsedSQL.processedSQL; + isExecEscapeSyntax = isExecEscapeSyntax(sql); + isCallEscapeSyntax = isCallEscapeSyntax(sql); userSQLParamPositions = parsedSQL.parameterPositions; initParams(userSQLParamPositions.length); useBulkCopyForBatchInsert = conn.getUseBulkCopyForBatchInsert(); @@ -397,6 +420,10 @@ final void initParams(int nParams) { for (int i = 0; i < nParams; i++) { inOutParam[i] = new Parameter(Util.shouldHonorAEForParameters(stmtColumnEncriptionSetting, connection)); } + + if (bReturnValueSyntax) { + inOutParam[0].setReturnValue(true); + } } @Override @@ -450,13 +477,14 @@ private String buildParamTypeDefinitions(Parameter[] params, boolean renewDefini return ""; // Output looks like @P0 timestamp, @P1 varchar - int stringLen = nCols * 2; // @P - stringLen += nCols; // spaces - stringLen += nCols -1; // commas - if (nCols > 10) - stringLen += 10 + ((nCols - 10) * 2); // @P{0-99} Numbers after p - else - stringLen += nCols; // @P{0-9} Numbers after p less than 10 + int stringLen = nCols * 2; // @P + stringLen += nCols; // spaces + stringLen += nCols - 1; // commas + if (nCols > 10) { + stringLen += 10 + ((nCols - 10) * 2); // @P{0-99} Numbers after p + } else { + stringLen += nCols; // @P{0-9} Numbers after p less than 10 + } // Computing the type definitions up front, so we can get exact string lengths needed for the string builder. String[] typeDefinitions = new String[nCols]; @@ -772,16 +800,23 @@ boolean onRetValue(TDSReader tdsReader) throws SQLServerException { /** * Sends the statement parameters by RPC. */ - void sendParamsByRPC(TDSWriter tdsWriter, Parameter[] params) throws SQLServerException { + void sendParamsByRPC(TDSWriter tdsWriter, Parameter[] params, boolean bReturnValueSyntax, + boolean callRpcDirectly) throws SQLServerException { char[] cParamName; - for (int index = 0; index < params.length; index++) { + int index = 0; + if (bReturnValueSyntax && !isCursorable(executeMethod) && !isTVPType && callRpcDirectly) { + returnParam = params[index]; + params[index].setReturnValue(true); + index++; + } + for (; index < params.length; index++) { if (JDBCType.TVP == params[index].getJdbcType()) { cParamName = new char[10]; int paramNameLen = SQLServerConnection.makeParamName(index, cParamName, 0, false); tdsWriter.writeByte((byte) paramNameLen); tdsWriter.writeString(new String(cParamName, 0, paramNameLen)); } - params[index].sendByRPC(tdsWriter, this); + params[index].sendByRPC(tdsWriter, callRpcDirectly, this); } } @@ -829,6 +864,25 @@ private void buildServerCursorPrepExecParams(TDSWriter tdsWriter) throws SQLServ tdsWriter.writeRPCInt(null, 0, true); } + private void buildRPCExecParams(TDSWriter tdsWriter) throws SQLServerException { + if (getStatementLogger().isLoggable(java.util.logging.Level.FINE)) { + getStatementLogger().fine(toString() + ": calling PROC" + ", SQL:" + preparedSQL); + } + + expectPrepStmtHandle = false; + executedSqlDirectly = true; + expectCursorOutParams = false; + outParamIndexAdjustment = 0; + tdsWriter.writeShort((short) procedureName.length()); // procedure name length + tdsWriter.writeString(procedureName); + if (connection.isAEv2()) { + tdsWriter.sendEnclavePackage(preparedSQL, enclaveCEKs); + } + + tdsWriter.writeByte((byte) 0); // RPC procedure option 1 + tdsWriter.writeByte((byte) 0); // RPC procedure option 2 + } + private void buildPrepParams(TDSWriter tdsWriter) throws SQLServerException { if (getStatementLogger().isLoggable(java.util.logging.Level.FINE)) getStatementLogger().fine(toString() + ": calling sp_prepare: PreparedHandle:" @@ -1146,6 +1200,7 @@ private boolean doPrepExec(TDSWriter tdsWriter, Parameter[] params, boolean hasN boolean needsPrepare = (hasNewTypeDefinitions && hasExistingTypeDefinitions) || !hasPreparedStatementHandle(); boolean isPrepareMethodSpPrepExec = connection.getPrepareMethod().equals(PrepareMethod.PREPEXEC.toString()); + boolean callRpcDirectly = callRPCDirectly(params); // Cursors don't use statement pooling. if (isCursorable(executeMethod)) { @@ -1154,9 +1209,14 @@ private boolean doPrepExec(TDSWriter tdsWriter, Parameter[] params, boolean hasN else buildServerCursorExecParams(tdsWriter); } else { + // if it is a parameterized stored procedure call and is not TVP, use sp_execute directly. + if (needsPrepare && callRpcDirectly) { + buildRPCExecParams(tdsWriter); + } // Move overhead of needing to do prepare & unprepare to only use cases that need more than one execution. // First execution, use sp_executesql, optimizing for assumption we will not re-use statement. - if (needsPrepare && !connection.getEnablePrepareOnFirstPreparedStatementCall() && !isExecutedAtLeastOnce) { + else if (needsPrepare && !connection.getEnablePrepareOnFirstPreparedStatementCall() + && !isExecutedAtLeastOnce) { buildExecSQLParams(tdsWriter); isExecutedAtLeastOnce = true; } else if (needsPrepare) { // Second execution, use prepared statements since we seem to be re-using it. @@ -1182,11 +1242,57 @@ private boolean doPrepExec(TDSWriter tdsWriter, Parameter[] params, boolean hasN } } - sendParamsByRPC(tdsWriter, params); + sendParamsByRPC(tdsWriter, params, bReturnValueSyntax, callRpcDirectly); return needsPrepare; } + /** + * Checks if we should call RPC directly for stored procedures + * + * @param params + * @return + * @throws SQLServerException + */ + boolean callRPCDirectly(Parameter[] params) throws SQLServerException { + int paramCount = SQLServerConnection.countParams(userSQL); + + // In order to execute sprocs directly the following must be true: + // 1. There must be a sproc name + // 2. There must be parameters + // 3. Parameters must not be a TVP type + // 4. Compliant CALL escape syntax + // If isExecEscapeSyntax is true, EXEC escape syntax is used then use prior behaviour of + // wrapping call to execute the procedure + return (null != procedureName && paramCount != 0 && !isTVPType(params) && isCallEscapeSyntax + && !isExecEscapeSyntax); + } + + /** + * Checks if the parameter is a TVP type. + * + * @param params + * @return + * @throws SQLServerException + */ + private boolean isTVPType(Parameter[] params) throws SQLServerException { + for (int i = 0; i < params.length; i++) { + if (JDBCType.TVP == params[i].getJdbcType()) { + isTVPType = true; + return true; + } + } + return false; + } + + private boolean isExecEscapeSyntax(String sql) { + return execEscapePattern.matcher(sql).find(); + } + + private boolean isCallEscapeSyntax(String sql) { + return callEscapePattern.matcher(sql).find(); + } + /** * Executes sp_prepare to prepare a parameterized statement and sets the prepared statement handle * @@ -1260,6 +1366,11 @@ private SQLServerResultSet buildExecuteMetaData() throws SQLServerException, SQL * The index specified was outside the number of parameters for the statement. */ final Parameter setterGetParam(int index) throws SQLServerException { + if (!connection.getUseFlexibleCallableStatements() && isSetByName && isSetByIndex) { + SQLServerException.makeFromDriverError(connection, this, + SQLServerException.getErrString("R_noNamedAndIndexedParameters"), null, false); + } + if (index < 1 || index > inOutParam.length) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_indexOutOfRange")); Object[] msgArgs = {index}; @@ -1308,6 +1419,7 @@ final void setSQLXMLInternal(int parameterIndex, SQLXML value) throws SQLServerE @Override public final void setAsciiStream(int parameterIndex, InputStream x) throws SQLException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setAsciiStream", new Object[] {parameterIndex, x}); checkClosed(); @@ -1317,6 +1429,7 @@ public final void setAsciiStream(int parameterIndex, InputStream x) throws SQLEx @Override public final void setAsciiStream(int n, java.io.InputStream x, int length) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setAsciiStream", new Object[] {n, x, length}); checkClosed(); @@ -1326,6 +1439,7 @@ public final void setAsciiStream(int n, java.io.InputStream x, int length) throw @Override public final void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setAsciiStream", new Object[] {parameterIndex, x, length}); checkClosed(); @@ -1335,6 +1449,7 @@ public final void setAsciiStream(int parameterIndex, InputStream x, long length) @Override public final void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setBigDecimal", new Object[] {parameterIndex, x}); checkClosed(); @@ -1345,6 +1460,7 @@ public final void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLServ @Override public final void setBigDecimal(int parameterIndex, BigDecimal x, int precision, int scale) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setBigDecimal", new Object[] {parameterIndex, x, precision, scale}); @@ -1356,6 +1472,7 @@ public final void setBigDecimal(int parameterIndex, BigDecimal x, int precision, @Override public final void setBigDecimal(int parameterIndex, BigDecimal x, int precision, int scale, boolean forceEncrypt) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setBigDecimal", new Object[] {parameterIndex, x, precision, scale, forceEncrypt}); @@ -1366,6 +1483,7 @@ public final void setBigDecimal(int parameterIndex, BigDecimal x, int precision, @Override public final void setMoney(int n, BigDecimal x) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setMoney", new Object[] {n, x}); checkClosed(); @@ -1375,6 +1493,7 @@ public final void setMoney(int n, BigDecimal x) throws SQLServerException { @Override public final void setMoney(int n, BigDecimal x, boolean forceEncrypt) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setMoney", new Object[] {n, x, forceEncrypt}); checkClosed(); @@ -1384,6 +1503,7 @@ public final void setMoney(int n, BigDecimal x, boolean forceEncrypt) throws SQL @Override public final void setSmallMoney(int n, BigDecimal x) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setSmallMoney", new Object[] {n, x}); checkClosed(); @@ -1393,6 +1513,7 @@ public final void setSmallMoney(int n, BigDecimal x) throws SQLServerException { @Override public final void setSmallMoney(int n, BigDecimal x, boolean forceEncrypt) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setSmallMoney", new Object[] {n, x, forceEncrypt}); checkClosed(); @@ -1402,6 +1523,7 @@ public final void setSmallMoney(int n, BigDecimal x, boolean forceEncrypt) throw @Override public final void setBinaryStream(int parameterIndex, InputStream x) throws SQLException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setBinaryStreaml", new Object[] {parameterIndex, x}); checkClosed(); @@ -1411,6 +1533,7 @@ public final void setBinaryStream(int parameterIndex, InputStream x) throws SQLE @Override public final void setBinaryStream(int n, java.io.InputStream x, int length) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setBinaryStream", new Object[] {n, x, length}); checkClosed(); @@ -1420,6 +1543,7 @@ public final void setBinaryStream(int n, java.io.InputStream x, int length) thro @Override public final void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setBinaryStream", new Object[] {parameterIndex, x, length}); checkClosed(); @@ -1429,6 +1553,7 @@ public final void setBinaryStream(int parameterIndex, InputStream x, long length @Override public final void setBoolean(int n, boolean x) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setBoolean", new Object[] {n, x}); checkClosed(); @@ -1438,6 +1563,7 @@ public final void setBoolean(int n, boolean x) throws SQLServerException { @Override public final void setBoolean(int n, boolean x, boolean forceEncrypt) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setBoolean", new Object[] {n, x, forceEncrypt}); checkClosed(); @@ -1447,6 +1573,7 @@ public final void setBoolean(int n, boolean x, boolean forceEncrypt) throws SQLS @Override public final void setByte(int n, byte x) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setByte", new Object[] {n, x}); checkClosed(); @@ -1456,6 +1583,7 @@ public final void setByte(int n, byte x) throws SQLServerException { @Override public final void setByte(int n, byte x, boolean forceEncrypt) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setByte", new Object[] {n, x, forceEncrypt}); checkClosed(); @@ -1465,6 +1593,7 @@ public final void setByte(int n, byte x, boolean forceEncrypt) throws SQLServerE @Override public final void setBytes(int n, byte[] x) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setBytes", new Object[] {n, x}); checkClosed(); @@ -1474,6 +1603,7 @@ public final void setBytes(int n, byte[] x) throws SQLServerException { @Override public final void setBytes(int n, byte[] x, boolean forceEncrypt) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setBytes", new Object[] {n, x, forceEncrypt}); checkClosed(); @@ -1483,6 +1613,7 @@ public final void setBytes(int n, byte[] x, boolean forceEncrypt) throws SQLServ @Override public final void setUniqueIdentifier(int index, String guid) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setUniqueIdentifier", new Object[] {index, guid}); checkClosed(); @@ -1492,6 +1623,7 @@ public final void setUniqueIdentifier(int index, String guid) throws SQLServerEx @Override public final void setUniqueIdentifier(int index, String guid, boolean forceEncrypt) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setUniqueIdentifier", new Object[] {index, guid, forceEncrypt}); @@ -1502,6 +1634,7 @@ public final void setUniqueIdentifier(int index, String guid, boolean forceEncry @Override public final void setDouble(int n, double x) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setDouble", new Object[] {n, x}); checkClosed(); @@ -1511,6 +1644,7 @@ public final void setDouble(int n, double x) throws SQLServerException { @Override public final void setDouble(int n, double x, boolean forceEncrypt) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setDouble", new Object[] {n, x, forceEncrypt}); checkClosed(); @@ -1520,6 +1654,7 @@ public final void setDouble(int n, double x, boolean forceEncrypt) throws SQLSer @Override public final void setFloat(int n, float x) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setFloat", new Object[] {n, x}); checkClosed(); @@ -1529,6 +1664,7 @@ public final void setFloat(int n, float x) throws SQLServerException { @Override public final void setFloat(int n, float x, boolean forceEncrypt) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setFloat", new Object[] {n, x, forceEncrypt}); checkClosed(); @@ -1538,6 +1674,7 @@ public final void setFloat(int n, float x, boolean forceEncrypt) throws SQLServe @Override public final void setGeometry(int n, Geometry x) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setGeometry", new Object[] {n, x}); checkClosed(); @@ -1547,6 +1684,7 @@ public final void setGeometry(int n, Geometry x) throws SQLServerException { @Override public final void setGeography(int n, Geography x) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setGeography", new Object[] {n, x}); checkClosed(); @@ -1556,6 +1694,7 @@ public final void setGeography(int n, Geography x) throws SQLServerException { @Override public final void setInt(int n, int value) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setInt", new Object[] {n, value}); checkClosed(); @@ -1565,6 +1704,7 @@ public final void setInt(int n, int value) throws SQLServerException { @Override public final void setInt(int n, int value, boolean forceEncrypt) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setInt", new Object[] {n, value, forceEncrypt}); checkClosed(); @@ -1574,6 +1714,7 @@ public final void setInt(int n, int value, boolean forceEncrypt) throws SQLServe @Override public final void setLong(int n, long x) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setLong", new Object[] {n, x}); checkClosed(); @@ -1583,6 +1724,7 @@ public final void setLong(int n, long x) throws SQLServerException { @Override public final void setLong(int n, long x, boolean forceEncrypt) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setLong", new Object[] {n, x, forceEncrypt}); checkClosed(); @@ -1592,6 +1734,7 @@ public final void setLong(int n, long x, boolean forceEncrypt) throws SQLServerE @Override public final void setNull(int index, int jdbcType) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setNull", new Object[] {index, jdbcType}); checkClosed(); @@ -1637,6 +1780,7 @@ final void setObjectNoType(int index, Object obj, boolean forceEncrypt) throws S @Override public final void setObject(int index, Object obj) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setObject", new Object[] {index, obj}); checkClosed(); @@ -1646,6 +1790,7 @@ public final void setObject(int index, Object obj) throws SQLServerException { @Override public final void setObject(int n, Object obj, int jdbcType) throws SQLServerException { + setByIndex(); String tvpName = null; if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setObject", new Object[] {n, obj, jdbcType}); @@ -1660,6 +1805,7 @@ public final void setObject(int n, Object obj, int jdbcType) throws SQLServerExc @Override public final void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setObject", new Object[] {parameterIndex, x, targetSqlType, scaleOrLength}); @@ -1683,6 +1829,7 @@ public final void setObject(int parameterIndex, Object x, int targetSqlType, @Override public final void setObject(int parameterIndex, Object x, int targetSqlType, Integer precision, int scale) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setObject", new Object[] {parameterIndex, x, targetSqlType, precision, scale}); @@ -1704,6 +1851,7 @@ public final void setObject(int parameterIndex, Object x, int targetSqlType, Int @Override public final void setObject(int parameterIndex, Object x, int targetSqlType, Integer precision, int scale, boolean forceEncrypt) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setObject", new Object[] {parameterIndex, x, targetSqlType, precision, scale, forceEncrypt}); @@ -1801,6 +1949,7 @@ public final void setObject(int parameterIndex, Object x, SQLType targetSqlType, @Override public final void setShort(int index, short x) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setShort", new Object[] {index, x}); checkClosed(); @@ -1810,6 +1959,7 @@ public final void setShort(int index, short x) throws SQLServerException { @Override public final void setShort(int index, short x, boolean forceEncrypt) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setShort", new Object[] {index, x, forceEncrypt}); checkClosed(); @@ -1819,6 +1969,7 @@ public final void setShort(int index, short x, boolean forceEncrypt) throws SQLS @Override public final void setString(int index, String str) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setString", new Object[] {index, str}); checkClosed(); @@ -1828,6 +1979,7 @@ public final void setString(int index, String str) throws SQLServerException { @Override public final void setString(int index, String str, boolean forceEncrypt) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setString", new Object[] {index, str, forceEncrypt}); checkClosed(); @@ -1837,6 +1989,7 @@ public final void setString(int index, String str, boolean forceEncrypt) throws @Override public final void setNString(int parameterIndex, String value) throws SQLException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setNString", new Object[] {parameterIndex, value}); checkClosed(); @@ -1846,6 +1999,7 @@ public final void setNString(int parameterIndex, String value) throws SQLExcepti @Override public final void setNString(int parameterIndex, String value, boolean forceEncrypt) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setNString", new Object[] {parameterIndex, value, forceEncrypt}); @@ -1856,6 +2010,7 @@ public final void setNString(int parameterIndex, String value, boolean forceEncr @Override public final void setTime(int n, java.sql.Time x) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setTime", new Object[] {n, x}); checkClosed(); @@ -1865,6 +2020,7 @@ public final void setTime(int n, java.sql.Time x) throws SQLServerException { @Override public final void setTime(int n, java.sql.Time x, int scale) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setTime", new Object[] {n, x, scale}); checkClosed(); @@ -1874,6 +2030,7 @@ public final void setTime(int n, java.sql.Time x, int scale) throws SQLServerExc @Override public final void setTime(int n, java.sql.Time x, int scale, boolean forceEncrypt) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setTime", new Object[] {n, x, scale, forceEncrypt}); checkClosed(); @@ -1883,6 +2040,7 @@ public final void setTime(int n, java.sql.Time x, int scale, boolean forceEncryp @Override public final void setTimestamp(int n, java.sql.Timestamp x) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setTimestamp", new Object[] {n, x}); checkClosed(); @@ -1892,6 +2050,7 @@ public final void setTimestamp(int n, java.sql.Timestamp x) throws SQLServerExce @Override public final void setTimestamp(int n, java.sql.Timestamp x, int scale) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setTimestamp", new Object[] {n, x, scale}); checkClosed(); @@ -1902,6 +2061,7 @@ public final void setTimestamp(int n, java.sql.Timestamp x, int scale) throws SQ @Override public final void setTimestamp(int n, java.sql.Timestamp x, int scale, boolean forceEncrypt) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setTimestamp", new Object[] {n, x, scale, forceEncrypt}); checkClosed(); @@ -1911,6 +2071,7 @@ public final void setTimestamp(int n, java.sql.Timestamp x, int scale, @Override public final void setDateTimeOffset(int n, microsoft.sql.DateTimeOffset x) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setDateTimeOffset", new Object[] {n, x}); checkClosed(); @@ -1920,6 +2081,7 @@ public final void setDateTimeOffset(int n, microsoft.sql.DateTimeOffset x) throw @Override public final void setDateTimeOffset(int n, microsoft.sql.DateTimeOffset x, int scale) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setDateTimeOffset", new Object[] {n, x, scale}); checkClosed(); @@ -1930,6 +2092,7 @@ public final void setDateTimeOffset(int n, microsoft.sql.DateTimeOffset x, int s @Override public final void setDateTimeOffset(int n, microsoft.sql.DateTimeOffset x, int scale, boolean forceEncrypt) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setDateTimeOffset", new Object[] {n, x, scale, forceEncrypt}); @@ -1940,6 +2103,7 @@ public final void setDateTimeOffset(int n, microsoft.sql.DateTimeOffset x, int s @Override public final void setDate(int n, java.sql.Date x) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setDate", new Object[] {n, x}); checkClosed(); @@ -1949,6 +2113,7 @@ public final void setDate(int n, java.sql.Date x) throws SQLServerException { @Override public final void setDateTime(int n, java.sql.Timestamp x) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setDateTime", new Object[] {n, x}); checkClosed(); @@ -1958,6 +2123,7 @@ public final void setDateTime(int n, java.sql.Timestamp x) throws SQLServerExcep @Override public final void setDateTime(int n, java.sql.Timestamp x, boolean forceEncrypt) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setDateTime", new Object[] {n, x, forceEncrypt}); checkClosed(); @@ -1967,6 +2133,7 @@ public final void setDateTime(int n, java.sql.Timestamp x, boolean forceEncrypt) @Override public final void setSmallDateTime(int n, java.sql.Timestamp x) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setSmallDateTime", new Object[] {n, x}); checkClosed(); @@ -1976,6 +2143,7 @@ public final void setSmallDateTime(int n, java.sql.Timestamp x) throws SQLServer @Override public final void setSmallDateTime(int n, java.sql.Timestamp x, boolean forceEncrypt) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setSmallDateTime", new Object[] {n, x, forceEncrypt}); checkClosed(); @@ -1985,6 +2153,7 @@ public final void setSmallDateTime(int n, java.sql.Timestamp x, boolean forceEnc @Override public final void setStructured(int n, String tvpName, SQLServerDataTable tvpDataTable) throws SQLServerException { + setByIndex(); tvpName = getTVPNameIfNull(n, tvpName); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setStructured", new Object[] {n, tvpName, tvpDataTable}); @@ -1995,6 +2164,7 @@ public final void setStructured(int n, String tvpName, SQLServerDataTable tvpDat @Override public final void setStructured(int n, String tvpName, ResultSet tvpResultSet) throws SQLServerException { + setByIndex(); tvpName = getTVPNameIfNull(n, tvpName); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setStructured", new Object[] {n, tvpName, tvpResultSet}); @@ -2006,6 +2176,7 @@ public final void setStructured(int n, String tvpName, ResultSet tvpResultSet) t @Override public final void setStructured(int n, String tvpName, ISQLServerDataRecord tvpBulkRecord) throws SQLServerException { + setByIndex(); tvpName = getTVPNameIfNull(n, tvpName); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setStructured", new Object[] {n, tvpName, tvpBulkRecord}); @@ -3035,7 +3206,7 @@ final void doExecutePreparedStatementBatch(PrepStmtBatchExecCmd batchCommand) th resetForReexecute(); tdsWriter = batchCommand.startRequest(TDS.PKT_RPC); buildExecParams(tdsWriter); - sendParamsByRPC(tdsWriter, batchParam); + sendParamsByRPC(tdsWriter, batchParam, bReturnValueSyntax, false); ensureExecuteResultsReader( batchCommand.startResponse(getIsResponseBufferingAdaptive())); startResults(); @@ -3129,6 +3300,7 @@ public final boolean getUseFmtOnly() throws SQLServerException { @Override public final void setCharacterStream(int parameterIndex, Reader reader) throws SQLException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setCharacterStream", new Object[] {parameterIndex, reader}); checkClosed(); @@ -3138,6 +3310,7 @@ public final void setCharacterStream(int parameterIndex, Reader reader) throws S @Override public final void setCharacterStream(int n, java.io.Reader reader, int length) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setCharacterStream", new Object[] {n, reader, length}); checkClosed(); @@ -3147,6 +3320,7 @@ public final void setCharacterStream(int n, java.io.Reader reader, int length) t @Override public final void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setCharacterStream", new Object[] {parameterIndex, reader, length}); @@ -3157,6 +3331,7 @@ public final void setCharacterStream(int parameterIndex, Reader reader, long len @Override public final void setNCharacterStream(int parameterIndex, Reader value) throws SQLException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setNCharacterStream", new Object[] {parameterIndex, value}); checkClosed(); @@ -3166,6 +3341,7 @@ public final void setNCharacterStream(int parameterIndex, Reader value) throws S @Override public final void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setNCharacterStream", new Object[] {parameterIndex, value, length}); @@ -3181,6 +3357,7 @@ public final void setRef(int i, java.sql.Ref x) throws SQLException { @Override public final void setBlob(int i, java.sql.Blob x) throws SQLException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setBlob", new Object[] {i, x}); checkClosed(); @@ -3190,6 +3367,7 @@ public final void setBlob(int i, java.sql.Blob x) throws SQLException { @Override public final void setBlob(int parameterIndex, InputStream inputStream) throws SQLException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setBlob", new Object[] {parameterIndex, inputStream}); checkClosed(); @@ -3200,6 +3378,7 @@ public final void setBlob(int parameterIndex, InputStream inputStream) throws SQ @Override public final void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setBlob", new Object[] {parameterIndex, inputStream, length}); @@ -3210,6 +3389,7 @@ public final void setBlob(int parameterIndex, InputStream inputStream, long leng @Override public final void setClob(int parameterIndex, java.sql.Clob clobValue) throws SQLException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setClob", new Object[] {parameterIndex, clobValue}); checkClosed(); @@ -3219,6 +3399,7 @@ public final void setClob(int parameterIndex, java.sql.Clob clobValue) throws SQ @Override public final void setClob(int parameterIndex, Reader reader) throws SQLException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setClob", new Object[] {parameterIndex, reader}); checkClosed(); @@ -3228,6 +3409,7 @@ public final void setClob(int parameterIndex, Reader reader) throws SQLException @Override public final void setClob(int parameterIndex, Reader reader, long length) throws SQLException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setClob", new Object[] {parameterIndex, reader, length}); checkClosed(); @@ -3237,6 +3419,7 @@ public final void setClob(int parameterIndex, Reader reader, long length) throws @Override public final void setNClob(int parameterIndex, NClob value) throws SQLException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setNClob", new Object[] {parameterIndex, value}); checkClosed(); @@ -3246,6 +3429,7 @@ public final void setNClob(int parameterIndex, NClob value) throws SQLException @Override public final void setNClob(int parameterIndex, Reader reader) throws SQLException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setNClob", new Object[] {parameterIndex, reader}); checkClosed(); @@ -3255,6 +3439,7 @@ public final void setNClob(int parameterIndex, Reader reader) throws SQLExceptio @Override public final void setNClob(int parameterIndex, Reader reader, long length) throws SQLException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setNClob", new Object[] {parameterIndex, reader, length}); checkClosed(); @@ -3269,6 +3454,7 @@ public final void setArray(int i, java.sql.Array x) throws SQLException { @Override public final void setDate(int n, java.sql.Date x, java.util.Calendar cal) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setDate", new Object[] {n, x, cal}); checkClosed(); @@ -3279,6 +3465,7 @@ public final void setDate(int n, java.sql.Date x, java.util.Calendar cal) throws @Override public final void setDate(int n, java.sql.Date x, java.util.Calendar cal, boolean forceEncrypt) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setDate", new Object[] {n, x, cal, forceEncrypt}); checkClosed(); @@ -3288,6 +3475,7 @@ public final void setDate(int n, java.sql.Date x, java.util.Calendar cal, @Override public final void setTime(int n, java.sql.Time x, java.util.Calendar cal) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setTime", new Object[] {n, x, cal}); checkClosed(); @@ -3298,6 +3486,7 @@ public final void setTime(int n, java.sql.Time x, java.util.Calendar cal) throws @Override public final void setTime(int n, java.sql.Time x, java.util.Calendar cal, boolean forceEncrypt) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setTime", new Object[] {n, x, cal, forceEncrypt}); checkClosed(); @@ -3307,6 +3496,7 @@ public final void setTime(int n, java.sql.Time x, java.util.Calendar cal, @Override public final void setTimestamp(int n, java.sql.Timestamp x, java.util.Calendar cal) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setTimestamp", new Object[] {n, x, cal}); checkClosed(); @@ -3318,6 +3508,7 @@ public final void setTimestamp(int n, java.sql.Timestamp x, java.util.Calendar c @Override public final void setTimestamp(int n, java.sql.Timestamp x, java.util.Calendar cal, boolean forceEncrypt) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setTimestamp", new Object[] {n, x, cal, forceEncrypt}); checkClosed(); @@ -3328,6 +3519,7 @@ public final void setTimestamp(int n, java.sql.Timestamp x, java.util.Calendar c @Override public final void setNull(int paramIndex, int sqlType, String typeName) throws SQLServerException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setNull", new Object[] {paramIndex, sqlType, typeName}); checkClosed(); @@ -3377,6 +3569,7 @@ public final void setRowId(int parameterIndex, RowId x) throws SQLException { @Override public final void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException { + setByIndex(); if (loggerExternal.isLoggable(java.util.logging.Level.FINER)) loggerExternal.entering(getClassNameLogging(), "setSQLXML", new Object[] {parameterIndex, xmlObject}); checkClosed(); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java index 64e140534..246c6cf60 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java @@ -144,6 +144,8 @@ protected Object[][] getContents() { {"R_stringNotInHex", "The string is not in a valid hex format."}, {"R_unknownType", "The Java type {0} is not a supported type."}, {"R_physicalConnectionIsClosed", "The physical connection is closed for this pooled connection."}, + {"R_noNamedAndIndexedParameters", "Cannot specify both named and indexed parameters when 'useFlexibleCallableStatements=false'"}, + {"R_unknownOutputParameter", "Cannot acquire output parameter value by name. No parameter index was associated with the output parameter name. If acquiring output parameter by name, verify that the output parameter was initially registered by name."}, {"R_invalidDataSourceReference", "Invalid DataSource reference."}, {"R_cantGetColumnValueFromDeletedRow", "Cannot get a value from a deleted row."}, {"R_cantGetUpdatedColumnValue", "Updated columns cannot be accessed until updateRow() or cancelRowUpdates() has been called."}, @@ -198,6 +200,7 @@ protected Object[][] getContents() { {"R_disableStatementPoolingPropertyDescription", "Disables the statement pooling feature."}, {"R_integratedSecurityPropertyDescription", "Indicates whether Windows authentication will be used to connect to SQL Server."}, {"R_useDefaultGSSCredentialPropertyDescription", "Indicates whether GSSCredential will be created using native GSS-API."}, + {"R_useFlexibleCallableStatementsPropertyDescription", "Indicates whether sp_sproc_columns will be used for parameter name lookup when setting or registering parameters for callable statements."}, {"R_authenticationSchemePropertyDescription", "The authentication scheme to be used for integrated authentication."}, {"R_lockTimeoutPropertyDescription", "The number of milliseconds to wait before the database reports a lock time-out."}, {"R_connectRetryCountPropertyDescription", "The number of reconnection attempts if there is a connection failure."}, diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java index b599856a7..f27e1b1ac 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java @@ -64,7 +64,15 @@ public class SQLServerStatement implements ISQLServerStatement { private static final String ACTIVITY_ID = " ActivityId: "; /** response buffer adaptive flag */ - private boolean isResponseBufferingAdaptive = false; + boolean isResponseBufferingAdaptive = false; + + /** TDS token return value status **/ + int returnValueStatus; + + /** Check if statement contains TVP Type */ + boolean isTVPType = false; + + protected static final int USER_DEFINED_FUNCTION_RETURN_STATUS = 2; final boolean getIsResponseBufferingAdaptive() { return isResponseBufferingAdaptive; @@ -112,10 +120,13 @@ final boolean wasExecuted() { return null != tdsReader; } + /** Return parameter for stored procedure calls */ + transient Parameter returnParam; + /** * The input and out parameters for statement execution. */ - transient Parameter[] inOutParam; // Parameters for prepared stmts and stored procedures + transient Parameter[] inOutParam = null; // Parameters for prepared stmts and stored procedures /** * The statement's connection. @@ -138,6 +149,12 @@ final boolean wasExecuted() { */ boolean isCloseOnCompletion = false; + /** Checks if the callable statement's parameters are set by name **/ + protected boolean isSetByName = false; + + /** Checks if the prepared statement's parameters were set by index **/ + protected boolean isSetByIndex = false; + /** * Currently executing or most recently executed TDSCommand (statement cmd, server cursor cmd, ...) subject to * cancellation through Statement.cancel. @@ -1133,6 +1150,14 @@ void checkClosed() throws SQLServerException { } } + void setByIndex() throws SQLServerException { + isSetByIndex = true; + if (!connection.getUseFlexibleCallableStatements() && isSetByName && isSetByIndex) { + SQLServerException.makeFromDriverError(connection, this, + SQLServerException.getErrString("R_noNamedAndIndexedParameters"), null, false); + } + } + /* ---------------- JDBC API methods ------------------ */ @Override @@ -1622,6 +1647,14 @@ boolean onRetStatus(TDSReader tdsReader) throws SQLServerException { else { procedureRetStatToken = new StreamRetStatus(); procedureRetStatToken.setFromTDS(tdsReader); + // Only read the return value from stored procedure if we are expecting one. Also, check that it is + // not cursorable and not TVP type. For these two, the driver is still following the old behavior of + // executing sp_executesql for stored procedures. + if (!isCursorable(executeMethod) && !isTVPType && null != inOutParam && inOutParam.length > 0 + && inOutParam[0].isReturnValue()) { + inOutParam[0].setFromReturnStatus(procedureRetStatToken.getStatus(), connection); + return false; + } } return true; @@ -1629,11 +1662,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 != USER_DEFINED_FUNCTION_RETURN_STATUS) { Parameter p = new Parameter( Util.shouldHonorAEForParameters(stmtColumnEncriptionSetting, connection)); p.skipRetValStatus(tdsReader); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerXAResource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerXAResource.java index fa669aaea..5c451b22b 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerXAResource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerXAResource.java @@ -414,8 +414,8 @@ private XAReturnValue dtc_XA_interface(int nType, Xid xid, int xaFlags) throws X initCS = (SQLServerCallableStatement) controlConnection .prepareCall("{call master..xp_sqljdbc_xa_init_ex(?, ?,?)}"); initCS.registerOutParameter(1, Types.INTEGER); // Return status - initCS.registerOutParameter(2, Types.CHAR); // Return error message - initCS.registerOutParameter(3, Types.CHAR); // Return version number + initCS.registerOutParameterNonPLP(2, Types.CHAR); // Return error message + initCS.registerOutParameterNonPLP(3, Types.CHAR); // Return version number try { initCS.execute(); } catch (SQLServerException eX) { @@ -525,21 +525,21 @@ private XAReturnValue dtc_XA_interface(int nType, Xid xid, int xaFlags) throws X sContext = "START:"; cs = getXACallableStatementHandle(XA_START); cs.registerOutParameter(n++, Types.INTEGER); // Return status - cs.registerOutParameter(n++, Types.CHAR); // Return error message + cs.registerOutParameterNonPLP(n++, Types.CHAR); // Return error message cs.setBytes(n++, gid); // Global XID cs.setBytes(n++, bid); // Branch ID cs.setInt(n++, xaFlags); // XA transaction flags - cs.registerOutParameter(n++, Types.BINARY); // Returned OLE transaction cookie + cs.registerOutParameterNonPLP(n++, Types.BINARY); // Returned OLE transaction cookie cs.setInt(n++, timeoutSeconds); // Transaction timeout in seconds. cs.setInt(n++, formatId); // Format ID - cs.registerOutParameter(n++, Types.CHAR); // DLL Version number + cs.registerOutParameterNonPLP(n++, Types.CHAR); // DLL Version number cs.setInt(n++, Integer.parseInt(version)); // Version of SQL Server cs.setInt(n++, instanceName.length()); // Length of SQL Server instance name cs.setBytes(n++, instanceName.getBytes()); // SQL Server instance name cs.setInt(n++, architectureMSSQL); // Architecture of SQL Server cs.setInt(n++, architectureOS); // Architecture of OS running SQL Server cs.setInt(n++, isTransacrionTimeoutSet); // pass 1 if setTransactionTimeout() is called - cs.registerOutParameter(n++, Types.BINARY); // Return UoW + cs.registerOutParameterNonPLP(n++, Types.BINARY); // Return UoW break; @@ -547,12 +547,12 @@ private XAReturnValue dtc_XA_interface(int nType, Xid xid, int xaFlags) throws X sContext = "END:"; cs = getXACallableStatementHandle(XA_END); cs.registerOutParameter(n++, Types.INTEGER); - cs.registerOutParameter(n++, Types.CHAR); + cs.registerOutParameterNonPLP(n++, Types.CHAR); cs.setBytes(n++, gid); cs.setBytes(n++, bid); cs.setInt(n++, xaFlags); cs.setInt(n++, formatId); - cs.registerOutParameter(n++, Types.BINARY); // Return UoW + cs.registerOutParameterNonPLP(n++, Types.BINARY); // Return UoW break; case XA_PREPARE: @@ -563,7 +563,7 @@ private XAReturnValue dtc_XA_interface(int nType, Xid xid, int xaFlags) throws X cs = getXACallableStatementHandle(XA_PREPARE); cs.registerOutParameter(n++, Types.INTEGER); - cs.registerOutParameter(n++, Types.CHAR); + cs.registerOutParameterNonPLP(n++, Types.CHAR); cs.setBytes(n++, gid); cs.setBytes(n++, bid); if ((SSTRANSTIGHTLYCPLD & xaFlags) == SSTRANSTIGHTLYCPLD) @@ -575,7 +575,7 @@ private XAReturnValue dtc_XA_interface(int nType, Xid xid, int xaFlags) throws X sContext = "COMMIT:"; cs = getXACallableStatementHandle(XA_COMMIT); cs.registerOutParameter(n++, Types.INTEGER); - cs.registerOutParameter(n++, Types.CHAR); + cs.registerOutParameterNonPLP(n++, Types.CHAR); cs.setBytes(n++, gid); cs.setBytes(n++, bid); cs.setInt(n++, xaFlags); @@ -590,7 +590,7 @@ private XAReturnValue dtc_XA_interface(int nType, Xid xid, int xaFlags) throws X cs = getXACallableStatementHandle(XA_ROLLBACK); cs.registerOutParameter(n++, Types.INTEGER); - cs.registerOutParameter(n++, Types.CHAR); + cs.registerOutParameterNonPLP(n++, Types.CHAR); cs.setBytes(n++, gid); cs.setBytes(n++, bid); if ((SSTRANSTIGHTLYCPLD & xaFlags) == SSTRANSTIGHTLYCPLD) @@ -605,7 +605,7 @@ private XAReturnValue dtc_XA_interface(int nType, Xid xid, int xaFlags) throws X else cs = getXACallableStatementHandle(XA_FORGET); cs.registerOutParameter(n++, Types.INTEGER); - cs.registerOutParameter(n++, Types.CHAR); + cs.registerOutParameterNonPLP(n++, Types.CHAR); cs.setBytes(n++, gid); cs.setBytes(n++, bid); if ((SSTRANSTIGHTLYCPLD & xaFlags) == SSTRANSTIGHTLYCPLD) @@ -617,9 +617,9 @@ private XAReturnValue dtc_XA_interface(int nType, Xid xid, int xaFlags) throws X sContext = "RECOVER:"; cs = getXACallableStatementHandle(XA_RECOVER); cs.registerOutParameter(n++, Types.INTEGER); - cs.registerOutParameter(n++, Types.CHAR); + cs.registerOutParameterNonPLP(n++, Types.CHAR); cs.setInt(n++, xaFlags); - cs.registerOutParameter(n++, Types.BINARY); + cs.registerOutParameterNonPLP(n++, Types.BINARY); // Format Id need not be sent for recover action break; default: diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java b/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java index 9ba2e83ce..49bb63496 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java @@ -135,6 +135,8 @@ final class DTV { int valueLength = 0; boolean sendStringParametersAsUnicode = true; + boolean isNonPLP = false; + /** * Sets a DTV value from a Java object. * @@ -295,7 +297,7 @@ void execute(DTV dtv, String strValue) throws SQLServerException { if (dtv.getJdbcType() == JDBCType.GUID) { tdsWriter.writeRPCUUID(name, UUID.fromString(strValue), isOutParam); } else { - tdsWriter.writeRPCStringUnicode(name, strValue, isOutParam, collation); + tdsWriter.writeRPCStringUnicode(name, strValue, isOutParam, collation, dtv.isNonPLP); } } @@ -318,7 +320,7 @@ void execute(DTV dtv, Clob clobValue) throws SQLServerException { if (null != collation && (JDBCType.CHAR == jdbcType || JDBCType.VARCHAR == jdbcType || JDBCType.LONGVARCHAR == jdbcType || JDBCType.CLOB == jdbcType)) { if (null == clobReader) { - tdsWriter.writeRPCByteArray(name, null, isOutParam, jdbcType, collation); + tdsWriter.writeRPCByteArray(name, null, isOutParam, jdbcType, collation, false); } else { ReaderInputStream clobStream = new ReaderInputStream(clobReader, collation.getCharset(), clobLength); @@ -329,7 +331,7 @@ void execute(DTV dtv, Clob clobValue) throws SQLServerException { } else // Send CLOB value as Unicode { if (null == clobReader) { - tdsWriter.writeRPCStringUnicode(name, null, isOutParam, collation); + tdsWriter.writeRPCStringUnicode(name, null, isOutParam, collation, false); } else { tdsWriter.writeRPCReaderUnicode(name, clobReader, clobLength, isOutParam, collation); } @@ -852,14 +854,40 @@ private void sendTemporal(DTV dtv, JavaType javaType, Object value) throws SQLSe switch (jdbcType) { case DATETIME: + if (null != cryptoMeta) { + tdsWriter.writeEncryptedRPCDateTime(name, + timestampNormalizedCalendar(calendar, javaType, conn.baseYear()), + subSecondNanos, isOutParam, jdbcType, statement); + } else { + if (conn.getDatetimeParameterType().equals(DatetimeType.DATETIME2.toString())) { + tdsWriter.writeRPCDateTime2(name, + timestampNormalizedCalendar(calendar, javaType, conn.baseYear()), + subSecondNanos, TDS.DEFAULT_FRACTIONAL_SECONDS_SCALE, isOutParam); + } else if (conn.getDatetimeParameterType().equals(DatetimeType.DATETIME.toString())) { + tdsWriter.writeRPCDateTime(name, + timestampNormalizedCalendar(calendar, javaType, conn.baseYear()), + subSecondNanos, isOutParam); + } + } + + break; + case SMALLDATETIME: + if (null != cryptoMeta) { + tdsWriter.writeEncryptedRPCDateTime(name, + timestampNormalizedCalendar(calendar, javaType, conn.baseYear()), + subSecondNanos, isOutParam, jdbcType, statement); + } else { + tdsWriter.writeRPCDateTime(name, + timestampNormalizedCalendar(calendar, javaType, conn.baseYear()), + subSecondNanos, isOutParam); + } + + break; + case TIMESTAMP: if (null != cryptoMeta) { - if ((JDBCType.DATETIME == jdbcType) || (JDBCType.SMALLDATETIME == jdbcType)) { - tdsWriter.writeEncryptedRPCDateTime(name, - timestampNormalizedCalendar(calendar, javaType, conn.baseYear()), - subSecondNanos, isOutParam, jdbcType, statement); - } else if (0 == valueLength) { + if (0 == valueLength) { tdsWriter.writeEncryptedRPCDateTime2(name, timestampNormalizedCalendar(calendar, javaType, conn.baseYear()), subSecondNanos, outScale, isOutParam, statement); @@ -868,10 +896,11 @@ private void sendTemporal(DTV dtv, JavaType javaType, Object value) throws SQLSe timestampNormalizedCalendar(calendar, javaType, conn.baseYear()), subSecondNanos, (valueLength), isOutParam, statement); } - } else + } else { tdsWriter.writeRPCDateTime2(name, timestampNormalizedCalendar(calendar, javaType, conn.baseYear()), subSecondNanos, TDS.MAX_FRACTIONAL_SECONDS_SCALE, isOutParam); + } break; @@ -1081,7 +1110,7 @@ void execute(DTV dtv, BigDecimal bigDecimalValue) throws SQLServerException { DriverError.NOT_SET, null); } else { String strValue = bigDecimalValue.toString(); - tdsWriter.writeRPCStringUnicode(name, strValue, isOutParam, collation); + tdsWriter.writeRPCStringUnicode(name, strValue, isOutParam, collation, false); } } else { tdsWriter.writeRPCBigDecimal(name, bigDecimalValue, outScale, isOutParam); @@ -1136,7 +1165,8 @@ void execute(DTV dtv, byte[] byteArrayValue) throws SQLServerException { } } else - tdsWriter.writeRPCByteArray(name, byteArrayValue, isOutParam, dtv.getJdbcType(), collation); + tdsWriter.writeRPCByteArray(name, byteArrayValue, isOutParam, dtv.getJdbcType(), collation, + dtv.isNonPLP); } @@ -1389,7 +1419,7 @@ void execute(DTV dtv, Blob blobValue) throws SQLServerException { } if (null == blobStream) { - tdsWriter.writeRPCByteArray(name, null, isOutParam, dtv.getJdbcType(), collation); + tdsWriter.writeRPCByteArray(name, null, isOutParam, dtv.getJdbcType(), collation, false); } else { tdsWriter.writeRPCInputStream(name, blobStream, blobLength, isOutParam, dtv.getJdbcType(), collation); } @@ -2338,8 +2368,10 @@ Object getValue(DTV dtv, JDBCType jdbcType, int scale, InputStreamGetterArgs str TypeInfo typeInfo, CryptoMetadata cryptoMetadata, TDSReader tdsReader, SQLServerStatement statement) throws SQLServerException { // Client side type conversion is not supported - if (this.jdbcType != jdbcType) + // Checking for sql_variant here since the check will be performed elsewhere. + if (this.jdbcType != jdbcType && jdbcType != JDBCType.SQL_VARIANT) { DataTypes.throwConversionError(this.jdbcType.toString(), jdbcType.toString()); + } return value; } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/tdsparser.java b/src/main/java/com/microsoft/sqlserver/jdbc/tdsparser.java index a88bcc62c..90eebb8df 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/tdsparser.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/tdsparser.java @@ -240,6 +240,17 @@ boolean onRetStatus(TDSReader tdsReader) throws SQLServerException { } boolean onRetValue(TDSReader tdsReader) throws SQLServerException { + // Very unlikely to return true. If we do, it was because any return values in the + // tds response were never read after the RPC. If they were never read, it's safe to skip + // them here + if (this.logContext.equals("ExecDoneHandler")) { + Parameter param = new Parameter(false); + param.skipRetValStatus(tdsReader); + param.skipValue(tdsReader, true); + + return true; + } + TDSParser.throwUnexpectedTokenException(tdsReader, logContext); return false; } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java index 1ed9749cc..e024e98cb 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java @@ -206,6 +206,9 @@ public void testDataSource() throws SQLServerException { assertEquals(booleanPropValue, ds.getUseDefaultGSSCredential(), TestResource.getResource("R_valuesAreDifferent")); + ds.setUseFlexibleCallableStatements(booleanPropValue); + assertEquals(booleanPropValue, ds.getUseFlexibleCallableStatements(), + TestResource.getResource("R_valuesAreDifferent")); ds.setCalcBigDecimalPrecision(booleanPropValue); assertEquals(booleanPropValue, ds.getCalcBigDecimalPrecision(), TestResource.getResource("R_valuesAreDifferent")); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java b/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java index 29a5e21dd..ae34ef0c0 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java @@ -44,7 +44,8 @@ protected Object[][] getContents() { {"R_timeValueTruncated", " The time value is truncated or not correct!"}, {"R_nullPointerExceptionFromResultSet", "Cannot invoke \"java.sql.ResultSet.next()\" because \"rs\" is null"}, {"R_invalidErrorMessage", "Invalid Error Message: "}, - {"R_kerberosNativeGSSFailure", "No valid credentials provided (Mechanism level: Failed to find any Kerberos tgt)"}, + {"R_kerberosNativeGSSFailure", + "No valid credentials provided (Mechanism level: Failed to find any Kerberos tgt)"}, {"R_expectedFailPassed", "Expected failure did not fail"}, {"R_dataTypeNotFound", "Cannot find data type"}, {"R_illegalCharWktPosition", "Illegal character in Well-Known text at position {0}."}, {"R_illegalCharWkt", "Illegal Well-Known text. Please make sure Well-Known text is valid."}, @@ -56,6 +57,10 @@ protected Object[][] getContents() { {"R_createDropAlterTableFailed", "Create/drop/alter table with preparedStatement failed!"}, {"R_grantFailed", "grant table with preparedStatement failed!"}, {"R_connectionIsClosed", "The connection is closed."}, + {"R_noNamedAndIndexedParameters", + "Cannot specify both named and indexed parameters when 'useFlexibleCallableStatements=false'"}, + {"R_unknownOutputParameter", + "Cannot acquire output parameter value by name. No parameter index was associated with the output parameter name. If acquiring output parameter by name, verify that the output parameter was initially registered by name."}, {"R_ConnectionURLNull", "The connection URL is null."}, {"R_connectionIsNotClosed", "The connection is not closed."}, {"R_invalidExceptionMessage", "Invalid exception message"}, @@ -68,6 +73,7 @@ protected Object[][] getContents() { {"R_deadConnection", "Dead connection should be invalid"}, {"R_wrongExceptionMessage", "Wrong exception message"}, {"R_wrongSqlState", "Wrong sql state"}, {"R_parameterNotDefined", "Parameter {0} was not defined"}, + {"R_notValidParameterForProcedure", "{0} is not a parameter for procedure {1}."}, {"R_unexpectedExceptionContent", "Unexpected content in exception message"}, {"R_connectionClosed", "The connection has been closed"}, {"R_conversionFailed", "Conversion failed when converting {0} to {1} data type"}, diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableStatementTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableStatementTest.java index f2d102d92..f2f5fbcca 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableStatementTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableStatementTest.java @@ -22,7 +22,6 @@ import java.util.TimeZone; import java.util.UUID; -import com.microsoft.sqlserver.testframework.PrepUtil; import org.junit.Assert; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -36,6 +35,8 @@ import com.microsoft.sqlserver.jdbc.SQLServerDataSource; import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.jdbc.TestUtils; +import com.microsoft.sqlserver.jdbc.SQLServerDataTable; +import com.microsoft.sqlserver.testframework.PrepUtil; import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.Constants; @@ -75,10 +76,16 @@ public class CallableStatementTest extends AbstractTest { .escapeIdentifier(RandomUtil.getIdentifier("manyParam_definedType")); private static String zeroParamSproc = AbstractSQLGenerator .escapeIdentifier(RandomUtil.getIdentifier("zeroParamSproc")); + private static String outOfOrderSproc = AbstractSQLGenerator + .escapeIdentifier(RandomUtil.getIdentifier("outOfOrderSproc")); + private static String byParamNameSproc = AbstractSQLGenerator + .escapeIdentifier(RandomUtil.getIdentifier("byParamNameSproc")); + private static String userDefinedFunction = AbstractSQLGenerator + .escapeIdentifier(RandomUtil.getIdentifier("userDefinedFunction")); /** * Setup before test - * + * * @throws SQLException */ @BeforeAll @@ -92,9 +99,12 @@ public static void setupTest() throws Exception { TestUtils.dropProcedureIfExists(inputParamsProcedureName, stmt); TestUtils.dropProcedureIfExists(getObjectLocalDateTimeProcedureName, stmt); TestUtils.dropProcedureIfExists(getObjectOffsetDateTimeProcedureName, stmt); + TestUtils.dropProcedureIfExists(zeroParamSproc, stmt); + TestUtils.dropProcedureIfExists(outOfOrderSproc, stmt); + TestUtils.dropProcedureIfExists(byParamNameSproc, stmt); TestUtils.dropProcedureIfExists(conditionalSproc, stmt); TestUtils.dropProcedureIfExists(simpleRetValSproc, stmt); - TestUtils.dropProcedureIfExists(zeroParamSproc, stmt); + TestUtils.dropFunctionIfExists(userDefinedFunction, stmt); TestUtils.dropUserDefinedTypeIfExists(manyParamUserDefinedType, stmt); TestUtils.dropProcedureIfExists(manyParamProc, stmt); TestUtils.dropTableIfExists(manyParamsTable, stmt); @@ -107,11 +117,25 @@ public static void setupTest() throws Exception { createUserDefinedType(); createTableManyParams(); createProcedureManyParams(); - createProcedureZeroParams(); createProcedureCurrentTime(); createGetObjectOffsetDateTimeProcedure(stmt); + createProcedureZeroParams(); + createOutOfOrderSproc(); + createByParamNameSproc(); createConditionalProcedure(); createSimpleRetValSproc(); + createUserDefinedFunction(); + } + } + + @Test + public void testCallableStatementClosedConnection() { + try (SQLServerCallableStatement stmt = (SQLServerCallableStatement) connection.prepareCall("sproc")) { + stmt.close(); // Prematurely close the statement, which causes inOutParams to be null. + stmt.setStructured("myParam", "myTvp", (SQLServerDataTable) null); + fail(TestResource.getResource("R_expectedFailPassed")); + } catch (Exception e) { + assertEquals(TestResource.getResource("R_statementClosed"), e.getMessage()); } } @@ -186,7 +210,7 @@ public void testCallableStatementSpPrepare() throws SQLException { /** * Tests CallableStatement.getString() with uniqueidentifier parameter - * + * * @throws SQLException */ @Test @@ -211,7 +235,7 @@ public void getStringGUIDTest() throws SQLException { /** * test for setNull(index, varchar) to behave as setNull(index, nvarchar) when SendStringParametersAsUnicode is true - * + * * @throws SQLException */ @Test @@ -287,7 +311,7 @@ public void testGetObjectAsLocalDateTime() throws SQLException { /** * Tests getObject(n, java.time.OffsetDateTime.class) and getObject(n, java.time.OffsetTime.class). - * + * * @throws SQLException */ @Test @@ -317,7 +341,7 @@ public void testGetObjectAsOffsetDateTime() throws SQLException { /** * recognize parameter names with and without leading '@' - * + * * @throws SQLException */ @Test @@ -380,6 +404,689 @@ public void testZeroParamSproc() throws SQLException { } } + @Test + public void testSprocCastingError() { + String call = "{? = CALL " + zeroParamSproc + "}"; + + try (CallableStatement cs = connection.prepareCall(call)) { + cs.registerOutParameter(1, Types.BINARY); + cs.execute(); // Should not be able to get return value as bytes + cs.getBytes(1); + fail(TestResource.getResource("R_expectedFailPassed")); + } catch (Exception e) { + assertTrue(e.getMessage().contains("cannot be cast to")); + } + } + + @Test + public void testNonOrderedRegisteringAndSettingOfIndexedAndNamedParams() throws SQLException { + String call = "{CALL " + outOfOrderSproc + " (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)}"; + + try (CallableStatement cstmt = connection.prepareCall(call)) { + int scale = 6; + Double obj1 = 2015.0123; + Double obj2 = 2015.012345; + Integer obj3 = -3; + Float obj4 = 2015.04f; + Integer obj5 = 3; + String obj6 = "foo"; + String obj7 = "b"; + Long obj8 = 2015L; + + // Set & register parameters using a mix of index and named parameters in a random + // non-consecutive order. When useFlexibleCallableStatements=true (the default), this should pass. + cstmt.setObject("i5", obj5, Types.CHAR); + cstmt.setObject("i6", obj6, Types.VARCHAR); + cstmt.setObject(7, obj7, Types.CHAR); + cstmt.setObject("i8", obj8, Types.SMALLINT); + + cstmt.setObject(1, obj1, Types.NUMERIC); + cstmt.setObject(2, obj2, Types.NUMERIC, scale); + cstmt.setObject(3, obj3, Types.INTEGER); + cstmt.setObject(4, obj4, Types.FLOAT); + + cstmt.registerOutParameter(13, Types.CHAR); + cstmt.registerOutParameter("o6", Types.VARCHAR); + cstmt.registerOutParameter(15, Types.CHAR); + cstmt.registerOutParameter(16, Types.SMALLINT); + + cstmt.registerOutParameter(9, Types.NUMERIC); + cstmt.registerOutParameter("o2", Types.NUMERIC, scale); + cstmt.registerOutParameter(11, Types.INTEGER); + cstmt.registerOutParameter("o4", Types.FLOAT); + + cstmt.execute(); + + // When useFlexibleCallableStatements=true (the default), getting the output parameters by either + // index or named should succeed. + assertEquals(obj1, cstmt.getDouble("o1")); + assertEquals(obj2, cstmt.getDouble("o2")); + assertEquals(obj3, cstmt.getInt("o3")); + assertEquals(obj4, cstmt.getFloat("o4")); + assertEquals(obj5, cstmt.getInt("o5")); + assertEquals(obj6, cstmt.getString("o6")); + assertEquals(obj7, cstmt.getString("o7")); + assertEquals(obj8, cstmt.getLong("o8")); + + assertEquals(obj1, cstmt.getDouble(9)); + assertEquals(obj2, cstmt.getDouble(10)); + assertEquals(obj3, cstmt.getInt(11)); + assertEquals(obj4, cstmt.getFloat(12)); + assertEquals(obj5, cstmt.getInt(13)); + assertEquals(obj6, cstmt.getString(14)); + assertEquals(obj7, cstmt.getString(15)); + assertEquals(obj8, cstmt.getLong(16)); + } + } + + @Test + public void useFlexibleCallableStatementFalseIndexedParameters() throws SQLException { + String call = "{CALL " + outOfOrderSproc + " (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)}"; + String connectionString = TestUtils.addOrOverrideProperty(getConnectionString(), + "useFlexibleCallableStatements", "false"); + + // When useFlexibleCallableStatement=false and when using only indexed parameters, cstmt should succeed. + try (Connection conn = DriverManager.getConnection(connectionString)) { + try (CallableStatement cstmt = conn.prepareCall(call)) { + int scale = 6; + double obj1 = 2015.0123; + double obj2 = 2015.012345; + int obj3 = -3; + float obj4 = 2015.04f; + int obj5 = 3; + String obj6 = "foo"; + String obj7 = "b"; + long obj8 = 2015L; + + cstmt.setObject(1, obj1, Types.NUMERIC); + cstmt.setObject(2, obj2, Types.NUMERIC, scale); + cstmt.setObject(3, obj3, Types.INTEGER); + cstmt.setObject(4, obj4, Types.FLOAT); + + cstmt.setObject(5, obj5, Types.CHAR); + cstmt.setObject(6, obj6, Types.VARCHAR); + cstmt.setObject(7, obj7, Types.CHAR); + cstmt.setObject(8, obj8, Types.SMALLINT); + + cstmt.registerOutParameter(9, Types.NUMERIC); + cstmt.registerOutParameter(10, Types.NUMERIC, scale); + cstmt.registerOutParameter(11, Types.INTEGER); + cstmt.registerOutParameter(12, Types.FLOAT); + + cstmt.registerOutParameter(13, Types.CHAR); + cstmt.registerOutParameter(14, Types.VARCHAR); + cstmt.registerOutParameter(15, Types.CHAR); + cstmt.registerOutParameter(16, Types.SMALLINT); + cstmt.execute(); + + assertEquals(obj1, cstmt.getDouble(9)); + assertEquals(obj2, cstmt.getDouble(10)); + assertEquals(obj3, cstmt.getInt(11)); + assertEquals(obj4, cstmt.getFloat(12)); + assertEquals(obj5, cstmt.getInt(13)); + assertEquals(obj6, cstmt.getString(14)); + assertEquals(obj7, cstmt.getString(15)); + assertEquals(obj8, cstmt.getLong(16)); + } + } + } + + @Test + public void useFlexibleCallableStatementFalseIndexedParametersRandomOrder() throws SQLException { + String call = "{CALL " + outOfOrderSproc + " (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)}"; + String connectionString = TestUtils.addOrOverrideProperty(getConnectionString(), + "useFlexibleCallableStatements", "false"); + + // When useFlexibleCallableStatement=false and when using only indexed parameters in a random order, + // cstmt should succeed. + try (Connection conn = DriverManager.getConnection(connectionString)) { + try (CallableStatement cstmt = conn.prepareCall(call)) { + int scale = 6; + double obj1 = 2015.0123; + double obj2 = 2015.012345; + int obj3 = -3; + float obj4 = 2015.04f; + int obj5 = 3; + String obj6 = "foo"; + String obj7 = "b"; + long obj8 = 2015L; + + cstmt.setObject(5, obj5, Types.CHAR); + cstmt.setObject(6, obj6, Types.VARCHAR); + cstmt.setObject(7, obj7, Types.CHAR); + cstmt.setObject(8, obj8, Types.SMALLINT); + + cstmt.setObject(1, obj1, Types.NUMERIC); + cstmt.setObject(2, obj2, Types.NUMERIC, scale); + cstmt.setObject(3, obj3, Types.INTEGER); + cstmt.setObject(4, obj4, Types.FLOAT); + + cstmt.registerOutParameter(13, Types.CHAR); + cstmt.registerOutParameter(14, Types.VARCHAR); + cstmt.registerOutParameter(15, Types.CHAR); + cstmt.registerOutParameter(16, Types.SMALLINT); + + cstmt.registerOutParameter(9, Types.NUMERIC); + cstmt.registerOutParameter(10, Types.NUMERIC, scale); + cstmt.registerOutParameter(11, Types.INTEGER); + cstmt.registerOutParameter(12, Types.FLOAT); + + cstmt.execute(); + + assertEquals(obj1, cstmt.getDouble(9)); + assertEquals(obj2, cstmt.getDouble(10)); + assertEquals(obj3, cstmt.getInt(11)); + assertEquals(obj4, cstmt.getFloat(12)); + assertEquals(obj5, cstmt.getInt(13)); + assertEquals(obj6, cstmt.getString(14)); + assertEquals(obj7, cstmt.getString(15)); + assertEquals(obj8, cstmt.getLong(16)); + } + } + } + + @Test + public void useFlexibleCallableStatementFalseMainlyIndexedParametersWithSettingSingleNamedParam() { + String call = "{CALL " + outOfOrderSproc + " (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)}"; + String connectionString = TestUtils.addOrOverrideProperty(getConnectionString(), + "useFlexibleCallableStatements", "false"); + + // When useFlexibleCallableStatement=false and when mainly using only indexed parameters, setting + // an input parameter by name will cause the cstmt to fail. + try (Connection conn = DriverManager.getConnection(connectionString)) { + try (CallableStatement cstmt = conn.prepareCall(call)) { + int scale = 6; + double obj1 = 2015.0123; + double obj2 = 2015.012345; + int obj3 = -3; + float obj4 = 2015.04f; + int obj5 = 3; + String obj6 = "foo"; + String obj7 = "b"; + long obj8 = 2015L; + + cstmt.setObject(1, obj1, Types.NUMERIC); + cstmt.setObject(2, obj2, Types.NUMERIC, scale); + cstmt.setObject(3, obj3, Types.INTEGER); + cstmt.setObject(4, obj4, Types.FLOAT); + + cstmt.setObject(5, obj5, Types.CHAR); + cstmt.setObject(6, obj6, Types.VARCHAR); + cstmt.setObject(7, obj7, Types.CHAR); + cstmt.setObject("i8", obj8, Types.SMALLINT); + + cstmt.registerOutParameter(9, Types.NUMERIC); + cstmt.registerOutParameter(10, Types.NUMERIC, scale); + cstmt.registerOutParameter(11, Types.INTEGER); + cstmt.registerOutParameter(12, Types.FLOAT); + + cstmt.registerOutParameter(13, Types.CHAR); + cstmt.registerOutParameter(14, Types.VARCHAR); + cstmt.registerOutParameter(15, Types.CHAR); + cstmt.registerOutParameter(16, Types.SMALLINT); + + cstmt.execute(); + + assertEquals(obj1, cstmt.getDouble(9)); + assertEquals(obj2, cstmt.getDouble(10)); + assertEquals(obj3, cstmt.getInt(11)); + assertEquals(obj4, cstmt.getFloat(12)); + assertEquals(obj5, cstmt.getInt(13)); + assertEquals(obj6, cstmt.getString(14)); + assertEquals(obj7, cstmt.getString(15)); + assertEquals(obj8, cstmt.getLong(16)); + + fail(TestResource.getResource("R_expectedFailPassed")); + } + } catch (Exception e) { + assertEquals(TestResource.getResource("R_noNamedAndIndexedParameters"), e.getMessage()); + } + } + + @Test + public void useFlexibleCallableStatementFalseMainlyIndexedParametersWithRegisteringSingleNamedOutputParam() { + String call = "{CALL " + outOfOrderSproc + " (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)}"; + String connectionString = TestUtils.addOrOverrideProperty(getConnectionString(), + "useFlexibleCallableStatements", "false"); + + // When useFlexibleCallableStatement=false and when mainly using only indexed parameters, registering + // an output parameter by name will cause the cstmt to fail. + try (Connection conn = DriverManager.getConnection(connectionString)) { + try (CallableStatement cstmt = conn.prepareCall(call)) { + int scale = 6; + double obj1 = 2015.0123; + double obj2 = 2015.012345; + int obj3 = -3; + float obj4 = 2015.04f; + int obj5 = 3; + String obj6 = "foo"; + String obj7 = "b"; + long obj8 = 2015L; + + cstmt.setObject(1, obj1, Types.NUMERIC); + cstmt.setObject(2, obj2, Types.NUMERIC, scale); + cstmt.setObject(3, obj3, Types.INTEGER); + cstmt.setObject(4, obj4, Types.FLOAT); + + cstmt.setObject(5, obj5, Types.CHAR); + cstmt.setObject(6, obj6, Types.VARCHAR); + cstmt.setObject(7, obj7, Types.CHAR); + cstmt.setObject(8, obj8, Types.SMALLINT); + + cstmt.registerOutParameter(9, Types.NUMERIC); + cstmt.registerOutParameter(10, Types.NUMERIC, scale); + cstmt.registerOutParameter(11, Types.INTEGER); + cstmt.registerOutParameter(12, Types.FLOAT); + + cstmt.registerOutParameter(13, Types.CHAR); + cstmt.registerOutParameter(14, Types.VARCHAR); + cstmt.registerOutParameter(15, Types.CHAR); + cstmt.registerOutParameter("o8", Types.SMALLINT); + + cstmt.execute(); + + assertEquals(obj1, cstmt.getDouble(9)); + assertEquals(obj2, cstmt.getDouble(10)); + assertEquals(obj3, cstmt.getInt(11)); + assertEquals(obj4, cstmt.getFloat(12)); + assertEquals(obj5, cstmt.getInt(13)); + assertEquals(obj6, cstmt.getString(14)); + assertEquals(obj7, cstmt.getString(15)); + assertEquals(obj8, cstmt.getLong(16)); + + fail(TestResource.getResource("R_expectedFailPassed")); + } + } catch (Exception e) { + assertEquals(TestResource.getResource("R_noNamedAndIndexedParameters"), e.getMessage()); + } + } + + @Test + public void useFlexibleCallableStatementFalseMainlyIndexedParametersWithGettingOutputParamByName() { + String call = "{CALL " + outOfOrderSproc + " (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)}"; + String connectionString = TestUtils.addOrOverrideProperty(getConnectionString(), + "useFlexibleCallableStatements", "false"); + + // When useFlexibleCallableStatement=false and when mainly using only indexed parameters, getting + // an output parameter by name will cause the cstmt to fail. + try (Connection conn = DriverManager.getConnection(connectionString)) { + try (CallableStatement cstmt = conn.prepareCall(call)) { + int scale = 6; + double obj1 = 2015.0123; + double obj2 = 2015.012345; + int obj3 = -3; + float obj4 = 2015.04f; + int obj5 = 3; + String obj6 = "foo"; + String obj7 = "b"; + long obj8 = 2015L; + + cstmt.setObject(1, obj1, Types.NUMERIC); + cstmt.setObject(2, obj2, Types.NUMERIC, scale); + cstmt.setObject(3, obj3, Types.INTEGER); + cstmt.setObject(4, obj4, Types.FLOAT); + + cstmt.setObject(5, obj5, Types.CHAR); + cstmt.setObject(6, obj6, Types.VARCHAR); + cstmt.setObject(7, obj7, Types.CHAR); + cstmt.setObject(8, obj8, Types.SMALLINT); + + cstmt.registerOutParameter(9, Types.NUMERIC); + cstmt.registerOutParameter(10, Types.NUMERIC, scale); + cstmt.registerOutParameter(11, Types.INTEGER); + cstmt.registerOutParameter(12, Types.FLOAT); + + cstmt.registerOutParameter(13, Types.CHAR); + cstmt.registerOutParameter(14, Types.VARCHAR); + cstmt.registerOutParameter(15, Types.CHAR); + cstmt.registerOutParameter(16, Types.SMALLINT); + + cstmt.execute(); + + assertEquals(obj1, cstmt.getDouble(9)); + assertEquals(obj2, cstmt.getDouble(10)); + assertEquals(obj3, cstmt.getInt(11)); + assertEquals(obj4, cstmt.getFloat(12)); + assertEquals(obj5, cstmt.getInt(13)); + assertEquals(obj6, cstmt.getString(14)); + assertEquals(obj7, cstmt.getString(15)); + assertEquals(obj8, cstmt.getLong("o8")); + + fail(TestResource.getResource("R_expectedFailPassed")); + } + } catch (Exception e) { + assertEquals(TestResource.getResource("R_noNamedAndIndexedParameters"), e.getMessage()); + } + } + + @Test + public void useFlexibleCallableStatementFalseNamedParameters() throws SQLException { + String call = "{CALL " + outOfOrderSproc + " (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)}"; + String connectionString = TestUtils.addOrOverrideProperty(getConnectionString(), + "useFlexibleCallableStatements", "false"); + + // When useFlexibleCallableStatement=false and when using only named parameters, cstmt should succeed. + try (Connection conn = DriverManager.getConnection(connectionString)) { + try (CallableStatement cstmt = conn.prepareCall(call)) { + int scale = 6; + double obj1 = 2015.0123; + double obj2 = 2015.012345; + int obj3 = -3; + float obj4 = 2015.04f; + int obj5 = 3; + String obj6 = "foo"; + String obj7 = "b"; + long obj8 = 2015L; + + cstmt.setObject("i1", obj1, Types.NUMERIC); + cstmt.setObject("i2", obj2, Types.NUMERIC, scale); + cstmt.setObject("i3", obj3, Types.INTEGER); + cstmt.setObject("i4", obj4, Types.FLOAT); + + cstmt.setObject("i5", obj5, Types.CHAR); + cstmt.setObject("i6", obj6, Types.VARCHAR); + cstmt.setObject("i7", obj7, Types.CHAR); + cstmt.setObject("i8", obj8, Types.SMALLINT); + + cstmt.registerOutParameter("o1", Types.NUMERIC); + cstmt.registerOutParameter("o2", Types.NUMERIC, scale); + cstmt.registerOutParameter("o3", Types.INTEGER); + cstmt.registerOutParameter("o4", Types.FLOAT); + + cstmt.registerOutParameter("o5", Types.CHAR); + cstmt.registerOutParameter("o6", Types.VARCHAR); + cstmt.registerOutParameter("o7", Types.CHAR); + cstmt.registerOutParameter("o8", Types.SMALLINT); + cstmt.execute(); + + assertEquals(obj1, cstmt.getDouble("o1")); + assertEquals(obj2, cstmt.getDouble("o2")); + assertEquals(obj3, cstmt.getInt("o3")); + assertEquals(obj4, cstmt.getFloat("o4")); + assertEquals(obj5, cstmt.getInt("o5")); + assertEquals(obj6, cstmt.getString("o6")); + assertEquals(obj7, cstmt.getString("o7")); + assertEquals(obj8, cstmt.getLong("o8")); + } + } + } + + @Test + public void useFlexibleCallableStatementFalseNamedParametersRandomOrder() throws SQLException { + String call = "{CALL " + outOfOrderSproc + " (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)}"; + String connectionString = TestUtils.addOrOverrideProperty(getConnectionString(), + "useFlexibleCallableStatements", "false"); + + // When useFlexibleCallableStatement=false and when using only named parameters in a random order, + // cstmt should succeed. + try (Connection conn = DriverManager.getConnection(connectionString)) { + try (CallableStatement cstmt = conn.prepareCall(call)) { + int scale = 6; + double obj1 = 2015.0123; + double obj2 = 2015.012345; + int obj3 = -3; + float obj4 = 2015.04f; + int obj5 = 3; + String obj6 = "foo"; + String obj7 = "b"; + long obj8 = 2015L; + + cstmt.setObject("i5", obj5, Types.CHAR); + cstmt.setObject("i6", obj6, Types.VARCHAR); + cstmt.setObject("i7", obj7, Types.CHAR); + cstmt.setObject("i8", obj8, Types.SMALLINT); + + cstmt.setObject("i1", obj1, Types.NUMERIC); + cstmt.setObject("i2", obj2, Types.NUMERIC, scale); + cstmt.setObject("i3", obj3, Types.INTEGER); + cstmt.setObject("i4", obj4, Types.FLOAT); + + cstmt.registerOutParameter("o5", Types.CHAR); + cstmt.registerOutParameter("o6", Types.VARCHAR); + cstmt.registerOutParameter("o7", Types.CHAR); + cstmt.registerOutParameter("o8", Types.SMALLINT); + + cstmt.registerOutParameter("o1", Types.NUMERIC); + cstmt.registerOutParameter("o2", Types.NUMERIC, scale); + cstmt.registerOutParameter("o3", Types.INTEGER); + cstmt.registerOutParameter("o4", Types.FLOAT); + + cstmt.execute(); + + assertEquals(obj1, cstmt.getDouble("o1")); + assertEquals(obj2, cstmt.getDouble("o2")); + assertEquals(obj3, cstmt.getInt("o3")); + assertEquals(obj4, cstmt.getFloat("o4")); + assertEquals(obj5, cstmt.getInt("o5")); + assertEquals(obj6, cstmt.getString("o6")); + assertEquals(obj7, cstmt.getString("o7")); + assertEquals(obj8, cstmt.getLong("o8")); + } + } + } + + @Test + public void useFlexibleCallableStatementFalseMainlyNamedParametersWithSettingSingleIndexedParam() { + String call = "{CALL " + outOfOrderSproc + " (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)}"; + String connectionString = TestUtils.addOrOverrideProperty(getConnectionString(), + "useFlexibleCallableStatements", "false"); + + // When useFlexibleCallableStatement=false and when mainly using only named parameters, setting + // an input parameter by index will cause the cstmt to fail. + try (Connection conn = DriverManager.getConnection(connectionString)) { + try (CallableStatement cstmt = conn.prepareCall(call)) { + int scale = 6; + double obj1 = 2015.0123; + double obj2 = 2015.012345; + int obj3 = -3; + float obj4 = 2015.04f; + int obj5 = 3; + String obj6 = "foo"; + String obj7 = "b"; + long obj8 = 2015L; + + cstmt.setObject("i1", obj1, Types.NUMERIC); + cstmt.setObject("i2", obj2, Types.NUMERIC, scale); + cstmt.setObject("i3", obj3, Types.INTEGER); + cstmt.setObject("i4", obj4, Types.FLOAT); + + cstmt.setObject("i5", obj5, Types.CHAR); + cstmt.setObject("i6", obj6, Types.VARCHAR); + cstmt.setObject("i7", obj7, Types.CHAR); + cstmt.setObject(8, obj8, Types.SMALLINT); + + cstmt.registerOutParameter("o1", Types.NUMERIC); + cstmt.registerOutParameter("o2", Types.NUMERIC, scale); + cstmt.registerOutParameter("o3", Types.INTEGER); + cstmt.registerOutParameter("o4", Types.FLOAT); + + cstmt.registerOutParameter("o5", Types.CHAR); + cstmt.registerOutParameter("o6", Types.VARCHAR); + cstmt.registerOutParameter("o7", Types.CHAR); + cstmt.registerOutParameter("o8", Types.SMALLINT); + cstmt.execute(); + + assertEquals(obj1, cstmt.getDouble("o1")); + assertEquals(obj2, cstmt.getDouble("o2")); + assertEquals(obj3, cstmt.getInt("o3")); + assertEquals(obj4, cstmt.getFloat("o4")); + assertEquals(obj5, cstmt.getInt("o5")); + assertEquals(obj6, cstmt.getString("o6")); + assertEquals(obj7, cstmt.getString("o7")); + assertEquals(obj8, cstmt.getLong("o8")); + + fail(TestResource.getResource("R_expectedFailPassed")); + } + } catch (Exception e) { + assertEquals(TestResource.getResource("R_noNamedAndIndexedParameters"), e.getMessage()); + } + } + + @Test + public void useFlexibleCallableStatementFalseMainlyNamedParametersWithRegisteringSingleIndexedOutputParam() { + String call = "{CALL " + outOfOrderSproc + " (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)}"; + String connectionString = TestUtils.addOrOverrideProperty(getConnectionString(), + "useFlexibleCallableStatements", "false"); + + // When useFlexibleCallableStatement=false and when mainly using only named parameters, registering + // an output parameter by index will cause the cstmt to fail. + try (Connection conn = DriverManager.getConnection(connectionString)) { + try (CallableStatement cstmt = conn.prepareCall(call)) { + int scale = 6; + double obj1 = 2015.0123; + double obj2 = 2015.012345; + int obj3 = -3; + float obj4 = 2015.04f; + int obj5 = 3; + String obj6 = "foo"; + String obj7 = "b"; + long obj8 = 2015L; + + cstmt.setObject("i1", obj1, Types.NUMERIC); + cstmt.setObject("i2", obj2, Types.NUMERIC, scale); + cstmt.setObject("i3", obj3, Types.INTEGER); + cstmt.setObject("i4", obj4, Types.FLOAT); + + cstmt.setObject("i5", obj5, Types.CHAR); + cstmt.setObject("i6", obj6, Types.VARCHAR); + cstmt.setObject("i7", obj7, Types.CHAR); + cstmt.setObject("i8", obj8, Types.SMALLINT); + + cstmt.registerOutParameter("o1", Types.NUMERIC); + cstmt.registerOutParameter("o2", Types.NUMERIC, scale); + cstmt.registerOutParameter("o3", Types.INTEGER); + cstmt.registerOutParameter("o4", Types.FLOAT); + + cstmt.registerOutParameter("o5", Types.CHAR); + cstmt.registerOutParameter("o6", Types.VARCHAR); + cstmt.registerOutParameter("o7", Types.CHAR); + cstmt.registerOutParameter(16, Types.SMALLINT); + cstmt.execute(); + + assertEquals(obj1, cstmt.getDouble("o1")); + assertEquals(obj2, cstmt.getDouble("o2")); + assertEquals(obj3, cstmt.getInt("o3")); + assertEquals(obj4, cstmt.getFloat("o4")); + assertEquals(obj5, cstmt.getInt("o5")); + assertEquals(obj6, cstmt.getString("o6")); + assertEquals(obj7, cstmt.getString("o7")); + assertEquals(obj8, cstmt.getLong("o8")); + + fail(TestResource.getResource("R_expectedFailPassed")); + } + } catch (Exception e) { + assertEquals(TestResource.getResource("R_noNamedAndIndexedParameters"), e.getMessage()); + } + } + + @Test + public void useFlexibleCallableStatementFalseMainlyNamedParametersWithGettingOutputParamByIndex() { + String call = "{CALL " + outOfOrderSproc + " (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)}"; + String connectionString = TestUtils.addOrOverrideProperty(getConnectionString(), + "useFlexibleCallableStatements", "false"); + + // When useFlexibleCallableStatement=false and when mainly using only named parameters, getting + // an output parameter by index will cause the cstmt to fail. + try (Connection conn = DriverManager.getConnection(connectionString)) { + try (CallableStatement cstmt = conn.prepareCall(call)) { + int scale = 6; + double obj1 = 2015.0123; + double obj2 = 2015.012345; + int obj3 = -3; + float obj4 = 2015.04f; + int obj5 = 3; + String obj6 = "foo"; + String obj7 = "b"; + long obj8 = 2015L; + + cstmt.setObject("i1", obj1, Types.NUMERIC); + cstmt.setObject("i2", obj2, Types.NUMERIC, scale); + cstmt.setObject("i3", obj3, Types.INTEGER); + cstmt.setObject("i4", obj4, Types.FLOAT); + + cstmt.setObject("i5", obj5, Types.CHAR); + cstmt.setObject("i6", obj6, Types.VARCHAR); + cstmt.setObject("i7", obj7, Types.CHAR); + cstmt.setObject("i8", obj8, Types.SMALLINT); + + cstmt.registerOutParameter("o1", Types.NUMERIC); + cstmt.registerOutParameter("o2", Types.NUMERIC, scale); + cstmt.registerOutParameter("o3", Types.INTEGER); + cstmt.registerOutParameter("o4", Types.FLOAT); + + cstmt.registerOutParameter("o5", Types.CHAR); + cstmt.registerOutParameter("o6", Types.VARCHAR); + cstmt.registerOutParameter("o7", Types.CHAR); + cstmt.registerOutParameter("o8", Types.SMALLINT); + cstmt.execute(); + + assertEquals(obj1, cstmt.getDouble("o1")); + assertEquals(obj2, cstmt.getDouble("o2")); + assertEquals(obj3, cstmt.getInt("o3")); + assertEquals(obj4, cstmt.getFloat("o4")); + assertEquals(obj5, cstmt.getInt("o5")); + assertEquals(obj6, cstmt.getString("o6")); + assertEquals(obj7, cstmt.getString("o7")); + assertEquals(obj8, cstmt.getLong(16)); + + fail(TestResource.getResource("R_expectedFailPassed")); + } + } catch (Exception e) { + assertEquals(TestResource.getResource("R_noNamedAndIndexedParameters"), e.getMessage()); + } + } + + @Test + public void testExecutingUserDefinedFunctionDirectly() throws SQLException { + String call = "{? = CALL " + userDefinedFunction + " (?,?,?,?,?,?)}"; + + try (CallableStatement cstmt = connection.prepareCall(call)) { + cstmt.setObject(2, "param"); + cstmt.setObject(3, "param"); + cstmt.setObject(4, "param"); + cstmt.setObject(5, "param"); + cstmt.setObject(6, "param"); + cstmt.setObject(7, "param"); + cstmt.registerOutParameter(1, Types.VARCHAR); + cstmt.execute(); + assertEquals("foobar", cstmt.getString(1)); + + // Re-execute again to test skipping of unread return values on statement close. + // If it fails, TDS errors will be thrown. + cstmt.execute(); + } + } + + @Test + public void testRegisteringOutputByIndexandAcquiringOutputParamByName() throws SQLException { + String call = "{CALL " + byParamNameSproc + " (?,?)}"; + + // Param names are p1 and p2 + try (CallableStatement cstmt = connection.prepareCall(call)) { + cstmt.setString(1, "foobar"); + cstmt.registerOutParameter(2, Types.NVARCHAR); + cstmt.execute(); + + assertEquals("foobar", cstmt.getString("p2")); + } + + try (CallableStatement cstmt = connection.prepareCall(call)) { + cstmt.setString("p1", "foobar"); + cstmt.registerOutParameter("p2", Types.NVARCHAR); + cstmt.execute(); + + assertEquals("foobar", cstmt.getString("p2")); + } + + try (CallableStatement cstmt = connection.prepareCall(call)) { + cstmt.setString("p1", "foobar"); + cstmt.registerOutParameter("p2", Types.NVARCHAR); + cstmt.execute(); + + assertEquals("foobar", cstmt.getString(2)); + } + } + @Test public void testExecuteSystemStoredProcedureNamedParametersAndIndexedParameterNoResultset() throws SQLException { String call0 = "EXEC sp_getapplock @Resource=?, @LockTimeout='0', @LockMode='Exclusive', @LockOwner='Session'"; @@ -600,7 +1307,7 @@ public void testTimestampStringConversion() throws SQLException { /** * Cleanup after test - * + * * @throws SQLException */ @AfterAll @@ -613,10 +1320,13 @@ public static void cleanup() throws SQLException { TestUtils.dropProcedureIfExists(inputParamsProcedureName, stmt); TestUtils.dropProcedureIfExists(getObjectLocalDateTimeProcedureName, stmt); TestUtils.dropProcedureIfExists(getObjectOffsetDateTimeProcedureName, stmt); + TestUtils.dropProcedureIfExists(zeroParamSproc, stmt); + TestUtils.dropProcedureIfExists(outOfOrderSproc, stmt); + TestUtils.dropProcedureIfExists(byParamNameSproc, stmt); TestUtils.dropProcedureIfExists(currentTimeProc, stmt); TestUtils.dropProcedureIfExists(conditionalSproc, stmt); TestUtils.dropProcedureIfExists(simpleRetValSproc, stmt); - TestUtils.dropProcedureIfExists(zeroParamSproc, stmt); + TestUtils.dropFunctionIfExists(userDefinedFunction, stmt); } } @@ -702,6 +1412,13 @@ private static void createTableManyParams() throws SQLException { } } + private static void createUserDefinedType() throws SQLException { + String TVPCreateCmd = "CREATE TYPE " + manyParamUserDefinedType + " FROM MONEY"; + try (Statement stmt = connection.createStatement()) { + stmt.executeUpdate(TVPCreateCmd); + } + } + private static void createProcedureZeroParams() throws SQLException { String sql = "CREATE PROCEDURE " + zeroParamSproc + " AS RETURN 1"; try (Statement stmt = connection.createStatement()) { @@ -709,10 +1426,33 @@ private static void createProcedureZeroParams() throws SQLException { } } - private static void createUserDefinedType() throws SQLException { - String TVPCreateCmd = "CREATE TYPE " + manyParamUserDefinedType + " FROM MONEY"; + private static void createOutOfOrderSproc() throws SQLException { + String sql = "CREATE PROCEDURE " + outOfOrderSproc + " @i1 NUMERIC(16,10)," + " @i2 NUMERIC(16,6)," + + " @i3 INT," + " @i4 REAL," + " @i5 CHAR," + " @i6 VARCHAR(6)," + " @i7 CHAR," + " @i8 SMALLINT, " + + " @o1 NUMERIC(16,10) OUTPUT," + " @o2 NUMERIC(16,6) OUTPUT," + " @o3 INT OUTPUT," + + " @o4 REAL OUTPUT," + " @o5 CHAR OUTPUT," + " @o6 VARCHAR(6) OUTPUT," + " @o7 CHAR OUTPUT," + + " @o8 SMALLINT OUTPUT" + " as begin " + " set @o1=@i1;" + " set @o2=@i2;" + " set @o3=@i3;" + + " set @o4=@i4;" + " set @o5=@i5;" + " set @o6=@i6;" + " set @o7=@i7;" + " set @o8=@i8;" + " end"; try (Statement stmt = connection.createStatement()) { - stmt.executeUpdate(TVPCreateCmd); + stmt.execute(sql); + } + } + + private static void createByParamNameSproc() throws SQLException { + String sql = "CREATE PROCEDURE " + byParamNameSproc + + " (@p1 nvarchar(30), @p2 nvarchar(30) output) AS BEGIN SELECT @p2 = @p1 END;"; + try (Statement stmt = connection.createStatement()) { + stmt.execute(sql); + } + } + + private static void createUserDefinedFunction() throws SQLException { + String sql = "CREATE FUNCTION " + userDefinedFunction + + " (@p0 char(20), @p1 varchar(50), @p2 varchar(max), @p3 nchar(30), @p4 nvarchar(60), @p5 nvarchar(max)) " + + "RETURNS varchar(50) AS BEGIN " + "DECLARE @ret varchar(50); " + "SELECT @ret = 'foobar'; " + + " RETURN @ret; end;"; + try (Statement stmt = connection.createStatement()) { + stmt.execute(sql); } } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/connection/RequestBoundaryMethodsTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/connection/RequestBoundaryMethodsTest.java index 0ae93feb3..3fdd65be8 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/connection/RequestBoundaryMethodsTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/RequestBoundaryMethodsTest.java @@ -515,14 +515,16 @@ private List getVerifiedMethodNames() { verifiedMethodNames.add("setAccessTokenCallbackClass"); verifiedMethodNames.add("getServerMessageHandler"); verifiedMethodNames.add("setServerMessageHandler"); + verifiedMethodNames.add("getCalcBigDecimalScale"); verifiedMethodNames.add("setcacheBulkCopyMetadata"); verifiedMethodNames.add("getcacheBulkCopyMetadata"); + verifiedMethodNames.add("setCalcBigDecimalScale"); + verifiedMethodNames.add("getUseFlexibleCallableStatements"); + verifiedMethodNames.add("setUseFlexibleCallableStatements"); verifiedMethodNames.add("getCalcBigDecimalPrecision"); verifiedMethodNames.add("setCalcBigDecimalPrecision"); verifiedMethodNames.add("registerBeforeReconnectListener"); verifiedMethodNames.add("removeBeforeReconnectListener"); - verifiedMethodNames.add("getUseFlexibleCallableStatements"); - verifiedMethodNames.add("setUseFlexibleCallableStatements"); return verifiedMethodNames; } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/DateAndTimeTypeTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/DateAndTimeTypeTest.java index 99dbe64dc..5b9745f13 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/DateAndTimeTypeTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/DateAndTimeTypeTest.java @@ -10,6 +10,8 @@ import java.sql.Connection; import java.sql.Date; import java.sql.PreparedStatement; +import java.sql.CallableStatement; +import java.sql.Types; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; @@ -49,6 +51,8 @@ public class DateAndTimeTypeTest extends AbstractTest { private static final String timeTVP = RandomUtil.getIdentifier("timeTVP"); private static final String timestampTVP = RandomUtil.getIdentifier("timestampTVP"); private static final String tableName = RandomUtil.getIdentifier("DataTypesTable"); + private static final String datetimeTable = RandomUtil.getIdentifier("DateTimeTable"); + private static final String datetimeSproc = RandomUtil.getIdentifier("dateTimeSproc"); private static final String primaryKeyConstraintName = "pk_" + tableName; /** @@ -205,21 +209,21 @@ public static void setupTests() throws Exception { * Test to make sure that a Timestamp is treated as a datetime object. */ @Test - public void testSendTimestampAsDatetime() throws Exception { + public void testSendTimestampAsDatetime() throws Exception { String expected = "2010-02-01T23:59:59.997"; String actual = null; String query = "SELECT CONVERT(VARCHAR(40), ?, 126) as [value]"; - try (SQLServerConnection conn = PrepUtil.getConnection(connectionString + ";datetimeParameterType=datetime"); - PreparedStatement stmt = conn.prepareStatement(query)) { + try (SQLServerConnection conn = PrepUtil.getConnection(connectionString + ";datetimeParameterType=datetime"); + PreparedStatement stmt = conn.prepareStatement(query)) { Timestamp ts = Timestamp.valueOf("2010-02-01 23:59:59.996"); // if cast to a datetime, 996ms is rounded up to 997ms /* - * send the timestamp to the server using the TIME SQL type rather than TIMESTAMP. The driver will - * strip the date portion and, because sendTimeAsDatetime=true, round the resulting time value to - * midnight because it should be sending a DATETIME which has only 1/300s accuracy - */ + * send the timestamp to the server using the TIME SQL type rather than TIMESTAMP. The driver will + * strip the date portion and, because sendTimeAsDatetime=true, round the resulting time value to + * midnight because it should be sending a DATETIME which has only 1/300s accuracy + */ stmt.setObject(1, ts, java.sql.Types.TIMESTAMP); ResultSet rs = stmt.executeQuery(); @@ -236,21 +240,21 @@ public void testSendTimestampAsDatetime() throws Exception { * Test to make sure that a Timestamp is treated as a datetime2 object. */ @Test - public void testSendTimestampAsDatetime2() throws Exception { + public void testSendTimestampAsDatetime2() throws Exception { String expected = "2010-02-02T23:59:59.1234567"; String actual = null; String query = "SELECT CONVERT(VARCHAR(40), ?, 126) as [value]"; - try (SQLServerConnection conn = PrepUtil.getConnection(connectionString + ";datetimeParameterType=datetime2"); - PreparedStatement stmt = conn.prepareStatement(query)) { + try (SQLServerConnection conn = PrepUtil.getConnection(connectionString + ";datetimeParameterType=datetime2"); + PreparedStatement stmt = conn.prepareStatement(query)) { Timestamp ts = Timestamp.valueOf("2010-02-02 23:59:59.1234567"); /* - * send the timestamp to the server using the TIME SQL type rather than TIMESTAMP. The driver will - * strip the date portion and, because sendTimeAsDatetime=true, round the resulting time value to - * midnight because it should be sending a DATETIME which has only 1/300s accuracy - */ + * send the timestamp to the server using the TIME SQL type rather than TIMESTAMP. The driver will + * strip the date portion and, because sendTimeAsDatetime=true, round the resulting time value to + * midnight because it should be sending a DATETIME which has only 1/300s accuracy + */ stmt.setObject(1, ts, java.sql.Types.TIMESTAMP); ResultSet rs = stmt.executeQuery(); @@ -267,22 +271,22 @@ public void testSendTimestampAsDatetime2() throws Exception { * Test to make sure that a Timestamp is treated as a datetime2 object. */ @Test - public void testSendTimestampAsDatetimeoffset() throws Exception { + public void testSendTimestampAsDatetimeoffset() throws Exception { String expected = "2010-02-03T23:59:59.7654321Z"; String actual = null; String query = "SELECT CONVERT(VARCHAR(40), ?, 127) as [value]"; - - try (SQLServerConnection conn = PrepUtil.getConnection(connectionString + ";datetimeParameterType=datetimeoffset"); - PreparedStatement stmt = conn.prepareStatement(query)) { + try (SQLServerConnection conn = PrepUtil + .getConnection(connectionString + ";datetimeParameterType=datetimeoffset"); + PreparedStatement stmt = conn.prepareStatement(query)) { Timestamp ts = Timestamp.valueOf("2010-02-03 23:59:59.7654321"); /* - * send the timestamp to the server using the TIME SQL type rather than TIMESTAMP. The driver will - * strip the date portion and, because sendTimeAsDatetime=true, round the resulting time value to - * midnight because it should be sending a DATETIME which has only 1/300s accuracy - */ + * send the timestamp to the server using the TIME SQL type rather than TIMESTAMP. The driver will + * strip the date portion and, because sendTimeAsDatetime=true, round the resulting time value to + * midnight because it should be sending a DATETIME which has only 1/300s accuracy + */ stmt.setObject(1, ts, java.sql.Types.TIMESTAMP); ResultSet rs = stmt.executeQuery(); @@ -295,6 +299,82 @@ public void testSendTimestampAsDatetimeoffset() throws Exception { assertEquals(expected, actual); } + @Test + public void testCstmtRegisterDatetimeOutParameterDateTimeParameterTypeDatetime() throws Exception { + String expected = "3160-08-17 19:09:06.937"; + String actual = null; + String query = "{CALL " + AbstractSQLGenerator.escapeIdentifier(datetimeSproc) + "(?)}"; + + // The following should be the T-SQL executed. Since datetimeParameterType=dateTime, the registered parameter type + // should be 'datetime' eg. declare @p1 datetime as shown below. The expected fractional seconds should be 3. + // + // declare @p1 datetime + // set @p1='3160-08-17 19:09:06.937' + // exec sproc @p1 output + // select @p1 + try (SQLServerConnection conn = PrepUtil.getConnection(connectionString + ";datetimeParameterType=datetime"); + CallableStatement stmt = conn.prepareCall(query)) { + + stmt.registerOutParameter(1, microsoft.sql.Types.DATETIME); + stmt.execute(); + + actual = stmt.getString(1); + } + + assertEquals(expected, actual.toString()); + } + + @Test + public void testCstmtRegisterDatetimeOutParameterDateTimeParameterTypeDatetime2() throws Exception { + String expected = "3160-08-17 19:09:06.937"; + String actual = null; + String query = "{CALL " + AbstractSQLGenerator.escapeIdentifier(datetimeSproc) + "(?)}"; + + // The following should be the T-SQL executed. Since datetimeParameterType=dateTime2, the registered param type + // should be 'datetime2(3)' eg. declare @p1 datetime2(3) as shown below. The expected fractional seconds + // should be 7 in the T-SQL, but in the expected actual output it's rounded so fractional seconds should be 3. + // + // declare @p1 datetime2(3) + // set @p1='set @p1='3160-08-17 19:09:06.9370000'' + // exec sproc @p1 output + // select @p1 + try (SQLServerConnection conn = PrepUtil.getConnection(connectionString + ";datetimeParameterType=datetime2"); + CallableStatement stmt = conn.prepareCall(query)) { + + stmt.registerOutParameter(1, microsoft.sql.Types.DATETIME); + stmt.execute(); + + actual = stmt.getString(1); + } + + assertEquals(expected, actual.toString()); + } + + @Test + public void testCstmtRegisterTimestampOutParameter() throws Exception { + String expected = "3160-08-17 19:09:06.9366667"; + String actual = null; + String query = "{CALL " + AbstractSQLGenerator.escapeIdentifier(datetimeSproc) + "(?)}"; + + // The following should be the T-SQL executed. Default driver behaviour for Timestamp SQL types is to register + // the param in the RPC as datetime2(7). So fractional seconds should be 7. + // + // declare @p1 datetime2(7) + // set @p1='3160-08-17 19:09:06.9366667' + // exec sproc @p1 output + // select @p1 + try (SQLServerConnection conn = PrepUtil.getConnection(connectionString); + CallableStatement stmt = conn.prepareCall(query)) { + + stmt.registerOutParameter(1, Types.TIMESTAMP); + stmt.execute(); + + actual = stmt.getString(1); + } + + assertEquals(expected, actual.toString()); + } + @BeforeEach public void testSetup() throws TestAbortedException, Exception { // To get TIME & setTime working on Servers >= 2008, we must add 'sendTimeAsDatetime=false' @@ -302,10 +382,19 @@ public void testSetup() throws TestAbortedException, Exception { try (Connection connection = PrepUtil.getConnection(connectionString + ";sendTimeAsDatetime=false"); Statement stmt = (SQLServerStatement) connection.createStatement()) { TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(tableName), stmt); + TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(datetimeTable), stmt); + TestUtils.dropProcedureIfExists(AbstractSQLGenerator.escapeIdentifier(datetimeSproc), stmt); String sql1 = "create table " + AbstractSQLGenerator.escapeIdentifier(tableName) + " (id integer not null, my_date date, my_time time, my_timestamp datetime2 constraint " + AbstractSQLGenerator.escapeIdentifier(primaryKeyConstraintName) + " primary key (id))"; + String sql2 = "create table " + AbstractSQLGenerator.escapeIdentifier(datetimeTable) + + " (c1 datetime2 NULL)"; + String sql3 = "create procedure " + AbstractSQLGenerator.escapeIdentifier(datetimeSproc) + + " (@p1 datetime2 output) as select top 1 @p1=c1 from " + + AbstractSQLGenerator.escapeIdentifier(datetimeTable); stmt.execute(sql1); + stmt.execute(sql2); + stmt.execute(sql3); // add one sample data String sPrepStmt = "insert into " + AbstractSQLGenerator.escapeIdentifier(tableName) @@ -321,6 +410,9 @@ public void testSetup() throws TestAbortedException, Exception { createTVPs(timeTVP, "time"); createTVPs(timestampTVP, "datetime2"); } + + stmt.execute("insert into " + AbstractSQLGenerator.escapeIdentifier(datetimeTable) + + " values('3160-08-17 19:09:06.9366667')"); } } @@ -328,6 +420,8 @@ public void testSetup() throws TestAbortedException, Exception { public static void terminateVariation() throws SQLException { try (Statement stmt = connection.createStatement()) { TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(tableName), stmt); + TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(datetimeTable), stmt); + TestUtils.dropProcedureIfExists(AbstractSQLGenerator.escapeIdentifier(datetimeSproc), stmt); } } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java index 5a436547c..0776d4b46 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecutionTest.java @@ -348,7 +348,7 @@ public void testValidTimezoneForTimestampBatchInsertWithBulkCopy() throws Except public void testValidTimezonesDstTimestampBatchInsertWithBulkCopy() throws Exception { Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); - for (String tzId: TimeZone.getAvailableIDs()) { + for (String tzId : TimeZone.getAvailableIDs()) { TimeZone.setDefault(TimeZone.getTimeZone(tzId)); long ms = 1696127400000L; // DST @@ -371,8 +371,8 @@ public void testValidTimezonesDstTimestampBatchInsertWithBulkCopy() throws Excep } // Insert Timestamp using bulkcopy for batch insert - try (Connection con = DriverManager.getConnection( - connectionString + ";useBulkCopyForBatchInsert=true;sendTemporalDataTypesAsStringForBulkCopy=false;"); + try (Connection con = DriverManager.getConnection(connectionString + + ";useBulkCopyForBatchInsert=true;sendTemporalDataTypesAsStringForBulkCopy=false;"); PreparedStatement pstmt = con.prepareStatement("INSERT INTO " + timestampTable1 + " VALUES(?)")) { Timestamp timestamp = new Timestamp(ms); @@ -419,8 +419,9 @@ public void testBatchInsertTimestampNoTimezoneDoubleConversion() throws Exceptio long ms = 1578743412000L; // Insert Timestamp using prepared statement when useBulkCopyForBatchInsert=true - try (Connection con = DriverManager.getConnection(connectionString - + ";useBulkCopyForBatchInsert=true;sendTemporalDataTypesAsStringForBulkCopy=false;"); Statement stmt = con.createStatement(); + try (Connection con = DriverManager.getConnection( + connectionString + ";useBulkCopyForBatchInsert=true;sendTemporalDataTypesAsStringForBulkCopy=false;"); + Statement stmt = con.createStatement(); PreparedStatement pstmt = con.prepareStatement("INSERT INTO " + timestampTable2 + " VALUES(?)")) { TestUtils.dropTableIfExists(timestampTable2, stmt); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java index 9c814916d..143865a93 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java @@ -1662,7 +1662,7 @@ public void testMathBigDecimalDivision() throws SQLException { */ @Test @Tag(Constants.xAzureSQLDW) - public void testResultSetErrors() throws Exception { + public void testRetrievingRegisteredOutParamWhenResultSetDoesNotExists() throws Exception { try (Connection con = getConnection(); Statement stmt = con.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE)) { @@ -1679,7 +1679,10 @@ public void testResultSetErrors() throws Exception { try (ResultSet rs = cstmt.executeQuery()) {} catch (Exception ex) {} ; - assertEquals(null, cstmt.getString(2), TestResource.getResource("R_valueNotMatch")); + try { + cstmt.getString(2); + fail(TestResource.getResource("R_expectedExceptionNotThrown")); + } catch (Exception e) {} } } }