From 943bcd2829eb4cec61ffdb18ca997824b22b2ce3 Mon Sep 17 00:00:00 2001 From: ulvii Date: Tue, 29 May 2018 12:56:00 -0700 Subject: [PATCH 01/17] Add | Request Boundary Methods - beginRequest()/endRequest() implementation --- .../jdbc/ISQLServerConnection43.java | 13 +- .../sqlserver/jdbc/SQLServerConnection.java | 140 +++++- .../sqlserver/jdbc/SQLServerConnection43.java | 55 ++- .../sqlserver/jdbc/SQLServerStatement.java | 4 +- .../RequestBoundaryMethodsTest.java | 420 ++++++++++++++++++ .../sqlserver/testframework/AbstractTest.java | 2 +- .../sqlserver/testframework/Utils.java | 9 + 7 files changed, 624 insertions(+), 19 deletions(-) create mode 100644 src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection43.java b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection43.java index 1689cde87..b8230f5f9 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection43.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection43.java @@ -1,19 +1,24 @@ package com.microsoft.sqlserver.jdbc; +import java.sql.SQLException; import java.sql.ShardingKey; public interface ISQLServerConnection43 extends ISQLServerConnection { + public void beginRequest() throws SQLException; + + public void endRequest() throws SQLException; + public void setShardingKey(ShardingKey shardingKey) throws SQLServerException; - + public void setShardingKey(ShardingKey shardingKey, ShardingKey superShardingKey) throws SQLServerException; - + public boolean setShardingKeyIfValid(ShardingKey shardingKey, int timeout) throws SQLServerException; - + public boolean setShardingKeyIfValid(ShardingKey shardingKey, ShardingKey superShardingKey, int timeout) throws SQLServerException; - + } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 377f45859..5fbb2c28d 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -36,6 +36,7 @@ import java.sql.Statement; import java.sql.Struct; import java.text.MessageFormat; +import java.util.ArrayList; import java.util.Arrays; import java.util.Enumeration; import java.util.HashMap; @@ -3197,6 +3198,9 @@ public Statement createStatement(int resultSetType, checkClosed(); Statement st = new SQLServerStatement(this, resultSetType, resultSetConcurrency, SQLServerStatementColumnEncryptionSetting.UseConnectionSetting); + if (requestStarted) { + addOpenStatement(st); + } loggerExternal.exiting(getClassNameLogging(), "createStatement", st); return st; } @@ -3220,7 +3224,9 @@ public PreparedStatement prepareStatement(String sql, st = new SQLServerPreparedStatement(this, sql, resultSetType, resultSetConcurrency, SQLServerStatementColumnEncryptionSetting.UseConnectionSetting); } - + if (requestStarted) { + addOpenStatement(st); + } loggerExternal.exiting(getClassNameLogging(), "prepareStatement", st); return st; } @@ -3243,7 +3249,9 @@ private PreparedStatement prepareStatement(String sql, else { st = new SQLServerPreparedStatement(this, sql, resultSetType, resultSetConcurrency, stmtColEncSetting); } - + if (requestStarted) { + addOpenStatement(st); + } loggerExternal.exiting(getClassNameLogging(), "prepareStatement", st); return st; } @@ -3267,7 +3275,9 @@ public CallableStatement prepareCall(String sql, st = new SQLServerCallableStatement(this, sql, resultSetType, resultSetConcurrency, SQLServerStatementColumnEncryptionSetting.UseConnectionSetting); } - + if (requestStarted) { + addOpenStatement(st); + } loggerExternal.exiting(getClassNameLogging(), "prepareCall", st); return st; } @@ -4633,6 +4643,9 @@ public Statement createStatement(int nType, checkValidHoldability(resultSetHoldability); checkMatchesCurrentHoldability(resultSetHoldability); Statement st = new SQLServerStatement(this, nType, nConcur, stmtColEncSetting); + if (requestStarted) { + addOpenStatement(st); + } loggerExternal.exiting(getClassNameLogging(), "createStatement", st); return st; } @@ -4695,7 +4708,9 @@ public PreparedStatement prepareStatement(java.lang.String sql, else { st = new SQLServerPreparedStatement(this, sql, nType, nConcur, stmtColEncSetting); } - + if (requestStarted) { + addOpenStatement(st); + } loggerExternal.exiting(getClassNameLogging(), "prepareStatement", st); return st; } @@ -4731,7 +4746,9 @@ public CallableStatement prepareCall(String sql, else { st = new SQLServerCallableStatement(this, sql, nType, nConcur, stmtColEncSetiing); } - + if (requestStarted) { + addOpenStatement(st); + } loggerExternal.exiting(getClassNameLogging(), "prepareCall", st); return st; } @@ -5283,14 +5300,99 @@ public T unwrap(Class iface) throws SQLException { return t; } - public void beginRequest() throws SQLFeatureNotSupportedException { + private boolean requestStarted = false; + private boolean originalDatabaseAutoCommitMode; + private int originalTransactionIsolationLevel; + private int originalNetworkTimeout; + private int originalHoldability; + private boolean originalSendTimeAsDatetime; + private int originalStatementPoolingCacheSize; + private boolean originalDisableStatementPooling; + private int originalServerPreparedStatementDiscardThreshold; + private Boolean originalEnablePrepareOnFirstPreparedStatementCall; + private String originalSCatalog = null; + private volatile SQLWarning originalSqlWarnings = null; + private List openStatements = null; + + protected void beginRequestInternal() throws SQLException { DriverJDBCVersion.checkSupportsJDBC43(); - throw new SQLFeatureNotSupportedException("beginRequest not implemented"); + synchronized (this) { + if (!requestStarted) { + originalDatabaseAutoCommitMode = databaseAutoCommitMode; + originalTransactionIsolationLevel = transactionIsolationLevel; + originalNetworkTimeout = getNetworkTimeout(); + originalHoldability = holdability; + originalSendTimeAsDatetime = sendTimeAsDatetime; + originalStatementPoolingCacheSize = statementPoolingCacheSize; + originalDisableStatementPooling = disableStatementPooling; + originalServerPreparedStatementDiscardThreshold = serverPreparedStatementDiscardThreshold; + originalEnablePrepareOnFirstPreparedStatementCall = enablePrepareOnFirstPreparedStatementCall; + originalSCatalog = sCatalog; + originalSqlWarnings = sqlWarnings; + openStatements = new ArrayList(); + requestStarted = true; + } + } } - public void endRequest() throws SQLFeatureNotSupportedException { + protected void endRequestInternal() throws SQLException { DriverJDBCVersion.checkSupportsJDBC43(); - throw new SQLFeatureNotSupportedException("endRequest not implemented"); + synchronized (this) { + if (requestStarted) { + if (!databaseAutoCommitMode) { + rollback(); + } + if (databaseAutoCommitMode != originalDatabaseAutoCommitMode) { + setAutoCommit(originalDatabaseAutoCommitMode); + } + if (transactionIsolationLevel != originalTransactionIsolationLevel) { + setTransactionIsolation(originalTransactionIsolationLevel); + } + if (getNetworkTimeout() != originalNetworkTimeout) { + setNetworkTimeout(null, originalNetworkTimeout); + } + if (holdability != originalHoldability) { + setHoldability(originalHoldability); + } + if (sendTimeAsDatetime != originalSendTimeAsDatetime) { + setSendTimeAsDatetime(originalSendTimeAsDatetime); + } + if (statementPoolingCacheSize != originalStatementPoolingCacheSize) { + setStatementPoolingCacheSize(originalStatementPoolingCacheSize); + } + if (disableStatementPooling != originalDisableStatementPooling) { + setDisableStatementPooling(originalDisableStatementPooling); + } + if (serverPreparedStatementDiscardThreshold != originalServerPreparedStatementDiscardThreshold) { + if (0 > originalServerPreparedStatementDiscardThreshold) { + setServerPreparedStatementDiscardThreshold(DEFAULT_SERVER_PREPARED_STATEMENT_DISCARD_THRESHOLD); + } + else { + setServerPreparedStatementDiscardThreshold(originalServerPreparedStatementDiscardThreshold); + } + } + if (enablePrepareOnFirstPreparedStatementCall != originalEnablePrepareOnFirstPreparedStatementCall) { + if (null == originalEnablePrepareOnFirstPreparedStatementCall) { + setEnablePrepareOnFirstPreparedStatementCall(DEFAULT_ENABLE_PREPARE_ON_FIRST_PREPARED_STATEMENT_CALL); + } + else { + setEnablePrepareOnFirstPreparedStatementCall(originalEnablePrepareOnFirstPreparedStatementCall); + } + } + if (!sCatalog.equals(originalSCatalog)) { + setCatalog(originalSCatalog); + } + sqlWarnings = originalSqlWarnings; + if (null != openStatements) { + while (!openStatements.isEmpty()) { + try (Statement st = openStatements.get(0)) { + } + } + openStatements.clear(); + } + requestStarted = false; + } + } } /** @@ -5866,6 +5968,26 @@ public void onEviction(Sha1HashKey key, PreparedStatementHandle handle) { } } } + + /** + * @param st + * Statement to add to openStatements + */ + final synchronized void addOpenStatement(Statement st) { + if (null != openStatements) { + openStatements.add(st); + } + } + + /** + * @param st + * Statement to remove from openStatements + */ + final synchronized void removeOpenStatement(Statement st) { + if (null != openStatements) { + openStatements.remove(st); + } + } } // Helper class for security manager functions used by SQLServerConnection class. diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection43.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection43.java index 217bcfc72..bf1dcd5f2 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection43.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection43.java @@ -1,5 +1,6 @@ package com.microsoft.sqlserver.jdbc; +import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.sql.ShardingKey; @@ -8,7 +9,51 @@ public class SQLServerConnection43 extends SQLServerConnection implements ISQLSe SQLServerConnection43(String parentInfo) throws SQLServerException { super(parentInfo); } - + + /** + * Hints to the driver that a request, an independent unit of work, is beginning on this connection. It backs up the values of the connection + * properties that are modifiable through public methods. Each request is independent of all other requests with regard to state local to the + * connection either on the client or the server. Work done between {@code beginRequest}, {@code endRequest} pairs does not depend on any other + * work done on the connection either as part of another request or outside of any request. A request may include multiple transactions. There may + * be dependencies on committed database state as that is not local to the connection. {@code beginRequest} marks the beginning of the work unit. + *

+ * Local state is defined as any state associated with a Connection that is local to the current Connection either in the client or the database + * that is not transparently reproducible. + *

+ * Calls to {@code beginRequest} and {@code endRequest} are not nested. Multiple calls to {@code beginRequest} without an intervening call to + * {@code endRequest} is not an error. The first {@code beginRequest} call marks the start of the request and subsequent calls are treated as a + * no-op It is recommended to enclose each unit of work in {@code beginRequest}, {@code endRequest} pairs such that there is no open transaction + * at the beginning or end of the request and no dependency on local state that crosses request boundaries. Committed database state is not local. + * + * This method is to be used by Connection pooling managers. + *

+ * The pooling manager should call {@code beginRequest} on the underlying connection prior to returning a connection to the caller. + *

+ * + * @throws SQLException + * if an error occurs + * @see endRequest + */ + public void beginRequest() throws SQLException { + beginRequestInternal(); + } + + /** + * Hints to the driver that a request, an independent unit of work, has completed. It rolls back the open transactions. Resets the connection + * properties that are modifiable through public methods back to their original values. Calls to {@code beginRequest} and {@code endRequest} are + * not nested. Multiple calls to {@code endRequest} without an intervening call to {@code beginRequest} is not an error. The first + * {@code endRequest} call marks the request completed and subsequent calls are treated as a no-op. If {@code endRequest} is called without an + * initial call to {@code beginRequest} is a no-op. This method is to be used by Connection pooling managers. + *

+ * + * @throws SQLException + * if an error occurs + * @see beginRequest + */ + public void endRequest() throws SQLException { + endRequestInternal(); + } + public void setShardingKey(ShardingKey shardingKey) throws SQLServerException { DriverJDBCVersion.checkSupportsJDBC43(); throw new SQLServerException("setShardingKey not implemented", new SQLFeatureNotSupportedException("setShardingKey not implemented")); @@ -17,20 +62,22 @@ public void setShardingKey(ShardingKey shardingKey) throws SQLServerException { public void setShardingKey(ShardingKey shardingKey, ShardingKey superShardingKey) throws SQLServerException { DriverJDBCVersion.checkSupportsJDBC43(); - throw new SQLServerException("setShardingKey not implemented", new SQLFeatureNotSupportedException("setShardingKey not implemented")) ; + throw new SQLServerException("setShardingKey not implemented", new SQLFeatureNotSupportedException("setShardingKey not implemented")); } public boolean setShardingKeyIfValid(ShardingKey shardingKey, int timeout) throws SQLServerException { DriverJDBCVersion.checkSupportsJDBC43(); - throw new SQLServerException("setShardingKeyIfValid not implemented", new SQLFeatureNotSupportedException("setShardingKeyIfValid not implemented")); + throw new SQLServerException("setShardingKeyIfValid not implemented", + new SQLFeatureNotSupportedException("setShardingKeyIfValid not implemented")); } public boolean setShardingKeyIfValid(ShardingKey shardingKey, ShardingKey superShardingKey, int timeout) throws SQLServerException { DriverJDBCVersion.checkSupportsJDBC43(); - throw new SQLServerException("setShardingKeyIfValid not implemented", new SQLFeatureNotSupportedException("setShardingKeyIfValid not implemented")); + throw new SQLServerException("setShardingKeyIfValid not implemented", + new SQLFeatureNotSupportedException("setShardingKeyIfValid not implemented")); } } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java index 1ba59c562..84a117ce9 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java @@ -631,7 +631,9 @@ void closeInternal() { // Regardless what happens when cleaning up, // the statement is considered closed. assert !bIsClosed; - + + connection.removeOpenStatement(this); + discardLastExecutionResults(); bIsClosed = true; diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java new file mode 100644 index 000000000..1d7330249 --- /dev/null +++ b/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java @@ -0,0 +1,420 @@ +package com.microsoft.sqlserver.jdbc.requestboundary; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import org.junit.jupiter.api.Test; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; + +import com.microsoft.sqlserver.jdbc.SQLServerConnection; +import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; +import com.microsoft.sqlserver.testframework.AbstractTest; +import com.microsoft.sqlserver.testframework.PrepUtil; +import com.microsoft.sqlserver.testframework.Utils; +import com.microsoft.sqlserver.testframework.util.RandomUtil; + +/** + * A class for testing Request Boundary Methods. + */ +@RunWith(JUnitPlatform.class) +public class RequestBoundaryMethodsTest extends AbstractTest { + SQLServerConnection con = null; + Statement stmt = null; + PreparedStatement pstmt = null; + CallableStatement cstmt = null; + ResultSet rs = null; + + /** + * Tests Request Boundary methods with SQLServerConnection properties that are modifiable through public APIs. + * + * @throws SQLException + */ + + @Test + public void testModifiableConnectionProperties() throws SQLException { + // List of SQLServerConnection fields that can be modified through public APIs. + boolean autoCommitMode1 = true; + int transactionIsolationLevel1 = SQLServerConnection.TRANSACTION_READ_COMMITTED; + int networkTimeout1 = 0; + int holdability1 = ResultSet.HOLD_CURSORS_OVER_COMMIT; + boolean sendTimeAsDatetime1 = true; + int statementPoolingCacheSize1 = 0; + boolean disableStatementPooling1 = true; + int serverPreparedStatementDiscardThreshold1 = 10; + boolean enablePrepareOnFirstPreparedStatementCall1 = false; + String sCatalog1 = "master"; + + boolean autoCommitMode2 = false; + int transactionIsolationLevel2 = SQLServerConnection.TRANSACTION_SERIALIZABLE; + int networkTimeout2 = 10; + int holdability2 = ResultSet.CLOSE_CURSORS_AT_COMMIT; + boolean sendTimeAsDatetime2 = false; + int statementPoolingCacheSize2 = 10; + boolean disableStatementPooling2 = false; + int serverPreparedStatementDiscardThreshold2 = 100; + boolean enablePrepareOnFirstPreparedStatementCall2 = true; + String sCatalog2 = "tempdb"; + + try { + con = connect(); + + if (Utils.isJDBC43AndGreater(con)) { + // First set of values. + setConnectionFields(con, autoCommitMode1, transactionIsolationLevel1, networkTimeout1, holdability1, sendTimeAsDatetime1, + statementPoolingCacheSize1, disableStatementPooling1, serverPreparedStatementDiscardThreshold1, + enablePrepareOnFirstPreparedStatementCall1, sCatalog1); + con.beginRequest(); + // Call setters with the second set of values inside beginRequest()/endRequest() block. + setConnectionFields(con, autoCommitMode2, transactionIsolationLevel2, networkTimeout2, holdability2, sendTimeAsDatetime2, + statementPoolingCacheSize2, disableStatementPooling2, serverPreparedStatementDiscardThreshold2, + enablePrepareOnFirstPreparedStatementCall2, sCatalog2); + con.endRequest(); + // Test if endRequest() resets the SQLServerConnection properties back to the first set of values. + compareValuesAgainstConnection(con, autoCommitMode1, transactionIsolationLevel1, networkTimeout1, holdability1, sendTimeAsDatetime1, + statementPoolingCacheSize1, disableStatementPooling1, serverPreparedStatementDiscardThreshold1, + enablePrepareOnFirstPreparedStatementCall1, sCatalog1); + + // Multiple calls to beginRequest() without an intervening call to endRequest() are no-op. + setConnectionFields(con, autoCommitMode2, transactionIsolationLevel2, networkTimeout2, holdability2, sendTimeAsDatetime2, + statementPoolingCacheSize2, disableStatementPooling2, serverPreparedStatementDiscardThreshold2, + enablePrepareOnFirstPreparedStatementCall2, sCatalog2); + con.beginRequest(); + setConnectionFields(con, autoCommitMode1, transactionIsolationLevel1, networkTimeout1, holdability1, sendTimeAsDatetime1, + statementPoolingCacheSize1, disableStatementPooling1, serverPreparedStatementDiscardThreshold1, + enablePrepareOnFirstPreparedStatementCall1, sCatalog1); + con.beginRequest(); + con.endRequest(); + // Same values as before the first beginRequest() + compareValuesAgainstConnection(con, autoCommitMode2, transactionIsolationLevel2, networkTimeout2, holdability2, sendTimeAsDatetime2, + statementPoolingCacheSize2, disableStatementPooling2, serverPreparedStatementDiscardThreshold2, + enablePrepareOnFirstPreparedStatementCall2, sCatalog2); + + // A call to endRequest() without an intervening call to beginRequest() is no-op. + setConnectionFields(con, autoCommitMode1, transactionIsolationLevel1, networkTimeout1, holdability1, sendTimeAsDatetime1, + statementPoolingCacheSize1, disableStatementPooling1, serverPreparedStatementDiscardThreshold1, + enablePrepareOnFirstPreparedStatementCall1, sCatalog1); + setConnectionFields(con, autoCommitMode2, transactionIsolationLevel2, networkTimeout2, holdability2, sendTimeAsDatetime2, + statementPoolingCacheSize2, disableStatementPooling2, serverPreparedStatementDiscardThreshold2, + enablePrepareOnFirstPreparedStatementCall2, sCatalog2); + con.endRequest(); + // No change. + compareValuesAgainstConnection(con, autoCommitMode2, transactionIsolationLevel2, networkTimeout2, holdability2, sendTimeAsDatetime2, + statementPoolingCacheSize2, disableStatementPooling2, serverPreparedStatementDiscardThreshold2, + enablePrepareOnFirstPreparedStatementCall2, sCatalog2); + } + } + finally { + if (null != con) { + con.close(); + } + } + } + + /** + * Tests Request Boundary methods with warnings. + * + * @throws SQLException + */ + @Test + public void testWarnings() throws SQLException { + try { + con = connect(); + + if (Utils.isJDBC43AndGreater(con)) { + con.beginRequest(); + generateWarning(con); + assertNotNull(con.getWarnings()); + con.endRequest(); + assertNull(con.getWarnings()); + + generateWarning(con); + con.endRequest(); + assertNotNull(con.getWarnings()); + + con.clearWarnings(); + con.beginRequest(); + generateWarning(con); + con.beginRequest(); + con.endRequest(); + assertNull(con.getWarnings()); + } + } + finally { + if (null != con) { + con.close(); + } + } + } + + /** + * Tests Request Boundary methods when there are open transactions. + * + * @throws SQLException + */ + @Test + public void testOpenTransactions() throws SQLException { + String tableName = null; + + try { + con = connect(); + + if (Utils.isJDBC43AndGreater(con)) { + stmt = con.createStatement(); + tableName = AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("RequestBoundary")); + Utils.dropTableIfExists(tableName, stmt); + stmt.executeUpdate("CREATE TABLE " + tableName + " (col int)"); + con.beginRequest(); + con.setAutoCommit(false); + stmt.executeUpdate("INSERT INTO " + tableName + " values(5)"); + // endRequest() does a rollback here, the value does not get inserted into the table. + con.endRequest(); + con.commit(); + + rs = con.createStatement().executeQuery("SELECT * from " + tableName); + assertTrue(!rs.isBeforeFirst(), "Should not have returned a result set."); + } + } + finally { + if (null != stmt) { + Utils.dropTableIfExists(tableName, stmt); + stmt.close(); + } + if (null != con) { + con.close(); + } + } + } + + /** + * Tests Request Boundary methods with statements. + * + * @throws SQLException + */ + @Test + public void testStatements() throws SQLException { + Statement stmt1 = null; + PreparedStatement ps = null; + CallableStatement cs = null; + ResultSet rs1 = null; + + try { + con = connect(); + + if (Utils.isJDBC43AndGreater(con)) { + stmt1 = con.createStatement(); + con.beginRequest(); + stmt = con.createStatement(); + rs = stmt.executeQuery("SELECT 1"); + rs.next(); + assertEquals(1, rs.getInt(1)); + con.endRequest(); + + assertTrue(!stmt1.isClosed(), "Statement created outside of beginRequest()/endRequest() block should not be closed."); + assertTrue(stmt.isClosed(), "Statment created inside beginRequest()/endRequest() block should be closed after endRequest()."); + assertTrue(rs.isClosed(), "ResultSet should be closed after endRequest()."); + stmt1.close(); + + // Multiple statements inside beginRequest()/endRequest() block + con.beginRequest(); + stmt = con.createStatement(); + String tableName = AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("RequestBoundary")); + Utils.dropTableIfExists(tableName, stmt); + stmt.executeUpdate("CREATE TABLE " + tableName + " (col int)"); + ps = con.prepareStatement("INSERT INTO " + tableName + " values (?)"); + ps.setInt(1, 2); + ps.executeUpdate(); + + stmt1 = con.createStatement(); + rs1 = stmt1.executeQuery("SELECT * FROM " + tableName); + rs1.next(); + assertEquals(2, rs1.getInt(1)); + Utils.dropTableIfExists(tableName, stmt); + + cs = con.prepareCall("{call sp_server_info}"); + cs.execute(); + con.endRequest(); + + assertTrue(stmt.isClosed()); + assertTrue(ps.isClosed()); + assertTrue(stmt1.isClosed()); + assertTrue(cs.isClosed()); + assertTrue(rs1.isClosed()); + } + } + finally { + if (null != stmt) { + stmt.close(); + } + if (null != stmt1) { + stmt1.close(); + } + if (null != ps) { + ps.close(); + } + if (null != cs) { + cs.close(); + } + if (null != con) { + con.close(); + } + } + } + + /** + * Tests Request Boundary methods in a multi-threaded environment. + * + * @throws SQLException + */ + @Test + public void testThreads() throws SQLException { + try { + con = connect(); + if (Utils.isJDBC43AndGreater(con)) { + Thread thread1 = new Thread() { + public void run() { + try { + con.setNetworkTimeout(null, 100); + con.setHoldability(ResultSet.CLOSE_CURSORS_AT_COMMIT); + } + catch (SQLException e) { + e.printStackTrace(); + } + } + }; + + Thread thread2 = new Thread() { + public void run() { + try { + stmt = con.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT 1"); + rs.next(); + assertEquals(1, rs.getInt(1)); + } + catch (SQLException e) { + e.printStackTrace(); + } + } + }; + + Thread thread3 = new Thread() { + public void run() { + try { + pstmt = con.prepareStatement("SELECT 1"); + ResultSet rs = pstmt.executeQuery(); + rs.next(); + assertEquals(1, rs.getInt(1)); + } + catch (SQLException e) { + e.printStackTrace(); + } + + } + }; + + int originalNetworkTimeout = con.getNetworkTimeout(); + int originalHoldability = con.getHoldability(); + con.beginRequest(); + thread1.start(); + thread2.start(); + thread3.start(); + try { + // Wait for threads to complete + Thread.sleep(3000); + } + catch (InterruptedException e) { + e.printStackTrace(); + } + con.endRequest(); + + assertEquals(originalNetworkTimeout, con.getNetworkTimeout()); + assertEquals(originalHoldability, con.getHoldability()); + assertTrue(stmt.isClosed()); + assertTrue(pstmt.isClosed()); + } + } + finally { + if (null != stmt) { + stmt.close(); + } + if (null != pstmt) { + pstmt.close(); + } + if (null != con) { + con.close(); + } + } + } + + private SQLServerConnection connect() throws SQLException { + SQLServerConnection connection = null; + try { + connection = PrepUtil.getConnection(getConfiguredProperty("mssql_jdbc_test_connection_properties")); + } + catch (ClassNotFoundException e) { + e.printStackTrace(); + } + return connection; + } + + private void setConnectionFields(SQLServerConnection con, + boolean autoCommitMode, + int transactionIsolationLevel, + int networkTimeout, + int holdability, + boolean sendTimeAsDatetime, + int statementPoolingCacheSize, + boolean disableStatementPooling, + int serverPreparedStatementDiscardThreshold, + boolean enablePrepareOnFirstPreparedStatementCall, + String sCatalog) throws SQLException { + con.setAutoCommit(autoCommitMode); + con.setTransactionIsolation(transactionIsolationLevel); + con.setNetworkTimeout(null, networkTimeout); + con.setHoldability(holdability); + con.setSendTimeAsDatetime(sendTimeAsDatetime); + con.setStatementPoolingCacheSize(statementPoolingCacheSize); + con.setDisableStatementPooling(disableStatementPooling); + con.setServerPreparedStatementDiscardThreshold(serverPreparedStatementDiscardThreshold); + con.setEnablePrepareOnFirstPreparedStatementCall(enablePrepareOnFirstPreparedStatementCall); + con.setCatalog(sCatalog); + } + + private void compareValuesAgainstConnection(SQLServerConnection con, + boolean autoCommitMode, + int transactionIsolationLevel, + int networkTimeout, + int holdability, + boolean sendTimeAsDatetime, + int statementPoolingCacheSize, + boolean disableStatementPooling, + int serverPreparedStatementDiscardThreshold, + boolean enablePrepareOnFirstPreparedStatementCall, + String sCatalog) throws SQLException { + final String description = " values do not match."; + assertEquals(autoCommitMode, con.getAutoCommit(), "autoCommitmode" + description); + assertEquals(transactionIsolationLevel, con.getTransactionIsolation(), "transactionIsolationLevel" + description); + assertEquals(networkTimeout, con.getNetworkTimeout(), "networkTimeout" + description); + assertEquals(holdability, con.getHoldability(), "holdability" + description); + assertEquals(sendTimeAsDatetime, con.getSendTimeAsDatetime(), "sendTimeAsDatetime" + description); + assertEquals(statementPoolingCacheSize, con.getStatementPoolingCacheSize(), "statementPoolingCacheSize" + description); + assertEquals(disableStatementPooling, con.getDisableStatementPooling(), "disableStatementPooling" + description); + assertEquals(serverPreparedStatementDiscardThreshold, con.getServerPreparedStatementDiscardThreshold(), + "serverPreparedStatementDiscardThreshold" + description); + assertEquals(enablePrepareOnFirstPreparedStatementCall, con.getEnablePrepareOnFirstPreparedStatementCall(), + "enablePrepareOnFirstPreparedStatementCall" + description); + assertEquals(sCatalog, con.getCatalog(), "sCatalog" + description); + } + + private void generateWarning(SQLServerConnection con) throws SQLException { + con.setClientInfo("name", "value"); + } +} \ No newline at end of file diff --git a/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java b/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java index e72ab2dc5..be7b4cd64 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java @@ -9,6 +9,7 @@ package com.microsoft.sqlserver.testframework; import java.sql.Connection; +import java.sql.SQLException; import java.util.Properties; import java.util.logging.ConsoleHandler; import java.util.logging.FileHandler; @@ -181,5 +182,4 @@ else if ("file".equalsIgnoreCase(loggingHandler)) { System.err.println("Some how could not invoke logging: " + e.getMessage()); } } - } diff --git a/src/test/java/com/microsoft/sqlserver/testframework/Utils.java b/src/test/java/com/microsoft/sqlserver/testframework/Utils.java index 229f7f4c6..6e199fa0c 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/Utils.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/Utils.java @@ -14,6 +14,7 @@ import java.io.ByteArrayInputStream; import java.io.CharArrayReader; import java.net.URI; +import java.sql.Connection; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; @@ -315,4 +316,12 @@ public static boolean parseByte(byte[] expectedData, return true; } + public static boolean isJDBC43AndGreater(Connection connection) throws SQLException{ + return getJDBCVersion(connection) >= 4.3F; + } + + public static float getJDBCVersion(Connection connection) throws SQLException { + return Float.valueOf(connection.getMetaData().getJDBCMajorVersion() + "." + connection.getMetaData().getJDBCMinorVersion()); + } + } \ No newline at end of file From d4c8bde2db4462a924f2780c6d98cee32cf515a6 Mon Sep 17 00:00:00 2001 From: ulvii Date: Tue, 29 May 2018 13:14:25 -0700 Subject: [PATCH 02/17] Fix | Remove unused import from AbstractTest --- .../java/com/microsoft/sqlserver/testframework/AbstractTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java b/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java index be7b4cd64..3df03ef53 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java @@ -9,7 +9,6 @@ package com.microsoft.sqlserver.testframework; import java.sql.Connection; -import java.sql.SQLException; import java.util.Properties; import java.util.logging.ConsoleHandler; import java.util.logging.FileHandler; From 0a569bc112579daa8b94435cc5e0a0d1eb9f9bcf Mon Sep 17 00:00:00 2001 From: ulvii Date: Tue, 29 May 2018 15:11:09 -0700 Subject: [PATCH 03/17] Fix | Increase network timeout --- .../jdbc/requestboundary/RequestBoundaryMethodsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java index 1d7330249..4b4ceb292 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java @@ -44,7 +44,7 @@ public void testModifiableConnectionProperties() throws SQLException { // List of SQLServerConnection fields that can be modified through public APIs. boolean autoCommitMode1 = true; int transactionIsolationLevel1 = SQLServerConnection.TRANSACTION_READ_COMMITTED; - int networkTimeout1 = 0; + int networkTimeout1 = 5; int holdability1 = ResultSet.HOLD_CURSORS_OVER_COMMIT; boolean sendTimeAsDatetime1 = true; int statementPoolingCacheSize1 = 0; From 00ead56ca491b0e76ebaedc6fd44c47a1be3de6e Mon Sep 17 00:00:00 2001 From: ulvii Date: Tue, 29 May 2018 15:37:14 -0700 Subject: [PATCH 04/17] Fix | Change catalog to model --- .../jdbc/requestboundary/RequestBoundaryMethodsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java index 4b4ceb292..f82180764 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java @@ -51,7 +51,7 @@ public void testModifiableConnectionProperties() throws SQLException { boolean disableStatementPooling1 = true; int serverPreparedStatementDiscardThreshold1 = 10; boolean enablePrepareOnFirstPreparedStatementCall1 = false; - String sCatalog1 = "master"; + String sCatalog1 = "model"; boolean autoCommitMode2 = false; int transactionIsolationLevel2 = SQLServerConnection.TRANSACTION_SERIALIZABLE; From ffe9b8e7856d439208dd5c0521b67c914af0421f Mon Sep 17 00:00:00 2001 From: ulvii Date: Tue, 29 May 2018 16:26:37 -0700 Subject: [PATCH 05/17] Fix | Wait for setNetworkTimeout to complete --- .../jdbc/requestboundary/RequestBoundaryMethodsTest.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java index f82180764..b5469f74c 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java @@ -331,6 +331,7 @@ public void run() { Thread.sleep(3000); } catch (InterruptedException e) { + Thread.currentThread().interrupt(); e.printStackTrace(); } con.endRequest(); @@ -379,6 +380,14 @@ private void setConnectionFields(SQLServerConnection con, con.setAutoCommit(autoCommitMode); con.setTransactionIsolation(transactionIsolationLevel); con.setNetworkTimeout(null, networkTimeout); + // Wait for setNetworkTimeout to complete + try { + Thread.sleep(3000); + } + catch (InterruptedException e) { + e.printStackTrace(); + Thread.currentThread().interrupt(); + } con.setHoldability(holdability); con.setSendTimeAsDatetime(sendTimeAsDatetime); con.setStatementPoolingCacheSize(statementPoolingCacheSize); From 8dd00822a9978057ee241de2e02ee93e94189574 Mon Sep 17 00:00:00 2001 From: ulvii Date: Wed, 30 May 2018 08:28:14 -0700 Subject: [PATCH 06/17] Fix | Change networkTimeout to milliseconds --- .../RequestBoundaryMethodsTest.java | 848 +++++++++--------- 1 file changed, 420 insertions(+), 428 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java index b5469f74c..47c0989e4 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java @@ -1,429 +1,421 @@ -package com.microsoft.sqlserver.jdbc.requestboundary; - -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.sql.CallableStatement; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; - -import org.junit.jupiter.api.Test; -import org.junit.platform.runner.JUnitPlatform; -import org.junit.runner.RunWith; - -import com.microsoft.sqlserver.jdbc.SQLServerConnection; -import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; -import com.microsoft.sqlserver.testframework.AbstractTest; -import com.microsoft.sqlserver.testframework.PrepUtil; -import com.microsoft.sqlserver.testframework.Utils; -import com.microsoft.sqlserver.testframework.util.RandomUtil; - -/** - * A class for testing Request Boundary Methods. - */ -@RunWith(JUnitPlatform.class) -public class RequestBoundaryMethodsTest extends AbstractTest { - SQLServerConnection con = null; - Statement stmt = null; - PreparedStatement pstmt = null; - CallableStatement cstmt = null; - ResultSet rs = null; - - /** - * Tests Request Boundary methods with SQLServerConnection properties that are modifiable through public APIs. - * - * @throws SQLException - */ - - @Test - public void testModifiableConnectionProperties() throws SQLException { - // List of SQLServerConnection fields that can be modified through public APIs. - boolean autoCommitMode1 = true; - int transactionIsolationLevel1 = SQLServerConnection.TRANSACTION_READ_COMMITTED; - int networkTimeout1 = 5; - int holdability1 = ResultSet.HOLD_CURSORS_OVER_COMMIT; - boolean sendTimeAsDatetime1 = true; - int statementPoolingCacheSize1 = 0; - boolean disableStatementPooling1 = true; - int serverPreparedStatementDiscardThreshold1 = 10; - boolean enablePrepareOnFirstPreparedStatementCall1 = false; - String sCatalog1 = "model"; - - boolean autoCommitMode2 = false; - int transactionIsolationLevel2 = SQLServerConnection.TRANSACTION_SERIALIZABLE; - int networkTimeout2 = 10; - int holdability2 = ResultSet.CLOSE_CURSORS_AT_COMMIT; - boolean sendTimeAsDatetime2 = false; - int statementPoolingCacheSize2 = 10; - boolean disableStatementPooling2 = false; - int serverPreparedStatementDiscardThreshold2 = 100; - boolean enablePrepareOnFirstPreparedStatementCall2 = true; - String sCatalog2 = "tempdb"; - - try { - con = connect(); - - if (Utils.isJDBC43AndGreater(con)) { - // First set of values. - setConnectionFields(con, autoCommitMode1, transactionIsolationLevel1, networkTimeout1, holdability1, sendTimeAsDatetime1, - statementPoolingCacheSize1, disableStatementPooling1, serverPreparedStatementDiscardThreshold1, - enablePrepareOnFirstPreparedStatementCall1, sCatalog1); - con.beginRequest(); - // Call setters with the second set of values inside beginRequest()/endRequest() block. - setConnectionFields(con, autoCommitMode2, transactionIsolationLevel2, networkTimeout2, holdability2, sendTimeAsDatetime2, - statementPoolingCacheSize2, disableStatementPooling2, serverPreparedStatementDiscardThreshold2, - enablePrepareOnFirstPreparedStatementCall2, sCatalog2); - con.endRequest(); - // Test if endRequest() resets the SQLServerConnection properties back to the first set of values. - compareValuesAgainstConnection(con, autoCommitMode1, transactionIsolationLevel1, networkTimeout1, holdability1, sendTimeAsDatetime1, - statementPoolingCacheSize1, disableStatementPooling1, serverPreparedStatementDiscardThreshold1, - enablePrepareOnFirstPreparedStatementCall1, sCatalog1); - - // Multiple calls to beginRequest() without an intervening call to endRequest() are no-op. - setConnectionFields(con, autoCommitMode2, transactionIsolationLevel2, networkTimeout2, holdability2, sendTimeAsDatetime2, - statementPoolingCacheSize2, disableStatementPooling2, serverPreparedStatementDiscardThreshold2, - enablePrepareOnFirstPreparedStatementCall2, sCatalog2); - con.beginRequest(); - setConnectionFields(con, autoCommitMode1, transactionIsolationLevel1, networkTimeout1, holdability1, sendTimeAsDatetime1, - statementPoolingCacheSize1, disableStatementPooling1, serverPreparedStatementDiscardThreshold1, - enablePrepareOnFirstPreparedStatementCall1, sCatalog1); - con.beginRequest(); - con.endRequest(); - // Same values as before the first beginRequest() - compareValuesAgainstConnection(con, autoCommitMode2, transactionIsolationLevel2, networkTimeout2, holdability2, sendTimeAsDatetime2, - statementPoolingCacheSize2, disableStatementPooling2, serverPreparedStatementDiscardThreshold2, - enablePrepareOnFirstPreparedStatementCall2, sCatalog2); - - // A call to endRequest() without an intervening call to beginRequest() is no-op. - setConnectionFields(con, autoCommitMode1, transactionIsolationLevel1, networkTimeout1, holdability1, sendTimeAsDatetime1, - statementPoolingCacheSize1, disableStatementPooling1, serverPreparedStatementDiscardThreshold1, - enablePrepareOnFirstPreparedStatementCall1, sCatalog1); - setConnectionFields(con, autoCommitMode2, transactionIsolationLevel2, networkTimeout2, holdability2, sendTimeAsDatetime2, - statementPoolingCacheSize2, disableStatementPooling2, serverPreparedStatementDiscardThreshold2, - enablePrepareOnFirstPreparedStatementCall2, sCatalog2); - con.endRequest(); - // No change. - compareValuesAgainstConnection(con, autoCommitMode2, transactionIsolationLevel2, networkTimeout2, holdability2, sendTimeAsDatetime2, - statementPoolingCacheSize2, disableStatementPooling2, serverPreparedStatementDiscardThreshold2, - enablePrepareOnFirstPreparedStatementCall2, sCatalog2); - } - } - finally { - if (null != con) { - con.close(); - } - } - } - - /** - * Tests Request Boundary methods with warnings. - * - * @throws SQLException - */ - @Test - public void testWarnings() throws SQLException { - try { - con = connect(); - - if (Utils.isJDBC43AndGreater(con)) { - con.beginRequest(); - generateWarning(con); - assertNotNull(con.getWarnings()); - con.endRequest(); - assertNull(con.getWarnings()); - - generateWarning(con); - con.endRequest(); - assertNotNull(con.getWarnings()); - - con.clearWarnings(); - con.beginRequest(); - generateWarning(con); - con.beginRequest(); - con.endRequest(); - assertNull(con.getWarnings()); - } - } - finally { - if (null != con) { - con.close(); - } - } - } - - /** - * Tests Request Boundary methods when there are open transactions. - * - * @throws SQLException - */ - @Test - public void testOpenTransactions() throws SQLException { - String tableName = null; - - try { - con = connect(); - - if (Utils.isJDBC43AndGreater(con)) { - stmt = con.createStatement(); - tableName = AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("RequestBoundary")); - Utils.dropTableIfExists(tableName, stmt); - stmt.executeUpdate("CREATE TABLE " + tableName + " (col int)"); - con.beginRequest(); - con.setAutoCommit(false); - stmt.executeUpdate("INSERT INTO " + tableName + " values(5)"); - // endRequest() does a rollback here, the value does not get inserted into the table. - con.endRequest(); - con.commit(); - - rs = con.createStatement().executeQuery("SELECT * from " + tableName); - assertTrue(!rs.isBeforeFirst(), "Should not have returned a result set."); - } - } - finally { - if (null != stmt) { - Utils.dropTableIfExists(tableName, stmt); - stmt.close(); - } - if (null != con) { - con.close(); - } - } - } - - /** - * Tests Request Boundary methods with statements. - * - * @throws SQLException - */ - @Test - public void testStatements() throws SQLException { - Statement stmt1 = null; - PreparedStatement ps = null; - CallableStatement cs = null; - ResultSet rs1 = null; - - try { - con = connect(); - - if (Utils.isJDBC43AndGreater(con)) { - stmt1 = con.createStatement(); - con.beginRequest(); - stmt = con.createStatement(); - rs = stmt.executeQuery("SELECT 1"); - rs.next(); - assertEquals(1, rs.getInt(1)); - con.endRequest(); - - assertTrue(!stmt1.isClosed(), "Statement created outside of beginRequest()/endRequest() block should not be closed."); - assertTrue(stmt.isClosed(), "Statment created inside beginRequest()/endRequest() block should be closed after endRequest()."); - assertTrue(rs.isClosed(), "ResultSet should be closed after endRequest()."); - stmt1.close(); - - // Multiple statements inside beginRequest()/endRequest() block - con.beginRequest(); - stmt = con.createStatement(); - String tableName = AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("RequestBoundary")); - Utils.dropTableIfExists(tableName, stmt); - stmt.executeUpdate("CREATE TABLE " + tableName + " (col int)"); - ps = con.prepareStatement("INSERT INTO " + tableName + " values (?)"); - ps.setInt(1, 2); - ps.executeUpdate(); - - stmt1 = con.createStatement(); - rs1 = stmt1.executeQuery("SELECT * FROM " + tableName); - rs1.next(); - assertEquals(2, rs1.getInt(1)); - Utils.dropTableIfExists(tableName, stmt); - - cs = con.prepareCall("{call sp_server_info}"); - cs.execute(); - con.endRequest(); - - assertTrue(stmt.isClosed()); - assertTrue(ps.isClosed()); - assertTrue(stmt1.isClosed()); - assertTrue(cs.isClosed()); - assertTrue(rs1.isClosed()); - } - } - finally { - if (null != stmt) { - stmt.close(); - } - if (null != stmt1) { - stmt1.close(); - } - if (null != ps) { - ps.close(); - } - if (null != cs) { - cs.close(); - } - if (null != con) { - con.close(); - } - } - } - - /** - * Tests Request Boundary methods in a multi-threaded environment. - * - * @throws SQLException - */ - @Test - public void testThreads() throws SQLException { - try { - con = connect(); - if (Utils.isJDBC43AndGreater(con)) { - Thread thread1 = new Thread() { - public void run() { - try { - con.setNetworkTimeout(null, 100); - con.setHoldability(ResultSet.CLOSE_CURSORS_AT_COMMIT); - } - catch (SQLException e) { - e.printStackTrace(); - } - } - }; - - Thread thread2 = new Thread() { - public void run() { - try { - stmt = con.createStatement(); - ResultSet rs = stmt.executeQuery("SELECT 1"); - rs.next(); - assertEquals(1, rs.getInt(1)); - } - catch (SQLException e) { - e.printStackTrace(); - } - } - }; - - Thread thread3 = new Thread() { - public void run() { - try { - pstmt = con.prepareStatement("SELECT 1"); - ResultSet rs = pstmt.executeQuery(); - rs.next(); - assertEquals(1, rs.getInt(1)); - } - catch (SQLException e) { - e.printStackTrace(); - } - - } - }; - - int originalNetworkTimeout = con.getNetworkTimeout(); - int originalHoldability = con.getHoldability(); - con.beginRequest(); - thread1.start(); - thread2.start(); - thread3.start(); - try { - // Wait for threads to complete - Thread.sleep(3000); - } - catch (InterruptedException e) { - Thread.currentThread().interrupt(); - e.printStackTrace(); - } - con.endRequest(); - - assertEquals(originalNetworkTimeout, con.getNetworkTimeout()); - assertEquals(originalHoldability, con.getHoldability()); - assertTrue(stmt.isClosed()); - assertTrue(pstmt.isClosed()); - } - } - finally { - if (null != stmt) { - stmt.close(); - } - if (null != pstmt) { - pstmt.close(); - } - if (null != con) { - con.close(); - } - } - } - - private SQLServerConnection connect() throws SQLException { - SQLServerConnection connection = null; - try { - connection = PrepUtil.getConnection(getConfiguredProperty("mssql_jdbc_test_connection_properties")); - } - catch (ClassNotFoundException e) { - e.printStackTrace(); - } - return connection; - } - - private void setConnectionFields(SQLServerConnection con, - boolean autoCommitMode, - int transactionIsolationLevel, - int networkTimeout, - int holdability, - boolean sendTimeAsDatetime, - int statementPoolingCacheSize, - boolean disableStatementPooling, - int serverPreparedStatementDiscardThreshold, - boolean enablePrepareOnFirstPreparedStatementCall, - String sCatalog) throws SQLException { - con.setAutoCommit(autoCommitMode); - con.setTransactionIsolation(transactionIsolationLevel); - con.setNetworkTimeout(null, networkTimeout); - // Wait for setNetworkTimeout to complete - try { - Thread.sleep(3000); - } - catch (InterruptedException e) { - e.printStackTrace(); - Thread.currentThread().interrupt(); - } - con.setHoldability(holdability); - con.setSendTimeAsDatetime(sendTimeAsDatetime); - con.setStatementPoolingCacheSize(statementPoolingCacheSize); - con.setDisableStatementPooling(disableStatementPooling); - con.setServerPreparedStatementDiscardThreshold(serverPreparedStatementDiscardThreshold); - con.setEnablePrepareOnFirstPreparedStatementCall(enablePrepareOnFirstPreparedStatementCall); - con.setCatalog(sCatalog); - } - - private void compareValuesAgainstConnection(SQLServerConnection con, - boolean autoCommitMode, - int transactionIsolationLevel, - int networkTimeout, - int holdability, - boolean sendTimeAsDatetime, - int statementPoolingCacheSize, - boolean disableStatementPooling, - int serverPreparedStatementDiscardThreshold, - boolean enablePrepareOnFirstPreparedStatementCall, - String sCatalog) throws SQLException { - final String description = " values do not match."; - assertEquals(autoCommitMode, con.getAutoCommit(), "autoCommitmode" + description); - assertEquals(transactionIsolationLevel, con.getTransactionIsolation(), "transactionIsolationLevel" + description); - assertEquals(networkTimeout, con.getNetworkTimeout(), "networkTimeout" + description); - assertEquals(holdability, con.getHoldability(), "holdability" + description); - assertEquals(sendTimeAsDatetime, con.getSendTimeAsDatetime(), "sendTimeAsDatetime" + description); - assertEquals(statementPoolingCacheSize, con.getStatementPoolingCacheSize(), "statementPoolingCacheSize" + description); - assertEquals(disableStatementPooling, con.getDisableStatementPooling(), "disableStatementPooling" + description); - assertEquals(serverPreparedStatementDiscardThreshold, con.getServerPreparedStatementDiscardThreshold(), - "serverPreparedStatementDiscardThreshold" + description); - assertEquals(enablePrepareOnFirstPreparedStatementCall, con.getEnablePrepareOnFirstPreparedStatementCall(), - "enablePrepareOnFirstPreparedStatementCall" + description); - assertEquals(sCatalog, con.getCatalog(), "sCatalog" + description); - } - - private void generateWarning(SQLServerConnection con) throws SQLException { - con.setClientInfo("name", "value"); - } +package com.microsoft.sqlserver.jdbc.requestboundary; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import org.junit.jupiter.api.Test; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; + +import com.microsoft.sqlserver.jdbc.SQLServerConnection; +import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; +import com.microsoft.sqlserver.testframework.AbstractTest; +import com.microsoft.sqlserver.testframework.PrepUtil; +import com.microsoft.sqlserver.testframework.Utils; +import com.microsoft.sqlserver.testframework.util.RandomUtil; + +/** + * A class for testing Request Boundary Methods. + */ +@RunWith(JUnitPlatform.class) +public class RequestBoundaryMethodsTest extends AbstractTest { + SQLServerConnection con = null; + Statement stmt = null; + PreparedStatement pstmt = null; + CallableStatement cstmt = null; + ResultSet rs = null; + + /** + * Tests Request Boundary methods with SQLServerConnection properties that are modifiable through public APIs. + * + * @throws SQLException + */ + + @Test + public void testModifiableConnectionProperties() throws SQLException { + // List of SQLServerConnection fields that can be modified through public APIs. + boolean autoCommitMode1 = true; + int transactionIsolationLevel1 = SQLServerConnection.TRANSACTION_READ_COMMITTED; + int networkTimeout1 = 5000; + int holdability1 = ResultSet.HOLD_CURSORS_OVER_COMMIT; + boolean sendTimeAsDatetime1 = true; + int statementPoolingCacheSize1 = 0; + boolean disableStatementPooling1 = true; + int serverPreparedStatementDiscardThreshold1 = 10; + boolean enablePrepareOnFirstPreparedStatementCall1 = false; + String sCatalog1 = "model"; + + boolean autoCommitMode2 = false; + int transactionIsolationLevel2 = SQLServerConnection.TRANSACTION_SERIALIZABLE; + int networkTimeout2 = 10000; + int holdability2 = ResultSet.CLOSE_CURSORS_AT_COMMIT; + boolean sendTimeAsDatetime2 = false; + int statementPoolingCacheSize2 = 10; + boolean disableStatementPooling2 = false; + int serverPreparedStatementDiscardThreshold2 = 100; + boolean enablePrepareOnFirstPreparedStatementCall2 = true; + String sCatalog2 = "tempdb"; + + try { + con = connect(); + + if (Utils.isJDBC43AndGreater(con)) { + // First set of values. + setConnectionFields(con, autoCommitMode1, transactionIsolationLevel1, networkTimeout1, holdability1, sendTimeAsDatetime1, + statementPoolingCacheSize1, disableStatementPooling1, serverPreparedStatementDiscardThreshold1, + enablePrepareOnFirstPreparedStatementCall1, sCatalog1); + con.beginRequest(); + // Call setters with the second set of values inside beginRequest()/endRequest() block. + setConnectionFields(con, autoCommitMode2, transactionIsolationLevel2, networkTimeout2, holdability2, sendTimeAsDatetime2, + statementPoolingCacheSize2, disableStatementPooling2, serverPreparedStatementDiscardThreshold2, + enablePrepareOnFirstPreparedStatementCall2, sCatalog2); + con.endRequest(); + // Test if endRequest() resets the SQLServerConnection properties back to the first set of values. + compareValuesAgainstConnection(con, autoCommitMode1, transactionIsolationLevel1, networkTimeout1, holdability1, sendTimeAsDatetime1, + statementPoolingCacheSize1, disableStatementPooling1, serverPreparedStatementDiscardThreshold1, + enablePrepareOnFirstPreparedStatementCall1, sCatalog1); + + // Multiple calls to beginRequest() without an intervening call to endRequest() are no-op. + setConnectionFields(con, autoCommitMode2, transactionIsolationLevel2, networkTimeout2, holdability2, sendTimeAsDatetime2, + statementPoolingCacheSize2, disableStatementPooling2, serverPreparedStatementDiscardThreshold2, + enablePrepareOnFirstPreparedStatementCall2, sCatalog2); + con.beginRequest(); + setConnectionFields(con, autoCommitMode1, transactionIsolationLevel1, networkTimeout1, holdability1, sendTimeAsDatetime1, + statementPoolingCacheSize1, disableStatementPooling1, serverPreparedStatementDiscardThreshold1, + enablePrepareOnFirstPreparedStatementCall1, sCatalog1); + con.beginRequest(); + con.endRequest(); + // Same values as before the first beginRequest() + compareValuesAgainstConnection(con, autoCommitMode2, transactionIsolationLevel2, networkTimeout2, holdability2, sendTimeAsDatetime2, + statementPoolingCacheSize2, disableStatementPooling2, serverPreparedStatementDiscardThreshold2, + enablePrepareOnFirstPreparedStatementCall2, sCatalog2); + + // A call to endRequest() without an intervening call to beginRequest() is no-op. + setConnectionFields(con, autoCommitMode1, transactionIsolationLevel1, networkTimeout1, holdability1, sendTimeAsDatetime1, + statementPoolingCacheSize1, disableStatementPooling1, serverPreparedStatementDiscardThreshold1, + enablePrepareOnFirstPreparedStatementCall1, sCatalog1); + setConnectionFields(con, autoCommitMode2, transactionIsolationLevel2, networkTimeout2, holdability2, sendTimeAsDatetime2, + statementPoolingCacheSize2, disableStatementPooling2, serverPreparedStatementDiscardThreshold2, + enablePrepareOnFirstPreparedStatementCall2, sCatalog2); + con.endRequest(); + // No change. + compareValuesAgainstConnection(con, autoCommitMode2, transactionIsolationLevel2, networkTimeout2, holdability2, sendTimeAsDatetime2, + statementPoolingCacheSize2, disableStatementPooling2, serverPreparedStatementDiscardThreshold2, + enablePrepareOnFirstPreparedStatementCall2, sCatalog2); + } + } + finally { + if (null != con) { + con.close(); + } + } + } + + /** + * Tests Request Boundary methods with warnings. + * + * @throws SQLException + */ + @Test + public void testWarnings() throws SQLException { + try { + con = connect(); + + if (Utils.isJDBC43AndGreater(con)) { + con.beginRequest(); + generateWarning(con); + assertNotNull(con.getWarnings()); + con.endRequest(); + assertNull(con.getWarnings()); + + generateWarning(con); + con.endRequest(); + assertNotNull(con.getWarnings()); + + con.clearWarnings(); + con.beginRequest(); + generateWarning(con); + con.beginRequest(); + con.endRequest(); + assertNull(con.getWarnings()); + } + } + finally { + if (null != con) { + con.close(); + } + } + } + + /** + * Tests Request Boundary methods when there are open transactions. + * + * @throws SQLException + */ + @Test + public void testOpenTransactions() throws SQLException { + String tableName = null; + + try { + con = connect(); + + if (Utils.isJDBC43AndGreater(con)) { + stmt = con.createStatement(); + tableName = AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("RequestBoundary")); + Utils.dropTableIfExists(tableName, stmt); + stmt.executeUpdate("CREATE TABLE " + tableName + " (col int)"); + con.beginRequest(); + con.setAutoCommit(false); + stmt.executeUpdate("INSERT INTO " + tableName + " values(5)"); + // endRequest() does a rollback here, the value does not get inserted into the table. + con.endRequest(); + con.commit(); + + rs = con.createStatement().executeQuery("SELECT * from " + tableName); + assertTrue(!rs.isBeforeFirst(), "Should not have returned a result set."); + } + } + finally { + if (null != stmt) { + Utils.dropTableIfExists(tableName, stmt); + stmt.close(); + } + if (null != con) { + con.close(); + } + } + } + + /** + * Tests Request Boundary methods with statements. + * + * @throws SQLException + */ + @Test + public void testStatements() throws SQLException { + Statement stmt1 = null; + PreparedStatement ps = null; + CallableStatement cs = null; + ResultSet rs1 = null; + + try { + con = connect(); + + if (Utils.isJDBC43AndGreater(con)) { + stmt1 = con.createStatement(); + con.beginRequest(); + stmt = con.createStatement(); + rs = stmt.executeQuery("SELECT 1"); + rs.next(); + assertEquals(1, rs.getInt(1)); + con.endRequest(); + + assertTrue(!stmt1.isClosed(), "Statement created outside of beginRequest()/endRequest() block should not be closed."); + assertTrue(stmt.isClosed(), "Statment created inside beginRequest()/endRequest() block should be closed after endRequest()."); + assertTrue(rs.isClosed(), "ResultSet should be closed after endRequest()."); + stmt1.close(); + + // Multiple statements inside beginRequest()/endRequest() block + con.beginRequest(); + stmt = con.createStatement(); + String tableName = AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("RequestBoundary")); + Utils.dropTableIfExists(tableName, stmt); + stmt.executeUpdate("CREATE TABLE " + tableName + " (col int)"); + ps = con.prepareStatement("INSERT INTO " + tableName + " values (?)"); + ps.setInt(1, 2); + ps.executeUpdate(); + + stmt1 = con.createStatement(); + rs1 = stmt1.executeQuery("SELECT * FROM " + tableName); + rs1.next(); + assertEquals(2, rs1.getInt(1)); + Utils.dropTableIfExists(tableName, stmt); + + cs = con.prepareCall("{call sp_server_info}"); + cs.execute(); + con.endRequest(); + + assertTrue(stmt.isClosed()); + assertTrue(ps.isClosed()); + assertTrue(stmt1.isClosed()); + assertTrue(cs.isClosed()); + assertTrue(rs1.isClosed()); + } + } + finally { + if (null != stmt) { + stmt.close(); + } + if (null != stmt1) { + stmt1.close(); + } + if (null != ps) { + ps.close(); + } + if (null != cs) { + cs.close(); + } + if (null != con) { + con.close(); + } + } + } + + /** + * Tests Request Boundary methods in a multi-threaded environment. + * + * @throws SQLException + */ + @Test + public void testThreads() throws SQLException { + try { + con = connect(); + if (Utils.isJDBC43AndGreater(con)) { + Thread thread1 = new Thread() { + public void run() { + try { + con.setNetworkTimeout(null, 100); + con.setHoldability(ResultSet.CLOSE_CURSORS_AT_COMMIT); + } + catch (SQLException e) { + e.printStackTrace(); + } + } + }; + + Thread thread2 = new Thread() { + public void run() { + try { + stmt = con.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT 1"); + rs.next(); + assertEquals(1, rs.getInt(1)); + } + catch (SQLException e) { + e.printStackTrace(); + } + } + }; + + Thread thread3 = new Thread() { + public void run() { + try { + pstmt = con.prepareStatement("SELECT 1"); + ResultSet rs = pstmt.executeQuery(); + rs.next(); + assertEquals(1, rs.getInt(1)); + } + catch (SQLException e) { + e.printStackTrace(); + } + + } + }; + + int originalNetworkTimeout = con.getNetworkTimeout(); + int originalHoldability = con.getHoldability(); + con.beginRequest(); + thread1.start(); + thread2.start(); + thread3.start(); + try { + // Wait for threads to complete + Thread.sleep(3000); + } + catch (InterruptedException e) { + Thread.currentThread().interrupt(); + e.printStackTrace(); + } + con.endRequest(); + + assertEquals(originalNetworkTimeout, con.getNetworkTimeout()); + assertEquals(originalHoldability, con.getHoldability()); + assertTrue(stmt.isClosed()); + assertTrue(pstmt.isClosed()); + } + } + finally { + if (null != stmt) { + stmt.close(); + } + if (null != pstmt) { + pstmt.close(); + } + if (null != con) { + con.close(); + } + } + } + + private SQLServerConnection connect() throws SQLException { + SQLServerConnection connection = null; + try { + connection = PrepUtil.getConnection(getConfiguredProperty("mssql_jdbc_test_connection_properties")); + } + catch (ClassNotFoundException e) { + e.printStackTrace(); + } + return connection; + } + + private void setConnectionFields(SQLServerConnection con, + boolean autoCommitMode, + int transactionIsolationLevel, + int networkTimeout, + int holdability, + boolean sendTimeAsDatetime, + int statementPoolingCacheSize, + boolean disableStatementPooling, + int serverPreparedStatementDiscardThreshold, + boolean enablePrepareOnFirstPreparedStatementCall, + String sCatalog) throws SQLException { + con.setAutoCommit(autoCommitMode); + con.setTransactionIsolation(transactionIsolationLevel); + con.setNetworkTimeout(null, networkTimeout); + con.setHoldability(holdability); + con.setSendTimeAsDatetime(sendTimeAsDatetime); + con.setStatementPoolingCacheSize(statementPoolingCacheSize); + con.setDisableStatementPooling(disableStatementPooling); + con.setServerPreparedStatementDiscardThreshold(serverPreparedStatementDiscardThreshold); + con.setEnablePrepareOnFirstPreparedStatementCall(enablePrepareOnFirstPreparedStatementCall); + con.setCatalog(sCatalog); + } + + private void compareValuesAgainstConnection(SQLServerConnection con, + boolean autoCommitMode, + int transactionIsolationLevel, + int networkTimeout, + int holdability, + boolean sendTimeAsDatetime, + int statementPoolingCacheSize, + boolean disableStatementPooling, + int serverPreparedStatementDiscardThreshold, + boolean enablePrepareOnFirstPreparedStatementCall, + String sCatalog) throws SQLException { + final String description = " values do not match."; + assertEquals(autoCommitMode, con.getAutoCommit(), "autoCommitmode" + description); + assertEquals(transactionIsolationLevel, con.getTransactionIsolation(), "transactionIsolationLevel" + description); + assertEquals(networkTimeout, con.getNetworkTimeout(), "networkTimeout" + description); + assertEquals(holdability, con.getHoldability(), "holdability" + description); + assertEquals(sendTimeAsDatetime, con.getSendTimeAsDatetime(), "sendTimeAsDatetime" + description); + assertEquals(statementPoolingCacheSize, con.getStatementPoolingCacheSize(), "statementPoolingCacheSize" + description); + assertEquals(disableStatementPooling, con.getDisableStatementPooling(), "disableStatementPooling" + description); + assertEquals(serverPreparedStatementDiscardThreshold, con.getServerPreparedStatementDiscardThreshold(), + "serverPreparedStatementDiscardThreshold" + description); + assertEquals(enablePrepareOnFirstPreparedStatementCall, con.getEnablePrepareOnFirstPreparedStatementCall(), + "enablePrepareOnFirstPreparedStatementCall" + description); + assertEquals(sCatalog, con.getCatalog(), "sCatalog" + description); + } + + private void generateWarning(SQLServerConnection con) throws SQLException { + con.setClientInfo("name", "value"); + } } \ No newline at end of file From 20cfcade995ae6c651c3b62a6a871f99bdffbd80 Mon Sep 17 00:00:00 2001 From: ulvii Date: Wed, 30 May 2018 08:50:22 -0700 Subject: [PATCH 07/17] Fix | Add newline to the end of Utils.java --- .../sqlserver/testframework/Utils.java | 654 +++++++++--------- 1 file changed, 327 insertions(+), 327 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/testframework/Utils.java b/src/test/java/com/microsoft/sqlserver/testframework/Utils.java index 6e199fa0c..5ae35122d 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/Utils.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/Utils.java @@ -1,327 +1,327 @@ -/* - * Microsoft JDBC Driver for SQL Server - * - * Copyright(c) Microsoft Corporation All rights reserved. - * - * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. - */ - -package com.microsoft.sqlserver.testframework; - -import static org.junit.Assert.fail; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.io.ByteArrayInputStream; -import java.io.CharArrayReader; -import java.net.URI; -import java.sql.Connection; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.microsoft.sqlserver.testframework.sqlType.SqlBigInt; -import com.microsoft.sqlserver.testframework.sqlType.SqlBinary; -import com.microsoft.sqlserver.testframework.sqlType.SqlBit; -import com.microsoft.sqlserver.testframework.sqlType.SqlChar; -import com.microsoft.sqlserver.testframework.sqlType.SqlDate; -import com.microsoft.sqlserver.testframework.sqlType.SqlDateTime; -import com.microsoft.sqlserver.testframework.sqlType.SqlDateTime2; -import com.microsoft.sqlserver.testframework.sqlType.SqlDateTimeOffset; -import com.microsoft.sqlserver.testframework.sqlType.SqlDecimal; -import com.microsoft.sqlserver.testframework.sqlType.SqlFloat; -import com.microsoft.sqlserver.testframework.sqlType.SqlInt; -import com.microsoft.sqlserver.testframework.sqlType.SqlMoney; -import com.microsoft.sqlserver.testframework.sqlType.SqlNChar; -import com.microsoft.sqlserver.testframework.sqlType.SqlNVarChar; -import com.microsoft.sqlserver.testframework.sqlType.SqlNVarCharMax; -import com.microsoft.sqlserver.testframework.sqlType.SqlNumeric; -import com.microsoft.sqlserver.testframework.sqlType.SqlReal; -import com.microsoft.sqlserver.testframework.sqlType.SqlSmallDateTime; -import com.microsoft.sqlserver.testframework.sqlType.SqlSmallInt; -import com.microsoft.sqlserver.testframework.sqlType.SqlSmallMoney; -import com.microsoft.sqlserver.testframework.sqlType.SqlTime; -import com.microsoft.sqlserver.testframework.sqlType.SqlTinyInt; -import com.microsoft.sqlserver.testframework.sqlType.SqlType; -import com.microsoft.sqlserver.testframework.sqlType.SqlVarBinary; -import com.microsoft.sqlserver.testframework.sqlType.SqlVarBinaryMax; -import com.microsoft.sqlserver.testframework.sqlType.SqlVarChar; -import com.microsoft.sqlserver.testframework.sqlType.SqlVarCharMax; - -/** - * Generic Utility class which we can access by test classes. - * - * @since 6.1.2 - */ -public class Utils { - public static final Logger log = Logger.getLogger("Utils"); - - // 'SQL' represents SQL Server, while 'SQLAzure' represents SQL Azure. - public static final String SERVER_TYPE_SQL_SERVER = "SQL"; - public static final String SERVER_TYPE_SQL_AZURE = "SQLAzure"; - // private static SqlType types = null; - private static ArrayList types = null; - - /** - * Returns serverType - * - * @return - */ - public static String getServerType() { - String serverType = null; - - String serverTypeProperty = getConfiguredProperty("server.type"); - if (null == serverTypeProperty) { - // default to SQL Server - serverType = SERVER_TYPE_SQL_SERVER; - } - else if (serverTypeProperty.equalsIgnoreCase(SERVER_TYPE_SQL_AZURE)) { - serverType = SERVER_TYPE_SQL_AZURE; - } - else if (serverTypeProperty.equalsIgnoreCase(SERVER_TYPE_SQL_SERVER)) { - serverType = SERVER_TYPE_SQL_SERVER; - } - else { - if (log.isLoggable(Level.FINE)) { - log.fine("Server.type '" + serverTypeProperty + "' is not supported yet. Default to SQL Server"); - } - serverType = SERVER_TYPE_SQL_SERVER; - } - return serverType; - } - - /** - * Read variable from property files if found null try to read from env. - * - * @param key - * @return Value - */ - public static String getConfiguredProperty(String key) { - String value = System.getProperty(key); - - if (value == null) { - value = System.getenv(key); - } - - return value; - } - - /** - * Convenient method for {@link #getConfiguredProperty(String)} - * - * @param key - * @return Value - */ - public static String getConfiguredProperty(String key, - String defaultValue) { - String value = getConfiguredProperty(key); - - if (value == null) { - value = defaultValue; - } - - return value; - } - - /** - * - * @param javatype - * @return - */ - public static SqlType find(Class javatype) { - if (null != types) { - types(); - for (SqlType type : types) { - if (type.getType() == javatype) - return type; - } - } - return null; - } - - /** - * - * @param name - * @return - */ - public static SqlType find(String name) { - if (null == types) - types(); - if (null != types) { - for (SqlType type : types) { - if (type.getName().equalsIgnoreCase(name)) - return type; - } - } - return null; - } - - /** - * - * @return - */ - public static ArrayList types() { - if (null == types) { - types = new ArrayList<>(); - - types.add(new SqlInt()); - types.add(new SqlSmallInt()); - types.add(new SqlTinyInt()); - types.add(new SqlBit()); - types.add(new SqlDateTime()); - types.add(new SqlSmallDateTime()); - types.add(new SqlDecimal()); - types.add(new SqlNumeric()); - types.add(new SqlReal()); - types.add(new SqlFloat()); - types.add(new SqlMoney()); - types.add(new SqlSmallMoney()); - types.add(new SqlVarChar()); - types.add(new SqlChar()); - // types.add(new SqlText()); - types.add(new SqlBinary()); - types.add(new SqlVarBinary()); - // types.add(new SqlImage()); - // types.add(new SqlTimestamp()); - - types.add(new SqlNVarChar()); - types.add(new SqlNChar()); - // types.add(new SqlNText()); - // types.add(new SqlGuid()); - - types.add(new SqlBigInt()); - // types.add(new SqlVariant(this)); - - // 9.0 types - types.add(new SqlVarCharMax()); - types.add(new SqlNVarCharMax()); - types.add(new SqlVarBinaryMax()); - // types.add(new SqlXml()); - - // 10.0 types - types.add(new SqlDate()); - types.add(new SqlDateTime2()); - types.add(new SqlTime()); - types.add(new SqlDateTimeOffset()); - } - return types; - } - - /** - * Wrapper Class for BinaryStream - * - */ - public static class DBBinaryStream extends ByteArrayInputStream { - byte[] data; - - // Constructor - public DBBinaryStream(byte[] value) { - super(value); - data = value; - } - - } - - /** - * Wrapper for CharacterStream - * - */ - public static class DBCharacterStream extends CharArrayReader { - String localValue; - - /** - * Constructor - * - * @param value - */ - public DBCharacterStream(String value) { - super(value.toCharArray()); - localValue = value; - } - - } - - /** - * Wrapper for NCharacterStream - */ - class DBNCharacterStream extends DBCharacterStream { - // Constructor - public DBNCharacterStream(String value) { - super(value); - } - } - - /** - * - * @return location of resource file - */ - public static String getCurrentClassPath() { - try { - String className = new Object() { - }.getClass().getEnclosingClass().getName(); - String location = Class.forName(className).getProtectionDomain().getCodeSource().getLocation().getPath() + "/"; - URI uri = new URI(location.toString()); - return uri.getPath(); - } - catch (Exception e) { - fail("Failed to get CSV file path. " + e.getMessage()); - } - return null; - } - - /** - * mimic "DROP TABLE IF EXISTS ..." for older versions of SQL Server - */ - public static void dropTableIfExists(String tableName, java.sql.Statement stmt) throws SQLException { - dropObjectIfExists(tableName, "IsTable", stmt); - } - - /** - * mimic "DROP PROCEDURE IF EXISTS ..." for older versions of SQL Server - */ - public static void dropProcedureIfExists(String procName, java.sql.Statement stmt) throws SQLException { - dropObjectIfExists(procName, "IsProcedure", stmt); - } - - /** - * actually perform the "DROP TABLE / PROCEDURE" - */ - private static void dropObjectIfExists(String objectName, String objectProperty, java.sql.Statement stmt) throws SQLException { - StringBuilder sb = new StringBuilder(); - if (!objectName.startsWith("[")) { sb.append("["); } - sb.append(objectName); - if (!objectName.endsWith("]")) { sb.append("]"); } - String bracketedObjectName = sb.toString(); - String sql = String.format( - "IF EXISTS " + - "( " + - "SELECT * from sys.objects " + - "WHERE object_id = OBJECT_ID(N'%s') AND OBJECTPROPERTY(object_id, N'%s') = 1 " + - ") " + - "DROP %s %s ", - bracketedObjectName, - objectProperty, - "IsProcedure".equals(objectProperty) ? "PROCEDURE" : "TABLE", - bracketedObjectName); - stmt.executeUpdate(sql); - } - - public static boolean parseByte(byte[] expectedData, - byte[] retrieved) { - assertTrue(Arrays.equals(expectedData, Arrays.copyOf(retrieved, expectedData.length)), " unexpected BINARY value, expected"); - for (int i = expectedData.length; i < retrieved.length; i++) { - assertTrue(0 == retrieved[i], "unexpected data BINARY"); - } - return true; - } - - public static boolean isJDBC43AndGreater(Connection connection) throws SQLException{ - return getJDBCVersion(connection) >= 4.3F; - } - - public static float getJDBCVersion(Connection connection) throws SQLException { - return Float.valueOf(connection.getMetaData().getJDBCMajorVersion() + "." + connection.getMetaData().getJDBCMinorVersion()); - } - -} \ No newline at end of file +/* + * Microsoft JDBC Driver for SQL Server + * + * Copyright(c) Microsoft Corporation All rights reserved. + * + * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. + */ + +package com.microsoft.sqlserver.testframework; + +import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.CharArrayReader; +import java.net.URI; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.microsoft.sqlserver.testframework.sqlType.SqlBigInt; +import com.microsoft.sqlserver.testframework.sqlType.SqlBinary; +import com.microsoft.sqlserver.testframework.sqlType.SqlBit; +import com.microsoft.sqlserver.testframework.sqlType.SqlChar; +import com.microsoft.sqlserver.testframework.sqlType.SqlDate; +import com.microsoft.sqlserver.testframework.sqlType.SqlDateTime; +import com.microsoft.sqlserver.testframework.sqlType.SqlDateTime2; +import com.microsoft.sqlserver.testframework.sqlType.SqlDateTimeOffset; +import com.microsoft.sqlserver.testframework.sqlType.SqlDecimal; +import com.microsoft.sqlserver.testframework.sqlType.SqlFloat; +import com.microsoft.sqlserver.testframework.sqlType.SqlInt; +import com.microsoft.sqlserver.testframework.sqlType.SqlMoney; +import com.microsoft.sqlserver.testframework.sqlType.SqlNChar; +import com.microsoft.sqlserver.testframework.sqlType.SqlNVarChar; +import com.microsoft.sqlserver.testframework.sqlType.SqlNVarCharMax; +import com.microsoft.sqlserver.testframework.sqlType.SqlNumeric; +import com.microsoft.sqlserver.testframework.sqlType.SqlReal; +import com.microsoft.sqlserver.testframework.sqlType.SqlSmallDateTime; +import com.microsoft.sqlserver.testframework.sqlType.SqlSmallInt; +import com.microsoft.sqlserver.testframework.sqlType.SqlSmallMoney; +import com.microsoft.sqlserver.testframework.sqlType.SqlTime; +import com.microsoft.sqlserver.testframework.sqlType.SqlTinyInt; +import com.microsoft.sqlserver.testframework.sqlType.SqlType; +import com.microsoft.sqlserver.testframework.sqlType.SqlVarBinary; +import com.microsoft.sqlserver.testframework.sqlType.SqlVarBinaryMax; +import com.microsoft.sqlserver.testframework.sqlType.SqlVarChar; +import com.microsoft.sqlserver.testframework.sqlType.SqlVarCharMax; + +/** + * Generic Utility class which we can access by test classes. + * + * @since 6.1.2 + */ +public class Utils { + public static final Logger log = Logger.getLogger("Utils"); + + // 'SQL' represents SQL Server, while 'SQLAzure' represents SQL Azure. + public static final String SERVER_TYPE_SQL_SERVER = "SQL"; + public static final String SERVER_TYPE_SQL_AZURE = "SQLAzure"; + // private static SqlType types = null; + private static ArrayList types = null; + + /** + * Returns serverType + * + * @return + */ + public static String getServerType() { + String serverType = null; + + String serverTypeProperty = getConfiguredProperty("server.type"); + if (null == serverTypeProperty) { + // default to SQL Server + serverType = SERVER_TYPE_SQL_SERVER; + } + else if (serverTypeProperty.equalsIgnoreCase(SERVER_TYPE_SQL_AZURE)) { + serverType = SERVER_TYPE_SQL_AZURE; + } + else if (serverTypeProperty.equalsIgnoreCase(SERVER_TYPE_SQL_SERVER)) { + serverType = SERVER_TYPE_SQL_SERVER; + } + else { + if (log.isLoggable(Level.FINE)) { + log.fine("Server.type '" + serverTypeProperty + "' is not supported yet. Default to SQL Server"); + } + serverType = SERVER_TYPE_SQL_SERVER; + } + return serverType; + } + + /** + * Read variable from property files if found null try to read from env. + * + * @param key + * @return Value + */ + public static String getConfiguredProperty(String key) { + String value = System.getProperty(key); + + if (value == null) { + value = System.getenv(key); + } + + return value; + } + + /** + * Convenient method for {@link #getConfiguredProperty(String)} + * + * @param key + * @return Value + */ + public static String getConfiguredProperty(String key, + String defaultValue) { + String value = getConfiguredProperty(key); + + if (value == null) { + value = defaultValue; + } + + return value; + } + + /** + * + * @param javatype + * @return + */ + public static SqlType find(Class javatype) { + if (null != types) { + types(); + for (SqlType type : types) { + if (type.getType() == javatype) + return type; + } + } + return null; + } + + /** + * + * @param name + * @return + */ + public static SqlType find(String name) { + if (null == types) + types(); + if (null != types) { + for (SqlType type : types) { + if (type.getName().equalsIgnoreCase(name)) + return type; + } + } + return null; + } + + /** + * + * @return + */ + public static ArrayList types() { + if (null == types) { + types = new ArrayList<>(); + + types.add(new SqlInt()); + types.add(new SqlSmallInt()); + types.add(new SqlTinyInt()); + types.add(new SqlBit()); + types.add(new SqlDateTime()); + types.add(new SqlSmallDateTime()); + types.add(new SqlDecimal()); + types.add(new SqlNumeric()); + types.add(new SqlReal()); + types.add(new SqlFloat()); + types.add(new SqlMoney()); + types.add(new SqlSmallMoney()); + types.add(new SqlVarChar()); + types.add(new SqlChar()); + // types.add(new SqlText()); + types.add(new SqlBinary()); + types.add(new SqlVarBinary()); + // types.add(new SqlImage()); + // types.add(new SqlTimestamp()); + + types.add(new SqlNVarChar()); + types.add(new SqlNChar()); + // types.add(new SqlNText()); + // types.add(new SqlGuid()); + + types.add(new SqlBigInt()); + // types.add(new SqlVariant(this)); + + // 9.0 types + types.add(new SqlVarCharMax()); + types.add(new SqlNVarCharMax()); + types.add(new SqlVarBinaryMax()); + // types.add(new SqlXml()); + + // 10.0 types + types.add(new SqlDate()); + types.add(new SqlDateTime2()); + types.add(new SqlTime()); + types.add(new SqlDateTimeOffset()); + } + return types; + } + + /** + * Wrapper Class for BinaryStream + * + */ + public static class DBBinaryStream extends ByteArrayInputStream { + byte[] data; + + // Constructor + public DBBinaryStream(byte[] value) { + super(value); + data = value; + } + + } + + /** + * Wrapper for CharacterStream + * + */ + public static class DBCharacterStream extends CharArrayReader { + String localValue; + + /** + * Constructor + * + * @param value + */ + public DBCharacterStream(String value) { + super(value.toCharArray()); + localValue = value; + } + + } + + /** + * Wrapper for NCharacterStream + */ + class DBNCharacterStream extends DBCharacterStream { + // Constructor + public DBNCharacterStream(String value) { + super(value); + } + } + + /** + * + * @return location of resource file + */ + public static String getCurrentClassPath() { + try { + String className = new Object() { + }.getClass().getEnclosingClass().getName(); + String location = Class.forName(className).getProtectionDomain().getCodeSource().getLocation().getPath() + "/"; + URI uri = new URI(location.toString()); + return uri.getPath(); + } + catch (Exception e) { + fail("Failed to get CSV file path. " + e.getMessage()); + } + return null; + } + + /** + * mimic "DROP TABLE IF EXISTS ..." for older versions of SQL Server + */ + public static void dropTableIfExists(String tableName, java.sql.Statement stmt) throws SQLException { + dropObjectIfExists(tableName, "IsTable", stmt); + } + + /** + * mimic "DROP PROCEDURE IF EXISTS ..." for older versions of SQL Server + */ + public static void dropProcedureIfExists(String procName, java.sql.Statement stmt) throws SQLException { + dropObjectIfExists(procName, "IsProcedure", stmt); + } + + /** + * actually perform the "DROP TABLE / PROCEDURE" + */ + private static void dropObjectIfExists(String objectName, String objectProperty, java.sql.Statement stmt) throws SQLException { + StringBuilder sb = new StringBuilder(); + if (!objectName.startsWith("[")) { sb.append("["); } + sb.append(objectName); + if (!objectName.endsWith("]")) { sb.append("]"); } + String bracketedObjectName = sb.toString(); + String sql = String.format( + "IF EXISTS " + + "( " + + "SELECT * from sys.objects " + + "WHERE object_id = OBJECT_ID(N'%s') AND OBJECTPROPERTY(object_id, N'%s') = 1 " + + ") " + + "DROP %s %s ", + bracketedObjectName, + objectProperty, + "IsProcedure".equals(objectProperty) ? "PROCEDURE" : "TABLE", + bracketedObjectName); + stmt.executeUpdate(sql); + } + + public static boolean parseByte(byte[] expectedData, + byte[] retrieved) { + assertTrue(Arrays.equals(expectedData, Arrays.copyOf(retrieved, expectedData.length)), " unexpected BINARY value, expected"); + for (int i = expectedData.length; i < retrieved.length; i++) { + assertTrue(0 == retrieved[i], "unexpected data BINARY"); + } + return true; + } + + public static boolean isJDBC43AndGreater(Connection connection) throws SQLException{ + return getJDBCVersion(connection) >= 4.3F; + } + + public static float getJDBCVersion(Connection connection) throws SQLException { + return Float.valueOf(connection.getMetaData().getJDBCMajorVersion() + "." + connection.getMetaData().getJDBCMinorVersion()); + } + +} From 3cd8e66f4cd44985d0f9256cafee504907ca8338 Mon Sep 17 00:00:00 2001 From: ulvii Date: Wed, 30 May 2018 08:52:55 -0700 Subject: [PATCH 08/17] Fix | Revert change to Utils.java --- src/test/java/com/microsoft/sqlserver/testframework/Utils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/microsoft/sqlserver/testframework/Utils.java b/src/test/java/com/microsoft/sqlserver/testframework/Utils.java index 5ae35122d..d4aedd710 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/Utils.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/Utils.java @@ -324,4 +324,4 @@ public static float getJDBCVersion(Connection connection) throws SQLException { return Float.valueOf(connection.getMetaData().getJDBCMajorVersion() + "." + connection.getMetaData().getJDBCMinorVersion()); } -} +} \ No newline at end of file From a76d8b191174ef3255fcd6f0020bb7d3dea65493 Mon Sep 17 00:00:00 2001 From: ulvii Date: Wed, 30 May 2018 08:57:13 -0700 Subject: [PATCH 09/17] Fix | Fix Utils.java --- src/test/java/com/microsoft/sqlserver/testframework/Utils.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/testframework/Utils.java b/src/test/java/com/microsoft/sqlserver/testframework/Utils.java index d4aedd710..d6c13341b 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/Utils.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/Utils.java @@ -323,5 +323,4 @@ public static boolean isJDBC43AndGreater(Connection connection) throws SQLExcept public static float getJDBCVersion(Connection connection) throws SQLException { return Float.valueOf(connection.getMetaData().getJDBCMajorVersion() + "." + connection.getMetaData().getJDBCMinorVersion()); } - -} \ No newline at end of file +} From c48e849b9b3a5f3118c57e258d89a0735a2c33de Mon Sep 17 00:00:00 2001 From: ulvii Date: Wed, 30 May 2018 09:01:06 -0700 Subject: [PATCH 10/17] Fix | Overwrite Utils from upstream --- .../sqlserver/testframework/Utils.java | 644 +++++++++--------- 1 file changed, 318 insertions(+), 326 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/testframework/Utils.java b/src/test/java/com/microsoft/sqlserver/testframework/Utils.java index d6c13341b..229f7f4c6 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/Utils.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/Utils.java @@ -1,326 +1,318 @@ -/* - * Microsoft JDBC Driver for SQL Server - * - * Copyright(c) Microsoft Corporation All rights reserved. - * - * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. - */ - -package com.microsoft.sqlserver.testframework; - -import static org.junit.Assert.fail; -import static org.junit.jupiter.api.Assertions.assertTrue; - -import java.io.ByteArrayInputStream; -import java.io.CharArrayReader; -import java.net.URI; -import java.sql.Connection; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.microsoft.sqlserver.testframework.sqlType.SqlBigInt; -import com.microsoft.sqlserver.testframework.sqlType.SqlBinary; -import com.microsoft.sqlserver.testframework.sqlType.SqlBit; -import com.microsoft.sqlserver.testframework.sqlType.SqlChar; -import com.microsoft.sqlserver.testframework.sqlType.SqlDate; -import com.microsoft.sqlserver.testframework.sqlType.SqlDateTime; -import com.microsoft.sqlserver.testframework.sqlType.SqlDateTime2; -import com.microsoft.sqlserver.testframework.sqlType.SqlDateTimeOffset; -import com.microsoft.sqlserver.testframework.sqlType.SqlDecimal; -import com.microsoft.sqlserver.testframework.sqlType.SqlFloat; -import com.microsoft.sqlserver.testframework.sqlType.SqlInt; -import com.microsoft.sqlserver.testframework.sqlType.SqlMoney; -import com.microsoft.sqlserver.testframework.sqlType.SqlNChar; -import com.microsoft.sqlserver.testframework.sqlType.SqlNVarChar; -import com.microsoft.sqlserver.testframework.sqlType.SqlNVarCharMax; -import com.microsoft.sqlserver.testframework.sqlType.SqlNumeric; -import com.microsoft.sqlserver.testframework.sqlType.SqlReal; -import com.microsoft.sqlserver.testframework.sqlType.SqlSmallDateTime; -import com.microsoft.sqlserver.testframework.sqlType.SqlSmallInt; -import com.microsoft.sqlserver.testframework.sqlType.SqlSmallMoney; -import com.microsoft.sqlserver.testframework.sqlType.SqlTime; -import com.microsoft.sqlserver.testframework.sqlType.SqlTinyInt; -import com.microsoft.sqlserver.testframework.sqlType.SqlType; -import com.microsoft.sqlserver.testframework.sqlType.SqlVarBinary; -import com.microsoft.sqlserver.testframework.sqlType.SqlVarBinaryMax; -import com.microsoft.sqlserver.testframework.sqlType.SqlVarChar; -import com.microsoft.sqlserver.testframework.sqlType.SqlVarCharMax; - -/** - * Generic Utility class which we can access by test classes. - * - * @since 6.1.2 - */ -public class Utils { - public static final Logger log = Logger.getLogger("Utils"); - - // 'SQL' represents SQL Server, while 'SQLAzure' represents SQL Azure. - public static final String SERVER_TYPE_SQL_SERVER = "SQL"; - public static final String SERVER_TYPE_SQL_AZURE = "SQLAzure"; - // private static SqlType types = null; - private static ArrayList types = null; - - /** - * Returns serverType - * - * @return - */ - public static String getServerType() { - String serverType = null; - - String serverTypeProperty = getConfiguredProperty("server.type"); - if (null == serverTypeProperty) { - // default to SQL Server - serverType = SERVER_TYPE_SQL_SERVER; - } - else if (serverTypeProperty.equalsIgnoreCase(SERVER_TYPE_SQL_AZURE)) { - serverType = SERVER_TYPE_SQL_AZURE; - } - else if (serverTypeProperty.equalsIgnoreCase(SERVER_TYPE_SQL_SERVER)) { - serverType = SERVER_TYPE_SQL_SERVER; - } - else { - if (log.isLoggable(Level.FINE)) { - log.fine("Server.type '" + serverTypeProperty + "' is not supported yet. Default to SQL Server"); - } - serverType = SERVER_TYPE_SQL_SERVER; - } - return serverType; - } - - /** - * Read variable from property files if found null try to read from env. - * - * @param key - * @return Value - */ - public static String getConfiguredProperty(String key) { - String value = System.getProperty(key); - - if (value == null) { - value = System.getenv(key); - } - - return value; - } - - /** - * Convenient method for {@link #getConfiguredProperty(String)} - * - * @param key - * @return Value - */ - public static String getConfiguredProperty(String key, - String defaultValue) { - String value = getConfiguredProperty(key); - - if (value == null) { - value = defaultValue; - } - - return value; - } - - /** - * - * @param javatype - * @return - */ - public static SqlType find(Class javatype) { - if (null != types) { - types(); - for (SqlType type : types) { - if (type.getType() == javatype) - return type; - } - } - return null; - } - - /** - * - * @param name - * @return - */ - public static SqlType find(String name) { - if (null == types) - types(); - if (null != types) { - for (SqlType type : types) { - if (type.getName().equalsIgnoreCase(name)) - return type; - } - } - return null; - } - - /** - * - * @return - */ - public static ArrayList types() { - if (null == types) { - types = new ArrayList<>(); - - types.add(new SqlInt()); - types.add(new SqlSmallInt()); - types.add(new SqlTinyInt()); - types.add(new SqlBit()); - types.add(new SqlDateTime()); - types.add(new SqlSmallDateTime()); - types.add(new SqlDecimal()); - types.add(new SqlNumeric()); - types.add(new SqlReal()); - types.add(new SqlFloat()); - types.add(new SqlMoney()); - types.add(new SqlSmallMoney()); - types.add(new SqlVarChar()); - types.add(new SqlChar()); - // types.add(new SqlText()); - types.add(new SqlBinary()); - types.add(new SqlVarBinary()); - // types.add(new SqlImage()); - // types.add(new SqlTimestamp()); - - types.add(new SqlNVarChar()); - types.add(new SqlNChar()); - // types.add(new SqlNText()); - // types.add(new SqlGuid()); - - types.add(new SqlBigInt()); - // types.add(new SqlVariant(this)); - - // 9.0 types - types.add(new SqlVarCharMax()); - types.add(new SqlNVarCharMax()); - types.add(new SqlVarBinaryMax()); - // types.add(new SqlXml()); - - // 10.0 types - types.add(new SqlDate()); - types.add(new SqlDateTime2()); - types.add(new SqlTime()); - types.add(new SqlDateTimeOffset()); - } - return types; - } - - /** - * Wrapper Class for BinaryStream - * - */ - public static class DBBinaryStream extends ByteArrayInputStream { - byte[] data; - - // Constructor - public DBBinaryStream(byte[] value) { - super(value); - data = value; - } - - } - - /** - * Wrapper for CharacterStream - * - */ - public static class DBCharacterStream extends CharArrayReader { - String localValue; - - /** - * Constructor - * - * @param value - */ - public DBCharacterStream(String value) { - super(value.toCharArray()); - localValue = value; - } - - } - - /** - * Wrapper for NCharacterStream - */ - class DBNCharacterStream extends DBCharacterStream { - // Constructor - public DBNCharacterStream(String value) { - super(value); - } - } - - /** - * - * @return location of resource file - */ - public static String getCurrentClassPath() { - try { - String className = new Object() { - }.getClass().getEnclosingClass().getName(); - String location = Class.forName(className).getProtectionDomain().getCodeSource().getLocation().getPath() + "/"; - URI uri = new URI(location.toString()); - return uri.getPath(); - } - catch (Exception e) { - fail("Failed to get CSV file path. " + e.getMessage()); - } - return null; - } - - /** - * mimic "DROP TABLE IF EXISTS ..." for older versions of SQL Server - */ - public static void dropTableIfExists(String tableName, java.sql.Statement stmt) throws SQLException { - dropObjectIfExists(tableName, "IsTable", stmt); - } - - /** - * mimic "DROP PROCEDURE IF EXISTS ..." for older versions of SQL Server - */ - public static void dropProcedureIfExists(String procName, java.sql.Statement stmt) throws SQLException { - dropObjectIfExists(procName, "IsProcedure", stmt); - } - - /** - * actually perform the "DROP TABLE / PROCEDURE" - */ - private static void dropObjectIfExists(String objectName, String objectProperty, java.sql.Statement stmt) throws SQLException { - StringBuilder sb = new StringBuilder(); - if (!objectName.startsWith("[")) { sb.append("["); } - sb.append(objectName); - if (!objectName.endsWith("]")) { sb.append("]"); } - String bracketedObjectName = sb.toString(); - String sql = String.format( - "IF EXISTS " + - "( " + - "SELECT * from sys.objects " + - "WHERE object_id = OBJECT_ID(N'%s') AND OBJECTPROPERTY(object_id, N'%s') = 1 " + - ") " + - "DROP %s %s ", - bracketedObjectName, - objectProperty, - "IsProcedure".equals(objectProperty) ? "PROCEDURE" : "TABLE", - bracketedObjectName); - stmt.executeUpdate(sql); - } - - public static boolean parseByte(byte[] expectedData, - byte[] retrieved) { - assertTrue(Arrays.equals(expectedData, Arrays.copyOf(retrieved, expectedData.length)), " unexpected BINARY value, expected"); - for (int i = expectedData.length; i < retrieved.length; i++) { - assertTrue(0 == retrieved[i], "unexpected data BINARY"); - } - return true; - } - - public static boolean isJDBC43AndGreater(Connection connection) throws SQLException{ - return getJDBCVersion(connection) >= 4.3F; - } - - public static float getJDBCVersion(Connection connection) throws SQLException { - return Float.valueOf(connection.getMetaData().getJDBCMajorVersion() + "." + connection.getMetaData().getJDBCMinorVersion()); - } -} +/* + * Microsoft JDBC Driver for SQL Server + * + * Copyright(c) Microsoft Corporation All rights reserved. + * + * This program is made available under the terms of the MIT License. See the LICENSE file in the project root for more information. + */ + +package com.microsoft.sqlserver.testframework; + +import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.ByteArrayInputStream; +import java.io.CharArrayReader; +import java.net.URI; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.microsoft.sqlserver.testframework.sqlType.SqlBigInt; +import com.microsoft.sqlserver.testframework.sqlType.SqlBinary; +import com.microsoft.sqlserver.testframework.sqlType.SqlBit; +import com.microsoft.sqlserver.testframework.sqlType.SqlChar; +import com.microsoft.sqlserver.testframework.sqlType.SqlDate; +import com.microsoft.sqlserver.testframework.sqlType.SqlDateTime; +import com.microsoft.sqlserver.testframework.sqlType.SqlDateTime2; +import com.microsoft.sqlserver.testframework.sqlType.SqlDateTimeOffset; +import com.microsoft.sqlserver.testframework.sqlType.SqlDecimal; +import com.microsoft.sqlserver.testframework.sqlType.SqlFloat; +import com.microsoft.sqlserver.testframework.sqlType.SqlInt; +import com.microsoft.sqlserver.testframework.sqlType.SqlMoney; +import com.microsoft.sqlserver.testframework.sqlType.SqlNChar; +import com.microsoft.sqlserver.testframework.sqlType.SqlNVarChar; +import com.microsoft.sqlserver.testframework.sqlType.SqlNVarCharMax; +import com.microsoft.sqlserver.testframework.sqlType.SqlNumeric; +import com.microsoft.sqlserver.testframework.sqlType.SqlReal; +import com.microsoft.sqlserver.testframework.sqlType.SqlSmallDateTime; +import com.microsoft.sqlserver.testframework.sqlType.SqlSmallInt; +import com.microsoft.sqlserver.testframework.sqlType.SqlSmallMoney; +import com.microsoft.sqlserver.testframework.sqlType.SqlTime; +import com.microsoft.sqlserver.testframework.sqlType.SqlTinyInt; +import com.microsoft.sqlserver.testframework.sqlType.SqlType; +import com.microsoft.sqlserver.testframework.sqlType.SqlVarBinary; +import com.microsoft.sqlserver.testframework.sqlType.SqlVarBinaryMax; +import com.microsoft.sqlserver.testframework.sqlType.SqlVarChar; +import com.microsoft.sqlserver.testframework.sqlType.SqlVarCharMax; + +/** + * Generic Utility class which we can access by test classes. + * + * @since 6.1.2 + */ +public class Utils { + public static final Logger log = Logger.getLogger("Utils"); + + // 'SQL' represents SQL Server, while 'SQLAzure' represents SQL Azure. + public static final String SERVER_TYPE_SQL_SERVER = "SQL"; + public static final String SERVER_TYPE_SQL_AZURE = "SQLAzure"; + // private static SqlType types = null; + private static ArrayList types = null; + + /** + * Returns serverType + * + * @return + */ + public static String getServerType() { + String serverType = null; + + String serverTypeProperty = getConfiguredProperty("server.type"); + if (null == serverTypeProperty) { + // default to SQL Server + serverType = SERVER_TYPE_SQL_SERVER; + } + else if (serverTypeProperty.equalsIgnoreCase(SERVER_TYPE_SQL_AZURE)) { + serverType = SERVER_TYPE_SQL_AZURE; + } + else if (serverTypeProperty.equalsIgnoreCase(SERVER_TYPE_SQL_SERVER)) { + serverType = SERVER_TYPE_SQL_SERVER; + } + else { + if (log.isLoggable(Level.FINE)) { + log.fine("Server.type '" + serverTypeProperty + "' is not supported yet. Default to SQL Server"); + } + serverType = SERVER_TYPE_SQL_SERVER; + } + return serverType; + } + + /** + * Read variable from property files if found null try to read from env. + * + * @param key + * @return Value + */ + public static String getConfiguredProperty(String key) { + String value = System.getProperty(key); + + if (value == null) { + value = System.getenv(key); + } + + return value; + } + + /** + * Convenient method for {@link #getConfiguredProperty(String)} + * + * @param key + * @return Value + */ + public static String getConfiguredProperty(String key, + String defaultValue) { + String value = getConfiguredProperty(key); + + if (value == null) { + value = defaultValue; + } + + return value; + } + + /** + * + * @param javatype + * @return + */ + public static SqlType find(Class javatype) { + if (null != types) { + types(); + for (SqlType type : types) { + if (type.getType() == javatype) + return type; + } + } + return null; + } + + /** + * + * @param name + * @return + */ + public static SqlType find(String name) { + if (null == types) + types(); + if (null != types) { + for (SqlType type : types) { + if (type.getName().equalsIgnoreCase(name)) + return type; + } + } + return null; + } + + /** + * + * @return + */ + public static ArrayList types() { + if (null == types) { + types = new ArrayList<>(); + + types.add(new SqlInt()); + types.add(new SqlSmallInt()); + types.add(new SqlTinyInt()); + types.add(new SqlBit()); + types.add(new SqlDateTime()); + types.add(new SqlSmallDateTime()); + types.add(new SqlDecimal()); + types.add(new SqlNumeric()); + types.add(new SqlReal()); + types.add(new SqlFloat()); + types.add(new SqlMoney()); + types.add(new SqlSmallMoney()); + types.add(new SqlVarChar()); + types.add(new SqlChar()); + // types.add(new SqlText()); + types.add(new SqlBinary()); + types.add(new SqlVarBinary()); + // types.add(new SqlImage()); + // types.add(new SqlTimestamp()); + + types.add(new SqlNVarChar()); + types.add(new SqlNChar()); + // types.add(new SqlNText()); + // types.add(new SqlGuid()); + + types.add(new SqlBigInt()); + // types.add(new SqlVariant(this)); + + // 9.0 types + types.add(new SqlVarCharMax()); + types.add(new SqlNVarCharMax()); + types.add(new SqlVarBinaryMax()); + // types.add(new SqlXml()); + + // 10.0 types + types.add(new SqlDate()); + types.add(new SqlDateTime2()); + types.add(new SqlTime()); + types.add(new SqlDateTimeOffset()); + } + return types; + } + + /** + * Wrapper Class for BinaryStream + * + */ + public static class DBBinaryStream extends ByteArrayInputStream { + byte[] data; + + // Constructor + public DBBinaryStream(byte[] value) { + super(value); + data = value; + } + + } + + /** + * Wrapper for CharacterStream + * + */ + public static class DBCharacterStream extends CharArrayReader { + String localValue; + + /** + * Constructor + * + * @param value + */ + public DBCharacterStream(String value) { + super(value.toCharArray()); + localValue = value; + } + + } + + /** + * Wrapper for NCharacterStream + */ + class DBNCharacterStream extends DBCharacterStream { + // Constructor + public DBNCharacterStream(String value) { + super(value); + } + } + + /** + * + * @return location of resource file + */ + public static String getCurrentClassPath() { + try { + String className = new Object() { + }.getClass().getEnclosingClass().getName(); + String location = Class.forName(className).getProtectionDomain().getCodeSource().getLocation().getPath() + "/"; + URI uri = new URI(location.toString()); + return uri.getPath(); + } + catch (Exception e) { + fail("Failed to get CSV file path. " + e.getMessage()); + } + return null; + } + + /** + * mimic "DROP TABLE IF EXISTS ..." for older versions of SQL Server + */ + public static void dropTableIfExists(String tableName, java.sql.Statement stmt) throws SQLException { + dropObjectIfExists(tableName, "IsTable", stmt); + } + + /** + * mimic "DROP PROCEDURE IF EXISTS ..." for older versions of SQL Server + */ + public static void dropProcedureIfExists(String procName, java.sql.Statement stmt) throws SQLException { + dropObjectIfExists(procName, "IsProcedure", stmt); + } + + /** + * actually perform the "DROP TABLE / PROCEDURE" + */ + private static void dropObjectIfExists(String objectName, String objectProperty, java.sql.Statement stmt) throws SQLException { + StringBuilder sb = new StringBuilder(); + if (!objectName.startsWith("[")) { sb.append("["); } + sb.append(objectName); + if (!objectName.endsWith("]")) { sb.append("]"); } + String bracketedObjectName = sb.toString(); + String sql = String.format( + "IF EXISTS " + + "( " + + "SELECT * from sys.objects " + + "WHERE object_id = OBJECT_ID(N'%s') AND OBJECTPROPERTY(object_id, N'%s') = 1 " + + ") " + + "DROP %s %s ", + bracketedObjectName, + objectProperty, + "IsProcedure".equals(objectProperty) ? "PROCEDURE" : "TABLE", + bracketedObjectName); + stmt.executeUpdate(sql); + } + + public static boolean parseByte(byte[] expectedData, + byte[] retrieved) { + assertTrue(Arrays.equals(expectedData, Arrays.copyOf(retrieved, expectedData.length)), " unexpected BINARY value, expected"); + for (int i = expectedData.length; i < retrieved.length; i++) { + assertTrue(0 == retrieved[i], "unexpected data BINARY"); + } + return true; + } + +} \ No newline at end of file From 7d695c95c6c45786d5509374d92906386ad32803 Mon Sep 17 00:00:00 2001 From: ulvii Date: Wed, 30 May 2018 09:03:21 -0700 Subject: [PATCH 11/17] Fix | Add the new methods back to Utils.java --- .../com/microsoft/sqlserver/testframework/Utils.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/microsoft/sqlserver/testframework/Utils.java b/src/test/java/com/microsoft/sqlserver/testframework/Utils.java index 229f7f4c6..a9c537d7b 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/Utils.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/Utils.java @@ -14,6 +14,7 @@ import java.io.ByteArrayInputStream; import java.io.CharArrayReader; import java.net.URI; +import java.sql.Connection; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; @@ -315,4 +316,11 @@ public static boolean parseByte(byte[] expectedData, return true; } -} \ No newline at end of file + public static boolean isJDBC43AndGreater(Connection connection) throws SQLException{ + return getJDBCVersion(connection) >= 4.3F; + } + + public static float getJDBCVersion(Connection connection) throws SQLException { + return Float.valueOf(connection.getMetaData().getJDBCMajorVersion() + "." + connection.getMetaData().getJDBCMinorVersion()); + } +} From a93642e0b4af279ff2516bc9090544ca7aac8945 Mon Sep 17 00:00:00 2001 From: ulvii Date: Wed, 30 May 2018 09:20:01 -0700 Subject: [PATCH 12/17] Fix | Add new line to the end of RequestBoundaryMethodsTest.java --- .../jdbc/requestboundary/RequestBoundaryMethodsTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java index 47c0989e4..a88388b10 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java @@ -418,4 +418,4 @@ private void compareValuesAgainstConnection(SQLServerConnection con, private void generateWarning(SQLServerConnection con) throws SQLException { con.setClientInfo("name", "value"); } -} \ No newline at end of file +} From 9b3c32eab08c4de884a1dd0bba68de0dc6f62119 Mon Sep 17 00:00:00 2001 From: ulvii Date: Tue, 5 Jun 2018 16:26:08 -0700 Subject: [PATCH 13/17] Fix | Applying review commits --- .../sqlserver/jdbc/SQLCollation.java | 22 +-- .../sqlserver/jdbc/SQLServerConnection.java | 28 +--- .../sqlserver/jdbc/SQLServerStatement.java | 5 +- .../RequestBoundaryMethodsTest.java | 140 +++++++++--------- .../sqlserver/testframework/Utils.java | 7 +- 5 files changed, 85 insertions(+), 117 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLCollation.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLCollation.java index d89a95e11..46f08e9c2 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLCollation.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLCollation.java @@ -47,24 +47,6 @@ final class SQLCollation implements java.io.Serializable static final int tdsLength() { return 5; } // Length of collation in TDS (in bytes) - /** - * Returns the collation info - * - * @return - */ - int getCollationInfo() { - return this.info; - } - - /** - * return sort ID - * - * @return - */ - int getCollationSortID() { - return this.sortId; - } - /** * Reads TDS collation from TDS buffer into SQLCollation class. * @param tdsReader @@ -530,11 +512,11 @@ private Encoding encodingFromSortId() throws UnsupportedEncodingException { static { // Populate the windows locale and sort order indices - localeIndex = new HashMap<>(); + localeIndex = new HashMap(); for (WindowsLocale locale : EnumSet.allOf(WindowsLocale.class)) localeIndex.put(locale.langID, locale); - sortOrderIndex = new HashMap<>(); + sortOrderIndex = new HashMap(); for (SortOrder sortOrder : EnumSet.allOf(SortOrder.class)) sortOrderIndex.put(sortOrder.sortId, sortOrder); } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index f2490cc5a..751a01cf3 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -36,10 +36,10 @@ import java.sql.Statement; import java.sql.Struct; import java.text.MessageFormat; -import java.util.ArrayList; import java.util.Arrays; import java.util.Enumeration; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; @@ -5332,7 +5332,6 @@ public T unwrap(Class iface) throws SQLException { private List openStatements = null; protected void beginRequestInternal() throws SQLException { - DriverJDBCVersion.checkSupportsJDBC43(); synchronized (this) { if (!requestStarted) { originalDatabaseAutoCommitMode = databaseAutoCommitMode; @@ -5342,18 +5341,17 @@ protected void beginRequestInternal() throws SQLException { originalSendTimeAsDatetime = sendTimeAsDatetime; originalStatementPoolingCacheSize = statementPoolingCacheSize; originalDisableStatementPooling = disableStatementPooling; - originalServerPreparedStatementDiscardThreshold = serverPreparedStatementDiscardThreshold; - originalEnablePrepareOnFirstPreparedStatementCall = enablePrepareOnFirstPreparedStatementCall; + originalServerPreparedStatementDiscardThreshold = getServerPreparedStatementDiscardThreshold(); + originalEnablePrepareOnFirstPreparedStatementCall = getEnablePrepareOnFirstPreparedStatementCall(); originalSCatalog = sCatalog; originalSqlWarnings = sqlWarnings; - openStatements = new ArrayList(); + openStatements = new LinkedList(); requestStarted = true; } } } protected void endRequestInternal() throws SQLException { - DriverJDBCVersion.checkSupportsJDBC43(); synchronized (this) { if (requestStarted) { if (!databaseAutoCommitMode) { @@ -5380,21 +5378,11 @@ protected void endRequestInternal() throws SQLException { if (disableStatementPooling != originalDisableStatementPooling) { setDisableStatementPooling(originalDisableStatementPooling); } - if (serverPreparedStatementDiscardThreshold != originalServerPreparedStatementDiscardThreshold) { - if (0 > originalServerPreparedStatementDiscardThreshold) { - setServerPreparedStatementDiscardThreshold(DEFAULT_SERVER_PREPARED_STATEMENT_DISCARD_THRESHOLD); - } - else { - setServerPreparedStatementDiscardThreshold(originalServerPreparedStatementDiscardThreshold); - } + if (getServerPreparedStatementDiscardThreshold() != originalServerPreparedStatementDiscardThreshold) { + setServerPreparedStatementDiscardThreshold(originalServerPreparedStatementDiscardThreshold); } - if (enablePrepareOnFirstPreparedStatementCall != originalEnablePrepareOnFirstPreparedStatementCall) { - if (null == originalEnablePrepareOnFirstPreparedStatementCall) { - setEnablePrepareOnFirstPreparedStatementCall(DEFAULT_ENABLE_PREPARE_ON_FIRST_PREPARED_STATEMENT_CALL); - } - else { - setEnablePrepareOnFirstPreparedStatementCall(originalEnablePrepareOnFirstPreparedStatementCall); - } + if (getEnablePrepareOnFirstPreparedStatementCall() != originalEnablePrepareOnFirstPreparedStatementCall) { + setEnablePrepareOnFirstPreparedStatementCall(originalEnablePrepareOnFirstPreparedStatementCall); } if (!sCatalog.equals(originalSCatalog)) { setCatalog(originalSCatalog); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java index c7066f29c..162120ae9 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java @@ -631,15 +631,14 @@ void closeInternal() { // Regardless what happens when cleaning up, // the statement is considered closed. assert !bIsClosed; - - connection.removeOpenStatement(this); - discardLastExecutionResults(); bIsClosed = true; autoGeneratedKeys = null; sqlWarnings = null; inOutParam = null; + + connection.removeOpenStatement(this); } public void close() throws SQLServerException { diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java index a88388b10..665ac3120 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java @@ -10,6 +10,7 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import java.util.concurrent.CountDownLatch; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; @@ -27,11 +28,6 @@ */ @RunWith(JUnitPlatform.class) public class RequestBoundaryMethodsTest extends AbstractTest { - SQLServerConnection con = null; - Statement stmt = null; - PreparedStatement pstmt = null; - CallableStatement cstmt = null; - ResultSet rs = null; /** * Tests Request Boundary methods with SQLServerConnection properties that are modifiable through public APIs. @@ -51,7 +47,7 @@ public void testModifiableConnectionProperties() throws SQLException { boolean disableStatementPooling1 = true; int serverPreparedStatementDiscardThreshold1 = 10; boolean enablePrepareOnFirstPreparedStatementCall1 = false; - String sCatalog1 = "model"; + String sCatalog1 = "master"; boolean autoCommitMode2 = false; int transactionIsolationLevel2 = SQLServerConnection.TRANSACTION_SERIALIZABLE; @@ -62,12 +58,13 @@ public void testModifiableConnectionProperties() throws SQLException { boolean disableStatementPooling2 = false; int serverPreparedStatementDiscardThreshold2 = 100; boolean enablePrepareOnFirstPreparedStatementCall2 = true; - String sCatalog2 = "tempdb"; + String sCatalog2 = RandomUtil.getIdentifier("RequestBoundaryDatabase"); - try { - con = connect(); + try (SQLServerConnection con = connect()) { + if (Utils.isJDBC43OrGreater(con)) { + // Second database + con.createStatement().executeUpdate("CREATE DATABASE [" + sCatalog2 + "]"); - if (Utils.isJDBC43AndGreater(con)) { // First set of values. setConnectionFields(con, autoCommitMode1, transactionIsolationLevel1, networkTimeout1, holdability1, sendTimeAsDatetime1, statementPoolingCacheSize1, disableStatementPooling1, serverPreparedStatementDiscardThreshold1, @@ -113,9 +110,9 @@ public void testModifiableConnectionProperties() throws SQLException { } } finally { - if (null != con) { - con.close(); - } + SQLServerConnection conDrop = connect(); + Utils.dropDatabaseIfExists(sCatalog2, conDrop.createStatement()); + conDrop.close(); } } @@ -126,10 +123,8 @@ public void testModifiableConnectionProperties() throws SQLException { */ @Test public void testWarnings() throws SQLException { - try { - con = connect(); - - if (Utils.isJDBC43AndGreater(con)) { + try (SQLServerConnection con = connect()) { + if (Utils.isJDBC43OrGreater(con)) { con.beginRequest(); generateWarning(con); assertNotNull(con.getWarnings()); @@ -148,11 +143,6 @@ public void testWarnings() throws SQLException { assertNull(con.getWarnings()); } } - finally { - if (null != con) { - con.close(); - } - } } /** @@ -162,14 +152,12 @@ public void testWarnings() throws SQLException { */ @Test public void testOpenTransactions() throws SQLException { + ResultSet rs = null; String tableName = null; - try { - con = connect(); - - if (Utils.isJDBC43AndGreater(con)) { - stmt = con.createStatement(); - tableName = AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("RequestBoundary")); + try (SQLServerConnection con = connect(); Statement stmt = con.createStatement();) { + if (Utils.isJDBC43OrGreater(con)) { + tableName = AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("RequestBoundaryTable")); Utils.dropTableIfExists(tableName, stmt); stmt.executeUpdate("CREATE TABLE " + tableName + " (col int)"); con.beginRequest(); @@ -184,13 +172,9 @@ public void testOpenTransactions() throws SQLException { } } finally { - if (null != stmt) { - Utils.dropTableIfExists(tableName, stmt); - stmt.close(); - } - if (null != con) { - con.close(); - } + SQLServerConnection conDrop = connect(); + Utils.dropTableIfExists(tableName, conDrop.createStatement()); + conDrop.close(); } } @@ -199,17 +183,19 @@ public void testOpenTransactions() throws SQLException { * * @throws SQLException */ + @SuppressWarnings("resource") @Test public void testStatements() throws SQLException { + Statement stmt = null; + ResultSet rs = null; Statement stmt1 = null; PreparedStatement ps = null; CallableStatement cs = null; ResultSet rs1 = null; + String tableName = null; - try { - con = connect(); - - if (Utils.isJDBC43AndGreater(con)) { + try (SQLServerConnection con = connect();) { + if (Utils.isJDBC43OrGreater(con)) { stmt1 = con.createStatement(); con.beginRequest(); stmt = con.createStatement(); @@ -226,7 +212,7 @@ public void testStatements() throws SQLException { // Multiple statements inside beginRequest()/endRequest() block con.beginRequest(); stmt = con.createStatement(); - String tableName = AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("RequestBoundary")); + tableName = AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("RequestBoundary")); Utils.dropTableIfExists(tableName, stmt); stmt.executeUpdate("CREATE TABLE " + tableName + " (col int)"); ps = con.prepareStatement("INSERT INTO " + tableName + " values (?)"); @@ -263,9 +249,6 @@ public void testStatements() throws SQLException { if (null != cs) { cs.close(); } - if (null != con) { - con.close(); - } } } @@ -276,17 +259,27 @@ public void testStatements() throws SQLException { */ @Test public void testThreads() throws SQLException { + class Variables { + volatile SQLServerConnection con = null; + volatile Statement stmt = null; + volatile PreparedStatement pstmt = null; + } + + final Variables sharedVariables = new Variables(); + final CountDownLatch latch = new CountDownLatch(3); try { - con = connect(); - if (Utils.isJDBC43AndGreater(con)) { + sharedVariables.con = connect(); + if (Utils.isJDBC43OrGreater(sharedVariables.con)) { Thread thread1 = new Thread() { public void run() { try { - con.setNetworkTimeout(null, 100); - con.setHoldability(ResultSet.CLOSE_CURSORS_AT_COMMIT); + sharedVariables.con.setNetworkTimeout(null, 100); + sharedVariables.con.setHoldability(ResultSet.CLOSE_CURSORS_AT_COMMIT); + latch.countDown(); } catch (SQLException e) { e.printStackTrace(); + Thread.currentThread().interrupt(); } } }; @@ -294,13 +287,15 @@ public void run() { Thread thread2 = new Thread() { public void run() { try { - stmt = con.createStatement(); - ResultSet rs = stmt.executeQuery("SELECT 1"); + sharedVariables.stmt = sharedVariables.con.createStatement(); + ResultSet rs = sharedVariables.stmt.executeQuery("SELECT 1"); rs.next(); assertEquals(1, rs.getInt(1)); + latch.countDown(); } catch (SQLException e) { e.printStackTrace(); + Thread.currentThread().interrupt(); } } }; @@ -308,49 +303,48 @@ public void run() { Thread thread3 = new Thread() { public void run() { try { - pstmt = con.prepareStatement("SELECT 1"); - ResultSet rs = pstmt.executeQuery(); + sharedVariables.pstmt = sharedVariables.con.prepareStatement("SELECT 1"); + ResultSet rs = sharedVariables.pstmt.executeQuery(); rs.next(); assertEquals(1, rs.getInt(1)); + latch.countDown(); } catch (SQLException e) { e.printStackTrace(); + Thread.currentThread().interrupt(); } } }; - int originalNetworkTimeout = con.getNetworkTimeout(); - int originalHoldability = con.getHoldability(); - con.beginRequest(); + int originalNetworkTimeout = sharedVariables.con.getNetworkTimeout(); + int originalHoldability = sharedVariables.con.getHoldability(); + sharedVariables.con.beginRequest(); thread1.start(); thread2.start(); thread3.start(); - try { - // Wait for threads to complete - Thread.sleep(3000); - } - catch (InterruptedException e) { - Thread.currentThread().interrupt(); - e.printStackTrace(); - } - con.endRequest(); + latch.await(); + sharedVariables.con.endRequest(); - assertEquals(originalNetworkTimeout, con.getNetworkTimeout()); - assertEquals(originalHoldability, con.getHoldability()); - assertTrue(stmt.isClosed()); - assertTrue(pstmt.isClosed()); + assertEquals(originalNetworkTimeout, sharedVariables.con.getNetworkTimeout()); + assertEquals(originalHoldability, sharedVariables.con.getHoldability()); + assertTrue(sharedVariables.stmt.isClosed()); + assertTrue(sharedVariables.pstmt.isClosed()); } } + catch (InterruptedException e) { + e.printStackTrace(); + Thread.currentThread().interrupt(); + } finally { - if (null != stmt) { - stmt.close(); + if (null != sharedVariables.stmt) { + sharedVariables.stmt.close(); } - if (null != pstmt) { - pstmt.close(); + if (null != sharedVariables.pstmt) { + sharedVariables.pstmt.close(); } - if (null != con) { - con.close(); + if (null != sharedVariables.con) { + sharedVariables.con.close(); } } } diff --git a/src/test/java/com/microsoft/sqlserver/testframework/Utils.java b/src/test/java/com/microsoft/sqlserver/testframework/Utils.java index a9c537d7b..cf761bbb1 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/Utils.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/Utils.java @@ -284,6 +284,11 @@ public static void dropProcedureIfExists(String procName, java.sql.Statement stm dropObjectIfExists(procName, "IsProcedure", stmt); } + public static void dropDatabaseIfExists(String databaseName, + java.sql.Statement stmt) throws SQLException { + stmt.executeUpdate("IF EXISTS(SELECT * from sys.databases WHERE name='" + databaseName + "') DROP DATABASE [" + databaseName + "]"); + } + /** * actually perform the "DROP TABLE / PROCEDURE" */ @@ -316,7 +321,7 @@ public static boolean parseByte(byte[] expectedData, return true; } - public static boolean isJDBC43AndGreater(Connection connection) throws SQLException{ + public static boolean isJDBC43OrGreater(Connection connection) throws SQLException{ return getJDBCVersion(connection) >= 4.3F; } From a7b76b82f8d3f3a48d1f4014921d50c474e2d131 Mon Sep 17 00:00:00 2001 From: ulvii Date: Tue, 5 Jun 2018 16:28:50 -0700 Subject: [PATCH 14/17] Fix | Revert changes to the SQLCollation --- .../sqlserver/jdbc/SQLCollation.java | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLCollation.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLCollation.java index 46f08e9c2..d89a95e11 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLCollation.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLCollation.java @@ -47,6 +47,24 @@ final class SQLCollation implements java.io.Serializable static final int tdsLength() { return 5; } // Length of collation in TDS (in bytes) + /** + * Returns the collation info + * + * @return + */ + int getCollationInfo() { + return this.info; + } + + /** + * return sort ID + * + * @return + */ + int getCollationSortID() { + return this.sortId; + } + /** * Reads TDS collation from TDS buffer into SQLCollation class. * @param tdsReader @@ -512,11 +530,11 @@ private Encoding encodingFromSortId() throws UnsupportedEncodingException { static { // Populate the windows locale and sort order indices - localeIndex = new HashMap(); + localeIndex = new HashMap<>(); for (WindowsLocale locale : EnumSet.allOf(WindowsLocale.class)) localeIndex.put(locale.langID, locale); - sortOrderIndex = new HashMap(); + sortOrderIndex = new HashMap<>(); for (SortOrder sortOrder : EnumSet.allOf(SortOrder.class)) sortOrderIndex.put(sortOrder.sortId, sortOrder); } From 70f01a69fd4c57fa2c085eb0c9141ae0d583836e Mon Sep 17 00:00:00 2001 From: ulvii Date: Tue, 5 Jun 2018 17:20:31 -0700 Subject: [PATCH 15/17] Fix | 4.2 test fix --- .../requestboundary/RequestBoundaryMethodsTest.java | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java index 665ac3120..a8339a0ad 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java @@ -107,13 +107,9 @@ public void testModifiableConnectionProperties() throws SQLException { compareValuesAgainstConnection(con, autoCommitMode2, transactionIsolationLevel2, networkTimeout2, holdability2, sendTimeAsDatetime2, statementPoolingCacheSize2, disableStatementPooling2, serverPreparedStatementDiscardThreshold2, enablePrepareOnFirstPreparedStatementCall2, sCatalog2); + Utils.dropDatabaseIfExists(sCatalog2, con.createStatement()); } } - finally { - SQLServerConnection conDrop = connect(); - Utils.dropDatabaseIfExists(sCatalog2, conDrop.createStatement()); - conDrop.close(); - } } /** @@ -169,13 +165,9 @@ public void testOpenTransactions() throws SQLException { rs = con.createStatement().executeQuery("SELECT * from " + tableName); assertTrue(!rs.isBeforeFirst(), "Should not have returned a result set."); + Utils.dropTableIfExists(tableName, con.createStatement()); } } - finally { - SQLServerConnection conDrop = connect(); - Utils.dropTableIfExists(tableName, conDrop.createStatement()); - conDrop.close(); - } } /** From 950bf28b9a973875badd6fb8c96ee667a584b618 Mon Sep 17 00:00:00 2001 From: ulvii Date: Tue, 5 Jun 2018 17:32:51 -0700 Subject: [PATCH 16/17] Fix | Use master when dropping a database --- .../jdbc/requestboundary/RequestBoundaryMethodsTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java index a8339a0ad..6ead1bb94 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java @@ -107,6 +107,8 @@ public void testModifiableConnectionProperties() throws SQLException { compareValuesAgainstConnection(con, autoCommitMode2, transactionIsolationLevel2, networkTimeout2, holdability2, sendTimeAsDatetime2, statementPoolingCacheSize2, disableStatementPooling2, serverPreparedStatementDiscardThreshold2, enablePrepareOnFirstPreparedStatementCall2, sCatalog2); + // drop the database + con.setCatalog("master"); Utils.dropDatabaseIfExists(sCatalog2, con.createStatement()); } } From b2158980e8f3af26e2c49a6d191f86d3ca19fcc9 Mon Sep 17 00:00:00 2001 From: ulvii Date: Thu, 7 Jun 2018 16:42:31 -0700 Subject: [PATCH 17/17] Fix | Moving RequestBoundaryMethodsTest.java to connection package --- .../com/microsoft/sqlserver/jdbc/SQLServerConnection.java | 6 +++--- .../RequestBoundaryMethodsTest.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) rename src/test/java/com/microsoft/sqlserver/jdbc/{requestboundary => connection}/RequestBoundaryMethodsTest.java (97%) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index d2f2ef3b8..918d58ee4 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -5323,9 +5323,9 @@ public T unwrap(Class iface) throws SQLException { private boolean originalDisableStatementPooling; private int originalServerPreparedStatementDiscardThreshold; private Boolean originalEnablePrepareOnFirstPreparedStatementCall; - private String originalSCatalog = null; - private volatile SQLWarning originalSqlWarnings = null; - private List openStatements = null; + private String originalSCatalog; + private volatile SQLWarning originalSqlWarnings; + private List openStatements; protected void beginRequestInternal() throws SQLException { synchronized (this) { diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/connection/RequestBoundaryMethodsTest.java similarity index 97% rename from src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java rename to src/test/java/com/microsoft/sqlserver/jdbc/connection/RequestBoundaryMethodsTest.java index 6ead1bb94..e646f63ed 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/requestboundary/RequestBoundaryMethodsTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/RequestBoundaryMethodsTest.java @@ -1,4 +1,4 @@ -package com.microsoft.sqlserver.jdbc.requestboundary; +package com.microsoft.sqlserver.jdbc.connection; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull;