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

Re-added support for stored procedure 'exec' escape syntax in CallableStatements #2325

Merged
merged 12 commits into from
Feb 15, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ public class SQLServerPreparedStatement extends SQLServerStatement implements IS
/** Processed SQL statement text, may not be same as what user initially passed. */
final String userSQL;

/** Unprocessed SQL statement text, should tbe same as what user initially passed. */
final String userSQLUnprocessed;

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

Expand Down Expand Up @@ -253,6 +256,7 @@ private boolean resetPrepStmtHandle(boolean discardCurrentCacheItem) {
procedureName = parsedSQL.procedureName;
bReturnValueSyntax = parsedSQL.bReturnValueSyntax;
userSQL = parsedSQL.processedSQL;
userSQLUnprocessed = sql;
lilgreenbird marked this conversation as resolved.
Show resolved Hide resolved
userSQLParamPositions = parsedSQL.parameterPositions;
initParams(userSQLParamPositions.length);
useBulkCopyForBatchInsert = conn.getUseBulkCopyForBatchInsert();
Expand Down Expand Up @@ -1210,7 +1214,7 @@ else if (needsPrepare && !connection.getEnablePrepareOnFirstPreparedStatementCal
*/
boolean callRPCDirectly(Parameter[] params) throws SQLServerException {
int paramCount = SQLServerConnection.countParams(userSQL);
return (null != procedureName && paramCount != 0 && !isTVPType(params));
return (null != procedureName && paramCount != 0 && !isTVPType(params) && !isExecCommand());
tkyc marked this conversation as resolved.
Show resolved Hide resolved
}

/**
Expand All @@ -1230,6 +1234,11 @@ private boolean isTVPType(Parameter[] params) throws SQLServerException {
return false;
}

private boolean isExecCommand() {
String command = userSQLUnprocessed.split(" ")[0].toLowerCase();
return command.equals("exec") || command.equals("execute");
tkyc marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* 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 @@ -81,7 +81,7 @@ public class CallableStatementTest extends AbstractTest {

/**
* Setup before test
*
*
* @throws SQLException
*/
@BeforeAll
Expand Down Expand Up @@ -201,7 +201,7 @@ public void testCallableStatementSpPrepare() throws SQLException {

/**
* Tests CallableStatement.getString() with uniqueidentifier parameter
*
*
* @throws SQLException
*/
@Test
Expand All @@ -226,7 +226,7 @@ public void getStringGUIDTest() throws SQLException {

/**
* test for setNull(index, varchar) to behave as setNull(index, nvarchar) when SendStringParametersAsUnicode is true
*
*
* @throws SQLException
*/
@Test
Expand Down Expand Up @@ -302,7 +302,7 @@ public void testGetObjectAsLocalDateTime() throws SQLException {

/**
* Tests getObject(n, java.time.OffsetDateTime.class) and getObject(n, java.time.OffsetTime.class).
*
*
* @throws SQLException
*/
@Test
Expand Down Expand Up @@ -332,7 +332,7 @@ public void testGetObjectAsOffsetDateTime() throws SQLException {

/**
* recognize parameter names with and without leading '@'
*
*
* @throws SQLException
*/
@Test
Expand Down Expand Up @@ -1067,9 +1067,83 @@ public void testRegisteringOutputByIndexandAcquiringOutputParamByName() throws S
}
}

@Test
public void testExecuteSystemStoredProcedureNamedParametersAndIndexedParameterNoResultset() throws SQLException {
String call = "EXEC sp_getapplock @Resource=?, @LockTimeout='60', @LockMode='Exclusive', @LockOwner='Session'";
tkyc marked this conversation as resolved.
Show resolved Hide resolved

try (CallableStatement cstmt = connection.prepareCall(call)) {
cstmt.setString(1, "resource");
cstmt.execute();
}
}

@Test
public void testExecSystemStoredProcedureNamedParametersAndIndexedParameterResultSet() throws SQLException {
String call = "exec sp_sproc_columns_100 ?, @ODBCVer=3, @fUsePattern=0";

try (CallableStatement cstmt = connection.prepareCall(call)) {
cstmt.setString(1, "sp_getapplock");

try (ResultSet rs = cstmt.executeQuery()) {
while (rs.next()) {
assertTrue(TestResource.getResource("R_resultSetEmpty"), !rs.getString(4).isEmpty());
}
}
}
}

@Test
public void testExecSystemStoredProcedureNoIndexedParametersResultSet() throws SQLException {
String call = "execute sp_sproc_columns_100 sp_getapplock, @ODBCVer=3, @fUsePattern=0";

try (CallableStatement cstmt = connection.prepareCall(call)) {

try (ResultSet rs = cstmt.executeQuery()) {
tkyc marked this conversation as resolved.
Show resolved Hide resolved
while (rs.next()) {
assertTrue(TestResource.getResource("R_resultSetEmpty"), !rs.getString(4).isEmpty());
}
}
}
}

@Test
public void testExecDocumentedSystemStoredProceduresIndexedParameters() throws SQLException {
String serverName;
String testTableName = "testTable";
Integer integer = new Integer(1);

try (Statement stmt = connection.createStatement(); ResultSet rs = stmt.executeQuery("SELECT @@SERVERNAME")) {
rs.next();
serverName = rs.getString(1);
}

String[] sprocs = {"EXEC sp_column_privileges ?", "EXECUTE sp_serveroption @@SERVERNAME, ?, TRUE",
"exec sp_catalogs ?", "execute sp_column_privileges ?", "EXEC sp_column_privileges_ex ?",
"EXECUTE sp_columns ?", "exec sp_columns_ex ?", "execute sp_datatype_info ?",
"EXEC sp_sproc_columns ?", "EXECUTE sp_server_info ?", "exec sp_special_columns ?",
"execute sp_statistics ?", "EXEC sp_table_privileges ?", "EXECUTE sp_table_privileges_ex ?",
"exec sp_tables ?", "execute sp_tables_ex ?"};

Object[] params = {testTableName, "DATA ACCESS", serverName, testTableName, serverName,
testTableName, serverName, integer, "sp_column_privileges", integer, testTableName,
testTableName, testTableName, serverName, testTableName, serverName};

int paramIndex = 0;

for (String sproc : sprocs) {
try (CallableStatement cstmt = connection.prepareCall(sproc)) {
cstmt.setObject(1, params[paramIndex]);
cstmt.execute();
paramIndex++;
} catch (Exception e) {
fail("Failed executing '" + sproc + "' with indexed parameter '" + params[paramIndex]);
tkyc marked this conversation as resolved.
Show resolved Hide resolved
}
}
}

/**
* Cleanup after test
*
*
* @throws SQLException
*/
@AfterAll
Expand Down
Loading