diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java index 8537f1d3f..9f0320eb7 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java @@ -258,7 +258,10 @@ boolean onDone(TDSReader tdsReader) throws SQLServerException { // Consume the done token and decide what to do with it... StreamDone doneToken = new StreamDone(); doneToken.setFromTDS(tdsReader); - connection.getSessionRecovery().decrementUnprocessedResponseCount(); + + if (doneToken.isFinal()) { + connection.getSessionRecovery().decrementUnprocessedResponseCount(); + } // If this is a non-final batch-terminating DONE token, // then stop parsing the response now and set up for diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index b49fea889..9331c583e 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -3996,6 +3996,10 @@ boolean executeCommand(TDSCommand newCommand) throws SQLServerException { false); } try { + if (null != preparedStatementHandleCache) { + preparedStatementHandleCache.clear(); + } + sessionRecovery.reconnect(newCommand); } catch (InterruptedException e) { // re-interrupt thread diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java index 3531d6076..f33e819f8 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java @@ -390,16 +390,21 @@ boolean onDone(TDSReader tdsReader) throws SQLServerException { // following the column metadata indicates an empty result set. rowCount = 0; + // decrementUnprocessedResponseCount() outside the "if" is not necessary here. It will over decrement if added. + short status = tdsReader.peekStatusFlag(); if ((status & TDS.DONE_ERROR) != 0 || (status & TDS.DONE_SRVERROR) != 0) { + StreamDone doneToken = new StreamDone(); + doneToken.setFromTDS(tdsReader); + if (doneToken.isFinal()) { + stmt.connection.getSessionRecovery().decrementUnprocessedResponseCount(); + } SQLServerError databaseError = this.getDatabaseError(); MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_serverError")); Object[] msgArgs = {status, (databaseError != null) ? databaseError.getErrorMessage() : ""}; SQLServerException.makeFromDriverError(stmt.connection, stmt, form.format(msgArgs), null, false); } - stmt.connection.getSessionRecovery().decrementUnprocessedResponseCount(); - return false; } } @@ -5389,6 +5394,11 @@ boolean onDone(TDSReader tdsReader) throws SQLServerException { StreamDone doneToken = new StreamDone(); doneToken.setFromTDS(tdsReader); + + if (doneToken.isFinal()) { + stmt.connection.getSessionRecovery().decrementUnprocessedResponseCount(); + } + if (doneToken.isFinal() && doneToken.isError()) { short status = tdsReader.peekStatusFlag(); SQLServerError databaseError = getDatabaseError(); @@ -5397,7 +5407,6 @@ boolean onDone(TDSReader tdsReader) throws SQLServerException { SQLServerException.makeFromDriverError(stmt.connection, stmt, form.format(msgArgs), null, false); } - stmt.connection.getSessionRecovery().decrementUnprocessedResponseCount(); // Done with all the rows in this fetch buffer and done with parsing // unless it's a server cursor, in which case there is a RETSTAT and diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java index d2dfab22b..0bdb14018 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java @@ -1494,7 +1494,10 @@ boolean onDone(TDSReader tdsReader) throws SQLServerException { // Handling DONE/DONEPROC/DONEINPROC tokens is a little tricky... StreamDone doneToken = new StreamDone(); doneToken.setFromTDS(tdsReader); - connection.getSessionRecovery().decrementUnprocessedResponseCount(); + + if (doneToken.isFinal()) { + connection.getSessionRecovery().decrementUnprocessedResponseCount(); + } // If the done token has the attention ack bit set, then record // it as the attention ack DONE token. We may or may not throw diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/resiliency/BasicConnectionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/resiliency/BasicConnectionTest.java index ce2f285fc..8b508e5b9 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/resiliency/BasicConnectionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/resiliency/BasicConnectionTest.java @@ -14,18 +14,21 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import java.util.UUID; import javax.sql.PooledConnection; +import com.microsoft.sqlserver.jdbc.RandomUtil; +import com.microsoft.sqlserver.jdbc.SQLServerConnection; +import com.microsoft.sqlserver.jdbc.SQLServerConnectionPoolDataSource; import com.microsoft.sqlserver.jdbc.SQLServerPooledConnection; +import com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement; +import com.microsoft.sqlserver.jdbc.TestResource; +import com.microsoft.sqlserver.jdbc.TestUtils; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; -import com.microsoft.sqlserver.jdbc.RandomUtil; -import com.microsoft.sqlserver.jdbc.SQLServerConnectionPoolDataSource; -import com.microsoft.sqlserver.jdbc.TestResource; -import com.microsoft.sqlserver.jdbc.TestUtils; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.Constants; @@ -301,6 +304,68 @@ public void testDSPooledConnectionAccessTokenCallbackIdleConnectionResiliency() } } + @Test + public void testPreparedStatementCacheShouldBeCleared() throws SQLException { + try (SQLServerConnection con = (SQLServerConnection) ResiliencyUtils.getConnection(connectionString)) { + int cacheSize = 2; + String query = String.format("/*testPreparedStatementCacheShouldBeCleared_%s*/SELECT 1; -- ", + UUID.randomUUID().toString()); + int discardedStatementCount = 1; + + // enable caching + con.setDisableStatementPooling(false); + con.setStatementPoolingCacheSize(cacheSize); + con.setServerPreparedStatementDiscardThreshold(discardedStatementCount); + + // add new statements to fill cache + for (int i = 0; i < cacheSize; ++i) { + try (SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) con + .prepareStatement(query + i)) { + pstmt.execute(); + pstmt.execute(); + } + } + + // nothing should be discarded yet + assertEquals(0, con.getDiscardedServerPreparedStatementCount()); + + ResiliencyUtils.killConnection(con, connectionString, 1); + + // add 1 more - if cache was not cleared this would cause it to be discarded + try (SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) con.prepareStatement(query)) { + pstmt.execute(); + pstmt.execute(); + } + assertEquals(0, con.getDiscardedServerPreparedStatementCount()); + } + } + + @Test + public void testUnprocessedResponseCountSuccessfulIdleConnectionRecovery() throws SQLException { + try (SQLServerConnection con = (SQLServerConnection) ResiliencyUtils.getConnection(connectionString)) { + int queriesToSend = 5; + String query = String.format("/*testUnprocessedResponseCountSuccessfulIdleConnectionRecovery_%s*/SELECT 1; -- ", + UUID.randomUUID()); + + for (int i = 0; i < queriesToSend; ++i) { + try (SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) con + .prepareStatement(query + i)) { + pstmt.executeQuery(); + pstmt.executeQuery(); + } + } + + // Kill the connection. If the unprocessedResponseCount is negative, test will fail. + ResiliencyUtils.killConnection(con, connectionString, 1); + + // Should successfully recover. + try (SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) con.prepareStatement(query)) { + pstmt.executeQuery(); + pstmt.executeQuery(); + } + } + } + private void basicReconnect(String connectionString) throws SQLException { // Ensure reconnects can happen multiple times over the same connection and subsequent connections for (int i1 = 0; i1 < 2; i1++) { diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/resiliency/ResultSetsWithResiliencyTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/resiliency/ResultSetsWithResiliencyTest.java index 42e8cfc51..2a381976d 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/resiliency/ResultSetsWithResiliencyTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/resiliency/ResultSetsWithResiliencyTest.java @@ -9,13 +9,16 @@ import static org.junit.Assert.fail; import static org.junit.jupiter.api.Assertions.assertTrue; +import java.sql.CallableStatement; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import java.sql.Types; +import com.microsoft.sqlserver.jdbc.SQLServerConnection; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Tag; @@ -36,14 +39,47 @@ public class ResultSetsWithResiliencyTest extends AbstractTest { static String tableName = AbstractSQLGenerator.escapeIdentifier("resilencyTestTable"); static int numberOfRows = 10; + private static String callableStatementICROnDoneTestSp = AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("CallableStatement_ICROnDoneTest_SP")); + private static String callableStatementICROnDoneErrorTestSp = AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("CallableStatement_ICROnDoneErrorTest_SP")); + private static String createClientCursorInitTableQuery = "create table %s (col1 int, col2 varchar(8000), col3 int identity(1,1))"; + private static String createFetchBufferTableQuery = "create table %s (col1 int not null)"; + private static String insertIntoFetchBufferTableQuery = "insert into %s (col1) values (%s);"; + private static final String clientCursorInitTable1 = AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("clientCursorInitTable1")); + private static final String clientCursorInitTable2 = AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("clientCursorInitTable2")); + private static final String clientCursorInitTable3 = AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("clientCursorInitTable3")); + private static final String fetchBufferTestTable1 = AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("fetchBufferTestTable1")); + private static final String fetchBufferTestTable2 = AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("fetchBufferTestTable2")); + private static final String clientCursorInitTableQuery1 = "select * from " + clientCursorInitTable1; + private static final String clientCursorInitTableQuery2 = "select * from " + clientCursorInitTable2; + private static final String clientCursorInitTableQuery3 = "select * from " + clientCursorInitTable3; + private static final String fetchBufferTableQuery1 = "select * from " + fetchBufferTestTable1; + private static final String fetchBufferTableQuery2 = "select * from " + fetchBufferTestTable2; + private static final String mockErrorMsg = "This is a mock query error."; + private static final String errorQuery = "RAISERROR('" + mockErrorMsg + "', 16, 1)"; + @BeforeAll public static void setupTests() throws Exception { setConnection(); try (Connection c = DriverManager.getConnection(connectionString); Statement s = c.createStatement();) { TestUtils.dropTableIfExists(tableName, s); + TestUtils.dropTableIfExists(clientCursorInitTable1, s); + TestUtils.dropTableIfExists(clientCursorInitTable2, s); + TestUtils.dropTableIfExists(clientCursorInitTable3, s); + TestUtils.dropTableIfExists(fetchBufferTestTable1, s); + TestUtils.dropTableIfExists(fetchBufferTestTable2, s); + TestUtils.dropProcedureIfExists(callableStatementICROnDoneTestSp, s); + TestUtils.dropProcedureIfExists(callableStatementICROnDoneErrorTestSp, s); + createTable(s); insertData(s); + + createCallableStatementOnDoneTestSp(s); + createCallableStatementOnDoneErrorTestSp(s); + + createTable(s, String.format(createClientCursorInitTableQuery, clientCursorInitTable1)); + createTable(s, String.format(createClientCursorInitTableQuery, clientCursorInitTable2)); + createTable(s, String.format(createClientCursorInitTableQuery, clientCursorInitTable3)); } } @@ -210,16 +246,289 @@ public void testKillSession() throws Exception { } } + @Test + public void testResultSetClientCursorInitializerOnDone() throws SQLException { + try (Connection con = ResiliencyUtils.getConnection(connectionString); Statement stmt = con.createStatement()) { + + boolean hasResults = stmt.execute(clientCursorInitTableQuery1+ "; " + clientCursorInitTableQuery2); + while(hasResults) { + ResultSet rs = stmt.getResultSet(); + while (rs.next()) {} + hasResults = stmt.getMoreResults(); + } + + ResiliencyUtils.killConnection(con, connectionString, 1); + + try (ResultSet rs = con.createStatement() + .executeQuery(clientCursorInitTableQuery3)) { + while(rs.next()) {} + } + } + } + + @Test + public void testResultSetErrorClientCursorInitializerOnDone() throws SQLException { + try (Connection con = ResiliencyUtils.getConnection(connectionString); Statement stmt = con.createStatement()) { + + try { + boolean hasResults = stmt.execute(clientCursorInitTableQuery1 + "; " + errorQuery); + while(hasResults) { + ResultSet rs = stmt.getResultSet(); + while (rs.next()) {} + hasResults = stmt.getMoreResults(); + } + } catch (SQLServerException se) { + if (!se.getMessage().equals(mockErrorMsg)) { + se.printStackTrace(); + fail("Mock Sql Server error message was expected."); + } + } + + ResiliencyUtils.killConnection(con, connectionString, 1); + + try (ResultSet rs = con.createStatement() + .executeQuery(clientCursorInitTableQuery3)) { + while (rs.next()) {} + } + } + } + + @Test + public void testCallableStatementOnDone() throws SQLException { + String sql = "{CALL " + callableStatementICROnDoneTestSp + " (?, ?)}"; + + try (Connection con = ResiliencyUtils.getConnection(connectionString)) { + + try (CallableStatement cs = con.prepareCall(sql)) { + cs.registerOutParameter(1, Types.TIMESTAMP); + cs.registerOutParameter(2, Types.TIMESTAMP); + cs.execute(); + cs.execute(); + cs.execute(); + } + + ResiliencyUtils.killConnection(con, connectionString, 1); + + try (CallableStatement cs2 = con.prepareCall(sql)) { + cs2.registerOutParameter(1, Types.TIMESTAMP); + cs2.registerOutParameter(2, Types.TIMESTAMP); + cs2.execute(); + } + } + } + + @Test + public void testCallableStatementErrorOnDone() throws SQLException { + String errorCallableStmt = "{CALL " + + callableStatementICROnDoneErrorTestSp + " (?, ?)}"; + String validCallableStmt = "{CALL " + + callableStatementICROnDoneTestSp + " (?, ?)}"; + + try (Connection con = ResiliencyUtils.getConnection(connectionString)) { + + try (CallableStatement cs = con.prepareCall(errorCallableStmt)) { + cs.registerOutParameter(1, Types.TIMESTAMP); + cs.registerOutParameter(2, Types.TIMESTAMP); + cs.execute(); + } catch (SQLServerException se) { + if (!se.getMessage().equals(mockErrorMsg)) { + se.printStackTrace(); + fail("Mock Sql Server error message was expected."); + } + } + + ResiliencyUtils.killConnection(con, connectionString, 1); + + try (CallableStatement cs2 = con.prepareCall(validCallableStmt)) { + cs2.registerOutParameter(1, Types.TIMESTAMP); + cs2.registerOutParameter(2, Types.TIMESTAMP); + cs2.execute(); + } + } + } + + @Test + public void testResultSetFetchBufferOnDone() throws SQLException { + + try (SQLServerConnection con = (SQLServerConnection) ResiliencyUtils.getConnection(connectionString)) { + try (Statement stmt = con.createStatement()) { + TestUtils.dropTableIfExists(fetchBufferTestTable1, stmt); + createTable(stmt, String.format(createFetchBufferTableQuery, fetchBufferTestTable1)); + insertData(stmt, String.format(insertIntoFetchBufferTableQuery, fetchBufferTestTable1, 1), 10); + } + + ResiliencyUtils.killConnection(con, connectionString, 1); + + try (Statement stmt = con.createStatement()) { + TestUtils.dropTableIfExists(fetchBufferTestTable2, stmt); + createTable(stmt, String.format(createFetchBufferTableQuery, fetchBufferTestTable2)); + insertData(stmt, String.format(insertIntoFetchBufferTableQuery, fetchBufferTestTable2, 1), 10); + } + + try (Statement stmt = con.createStatement()) { + boolean hasResults = stmt.execute(fetchBufferTableQuery1 + "; " + fetchBufferTableQuery2); + while(hasResults) { + ResultSet rs = stmt.getResultSet(); + while (rs.next()) {} + hasResults = stmt.getMoreResults(); + } + } + + ResiliencyUtils.killConnection(con, connectionString, 1); + + try (Statement stmt = con.createStatement()) { + ResultSet rs = stmt.executeQuery(fetchBufferTableQuery2); + while (rs.next()) {} + } + } + } + + @Test + public void testResultSetErrorFetchBufferOnDone() throws SQLException { + try (SQLServerConnection con = (SQLServerConnection) ResiliencyUtils.getConnection(connectionString)) { + try (Statement stmt = con.createStatement()) { + TestUtils.dropTableIfExists(fetchBufferTestTable1, stmt); + createTable(stmt, String.format(createFetchBufferTableQuery, fetchBufferTestTable1)); + insertData(stmt, errorQuery, 10); + } catch (SQLServerException se) { + if (!se.getMessage().equals(mockErrorMsg)) { + se.printStackTrace(); + fail("Mock Sql Server error message was expected."); + } + } + + ResiliencyUtils.killConnection(con, connectionString, 1); + + try (Statement stmt = con.createStatement()) { + TestUtils.dropTableIfExists(fetchBufferTestTable2, stmt); + createTable(stmt, String.format(createFetchBufferTableQuery, fetchBufferTestTable2)); + insertData(stmt, String.format(insertIntoFetchBufferTableQuery, fetchBufferTestTable2, 1), 10); + } + + try (Statement stmt = con.createStatement()) { + boolean hasResults = stmt.execute(fetchBufferTableQuery1 + "; " + errorQuery); + while(hasResults) { + ResultSet rs = stmt.getResultSet(); + while (rs.next()) {} + hasResults = stmt.getMoreResults(); + } + } catch (SQLServerException se) { + if (!se.getMessage().equals(mockErrorMsg)) { + se.printStackTrace(); + fail("Mock Sql Server error message was expected."); + } + } + + ResiliencyUtils.killConnection(con, connectionString, 1); + + try (Statement stmt = con.createStatement()) { + ResultSet rs = stmt.executeQuery(fetchBufferTableQuery2); + while (rs.next()) {} + } + } + } + + /* + * Test killing a session while retrieving multiple result sets + */ + @Test + public void testMultipleResultSets() throws Exception { + try (Connection c = ResiliencyUtils.getConnection(connectionString); Statement s = c.createStatement()) { + boolean results = s.execute("SELECT 1;SELECT 2"); + int rsCount = 0; + do { + if (results) { + try (ResultSet rs = s.getResultSet()) { + rsCount++; + + while (rs.next()) { + ResiliencyUtils.killConnection(c, connectionString, 0); + assertTrue(rs.getString(1).equals(String.valueOf(rsCount))); + } + } + } + results = s.getMoreResults(); + } while (results); + } catch (SQLException e) { + if (!("08S01" == e.getSQLState() + || e.getMessage().matches(TestUtils.formatErrorMsg("R_crClientUnrecoverable")))) { + e.printStackTrace(); + } + assertTrue( + "08S01" == e.getSQLState() + || e.getMessage().matches(TestUtils.formatErrorMsg("R_crClientUnrecoverable")), + e.getMessage()); + } + } + + /* + * Test killing a session while retrieving result set that causes an exception + */ + @Test + public void testResultSetWithException() throws Exception { + try (Connection c = ResiliencyUtils.getConnection(connectionString); Statement s = c.createStatement(); + ResultSet rs = s.executeQuery("SELECT 1/0")) { + + while (rs.next()) { + ResiliencyUtils.killConnection(c, connectionString, 0); + // driver should not have successfully reconnected but it did + fail(TestResource.getResource("R_expectedFailPassed")); + } + } catch (SQLException e) { + if (!e.getMessage().contains("Divide by zero error")) { + e.printStackTrace(); + } + } + } + + /* + * Test killing a session while retrieving multiple result sets that causes an exception + */ + @Test + public void testMultipleResultSetsWithException() throws Exception { + try (Connection c = ResiliencyUtils.getConnection(connectionString); Statement s = c.createStatement()) { + boolean results = s.execute("SELECT 1;SELECT 1/0"); + int rsCount = 0; + do { + if (results) { + try (ResultSet rs = s.getResultSet()) { + rsCount++; + + while (rs.next()) { + ResiliencyUtils.killConnection(c, connectionString, 0); + assertTrue(rs.getString(1).equals(String.valueOf(rsCount))); + } + } + } + results = s.getMoreResults(); + } while (results); + } catch (SQLException e) { + if (!e.getMessage().contains("Divide by zero error")) { + e.printStackTrace(); + } + } + } + private static void createTable(Statement s) throws SQLException { s.execute("CREATE TABLE " + tableName + " (id int IDENTITY, data varchar(50));"); } + private static void createTable(Statement s, String query) throws SQLException { + s.execute(query); + } + private static void insertData(Statement s) throws SQLException { for (int i = 1; i <= numberOfRows; i++) { s.executeUpdate("INSERT INTO " + tableName + " VALUES ('testData" + i + "');"); } } + private static void insertData(Statement s, String query, int rows) throws SQLException { + for (int i = 0; i < rows; i++) { + s.executeUpdate(query); + } + } + private void verifyResultSet(ResultSet rs) throws SQLException { int count = 0; while (rs.next()) { @@ -246,10 +555,32 @@ private void verifyResultSetResponseBuffering(String responseBuffering, } } + private static void createCallableStatementOnDoneTestSp(Statement stmt) throws SQLException { + String sql = "CREATE PROCEDURE " + callableStatementICROnDoneTestSp + + "(@p1 datetime2(7) OUTPUT, @p2 datetime2(7) OUTPUT) AS " + + "SELECT @p1 = '2018-03-11T02:00:00.1234567'; SELECT @p2 = '2022-03-11T02:00:00.1234567';"; + stmt.execute(sql); + } + + private static void createCallableStatementOnDoneErrorTestSp(Statement stmt) throws SQLException { + String sql = "CREATE PROCEDURE " + callableStatementICROnDoneErrorTestSp + + "(@p1 datetime2(7) OUTPUT, @p2 datetime2(7) OUTPUT) AS " + + "SELECT @p1 = '2018-03-11T02:00:00.1234567'; SELECT @p2 = '2022-03-11T02:00:00.1234567'; " + + errorQuery; + stmt.execute(sql); + } + @AfterAll public static void cleanUp() throws SQLException { try (Connection c = DriverManager.getConnection(connectionString); Statement s = c.createStatement()) { TestUtils.dropTableIfExists(tableName, s); + TestUtils.dropTableIfExists(clientCursorInitTable1, s); + TestUtils.dropTableIfExists(clientCursorInitTable2, s); + TestUtils.dropTableIfExists(clientCursorInitTable3, s); + TestUtils.dropTableIfExists(fetchBufferTestTable1, s); + TestUtils.dropTableIfExists(fetchBufferTestTable2, s); + TestUtils.dropProcedureIfExists(callableStatementICROnDoneTestSp, s); + TestUtils.dropProcedureIfExists(callableStatementICROnDoneErrorTestSp, s); } } }