From 4b195aa66563bdae1dc0dff28c001791a9054ba2 Mon Sep 17 00:00:00 2001 From: Terry <32403408+tkyc@users.noreply.github.com> Date: Tue, 13 Dec 2022 11:01:12 -0800 Subject: [PATCH 1/2] Fixed Idle Connection recovery so that unprocessedResponseCount isn't over decremented (#1989) * Updated onDone with reason/comment for unecessary decrement for future reference * Added more ICR tests * Added missing brackets * Test table names needed to be unique * Drop tables within test if exist Co-authored-by: lilgreenbird --- .../jdbc/SQLServerCallableStatement.java | 5 +- .../sqlserver/jdbc/SQLServerConnection.java | 4 + .../sqlserver/jdbc/SQLServerResultSet.java | 23 +- .../sqlserver/jdbc/SQLServerStatement.java | 5 +- .../jdbc/resiliency/BasicConnectionTest.java | 73 +++- .../ResultSetsWithResiliencyTest.java | 331 ++++++++++++++++++ 6 files changed, 432 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java index 18100a3ea..5aadf124e 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java @@ -255,7 +255,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 bf36b425f..1bd27b239 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -3882,6 +3882,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 edd702e1f..85b9781f1 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java @@ -381,10 +381,16 @@ boolean onDone(TDSReader tdsReader) throws SQLServerException { // following the column metadata indicates an empty result set. rowCount = 0; - short status = tdsReader.peekStatusFlag(); - stmt.connection.getSessionRecovery().decrementUnprocessedResponseCount(); + // 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}; SQLServerException.makeFromDriverError(stmt.connection, stmt, form.format(msgArgs), null, false); @@ -5376,7 +5382,18 @@ boolean onDone(TDSReader tdsReader) throws SQLServerException { StreamDone doneToken = new StreamDone(); doneToken.setFromTDS(tdsReader); - stmt.connection.getSessionRecovery().decrementUnprocessedResponseCount(); + + if (doneToken.isFinal()) { + stmt.connection.getSessionRecovery().decrementUnprocessedResponseCount(); + } + + if (doneToken.isFinal() && doneToken.isError()) { + short status = tdsReader.peekStatusFlag(); + SQLServerError databaseError = 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); + } // 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 3a33edef4..966930b28 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java @@ -1480,7 +1480,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 dca162fe3..a73a81443 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/resiliency/BasicConnectionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/resiliency/BasicConnectionTest.java @@ -14,17 +14,20 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import java.util.UUID; import javax.sql.PooledConnection; -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.SQLServerConnection; import com.microsoft.sqlserver.jdbc.SQLServerConnectionPoolDataSource; +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.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.Constants; @@ -266,6 +269,68 @@ public void testPooledConnectionLang() throws SQLException { } } + @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); } } } From 704057ec2cf15700226108f885d08bb8599da98a Mon Sep 17 00:00:00 2001 From: Terry Chow Date: Wed, 14 Dec 2022 09:56:14 -0800 Subject: [PATCH 2/2] Resolved merge conflicts --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index fdf6b7c57..edb83a4ef 100644 --- a/pom.xml +++ b/pom.xml @@ -74,7 +74,7 @@ [1.3.2, 1.9.0] - 5.5.2 + 5.8.2 3.4.2 2.7.0 1.7.30