Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Execute stored procedures directly for RPC calls #2410

Merged
merged 6 commits into from
May 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -216,7 +216,7 @@ && callRPCDirectly(inOutParam)) {
}

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

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

if (bReturnValueSyntax && (nOutParamsAssigned == 0) && !isCursorable(executeMethod) && !isTVPType
&& callRPCDirectly(inOutParam) && returnValueStatus != userDefinedFunctionReturnStatus) {
&& callRPCDirectly(inOutParam) && returnValueStatus != USER_DEFINED_FUNCTION_RETURN_STATUS) {
nOutParamsAssigned++;
}

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

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

if ((outParamIndex < 0 || outParamIndex >= inOutParam.length)
|| (!inOutParam[outParamIndex].isOutput())) {

// For RPC calls with out parameters, the initial return value token will indicate
// it being a RPC. In such case, consume the token as it does not contain the out parameter
// value. The subsequent token will have the value.
if (outParamHandler.srv.getStatus() == USER_DEFINED_FUNCTION_RETURN_STATUS) {
continue;
}

if (getStatementLogger().isLoggable(java.util.logging.Level.INFO)) {
getStatementLogger().info(toString() + " Unexpected outParamIndex: " + outParamIndex
+ "; adjustment: " + outParamIndexAdjustment);
}

connection.throwInvalidTDS();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,6 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
// flag whether is call escape syntax
private boolean isCallEscapeSyntax;

// flag whether is four part syntax
private boolean isFourPartSyntax;

/** Parameter positions in processed SQL statement text. */
final int[] userSQLParamPositions;

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

/**
* Regex for four part syntax
*/
private static final Pattern fourPartSyntaxPattern = Pattern.compile("(.+)\\.(.+)\\.(.+)\\.(.+)");

/** Returns the prepared statement SQL */
@Override
public String toString() {
Expand Down Expand Up @@ -290,7 +282,6 @@ private boolean resetPrepStmtHandle(boolean discardCurrentCacheItem) {
userSQL = parsedSQL.processedSQL;
isExecEscapeSyntax = isExecEscapeSyntax(sql);
isCallEscapeSyntax = isCallEscapeSyntax(sql);
isFourPartSyntax = isFourPartSyntax(sql);
userSQLParamPositions = parsedSQL.parameterPositions;
initParams(userSQLParamPositions.length);
useBulkCopyForBatchInsert = conn.getUseBulkCopyForBatchInsert();
Expand Down Expand Up @@ -1258,10 +1249,8 @@ boolean callRPCDirectly(Parameter[] params) throws SQLServerException {
// 4. Compliant CALL escape syntax
// If isExecEscapeSyntax is true, EXEC escape syntax is used then use prior behaviour of
// wrapping call to execute the procedure
// If isFourPartSyntax is true, sproc is being executed against linked server, then
// use prior behaviour of wrapping call to execute procedure
return (null != procedureName && paramCount != 0 && !isTVPType(params) && isCallEscapeSyntax
&& !isExecEscapeSyntax && !isFourPartSyntax);
&& !isExecEscapeSyntax);
}

/**
Expand Down Expand Up @@ -1289,10 +1278,6 @@ private boolean isCallEscapeSyntax(String sql) {
return callEscapePattern.matcher(sql).find();
}

private boolean isFourPartSyntax(String sql) {
return fourPartSyntaxPattern.matcher(sql).find();
}

/**
* Executes sp_prepare to prepare a parameterized statement and sets the prepared statement handle
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public class SQLServerStatement implements ISQLServerStatement {
/** Check if statement contains TVP Type */
boolean isTVPType = false;

static int userDefinedFunctionReturnStatus = 2;
protected static final int USER_DEFINED_FUNCTION_RETURN_STATUS = 2;

final boolean getIsResponseBufferingAdaptive() {
return isResponseBufferingAdaptive;
Expand Down Expand Up @@ -1676,7 +1676,7 @@ boolean onRetValue(TDSReader tdsReader) throws SQLServerException {
// 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 && status != userDefinedFunctionReturnStatus) {
if (moreResults && null == procedureRetStatToken && status != USER_DEFINED_FUNCTION_RETURN_STATUS) {
Parameter p = new Parameter(
Util.shouldHonorAEForParameters(stmtColumnEncriptionSetting, connection));
p.skipRetValStatus(tdsReader);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,14 @@ final class StreamRetValue extends StreamPacket {
*/
private int ordinalOrLength;

final int getOrdinalOrLength() {
int getOrdinalOrLength() {
return ordinalOrLength;
}

int getStatus() {
return status;
}

/*
* Status: 0x01 if the return value is an OUTPUT parameter of a stored procedure 0x02 if the return value is from a
* User Defined Function
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1187,6 +1187,8 @@ public void testFourPartSyntaxCallEscapeSyntax() throws SQLException {
Statement stmt = linkedServerConnection.createStatement()) {
stmt.execute(
"create or alter procedure dbo.TestAdd(@Num1 int, @Num2 int, @Result int output) as begin set @Result = @Num1 + @Num2; end;");

stmt.execute("create or alter procedure dbo.TestReturn(@Num1 int) as select @Num1 return @Num1*3 ");
}

try (CallableStatement cstmt = connection
Expand All @@ -1211,6 +1213,15 @@ public void testFourPartSyntaxCallEscapeSyntax() throws SQLException {
cstmt.execute();
assertEquals(sum, cstmt.getInt(3));
}

try (CallableStatement cstmt = connection
.prepareCall("{? = call [" + linkedServer + "].master.dbo.TestReturn(?)}")) {
int expected = 15;
tkyc marked this conversation as resolved.
Show resolved Hide resolved
cstmt.registerOutParameter(1, java.sql.Types.INTEGER);
cstmt.setInt(2, 5);
cstmt.execute();
assertEquals(expected, cstmt.getInt(1));
}
}

/**
Expand Down
Loading