| 
6 | 6 | package com.microsoft.sqlserver.jdbc.resiliency;  | 
7 | 7 | 
 
  | 
8 | 8 | import static org.junit.Assert.assertEquals;  | 
 | 9 | +import static org.junit.Assert.assertNotEquals;  | 
9 | 10 | import static org.junit.Assert.assertTrue;  | 
10 | 11 | import static org.junit.Assert.fail;  | 
11 | 12 | 
 
  | 
12 | 13 | import java.lang.reflect.Field;  | 
13 | 14 | import java.sql.Connection;  | 
14 | 15 | import java.sql.DriverManager;  | 
 | 16 | +import java.sql.PreparedStatement;  | 
15 | 17 | import java.sql.ResultSet;  | 
16 | 18 | import java.sql.SQLException;  | 
17 | 19 | import java.sql.Statement;  | 
 | 20 | +import java.util.Collections;  | 
 | 21 | +import java.util.LinkedList;  | 
 | 22 | +import java.util.List;  | 
18 | 23 | import java.util.UUID;  | 
19 | 24 | 
 
  | 
20 | 25 | import javax.sql.PooledConnection;  | 
21 | 26 | 
 
  | 
 | 27 | +import org.junit.jupiter.api.BeforeAll;  | 
 | 28 | +import org.junit.jupiter.api.Tag;  | 
 | 29 | +import org.junit.jupiter.api.Test;  | 
 | 30 | + | 
22 | 31 | import com.microsoft.sqlserver.jdbc.RandomUtil;  | 
23 | 32 | import com.microsoft.sqlserver.jdbc.SQLServerConnection;  | 
24 | 33 | import com.microsoft.sqlserver.jdbc.SQLServerConnectionPoolDataSource;  | 
25 | 34 | import com.microsoft.sqlserver.jdbc.SQLServerPooledConnection;  | 
26 | 35 | import com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement;  | 
27 | 36 | import com.microsoft.sqlserver.jdbc.TestResource;  | 
28 | 37 | import com.microsoft.sqlserver.jdbc.TestUtils;  | 
29 |  | -import org.junit.jupiter.api.BeforeAll;  | 
30 |  | -import org.junit.jupiter.api.Tag;  | 
31 |  | -import org.junit.jupiter.api.Test;  | 
32 |  | - | 
33 | 38 | import com.microsoft.sqlserver.testframework.AbstractTest;  | 
34 | 39 | import com.microsoft.sqlserver.testframework.Constants;  | 
35 | 40 | 
 
  | 
@@ -367,6 +372,99 @@ public void testPreparedStatementCacheShouldBeCleared() throws SQLException {  | 
367 | 372 |         }  | 
368 | 373 |     }  | 
369 | 374 | 
 
  | 
 | 375 | +    @Test  | 
 | 376 | +    public void testPreparedStatementHandleOfStatementShouldBeCleared() throws SQLException {  | 
 | 377 | +        try (SQLServerConnection con = (SQLServerConnection) ResiliencyUtils.getConnection(connectionString)) {  | 
 | 378 | +            int cacheSize = 2;  | 
 | 379 | +            String query = String.format("/*testPreparedStatementHandleOfStatementShouldBeCleared%s*/SELECT 1; -- ",  | 
 | 380 | +                    UUID.randomUUID().toString());  | 
 | 381 | + | 
 | 382 | +            // enable caching  | 
 | 383 | +            con.setDisableStatementPooling(false);  | 
 | 384 | +            con.setStatementPoolingCacheSize(cacheSize);  | 
 | 385 | +            con.setServerPreparedStatementDiscardThreshold(cacheSize);  | 
 | 386 | + | 
 | 387 | +            List<SQLServerPreparedStatement> statements = new LinkedList<>();  | 
 | 388 | + | 
 | 389 | +            // add statements to fill cache  | 
 | 390 | +            for (int i = 0; i < cacheSize + 1; ++i) {  | 
 | 391 | +                SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) con.prepareStatement(query + i);  | 
 | 392 | +                pstmt.execute();  | 
 | 393 | +                pstmt.execute();  | 
 | 394 | +                pstmt.execute();  | 
 | 395 | +                pstmt.getMoreResults();  | 
 | 396 | +                statements.add(pstmt);  | 
 | 397 | +            }  | 
 | 398 | + | 
 | 399 | +            // handle of the prepared statement should be set  | 
 | 400 | +            assertNotEquals(0, statements.get(1).getPreparedStatementHandle());  | 
 | 401 | + | 
 | 402 | +            ResiliencyUtils.killConnection(con, connectionString, 1);  | 
 | 403 | + | 
 | 404 | +            // call first statement to trigger reconnect  | 
 | 405 | +            statements.get(0).execute();  | 
 | 406 | + | 
 | 407 | +            // handle of the other statements should be cleared after reconnect  | 
 | 408 | +            assertEquals(0, statements.get(1).getPreparedStatementHandle());  | 
 | 409 | +        }  | 
 | 410 | +    }  | 
 | 411 | + | 
 | 412 | +    @Test  | 
 | 413 | +    public void testPreparedStatementShouldNotUseWrongHandleAfterReconnect() throws SQLException {  | 
 | 414 | +        try (SQLServerConnection con = (SQLServerConnection) ResiliencyUtils.getConnection(connectionString)) {  | 
 | 415 | +            int cacheSize = 3;  | 
 | 416 | +            String queryOne = "select * from sys.sysusers where name=?;";  | 
 | 417 | +            String queryTwo = "select * from sys.sysusers where name=? and uid=?;";  | 
 | 418 | +            String queryThree = "select * from sys.sysusers where name=? and uid=? and islogin=?";  | 
 | 419 | + | 
 | 420 | +            String parameterOne = "name";  | 
 | 421 | +            int parameterUid = 0;  | 
 | 422 | +            int parameterIsLogin = 0;  | 
 | 423 | + | 
 | 424 | +            // enable caching  | 
 | 425 | +            con.setDisableStatementPooling(false);  | 
 | 426 | +            con.setStatementPoolingCacheSize(cacheSize);  | 
 | 427 | +            con.setServerPreparedStatementDiscardThreshold(cacheSize);  | 
 | 428 | + | 
 | 429 | +            List<PreparedStatement> statements = new LinkedList<>();  | 
 | 430 | + | 
 | 431 | +            PreparedStatement ps = con.prepareStatement(queryOne);  | 
 | 432 | +            ps.setString(1, parameterOne);  | 
 | 433 | +            statements.add(ps);  | 
 | 434 | + | 
 | 435 | +            ps = con.prepareStatement(queryTwo);  | 
 | 436 | +            ps.setString(1, parameterOne);  | 
 | 437 | +            ps.setInt(2, parameterUid);  | 
 | 438 | +            statements.add(ps);  | 
 | 439 | + | 
 | 440 | +            ps = con.prepareStatement(queryThree);  | 
 | 441 | +            ps.setString(1, parameterOne);  | 
 | 442 | +            ps.setInt(2, parameterUid);  | 
 | 443 | +            ps.setInt(3, parameterIsLogin);  | 
 | 444 | +            statements.add(ps);  | 
 | 445 | + | 
 | 446 | +            // add new statements to fill cache  | 
 | 447 | +            for (PreparedStatement preparedStatement : statements) {  | 
 | 448 | +                preparedStatement.execute();  | 
 | 449 | +                preparedStatement.execute();  | 
 | 450 | +                preparedStatement.execute();  | 
 | 451 | +                preparedStatement.getMoreResults();  | 
 | 452 | +            }  | 
 | 453 | + | 
 | 454 | +            ResiliencyUtils.killConnection(con, connectionString, 1);  | 
 | 455 | + | 
 | 456 | +            // call statements in reversed order, in order to force the statement to use the wrong handle  | 
 | 457 | +            // first execute triggers a reconnect  | 
 | 458 | +            Collections.reverse(statements);  | 
 | 459 | +            for (PreparedStatement preparedStatement : statements) {  | 
 | 460 | +                preparedStatement.execute();  | 
 | 461 | +                preparedStatement.execute();  | 
 | 462 | +                preparedStatement.execute();  | 
 | 463 | +                preparedStatement.getMoreResults();  | 
 | 464 | +            }  | 
 | 465 | +        }  | 
 | 466 | +    }  | 
 | 467 | + | 
370 | 468 |     @Test  | 
371 | 469 |     public void testUnprocessedResponseCountSuccessfulIdleConnectionRecovery() throws SQLException {  | 
372 | 470 |         try (SQLServerConnection con = (SQLServerConnection) ResiliencyUtils.getConnection(connectionString)) {  | 
 | 
0 commit comments