Repository: microsoft/mssql-jdbc
Pull request #2154 (2023-06-28 23:40:36)

Execute Stored Procedures Directly
Continued work from https://github.com/microsoft/mssql-jdbc/pull/547.

Points of interest:

1. In order to use parameter names and indexes interchangeably for acquiring/setting parameter eg. `cstmt.getXXX` or `cstmt.registerOutputParameter`, the driver will need to continue to use metadata query to get the parameter names when the new CS property `useFlexibleCallableStatements=true` (default behaviour).

2. xp_sqljdbc_xa calls have a special case registering their output parameters for CHAR and BINARY types as non-PLP. Prior to this PR, because cstmts calls were wrapped the RPC parameters sent to the server were defined within the `sp_execute` calls and so it wasn't necessary to do so.

3. To read the output parameter return values, parsing the tds response is deferred to the `getXXX` calls. So, if a user executes a cstmt but doesn't get/read the output params, the return values in the tds response will be in limbo/waiting for resolution. In such case, on statement close we will now skip these return values in the tds response which is safe to do.

4. Prior to the PR, it was possible to get the return value from sprocs as a byte array. This shouldn't be possible and this is corrected in this PR. It will throw a casting exception.

5. To allow for unordered setting/registering of parameters, we'll need to continue to use a metadata query for the parameter names (this goes along with point 1 when `useFlexibleCallableStatements=true`). 

6. If `outParams` are null then likely the statement was closed, so we throw an exception.

7. Potentially we can skip building the type definitions as this is no longer needed since we send the RPC params directly. However, this breaks AE and so needs more investigation.

8. For user defined functions (UDF) to work, we need to know when the TDS's response return value is for a sproc or a UDF. To do so, new logic was added to parse the TDS's response to determine this.

9. On the main branch for datetime (datetime, datetime2), the RPC call would always send the parameter as datetime2(7). However, since the cstmts are wrapped, the real param definition was set in the `sp_executesql/sp_prepexec` by building the type definitions in `buildParamTypeDefinitions` which are affected by the `datetimeParameterType` CS property. Since we are executing cstmts directly, datetime (datetime, datetime2) will now do the RPC with the exact datetime parameter definition. Meaning, depending on the `datetimeParameterType` CS property, datetime2 will be sent in the RPC as datetime2(3) exactly by default (which was the prior behaviour done by/indirectly set in the build type definitions) or sent plainly as a datetime.

10. New CS property `useFlexibleCallableStatements`. Default behaviour is `useFlexibleCallableStatements=true`, and in such case the driver will continue to use `sp_sproc_columns`. Otherwise, when `useFlexibleCallableStatements=false`, setting/registering parameters for callable statements require the following:

- a) Setting/Registering callable statement parameters must be exclusively by index or exclusively by parameter name. A mix of index and parameter names will not work.
- b) Setting/Registering callable statement parameters must be in same order as the sproc definition. Setting the parameters in an adhoc order will not work.
- c) Sprocs with cursorable methods will continue as is with sp_sproc_calls regardless of each new CS prop scenario.


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b0bc8192..d9595784 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,30 @@ All notable changes to this project will be documented in this file.
 
 The format is based on [Keep a Changelog](http://keepachangelog.com/)
 
+## [12.5.0] Preview Release
+### Added
+- Added connection property, `useDefaultJaasConfig`, to allow Kerberos authentication without any additional external configuration [#2147](https://github.com/microsoft/mssql-jdbc/pull/2147)
+- Allow calling of stored procedures directly, simplifying the procedure and improving performance [#2154](https://github.com/microsoft/mssql-jdbc/pull/2154)
+- Added connection property, `useDefaultGSSCredential`, to allow the driver to create GSSCredential on behalf of a user using Native GSS-API for Kerberos authentication [#2177](https://github.com/microsoft/mssql-jdbc/pull/2177)
+- Added Java 21 support [#2229](https://github.com/microsoft/mssql-jdbc/pull/2229)
+- Added connection property, `calcBigDecimalScale`, to allow the driver to calculate scale and percision from Big Decimal inputs [#2248](https://github.com/microsoft/mssql-jdbc/pull/2248)
+- Added a new named logger for connection open retries and idle connection resiliency reconnects [#2250](https://github.com/microsoft/mssql-jdbc/pull/2250)
+### Changed
+- Changed how IBM JDK is checked for to prevent issues with OSGi environments [#2150](https://github.com/microsoft/mssql-jdbc/pull/2150)
+- Updated azure-security-keyvault-keys, bouncycastle, and h2 library versions [#2162](https://github.com/microsoft/mssql-jdbc/pull/2162)[#2182](https://github.com/microsoft/mssql-jdbc/pull/2182)[#2249](https://github.com/microsoft/mssql-jdbc/pull/2249)
+- Changes to bulkcopy to allow for performance improvements when loading a large number of timestamps [#2194](https://github.com/microsoft/mssql-jdbc/pull/2194)
+- Properly synchronize all calls to MSAL, preventing the driver from making extra calls and providing unneccessary dialogues [#2218](https://github.com/microsoft/mssql-jdbc/pull/2218)
+- Changed driver retry behavior to retry the correct number of times based on connectRetryCount [#2247](https://github.com/microsoft/mssql-jdbc/pull/2247)
+### Fixed issues
+- Fix to ignore irrelevant computed columns during bulk insert [#1562](https://github.com/microsoft/mssql-jdbc/pull/1562)
+- Fixed an issue where signature was not properly verfied when using Java Key Store, as well as adding a new API to sign column master key metadata (and return generated signature) for use with Java Key Store and Azure Key Vault [#2160](https://github.com/microsoft/mssql-jdbc/pull/2160)
+- Fixed an issue where a null SQLState was returned when trying to convert a date to a long [#2185](https://github.com/microsoft/mssql-jdbc/pull/2185)
+- Fixed an issue where schemaPattern was not properly being escaped in SQLServerDatabaseMetadata [#2195](https://github.com/microsoft/mssql-jdbc/pull/2195)
+- Fixes getObject()'s erroneous conversion of DateTimeOffset to LocalDateTime [#2204](https://github.com/microsoft/mssql-jdbc/pull/2204)
+- Fixed an issue with ParameterMetadata not properly checking for an invalid index 0 [#2217](https://github.com/microsoft/mssql-jdbc/pull/2217)
+- Fixed bulkcopy metadata query to make use of cached data, if it's available [#2231](https://github.com/microsoft/mssql-jdbc/pull/2231)
+- Fixed an issue when writing TVP row values, where input values were not properly filtered for BigDecimal scale [#2239](https://github.com/microsoft/mssql-jdbc/pull/2239)
+
 ## [12.4.0] Stable Release
 ### Fixed issues
 - Revert #2051 (Big decimal precision) / #2116 (Fix for bigDecimal values between 0 and 1 having too high of a precision) [#2176](https://github.com/microsoft/mssql-jdbc/pull/2176)
diff --git a/pom.xml b/pom.xml
index 3052d34f..385bddfa 100644
--- a/pom.xml
+++ b/pom.xml
@@ -43,7 +43,7 @@
 			xAzureSQLDW - - - - For tests not compatible with Azure Data Warehouse - 
 			xAzureSQLMI - - - - For tests not compatible with Azure SQL Managed Instance 
 			NTLM  - - - - - - - For tests using NTLM Authentication mode (excluded by default)
-			kerberos -  - - - - For tests using Kerberos authentication (excluded by default)
+			kerberos  - - - - - For tests using Kerberos authentication (excluded by default)
 			reqExternalSetup - For tests requiring external setup (excluded by default)
 			clientCertAuth - - For tests requiring client certificate authentication 
 			setup (excluded by default) - - - - - - - - - - - - - - - - - - - - - - - 
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java
index 9e239c5d..0039457d 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java
@@ -466,6 +466,7 @@ final class TDS {
     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");
@@ -4788,7 +4789,7 @@ final class TDSWriter {
      * Utility for internal writeRPCString calls
      */
     void writeRPCStringUnicode(String sValue) throws SQLServerException {
-        writeRPCStringUnicode(null, sValue, false, null);
+        writeRPCStringUnicode(null, sValue, false, null, false);
     }
 
     /**
@@ -4803,8 +4804,8 @@ final class TDSWriter {
      * @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
@@ -4816,10 +4817,9 @@ final class TDSWriter {
          * 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);
 
-            // Handle Yukon v*max type header here.
             writeVMaxHeader(nValueLen, // Length
                     bValueNull, // Is null?
                     collation);
@@ -5418,7 +5418,6 @@ final class TDSWriter {
                     // Use PLP encoding on Yukon and later with long values
                     if (!isShortValue) // PLP
                     {
-                        // Handle Yukon v*max type header here.
                         writeShort((short) 0xFFFF);
                         con.getDatabaseCollation().writeCollation(this);
                     } else // non PLP
@@ -5436,7 +5435,6 @@ final class TDSWriter {
                     isShortValue = pair.getValue().precision <= DataTypes.SHORT_VARTYPE_MAX_BYTES;
                     // Use PLP encoding on Yukon and later with long values
                     if (!isShortValue) // PLP
-                        // Handle Yukon v*max type header here.
                         writeShort((short) 0xFFFF);
                     else // non PLP
                         writeShort((short) DataTypes.SHORT_VARTYPE_MAX_BYTES);
@@ -5571,8 +5569,8 @@ final class TDSWriter {
         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);
@@ -5618,8 +5616,7 @@ final class TDSWriter {
 
         writeRPCNameValType(sName, bOut, tdsType);
 
-        if (usePLP) {
-            // Handle Yukon v*max type header here.
+        if (usePLP && !isNonPLP) {
             writeVMaxHeader(nValueLen, bValueNull, collation);
 
             // Send the data.
@@ -6400,7 +6397,6 @@ final class TDSWriter {
 
             writeRPCNameValType(sName, bOut, jdbcType.isTextual() ? TDSType.BIGVARCHAR : TDSType.BIGVARBINARY);
 
-            // Handle Yukon v*max type header here.
             writeVMaxHeader(streamLength, false, jdbcType.isTextual() ? collation : null);
         }
 
@@ -6540,7 +6536,6 @@ final class TDSWriter {
 
             writeRPCNameValType(sName, bOut, TDSType.NVARCHAR);
 
-            // Handle Yukon v*max type header here.
             writeVMaxHeader(
                     (DataTypes.UNKNOWN_STREAM_LENGTH == reLength) ? DataTypes.UNKNOWN_STREAM_LENGTH : 2 * reLength, // Length
                                                                                                                     // (in
@@ -6995,6 +6990,35 @@ final class TDSReader implements Serializable {
         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/ISQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection.java
index 314e6443..28e06598 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection.java
@@ -496,5 +496,5 @@ public interface ISQLServerConnection extends java.sql.Connection {
      * @param calcBigDecimalScale
      *        A boolean that indicates if the driver should calculate scale from inputted big decimal values.
      */
-    void setCalcBigDecimalScale(boolean computeBigDecimal);
+    void setCalcBigDecimalScale(boolean calcBigDecimalScale);
 }
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerDataSource.java b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerDataSource.java
index 1e623875..af6b1afc 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.
      *
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java b/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java
index fe098500..858111ca 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/Parameter.java
@@ -37,6 +37,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;
     }
@@ -48,6 +50,7 @@ final class Parameter {
     private boolean shouldHonorAEForParameter = false;
     private boolean userProvidesPrecision = false;
     private boolean userProvidesScale = false;
+    private boolean isReturnValue = false;
 
     // The parameter type definition
     private String typeDefinition = null;
@@ -70,6 +73,60 @@ final class Parameter {
         return null != registeredOutDTV;
     }
 
+    /**
+     * 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;
+    }
+
+    /**
+     * Returns the `inputDTV` instance of the parameter
+     *
+     * @return inputDTV
+     */
+    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.
@@ -246,7 +303,7 @@ final class Parameter {
         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());
     }
 
@@ -387,10 +444,14 @@ final class Parameter {
 
     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);
@@ -1206,15 +1267,17 @@ final class Parameter {
         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/SQLServerCallableStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java
index ee6be290..07c8969a 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<String, Integer> 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,6 +110,7 @@ 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
@@ -157,6 +171,11 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         loggerExternal.exiting(getClassNameLogging(), "registerOutParameter");
     }
 
+    void registerOutParameterNonPLP(int index, int sqlType) throws SQLServerException {
+        registerOutParameter(index, sqlType);
+        inOutParam[index - 1].isNonPLP = true;
+    }
+
     /**
      * Locate any output parameter values returned from the procedure call
      */
@@ -168,13 +187,29 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         processResults();
 
         // if this item has been indexed already leave!
-        if (inOutParam[i - 1] == lastParamAccessed || inOutParam[i - 1].isValueGotten())
+        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 != userDefinedFunctionReturnStatus) {
+            return inOutParam[i - 1];
+        }
 
         // Skip OUT parameters (buffering them as we go) until we
         // reach the one we're looking for.
-        while (outParamIndex != i - 1)
+        while (outParamIndex != i - 1) {
             skipOutParameters(1, false);
+        }
 
         return inOutParam[i - 1];
     }
@@ -226,18 +261,24 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         }
 
         // 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;
+            }
         }
     }
 
@@ -308,60 +349,75 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
 
         OutParamHandler outParamHandler = new OutParamHandler();
 
+        if (bReturnValueSyntax && (nOutParamsAssigned == 0) && !isCursorable(executeMethod) && !isTVPType
+                && callRPCDirectly(inOutParam) && returnValueStatus != userDefinedFunctionReturnStatus) {
+            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();
+
+                if (bReturnValueSyntax && !isCursorable(executeMethod) && !isTVPType && callRPCDirectly(inOutParam)
+                        && returnValueStatus != userDefinedFunctionReturnStatus) {
+                    outParamIndex++;
+                } else {
+                    // Statements need to have their out param indices adjusted by the number
+                    // of sp_[cursor][prep]exec params.
+                    outParamIndex -= outParamIndexAdjustment;
+                }
 
-            // 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 ((outParamIndex < 0 || outParamIndex >= inOutParam.length)
+                        || (!inOutParam[outParamIndex].isOutput())) {
+                    if (getStatementLogger().isLoggable(java.util.logging.Level.INFO)) {
+                        getStatementLogger().info(toString() + " Unexpected outParamIndex: " + outParamIndex
+                                + "; adjustment: " + outParamIndexAdjustment);
+                    }
+                    connection.throwInvalidTDS();
                 }
-                connection.throwInvalidTDS();
-            }
 
-            ++nOutParamsAssigned;
+                ++nOutParamsAssigned;
+            }
         }
     }
 
@@ -414,6 +470,10 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
 
         // 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);
@@ -444,7 +504,13 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
     }
 
     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 {
@@ -487,7 +553,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
     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;
     }
@@ -510,7 +577,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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();
         }
@@ -531,7 +599,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
     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;
     }
@@ -561,7 +630,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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);
@@ -581,7 +651,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
     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;
     }
@@ -600,7 +671,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
     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;
@@ -619,7 +691,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
     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;
     }
@@ -637,7 +710,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
     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;
     }
@@ -657,7 +731,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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;
     }
@@ -675,7 +750,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
     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;
     }
@@ -691,10 +767,10 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
 
     @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;
     }
@@ -713,7 +789,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
     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;
     }
@@ -826,7 +903,7 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
     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());
@@ -838,7 +915,7 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
     public <T> T getObject(String parameterName, Class<T> 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;
@@ -846,7 +923,6 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
 
     @Override
     public short getShort(int index) throws SQLServerException {
-
         loggerExternal.entering(getClassNameLogging(), "getShort", index);
         checkClosed();
         Short value = (Short) getValue(index, JDBCType.SMALLINT);
@@ -858,7 +934,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
     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;
     }
@@ -877,7 +954,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
     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;
     }
@@ -897,7 +975,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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;
     }
@@ -916,7 +995,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
     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;
     }
@@ -932,11 +1012,12 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
     }
 
     @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;
     }
@@ -963,7 +1044,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
     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;
     }
@@ -979,11 +1061,12 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
     }
 
     @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;
     }
@@ -1002,7 +1085,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
     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;
     }
@@ -1018,11 +1102,13 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
     }
 
     @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;
     }
@@ -1053,8 +1139,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             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),
-                JDBCType.DATETIMEOFFSET);
+        microsoft.sql.DateTimeOffset value = (microsoft.sql.DateTimeOffset) getValue(
+                findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD), JDBCType.DATETIMEOFFSET);
         loggerExternal.exiting(getClassNameLogging(), "getDateTimeOffset", value);
         return value;
     }
@@ -1084,7 +1170,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
     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;
     }
@@ -1102,7 +1189,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
     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;
     }
@@ -1120,7 +1208,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
     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;
     }
@@ -1138,7 +1227,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
     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;
     }
@@ -1156,7 +1246,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
     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;
     }
@@ -1174,7 +1265,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
     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;
     }
@@ -1192,7 +1284,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
     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;
     }
@@ -1210,7 +1303,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
     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;
     }
@@ -1240,7 +1334,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
     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;
     }
@@ -1258,7 +1353,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
     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 +1368,7 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
     @Override
     public Object getObject(String parameterName, java.util.Map<String, Class<?>> m) throws SQLException {
         checkClosed();
-        return getObject(findColumn(parameterName), m);
+        return getObject(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD), m);
     }
 
     @Override
@@ -1284,7 +1380,7 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
     @Override
     public Ref getRef(String parameterName) throws SQLException {
         checkClosed();
-        return getRef(findColumn(parameterName));
+        return getRef(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_GETTER_METHOD));
     }
 
     @Override
@@ -1296,106 +1392,153 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
     @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(", ");
-                }
-                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);
-                }
+    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 inOutParam is null, likely the statement was closed beforehand.
+        if (null == inOutParam) {
+            SQLServerException.makeFromDriverError(connection, this,
+                    SQLServerException.getErrString("R_statementIsClosed"), null, false);
+        }
+
+        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++);
+                    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
@@ -1405,7 +1548,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             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");
     }
 
@@ -1416,7 +1560,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             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");
     }
 
@@ -1425,7 +1570,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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");
     }
 
@@ -1436,7 +1582,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             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");
     }
 
@@ -1445,7 +1592,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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");
     }
 
@@ -1456,17 +1604,18 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             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");
     }
 
@@ -1476,18 +1625,19 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             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");
     }
 
@@ -1496,8 +1646,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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");
     }
 
@@ -1507,7 +1657,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             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");
     }
 
@@ -1516,17 +1667,18 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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");
     }
 
@@ -1535,7 +1687,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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");
     }
 
@@ -1544,26 +1697,28 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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");
     }
 
@@ -1572,7 +1727,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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");
     }
 
@@ -1582,7 +1738,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             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");
     }
 
@@ -1591,7 +1748,7 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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");
     }
 
@@ -1602,12 +1759,15 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             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");
     }
 
@@ -1617,8 +1777,9 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             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 +1795,10 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         // 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 +1816,11 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         // 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 +1830,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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 +1841,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             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 +1852,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             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 +1862,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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 +1873,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             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 +1884,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             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 +1894,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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 +1904,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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 +1915,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             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 +1925,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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 +1935,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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 +1947,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             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 +1957,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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 +1968,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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 +1980,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             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 +1990,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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 +2000,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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 +2010,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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 +2022,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             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 +2032,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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 +2044,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             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 +2054,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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,7 +2066,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             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");
     }
 
@@ -1895,7 +2076,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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");
     }
 
@@ -1905,7 +2087,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             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");
     }
 
@@ -1914,7 +2097,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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");
     }
 
@@ -1924,7 +2108,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             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");
     }
 
@@ -1933,7 +2118,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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");
     }
 
@@ -1943,7 +2129,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             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");
     }
 
@@ -1952,7 +2139,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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");
     }
 
@@ -1962,7 +2150,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             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");
     }
 
@@ -1971,7 +2160,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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");
     }
 
@@ -1981,7 +2171,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             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");
     }
 
@@ -1990,7 +2181,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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");
     }
 
@@ -2000,7 +2192,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             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");
     }
 
@@ -2009,7 +2202,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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");
     }
 
@@ -2020,7 +2214,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             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");
     }
 
@@ -2031,8 +2226,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             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");
     }
 
@@ -2041,7 +2236,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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");
     }
 
@@ -2051,7 +2247,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             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");
     }
 
@@ -2060,7 +2257,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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");
     }
 
@@ -2070,7 +2268,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             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");
     }
 
@@ -2079,7 +2278,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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");
     }
 
@@ -2088,7 +2288,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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");
     }
 
@@ -2097,7 +2298,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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");
     }
 
@@ -2107,7 +2309,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             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");
     }
 
@@ -2116,7 +2319,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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");
     }
 
@@ -2126,7 +2330,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             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");
     }
 
@@ -2135,7 +2340,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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");
     }
 
@@ -2145,7 +2351,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             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");
     }
 
@@ -2154,8 +2361,9 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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");
     }
 
@@ -2164,8 +2372,9 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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");
     }
 
@@ -2173,43 +2382,49 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
     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");
     }
 
@@ -2230,7 +2445,7 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
         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");
     }
 
@@ -2247,7 +2462,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
     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;
     }
@@ -2275,7 +2491,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             loggerExternal.entering(getClassNameLogging(), "registerOutParameter",
                     new Object[] {parameterName, sqlType, typeName});
         checkClosed();
-        registerOutParameter(findColumn(parameterName), sqlType, typeName);
+        registerOutParameter(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), sqlType,
+                typeName);
         loggerExternal.exiting(getClassNameLogging(), "registerOutParameter");
     }
 
@@ -2285,7 +2502,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             loggerExternal.entering(getClassNameLogging(), "registerOutParameter",
                     new Object[] {parameterName, sqlType, scale});
         checkClosed();
-        registerOutParameter(findColumn(parameterName), sqlType, scale);
+        registerOutParameter(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), sqlType,
+                scale);
         loggerExternal.exiting(getClassNameLogging(), "registerOutParameter");
     }
 
@@ -2296,7 +2514,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             loggerExternal.entering(getClassNameLogging(), "registerOutParameter",
                     new Object[] {parameterName, sqlType, scale});
         checkClosed();
-        registerOutParameter(findColumn(parameterName), sqlType, precision, scale);
+        registerOutParameter(findColumn(parameterName, CallableStatementGetterSetterMethod.IS_SETTER_METHOD), sqlType,
+                precision, scale);
         loggerExternal.exiting(getClassNameLogging(), "registerOutParameter");
     }
 
@@ -2306,7 +2525,7 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple
             loggerExternal.entering(getClassNameLogging(), "registerOutParameter",
                     new Object[] {parameterName, sqlType});
         checkClosed();
-        registerOutParameter(findColumn(parameterName), sqlType);
+        registerOutParameter(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 4ffc8f32..5e148f31 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java
@@ -164,6 +164,9 @@ 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
@@ -508,6 +511,10 @@ public class SQLServerConnection implements ISQLServerConnection, java.io.Serial
         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;
 
@@ -2254,6 +2261,15 @@ public class SQLServerConnection implements ISQLServerConnection, java.io.Serial
                 }
                 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;
@@ -7448,7 +7464,7 @@ public class SQLServerConnection implements ISQLServerConnection, java.io.Serial
 
         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;
 
@@ -7866,6 +7882,29 @@ public class SQLServerConnection implements ISQLServerConnection, java.io.Serial
         this.accessTokenCallbackClass = accessTokenCallbackClass;
     }
 
+    /**
+     * Returns whether or not sp_sproc_columns is being used for parameter name lookup.
+     *
+     * @return useFlexibleCallableStatements
+     */
+    public boolean getUseFlexibleCallableStatements() {
+        return this.useFlexibleCallableStatements;
+    }
+
+    /**
+     * 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.
+     */
+    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
      * threshold has been reached.
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java
index 626dca78..a24e5f51 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java
@@ -250,6 +250,17 @@ public class SQLServerDataSource
                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);
diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java
index 3eac09e9..727e165b 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java
@@ -697,6 +697,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_SCALE("calcBigDecimalScale", false);
 
     private final String name;
@@ -774,6 +775,9 @@ public final class SQLServerDriver implements java.sql.Driver {
             new SQLServerDriverPropertyInfo(SQLServerDriverBooleanProperty.USE_DEFAULT_GSS_CREDENTIAL.toString(),
                     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 cfe57812..52e1d542 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java
@@ -374,6 +374,10 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
         for (int i = 0; i < nParams; i++) {
             inOutParam[i] = new Parameter(Util.shouldHonorAEForParameters(stmtColumnEncriptionSetting, connection));
         }
+
+        if (bReturnValueSyntax) {
+            inOutParam[0].setReturnValue(true);
+        }
     }
 
     @Override
@@ -427,13 +431,14 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
             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];
@@ -745,16 +750,23 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
     /**
      * 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);
         }
     }
 
@@ -802,6 +814,25 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
         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:"
@@ -1119,6 +1150,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
         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)) {
@@ -1127,9 +1159,14 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
             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.
@@ -1155,11 +1192,40 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
             }
         }
 
-        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);
+        return (null != procedureName && paramCount != 0 && !isTVPType(params));
+    }
+
+    /**
+     * 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;
+    }
+
     /**
      * Executes sp_prepare to prepare a parameterized statement and sets the prepared statement handle
      *
@@ -1233,6 +1299,11 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
      *            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};
@@ -1281,6 +1352,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setAsciiStream(int parameterIndex, InputStream x) throws SQLException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setAsciiStream", new Object[] {parameterIndex, x});
         checkClosed();
@@ -1290,6 +1362,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setAsciiStream(int n, java.io.InputStream x, int length) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setAsciiStream", new Object[] {n, x, length});
         checkClosed();
@@ -1299,6 +1372,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setAsciiStream", new Object[] {parameterIndex, x, length});
         checkClosed();
@@ -1308,6 +1382,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setBigDecimal", new Object[] {parameterIndex, x});
         checkClosed();
@@ -1318,6 +1393,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
     @Override
     public final void setBigDecimal(int parameterIndex, BigDecimal x, int precision,
             int scale) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setBigDecimal",
                     new Object[] {parameterIndex, x, precision, scale});
@@ -1329,6 +1405,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
     @Override
     public final void setBigDecimal(int parameterIndex, BigDecimal x, int precision, int scale,
             boolean forceEncrypt) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setBigDecimal",
                     new Object[] {parameterIndex, x, precision, scale, forceEncrypt});
@@ -1339,6 +1416,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setMoney(int n, BigDecimal x) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setMoney", new Object[] {n, x});
         checkClosed();
@@ -1348,6 +1426,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setMoney(int n, BigDecimal x, boolean forceEncrypt) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setMoney", new Object[] {n, x, forceEncrypt});
         checkClosed();
@@ -1357,6 +1436,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setSmallMoney(int n, BigDecimal x) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setSmallMoney", new Object[] {n, x});
         checkClosed();
@@ -1366,6 +1446,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setSmallMoney(int n, BigDecimal x, boolean forceEncrypt) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setSmallMoney", new Object[] {n, x, forceEncrypt});
         checkClosed();
@@ -1375,6 +1456,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setBinaryStream(int parameterIndex, InputStream x) throws SQLException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setBinaryStreaml", new Object[] {parameterIndex, x});
         checkClosed();
@@ -1384,6 +1466,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setBinaryStream(int n, java.io.InputStream x, int length) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setBinaryStream", new Object[] {n, x, length});
         checkClosed();
@@ -1393,6 +1476,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setBinaryStream", new Object[] {parameterIndex, x, length});
         checkClosed();
@@ -1402,6 +1486,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setBoolean(int n, boolean x) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setBoolean", new Object[] {n, x});
         checkClosed();
@@ -1411,6 +1496,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setBoolean(int n, boolean x, boolean forceEncrypt) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setBoolean", new Object[] {n, x, forceEncrypt});
         checkClosed();
@@ -1420,6 +1506,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setByte(int n, byte x) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setByte", new Object[] {n, x});
         checkClosed();
@@ -1429,6 +1516,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setByte(int n, byte x, boolean forceEncrypt) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setByte", new Object[] {n, x, forceEncrypt});
         checkClosed();
@@ -1438,6 +1526,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setBytes(int n, byte[] x) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setBytes", new Object[] {n, x});
         checkClosed();
@@ -1447,6 +1536,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setBytes(int n, byte[] x, boolean forceEncrypt) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setBytes", new Object[] {n, x, forceEncrypt});
         checkClosed();
@@ -1456,6 +1546,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setUniqueIdentifier(int index, String guid) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setUniqueIdentifier", new Object[] {index, guid});
         checkClosed();
@@ -1465,6 +1556,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setUniqueIdentifier(int index, String guid, boolean forceEncrypt) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setUniqueIdentifier",
                     new Object[] {index, guid, forceEncrypt});
@@ -1475,6 +1567,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setDouble(int n, double x) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setDouble", new Object[] {n, x});
         checkClosed();
@@ -1484,6 +1577,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setDouble(int n, double x, boolean forceEncrypt) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setDouble", new Object[] {n, x, forceEncrypt});
         checkClosed();
@@ -1493,6 +1587,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setFloat(int n, float x) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setFloat", new Object[] {n, x});
         checkClosed();
@@ -1502,6 +1597,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setFloat(int n, float x, boolean forceEncrypt) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setFloat", new Object[] {n, x, forceEncrypt});
         checkClosed();
@@ -1511,6 +1607,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setGeometry(int n, Geometry x) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setGeometry", new Object[] {n, x});
         checkClosed();
@@ -1520,6 +1617,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setGeography(int n, Geography x) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setGeography", new Object[] {n, x});
         checkClosed();
@@ -1529,6 +1627,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setInt(int n, int value) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setInt", new Object[] {n, value});
         checkClosed();
@@ -1538,6 +1637,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setInt(int n, int value, boolean forceEncrypt) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setInt", new Object[] {n, value, forceEncrypt});
         checkClosed();
@@ -1547,6 +1647,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setLong(int n, long x) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setLong", new Object[] {n, x});
         checkClosed();
@@ -1556,6 +1657,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setLong(int n, long x, boolean forceEncrypt) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setLong", new Object[] {n, x, forceEncrypt});
         checkClosed();
@@ -1565,6 +1667,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setNull(int index, int jdbcType) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setNull", new Object[] {index, jdbcType});
         checkClosed();
@@ -1610,6 +1713,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setObject(int index, Object obj) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setObject", new Object[] {index, obj});
         checkClosed();
@@ -1619,6 +1723,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setObject(int n, Object obj, int jdbcType) throws SQLServerException {
+        isSetByIndex = true;
         String tvpName = null;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setObject", new Object[] {n, obj, jdbcType});
@@ -1633,6 +1738,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
     @Override
     public final void setObject(int parameterIndex, Object x, int targetSqlType,
             int scaleOrLength) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setObject",
                     new Object[] {parameterIndex, x, targetSqlType, scaleOrLength});
@@ -1656,6 +1762,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
     @Override
     public final void setObject(int parameterIndex, Object x, int targetSqlType, Integer precision,
             int scale) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setObject",
                     new Object[] {parameterIndex, x, targetSqlType, precision, scale});
@@ -1677,6 +1784,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
     @Override
     public final void setObject(int parameterIndex, Object x, int targetSqlType, Integer precision, int scale,
             boolean forceEncrypt) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setObject",
                     new Object[] {parameterIndex, x, targetSqlType, precision, scale, forceEncrypt});
@@ -1774,6 +1882,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setShort(int index, short x) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setShort", new Object[] {index, x});
         checkClosed();
@@ -1783,6 +1892,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setShort(int index, short x, boolean forceEncrypt) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setShort", new Object[] {index, x, forceEncrypt});
         checkClosed();
@@ -1792,6 +1902,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setString(int index, String str) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setString", new Object[] {index, str});
         checkClosed();
@@ -1801,6 +1912,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setString(int index, String str, boolean forceEncrypt) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setString", new Object[] {index, str, forceEncrypt});
         checkClosed();
@@ -1810,6 +1922,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setNString(int parameterIndex, String value) throws SQLException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setNString", new Object[] {parameterIndex, value});
         checkClosed();
@@ -1819,6 +1932,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setNString(int parameterIndex, String value, boolean forceEncrypt) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setNString",
                     new Object[] {parameterIndex, value, forceEncrypt});
@@ -1829,6 +1943,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setTime(int n, java.sql.Time x) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setTime", new Object[] {n, x});
         checkClosed();
@@ -1838,6 +1953,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setTime(int n, java.sql.Time x, int scale) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setTime", new Object[] {n, x, scale});
         checkClosed();
@@ -1847,6 +1963,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setTime(int n, java.sql.Time x, int scale, boolean forceEncrypt) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setTime", new Object[] {n, x, scale, forceEncrypt});
         checkClosed();
@@ -1856,6 +1973,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setTimestamp(int n, java.sql.Timestamp x) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setTimestamp", new Object[] {n, x});
         checkClosed();
@@ -1865,6 +1983,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setTimestamp(int n, java.sql.Timestamp x, int scale) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setTimestamp", new Object[] {n, x, scale});
         checkClosed();
@@ -1875,6 +1994,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
     @Override
     public final void setTimestamp(int n, java.sql.Timestamp x, int scale,
             boolean forceEncrypt) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setTimestamp", new Object[] {n, x, scale, forceEncrypt});
         checkClosed();
@@ -1884,6 +2004,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setDateTimeOffset(int n, microsoft.sql.DateTimeOffset x) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setDateTimeOffset", new Object[] {n, x});
         checkClosed();
@@ -1893,6 +2014,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setDateTimeOffset(int n, microsoft.sql.DateTimeOffset x, int scale) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setDateTimeOffset", new Object[] {n, x, scale});
         checkClosed();
@@ -1903,6 +2025,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
     @Override
     public final void setDateTimeOffset(int n, microsoft.sql.DateTimeOffset x, int scale,
             boolean forceEncrypt) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setDateTimeOffset",
                     new Object[] {n, x, scale, forceEncrypt});
@@ -1913,6 +2036,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setDate(int n, java.sql.Date x) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setDate", new Object[] {n, x});
         checkClosed();
@@ -1922,6 +2046,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setDateTime(int n, java.sql.Timestamp x) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setDateTime", new Object[] {n, x});
         checkClosed();
@@ -1931,6 +2056,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setDateTime(int n, java.sql.Timestamp x, boolean forceEncrypt) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setDateTime", new Object[] {n, x, forceEncrypt});
         checkClosed();
@@ -1940,6 +2066,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setSmallDateTime(int n, java.sql.Timestamp x) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setSmallDateTime", new Object[] {n, x});
         checkClosed();
@@ -1949,6 +2076,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setSmallDateTime(int n, java.sql.Timestamp x, boolean forceEncrypt) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setSmallDateTime", new Object[] {n, x, forceEncrypt});
         checkClosed();
@@ -1958,6 +2086,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setStructured(int n, String tvpName, SQLServerDataTable tvpDataTable) throws SQLServerException {
+        isSetByIndex = true;
         tvpName = getTVPNameIfNull(n, tvpName);
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setStructured", new Object[] {n, tvpName, tvpDataTable});
@@ -1968,6 +2097,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setStructured(int n, String tvpName, ResultSet tvpResultSet) throws SQLServerException {
+        isSetByIndex = true;
         tvpName = getTVPNameIfNull(n, tvpName);
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setStructured", new Object[] {n, tvpName, tvpResultSet});
@@ -1979,6 +2109,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
     @Override
     public final void setStructured(int n, String tvpName,
             ISQLServerDataRecord tvpBulkRecord) throws SQLServerException {
+        isSetByIndex = true;
         tvpName = getTVPNameIfNull(n, tvpName);
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setStructured", new Object[] {n, tvpName, tvpBulkRecord});
@@ -2991,7 +3122,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
                                     resetForReexecute();
                                     tdsWriter = batchCommand.startRequest(TDS.PKT_RPC);
                                     buildExecParams(tdsWriter);
-                                    sendParamsByRPC(tdsWriter, batchParam);
+                                    sendParamsByRPC(tdsWriter, batchParam, bReturnValueSyntax, false);
                                     ensureExecuteResultsReader(
                                             batchCommand.startResponse(getIsResponseBufferingAdaptive()));
                                     startResults();
@@ -3085,6 +3216,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setCharacterStream(int parameterIndex, Reader reader) throws SQLException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setCharacterStream", new Object[] {parameterIndex, reader});
         checkClosed();
@@ -3094,6 +3226,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setCharacterStream(int n, java.io.Reader reader, int length) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setCharacterStream", new Object[] {n, reader, length});
         checkClosed();
@@ -3103,6 +3236,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setCharacterStream",
                     new Object[] {parameterIndex, reader, length});
@@ -3113,6 +3247,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setNCharacterStream(int parameterIndex, Reader value) throws SQLException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setNCharacterStream", new Object[] {parameterIndex, value});
         checkClosed();
@@ -3122,6 +3257,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setNCharacterStream",
                     new Object[] {parameterIndex, value, length});
@@ -3137,6 +3273,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setBlob(int i, java.sql.Blob x) throws SQLException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setBlob", new Object[] {i, x});
         checkClosed();
@@ -3146,6 +3283,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setBlob(int parameterIndex, InputStream inputStream) throws SQLException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setBlob", new Object[] {parameterIndex, inputStream});
         checkClosed();
@@ -3156,6 +3294,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setBlob",
                     new Object[] {parameterIndex, inputStream, length});
@@ -3166,6 +3305,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setClob(int parameterIndex, java.sql.Clob clobValue) throws SQLException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setClob", new Object[] {parameterIndex, clobValue});
         checkClosed();
@@ -3175,6 +3315,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setClob(int parameterIndex, Reader reader) throws SQLException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setClob", new Object[] {parameterIndex, reader});
         checkClosed();
@@ -3184,6 +3325,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setClob(int parameterIndex, Reader reader, long length) throws SQLException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setClob", new Object[] {parameterIndex, reader, length});
         checkClosed();
@@ -3193,6 +3335,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setNClob(int parameterIndex, NClob value) throws SQLException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setNClob", new Object[] {parameterIndex, value});
         checkClosed();
@@ -3202,6 +3345,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setNClob(int parameterIndex, Reader reader) throws SQLException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setNClob", new Object[] {parameterIndex, reader});
         checkClosed();
@@ -3211,6 +3355,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setNClob(int parameterIndex, Reader reader, long length) throws SQLException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setNClob", new Object[] {parameterIndex, reader, length});
         checkClosed();
@@ -3225,6 +3370,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setDate(int n, java.sql.Date x, java.util.Calendar cal) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setDate", new Object[] {n, x, cal});
         checkClosed();
@@ -3235,6 +3381,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
     @Override
     public final void setDate(int n, java.sql.Date x, java.util.Calendar cal,
             boolean forceEncrypt) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setDate", new Object[] {n, x, cal, forceEncrypt});
         checkClosed();
@@ -3244,6 +3391,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setTime(int n, java.sql.Time x, java.util.Calendar cal) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setTime", new Object[] {n, x, cal});
         checkClosed();
@@ -3254,6 +3402,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
     @Override
     public final void setTime(int n, java.sql.Time x, java.util.Calendar cal,
             boolean forceEncrypt) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setTime", new Object[] {n, x, cal, forceEncrypt});
         checkClosed();
@@ -3263,6 +3412,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setTimestamp(int n, java.sql.Timestamp x, java.util.Calendar cal) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setTimestamp", new Object[] {n, x, cal});
         checkClosed();
@@ -3273,6 +3423,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
     @Override
     public final void setTimestamp(int n, java.sql.Timestamp x, java.util.Calendar cal,
             boolean forceEncrypt) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setTimestamp", new Object[] {n, x, cal, forceEncrypt});
         checkClosed();
@@ -3282,6 +3433,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setNull(int paramIndex, int sqlType, String typeName) throws SQLServerException {
+        isSetByIndex = true;
         if (loggerExternal.isLoggable(java.util.logging.Level.FINER))
             loggerExternal.entering(getClassNameLogging(), "setNull", new Object[] {paramIndex, sqlType, typeName});
         checkClosed();
@@ -3331,6 +3483,7 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
 
     @Override
     public final void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException {
+        isSetByIndex = true;
         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 f4fd2a25..9055e9ad 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 @@ public final class SQLServerResource extends ListResourceBundle {
         {"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", "Detected uncompliant use of both named and indexed parameters while 'useFlexibleCallableStatements=false'. It is suggested to either exclusively use named parameters or indexed parameters."},
+        {"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 @@ public final class SQLServerResource extends ListResourceBundle {
         {"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 d9dfdfdb..2fe36051 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;
+
+    static int userDefinedFunctionReturnStatus = 2;
 
     final boolean getIsResponseBufferingAdaptive() {
         return isResponseBufferingAdaptive;
@@ -112,10 +120,13 @@ public class SQLServerStatement implements ISQLServerStatement {
         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 @@ public class SQLServerStatement implements ISQLServerStatement {
      */
     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.
@@ -1609,6 +1626,14 @@ public class SQLServerStatement implements ISQLServerStatement {
                 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;
@@ -1616,11 +1641,21 @@ public class SQLServerStatement implements ISQLServerStatement {
 
             @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 != userDefinedFunctionReturnStatus) {
                     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 7f9c44a2..6d82bf83 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerXAResource.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerXAResource.java
@@ -417,8 +417,8 @@ public final class SQLServerXAResource implements javax.transaction.xa.XAResourc
                             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) {
@@ -528,21 +528,21 @@ public final class SQLServerXAResource implements javax.transaction.xa.XAResourc
                     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;
 
@@ -550,12 +550,12 @@ public final class SQLServerXAResource implements javax.transaction.xa.XAResourc
                     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:
@@ -566,7 +566,7 @@ public final class SQLServerXAResource implements javax.transaction.xa.XAResourc
                         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)
@@ -578,7 +578,7 @@ public final class SQLServerXAResource implements javax.transaction.xa.XAResourc
                     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);
@@ -593,7 +593,7 @@ public final class SQLServerXAResource implements javax.transaction.xa.XAResourc
                         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)
@@ -608,7 +608,7 @@ public final class SQLServerXAResource implements javax.transaction.xa.XAResourc
                     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)
@@ -620,9 +620,9 @@ public final class SQLServerXAResource implements javax.transaction.xa.XAResourc
                     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 ab76347b..1be900a3 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java
@@ -132,6 +132,8 @@ final class DTV {
     int valueLength = 0;
     boolean sendStringParametersAsUnicode = true;
 
+    boolean isNonPLP = false;
+
     /**
      * Sets a DTV value from a Java object.
      *
@@ -289,7 +291,7 @@ final class DTV {
         }
 
         void execute(DTV dtv, String strValue) throws SQLServerException {
-            tdsWriter.writeRPCStringUnicode(name, strValue, isOutParam, collation);
+            tdsWriter.writeRPCStringUnicode(name, strValue, isOutParam, collation, dtv.isNonPLP);
         }
 
         void execute(DTV dtv, Clob clobValue) throws SQLServerException {
@@ -311,7 +313,7 @@ final class DTV {
             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);
@@ -322,7 +324,7 @@ final class DTV {
             } 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);
                 }
@@ -845,14 +847,41 @@ final class DTV {
 
                     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);
@@ -861,10 +890,11 @@ final class DTV {
                                             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;
 
@@ -1074,7 +1104,7 @@ final class DTV {
                             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);
@@ -1125,7 +1155,8 @@ final class DTV {
                 }
 
             } else
-                tdsWriter.writeRPCByteArray(name, byteArrayValue, isOutParam, dtv.getJdbcType(), collation);
+                tdsWriter.writeRPCByteArray(name, byteArrayValue, isOutParam, dtv.getJdbcType(), collation,
+                        dtv.isNonPLP);
 
         }
 
@@ -1378,7 +1409,7 @@ final class DTV {
             }
 
             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);
             }
@@ -2319,8 +2350,10 @@ final class AppDTVImpl extends DTVImpl {
             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 a26133f6..df50180b 100644
--- a/src/main/java/com/microsoft/sqlserver/jdbc/tdsparser.java
+++ b/src/main/java/com/microsoft/sqlserver/jdbc/tdsparser.java
@@ -232,6 +232,17 @@ class TDSTokenHandler {
     }
 
     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;
     }


Test patch:
diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java
index f4496236..9736254d 100644
--- a/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java
+++ b/src/test/java/com/microsoft/sqlserver/jdbc/SQLServerConnectionTest.java
@@ -205,6 +205,9 @@ public class SQLServerConnectionTest extends AbstractTest {
         assertEquals(booleanPropValue, ds.getUseDefaultGSSCredential(),
                 TestResource.getResource("R_valuesAreDifferent"));
 
+        ds.setUseFlexibleCallableStatements(booleanPropValue);
+        assertEquals(booleanPropValue, ds.getUseFlexibleCallableStatements(),
+                TestResource.getResource("R_valuesAreDifferent"));
         ds.setCalcBigDecimalScale(booleanPropValue);
         assertEquals(booleanPropValue, ds.getCalcBigDecimalScale(), 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 9a4c4177..449ea1f0 100644
--- a/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java
+++ b/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java
@@ -43,7 +43,8 @@ public final class TestResource extends ListResourceBundle {
             {"R_lengthTruncated", " The inserted length is truncated or not correct!"},
             {"R_timeValueTruncated", " The time value is truncated or not correct!"},
             {"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."},
@@ -55,6 +56,8 @@ public final class TestResource extends ListResourceBundle {
             {"R_createDropAlterTableFailed", "Create/drop/alter table with preparedStatement failed!"},
             {"R_grantFailed", "grant table with preparedStatement failed!"},
             {"R_connectionIsClosed", "The connection is closed."},
+            {"R_noNamedAndIndexedParameters", "Detected uncompliant use of both named and indexed parameters while 'useFlexibleCallableStatements=false'. It is suggested to either exclusively use named parameters or indexed parameters."},
+            {"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"},
@@ -63,6 +66,7 @@ public final class TestResource extends ListResourceBundle {
             {"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"},
@@ -204,6 +208,7 @@ public final class TestResource extends ListResourceBundle {
             {"R_connectTimedOut", "connect timed out"},
             {"R_sessionKilled", "Cannot continue the execution because the session is in the kill state"},
             {"R_failedFedauth", "Failed to acquire fedauth token: "},
-            {"R_noLoginModulesConfiguredForJdbcDriver", "javax.security.auth.login.LoginException (No LoginModules configured for SQLJDBCDriver)"},
+            {"R_noLoginModulesConfiguredForJdbcDriver",
+                    "javax.security.auth.login.LoginException (No LoginModules configured for SQLJDBCDriver)"},
             {"R_unexpectedThreadCount", "Thread count is higher than expected."}};
 }
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 239ae1f7..e9d73b79 100644
--- a/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableStatementTest.java
+++ b/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableStatementTest.java
@@ -1,5 +1,6 @@
 package com.microsoft.sqlserver.jdbc.callablestatement;
 
+import static org.junit.Assert.assertTrue;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.fail;
@@ -21,7 +22,7 @@ import java.time.LocalTime;
 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;
 import org.junit.jupiter.api.Tag;
@@ -34,10 +35,14 @@ import com.microsoft.sqlserver.jdbc.SQLServerCallableStatement;
 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;
 
+import javax.sql.DataSource;
+
 
 /**
  * Test CallableStatement
@@ -65,6 +70,14 @@ public class CallableStatementTest extends AbstractTest {
             .escapeIdentifier(RandomUtil.getIdentifier("manyParam_Procedure"));
     private static String manyParamUserDefinedType = AbstractSQLGenerator
             .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
@@ -82,6 +95,10 @@ public class CallableStatementTest extends AbstractTest {
             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.dropFunctionIfExists(userDefinedFunction, stmt);
             TestUtils.dropUserDefinedTypeIfExists(manyParamUserDefinedType, stmt);
             TestUtils.dropProcedureIfExists(manyParamProc, stmt);
             TestUtils.dropTableIfExists(manyParamsTable, stmt);
@@ -95,6 +112,21 @@ public class CallableStatementTest extends AbstractTest {
             createTableManyParams();
             createProcedureManyParams();
             createGetObjectOffsetDateTimeProcedure(stmt);
+            createProcedureZeroParams();
+            createOutOfOrderSproc();
+            createByParamNameSproc();
+            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());
         }
     }
 
@@ -332,7 +364,6 @@ public class CallableStatementTest extends AbstractTest {
             cs.setString("@whatever", "test");
             fail(TestResource.getResource("R_shouldThrowException"));
         } catch (SQLException sse) {
-
             MessageFormat form = new MessageFormat(TestResource.getResource("R_parameterNotDefined"));
             Object[] msgArgs = {"@whatever"};
 
@@ -342,6 +373,551 @@ public class CallableStatementTest extends AbstractTest {
         }
     }
 
+    @Test
+    public void testZeroParamSproc() throws SQLException {
+        String call = "{? = CALL " + zeroParamSproc + "}";
+
+        try (CallableStatement cs = connection.prepareCall(call)) {
+            cs.registerOutParameter(1, Types.INTEGER);
+            cs.execute();
+            assertEquals(1, cs.getInt(1));
+        }
+    }
+
+    @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 testNonOrderedRegisteringAndSettingOfParams() 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 = "bar";
+            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(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(9, Types.NUMERIC);
+            cstmt.registerOutParameter("o2", Types.NUMERIC, scale);
+            cstmt.registerOutParameter("o3", Types.INTEGER);
+            cstmt.registerOutParameter("o4", Types.FLOAT);
+
+            cstmt.registerOutParameter(13, Types.CHAR);
+            cstmt.registerOutParameter(14, Types.VARCHAR);
+            cstmt.registerOutParameter(15, Types.CHAR);
+            cstmt.registerOutParameter(16, Types.SMALLINT);
+            cstmt.execute();
+        }
+    }
+
+    @Test
+    public void testNamedParametersUseFlexibleCallableStatementFalse() throws SQLException {
+        String call = "{CALL " + outOfOrderSproc + " (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)}";
+        String connectionString = TestUtils.addOrOverrideProperty(getConnectionString(), "useFlexibleCallableStatements", "false");
+
+        // useFlexibleCallableStatement=false and using all named parameters
+        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 testNamedParametersAcquireOutputParamValuesByIndexUseFlexibleCallableStatementFalse() throws SQLException {
+        String call = "{CALL " + outOfOrderSproc + " (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)}";
+        String connectionString = TestUtils.addOrOverrideProperty(getConnectionString(), "useFlexibleCallableStatements", "false");
+
+        // useFlexibleCallableStatement=false
+        // Setting parameters by name
+        // Registering output parameters by name
+        // Acquiring output parameter values by index
+        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(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 testNamedParametersAndOutParamByIndexUseFlexibleCallableStatementFalse() throws SQLException {
+        String call = "{CALL " + outOfOrderSproc + " (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)}";
+        String connectionString = TestUtils.addOrOverrideProperty(getConnectionString(), "useFlexibleCallableStatements", "false");
+
+        // useFlexibleCallableStatement=false
+        // Setting parameters by name
+        // Registering output params by index
+        // Acquiring output param values by index
+        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(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 testNamedParameterOutputParameterErrorFlexibleCallableStatementFalse() throws SQLException {
+        String call = "{CALL " + outOfOrderSproc + " (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)}";
+        String connectionString = TestUtils.addOrOverrideProperty(getConnectionString(), "useFlexibleCallableStatements", "false");
+
+        // useFlexibleCallableStatement=false
+        // Setting parameters by name
+        // Registering output params by index
+        // Acquiring output param value 'o1' by name
+        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(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("o1"));
+                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_unknownOutputParameter"), e.getMessage());
+            }
+        }
+    }
+
+    @Test
+    public void testNamedParametersAndByIndexErrorUseFlexibleCallableStatementFalse() throws SQLException {
+        String call = "{CALL " + outOfOrderSproc + " (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)}";
+        String connectionString = TestUtils.addOrOverrideProperty(getConnectionString(), "useFlexibleCallableStatements", "false");
+
+        // useFlexibleCallableStatement=false
+        // Using majority named parameters and setting parameter by index
+        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 testIndexedParametersUseFlexibleCallableStatementFalse() throws SQLException {
+        String call = "{CALL " + outOfOrderSproc + " (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)}";
+        String connectionString = TestUtils.addOrOverrideProperty(getConnectionString(), "useFlexibleCallableStatements", "false");
+
+        // useFlexibleCallableStatement=false and using all index parameters
+        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 testIndexedParametersAcquireOutputValueByNameErrorUseFlexibleCallableStatementFalse() throws SQLException {
+        String call = "{CALL " + outOfOrderSproc + " (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)}";
+        String connectionString = TestUtils.addOrOverrideProperty(getConnectionString(), "useFlexibleCallableStatements", "false");
+
+        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 testIndexedParametersRegisterOutputParamByNameUseFlexibleCallableStatementFalse() throws SQLException {
+        String call = "{CALL " + outOfOrderSproc + " (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)}";
+        String connectionString = TestUtils.addOrOverrideProperty(getConnectionString(), "useFlexibleCallableStatements", "false");
+
+        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("o1", 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 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));
+        }
+    }
+
     /**
      * Cleanup after test
      * 
@@ -357,6 +933,10 @@ public class CallableStatementTest extends AbstractTest {
             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.dropFunctionIfExists(userDefinedFunction, stmt);
         }
     }
 
@@ -425,4 +1005,41 @@ public class CallableStatementTest extends AbstractTest {
             stmt.executeUpdate(TVPCreateCmd);
         }
     }
+
+    private static void createProcedureZeroParams() throws SQLException {
+        String sql = "CREATE PROCEDURE " + zeroParamSproc + " AS RETURN 1";
+        try (Statement stmt = connection.createStatement()) {
+            stmt.execute(sql);
+        }
+    }
+
+    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.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 bb524b67..e02be6e4 100644
--- a/src/test/java/com/microsoft/sqlserver/jdbc/connection/RequestBoundaryMethodsTest.java
+++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/RequestBoundaryMethodsTest.java
@@ -509,6 +509,8 @@ public class RequestBoundaryMethodsTest extends AbstractTest {
         verifiedMethodNames.add("setMsiTokenCacheTtl");
         verifiedMethodNames.add("getAccessTokenCallbackClass");
         verifiedMethodNames.add("setAccessTokenCallbackClass");
+        verifiedMethodNames.add("getUseFlexibleCallableStatements");
+        verifiedMethodNames.add("setUseFlexibleCallableStatements");
         verifiedMethodNames.add("getCalcBigDecimalScale");
         verifiedMethodNames.add("setCalcBigDecimalScale");
         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 99dbe64d..e8714b1c 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 static org.junit.jupiter.api.Assertions.assertEquals;
 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 class DateAndTimeTypeTest extends AbstractTest {
      * 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 class DateAndTimeTypeTest extends AbstractTest {
      * 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 class DateAndTimeTypeTest extends AbstractTest {
      * 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,85 @@ public class DateAndTimeTypeTest extends AbstractTest {
         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);
+            System.out.println(actual);
+        }
+
+        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);
+            System.out.println(actual);
+        }
+
+        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);
+            System.out.println(actual);
+        }
+
+        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 +385,19 @@ public class DateAndTimeTypeTest extends AbstractTest {
         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 +413,9 @@ public class DateAndTimeTypeTest extends AbstractTest {
                 createTVPs(timeTVP, "time");
                 createTVPs(timestampTVP, "datetime2");
             }
+
+            stmt.execute("insert into " + AbstractSQLGenerator.escapeIdentifier(datetimeTable)
+                    + " values('3160-08-17 19:09:06.9366667')");
         }
     }
 
@@ -328,6 +423,8 @@ public class DateAndTimeTypeTest extends AbstractTest {
     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/StatementTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java
index deb7abc0..f6215479 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
@@ -1661,7 +1661,7 @@ public class StatementTest extends AbstractTest {
          */
         @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)) {
 
@@ -1678,7 +1678,10 @@ public class StatementTest extends AbstractTest {
 
                     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) {}
                 }
             }
         }