Skip to content
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
f8811ea
Implement Sybase migration support with dynamicPrepare property
divang Oct 24, 2025
7dac544
Revert "Implement Sybase migration support with dynamicPrepare property"
divang Oct 24, 2025
cfb0e43
Add SqlServerPreparedStatementExpander with comprehensive test coverage
divang Nov 11, 2025
73cc7e2
Fix SqlServerPreparedStatementExpander test failures
divang Nov 11, 2025
b5f2299
Add 7 new E2E test cases for various SQL Server data types
divang Nov 11, 2025
9acdfc8
Added Sybase test code
divang Nov 11, 2025
c49e2d3
Merge branch 'main' of https://github.com/microsoft/mssql-jdbc into d…
divang Nov 17, 2025
47009b3
Add comprehensive test coverage for EXEC prepare method (#2834)
Ananya2 Nov 17, 2025
70d8b30
Fixed PrepareMethod - EXEC method related test cases
divang Nov 17, 2025
6f5de5c
Fixed pom.xml typo
divang Nov 17, 2025
0615022
Remove Sybase-related test files
divang Nov 27, 2025
676f27d
Consolidate SQL parameter expansion into SQLServerConnection
divang Nov 27, 2025
665951c
Fix parameter expansion to respect sendStringParametersAsUnicode and …
divang Nov 27, 2025
caf775c
Apply sendStringParametersAsUnicode config to Clob and fallback strin…
divang Nov 27, 2025
f1d26c1
Refactor replaceParameterMarkers to simplify parameter substitution
divang Nov 28, 2025
ba3b584
Fixed batch update count issue and cleaned the code
divang Dec 1, 2025
37ebc5c
When prepareMethod is set to 'exec', batch execution was failing enti…
divang Dec 2, 2025
c7712a9
Update count in case of batch related sql statements
divang Dec 2, 2025
ed847f5
Batching of SQL statement in case of exec preparedmethod
divang Dec 4, 2025
d01859f
Added replaceParameterMarkersWithValues() method and propogated execp…
divang Dec 8, 2025
8e2db67
Correct temp table naming and constraint violation assertions in Batc…
divang Dec 15, 2025
9adf6bf
Cleaned up unused code.
divang Dec 15, 2025
4c77ac7
Fixed Datae/Time converiosn in formatLiteralValue() method
divang Dec 15, 2025
5c7c24f
Cleaned up the old approach and hanlded review comments
divang Dec 16, 2025
7f3ef8d
timeoutOccurred variable is not required at this place
divang Dec 16, 2025
677a709
Handled OOO case replaceParameterMarkersWithValues() method
divang Dec 17, 2025
6598483
Fixed ADO pipeline for JDK8
divang Dec 17, 2025
867f204
Rename exec to scopeTempTablesToConnection and add temp table detecti…
divang Dec 24, 2025
a35c878
Merge branch 'main' into dev/divang/sybase-migration-dynamic-prepare
divang Dec 24, 2025
c4fe035
Fixed the old exec and renamed it to PreparedStatementTest for the pr…
divang Dec 24, 2025
b3c2905
Merge branch 'dev/divang/sybase-migration-dynamic-prepare' of https:/…
divang Dec 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8425,6 +8425,41 @@ void endRequestInternal() throws SQLException {

String replaceParameterMarkers(String sqlSrc, int[] paramPositions, Parameter[] params,
boolean isReturnValueSyntax) {
return replaceParameterMarkers(sqlSrc, paramPositions, params, isReturnValueSyntax, false);
}

String replaceParameterMarkers(String sqlSrc, int[] paramPositions, Parameter[] params,
boolean isReturnValueSyntax, boolean useDirectValues) {

if (useDirectValues) {
// EXEC method: substitute actual parameter values directly into SQL
if (params == null || params.length == 0) {
return sqlSrc;
}

// Convert Parameter array to List<Object> for
// SqlServerPreparedStatementExpander
List<Object> paramValues = new ArrayList<>(params.length);
for (Parameter param : params) {
if (param == null) {
paramValues.add(null);
} else {
try {
Object value = param.getSetterValue();
paramValues.add(value);
} catch (Exception e) {
// If getSetterValue() fails, add null
paramValues.add(null);
}
}
}

// Use SqlServerPreparedStatementExpander for robust parameter replacement
// This handles SQL parsing, comments, quotes, and NULL rewrites automatically
return SqlServerPreparedStatementExpander.expand(sqlSrc, paramValues);
}

// Existing RPC logic: replace ? with @p1, @p2 parameter names
final int MAX_PARAM_NAME_LEN = 6;
char[] sqlDst = new char[sqlSrc.length() + (params.length * (MAX_PARAM_NAME_LEN + OUT.length))
+ (params.length * 2)];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,8 @@ public String toString() {

enum PrepareMethod {
PREPEXEC("prepexec"), // sp_prepexec, default prepare method
PREPARE("prepare");
PREPARE("prepare"),
EXEC("exec");

private final String value;

Expand Down Expand Up @@ -840,7 +841,7 @@ public final class SQLServerDriver implements java.sql.Driver {
SQLServerDriverStringProperty.SERVER_CERTIFICATE.getDefaultValue(), false, null),
new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.PREPARE_METHOD.toString(),
SQLServerDriverStringProperty.PREPARE_METHOD.getDefaultValue(), false,
new String[] {PrepareMethod.PREPEXEC.toString(), PrepareMethod.PREPARE.toString()}),
new String[] {PrepareMethod.PREPEXEC.toString(), PrepareMethod.PREPARE.toString(), PrepareMethod.EXEC.toString()}),
new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.FAILOVER_PARTNER.toString(),
SQLServerDriverStringProperty.FAILOVER_PARTNER.getDefaultValue(), false, null),
new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.HOSTNAME_IN_CERTIFICATE.toString(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -678,7 +678,10 @@ final void doExecutePreparedStatement(PrepStmtExecCmd command) throws SQLServerE

// Start the request and detach the response reader so that we can
// continue using it after we return.
TDSWriter tdsWriter = command.startRequest(TDS.PKT_RPC);
// Use PKT_QUERY for exec mode (direct execution), PKT_RPC for prepared
// statements
boolean isPrepareMethodExec = connection.getPrepareMethod().equals(PrepareMethod.EXEC.toString());
TDSWriter tdsWriter = command.startRequest(isPrepareMethodExec ? TDS.PKT_QUERY : TDS.PKT_RPC);

needsPrepare = doPrepExec(tdsWriter, inOutParam, hasNewTypeDefinitions, hasExistingTypeDefinitions,
command);
Expand Down Expand Up @@ -1119,6 +1122,10 @@ private boolean reuseCachedHandle(boolean hasNewTypeDefinitions, boolean discard
if (isCursorable(executeMethod))
return false;

// No caching for exec method as it always executes directly without preparation
if (connection.getPrepareMethod().equals(PrepareMethod.EXEC.toString()))
return false;

// If current cache items needs to be discarded or New type definitions found with existing cached handle
// reference then deregister cached
// handle.
Expand Down Expand Up @@ -1165,6 +1172,31 @@ private boolean doPrepExec(TDSWriter tdsWriter, Parameter[] params, boolean hasN

boolean needsPrepare = (hasNewTypeDefinitions && hasExistingTypeDefinitions) || !hasPreparedStatementHandle();
boolean isPrepareMethodSpPrepExec = connection.getPrepareMethod().equals(PrepareMethod.PREPEXEC.toString());
boolean isPrepareMethodExec = connection.getPrepareMethod().equals(PrepareMethod.EXEC.toString());

// If using exec method, execute directly like regular Statement to mimic Sybase
// DYNAMIC_PREPARE=false
if (isPrepareMethodExec) {
// Build direct SQL using enhanced replaceParameterMarkers with direct values
String directSQL = connection.replaceParameterMarkers(userSQL, userSQLParamPositions, params, false, true);
// Note: TDS request already started as PKT_QUERY in caller

// For batch execution, prepend SET NOCOUNT OFF to ensure we get individual row
// counts
if (executeMethod == EXECUTE_BATCH) {
tdsWriter.writeString("SET NOCOUNT OFF; " + directSQL);
} else {
tdsWriter.writeString(directSQL);
}

expectPrepStmtHandle = false;
executedSqlDirectly = true;
expectCursorOutParams = false;
outParamIndexAdjustment = 0;
resetPrepStmtHandle(false);

return false; // No preparation needed
}

// Cursors don't use statement pooling.
if (isCursorable(executeMethod)) {
Expand Down Expand Up @@ -3062,12 +3094,41 @@ final void doExecutePreparedStatementBatch(PrepStmtBatchExecCmd batchCommand) th
hasNewTypeDefinitions = false;
}

boolean isPrepareMethodExec = connection.getPrepareMethod()
.equals(PrepareMethod.EXEC.toString());

if (numBatchesExecuted < numBatchesPrepared) {
// assert null != tdsWriter;
tdsWriter.writeByte((byte) NBATCH_STATEMENT_DELIMITER);
if (isPrepareMethodExec) {
// For EXEC method with batching, each statement must be sent separately
// to get individual update counts. Close current request and start a new one.
ensureExecuteResultsReader(batchCommand.startResponse(getIsResponseBufferingAdaptive()));

// Process results for previous statement
while (numBatchesExecuted < numBatchesPrepared - 1) {
startResults();
if (!getNextResult(true))
break;
if (null != resultSet) {
SQLServerException.makeFromDriverError(connection, this,
SQLServerException.getErrString("R_resultsetGeneratedForUpdate"), null,
false);
}
batchCommand.updateCounts[numBatchesExecuted++] = getUpdateCount();
}

// Start new request for next batch item
resetForReexecute();
tdsWriter = batchCommand.startRequest(TDS.PKT_QUERY);
} else {
// For RPC methods, use batch delimiter
tdsWriter.writeByte((byte) NBATCH_STATEMENT_DELIMITER);
}
} else {
resetForReexecute();
tdsWriter = batchCommand.startRequest(TDS.PKT_RPC);
// Use PKT_QUERY for exec mode (direct execution), PKT_RPC for prepared
// statements
tdsWriter = batchCommand.startRequest(isPrepareMethodExec ? TDS.PKT_QUERY : TDS.PKT_RPC);
}

// If we have to (re)prepare the statement then we must execute it so
Expand Down
Loading
Loading