Skip to content

Commit df5bfa6

Browse files
authored
Execute stored procedures directly for RPC calls (#2410)
* RPC fix * Assertion index correction * Removed magic number * Removed login drop command * Code review changes * Formatting
1 parent a35798e commit df5bfa6

5 files changed

Lines changed: 31 additions & 23 deletions

File tree

src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ && callRPCDirectly(inOutParam)) {
216216
}
217217

218218
if (inOutParam[i - 1].isReturnValue() && bReturnValueSyntax && !isCursorable(executeMethod) && !isTVPType
219-
&& returnValueStatus != userDefinedFunctionReturnStatus) {
219+
&& returnValueStatus != USER_DEFINED_FUNCTION_RETURN_STATUS) {
220220
return inOutParam[i - 1];
221221
}
222222

@@ -269,7 +269,6 @@ final void processOutParameters() throws SQLServerException {
269269
// the response stream.
270270
for (int index = 0; index < inOutParam.length; ++index) {
271271
if (index != outParamIndex && inOutParam[index].isValueGotten()) {
272-
assert inOutParam[index].isOutput();
273272
inOutParam[index].resetOutputValue();
274273
}
275274
}
@@ -365,7 +364,7 @@ boolean onRetValue(TDSReader tdsReader) throws SQLServerException {
365364
OutParamHandler outParamHandler = new OutParamHandler();
366365

367366
if (bReturnValueSyntax && (nOutParamsAssigned == 0) && !isCursorable(executeMethod) && !isTVPType
368-
&& callRPCDirectly(inOutParam) && returnValueStatus != userDefinedFunctionReturnStatus) {
367+
&& callRPCDirectly(inOutParam) && returnValueStatus != USER_DEFINED_FUNCTION_RETURN_STATUS) {
369368
nOutParamsAssigned++;
370369
}
371370

@@ -414,7 +413,7 @@ && callRPCDirectly(inOutParam) && returnValueStatus != userDefinedFunctionReturn
414413
outParamIndex = outParamHandler.srv.getOrdinalOrLength();
415414

416415
if (bReturnValueSyntax && !isCursorable(executeMethod) && !isTVPType && callRPCDirectly(inOutParam)
417-
&& returnValueStatus != userDefinedFunctionReturnStatus) {
416+
&& returnValueStatus != USER_DEFINED_FUNCTION_RETURN_STATUS) {
418417
outParamIndex++;
419418
} else {
420419
// Statements need to have their out param indices adjusted by the number
@@ -424,10 +423,19 @@ && callRPCDirectly(inOutParam) && returnValueStatus != userDefinedFunctionReturn
424423

425424
if ((outParamIndex < 0 || outParamIndex >= inOutParam.length)
426425
|| (!inOutParam[outParamIndex].isOutput())) {
426+
427+
// For RPC calls with out parameters, the initial return value token will indicate
428+
// it being a RPC. In such case, consume the token as it does not contain the out parameter
429+
// value. The subsequent token will have the value.
430+
if (outParamHandler.srv.getStatus() == USER_DEFINED_FUNCTION_RETURN_STATUS) {
431+
continue;
432+
}
433+
427434
if (getStatementLogger().isLoggable(java.util.logging.Level.INFO)) {
428435
getStatementLogger().info(toString() + " Unexpected outParamIndex: " + outParamIndex
429436
+ "; adjustment: " + outParamIndexAdjustment);
430437
}
438+
431439
connection.throwInvalidTDS();
432440
}
433441

src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -77,9 +77,6 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
7777
// flag whether is call escape syntax
7878
private boolean isCallEscapeSyntax;
7979

80-
// flag whether is four part syntax
81-
private boolean isFourPartSyntax;
82-
8380
/** Parameter positions in processed SQL statement text. */
8481
final int[] userSQLParamPositions;
8582

@@ -149,11 +146,6 @@ private void setPreparedStatementHandle(int handle) {
149146
*/
150147
private static final Pattern execEscapePattern = Pattern.compile("^\\s*(?i)(?:exec|execute)\\b");
151148

152-
/**
153-
* Regex for four part syntax
154-
*/
155-
private static final Pattern fourPartSyntaxPattern = Pattern.compile("(.+)\\.(.+)\\.(.+)\\.(.+)");
156-
157149
/** Returns the prepared statement SQL */
158150
@Override
159151
public String toString() {
@@ -290,7 +282,6 @@ private boolean resetPrepStmtHandle(boolean discardCurrentCacheItem) {
290282
userSQL = parsedSQL.processedSQL;
291283
isExecEscapeSyntax = isExecEscapeSyntax(sql);
292284
isCallEscapeSyntax = isCallEscapeSyntax(sql);
293-
isFourPartSyntax = isFourPartSyntax(sql);
294285
userSQLParamPositions = parsedSQL.parameterPositions;
295286
initParams(userSQLParamPositions.length);
296287
useBulkCopyForBatchInsert = conn.getUseBulkCopyForBatchInsert();
@@ -1258,10 +1249,8 @@ boolean callRPCDirectly(Parameter[] params) throws SQLServerException {
12581249
// 4. Compliant CALL escape syntax
12591250
// If isExecEscapeSyntax is true, EXEC escape syntax is used then use prior behaviour of
12601251
// wrapping call to execute the procedure
1261-
// If isFourPartSyntax is true, sproc is being executed against linked server, then
1262-
// use prior behaviour of wrapping call to execute procedure
12631252
return (null != procedureName && paramCount != 0 && !isTVPType(params) && isCallEscapeSyntax
1264-
&& !isExecEscapeSyntax && !isFourPartSyntax);
1253+
&& !isExecEscapeSyntax);
12651254
}
12661255

12671256
/**
@@ -1289,10 +1278,6 @@ private boolean isCallEscapeSyntax(String sql) {
12891278
return callEscapePattern.matcher(sql).find();
12901279
}
12911280

1292-
private boolean isFourPartSyntax(String sql) {
1293-
return fourPartSyntaxPattern.matcher(sql).find();
1294-
}
1295-
12961281
/**
12971282
* Executes sp_prepare to prepare a parameterized statement and sets the prepared statement handle
12981283
*

src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public class SQLServerStatement implements ISQLServerStatement {
7272
/** Check if statement contains TVP Type */
7373
boolean isTVPType = false;
7474

75-
static int userDefinedFunctionReturnStatus = 2;
75+
protected static final int USER_DEFINED_FUNCTION_RETURN_STATUS = 2;
7676

7777
final boolean getIsResponseBufferingAdaptive() {
7878
return isResponseBufferingAdaptive;
@@ -1676,7 +1676,7 @@ boolean onRetValue(TDSReader tdsReader) throws SQLServerException {
16761676
// in which case we need to stop parsing and let CallableStatement take over.
16771677
// A RETVALUE token appearing in the execution results, but before any RETSTATUS
16781678
// token, is a TEXTPTR return value that should be ignored.
1679-
if (moreResults && null == procedureRetStatToken && status != userDefinedFunctionReturnStatus) {
1679+
if (moreResults && null == procedureRetStatToken && status != USER_DEFINED_FUNCTION_RETURN_STATUS) {
16801680
Parameter p = new Parameter(
16811681
Util.shouldHonorAEForParameters(stmtColumnEncriptionSetting, connection));
16821682
p.skipRetValStatus(tdsReader);

src/main/java/com/microsoft/sqlserver/jdbc/StreamRetValue.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,14 @@ final class StreamRetValue extends StreamPacket {
1818
*/
1919
private int ordinalOrLength;
2020

21-
final int getOrdinalOrLength() {
21+
int getOrdinalOrLength() {
2222
return ordinalOrLength;
2323
}
2424

25+
int getStatus() {
26+
return status;
27+
}
28+
2529
/*
2630
* Status: 0x01 if the return value is an OUTPUT parameter of a stored procedure 0x02 if the return value is from a
2731
* User Defined Function

src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableStatementTest.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1187,6 +1187,8 @@ public void testFourPartSyntaxCallEscapeSyntax() throws SQLException {
11871187
Statement stmt = linkedServerConnection.createStatement()) {
11881188
stmt.execute(
11891189
"create or alter procedure dbo.TestAdd(@Num1 int, @Num2 int, @Result int output) as begin set @Result = @Num1 + @Num2; end;");
1190+
1191+
stmt.execute("create or alter procedure dbo.TestReturn(@Num1 int) as select @Num1 return @Num1*3 ");
11901192
}
11911193

11921194
try (CallableStatement cstmt = connection
@@ -1211,6 +1213,15 @@ public void testFourPartSyntaxCallEscapeSyntax() throws SQLException {
12111213
cstmt.execute();
12121214
assertEquals(sum, cstmt.getInt(3));
12131215
}
1216+
1217+
try (CallableStatement cstmt = connection
1218+
.prepareCall("{? = call [" + linkedServer + "].master.dbo.TestReturn(?)}")) {
1219+
int expected = 15;
1220+
cstmt.registerOutParameter(1, java.sql.Types.INTEGER);
1221+
cstmt.setInt(2, 5);
1222+
cstmt.execute();
1223+
assertEquals(expected, cstmt.getInt(1));
1224+
}
12141225
}
12151226

12161227
/**

0 commit comments

Comments
 (0)