From eb3df3a2b273467cbfbdba909a15f206f64a70dd Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Tue, 13 Nov 2018 11:18:12 -0800 Subject: [PATCH 01/51] ported tests from VSO and added tests to increase code coverage --- .../microsoft/sqlserver/jdbc/bvt/BvtTest.java | 69 +++++++ .../jdbc/callablestatement/CallableMixed.java | 116 +++++++++++ .../jdbc/unit/serial/DTOSerialT.java | 184 ++++++++++++++++++ 3 files changed, 369 insertions(+) create mode 100644 src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixed.java create mode 100644 src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialT.java diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/bvt/BvtTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/bvt/BvtTest.java index 0d5f768d4e..64401859ec 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/bvt/BvtTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/bvt/BvtTest.java @@ -10,6 +10,7 @@ import java.math.BigDecimal; import java.sql.DatabaseMetaData; +import java.sql.ResultSet; import java.sql.SQLException; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -27,6 +28,7 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import com.microsoft.sqlserver.jdbc.SQLServerResultSet; import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.DBPreparedStatement; import com.microsoft.sqlserver.testframework.DBResultSet; @@ -418,6 +420,73 @@ public void testResultSetSelectMethod() throws SQLException { } } + /** + * Call resultset methods to run thru some code paths + * + * @throws SQLException + */ + @Test + public void testResultSetMethods() throws SQLException { + try (DBConnection conn = new DBConnection(connectionString); + DBStatement stmt = conn.createStatement(DBResultSetTypes.TYPE_SCROLL_SENSITIVE_CONCUR_UPDATABLE); + DBResultSet rs = stmt.selectAll(table1)) { + + ((ResultSet) rs.product()).clearWarnings(); + + assert (((ResultSet) rs.product()).getType() == ResultSet.TYPE_SCROLL_SENSITIVE); + + // check cursor + ((ResultSet) rs.product()).first(); + assert (((ResultSet) rs.product()).isFirst()); + + ((ResultSet) rs.product()).relative(1); + assert (!((ResultSet) rs.product()).isFirst()); + + ((ResultSet) rs.product()).last(); + assert (((ResultSet) rs.product()).isLast()); + + ((ResultSet) rs.product()).beforeFirst(); + assert (!((ResultSet) rs.product()).isLast()); + + ((ResultSet) rs.product()).afterLast(); + assert (((ResultSet) rs.product()).isAfterLast()); + assert (!((ResultSet) rs.product()).isLast()); + + ((ResultSet) rs.product()).absolute(1); + assert (((ResultSet) rs.product()).getRow() == 1); + + ((ResultSet) rs.product()).moveToInsertRow(); + assert (((ResultSet) rs.product()).getRow() == 0); + + ((ResultSet) rs.product()).moveToCurrentRow(); + assert (((ResultSet) rs.product()).getRow() == 1); + assert (!((ResultSet) rs.product()).rowInserted()); + assert (!((ResultSet) rs.product()).rowUpdated()); + + // check concurrency method + assert (((ResultSet) rs.product()).getConcurrency() == ResultSet.CONCUR_UPDATABLE); + + // check fetch direction + ((ResultSet) rs.product()).setFetchDirection(ResultSet.FETCH_FORWARD); + assert (((ResultSet) rs.product()).getFetchDirection() == ResultSet.FETCH_FORWARD); + + // check fetch size + ((ResultSet) rs.product()).setFetchSize(1); + assert (((ResultSet) rs.product()).getFetchSize() == 1); + + // test delete row + while (rs.next()) { + ((ResultSet) rs.product()).moveToCurrentRow(); + if (((ResultSet) rs.product()).getRow() == 1) { + ((ResultSet) rs.product()).deleteRow(); + assert (((ResultSet) rs.product()).rowDeleted()); + } + } + } catch (Exception e) { + fail(e.toString()); + } + } + /** * drops tables * diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixed.java b/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixed.java new file mode 100644 index 0000000000..bdaa3f7700 --- /dev/null +++ b/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixed.java @@ -0,0 +1,116 @@ +package com.microsoft.sqlserver.jdbc.callablestatement; + +import java.sql.*; + +import com.microsoft.sqlserver.jdbc.RandomUtil; +import com.microsoft.sqlserver.jdbc.SQLServerDriver; +import com.microsoft.sqlserver.jdbc.TestUtils; +import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; + +import org.junit.jupiter.api.Test; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; +import static org.junit.jupiter.api.Assertions.fail; + + +@RunWith(JUnitPlatform.class) +public class CallableMixed { + + @Test + public void datatypestest() throws Exception { + Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); + String connectionString = TestUtils.getConfiguredProperty("mssql_jdbc_test_connection_properties"); + String tableName = RandomUtil.getIdentifier("TFOO3"); + String procName = RandomUtil.getIdentifier("SPFOO3"); + + try (Connection conn = DriverManager.getConnection(connectionString)) { + try (Statement stmt = conn.createStatement()) { + try { + stmt.executeUpdate("DROP TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName)); + stmt.executeUpdate(" DROP PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName)); + } catch (Exception e) {} + + String createSQL = "create table " + AbstractSQLGenerator.escapeIdentifier(tableName) + + "(c1_int int primary key, col2 int)"; + stmt.executeUpdate(createSQL); + + stmt.executeUpdate("Insert into " + AbstractSQLGenerator.escapeIdentifier(tableName) + " values(0, 1)"); + } + + try (Statement stmt = conn.createStatement()) { + stmt.executeUpdate("CREATE PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName) + + " (@p2_int int, @p2_int_out int OUTPUT, @p4_smallint smallint, @p4_smallint_out smallint OUTPUT) AS begin transaction SELECT * FROM " + + AbstractSQLGenerator.escapeIdentifier(tableName) + + " ; SELECT @p2_int_out=@p2_int, @p4_smallint_out=@p4_smallint commit transaction RETURN -2147483648"); + } + + try (CallableStatement cstmt = conn.prepareCall( + "{ ? = CALL " + AbstractSQLGenerator.escapeIdentifier(procName) + " (?, ?, ?, ?) }")) { + cstmt.registerOutParameter((int) 1, (int) 4); + cstmt.setObject((int) 2, Integer.valueOf("31"), (int) 4); + cstmt.registerOutParameter((int) 3, (int) 4); + cstmt.registerOutParameter((int) 5, java.sql.Types.BINARY); // Test OUT param + // re-registration + // (Defect 60921) + cstmt.registerOutParameter((int) 5, (int) 5); + cstmt.setObject((int) 4, Short.valueOf("-5372"), (int) 5); + + // get results and a value + ResultSet rs = cstmt.executeQuery(); + rs.next(); + + if (rs.getInt(1) != 0) { + fail("Received data not equal to setdata"); + + } + + if (cstmt.getInt((int) 5) != -5372) { + fail("Received data not equal to setdata"); + + } + // do nothing and reexecute + rs = cstmt.executeQuery(); + // get the param without getting the resultset + rs = cstmt.executeQuery(); + if (cstmt.getInt((int) 1) != -2147483648) { + fail("Received data not equal to setdata"); + + } + + if (cstmt.getInt((int) 1) != -2147483648) { + fail("Received data not equal to setdata"); + + } + + rs = cstmt.executeQuery(); + rs.next(); + + if (rs.getInt(1) != 0) { + fail("Received data not equal to setdata"); + + } + + if (cstmt.getInt((int) 1) != -2147483648) { + fail("Received data not equal to setdata"); + + } + + if (cstmt.getInt((int) 5) != -5372) { + fail("Received data not equal to setdata"); + + } + + rs = cstmt.executeQuery(); + } + } finally { + try (Connection conn = DriverManager.getConnection(connectionString); + Statement stmt = conn.createStatement()) { + TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(tableName), stmt); + } catch (SQLException e) { + fail(e.toString()); + } + + } + } + +} diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialT.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialT.java new file mode 100644 index 0000000000..d6bcc4439a --- /dev/null +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialT.java @@ -0,0 +1,184 @@ +package com.microsoft.sqlserver.jdbc.unit.serial; + +import java.sql.*; + +import org.junit.jupiter.api.Test; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; + +import junit.framework.*; +import javax.sql.*; + +import com.microsoft.sqlserver.jdbc.*; +import microsoft.sql.*; + +import java.io.*; + +import static org.junit.jupiter.api.Assertions.fail; + + +@RunWith(JUnitPlatform.class) +public class DTOSerialT { + private static final String dateString = "2007-05-08 12:35:29.1234567 +12:15"; + + // public static void testDSerial(String connString) throws Exception + @Test + public void testDSerial() throws Exception { + Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); + String connectionString = TestUtils.getConfiguredProperty("mssql_jdbc_test_connection_properties"); + + try (Connection conn = DriverManager.getConnection(connectionString); + Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)) { + + // create a DTO + ResultSet rs = stmt.executeQuery( + "SELECT CAST('" + dateString + "' AS datetimeoffset(7)) AS" + " 'datetimeoffset IS08601' "); + rs.next(); + verifyCorrectSerialization(((SQLServerResultSet) rs).getDateTimeOffset(1)); + verifyMessedSerialization(); + } + } + + public void testESerial() throws Exception { + String connectionString = TestUtils.getConfiguredProperty("mssql_jdbc_test_connection_properties"); + + try (Connection conn = DriverManager.getConnection(connectionString); + Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)) { + + // raise an error. + SQLServerException currException = null; + + try { + stmt.executeUpdate("RAISERROR ('foo', 13,1) WITH LOG"); + } catch (SQLServerException x) { + currException = x; + } + // store the info + String errInfo = currException.toString(); + String sqlState = currException.getSQLState(); + int errCode = currException.getErrorCode(); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(bos); + // serialize the exception; + out.writeObject(currException); + ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray())); + SQLServerException ex = (SQLServerException) in.readObject(); + String newErrInfo = ex.toString(); + + if (!errInfo.equals(newErrInfo)) { + fail("Errors are different."); + } + if (sqlState != ex.getSQLState()) { + fail("Errors are different."); + } + if (errCode != ex.getErrorCode()) { + fail("Errors are different."); + } + } + } + + // Positive test case, this should succeed + private static void verifyCorrectSerialization(DateTimeOffset dto) throws Exception { + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(bos); + // serialize the DateTimeOffset; + out.writeObject(dto); + ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray())); + DateTimeOffset dtn = (DateTimeOffset) in.readObject(); + verifyDTOEqual(dto, dtn); + // Make sure that you can send rehydrated to server + verifyCorrectSend(dtn); + } + + // this is to make sure that the rehydrated date can be sent to server correctly + private static void verifyCorrectSend(DateTimeOffset dtN) throws Exception { + String connectionString = TestUtils.getConfiguredProperty("mssql_jdbc_test_connection_properties"); + + // create a DTO + try (Connection conn = DriverManager.getConnection(connectionString); + + SQLServerPreparedStatement ps = (SQLServerPreparedStatement) conn + .prepareStatement("SELECT CAST(? AS datetimeoffset(7)) AS" + " 'datetimeoffset IS08601' ")) { + ps.setDateTimeOffset(1, dtN); + ResultSet rs = ps.executeQuery(); + rs.next(); + verifyDTOEqual(dtN, ((SQLServerResultSet) rs).getDateTimeOffset(1)); + } + } + + /* + * I have created two files with wrong nano value (-1) and wrong offset (15*60) by "editing" couple of generated + * serialized files I have checked them in here so they can be used verify the error handling. Note the code to + * generate this is checked in as a text file in the same dir. DateTimeOffset.java.txt + */ + // Negative test cases. + private static void verifyMessedSerialization() throws Exception { + // these values are from the wrongnanos.dat wrongoffset.dat files checked in with the code + // the values represent the serialized DTO class with invalid values. + byte wrongnanos[] = {-84, -19, 0, 5, 115, 114, 0, 47, 109, 105, 99, 114, 111, 115, 111, 102, 116, 46, 115, 113, + 108, 46, 68, 97, 116, 101, 84, 105, 109, 101, 79, 102, 102, 115, 101, 116, 36, 83, 101, 114, 105, 97, + 108, 105, 122, 97, 116, 105, 111, 110, 80, 114, 111, 120, 121, 9, 57, 90, 0, -49, -42, -72, 50, 2, 0, 3, + 73, 0, 13, 109, 105, 110, 117, 116, 101, 115, 79, 102, 102, 115, 101, 116, 73, 0, 5, 110, 97, 110, 111, + 115, 74, 0, 9, 117, 116, 99, 77, 105, 108, 108, 105, 115, 120, 112, 0, 0, 3, 12, -1, -1, -1, -1, 0, 0, + 0, 0, 0, 0, 0, 1}; + byte wrongoffset[] = {-84, -19, 0, 5, 115, 114, 0, 47, 109, 105, 99, 114, 111, 115, 111, 102, 116, 46, 115, 113, + 108, 46, 68, 97, 116, 101, 84, 105, 109, 101, 79, 102, 102, 115, 101, 116, 36, 83, 101, 114, 105, 97, + 108, 105, 122, 97, 116, 105, 111, 110, 80, 114, 111, 120, 121, 9, 57, 90, 0, -49, -42, -72, 50, 2, 0, 3, + 73, 0, 13, 109, 105, 110, 117, 116, 101, 115, 79, 102, 102, 115, 101, 116, 73, 0, 5, 110, 97, 110, 111, + 115, 74, 0, 9, 117, 116, 99, 77, 105, 108, 108, 105, 115, 120, 112, 0, 0, 3, -124, 0, 0, 1, 44, 0, 0, 0, + 0, 0, 0, 0, 1}; + // These two serialized forms throw the exception illegalargument + boolean exThrown = false; + try { + verifyMessedSerializationHelper(wrongnanos); + } catch (IllegalArgumentException e) { + exThrown = true; + } + + if (!exThrown) { + fail("wrongnanos serialized form succeeded."); + } + + exThrown = false; + try { + verifyMessedSerializationHelper(wrongoffset); + } catch (IllegalArgumentException e) { + exThrown = true; + } + + if (!exThrown) { + fail("wrongnanos serialized form succeeded."); + } + } + + private static void verifyMessedSerializationHelper(byte[] svalue) throws Exception { + ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(svalue)); + DateTimeOffset dtn = (DateTimeOffset) in.readObject(); + } + + // This function is used to make sure the hydrated is equal to original string and the initial DTO + private static void verifyDTOEqual(DateTimeOffset initial, DateTimeOffset hydrated) throws Exception { + // check string + String info = initial.toString(); + String newInfo = hydrated.toString(); + // check timestamp + java.sql.Timestamp originalTS = initial.getTimestamp(); + java.sql.Timestamp hydratedTS = hydrated.getTimestamp(); + // and offset + int originalOffset = initial.getMinutesOffset(); + int hydratedOffset = hydrated.getMinutesOffset(); + + if (!info.equals(newInfo)) { + fail("Strings are different."); + } + if (!info.equals(dateString)) { + fail("Strings are different from original."); + } + if (!initial.equals(hydrated)) { + fail("Equality test fails."); + } + if (!originalTS.equals(hydratedTS)) { + fail("Equality test fails from original."); + } + } +} From c31b1f34bc131ef693252889871ce038338a4d13 Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Wed, 14 Nov 2018 12:20:09 -0800 Subject: [PATCH 02/51] moved resultset tests to resultset --- .../microsoft/sqlserver/jdbc/bvt/BvtTest.java | 68 ----------- .../jdbc/resultset/ResultSetTest.java | 107 ++++++++++++++++++ 2 files changed, 107 insertions(+), 68 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/bvt/BvtTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/bvt/BvtTest.java index 64401859ec..0988a861b0 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/bvt/BvtTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/bvt/BvtTest.java @@ -10,7 +10,6 @@ import java.math.BigDecimal; import java.sql.DatabaseMetaData; -import java.sql.ResultSet; import java.sql.SQLException; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -420,73 +419,6 @@ public void testResultSetSelectMethod() throws SQLException { } } - /** - * Call resultset methods to run thru some code paths - * - * @throws SQLException - */ - @Test - public void testResultSetMethods() throws SQLException { - try (DBConnection conn = new DBConnection(connectionString); - DBStatement stmt = conn.createStatement(DBResultSetTypes.TYPE_SCROLL_SENSITIVE_CONCUR_UPDATABLE); - DBResultSet rs = stmt.selectAll(table1)) { - - ((ResultSet) rs.product()).clearWarnings(); - - assert (((ResultSet) rs.product()).getType() == ResultSet.TYPE_SCROLL_SENSITIVE); - - // check cursor - ((ResultSet) rs.product()).first(); - assert (((ResultSet) rs.product()).isFirst()); - - ((ResultSet) rs.product()).relative(1); - assert (!((ResultSet) rs.product()).isFirst()); - - ((ResultSet) rs.product()).last(); - assert (((ResultSet) rs.product()).isLast()); - - ((ResultSet) rs.product()).beforeFirst(); - assert (!((ResultSet) rs.product()).isLast()); - - ((ResultSet) rs.product()).afterLast(); - assert (((ResultSet) rs.product()).isAfterLast()); - assert (!((ResultSet) rs.product()).isLast()); - - ((ResultSet) rs.product()).absolute(1); - assert (((ResultSet) rs.product()).getRow() == 1); - - ((ResultSet) rs.product()).moveToInsertRow(); - assert (((ResultSet) rs.product()).getRow() == 0); - - ((ResultSet) rs.product()).moveToCurrentRow(); - assert (((ResultSet) rs.product()).getRow() == 1); - assert (!((ResultSet) rs.product()).rowInserted()); - assert (!((ResultSet) rs.product()).rowUpdated()); - - // check concurrency method - assert (((ResultSet) rs.product()).getConcurrency() == ResultSet.CONCUR_UPDATABLE); - - // check fetch direction - ((ResultSet) rs.product()).setFetchDirection(ResultSet.FETCH_FORWARD); - assert (((ResultSet) rs.product()).getFetchDirection() == ResultSet.FETCH_FORWARD); - - // check fetch size - ((ResultSet) rs.product()).setFetchSize(1); - assert (((ResultSet) rs.product()).getFetchSize() == 1); - - // test delete row - while (rs.next()) { - ((ResultSet) rs.product()).moveToCurrentRow(); - if (((ResultSet) rs.product()).getRow() == 1) { - ((ResultSet) rs.product()).deleteRow(); - assert (((ResultSet) rs.product()).rowDeleted()); - } - } - } catch (Exception e) { - fail(e.toString()); - } - } - /** * drops tables * diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java index 574a928dfd..6144e4e288 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java @@ -10,6 +10,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.math.BigDecimal; import java.sql.Blob; @@ -33,9 +34,14 @@ import com.microsoft.sqlserver.jdbc.ISQLServerResultSet; import com.microsoft.sqlserver.jdbc.RandomUtil; +import com.microsoft.sqlserver.jdbc.SQLServerResultSet; import com.microsoft.sqlserver.jdbc.TestUtils; import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; import com.microsoft.sqlserver.testframework.AbstractTest; +import com.microsoft.sqlserver.testframework.DBConnection; +import com.microsoft.sqlserver.testframework.DBResultSet; +import com.microsoft.sqlserver.testframework.DBResultSetTypes; +import com.microsoft.sqlserver.testframework.DBStatement; @RunWith(JUnitPlatform.class) @@ -329,4 +335,105 @@ public void testGetterOnNull() throws SQLException { assertEquals(null, rs.getTime(1)); } } + + @Test + public void testHoldability() throws SQLException { + int[] holdabilityOptions = {ResultSet.HOLD_CURSORS_OVER_COMMIT, ResultSet.CLOSE_CURSORS_AT_COMMIT}; + + try (Connection con = DriverManager.getConnection(connectionString); Statement stmt = con.createStatement(); + ResultSet rs = stmt.executeQuery("select null")) { + + int connHold = con.getHoldability(); + assertEquals(stmt.getResultSetHoldability(), connHold); + assertEquals(rs.getHoldability(), connHold); + + for (int i = 0; i < holdabilityOptions.length; i++) { + + if ((connHold = con.getHoldability()) != holdabilityOptions[i]) { + con.setHoldability(holdabilityOptions[i]); + assertEquals(con.getHoldability(), holdabilityOptions[i]); + } + } + } + } + + /** + * Call resultset methods to run thru some code paths + * + * @throws SQLException + */ + @Test + public void testResultSetMethods() throws SQLException { + try (Connection con = DriverManager.getConnection(connectionString); Statement stmt = con + .createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)) { + + stmt.executeUpdate( + "create table " + AbstractSQLGenerator.escapeIdentifier(tableName) + " (col1 int primary key)"); + stmt.executeUpdate("insert into " + AbstractSQLGenerator.escapeIdentifier(tableName) + " values(0)"); + stmt.executeUpdate("insert into " + AbstractSQLGenerator.escapeIdentifier(tableName) + " values(1)"); + stmt.executeUpdate("insert into " + AbstractSQLGenerator.escapeIdentifier(tableName) + " values(2)"); + + try (ResultSet rs = stmt + .executeQuery("select * from " + AbstractSQLGenerator.escapeIdentifier(tableName))) { + + rs.clearWarnings(); + + assert (rs.getType() == ResultSet.TYPE_SCROLL_SENSITIVE); + + // check cursor + rs.first(); + assert (rs.isFirst()); + + rs.relative(1); + assert (!rs.isFirst()); + + rs.last(); + assert (rs.isLast()); + + rs.beforeFirst(); + assert (rs.isBeforeFirst()); + + rs.afterLast(); + assert (rs.isAfterLast()); + assert (!rs.isLast()); + + rs.absolute(1); + assert (rs.getRow() == 1); + + rs.moveToInsertRow(); + assert (rs.getRow() == 0); + rs.moveToCurrentRow(); + assert (rs.getRow() == 1); + + // no inserts or updates + assert (!rs.rowInserted()); + assert (!rs.rowUpdated()); + + // check concurrency method + assert (rs.getConcurrency() == ResultSet.CONCUR_UPDATABLE); + + // check fetch direction + rs.setFetchDirection(ResultSet.FETCH_FORWARD); + assert (rs.getFetchDirection() == ResultSet.FETCH_FORWARD); + + // check fetch size + rs.setFetchSize(1); + assert (rs.getFetchSize() == 1); + + rs.refreshRow(); + + // test delete row + do { + rs.moveToCurrentRow(); + rs.deleteRow(); + assert (rs.rowDeleted()); + } while (rs.next()); + + } catch (Exception e) { + fail(e.toString()); + } finally { + stmt.executeUpdate("drop table " + AbstractSQLGenerator.escapeIdentifier(tableName)); + } + } + } } From 145ee721e7cf42d2d70452985164f409f956c434 Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Wed, 14 Nov 2018 12:21:29 -0800 Subject: [PATCH 03/51] removed unused import --- src/test/java/com/microsoft/sqlserver/jdbc/bvt/BvtTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/bvt/BvtTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/bvt/BvtTest.java index 0988a861b0..0d5f768d4e 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/bvt/BvtTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/bvt/BvtTest.java @@ -27,7 +27,6 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; -import com.microsoft.sqlserver.jdbc.SQLServerResultSet; import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.DBPreparedStatement; import com.microsoft.sqlserver.testframework.DBResultSet; From fd123b29106dbadb61bd544835c1070f6a858075 Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Thu, 15 Nov 2018 16:55:33 -0800 Subject: [PATCH 04/51] added tests to increase code coverage --- .../sqlserver/jdbc/TestResource.java | 3 +- .../microsoft/sqlserver/jdbc/TestUtils.java | 34 + ...lableMixed.java => CallableMixedTest.java} | 2 +- .../jdbc/datatypes/KatmaiDataTypesTest.java | 1752 +++++++++++++++++ .../jdbc/resultset/ResultSetTest.java | 62 +- .../{DTOSerialT.java => DTOSerialTest.java} | 3 +- .../jdbc/unit/statement/StatementTest.java | 10 +- 7 files changed, 1844 insertions(+), 22 deletions(-) rename src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/{CallableMixed.java => CallableMixedTest.java} (96%) create mode 100644 src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java rename src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/{DTOSerialT.java => DTOSerialTest.java} (97%) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java b/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java index 8b2ca84b4b..d300f9e340 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java @@ -153,6 +153,7 @@ protected Object[][] getContents() { {"R_expectedValue", "Expected value: "}, {"R_expectedValueAtIndex", "Expected value at index: "}, {"R_switchFailed", "Switch case is not matched with data"}, {"R_resultsetNotInstance", "Result set is not instance of SQLServerResultSet"}, - + {"R_resultsetNotInstance", "Result set is not instance of SQLServerResultSet"}, + {"R_noJRESupport", "No JRE support for {0}"}, }; } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/TestUtils.java b/src/test/java/com/microsoft/sqlserver/jdbc/TestUtils.java index bb7d317d42..a580def648 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/TestUtils.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/TestUtils.java @@ -66,6 +66,8 @@ public class TestUtils { public static final String SERVER_TYPE_SQL_AZURE = "SQLAzure"; // private static SqlType types = null; private static ArrayList types = null; + private final static int ENGINE_EDITION_FOR_SQL_AZURE = 5; + private final static int ENGINE_EDITION_FOR_SQL_AZURE_DW = 6; /** * Returns serverType @@ -623,6 +625,8 @@ public static Object roundSmallDateTimeValue(Object value) { * @return boolean */ public static boolean serverSupportsDataClassification(Statement stmt) { + + try { stmt.execute("SELECT * FROM SYS.SENSITIVITY_CLASSIFICATIONS"); } catch (SQLException e) { @@ -666,4 +670,34 @@ public static boolean supportJDBC43(Connection con) throws SQLException { public static String escapeSingleQuotes(String name) { return name.replace("'", "''"); } + + /** + * Returns if connected to SQL Azure + * @param con + * connection to server + * @return boolean + * @throws SQLException + */ + public static boolean isSqlAzure(Connection con) throws SQLException { + try (ResultSet rs = con.createStatement().executeQuery("SELECT CAST(SERVERPROPERTY('EngineEdition') as INT)")) { + rs.next(); + int engineEdition = rs.getInt(1); + return (engineEdition == ENGINE_EDITION_FOR_SQL_AZURE || engineEdition == ENGINE_EDITION_FOR_SQL_AZURE_DW); + } + } + + /** + * Returns if connected to SQL Azure DW + * @param con + * connection to server + * @return boolean + * @throws SQLException + */ + public static boolean isSqlAzureDW(Connection con) throws SQLException { + try (ResultSet rs = con.createStatement().executeQuery("SELECT CAST(SERVERPROPERTY('EngineEdition') as INT)")) { + rs.next(); + int engineEdition = rs.getInt(1); + return (engineEdition == ENGINE_EDITION_FOR_SQL_AZURE_DW); + } + } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixed.java b/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java similarity index 96% rename from src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixed.java rename to src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java index bdaa3f7700..91b6615041 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixed.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java @@ -14,7 +14,7 @@ @RunWith(JUnitPlatform.class) -public class CallableMixed { +public class CallableMixedTest { @Test public void datatypestest() throws Exception { diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java new file mode 100644 index 0000000000..5c15b55a52 --- /dev/null +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java @@ -0,0 +1,1752 @@ +package com.microsoft.sqlserver.jdbc.datatypes; + +import java.sql.*; +import java.text.MessageFormat; + +import com.microsoft.sqlserver.jdbc.*; +import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; +import com.microsoft.sqlserver.testframework.AbstractTest; + +import java.math.*; +import java.util.*; + +import org.junit.jupiter.api.Test; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assumptions.assumeTrue; +import static org.junit.Assert.fail; + +import microsoft.sql.DateTimeOffset; + + +/* + * This test suite tests all kinds of temporal data types for Katmai or later versions. It has tests for + * date/time/datetime2/datetimeoffset data types. Also includes tests for new data type mappings in JDBC 4.1. + */ +@RunWith(JUnitPlatform.class) +public class KatmaiDataTypesTest extends AbstractTest { + + final static String tableName = RandomUtil.getIdentifier("KatmaiDataTypesTable"); + final static String procName = RandomUtil.getIdentifier("KatmaiDataTypesTableProc"); + + /* + * public void testSparse() throws Exception { + * assumeTrue(!isSqlAzure(DriverManager.getConnection(connectionString)), TestResource.getResource("R_skipAzure")); + * try (Connection connection = DriverManager.getConnection(connectionString) { + * SparseTest.runTest(connectionString); } } public void testJDBC41BigInteger() throws Exception { if + * (fxConnection.isServerSqlAzureDW()) { + * CTestLog.Skip("this test is skipped because it cannot be meaningfully tested on SQL Azure DW."); } + * BigIntegerTest.runTest(connectionString); } + */ + + enum SQLType { + date("yyyy-mm-dd", 0, java.sql.Types.DATE, "java.sql.Date"), + + time("hh:mm:ss", 7, java.sql.Types.TIME, "java.sql.Time"), + + datetime("yyyy-mm-dd hh:mm:ss", 3, java.sql.Types.TIMESTAMP, "java.sql.Timestamp"), + + datetime2("yyyy-mm-dd hh:mm:ss", 7, java.sql.Types.TIMESTAMP, "java.sql.Timestamp"), + + datetimeoffset("yyyy-mm-dd hh:mm:ss +hh:mm", 7, microsoft.sql.Types.DATETIMEOFFSET, "microsoft.sql.DateTimeOffset"); + + final int basePrecision; + final int maxPrecision; + final int maxFractionalSecondsDigits; + final int jdbcType; + final String className; + + SQLType(String format, int maxFractionalSecondsDigits, int jdbcType, String className) { + assert maxFractionalSecondsDigits >= 0; + + this.basePrecision = format.length(); + this.maxFractionalSecondsDigits = maxFractionalSecondsDigits; + this.maxPrecision = basePrecision + + ((maxFractionalSecondsDigits > 0) ? (1 + maxFractionalSecondsDigits) : 0); + this.jdbcType = jdbcType; + this.className = className; + } + }; + + abstract static class SQLValue { + private final SQLType sqlType; + private final String stringValue; + + final String getString() { + return stringValue; + } + + private final String sqlTypeExpression; + private final int precision; + private final int scale; + + SQLValue(SQLType sqlType, String stringValue) { + this.sqlType = sqlType; + this.stringValue = stringValue; + this.sqlTypeExpression = sqlType.toString(); + this.precision = sqlType.maxPrecision; + this.scale = sqlType.maxFractionalSecondsDigits; + } + + SQLValue(SQLType sqlType, String stringValue, int fractionalSecondsDigits) { + this.sqlType = sqlType; + this.stringValue = stringValue; + this.sqlTypeExpression = sqlType.toString() + "(" + fractionalSecondsDigits + ")"; + this.precision = sqlType.basePrecision + (1 + fractionalSecondsDigits); + this.scale = fractionalSecondsDigits; + } + + private String sqlCastExpression() { + return "CAST('" + stringValue + "' AS " + sqlTypeExpression + ")"; + } + + void verifyResultSetMetaData(Connection conn) throws Exception { + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT TOP 0 " + sqlCastExpression()); + + try { + ResultSetMetaData metadata = rs.getMetaData(); + + assertEquals(metadata.getColumnType(1), sqlType.jdbcType, "getColumnType() of " + sqlCastExpression()); + assertEquals(metadata.getColumnTypeName(1), sqlType.toString(), + "getColumnTypeName() of " + sqlCastExpression()); + assertEquals(metadata.getPrecision(1), precision, "getPrecision() of " + sqlCastExpression()); + + // Display size of temporal types is the precision per JDBC spec + assertEquals(metadata.getColumnDisplaySize(1), precision, + "getColumnDisplaySize() of " + sqlCastExpression()); + // Scale is interpreted as number of fractional seconds precision + assertEquals(metadata.getScale(1), scale, "getScale() of " + sqlCastExpression()); + assertEquals(metadata.getColumnClassName(1), sqlType.className, + "getColumnClassName() of " + sqlCastExpression()); + // Katmai temporal types are not signed + assertEquals(metadata.isSigned(1), false, "isSigned() of " + sqlCastExpression()); + + // Katmai temporal types are searchable (i.e. usable in a WHERE clause) + assertEquals(metadata.isSearchable(1), true, "isSearchable() of " + sqlCastExpression()); + } finally { + rs.close(); + stmt.close(); + } + } + + void verifyParameterMetaData(Connection conn) throws Exception { + + final String procName = RandomUtil.getIdentifier("testParameterMetaData"); + // final String procName = "[" + driver.createuniqueidentifer("testParameterMetaData") + "]"; + + Statement stmt = null; + PreparedStatement pstmt = null; + + try { + stmt = conn.createStatement(); + + // Create the stored proc + stmt.executeUpdate("CREATE PROCEDURE " + procName + " @arg " + sqlTypeExpression + " AS SELECT @arg"); + + pstmt = conn.prepareStatement("{call " + procName + "(?)}"); + ParameterMetaData metadata = pstmt.getParameterMetaData(); + + assertEquals(metadata.getParameterType(1), sqlType.jdbcType, + "getParameterType() of " + sqlCastExpression()); + assertEquals(metadata.getParameterTypeName(1), sqlType.toString(), + "getParameterTypeName() of " + sqlCastExpression()); + assertEquals(metadata.getPrecision(1), precision, "getPrecision() of " + sqlCastExpression()); + + // Scale is interpreted as number of fractional seconds precision + assertEquals(metadata.getScale(1), scale, "getScale() of " + sqlCastExpression()); + assertEquals(metadata.getParameterClassName(1), sqlType.className, + "getParameterClassName() of " + sqlCastExpression()); + // Katmai temporal types are not signed + assertEquals(metadata.isSigned(1), false, "isSigned() of " + sqlCastExpression()); + } finally { + if (null != pstmt) + pstmt.close(); + + if (null != stmt) { + stmt.executeUpdate("DROP PROCEDURE " + procName); + stmt.close(); + } + } + } + + abstract void verifyRSGetters(ResultSet rs) throws Exception; + + void verifyRSGetters(Connection conn) throws Exception { + Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery( + "SELECT " + sqlCastExpression() + ", CAST(" + sqlCastExpression() + " AS VARCHAR(60))"); + rs.next(); + verifyRSGetters(rs); + rs.close(); + stmt.close(); + } + + abstract void verifyRSUpdaters(ResultSet rs) throws Exception; + + void verifyRSUpdaters(Connection conn) throws Exception { + + assumeTrue(TestUtils.isSqlAzureDW(conn), TestResource.getResource("R_skipAzure")); + + Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); + try { + stmt.executeUpdate("DROP TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName)); + } catch (SQLException e) {} + stmt.executeUpdate("CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName) + " (col1 " + + sqlTypeExpression + ", col2 int identity(1,1) primary key)"); + + try { + stmt.executeUpdate("INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableName) + " VALUES (" + + sqlCastExpression() + ")"); + + ResultSet rs = stmt.executeQuery("SELECT * FROM " + AbstractSQLGenerator.escapeIdentifier(tableName)); + rs.next(); + verifyRSUpdaters(rs); + rs.close(); + } finally { + stmt.executeUpdate("DROP TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName)); + } + + stmt.close(); + } + + /* + * For testing the setObject and setNull methods in PreparedStatement, use the verifySetter* methods. These + * methods prepare a single statement and execute it for all different data types by calling the appropriate + * 'setObject' methods for each data type and/or type conversion. + */ + abstract void verifySetters(PreparedStatement ps) throws Exception; + + abstract void verifySettersUtilDate(PreparedStatement ps) throws Exception; + + abstract void verifySettersCalendar(PreparedStatement ps) throws Exception; + + void verifySetters(Connection conn) throws Exception { + assumeTrue(TestUtils.isSqlAzureDW(conn), TestResource.getResource("R_skipAzure")); + + Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); + try { + stmt.executeUpdate("DROP TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName)); + } catch (SQLException e) {} + stmt.executeUpdate("CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName) + " (col1 " + + sqlTypeExpression + ", col2 int identity(1,1) primary key)"); + + PreparedStatement ps = conn.prepareStatement( + "INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableName) + " VALUES (?) SELECT * FROM " + + AbstractSQLGenerator.escapeIdentifier(tableName), + ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); + try { + verifySetters(ps); + // Verify setObject function for the new mapping in JDBC41 (java.util.Date to TIMESTAMP) + stmt.executeUpdate("TRUNCATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName)); + + verifySettersUtilDate(ps); + // Verify setObject function for the new mapping in JDBC41 (java.util.Calendar to TIMESTAMP) + stmt.executeUpdate("TRUNCATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName)); + verifySettersCalendar(ps); + } finally { + stmt.executeUpdate("DROP TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName)); + } + + ps.close(); + stmt.close(); + } + + abstract void verifyCSGetters(CallableStatement cs) throws Exception; + + void verifyCSGetters(Connection conn) throws Exception { + Statement stmt = conn.createStatement(); + try { + stmt.executeUpdate("DROP PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName)); + } catch (SQLException e) {} + stmt.executeUpdate("CREATE PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName) + " @argIn " + + sqlTypeExpression + "," + " @argOut " + sqlTypeExpression + " OUTPUT" + " AS " + + " SET @argOut=@argIn"); + + CallableStatement cs = conn + .prepareCall("{call " + AbstractSQLGenerator.escapeIdentifier(procName) + "(?,?)}"); + try { + verifyCSGetters(cs); + } finally { + stmt.executeUpdate("DROP PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName)); + } + + cs.close(); + stmt.close(); + } + } + + static final class DateValue extends SQLValue { + private final java.sql.Date expected; + + DateValue(String stringValue) { + super(SQLType.date, stringValue); + this.expected = java.sql.Date.valueOf(stringValue); + } + + private java.sql.Timestamp expectedTimestampMillisPrecision() { + return new java.sql.Timestamp(expected.getTime()); + } + + private java.util.Date expectedUtilDate() { + return (java.util.Date) expected; + } + + private java.util.Calendar expectedCalendar() { + Calendar cal = Calendar.getInstance(); + cal.clear(); + cal.setTimeInMillis(expected.getTime()); + return cal; + } + + void verifyRSGetters(ResultSet rs) throws Exception { + + assertEquals(rs.getDate(1), expected, "getDate mismatch"); + + assertEquals(rs.getTimestamp(1), new java.sql.Timestamp(expected.getTime()), "getTimestamp mismatch"); + + assertEquals(rs.getString(1), expected.toString(), "getString mismatch"); + + rs.close(); + } + + void verifyRSUpdaters(ResultSet rs) throws Exception { + rs.updateDate(1, expected); + rs.updateRow(); + + assertEquals(rs.getDate(1), expected, "updateDate mismatch"); + } + + void verifySetters(PreparedStatement ps) throws Exception { + ps.setDate(1, expected); + ps.execute(); + ps.getMoreResults(); + ResultSet rs = ps.getResultSet(); + rs.next(); + + assertEquals(rs.getDate(1), expected, "setDate mismatch"); + } + + void verifySettersUtilDate(PreparedStatement ps) throws Exception { + int currentRow = 0; + ps.setObject(1, expectedUtilDate()); + ps.execute(); + ps.getMoreResults(); + ResultSet rs = ps.getResultSet(); + rs.next(); + assertEquals(rs.getDate(1), expected, "getDate mismatch"); + assertEquals((java.util.Date) rs.getObject(1), expectedUtilDate(), "getObject mismatch"); + + // Test the additional conversions introduced in JDBC41 for types setters + // Test datetime2 column with target type TIMESTAMP + ps.setObject(1, expectedUtilDate(), java.sql.Types.DATE); + ps.execute(); + ps.getMoreResults(); + rs = ps.getResultSet(); + // Go to the first row + rs.next(); + // Go to the row just inserted + rs.relative(++currentRow); + assertEquals(rs.getDate(1), expected, "getDate mismatch"); + + ps.setObject(1, expectedUtilDate(), java.sql.Types.TIMESTAMP); + ps.execute(); + ps.getMoreResults(); + rs = ps.getResultSet(); + // Go to the first row + rs.next(); + // Go to the row just inserted + rs.relative(++currentRow); + assertEquals(rs.getTimestamp(1), expectedTimestampMillisPrecision(), "getTimestamp mismatch"); + + // Test the setNull() methods for different data type conversions + ps.setNull(1, java.sql.Types.DATE); + ps.execute(); + ps.getMoreResults(); + rs = ps.getResultSet(); + // Go to the next row + rs.next(); + rs.relative(++currentRow); + // Read the column, first before calling rs.wasNull() + rs.getTimestamp(1); + assertEquals(rs.wasNull(), true, "getTimestamp mismatch"); + + ps.setNull(1, java.sql.Types.TIMESTAMP); + ps.execute(); + ps.getMoreResults(); + rs = ps.getResultSet(); + // Go to the next row + rs.next(); + rs.relative(++currentRow); + // Read the column, first before calling rs.wasNull() + rs.getTimestamp(1); + assertEquals(rs.wasNull(), true, "getTimestamp mismatch"); + } + + void verifySettersCalendar(PreparedStatement ps) throws Exception { + int currentRow = 0; + ps.setObject(1, expectedCalendar()); + ps.execute(); + ps.getMoreResults(); + ResultSet rs = ps.getResultSet(); + rs.next(); + assertEquals(rs.getDate(1), expected, "getDate mismatch"); + // Cannot test rs.getObject for the Calendar object type, as none of Time, Timestamp, java.sql.Date + // or java.util.Date can be cast to a calendar + + // Test the additional conversions introduced in JDBC41 for types setters + // Test datetime2 column with target type TIMESTAMP + ps.setObject(1, expectedCalendar(), java.sql.Types.DATE); + ps.execute(); + ps.getMoreResults(); + rs = ps.getResultSet(); + // Go to the first row + rs.next(); + // Go to the row just inserted + rs.relative(++currentRow); + assertEquals(rs.getDate(1), expected, "getDate mismatch"); + + ps.setObject(1, expectedCalendar(), java.sql.Types.TIMESTAMP); + ps.execute(); + ps.getMoreResults(); + rs = ps.getResultSet(); + // Go to the first row + rs.next(); + // Go to the row just inserted + rs.relative(++currentRow); + assertEquals(rs.getTimestamp(1), expectedTimestampMillisPrecision(), "getTimestamp mismatch"); + } + + void verifyCSGetters(CallableStatement cs) throws Exception { + cs.setDate(1, expected); + cs.registerOutParameter(2, java.sql.Types.DATE); + cs.execute(); + assertEquals(cs.getDate(2), expected, "getDate mismatch"); + assertEquals(cs.getObject(2), expected, "getObject mismatch"); + } + } + + static final class TimeValue extends SQLValue { + private int hour; + private int minute; + private int second; + private int nanos; + private TimeZone tz; + + TimeValue(String stringValue) { + super(SQLType.time, stringValue); + initExpected(stringValue, TimeZone.getDefault()); + } + + TimeValue(String stringValue, int fractionalSecondsDigits) { + super(SQLType.time, stringValue, fractionalSecondsDigits); + initExpected(stringValue, TimeZone.getDefault()); + } + + private void initExpected(String stringValue, TimeZone tz) { + // "hh:mm:ss[.nnnnnnn]" + this.hour = Integer.valueOf(stringValue.substring(0, 2)); + this.minute = Integer.valueOf(stringValue.substring(3, 5)); + this.second = Integer.valueOf(stringValue.substring(6, 8)); + + this.nanos = (8 == stringValue.indexOf('.')) ? (new BigDecimal(stringValue.substring(8))) + .scaleByPowerOfTen(9).intValue() : 0; + + this.tz = tz; + } + + private java.sql.Timestamp expectedTimestamp() { + Calendar cal = Calendar.getInstance(tz); + cal.clear(); + cal.set(1900, Calendar.JANUARY, 1, hour, minute, second); + + java.sql.Timestamp timestamp = new java.sql.Timestamp(cal.getTimeInMillis()); + timestamp.setNanos(nanos); + + return timestamp; + } + + private java.sql.Time expectedTime() { + Calendar cal = Calendar.getInstance(tz); + cal.clear(); + cal.set(1970, Calendar.JANUARY, 1, hour, minute, second); + + cal.set(Calendar.MILLISECOND, (nanos + 500000) / 1000000); + + return new java.sql.Time(cal.getTimeInMillis()); + } + + private java.sql.Timestamp expectedTimestampMillisPrecision() { + Calendar cal = Calendar.getInstance(tz); + cal.clear(); + cal.set(1900, Calendar.JANUARY, 1, hour, minute, second); + cal.set(Calendar.MILLISECOND, (nanos + 500000) / 1000000); + + java.sql.Timestamp timestamp = new java.sql.Timestamp(cal.getTimeInMillis()); + + return timestamp; + } + + private java.util.Date expectedUtilDate() { + Calendar cal = Calendar.getInstance(tz); + cal.clear(); + cal.set(1970, Calendar.JANUARY, 1, hour, minute, second); + + cal.set(Calendar.MILLISECOND, (nanos + 500000) / 1000000); + java.util.Date udate = new java.util.Date(cal.getTimeInMillis()); + return udate; + + } + + private java.util.Calendar expectedCalendar() { + Calendar cal = Calendar.getInstance(tz); + cal.clear(); + cal.set(1970, Calendar.JANUARY, 1, hour, minute, second); + + cal.set(Calendar.MILLISECOND, (nanos + 500000) / 1000000); + return cal; + } + + void verifyRSGetters(ResultSet rs) throws Exception { + assertEquals(rs.getTime(1), expectedTime(), "getTime mismatch"); + + assertEquals(rs.getTimestamp(1), expectedTimestamp(), "getTimestamp mismatch"); + + assertEquals(rs.getString(1), this.getString(), "getString mismatch"); + } + + void verifyRSUpdaters(ResultSet rs) throws Exception { + rs.updateTime(1, expectedTime()); + rs.updateRow(); + + assertEquals(rs.getTime(1), expectedTime(), "updateTime mismatch"); + } + + void verifySetters(PreparedStatement ps) throws Exception { + ps.setTime(1, expectedTime()); + ps.execute(); + ps.getMoreResults(); + ResultSet rs = ps.getResultSet(); + rs.next(); + + assertEquals(rs.getTime(1), expectedTime(), "setTime mismatch"); + } + + void verifySettersUtilDate(PreparedStatement ps) throws Exception { + int currentRow = 0; + ps.setObject(1, expectedUtilDate()); + ps.execute(); + ps.getMoreResults(); + ResultSet rs = ps.getResultSet(); + rs.next(); + assertEquals(rs.getTime(1), expectedTime(), "getTime mismatch"); + assertEquals(rs.getTimestamp(1), expectedTimestampMillisPrecision(), "getTimestamp mismatch"); + assertEquals((java.util.Date) rs.getObject(1), expectedUtilDate(), "getObject mismatch"); + + // Test the additional conversions introduced in JDBC41 for types setters + // Test datetime2 column with target type TIMESTAMP + ps.setObject(1, expectedUtilDate(), java.sql.Types.TIME); + ps.execute(); + ps.getMoreResults(); + rs = ps.getResultSet(); + // Go to the first row + rs.next(); + // Go to the row just inserted + rs.relative(++currentRow); + assertEquals(rs.getTime(1), expectedTime(), "getTime mismatch"); + + ps.setObject(1, expectedUtilDate(), java.sql.Types.TIMESTAMP); + ps.execute(); + ps.getMoreResults(); + rs = ps.getResultSet(); + // Go to the first row + rs.next(); + // Go to the row just inserted + rs.relative(++currentRow); + assertEquals(rs.getTimestamp(1), expectedTimestampMillisPrecision(), "getTimestamp mismatch"); + + // Test the setNull() methods for different data type conversions + ps.setNull(1, java.sql.Types.TIME); + ps.execute(); + ps.getMoreResults(); + rs = ps.getResultSet(); + // Go to the next row + rs.next(); + rs.relative(++currentRow); + // Read the column, first before calling rs.wasNull() + rs.getTimestamp(1); + assertEquals(rs.wasNull(), true, "getTimestamp mismatch"); + + ps.setNull(1, java.sql.Types.TIMESTAMP); + ps.execute(); + ps.getMoreResults(); + rs = ps.getResultSet(); + // Go to the next row + rs.next(); + rs.relative(++currentRow); + // Read the column, first before calling rs.wasNull() + rs.getTimestamp(1); + assertEquals(rs.wasNull(), true, "getTimestamp mismatch"); + + } + + void verifySettersCalendar(PreparedStatement ps) throws Exception { + int currentRow = 0; + ps.setObject(1, expectedCalendar()); + ps.execute(); + ps.getMoreResults(); + ResultSet rs = ps.getResultSet(); + rs.next(); + assertEquals(rs.getTime(1), expectedTime(), "getTime mismatch"); + + assertEquals(rs.getTimestamp(1), expectedTimestampMillisPrecision(), "getTimestamp mismatch"); + // Cannot test rs.getObject for the Calendar object type, as none of Time, Timestamp, java.sql.Date + // or java.util.Date can be cast to a calendar + + // Test the additional conversions introduced in JDBC41 for types setters + // Test datetime2 column with target type TIMESTAMP + ps.setObject(1, expectedCalendar(), java.sql.Types.TIME); + ps.execute(); + ps.getMoreResults(); + rs = ps.getResultSet(); + // Go to the first row + rs.next(); + // Go to the row just inserted + rs.relative(++currentRow); + assertEquals(rs.getTime(1), expectedTime(), "getTime mismatch"); + + ps.setObject(1, expectedCalendar(), java.sql.Types.TIMESTAMP); + ps.execute(); + ps.getMoreResults(); + rs = ps.getResultSet(); + // Go to the first row + rs.next(); + // Go to the row just inserted + rs.relative(++currentRow); + assertEquals(rs.getTimestamp(1), expectedTimestampMillisPrecision(), "getTimestamp mismatch"); + } + + void verifyCSGetters(CallableStatement cs) throws Exception { + cs.setTime(1, expectedTime()); + cs.registerOutParameter(2, java.sql.Types.TIME); + cs.execute(); + assertEquals(cs.getTime(2), expectedTime(), "getTime mismatch"); + assertEquals(cs.getObject(2), expectedTime(), "getObject mismatch"); + } + } + + static final class DateTime2Value extends SQLValue { + private String stringValue; + private long utcMillis; + private int nanos; + private TimeZone tz; + + DateTime2Value(String stringValue) { + super(SQLType.datetime2, stringValue); + initExpected(stringValue, TimeZone.getDefault()); + } + + DateTime2Value(String stringValue, int fractionalSecondsDigits) { + super(SQLType.datetime2, stringValue, fractionalSecondsDigits); + initExpected(stringValue, TimeZone.getDefault()); + } + + DateTime2Value(String stringValue, String timeZone) { + super(SQLType.datetime2, stringValue); + initExpected(stringValue, TimeZone.getTimeZone(timeZone)); + } + + DateTime2Value(String stringValue, int fractionalSecondsDigits, String timeZone) { + super(SQLType.datetime2, stringValue, fractionalSecondsDigits); + initExpected(stringValue, TimeZone.getTimeZone(timeZone)); + } + + private void initExpected(String timestampString, TimeZone tz) { + // "yyyy-MM-dd hh:mm:ss[.nnnnnnn]" + int year = Integer.valueOf(timestampString.substring(0, 4)); + int month = Integer.valueOf(timestampString.substring(5, 7)); + int day = Integer.valueOf(timestampString.substring(8, 10)); + int hour = Integer.valueOf(timestampString.substring(11, 13)); + int minute = Integer.valueOf(timestampString.substring(14, 16)); + int second = Integer.valueOf(timestampString.substring(17, 19)); + + int nanos = (19 == timestampString.indexOf('.')) ? (new BigDecimal(timestampString.substring(19))) + .scaleByPowerOfTen(9).intValue() : 0; + + Calendar cal = Calendar.getInstance(tz); + cal.set(Calendar.YEAR, year); + cal.set(Calendar.MONTH, month - 1); + cal.set(Calendar.DAY_OF_MONTH, day); + cal.set(Calendar.HOUR_OF_DAY, hour); + cal.set(Calendar.MINUTE, minute); + cal.set(Calendar.SECOND, second); + cal.set(Calendar.MILLISECOND, nanos / 1000000); + + this.stringValue = timestampString; + this.utcMillis = cal.getTimeInMillis(); + this.nanos = nanos; + this.tz = tz; + } + + private java.sql.Date expectedDate() { + Calendar cal = Calendar.getInstance(tz); + cal.setTimeInMillis(utcMillis); + cal.set(Calendar.HOUR_OF_DAY, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + return new java.sql.Date(cal.getTimeInMillis()); + } + + private java.sql.Time expectedTime() { + Calendar cal = Calendar.getInstance(tz); + cal.setTimeInMillis(utcMillis); + if (nanos % 1000000 >= 500000) + cal.add(Calendar.MILLISECOND, 1); + cal.set(Calendar.YEAR, 1970); + cal.set(Calendar.MONTH, Calendar.JANUARY); + cal.set(Calendar.DAY_OF_MONTH, 1); + return new java.sql.Time(cal.getTimeInMillis()); + } + + private java.sql.Timestamp expectedTimestamp() { + Timestamp timestamp = new java.sql.Timestamp(utcMillis); + timestamp.setNanos(nanos); + return timestamp; + } + + private java.sql.Timestamp expectedTimestampMillisPrecision() { + Timestamp timestamp = new java.sql.Timestamp(utcMillis); + // Cannot set the nanos to 0, as per doc " the fractional seconds are stored in the nanos field of the + // Timestamp object." + // timestamp.setNanos(0); + return timestamp; + } + + private java.util.Date expectedUtilDate() { + return new java.util.Date(utcMillis); + } + + private java.util.Calendar expectedCalendar() { + Calendar cal = Calendar.getInstance(tz); + cal.setTimeInMillis(utcMillis); + return cal; + } + + void verifyRSGetters(ResultSet rs) throws Exception { + assertEquals(rs.getDate(1, Calendar.getInstance(tz)), expectedDate(), "getDate mismatch"); + + assertEquals(rs.getTime(1, Calendar.getInstance(tz)), expectedTime(), "getTime mismatch"); + + assertEquals(rs.getTimestamp(1, Calendar.getInstance(tz)), expectedTimestamp(), "getTimestamp mismatch"); + + assertEquals(rs.getString(1), stringValue, "getString mismatch"); + } + + void verifyRSUpdaters(ResultSet rs) throws Exception { + // Unlike PreparedStatement.setTimestamp(), there is no ResultSet.updateTimestamp() + // that takes a Calendar argument for passing in the time zone. ResultSet.updateTimestamp() + // always uses the VM default time zone. So we have to temporarily change it while doing + // the update. + TimeZone tzDefault = TimeZone.getDefault(); + try { + TimeZone.setDefault(tz); + + // Update the timestamp value with this value's time zone (set as the VM default above) + rs.updateTimestamp(1, expectedTimestamp()); + rs.updateRow(); + + // Verify the update (this value's time zone is still the default) + assertEquals(rs.getTimestamp(1), expectedTimestamp(), "updateTimestamp mismatch (default time zone)"); + } finally { + // Restore the original default time zone + TimeZone.setDefault(tzDefault); + } + + // Verify the update (after restoring the default time zone) using the getTimestamp + // variant that takes a time zone argument (as a Calendar) + assertEquals(rs.getTimestamp(1, Calendar.getInstance(tz)), expectedTimestamp(), + "updateTimestamp mismatch (explicit time zone)"); + } + + void verifySetters(PreparedStatement ps) throws Exception { + // Verify PreparedStatement.setTimestamp with default time zone first. + // Temporarily change the VM default time zone as in the ResultSet verifier. + TimeZone tzDefault = TimeZone.getDefault(); + try { + TimeZone.setDefault(tz); + + ps.setTimestamp(1, expectedTimestamp()); + ps.execute(); + ps.getMoreResults(); + ResultSet rs = ps.getResultSet(); + rs.next(); + + assertEquals(rs.getTimestamp(1), expectedTimestamp(), "setTimestamp mismatch (default time zone)"); + } finally { + TimeZone.setDefault(tzDefault); + } + + // Verify PreparedStatement.setTimestamp(..., Calendar) as well (don't change the VM default) + ps.setTimestamp(1, expectedTimestamp(), Calendar.getInstance(tz)); + ps.execute(); + ps.getMoreResults(); + ResultSet rs = ps.getResultSet(); + rs.next(); + + assertEquals(rs.getTimestamp(1, Calendar.getInstance(tz)), expectedTimestamp(), "setTimestamp mismatch"); + } + + void verifySettersUtilDate(PreparedStatement ps) throws Exception { + int currentRow = 0; + ps.setObject(1, expectedUtilDate()); + ps.execute(); + ps.getMoreResults(); + ResultSet rs = ps.getResultSet(); + rs.next(); + assertEquals(rs.getTimestamp(1), expectedTimestampMillisPrecision(), "getTimestamp mismatch"); + assertEquals((java.util.Date) rs.getObject(1), expectedUtilDate(), "getObject mismatch"); + + // Test the additional conversions introduced in JDBC41 for types setters + // Test datetime2 column with target type TIMESTAMP + ps.setObject(1, expectedUtilDate(), java.sql.Types.TIMESTAMP); + ps.execute(); + ps.getMoreResults(); + rs = ps.getResultSet(); + // Go to the next row + rs.next(); + rs.relative(++currentRow); + assertEquals(rs.getTimestamp(1), expectedTimestampMillisPrecision(), "getTimestamp mismatch"); + + // Test the setNull() methods for different data type conversions + ps.setNull(1, java.sql.Types.TIME); + ps.execute(); + ps.getMoreResults(); + rs = ps.getResultSet(); + // Go to the next row + rs.next(); + rs.relative(++currentRow); + // Read the column, first before calling rs.wasNull() + rs.getTimestamp(1); + assertEquals(rs.wasNull(), true, "getTimestamp mismatch"); + + ps.setNull(1, java.sql.Types.DATE); + ps.execute(); + ps.getMoreResults(); + rs = ps.getResultSet(); + // Go to the next row + rs.next(); + rs.relative(++currentRow); + // Read the column, first before calling rs.wasNull() + rs.getTimestamp(1); + assertEquals(rs.wasNull(), true, "getTimestamp mismatch"); + + ps.setNull(1, java.sql.Types.TIMESTAMP); + ps.execute(); + ps.getMoreResults(); + rs = ps.getResultSet(); + // Go to the next row + rs.next(); + rs.relative(++currentRow); + // Read the column, first before calling rs.wasNull() + rs.getTimestamp(1); + assertEquals(rs.wasNull(), true, "getTimestamp mismatch"); + } + + void verifySettersCalendar(PreparedStatement ps) throws Exception { + int currentRow = 0; + ps.setObject(1, expectedCalendar()); + ps.execute(); + ps.getMoreResults(); + ResultSet rs = ps.getResultSet(); + rs.next(); + assertEquals(rs.getTimestamp(1), expectedTimestampMillisPrecision(), "getTimestamp mismatch"); + // Cannot test rs.getObject for the Calendar object type, as none of Time, Timestamp, java.sql.Date + // or java.util.Date can be cast to a calendar + + // Test the additional conversions introduced in JDBC41 for types setters + // Test datetime2 column with target type TIMESTAMP + ps.setObject(1, expectedCalendar(), java.sql.Types.TIMESTAMP); + ps.execute(); + ps.getMoreResults(); + rs = ps.getResultSet(); + // Go to the next row + rs.next(); + rs.relative(++currentRow); + assertEquals(rs.getTimestamp(1), expectedTimestampMillisPrecision(), "getTimestamp mismatch"); + } + + void verifyCSGetters(CallableStatement cs) throws Exception { + cs.setTimestamp(1, expectedTimestamp(), Calendar.getInstance(tz)); + cs.registerOutParameter(2, java.sql.Types.TIMESTAMP); + cs.execute(); + + // Verify typed getter (with time zone argument) + assertEquals(cs.getTimestamp(2, Calendar.getInstance(tz)), expectedTimestamp(), "getTimestamp mismatch"); + + // Verify getObject (no provision for time zone argument - need to push/pop the default) + TimeZone tzDefault = TimeZone.getDefault(); + try { + TimeZone.setDefault(tz); + + assertEquals(cs.getObject(2), expectedTimestamp(), "getObject mismatch"); + } finally { + TimeZone.setDefault(tzDefault); + } + } + } + + static final class DateTimeOffsetValue extends SQLValue { + private final DateTimeOffset dto; + + DateTimeOffset get() { + return dto; + } + + private final DateTimeOffset initExpected(String stringValue, int fractionalSecondsDigits) { + int lastColon = stringValue.lastIndexOf(':'); + + String offsetString = stringValue.substring(lastColon - 3); + int minutesOffset = 60 * Integer.valueOf(offsetString.substring(1, 3)) + + Integer.valueOf(offsetString.substring(4, 6)); + + if (offsetString.startsWith("-")) + minutesOffset = -minutesOffset; + + String timestampString = stringValue.substring(0, lastColon - 4); + int year = Integer.valueOf(timestampString.substring(0, 4)); + int month = Integer.valueOf(timestampString.substring(5, 7)); + int day = Integer.valueOf(timestampString.substring(8, 10)); + int hour = Integer.valueOf(timestampString.substring(11, 13)); + int minute = Integer.valueOf(timestampString.substring(14, 16)); + int second = Integer.valueOf(timestampString.substring(17, 19)); + + int nanos = (19 == timestampString.indexOf('.')) ? (new BigDecimal(timestampString.substring(19))) + .scaleByPowerOfTen(9).intValue() : 0; + + Calendar cal = Calendar.getInstance(Locale.US); + cal.set(Calendar.ZONE_OFFSET, 1000 * 60 * minutesOffset); + cal.set(Calendar.DST_OFFSET, 0); + cal.set(Calendar.YEAR, year); + cal.set(Calendar.MONTH, month - 1); + cal.set(Calendar.DAY_OF_MONTH, day); + cal.set(Calendar.HOUR_OF_DAY, hour); + cal.set(Calendar.MINUTE, minute); + cal.set(Calendar.SECOND, second); + + java.sql.Timestamp timestamp = new java.sql.Timestamp(cal.getTimeInMillis()); + timestamp.setNanos(nanos); + + return DateTimeOffset.valueOf(timestamp, minutesOffset); + } + + DateTimeOffsetValue(String stringValue) { + super(SQLType.datetimeoffset, stringValue); + dto = initExpected(stringValue, -1); + } + + DateTimeOffsetValue(String stringValue, int fractionalSecondsDigits) { + super(SQLType.datetimeoffset, stringValue); + dto = initExpected(stringValue, fractionalSecondsDigits); + } + + private java.sql.Date expectedDate() { + Calendar cal = Calendar.getInstance(new SimpleTimeZone(1000 * 60 * dto.getMinutesOffset(), ""), Locale.US); + cal.setTimeInMillis(dto.getTimestamp().getTime()); + cal.set(Calendar.HOUR_OF_DAY, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + return new java.sql.Date(cal.getTimeInMillis()); + } + + private java.sql.Time expectedTime() { + Calendar cal = Calendar.getInstance(new SimpleTimeZone(1000 * 60 * dto.getMinutesOffset(), ""), Locale.US); + cal.set(Calendar.ZONE_OFFSET, 1000 * 60 * dto.getMinutesOffset()); + cal.setTimeInMillis(dto.getTimestamp().getTime()); + if (dto.getTimestamp().getNanos() % 1000000 >= 500000) + cal.add(Calendar.MILLISECOND, 1); + cal.set(Calendar.YEAR, 1970); + cal.set(Calendar.MONTH, Calendar.JANUARY); + cal.set(Calendar.DAY_OF_MONTH, 1); + return new java.sql.Time(cal.getTimeInMillis()); + } + + private java.sql.Timestamp expectedTimestamp() { + Calendar cal = Calendar.getInstance(new SimpleTimeZone(1000 * 60 * dto.getMinutesOffset(), ""), Locale.US); + cal.set(Calendar.ZONE_OFFSET, 1000 * 60 * dto.getMinutesOffset()); + cal.setTimeInMillis(dto.getTimestamp().getTime()); + if (dto.getTimestamp().getNanos() % 1000000 >= 500000) + cal.add(Calendar.MILLISECOND, 1); + return new java.sql.Timestamp(cal.getTimeInMillis()); + } + + private java.util.Date expectedUtilDate() { + Calendar cal = Calendar.getInstance(new SimpleTimeZone(1000 * 60 * dto.getMinutesOffset(), ""), Locale.US); + cal.set(Calendar.ZONE_OFFSET, 1000 * 60 * dto.getMinutesOffset()); + cal.setTimeInMillis(dto.getTimestamp().getTime()); + if (dto.getTimestamp().getNanos() % 1000000 >= 500000) + cal.add(Calendar.MILLISECOND, 1); + return new java.util.Date(cal.getTimeInMillis()); + } + + private java.util.Calendar expectedCalendar() { + Calendar cal = Calendar.getInstance(new SimpleTimeZone(1000 * 60 * dto.getMinutesOffset(), ""), Locale.US); + cal.set(Calendar.ZONE_OFFSET, 1000 * 60 * dto.getMinutesOffset()); + cal.setTimeInMillis(dto.getTimestamp().getTime()); + if (dto.getTimestamp().getNanos() % 1000000 >= 500000) + cal.add(Calendar.MILLISECOND, 1); + return cal; + } + + void verifyRSGetters(ResultSet rs) throws Exception { + assertEquals(rs.getDate(1), expectedDate(), "getDate mismatch"); + + assertEquals(rs.getTime(1), expectedTime(), "getTime mismatch"); + + assertEquals(rs.getTimestamp(1), dto.getTimestamp(), "getTimestamp mismatch"); + + assertEquals(((SQLServerResultSet) rs).getDateTimeOffset(1), dto, "getDateTimeOffset mismatch"); + + assertEquals(rs.getString(1), this.getString(), "getString mismatch"); + } + + void verifyRSUpdaters(ResultSet rs) throws Exception { + ((SQLServerResultSet) rs).updateDateTimeOffset(1, dto); + rs.updateRow(); + + assertEquals(((SQLServerResultSet) rs).getDateTimeOffset(1), dto, "updateDateTimeOffset mismatch"); + } + + void verifySetters(PreparedStatement ps) throws Exception { + ((SQLServerPreparedStatement) ps).setDateTimeOffset(1, dto); + ps.execute(); + ps.getMoreResults(); + ResultSet rs = ps.getResultSet(); + rs.next(); + + assertEquals(((SQLServerResultSet) rs).getDateTimeOffset(1), dto, "setDateTimeOffset mismatch"); + } + + void verifySettersUtilDate(PreparedStatement ps) throws Exception { + /* + * Cannot test by setObject(...,java.util.Date) with the back end data type as datetimeoffset because by + * design anything but datetimeoffset is normalized to UTC time zone in the source code (in sendTemporal + * function in dtv.java) setObject(...,java.sql.Date) and setObject(...,java.utl.date) gets same exception + */ + } + + void verifySettersCalendar(PreparedStatement ps) throws Exception { + /* + * Cannot test by setObject(...,java.util.Date) with the back end data type as datetimeoffset because by + * design anything but datetimeoffset is normalized to UTC time zone in the source code (in sendTemporal + * function in dtv.java) + */ + } + + void verifyCSGetters(CallableStatement cs) throws Exception { + ((SQLServerCallableStatement) cs).setDateTimeOffset(1, dto); + cs.registerOutParameter(2, microsoft.sql.Types.DATETIMEOFFSET); + cs.execute(); + assertEquals(((SQLServerCallableStatement) cs).getDateTimeOffset(2), dto, "getDateTimeOffset mismatch"); + + assertEquals(cs.getObject(2), dto, "getObject mismatch"); + } + } + + enum TestValue { + POST_GREGORIAN_DATETIME2(new DateTime2Value("1582-10-25 15:07:09.0810000")), + + PRE_GREGORIAN_DTO_VALUE(new DateTimeOffsetValue("1414-01-05 00:00:00.0000000 -08:00")), + + PRE_GREGORIAN_DATETIME2_VALUE(new DateTime2Value("1414-01-05 00:00:00.0000000")), + + ANOTHER_TEST(new DateTimeOffsetValue("3431-04-13 15:23:32.7954829 -05:32")), + + BOA_VISTA(new DateTime2Value("6854-01-27 04:39:54.86772", 5, "America/Boa_Vista")), + + NEGATIVE_OFFSET(new DateTimeOffsetValue("3431-04-13 21:42:14.7954829 -05:32")), + + SOMETIME(new TimeValue("11:58:31.456789", 6)), + + THE_LAST_MILLISECOND_WITH_TIME_ZONE(new DateTimeOffsetValue("9999-12-31 23:59:59.9999999 +14:00")), + + COMMON_ERA_FIRST_DAY(new DateValue("0001-01-01")), + + PRE_CUTOVER(new DateValue("1582-10-04")), + + // Dates in the Gregorian cutover date range appear as 10 days later than what they should. + // This behavior is consistent with other JDBC drivers, such as IBM's: + // http://publib.boulder.ibm.com/infocenter/dzichelp/v2r2/index.jsp?topic=/com.ibm.db2.doc.java/com.ibm.db2.luw.apdv.java.doc/doc/r0053436.htm + CUTOVER_START(new DateValue("1582-10-05")), + + CUTOVER_END(new DateValue("1582-10-14")), + + POST_CUTOVER(new DateValue("1582-10-15")), + + // Last "slow path" date + POST_CUTOVER_PLUS_1(new DateValue("1582-10-16")), + + // First "fast path" date + POST_CUTOVER_PLUS_2(new DateValue("1582-10-17")), + + // VSTS 403522 + // Post-cutover date requiring preservation of "wall calendar" date + // in computing Calendar.DAY_OF_YEAR. + POST_CUTOVER_NOVEMBER(new DateTime2Value("1582-11-15 15:07:09.0810000")), + + A_RECENT_DATE(new DateValue("2009-10-20")), + + A_FRACTION_OF_A_SECOND_PAST_MIDNIGHT(new TimeValue("00:00:00.007", 3)), + + TIME_7(new TimeValue("11:58:31.9999999", 7)), + + DATETIME2_4(new DateTime2Value("2009-10-20 11:58:31.1234", 4)), + + DATETIMEOFFSET(new DateTimeOffsetValue("2009-10-20 11:58:31.1230000 +07:00")); + + final SQLValue sqlValue; + + TestValue(SQLValue sqlValue) { + this.sqlValue = sqlValue; + } + }; + + public void testResultSetGetters() throws Exception { + Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); + Connection conn = DriverManager.getConnection(connectionString); + + for (TestValue value : TestValue.values()) + value.sqlValue.verifyRSGetters(conn); + + conn.close(); + } + + public void testResultSetUpdaters() throws Exception { + Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); + Connection conn = DriverManager.getConnection(connectionString); + + for (TestValue value : TestValue.values()) + value.sqlValue.verifyRSUpdaters(conn); + + conn.close(); + } + + public void testSetters() throws Exception { + Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); + Connection conn = DriverManager.getConnection(connectionString + ";sendTimeAsDateTime=true"); + + for (TestValue value : TestValue.values()) + value.sqlValue.verifySetters(conn); + + conn.close(); + } + + public void testCallableStatementGetters() throws Exception { + Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); + Connection conn = DriverManager.getConnection(connectionString); + + for (TestValue value : TestValue.values()) + value.sqlValue.verifyCSGetters(conn); + + conn.close(); + } + + public void testResultSetMetaData() throws Exception { + Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); + Connection conn = DriverManager.getConnection(connectionString); + + for (TestValue value : TestValue.values()) + value.sqlValue.verifyResultSetMetaData(conn); + + conn.close(); + } + + public void testParameterMetaData() throws Exception { + Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); + Connection conn = DriverManager.getConnection(connectionString); + + for (TestValue value : TestValue.values()) + value.sqlValue.verifyParameterMetaData(conn); + + conn.close(); + } + + /** + * VSTS 411537 - CS.setObject(timestamp, TIME)/registerOutParam(TIME) on time backend type seems to ignore + * sendTimeAsDatetime knob. + */ + public void testSendTimestampAsTimeAsDatetime() throws Exception { + Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); + Connection conn = DriverManager.getConnection(connectionString + ";sendTimeAsDatetime=true"); + Statement stmt = conn.createStatement(); + try { + stmt.executeUpdate("DROP PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName)); + } catch (SQLException e) {} + stmt.executeUpdate("CREATE PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName) + " @argIn time(7), " + + " @argOut time(7) OUTPUT " + " AS " + " SET @argOut=@argIn"); + + CallableStatement cs = conn.prepareCall("{call " + AbstractSQLGenerator.escapeIdentifier(procName) + "(?,?)}"); + + // Set up a timestamp with a time component that is the last millisecond of the day... + java.sql.Timestamp ts = java.sql.Timestamp.valueOf("2010-02-15 23:59:59.999"); + + // ... and send that timestamp to the server using the TIME SQL type rather than TIMESTAMP. + // If the driver is doing the right thing, it strips the date portion and, because + // sendTimeAsDatetime=true, rounds the resulting time value to midnight because it should + // be sending a DATETIME which has only 1/300s accuracy. + cs.setObject(1, ts, java.sql.Types.TIME); + cs.registerOutParameter(2, java.sql.Types.TIME); + cs.execute(); + + // Fetch the OUT parameter and verify that we have a date-normalized TIME of midnight + java.sql.Time timeOut = cs.getTime(2); + java.sql.Timestamp tsOut = new java.sql.Timestamp(timeOut.getTime()); + assertEquals(tsOut.toString(), "1970-01-01 00:00:00.0", "CS.getTime() - Wrong OUT value"); + + cs.close(); + stmt.executeUpdate("DROP PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName)); + stmt.close(); + conn.close(); + } + + // 507919 - Sending Timestamp to the server via an updater does not result in the same behavior as a setter wrt + // double-rounding of fractional seconds + public void testDoubleRounding() throws Exception { + try (Connection conn = DriverManager.getConnection(connectionString)) { + + // create a table with a datetimeoffset column and insert a value in it + Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); + assumeTrue(TestUtils.isSqlAzureDW(conn), TestResource.getResource("R_skipAzure")); + + String sql; + try (Statement stmt = conn.createStatement()) { + // SQL Azure requires each table to have a clustered index, so change col1 to the primary key + sql = "CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName) + + " (col1 int primary key, col2 datetimeoffset(6))"; + stmt.executeUpdate(sql); + sql = "INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableName) + + " VALUES(1, '2010-04-29 10:51:12.123456 +00:00')"; + stmt.executeUpdate(sql); + + } + + try (Statement stmt = conn.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE)) { + + DateTimeOffset dto; + DateTimeOffset actualDto; + + // create select query and update the datetimeoffset + String query = "SELECT * FROM " + AbstractSQLGenerator.escapeIdentifier(tableName); + + try (ResultSet rs = stmt.executeQuery(query)) { + rs.next(); + + // when double-rounding to scale of 6, nanos should be 832909 + java.sql.Timestamp ts = java.sql.Timestamp.valueOf("3432-12-21 15:22:58.832908453"); + dto = DateTimeOffset.valueOf(ts, 0); + + // ts value that goes to driver is 3432-12-21 15:22:58.832908453 + rs.updateTimestamp(2, ts); + rs.updateRow(); // update row + } + + // get the datetime offset from server + sql = "SELECT col2 FROM " + AbstractSQLGenerator.escapeIdentifier(tableName); + try (ResultSet rs = stmt.executeQuery(sql)) { + rs.next(); + Object value = rs.getObject(1); + // 3432-12-21 15:22:58.832909 + actualDto = (DateTimeOffset) value; + } + + // compare after doing a cast on the server to datetimeoffset(6) + sql = "SELECT CAST('" + dto.getTimestamp().toString() + "' as datetimeoffset(6))"; + try (ResultSet rs = stmt.executeQuery(sql)) { + rs.next(); + // 3432-12-21 23:22:58.832909 +00:00 + DateTimeOffset expectedDto = (DateTimeOffset) rs.getObject(1); + assertEquals(actualDto, expectedDto, + "Actual dto: " + actualDto.toString() + ";Expected dto: " + expectedDto.toString()); + } + } finally { + try (Statement stmt = conn.createStatement()) { + stmt.executeUpdate("drop table " + AbstractSQLGenerator.escapeIdentifier(tableName)); + } catch (SQLException e) { + fail(TestResource.getResource("R_createDropTableFailed") + e.toString()); + } + } + } + } + + /** + * Tests "fail fast" SQLException path when a Japanese imperial calendar is used with values representing the first + * year of an imperial era. + * + * See for more details: http://java.sun.com/javase/6/docs/technotes/guides/intl/calendar.doc.html + */ + public void testWithJapaneseImperialCalendar() throws Exception { + // From http://java.sun.com/javase/6/docs/api/java/util/Locale.html : + // "Note: When you ask for a resource for a particular locale, + // you get back the best available match, not necessarily precisely what you asked for. + // For more information, look at ResourceBundle." + // + // Japanese Imperial locale does not exist with some VMs. In these VMs, Locale.US is + // substituted as "best available match". + Locale japaneseImperialLocale = new Locale("ja", "JP", "JP"); + Calendar japaneseImperialCalendar = Calendar.getInstance(japaneseImperialLocale); + + MessageFormat cal = new MessageFormat(TestResource.getResource("R_noJRESupport")); + assumeTrue(GregorianCalendar.class.isInstance(japaneseImperialCalendar), + cal.format(japaneseImperialLocale.toString())); + + Locale defaultLocale = Locale.getDefault(); + Locale.setDefault(japaneseImperialLocale); + try { + Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); + Connection conn = DriverManager.getConnection(connectionString + ";sendTimeAsDatetime=true"); + ResultSet rs; + + // Get Gregorian date using Japanese imperial calendar + rs = conn.createStatement().executeQuery("SELECT CAST('0821-01-04' AS DATE)"); + rs.next(); + java.sql.Date date = rs.getDate(1, japaneseImperialCalendar); + assertEquals(date.toString(), "0821-01-04", "Get pre-Meiji"); + rs.close(); + + PreparedStatement ps = conn.prepareStatement("SELECT CAST(? AS VARCHAR(40))"); + java.sql.Timestamp ts; + + // Set second day of first year of Taisho era (1912 Gregorian) + // Note: Taisho era began July 30, 1912 Gregorian; second day of that year was July 31. + japaneseImperialCalendar.clear(); + japaneseImperialCalendar.set(Calendar.ERA, 2); // Taisho -> ERA 2 + japaneseImperialCalendar.set(Calendar.YEAR, 1); + japaneseImperialCalendar.set(Calendar.DAY_OF_YEAR, 2); + ts = new java.sql.Timestamp(japaneseImperialCalendar.getTimeInMillis()); + ps.setTimestamp(1, ts, japaneseImperialCalendar); + rs = ps.executeQuery(); + rs.next(); + assertEquals(rs.getString(1), "1912-07-31 00:00:00.0000000", "Set Taisho 1"); + rs.close(); + + // Set second year of Showa era (1927 Gregorian) + japaneseImperialCalendar.clear(); + japaneseImperialCalendar.set(Calendar.ERA, 3); // Showa -> ERA 3 + japaneseImperialCalendar.set(Calendar.YEAR, 2); + japaneseImperialCalendar.set(Calendar.MONTH, Calendar.FEBRUARY); + japaneseImperialCalendar.set(Calendar.DATE, 15); + japaneseImperialCalendar.set(Calendar.HOUR_OF_DAY, 8); + japaneseImperialCalendar.set(Calendar.MINUTE, 49); + japaneseImperialCalendar.set(Calendar.SECOND, 3); + japaneseImperialCalendar.set(Calendar.MILLISECOND, 87); + ts = new java.sql.Timestamp(japaneseImperialCalendar.getTimeInMillis()); + ps.setTimestamp(1, ts, japaneseImperialCalendar); + rs = ps.executeQuery(); + rs.next(); + assertEquals(rs.getString(1), "1927-02-15 08:49:03.0870000", "Set Showa 2"); + rs.close(); + + ps.close(); + conn.close(); + } finally { + Locale.setDefault(defaultLocale); + } + } + + enum StringFormatTestValue { + TIME_ZEROS_FILL("02:34:56.1", "TIME(3)"), + + TIME_NO_FRACTIONAL_SECONDS_WITH_SCALE("02:34:56", "TIME(3)"), + + TIME_ROUNDED_WITH_SCALE("02:34:56.777", "TIME(2)"), + + TIME_WITH_ZERO_SCALE("02:34:56", "TIME(0)"), + + DATETIME2_ZEROS_FILL("2010-03-10 02:34:56.1", "DATETIME2(3)"), + + DATETIME2_NO_FRACTIONAL_SECONDS_WITH_SCALE("2010-03-10 02:34:56", "DATETIME2(3)"), + + DATETIME2_ROUNDED_WITH_SCALE("2010-03-10 02:34:56.777", "DATETIME2(2)"), + + DATETIME2_WITH_ZERO_SCALE("2010-03-10 02:34:56", "DATETIME2(0)"), + + DATETIMEOFFSET_ZEROS_FILL("2010-03-10 02:34:56.1 -08:00", "DATETIMEOFFSET(3)"), + + DATETIMEOFFSET_NO_FRACTIONAL_SECONDS_WITH_SCALE("2010-03-10 02:34:56 -08:00", "DATETIMEOFFSET(3)"), + + DATETIMEOFFSET_ROUNDED_WITH_SCALE("2010-03-10 02:34:56.777 -08:00", "DATETIMEOFFSET(2)"), + + DATETIMEOFFSET_WITH_ZERO_SCALE("2010-03-10 02:34:56 -08:00", "DATETIMEOFFSET(0)"); + + final String sqlLiteral; + final String sqlType; + + StringFormatTestValue(String sqlLiteral, String sqlType) { + this.sqlLiteral = sqlLiteral; + this.sqlType = sqlType; + } + } + + @Test + public void testGetString() throws Exception { + Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); + Connection conn = DriverManager.getConnection(connectionString); + Statement stmt = conn.createStatement(); + + for (StringFormatTestValue testValue : EnumSet.allOf(StringFormatTestValue.class)) { + String query = "SELECT " + "CAST('" + testValue.sqlLiteral + "' AS " + testValue.sqlType + "), " + + "CAST(CAST('" + testValue.sqlLiteral + "' AS " + testValue.sqlType + ") AS VARCHAR)"; + + ResultSet rs = stmt.executeQuery(query); + rs.next(); + assertEquals(rs.getString(1), rs.getString(2), "Test failed for " + testValue); + rs.close(); + } + + stmt.close(); + conn.close(); + } + + @Test + public void testWithThaiLocale() throws Exception { + Locale locale = Locale.getDefault(); + Locale.setDefault(new Locale("th", "TH")); + try { + Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); + Connection conn = DriverManager.getConnection(connectionString + ";sendTimeAsDatetime=true"); + + ResultSet rs; + + // Test setter conversions + PreparedStatement ps = conn.prepareStatement("SELECT CAST(? AS VARCHAR(40))"); + + // Need to use the following constructor for running against IBM JVM. Here, year should be year-1900, month + // is from 0-11. + java.sql.Timestamp ts = new java.sql.Timestamp(2009 - 1900, 10, 17, 15, 23, 32, 0); + + // Test PreparedStatement with Timestamp + // Value sent as DATETIME2; result should have 7 digits of subsecond precision) + ps.setTimestamp(1, ts); + rs = ps.executeQuery(); + rs.next(); + assertEquals(rs.getString(1), "2009-11-17 15:23:32.0000000", "Timestamp mismatch"); + rs.close(); + + // Test PreparedStatement with Time + // Value sent as DATETIME w/Unix Epoch as base date when sendTimeAsDatetime=true + java.sql.Time time = new java.sql.Time(ts.getTime()); + ps.setTime(1, time); + rs = ps.executeQuery(); + rs.next(); + assertEquals(rs.getString(1), "Jan 1 1970 3:23PM", "Time mismatch"); + rs.close(); + + // Test PreparedStatement with Date + java.sql.Date date = new java.sql.Date(ts.getTime()); + ps.setDate(1, date); + rs = ps.executeQuery(); + rs.next(); + assertEquals(rs.getString(1), "2009-11-17", "Date mismatch"); + rs.close(); + + // Test PreparedStatement with Date (using Buddhist calendar) + date = new java.sql.Date(ts.getTime()); + ps.setDate(1, date, Calendar.getInstance()); + rs = ps.executeQuery(); + rs.next(); + assertEquals(rs.getString(1), "2009-11-17", "Date mismatch (w/calendar)"); + rs.close(); + + // Test PreparedStatement with DateTimeOffset (using Buddhist calendar) + // Note: Expected value does not reflect Buddhist year, even though a Buddhist calendar is used. + DateTimeOffset dto = DateTimeOffset.valueOf(ts, Calendar.getInstance()); + ((SQLServerPreparedStatement) ps).setDateTimeOffset(1, dto); + rs = ps.executeQuery(); + rs.next(); + assertEquals(rs.getString(1), "2009-11-17 15:23:32.0000000 -08:00", "DateTimeOffset mismatch"); + rs.close(); + + ps.close(); + conn.close(); + } finally { + Locale.setDefault(locale); + } + } + + // DCR 393826 - DCR Need base date compatibility for Time to DATETIMEx conversions with 2.0 driver + @Test + public void testBaseDate() throws Exception { + Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); + + // Test Java base date (1/1/1970) + Connection conn = DriverManager.getConnection(connectionString + ";sendTimeAsDatetime=true"); + PreparedStatement ps = conn.prepareStatement("SELECT CAST(? AS DATETIME)"); + ps.setTime(1, java.sql.Time.valueOf("12:34:56")); + ResultSet rs = ps.executeQuery(); + rs.next(); + Timestamp ts = rs.getTimestamp(1); + assertEquals(ts.toString(), "1970-01-01 12:34:56.0", "Expected Java base date"); + rs.close(); + ps.close(); + conn.close(); + + // Test SQL Server base date (1/1/1900) + conn = DriverManager.getConnection(connectionString + ";sendTimeAsDatetime=false"); + ps = conn.prepareStatement("SELECT CAST(? AS DATETIME)"); + ps.setTime(1, java.sql.Time.valueOf("12:34:56")); + rs = ps.executeQuery(); + rs.next(); + ts = rs.getTimestamp(1); + assertEquals(ts.toString(), "1900-01-01 12:34:56.0", "Expected SQL Server base date"); + rs.close(); + ps.close(); + conn.close(); + } + + // VSTS 393831 - setTimestamp to DATETIMEOFFSET must yield a value in local time with UTC time zone offset (+00:00) + @Test + public void testTimestampToDateTimeOffset() throws Exception { + Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); + + Connection conn = DriverManager.getConnection(connectionString); + PreparedStatement ps = conn.prepareStatement("SELECT CAST(? AS DATETIMEOFFSET)"); + ps.setTimestamp(1, java.sql.Timestamp.valueOf("2010-01-06 12:34:56")); + ResultSet rs = ps.executeQuery(); + rs.next(); + DateTimeOffset dto = ((SQLServerResultSet) rs).getDateTimeOffset(1); + assertEquals(dto.toString(), "2010-01-06 12:34:56 +00:00", "Wrong value"); + rs.close(); + ps.close(); + conn.close(); + } + + // VSTS 400431 - PS.setObject() on a datetime2 with values on or after 0700-02-29 have a value one day ahead stored + // in the server + @Test + public void testJulianLeapYear() throws Exception { + Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); + Connection conn = DriverManager.getConnection(connectionString + ";sendTimeAsDatetime=true"); + // PreparedStatement ps = conn.prepareStatement("SELECT CAST(? AS VARCHAR)"); + PreparedStatement ps = conn.prepareStatement("SELECT CAST(? AS DATE)"); + ResultSet rs; + + // Julian date from string + java.sql.Date julianDate = java.sql.Date.valueOf("0700-03-01"); + ps.setDate(1, julianDate); + rs = ps.executeQuery(); + rs.next(); + assertEquals(rs.getString(1), "0700-03-01", "Wrong date returned"); + rs.close(); + + ps.close(); + conn.close(); + } + + @Test + public void testGetTimeRounding() throws Exception { + Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); + + Connection conn = DriverManager.getConnection(connectionString + ";sendTimeAsDatetime=true"); + Statement stmt = conn.createStatement(); + ResultSet rs; + + // Test getTime() rounding from TIME(6) SQL type + rs = stmt.executeQuery("SELECT CAST('12:34:56.999500' AS TIME)"); + rs.next(); + assertEquals(rs.getTime(1).toString(), "12:34:57", "Rounding from TIME(6) failed"); + rs.close(); + + // Test getTime() rounding from character data + rs = stmt.executeQuery("SELECT '12:34:56.999500'"); + rs.next(); + assertEquals(rs.getTime(1).toString(), "12:34:57", "Rounding from character data failed"); + rs.close(); + + stmt.close(); + conn.close(); + } + + @Test + public void testGregorianCutoverDateTime2() throws Exception { + Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); + + Connection conn = DriverManager.getConnection(connectionString + ";sendTimeAsDatetime=true"); + PreparedStatement ps = conn.prepareStatement("SELECT CAST(? AS VARCHAR)"); + ResultSet rs; + Timestamp ts; + + // Test setting value during the Gregorian cutover via Timestamp constructed from String + ts = java.sql.Timestamp.valueOf("1582-10-24 15:07:09.081"); + ps.setTimestamp(1, ts); + rs = ps.executeQuery(); + rs.next(); + assertEquals(rs.getString(1), "1582-10-24 15:07:09.0810000", "Wrong value"); + rs.close(); + + // Test setting value during the Gregorian cutover via Timestamp constructed from Calendar + Calendar cal = Calendar.getInstance(); + cal.set(1582, Calendar.NOVEMBER, 1, 15, 7, 9); + cal.set(Calendar.MILLISECOND, 81); + ts = new java.sql.Timestamp(cal.getTimeInMillis()); + ps.setTimestamp(1, ts); + rs = ps.executeQuery(); + rs.next(); + assertEquals(rs.getString(1), "1582-11-01 15:07:09.0810000", "Wrong value"); + rs.close(); + + ps.close(); + conn.close(); + } + + // VSTS 393831 - setTimestamp to DATETIMEOFFSET must yield a value in local time with UTC time zone offset (+00:00) + // + // In this case, verify that SELECT with a WHERE clause doesn't fail due to mapping the Timestamp value to a + // SQL Server type that does not compare equal. For example, a DATETIMEOFFSET and DATETIME only compare equal + // if the DATETIMEOFFSET offset is 0 (UTC). + @Test + public void testTimestampToDateTime() throws Exception { + Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); + + Connection conn = DriverManager.getConnection(connectionString); + PreparedStatement ps = conn.prepareStatement("SELECT 1 WHERE ?=CAST('2009-12-17 17:00:29' AS DATETIME)"); + ps.setTimestamp(1, java.sql.Timestamp.valueOf("2009-12-17 17:00:29")); + ResultSet rs = ps.executeQuery(); + assertEquals(rs.next(), true, "Timestamp to DATETIME comparison failed for equal values"); + rs.close(); + ps.close(); + conn.close(); + } + + // testUpdateMisc + // + // Haphazard collection of bugs that popped up during unit testing (i.e. regression tests) + @Test + public void testUpdateMisc() throws Exception { + + // + ""[" + driver.createuniqueidentifer("testUpdateMisc") + "]"; + Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); + + SQLServerConnection conn = (SQLServerConnection) DriverManager + .getConnection(connectionString + ";sendTimeAsDatetime=true"); + + assumeTrue(TestUtils.isSqlAzureDW(conn), TestResource.getResource("R_skipAzure")); + + Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); + try { + stmt.executeUpdate("DROP TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName)); + } catch (SQLException e) {} + stmt.executeUpdate("CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName) + + " (col1 datetimeoffset(2)," + " col2 datetime," + " col3 time(5)," + " col4 datetime2(5)," + + " col5 smalldatetime," + " col6 time(2)," + " col7 int identity(1,1) primary key)"); + stmt.executeUpdate("INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableName) + " VALUES (" + + " '2010-01-12 09:00:23.17 -08:00'," + " '2010-01-12 10:20:23'," + " '10:20:23'," + + " '2010-01-12 10:20:23'," + " '2010-01-12 11:45:17'," + " '10:20:23')"); + + try { + DateTimeOffset dto; + + ResultSet rs = stmt.executeQuery( + "SELECT *, CAST(col1 AS VARCHAR) FROM " + AbstractSQLGenerator.escapeIdentifier(tableName)); + rs.next(); + + // Update datetimeoffset(2) from pre-Gregorian Date + rs.updateDate(1, java.sql.Date.valueOf("0814-02-18")); + rs.updateRow(); + assertEquals(((SQLServerResultSet) rs).getDateTimeOffset(1).toString(), "0814-02-18 00:00:00 +00:00", + "Update of datetimeoffset(2) from pre-Gregorian Date"); + + // Update datetimeoffset(2) from "last" Time + rs.updateTime(1, new java.sql.Time(Timestamp.valueOf("1970-01-01 23:59:59.998").getTime())); + rs.updateRow(); + assertEquals(((SQLServerResultSet) rs).getDateTimeOffset(1).toString(), "1970-01-02 00:00:00 +00:00", + "Update datetimeoffset(2) from last Time"); + + // Update datetimeoffset(2) from the "last" Timestamp + rs.updateTimestamp(1, java.sql.Timestamp.valueOf("9999-12-31 23:59:59.998")); + rs.updateRow(); + dto = ((SQLServerResultSet) rs).getDateTimeOffset(1); + assertEquals(dto.toString(), "9999-12-31 23:59:59.99 +00:00", + "Update datetimeoffset(2) from last Timestamp"); + + // Attempt to update datetimeoffset(2) from the first out of range value + // Verify that an exception is thrown and that the statement/connection is still usable after + try { + java.sql.Timestamp tsInvalid = java.sql.Timestamp.valueOf("9999-12-31 23:59:59.999999999"); + tsInvalid = new java.sql.Timestamp(tsInvalid.getTime() + 1); + rs.updateTimestamp(1, tsInvalid); + rs.updateRow(); + assertEquals(false, true, "Update succeeded with out of range value"); + } catch (SQLServerException e) { + assertEquals(e.getSQLState(), "22008", // data exception - datetime field overflow (ISO/IEC + // 9075-2:1999) + "Wrong exception received"); + } + + // Update time(5) from Timestamp with nanos more precise than 100ns + Timestamp ts = java.sql.Timestamp.valueOf("2010-01-12 11:05:23"); + ts.setNanos(987659999); + rs.updateTimestamp(3, ts); + rs.updateRow(); + assertEquals(rs.getTimestamp(3).toString(), "1900-01-01 11:05:23.98766", + "Update time(5) from Timestamp with sub-100ns nanos"); + + // Update time(5) from Timestamp to max value in a day. The value should not be rounded + ts = java.sql.Timestamp.valueOf("2010-01-12 23:59:59"); + ts.setNanos(999999999); + Time time = new java.sql.Time(ts.getTime()); + rs.updateTimestamp(3, ts); + rs.updateRow(); + assertEquals(rs.getTimestamp(3).toString(), "1900-01-01 23:59:59.99999", + "Update time(5) from Timestamp to max value in a day"); + + // Update time(2) from Time to max value in a day. The value should not be rounded + rs.updateTime(6, time); + rs.updateRow(); + assertEquals(new java.sql.Timestamp(rs.getTime(6).getTime()).toString(), // conversion to timestamp is + // necessary to see fractional + // secs + "1970-01-01 23:59:59.99", "Update time(2) from Time to max value in a day"); + + // Update time(5) from Timestamp to max value in a second. The value should be rounded + ts = java.sql.Timestamp.valueOf("2010-01-12 23:59:58"); + ts.setNanos(999999999); + time = new java.sql.Time(ts.getTime()); + rs.updateTimestamp(3, ts); + rs.updateRow(); + assertEquals(rs.getTimestamp(3).toString(), "1900-01-01 23:59:59.0", + "Update time(5) from Timestamp to max value in a second"); + + // Update time(2) from Time to max value in a second. The value should be rounded + rs.updateTime(6, time); + rs.updateRow(); + assertEquals(new java.sql.Timestamp(rs.getTime(6).getTime()).toString(), // conversion to timestamp is + // necessary to see fractional + // secs + "1970-01-01 23:59:59.0", "Update time(2) from Time to max value in a second"); + + // Update datetime w/expected rounding of nanos to DATETIME's 1/300second resolution + ts = java.sql.Timestamp.valueOf("6289-04-22 05:13:57.6745106"); + rs.updateTimestamp(2, ts); + rs.updateRow(); + assertEquals(rs.getTimestamp(2).toString(), "6289-04-22 05:13:57.677", + "Update datetime from Timestamp with sub-1/3second nanos"); + + // Update datetime with rounding-induced overflow from Time + // (should roll date part to 1/2/1970) + ts = java.sql.Timestamp.valueOf("2010-01-18 23:59:59.999"); + rs.updateTime(2, new java.sql.Time(ts.getTime())); + rs.updateRow(); + assertEquals(rs.getTimestamp(2).toString(), "1970-01-02 00:00:00.0", + "Update datetime from Time near next day"); + + rs.close(); + } finally { + stmt.executeUpdate("DROP TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName)); + stmt.close(); + conn.close(); + } + } + +} diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java index 6144e4e288..d7625a514f 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java @@ -356,7 +356,7 @@ public void testHoldability() throws SQLException { } } } - + /** * Call resultset methods to run thru some code paths * @@ -364,14 +364,17 @@ public void testHoldability() throws SQLException { */ @Test public void testResultSetMethods() throws SQLException { - try (Connection con = DriverManager.getConnection(connectionString); Statement stmt = con - .createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)) { + try (Connection con = DriverManager.getConnection(connectionString); + Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)) { + stmt.executeUpdate("create table " + AbstractSQLGenerator.escapeIdentifier(tableName) + + " (col1 int primary key, col2 varchar(255))"); + stmt.executeUpdate( + "insert into " + AbstractSQLGenerator.escapeIdentifier(tableName) + " values(0, " + " 'one')"); stmt.executeUpdate( - "create table " + AbstractSQLGenerator.escapeIdentifier(tableName) + " (col1 int primary key)"); - stmt.executeUpdate("insert into " + AbstractSQLGenerator.escapeIdentifier(tableName) + " values(0)"); - stmt.executeUpdate("insert into " + AbstractSQLGenerator.escapeIdentifier(tableName) + " values(1)"); - stmt.executeUpdate("insert into " + AbstractSQLGenerator.escapeIdentifier(tableName) + " values(2)"); + "insert into " + AbstractSQLGenerator.escapeIdentifier(tableName) + " values(1, " + "'two')"); + stmt.executeUpdate( + "insert into " + AbstractSQLGenerator.escapeIdentifier(tableName) + " values(2, " + "'three')"); try (ResultSet rs = stmt .executeQuery("select * from " + AbstractSQLGenerator.escapeIdentifier(tableName))) { @@ -383,13 +386,13 @@ public void testResultSetMethods() throws SQLException { // check cursor rs.first(); assert (rs.isFirst()); - + rs.relative(1); assert (!rs.isFirst()); rs.last(); assert (rs.isLast()); - + rs.beforeFirst(); assert (rs.isBeforeFirst()); @@ -402,9 +405,38 @@ public void testResultSetMethods() throws SQLException { rs.moveToInsertRow(); assert (rs.getRow() == 0); + + // insert and update + rs.updateInt(1, 4); + rs.updateString(2, "four"); + rs.insertRow(); + + rs.updateObject(1, 5); + rs.updateObject(2, new String("five")); + rs.insertRow(); + + rs.updateObject("col1", 6); + rs.updateObject("col2", new String("six")); + rs.insertRow(); + + rs.updateObject(1, 7, 0); + rs.updateObject("col2", new String("seven"), 0); + rs.insertRow(); + + // valid column names + assert (rs.findColumn("col1") == 1); + assert (rs.findColumn("col2") == 2); + + // invalid column name + try { + rs.findColumn("col3"); + } catch (SQLException e) { + assertTrue(e.getMessage().contains("column name col3 is not valid")); + } + rs.moveToCurrentRow(); assert (rs.getRow() == 1); - + // no inserts or updates assert (!rs.rowInserted()); assert (!rs.rowUpdated()); @@ -422,11 +454,15 @@ public void testResultSetMethods() throws SQLException { rs.refreshRow(); - // test delete row + rs.previous(); + assert (!rs.rowDeleted()); + rs.next(); + + // delete row do { rs.moveToCurrentRow(); - rs.deleteRow(); - assert (rs.rowDeleted()); + rs.deleteRow(); + assert (rs.rowDeleted()); } while (rs.next()); } catch (Exception e) { diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialT.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java similarity index 97% rename from src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialT.java rename to src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java index d6bcc4439a..d71a12d323 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialT.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java @@ -7,7 +7,6 @@ import org.junit.runner.RunWith; import junit.framework.*; -import javax.sql.*; import com.microsoft.sqlserver.jdbc.*; import microsoft.sql.*; @@ -18,7 +17,7 @@ @RunWith(JUnitPlatform.class) -public class DTOSerialT { +public class DTOSerialTest { private static final String dateString = "2007-05-08 12:35:29.1234567 +12:15"; // public static void testDSerial(String connString) throws Exception diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java index b43dd312e7..ac9d27dfd0 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java @@ -1111,11 +1111,11 @@ public void testJdbc41CallableStatementMethods() throws Exception { + "(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)}")) { cstmt.registerOutParameter(1, java.sql.Types.VARCHAR); cstmt.registerOutParameter(2, java.sql.Types.INTEGER); - cstmt.registerOutParameter(3, java.sql.Types.FLOAT); - cstmt.registerOutParameter(4, java.sql.Types.DECIMAL); + cstmt.registerOutParameter("col3Value", java.sql.Types.FLOAT); + cstmt.registerOutParameter(4, java.sql.Types.DECIMAL, 2); cstmt.registerOutParameter(5, microsoft.sql.Types.GUID); cstmt.registerOutParameter(6, java.sql.Types.SQLXML); - cstmt.registerOutParameter(7, java.sql.Types.VARBINARY); + cstmt.registerOutParameter("col7Value", java.sql.Types.VARBINARY, 10); cstmt.registerOutParameter(8, java.sql.Types.CLOB); cstmt.registerOutParameter(9, java.sql.Types.NCLOB); cstmt.registerOutParameter(10, java.sql.Types.VARBINARY); @@ -1123,8 +1123,8 @@ public void testJdbc41CallableStatementMethods() throws Exception { cstmt.registerOutParameter(12, java.sql.Types.TIME); cstmt.registerOutParameter(13, java.sql.Types.TIMESTAMP); cstmt.registerOutParameter(14, java.sql.Types.TIMESTAMP_WITH_TIMEZONE); - cstmt.registerOutParameter(15, java.sql.Types.DECIMAL); - cstmt.registerOutParameter(16, java.sql.Types.DECIMAL); + cstmt.registerOutParameter(15, java.sql.Types.DECIMAL, "DECIMAL"); + cstmt.registerOutParameter("col16Value", java.sql.Types.DECIMAL, "DECIMAL"); cstmt.execute(); assertEquals("hello", cstmt.getObject(1, String.class)); From 3275750d8a225ad5da9c5a1c14a7d5c8f18525a4 Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Fri, 16 Nov 2018 16:38:42 -0800 Subject: [PATCH 05/51] fixed issue with timezone --- .../sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java index 5c15b55a52..48e7e6e2ee 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java @@ -1461,10 +1461,11 @@ public void testWithThaiLocale() throws Exception { // Test PreparedStatement with DateTimeOffset (using Buddhist calendar) // Note: Expected value does not reflect Buddhist year, even though a Buddhist calendar is used. - DateTimeOffset dto = DateTimeOffset.valueOf(ts, Calendar.getInstance()); + DateTimeOffset dto = DateTimeOffset.valueOf(ts, Calendar.getInstance(TimeZone.getDefault())); ((SQLServerPreparedStatement) ps).setDateTimeOffset(1, dto); rs = ps.executeQuery(); rs.next(); + assertEquals(rs.getString(1), "2009-11-17 15:23:32.0000000 -08:00", "DateTimeOffset mismatch"); rs.close(); From 45eeef5235147f001bb87cde5b10cf3bc0d1b1db Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Fri, 16 Nov 2018 17:53:49 -0800 Subject: [PATCH 06/51] fixed issue with timezone --- .../jdbc/datatypes/KatmaiDataTypesTest.java | 30 ++++++------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java index 48e7e6e2ee..f1b762924a 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java @@ -30,16 +30,6 @@ public class KatmaiDataTypesTest extends AbstractTest { final static String tableName = RandomUtil.getIdentifier("KatmaiDataTypesTable"); final static String procName = RandomUtil.getIdentifier("KatmaiDataTypesTableProc"); - /* - * public void testSparse() throws Exception { - * assumeTrue(!isSqlAzure(DriverManager.getConnection(connectionString)), TestResource.getResource("R_skipAzure")); - * try (Connection connection = DriverManager.getConnection(connectionString) { - * SparseTest.runTest(connectionString); } } public void testJDBC41BigInteger() throws Exception { if - * (fxConnection.isServerSqlAzureDW()) { - * CTestLog.Skip("this test is skipped because it cannot be meaningfully tested on SQL Azure DW."); } - * BigIntegerTest.runTest(connectionString); } - */ - enum SQLType { date("yyyy-mm-dd", 0, java.sql.Types.DATE, "java.sql.Date"), @@ -133,9 +123,6 @@ void verifyResultSetMetaData(Connection conn) throws Exception { void verifyParameterMetaData(Connection conn) throws Exception { - final String procName = RandomUtil.getIdentifier("testParameterMetaData"); - // final String procName = "[" + driver.createuniqueidentifer("testParameterMetaData") + "]"; - Statement stmt = null; PreparedStatement pstmt = null; @@ -143,9 +130,9 @@ void verifyParameterMetaData(Connection conn) throws Exception { stmt = conn.createStatement(); // Create the stored proc - stmt.executeUpdate("CREATE PROCEDURE " + procName + " @arg " + sqlTypeExpression + " AS SELECT @arg"); + stmt.executeUpdate("CREATE PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName) + " @arg " + sqlTypeExpression + " AS SELECT @arg"); - pstmt = conn.prepareStatement("{call " + procName + "(?)}"); + pstmt = conn.prepareStatement("{call " + AbstractSQLGenerator.escapeIdentifier(procName) + "(?)}"); ParameterMetaData metadata = pstmt.getParameterMetaData(); assertEquals(metadata.getParameterType(1), sqlType.jdbcType, @@ -165,7 +152,7 @@ void verifyParameterMetaData(Connection conn) throws Exception { pstmt.close(); if (null != stmt) { - stmt.executeUpdate("DROP PROCEDURE " + procName); + stmt.executeUpdate("DROP PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName)); stmt.close(); } } @@ -187,7 +174,7 @@ void verifyRSGetters(Connection conn) throws Exception { void verifyRSUpdaters(Connection conn) throws Exception { - assumeTrue(TestUtils.isSqlAzureDW(conn), TestResource.getResource("R_skipAzure")); + assumeTrue(!TestUtils.isSqlAzureDW(conn), TestResource.getResource("R_skipAzure")); Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); try { @@ -223,7 +210,7 @@ void verifyRSUpdaters(Connection conn) throws Exception { abstract void verifySettersCalendar(PreparedStatement ps) throws Exception; void verifySetters(Connection conn) throws Exception { - assumeTrue(TestUtils.isSqlAzureDW(conn), TestResource.getResource("R_skipAzure")); + assumeTrue(!TestUtils.isSqlAzureDW(conn), TestResource.getResource("R_skipAzure")); Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); try { @@ -1218,7 +1205,7 @@ public void testDoubleRounding() throws Exception { // create a table with a datetimeoffset column and insert a value in it Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); - assumeTrue(TestUtils.isSqlAzureDW(conn), TestResource.getResource("R_skipAzure")); + assumeTrue(!TestUtils.isSqlAzureDW(conn), TestResource.getResource("R_skipAzure")); String sql; try (Statement stmt = conn.createStatement()) { @@ -1461,7 +1448,8 @@ public void testWithThaiLocale() throws Exception { // Test PreparedStatement with DateTimeOffset (using Buddhist calendar) // Note: Expected value does not reflect Buddhist year, even though a Buddhist calendar is used. - DateTimeOffset dto = DateTimeOffset.valueOf(ts, Calendar.getInstance(TimeZone.getDefault())); + DateTimeOffset dto = DateTimeOffset.valueOf(ts, Calendar.getInstance(TimeZone.getTimeZone("America/Los_Angeles"))); + ((SQLServerPreparedStatement) ps).setDateTimeOffset(1, dto); rs = ps.executeQuery(); rs.next(); @@ -1632,7 +1620,7 @@ public void testUpdateMisc() throws Exception { SQLServerConnection conn = (SQLServerConnection) DriverManager .getConnection(connectionString + ";sendTimeAsDatetime=true"); - assumeTrue(TestUtils.isSqlAzureDW(conn), TestResource.getResource("R_skipAzure")); + assumeTrue(!TestUtils.isSqlAzureDW(conn), TestResource.getResource("R_skipAzure")); Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); try { From c5f4a9d415ffeb6e83ca5695ab1d5408dfe4ec38 Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Mon, 19 Nov 2018 13:26:09 -0800 Subject: [PATCH 07/51] modified testWithThaiLocale to use current time --- .../jdbc/datatypes/KatmaiDataTypesTest.java | 126 +++++++++--------- 1 file changed, 66 insertions(+), 60 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java index f1b762924a..5dca35f08f 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java @@ -9,6 +9,8 @@ import java.math.*; import java.util.*; +import java.sql.Date; +import java.sql.Timestamp; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; @@ -31,7 +33,7 @@ public class KatmaiDataTypesTest extends AbstractTest { final static String procName = RandomUtil.getIdentifier("KatmaiDataTypesTableProc"); enum SQLType { - date("yyyy-mm-dd", 0, java.sql.Types.DATE, "java.sql.Date"), + date("yyyy-mm-dd", 0, java.sql.Types.DATE, "Date"), time("hh:mm:ss", 7, java.sql.Types.TIME, "java.sql.Time"), @@ -265,15 +267,15 @@ void verifyCSGetters(Connection conn) throws Exception { } static final class DateValue extends SQLValue { - private final java.sql.Date expected; + private final Date expected; DateValue(String stringValue) { super(SQLType.date, stringValue); - this.expected = java.sql.Date.valueOf(stringValue); + this.expected = Date.valueOf(stringValue); } - private java.sql.Timestamp expectedTimestampMillisPrecision() { - return new java.sql.Timestamp(expected.getTime()); + private Timestamp expectedTimestampMillisPrecision() { + return new Timestamp(expected.getTime()); } private java.util.Date expectedUtilDate() { @@ -291,7 +293,7 @@ void verifyRSGetters(ResultSet rs) throws Exception { assertEquals(rs.getDate(1), expected, "getDate mismatch"); - assertEquals(rs.getTimestamp(1), new java.sql.Timestamp(expected.getTime()), "getTimestamp mismatch"); + assertEquals(rs.getTimestamp(1), new Timestamp(expected.getTime()), "getTimestamp mismatch"); assertEquals(rs.getString(1), expected.toString(), "getString mismatch"); @@ -379,7 +381,7 @@ void verifySettersCalendar(PreparedStatement ps) throws Exception { ResultSet rs = ps.getResultSet(); rs.next(); assertEquals(rs.getDate(1), expected, "getDate mismatch"); - // Cannot test rs.getObject for the Calendar object type, as none of Time, Timestamp, java.sql.Date + // Cannot test rs.getObject for the Calendar object type, as none of Time, Timestamp, Date // or java.util.Date can be cast to a calendar // Test the additional conversions introduced in JDBC41 for types setters @@ -443,12 +445,12 @@ private void initExpected(String stringValue, TimeZone tz) { this.tz = tz; } - private java.sql.Timestamp expectedTimestamp() { + private Timestamp expectedTimestamp() { Calendar cal = Calendar.getInstance(tz); cal.clear(); cal.set(1900, Calendar.JANUARY, 1, hour, minute, second); - java.sql.Timestamp timestamp = new java.sql.Timestamp(cal.getTimeInMillis()); + Timestamp timestamp = new Timestamp(cal.getTimeInMillis()); timestamp.setNanos(nanos); return timestamp; @@ -464,13 +466,13 @@ private java.sql.Time expectedTime() { return new java.sql.Time(cal.getTimeInMillis()); } - private java.sql.Timestamp expectedTimestampMillisPrecision() { + private Timestamp expectedTimestampMillisPrecision() { Calendar cal = Calendar.getInstance(tz); cal.clear(); cal.set(1900, Calendar.JANUARY, 1, hour, minute, second); cal.set(Calendar.MILLISECOND, (nanos + 500000) / 1000000); - java.sql.Timestamp timestamp = new java.sql.Timestamp(cal.getTimeInMillis()); + Timestamp timestamp = new Timestamp(cal.getTimeInMillis()); return timestamp; } @@ -588,7 +590,7 @@ void verifySettersCalendar(PreparedStatement ps) throws Exception { assertEquals(rs.getTime(1), expectedTime(), "getTime mismatch"); assertEquals(rs.getTimestamp(1), expectedTimestampMillisPrecision(), "getTimestamp mismatch"); - // Cannot test rs.getObject for the Calendar object type, as none of Time, Timestamp, java.sql.Date + // Cannot test rs.getObject for the Calendar object type, as none of Time, Timestamp, Date // or java.util.Date can be cast to a calendar // Test the additional conversions introduced in JDBC41 for types setters @@ -676,14 +678,14 @@ private void initExpected(String timestampString, TimeZone tz) { this.tz = tz; } - private java.sql.Date expectedDate() { + private Date expectedDate() { Calendar cal = Calendar.getInstance(tz); cal.setTimeInMillis(utcMillis); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); - return new java.sql.Date(cal.getTimeInMillis()); + return new Date(cal.getTimeInMillis()); } private java.sql.Time expectedTime() { @@ -697,14 +699,14 @@ private java.sql.Time expectedTime() { return new java.sql.Time(cal.getTimeInMillis()); } - private java.sql.Timestamp expectedTimestamp() { - Timestamp timestamp = new java.sql.Timestamp(utcMillis); + private Timestamp expectedTimestamp() { + Timestamp timestamp = new Timestamp(utcMillis); timestamp.setNanos(nanos); return timestamp; } - private java.sql.Timestamp expectedTimestampMillisPrecision() { - Timestamp timestamp = new java.sql.Timestamp(utcMillis); + private Timestamp expectedTimestampMillisPrecision() { + Timestamp timestamp = new Timestamp(utcMillis); // Cannot set the nanos to 0, as per doc " the fractional seconds are stored in the nanos field of the // Timestamp object." // timestamp.setNanos(0); @@ -849,7 +851,7 @@ void verifySettersCalendar(PreparedStatement ps) throws Exception { ResultSet rs = ps.getResultSet(); rs.next(); assertEquals(rs.getTimestamp(1), expectedTimestampMillisPrecision(), "getTimestamp mismatch"); - // Cannot test rs.getObject for the Calendar object type, as none of Time, Timestamp, java.sql.Date + // Cannot test rs.getObject for the Calendar object type, as none of Time, Timestamp, Date // or java.util.Date can be cast to a calendar // Test the additional conversions introduced in JDBC41 for types setters @@ -922,7 +924,7 @@ private final DateTimeOffset initExpected(String stringValue, int fractionalSeco cal.set(Calendar.MINUTE, minute); cal.set(Calendar.SECOND, second); - java.sql.Timestamp timestamp = new java.sql.Timestamp(cal.getTimeInMillis()); + Timestamp timestamp = new Timestamp(cal.getTimeInMillis()); timestamp.setNanos(nanos); return DateTimeOffset.valueOf(timestamp, minutesOffset); @@ -938,14 +940,14 @@ private final DateTimeOffset initExpected(String stringValue, int fractionalSeco dto = initExpected(stringValue, fractionalSecondsDigits); } - private java.sql.Date expectedDate() { + private Date expectedDate() { Calendar cal = Calendar.getInstance(new SimpleTimeZone(1000 * 60 * dto.getMinutesOffset(), ""), Locale.US); cal.setTimeInMillis(dto.getTimestamp().getTime()); cal.set(Calendar.HOUR_OF_DAY, 0); cal.set(Calendar.MINUTE, 0); cal.set(Calendar.SECOND, 0); cal.set(Calendar.MILLISECOND, 0); - return new java.sql.Date(cal.getTimeInMillis()); + return new Date(cal.getTimeInMillis()); } private java.sql.Time expectedTime() { @@ -960,13 +962,13 @@ private java.sql.Time expectedTime() { return new java.sql.Time(cal.getTimeInMillis()); } - private java.sql.Timestamp expectedTimestamp() { + private Timestamp expectedTimestamp() { Calendar cal = Calendar.getInstance(new SimpleTimeZone(1000 * 60 * dto.getMinutesOffset(), ""), Locale.US); cal.set(Calendar.ZONE_OFFSET, 1000 * 60 * dto.getMinutesOffset()); cal.setTimeInMillis(dto.getTimestamp().getTime()); if (dto.getTimestamp().getNanos() % 1000000 >= 500000) cal.add(Calendar.MILLISECOND, 1); - return new java.sql.Timestamp(cal.getTimeInMillis()); + return new Timestamp(cal.getTimeInMillis()); } private java.util.Date expectedUtilDate() { @@ -1020,7 +1022,7 @@ void verifySettersUtilDate(PreparedStatement ps) throws Exception { /* * Cannot test by setObject(...,java.util.Date) with the back end data type as datetimeoffset because by * design anything but datetimeoffset is normalized to UTC time zone in the source code (in sendTemporal - * function in dtv.java) setObject(...,java.sql.Date) and setObject(...,java.utl.date) gets same exception + * function in dtv.java) setObject(...,Date) and setObject(...,java.utl.date) gets same exception */ } @@ -1177,7 +1179,7 @@ public void testSendTimestampAsTimeAsDatetime() throws Exception { CallableStatement cs = conn.prepareCall("{call " + AbstractSQLGenerator.escapeIdentifier(procName) + "(?,?)}"); // Set up a timestamp with a time component that is the last millisecond of the day... - java.sql.Timestamp ts = java.sql.Timestamp.valueOf("2010-02-15 23:59:59.999"); + Timestamp ts = Timestamp.valueOf("2010-02-15 23:59:59.999"); // ... and send that timestamp to the server using the TIME SQL type rather than TIMESTAMP. // If the driver is doing the right thing, it strips the date portion and, because @@ -1189,7 +1191,7 @@ public void testSendTimestampAsTimeAsDatetime() throws Exception { // Fetch the OUT parameter and verify that we have a date-normalized TIME of midnight java.sql.Time timeOut = cs.getTime(2); - java.sql.Timestamp tsOut = new java.sql.Timestamp(timeOut.getTime()); + Timestamp tsOut = new Timestamp(timeOut.getTime()); assertEquals(tsOut.toString(), "1970-01-01 00:00:00.0", "CS.getTime() - Wrong OUT value"); cs.close(); @@ -1231,7 +1233,7 @@ public void testDoubleRounding() throws Exception { rs.next(); // when double-rounding to scale of 6, nanos should be 832909 - java.sql.Timestamp ts = java.sql.Timestamp.valueOf("3432-12-21 15:22:58.832908453"); + Timestamp ts = Timestamp.valueOf("3432-12-21 15:22:58.832908453"); dto = DateTimeOffset.valueOf(ts, 0); // ts value that goes to driver is 3432-12-21 15:22:58.832908453 @@ -1298,12 +1300,12 @@ public void testWithJapaneseImperialCalendar() throws Exception { // Get Gregorian date using Japanese imperial calendar rs = conn.createStatement().executeQuery("SELECT CAST('0821-01-04' AS DATE)"); rs.next(); - java.sql.Date date = rs.getDate(1, japaneseImperialCalendar); + Date date = rs.getDate(1, japaneseImperialCalendar); assertEquals(date.toString(), "0821-01-04", "Get pre-Meiji"); rs.close(); PreparedStatement ps = conn.prepareStatement("SELECT CAST(? AS VARCHAR(40))"); - java.sql.Timestamp ts; + Timestamp ts; // Set second day of first year of Taisho era (1912 Gregorian) // Note: Taisho era began July 30, 1912 Gregorian; second day of that year was July 31. @@ -1311,7 +1313,7 @@ public void testWithJapaneseImperialCalendar() throws Exception { japaneseImperialCalendar.set(Calendar.ERA, 2); // Taisho -> ERA 2 japaneseImperialCalendar.set(Calendar.YEAR, 1); japaneseImperialCalendar.set(Calendar.DAY_OF_YEAR, 2); - ts = new java.sql.Timestamp(japaneseImperialCalendar.getTimeInMillis()); + ts = new Timestamp(japaneseImperialCalendar.getTimeInMillis()); ps.setTimestamp(1, ts, japaneseImperialCalendar); rs = ps.executeQuery(); rs.next(); @@ -1328,7 +1330,7 @@ public void testWithJapaneseImperialCalendar() throws Exception { japaneseImperialCalendar.set(Calendar.MINUTE, 49); japaneseImperialCalendar.set(Calendar.SECOND, 3); japaneseImperialCalendar.set(Calendar.MILLISECOND, 87); - ts = new java.sql.Timestamp(japaneseImperialCalendar.getTimeInMillis()); + ts = new Timestamp(japaneseImperialCalendar.getTimeInMillis()); ps.setTimestamp(1, ts, japaneseImperialCalendar); rs = ps.executeQuery(); rs.next(); @@ -1398,6 +1400,11 @@ public void testGetString() throws Exception { @Test public void testWithThaiLocale() throws Exception { + java.text.SimpleDateFormat tsFormat = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS0000"); + java.text.SimpleDateFormat timeFormat = new java.text.SimpleDateFormat("K:mmaa"); + java.text.SimpleDateFormat dateFormat = new java.text.SimpleDateFormat("yyyy-MM-dd"); + java.text.SimpleDateFormat dtoFormat = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS0000 XXX"); + Locale locale = Locale.getDefault(); Locale.setDefault(new Locale("th", "TH")); try { @@ -1411,50 +1418,49 @@ public void testWithThaiLocale() throws Exception { // Need to use the following constructor for running against IBM JVM. Here, year should be year-1900, month // is from 0-11. - java.sql.Timestamp ts = new java.sql.Timestamp(2009 - 1900, 10, 17, 15, 23, 32, 0); + Timestamp ts = new Timestamp(System.currentTimeMillis()); // Test PreparedStatement with Timestamp // Value sent as DATETIME2; result should have 7 digits of subsecond precision) ps.setTimestamp(1, ts); rs = ps.executeQuery(); rs.next(); - assertEquals(rs.getString(1), "2009-11-17 15:23:32.0000000", "Timestamp mismatch"); + assertEquals(rs.getString(1), tsFormat.format(ts), "Timestamp mismatch"); + rs.close(); // Test PreparedStatement with Time // Value sent as DATETIME w/Unix Epoch as base date when sendTimeAsDatetime=true - java.sql.Time time = new java.sql.Time(ts.getTime()); + java.sql.Time time = new java.sql.Time(ts.getTime()); ps.setTime(1, time); rs = ps.executeQuery(); rs.next(); - assertEquals(rs.getString(1), "Jan 1 1970 3:23PM", "Time mismatch"); + assertEquals(rs.getString(1), "Jan 1 1970 " + timeFormat.format(ts.getTime()), "Time mismatch"); rs.close(); // Test PreparedStatement with Date - java.sql.Date date = new java.sql.Date(ts.getTime()); + Date date = new Date(ts.getTime()); ps.setDate(1, date); rs = ps.executeQuery(); rs.next(); - assertEquals(rs.getString(1), "2009-11-17", "Date mismatch"); + assertEquals(rs.getString(1), dateFormat.format(ts), "Date mismatch"); rs.close(); // Test PreparedStatement with Date (using Buddhist calendar) - date = new java.sql.Date(ts.getTime()); + date = new Date(ts.getTime()); ps.setDate(1, date, Calendar.getInstance()); rs = ps.executeQuery(); rs.next(); - assertEquals(rs.getString(1), "2009-11-17", "Date mismatch (w/calendar)"); + assertEquals(rs.getString(1), dateFormat.format(ts), "Date mismatch (w/calendar)"); rs.close(); // Test PreparedStatement with DateTimeOffset (using Buddhist calendar) // Note: Expected value does not reflect Buddhist year, even though a Buddhist calendar is used. - DateTimeOffset dto = DateTimeOffset.valueOf(ts, Calendar.getInstance(TimeZone.getTimeZone("America/Los_Angeles"))); - + DateTimeOffset dto = DateTimeOffset.valueOf(ts, Calendar.getInstance()); ((SQLServerPreparedStatement) ps).setDateTimeOffset(1, dto); rs = ps.executeQuery(); rs.next(); - - assertEquals(rs.getString(1), "2009-11-17 15:23:32.0000000 -08:00", "DateTimeOffset mismatch"); + assertEquals(rs.getString(1), dtoFormat.format(ts), "DateTimeOffset mismatch"); rs.close(); ps.close(); @@ -1501,7 +1507,7 @@ public void testTimestampToDateTimeOffset() throws Exception { Connection conn = DriverManager.getConnection(connectionString); PreparedStatement ps = conn.prepareStatement("SELECT CAST(? AS DATETIMEOFFSET)"); - ps.setTimestamp(1, java.sql.Timestamp.valueOf("2010-01-06 12:34:56")); + ps.setTimestamp(1, Timestamp.valueOf("2010-01-06 12:34:56")); ResultSet rs = ps.executeQuery(); rs.next(); DateTimeOffset dto = ((SQLServerResultSet) rs).getDateTimeOffset(1); @@ -1522,7 +1528,7 @@ public void testJulianLeapYear() throws Exception { ResultSet rs; // Julian date from string - java.sql.Date julianDate = java.sql.Date.valueOf("0700-03-01"); + Date julianDate = Date.valueOf("0700-03-01"); ps.setDate(1, julianDate); rs = ps.executeQuery(); rs.next(); @@ -1567,7 +1573,7 @@ public void testGregorianCutoverDateTime2() throws Exception { Timestamp ts; // Test setting value during the Gregorian cutover via Timestamp constructed from String - ts = java.sql.Timestamp.valueOf("1582-10-24 15:07:09.081"); + ts = Timestamp.valueOf("1582-10-24 15:07:09.081"); ps.setTimestamp(1, ts); rs = ps.executeQuery(); rs.next(); @@ -1578,7 +1584,7 @@ public void testGregorianCutoverDateTime2() throws Exception { Calendar cal = Calendar.getInstance(); cal.set(1582, Calendar.NOVEMBER, 1, 15, 7, 9); cal.set(Calendar.MILLISECOND, 81); - ts = new java.sql.Timestamp(cal.getTimeInMillis()); + ts = new Timestamp(cal.getTimeInMillis()); ps.setTimestamp(1, ts); rs = ps.executeQuery(); rs.next(); @@ -1600,7 +1606,7 @@ public void testTimestampToDateTime() throws Exception { Connection conn = DriverManager.getConnection(connectionString); PreparedStatement ps = conn.prepareStatement("SELECT 1 WHERE ?=CAST('2009-12-17 17:00:29' AS DATETIME)"); - ps.setTimestamp(1, java.sql.Timestamp.valueOf("2009-12-17 17:00:29")); + ps.setTimestamp(1, Timestamp.valueOf("2009-12-17 17:00:29")); ResultSet rs = ps.executeQuery(); assertEquals(rs.next(), true, "Timestamp to DATETIME comparison failed for equal values"); rs.close(); @@ -1641,7 +1647,7 @@ public void testUpdateMisc() throws Exception { rs.next(); // Update datetimeoffset(2) from pre-Gregorian Date - rs.updateDate(1, java.sql.Date.valueOf("0814-02-18")); + rs.updateDate(1, Date.valueOf("0814-02-18")); rs.updateRow(); assertEquals(((SQLServerResultSet) rs).getDateTimeOffset(1).toString(), "0814-02-18 00:00:00 +00:00", "Update of datetimeoffset(2) from pre-Gregorian Date"); @@ -1653,7 +1659,7 @@ public void testUpdateMisc() throws Exception { "Update datetimeoffset(2) from last Time"); // Update datetimeoffset(2) from the "last" Timestamp - rs.updateTimestamp(1, java.sql.Timestamp.valueOf("9999-12-31 23:59:59.998")); + rs.updateTimestamp(1, Timestamp.valueOf("9999-12-31 23:59:59.998")); rs.updateRow(); dto = ((SQLServerResultSet) rs).getDateTimeOffset(1); assertEquals(dto.toString(), "9999-12-31 23:59:59.99 +00:00", @@ -1662,8 +1668,8 @@ public void testUpdateMisc() throws Exception { // Attempt to update datetimeoffset(2) from the first out of range value // Verify that an exception is thrown and that the statement/connection is still usable after try { - java.sql.Timestamp tsInvalid = java.sql.Timestamp.valueOf("9999-12-31 23:59:59.999999999"); - tsInvalid = new java.sql.Timestamp(tsInvalid.getTime() + 1); + Timestamp tsInvalid = Timestamp.valueOf("9999-12-31 23:59:59.999999999"); + tsInvalid = new Timestamp(tsInvalid.getTime() + 1); rs.updateTimestamp(1, tsInvalid); rs.updateRow(); assertEquals(false, true, "Update succeeded with out of range value"); @@ -1674,7 +1680,7 @@ public void testUpdateMisc() throws Exception { } // Update time(5) from Timestamp with nanos more precise than 100ns - Timestamp ts = java.sql.Timestamp.valueOf("2010-01-12 11:05:23"); + Timestamp ts = Timestamp.valueOf("2010-01-12 11:05:23"); ts.setNanos(987659999); rs.updateTimestamp(3, ts); rs.updateRow(); @@ -1682,7 +1688,7 @@ public void testUpdateMisc() throws Exception { "Update time(5) from Timestamp with sub-100ns nanos"); // Update time(5) from Timestamp to max value in a day. The value should not be rounded - ts = java.sql.Timestamp.valueOf("2010-01-12 23:59:59"); + ts = Timestamp.valueOf("2010-01-12 23:59:59"); ts.setNanos(999999999); Time time = new java.sql.Time(ts.getTime()); rs.updateTimestamp(3, ts); @@ -1693,13 +1699,13 @@ public void testUpdateMisc() throws Exception { // Update time(2) from Time to max value in a day. The value should not be rounded rs.updateTime(6, time); rs.updateRow(); - assertEquals(new java.sql.Timestamp(rs.getTime(6).getTime()).toString(), // conversion to timestamp is + assertEquals(new Timestamp(rs.getTime(6).getTime()).toString(), // conversion to timestamp is // necessary to see fractional // secs "1970-01-01 23:59:59.99", "Update time(2) from Time to max value in a day"); // Update time(5) from Timestamp to max value in a second. The value should be rounded - ts = java.sql.Timestamp.valueOf("2010-01-12 23:59:58"); + ts = Timestamp.valueOf("2010-01-12 23:59:58"); ts.setNanos(999999999); time = new java.sql.Time(ts.getTime()); rs.updateTimestamp(3, ts); @@ -1710,13 +1716,13 @@ public void testUpdateMisc() throws Exception { // Update time(2) from Time to max value in a second. The value should be rounded rs.updateTime(6, time); rs.updateRow(); - assertEquals(new java.sql.Timestamp(rs.getTime(6).getTime()).toString(), // conversion to timestamp is + assertEquals(new Timestamp(rs.getTime(6).getTime()).toString(), // conversion to timestamp is // necessary to see fractional // secs "1970-01-01 23:59:59.0", "Update time(2) from Time to max value in a second"); // Update datetime w/expected rounding of nanos to DATETIME's 1/300second resolution - ts = java.sql.Timestamp.valueOf("6289-04-22 05:13:57.6745106"); + ts = Timestamp.valueOf("6289-04-22 05:13:57.6745106"); rs.updateTimestamp(2, ts); rs.updateRow(); assertEquals(rs.getTimestamp(2).toString(), "6289-04-22 05:13:57.677", @@ -1724,7 +1730,7 @@ public void testUpdateMisc() throws Exception { // Update datetime with rounding-induced overflow from Time // (should roll date part to 1/2/1970) - ts = java.sql.Timestamp.valueOf("2010-01-18 23:59:59.999"); + ts = Timestamp.valueOf("2010-01-18 23:59:59.999"); rs.updateTime(2, new java.sql.Time(ts.getTime())); rs.updateRow(); assertEquals(rs.getTimestamp(2).toString(), "1970-01-02 00:00:00.0", From d93e0fe7beb97c7adea8b71d1c0fcf31d0cd86bf Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Mon, 19 Nov 2018 13:47:47 -0800 Subject: [PATCH 08/51] fixed issue with timezone --- .../sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java index 5dca35f08f..ad44458bbb 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java @@ -1456,7 +1456,8 @@ public void testWithThaiLocale() throws Exception { // Test PreparedStatement with DateTimeOffset (using Buddhist calendar) // Note: Expected value does not reflect Buddhist year, even though a Buddhist calendar is used. - DateTimeOffset dto = DateTimeOffset.valueOf(ts, Calendar.getInstance()); + DateTimeOffset dto = DateTimeOffset.valueOf(ts, Calendar.getInstance(TimeZone.getTimeZone("America/Los_Angeles"))); + ((SQLServerPreparedStatement) ps).setDateTimeOffset(1, dto); rs = ps.executeQuery(); rs.next(); From 27702d335a4195b5d4d5d352ff22dfa3ce424852 Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Mon, 19 Nov 2018 15:35:45 -0800 Subject: [PATCH 09/51] fixed issue with timezone --- .../sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java index ad44458bbb..9823b06798 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java @@ -1403,8 +1403,7 @@ public void testWithThaiLocale() throws Exception { java.text.SimpleDateFormat tsFormat = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS0000"); java.text.SimpleDateFormat timeFormat = new java.text.SimpleDateFormat("K:mmaa"); java.text.SimpleDateFormat dateFormat = new java.text.SimpleDateFormat("yyyy-MM-dd"); - java.text.SimpleDateFormat dtoFormat = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS0000 XXX"); - + Locale locale = Locale.getDefault(); Locale.setDefault(new Locale("th", "TH")); try { @@ -1456,12 +1455,13 @@ public void testWithThaiLocale() throws Exception { // Test PreparedStatement with DateTimeOffset (using Buddhist calendar) // Note: Expected value does not reflect Buddhist year, even though a Buddhist calendar is used. - DateTimeOffset dto = DateTimeOffset.valueOf(ts, Calendar.getInstance(TimeZone.getTimeZone("America/Los_Angeles"))); - + DateTimeOffset dto = DateTimeOffset.valueOf(ts, Calendar.getInstance()); ((SQLServerPreparedStatement) ps).setDateTimeOffset(1, dto); rs = ps.executeQuery(); rs.next(); - assertEquals(rs.getString(1), dtoFormat.format(ts), "DateTimeOffset mismatch"); + + // local time zone may not be same as server time zone + assertEquals(rs.getString(1).substring(0, 27), tsFormat.format(ts), "DateTimeOffset mismatch"); rs.close(); ps.close(); From a48c8107b6c1fec6f355918b7e97293c35683cd5 Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Mon, 19 Nov 2018 18:22:37 -0800 Subject: [PATCH 10/51] fixed issue with timezone --- .../jdbc/datatypes/KatmaiDataTypesTest.java | 36 +++++++++++-------- 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java index 9823b06798..e505ed8faa 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java @@ -132,7 +132,8 @@ void verifyParameterMetaData(Connection conn) throws Exception { stmt = conn.createStatement(); // Create the stored proc - stmt.executeUpdate("CREATE PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName) + " @arg " + sqlTypeExpression + " AS SELECT @arg"); + stmt.executeUpdate("CREATE PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName) + " @arg " + + sqlTypeExpression + " AS SELECT @arg"); pstmt = conn.prepareStatement("{call " + AbstractSQLGenerator.escapeIdentifier(procName) + "(?)}"); ParameterMetaData metadata = pstmt.getParameterMetaData(); @@ -1403,9 +1404,11 @@ public void testWithThaiLocale() throws Exception { java.text.SimpleDateFormat tsFormat = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS0000"); java.text.SimpleDateFormat timeFormat = new java.text.SimpleDateFormat("K:mmaa"); java.text.SimpleDateFormat dateFormat = new java.text.SimpleDateFormat("yyyy-MM-dd"); + java.text.SimpleDateFormat dtoFormat = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS0000 XXX"); Locale locale = Locale.getDefault(); Locale.setDefault(new Locale("th", "TH")); + try { Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); Connection conn = DriverManager.getConnection(connectionString + ";sendTimeAsDatetime=true"); @@ -1414,27 +1417,30 @@ public void testWithThaiLocale() throws Exception { // Test setter conversions PreparedStatement ps = conn.prepareStatement("SELECT CAST(? AS VARCHAR(40))"); - + // Need to use the following constructor for running against IBM JVM. Here, year should be year-1900, month // is from 0-11. Timestamp ts = new Timestamp(System.currentTimeMillis()); - + // Test PreparedStatement with Timestamp // Value sent as DATETIME2; result should have 7 digits of subsecond precision) ps.setTimestamp(1, ts); rs = ps.executeQuery(); rs.next(); assertEquals(rs.getString(1), tsFormat.format(ts), "Timestamp mismatch"); - rs.close(); // Test PreparedStatement with Time // Value sent as DATETIME w/Unix Epoch as base date when sendTimeAsDatetime=true - java.sql.Time time = new java.sql.Time(ts.getTime()); + Time time = new Time(ts.getTime()); ps.setTime(1, time); rs = ps.executeQuery(); rs.next(); - assertEquals(rs.getString(1), "Jan 1 1970 " + timeFormat.format(ts.getTime()), "Time mismatch"); + + // compare these separately since there may be an extra space between the 2 + assertEquals(rs.getString(1).substring(0, 11), "Jan 1 1970", "Time mismatch"); + assertEquals(rs.getString(1).substring(rs.getString(1).length() - 7).trim(), + timeFormat.format(ts.getTime()), "Time mismatch"); rs.close(); // Test PreparedStatement with Date @@ -1455,13 +1461,15 @@ public void testWithThaiLocale() throws Exception { // Test PreparedStatement with DateTimeOffset (using Buddhist calendar) // Note: Expected value does not reflect Buddhist year, even though a Buddhist calendar is used. - DateTimeOffset dto = DateTimeOffset.valueOf(ts, Calendar.getInstance()); + DateTimeOffset dto = DateTimeOffset.valueOf(ts, + Calendar.getInstance(TimeZone.getTimeZone("America/Los_Angeles"))); + ((SQLServerPreparedStatement) ps).setDateTimeOffset(1, dto); rs = ps.executeQuery(); rs.next(); - - // local time zone may not be same as server time zone - assertEquals(rs.getString(1).substring(0, 27), tsFormat.format(ts), "DateTimeOffset mismatch"); + + dtoFormat.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles")); + assertEquals(rs.getString(1), dtoFormat.format(ts), "DateTimeOffset mismatch"); rs.close(); ps.close(); @@ -1701,8 +1709,8 @@ public void testUpdateMisc() throws Exception { rs.updateTime(6, time); rs.updateRow(); assertEquals(new Timestamp(rs.getTime(6).getTime()).toString(), // conversion to timestamp is - // necessary to see fractional - // secs + // necessary to see fractional + // secs "1970-01-01 23:59:59.99", "Update time(2) from Time to max value in a day"); // Update time(5) from Timestamp to max value in a second. The value should be rounded @@ -1718,8 +1726,8 @@ public void testUpdateMisc() throws Exception { rs.updateTime(6, time); rs.updateRow(); assertEquals(new Timestamp(rs.getTime(6).getTime()).toString(), // conversion to timestamp is - // necessary to see fractional - // secs + // necessary to see fractional + // secs "1970-01-01 23:59:59.0", "Update time(2) from Time to max value in a second"); // Update datetime w/expected rounding of nanos to DATETIME's 1/300second resolution From 9c697cf400513b71b49c3d2aac9878367d8ac06d Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Mon, 19 Nov 2018 22:47:21 -0800 Subject: [PATCH 11/51] added SparseTest and BigIntegerTest and cleaned up with try-with-resources --- .../jdbc/datatypes/BigIntegerTest.java | 243 +++++ .../jdbc/datatypes/KatmaiDataTypesTest.java | 966 ++++++++---------- .../sqlserver/jdbc/datatypes/SparseTest.java | 61 ++ 3 files changed, 751 insertions(+), 519 deletions(-) create mode 100644 src/test/java/com/microsoft/sqlserver/jdbc/datatypes/BigIntegerTest.java create mode 100644 src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SparseTest.java diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/BigIntegerTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/BigIntegerTest.java new file mode 100644 index 0000000000..a426572453 --- /dev/null +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/BigIntegerTest.java @@ -0,0 +1,243 @@ +package com.microsoft.sqlserver.jdbc.datatypes; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import org.junit.jupiter.api.Test; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.math.BigInteger; + +import com.microsoft.sqlserver.jdbc.RandomUtil; +import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; +import com.microsoft.sqlserver.testframework.AbstractTest; + +/* + * This test is for testing the setObject methods for the new data type mappings in JDBC 4.1 for java.math.BigInteger + */ +@RunWith(JUnitPlatform.class) +public class BigIntegerTest extends AbstractTest { + + enum TestType { + SETOBJECT_WITHTYPE, // This is to test conversions in Table B-5 + SETOBJECT_WITHOUTTYPE, // This is to test conversions in Table B-4 + SETNULL // This is to test setNull method + }; + + final static String tableName = RandomUtil.getIdentifier("BigIntegerTestTable"); + + @Test + public void testJDBC41BigInteger() throws Exception { + try (Connection conn = DriverManager.getConnection(connectionString); Statement stmt = conn.createStatement()) { + + // Create the test table + try { + stmt.executeUpdate("drop table " + AbstractSQLGenerator.escapeIdentifier(tableName)); + } catch (Exception e) {} + + String query = "create table " + AbstractSQLGenerator.escapeIdentifier(tableName) + + " (col1 varchar(100), col2 bigint, col3 real, col4 float, " + + "col5 numeric(38,0), col6 int, col7 smallint, col8 char(100), col9 varchar(max), " + + "id int IDENTITY primary key)"; + stmt.executeUpdate(query); + + try (PreparedStatement pstmt = conn + .prepareStatement("INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableName) + + " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?) SELECT * FROM " + + AbstractSQLGenerator.escapeIdentifier(tableName) + " where id = ?")) { + + // test that the driver converts the BigInteger values greater than LONG.MAX_VALUE and lesser than + // LONG.MIN_VALUE correctly + // A random value that is bigger than LONG.MAX_VALUE + BigInteger bigIntPos = new BigInteger("922337203685477580776767676"); + // A random value that is smaller than LONG.MIN_VALUE + BigInteger bigIntNeg = new BigInteger("-922337203685477580776767676"); + + // Test the setObject method for different types of BigInteger values. Since BigInteger is mapped to + // JDBC + // BIGINT, the max and min limits for + int row = 1; + testSetObject(AbstractSQLGenerator.escapeIdentifier(tableName), BigInteger.valueOf(Long.MAX_VALUE), + row++, pstmt, TestType.SETOBJECT_WITHTYPE); + + testSetObject(AbstractSQLGenerator.escapeIdentifier(tableName), BigInteger.valueOf(Long.MIN_VALUE), + row++, pstmt, TestType.SETOBJECT_WITHTYPE); + testSetObject(AbstractSQLGenerator.escapeIdentifier(tableName), BigInteger.valueOf(10), row++, pstmt, + TestType.SETOBJECT_WITHTYPE); + testSetObject(AbstractSQLGenerator.escapeIdentifier(tableName), BigInteger.valueOf(-10), row++, pstmt, + TestType.SETOBJECT_WITHTYPE); + testSetObject(AbstractSQLGenerator.escapeIdentifier(tableName), BigInteger.ZERO, row++, pstmt, + TestType.SETOBJECT_WITHTYPE); + testSetObject(AbstractSQLGenerator.escapeIdentifier(tableName), bigIntPos, row++, pstmt, + TestType.SETOBJECT_WITHTYPE); + testSetObject(AbstractSQLGenerator.escapeIdentifier(tableName), bigIntNeg, row++, pstmt, + TestType.SETOBJECT_WITHTYPE); + + // Test setObject method with SQL TYPE parameter + testSetObject(AbstractSQLGenerator.escapeIdentifier(tableName), BigInteger.valueOf(Long.MAX_VALUE), + row++, pstmt, TestType.SETOBJECT_WITHOUTTYPE); + testSetObject(AbstractSQLGenerator.escapeIdentifier(tableName), BigInteger.valueOf(Long.MIN_VALUE), + row++, pstmt, TestType.SETOBJECT_WITHOUTTYPE); + testSetObject(AbstractSQLGenerator.escapeIdentifier(tableName), BigInteger.valueOf(1000), row++, pstmt, + TestType.SETOBJECT_WITHOUTTYPE); + testSetObject(AbstractSQLGenerator.escapeIdentifier(tableName), BigInteger.valueOf(-1000), row++, pstmt, + TestType.SETOBJECT_WITHOUTTYPE); + testSetObject(AbstractSQLGenerator.escapeIdentifier(tableName), BigInteger.ZERO, row++, pstmt, + TestType.SETOBJECT_WITHOUTTYPE); + testSetObject(AbstractSQLGenerator.escapeIdentifier(tableName), bigIntPos, row++, pstmt, + TestType.SETOBJECT_WITHOUTTYPE); + testSetObject(AbstractSQLGenerator.escapeIdentifier(tableName), bigIntNeg, row++, pstmt, + TestType.SETOBJECT_WITHOUTTYPE); + + // Test setNull + testSetObject(AbstractSQLGenerator.escapeIdentifier(tableName), bigIntNeg, row++, pstmt, + TestType.SETNULL); + + try { + stmt.executeUpdate("drop table " + AbstractSQLGenerator.escapeIdentifier(tableName)); + } catch (Exception e) {} + } + } + } + + static void testSetObject(String tableName, BigInteger obj, int id, PreparedStatement pstmt, + TestType testType) throws SQLException { + if (TestType.SETOBJECT_WITHTYPE == testType) { + callSetObjectWithType(obj, pstmt); + } else if (TestType.SETOBJECT_WITHOUTTYPE == testType) { + callSetObjectWithoutType(obj, pstmt); + } else if (TestType.SETNULL == testType) { + callSetNull(obj, pstmt); + } else + assertEquals(true, false, "Invalid test type"); + + // The id column + pstmt.setObject(10, id); + + pstmt.execute(); + pstmt.getMoreResults(); + try (ResultSet rs = pstmt.getResultSet()) { + rs.next(); + + if (TestType.SETNULL == testType) { + for (int i = 1; 9 >= i; ++i) { + // Get the data first before calling rs.wasNull() + rs.getString(i); + assertEquals(rs.wasNull(), true, "setNull mismatch"); + } + return; + } + + if ((0 > obj.compareTo(BigInteger.valueOf(Long.MIN_VALUE))) + || (0 < obj.compareTo(BigInteger.valueOf(Long.MAX_VALUE)))) { + // For the BigInteger values greater/less than Long limits test only the long data type. + // This test is here just to make sure the driver does not do anything wired when the value is + // bigger/smaller than JDBC BIGINT + assertEquals(rs.getString(1), Long.valueOf(obj.longValue()).toString(), + "getString(greater/less than Long limits) mismatch"); + assertEquals(rs.getLong(2), obj.longValue(), "getLong(greater/less than Long limits) mismatch"); + // As CHAR is fixed length, rs.getString() returns a string of the size allocated in the database. + // Need to trim it for comparison. + assertEquals(rs.getString(8).trim(), Long.valueOf(obj.longValue()).toString(), + "getString(greater/less than Long limits (char)) mismatch"); + + assertEquals(rs.getString(9), Long.valueOf(obj.longValue()).toString(), + "getString(greater/less than Long limits (varchar(max)))) mismatch"); + } else { + assertEquals(rs.getString(1), obj.toString(), "getString mismatch"); + assertEquals(rs.getLong(2), obj.longValue(), "getLong mismatch"); + assertEquals(rs.getFloat(3), obj.floatValue(), "getFloat mismatch"); + assertEquals(rs.getDouble(4), obj.doubleValue(), "getDouble(float) mismatch"); + assertEquals(rs.getDouble(5), obj.doubleValue(), "getDouble(numeric) mismatch"); + if (obj.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) >= 0) { + assertEquals(rs.getInt(6), Integer.MAX_VALUE, "getInt(numeric) mismatch"); + } else if (obj.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) <= 0) { + assertEquals(rs.getInt(6), Integer.MIN_VALUE, "getInt(numeric) mismatch"); + } else { + assertEquals(rs.getInt(6), obj.intValue(), "getInt(numeric) mismatch"); + } + if (obj.compareTo(BigInteger.valueOf(Short.MAX_VALUE)) >= 0) { + assertEquals(rs.getShort(7), Short.MAX_VALUE, "getShort(numeric) mismatch"); + } else if (obj.compareTo(BigInteger.valueOf(Short.MIN_VALUE)) <= 0) { + assertEquals(rs.getShort(7), Short.MIN_VALUE, "getShort(numeric) mismatch"); + } else { + assertEquals(rs.getShort(7), obj.shortValue(), "getShort(numeric) mismatch"); + } + + assertEquals(rs.getString(8).trim(), obj.toString(), "getString(char) mismatch"); + assertEquals(rs.getString(9), obj.toString(), "getString(varchar(max)) mismatch"); + } + } + } + + static void callSetObjectWithType(BigInteger obj, PreparedStatement pstmt) throws SQLException { + pstmt.setObject(1, obj, java.sql.Types.VARCHAR); + pstmt.setObject(2, obj, java.sql.Types.BIGINT); + pstmt.setObject(3, obj, java.sql.Types.FLOAT); + pstmt.setObject(4, obj, java.sql.Types.DOUBLE); + pstmt.setObject(5, obj, java.sql.Types.NUMERIC); + // Use Integer/Short limits instead of Long limits for the int/smallint column + if (obj.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) >= 0) { + pstmt.setObject(6, BigInteger.valueOf(Integer.MAX_VALUE), java.sql.Types.INTEGER); + } else if (obj.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) <= 0) { + pstmt.setObject(6, BigInteger.valueOf(Integer.MIN_VALUE), java.sql.Types.INTEGER); + } else { + pstmt.setObject(6, obj, java.sql.Types.INTEGER); + } + if (obj.compareTo(BigInteger.valueOf(Short.MAX_VALUE)) >= 0) { + pstmt.setObject(7, BigInteger.valueOf(Short.MAX_VALUE), java.sql.Types.SMALLINT); + } else if (obj.compareTo(BigInteger.valueOf(Short.MIN_VALUE)) <= 0) { + pstmt.setObject(7, BigInteger.valueOf(Short.MIN_VALUE), java.sql.Types.SMALLINT); + } else { + pstmt.setObject(7, obj, java.sql.Types.SMALLINT); + } + pstmt.setObject(8, obj, java.sql.Types.CHAR); + pstmt.setObject(9, obj, java.sql.Types.LONGVARCHAR); + } + + static void callSetObjectWithoutType(BigInteger obj, PreparedStatement pstmt) throws SQLException { + // Cannot send a long value to a column of type int/smallint (even if the long value is small enough to fit in + // those types) + pstmt.setObject(1, obj); + pstmt.setObject(2, obj); + pstmt.setObject(3, obj); + pstmt.setObject(4, obj); + pstmt.setObject(5, obj); + // Use Integer/Short limits instead of Long limits for the int/smallint column + if (obj.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) >= 0) { + pstmt.setObject(6, BigInteger.valueOf(Integer.MAX_VALUE)); + } else if (obj.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) <= 0) { + pstmt.setObject(6, BigInteger.valueOf(Integer.MIN_VALUE)); + } else { + pstmt.setObject(6, obj); + } + if (obj.compareTo(BigInteger.valueOf(Short.MAX_VALUE)) >= 0) { + pstmt.setObject(7, BigInteger.valueOf(Short.MAX_VALUE)); + } else if (obj.compareTo(BigInteger.valueOf(Short.MIN_VALUE)) <= 0) { + pstmt.setObject(7, BigInteger.valueOf(Short.MIN_VALUE)); + } else { + pstmt.setObject(7, obj); + } + + pstmt.setObject(8, obj); + pstmt.setObject(9, obj); + } + + static void callSetNull(BigInteger obj, PreparedStatement pstmt) throws SQLException { + pstmt.setNull(1, java.sql.Types.VARCHAR); + pstmt.setNull(2, java.sql.Types.BIGINT); + pstmt.setNull(3, java.sql.Types.FLOAT); + pstmt.setNull(4, java.sql.Types.DOUBLE); + pstmt.setNull(5, java.sql.Types.NUMERIC); + pstmt.setNull(6, java.sql.Types.INTEGER); + pstmt.setNull(7, java.sql.Types.SMALLINT); + pstmt.setNull(8, java.sql.Types.CHAR); + pstmt.setNull(9, java.sql.Types.LONGVARCHAR); + } +} diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java index e505ed8faa..c8d3ff4695 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java @@ -10,7 +10,6 @@ import java.math.*; import java.util.*; import java.sql.Date; -import java.sql.Timestamp; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; @@ -94,69 +93,62 @@ private String sqlCastExpression() { } void verifyResultSetMetaData(Connection conn) throws Exception { - Statement stmt = conn.createStatement(); - ResultSet rs = stmt.executeQuery("SELECT TOP 0 " + sqlCastExpression()); - - try { - ResultSetMetaData metadata = rs.getMetaData(); - - assertEquals(metadata.getColumnType(1), sqlType.jdbcType, "getColumnType() of " + sqlCastExpression()); - assertEquals(metadata.getColumnTypeName(1), sqlType.toString(), - "getColumnTypeName() of " + sqlCastExpression()); - assertEquals(metadata.getPrecision(1), precision, "getPrecision() of " + sqlCastExpression()); - - // Display size of temporal types is the precision per JDBC spec - assertEquals(metadata.getColumnDisplaySize(1), precision, - "getColumnDisplaySize() of " + sqlCastExpression()); - // Scale is interpreted as number of fractional seconds precision - assertEquals(metadata.getScale(1), scale, "getScale() of " + sqlCastExpression()); - assertEquals(metadata.getColumnClassName(1), sqlType.className, - "getColumnClassName() of " + sqlCastExpression()); - // Katmai temporal types are not signed - assertEquals(metadata.isSigned(1), false, "isSigned() of " + sqlCastExpression()); - - // Katmai temporal types are searchable (i.e. usable in a WHERE clause) - assertEquals(metadata.isSearchable(1), true, "isSearchable() of " + sqlCastExpression()); - } finally { - rs.close(); - stmt.close(); + try (Statement stmt = conn.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT TOP 0 " + sqlCastExpression())) { + + try { + ResultSetMetaData metadata = rs.getMetaData(); + + assertEquals(metadata.getColumnType(1), sqlType.jdbcType, + "getColumnType() of " + sqlCastExpression()); + assertEquals(metadata.getColumnTypeName(1), sqlType.toString(), + "getColumnTypeName() of " + sqlCastExpression()); + assertEquals(metadata.getPrecision(1), precision, "getPrecision() of " + sqlCastExpression()); + + // Display size of temporal types is the precision per JDBC spec + assertEquals(metadata.getColumnDisplaySize(1), precision, + "getColumnDisplaySize() of " + sqlCastExpression()); + // Scale is interpreted as number of fractional seconds precision + assertEquals(metadata.getScale(1), scale, "getScale() of " + sqlCastExpression()); + assertEquals(metadata.getColumnClassName(1), sqlType.className, + "getColumnClassName() of " + sqlCastExpression()); + // Katmai temporal types are not signed + assertEquals(metadata.isSigned(1), false, "isSigned() of " + sqlCastExpression()); + + // Katmai temporal types are searchable (i.e. usable in a WHERE clause) + assertEquals(metadata.isSearchable(1), true, "isSearchable() of " + sqlCastExpression()); + + } finally {} } } void verifyParameterMetaData(Connection conn) throws Exception { - Statement stmt = null; - PreparedStatement pstmt = null; - - try { - stmt = conn.createStatement(); - + try (Statement stmt = conn.createStatement()) { // Create the stored proc stmt.executeUpdate("CREATE PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName) + " @arg " + sqlTypeExpression + " AS SELECT @arg"); - pstmt = conn.prepareStatement("{call " + AbstractSQLGenerator.escapeIdentifier(procName) + "(?)}"); - ParameterMetaData metadata = pstmt.getParameterMetaData(); - - assertEquals(metadata.getParameterType(1), sqlType.jdbcType, - "getParameterType() of " + sqlCastExpression()); - assertEquals(metadata.getParameterTypeName(1), sqlType.toString(), - "getParameterTypeName() of " + sqlCastExpression()); - assertEquals(metadata.getPrecision(1), precision, "getPrecision() of " + sqlCastExpression()); - - // Scale is interpreted as number of fractional seconds precision - assertEquals(metadata.getScale(1), scale, "getScale() of " + sqlCastExpression()); - assertEquals(metadata.getParameterClassName(1), sqlType.className, - "getParameterClassName() of " + sqlCastExpression()); - // Katmai temporal types are not signed - assertEquals(metadata.isSigned(1), false, "isSigned() of " + sqlCastExpression()); + try (PreparedStatement pstmt = conn + .prepareStatement("{call " + AbstractSQLGenerator.escapeIdentifier(procName) + "(?)}")) { + ParameterMetaData metadata = pstmt.getParameterMetaData(); + + assertEquals(metadata.getParameterType(1), sqlType.jdbcType, + "getParameterType() of " + sqlCastExpression()); + assertEquals(metadata.getParameterTypeName(1), sqlType.toString(), + "getParameterTypeName() of " + sqlCastExpression()); + assertEquals(metadata.getPrecision(1), precision, "getPrecision() of " + sqlCastExpression()); + + // Scale is interpreted as number of fractional seconds precision + assertEquals(metadata.getScale(1), scale, "getScale() of " + sqlCastExpression()); + assertEquals(metadata.getParameterClassName(1), sqlType.className, + "getParameterClassName() of " + sqlCastExpression()); + // Katmai temporal types are not signed + assertEquals(metadata.isSigned(1), false, "isSigned() of " + sqlCastExpression()); + } } finally { - if (null != pstmt) - pstmt.close(); - - if (null != stmt) { + try (Statement stmt = conn.createStatement()) { stmt.executeUpdate("DROP PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName)); - stmt.close(); } } } @@ -164,13 +156,11 @@ void verifyParameterMetaData(Connection conn) throws Exception { abstract void verifyRSGetters(ResultSet rs) throws Exception; void verifyRSGetters(Connection conn) throws Exception { - Statement stmt = conn.createStatement(); - ResultSet rs = stmt.executeQuery( - "SELECT " + sqlCastExpression() + ", CAST(" + sqlCastExpression() + " AS VARCHAR(60))"); - rs.next(); - verifyRSGetters(rs); - rs.close(); - stmt.close(); + try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery( + "SELECT " + sqlCastExpression() + ", CAST(" + sqlCastExpression() + " AS VARCHAR(60))")) { + rs.next(); + verifyRSGetters(rs); + } } abstract void verifyRSUpdaters(ResultSet rs) throws Exception; @@ -179,26 +169,24 @@ void verifyRSUpdaters(Connection conn) throws Exception { assumeTrue(!TestUtils.isSqlAzureDW(conn), TestResource.getResource("R_skipAzure")); - Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); - try { + try (Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)) { + stmt.executeUpdate("DROP TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName)); - } catch (SQLException e) {} - stmt.executeUpdate("CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName) + " (col1 " - + sqlTypeExpression + ", col2 int identity(1,1) primary key)"); - try { + stmt.executeUpdate("CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName) + " (col1 " + + sqlTypeExpression + ", col2 int identity(1,1) primary key)"); + stmt.executeUpdate("INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableName) + " VALUES (" + sqlCastExpression() + ")"); - ResultSet rs = stmt.executeQuery("SELECT * FROM " + AbstractSQLGenerator.escapeIdentifier(tableName)); - rs.next(); - verifyRSUpdaters(rs); - rs.close(); - } finally { - stmt.executeUpdate("DROP TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName)); + try (ResultSet rs = stmt + .executeQuery("SELECT * FROM " + AbstractSQLGenerator.escapeIdentifier(tableName))) { + rs.next(); + verifyRSUpdaters(rs); + } finally { + stmt.executeUpdate("DROP TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName)); + } } - - stmt.close(); } /* @@ -215,55 +203,45 @@ void verifyRSUpdaters(Connection conn) throws Exception { void verifySetters(Connection conn) throws Exception { assumeTrue(!TestUtils.isSqlAzureDW(conn), TestResource.getResource("R_skipAzure")); - Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); - try { - stmt.executeUpdate("DROP TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName)); - } catch (SQLException e) {} - stmt.executeUpdate("CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName) + " (col1 " - + sqlTypeExpression + ", col2 int identity(1,1) primary key)"); - - PreparedStatement ps = conn.prepareStatement( - "INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableName) + " VALUES (?) SELECT * FROM " - + AbstractSQLGenerator.escapeIdentifier(tableName), - ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY); - try { - verifySetters(ps); - // Verify setObject function for the new mapping in JDBC41 (java.util.Date to TIMESTAMP) - stmt.executeUpdate("TRUNCATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName)); - - verifySettersUtilDate(ps); - // Verify setObject function for the new mapping in JDBC41 (java.util.Calendar to TIMESTAMP) - stmt.executeUpdate("TRUNCATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName)); - verifySettersCalendar(ps); - } finally { + try (Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)) { stmt.executeUpdate("DROP TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName)); + stmt.executeUpdate("CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName) + " (col1 " + + sqlTypeExpression + ", col2 int identity(1,1) primary key)"); + + try (PreparedStatement ps = conn.prepareStatement( + "INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableName) + " VALUES (?) SELECT * FROM " + + AbstractSQLGenerator.escapeIdentifier(tableName), + ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY)) { + verifySetters(ps); + // Verify setObject function for the new mapping in JDBC41 (java.util.Date to TIMESTAMP) + stmt.executeUpdate("TRUNCATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName)); + + verifySettersUtilDate(ps); + // Verify setObject function for the new mapping in JDBC41 (java.util.Calendar to TIMESTAMP) + stmt.executeUpdate("TRUNCATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName)); + verifySettersCalendar(ps); + } finally { + stmt.executeUpdate("DROP TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName)); + } } - - ps.close(); - stmt.close(); } abstract void verifyCSGetters(CallableStatement cs) throws Exception; void verifyCSGetters(Connection conn) throws Exception { - Statement stmt = conn.createStatement(); - try { - stmt.executeUpdate("DROP PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName)); - } catch (SQLException e) {} - stmt.executeUpdate("CREATE PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName) + " @argIn " - + sqlTypeExpression + "," + " @argOut " + sqlTypeExpression + " OUTPUT" + " AS " - + " SET @argOut=@argIn"); - - CallableStatement cs = conn - .prepareCall("{call " + AbstractSQLGenerator.escapeIdentifier(procName) + "(?,?)}"); - try { - verifyCSGetters(cs); - } finally { + try (Statement stmt = conn.createStatement()) { stmt.executeUpdate("DROP PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName)); + stmt.executeUpdate("CREATE PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName) + " @argIn " + + sqlTypeExpression + "," + " @argOut " + sqlTypeExpression + " OUTPUT" + " AS " + + " SET @argOut=@argIn"); + + try (CallableStatement cs = conn + .prepareCall("{call " + AbstractSQLGenerator.escapeIdentifier(procName) + "(?,?)}")) { + verifyCSGetters(cs); + } finally { + stmt.executeUpdate("DROP PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName)); + } } - - cs.close(); - stmt.close(); } } @@ -1104,63 +1082,50 @@ enum TestValue { }; public void testResultSetGetters() throws Exception { - Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); - Connection conn = DriverManager.getConnection(connectionString); - - for (TestValue value : TestValue.values()) - value.sqlValue.verifyRSGetters(conn); + try (Connection conn = DriverManager.getConnection(connectionString)) { - conn.close(); + for (TestValue value : TestValue.values()) + value.sqlValue.verifyRSGetters(conn); + } } public void testResultSetUpdaters() throws Exception { - Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); - Connection conn = DriverManager.getConnection(connectionString); - - for (TestValue value : TestValue.values()) - value.sqlValue.verifyRSUpdaters(conn); + try (Connection conn = DriverManager.getConnection(connectionString)) { + for (TestValue value : TestValue.values()) - conn.close(); + value.sqlValue.verifyRSUpdaters(conn); + } } public void testSetters() throws Exception { - Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); - Connection conn = DriverManager.getConnection(connectionString + ";sendTimeAsDateTime=true"); + try (Connection conn = DriverManager.getConnection(connectionString + ";sendTimeAsDateTime=true")) { - for (TestValue value : TestValue.values()) - value.sqlValue.verifySetters(conn); - - conn.close(); + for (TestValue value : TestValue.values()) + value.sqlValue.verifySetters(conn); + } } public void testCallableStatementGetters() throws Exception { - Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); - Connection conn = DriverManager.getConnection(connectionString); - - for (TestValue value : TestValue.values()) - value.sqlValue.verifyCSGetters(conn); - - conn.close(); + try (Connection conn = DriverManager.getConnection(connectionString)) { + for (TestValue value : TestValue.values()) + value.sqlValue.verifyCSGetters(conn); + } } public void testResultSetMetaData() throws Exception { - Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); - Connection conn = DriverManager.getConnection(connectionString); - - for (TestValue value : TestValue.values()) - value.sqlValue.verifyResultSetMetaData(conn); + try (Connection conn = DriverManager.getConnection(connectionString)) { - conn.close(); + for (TestValue value : TestValue.values()) + value.sqlValue.verifyResultSetMetaData(conn); + } } public void testParameterMetaData() throws Exception { - Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); - Connection conn = DriverManager.getConnection(connectionString); - - for (TestValue value : TestValue.values()) - value.sqlValue.verifyParameterMetaData(conn); + try (Connection conn = DriverManager.getConnection(connectionString)) { - conn.close(); + for (TestValue value : TestValue.values()) + value.sqlValue.verifyParameterMetaData(conn); + } ; } /** @@ -1168,37 +1133,40 @@ public void testParameterMetaData() throws Exception { * sendTimeAsDatetime knob. */ public void testSendTimestampAsTimeAsDatetime() throws Exception { - Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); - Connection conn = DriverManager.getConnection(connectionString + ";sendTimeAsDatetime=true"); - Statement stmt = conn.createStatement(); - try { - stmt.executeUpdate("DROP PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName)); - } catch (SQLException e) {} - stmt.executeUpdate("CREATE PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName) + " @argIn time(7), " - + " @argOut time(7) OUTPUT " + " AS " + " SET @argOut=@argIn"); - - CallableStatement cs = conn.prepareCall("{call " + AbstractSQLGenerator.escapeIdentifier(procName) + "(?,?)}"); - - // Set up a timestamp with a time component that is the last millisecond of the day... - Timestamp ts = Timestamp.valueOf("2010-02-15 23:59:59.999"); - - // ... and send that timestamp to the server using the TIME SQL type rather than TIMESTAMP. - // If the driver is doing the right thing, it strips the date portion and, because - // sendTimeAsDatetime=true, rounds the resulting time value to midnight because it should - // be sending a DATETIME which has only 1/300s accuracy. - cs.setObject(1, ts, java.sql.Types.TIME); - cs.registerOutParameter(2, java.sql.Types.TIME); - cs.execute(); - - // Fetch the OUT parameter and verify that we have a date-normalized TIME of midnight - java.sql.Time timeOut = cs.getTime(2); - Timestamp tsOut = new Timestamp(timeOut.getTime()); - assertEquals(tsOut.toString(), "1970-01-01 00:00:00.0", "CS.getTime() - Wrong OUT value"); - - cs.close(); - stmt.executeUpdate("DROP PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName)); - stmt.close(); - conn.close(); + try (Connection conn = DriverManager.getConnection(connectionString + ";sendTimeAsDatetime=true")) { + try (Statement stmt = conn.createStatement()) { + + stmt.executeUpdate("DROP PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName)); + + stmt.executeUpdate("CREATE PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName) + + " @argIn time(7), " + " @argOut time(7) OUTPUT " + " AS " + " SET @argOut=@argIn"); + + try (CallableStatement cs = conn + .prepareCall("{call " + AbstractSQLGenerator.escapeIdentifier(procName) + "(?,?)}")) { + + // Set up a timestamp with a time component that is the last millisecond of the day... + Timestamp ts = Timestamp.valueOf("2010-02-15 23:59:59.999"); + + // ... and send that timestamp to the server using the TIME SQL type rather than TIMESTAMP. + // If the driver is doing the right thing, it strips the date portion and, because + // sendTimeAsDatetime=true, rounds the resulting time value to midnight because it should + // be sending a DATETIME which has only 1/300s accuracy. + cs.setObject(1, ts, java.sql.Types.TIME); + cs.registerOutParameter(2, java.sql.Types.TIME); + cs.execute(); + + // Fetch the OUT parameter and verify that we have a date-normalized TIME of midnight + java.sql.Time timeOut = cs.getTime(2); + Timestamp tsOut = new Timestamp(timeOut.getTime()); + assertEquals(tsOut.toString(), "1970-01-01 00:00:00.0", "CS.getTime() - Wrong OUT value"); + + } + } finally { + try (Statement stmt = conn.createStatement()) { + stmt.executeUpdate("DROP PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName)); + } + } + } } // 507919 - Sending Timestamp to the server via an updater does not result in the same behavior as a setter wrt @@ -1207,7 +1175,6 @@ public void testDoubleRounding() throws Exception { try (Connection conn = DriverManager.getConnection(connectionString)) { // create a table with a datetimeoffset column and insert a value in it - Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); assumeTrue(!TestUtils.isSqlAzureDW(conn), TestResource.getResource("R_skipAzure")); String sql; @@ -1293,53 +1260,49 @@ public void testWithJapaneseImperialCalendar() throws Exception { Locale defaultLocale = Locale.getDefault(); Locale.setDefault(japaneseImperialLocale); - try { - Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); - Connection conn = DriverManager.getConnection(connectionString + ";sendTimeAsDatetime=true"); - ResultSet rs; - - // Get Gregorian date using Japanese imperial calendar - rs = conn.createStatement().executeQuery("SELECT CAST('0821-01-04' AS DATE)"); - rs.next(); - Date date = rs.getDate(1, japaneseImperialCalendar); - assertEquals(date.toString(), "0821-01-04", "Get pre-Meiji"); - rs.close(); - PreparedStatement ps = conn.prepareStatement("SELECT CAST(? AS VARCHAR(40))"); - Timestamp ts; + try (Connection conn = DriverManager.getConnection(connectionString + ";sendTimeAsDatetime=true")) { - // Set second day of first year of Taisho era (1912 Gregorian) - // Note: Taisho era began July 30, 1912 Gregorian; second day of that year was July 31. - japaneseImperialCalendar.clear(); - japaneseImperialCalendar.set(Calendar.ERA, 2); // Taisho -> ERA 2 - japaneseImperialCalendar.set(Calendar.YEAR, 1); - japaneseImperialCalendar.set(Calendar.DAY_OF_YEAR, 2); - ts = new Timestamp(japaneseImperialCalendar.getTimeInMillis()); - ps.setTimestamp(1, ts, japaneseImperialCalendar); - rs = ps.executeQuery(); - rs.next(); - assertEquals(rs.getString(1), "1912-07-31 00:00:00.0000000", "Set Taisho 1"); - rs.close(); + // Get Gregorian date using Japanese imperial calendar + try (ResultSet rs = conn.createStatement().executeQuery("SELECT CAST('0821-01-04' AS DATE)")) { + rs.next(); + Date date = rs.getDate(1, japaneseImperialCalendar); + assertEquals(date.toString(), "0821-01-04", "Get pre-Meiji"); + } - // Set second year of Showa era (1927 Gregorian) - japaneseImperialCalendar.clear(); - japaneseImperialCalendar.set(Calendar.ERA, 3); // Showa -> ERA 3 - japaneseImperialCalendar.set(Calendar.YEAR, 2); - japaneseImperialCalendar.set(Calendar.MONTH, Calendar.FEBRUARY); - japaneseImperialCalendar.set(Calendar.DATE, 15); - japaneseImperialCalendar.set(Calendar.HOUR_OF_DAY, 8); - japaneseImperialCalendar.set(Calendar.MINUTE, 49); - japaneseImperialCalendar.set(Calendar.SECOND, 3); - japaneseImperialCalendar.set(Calendar.MILLISECOND, 87); - ts = new Timestamp(japaneseImperialCalendar.getTimeInMillis()); - ps.setTimestamp(1, ts, japaneseImperialCalendar); - rs = ps.executeQuery(); - rs.next(); - assertEquals(rs.getString(1), "1927-02-15 08:49:03.0870000", "Set Showa 2"); - rs.close(); + try (PreparedStatement ps = conn.prepareStatement("SELECT CAST(? AS VARCHAR(40))")) { + Timestamp ts; + + // Set second day of first year of Taisho era (1912 Gregorian) + // Note: Taisho era began July 30, 1912 Gregorian; second day of that year was July 31. + japaneseImperialCalendar.clear(); + japaneseImperialCalendar.set(Calendar.ERA, 2); // Taisho -> ERA 2 + japaneseImperialCalendar.set(Calendar.YEAR, 1); + japaneseImperialCalendar.set(Calendar.DAY_OF_YEAR, 2); + ts = new Timestamp(japaneseImperialCalendar.getTimeInMillis()); + ps.setTimestamp(1, ts, japaneseImperialCalendar); + try (ResultSet rs = ps.executeQuery()) { + rs.next(); + assertEquals(rs.getString(1), "1912-07-31 00:00:00.0000000", "Set Taisho 1"); + } - ps.close(); - conn.close(); + // Set second year of Showa era (1927 Gregorian) + japaneseImperialCalendar.clear(); + japaneseImperialCalendar.set(Calendar.ERA, 3); // Showa -> ERA 3 + japaneseImperialCalendar.set(Calendar.YEAR, 2); + japaneseImperialCalendar.set(Calendar.MONTH, Calendar.FEBRUARY); + japaneseImperialCalendar.set(Calendar.DATE, 15); + japaneseImperialCalendar.set(Calendar.HOUR_OF_DAY, 8); + japaneseImperialCalendar.set(Calendar.MINUTE, 49); + japaneseImperialCalendar.set(Calendar.SECOND, 3); + japaneseImperialCalendar.set(Calendar.MILLISECOND, 87); + ts = new Timestamp(japaneseImperialCalendar.getTimeInMillis()); + ps.setTimestamp(1, ts, japaneseImperialCalendar); + try (ResultSet rs = ps.executeQuery()) { + rs.next(); + assertEquals(rs.getString(1), "1927-02-15 08:49:03.0870000", "Set Showa 2"); + } + } } finally { Locale.setDefault(defaultLocale); } @@ -1381,22 +1344,18 @@ enum StringFormatTestValue { @Test public void testGetString() throws Exception { - Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); - Connection conn = DriverManager.getConnection(connectionString); - Statement stmt = conn.createStatement(); + try (Connection conn = DriverManager.getConnection(connectionString); Statement stmt = conn.createStatement()) { - for (StringFormatTestValue testValue : EnumSet.allOf(StringFormatTestValue.class)) { - String query = "SELECT " + "CAST('" + testValue.sqlLiteral + "' AS " + testValue.sqlType + "), " - + "CAST(CAST('" + testValue.sqlLiteral + "' AS " + testValue.sqlType + ") AS VARCHAR)"; + for (StringFormatTestValue testValue : EnumSet.allOf(StringFormatTestValue.class)) { + String query = "SELECT " + "CAST('" + testValue.sqlLiteral + "' AS " + testValue.sqlType + "), " + + "CAST(CAST('" + testValue.sqlLiteral + "' AS " + testValue.sqlType + ") AS VARCHAR)"; - ResultSet rs = stmt.executeQuery(query); - rs.next(); - assertEquals(rs.getString(1), rs.getString(2), "Test failed for " + testValue); - rs.close(); + try (ResultSet rs = stmt.executeQuery(query)) { + rs.next(); + assertEquals(rs.getString(1), rs.getString(2), "Test failed for " + testValue); + } + } } - - stmt.close(); - conn.close(); } @Test @@ -1409,71 +1368,65 @@ public void testWithThaiLocale() throws Exception { Locale locale = Locale.getDefault(); Locale.setDefault(new Locale("th", "TH")); - try { - Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); - Connection conn = DriverManager.getConnection(connectionString + ";sendTimeAsDatetime=true"); - - ResultSet rs; - + try (Connection conn = DriverManager.getConnection(connectionString + ";sendTimeAsDatetime=true")) { // Test setter conversions - PreparedStatement ps = conn.prepareStatement("SELECT CAST(? AS VARCHAR(40))"); - - // Need to use the following constructor for running against IBM JVM. Here, year should be year-1900, month - // is from 0-11. - Timestamp ts = new Timestamp(System.currentTimeMillis()); + try (PreparedStatement ps = conn.prepareStatement("SELECT CAST(? AS VARCHAR(40))")) { - // Test PreparedStatement with Timestamp - // Value sent as DATETIME2; result should have 7 digits of subsecond precision) - ps.setTimestamp(1, ts); - rs = ps.executeQuery(); - rs.next(); - assertEquals(rs.getString(1), tsFormat.format(ts), "Timestamp mismatch"); - rs.close(); + // Need to use the following constructor for running against IBM JVM. Here, year should be year-1900, + // month + // is from 0-11. + Timestamp ts = new Timestamp(System.currentTimeMillis()); - // Test PreparedStatement with Time - // Value sent as DATETIME w/Unix Epoch as base date when sendTimeAsDatetime=true - Time time = new Time(ts.getTime()); - ps.setTime(1, time); - rs = ps.executeQuery(); - rs.next(); + // Test PreparedStatement with Timestamp + // Value sent as DATETIME2; result should have 7 digits of subsecond precision) + ps.setTimestamp(1, ts); + try (ResultSet rs = ps.executeQuery()) { + rs.next(); + assertEquals(rs.getString(1), tsFormat.format(ts), "Timestamp mismatch"); + } - // compare these separately since there may be an extra space between the 2 - assertEquals(rs.getString(1).substring(0, 11), "Jan 1 1970", "Time mismatch"); - assertEquals(rs.getString(1).substring(rs.getString(1).length() - 7).trim(), - timeFormat.format(ts.getTime()), "Time mismatch"); - rs.close(); + // Test PreparedStatement with Time + // Value sent as DATETIME w/Unix Epoch as base date when sendTimeAsDatetime=true + Time time = new Time(ts.getTime()); + ps.setTime(1, time); + try (ResultSet rs = ps.executeQuery()) { + rs.next(); - // Test PreparedStatement with Date - Date date = new Date(ts.getTime()); - ps.setDate(1, date); - rs = ps.executeQuery(); - rs.next(); - assertEquals(rs.getString(1), dateFormat.format(ts), "Date mismatch"); - rs.close(); + // compare these separately since there may be an extra space between the 2 + assertEquals(rs.getString(1).substring(0, 11), "Jan 1 1970", "Time mismatch"); + assertEquals(rs.getString(1).substring(rs.getString(1).length() - 7).trim(), + timeFormat.format(ts.getTime()), "Time mismatch"); + } - // Test PreparedStatement with Date (using Buddhist calendar) - date = new Date(ts.getTime()); - ps.setDate(1, date, Calendar.getInstance()); - rs = ps.executeQuery(); - rs.next(); - assertEquals(rs.getString(1), dateFormat.format(ts), "Date mismatch (w/calendar)"); - rs.close(); + // Test PreparedStatement with Date + Date date = new Date(ts.getTime()); + ps.setDate(1, date); + try (ResultSet rs = ps.executeQuery()) { + rs.next(); + assertEquals(rs.getString(1), dateFormat.format(ts), "Date mismatch"); + } - // Test PreparedStatement with DateTimeOffset (using Buddhist calendar) - // Note: Expected value does not reflect Buddhist year, even though a Buddhist calendar is used. - DateTimeOffset dto = DateTimeOffset.valueOf(ts, - Calendar.getInstance(TimeZone.getTimeZone("America/Los_Angeles"))); + // Test PreparedStatement with Date (using Buddhist calendar) + date = new Date(ts.getTime()); + ps.setDate(1, date, Calendar.getInstance()); + try (ResultSet rs = ps.executeQuery()) { + rs.next(); + assertEquals(rs.getString(1), dateFormat.format(ts), "Date mismatch (w/calendar)"); + } - ((SQLServerPreparedStatement) ps).setDateTimeOffset(1, dto); - rs = ps.executeQuery(); - rs.next(); + // Test PreparedStatement with DateTimeOffset (using Buddhist calendar) + // Note: Expected value does not reflect Buddhist year, even though a Buddhist calendar is used. + DateTimeOffset dto = DateTimeOffset.valueOf(ts, + Calendar.getInstance(TimeZone.getTimeZone("America/Los_Angeles"))); - dtoFormat.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles")); - assertEquals(rs.getString(1), dtoFormat.format(ts), "DateTimeOffset mismatch"); - rs.close(); + ((SQLServerPreparedStatement) ps).setDateTimeOffset(1, dto); + try (ResultSet rs = ps.executeQuery()) { + rs.next(); - ps.close(); - conn.close(); + dtoFormat.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles")); + assertEquals(rs.getString(1), dtoFormat.format(ts), "DateTimeOffset mismatch"); + } + } } finally { Locale.setDefault(locale); } @@ -1482,145 +1435,127 @@ public void testWithThaiLocale() throws Exception { // DCR 393826 - DCR Need base date compatibility for Time to DATETIMEx conversions with 2.0 driver @Test public void testBaseDate() throws Exception { - Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); + Timestamp ts; // Test Java base date (1/1/1970) - Connection conn = DriverManager.getConnection(connectionString + ";sendTimeAsDatetime=true"); - PreparedStatement ps = conn.prepareStatement("SELECT CAST(? AS DATETIME)"); - ps.setTime(1, java.sql.Time.valueOf("12:34:56")); - ResultSet rs = ps.executeQuery(); - rs.next(); - Timestamp ts = rs.getTimestamp(1); - assertEquals(ts.toString(), "1970-01-01 12:34:56.0", "Expected Java base date"); - rs.close(); - ps.close(); - conn.close(); + try (Connection conn = DriverManager.getConnection(connectionString + ";sendTimeAsDatetime=true"); + PreparedStatement ps = conn.prepareStatement("SELECT CAST(? AS DATETIME)")) { + ps.setTime(1, java.sql.Time.valueOf("12:34:56")); + + try (ResultSet rs = ps.executeQuery()) { + rs.next(); + ts = rs.getTimestamp(1); + assertEquals(ts.toString(), "1970-01-01 12:34:56.0", "Expected Java base date"); + } + } // Test SQL Server base date (1/1/1900) - conn = DriverManager.getConnection(connectionString + ";sendTimeAsDatetime=false"); - ps = conn.prepareStatement("SELECT CAST(? AS DATETIME)"); - ps.setTime(1, java.sql.Time.valueOf("12:34:56")); - rs = ps.executeQuery(); - rs.next(); - ts = rs.getTimestamp(1); - assertEquals(ts.toString(), "1900-01-01 12:34:56.0", "Expected SQL Server base date"); - rs.close(); - ps.close(); - conn.close(); + try (Connection conn = DriverManager.getConnection(connectionString + ";sendTimeAsDatetime=false"); + PreparedStatement ps = conn.prepareStatement("SELECT CAST(? AS DATETIME)")) { + ps.setTime(1, java.sql.Time.valueOf("12:34:56")); + try (ResultSet rs = ps.executeQuery()) { + rs.next(); + ts = rs.getTimestamp(1); + assertEquals(ts.toString(), "1900-01-01 12:34:56.0", "Expected SQL Server base date"); + } + } } - // VSTS 393831 - setTimestamp to DATETIMEOFFSET must yield a value in local time with UTC time zone offset (+00:00) + // VSTS 393831 - setTimestamp to DATETIMEOFFSET must yield a value in local time with UTC time zone offset + // (+00:00) @Test public void testTimestampToDateTimeOffset() throws Exception { - Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); - - Connection conn = DriverManager.getConnection(connectionString); - PreparedStatement ps = conn.prepareStatement("SELECT CAST(? AS DATETIMEOFFSET)"); - ps.setTimestamp(1, Timestamp.valueOf("2010-01-06 12:34:56")); - ResultSet rs = ps.executeQuery(); - rs.next(); - DateTimeOffset dto = ((SQLServerResultSet) rs).getDateTimeOffset(1); - assertEquals(dto.toString(), "2010-01-06 12:34:56 +00:00", "Wrong value"); - rs.close(); - ps.close(); - conn.close(); + try (Connection conn = DriverManager.getConnection(connectionString); + PreparedStatement ps = conn.prepareStatement("SELECT CAST(? AS DATETIMEOFFSET)")) { + ps.setTimestamp(1, Timestamp.valueOf("2010-01-06 12:34:56")); + try (ResultSet rs = ps.executeQuery()) { + rs.next(); + DateTimeOffset dto = ((SQLServerResultSet) rs).getDateTimeOffset(1); + assertEquals(dto.toString(), "2010-01-06 12:34:56 +00:00", "Wrong value"); + } + } } - // VSTS 400431 - PS.setObject() on a datetime2 with values on or after 0700-02-29 have a value one day ahead stored + // VSTS 400431 - PS.setObject() on a datetime2 with values on or after 0700-02-29 have a value one day ahead + // stored // in the server @Test public void testJulianLeapYear() throws Exception { - Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); - Connection conn = DriverManager.getConnection(connectionString + ";sendTimeAsDatetime=true"); - // PreparedStatement ps = conn.prepareStatement("SELECT CAST(? AS VARCHAR)"); - PreparedStatement ps = conn.prepareStatement("SELECT CAST(? AS DATE)"); - ResultSet rs; - - // Julian date from string - Date julianDate = Date.valueOf("0700-03-01"); - ps.setDate(1, julianDate); - rs = ps.executeQuery(); - rs.next(); - assertEquals(rs.getString(1), "0700-03-01", "Wrong date returned"); - rs.close(); - - ps.close(); - conn.close(); + try (Connection conn = DriverManager.getConnection(connectionString + ";sendTimeAsDatetime=true"); + // PreparedStatement ps = conn.prepareStatement("SELECT CAST(? AS VARCHAR)"); + PreparedStatement ps = conn.prepareStatement("SELECT CAST(? AS DATE)")) { + + // Julian date from string + Date julianDate = Date.valueOf("0700-03-01"); + ps.setDate(1, julianDate); + try (ResultSet rs = ps.executeQuery()) { + rs.next(); + assertEquals(rs.getString(1), "0700-03-01", "Wrong date returned"); + } + } } @Test public void testGetTimeRounding() throws Exception { - Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); - - Connection conn = DriverManager.getConnection(connectionString + ";sendTimeAsDatetime=true"); - Statement stmt = conn.createStatement(); - ResultSet rs; - - // Test getTime() rounding from TIME(6) SQL type - rs = stmt.executeQuery("SELECT CAST('12:34:56.999500' AS TIME)"); - rs.next(); - assertEquals(rs.getTime(1).toString(), "12:34:57", "Rounding from TIME(6) failed"); - rs.close(); - - // Test getTime() rounding from character data - rs = stmt.executeQuery("SELECT '12:34:56.999500'"); - rs.next(); - assertEquals(rs.getTime(1).toString(), "12:34:57", "Rounding from character data failed"); - rs.close(); - - stmt.close(); - conn.close(); + try (Connection conn = DriverManager.getConnection(connectionString + ";sendTimeAsDatetime=true"); + Statement stmt = conn.createStatement()) { + + // Test getTime() rounding from TIME(6) SQL type + try (ResultSet rs = stmt.executeQuery("SELECT CAST('12:34:56.999500' AS TIME)")) { + rs.next(); + assertEquals(rs.getTime(1).toString(), "12:34:57", "Rounding from TIME(6) failed"); + } + + // Test getTime() rounding from character data + try (ResultSet rs = stmt.executeQuery("SELECT '12:34:56.999500'")) { + rs.next(); + assertEquals(rs.getTime(1).toString(), "12:34:57", "Rounding from character data failed"); + } + } } @Test public void testGregorianCutoverDateTime2() throws Exception { - Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); + try (Connection conn = DriverManager.getConnection(connectionString + ";sendTimeAsDatetime=true"); + PreparedStatement ps = conn.prepareStatement("SELECT CAST(? AS VARCHAR)")) { + Timestamp ts; - Connection conn = DriverManager.getConnection(connectionString + ";sendTimeAsDatetime=true"); - PreparedStatement ps = conn.prepareStatement("SELECT CAST(? AS VARCHAR)"); - ResultSet rs; - Timestamp ts; + // Test setting value during the Gregorian cutover via Timestamp constructed from String + ts = Timestamp.valueOf("1582-10-24 15:07:09.081"); + ps.setTimestamp(1, ts); + try (ResultSet rs = ps.executeQuery()) { + rs.next(); + assertEquals(rs.getString(1), "1582-10-24 15:07:09.0810000", "Wrong value"); + } - // Test setting value during the Gregorian cutover via Timestamp constructed from String - ts = Timestamp.valueOf("1582-10-24 15:07:09.081"); - ps.setTimestamp(1, ts); - rs = ps.executeQuery(); - rs.next(); - assertEquals(rs.getString(1), "1582-10-24 15:07:09.0810000", "Wrong value"); - rs.close(); - - // Test setting value during the Gregorian cutover via Timestamp constructed from Calendar - Calendar cal = Calendar.getInstance(); - cal.set(1582, Calendar.NOVEMBER, 1, 15, 7, 9); - cal.set(Calendar.MILLISECOND, 81); - ts = new Timestamp(cal.getTimeInMillis()); - ps.setTimestamp(1, ts); - rs = ps.executeQuery(); - rs.next(); - assertEquals(rs.getString(1), "1582-11-01 15:07:09.0810000", "Wrong value"); - rs.close(); - - ps.close(); - conn.close(); + // Test setting value during the Gregorian cutover via Timestamp constructed from Calendar + Calendar cal = Calendar.getInstance(); + cal.set(1582, Calendar.NOVEMBER, 1, 15, 7, 9); + cal.set(Calendar.MILLISECOND, 81); + ts = new Timestamp(cal.getTimeInMillis()); + ps.setTimestamp(1, ts); + try (ResultSet rs = ps.executeQuery()) { + rs.next(); + assertEquals(rs.getString(1), "1582-11-01 15:07:09.0810000", "Wrong value"); + } + } } - // VSTS 393831 - setTimestamp to DATETIMEOFFSET must yield a value in local time with UTC time zone offset (+00:00) + // VSTS 393831 - setTimestamp to DATETIMEOFFSET must yield a value in local time with UTC time zone offset + // (+00:00) // // In this case, verify that SELECT with a WHERE clause doesn't fail due to mapping the Timestamp value to a // SQL Server type that does not compare equal. For example, a DATETIMEOFFSET and DATETIME only compare equal // if the DATETIMEOFFSET offset is 0 (UTC). @Test public void testTimestampToDateTime() throws Exception { - Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); - - Connection conn = DriverManager.getConnection(connectionString); - PreparedStatement ps = conn.prepareStatement("SELECT 1 WHERE ?=CAST('2009-12-17 17:00:29' AS DATETIME)"); - ps.setTimestamp(1, Timestamp.valueOf("2009-12-17 17:00:29")); - ResultSet rs = ps.executeQuery(); - assertEquals(rs.next(), true, "Timestamp to DATETIME comparison failed for equal values"); - rs.close(); - ps.close(); - conn.close(); + try (Connection conn = DriverManager.getConnection(connectionString); PreparedStatement ps = conn + .prepareStatement("SELECT 1 WHERE ?=CAST('2009-12-17 17:00:29' AS DATETIME)")) { + ps.setTimestamp(1, Timestamp.valueOf("2009-12-17 17:00:29")); + try (ResultSet rs = ps.executeQuery()) { + assertEquals(rs.next(), true, "Timestamp to DATETIME comparison failed for equal values"); + } + } } // testUpdateMisc @@ -1628,129 +1563,122 @@ public void testTimestampToDateTime() throws Exception { // Haphazard collection of bugs that popped up during unit testing (i.e. regression tests) @Test public void testUpdateMisc() throws Exception { + try (SQLServerConnection conn = (SQLServerConnection) DriverManager + .getConnection(connectionString + ";sendTimeAsDatetime=true")) { - // + ""[" + driver.createuniqueidentifer("testUpdateMisc") + "]"; - Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); - - SQLServerConnection conn = (SQLServerConnection) DriverManager - .getConnection(connectionString + ";sendTimeAsDatetime=true"); - - assumeTrue(!TestUtils.isSqlAzureDW(conn), TestResource.getResource("R_skipAzure")); - - Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); - try { - stmt.executeUpdate("DROP TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName)); - } catch (SQLException e) {} - stmt.executeUpdate("CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName) - + " (col1 datetimeoffset(2)," + " col2 datetime," + " col3 time(5)," + " col4 datetime2(5)," - + " col5 smalldatetime," + " col6 time(2)," + " col7 int identity(1,1) primary key)"); - stmt.executeUpdate("INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableName) + " VALUES (" - + " '2010-01-12 09:00:23.17 -08:00'," + " '2010-01-12 10:20:23'," + " '10:20:23'," - + " '2010-01-12 10:20:23'," + " '2010-01-12 11:45:17'," + " '10:20:23')"); - - try { - DateTimeOffset dto; + assumeTrue(!TestUtils.isSqlAzureDW(conn), TestResource.getResource("R_skipAzure")); - ResultSet rs = stmt.executeQuery( - "SELECT *, CAST(col1 AS VARCHAR) FROM " + AbstractSQLGenerator.escapeIdentifier(tableName)); - rs.next(); + try (Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)) { + try { + stmt.executeUpdate("DROP TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName)); + } catch (SQLException e) {} - // Update datetimeoffset(2) from pre-Gregorian Date - rs.updateDate(1, Date.valueOf("0814-02-18")); - rs.updateRow(); - assertEquals(((SQLServerResultSet) rs).getDateTimeOffset(1).toString(), "0814-02-18 00:00:00 +00:00", - "Update of datetimeoffset(2) from pre-Gregorian Date"); - - // Update datetimeoffset(2) from "last" Time - rs.updateTime(1, new java.sql.Time(Timestamp.valueOf("1970-01-01 23:59:59.998").getTime())); - rs.updateRow(); - assertEquals(((SQLServerResultSet) rs).getDateTimeOffset(1).toString(), "1970-01-02 00:00:00 +00:00", - "Update datetimeoffset(2) from last Time"); + stmt.executeUpdate("CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName) + + " (col1 datetimeoffset(2)," + " col2 datetime," + " col3 time(5)," + " col4 datetime2(5)," + + " col5 smalldatetime," + " col6 time(2)," + " col7 int identity(1,1) primary key)"); + stmt.executeUpdate("INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableName) + " VALUES (" + + " '2010-01-12 09:00:23.17 -08:00'," + " '2010-01-12 10:20:23'," + " '10:20:23'," + + " '2010-01-12 10:20:23'," + " '2010-01-12 11:45:17'," + " '10:20:23')"); + DateTimeOffset dto; - // Update datetimeoffset(2) from the "last" Timestamp - rs.updateTimestamp(1, Timestamp.valueOf("9999-12-31 23:59:59.998")); - rs.updateRow(); - dto = ((SQLServerResultSet) rs).getDateTimeOffset(1); - assertEquals(dto.toString(), "9999-12-31 23:59:59.99 +00:00", - "Update datetimeoffset(2) from last Timestamp"); + try (ResultSet rs = stmt.executeQuery( + "SELECT *, CAST(col1 AS VARCHAR) FROM " + AbstractSQLGenerator.escapeIdentifier(tableName))) { + rs.next(); - // Attempt to update datetimeoffset(2) from the first out of range value - // Verify that an exception is thrown and that the statement/connection is still usable after - try { - Timestamp tsInvalid = Timestamp.valueOf("9999-12-31 23:59:59.999999999"); - tsInvalid = new Timestamp(tsInvalid.getTime() + 1); - rs.updateTimestamp(1, tsInvalid); - rs.updateRow(); - assertEquals(false, true, "Update succeeded with out of range value"); - } catch (SQLServerException e) { - assertEquals(e.getSQLState(), "22008", // data exception - datetime field overflow (ISO/IEC - // 9075-2:1999) - "Wrong exception received"); + // Update datetimeoffset(2) from pre-Gregorian Date + rs.updateDate(1, Date.valueOf("0814-02-18")); + rs.updateRow(); + assertEquals(((SQLServerResultSet) rs).getDateTimeOffset(1).toString(), + "0814-02-18 00:00:00 +00:00", "Update of datetimeoffset(2) from pre-Gregorian Date"); + + // Update datetimeoffset(2) from "last" Time + rs.updateTime(1, new java.sql.Time(Timestamp.valueOf("1970-01-01 23:59:59.998").getTime())); + rs.updateRow(); + assertEquals(((SQLServerResultSet) rs).getDateTimeOffset(1).toString(), + "1970-01-02 00:00:00 +00:00", "Update datetimeoffset(2) from last Time"); + + // Update datetimeoffset(2) from the "last" Timestamp + rs.updateTimestamp(1, Timestamp.valueOf("9999-12-31 23:59:59.998")); + rs.updateRow(); + dto = ((SQLServerResultSet) rs).getDateTimeOffset(1); + assertEquals(dto.toString(), "9999-12-31 23:59:59.99 +00:00", + "Update datetimeoffset(2) from last Timestamp"); + + // Attempt to update datetimeoffset(2) from the first out of range value + // Verify that an exception is thrown and that the statement/connection is still usable after + try { + Timestamp tsInvalid = Timestamp.valueOf("9999-12-31 23:59:59.999999999"); + tsInvalid = new Timestamp(tsInvalid.getTime() + 1); + rs.updateTimestamp(1, tsInvalid); + rs.updateRow(); + assertEquals(false, true, "Update succeeded with out of range value"); + } catch (SQLServerException e) { + assertEquals(e.getSQLState(), "22008", // data exception - datetime field overflow (ISO/IEC + // 9075-2:1999) + "Wrong exception received"); + } + + // Update time(5) from Timestamp with nanos more precise than 100ns + Timestamp ts = Timestamp.valueOf("2010-01-12 11:05:23"); + ts.setNanos(987659999); + rs.updateTimestamp(3, ts); + rs.updateRow(); + assertEquals(rs.getTimestamp(3).toString(), "1900-01-01 11:05:23.98766", + "Update time(5) from Timestamp with sub-100ns nanos"); + + // Update time(5) from Timestamp to max value in a day. The value should not be rounded + ts = Timestamp.valueOf("2010-01-12 23:59:59"); + ts.setNanos(999999999); + Time time = new java.sql.Time(ts.getTime()); + rs.updateTimestamp(3, ts); + rs.updateRow(); + assertEquals(rs.getTimestamp(3).toString(), "1900-01-01 23:59:59.99999", + "Update time(5) from Timestamp to max value in a day"); + + // Update time(2) from Time to max value in a day. The value should not be rounded + rs.updateTime(6, time); + rs.updateRow(); + assertEquals(new Timestamp(rs.getTime(6).getTime()).toString(), // conversion to timestamp is + // necessary to see fractional + // secs + "1970-01-01 23:59:59.99", "Update time(2) from Time to max value in a day"); + + // Update time(5) from Timestamp to max value in a second. The value should be rounded + ts = Timestamp.valueOf("2010-01-12 23:59:58"); + ts.setNanos(999999999); + time = new java.sql.Time(ts.getTime()); + rs.updateTimestamp(3, ts); + rs.updateRow(); + assertEquals(rs.getTimestamp(3).toString(), "1900-01-01 23:59:59.0", + "Update time(5) from Timestamp to max value in a second"); + + // Update time(2) from Time to max value in a second. The value should be rounded + rs.updateTime(6, time); + rs.updateRow(); + assertEquals(new Timestamp(rs.getTime(6).getTime()).toString(), // conversion to timestamp is + // necessary to see fractional + // secs + "1970-01-01 23:59:59.0", "Update time(2) from Time to max value in a second"); + + // Update datetime w/expected rounding of nanos to DATETIME's 1/300second resolution + ts = Timestamp.valueOf("6289-04-22 05:13:57.6745106"); + rs.updateTimestamp(2, ts); + rs.updateRow(); + assertEquals(rs.getTimestamp(2).toString(), "6289-04-22 05:13:57.677", + "Update datetime from Timestamp with sub-1/3second nanos"); + + // Update datetime with rounding-induced overflow from Time + // (should roll date part to 1/2/1970) + ts = Timestamp.valueOf("2010-01-18 23:59:59.999"); + rs.updateTime(2, new java.sql.Time(ts.getTime())); + rs.updateRow(); + assertEquals(rs.getTimestamp(2).toString(), "1970-01-02 00:00:00.0", + "Update datetime from Time near next day"); + + } finally { + stmt.executeUpdate("DROP TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName)); + } } - - // Update time(5) from Timestamp with nanos more precise than 100ns - Timestamp ts = Timestamp.valueOf("2010-01-12 11:05:23"); - ts.setNanos(987659999); - rs.updateTimestamp(3, ts); - rs.updateRow(); - assertEquals(rs.getTimestamp(3).toString(), "1900-01-01 11:05:23.98766", - "Update time(5) from Timestamp with sub-100ns nanos"); - - // Update time(5) from Timestamp to max value in a day. The value should not be rounded - ts = Timestamp.valueOf("2010-01-12 23:59:59"); - ts.setNanos(999999999); - Time time = new java.sql.Time(ts.getTime()); - rs.updateTimestamp(3, ts); - rs.updateRow(); - assertEquals(rs.getTimestamp(3).toString(), "1900-01-01 23:59:59.99999", - "Update time(5) from Timestamp to max value in a day"); - - // Update time(2) from Time to max value in a day. The value should not be rounded - rs.updateTime(6, time); - rs.updateRow(); - assertEquals(new Timestamp(rs.getTime(6).getTime()).toString(), // conversion to timestamp is - // necessary to see fractional - // secs - "1970-01-01 23:59:59.99", "Update time(2) from Time to max value in a day"); - - // Update time(5) from Timestamp to max value in a second. The value should be rounded - ts = Timestamp.valueOf("2010-01-12 23:59:58"); - ts.setNanos(999999999); - time = new java.sql.Time(ts.getTime()); - rs.updateTimestamp(3, ts); - rs.updateRow(); - assertEquals(rs.getTimestamp(3).toString(), "1900-01-01 23:59:59.0", - "Update time(5) from Timestamp to max value in a second"); - - // Update time(2) from Time to max value in a second. The value should be rounded - rs.updateTime(6, time); - rs.updateRow(); - assertEquals(new Timestamp(rs.getTime(6).getTime()).toString(), // conversion to timestamp is - // necessary to see fractional - // secs - "1970-01-01 23:59:59.0", "Update time(2) from Time to max value in a second"); - - // Update datetime w/expected rounding of nanos to DATETIME's 1/300second resolution - ts = Timestamp.valueOf("6289-04-22 05:13:57.6745106"); - rs.updateTimestamp(2, ts); - rs.updateRow(); - assertEquals(rs.getTimestamp(2).toString(), "6289-04-22 05:13:57.677", - "Update datetime from Timestamp with sub-1/3second nanos"); - - // Update datetime with rounding-induced overflow from Time - // (should roll date part to 1/2/1970) - ts = Timestamp.valueOf("2010-01-18 23:59:59.999"); - rs.updateTime(2, new java.sql.Time(ts.getTime())); - rs.updateRow(); - assertEquals(rs.getTimestamp(2).toString(), "1970-01-02 00:00:00.0", - "Update datetime from Time near next day"); - - rs.close(); - } finally { - stmt.executeUpdate("DROP TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName)); - stmt.close(); - conn.close(); } } - } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SparseTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SparseTest.java new file mode 100644 index 0000000000..c06dd667b3 --- /dev/null +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SparseTest.java @@ -0,0 +1,61 @@ +package com.microsoft.sqlserver.jdbc.datatypes; + +import java.sql.*; +import org.junit.jupiter.api.Test; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assumptions.assumeTrue; +import static org.junit.Assert.fail; + +import com.microsoft.sqlserver.jdbc.RandomUtil; +import com.microsoft.sqlserver.jdbc.TestResource; +import com.microsoft.sqlserver.jdbc.TestUtils; +import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; +import com.microsoft.sqlserver.testframework.AbstractTest; + +@RunWith(JUnitPlatform.class) +public class SparseTest extends AbstractTest +{ + final static String tableName = RandomUtil.getIdentifier("SparseTestTable"); + + @Test + public void testSparse() throws Exception { + try (Connection conn = DriverManager.getConnection(connectionString)) { + assumeTrue(!TestUtils.isSqlAzureDW(conn), TestResource.getResource("R_skipAzure")); + try (Statement stmt = conn.createStatement()) { + + // Create the test table + try { + stmt.executeUpdate("drop table " + AbstractSQLGenerator.escapeIdentifier(tableName)); + } catch (Exception e) {} + + StringBuilder bd = new StringBuilder(); + bd.append("create table " + AbstractSQLGenerator.escapeIdentifier(tableName) + + " (col1 int, col2 varbinary(max)"); + for (int i = 3; i <= 1024; i++) { + bd.append(", col" + i + " varchar(20) SPARSE NULL"); + } + bd.append(")"); + String query = bd.toString(); + + stmt.executeUpdate(query); + + stmt.executeUpdate("insert into " + AbstractSQLGenerator.escapeIdentifier(tableName) + + " (col1, col2, col1023)values(1, 0x45, 'yo')"); + + try (ResultSet rs = stmt + .executeQuery("Select * from " + AbstractSQLGenerator.escapeIdentifier(tableName))) { + rs.next(); + assertEquals(rs.getString("col1023"), "yo", "Wrong value returned"); + } + } finally { + try (Statement stmt = conn.createStatement()) { + stmt.executeUpdate("drop table " + AbstractSQLGenerator.escapeIdentifier(tableName)); + } catch (Exception e) { + fail(TestResource.getResource("R_createDropTableFailed") + e.toString()); + } + } + } + } +} From 83b6ddc718d3fc6912dd1a80ec267a552f859147 Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Thu, 22 Nov 2018 17:30:55 -0800 Subject: [PATCH 12/51] review changes --- .../microsoft/sqlserver/jdbc/TestResource.java | 1 - .../callablestatement/CallableMixedTest.java | 17 ++--------------- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java b/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java index d300f9e340..e4c538a308 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java @@ -153,7 +153,6 @@ protected Object[][] getContents() { {"R_expectedValue", "Expected value: "}, {"R_expectedValueAtIndex", "Expected value at index: "}, {"R_switchFailed", "Switch case is not matched with data"}, {"R_resultsetNotInstance", "Result set is not instance of SQLServerResultSet"}, - {"R_resultsetNotInstance", "Result set is not instance of SQLServerResultSet"}, {"R_noJRESupport", "No JRE support for {0}"}, }; } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java index 91b6615041..790e140115 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java @@ -25,19 +25,14 @@ public void datatypestest() throws Exception { try (Connection conn = DriverManager.getConnection(connectionString)) { try (Statement stmt = conn.createStatement()) { - try { - stmt.executeUpdate("DROP TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName)); - stmt.executeUpdate(" DROP PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName)); - } catch (Exception e) {} + TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(tableName), stmt); String createSQL = "create table " + AbstractSQLGenerator.escapeIdentifier(tableName) + "(c1_int int primary key, col2 int)"; stmt.executeUpdate(createSQL); stmt.executeUpdate("Insert into " + AbstractSQLGenerator.escapeIdentifier(tableName) + " values(0, 1)"); - } - try (Statement stmt = conn.createStatement()) { stmt.executeUpdate("CREATE PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName) + " (@p2_int int, @p2_int_out int OUTPUT, @p4_smallint smallint, @p4_smallint_out smallint OUTPUT) AS begin transaction SELECT * FROM " + AbstractSQLGenerator.escapeIdentifier(tableName) @@ -61,25 +56,22 @@ public void datatypestest() throws Exception { if (rs.getInt(1) != 0) { fail("Received data not equal to setdata"); - } if (cstmt.getInt((int) 5) != -5372) { fail("Received data not equal to setdata"); - } + // do nothing and reexecute rs = cstmt.executeQuery(); // get the param without getting the resultset rs = cstmt.executeQuery(); if (cstmt.getInt((int) 1) != -2147483648) { fail("Received data not equal to setdata"); - } if (cstmt.getInt((int) 1) != -2147483648) { fail("Received data not equal to setdata"); - } rs = cstmt.executeQuery(); @@ -87,17 +79,14 @@ public void datatypestest() throws Exception { if (rs.getInt(1) != 0) { fail("Received data not equal to setdata"); - } if (cstmt.getInt((int) 1) != -2147483648) { fail("Received data not equal to setdata"); - } if (cstmt.getInt((int) 5) != -5372) { fail("Received data not equal to setdata"); - } rs = cstmt.executeQuery(); @@ -109,8 +98,6 @@ public void datatypestest() throws Exception { } catch (SQLException e) { fail(e.toString()); } - } } - } From b3c5ac5c9800a07f0ea97214972865ca9286bad7 Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Fri, 23 Nov 2018 15:34:29 -0800 Subject: [PATCH 13/51] review comments --- .../microsoft/sqlserver/jdbc/TestUtils.java | 2 - .../jdbc/resultset/ResultSetTest.java | 6 -- .../jdbc/unit/serial/DTOSerialTest.java | 2 - .../jdbc/unit/serial/DateTimeOffset.java.txt | 87 +++++++++++++++++++ 4 files changed, 87 insertions(+), 10 deletions(-) create mode 100644 src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DateTimeOffset.java.txt diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/TestUtils.java b/src/test/java/com/microsoft/sqlserver/jdbc/TestUtils.java index a580def648..0322a10113 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/TestUtils.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/TestUtils.java @@ -625,8 +625,6 @@ public static Object roundSmallDateTimeValue(Object value) { * @return boolean */ public static boolean serverSupportsDataClassification(Statement stmt) { - - try { stmt.execute("SELECT * FROM SYS.SENSITIVITY_CLASSIFICATIONS"); } catch (SQLException e) { diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java index d7625a514f..2e8764e9f3 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java @@ -34,15 +34,9 @@ import com.microsoft.sqlserver.jdbc.ISQLServerResultSet; import com.microsoft.sqlserver.jdbc.RandomUtil; -import com.microsoft.sqlserver.jdbc.SQLServerResultSet; import com.microsoft.sqlserver.jdbc.TestUtils; import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; import com.microsoft.sqlserver.testframework.AbstractTest; -import com.microsoft.sqlserver.testframework.DBConnection; -import com.microsoft.sqlserver.testframework.DBResultSet; -import com.microsoft.sqlserver.testframework.DBResultSetTypes; -import com.microsoft.sqlserver.testframework.DBStatement; - @RunWith(JUnitPlatform.class) public class ResultSetTest extends AbstractTest { diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java index d71a12d323..3b0c951ad2 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java @@ -6,8 +6,6 @@ import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; -import junit.framework.*; - import com.microsoft.sqlserver.jdbc.*; import microsoft.sql.*; diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DateTimeOffset.java.txt b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DateTimeOffset.java.txt new file mode 100644 index 0000000000..524864044a --- /dev/null +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DateTimeOffset.java.txt @@ -0,0 +1,87 @@ +package microsoft.sql; + +import java.io.File; +import java.io.FileOutputStream; + +/* Copyright Microsoft All rights reserved. */ +/* + * Dummy class to create serialized values that should not be accepted by the standard + * class. + */ + + +/** + * !DUMMY CLASS! + * + * + */ +public final class DateTimeOffset extends Object implements java.io.Serializable +{ + private static final long serialVersionUID = 541973748553014280L; + + public final long utcMillis; + public final int nanos; + public final int minutesOffset; + + public DateTimeOffset(long utcMillis, int nanos, int minutesOffset) + { + this.utcMillis = utcMillis; + this.nanos = nanos; + this.minutesOffset = minutesOffset; + } + public static class SerializationProxy implements java.io.Serializable + { + private final long utcMillis; + private final int nanos; + private final int minutesOffset; + + SerializationProxy(DateTimeOffset dateTimeOffset) + { + this.utcMillis = dateTimeOffset.utcMillis; + this.nanos = dateTimeOffset.nanos; + this.minutesOffset = dateTimeOffset.minutesOffset; + } + + private static final long serialVersionUID = 664661379547314226L; + + } + + private Object writeReplace() + { + return new SerializationProxy(this); + } + + public static void createBadNanos()throws Exception + { + File simple = new File("d:\\temp\\wrongnanos.dat"); + // -1 should not be accepted + DateTimeOffset dtm = new DateTimeOffset(1, -1, 13*60); + createBadSerializedForm(simple, dtm); + } + public static void createBadOffset()throws Exception + { + File simple = new File("d:\\temp\\wrongoffset.dat"); + // 15 hours offset not valid + DateTimeOffset dtm = new DateTimeOffset(1, 300, 15*60); + createBadSerializedForm(simple, dtm); + } + static void createBadSerializedForm(File simple, DateTimeOffset dtm) throws Exception + { + FileOutputStream so = new java.io.FileOutputStream(simple); + java.io.ObjectOutput s = new java.io.ObjectOutputStream(so); + s.writeObject(dtm); + s.flush(); + s.close(); + } + + private void readObject(java.io.ObjectInputStream stream) throws java.io.InvalidObjectException + { + // For added security/robustness, the only way to rehydrate a serialized DateTimeOffset + // is to use a SerializationProxy. Direct use of readObject() is not supported. + // + // Note that the reason provided for the InvalidObjectException is deliberately less + // than specific. Anyone trying to "hack" this class doesn't need to know why we + // didn't let them... + throw new java.io.InvalidObjectException(""); + } +} From 8f71ce674e93962596025e4d60043641c0fc8151 Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Fri, 23 Nov 2018 15:37:31 -0800 Subject: [PATCH 14/51] more review updates --- .../sqlserver/jdbc/unit/serial/DTOSerialTest.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java index 3b0c951ad2..5f294f35df 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java @@ -7,6 +7,8 @@ import org.junit.runner.RunWith; import com.microsoft.sqlserver.jdbc.*; +import com.microsoft.sqlserver.testframework.AbstractTest; + import microsoft.sql.*; import java.io.*; @@ -15,15 +17,12 @@ @RunWith(JUnitPlatform.class) -public class DTOSerialTest { +public class DTOSerialTest extends AbstractTest { private static final String dateString = "2007-05-08 12:35:29.1234567 +12:15"; // public static void testDSerial(String connString) throws Exception @Test public void testDSerial() throws Exception { - Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); - String connectionString = TestUtils.getConfiguredProperty("mssql_jdbc_test_connection_properties"); - try (Connection conn = DriverManager.getConnection(connectionString); Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)) { From c46db9fc08b57b643d0f4a60d179753834d3a7e5 Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Sun, 25 Nov 2018 21:35:43 -0800 Subject: [PATCH 15/51] review updates --- .../microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java | 1 - .../sqlserver/jdbc/unit/serial/DateTimeOffset.java.txt | 4 ---- 2 files changed, 5 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java index 5f294f35df..c0c9025b15 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java @@ -92,7 +92,6 @@ private static void verifyCorrectSend(DateTimeOffset dtN) throws Exception { // create a DTO try (Connection conn = DriverManager.getConnection(connectionString); - SQLServerPreparedStatement ps = (SQLServerPreparedStatement) conn .prepareStatement("SELECT CAST(? AS datetimeoffset(7)) AS" + " 'datetimeoffset IS08601' ")) { ps.setDateTimeOffset(1, dtN); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DateTimeOffset.java.txt b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DateTimeOffset.java.txt index 524864044a..3db7773deb 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DateTimeOffset.java.txt +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DateTimeOffset.java.txt @@ -78,10 +78,6 @@ public final class DateTimeOffset extends Object implements java.io.Serializable { // For added security/robustness, the only way to rehydrate a serialized DateTimeOffset // is to use a SerializationProxy. Direct use of readObject() is not supported. - // - // Note that the reason provided for the InvalidObjectException is deliberately less - // than specific. Anyone trying to "hack" this class doesn't need to know why we - // didn't let them... throw new java.io.InvalidObjectException(""); } } From 3a0703d1404f422e76d78fa89e9d03b0a2e402af Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Mon, 26 Nov 2018 12:17:05 -0800 Subject: [PATCH 16/51] removed dummy file --- .../jdbc/unit/serial/DTOSerialTest.java | 25 +++--- .../jdbc/unit/serial/DateTimeOffset.java.txt | 83 ------------------- 2 files changed, 11 insertions(+), 97 deletions(-) delete mode 100644 src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DateTimeOffset.java.txt diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java index c0c9025b15..0b4126e99f 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java @@ -65,10 +65,10 @@ public void testESerial() throws Exception { fail("Errors are different."); } if (sqlState != ex.getSQLState()) { - fail("Errors are different."); + fail("Sql states are different."); } if (errCode != ex.getErrorCode()) { - fail("Errors are different."); + fail("Error codes are different."); } } } @@ -101,31 +101,28 @@ private static void verifyCorrectSend(DateTimeOffset dtN) throws Exception { } } - /* - * I have created two files with wrong nano value (-1) and wrong offset (15*60) by "editing" couple of generated - * serialized files I have checked them in here so they can be used verify the error handling. Note the code to - * generate this is checked in as a text file in the same dir. DateTimeOffset.java.txt - */ // Negative test cases. private static void verifyMessedSerialization() throws Exception { - // these values are from the wrongnanos.dat wrongoffset.dat files checked in with the code - // the values represent the serialized DTO class with invalid values. - byte wrongnanos[] = {-84, -19, 0, 5, 115, 114, 0, 47, 109, 105, 99, 114, 111, 115, 111, 102, 116, 46, 115, 113, + // serialized DTO class with wrong nano values (-1) + byte wrongNanos[] = {-84, -19, 0, 5, 115, 114, 0, 47, 109, 105, 99, 114, 111, 115, 111, 102, 116, 46, 115, 113, 108, 46, 68, 97, 116, 101, 84, 105, 109, 101, 79, 102, 102, 115, 101, 116, 36, 83, 101, 114, 105, 97, 108, 105, 122, 97, 116, 105, 111, 110, 80, 114, 111, 120, 121, 9, 57, 90, 0, -49, -42, -72, 50, 2, 0, 3, 73, 0, 13, 109, 105, 110, 117, 116, 101, 115, 79, 102, 102, 115, 101, 116, 73, 0, 5, 110, 97, 110, 111, 115, 74, 0, 9, 117, 116, 99, 77, 105, 108, 108, 105, 115, 120, 112, 0, 0, 3, 12, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 1}; - byte wrongoffset[] = {-84, -19, 0, 5, 115, 114, 0, 47, 109, 105, 99, 114, 111, 115, 111, 102, 116, 46, 115, 113, + + // serialized DTO class with wrong offset (15*60) + byte wrongOffset[] = {-84, -19, 0, 5, 115, 114, 0, 47, 109, 105, 99, 114, 111, 115, 111, 102, 116, 46, 115, 113, 108, 46, 68, 97, 116, 101, 84, 105, 109, 101, 79, 102, 102, 115, 101, 116, 36, 83, 101, 114, 105, 97, 108, 105, 122, 97, 116, 105, 111, 110, 80, 114, 111, 120, 121, 9, 57, 90, 0, -49, -42, -72, 50, 2, 0, 3, 73, 0, 13, 109, 105, 110, 117, 116, 101, 115, 79, 102, 102, 115, 101, 116, 73, 0, 5, 110, 97, 110, 111, 115, 74, 0, 9, 117, 116, 99, 77, 105, 108, 108, 105, 115, 120, 112, 0, 0, 3, -124, 0, 0, 1, 44, 0, 0, 0, 0, 0, 0, 0, 1}; - // These two serialized forms throw the exception illegalargument + + // These two serialized forms throw the exception IllegalArgumentException boolean exThrown = false; try { - verifyMessedSerializationHelper(wrongnanos); + verifyMessedSerializationHelper(wrongNanos); } catch (IllegalArgumentException e) { exThrown = true; } @@ -136,7 +133,7 @@ private static void verifyMessedSerialization() throws Exception { exThrown = false; try { - verifyMessedSerializationHelper(wrongoffset); + verifyMessedSerializationHelper(wrongOffset); } catch (IllegalArgumentException e) { exThrown = true; } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DateTimeOffset.java.txt b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DateTimeOffset.java.txt deleted file mode 100644 index 3db7773deb..0000000000 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DateTimeOffset.java.txt +++ /dev/null @@ -1,83 +0,0 @@ -package microsoft.sql; - -import java.io.File; -import java.io.FileOutputStream; - -/* Copyright Microsoft All rights reserved. */ -/* - * Dummy class to create serialized values that should not be accepted by the standard - * class. - */ - - -/** - * !DUMMY CLASS! - * - * - */ -public final class DateTimeOffset extends Object implements java.io.Serializable -{ - private static final long serialVersionUID = 541973748553014280L; - - public final long utcMillis; - public final int nanos; - public final int minutesOffset; - - public DateTimeOffset(long utcMillis, int nanos, int minutesOffset) - { - this.utcMillis = utcMillis; - this.nanos = nanos; - this.minutesOffset = minutesOffset; - } - public static class SerializationProxy implements java.io.Serializable - { - private final long utcMillis; - private final int nanos; - private final int minutesOffset; - - SerializationProxy(DateTimeOffset dateTimeOffset) - { - this.utcMillis = dateTimeOffset.utcMillis; - this.nanos = dateTimeOffset.nanos; - this.minutesOffset = dateTimeOffset.minutesOffset; - } - - private static final long serialVersionUID = 664661379547314226L; - - } - - private Object writeReplace() - { - return new SerializationProxy(this); - } - - public static void createBadNanos()throws Exception - { - File simple = new File("d:\\temp\\wrongnanos.dat"); - // -1 should not be accepted - DateTimeOffset dtm = new DateTimeOffset(1, -1, 13*60); - createBadSerializedForm(simple, dtm); - } - public static void createBadOffset()throws Exception - { - File simple = new File("d:\\temp\\wrongoffset.dat"); - // 15 hours offset not valid - DateTimeOffset dtm = new DateTimeOffset(1, 300, 15*60); - createBadSerializedForm(simple, dtm); - } - static void createBadSerializedForm(File simple, DateTimeOffset dtm) throws Exception - { - FileOutputStream so = new java.io.FileOutputStream(simple); - java.io.ObjectOutput s = new java.io.ObjectOutputStream(so); - s.writeObject(dtm); - s.flush(); - s.close(); - } - - private void readObject(java.io.ObjectInputStream stream) throws java.io.InvalidObjectException - { - // For added security/robustness, the only way to rehydrate a serialized DateTimeOffset - // is to use a SerializationProxy. Direct use of readObject() is not supported. - throw new java.io.InvalidObjectException(""); - } -} From d5071146c9d9563b6c8493c048cb01ade2d1c224 Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Mon, 26 Nov 2018 18:03:52 -0800 Subject: [PATCH 17/51] review udpates --- .../microsoft/sqlserver/jdbc/TestUtils.java | 9 ++ .../callablestatement/CallableMixedTest.java | 18 ++- .../jdbc/datatypes/BigIntegerTest.java | 61 ++++----- .../jdbc/datatypes/KatmaiDataTypesTest.java | 123 ++++++++---------- .../sqlserver/jdbc/datatypes/SparseTest.java | 22 ++-- 5 files changed, 106 insertions(+), 127 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/TestUtils.java b/src/test/java/com/microsoft/sqlserver/jdbc/TestUtils.java index 0322a10113..bc3ee69d79 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/TestUtils.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/TestUtils.java @@ -64,11 +64,16 @@ public class TestUtils { // '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; private final static int ENGINE_EDITION_FOR_SQL_AZURE = 5; private final static int ENGINE_EDITION_FOR_SQL_AZURE_DW = 6; + // whether we determined if the target server is SQL Azure + private static boolean _determinedSqlAzureOrSqlServer = false; + private static boolean _isSqlAzure = false; + /** * Returns serverType * @@ -677,6 +682,10 @@ public static String escapeSingleQuotes(String name) { * @throws SQLException */ public static boolean isSqlAzure(Connection con) throws SQLException { + if (_determinedSqlAzureOrSqlServer) { + return _isSqlAzure; + } + try (ResultSet rs = con.createStatement().executeQuery("SELECT CAST(SERVERPROPERTY('EngineEdition') as INT)")) { rs.next(); int engineEdition = rs.getInt(1); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java index 790e140115..933fba4136 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java @@ -3,9 +3,9 @@ import java.sql.*; import com.microsoft.sqlserver.jdbc.RandomUtil; -import com.microsoft.sqlserver.jdbc.SQLServerDriver; import com.microsoft.sqlserver.jdbc.TestUtils; import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; +import com.microsoft.sqlserver.testframework.AbstractTest; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; @@ -14,28 +14,26 @@ @RunWith(JUnitPlatform.class) -public class CallableMixedTest { +public class CallableMixedTest extends AbstractTest { @Test public void datatypestest() throws Exception { - Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver"); - String connectionString = TestUtils.getConfiguredProperty("mssql_jdbc_test_connection_properties"); String tableName = RandomUtil.getIdentifier("TFOO3"); + String escapedTableName = AbstractSQLGenerator.escapeIdentifier(tableName); String procName = RandomUtil.getIdentifier("SPFOO3"); try (Connection conn = DriverManager.getConnection(connectionString)) { try (Statement stmt = conn.createStatement()) { - TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(tableName), stmt); + TestUtils.dropTableIfExists(escapedTableName, stmt); - String createSQL = "create table " + AbstractSQLGenerator.escapeIdentifier(tableName) - + "(c1_int int primary key, col2 int)"; + String createSQL = "create table " + escapedTableName + "(c1_int int primary key, col2 int)"; stmt.executeUpdate(createSQL); - stmt.executeUpdate("Insert into " + AbstractSQLGenerator.escapeIdentifier(tableName) + " values(0, 1)"); + stmt.executeUpdate("Insert into " + escapedTableName + " values(0, 1)"); stmt.executeUpdate("CREATE PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName) + " (@p2_int int, @p2_int_out int OUTPUT, @p4_smallint smallint, @p4_smallint_out smallint OUTPUT) AS begin transaction SELECT * FROM " - + AbstractSQLGenerator.escapeIdentifier(tableName) + + escapedTableName + " ; SELECT @p2_int_out=@p2_int, @p4_smallint_out=@p4_smallint commit transaction RETURN -2147483648"); } @@ -94,7 +92,7 @@ public void datatypestest() throws Exception { } finally { try (Connection conn = DriverManager.getConnection(connectionString); Statement stmt = conn.createStatement()) { - TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(tableName), stmt); + TestUtils.dropTableIfExists(escapedTableName, stmt); } catch (SQLException e) { fail(e.toString()); } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/BigIntegerTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/BigIntegerTest.java index a426572453..670515ec07 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/BigIntegerTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/BigIntegerTest.java @@ -15,9 +15,11 @@ import java.math.BigInteger; import com.microsoft.sqlserver.jdbc.RandomUtil; +import com.microsoft.sqlserver.jdbc.TestUtils; import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; import com.microsoft.sqlserver.testframework.AbstractTest; + /* * This test is for testing the setObject methods for the new data type mappings in JDBC 4.1 for java.math.BigInteger */ @@ -31,26 +33,23 @@ enum TestType { }; final static String tableName = RandomUtil.getIdentifier("BigIntegerTestTable"); + final static String escapedTableName = AbstractSQLGenerator.escapeIdentifier(tableName); @Test public void testJDBC41BigInteger() throws Exception { try (Connection conn = DriverManager.getConnection(connectionString); Statement stmt = conn.createStatement()) { // Create the test table - try { - stmt.executeUpdate("drop table " + AbstractSQLGenerator.escapeIdentifier(tableName)); - } catch (Exception e) {} + TestUtils.dropTableIfExists(escapedTableName, stmt); - String query = "create table " + AbstractSQLGenerator.escapeIdentifier(tableName) + String query = "create table " + escapedTableName + " (col1 varchar(100), col2 bigint, col3 real, col4 float, " + "col5 numeric(38,0), col6 int, col7 smallint, col8 char(100), col9 varchar(max), " + "id int IDENTITY primary key)"; stmt.executeUpdate(query); - try (PreparedStatement pstmt = conn - .prepareStatement("INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableName) - + " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?) SELECT * FROM " - + AbstractSQLGenerator.escapeIdentifier(tableName) + " where id = ?")) { + try (PreparedStatement pstmt = conn.prepareStatement("INSERT INTO " + escapedTableName + + " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?) SELECT * FROM " + escapedTableName + " where id = ?")) { // test that the driver converts the BigInteger values greater than LONG.MAX_VALUE and lesser than // LONG.MIN_VALUE correctly @@ -63,45 +62,33 @@ public void testJDBC41BigInteger() throws Exception { // JDBC // BIGINT, the max and min limits for int row = 1; - testSetObject(AbstractSQLGenerator.escapeIdentifier(tableName), BigInteger.valueOf(Long.MAX_VALUE), - row++, pstmt, TestType.SETOBJECT_WITHTYPE); - - testSetObject(AbstractSQLGenerator.escapeIdentifier(tableName), BigInteger.valueOf(Long.MIN_VALUE), - row++, pstmt, TestType.SETOBJECT_WITHTYPE); - testSetObject(AbstractSQLGenerator.escapeIdentifier(tableName), BigInteger.valueOf(10), row++, pstmt, - TestType.SETOBJECT_WITHTYPE); - testSetObject(AbstractSQLGenerator.escapeIdentifier(tableName), BigInteger.valueOf(-10), row++, pstmt, - TestType.SETOBJECT_WITHTYPE); - testSetObject(AbstractSQLGenerator.escapeIdentifier(tableName), BigInteger.ZERO, row++, pstmt, + testSetObject(escapedTableName, BigInteger.valueOf(Long.MAX_VALUE), row++, pstmt, TestType.SETOBJECT_WITHTYPE); - testSetObject(AbstractSQLGenerator.escapeIdentifier(tableName), bigIntPos, row++, pstmt, - TestType.SETOBJECT_WITHTYPE); - testSetObject(AbstractSQLGenerator.escapeIdentifier(tableName), bigIntNeg, row++, pstmt, + + testSetObject(escapedTableName, BigInteger.valueOf(Long.MIN_VALUE), row++, pstmt, TestType.SETOBJECT_WITHTYPE); + testSetObject(escapedTableName, BigInteger.valueOf(10), row++, pstmt, TestType.SETOBJECT_WITHTYPE); + testSetObject(escapedTableName, BigInteger.valueOf(-10), row++, pstmt, TestType.SETOBJECT_WITHTYPE); + testSetObject(escapedTableName, BigInteger.ZERO, row++, pstmt, TestType.SETOBJECT_WITHTYPE); + testSetObject(escapedTableName, bigIntPos, row++, pstmt, TestType.SETOBJECT_WITHTYPE); + testSetObject(escapedTableName, bigIntNeg, row++, pstmt, TestType.SETOBJECT_WITHTYPE); // Test setObject method with SQL TYPE parameter - testSetObject(AbstractSQLGenerator.escapeIdentifier(tableName), BigInteger.valueOf(Long.MAX_VALUE), - row++, pstmt, TestType.SETOBJECT_WITHOUTTYPE); - testSetObject(AbstractSQLGenerator.escapeIdentifier(tableName), BigInteger.valueOf(Long.MIN_VALUE), - row++, pstmt, TestType.SETOBJECT_WITHOUTTYPE); - testSetObject(AbstractSQLGenerator.escapeIdentifier(tableName), BigInteger.valueOf(1000), row++, pstmt, - TestType.SETOBJECT_WITHOUTTYPE); - testSetObject(AbstractSQLGenerator.escapeIdentifier(tableName), BigInteger.valueOf(-1000), row++, pstmt, - TestType.SETOBJECT_WITHOUTTYPE); - testSetObject(AbstractSQLGenerator.escapeIdentifier(tableName), BigInteger.ZERO, row++, pstmt, + testSetObject(escapedTableName, BigInteger.valueOf(Long.MAX_VALUE), row++, pstmt, TestType.SETOBJECT_WITHOUTTYPE); - testSetObject(AbstractSQLGenerator.escapeIdentifier(tableName), bigIntPos, row++, pstmt, + testSetObject(escapedTableName, BigInteger.valueOf(Long.MIN_VALUE), row++, pstmt, TestType.SETOBJECT_WITHOUTTYPE); - testSetObject(AbstractSQLGenerator.escapeIdentifier(tableName), bigIntNeg, row++, pstmt, + testSetObject(escapedTableName, BigInteger.valueOf(1000), row++, pstmt, TestType.SETOBJECT_WITHOUTTYPE); + testSetObject(escapedTableName, BigInteger.valueOf(-1000), row++, pstmt, TestType.SETOBJECT_WITHOUTTYPE); + testSetObject(escapedTableName, BigInteger.ZERO, row++, pstmt, TestType.SETOBJECT_WITHOUTTYPE); + testSetObject(escapedTableName, bigIntPos, row++, pstmt, TestType.SETOBJECT_WITHOUTTYPE); + testSetObject(escapedTableName, bigIntNeg, row++, pstmt, TestType.SETOBJECT_WITHOUTTYPE); // Test setNull - testSetObject(AbstractSQLGenerator.escapeIdentifier(tableName), bigIntNeg, row++, pstmt, - TestType.SETNULL); + testSetObject(escapedTableName, bigIntNeg, row++, pstmt, TestType.SETNULL); - try { - stmt.executeUpdate("drop table " + AbstractSQLGenerator.escapeIdentifier(tableName)); - } catch (Exception e) {} + TestUtils.dropTableIfExists(escapedTableName, stmt); } } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java index c8d3ff4695..fee805fd4c 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java @@ -29,7 +29,10 @@ public class KatmaiDataTypesTest extends AbstractTest { final static String tableName = RandomUtil.getIdentifier("KatmaiDataTypesTable"); + final static String escapedTableName = AbstractSQLGenerator.escapeIdentifier(tableName); + final static String procName = RandomUtil.getIdentifier("KatmaiDataTypesTableProc"); + final static String escapedProcName = AbstractSQLGenerator.escapeIdentifier(procName); enum SQLType { date("yyyy-mm-dd", 0, java.sql.Types.DATE, "Date"), @@ -88,6 +91,17 @@ final String getString() { this.scale = fractionalSecondsDigits; } + /** + * For testing the setObject and setNull methods in PreparedStatement, use the verifySetter* methods. These + * methods prepare a single statement and execute it for all different data types by calling the appropriate + * 'setObject' methods for each data type and/or type conversion. + */ + abstract void verifySetters(PreparedStatement ps) throws Exception; + + abstract void verifySettersUtilDate(PreparedStatement ps) throws Exception; + + abstract void verifySettersCalendar(PreparedStatement ps) throws Exception; + private String sqlCastExpression() { return "CAST('" + stringValue + "' AS " + sqlTypeExpression + ")"; } @@ -126,11 +140,10 @@ void verifyParameterMetaData(Connection conn) throws Exception { try (Statement stmt = conn.createStatement()) { // Create the stored proc - stmt.executeUpdate("CREATE PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName) + " @arg " - + sqlTypeExpression + " AS SELECT @arg"); + stmt.executeUpdate( + "CREATE PROCEDURE " + escapedProcName + " @arg " + sqlTypeExpression + " AS SELECT @arg"); - try (PreparedStatement pstmt = conn - .prepareStatement("{call " + AbstractSQLGenerator.escapeIdentifier(procName) + "(?)}")) { + try (PreparedStatement pstmt = conn.prepareStatement("{call " + escapedProcName + "(?)}")) { ParameterMetaData metadata = pstmt.getParameterMetaData(); assertEquals(metadata.getParameterType(1), sqlType.jdbcType, @@ -148,7 +161,7 @@ void verifyParameterMetaData(Connection conn) throws Exception { } } finally { try (Statement stmt = conn.createStatement()) { - stmt.executeUpdate("DROP PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName)); + TestUtils.dropProcedureIfExists(AbstractSQLGenerator.escapeIdentifier(escapedProcName), stmt); } } } @@ -171,57 +184,43 @@ void verifyRSUpdaters(Connection conn) throws Exception { try (Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)) { - stmt.executeUpdate("DROP TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName)); + TestUtils.dropTableIfExists(escapedTableName, stmt); - stmt.executeUpdate("CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName) + " (col1 " - + sqlTypeExpression + ", col2 int identity(1,1) primary key)"); + stmt.executeUpdate("CREATE TABLE " + escapedTableName + " (col1 " + sqlTypeExpression + + ", col2 int identity(1,1) primary key)"); - stmt.executeUpdate("INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableName) + " VALUES (" - + sqlCastExpression() + ")"); + stmt.executeUpdate("INSERT INTO " + escapedTableName + " VALUES (" + sqlCastExpression() + ")"); - try (ResultSet rs = stmt - .executeQuery("SELECT * FROM " + AbstractSQLGenerator.escapeIdentifier(tableName))) { + try (ResultSet rs = stmt.executeQuery("SELECT * FROM " + escapedTableName)) { rs.next(); verifyRSUpdaters(rs); } finally { - stmt.executeUpdate("DROP TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName)); + TestUtils.dropTableIfExists(escapedTableName, stmt); } } } - /* - * For testing the setObject and setNull methods in PreparedStatement, use the verifySetter* methods. These - * methods prepare a single statement and execute it for all different data types by calling the appropriate - * 'setObject' methods for each data type and/or type conversion. - */ - abstract void verifySetters(PreparedStatement ps) throws Exception; - - abstract void verifySettersUtilDate(PreparedStatement ps) throws Exception; - - abstract void verifySettersCalendar(PreparedStatement ps) throws Exception; - void verifySetters(Connection conn) throws Exception { assumeTrue(!TestUtils.isSqlAzureDW(conn), TestResource.getResource("R_skipAzure")); try (Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)) { - stmt.executeUpdate("DROP TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName)); - stmt.executeUpdate("CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName) + " (col1 " - + sqlTypeExpression + ", col2 int identity(1,1) primary key)"); + TestUtils.dropTableIfExists(escapedTableName, stmt); + stmt.executeUpdate("CREATE TABLE " + escapedTableName + " (col1 " + sqlTypeExpression + + ", col2 int identity(1,1) primary key)"); try (PreparedStatement ps = conn.prepareStatement( - "INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableName) + " VALUES (?) SELECT * FROM " - + AbstractSQLGenerator.escapeIdentifier(tableName), + "INSERT INTO " + escapedTableName + " VALUES (?) SELECT * FROM " + escapedTableName, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY)) { verifySetters(ps); // Verify setObject function for the new mapping in JDBC41 (java.util.Date to TIMESTAMP) - stmt.executeUpdate("TRUNCATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName)); + stmt.executeUpdate("TRUNCATE TABLE " + escapedTableName); verifySettersUtilDate(ps); // Verify setObject function for the new mapping in JDBC41 (java.util.Calendar to TIMESTAMP) - stmt.executeUpdate("TRUNCATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName)); + stmt.executeUpdate("TRUNCATE TABLE " + escapedTableName); verifySettersCalendar(ps); } finally { - stmt.executeUpdate("DROP TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName)); + TestUtils.dropTableIfExists(escapedTableName, stmt); } } } @@ -230,16 +229,14 @@ void verifySetters(Connection conn) throws Exception { void verifyCSGetters(Connection conn) throws Exception { try (Statement stmt = conn.createStatement()) { - stmt.executeUpdate("DROP PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName)); - stmt.executeUpdate("CREATE PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName) + " @argIn " - + sqlTypeExpression + "," + " @argOut " + sqlTypeExpression + " OUTPUT" + " AS " - + " SET @argOut=@argIn"); + TestUtils.dropProcedureIfExists(AbstractSQLGenerator.escapeIdentifier(escapedProcName), stmt); + stmt.executeUpdate("CREATE PROCEDURE " + escapedProcName + " @argIn " + sqlTypeExpression + "," + + " @argOut " + sqlTypeExpression + " OUTPUT" + " AS " + " SET @argOut=@argIn"); - try (CallableStatement cs = conn - .prepareCall("{call " + AbstractSQLGenerator.escapeIdentifier(procName) + "(?,?)}")) { + try (CallableStatement cs = conn.prepareCall("{call " + escapedProcName + "(?,?)}")) { verifyCSGetters(cs); } finally { - stmt.executeUpdate("DROP PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName)); + TestUtils.dropProcedureIfExists(AbstractSQLGenerator.escapeIdentifier(escapedProcName), stmt); } } } @@ -1135,14 +1132,11 @@ public void testParameterMetaData() throws Exception { public void testSendTimestampAsTimeAsDatetime() throws Exception { try (Connection conn = DriverManager.getConnection(connectionString + ";sendTimeAsDatetime=true")) { try (Statement stmt = conn.createStatement()) { + TestUtils.dropProcedureIfExists(AbstractSQLGenerator.escapeIdentifier(escapedProcName), stmt); + stmt.executeUpdate("CREATE PROCEDURE " + escapedProcName + " @argIn time(7), " + + " @argOut time(7) OUTPUT " + " AS " + " SET @argOut=@argIn"); - stmt.executeUpdate("DROP PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName)); - - stmt.executeUpdate("CREATE PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName) - + " @argIn time(7), " + " @argOut time(7) OUTPUT " + " AS " + " SET @argOut=@argIn"); - - try (CallableStatement cs = conn - .prepareCall("{call " + AbstractSQLGenerator.escapeIdentifier(procName) + "(?,?)}")) { + try (CallableStatement cs = conn.prepareCall("{call " + escapedProcName + "(?,?)}")) { // Set up a timestamp with a time component that is the last millisecond of the day... Timestamp ts = Timestamp.valueOf("2010-02-15 23:59:59.999"); @@ -1163,7 +1157,7 @@ public void testSendTimestampAsTimeAsDatetime() throws Exception { } } finally { try (Statement stmt = conn.createStatement()) { - stmt.executeUpdate("DROP PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName)); + TestUtils.dropProcedureIfExists(AbstractSQLGenerator.escapeIdentifier(escapedProcName), stmt); } } } @@ -1180,11 +1174,9 @@ public void testDoubleRounding() throws Exception { String sql; try (Statement stmt = conn.createStatement()) { // SQL Azure requires each table to have a clustered index, so change col1 to the primary key - sql = "CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName) - + " (col1 int primary key, col2 datetimeoffset(6))"; + sql = "CREATE TABLE " + escapedTableName + " (col1 int primary key, col2 datetimeoffset(6))"; stmt.executeUpdate(sql); - sql = "INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableName) - + " VALUES(1, '2010-04-29 10:51:12.123456 +00:00')"; + sql = "INSERT INTO " + escapedTableName + " VALUES(1, '2010-04-29 10:51:12.123456 +00:00')"; stmt.executeUpdate(sql); } @@ -1195,7 +1187,7 @@ public void testDoubleRounding() throws Exception { DateTimeOffset actualDto; // create select query and update the datetimeoffset - String query = "SELECT * FROM " + AbstractSQLGenerator.escapeIdentifier(tableName); + String query = "SELECT * FROM " + escapedTableName; try (ResultSet rs = stmt.executeQuery(query)) { rs.next(); @@ -1210,7 +1202,7 @@ public void testDoubleRounding() throws Exception { } // get the datetime offset from server - sql = "SELECT col2 FROM " + AbstractSQLGenerator.escapeIdentifier(tableName); + sql = "SELECT col2 FROM " + escapedTableName; try (ResultSet rs = stmt.executeQuery(sql)) { rs.next(); Object value = rs.getObject(1); @@ -1229,7 +1221,7 @@ public void testDoubleRounding() throws Exception { } } finally { try (Statement stmt = conn.createStatement()) { - stmt.executeUpdate("drop table " + AbstractSQLGenerator.escapeIdentifier(tableName)); + TestUtils.dropTableIfExists(escapedTableName, stmt); } catch (SQLException e) { fail(TestResource.getResource("R_createDropTableFailed") + e.toString()); } @@ -1569,20 +1561,17 @@ public void testUpdateMisc() throws Exception { assumeTrue(!TestUtils.isSqlAzureDW(conn), TestResource.getResource("R_skipAzure")); try (Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)) { - try { - stmt.executeUpdate("DROP TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName)); - } catch (SQLException e) {} - - stmt.executeUpdate("CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName) - + " (col1 datetimeoffset(2)," + " col2 datetime," + " col3 time(5)," + " col4 datetime2(5)," - + " col5 smalldatetime," + " col6 time(2)," + " col7 int identity(1,1) primary key)"); - stmt.executeUpdate("INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableName) + " VALUES (" - + " '2010-01-12 09:00:23.17 -08:00'," + " '2010-01-12 10:20:23'," + " '10:20:23'," - + " '2010-01-12 10:20:23'," + " '2010-01-12 11:45:17'," + " '10:20:23')"); + TestUtils.dropTableIfExists(escapedTableName, stmt); + + stmt.executeUpdate("CREATE TABLE " + escapedTableName + " (col1 datetimeoffset(2)," + " col2 datetime," + + " col3 time(5)," + " col4 datetime2(5)," + " col5 smalldatetime," + " col6 time(2)," + + " col7 int identity(1,1) primary key)"); + stmt.executeUpdate("INSERT INTO " + escapedTableName + " VALUES (" + " '2010-01-12 09:00:23.17 -08:00'," + + " '2010-01-12 10:20:23'," + " '10:20:23'," + " '2010-01-12 10:20:23'," + + " '2010-01-12 11:45:17'," + " '10:20:23')"); DateTimeOffset dto; - try (ResultSet rs = stmt.executeQuery( - "SELECT *, CAST(col1 AS VARCHAR) FROM " + AbstractSQLGenerator.escapeIdentifier(tableName))) { + try (ResultSet rs = stmt.executeQuery("SELECT *, CAST(col1 AS VARCHAR) FROM " + escapedTableName)) { rs.next(); // Update datetimeoffset(2) from pre-Gregorian Date @@ -1676,7 +1665,7 @@ public void testUpdateMisc() throws Exception { "Update datetime from Time near next day"); } finally { - stmt.executeUpdate("DROP TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName)); + TestUtils.dropTableIfExists(escapedTableName, stmt); } } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SparseTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SparseTest.java index c06dd667b3..40f4573d1c 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SparseTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SparseTest.java @@ -1,6 +1,6 @@ package com.microsoft.sqlserver.jdbc.datatypes; -import java.sql.*; +import java.sql.*; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; @@ -14,10 +14,11 @@ import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; import com.microsoft.sqlserver.testframework.AbstractTest; + @RunWith(JUnitPlatform.class) -public class SparseTest extends AbstractTest -{ +public class SparseTest extends AbstractTest { final static String tableName = RandomUtil.getIdentifier("SparseTestTable"); + final static String escapedTableName = AbstractSQLGenerator.escapeIdentifier(tableName); @Test public void testSparse() throws Exception { @@ -26,13 +27,10 @@ public void testSparse() throws Exception { try (Statement stmt = conn.createStatement()) { // Create the test table - try { - stmt.executeUpdate("drop table " + AbstractSQLGenerator.escapeIdentifier(tableName)); - } catch (Exception e) {} + TestUtils.dropTableIfExists(escapedTableName, stmt); StringBuilder bd = new StringBuilder(); - bd.append("create table " + AbstractSQLGenerator.escapeIdentifier(tableName) - + " (col1 int, col2 varbinary(max)"); + bd.append("create table " + escapedTableName + " (col1 int, col2 varbinary(max)"); for (int i = 3; i <= 1024; i++) { bd.append(", col" + i + " varchar(20) SPARSE NULL"); } @@ -41,17 +39,15 @@ public void testSparse() throws Exception { stmt.executeUpdate(query); - stmt.executeUpdate("insert into " + AbstractSQLGenerator.escapeIdentifier(tableName) - + " (col1, col2, col1023)values(1, 0x45, 'yo')"); + stmt.executeUpdate("insert into " + escapedTableName + " (col1, col2, col1023)values(1, 0x45, 'yo')"); - try (ResultSet rs = stmt - .executeQuery("Select * from " + AbstractSQLGenerator.escapeIdentifier(tableName))) { + try (ResultSet rs = stmt.executeQuery("Select * from " + escapedTableName)) { rs.next(); assertEquals(rs.getString("col1023"), "yo", "Wrong value returned"); } } finally { try (Statement stmt = conn.createStatement()) { - stmt.executeUpdate("drop table " + AbstractSQLGenerator.escapeIdentifier(tableName)); + TestUtils.dropTableIfExists(escapedTableName, stmt); } catch (Exception e) { fail(TestResource.getResource("R_createDropTableFailed") + e.toString()); } From 2994200b1f2e7c2233b0dfe76eb3cfa70d5a6a48 Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Tue, 27 Nov 2018 13:06:09 -0800 Subject: [PATCH 18/51] more cleanup --- .../bulkCopy/BulkCopyColumnMappingTest.java | 59 +++++------------ .../callablestatement/CallableMixedTest.java | 65 ++++++++++--------- .../jdbc/datatypes/KatmaiDataTypesTest.java | 22 +++---- 3 files changed, 60 insertions(+), 86 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyColumnMappingTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyColumnMappingTest.java index 0da47193c0..9452d5dd69 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyColumnMappingTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyColumnMappingTest.java @@ -9,6 +9,7 @@ import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; +import java.sql.Statement; import java.text.MessageFormat; import java.util.concurrent.ThreadLocalRandom; @@ -21,6 +22,7 @@ import com.microsoft.sqlserver.jdbc.ComparisonUtil; import com.microsoft.sqlserver.jdbc.TestResource; +import com.microsoft.sqlserver.jdbc.TestUtils; import com.microsoft.sqlserver.testframework.DBConnection; import com.microsoft.sqlserver.testframework.DBResultSet; import com.microsoft.sqlserver.testframework.DBStatement; @@ -55,7 +57,7 @@ public static void closeConnection() throws SQLException { @Test @DisplayName("BulkCopy:test no explicit column mapping") - public void testNoExplicitCM() { + public void testNoExplicitCM() throws SQLException { DBTable destTable = null; try { // create dest table @@ -67,16 +69,13 @@ public void testNoExplicitCM() { bulkWrapper.setUsingConnection((0 == ThreadLocalRandom.current().nextInt(2)) ? true : false); BulkCopyTestUtil.performBulkCopy(bulkWrapper, sourceTable, destTable); } finally { - if (null != destTable) { - // drop dest table - stmt.dropTable(destTable); - } + TestUtils.dropTableIfExists(destTable.getEscapedTableName(), (Statement) stmt.product()); } } @Test @DisplayName("BulkCopy:test explicit column mapping") - public void testExplicitCM() { + public void testExplicitCM() throws SQLException { DBTable destTable = null; try { // create dest table @@ -109,16 +108,13 @@ public void testExplicitCM() { } BulkCopyTestUtil.performBulkCopy(bulkWrapper, sourceTable, destTable); } finally { - // drop dest table - if (null != destTable) { - stmt.dropTable(destTable); - } + TestUtils.dropTableIfExists(destTable.getEscapedTableName(), (Statement) stmt.product()); } } @Test @DisplayName("BulkCopy:test unicode column mapping") - public void testUnicodeCM() { + public void testUnicodeCM() throws SQLException { DBTable sourceTableUnicode = null; DBTable destTableUnicode = null; try { @@ -157,18 +153,14 @@ public void testUnicodeCM() { } BulkCopyTestUtil.performBulkCopy(bulkWrapper, sourceTableUnicode, destTableUnicode); } finally { - if (null != sourceTableUnicode) { - dropTable(sourceTableUnicode.getEscapedTableName()); - } - if (null != destTableUnicode) { - dropTable(destTableUnicode.getEscapedTableName()); - } + TestUtils.dropTableIfExists(sourceTableUnicode.getEscapedTableName(), (Statement) stmt.product()); + TestUtils.dropTableIfExists(destTableUnicode.getEscapedTableName(), (Statement) stmt.product()); } } @Test @DisplayName("BulkCopy:test repetitive column mapping") - public void testRepetitiveCM() { + public void testRepetitiveCM() throws SQLException { DBTable sourceTable1 = null; DBTable destTable = null; try { @@ -224,18 +216,14 @@ public void testRepetitiveCM() { fail(form.format(msgArgs) + "\n" + destTable.getTableName() + "\n" + e.getMessage()); } } finally { - if (null != sourceTable1) { - dropTable(sourceTable1.getEscapedTableName()); - } - if (null != destTable) { - dropTable(destTable.getEscapedTableName()); - } + TestUtils.dropTableIfExists(sourceTable1.getEscapedTableName(), (Statement) stmt.product()); + TestUtils.dropTableIfExists(destTable.getEscapedTableName(), (Statement) stmt.product()); } } @Test @DisplayName("BulkCopy:test implicit mismatched column mapping") - public void testImplicitMismatchCM() { + public void testImplicitMismatchCM() throws SQLException { DBTable destTable = null; try { // create non unicode dest table with different schema from source table @@ -268,15 +256,13 @@ public void testImplicitMismatchCM() { } BulkCopyTestUtil.performBulkCopy(bulkWrapper, sourceTable, destTable, true, true); } finally { - if (null != destTable) { - stmt.dropTable(destTable); - } + TestUtils.dropTableIfExists(destTable.getEscapedTableName(), (Statement) stmt.product()); } } @Test @DisplayName("BulkCopy:test invalid column mapping") - public void testInvalidCM() { + public void testInvalidCM() throws SQLException { DBTable destTable = null; try { // create dest table @@ -359,9 +345,7 @@ public void testInvalidCM() { bulkWrapper.setColumnMapping(Integer.MIN_VALUE, Integer.MAX_VALUE); BulkCopyTestUtil.performBulkCopy(bulkWrapper, sourceTable, destTable, true, true); } finally { - if (null != destTable) { - stmt.dropTable(destTable); - } + TestUtils.dropTableIfExists(destTable.getEscapedTableName(), (Statement) stmt.product()); } } @@ -405,15 +389,4 @@ private void validateValuesRepetitiveCM(DBConnection con, DBTable sourceTable, } } } - - private void dropTable(String tableName) { - - String dropSQL = "DROP TABLE [dbo]." + tableName; - try { - stmt.execute(dropSQL); - } catch (SQLException e) { - fail(tableName + " " + TestResource.getResource("R_tableNotDropped") + "\n" + e.getMessage()); - } - } - } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java index 933fba4136..a8dbbeaefa 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java @@ -21,6 +21,7 @@ public void datatypestest() throws Exception { String tableName = RandomUtil.getIdentifier("TFOO3"); String escapedTableName = AbstractSQLGenerator.escapeIdentifier(tableName); String procName = RandomUtil.getIdentifier("SPFOO3"); + String escapedProcName = AbstractSQLGenerator.escapeIdentifier(procName); try (Connection conn = DriverManager.getConnection(connectionString)) { try (Statement stmt = conn.createStatement()) { @@ -31,63 +32,63 @@ public void datatypestest() throws Exception { stmt.executeUpdate("Insert into " + escapedTableName + " values(0, 1)"); - stmt.executeUpdate("CREATE PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName) + stmt.executeUpdate("CREATE PROCEDURE " + escapedProcName + " (@p2_int int, @p2_int_out int OUTPUT, @p4_smallint smallint, @p4_smallint_out smallint OUTPUT) AS begin transaction SELECT * FROM " + escapedTableName + " ; SELECT @p2_int_out=@p2_int, @p4_smallint_out=@p4_smallint commit transaction RETURN -2147483648"); } - try (CallableStatement cstmt = conn.prepareCall( - "{ ? = CALL " + AbstractSQLGenerator.escapeIdentifier(procName) + " (?, ?, ?, ?) }")) { + try (CallableStatement cstmt = conn.prepareCall("{ ? = CALL " + escapedProcName + " (?, ?, ?, ?) }")) { cstmt.registerOutParameter((int) 1, (int) 4); cstmt.setObject((int) 2, Integer.valueOf("31"), (int) 4); cstmt.registerOutParameter((int) 3, (int) 4); - cstmt.registerOutParameter((int) 5, java.sql.Types.BINARY); // Test OUT param - // re-registration - // (Defect 60921) + + // Test OUT param re-registration + cstmt.registerOutParameter((int) 5, java.sql.Types.BINARY); + cstmt.registerOutParameter((int) 5, (int) 5); cstmt.setObject((int) 4, Short.valueOf("-5372"), (int) 5); // get results and a value - ResultSet rs = cstmt.executeQuery(); - rs.next(); + try (ResultSet rs = cstmt.executeQuery()) { + rs.next(); - if (rs.getInt(1) != 0) { - fail("Received data not equal to setdata"); - } + if (rs.getInt(1) != 0) { + fail("Received data not equal to setdata"); + } - if (cstmt.getInt((int) 5) != -5372) { - fail("Received data not equal to setdata"); + if (cstmt.getInt((int) 5) != -5372) { + fail("Received data not equal to setdata"); + } } // do nothing and reexecute - rs = cstmt.executeQuery(); - // get the param without getting the resultset - rs = cstmt.executeQuery(); - if (cstmt.getInt((int) 1) != -2147483648) { - fail("Received data not equal to setdata"); - } + try (ResultSet rs = cstmt.executeQuery()) {} - if (cstmt.getInt((int) 1) != -2147483648) { - fail("Received data not equal to setdata"); + // get the param without getting the resultset + try (ResultSet rs = cstmt.executeQuery()) { + if (cstmt.getInt((int) 1) != -2147483648) { + fail("Received data not equal to setdata"); + } } - rs = cstmt.executeQuery(); - rs.next(); + try (ResultSet rs = cstmt.executeQuery()) { + rs.next(); - if (rs.getInt(1) != 0) { - fail("Received data not equal to setdata"); - } + if (rs.getInt(1) != 0) { + fail("Received data not equal to setdata"); + } - if (cstmt.getInt((int) 1) != -2147483648) { - fail("Received data not equal to setdata"); - } + if (cstmt.getInt((int) 1) != -2147483648) { + fail("Received data not equal to setdata"); + } - if (cstmt.getInt((int) 5) != -5372) { - fail("Received data not equal to setdata"); + if (cstmt.getInt((int) 5) != -5372) { + fail("Received data not equal to setdata"); + } } - rs = cstmt.executeQuery(); + try (ResultSet rs = cstmt.executeQuery()) {} } } finally { try (Connection conn = DriverManager.getConnection(connectionString); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java index fee805fd4c..17002976f3 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java @@ -102,6 +102,12 @@ final String getString() { abstract void verifySettersCalendar(PreparedStatement ps) throws Exception; + abstract void verifyRSGetters(ResultSet rs) throws Exception; + + abstract void verifyRSUpdaters(ResultSet rs) throws Exception; + + abstract void verifyCSGetters(CallableStatement cs) throws Exception; + private String sqlCastExpression() { return "CAST('" + stringValue + "' AS " + sqlTypeExpression + ")"; } @@ -161,13 +167,11 @@ void verifyParameterMetaData(Connection conn) throws Exception { } } finally { try (Statement stmt = conn.createStatement()) { - TestUtils.dropProcedureIfExists(AbstractSQLGenerator.escapeIdentifier(escapedProcName), stmt); + TestUtils.dropProcedureIfExists(escapedProcName, stmt); } } } - abstract void verifyRSGetters(ResultSet rs) throws Exception; - void verifyRSGetters(Connection conn) throws Exception { try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery( "SELECT " + sqlCastExpression() + ", CAST(" + sqlCastExpression() + " AS VARCHAR(60))")) { @@ -176,8 +180,6 @@ void verifyRSGetters(Connection conn) throws Exception { } } - abstract void verifyRSUpdaters(ResultSet rs) throws Exception; - void verifyRSUpdaters(Connection conn) throws Exception { assumeTrue(!TestUtils.isSqlAzureDW(conn), TestResource.getResource("R_skipAzure")); @@ -225,18 +227,16 @@ void verifySetters(Connection conn) throws Exception { } } - abstract void verifyCSGetters(CallableStatement cs) throws Exception; - void verifyCSGetters(Connection conn) throws Exception { try (Statement stmt = conn.createStatement()) { - TestUtils.dropProcedureIfExists(AbstractSQLGenerator.escapeIdentifier(escapedProcName), stmt); + TestUtils.dropProcedureIfExists(escapedProcName, stmt); stmt.executeUpdate("CREATE PROCEDURE " + escapedProcName + " @argIn " + sqlTypeExpression + "," + " @argOut " + sqlTypeExpression + " OUTPUT" + " AS " + " SET @argOut=@argIn"); try (CallableStatement cs = conn.prepareCall("{call " + escapedProcName + "(?,?)}")) { verifyCSGetters(cs); } finally { - TestUtils.dropProcedureIfExists(AbstractSQLGenerator.escapeIdentifier(escapedProcName), stmt); + TestUtils.dropProcedureIfExists(escapedProcName, stmt); } } } @@ -1132,7 +1132,7 @@ public void testParameterMetaData() throws Exception { public void testSendTimestampAsTimeAsDatetime() throws Exception { try (Connection conn = DriverManager.getConnection(connectionString + ";sendTimeAsDatetime=true")) { try (Statement stmt = conn.createStatement()) { - TestUtils.dropProcedureIfExists(AbstractSQLGenerator.escapeIdentifier(escapedProcName), stmt); + TestUtils.dropProcedureIfExists(escapedProcName, stmt); stmt.executeUpdate("CREATE PROCEDURE " + escapedProcName + " @argIn time(7), " + " @argOut time(7) OUTPUT " + " AS " + " SET @argOut=@argIn"); @@ -1157,7 +1157,7 @@ public void testSendTimestampAsTimeAsDatetime() throws Exception { } } finally { try (Statement stmt = conn.createStatement()) { - TestUtils.dropProcedureIfExists(AbstractSQLGenerator.escapeIdentifier(escapedProcName), stmt); + TestUtils.dropProcedureIfExists(escapedProcName, stmt); } } } From e3512f92dae320244044127750e73954781417a8 Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Wed, 28 Nov 2018 13:07:25 -0800 Subject: [PATCH 19/51] updated isSqlAzure --- .../java/com/microsoft/sqlserver/jdbc/TestUtils.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/TestUtils.java b/src/test/java/com/microsoft/sqlserver/jdbc/TestUtils.java index bc3ee69d79..1cfd221685 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/TestUtils.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/TestUtils.java @@ -70,9 +70,10 @@ public class TestUtils { private final static int ENGINE_EDITION_FOR_SQL_AZURE = 5; private final static int ENGINE_EDITION_FOR_SQL_AZURE_DW = 6; - // whether we determined if the target server is SQL Azure + // whether we determined if the target server is SQL Azure or DW private static boolean _determinedSqlAzureOrSqlServer = false; private static boolean _isSqlAzure = false; + private static boolean _isSqlAzureDW = false; /** * Returns serverType @@ -689,6 +690,8 @@ public static boolean isSqlAzure(Connection con) throws SQLException { try (ResultSet rs = con.createStatement().executeQuery("SELECT CAST(SERVERPROPERTY('EngineEdition') as INT)")) { rs.next(); int engineEdition = rs.getInt(1); + _determinedSqlAzureOrSqlServer = true; + _isSqlAzure = true; return (engineEdition == ENGINE_EDITION_FOR_SQL_AZURE || engineEdition == ENGINE_EDITION_FOR_SQL_AZURE_DW); } } @@ -701,9 +704,15 @@ public static boolean isSqlAzure(Connection con) throws SQLException { * @throws SQLException */ public static boolean isSqlAzureDW(Connection con) throws SQLException { + if (_determinedSqlAzureOrSqlServer) { + return _isSqlAzureDW; + } + try (ResultSet rs = con.createStatement().executeQuery("SELECT CAST(SERVERPROPERTY('EngineEdition') as INT)")) { rs.next(); int engineEdition = rs.getInt(1); + _determinedSqlAzureOrSqlServer = true; + _isSqlAzureDW = true; return (engineEdition == ENGINE_EDITION_FOR_SQL_AZURE_DW); } } From 7452606b7d97d95ee0c02273ddb962ba71d740e9 Mon Sep 17 00:00:00 2001 From: ulvii Date: Fri, 30 Nov 2018 17:28:56 -0800 Subject: [PATCH 20/51] Pull master history to dev branch (#897) From 02467ebca945d68f902cbf62fc0f32123e21fda3 Mon Sep 17 00:00:00 2001 From: ulvii Date: Mon, 3 Dec 2018 12:27:15 -0800 Subject: [PATCH 21/51] Release | Change the development version to 7.1.4-SNAPSHOT --- build.gradle | 2 +- pom.xml | 2 +- src/main/java/com/microsoft/sqlserver/jdbc/SQLJdbcVersion.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index d084185a8c..4f099b60d1 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ apply plugin: 'java' -version = '7.1.3' +version = '7.1.4-SNAPSHOT' def jreVersion = "" def testOutputDir = file("build/classes/java/test") def archivesBaseName = 'mssql-jdbc' diff --git a/pom.xml b/pom.xml index 7b8e944adc..41792d7779 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.microsoft.sqlserver mssql-jdbc - 7.1.3 + 7.1.4-SNAPSHOT jar Microsoft JDBC Driver for SQL Server diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLJdbcVersion.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLJdbcVersion.java index 05c10cbe1a..49eed7bdfd 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLJdbcVersion.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLJdbcVersion.java @@ -8,6 +8,6 @@ final class SQLJdbcVersion { static final int major = 7; static final int minor = 1; - static final int patch = 3; + static final int patch = 4; static final int build = 0; } From 5217270f6542a91a707143176e534688b09dafac Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Wed, 12 Dec 2018 23:11:42 -0800 Subject: [PATCH 22/51] review updates --- .../callablestatement/CallableMixedTest.java | 19 ++++++++++----- .../sqlserver/jdbc/datatypes/SparseTest.java | 15 ++++++++---- .../jdbc/resultset/ResultSetTest.java | 8 +++++-- .../jdbc/unit/serial/DTOSerialTest.java | 23 +++++++++++++------ 4 files changed, 46 insertions(+), 19 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java index a8dbbeaefa..f7e857545c 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java @@ -1,16 +1,23 @@ package com.microsoft.sqlserver.jdbc.callablestatement; -import java.sql.*; -import com.microsoft.sqlserver.jdbc.RandomUtil; -import com.microsoft.sqlserver.jdbc.TestUtils; -import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; -import com.microsoft.sqlserver.testframework.AbstractTest; +import static org.junit.jupiter.api.Assertions.fail; + +import java.sql.CallableStatement; +import java.sql.Connection; +import java.sql.DriverManager; +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 static org.junit.jupiter.api.Assertions.fail; + +import com.microsoft.sqlserver.jdbc.RandomUtil; +import com.microsoft.sqlserver.jdbc.TestUtils; +import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; +import com.microsoft.sqlserver.testframework.AbstractTest; @RunWith(JUnitPlatform.class) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SparseTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SparseTest.java index 40f4573d1c..e63901d144 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SparseTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SparseTest.java @@ -1,12 +1,17 @@ package com.microsoft.sqlserver.jdbc.datatypes; -import java.sql.*; +import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assumptions.assumeTrue; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.Statement; + import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assumptions.assumeTrue; -import static org.junit.Assert.fail; import com.microsoft.sqlserver.jdbc.RandomUtil; import com.microsoft.sqlserver.jdbc.TestResource; @@ -44,6 +49,8 @@ public void testSparse() throws Exception { try (ResultSet rs = stmt.executeQuery("Select * from " + escapedTableName)) { rs.next(); assertEquals(rs.getString("col1023"), "yo", "Wrong value returned"); + assertEquals(rs.getInt("col1"), 1, "Wrong value returned"); + assertEquals(rs.getBytes("col2")[0], 0x45, "Wrong value returned"); } } finally { try (Statement stmt = conn.createStatement()) { diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java index 2e8764e9f3..e163cba633 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java @@ -330,8 +330,13 @@ public void testGetterOnNull() throws SQLException { } } + /** + * Tests getters and setters for holdability. + * + * @throws SQLException + */ @Test - public void testHoldability() throws SQLException { + public void testGetSetHoldability() throws SQLException { int[] holdabilityOptions = {ResultSet.HOLD_CURSORS_OVER_COMMIT, ResultSet.CLOSE_CURSORS_AT_COMMIT}; try (Connection con = DriverManager.getConnection(connectionString); Statement stmt = con.createStatement(); @@ -342,7 +347,6 @@ public void testHoldability() throws SQLException { assertEquals(rs.getHoldability(), connHold); for (int i = 0; i < holdabilityOptions.length; i++) { - if ((connHold = con.getHoldability()) != holdabilityOptions[i]) { con.setHoldability(holdabilityOptions[i]); assertEquals(con.getHoldability(), holdabilityOptions[i]); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java index 0b4126e99f..f448025a3c 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java @@ -1,19 +1,28 @@ package com.microsoft.sqlserver.jdbc.unit.serial; -import java.sql.*; + +import static org.junit.jupiter.api.Assertions.fail; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +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.*; +import com.microsoft.sqlserver.jdbc.SQLServerException; +import com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement; +import com.microsoft.sqlserver.jdbc.SQLServerResultSet; +import com.microsoft.sqlserver.jdbc.TestUtils; import com.microsoft.sqlserver.testframework.AbstractTest; -import microsoft.sql.*; - -import java.io.*; - -import static org.junit.jupiter.api.Assertions.fail; +import microsoft.sql.DateTimeOffset; @RunWith(JUnitPlatform.class) From 79ac177cefeda917c477df735a658f4a1cc50bb9 Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Wed, 12 Dec 2018 23:51:15 -0800 Subject: [PATCH 23/51] more review updates --- .../sqlserver/jdbc/unit/serial/DTOSerialTest.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java index f448025a3c..24b99cba4a 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java @@ -1,6 +1,5 @@ package com.microsoft.sqlserver.jdbc.unit.serial; - import static org.junit.jupiter.api.Assertions.fail; import java.io.ByteArrayInputStream; @@ -11,6 +10,8 @@ import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.Statement; +import java.text.SimpleDateFormat; +import java.util.Date; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; @@ -27,11 +28,12 @@ @RunWith(JUnitPlatform.class) public class DTOSerialTest extends AbstractTest { - private static final String dateString = "2007-05-08 12:35:29.1234567 +12:15"; + private static final String dateString = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSSSS XXX").format(new Date()); // public static void testDSerial(String connString) throws Exception @Test public void testDSerial() throws Exception { + System.out.println("datString=" + dateString); try (Connection conn = DriverManager.getConnection(connectionString); Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)) { @@ -137,7 +139,7 @@ private static void verifyMessedSerialization() throws Exception { } if (!exThrown) { - fail("wrongnanos serialized form succeeded."); + fail("serialized form with wrong nano values unexpectedly succeeded"); } exThrown = false; @@ -148,7 +150,7 @@ private static void verifyMessedSerialization() throws Exception { } if (!exThrown) { - fail("wrongnanos serialized form succeeded."); + fail("serialized form with wrong offset unexpectedly succeeded"); } } From ada91ae8bf298285a56f5cc8d042005925224009 Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Thu, 13 Dec 2018 00:04:53 -0800 Subject: [PATCH 24/51] fixed imports --- .../jdbc/datatypes/KatmaiDataTypesTest.java | 44 ++++++++++++++----- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java index 17002976f3..2d4dd173e8 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java @@ -1,22 +1,44 @@ package com.microsoft.sqlserver.jdbc.datatypes; -import java.sql.*; -import java.text.MessageFormat; - -import com.microsoft.sqlserver.jdbc.*; -import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; -import com.microsoft.sqlserver.testframework.AbstractTest; +import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assumptions.assumeTrue; -import java.math.*; -import java.util.*; +import java.math.BigDecimal; +import java.sql.CallableStatement; +import java.sql.Connection; import java.sql.Date; +import java.sql.DriverManager; +import java.sql.ParameterMetaData; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Time; +import java.sql.Timestamp; +import java.text.MessageFormat; +import java.util.Calendar; +import java.util.EnumSet; +import java.util.GregorianCalendar; +import java.util.Locale; +import java.util.SimpleTimeZone; +import java.util.TimeZone; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assumptions.assumeTrue; -import static org.junit.Assert.fail; + +import com.microsoft.sqlserver.jdbc.RandomUtil; +import com.microsoft.sqlserver.jdbc.SQLServerCallableStatement; +import com.microsoft.sqlserver.jdbc.SQLServerConnection; +import com.microsoft.sqlserver.jdbc.SQLServerException; +import com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement; +import com.microsoft.sqlserver.jdbc.SQLServerResultSet; +import com.microsoft.sqlserver.jdbc.TestResource; +import com.microsoft.sqlserver.jdbc.TestUtils; +import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; +import com.microsoft.sqlserver.testframework.AbstractTest; import microsoft.sql.DateTimeOffset; From 36838a8c7c2111ef02221bb8d6783e8e12a3c20a Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Thu, 13 Dec 2018 11:54:26 -0800 Subject: [PATCH 25/51] updated failed error strings with more detail --- .../jdbc/unit/serial/DTOSerialTest.java | 53 ++++++++++--------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java index 24b99cba4a..889b119b68 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java @@ -30,10 +30,8 @@ public class DTOSerialTest extends AbstractTest { private static final String dateString = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSSSS XXX").format(new Date()); - // public static void testDSerial(String connString) throws Exception @Test public void testDSerial() throws Exception { - System.out.println("datString=" + dateString); try (Connection conn = DriverManager.getConnection(connectionString); Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)) { @@ -46,9 +44,8 @@ public void testDSerial() throws Exception { } } + @Test public void testESerial() throws Exception { - String connectionString = TestUtils.getConfiguredProperty("mssql_jdbc_test_connection_properties"); - try (Connection conn = DriverManager.getConnection(connectionString); Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)) { @@ -57,29 +54,31 @@ public void testESerial() throws Exception { try { stmt.executeUpdate("RAISERROR ('foo', 13,1) WITH LOG"); + } catch (SQLServerException x) { currException = x; } + // store the info - String errInfo = currException.toString(); - String sqlState = currException.getSQLState(); - int errCode = currException.getErrorCode(); ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(bos); + // serialize the exception; out.writeObject(currException); ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray())); SQLServerException ex = (SQLServerException) in.readObject(); - String newErrInfo = ex.toString(); - if (!errInfo.equals(newErrInfo)) { - fail("Errors are different."); + if (!currException.toString().equals(ex.toString())) { + fail("Error strings are different. Expected: " + currException.toString() + " Received: " + + ex.toString()); } - if (sqlState != ex.getSQLState()) { - fail("Sql states are different."); + if (!currException.getSQLState().equals(ex.getSQLState())) { + fail("Sql states are different. Expected: " + currException.getSQLState() + " Received: " + + ex.getSQLState()); } - if (errCode != ex.getErrorCode()) { - fail("Error codes are different."); + if (currException.getErrorCode() != ex.getErrorCode()) { + fail("Error codes are different. Expected: " + currException.getErrorCode() + " Received: " + + ex.getErrorCode()); } } } @@ -161,27 +160,31 @@ private static void verifyMessedSerializationHelper(byte[] svalue) throws Except // This function is used to make sure the hydrated is equal to original string and the initial DTO private static void verifyDTOEqual(DateTimeOffset initial, DateTimeOffset hydrated) throws Exception { - // check string - String info = initial.toString(); - String newInfo = hydrated.toString(); - // check timestamp + String initialStr = initial.toString(); + String hydratedStr = hydrated.toString(); + java.sql.Timestamp originalTS = initial.getTimestamp(); java.sql.Timestamp hydratedTS = hydrated.getTimestamp(); + // and offset - int originalOffset = initial.getMinutesOffset(); + int initiallOffset = initial.getMinutesOffset(); int hydratedOffset = hydrated.getMinutesOffset(); - if (!info.equals(newInfo)) { - fail("Strings are different."); + if (!initialStr.equals(hydratedStr)) { + fail("Hydrated string is different. Expected: " + initialStr + " Received: " + hydratedStr); } - if (!info.equals(dateString)) { - fail("Strings are different from original."); + if (!initialStr.equals(dateString)) { + fail("String is different from original datestring. Expected: " + dateString + " Received: " + initialStr); } if (!initial.equals(hydrated)) { - fail("Equality test fails."); + fail("Hydrated datetimeoffset is different. Expected: " + initial + " Received: " + hydrated); } if (!originalTS.equals(hydratedTS)) { - fail("Equality test fails from original."); + fail("Hydrated timestamp is different. Expected: " + initial.getTimestamp() + " Received: " + + hydrated.getTimestamp()); + } + if (initiallOffset != hydratedOffset) { + fail("Hydrated offset is different. Expected: " + initiallOffset + " Received: " + hydratedOffset); } } } From a196be9464c06983671141a5af9e8d2a3fd2d608 Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Thu, 13 Dec 2018 12:48:41 -0800 Subject: [PATCH 26/51] added locale to time format --- .../microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java index 889b119b68..b6254bb9e7 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java @@ -12,6 +12,7 @@ import java.sql.Statement; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.Locale; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; @@ -28,7 +29,8 @@ @RunWith(JUnitPlatform.class) public class DTOSerialTest extends AbstractTest { - private static final String dateString = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSSSS XXX").format(new Date()); + private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSSSS XXX", Locale.US); + private static final String dateString = sdf.format(new Date()); @Test public void testDSerial() throws Exception { @@ -87,11 +89,13 @@ public void testESerial() throws Exception { private static void verifyCorrectSerialization(DateTimeOffset dto) throws Exception { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(bos); + // serialize the DateTimeOffset; out.writeObject(dto); ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray())); DateTimeOffset dtn = (DateTimeOffset) in.readObject(); verifyDTOEqual(dto, dtn); + // Make sure that you can send rehydrated to server verifyCorrectSend(dtn); } From c7df1ed99ec8877c1ea5c320cc71f40501542a4e Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Thu, 13 Dec 2018 15:42:39 -0800 Subject: [PATCH 27/51] format date for comparision --- .../sqlserver/jdbc/unit/serial/DTOSerialTest.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java index b6254bb9e7..93056885f6 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java @@ -29,7 +29,7 @@ @RunWith(JUnitPlatform.class) public class DTOSerialTest extends AbstractTest { - private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSSSS XXX", Locale.US); + private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSSS XXX", Locale.US); private static final String dateString = sdf.format(new Date()); @Test @@ -177,8 +177,10 @@ private static void verifyDTOEqual(DateTimeOffset initial, DateTimeOffset hydrat if (!initialStr.equals(hydratedStr)) { fail("Hydrated string is different. Expected: " + initialStr + " Received: " + hydratedStr); } - if (!initialStr.equals(dateString)) { - fail("String is different from original datestring. Expected: " + dateString + " Received: " + initialStr); + + String formattedDate = sdf.format(sdf.parse(initialStr)); + if (!formattedDate.equals(dateString)) { + fail("String is different from original datestring. Expected: " + dateString + " Received: " + formattedDate); } if (!initial.equals(hydrated)) { fail("Hydrated datetimeoffset is different. Expected: " + initial + " Received: " + hydrated); From 9a1c8cec153c8407a43d874e028df2c97a312f0d Mon Sep 17 00:00:00 2001 From: Simon Legner Date: Fri, 14 Dec 2018 01:43:14 +0100 Subject: [PATCH 28/51] Improvement | Fix synchronization on a non-final field (#860) --- .../java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java | 2 +- .../microsoft/sqlserver/jdbc/SQLServerSymmetricKeyCache.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 92355d90fe..dd152b9fcf 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -3271,7 +3271,7 @@ public int getTransactionIsolation() throws SQLServerException { } volatile SQLWarning sqlWarnings; // the SQL warnings chain - Object warningSynchronization = new Object(); + private final Object warningSynchronization = new Object(); // Think about returning a copy when we implement additional warnings. @Override diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSymmetricKeyCache.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSymmetricKeyCache.java index bebb43e98b..ab839d20f8 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSymmetricKeyCache.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSymmetricKeyCache.java @@ -51,7 +51,7 @@ public void run() { * */ final class SQLServerSymmetricKeyCache { - static Object lock = new Object(); + static final Object lock = new Object(); private final ConcurrentHashMap cache; private static final SQLServerSymmetricKeyCache instance = new SQLServerSymmetricKeyCache(); private static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1, new ThreadFactory() { From dbcc0a3031059f64b57d6778eb401d7d3d3668a4 Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Thu, 13 Dec 2018 17:33:19 -0800 Subject: [PATCH 29/51] 1 more timezone fix --- .../sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java | 2 +- .../sqlserver/jdbc/unit/serial/DTOSerialTest.java | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java index 2d4dd173e8..9577014813 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java @@ -1375,7 +1375,7 @@ public void testGetString() throws Exception { @Test public void testWithThaiLocale() throws Exception { java.text.SimpleDateFormat tsFormat = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS0000"); - java.text.SimpleDateFormat timeFormat = new java.text.SimpleDateFormat("K:mmaa"); + java.text.SimpleDateFormat timeFormat = new java.text.SimpleDateFormat("h:mmaa"); java.text.SimpleDateFormat dateFormat = new java.text.SimpleDateFormat("yyyy-MM-dd"); java.text.SimpleDateFormat dtoFormat = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS0000 XXX"); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java index 93056885f6..9b6ea5519a 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java @@ -12,7 +12,7 @@ import java.sql.Statement; import java.text.SimpleDateFormat; import java.util.Date; -import java.util.Locale; +import java.util.TimeZone; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; @@ -29,11 +29,14 @@ @RunWith(JUnitPlatform.class) public class DTOSerialTest extends AbstractTest { - private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSSS XXX", Locale.US); - private static final String dateString = sdf.format(new Date()); + private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSSSS XXX"); + private static String dateString; @Test public void testDSerial() throws Exception { + sdf.setTimeZone(TimeZone.getTimeZone("Z")); + dateString = sdf.format(new Date()); + try (Connection conn = DriverManager.getConnection(connectionString); Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)) { From 9276e409926acc0ff03c614101c76453f4508949 Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Mon, 17 Dec 2018 10:24:32 -0800 Subject: [PATCH 30/51] Remove hardcoded error messages from test file (#904) Improvement | Remove hardcoded error messages from test file (#904) --- .../sqlserver/jdbc/TestResource.java | 4 +++- .../BatchExecutionWithBulkCopyTest.java | 19 +++++++++---------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java b/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java index 8b2ca84b4b..b043898784 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java @@ -153,6 +153,8 @@ protected Object[][] getContents() { {"R_expectedValue", "Expected value: "}, {"R_expectedValueAtIndex", "Expected value at index: "}, {"R_switchFailed", "Switch case is not matched with data"}, {"R_resultsetNotInstance", "Result set is not instance of SQLServerResultSet"}, - + {"R_incorrectColumnNum", "Column name or number of supplied values does not match table definition."}, + {"R_incorrectColumnNumInsert", "There are fewer columns in the INSERT statement than values specified in the VALUES clause. The number of values in the VALUES clause must match the number of columns specified in the INSERT statement."}, + {"R_incorrectSyntaxTable", "Incorrect syntax near the keyword 'table'."}, }; } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java index 80f13604c6..fdac78e14d 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java @@ -30,6 +30,7 @@ import com.microsoft.sqlserver.jdbc.SQLServerConnection; import com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement; import com.microsoft.sqlserver.jdbc.SQLServerStatement; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.jdbc.TestUtils; import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; import com.microsoft.sqlserver.testframework.AbstractTest; @@ -536,9 +537,9 @@ public void testIllegalNumberOfArgNoColumnList() throws Exception { pstmt.addBatch(); pstmt.executeBatch(); - throw new Exception("Test did not throw an exception when it was expected."); + throw new Exception(TestResource.getResource("R_expectedExceptionNotThrown")); } catch (BatchUpdateException e) { - assertEquals("Column name or number of supplied values does not match table definition.", e.getMessage()); + assertEquals(TestResource.getResource("R_incorrectColumnNum"), e.getMessage()); } invalid = "insert into " + AbstractSQLGenerator.escapeIdentifier(tableName) @@ -558,11 +559,9 @@ public void testIllegalNumberOfArgNoColumnList() throws Exception { pstmt.addBatch(); pstmt.executeBatch(); - throw new Exception("Test did not throw an exception when it was expected."); + throw new Exception(TestResource.getResource("R_expectedExceptionNotThrown")); } catch (BatchUpdateException e) { - assertEquals( - "There are fewer columns in the INSERT statement than values specified in the VALUES clause. The number of values in the VALUES clause must match the number of columns specified in the INSERT statement.", - e.getMessage()); + assertEquals(TestResource.getResource("R_incorrectColumnNumInsert"), e.getMessage()); } } @@ -585,9 +584,9 @@ public void testNonParameterizedQuery() throws Exception { pstmt.addBatch(); pstmt.executeBatch(); - throw new Exception("Test did not throw an exception when it was expected."); + throw new Exception(TestResource.getResource("R_expectedExceptionNotThrown")); } catch (BatchUpdateException e) { - assertEquals("Incorrect syntax near the keyword 'table'.", e.getMessage()); + assertEquals(TestResource.getResource("R_incorrectSyntaxTable"), e.getMessage()); } invalid = "insert into " + AbstractSQLGenerator.escapeIdentifier(tableName) + " values ('?', ?,? ,?) "; @@ -605,9 +604,9 @@ public void testNonParameterizedQuery() throws Exception { pstmt.addBatch(); pstmt.executeBatch(); - throw new Exception("Test did not throw an exception when it was expected."); + throw new Exception(TestResource.getResource("R_expectedExceptionNotThrown")); } catch (BatchUpdateException e) { - assertEquals("Column name or number of supplied values does not match table definition.", e.getMessage()); + assertEquals(TestResource.getResource("R_incorrectColumnNum"), e.getMessage()); } } From 5afcae36b0948297d0ef63951557438db1aa1e3c Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Mon, 17 Dec 2018 12:35:04 -0800 Subject: [PATCH 31/51] GitHub | Update Issue and Pull Request templates. (#906) --- .github/ISSUE_TEMPLATE/bug_report.md | 36 +++++++++++++++++++ .github/ISSUE_TEMPLATE/feature-request.md | 27 ++++++++++++++ .github/ISSUE_TEMPLATE/question.md | 14 ++++++++ .github/PULL_REQUEST_TEMPLATE/bug_fix.md | 19 ++++++++++ .github/PULL_REQUEST_TEMPLATE/feature.md | 16 +++++++++ .github/PULL_REQUEST_TEMPLATE/improvements.md | 9 +++++ .github/PULL_REQUEST_TEMPLATE/other.md | 16 +++++++++ .github/PULL_REQUEST_TEMPLATE/release.md | 9 +++++ 8 files changed, 146 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/feature-request.md create mode 100644 .github/ISSUE_TEMPLATE/question.md create mode 100644 .github/PULL_REQUEST_TEMPLATE/bug_fix.md create mode 100644 .github/PULL_REQUEST_TEMPLATE/feature.md create mode 100644 .github/PULL_REQUEST_TEMPLATE/improvements.md create mode 100644 .github/PULL_REQUEST_TEMPLATE/other.md create mode 100644 .github/PULL_REQUEST_TEMPLATE/release.md diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000..a43efd7d3e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,36 @@ +--- +name: Bug report +about: Report a bug to help us improve +title: "[BUG]" +labels: bug +assignees: '' + +--- + +## Driver version + + +## SQL Server version + + +## Client Operating System + + +## JAVA/JVM version + + +## Table schema + + +## Problem description + +1. Expected behaviour: +2. Actual behaviour: +3. Error message/stack trace: +4. Any other details that can be helpful: + +## JDBC trace logs + + +## Reproduction code + diff --git a/.github/ISSUE_TEMPLATE/feature-request.md b/.github/ISSUE_TEMPLATE/feature-request.md new file mode 100644 index 0000000000..ba9ff691be --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature-request.md @@ -0,0 +1,27 @@ +--- +name: Feature Request +about: Suggest an idea for this project +title: "[FEATURE REQUEST]" +labels: enhancement +assignees: '' + +--- + +## Is your feature request related to a problem? If so, please give a short summary of the problem and how the feature would resolve it + + +## Describe the preferred solution + + +## Describe alternatives you've considered + + +## Additional context + + +## Reference Documentations/Specifications + + + +## Reference Implementation + diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md new file mode 100644 index 0000000000..1131f35dbf --- /dev/null +++ b/.github/ISSUE_TEMPLATE/question.md @@ -0,0 +1,14 @@ +--- +name: Question +about: Discuss an idea to see if it would be an appropriate Issue. +title: "[QUESTION]" +labels: question +assignees: '' + +--- + +## Question + + +## Relevant Issues and Pull Requests + diff --git a/.github/PULL_REQUEST_TEMPLATE/bug_fix.md b/.github/PULL_REQUEST_TEMPLATE/bug_fix.md new file mode 100644 index 0000000000..23dae2c11d --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/bug_fix.md @@ -0,0 +1,19 @@ +--- +name: Bug Fix +about: Fix for a driver issue +title: "Fix | " + +--- + +## Problem description + + +### Fixes existing GitHub issue + + +## Fix Description + + +## New Public APIs + + diff --git a/.github/PULL_REQUEST_TEMPLATE/feature.md b/.github/PULL_REQUEST_TEMPLATE/feature.md new file mode 100644 index 0000000000..cbf4e12e4d --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/feature.md @@ -0,0 +1,16 @@ +--- +name: Feature +about: Propose a new feature to the driver +title: "Feature | " + +--- + +## Brief Description + + + +### Fixes existing GitHub issue + + +## New Public APIs + diff --git a/.github/PULL_REQUEST_TEMPLATE/improvements.md b/.github/PULL_REQUEST_TEMPLATE/improvements.md new file mode 100644 index 0000000000..2ec675af36 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/improvements.md @@ -0,0 +1,9 @@ +--- +name: Improvements +about: Improve driver's performance/code quality +title: "Improvement | " + +--- + +## Brief Description + diff --git a/.github/PULL_REQUEST_TEMPLATE/other.md b/.github/PULL_REQUEST_TEMPLATE/other.md new file mode 100644 index 0000000000..bc07bb1904 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/other.md @@ -0,0 +1,16 @@ +--- +name: Other +about: Propose a custom change to the project source code. +title: "Other | " + +--- + +## Brief Description + + + +### Fixes existing GitHub issue + + +## New Public APIs + diff --git a/.github/PULL_REQUEST_TEMPLATE/release.md b/.github/PULL_REQUEST_TEMPLATE/release.md new file mode 100644 index 0000000000..0f37e458ab --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE/release.md @@ -0,0 +1,9 @@ +--- +name: Release +about: Push changes for release preparations +title: "Release | " + +--- + +## Brief Description + From bcc87326e8b92658060e8fe88010a18dc77ec092 Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Mon, 17 Dec 2018 12:47:16 -0800 Subject: [PATCH 32/51] cleanup leaded stream resources --- .../callablestatement/CallableMixedTest.java | 3 - .../jdbc/resultset/ResultSetTest.java | 3 +- .../jdbc/unit/serial/DTOSerialTest.java | 91 ++++++++++--------- 3 files changed, 50 insertions(+), 47 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java index f7e857545c..df3c8188d3 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java @@ -1,6 +1,5 @@ package com.microsoft.sqlserver.jdbc.callablestatement; - import static org.junit.jupiter.api.Assertions.fail; import java.sql.CallableStatement; @@ -94,8 +93,6 @@ public void datatypestest() throws Exception { fail("Received data not equal to setdata"); } } - - try (ResultSet rs = cstmt.executeQuery()) {} } } finally { try (Connection conn = DriverManager.getConnection(connectionString); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java index e163cba633..ffc3e9a842 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java @@ -38,6 +38,7 @@ import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; import com.microsoft.sqlserver.testframework.AbstractTest; + @RunWith(JUnitPlatform.class) public class ResultSetTest extends AbstractTest { private static final String tableName = RandomUtil.getIdentifier("StatementParam"); @@ -466,7 +467,7 @@ public void testResultSetMethods() throws SQLException { } catch (Exception e) { fail(e.toString()); } finally { - stmt.executeUpdate("drop table " + AbstractSQLGenerator.escapeIdentifier(tableName)); + TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(tableName), stmt); } } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java index 9b6ea5519a..e8a4c79737 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java @@ -21,7 +21,6 @@ import com.microsoft.sqlserver.jdbc.SQLServerException; import com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement; import com.microsoft.sqlserver.jdbc.SQLServerResultSet; -import com.microsoft.sqlserver.jdbc.TestUtils; import com.microsoft.sqlserver.testframework.AbstractTest; import microsoft.sql.DateTimeOffset; @@ -41,11 +40,12 @@ public void testDSerial() throws Exception { Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)) { // create a DTO - ResultSet rs = stmt.executeQuery( - "SELECT CAST('" + dateString + "' AS datetimeoffset(7)) AS" + " 'datetimeoffset IS08601' "); - rs.next(); - verifyCorrectSerialization(((SQLServerResultSet) rs).getDateTimeOffset(1)); - verifyMessedSerialization(); + try (ResultSet rs = stmt.executeQuery( + "SELECT CAST('" + dateString + "' AS datetimeoffset(7)) AS" + " 'datetimeoffset IS08601' ")) { + rs.next(); + verifyCorrectSerialization(((SQLServerResultSet) rs).getDateTimeOffset(1)); + verifyMessedSerialization(); + } } } @@ -65,56 +65,59 @@ public void testESerial() throws Exception { } // store the info - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - ObjectOutputStream out = new ObjectOutputStream(bos); - - // serialize the exception; - out.writeObject(currException); - ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray())); - SQLServerException ex = (SQLServerException) in.readObject(); - - if (!currException.toString().equals(ex.toString())) { - fail("Error strings are different. Expected: " + currException.toString() + " Received: " - + ex.toString()); - } - if (!currException.getSQLState().equals(ex.getSQLState())) { - fail("Sql states are different. Expected: " + currException.getSQLState() + " Received: " - + ex.getSQLState()); - } - if (currException.getErrorCode() != ex.getErrorCode()) { - fail("Error codes are different. Expected: " + currException.getErrorCode() + " Received: " - + ex.getErrorCode()); + try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(bos)) { + + // serialize the exception; + out.writeObject(currException); + try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()))) { + SQLServerException ex = (SQLServerException) in.readObject(); + + if (!currException.toString().equals(ex.toString())) { + fail("Error strings are different. Expected: " + currException.toString() + " Received: " + + ex.toString()); + } + if (!currException.getSQLState().equals(ex.getSQLState())) { + fail("Sql states are different. Expected: " + currException.getSQLState() + " Received: " + + ex.getSQLState()); + } + if (currException.getErrorCode() != ex.getErrorCode()) { + fail("Error codes are different. Expected: " + currException.getErrorCode() + " Received: " + + ex.getErrorCode()); + } + } } } } // Positive test case, this should succeed private static void verifyCorrectSerialization(DateTimeOffset dto) throws Exception { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - ObjectOutputStream out = new ObjectOutputStream(bos); + try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); + ObjectOutputStream out = new ObjectOutputStream(bos)) { - // serialize the DateTimeOffset; - out.writeObject(dto); - ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray())); - DateTimeOffset dtn = (DateTimeOffset) in.readObject(); - verifyDTOEqual(dto, dtn); + // serialize the DateTimeOffset; + out.writeObject(dto); + try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()))) { + DateTimeOffset dtn = (DateTimeOffset) in.readObject(); + verifyDTOEqual(dto, dtn); - // Make sure that you can send rehydrated to server - verifyCorrectSend(dtn); + // Make sure that you can send rehydrated to server + verifyCorrectSend(dtn); + } + } } // this is to make sure that the rehydrated date can be sent to server correctly private static void verifyCorrectSend(DateTimeOffset dtN) throws Exception { - String connectionString = TestUtils.getConfiguredProperty("mssql_jdbc_test_connection_properties"); - // create a DTO try (Connection conn = DriverManager.getConnection(connectionString); SQLServerPreparedStatement ps = (SQLServerPreparedStatement) conn .prepareStatement("SELECT CAST(? AS datetimeoffset(7)) AS" + " 'datetimeoffset IS08601' ")) { ps.setDateTimeOffset(1, dtN); - ResultSet rs = ps.executeQuery(); - rs.next(); - verifyDTOEqual(dtN, ((SQLServerResultSet) rs).getDateTimeOffset(1)); + try (ResultSet rs = ps.executeQuery()) { + rs.next(); + verifyDTOEqual(dtN, ((SQLServerResultSet) rs).getDateTimeOffset(1)); + } } } @@ -161,8 +164,9 @@ private static void verifyMessedSerialization() throws Exception { } private static void verifyMessedSerializationHelper(byte[] svalue) throws Exception { - ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(svalue)); - DateTimeOffset dtn = (DateTimeOffset) in.readObject(); + try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(svalue))) { + DateTimeOffset dtn = (DateTimeOffset) in.readObject(); + } } // This function is used to make sure the hydrated is equal to original string and the initial DTO @@ -180,10 +184,11 @@ private static void verifyDTOEqual(DateTimeOffset initial, DateTimeOffset hydrat if (!initialStr.equals(hydratedStr)) { fail("Hydrated string is different. Expected: " + initialStr + " Received: " + hydratedStr); } - + String formattedDate = sdf.format(sdf.parse(initialStr)); if (!formattedDate.equals(dateString)) { - fail("String is different from original datestring. Expected: " + dateString + " Received: " + formattedDate); + fail("String is different from original datestring. Expected: " + dateString + " Received: " + + formattedDate); } if (!initial.equals(hydrated)) { fail("Hydrated datetimeoffset is different. Expected: " + initial + " Received: " + hydrated); From b6bae75a6423b4833b3010339eaf832e36904008 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Mon, 17 Dec 2018 13:55:26 -0800 Subject: [PATCH 33/51] Improvement | Added new APIs to retrieve SQL Server error information received with SQLServerException (#905) --- .../microsoft/sqlserver/jdbc/IOBuffer.java | 4 +- .../sqlserver/jdbc/KerbAuthentication.java | 1 - .../sqlserver/jdbc/SQLServerError.java | 110 ++++++++++++++++++ .../sqlserver/jdbc/SQLServerException.java | 61 ++++++---- .../sqlserver/jdbc/SQLServerResultSet.java | 2 +- .../sqlserver/jdbc/SQLServerStatement.java | 4 +- .../microsoft/sqlserver/jdbc/StreamError.java | 63 ---------- .../microsoft/sqlserver/jdbc/StreamInfo.java | 2 +- .../microsoft/sqlserver/jdbc/tdsparser.java | 10 +- .../bulkCopy/BulkCopyColumnMappingTest.java | 3 +- .../microsoft/sqlserver/jdbc/bvt/BvtTest.java | 14 +-- .../CallableStatementTest.java | 2 +- .../DatabaseMetaDataTest.java | 1 - .../sqlserver/jdbc/tvp/TVPNumericTest.java | 4 +- .../sqlserver/jdbc/tvp/TVPSchemaTest.java | 2 +- .../jdbc/unit/SQLServerErrorTest.java | 107 +++++++++++++++++ .../sqlserver/testframework/AbstractTest.java | 1 - 17 files changed, 275 insertions(+), 116 deletions(-) create mode 100644 src/main/java/com/microsoft/sqlserver/jdbc/SQLServerError.java delete mode 100644 src/main/java/com/microsoft/sqlserver/jdbc/StreamError.java create mode 100644 src/test/java/com/microsoft/sqlserver/jdbc/unit/SQLServerErrorTest.java diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java index 056cf721ff..60e976f6f4 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java @@ -4587,10 +4587,10 @@ void writeTVPRows(TVP value) throws SQLServerException { int tokenType = tdsReader.peekTokenType(); if (TDS.TDS_ERR == tokenType) { - StreamError databaseError = new StreamError(); + SQLServerError databaseError = new SQLServerError(); databaseError.setFromTDS(tdsReader); - SQLServerException.makeFromDatabaseError(con, null, databaseError.getMessage(), databaseError, + SQLServerException.makeFromDatabaseError(con, null, databaseError.getErrorMessage(), databaseError, false); } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/KerbAuthentication.java b/src/main/java/com/microsoft/sqlserver/jdbc/KerbAuthentication.java index d0a99ba1c6..b2f546b9ed 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/KerbAuthentication.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/KerbAuthentication.java @@ -5,7 +5,6 @@ package com.microsoft.sqlserver.jdbc; -import java.lang.reflect.Method; import java.net.IDN; import java.net.InetAddress; import java.net.UnknownHostException; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerError.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerError.java new file mode 100644 index 0000000000..b55cf566c4 --- /dev/null +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerError.java @@ -0,0 +1,110 @@ +/* + * 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.jdbc; + +import java.io.Serializable; + +/** + * SQLServerError represents a TDS error or message event. + */ +public final class SQLServerError extends StreamPacket implements Serializable { + /** + * Always update serialVersionUID when prompted + */ + private static final long serialVersionUID = -7304033613218700719L; + private String errorMessage = ""; + private int errorNumber; + private int errorState; + private int errorSeverity; + private String serverName; + private String procName; + private long lineNumber; + + /** + * Returns error message as received from SQL Server + * + * @return Error Message + */ + public String getErrorMessage() { + return errorMessage; + } + + /** + * Returns error number as received from SQL Server + * + * @return Error Number + */ + public int getErrorNumber() { + return errorNumber; + } + + /** + * Returns error state as received from SQL Server + * + * @return Error State + */ + public int getErrorState() { + return errorState; + } + + /** + * Returns Severity of error (as int value) as received from SQL Server + * + * @return Error Severity + */ + public int getErrorSeverity() { + return errorSeverity; + } + + /** + * Returns name of the server where exception occurs as received from SQL Server + * + * @return Server Name + */ + public String getServerName() { + return serverName; + } + + /** + * Returns name of the stored procedure where exception occurs as received from SQL Server + * + * @return Procedure Name + */ + public String getProcedureName() { + return procName; + } + + /** + * Returns line number where the error occurred in Stored Procedure returned by getProcedureName() as + * received from SQL Server + * + * @return Line Number + */ + public long getLineNumber() { + return lineNumber; + } + + SQLServerError() { + super(TDS.TDS_ERR); + } + + void setFromTDS(TDSReader tdsReader) throws SQLServerException { + if (TDS.TDS_ERR != tdsReader.readUnsignedByte()) + assert false; + setContentsFromTDS(tdsReader); + } + + void setContentsFromTDS(TDSReader tdsReader) throws SQLServerException { + tdsReader.readUnsignedShort(); // token length (ignored) + errorNumber = tdsReader.readInt(); + errorState = tdsReader.readUnsignedByte(); + errorSeverity = tdsReader.readUnsignedByte(); // matches master.dbo.sysmessages + errorMessage = tdsReader.readUnicodeString(tdsReader.readUnsignedShort()); + serverName = tdsReader.readUnicodeString(tdsReader.readUnsignedByte()); + procName = tdsReader.readUnicodeString(tdsReader.readUnsignedByte()); + lineNumber = tdsReader.readUnsignedInt(); + } +} diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerException.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerException.java index bdea428d6d..2b10a8dbca 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerException.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerException.java @@ -84,6 +84,7 @@ public final class SQLServerException extends java.sql.SQLException { static final int DATA_CLASSIFICATION_INVALID_INFORMATION_TYPE_INDEX = 13; private int driverErrorCode = DRIVER_ERROR_NONE; + private SQLServerError sqlServerError; final int getDriverErrorCode() { return driverErrorCode; @@ -97,9 +98,9 @@ final void setDriverErrorCode(int value) { * Logs an exception to the driver log file. * * @param o - * the io buffer that generated the exception + * the IO buffer that generated the exception * @param errText - * the excception message + * the exception message * @param bStack * true to generate the stack trace */ @@ -181,17 +182,17 @@ static String getErrString(String errCode) { * the exception message * @param errState * the exception state - * @param streamError - * the StreamError object + * @param sqlServerError + * the SQLServerError object * @param bStack * true to generate the stack trace */ - SQLServerException(Object obj, String errText, String errState, StreamError streamError, boolean bStack) { - super(errText, errState, streamError.getErrorNumber()); - - // Log SQL error with info from StreamError. - errText = "Msg " + streamError.getErrorNumber() + ", Level " + streamError.getErrorSeverity() + ", State " - + streamError.getErrorState() + ", " + errText; + SQLServerException(Object obj, String errText, String errState, SQLServerError sqlServerError, boolean bStack) { + super(errText, errState, sqlServerError.getErrorNumber()); + this.sqlServerError = sqlServerError; + // Log SQL error with info from SQLServerError. + errText = "Msg " + sqlServerError.getErrorNumber() + ", Level " + sqlServerError.getErrorSeverity() + ", State " + + sqlServerError.getErrorState() + ", " + errText; logException(obj, errText, bStack); } @@ -202,9 +203,9 @@ static String getErrString(String errCode) { * the connection * @param obj * @param errText - * the excception message + * the exception message * @param state - * he excpeption state + * the exception state * @param bStack * true to generate the stack trace * @throws SQLServerException @@ -235,28 +236,28 @@ static void makeFromDriverError(SQLServerConnection con, Object obj, String errT } /** - * Builds a new SQL Exception from a streamError detected by the driver. + * Builds a new SQL Exception from a SQLServerError detected by the driver. * * @param con * the connection * @param obj * @param errText - * the excception message - * @param streamError + * the exception message + * @param sqlServerError * @param bStack * true to generate the stack trace * @throws SQLServerException */ - static void makeFromDatabaseError(SQLServerConnection con, Object obj, String errText, StreamError streamError, - boolean bStack) throws SQLServerException { - String state = generateStateCode(con, streamError.getErrorNumber(), streamError.getErrorState()); + static void makeFromDatabaseError(SQLServerConnection con, Object obj, String errText, + SQLServerError sqlServerError, boolean bStack) throws SQLServerException { + String state = generateStateCode(con, sqlServerError.getErrorNumber(), sqlServerError.getErrorState()); SQLServerException theException = new SQLServerException(obj, - SQLServerException.checkAndAppendClientConnId(errText, con), state, streamError, bStack); + SQLServerException.checkAndAppendClientConnId(errText, con), state, sqlServerError, bStack); theException.setDriverErrorCode(DRIVER_ERROR_FROM_DATABASE); // Close the connection if we get a severity 20 or higher error class (nClass is severity of error). - if ((streamError.getErrorSeverity() >= 20) && (null != con)) { + if ((sqlServerError.getErrorSeverity() >= 20) && (null != con)) { con.notifyPooledConnection(theException); con.close(); } @@ -366,18 +367,18 @@ static String generateStateCode(SQLServerConnection con, int errNum, int databas * Appends ClientConnectionId to an error message if applicable. * * @param errMsg - * - the original error message. + * the original error message * @param conn - * - the SQLServerConnection object - * @return error string concated by ClientConnectionId(in string format) if applicable, otherwise, return original - * error string. + * the SQLServerConnection object + * @return error string concatenated by ClientConnectionId(in string format) if applicable, otherwise, return + * original error string. */ static String checkAndAppendClientConnId(String errMsg, SQLServerConnection conn) throws SQLServerException { if (null != conn && conn.attachConnId()) { UUID clientConnId = conn.getClientConIdInternal(); assert null != clientConnId; StringBuilder sb = new StringBuilder(errMsg); - // This syntex of adding connection id is matched in a retry logic. If anything changes here, make + // This syntax of adding connection id is matched in a retry logic. If anything changes here, make // necessary changes to enableSSL() function's exception handling mechanism. sb.append(LOG_CLIENT_CONNECTION_ID_PREFIX); sb.append(clientConnId.toString()); @@ -395,4 +396,14 @@ static void throwNotSupportedException(SQLServerConnection con, Object obj) thro static void throwFeatureNotSupportedException() throws SQLFeatureNotSupportedException { throw new SQLFeatureNotSupportedException(SQLServerException.getErrString("R_notSupported")); } + + /** + * Returns SQLServerError object containing detailed info about exception as received from SQL Server. This API + * returns null if no server error has occurred. + * + * @return SQLServerError + */ + public SQLServerError getSQLServerError() { + return sqlServerError; + } } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java index 8a6e3391c9..1aef808039 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java @@ -5398,7 +5398,7 @@ final RowType nextRow() throws SQLServerException { if (fetchBufferCurrentRowType.equals(RowType.UNKNOWN) && null != fetchBufferTokenHandler.getDatabaseError()) { SQLServerException.makeFromDatabaseError(stmt.connection, null, - fetchBufferTokenHandler.getDatabaseError().getMessage(), + fetchBufferTokenHandler.getDatabaseError().getErrorMessage(), fetchBufferTokenHandler.getDatabaseError(), false); } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java index 7f166b0723..39ab2702f4 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java @@ -1572,7 +1572,7 @@ boolean onInfo(TDSReader tdsReader) throws SQLServerException { executedSqlDirectly = true; SQLWarning warning = new SQLWarning( - infoToken.msg.getMessage(), SQLServerException.generateStateCode(connection, + infoToken.msg.getErrorMessage(), SQLServerException.generateStateCode(connection, infoToken.msg.getErrorNumber(), infoToken.msg.getErrorState()), infoToken.msg.getErrorNumber()); @@ -1611,7 +1611,7 @@ boolean onInfo(TDSReader tdsReader) throws SQLServerException { // Check for errors first. if (null != nextResult.getDatabaseError()) { - SQLServerException.makeFromDatabaseError(connection, null, nextResult.getDatabaseError().getMessage(), + SQLServerException.makeFromDatabaseError(connection, null, nextResult.getDatabaseError().getErrorMessage(), nextResult.getDatabaseError(), false); } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/StreamError.java b/src/main/java/com/microsoft/sqlserver/jdbc/StreamError.java deleted file mode 100644 index d28b5a7264..0000000000 --- a/src/main/java/com/microsoft/sqlserver/jdbc/StreamError.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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.jdbc; - -/** - * StreamError represents a TDS error or message event. - */ - -final class StreamError extends StreamPacket { - /** the error message */ - String errorMessage = ""; - /** the error number */ - int errorNumber; - /** the tds error state */ - int errorState; - /** the tds error severity */ - int errorSeverity; - - String serverName; - String procName; - long lineNumber; - - final String getMessage() { - return errorMessage; - } - - final int getErrorNumber() { - return errorNumber; - } - - final int getErrorState() { - return errorState; - } - - final int getErrorSeverity() { - return errorSeverity; - } - - StreamError() { - super(TDS.TDS_ERR); - } - - void setFromTDS(TDSReader tdsReader) throws SQLServerException { - if (TDS.TDS_ERR != tdsReader.readUnsignedByte()) - assert false; - setContentsFromTDS(tdsReader); - } - - void setContentsFromTDS(TDSReader tdsReader) throws SQLServerException { - tdsReader.readUnsignedShort(); // token length (ignored) - errorNumber = tdsReader.readInt(); - errorState = tdsReader.readUnsignedByte(); - errorSeverity = tdsReader.readUnsignedByte(); // matches master.dbo.sysmessages - errorMessage = tdsReader.readUnicodeString(tdsReader.readUnsignedShort()); - serverName = tdsReader.readUnicodeString(tdsReader.readUnsignedByte()); - procName = tdsReader.readUnicodeString(tdsReader.readUnsignedByte()); - lineNumber = tdsReader.readUnsignedInt(); - - } -} diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/StreamInfo.java b/src/main/java/com/microsoft/sqlserver/jdbc/StreamInfo.java index c4134ce614..5b4cb571fa 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/StreamInfo.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/StreamInfo.java @@ -6,7 +6,7 @@ package com.microsoft.sqlserver.jdbc; final class StreamInfo extends StreamPacket { - final StreamError msg = new StreamError(); + final SQLServerError msg = new SQLServerError(); StreamInfo() { super(TDS.TDS_MSG); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/tdsparser.java b/src/main/java/com/microsoft/sqlserver/jdbc/tdsparser.java index abfae2a839..4e07bc6b55 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/tdsparser.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/tdsparser.java @@ -155,12 +155,12 @@ static void ignoreLengthPrefixedToken(TDSReader tdsReader) throws SQLServerExcep class TDSTokenHandler { final String logContext; - private StreamError databaseError; + private SQLServerError databaseError; /** TDS protocol diagnostics logger */ private static Logger logger = Logger.getLogger("com.microsoft.sqlserver.jdbc.internals.TDS.TOKEN"); - final StreamError getDatabaseError() { + final SQLServerError getDatabaseError() { return databaseError; } @@ -206,10 +206,10 @@ boolean onDone(TDSReader tdsReader) throws SQLServerException { boolean onError(TDSReader tdsReader) throws SQLServerException { if (null == databaseError) { - databaseError = new StreamError(); + databaseError = new SQLServerError(); databaseError.setFromTDS(tdsReader); } else { - (new StreamError()).setFromTDS(tdsReader); + (new SQLServerError()).setFromTDS(tdsReader); } return true; @@ -255,7 +255,7 @@ boolean onTabName(TDSReader tdsReader) throws SQLServerException { void onEOF(TDSReader tdsReader) throws SQLServerException { if (null != getDatabaseError()) { - SQLServerException.makeFromDatabaseError(tdsReader.getConnection(), null, getDatabaseError().getMessage(), + SQLServerException.makeFromDatabaseError(tdsReader.getConnection(), null, getDatabaseError().getErrorMessage(), getDatabaseError(), false); } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyColumnMappingTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyColumnMappingTest.java index e6b96f1833..eefbc4ca38 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyColumnMappingTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyColumnMappingTest.java @@ -4,8 +4,8 @@ */ package com.microsoft.sqlserver.jdbc.bulkCopy; -import static org.junit.jupiter.api.Assertions.fail; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; import java.sql.ResultSet; import java.sql.ResultSetMetaData; @@ -22,7 +22,6 @@ import com.microsoft.sqlserver.jdbc.ComparisonUtil; import com.microsoft.sqlserver.jdbc.TestResource; -import com.microsoft.sqlserver.jdbc.TestUtils; import com.microsoft.sqlserver.testframework.DBConnection; import com.microsoft.sqlserver.testframework.DBResultSet; import com.microsoft.sqlserver.testframework.DBStatement; diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/bvt/BvtTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/bvt/BvtTest.java index 0d5f768d4e..6dd760f28e 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/bvt/BvtTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/bvt/BvtTest.java @@ -14,23 +14,21 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; +import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.DBConnection; -import com.microsoft.sqlserver.testframework.DBStatement; -import com.microsoft.sqlserver.testframework.DBTable; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.DBPreparedStatement; import com.microsoft.sqlserver.testframework.DBResultSet; import com.microsoft.sqlserver.testframework.DBResultSetTypes; +import com.microsoft.sqlserver.testframework.DBStatement; +import com.microsoft.sqlserver.testframework.DBTable; @RunWith(JUnitPlatform.class) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableStatementTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableStatementTest.java index fde8003532..4cac1a8e46 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableStatementTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableStatementTest.java @@ -24,8 +24,8 @@ import com.microsoft.sqlserver.jdbc.SQLServerDataSource; import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.jdbc.TestUtils; -import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; +import com.microsoft.sqlserver.testframework.AbstractTest; /** diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java index 3a4a054e28..5bcbcfc672 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java @@ -22,7 +22,6 @@ import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.DriverManager; -import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/tvp/TVPNumericTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/tvp/TVPNumericTest.java index 0cde147303..49dafa04d6 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/tvp/TVPNumericTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/tvp/TVPNumericTest.java @@ -4,11 +4,11 @@ */ package com.microsoft.sqlserver.jdbc.tvp; +import java.sql.Connection; +import java.sql.DriverManager; import java.sql.SQLException; import java.sql.SQLTimeoutException; -import java.sql.Connection; import java.sql.Statement; -import java.sql.DriverManager; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/tvp/TVPSchemaTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/tvp/TVPSchemaTest.java index a375fe044d..d3165489eb 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/tvp/TVPSchemaTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/tvp/TVPSchemaTest.java @@ -9,9 +9,9 @@ import java.io.IOException; import java.sql.Connection; import java.sql.DriverManager; +import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; -import java.sql.ResultSet; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/SQLServerErrorTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/SQLServerErrorTest.java new file mode 100644 index 0000000000..8d21f523ed --- /dev/null +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/SQLServerErrorTest.java @@ -0,0 +1,107 @@ +/* + * 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.jdbc.unit; + +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +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.jdbc.SQLServerDataSource; +import com.microsoft.sqlserver.jdbc.SQLServerError; +import com.microsoft.sqlserver.jdbc.SQLServerException; +import com.microsoft.sqlserver.jdbc.TestResource; +import com.microsoft.sqlserver.testframework.AbstractTest; + + +@RunWith(JUnitPlatform.class) +public class SQLServerErrorTest extends AbstractTest { + static int loginTimeOutInSeconds = 10; + + @Test + public void testLoginFailedError() { + SQLServerDataSource ds = new SQLServerDataSource(); + ds.setURL(connectionString); + ds.setLoginTimeout(loginTimeOutInSeconds); + ds.setPassword(""); + try (SQLServerConnection connection = (SQLServerConnection) ds.getConnection()) { + fail(TestResource.getResource("R_expectedFailPassed")); + } catch (SQLServerException e) { + assertNotNull(e); + assertNotNull(e.getSQLServerError()); + + SQLServerError sse = e.getSQLServerError(); + assertTrue(sse.getErrorMessage().contains(TestResource.getResource("R_loginFailed"))); + assertEquals(18456, sse.getErrorNumber()); + assertEquals(1, sse.getErrorState()); + assertEquals(14, sse.getErrorSeverity()); + assertEquals("", sse.getProcedureName()); + assertEquals(1, sse.getLineNumber()); + } + } + + @Test + public void testClosedStatementError() throws SQLException { + SQLServerDataSource ds = new SQLServerDataSource(); + ds.setURL(connectionString); + try (SQLServerConnection connection = (SQLServerConnection) ds.getConnection(); + Statement stmt = connection.createStatement()) { + stmt.close(); + try (ResultSet rs = stmt.executeQuery("SELECT 1")) { + fail(TestResource.getResource("R_expectedFailPassed")); + } catch (SQLServerException e) { + assertNotNull(e); + assertNull(e.getSQLServerError()); + } + } + } + + @Test + public void testClosedResultSetError() throws SQLException { + SQLServerDataSource ds = new SQLServerDataSource(); + ds.setURL(connectionString); + try (SQLServerConnection connection = (SQLServerConnection) ds.getConnection(); + Statement stmt = connection.createStatement(); ResultSet rs = stmt.executeQuery("SELECT 1")) { + rs.close(); + rs.next(); + fail(TestResource.getResource("R_expectedFailPassed")); + } catch (SQLServerException e) { + assertNotNull(e); + assertNull(e.getSQLServerError()); + } + } + + @Test + public void testInvalidTableName() throws SQLException { + SQLServerDataSource ds = new SQLServerDataSource(); + ds.setURL(connectionString); + try (SQLServerConnection connection = (SQLServerConnection) ds.getConnection(); + Statement stmt = connection.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT * from INVALID_TABLE_NAME")) { + fail(TestResource.getResource("R_expectedFailPassed")); + } catch (SQLServerException e) { + assertNotNull(e); + assertNotNull(e.getSQLServerError()); + + SQLServerError sse = e.getSQLServerError(); + assertTrue(sse.getErrorMessage().contains(TestResource.getResource("R_invalidObjectName"))); + assertEquals(208, sse.getErrorNumber()); + assertEquals(1, sse.getErrorState()); + assertEquals(16, sse.getErrorSeverity()); + assertEquals("", sse.getProcedureName()); + assertEquals(1, sse.getLineNumber()); + } + } +} diff --git a/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java b/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java index b0b098850c..0e58d31513 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java @@ -83,7 +83,6 @@ public static void setup() throws Exception { try { Assertions.assertNotNull(connectionString, "Connection String should not be null"); connection = PrepUtil.getConnection(connectionString, info); - } catch (Exception e) { throw e; } From fe01b416b28f381553da903fd142c76b308fd546 Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Mon, 17 Dec 2018 17:55:13 -0800 Subject: [PATCH 34/51] manually merged with #903 changes for now --- .../bulkCopy/BulkCopyColumnMappingTest.java | 1 + .../jdbc/unit/serial/DTOSerialTest.java | 12 +++-- .../sqlserver/testframework/AbstractTest.java | 46 ++++++++++++++++++- 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyColumnMappingTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyColumnMappingTest.java index 824d6a36fe..bbc538e07e 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyColumnMappingTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyColumnMappingTest.java @@ -23,6 +23,7 @@ import com.microsoft.sqlserver.jdbc.ComparisonUtil; import com.microsoft.sqlserver.jdbc.TestResource; +import com.microsoft.sqlserver.jdbc.TestUtils; import com.microsoft.sqlserver.testframework.DBConnection; import com.microsoft.sqlserver.testframework.DBResultSet; import com.microsoft.sqlserver.testframework.DBStatement; diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java index e8a4c79737..6e1e8155c7 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java @@ -28,7 +28,7 @@ @RunWith(JUnitPlatform.class) public class DTOSerialTest extends AbstractTest { - private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSSSS XXX"); + private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS XXX"); private static String dateString; @Test @@ -91,6 +91,7 @@ public void testESerial() throws Exception { } // Positive test case, this should succeed + @Test private static void verifyCorrectSerialization(DateTimeOffset dto) throws Exception { try (ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(bos)) { @@ -108,20 +109,22 @@ private static void verifyCorrectSerialization(DateTimeOffset dto) throws Except } // this is to make sure that the rehydrated date can be sent to server correctly - private static void verifyCorrectSend(DateTimeOffset dtN) throws Exception { + @Test + private static void verifyCorrectSend(DateTimeOffset dtn) throws Exception { // create a DTO try (Connection conn = DriverManager.getConnection(connectionString); SQLServerPreparedStatement ps = (SQLServerPreparedStatement) conn .prepareStatement("SELECT CAST(? AS datetimeoffset(7)) AS" + " 'datetimeoffset IS08601' ")) { - ps.setDateTimeOffset(1, dtN); + ps.setDateTimeOffset(1, dtn); try (ResultSet rs = ps.executeQuery()) { rs.next(); - verifyDTOEqual(dtN, ((SQLServerResultSet) rs).getDateTimeOffset(1)); + verifyDTOEqual(dtn, ((SQLServerResultSet) rs).getDateTimeOffset(1)); } } } // Negative test cases. + @Test private static void verifyMessedSerialization() throws Exception { // serialized DTO class with wrong nano values (-1) byte wrongNanos[] = {-84, -19, 0, 5, 115, 114, 0, 47, 109, 105, 99, 114, 111, 115, 111, 102, 116, 46, 115, 113, @@ -165,6 +168,7 @@ private static void verifyMessedSerialization() throws Exception { private static void verifyMessedSerializationHelper(byte[] svalue) throws Exception { try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(svalue))) { + //this will throw error for negative tests DateTimeOffset dtn = (DateTimeOffset) in.readObject(); } } diff --git a/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java b/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java index 0e58d31513..0d2e6f37cf 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java @@ -6,6 +6,9 @@ package com.microsoft.sqlserver.testframework; import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; import java.util.Properties; import java.util.logging.ConsoleHandler; import java.util.logging.FileHandler; @@ -52,6 +55,12 @@ public abstract class AbstractTest { protected static Properties info = new Properties(); + private final static int ENGINE_EDITION_FOR_SQL_AZURE = 5; + private final static int ENGINE_EDITION_FOR_SQL_AZURE_DW = 6; + private static boolean _determinedSqlAzureOrSqlServer = false; + private static boolean _isSqlAzure = false; + private static boolean _isSqlAzureDW = false; + /** * This will take care of all initialization before running the Test Suite. * @@ -83,6 +92,7 @@ public static void setup() throws Exception { try { Assertions.assertNotNull(connectionString, "Connection String should not be null"); connection = PrepUtil.getConnection(connectionString, info); + isSqlAzureOrAzureDW(connection); } catch (Exception e) { throw e; } @@ -164,13 +174,45 @@ public static void invokeLogging() { handler.setLevel(Level.FINEST); Logger.getLogger("").addHandler(handler); } - // By default, Loggers also send their output to their parent logger.   + // By default, Loggers also send their output to their parent logger. // Typically the root Logger is configured with a set of Handlers that essentially act as default handlers - // for all loggers.  + // for all loggers. Logger logger = Logger.getLogger("com.microsoft.sqlserver.jdbc"); logger.setLevel(Level.FINEST); } catch (Exception e) { System.err.println("Some how could not invoke logging: " + e.getMessage()); } } + + public static boolean isSqlAzure() { + return _isSqlAzure; + } + + public static boolean isSqlAzureDW() { + return _isSqlAzureDW; + } + + /** + * Determines the server's type. + * + * @param con + * connection to server + * @return void + * @throws SQLException + */ + private static void isSqlAzureOrAzureDW(Connection con) throws SQLException { + if (_determinedSqlAzureOrSqlServer) { + return; + } + + try (Statement stmt = con.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT CAST(SERVERPROPERTY('EngineEdition') as INT)")) { + rs.next(); + int engineEdition = rs.getInt(1); + _isSqlAzure = (engineEdition == ENGINE_EDITION_FOR_SQL_AZURE + || engineEdition == ENGINE_EDITION_FOR_SQL_AZURE_DW); + _isSqlAzureDW = (engineEdition == ENGINE_EDITION_FOR_SQL_AZURE_DW); + _determinedSqlAzureOrSqlServer = true; + } + } } From 7c804bcf2762413cde24b1d17e3c2ca12940abe3 Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Tue, 18 Dec 2018 11:39:32 -0800 Subject: [PATCH 35/51] Add ability to perform JUnit testing against Azure DW (#903) Improvement | Add ability to perform JUnit testing against Azure DW (#903) --- pom.xml | 3 + .../sqlserver/jdbc/TestResource.java | 11 ++- .../microsoft/sqlserver/jdbc/TestUtils.java | 1 + .../jdbc/bulkCopy/BulkCopyAllTypesTest.java | 17 ++-- .../microsoft/sqlserver/jdbc/bvt/BvtTest.java | 10 ++- .../jdbc/connection/ConnectionDriverTest.java | 18 ++--- .../connection/ConnectionWrapper43Test.java | 2 + .../jdbc/connection/DriverVersionTest.java | 2 + .../connection/NativeMSSQLDataSourceTest.java | 5 ++ .../jdbc/connection/PoolingTest.java | 8 +- .../jdbc/connection/SSLProtocolTest.java | 2 + .../jdbc/connection/WarningTest.java | 2 + .../DatabaseMetaDataTest.java | 12 ++- .../sqlserver/jdbc/fips/FipsEnvTest.java | 7 +- .../sqlserver/jdbc/fips/FipsTest.java | 8 +- .../BatchExecutionWithBulkCopyTest.java | 80 +++++++------------ .../BatchExecutionWithNullTest.java | 2 + .../preparedStatement/RegressionTest.java | 6 ++ .../jdbc/resultset/ResultSetTest.java | 8 +- .../trustmanager/CustomTrustManagerTest.java | 2 + .../sqlserver/jdbc/unit/UTF8SupportTest.java | 2 +- .../statement/BatchExecuteWithErrorsTest.java | 21 +++-- .../jdbc/unit/statement/PoolableTest.java | 2 + .../jdbc/unit/statement/RegressionTest.java | 5 +- .../jdbc/unit/statement/StatementTest.java | 58 +++++++------- .../jdbc/unit/statement/WrapperTest.java | 2 + .../testframework/AbstractParentWrapper.java | 1 + .../testframework/AbstractSQLGenerator.java | 1 + .../sqlserver/testframework/AbstractTest.java | 42 ++++++++++ .../sqlserver/testframework/DBConnection.java | 22 ----- .../sqlserver/testframework/DBSchema.java | 3 - .../sqlserver/testframework/DBTable.java | 14 ++-- 32 files changed, 222 insertions(+), 157 deletions(-) diff --git a/pom.xml b/pom.xml index 41792d7779..7289aed62b 100644 --- a/pom.xml +++ b/pom.xml @@ -41,6 +41,7 @@ UTF-8 1.3.1 5.3.1 + @@ -197,6 +198,7 @@ ${skipTestTag} + ${testGroup} @@ -266,6 +268,7 @@ ${skipTestTag} + ${testGroup} diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java b/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java index b043898784..60f97fbb69 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java @@ -57,6 +57,9 @@ protected Object[][] getContents() { {"R_conversionFailed", "Conversion failed when converting {0} to {1} data type"}, {"R_invalidQueryTimeout", "The query timeout value {0} is not valid."}, {"R_skipAzure", "Skipping test case on Azure SQL."}, + {"R_issueAzureDW", "This is a known failure in DW for now."}, + {"R_cursorAzureDW", "Cursor support is not implemented for Azure DW."}, + {"R_spatialDWNotSupported", "Geometry/Geography is not supported for DW."}, {"R_expectedExceptionNotThrown", "Expected exception is not thrown."}, {"R_errorNotCalled", "Error occurred is not called."}, {"R_errorCalled", "Error occurred is called."}, {"R_supportUnwrapping", "{0} supports unwrapping."}, @@ -127,6 +130,7 @@ protected Object[][] getContents() { {"R_errorFollowInserts", "Test error followed by inserts"}, {"R_errorFollow50280", "Test insert followed by non-fatal error (50280)"}, {"R_syntaxErrorDateConvert", "Syntax error converting date"}, + {"R_syntaxErrorDateConvertDW", "Conversion failed when converting date and/or time from character string."}, {"R_dateConvertError", "Conversion failed when converting date"}, {"R_incompatJDBC", "Aborting test case as JDBC version is not compatible."}, {"R_unexpectedException", "Unexpected exception occurred"}, {"R_addBatchFailed", "addBatch failed"}, @@ -154,7 +158,10 @@ protected Object[][] getContents() { {"R_switchFailed", "Switch case is not matched with data"}, {"R_resultsetNotInstance", "Result set is not instance of SQLServerResultSet"}, {"R_incorrectColumnNum", "Column name or number of supplied values does not match table definition."}, - {"R_incorrectColumnNumInsert", "There are fewer columns in the INSERT statement than values specified in the VALUES clause. The number of values in the VALUES clause must match the number of columns specified in the INSERT statement."}, + {"R_incorrectColumnNumInsert", + "There are fewer columns in the INSERT statement than values specified in the VALUES clause. The number of values in the VALUES clause must match the number of columns specified in the INSERT statement."}, + {"R_incorrectColumnNumInsertDW", + "Column name or number of supplied values does not match table definition."}, {"R_incorrectSyntaxTable", "Incorrect syntax near the keyword 'table'."}, - }; + {"R_incorrectSyntaxTableDW", "Parse error at line: 1, column: 106: Incorrect syntax near 'table'."},}; } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/TestUtils.java b/src/test/java/com/microsoft/sqlserver/jdbc/TestUtils.java index bb7d317d42..48b1f0d305 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/TestUtils.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/TestUtils.java @@ -64,6 +64,7 @@ public class TestUtils { // '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; diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyAllTypesTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyAllTypesTest.java index 2fdd955a3a..7b518741f5 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyAllTypesTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyAllTypesTest.java @@ -36,12 +36,17 @@ public class BulkCopyAllTypesTest extends AbstractTest { */ @Test public void testTVPResultSet() throws SQLException { - testBulkCopyResultSet(false, null, null); - testBulkCopyResultSet(true, null, null); - testBulkCopyResultSet(false, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); - testBulkCopyResultSet(false, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE); - testBulkCopyResultSet(false, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY); - testBulkCopyResultSet(false, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); + if (isSqlAzureDW()) { + testBulkCopyResultSet(false, null, null); + testBulkCopyResultSet(false, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); + } else { + testBulkCopyResultSet(false, null, null); + testBulkCopyResultSet(true, null, null); + testBulkCopyResultSet(false, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); + testBulkCopyResultSet(false, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE); + testBulkCopyResultSet(false, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY); + testBulkCopyResultSet(false, ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); + } } private void testBulkCopyResultSet(boolean setSelectMethod, Integer resultSetType, diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/bvt/BvtTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/bvt/BvtTest.java index 6dd760f28e..0ce9a723af 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/bvt/BvtTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/bvt/BvtTest.java @@ -7,6 +7,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assumptions.assumeFalse; import java.math.BigDecimal; import java.sql.DatabaseMetaData; @@ -17,6 +18,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; @@ -33,6 +35,7 @@ @RunWith(JUnitPlatform.class) @DisplayName("BVT Test") +@Tag("AzureDWTest") public class BvtTest extends AbstractTest { private static String driverNamePattern = "Microsoft JDBC Driver \\d.\\d for SQL Server"; static DBTable table1; @@ -145,6 +148,7 @@ public void testStmtForwardOnlyReadOnly() throws SQLException, ClassNotFoundExce */ @Test public void testStmtScrollInsensitiveReadOnly() throws SQLException, ClassNotFoundException { + assumeFalse(isSqlAzureDW(), TestResource.getResource("R_cursorAzureDW")); try (DBConnection conn = new DBConnection(connectionString); DBStatement stmt = conn.createStatement(DBResultSetTypes.TYPE_SCROLL_INSENSITIVE_CONCUR_READ_ONLY); DBResultSet rs = stmt.selectAll(table1)) { @@ -165,6 +169,7 @@ public void testStmtScrollInsensitiveReadOnly() throws SQLException, ClassNotFou */ @Test public void testStmtScrollSensitiveReadOnly() throws SQLException { + assumeFalse(isSqlAzureDW(), TestResource.getResource("R_cursorAzureDW")); try (DBConnection conn = new DBConnection(connectionString); DBStatement stmt = conn.createStatement(DBResultSetTypes.TYPE_SCROLL_SENSITIVE_CONCUR_READ_ONLY); DBResultSet rs = stmt.selectAll(table1)) { @@ -187,6 +192,7 @@ public void testStmtScrollSensitiveReadOnly() throws SQLException { */ @Test public void testStmtForwardOnlyUpdateable() throws SQLException { + assumeFalse(isSqlAzureDW(), TestResource.getResource("R_cursorAzureDW")); try (DBConnection conn = new DBConnection(connectionString); DBStatement stmt = conn.createStatement(DBResultSetTypes.TYPE_FORWARD_ONLY_CONCUR_UPDATABLE); DBResultSet rs = stmt.selectAll(table1)) { @@ -214,6 +220,7 @@ public void testStmtForwardOnlyUpdateable() throws SQLException { */ @Test public void testStmtScrollSensitiveUpdatable() throws SQLException { + assumeFalse(isSqlAzureDW(), TestResource.getResource("R_cursorAzureDW")); try (DBConnection conn = new DBConnection(connectionString); DBStatement stmt = conn.createStatement(DBResultSetTypes.TYPE_SCROLL_SENSITIVE_CONCUR_UPDATABLE); DBResultSet rs = stmt.selectAll(table1)) { @@ -237,7 +244,7 @@ public void testStmtScrollSensitiveUpdatable() throws SQLException { */ @Test public void testStmtSSScrollDynamicOptimisticCC() throws SQLException { - + assumeFalse(isSqlAzureDW(), TestResource.getResource("R_cursorAzureDW")); try (DBConnection conn = new DBConnection(connectionString); DBStatement stmt = conn.createStatement(DBResultSetTypes.TYPE_DYNAMIC_CONCUR_OPTIMISTIC); DBResultSet rs = stmt.selectAll(table1)) { @@ -410,6 +417,7 @@ public void testResultSetAndCloseStmt() throws SQLException { */ @Test public void testResultSetSelectMethod() throws SQLException { + assumeFalse(isSqlAzureDW(), TestResource.getResource("R_cursorAzureDW")); try (DBConnection conn = new DBConnection(connectionString + ";selectMethod=cursor;"); DBStatement stmt = conn.createStatement(); DBResultSet rs = stmt.selectAll(table1)) { rs.verify(table1); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/connection/ConnectionDriverTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/connection/ConnectionDriverTest.java index 00eeb986ab..2e256f7b1a 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/connection/ConnectionDriverTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/ConnectionDriverTest.java @@ -7,7 +7,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; -import static org.junit.jupiter.api.Assumptions.assumeTrue; +import static org.junit.jupiter.api.Assumptions.assumeFalse; import java.sql.Connection; import java.sql.DriverManager; @@ -42,10 +42,10 @@ import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; import com.microsoft.sqlserver.testframework.AbstractTest; -import com.microsoft.sqlserver.testframework.DBConnection; @RunWith(JUnitPlatform.class) +@Tag("AzureDWTest") public class ConnectionDriverTest extends AbstractTest { // If no retry is done, the function should at least exit in 5 seconds static int threshHoldForNoRetryInMilliseconds = 5000; @@ -165,8 +165,7 @@ public void connectionErrorOccurred(ConnectionEvent event) { */ @Test public void testConnectionEvents() throws SQLException { - assumeTrue(!DBConnection.isSqlAzure(DriverManager.getConnection(connectionString)), - TestResource.getResource("R_skipAzure")); + assumeFalse(isSqlAzure(), TestResource.getResource("R_skipAzure")); SQLServerConnectionPoolDataSource mds = new SQLServerConnectionPoolDataSource(); mds.setURL(connectionString); @@ -197,8 +196,7 @@ public void testConnectionEvents() throws SQLException { @Test public void testConnectionPoolGetTwice() throws SQLException { - assumeTrue(!DBConnection.isSqlAzure(DriverManager.getConnection(connectionString)), - TestResource.getResource("R_skipAzure")); + assumeFalse(isSqlAzure(), TestResource.getResource("R_skipAzure")); SQLServerConnectionPoolDataSource mds = new SQLServerConnectionPoolDataSource(); mds.setURL(connectionString); @@ -211,7 +209,7 @@ public void testConnectionPoolGetTwice() throws SQLException { try (Connection con = pooledConnection.getConnection(); Statement stmt = con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)) { // raise a non severe exception and make sure that the connection is not closed. - stmt.executeUpdate("RAISERROR ('foo', 3,1) WITH LOG"); + stmt.executeUpdate("RAISERROR ('foo', 3,1)"); // not a serious error there should not be any errors. assertTrue(!myE.errorOccurred, TestResource.getResource("R_errorCalled")); // check to make sure that connection is not closed. @@ -225,8 +223,7 @@ public void testConnectionPoolGetTwice() throws SQLException { @Test public void testConnectionClosed() throws SQLException { - assumeTrue(!DBConnection.isSqlAzure(DriverManager.getConnection(connectionString)), - TestResource.getResource("R_skipAzure")); + assumeFalse(isSqlAzure(), TestResource.getResource("R_skipAzure")); SQLServerDataSource mds = new SQLServerDataSource(); mds.setURL(connectionString); @@ -304,8 +301,7 @@ public void testNegativeTimeout() throws Exception { @Test public void testDeadConnection() throws SQLException { - assumeTrue(!DBConnection.isSqlAzure(DriverManager.getConnection(connectionString)), - TestResource.getResource("R_skipAzure")); + assumeFalse(isSqlAzure(), TestResource.getResource("R_skipAzure")); String tableName = RandomUtil.getIdentifier("ConnectionTestTable"); try (SQLServerConnection conn = (SQLServerConnection) DriverManager diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/connection/ConnectionWrapper43Test.java b/src/test/java/com/microsoft/sqlserver/jdbc/connection/ConnectionWrapper43Test.java index 6329f7e02a..8f8825b719 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/connection/ConnectionWrapper43Test.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/ConnectionWrapper43Test.java @@ -13,6 +13,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; @@ -27,6 +28,7 @@ * */ @RunWith(JUnitPlatform.class) +@Tag("AzureDWTest") public class ConnectionWrapper43Test extends AbstractTest { static Connection connection = null; double javaVersion = Double.parseDouble(System.getProperty("java.specification.version")); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/connection/DriverVersionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/connection/DriverVersionTest.java index b315a27784..adc87936d0 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/connection/DriverVersionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/DriverVersionTest.java @@ -10,6 +10,7 @@ import java.util.Arrays; import java.util.Random; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; @@ -27,6 +28,7 @@ * */ @RunWith(JUnitPlatform.class) +@Tag("AzureDWTest") public class DriverVersionTest extends AbstractTest { Random rand = new Random(); int major = rand.nextInt(256); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/connection/NativeMSSQLDataSourceTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/connection/NativeMSSQLDataSourceTest.java index edfc6e3a72..3d5d799f73 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/connection/NativeMSSQLDataSourceTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/NativeMSSQLDataSourceTest.java @@ -17,6 +17,7 @@ import java.sql.Connection; import java.sql.SQLException; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; @@ -33,6 +34,7 @@ public class NativeMSSQLDataSourceTest extends AbstractTest { @Test + @Tag("AzureDWTest") public void testNativeMSSQLDataSource() throws SQLException { SQLServerXADataSource ds = new SQLServerXADataSource(); ds.setLastUpdateCount(true); @@ -40,6 +42,7 @@ public void testNativeMSSQLDataSource() throws SQLException { } @Test + @Tag("AzureDWTest") public void testSerialization() throws IOException { try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ObjectOutput objectOutput = new ObjectOutputStream(outputStream)) { @@ -52,6 +55,7 @@ public void testSerialization() throws IOException { } @Test + @Tag("AzureDWTest") public void testDSNormal() throws ClassNotFoundException, IOException, SQLException { SQLServerDataSource ds = new SQLServerDataSource(); ds.setURL(connectionString); @@ -74,6 +78,7 @@ public void testDSTSPassword() throws ClassNotFoundException, IOException, SQLEx } @Test + @Tag("AzureDWTest") public void testInterfaceWrapping() throws ClassNotFoundException, SQLException { SQLServerDataSource ds = new SQLServerDataSource(); assertEquals(true, ds.isWrapperFor(Class.forName("com.microsoft.sqlserver.jdbc.ISQLServerDataSource"))); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/connection/PoolingTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/connection/PoolingTest.java index 9408f7d3de..cc56b26810 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/connection/PoolingTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/PoolingTest.java @@ -24,6 +24,7 @@ import org.apache.commons.dbcp2.BasicDataSource; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; @@ -35,7 +36,6 @@ import com.microsoft.sqlserver.jdbc.TestUtils; import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; import com.microsoft.sqlserver.testframework.AbstractTest; -import com.microsoft.sqlserver.testframework.DBConnection; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; @@ -45,14 +45,14 @@ * */ @RunWith(JUnitPlatform.class) +@Tag("AzureDWTest") public class PoolingTest extends AbstractTest { static String tempTableName = RandomUtil.getIdentifier("#poolingtest"); static String tableName = RandomUtil.getIdentifier("PoolingTestTable"); @Test public void testPooling() throws SQLException { - assumeTrue(!DBConnection.isSqlAzure(DriverManager.getConnection(connectionString)), - "Skipping test case on Azure SQL."); + assumeTrue(!isSqlAzure(), TestResource.getResource("R_skipAzure")); SQLServerXADataSource XADataSource1 = new SQLServerXADataSource(); XADataSource1.setURL(connectionString); @@ -102,7 +102,7 @@ public void testConnectionPoolConnFunctions() throws SQLException { String sql1 = "if exists (select * from dbo.sysobjects where name = '" + TestUtils.escapeSingleQuotes(tableName) + "' and type = 'U')\n" + "drop table " + AbstractSQLGenerator.escapeIdentifier(tableName) + "\n" + "create table " + AbstractSQLGenerator.escapeIdentifier(tableName) + "\n" + "(\n" - + "wibble_id int primary key not null,\n" + "counter int null\n" + ");"; + + "wibble_id int not null,\n" + "counter int null\n" + ");"; String sql2 = "if exists (select * from dbo.sysobjects where name = '" + TestUtils.escapeSingleQuotes(tableName) + "' and type = 'U')\n" + "drop table " + AbstractSQLGenerator.escapeIdentifier(tableName) + "\n"; diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/connection/SSLProtocolTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/connection/SSLProtocolTest.java index cafc5ebb49..79d8c28d2e 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/connection/SSLProtocolTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/SSLProtocolTest.java @@ -13,6 +13,7 @@ import java.sql.DriverManager; import java.text.MessageFormat; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; @@ -27,6 +28,7 @@ * Tests new connection property sslProtocol */ @RunWith(JUnitPlatform.class) +@Tag("AzureDWTest") public class SSLProtocolTest extends AbstractTest { /** diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/connection/WarningTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/connection/WarningTest.java index e9693b8c08..8ed2af2157 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/connection/WarningTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/WarningTest.java @@ -15,6 +15,7 @@ import java.util.List; import java.util.Properties; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; @@ -25,6 +26,7 @@ @RunWith(JUnitPlatform.class) +@Tag("AzureDWTest") public class WarningTest extends AbstractTest { @Test public void testWarnings() throws SQLException { diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java index 5bcbcfc672..ca86f0a6e4 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java @@ -30,6 +30,7 @@ import java.util.jar.Attributes; import java.util.jar.Manifest; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; @@ -54,6 +55,7 @@ public class DatabaseMetaDataTest extends AbstractTest { * @throws SQLException */ @Test + @Tag("AzureDWTest") public void testDatabaseMetaDataWrapper() throws SQLException { try (Connection con = DriverManager.getConnection(connectionString)) { DatabaseMetaData databaseMetaData = con.getMetaData(); @@ -76,6 +78,7 @@ public void testDatabaseMetaDataWrapper() throws SQLException { * IOExcption */ @Test + @Tag("AzureDWTest") public void testDriverVersion() throws SQLException, IOException { String manifestFile = TestUtils.getCurrentClassPath() + "META-INF/MANIFEST.MF"; manifestFile = manifestFile.replace("test-classes", "classes"); @@ -118,6 +121,7 @@ public void testDriverVersion() throws SQLException, IOException { * @throws SQLException */ @Test + @Tag("AzureDWTest") public void testGetURL() throws SQLException { try (SQLServerConnection conn = (SQLServerConnection) DriverManager.getConnection(connectionString)) { DatabaseMetaData databaseMetaData = conn.getMetaData(); @@ -139,7 +143,7 @@ public void testDBUserLogin() throws SQLException { try (SQLServerConnection conn = (SQLServerConnection) DriverManager.getConnection(connectionString)) { DatabaseMetaData databaseMetaData = conn.getMetaData(); - String connectionString = getConfiguredProperty("mssql_jdbc_test_connection_properties"); + String connectionString = getConnectionString(); connectionString = connectionString.toLowerCase(); @@ -174,6 +178,7 @@ public void testDBUserLogin() throws SQLException { * @throws SQLException */ @Test + @Tag("AzureDWTest") public void testDBSchema() throws SQLException { try (SQLServerConnection conn = (SQLServerConnection) DriverManager.getConnection(connectionString); ResultSet rs = conn.getMetaData().getSchemas()) { @@ -295,6 +300,7 @@ public void testDBSchemasForDashedCatalogNameWithPattern() throws SQLException { * @throws SQLException */ @Test + @Tag("AzureDWTest") /* * try (ResultSet rsCatalog = connection.getMetaData().getCatalogs(); ResultSet rs = connection.getMetaData() * .getTables(rsCatalog.getString("TABLE_CAT"), null, "%", new String[] {"TABLE"})) { @@ -334,6 +340,7 @@ public void testDBTables() throws SQLException { * @throws SQLException */ @Test + @Tag("AzureDWTest") public void testGetDBColumn() throws SQLException { try (Connection conn = DriverManager.getConnection(connectionString)) { @@ -423,6 +430,7 @@ public void testGetColumnPrivileges() throws SQLException { * @throws SQLException */ @Test + @Tag("AzureDWTest") public void testGetFunctionsWithWrongParams() throws SQLException { try (Connection conn = DriverManager.getConnection(connectionString)) { conn.getMetaData().getFunctions("", null, "xp_%"); @@ -436,6 +444,7 @@ public void testGetFunctionsWithWrongParams() throws SQLException { * @throws SQLException */ @Test + @Tag("AzureDWTest") public void testGetFunctions() throws SQLException { try (Connection conn = DriverManager.getConnection(connectionString); ResultSet rs = conn.getMetaData().getFunctions(null, null, "xp_%")) { @@ -462,6 +471,7 @@ public void testGetFunctions() throws SQLException { * @throws SQLException */ @Test + @Tag("AzureDWTest") public void testGetFunctionColumns() throws SQLException { try (Connection conn = DriverManager.getConnection(connectionString)) { diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/fips/FipsEnvTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/fips/FipsEnvTest.java index 32e40b5077..ad06973a6b 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/fips/FipsEnvTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/fips/FipsEnvTest.java @@ -16,12 +16,14 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; import com.microsoft.sqlserver.jdbc.TestResource; -import com.microsoft.sqlserver.jdbc.TestUtils;; +import com.microsoft.sqlserver.jdbc.TestUtils; +import com.microsoft.sqlserver.testframework.AbstractTest; /** @@ -30,7 +32,8 @@ * @since 6.1.2 */ @RunWith(JUnitPlatform.class) -public class FipsEnvTest { +@Tag("AzureDWTest") +public class FipsEnvTest extends AbstractTest { protected static Logger logger = Logger.getLogger("FipsEnvTest"); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/fips/FipsTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/fips/FipsTest.java index 787d75274b..b1e89e3975 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/fips/FipsTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/fips/FipsTest.java @@ -12,6 +12,7 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; @@ -19,7 +20,7 @@ import com.microsoft.sqlserver.jdbc.SQLServerDataSource; import com.microsoft.sqlserver.jdbc.StringUtils; import com.microsoft.sqlserver.jdbc.TestResource; -import com.microsoft.sqlserver.jdbc.TestUtils; +import com.microsoft.sqlserver.testframework.AbstractTest; import com.microsoft.sqlserver.testframework.PrepUtil;; @@ -27,14 +28,15 @@ * Test class for testing FIPS property settings. */ @RunWith(JUnitPlatform.class) -public class FipsTest { +@Tag("AzureDWTest") +public class FipsTest extends AbstractTest { private static String connectionString; private static String[] dataSourceProps; @BeforeAll public static void init() { - connectionString = TestUtils.getConfiguredProperty("mssql_jdbc_test_connection_properties"); + connectionString = getConnectionString(); dataSourceProps = getDataSourceProperties(); } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java index fdac78e14d..e3fbaa8b0c 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java @@ -3,6 +3,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assumptions.assumeFalse; import java.lang.reflect.Field; import java.lang.reflect.Method; @@ -19,6 +20,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; @@ -37,6 +39,7 @@ @RunWith(JUnitPlatform.class) +@Tag("AzureDWTest") public class BatchExecutionWithBulkCopyTest extends AbstractTest { static String tableName = RandomUtil.getIdentifier("BulkCopyParseTest"); @@ -161,6 +164,7 @@ public void testAll() throws Exception { @Test public void testAllcolumns() throws Exception { + assumeFalse(isSqlAzureDW(), TestResource.getResource("R_issueAzureDW")); String valid = "INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableName) + " values " + "(" + "?, " + "?, " + "?, " + "?, " + "?, " + "?, " + "?, " + "?, " + "? " + ")"; @@ -213,6 +217,7 @@ public void testAllcolumns() throws Exception { @Test public void testMixColumns() throws Exception { + assumeFalse(isSqlAzureDW(), TestResource.getResource("R_issueAzureDW")); String valid = "INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableName) + " (c1, c3, c5, c8) values " + "(" + "?, " + "?, " + "?, " + "? " + ")"; @@ -262,6 +267,7 @@ public void testMixColumns() throws Exception { @Test public void testNullOrEmptyColumns() throws Exception { + assumeFalse(isSqlAzureDW(), TestResource.getResource("R_issueAzureDW")); String valid = "INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableName) + " (c1, c2, c3, c4, c5, c6, c7) values " + "(" + "?, " + "?, " + "?, " + "?, " + "?, " + "?, " + "? " + ")"; @@ -307,46 +313,6 @@ public void testNullOrEmptyColumns() throws Exception { } } - // Non-parameterized queries are not supported anymore. - // @Test - public void testAllFilledColumns() throws Exception { - String valid = "INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableName) + " values " + "(" + "1234, " - + "false, " + "a, " + "null, " + "null, " + "123.45, " + "b, " + "varc, " + "sadf, " + ")"; - - try (Connection connection = DriverManager.getConnection(connectionString + ";useBulkCopyForBatchInsert=true;"); - SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) connection.prepareStatement(valid); - Statement stmt = (SQLServerStatement) connection.createStatement();) { - Field f1 = SQLServerConnection.class.getDeclaredField("isAzureDW"); - f1.setAccessible(true); - f1.set(connection, true); - - pstmt.addBatch(); - - pstmt.executeBatch(); - - try (ResultSet rs = stmt - .executeQuery("SELECT * FROM " + AbstractSQLGenerator.escapeIdentifier(tableName))) { - - Object[] expected = new Object[9]; - - expected[0] = 1234; - expected[1] = false; - expected[2] = "a"; - expected[3] = null; - expected[4] = null; - expected[5] = 123.45; - expected[6] = "b"; - expected[7] = "varc"; - expected[8] = "sadf"; - - rs.next(); - for (int i = 0; i < expected.length; i++) { - assertEquals(expected[i], rs.getObject(i + 1)); - } - } - } - } - @Test public void testSquareBracketAgainstDB() throws Exception { String valid = "insert into " + AbstractSQLGenerator.escapeIdentifier(squareBracketTableName) + " values (?)"; @@ -469,6 +435,7 @@ public void testColumnNameMixAgainstDB() throws Exception { @Test public void testAllColumnsLargeBatch() throws Exception { + assumeFalse(isSqlAzureDW(), TestResource.getResource("R_issueAzureDW")); String valid = "INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableName) + " values " + "(" + "?, " + "?, " + "?, " + "?, " + "?, " + "?, " + "?, " + "?, " + "? " + ")"; @@ -561,18 +528,24 @@ public void testIllegalNumberOfArgNoColumnList() throws Exception { pstmt.executeBatch(); throw new Exception(TestResource.getResource("R_expectedExceptionNotThrown")); } catch (BatchUpdateException e) { - assertEquals(TestResource.getResource("R_incorrectColumnNumInsert"), e.getMessage()); + if (isSqlAzureDW()) { + assertEquals(TestResource.getResource("R_incorrectColumnNumInsertDW"), e.getMessage()); + } else { + assertEquals(TestResource.getResource("R_incorrectColumnNumInsert"), e.getMessage()); + } } } @Test public void testNonParameterizedQuery() throws Exception { + assumeFalse(isSqlAzureDW(), TestResource.getResource("R_issueAzureDW")); String invalid = "insert into " + AbstractSQLGenerator.escapeIdentifier(tableName) + " values ((SELECT * from table where c1=?), ?,? ,?) "; try (Connection connection = DriverManager.getConnection(connectionString + ";useBulkCopyForBatchInsert=true;"); SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) connection.prepareStatement(invalid); Statement stmt = (SQLServerStatement) connection.createStatement();) { + Field f1 = SQLServerConnection.class.getDeclaredField("isAzureDW"); f1.setAccessible(true); f1.set(connection, true); @@ -586,7 +559,11 @@ public void testNonParameterizedQuery() throws Exception { pstmt.executeBatch(); throw new Exception(TestResource.getResource("R_expectedExceptionNotThrown")); } catch (BatchUpdateException e) { - assertEquals(TestResource.getResource("R_incorrectSyntaxTable"), e.getMessage()); + if (isSqlAzureDW()) { + assertEquals(TestResource.getResource("R_incorrectSyntaxTableDW"), e.getMessage()); + } else { + assertEquals(TestResource.getResource("R_incorrectSyntaxTable"), e.getMessage()); + } } invalid = "insert into " + AbstractSQLGenerator.escapeIdentifier(tableName) + " values ('?', ?,? ,?) "; @@ -612,12 +589,13 @@ public void testNonParameterizedQuery() throws Exception { @Test public void testNonSupportedColumns() throws Exception { + assumeFalse(isSqlAzureDW(), TestResource.getResource("R_spatialDWNotSupported")); String valid = "insert into " + AbstractSQLGenerator.escapeIdentifier(unsupportedTableName) + " values (?, ?, ?, ?)"; try (Connection connection = DriverManager.getConnection(connectionString + ";useBulkCopyForBatchInsert=true;"); SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) connection.prepareStatement(valid); - Statement stmt = (SQLServerStatement) connection.createStatement();) { + Statement stmt = (SQLServerStatement) connection.createStatement()) { Field f1 = SQLServerConnection.class.getDeclaredField("isAzureDW"); f1.setAccessible(true); f1.set(connection, true); @@ -653,16 +631,14 @@ public void testNonSupportedColumns() throws Exception { @BeforeEach public void testSetup() throws TestAbortedException, Exception { - try (Connection connection = DriverManager - .getConnection(connectionString + ";useBulkCopyForBatchInsert=true;")) { - try (Statement stmt = (SQLServerStatement) connection.createStatement()) { - TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(tableName), stmt); - String sql1 = "create table " + AbstractSQLGenerator.escapeIdentifier(tableName) + " " + "(" - + "c1 int DEFAULT 1234, " + "c2 bit, " + "c3 char DEFAULT NULL, " + "c4 date, " - + "c5 datetime2, " + "c6 float, " + "c7 nchar, " + "c8 varchar(20), " + "c9 varchar(max)" + ")"; + try (Connection connection = DriverManager.getConnection(connectionString + ";useBulkCopyForBatchInsert=true;"); + Statement stmt = (SQLServerStatement) connection.createStatement()) { + TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(tableName), stmt); + String sql1 = "create table " + AbstractSQLGenerator.escapeIdentifier(tableName) + " " + "(" + + "c1 int DEFAULT 1234, " + "c2 bit, " + "c3 char DEFAULT NULL, " + "c4 date, " + "c5 datetime2, " + + "c6 float, " + "c7 nchar, " + "c8 varchar(20), " + "c9 varchar(8000)" + ")"; - stmt.execute(sql1); - } + stmt.execute(sql1); } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithNullTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithNullTest.java index 8694c91b6d..9603963a4c 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithNullTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithNullTest.java @@ -16,6 +16,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; @@ -31,6 +32,7 @@ @RunWith(JUnitPlatform.class) +@Tag("AzureDWTest") public class BatchExecutionWithNullTest extends AbstractTest { static String tableName = RandomUtil.getIdentifier("esimple"); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/RegressionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/RegressionTest.java index 749e7a799e..58da912329 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/RegressionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/RegressionTest.java @@ -18,6 +18,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; @@ -60,6 +61,7 @@ public static void setupTest() throws SQLException { * @throws SQLException */ @Test + @Tag("AzureDWTest") public void createViewTest() throws SQLException { try (Connection con = DriverManager.getConnection(connectionString); PreparedStatement pstmt1 = con.prepareStatement( @@ -80,6 +82,7 @@ public void createViewTest() throws SQLException { * @throws SQLException */ @Test + @Tag("AzureDWTest") public void createSchemaTest() throws SQLException { try (Connection con = DriverManager.getConnection(connectionString); PreparedStatement pstmt1 = con @@ -100,6 +103,7 @@ public void createSchemaTest() throws SQLException { * @throws SQLException */ @Test + @Tag("AzureDWTest") public void createTableTest() throws SQLException { try (Connection con = DriverManager.getConnection(connectionString); PreparedStatement pstmt1 = con.prepareStatement( @@ -120,6 +124,7 @@ public void createTableTest() throws SQLException { * @throws SQLException */ @Test + @Tag("AzureDWTest") public void alterTableTest() throws SQLException { try (Connection con = DriverManager.getConnection(connectionString); PreparedStatement pstmt1 = con.prepareStatement( @@ -143,6 +148,7 @@ public void alterTableTest() throws SQLException { * @throws SQLException */ @Test + @Tag("AzureDWTest") public void grantTest() throws SQLException { try (Connection con = DriverManager.getConnection(connectionString); PreparedStatement pstmt1 = con.prepareStatement( diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java index 574a928dfd..366ca00ada 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java @@ -27,6 +27,7 @@ import java.util.TimeZone; import java.util.UUID; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; @@ -254,6 +255,7 @@ public void testJdbc41ResultSetMethods() throws SQLException { * @throws SQLException */ @Test + @Tag("AzureDWTest") public void testGetObjectAsLocalDateTime() throws SQLException { try (Connection con = DriverManager.getConnection(connectionString); Statement stmt = con.createStatement()) { TimeZone prevTimeZone = TimeZone.getDefault(); @@ -265,7 +267,7 @@ public void testGetObjectAsLocalDateTime() throws SQLException { final String testValueDateTime = testValueDate + "T" + testValueTime; stmt.executeUpdate("CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName) - + " (id INT PRIMARY KEY, dt2 DATETIME2)"); + + " (id INT, dt2 DATETIME2)"); stmt.executeUpdate("INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableName) + " (id, dt2) VALUES (1, '" + testValueDateTime + "')"); @@ -297,11 +299,12 @@ public void testGetObjectAsLocalDateTime() throws SQLException { * @throws SQLException */ @Test + @Tag("AzureDWTest") public void testResultSetWrapper() throws SQLException { try (Connection con = DriverManager.getConnection(connectionString); Statement stmt = con.createStatement()) { stmt.executeUpdate("create table " + AbstractSQLGenerator.escapeIdentifier(tableName) - + " (col1 int, col2 text, col3 int identity(1,1) primary key)"); + + " (col1 int, col2 varchar(8000), col3 int identity(1,1))"); try (ResultSet rs = stmt .executeQuery("select * from " + AbstractSQLGenerator.escapeIdentifier(tableName))) { @@ -322,6 +325,7 @@ public void testResultSetWrapper() throws SQLException { * @throws SQLException */ @Test + @Tag("AzureDWTest") public void testGetterOnNull() throws SQLException { try (Connection con = DriverManager.getConnection(connectionString); Statement stmt = con.createStatement(); ResultSet rs = stmt.executeQuery("select null")) { diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/ssl/trustmanager/CustomTrustManagerTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/ssl/trustmanager/CustomTrustManagerTest.java index b60ff3b1d4..ba8c761ffb 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/ssl/trustmanager/CustomTrustManagerTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/ssl/trustmanager/CustomTrustManagerTest.java @@ -11,6 +11,7 @@ import java.sql.DriverManager; import java.sql.SQLException; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; @@ -20,6 +21,7 @@ @RunWith(JUnitPlatform.class) +@Tag("AzureDWTest") public class CustomTrustManagerTest extends AbstractTest { /** diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/UTF8SupportTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/UTF8SupportTest.java index 2287856592..6088ff9ba8 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/UTF8SupportTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/UTF8SupportTest.java @@ -107,7 +107,7 @@ public void testVarchar() throws SQLException { @BeforeAll public static void setUp() throws ClassNotFoundException, SQLException { - connection = PrepUtil.getConnection(getConfiguredProperty("mssql_jdbc_test_connection_properties")); + connection = PrepUtil.getConnection(getConnectionString()); if (TestUtils.serverSupportsUTF8(connection)) { databaseName = RandomUtil.getIdentifier("UTF8Database"); tableName = RandomUtil.getIdentifier("RequestBoundaryTable"); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecuteWithErrorsTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecuteWithErrorsTest.java index 7d14a67ab0..9bcce59ded 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecuteWithErrorsTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/BatchExecuteWithErrorsTest.java @@ -22,6 +22,7 @@ import java.util.logging.Logger; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; @@ -32,7 +33,6 @@ import com.microsoft.sqlserver.jdbc.TestUtils; import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; import com.microsoft.sqlserver.testframework.AbstractTest; -import com.microsoft.sqlserver.testframework.DBConnection; /** @@ -40,6 +40,7 @@ * */ @RunWith(JUnitPlatform.class) +@Tag("AzureDWTest") public class BatchExecuteWithErrorsTest extends AbstractTest { public static final Logger log = Logger.getLogger("BatchExecuteWithErrors"); @@ -99,7 +100,7 @@ private void Repro47239Internal(String mode) throws Exception { String error; String severe; try (Connection con = DriverManager.getConnection(connectionString)) { - if (DBConnection.isSqlAzure(con)) { + if (isSqlAzure()) { // SQL Azure will throw exception for "raiserror WITH LOG", so the following RAISERROR statements have // not // "with log" option @@ -144,7 +145,7 @@ private void Repro47239Internal(String mode) throws Exception { TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(tableName), stmt); } catch (Exception ignored) {} stmt.executeUpdate("create table " + AbstractSQLGenerator.escapeIdentifier(tableName) - + " (c1_int int, c2_varchar varchar(20), c3_date datetime, c4_int int identity(1,1) primary key)"); + + " (c1_int int, c2_varchar varchar(20), c3_date datetime, c4_int int identity(1,1))"); // Regular Statement batch update expectedUpdateCounts = new int[] {1, -2, 1, -2, 1, -2}; @@ -241,7 +242,13 @@ private void Repro47239Internal(String mode) throws Exception { try { stmt.executeBatch(); } catch (BatchUpdateException bue) { - assertThat(bue.getMessage(), containsString(TestResource.getResource("R_syntaxErrorDateConvert"))); + if (isSqlAzureDW()) { + assertThat(bue.getMessage(), + containsString(TestResource.getResource("R_syntaxErrorDateConvertDW"))); + } else { + assertThat(bue.getMessage(), + containsString(TestResource.getResource("R_syntaxErrorDateConvert"))); + } // CTestLog.CompareStartsWith(bue.getMessage(), "Syntax error converting date", "Transaction // rollback with conversion error threw wrong // BatchUpdateException"); @@ -264,7 +271,7 @@ private void Repro47239Internal(String mode) throws Exception { // errors" // It is worthwhile to still execute the first 5 test scenarios of this test case, in order to have best // test coverage. - if (!DBConnection.isSqlAzure(conn)) { + if (!isSqlAzure()) { // Test Severe (connection-closing) errors stmt.addBatch(error); stmt.addBatch(insertStmt); @@ -315,7 +322,7 @@ private void Repro47239largeInternal(String mode) throws Exception { final String warning; final String error; final String severe; - if (DBConnection.isSqlAzure(con)) { + if (isSqlAzure()) { // SQL Azure will throw exception for "raiserror WITH LOG", so the following RAISERROR statements have // not // "with log" option @@ -472,7 +479,7 @@ private void Repro47239largeInternal(String mode) throws Exception { // It is worthwhile to still execute the first 5 test scenarios of this test case, in order to have // best // test coverage. - if (!DBConnection.isSqlAzure(DriverManager.getConnection(connectionString))) { + if (!isSqlAzure()) { // Test Severe (connection-closing) errors stmt.addBatch(error); stmt.addBatch(insertStmt); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PoolableTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PoolableTest.java index d053fd8f97..ba8a683cc3 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PoolableTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PoolableTest.java @@ -16,6 +16,7 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; @@ -33,6 +34,7 @@ * */ @RunWith(JUnitPlatform.class) +@Tag("AzureDWTest") public class PoolableTest extends AbstractTest { /** diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/RegressionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/RegressionTest.java index 2daad278f2..27ff3ea442 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/RegressionTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/RegressionTest.java @@ -28,7 +28,6 @@ import com.microsoft.sqlserver.jdbc.TestUtils; import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; import com.microsoft.sqlserver.testframework.AbstractTest; -import com.microsoft.sqlserver.testframework.DBConnection; @RunWith(JUnitPlatform.class) @@ -67,7 +66,7 @@ public void testServerCursorPStmt() throws SQLException { // create stored proc String storedProcString; - if (DBConnection.isSqlAzure(con)) { + if (isSqlAzure()) { // On SQL Azure, 'SELECT INTO' is not supported. So do not use it. storedProcString = "CREATE PROCEDURE " + AbstractSQLGenerator.escapeIdentifier(procName) + " @param varchar(3) AS SELECT col3 FROM " + AbstractSQLGenerator.escapeIdentifier(tableName) @@ -115,7 +114,7 @@ public void testSelectIntoUpdateCount() throws SQLException { try (SQLServerConnection con = (SQLServerConnection) DriverManager.getConnection(connectionString)) { // Azure does not do SELECT INTO - if (!DBConnection.isSqlAzure(con)) { + if (!isSqlAzure()) { tableName = RandomUtil.getIdentifier("[#SourceTableForSelectInto]]"); try (Statement stmt = con.createStatement()) { diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java index b43dd312e7..bb9833a40f 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java @@ -37,6 +37,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; @@ -81,7 +82,7 @@ public void init() throws Exception { TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(tableName), stmt); } catch (SQLException e) {} stmt.executeUpdate("CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName) - + " (col1 INT PRIMARY KEY, col2 VARCHAR(" + TEST_STRING.length() + "))"); + + " (col1 INT, col2 VARCHAR(" + TEST_STRING.length() + "))"); for (int i = 0; i < NUM_TABLE_ROWS; i++) stmt.executeUpdate("INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableName) + " (col1, col2) VALUES (" + i + ", '" + TEST_STRING + "')"); @@ -328,7 +329,7 @@ public void testCancelBlockedResponse() throws Exception { // we have to set its isolation level to REPEATABLE_READ (or SERIALIZABLE) in SQL Azure. // // Reference: http://msdn.microsoft.com/en-us/library/ee336245.aspx#isolevels - if (DBConnection.isSqlAzure(con)) { + if (isSqlAzure()) { con.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ); } @@ -419,7 +420,7 @@ public void testCancelBlockedResponsePS() throws Exception { // we have to set its isolation level to REPEATABLE_READ (or SERIALIZABLE) in SQL Azure. // // Reference: http://msdn.microsoft.com/en-us/library/ee336245.aspx#isolevels - if (DBConnection.isSqlAzure(con)) { + if (isSqlAzure()) { con.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ); } @@ -515,7 +516,7 @@ public void testCancelBlockedCursoredResponse() throws Exception { // we have to set its isolation level to REPEATABLE_READ (or SERIALIZABLE) in SQL Azure. // // Reference: http://msdn.microsoft.com/en-us/library/ee336245.aspx#isolevels - if (DBConnection.isSqlAzure(con)) { + if (isSqlAzure()) { con.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ); } @@ -888,6 +889,7 @@ public void testCloseOnCompletion() throws Exception { } @Nested + @Tag("AzureDWTest") public class TCStatement { private final String table1Name = RandomUtil.getIdentifier("TCStatement1"); private final String table2Name = RandomUtil.getIdentifier("TCStatement2"); @@ -966,10 +968,8 @@ public void testConsecutiveQueries() throws Exception { TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(table2Name), stmt); } catch (SQLException e) {} - stmt.executeUpdate("CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(table1Name) - + " (col1 INT PRIMARY KEY)"); - stmt.executeUpdate("CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(table2Name) - + " (col1 INT PRIMARY KEY)"); + stmt.executeUpdate("CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(table1Name) + " (col1 INT)"); + stmt.executeUpdate("CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(table2Name) + " (col1 INT)"); try (ResultSet rs = stmt.executeQuery( "SELECT * FROM " + AbstractSQLGenerator.escapeIdentifier(table2Name))) {} catch (Exception e) { @@ -1359,7 +1359,7 @@ public void testResultSetParams() throws Exception { Statement stmt = con.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE)) { stmt.executeUpdate("create table " + AbstractSQLGenerator.escapeIdentifier(tableName) - + " (col1 int, col2 text, col3 int identity(1,1) primary key)"); + + " (col1 int, col2 text, col3 int identity(1,1))"); stmt.executeUpdate( "Insert into " + AbstractSQLGenerator.escapeIdentifier(tableName) + " values(0, 'hello')"); stmt.executeUpdate( @@ -1394,7 +1394,7 @@ public void testResultSetNullParams() throws Exception { Statement stmt = con.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE)) { stmt.executeUpdate("create table " + AbstractSQLGenerator.escapeIdentifier(tableName) - + " (col1 int, col2 text, col3 int identity(1,1) primary key)"); + + " (col1 int, col2 text, col3 int identity(1,1))"); stmt.executeUpdate( "Insert into " + AbstractSQLGenerator.escapeIdentifier(tableName) + " values(0, 'hello')"); stmt.executeUpdate( @@ -1427,8 +1427,7 @@ public void testFailedToResumeTransaction() throws Exception { try (Connection con = DriverManager.getConnection(connectionString); Statement stmt = con.createStatement()) { - stmt.executeUpdate( - "create table " + AbstractSQLGenerator.escapeIdentifier(tableName) + " (col1 int primary key)"); + stmt.executeUpdate("create table " + AbstractSQLGenerator.escapeIdentifier(tableName) + " (col1 int)"); stmt.executeUpdate("Insert into " + AbstractSQLGenerator.escapeIdentifier(tableName) + " values(0)"); stmt.executeUpdate("Insert into " + AbstractSQLGenerator.escapeIdentifier(tableName) + " values(1)"); stmt.executeUpdate("Insert into " + AbstractSQLGenerator.escapeIdentifier(tableName) + " values(2)"); @@ -1460,7 +1459,7 @@ public void testResultSetErrors() throws Exception { Statement stmt = con.createStatement(ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE)) { stmt.executeUpdate("create table " + AbstractSQLGenerator.escapeIdentifier(tableName) - + " (col1 int, col2 text, col3 int identity(1,1) primary key)"); + + " (col1 int, col2 text, col3 int identity(1,1))"); stmt.executeUpdate( "Insert into " + AbstractSQLGenerator.escapeIdentifier(tableName) + " values(0, 'hello')"); stmt.executeUpdate( @@ -1491,8 +1490,7 @@ public void testRowError() throws Exception { try (Connection con = DriverManager.getConnection(connectionString); Statement stmt = con.createStatement()) { - stmt.executeUpdate( - "create table " + AbstractSQLGenerator.escapeIdentifier(tableName) + " (col1 int primary key)"); + stmt.executeUpdate("create table " + AbstractSQLGenerator.escapeIdentifier(tableName) + " (col1 int)"); stmt.executeUpdate("insert into " + AbstractSQLGenerator.escapeIdentifier(tableName) + " values(0)"); stmt.executeUpdate("insert into " + AbstractSQLGenerator.escapeIdentifier(tableName) + " values(1)"); stmt.executeUpdate("insert into " + AbstractSQLGenerator.escapeIdentifier(tableName) + " values(2)"); @@ -1608,7 +1606,7 @@ private Connection createConnectionAndPopulateData() throws Exception { Statement stmt = con.createStatement(); stmt.executeUpdate("CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName) - + "(col1_int int PRIMARY KEY IDENTITY(1,1), col2_varchar varchar(200), col3_varchar varchar(20) SPARSE NULL, col4_smallint smallint SPARSE NULL, col5_xml XML COLUMN_SET FOR ALL_SPARSE_COLUMNS, col6_nvarcharMax NVARCHAR(MAX), col7_varcharMax VARCHAR(MAX))"); + + "(col1_int int IDENTITY(1,1), col2_varchar varchar(200), col3_varchar varchar(20) SPARSE NULL, col4_smallint smallint SPARSE NULL, col5_xml XML COLUMN_SET FOR ALL_SPARSE_COLUMNS, col6_nvarcharMax NVARCHAR(MAX), col7_varcharMax VARCHAR(MAX))"); stmt.executeUpdate("INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableName) + " DEFAULT VALUES"); assertTrue(con != null, "connection is null"); @@ -1797,7 +1795,7 @@ public void testNBCRowForAllNulls() throws Exception { } catch (SQLException e) {} String createTableQuery = "CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName) - + "(col1 int PRIMARY KEY IDENTITY(1,1)"; + + "(col1 int IDENTITY(1,1)"; int noOfColumns = 128; for (int i = 2; i <= noOfColumns; i++) { @@ -1852,7 +1850,7 @@ public void testNBCROWWithRandomAccess() throws Exception { // construct a query to create a table with 100 columns String createTableQuery = "CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName) - + "(col1 int PRIMARY KEY IDENTITY(1,1)"; + + "(col1 int IDENTITY(1,1)"; for (int i = 2; i <= noOfColumns; i++) { createTableQuery = createTableQuery + ", col" + i + " int"; @@ -1933,6 +1931,7 @@ public void testNBCROWWithRandomAccess() throws Exception { } @Nested + @Tag("AzureDWTest") public class TCStatementIsClosed { @Test public void testActiveStatement() throws Exception { @@ -1996,6 +1995,7 @@ public void testClosedConnection() throws Exception { } @Nested + @Tag("AzureDWTest") public class TCResultSetIsClosed { /** @@ -2125,14 +2125,14 @@ public void setup() throws Exception { } catch (SQLException e) { throw new SQLException(e); } - stmt.executeUpdate("CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName) - + " (col1 INT PRIMARY KEY)"); + stmt.executeUpdate( + "CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName) + " (col1 INT )"); for (int i = 0; i < NUM_ROWS; i++) stmt.executeUpdate("INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableName) + " (col1) VALUES (" + i + ")"); stmt.executeUpdate("CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(table2Name) - + " (NAME VARCHAR(100), col2 int identity(1,1) primary key)"); + + " (NAME VARCHAR(100), col2 int identity(1,1) )"); stmt.executeUpdate("INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(table2Name) + " (NAME) VALUES ('BLAH')"); stmt.executeUpdate("INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(table2Name) @@ -2273,8 +2273,8 @@ public void setup() throws Exception { } catch (SQLException e) { System.out.println(e.toString()); } - stmt.executeUpdate("CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName) - + " (col1 INT primary key)"); + stmt.executeUpdate( + "CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName) + " (col1 INT )"); for (int i = 0; i < NUM_ROWS; i++) stmt.executeUpdate("INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableName) + " (col1) VALUES (" + i + ")"); @@ -2283,7 +2283,7 @@ public void setup() throws Exception { // support // sp_addmessage. try (Connection dbConn = DriverManager.getConnection(connectionString)) { - if (DBConnection.isSqlAzure(dbConn)) { + if (isSqlAzure()) { log.fine( "Because SQL Azure does not support sp_addmessage, 'EXEC sp_addmessage ...' is skipped."); } else { @@ -2335,7 +2335,7 @@ public void testUpdateCountAfterRaiseError() throws Exception { } catch (SQLException e) { String expectedMessage; // SQL Azure does not support sp_addmessage, so the user-defined message cannot be added. - if (DBConnection.isSqlAzure(con)) // SQL Azure + if (isSqlAzure()) // SQL Azure { expectedMessage = errorMessage50001InSqlAzure; } else // SQL Server @@ -2383,7 +2383,7 @@ public void testUpdateCountAfterErrorInTriggerLastUpdateCountFalse() throws Exce } catch (SQLException e) { String expectedMessage; // SQL Azure does not support sp_addmessage, so the user-defined message cannot be added. - if (DBConnection.isSqlAzure(con)) // SQL Azure + if (isSqlAzure()) // SQL Azure { expectedMessage = errorMessage50001InSqlAzure; } else // SQL Server @@ -2426,7 +2426,7 @@ public void testUpdateCountAfterErrorInTriggerLastUpdateCountTrue() throws Excep } catch (SQLException e) { String expectedMessage; // SQL Azure does not support sp_addmessage, so the user-defined message cannot be added. - if (DBConnection.isSqlAzure(con)) // SQL Azure + if (isSqlAzure()) // SQL Azure { expectedMessage = errorMessage50001InSqlAzure; } else // SQL Server @@ -2488,8 +2488,8 @@ public void setup() throws Exception { } catch (Exception e) { throw new SQLException(TestResource.getResource("R_unexpectedException"), e); } - stmt.executeUpdate("CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName) - + " (col1 INT primary key)"); + stmt.executeUpdate( + "CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName) + " (col1 INT )"); for (int i = 0; i < NUM_ROWS; i++) stmt.executeUpdate("INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableName) + " (col1) VALUES (" + i + ")"); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/WrapperTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/WrapperTest.java index b85ea6e2c0..28ce53717a 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/WrapperTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/WrapperTest.java @@ -14,6 +14,7 @@ import java.sql.Statement; import java.text.MessageFormat; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; @@ -31,6 +32,7 @@ * */ @RunWith(JUnitPlatform.class) +@Tag("AzureDWTest") public class WrapperTest extends AbstractTest { /** diff --git a/src/test/java/com/microsoft/sqlserver/testframework/AbstractParentWrapper.java b/src/test/java/com/microsoft/sqlserver/testframework/AbstractParentWrapper.java index de22f6c483..47427a38c1 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/AbstractParentWrapper.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/AbstractParentWrapper.java @@ -11,6 +11,7 @@ */ public abstract class AbstractParentWrapper { static final int ENGINE_EDITION_FOR_SQL_AZURE = 5; + static final int ENGINE_EDITION_FOR_SQL_AZURE_DW = 6; AbstractParentWrapper parent = null; Object internal = null; diff --git a/src/test/java/com/microsoft/sqlserver/testframework/AbstractSQLGenerator.java b/src/test/java/com/microsoft/sqlserver/testframework/AbstractSQLGenerator.java index a60c53abe1..042ca2700d 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/AbstractSQLGenerator.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/AbstractSQLGenerator.java @@ -23,6 +23,7 @@ public abstract class AbstractSQLGenerator {// implements ISQLGenerator { protected static final String DEFAULT = "DEFAULT"; protected static final String COMMA = ","; protected static final String QUESTION_MARK = "?"; + protected static final String SEMI_COLON = ";"; // FIXME: Find good word for '. Better replaced by wrapIdentifier. protected static final String TICK = "'"; diff --git a/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java b/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java index 0e58d31513..e06e2e405f 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java @@ -6,6 +6,9 @@ package com.microsoft.sqlserver.testframework; import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; import java.util.Properties; import java.util.logging.ConsoleHandler; import java.util.logging.FileHandler; @@ -52,6 +55,12 @@ public abstract class AbstractTest { protected static Properties info = new Properties(); + private final static int ENGINE_EDITION_FOR_SQL_AZURE = 5; + private final static int ENGINE_EDITION_FOR_SQL_AZURE_DW = 6; + private static boolean _determinedSqlAzureOrSqlServer = false; + private static boolean _isSqlAzure = false; + private static boolean _isSqlAzureDW = false; + /** * This will take care of all initialization before running the Test Suite. * @@ -83,6 +92,7 @@ public static void setup() throws Exception { try { Assertions.assertNotNull(connectionString, "Connection String should not be null"); connection = PrepUtil.getConnection(connectionString, info); + isSqlAzureOrAzureDW(connection); } catch (Exception e) { throw e; } @@ -173,4 +183,36 @@ public static void invokeLogging() { System.err.println("Some how could not invoke logging: " + e.getMessage()); } } + + public static boolean isSqlAzure() { + return _isSqlAzure; + } + + public static boolean isSqlAzureDW() { + return _isSqlAzureDW; + } + + /** + * Determines the server's type. + * + * @param con + * connection to server + * @return void + * @throws SQLException + */ + private static void isSqlAzureOrAzureDW(Connection con) throws SQLException { + if (_determinedSqlAzureOrSqlServer) { + return; + } + + try (Statement stmt = con.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT CAST(SERVERPROPERTY('EngineEdition') as INT)")) { + rs.next(); + int engineEdition = rs.getInt(1); + _isSqlAzure = (engineEdition == ENGINE_EDITION_FOR_SQL_AZURE + || engineEdition == ENGINE_EDITION_FOR_SQL_AZURE_DW); + _isSqlAzureDW = (engineEdition == ENGINE_EDITION_FOR_SQL_AZURE_DW); + _determinedSqlAzureOrSqlServer = true; + } + } } diff --git a/src/test/java/com/microsoft/sqlserver/testframework/DBConnection.java b/src/test/java/com/microsoft/sqlserver/testframework/DBConnection.java index 03e70faa76..e3631238f4 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/DBConnection.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/DBConnection.java @@ -7,7 +7,6 @@ import static org.junit.jupiter.api.Assertions.fail; -import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.SQLException; @@ -20,7 +19,6 @@ */ public class DBConnection extends AbstractParentWrapper implements AutoCloseable { private double serverversion = 0; - // TODO: add Isolation Level // TODO: add auto commit // TODO: add connection Savepoint and rollback @@ -164,26 +162,6 @@ public DatabaseMetaData getMetaData() throws SQLException { return product; } - /** - * - * @param con - * @return - * @throws SQLException - */ - public static boolean isSqlAzure(Connection con) throws SQLException { - boolean isSqlAzure = false; - - ResultSet rs = con.createStatement().executeQuery("SELECT CAST(SERVERPROPERTY('EngineEdition') as INT)"); - rs.next(); - int engineEdition = rs.getInt(1); - rs.close(); - if (ENGINE_EDITION_FOR_SQL_AZURE == engineEdition) { - isSqlAzure = true; - } - - return isSqlAzure; - } - /** * @param string * @return diff --git a/src/test/java/com/microsoft/sqlserver/testframework/DBSchema.java b/src/test/java/com/microsoft/sqlserver/testframework/DBSchema.java index 24b2050921..6ef27f69fb 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/DBSchema.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/DBSchema.java @@ -81,9 +81,6 @@ public class DBSchema { // Binary sqlTypes.add(new SqlBinary()); sqlTypes.add(new SqlVarBinary()); - - // TODO: - // Other types } } diff --git a/src/test/java/com/microsoft/sqlserver/testframework/DBTable.java b/src/test/java/com/microsoft/sqlserver/testframework/DBTable.java index 3699acb3de..9eeaff785e 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/DBTable.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/DBTable.java @@ -361,15 +361,12 @@ public DBTable cloneSchema() { String populateTableSql() { StringJoiner sb = new StringJoiner(SPACE_CHAR); - sb.add("INSERT"); - sb.add("INTO"); - sb.add(escapedTableName); - sb.add("VALUES"); - for (int i = 0; i < totalRows; i++) { - if (i != 0) { - sb.add(COMMA); - } + sb.add("INSERT"); + sb.add("INTO"); + sb.add(escapedTableName); + sb.add("VALUES"); + sb.add(OPEN_BRACKET); for (int colNum = 0; colNum < totalColumns; colNum++) { @@ -387,6 +384,7 @@ String populateTableSql() { } } sb.add(CLOSE_BRACKET); + sb.add(SEMI_COLON); } return (sb.toString()); From 238b66f0c5b2799e5aa2aa4c0faebd93b0f36ad4 Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Tue, 18 Dec 2018 14:01:19 -0800 Subject: [PATCH 36/51] more cleanup on assertEqual checks --- .../callablestatement/CallableMixedTest.java | 14 +++++++------- .../sqlserver/jdbc/datatypes/SparseTest.java | 6 +++--- .../jdbc/unit/serial/DTOSerialTest.java | 18 +++++++++--------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java index 38e31b7145..4d3e255a13 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java @@ -1,6 +1,6 @@ package com.microsoft.sqlserver.jdbc.callablestatement; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; import java.sql.CallableStatement; @@ -60,9 +60,9 @@ public void datatypestest() throws Exception { try (ResultSet rs = cstmt.executeQuery()) { rs.next(); - assertTrue(rs.getInt(1) == 0); + assertEquals(rs.getInt(1), 0); - assertTrue(cstmt.getInt((int) 5) == -5372); + assertEquals(cstmt.getInt((int) 5), -5372); } // do nothing and reexecute @@ -70,17 +70,17 @@ public void datatypestest() throws Exception { // get the param without getting the resultset try (ResultSet rs = cstmt.executeQuery()) { - assertTrue(cstmt.getInt((int) 1) == -2147483648); + assertEquals(cstmt.getInt((int) 1), -2147483648); } try (ResultSet rs = cstmt.executeQuery()) { rs.next(); - assertTrue(rs.getInt(1) == 0); + assertEquals(rs.getInt(1), 0); - assertTrue(cstmt.getInt((int) 1) == -2147483648); + assertEquals(cstmt.getInt((int) 1), -2147483648); - assertTrue(cstmt.getInt((int) 5) == -5372); + assertEquals(cstmt.getInt((int) 5), -5372); } } } finally { diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SparseTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SparseTest.java index e63901d144..b411637874 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SparseTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SparseTest.java @@ -48,9 +48,9 @@ public void testSparse() throws Exception { try (ResultSet rs = stmt.executeQuery("Select * from " + escapedTableName)) { rs.next(); - assertEquals(rs.getString("col1023"), "yo", "Wrong value returned"); - assertEquals(rs.getInt("col1"), 1, "Wrong value returned"); - assertEquals(rs.getBytes("col2")[0], 0x45, "Wrong value returned"); + assertEquals(rs.getString("col1023"), "yo"); + assertEquals(rs.getInt("col1"), 1); + assertEquals(rs.getBytes("col2")[0], 0x45); } } finally { try (Statement stmt = conn.createStatement()) { diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java index 4acd7ec838..a7e05c371b 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java @@ -1,6 +1,6 @@ package com.microsoft.sqlserver.jdbc.unit.serial; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; import java.io.ByteArrayInputStream; @@ -75,9 +75,9 @@ public void testESerial() throws Exception { try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()))) { SQLServerException ex = (SQLServerException) in.readObject(); - assertTrue(currException.toString().equals(ex.toString())); - assertTrue(currException.getSQLState().equals(ex.getSQLState())); - assertTrue(currException.getErrorCode() == ex.getErrorCode()); + assertEquals(currException.toString(), ex.toString()); + assertEquals(currException.getSQLState(), ex.getSQLState()); + assertEquals(currException.getErrorCode(), ex.getErrorCode()); } } } @@ -179,19 +179,19 @@ private static void verifyDTOEqual(DateTimeOffset initial, DateTimeOffset hydrat int hydratedOffset = hydrated.getMinutesOffset(); // check hydrated string - assertTrue(initialStr.equals(hydratedStr)); + assertEquals(initialStr, hydratedStr); // check formatted date string String formattedDate = sdf.format(sdf.parse(initialStr)); - assertTrue(formattedDate.equals(dateString)); + assertEquals(formattedDate, dateString); // check hydrated datetimeoffset - assertTrue(initial.equals(hydrated)); + assertEquals(initial, hydrated); // check hydrated timestamp - assertTrue(originalTS.equals(hydratedTS)); + assertEquals(originalTS, hydratedTS); // check hydrated offset - assertTrue(initiallOffset == hydratedOffset); + assertEquals(initiallOffset, hydratedOffset); } } From dc1a721607ed936140de87b03926c0aeed0393fc Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Tue, 18 Dec 2018 14:28:40 -0800 Subject: [PATCH 37/51] more cleanup on assertEqual checks --- .../microsoft/sqlserver/jdbc/TestUtils.java | 49 ------ .../jdbc/datatypes/BigIntegerTest.java | 150 +++++++++--------- .../jdbc/datatypes/KatmaiDataTypesTest.java | 8 +- .../sqlserver/jdbc/datatypes/SparseTest.java | 5 +- 4 files changed, 81 insertions(+), 131 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/TestUtils.java b/src/test/java/com/microsoft/sqlserver/jdbc/TestUtils.java index 7b68407754..48b1f0d305 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/TestUtils.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/TestUtils.java @@ -67,13 +67,6 @@ public class TestUtils { // private static SqlType types = null; private static ArrayList types = null; - private final static int ENGINE_EDITION_FOR_SQL_AZURE = 5; - private final static int ENGINE_EDITION_FOR_SQL_AZURE_DW = 6; - - // whether we determined if the target server is SQL Azure or DW - private static boolean _determinedSqlAzureOrSqlServer = false; - private static boolean _isSqlAzure = false; - private static boolean _isSqlAzureDW = false; /** * Returns serverType @@ -674,46 +667,4 @@ public static boolean supportJDBC43(Connection con) throws SQLException { public static String escapeSingleQuotes(String name) { return name.replace("'", "''"); } - - /** - * Returns if connected to SQL Azure - * @param con - * connection to server - * @return boolean - * @throws SQLException - */ - public static boolean isSqlAzure(Connection con) throws SQLException { - if (_determinedSqlAzureOrSqlServer) { - return _isSqlAzure; - } - - try (ResultSet rs = con.createStatement().executeQuery("SELECT CAST(SERVERPROPERTY('EngineEdition') as INT)")) { - rs.next(); - int engineEdition = rs.getInt(1); - _determinedSqlAzureOrSqlServer = true; - _isSqlAzure = true; - return (engineEdition == ENGINE_EDITION_FOR_SQL_AZURE || engineEdition == ENGINE_EDITION_FOR_SQL_AZURE_DW); - } - } - - /** - * Returns if connected to SQL Azure DW - * @param con - * connection to server - * @return boolean - * @throws SQLException - */ - public static boolean isSqlAzureDW(Connection con) throws SQLException { - if (_determinedSqlAzureOrSqlServer) { - return _isSqlAzureDW; - } - - try (ResultSet rs = con.createStatement().executeQuery("SELECT CAST(SERVERPROPERTY('EngineEdition') as INT)")) { - rs.next(); - int engineEdition = rs.getInt(1); - _determinedSqlAzureOrSqlServer = true; - _isSqlAzureDW = true; - return (engineEdition == ENGINE_EDITION_FOR_SQL_AZURE_DW); - } - } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/BigIntegerTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/BigIntegerTest.java index 670515ec07..73b6cb0739 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/BigIntegerTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/BigIntegerTest.java @@ -1,5 +1,8 @@ package com.microsoft.sqlserver.jdbc.datatypes; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.math.BigInteger; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; @@ -10,9 +13,6 @@ import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; -import static org.junit.jupiter.api.Assertions.assertEquals; - -import java.math.BigInteger; import com.microsoft.sqlserver.jdbc.RandomUtil; import com.microsoft.sqlserver.jdbc.TestUtils; @@ -37,58 +37,63 @@ enum TestType { @Test public void testJDBC41BigInteger() throws Exception { - try (Connection conn = DriverManager.getConnection(connectionString); Statement stmt = conn.createStatement()) { - - // Create the test table - TestUtils.dropTableIfExists(escapedTableName, stmt); - - String query = "create table " + escapedTableName - + " (col1 varchar(100), col2 bigint, col3 real, col4 float, " - + "col5 numeric(38,0), col6 int, col7 smallint, col8 char(100), col9 varchar(max), " - + "id int IDENTITY primary key)"; - stmt.executeUpdate(query); - - try (PreparedStatement pstmt = conn.prepareStatement("INSERT INTO " + escapedTableName - + " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?) SELECT * FROM " + escapedTableName + " where id = ?")) { - - // test that the driver converts the BigInteger values greater than LONG.MAX_VALUE and lesser than - // LONG.MIN_VALUE correctly - // A random value that is bigger than LONG.MAX_VALUE - BigInteger bigIntPos = new BigInteger("922337203685477580776767676"); - // A random value that is smaller than LONG.MIN_VALUE - BigInteger bigIntNeg = new BigInteger("-922337203685477580776767676"); - - // Test the setObject method for different types of BigInteger values. Since BigInteger is mapped to - // JDBC - // BIGINT, the max and min limits for - int row = 1; - testSetObject(escapedTableName, BigInteger.valueOf(Long.MAX_VALUE), row++, pstmt, - TestType.SETOBJECT_WITHTYPE); - - testSetObject(escapedTableName, BigInteger.valueOf(Long.MIN_VALUE), row++, pstmt, - TestType.SETOBJECT_WITHTYPE); - testSetObject(escapedTableName, BigInteger.valueOf(10), row++, pstmt, TestType.SETOBJECT_WITHTYPE); - testSetObject(escapedTableName, BigInteger.valueOf(-10), row++, pstmt, TestType.SETOBJECT_WITHTYPE); - testSetObject(escapedTableName, BigInteger.ZERO, row++, pstmt, TestType.SETOBJECT_WITHTYPE); - testSetObject(escapedTableName, bigIntPos, row++, pstmt, TestType.SETOBJECT_WITHTYPE); - testSetObject(escapedTableName, bigIntNeg, row++, pstmt, TestType.SETOBJECT_WITHTYPE); - - // Test setObject method with SQL TYPE parameter - testSetObject(escapedTableName, BigInteger.valueOf(Long.MAX_VALUE), row++, pstmt, - TestType.SETOBJECT_WITHOUTTYPE); - testSetObject(escapedTableName, BigInteger.valueOf(Long.MIN_VALUE), row++, pstmt, - TestType.SETOBJECT_WITHOUTTYPE); - testSetObject(escapedTableName, BigInteger.valueOf(1000), row++, pstmt, TestType.SETOBJECT_WITHOUTTYPE); - testSetObject(escapedTableName, BigInteger.valueOf(-1000), row++, pstmt, - TestType.SETOBJECT_WITHOUTTYPE); - testSetObject(escapedTableName, BigInteger.ZERO, row++, pstmt, TestType.SETOBJECT_WITHOUTTYPE); - testSetObject(escapedTableName, bigIntPos, row++, pstmt, TestType.SETOBJECT_WITHOUTTYPE); - testSetObject(escapedTableName, bigIntNeg, row++, pstmt, TestType.SETOBJECT_WITHOUTTYPE); - - // Test setNull - testSetObject(escapedTableName, bigIntNeg, row++, pstmt, TestType.SETNULL); + try (Connection conn = DriverManager.getConnection(connectionString)) { + try (Statement stmt = conn.createStatement()) { + // Create the test table TestUtils.dropTableIfExists(escapedTableName, stmt); + + String query = "create table " + escapedTableName + + " (col1 varchar(100), col2 bigint, col3 real, col4 float, " + + "col5 numeric(38,0), col6 int, col7 smallint, col8 char(100), col9 varchar(max), " + + "id int IDENTITY primary key)"; + stmt.executeUpdate(query); + + try (PreparedStatement pstmt = conn.prepareStatement("INSERT INTO " + escapedTableName + + " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?) SELECT * FROM " + escapedTableName + " where id = ?")) { + + // test that the driver converts the BigInteger values greater than LONG.MAX_VALUE and lesser than + // LONG.MIN_VALUE correctly + // A random value that is bigger than LONG.MAX_VALUE + BigInteger bigIntPos = new BigInteger("922337203685477580776767676"); + // A random value that is smaller than LONG.MIN_VALUE + BigInteger bigIntNeg = new BigInteger("-922337203685477580776767676"); + + // Test the setObject method for different types of BigInteger values. Since BigInteger is mapped to + // JDBC + // BIGINT, the max and min limits for + int row = 1; + testSetObject(escapedTableName, BigInteger.valueOf(Long.MAX_VALUE), row++, pstmt, + TestType.SETOBJECT_WITHTYPE); + + testSetObject(escapedTableName, BigInteger.valueOf(Long.MIN_VALUE), row++, pstmt, + TestType.SETOBJECT_WITHTYPE); + testSetObject(escapedTableName, BigInteger.valueOf(10), row++, pstmt, TestType.SETOBJECT_WITHTYPE); + testSetObject(escapedTableName, BigInteger.valueOf(-10), row++, pstmt, TestType.SETOBJECT_WITHTYPE); + testSetObject(escapedTableName, BigInteger.ZERO, row++, pstmt, TestType.SETOBJECT_WITHTYPE); + testSetObject(escapedTableName, bigIntPos, row++, pstmt, TestType.SETOBJECT_WITHTYPE); + testSetObject(escapedTableName, bigIntNeg, row++, pstmt, TestType.SETOBJECT_WITHTYPE); + + // Test setObject method with SQL TYPE parameter + testSetObject(escapedTableName, BigInteger.valueOf(Long.MAX_VALUE), row++, pstmt, + TestType.SETOBJECT_WITHOUTTYPE); + testSetObject(escapedTableName, BigInteger.valueOf(Long.MIN_VALUE), row++, pstmt, + TestType.SETOBJECT_WITHOUTTYPE); + testSetObject(escapedTableName, BigInteger.valueOf(1000), row++, pstmt, + TestType.SETOBJECT_WITHOUTTYPE); + testSetObject(escapedTableName, BigInteger.valueOf(-1000), row++, pstmt, + TestType.SETOBJECT_WITHOUTTYPE); + testSetObject(escapedTableName, BigInteger.ZERO, row++, pstmt, TestType.SETOBJECT_WITHOUTTYPE); + testSetObject(escapedTableName, bigIntPos, row++, pstmt, TestType.SETOBJECT_WITHOUTTYPE); + testSetObject(escapedTableName, bigIntNeg, row++, pstmt, TestType.SETOBJECT_WITHOUTTYPE); + + // Test setNull + testSetObject(escapedTableName, bigIntNeg, row++, pstmt, TestType.SETNULL); + } + } finally { + try (Statement stmt = conn.createStatement()) { + TestUtils.dropTableIfExists(escapedTableName, stmt); + } } } } @@ -102,7 +107,7 @@ static void testSetObject(String tableName, BigInteger obj, int id, PreparedStat } else if (TestType.SETNULL == testType) { callSetNull(obj, pstmt); } else - assertEquals(true, false, "Invalid test type"); + return; // The id column pstmt.setObject(10, id); @@ -116,7 +121,7 @@ static void testSetObject(String tableName, BigInteger obj, int id, PreparedStat for (int i = 1; 9 >= i; ++i) { // Get the data first before calling rs.wasNull() rs.getString(i); - assertEquals(rs.wasNull(), true, "setNull mismatch"); + assertEquals(rs.wasNull(), true); } return; } @@ -126,39 +131,36 @@ static void testSetObject(String tableName, BigInteger obj, int id, PreparedStat // For the BigInteger values greater/less than Long limits test only the long data type. // This test is here just to make sure the driver does not do anything wired when the value is // bigger/smaller than JDBC BIGINT - assertEquals(rs.getString(1), Long.valueOf(obj.longValue()).toString(), - "getString(greater/less than Long limits) mismatch"); + assertEquals(rs.getString(1), Long.valueOf(obj.longValue()).toString()); assertEquals(rs.getLong(2), obj.longValue(), "getLong(greater/less than Long limits) mismatch"); // As CHAR is fixed length, rs.getString() returns a string of the size allocated in the database. // Need to trim it for comparison. - assertEquals(rs.getString(8).trim(), Long.valueOf(obj.longValue()).toString(), - "getString(greater/less than Long limits (char)) mismatch"); + assertEquals(rs.getString(8).trim(), Long.valueOf(obj.longValue()).toString()); - assertEquals(rs.getString(9), Long.valueOf(obj.longValue()).toString(), - "getString(greater/less than Long limits (varchar(max)))) mismatch"); + assertEquals(rs.getString(9), Long.valueOf(obj.longValue()).toString()); } else { - assertEquals(rs.getString(1), obj.toString(), "getString mismatch"); - assertEquals(rs.getLong(2), obj.longValue(), "getLong mismatch"); - assertEquals(rs.getFloat(3), obj.floatValue(), "getFloat mismatch"); - assertEquals(rs.getDouble(4), obj.doubleValue(), "getDouble(float) mismatch"); - assertEquals(rs.getDouble(5), obj.doubleValue(), "getDouble(numeric) mismatch"); + assertEquals(rs.getString(1), obj.toString()); + assertEquals(rs.getLong(2), obj.longValue()); + assertEquals(rs.getFloat(3), obj.floatValue()); + assertEquals(rs.getDouble(4), obj.doubleValue()); + assertEquals(rs.getDouble(5), obj.doubleValue()); if (obj.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) >= 0) { - assertEquals(rs.getInt(6), Integer.MAX_VALUE, "getInt(numeric) mismatch"); + assertEquals(rs.getInt(6), Integer.MAX_VALUE); } else if (obj.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) <= 0) { - assertEquals(rs.getInt(6), Integer.MIN_VALUE, "getInt(numeric) mismatch"); + assertEquals(rs.getInt(6), Integer.MIN_VALUE); } else { - assertEquals(rs.getInt(6), obj.intValue(), "getInt(numeric) mismatch"); + assertEquals(rs.getInt(6), obj.intValue()); } if (obj.compareTo(BigInteger.valueOf(Short.MAX_VALUE)) >= 0) { - assertEquals(rs.getShort(7), Short.MAX_VALUE, "getShort(numeric) mismatch"); + assertEquals(rs.getShort(7), Short.MAX_VALUE); } else if (obj.compareTo(BigInteger.valueOf(Short.MIN_VALUE)) <= 0) { - assertEquals(rs.getShort(7), Short.MIN_VALUE, "getShort(numeric) mismatch"); + assertEquals(rs.getShort(7), Short.MIN_VALUE); } else { - assertEquals(rs.getShort(7), obj.shortValue(), "getShort(numeric) mismatch"); + assertEquals(rs.getShort(7), obj.shortValue()); } - assertEquals(rs.getString(8).trim(), obj.toString(), "getString(char) mismatch"); - assertEquals(rs.getString(9), obj.toString(), "getString(varchar(max)) mismatch"); + assertEquals(rs.getString(8).trim(), obj.toString()); + assertEquals(rs.getString(9), obj.toString()); } } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java index 993be3b9e3..2dcc707a7f 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java @@ -202,7 +202,7 @@ void verifyRSGetters(Connection conn) throws Exception { } void verifyRSUpdaters(Connection conn) throws Exception { - assumeTrue(!TestUtils.isSqlAzureDW(conn), TestResource.getResource("R_skipAzure")); + assumeTrue(!isSqlAzureDW(), TestResource.getResource("R_skipAzure")); try (Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)) { @@ -223,7 +223,7 @@ void verifyRSUpdaters(Connection conn) throws Exception { } void verifySetters(Connection conn) throws Exception { - assumeTrue(!TestUtils.isSqlAzureDW(conn), TestResource.getResource("R_skipAzure")); + assumeTrue(!isSqlAzureDW(), TestResource.getResource("R_skipAzure")); try (Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)) { TestUtils.dropTableIfExists(escapedTableName, stmt); @@ -1180,7 +1180,7 @@ public void testDoubleRounding() throws Exception { try (Connection conn = DriverManager.getConnection(connectionString)) { // create a table with a datetimeoffset column and insert a value in it - assumeTrue(!TestUtils.isSqlAzureDW(conn), TestResource.getResource("R_skipAzure")); + assumeTrue(!isSqlAzureDW(), TestResource.getResource("R_skipAzure")); String sql; try (Statement stmt = conn.createStatement()) { @@ -1575,7 +1575,7 @@ public void testUpdateMisc() throws Exception { try (SQLServerConnection conn = (SQLServerConnection) DriverManager .getConnection(connectionString + ";sendTimeAsDatetime=true")) { - assumeTrue(!TestUtils.isSqlAzureDW(conn), TestResource.getResource("R_skipAzure")); + assumeTrue(!isSqlAzureDW(), TestResource.getResource("R_skipAzure")); try (Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)) { TestUtils.dropTableIfExists(escapedTableName, stmt); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SparseTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SparseTest.java index b411637874..6c5103c4a8 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SparseTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SparseTest.java @@ -1,6 +1,5 @@ package com.microsoft.sqlserver.jdbc.datatypes; -import static org.junit.Assert.fail; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assumptions.assumeTrue; @@ -28,7 +27,7 @@ public class SparseTest extends AbstractTest { @Test public void testSparse() throws Exception { try (Connection conn = DriverManager.getConnection(connectionString)) { - assumeTrue(!TestUtils.isSqlAzureDW(conn), TestResource.getResource("R_skipAzure")); + assumeTrue(!isSqlAzureDW(), TestResource.getResource("R_skipAzure")); try (Statement stmt = conn.createStatement()) { // Create the test table @@ -55,8 +54,6 @@ public void testSparse() throws Exception { } finally { try (Statement stmt = conn.createStatement()) { TestUtils.dropTableIfExists(escapedTableName, stmt); - } catch (Exception e) { - fail(TestResource.getResource("R_createDropTableFailed") + e.toString()); } } } From 6eac22ce6c9929c2383ecdf30e9424a2bbb8e21f Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Tue, 18 Dec 2018 22:38:39 -0800 Subject: [PATCH 38/51] cosmetic changes from review --- .../callablestatement/CallableMixedTest.java | 5 -- .../jdbc/datatypes/BigIntegerTest.java | 50 ++++++++++++------- .../jdbc/datatypes/KatmaiDataTypesTest.java | 44 ++++++++-------- 3 files changed, 52 insertions(+), 47 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java index 4d3e255a13..c8ab9c9377 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java @@ -59,9 +59,7 @@ public void datatypestest() throws Exception { // get results and a value try (ResultSet rs = cstmt.executeQuery()) { rs.next(); - assertEquals(rs.getInt(1), 0); - assertEquals(cstmt.getInt((int) 5), -5372); } @@ -75,11 +73,8 @@ public void datatypestest() throws Exception { try (ResultSet rs = cstmt.executeQuery()) { rs.next(); - assertEquals(rs.getInt(1), 0); - assertEquals(cstmt.getInt((int) 1), -2147483648); - assertEquals(cstmt.getInt((int) 5), -5372); } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/BigIntegerTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/BigIntegerTest.java index 73b6cb0739..ee02e1a7e3 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/BigIntegerTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/BigIntegerTest.java @@ -21,22 +21,25 @@ /* - * This test is for testing the setObject methods for the new data type mappings in JDBC 4.1 for java.math.BigInteger + * This test is for testing the setObject methods for the data type mappings in JDBC for java.math.BigInteger */ @RunWith(JUnitPlatform.class) public class BigIntegerTest extends AbstractTest { enum TestType { - SETOBJECT_WITHTYPE, // This is to test conversions in Table B-5 - SETOBJECT_WITHOUTTYPE, // This is to test conversions in Table B-4 + SETOBJECT_WITHTYPE, // This is to test conversions with type + SETOBJECT_WITHOUTTYPE, // This is to test conversions without type SETNULL // This is to test setNull method }; final static String tableName = RandomUtil.getIdentifier("BigIntegerTestTable"); final static String escapedTableName = AbstractSQLGenerator.escapeIdentifier(tableName); + /* + * Test BigInteger conversions + */ @Test - public void testJDBC41BigInteger() throws Exception { + public void testBigInteger() throws Exception { try (Connection conn = DriverManager.getConnection(connectionString)) { try (Statement stmt = conn.createStatement()) { @@ -52,16 +55,16 @@ public void testJDBC41BigInteger() throws Exception { try (PreparedStatement pstmt = conn.prepareStatement("INSERT INTO " + escapedTableName + " VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?) SELECT * FROM " + escapedTableName + " where id = ?")) { - // test that the driver converts the BigInteger values greater than LONG.MAX_VALUE and lesser than - // LONG.MIN_VALUE correctly + /* + * test conversion of BigInteger values greater than LONG.MAX_VALUE and lesser than LONG.MIN_VALUE + */ + // A random value that is bigger than LONG.MAX_VALUE BigInteger bigIntPos = new BigInteger("922337203685477580776767676"); // A random value that is smaller than LONG.MIN_VALUE BigInteger bigIntNeg = new BigInteger("-922337203685477580776767676"); - // Test the setObject method for different types of BigInteger values. Since BigInteger is mapped to - // JDBC - // BIGINT, the max and min limits for + // Test the setObject method for different types of BigInteger values int row = 1; testSetObject(escapedTableName, BigInteger.valueOf(Long.MAX_VALUE), row++, pstmt, TestType.SETOBJECT_WITHTYPE); @@ -128,15 +131,18 @@ static void testSetObject(String tableName, BigInteger obj, int id, PreparedStat if ((0 > obj.compareTo(BigInteger.valueOf(Long.MIN_VALUE))) || (0 < obj.compareTo(BigInteger.valueOf(Long.MAX_VALUE)))) { - // For the BigInteger values greater/less than Long limits test only the long data type. - // This test is here just to make sure the driver does not do anything wired when the value is - // bigger/smaller than JDBC BIGINT + /* + * For the BigInteger values greater/less than Long limits test only the long data type. This tests when + * the value is bigger/smaller than JDBC BIGINT + */ assertEquals(rs.getString(1), Long.valueOf(obj.longValue()).toString()); - assertEquals(rs.getLong(2), obj.longValue(), "getLong(greater/less than Long limits) mismatch"); - // As CHAR is fixed length, rs.getString() returns a string of the size allocated in the database. - // Need to trim it for comparison. - assertEquals(rs.getString(8).trim(), Long.valueOf(obj.longValue()).toString()); + assertEquals(rs.getLong(2), obj.longValue()); + /* + * As CHAR is fixed length, rs.getString() returns a string of the size allocated in the database. Need + * to trim it for comparison. + */ + assertEquals(rs.getString(8).trim(), Long.valueOf(obj.longValue()).toString()); assertEquals(rs.getString(9), Long.valueOf(obj.longValue()).toString()); } else { assertEquals(rs.getString(1), obj.toString()); @@ -144,6 +150,7 @@ static void testSetObject(String tableName, BigInteger obj, int id, PreparedStat assertEquals(rs.getFloat(3), obj.floatValue()); assertEquals(rs.getDouble(4), obj.doubleValue()); assertEquals(rs.getDouble(5), obj.doubleValue()); + if (obj.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) >= 0) { assertEquals(rs.getInt(6), Integer.MAX_VALUE); } else if (obj.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) <= 0) { @@ -151,6 +158,7 @@ static void testSetObject(String tableName, BigInteger obj, int id, PreparedStat } else { assertEquals(rs.getInt(6), obj.intValue()); } + if (obj.compareTo(BigInteger.valueOf(Short.MAX_VALUE)) >= 0) { assertEquals(rs.getShort(7), Short.MAX_VALUE); } else if (obj.compareTo(BigInteger.valueOf(Short.MIN_VALUE)) <= 0) { @@ -171,6 +179,7 @@ static void callSetObjectWithType(BigInteger obj, PreparedStatement pstmt) throw pstmt.setObject(3, obj, java.sql.Types.FLOAT); pstmt.setObject(4, obj, java.sql.Types.DOUBLE); pstmt.setObject(5, obj, java.sql.Types.NUMERIC); + // Use Integer/Short limits instead of Long limits for the int/smallint column if (obj.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) >= 0) { pstmt.setObject(6, BigInteger.valueOf(Integer.MAX_VALUE), java.sql.Types.INTEGER); @@ -179,6 +188,7 @@ static void callSetObjectWithType(BigInteger obj, PreparedStatement pstmt) throw } else { pstmt.setObject(6, obj, java.sql.Types.INTEGER); } + if (obj.compareTo(BigInteger.valueOf(Short.MAX_VALUE)) >= 0) { pstmt.setObject(7, BigInteger.valueOf(Short.MAX_VALUE), java.sql.Types.SMALLINT); } else if (obj.compareTo(BigInteger.valueOf(Short.MIN_VALUE)) <= 0) { @@ -191,13 +201,16 @@ static void callSetObjectWithType(BigInteger obj, PreparedStatement pstmt) throw } static void callSetObjectWithoutType(BigInteger obj, PreparedStatement pstmt) throws SQLException { - // Cannot send a long value to a column of type int/smallint (even if the long value is small enough to fit in - // those types) + /* + * Cannot send a long value to a column of type int/smallint (even if the long value is small enough to fit in + * those types) + */ pstmt.setObject(1, obj); pstmt.setObject(2, obj); pstmt.setObject(3, obj); pstmt.setObject(4, obj); pstmt.setObject(5, obj); + // Use Integer/Short limits instead of Long limits for the int/smallint column if (obj.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) >= 0) { pstmt.setObject(6, BigInteger.valueOf(Integer.MAX_VALUE)); @@ -206,6 +219,7 @@ static void callSetObjectWithoutType(BigInteger obj, PreparedStatement pstmt) th } else { pstmt.setObject(6, obj); } + if (obj.compareTo(BigInteger.valueOf(Short.MAX_VALUE)) >= 0) { pstmt.setObject(7, BigInteger.valueOf(Short.MAX_VALUE)); } else if (obj.compareTo(BigInteger.valueOf(Short.MIN_VALUE)) <= 0) { diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java index 2dcc707a7f..5b57ac5719 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java @@ -45,7 +45,7 @@ /* * This test suite tests all kinds of temporal data types for Katmai or later versions. It has tests for - * date/time/datetime2/datetimeoffset data types. Also includes tests for new data type mappings in JDBC 4.1. + * date/time/datetime2/datetimeoffset data types. Also includes tests for data type mappings. */ @RunWith(JUnitPlatform.class) public class KatmaiDataTypesTest extends AbstractTest { @@ -138,29 +138,25 @@ void verifyResultSetMetaData(Connection conn) throws Exception { try (Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT TOP 0 " + sqlCastExpression())) { - try { - ResultSetMetaData metadata = rs.getMetaData(); - - assertEquals(metadata.getColumnType(1), sqlType.jdbcType, - "getColumnType() of " + sqlCastExpression()); - assertEquals(metadata.getColumnTypeName(1), sqlType.toString(), - "getColumnTypeName() of " + sqlCastExpression()); - assertEquals(metadata.getPrecision(1), precision, "getPrecision() of " + sqlCastExpression()); - - // Display size of temporal types is the precision per JDBC spec - assertEquals(metadata.getColumnDisplaySize(1), precision, - "getColumnDisplaySize() of " + sqlCastExpression()); - // Scale is interpreted as number of fractional seconds precision - assertEquals(metadata.getScale(1), scale, "getScale() of " + sqlCastExpression()); - assertEquals(metadata.getColumnClassName(1), sqlType.className, - "getColumnClassName() of " + sqlCastExpression()); - // Katmai temporal types are not signed - assertEquals(metadata.isSigned(1), false, "isSigned() of " + sqlCastExpression()); - - // Katmai temporal types are searchable (i.e. usable in a WHERE clause) - assertEquals(metadata.isSearchable(1), true, "isSearchable() of " + sqlCastExpression()); - - } finally {} + ResultSetMetaData metadata = rs.getMetaData(); + + assertEquals(metadata.getColumnType(1), sqlType.jdbcType, "getColumnType() of " + sqlCastExpression()); + assertEquals(metadata.getColumnTypeName(1), sqlType.toString(), + "getColumnTypeName() of " + sqlCastExpression()); + assertEquals(metadata.getPrecision(1), precision, "getPrecision() of " + sqlCastExpression()); + + // Display size of temporal types is the precision per JDBC spec + assertEquals(metadata.getColumnDisplaySize(1), precision, + "getColumnDisplaySize() of " + sqlCastExpression()); + // Scale is interpreted as number of fractional seconds precision + assertEquals(metadata.getScale(1), scale, "getScale() of " + sqlCastExpression()); + assertEquals(metadata.getColumnClassName(1), sqlType.className, + "getColumnClassName() of " + sqlCastExpression()); + // Katmai temporal types are not signed + assertEquals(metadata.isSigned(1), false, "isSigned() of " + sqlCastExpression()); + + // Katmai temporal types are searchable (i.e. usable in a WHERE clause) + assertEquals(metadata.isSearchable(1), true, "isSearchable() of " + sqlCastExpression()); } } From 72956ed72497d62d21f27046513544bd8a8bee8c Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Wed, 19 Dec 2018 00:42:22 -0800 Subject: [PATCH 39/51] updated all assertEquals params to proper expected, actual order --- .../callablestatement/CallableMixedTest.java | 12 +- .../jdbc/datatypes/BigIntegerTest.java | 36 +-- .../jdbc/datatypes/KatmaiDataTypesTest.java | 230 +++++++++--------- .../sqlserver/jdbc/datatypes/SparseTest.java | 6 +- .../jdbc/unit/serial/DTOSerialTest.java | 11 +- 5 files changed, 144 insertions(+), 151 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java index c8ab9c9377..697e345cc5 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java @@ -59,8 +59,8 @@ public void datatypestest() throws Exception { // get results and a value try (ResultSet rs = cstmt.executeQuery()) { rs.next(); - assertEquals(rs.getInt(1), 0); - assertEquals(cstmt.getInt((int) 5), -5372); + assertEquals(0, rs.getInt(1)); + assertEquals(-5372, cstmt.getInt((int) 5)); } // do nothing and reexecute @@ -68,14 +68,14 @@ public void datatypestest() throws Exception { // get the param without getting the resultset try (ResultSet rs = cstmt.executeQuery()) { - assertEquals(cstmt.getInt((int) 1), -2147483648); + assertEquals(-2147483648, cstmt.getInt((int) 1)); } try (ResultSet rs = cstmt.executeQuery()) { rs.next(); - assertEquals(rs.getInt(1), 0); - assertEquals(cstmt.getInt((int) 1), -2147483648); - assertEquals(cstmt.getInt((int) 5), -5372); + assertEquals(0, rs.getInt(1)); + assertEquals(-2147483648, cstmt.getInt((int) 1)); + assertEquals(-5372, cstmt.getInt((int) 5)); } } } finally { diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/BigIntegerTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/BigIntegerTest.java index ee02e1a7e3..4a63ec8944 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/BigIntegerTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/BigIntegerTest.java @@ -124,7 +124,7 @@ static void testSetObject(String tableName, BigInteger obj, int id, PreparedStat for (int i = 1; 9 >= i; ++i) { // Get the data first before calling rs.wasNull() rs.getString(i); - assertEquals(rs.wasNull(), true); + assertEquals(true, rs.wasNull()); } return; } @@ -135,40 +135,40 @@ static void testSetObject(String tableName, BigInteger obj, int id, PreparedStat * For the BigInteger values greater/less than Long limits test only the long data type. This tests when * the value is bigger/smaller than JDBC BIGINT */ - assertEquals(rs.getString(1), Long.valueOf(obj.longValue()).toString()); - assertEquals(rs.getLong(2), obj.longValue()); + assertEquals(Long.valueOf(obj.longValue()).toString(), rs.getString(1)); + assertEquals(obj.longValue(), rs.getLong(2)); /* * As CHAR is fixed length, rs.getString() returns a string of the size allocated in the database. Need * to trim it for comparison. */ - assertEquals(rs.getString(8).trim(), Long.valueOf(obj.longValue()).toString()); - assertEquals(rs.getString(9), Long.valueOf(obj.longValue()).toString()); + assertEquals(Long.valueOf(obj.longValue()).toString(), rs.getString(8).trim()); + assertEquals(Long.valueOf(obj.longValue()).toString(), rs.getString(9)); } else { - assertEquals(rs.getString(1), obj.toString()); - assertEquals(rs.getLong(2), obj.longValue()); - assertEquals(rs.getFloat(3), obj.floatValue()); - assertEquals(rs.getDouble(4), obj.doubleValue()); - assertEquals(rs.getDouble(5), obj.doubleValue()); + assertEquals(obj.toString(), rs.getString(1)); + assertEquals(obj.longValue(), rs.getLong(2)); + assertEquals(obj.floatValue(), rs.getFloat(3)); + assertEquals(obj.doubleValue(), rs.getDouble(4)); + assertEquals(obj.doubleValue(), rs.getDouble(5)); if (obj.compareTo(BigInteger.valueOf(Integer.MAX_VALUE)) >= 0) { - assertEquals(rs.getInt(6), Integer.MAX_VALUE); + assertEquals(Integer.MAX_VALUE, rs.getInt(6)); } else if (obj.compareTo(BigInteger.valueOf(Integer.MIN_VALUE)) <= 0) { - assertEquals(rs.getInt(6), Integer.MIN_VALUE); + assertEquals(Integer.MIN_VALUE, rs.getInt(6)); } else { - assertEquals(rs.getInt(6), obj.intValue()); + assertEquals(obj.intValue(), rs.getInt(6)); } if (obj.compareTo(BigInteger.valueOf(Short.MAX_VALUE)) >= 0) { - assertEquals(rs.getShort(7), Short.MAX_VALUE); + assertEquals(Short.MAX_VALUE, rs.getShort(7)); } else if (obj.compareTo(BigInteger.valueOf(Short.MIN_VALUE)) <= 0) { - assertEquals(rs.getShort(7), Short.MIN_VALUE); + assertEquals(Short.MIN_VALUE, rs.getShort(7)); } else { - assertEquals(rs.getShort(7), obj.shortValue()); + assertEquals(obj.shortValue(), rs.getShort(7)); } - assertEquals(rs.getString(8).trim(), obj.toString()); - assertEquals(rs.getString(9), obj.toString()); + assertEquals(obj.toString(), rs.getString(8).trim()); + assertEquals(obj.toString(), rs.getString(9)); } } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java index 5b57ac5719..e6f76a4909 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java @@ -140,23 +140,25 @@ void verifyResultSetMetaData(Connection conn) throws Exception { ResultSetMetaData metadata = rs.getMetaData(); - assertEquals(metadata.getColumnType(1), sqlType.jdbcType, "getColumnType() of " + sqlCastExpression()); - assertEquals(metadata.getColumnTypeName(1), sqlType.toString(), + assertEquals(sqlType.jdbcType, metadata.getColumnType(1), "getColumnType() of " + sqlCastExpression()); + assertEquals(sqlType.toString(), metadata.getColumnTypeName(1), "getColumnTypeName() of " + sqlCastExpression()); - assertEquals(metadata.getPrecision(1), precision, "getPrecision() of " + sqlCastExpression()); + assertEquals(precision, metadata.getPrecision(1), "getPrecision() of " + sqlCastExpression()); // Display size of temporal types is the precision per JDBC spec - assertEquals(metadata.getColumnDisplaySize(1), precision, + assertEquals(precision, metadata.getColumnDisplaySize(1), "getColumnDisplaySize() of " + sqlCastExpression()); + // Scale is interpreted as number of fractional seconds precision - assertEquals(metadata.getScale(1), scale, "getScale() of " + sqlCastExpression()); - assertEquals(metadata.getColumnClassName(1), sqlType.className, + assertEquals(scale, metadata.getScale(1), "getScale() of " + sqlCastExpression()); + assertEquals(sqlType.className, metadata.getColumnClassName(1), "getColumnClassName() of " + sqlCastExpression()); + // Katmai temporal types are not signed - assertEquals(metadata.isSigned(1), false, "isSigned() of " + sqlCastExpression()); + assertEquals(false, metadata.isSigned(1), "isSigned() of " + sqlCastExpression()); // Katmai temporal types are searchable (i.e. usable in a WHERE clause) - assertEquals(metadata.isSearchable(1), true, "isSearchable() of " + sqlCastExpression()); + assertEquals(true, metadata.isSearchable(1), "isSearchable() of " + sqlCastExpression()); } } @@ -169,18 +171,19 @@ void verifyParameterMetaData(Connection conn) throws Exception { try (PreparedStatement pstmt = conn.prepareStatement("{call " + escapedProcName + "(?)}")) { ParameterMetaData metadata = pstmt.getParameterMetaData(); - assertEquals(metadata.getParameterType(1), sqlType.jdbcType, + assertEquals(sqlType.jdbcType, metadata.getParameterType(1), "getParameterType() of " + sqlCastExpression()); - assertEquals(metadata.getParameterTypeName(1), sqlType.toString(), + assertEquals(sqlType.toString(), metadata.getParameterTypeName(1), "getParameterTypeName() of " + sqlCastExpression()); - assertEquals(metadata.getPrecision(1), precision, "getPrecision() of " + sqlCastExpression()); + assertEquals(precision, metadata.getPrecision(1), "getPrecision() of " + sqlCastExpression()); // Scale is interpreted as number of fractional seconds precision - assertEquals(metadata.getScale(1), scale, "getScale() of " + sqlCastExpression()); - assertEquals(metadata.getParameterClassName(1), sqlType.className, + assertEquals(scale, metadata.getScale(1), "getScale() of " + sqlCastExpression()); + assertEquals(sqlType.className, metadata.getParameterClassName(1), "getParameterClassName() of " + sqlCastExpression()); + // Katmai temporal types are not signed - assertEquals(metadata.isSigned(1), false, "isSigned() of " + sqlCastExpression()); + assertEquals(false, metadata.isSigned(1), "isSigned() of " + sqlCastExpression()); } } finally { try (Statement stmt = conn.createStatement()) { @@ -282,18 +285,16 @@ private java.util.Calendar expectedCalendar() { } void verifyRSGetters(ResultSet rs) throws Exception { - assertEquals(rs.getDate(1), expected); - - assertEquals(rs.getTimestamp(1), new Timestamp(expected.getTime())); - - assertEquals(rs.getString(1), expected.toString()); + assertEquals(expected, rs.getDate(1)); + assertEquals(new Timestamp(expected.getTime()), rs.getTimestamp(1)); + assertEquals(expected.toString(), rs.getString(1)); } void verifyRSUpdaters(ResultSet rs) throws Exception { rs.updateDate(1, expected); rs.updateRow(); - assertEquals(rs.getDate(1), expected); + assertEquals(expected, rs.getDate(1)); } void verifySetters(PreparedStatement ps) throws Exception { @@ -303,7 +304,7 @@ void verifySetters(PreparedStatement ps) throws Exception { ResultSet rs = ps.getResultSet(); rs.next(); - assertEquals(rs.getDate(1), expected); + assertEquals(expected, rs.getDate(1)); } void verifySettersUtilDate(PreparedStatement ps) throws Exception { @@ -313,8 +314,8 @@ void verifySettersUtilDate(PreparedStatement ps) throws Exception { ps.getMoreResults(); ResultSet rs = ps.getResultSet(); rs.next(); - assertEquals(rs.getDate(1), expected); - assertEquals((java.util.Date) rs.getObject(1), expectedUtilDate()); + assertEquals(expected, rs.getDate(1)); + assertEquals(expectedUtilDate(), (java.util.Date) rs.getObject(1)); // Test the additional conversions introduced in JDBC41 for types setters // Test datetime2 column with target type TIMESTAMP @@ -326,7 +327,7 @@ void verifySettersUtilDate(PreparedStatement ps) throws Exception { rs.next(); // Go to the row just inserted rs.relative(++currentRow); - assertEquals(rs.getDate(1), expected); + assertEquals(expected, rs.getDate(1)); ps.setObject(1, expectedUtilDate(), java.sql.Types.TIMESTAMP); ps.execute(); @@ -336,7 +337,7 @@ void verifySettersUtilDate(PreparedStatement ps) throws Exception { rs.next(); // Go to the row just inserted rs.relative(++currentRow); - assertEquals(rs.getTimestamp(1), expectedTimestampMillisPrecision()); + assertEquals(expectedTimestampMillisPrecision(), rs.getTimestamp(1)); // Test the setNull() methods for different data type conversions ps.setNull(1, java.sql.Types.DATE); @@ -348,7 +349,7 @@ void verifySettersUtilDate(PreparedStatement ps) throws Exception { rs.relative(++currentRow); // Read the column, first before calling rs.wasNull() rs.getTimestamp(1); - assertEquals(rs.wasNull(), true); + assertEquals(true, rs.wasNull()); ps.setNull(1, java.sql.Types.TIMESTAMP); ps.execute(); @@ -359,7 +360,7 @@ void verifySettersUtilDate(PreparedStatement ps) throws Exception { rs.relative(++currentRow); // Read the column, first before calling rs.wasNull() rs.getTimestamp(1); - assertEquals(rs.wasNull(), true); + assertEquals(true, rs.wasNull()); } void verifySettersCalendar(PreparedStatement ps) throws Exception { @@ -369,7 +370,7 @@ void verifySettersCalendar(PreparedStatement ps) throws Exception { ps.getMoreResults(); ResultSet rs = ps.getResultSet(); rs.next(); - assertEquals(rs.getDate(1), expected); + assertEquals(expected, rs.getDate(1)); // Cannot test rs.getObject for the Calendar object type, as none of Time, Timestamp, Date // or java.util.Date can be cast to a calendar @@ -383,7 +384,7 @@ void verifySettersCalendar(PreparedStatement ps) throws Exception { rs.next(); // Go to the row just inserted rs.relative(++currentRow); - assertEquals(rs.getDate(1), expected); + assertEquals(expected, rs.getDate(1)); ps.setObject(1, expectedCalendar(), java.sql.Types.TIMESTAMP); ps.execute(); @@ -393,15 +394,15 @@ void verifySettersCalendar(PreparedStatement ps) throws Exception { rs.next(); // Go to the row just inserted rs.relative(++currentRow); - assertEquals(rs.getTimestamp(1), expectedTimestampMillisPrecision()); + assertEquals(expectedTimestampMillisPrecision(), rs.getTimestamp(1)); } void verifyCSGetters(CallableStatement cs) throws Exception { cs.setDate(1, expected); cs.registerOutParameter(2, java.sql.Types.DATE); cs.execute(); - assertEquals(cs.getDate(2), expected); - assertEquals(cs.getObject(2), expected); + assertEquals(expected, cs.getDate(2)); + assertEquals(expected, cs.getObject(2)); } } @@ -487,18 +488,16 @@ private java.util.Calendar expectedCalendar() { } void verifyRSGetters(ResultSet rs) throws Exception { - assertEquals(rs.getTime(1), expectedTime()); - - assertEquals(rs.getTimestamp(1), expectedTimestamp()); - - assertEquals(rs.getString(1), this.getString()); + assertEquals(expectedTime(), rs.getTime(1)); + assertEquals(expectedTimestamp(), rs.getTimestamp(1)); + assertEquals(this.getString(), rs.getString(1)); } void verifyRSUpdaters(ResultSet rs) throws Exception { rs.updateTime(1, expectedTime()); rs.updateRow(); - assertEquals(rs.getTime(1), expectedTime()); + assertEquals(expectedTime(), rs.getTime(1)); } void verifySetters(PreparedStatement ps) throws Exception { @@ -508,7 +507,7 @@ void verifySetters(PreparedStatement ps) throws Exception { ResultSet rs = ps.getResultSet(); rs.next(); - assertEquals(rs.getTime(1), expectedTime()); + assertEquals(expectedTime(), rs.getTime(1)); } void verifySettersUtilDate(PreparedStatement ps) throws Exception { @@ -518,9 +517,9 @@ void verifySettersUtilDate(PreparedStatement ps) throws Exception { ps.getMoreResults(); ResultSet rs = ps.getResultSet(); rs.next(); - assertEquals(rs.getTime(1), expectedTime()); - assertEquals(rs.getTimestamp(1), expectedTimestampMillisPrecision()); - assertEquals((java.util.Date) rs.getObject(1), expectedUtilDate()); + assertEquals(expectedTime(), rs.getTime(1)); + assertEquals(expectedTimestampMillisPrecision(), rs.getTimestamp(1)); + assertEquals(expectedUtilDate(), (java.util.Date) rs.getObject(1)); // Test the additional conversions introduced in JDBC41 for types setters // Test datetime2 column with target type TIMESTAMP @@ -532,7 +531,7 @@ void verifySettersUtilDate(PreparedStatement ps) throws Exception { rs.next(); // Go to the row just inserted rs.relative(++currentRow); - assertEquals(rs.getTime(1), expectedTime()); + assertEquals(expectedTime(), rs.getTime(1)); ps.setObject(1, expectedUtilDate(), java.sql.Types.TIMESTAMP); ps.execute(); @@ -542,7 +541,7 @@ void verifySettersUtilDate(PreparedStatement ps) throws Exception { rs.next(); // Go to the row just inserted rs.relative(++currentRow); - assertEquals(rs.getTimestamp(1), expectedTimestampMillisPrecision()); + assertEquals(expectedTimestampMillisPrecision(), rs.getTimestamp(1)); // Test the setNull() methods for different data type conversions ps.setNull(1, java.sql.Types.TIME); @@ -554,7 +553,7 @@ void verifySettersUtilDate(PreparedStatement ps) throws Exception { rs.relative(++currentRow); // Read the column, first before calling rs.wasNull() rs.getTimestamp(1); - assertEquals(rs.wasNull(), true); + assertEquals(true, rs.wasNull()); ps.setNull(1, java.sql.Types.TIMESTAMP); ps.execute(); @@ -565,7 +564,7 @@ void verifySettersUtilDate(PreparedStatement ps) throws Exception { rs.relative(++currentRow); // Read the column, first before calling rs.wasNull() rs.getTimestamp(1); - assertEquals(rs.wasNull(), true); + assertEquals(true, rs.wasNull()); } @@ -576,7 +575,7 @@ void verifySettersCalendar(PreparedStatement ps) throws Exception { ps.getMoreResults(); ResultSet rs = ps.getResultSet(); rs.next(); - assertEquals(rs.getTime(1), expectedTime()); + assertEquals(expectedTime(), rs.getTime(1)); assertEquals(rs.getTimestamp(1), expectedTimestampMillisPrecision()); // Cannot test rs.getObject for the Calendar object type, as none of Time, Timestamp, Date @@ -592,7 +591,7 @@ void verifySettersCalendar(PreparedStatement ps) throws Exception { rs.next(); // Go to the row just inserted rs.relative(++currentRow); - assertEquals(rs.getTime(1), expectedTime()); + assertEquals(expectedTime(), rs.getTime(1)); ps.setObject(1, expectedCalendar(), java.sql.Types.TIMESTAMP); ps.execute(); @@ -602,15 +601,15 @@ void verifySettersCalendar(PreparedStatement ps) throws Exception { rs.next(); // Go to the row just inserted rs.relative(++currentRow); - assertEquals(rs.getTimestamp(1), expectedTimestampMillisPrecision()); + assertEquals(expectedTimestampMillisPrecision(), rs.getTimestamp(1)); } void verifyCSGetters(CallableStatement cs) throws Exception { cs.setTime(1, expectedTime()); cs.registerOutParameter(2, java.sql.Types.TIME); cs.execute(); - assertEquals(cs.getTime(2), expectedTime()); - assertEquals(cs.getObject(2), expectedTime()); + assertEquals(expectedTime(), cs.getTime(2)); + assertEquals(expectedTime(), cs.getObject(2)); } } @@ -713,13 +712,10 @@ private java.util.Calendar expectedCalendar() { } void verifyRSGetters(ResultSet rs) throws Exception { - assertEquals(rs.getDate(1, Calendar.getInstance(tz)), expectedDate()); - - assertEquals(rs.getTime(1, Calendar.getInstance(tz)), expectedTime()); - - assertEquals(rs.getTimestamp(1, Calendar.getInstance(tz)), expectedTimestamp()); - - assertEquals(rs.getString(1), stringValue); + assertEquals(expectedDate(), rs.getDate(1, Calendar.getInstance(tz))); + assertEquals(expectedTime(), rs.getTime(1, Calendar.getInstance(tz))); + assertEquals(expectedTimestamp(), rs.getTimestamp(1, Calendar.getInstance(tz))); + assertEquals(stringValue, rs.getString(1)); } void verifyRSUpdaters(ResultSet rs) throws Exception { @@ -736,7 +732,7 @@ void verifyRSUpdaters(ResultSet rs) throws Exception { rs.updateRow(); // Verify the update (this value's time zone is still the default) - assertEquals(rs.getTimestamp(1), expectedTimestamp()); + assertEquals(expectedTimestamp(), rs.getTimestamp(1)); } finally { // Restore the original default time zone TimeZone.setDefault(tzDefault); @@ -744,7 +740,7 @@ void verifyRSUpdaters(ResultSet rs) throws Exception { // Verify the update (after restoring the default time zone) using the getTimestamp // variant that takes a time zone argument (as a Calendar) - assertEquals(rs.getTimestamp(1, Calendar.getInstance(tz)), expectedTimestamp()); + assertEquals(expectedTimestamp(), rs.getTimestamp(1, Calendar.getInstance(tz))); } void verifySetters(PreparedStatement ps) throws Exception { @@ -760,7 +756,7 @@ void verifySetters(PreparedStatement ps) throws Exception { ResultSet rs = ps.getResultSet(); rs.next(); - assertEquals(rs.getTimestamp(1), expectedTimestamp()); + assertEquals(expectedTimestamp(), rs.getTimestamp(1)); } finally { TimeZone.setDefault(tzDefault); } @@ -772,7 +768,7 @@ void verifySetters(PreparedStatement ps) throws Exception { ResultSet rs = ps.getResultSet(); rs.next(); - assertEquals(rs.getTimestamp(1, Calendar.getInstance(tz)), expectedTimestamp()); + assertEquals(expectedTimestamp(), rs.getTimestamp(1, Calendar.getInstance(tz))); } void verifySettersUtilDate(PreparedStatement ps) throws Exception { @@ -782,8 +778,8 @@ void verifySettersUtilDate(PreparedStatement ps) throws Exception { ps.getMoreResults(); ResultSet rs = ps.getResultSet(); rs.next(); - assertEquals(rs.getTimestamp(1), expectedTimestampMillisPrecision()); - assertEquals((java.util.Date) rs.getObject(1), expectedUtilDate()); + assertEquals(expectedTimestampMillisPrecision(), rs.getTimestamp(1)); + assertEquals(expectedUtilDate(), (java.util.Date) rs.getObject(1)); // Test the additional conversions introduced in JDBC41 for types setters // Test datetime2 column with target type TIMESTAMP @@ -794,7 +790,7 @@ void verifySettersUtilDate(PreparedStatement ps) throws Exception { // Go to the next row rs.next(); rs.relative(++currentRow); - assertEquals(rs.getTimestamp(1), expectedTimestampMillisPrecision()); + assertEquals(expectedTimestampMillisPrecision(), rs.getTimestamp(1)); // Test the setNull() methods for different data type conversions ps.setNull(1, java.sql.Types.TIME); @@ -806,7 +802,7 @@ void verifySettersUtilDate(PreparedStatement ps) throws Exception { rs.relative(++currentRow); // Read the column, first before calling rs.wasNull() rs.getTimestamp(1); - assertEquals(rs.wasNull(), true); + assertEquals(true, rs.wasNull()); ps.setNull(1, java.sql.Types.DATE); ps.execute(); @@ -817,7 +813,7 @@ void verifySettersUtilDate(PreparedStatement ps) throws Exception { rs.relative(++currentRow); // Read the column, first before calling rs.wasNull() rs.getTimestamp(1); - assertEquals(rs.wasNull(), true); + assertEquals(true, rs.wasNull()); ps.setNull(1, java.sql.Types.TIMESTAMP); ps.execute(); @@ -828,7 +824,7 @@ void verifySettersUtilDate(PreparedStatement ps) throws Exception { rs.relative(++currentRow); // Read the column, first before calling rs.wasNull() rs.getTimestamp(1); - assertEquals(rs.wasNull(), true); + assertEquals(true, rs.wasNull()); } void verifySettersCalendar(PreparedStatement ps) throws Exception { @@ -838,7 +834,7 @@ void verifySettersCalendar(PreparedStatement ps) throws Exception { ps.getMoreResults(); ResultSet rs = ps.getResultSet(); rs.next(); - assertEquals(rs.getTimestamp(1), expectedTimestampMillisPrecision()); + assertEquals(expectedTimestampMillisPrecision(), rs.getTimestamp(1)); // Cannot test rs.getObject for the Calendar object type, as none of Time, Timestamp, Date // or java.util.Date can be cast to a calendar @@ -851,7 +847,7 @@ void verifySettersCalendar(PreparedStatement ps) throws Exception { // Go to the next row rs.next(); rs.relative(++currentRow); - assertEquals(rs.getTimestamp(1), expectedTimestampMillisPrecision()); + assertEquals(expectedTimestampMillisPrecision(), rs.getTimestamp(1)); } void verifyCSGetters(CallableStatement cs) throws Exception { @@ -860,14 +856,14 @@ void verifyCSGetters(CallableStatement cs) throws Exception { cs.execute(); // Verify typed getter (with time zone argument) - assertEquals(cs.getTimestamp(2, Calendar.getInstance(tz)), expectedTimestamp()); + assertEquals(expectedTimestamp(), cs.getTimestamp(2, Calendar.getInstance(tz))); // Verify getObject (no provision for time zone argument - need to push/pop the default) TimeZone tzDefault = TimeZone.getDefault(); try { TimeZone.setDefault(tz); - assertEquals(cs.getObject(2), expectedTimestamp()); + assertEquals(expectedTimestamp(), cs.getObject(2)); } finally { TimeZone.setDefault(tzDefault); } @@ -978,22 +974,18 @@ private java.util.Calendar expectedCalendar() { } void verifyRSGetters(ResultSet rs) throws Exception { - assertEquals(rs.getDate(1), expectedDate()); - - assertEquals(rs.getTime(1), expectedTime()); - - assertEquals(rs.getTimestamp(1), dto.getTimestamp()); - - assertEquals(((SQLServerResultSet) rs).getDateTimeOffset(1), dto); - - assertEquals(rs.getString(1), this.getString()); + assertEquals(expectedDate(), rs.getDate(1)); + assertEquals(expectedTime(), rs.getTime(1)); + assertEquals(dto.getTimestamp(), rs.getTimestamp(1)); + assertEquals(dto, ((SQLServerResultSet) rs).getDateTimeOffset(1)); + assertEquals(this.getString(), rs.getString(1)); } void verifyRSUpdaters(ResultSet rs) throws Exception { ((SQLServerResultSet) rs).updateDateTimeOffset(1, dto); rs.updateRow(); - assertEquals(((SQLServerResultSet) rs).getDateTimeOffset(1), dto); + assertEquals(dto, ((SQLServerResultSet) rs).getDateTimeOffset(1)); } void verifySetters(PreparedStatement ps) throws Exception { @@ -1003,7 +995,7 @@ void verifySetters(PreparedStatement ps) throws Exception { ResultSet rs = ps.getResultSet(); rs.next(); - assertEquals(((SQLServerResultSet) rs).getDateTimeOffset(1), dto); + assertEquals(dto, ((SQLServerResultSet) rs).getDateTimeOffset(1)); } void verifySettersUtilDate(PreparedStatement ps) throws Exception { @@ -1026,9 +1018,9 @@ void verifyCSGetters(CallableStatement cs) throws Exception { ((SQLServerCallableStatement) cs).setDateTimeOffset(1, dto); cs.registerOutParameter(2, microsoft.sql.Types.DATETIMEOFFSET); cs.execute(); - assertEquals(((SQLServerCallableStatement) cs).getDateTimeOffset(2), dto); + assertEquals(dto, ((SQLServerCallableStatement) cs).getDateTimeOffset(2)); - assertEquals(cs.getObject(2), dto); + assertEquals(dto, cs.getObject(2)); } } @@ -1159,7 +1151,7 @@ public void testSendTimestampAsTimeAsDatetime() throws Exception { // Fetch the OUT parameter and verify that we have a date-normalized TIME of midnight java.sql.Time timeOut = cs.getTime(2); Timestamp tsOut = new Timestamp(timeOut.getTime()); - assertEquals(tsOut.toString(), "1970-01-01 00:00:00.0"); + assertEquals("1970-01-01 00:00:00.0", tsOut.toString()); } } finally { @@ -1222,7 +1214,7 @@ public void testDoubleRounding() throws Exception { rs.next(); // 3432-12-21 23:22:58.832909 +00:00 DateTimeOffset expectedDto = (DateTimeOffset) rs.getObject(1); - assertEquals(actualDto, expectedDto); + assertEquals(expectedDto, actualDto); } } finally { try (Statement stmt = conn.createStatement()) { @@ -1266,7 +1258,7 @@ public void testWithJapaneseImperialCalendar() throws Exception { Date date = rs.getDate(1, japaneseImperialCalendar); // check pre-Meiji - assertEquals(date.toString(), "0821-01-04"); + assertEquals("0821-01-04", date.toString()); } try (PreparedStatement ps = conn.prepareStatement("SELECT CAST(? AS VARCHAR(40))")) { @@ -1283,7 +1275,7 @@ public void testWithJapaneseImperialCalendar() throws Exception { try (ResultSet rs = ps.executeQuery()) { rs.next(); // check Taisho 1 - assertEquals(rs.getString(1), "1912-07-31 00:00:00.0000000"); + assertEquals("1912-07-31 00:00:00.0000000", rs.getString(1)); } // Set second year of Showa era (1927 Gregorian) @@ -1301,7 +1293,7 @@ public void testWithJapaneseImperialCalendar() throws Exception { try (ResultSet rs = ps.executeQuery()) { rs.next(); // check Showa 2 - assertEquals(rs.getString(1), "1927-02-15 08:49:03.0870000"); + assertEquals("1927-02-15 08:49:03.0870000", rs.getString(1)); } } } finally { @@ -1383,7 +1375,7 @@ public void testWithThaiLocale() throws Exception { ps.setTimestamp(1, ts); try (ResultSet rs = ps.executeQuery()) { rs.next(); - assertEquals(rs.getString(1), tsFormat.format(ts)); + assertEquals(tsFormat.format(ts), rs.getString(1)); } // Test PreparedStatement with Time @@ -1394,9 +1386,9 @@ public void testWithThaiLocale() throws Exception { rs.next(); // compare these separately since there may be an extra space between the 2 - assertEquals(rs.getString(1).substring(0, 11), "Jan 1 1970"); - assertEquals(rs.getString(1).substring(rs.getString(1).length() - 7).trim(), - timeFormat.format(ts.getTime())); + assertEquals("Jan 1 1970", rs.getString(1).substring(0, 11)); + assertEquals(timeFormat.format(ts.getTime()), + rs.getString(1).substring(rs.getString(1).length() - 7).trim()); } // Test PreparedStatement with Date @@ -1404,7 +1396,7 @@ public void testWithThaiLocale() throws Exception { ps.setDate(1, date); try (ResultSet rs = ps.executeQuery()) { rs.next(); - assertEquals(rs.getString(1), dateFormat.format(ts)); + assertEquals(dateFormat.format(ts), rs.getString(1)); } // Test PreparedStatement with Date (using Buddhist calendar) @@ -1412,7 +1404,7 @@ public void testWithThaiLocale() throws Exception { ps.setDate(1, date, Calendar.getInstance()); try (ResultSet rs = ps.executeQuery()) { rs.next(); - assertEquals(rs.getString(1), dateFormat.format(ts)); + assertEquals(dateFormat.format(ts), rs.getString(1)); } // Test PreparedStatement with DateTimeOffset (using Buddhist calendar) @@ -1425,7 +1417,7 @@ public void testWithThaiLocale() throws Exception { rs.next(); dtoFormat.setTimeZone(TimeZone.getTimeZone("America/Los_Angeles")); - assertEquals(rs.getString(1), dtoFormat.format(ts)); + assertEquals(dtoFormat.format(ts), rs.getString(1)); } } } finally { @@ -1447,7 +1439,7 @@ public void testBaseDate() throws Exception { rs.next(); ts = rs.getTimestamp(1); // java base date - assertEquals(ts.toString(), "1970-01-01 12:34:56.0"); + assertEquals("1970-01-01 12:34:56.0", ts.toString()); } } @@ -1460,7 +1452,7 @@ public void testBaseDate() throws Exception { rs.next(); ts = rs.getTimestamp(1); // SQL Server base date - assertEquals(ts.toString(), "1900-01-01 12:34:56.0"); + assertEquals("1900-01-01 12:34:56.0", ts.toString()); } } } @@ -1476,7 +1468,7 @@ public void testTimestampToDateTimeOffset() throws Exception { try (ResultSet rs = ps.executeQuery()) { rs.next(); DateTimeOffset dto = ((SQLServerResultSet) rs).getDateTimeOffset(1); - assertEquals(dto.toString(), "2010-01-06 12:34:56 +00:00"); + assertEquals("2010-01-06 12:34:56 +00:00", dto.toString()); } } } @@ -1495,7 +1487,7 @@ public void testJulianLeapYear() throws Exception { ps.setDate(1, julianDate); try (ResultSet rs = ps.executeQuery()) { rs.next(); - assertEquals(rs.getString(1), "0700-03-01"); + assertEquals("0700-03-01", rs.getString(1)); } } } @@ -1508,13 +1500,13 @@ public void testGetTimeRounding() throws Exception { // Test getTime() rounding from TIME(6) SQL type try (ResultSet rs = stmt.executeQuery("SELECT CAST('12:34:56.999500' AS TIME)")) { rs.next(); - assertEquals(rs.getTime(1).toString(), "12:34:57"); + assertEquals("12:34:57", rs.getTime(1).toString()); } // Test getTime() rounding from character data try (ResultSet rs = stmt.executeQuery("SELECT '12:34:56.999500'")) { rs.next(); - assertEquals(rs.getTime(1).toString(), "12:34:57"); + assertEquals("12:34:57", rs.getTime(1).toString()); } } } @@ -1530,7 +1522,7 @@ public void testGregorianCutoverDateTime2() throws Exception { ps.setTimestamp(1, ts); try (ResultSet rs = ps.executeQuery()) { rs.next(); - assertEquals(rs.getString(1), "1582-10-24 15:07:09.0810000"); + assertEquals("1582-10-24 15:07:09.0810000", rs.getString(1)); } // Test setting value during the Gregorian cutover via Timestamp constructed from Calendar @@ -1541,7 +1533,7 @@ public void testGregorianCutoverDateTime2() throws Exception { ps.setTimestamp(1, ts); try (ResultSet rs = ps.executeQuery()) { rs.next(); - assertEquals(rs.getString(1), "1582-11-01 15:07:09.0810000"); + assertEquals("1582-11-01 15:07:09.0810000", rs.getString(1)); } } } @@ -1558,7 +1550,7 @@ public void testTimestampToDateTime() throws Exception { .prepareStatement("SELECT 1 WHERE ?=CAST('2009-12-17 17:00:29' AS DATETIME)")) { ps.setTimestamp(1, Timestamp.valueOf("2009-12-17 17:00:29")); try (ResultSet rs = ps.executeQuery()) { - assertEquals(rs.next(), true); + assertEquals(true, rs.next()); } } } @@ -1590,20 +1582,20 @@ public void testUpdateMisc() throws Exception { // Update datetimeoffset(2) from pre-Gregorian Date rs.updateDate(1, Date.valueOf("0814-02-18")); rs.updateRow(); - assertEquals(((SQLServerResultSet) rs).getDateTimeOffset(1).toString(), - "0814-02-18 00:00:00 +00:00"); + assertEquals("0814-02-18 00:00:00 +00:00", + ((SQLServerResultSet) rs).getDateTimeOffset(1).toString()); // Update datetimeoffset(2) from "last" Time rs.updateTime(1, new java.sql.Time(Timestamp.valueOf("1970-01-01 23:59:59.998").getTime())); rs.updateRow(); - assertEquals(((SQLServerResultSet) rs).getDateTimeOffset(1).toString(), - "1970-01-02 00:00:00 +00:00"); + assertEquals("1970-01-02 00:00:00 +00:00", + ((SQLServerResultSet) rs).getDateTimeOffset(1).toString()); // Update datetimeoffset(2) from the "last" Timestamp rs.updateTimestamp(1, Timestamp.valueOf("9999-12-31 23:59:59.998")); rs.updateRow(); dto = ((SQLServerResultSet) rs).getDateTimeOffset(1); - assertEquals(dto.toString(), "9999-12-31 23:59:59.99 +00:00"); + assertEquals("9999-12-31 23:59:59.99 +00:00", dto.toString()); // Attempt to update datetimeoffset(2) from the first out of range value // Verify that an exception is thrown and that the statement/connection is still usable after @@ -1616,7 +1608,7 @@ public void testUpdateMisc() throws Exception { exceptionThrown = false; } catch (SQLServerException e) { // data exception - datetime field overflow (ISO/IEC 9075-2:1999 - assertEquals(e.getSQLState(), "22008"); + assertEquals("22008", e.getSQLState()); } if (!exceptionThrown) { @@ -1628,7 +1620,7 @@ public void testUpdateMisc() throws Exception { ts.setNanos(987659999); rs.updateTimestamp(3, ts); rs.updateRow(); - assertEquals(rs.getTimestamp(3).toString(), "1900-01-01 11:05:23.98766"); + assertEquals("1900-01-01 11:05:23.98766", rs.getTimestamp(3).toString()); // Update time(5) from Timestamp to max value in a day. The value should not be rounded ts = Timestamp.valueOf("2010-01-12 23:59:59"); @@ -1636,13 +1628,13 @@ public void testUpdateMisc() throws Exception { Time time = new java.sql.Time(ts.getTime()); rs.updateTimestamp(3, ts); rs.updateRow(); - assertEquals(rs.getTimestamp(3).toString(), "1900-01-01 23:59:59.99999"); + assertEquals("1900-01-01 23:59:59.99999", rs.getTimestamp(3).toString()); // Update time(2) from Time to max value in a day. The value should not be rounded rs.updateTime(6, time); rs.updateRow(); // conversion to timestamp is necessary to see fractional secs - assertEquals(new Timestamp(rs.getTime(6).getTime()).toString(), "1970-01-01 23:59:59.99"); + assertEquals("1970-01-01 23:59:59.99", new Timestamp(rs.getTime(6).getTime()).toString()); // Update time(5) from Timestamp to max value in a second. The value should be rounded ts = Timestamp.valueOf("2010-01-12 23:59:58"); @@ -1650,26 +1642,26 @@ public void testUpdateMisc() throws Exception { time = new java.sql.Time(ts.getTime()); rs.updateTimestamp(3, ts); rs.updateRow(); - assertEquals(rs.getTimestamp(3).toString(), "1900-01-01 23:59:59.0"); + assertEquals("1900-01-01 23:59:59.0", rs.getTimestamp(3).toString()); // Update time(2) from Time to max value in a second. The value should be rounded rs.updateTime(6, time); rs.updateRow(); // conversion to timestamp is necessary to see fractional secs - assertEquals(new Timestamp(rs.getTime(6).getTime()).toString(), "1970-01-01 23:59:59.0"); + assertEquals("1970-01-01 23:59:59.0", new Timestamp(rs.getTime(6).getTime()).toString()); // Update datetime w/expected rounding of nanos to DATETIME's 1/300second resolution ts = Timestamp.valueOf("6289-04-22 05:13:57.6745106"); rs.updateTimestamp(2, ts); rs.updateRow(); - assertEquals(rs.getTimestamp(2).toString(), "6289-04-22 05:13:57.677"); + assertEquals("6289-04-22 05:13:57.677", rs.getTimestamp(2).toString()); // Update datetime with rounding-induced overflow from Time // (should roll date part to 1/2/1970) ts = Timestamp.valueOf("2010-01-18 23:59:59.999"); rs.updateTime(2, new java.sql.Time(ts.getTime())); rs.updateRow(); - assertEquals(rs.getTimestamp(2).toString(), "1970-01-02 00:00:00.0"); + assertEquals("1970-01-02 00:00:00.0", rs.getTimestamp(2).toString()); } finally { TestUtils.dropTableIfExists(escapedTableName, stmt); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SparseTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SparseTest.java index 6c5103c4a8..436e62089d 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SparseTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SparseTest.java @@ -47,9 +47,9 @@ public void testSparse() throws Exception { try (ResultSet rs = stmt.executeQuery("Select * from " + escapedTableName)) { rs.next(); - assertEquals(rs.getString("col1023"), "yo"); - assertEquals(rs.getInt("col1"), 1); - assertEquals(rs.getBytes("col2")[0], 0x45); + assertEquals("yo", rs.getString("col1023")); + assertEquals(1, rs.getInt("col1")); + assertEquals(0x45, rs.getBytes("col2")[0]); } } finally { try (Statement stmt = conn.createStatement()) { diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java index a7e05c371b..f08ce00b21 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java @@ -30,7 +30,7 @@ @RunWith(JUnitPlatform.class) public class DTOSerialTest extends AbstractTest { - private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS XXX"); + private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss XXX"); private static String dateString; @Test @@ -75,9 +75,9 @@ public void testESerial() throws Exception { try (ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()))) { SQLServerException ex = (SQLServerException) in.readObject(); - assertEquals(currException.toString(), ex.toString()); - assertEquals(currException.getSQLState(), ex.getSQLState()); - assertEquals(currException.getErrorCode(), ex.getErrorCode()); + assertEquals(ex.toString(), currException.toString()); + assertEquals(ex.getSQLState(), currException.getSQLState()); + assertEquals(ex.getErrorCode(), currException.getErrorCode()); } } } @@ -183,7 +183,8 @@ private static void verifyDTOEqual(DateTimeOffset initial, DateTimeOffset hydrat // check formatted date string String formattedDate = sdf.format(sdf.parse(initialStr)); - assertEquals(formattedDate, dateString); + + assertEquals(dateString, formattedDate); // check hydrated datetimeoffset assertEquals(initial, hydrated); From db38827ebfece40e855e6c6c8f0baae90fb528ae Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Thu, 20 Dec 2018 00:29:59 -0800 Subject: [PATCH 40/51] fixed comments and leaked resultsets --- .../callablestatement/CallableMixedTest.java | 4 - .../jdbc/datatypes/KatmaiDataTypesTest.java | 505 +++++++++--------- .../jdbc/resultset/ResultSetTest.java | 4 +- .../jdbc/unit/serial/DTOSerialTest.java | 2 +- 4 files changed, 245 insertions(+), 270 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java index 697e345cc5..07890734cc 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/callablestatement/CallableMixedTest.java @@ -52,7 +52,6 @@ public void datatypestest() throws Exception { // Test OUT param re-registration cstmt.registerOutParameter((int) 5, java.sql.Types.BINARY); - cstmt.registerOutParameter((int) 5, (int) 5); cstmt.setObject((int) 4, Short.valueOf("-5372"), (int) 5); @@ -63,9 +62,6 @@ public void datatypestest() throws Exception { assertEquals(-5372, cstmt.getInt((int) 5)); } - // do nothing and reexecute - try (ResultSet rs = cstmt.executeQuery()) {} - // get the param without getting the resultset try (ResultSet rs = cstmt.executeQuery()) { assertEquals(-2147483648, cstmt.getInt((int) 1)); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java index e6f76a4909..cd256d0564 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java @@ -113,7 +113,7 @@ final String getString() { this.scale = fractionalSecondsDigits; } - /** + /* * For testing the setObject and setNull methods in PreparedStatement, use the verifySetter* methods. These * methods prepare a single statement and execute it for all different data types by calling the appropriate * 'setObject' methods for each data type and/or type conversion. @@ -204,7 +204,6 @@ void verifyRSUpdaters(Connection conn) throws Exception { assumeTrue(!isSqlAzureDW(), TestResource.getResource("R_skipAzure")); try (Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE)) { - TestUtils.dropTableIfExists(escapedTableName, stmt); stmt.executeUpdate("CREATE TABLE " + escapedTableName + " (col1 " + sqlTypeExpression @@ -233,11 +232,11 @@ void verifySetters(Connection conn) throws Exception { "INSERT INTO " + escapedTableName + " VALUES (?) SELECT * FROM " + escapedTableName, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY)) { verifySetters(ps); - // Verify setObject function for the new mapping in JDBC41 (java.util.Date to TIMESTAMP) + // Verify setObject function for the mapping java.util.Date to TIMESTAMP stmt.executeUpdate("TRUNCATE TABLE " + escapedTableName); verifySettersUtilDate(ps); - // Verify setObject function for the new mapping in JDBC41 (java.util.Calendar to TIMESTAMP) + // Verify setObject function for the mapping java.util.Calendar to TIMESTAMP stmt.executeUpdate("TRUNCATE TABLE " + escapedTableName); verifySettersCalendar(ps); } finally { @@ -301,10 +300,10 @@ void verifySetters(PreparedStatement ps) throws Exception { ps.setDate(1, expected); ps.execute(); ps.getMoreResults(); - ResultSet rs = ps.getResultSet(); - rs.next(); - - assertEquals(expected, rs.getDate(1)); + try (ResultSet rs = ps.getResultSet()) { + rs.next(); + assertEquals(expected, rs.getDate(1)); + } } void verifySettersUtilDate(PreparedStatement ps) throws Exception { @@ -312,55 +311,59 @@ void verifySettersUtilDate(PreparedStatement ps) throws Exception { ps.setObject(1, expectedUtilDate()); ps.execute(); ps.getMoreResults(); - ResultSet rs = ps.getResultSet(); - rs.next(); - assertEquals(expected, rs.getDate(1)); - assertEquals(expectedUtilDate(), (java.util.Date) rs.getObject(1)); + try (ResultSet rs = ps.getResultSet()) { + rs.next(); + assertEquals(expected, rs.getDate(1)); + assertEquals(expectedUtilDate(), (java.util.Date) rs.getObject(1)); + } - // Test the additional conversions introduced in JDBC41 for types setters // Test datetime2 column with target type TIMESTAMP ps.setObject(1, expectedUtilDate(), java.sql.Types.DATE); ps.execute(); ps.getMoreResults(); - rs = ps.getResultSet(); - // Go to the first row - rs.next(); - // Go to the row just inserted - rs.relative(++currentRow); - assertEquals(expected, rs.getDate(1)); + try (ResultSet rs = ps.getResultSet()) { + // Go to the first row + rs.next(); + // Go to the row just inserted + rs.relative(++currentRow); + assertEquals(expected, rs.getDate(1)); + } ps.setObject(1, expectedUtilDate(), java.sql.Types.TIMESTAMP); ps.execute(); ps.getMoreResults(); - rs = ps.getResultSet(); - // Go to the first row - rs.next(); - // Go to the row just inserted - rs.relative(++currentRow); - assertEquals(expectedTimestampMillisPrecision(), rs.getTimestamp(1)); + try (ResultSet rs = ps.getResultSet()) { + // Go to the first row + rs.next(); + // Go to the row just inserted + rs.relative(++currentRow); + assertEquals(expectedTimestampMillisPrecision(), rs.getTimestamp(1)); + } // Test the setNull() methods for different data type conversions ps.setNull(1, java.sql.Types.DATE); ps.execute(); ps.getMoreResults(); - rs = ps.getResultSet(); - // Go to the next row - rs.next(); - rs.relative(++currentRow); - // Read the column, first before calling rs.wasNull() - rs.getTimestamp(1); - assertEquals(true, rs.wasNull()); + try (ResultSet rs = ps.getResultSet()) { + // Go to the next row + rs.next(); + rs.relative(++currentRow); + // Read the column, first before calling rs.wasNull() + rs.getTimestamp(1); + assertEquals(true, rs.wasNull()); + } ps.setNull(1, java.sql.Types.TIMESTAMP); ps.execute(); ps.getMoreResults(); - rs = ps.getResultSet(); - // Go to the next row - rs.next(); - rs.relative(++currentRow); - // Read the column, first before calling rs.wasNull() - rs.getTimestamp(1); - assertEquals(true, rs.wasNull()); + try (ResultSet rs = ps.getResultSet()) { + // Go to the next row + rs.next(); + rs.relative(++currentRow); + // Read the column, first before calling rs.wasNull() + rs.getTimestamp(1); + assertEquals(true, rs.wasNull()); + } } void verifySettersCalendar(PreparedStatement ps) throws Exception { @@ -368,33 +371,33 @@ void verifySettersCalendar(PreparedStatement ps) throws Exception { ps.setObject(1, expectedCalendar()); ps.execute(); ps.getMoreResults(); - ResultSet rs = ps.getResultSet(); - rs.next(); - assertEquals(expected, rs.getDate(1)); - // Cannot test rs.getObject for the Calendar object type, as none of Time, Timestamp, Date - // or java.util.Date can be cast to a calendar + try (ResultSet rs = ps.getResultSet()) { + rs.next(); + assertEquals(expected, rs.getDate(1)); + } - // Test the additional conversions introduced in JDBC41 for types setters - // Test datetime2 column with target type TIMESTAMP + // test datetime2 column with target type TIMESTAMP ps.setObject(1, expectedCalendar(), java.sql.Types.DATE); ps.execute(); ps.getMoreResults(); - rs = ps.getResultSet(); - // Go to the first row - rs.next(); - // Go to the row just inserted - rs.relative(++currentRow); - assertEquals(expected, rs.getDate(1)); + try (ResultSet rs = ps.getResultSet()) { + // Go to the first row + rs.next(); + // Go to the row just inserted + rs.relative(++currentRow); + assertEquals(expected, rs.getDate(1)); + } ps.setObject(1, expectedCalendar(), java.sql.Types.TIMESTAMP); ps.execute(); ps.getMoreResults(); - rs = ps.getResultSet(); - // Go to the first row - rs.next(); - // Go to the row just inserted - rs.relative(++currentRow); - assertEquals(expectedTimestampMillisPrecision(), rs.getTimestamp(1)); + try (ResultSet rs = ps.getResultSet()) { + // Go to the first row + rs.next(); + // Go to the row just inserted + rs.relative(++currentRow); + assertEquals(expectedTimestampMillisPrecision(), rs.getTimestamp(1)); + } } void verifyCSGetters(CallableStatement cs) throws Exception { @@ -463,7 +466,6 @@ private Timestamp expectedTimestampMillisPrecision() { cal.set(Calendar.MILLISECOND, (nanos + 500000) / 1000000); Timestamp timestamp = new Timestamp(cal.getTimeInMillis()); - return timestamp; } @@ -504,10 +506,10 @@ void verifySetters(PreparedStatement ps) throws Exception { ps.setTime(1, expectedTime()); ps.execute(); ps.getMoreResults(); - ResultSet rs = ps.getResultSet(); - rs.next(); - - assertEquals(expectedTime(), rs.getTime(1)); + try (ResultSet rs = ps.getResultSet()) { + rs.next(); + assertEquals(expectedTime(), rs.getTime(1)); + } } void verifySettersUtilDate(PreparedStatement ps) throws Exception { @@ -515,57 +517,60 @@ void verifySettersUtilDate(PreparedStatement ps) throws Exception { ps.setObject(1, expectedUtilDate()); ps.execute(); ps.getMoreResults(); - ResultSet rs = ps.getResultSet(); - rs.next(); - assertEquals(expectedTime(), rs.getTime(1)); - assertEquals(expectedTimestampMillisPrecision(), rs.getTimestamp(1)); - assertEquals(expectedUtilDate(), (java.util.Date) rs.getObject(1)); + try (ResultSet rs = ps.getResultSet()) { + rs.next(); + assertEquals(expectedTime(), rs.getTime(1)); + assertEquals(expectedTimestampMillisPrecision(), rs.getTimestamp(1)); + assertEquals(expectedUtilDate(), (java.util.Date) rs.getObject(1)); + } - // Test the additional conversions introduced in JDBC41 for types setters // Test datetime2 column with target type TIMESTAMP ps.setObject(1, expectedUtilDate(), java.sql.Types.TIME); ps.execute(); ps.getMoreResults(); - rs = ps.getResultSet(); - // Go to the first row - rs.next(); - // Go to the row just inserted - rs.relative(++currentRow); - assertEquals(expectedTime(), rs.getTime(1)); + try (ResultSet rs = ps.getResultSet()) { + // Go to the first row + rs.next(); + // Go to the row just inserted + rs.relative(++currentRow); + assertEquals(expectedTime(), rs.getTime(1)); + } ps.setObject(1, expectedUtilDate(), java.sql.Types.TIMESTAMP); ps.execute(); ps.getMoreResults(); - rs = ps.getResultSet(); - // Go to the first row - rs.next(); - // Go to the row just inserted - rs.relative(++currentRow); - assertEquals(expectedTimestampMillisPrecision(), rs.getTimestamp(1)); + try (ResultSet rs = ps.getResultSet()) { + // Go to the first row + rs.next(); + // Go to the row just inserted + rs.relative(++currentRow); + assertEquals(expectedTimestampMillisPrecision(), rs.getTimestamp(1)); + } // Test the setNull() methods for different data type conversions ps.setNull(1, java.sql.Types.TIME); ps.execute(); ps.getMoreResults(); - rs = ps.getResultSet(); - // Go to the next row - rs.next(); - rs.relative(++currentRow); - // Read the column, first before calling rs.wasNull() - rs.getTimestamp(1); - assertEquals(true, rs.wasNull()); + try (ResultSet rs = ps.getResultSet()) { + // Go to the next row + rs.next(); + rs.relative(++currentRow); + // Read the column, first before calling rs.wasNull() + rs.getTimestamp(1); + assertEquals(true, rs.wasNull()); + } ps.setNull(1, java.sql.Types.TIMESTAMP); ps.execute(); ps.getMoreResults(); - rs = ps.getResultSet(); - // Go to the next row - rs.next(); - rs.relative(++currentRow); - // Read the column, first before calling rs.wasNull() - rs.getTimestamp(1); - assertEquals(true, rs.wasNull()); - + try (ResultSet rs = ps.getResultSet()) { + // Go to the next row + rs.next(); + rs.relative(++currentRow); + // Read the column, first before calling rs.wasNull() + rs.getTimestamp(1); + assertEquals(true, rs.wasNull()); + } } void verifySettersCalendar(PreparedStatement ps) throws Exception { @@ -573,35 +578,34 @@ void verifySettersCalendar(PreparedStatement ps) throws Exception { ps.setObject(1, expectedCalendar()); ps.execute(); ps.getMoreResults(); - ResultSet rs = ps.getResultSet(); - rs.next(); - assertEquals(expectedTime(), rs.getTime(1)); - - assertEquals(rs.getTimestamp(1), expectedTimestampMillisPrecision()); - // Cannot test rs.getObject for the Calendar object type, as none of Time, Timestamp, Date - // or java.util.Date can be cast to a calendar + try (ResultSet rs = ps.getResultSet()) { + rs.next(); + assertEquals(expectedTime(), rs.getTime(1)); + assertEquals(expectedTimestampMillisPrecision(), rs.getTimestamp(1)); + } - // Test the additional conversions introduced in JDBC41 for types setters - // Test datetime2 column with target type TIMESTAMP + // test datetime2 column with target type TIMESTAMP ps.setObject(1, expectedCalendar(), java.sql.Types.TIME); ps.execute(); ps.getMoreResults(); - rs = ps.getResultSet(); - // Go to the first row - rs.next(); - // Go to the row just inserted - rs.relative(++currentRow); - assertEquals(expectedTime(), rs.getTime(1)); + try (ResultSet rs = ps.getResultSet()) { + // Go to the first row + rs.next(); + // Go to the row just inserted + rs.relative(++currentRow); + assertEquals(expectedTime(), rs.getTime(1)); + } ps.setObject(1, expectedCalendar(), java.sql.Types.TIMESTAMP); ps.execute(); ps.getMoreResults(); - rs = ps.getResultSet(); - // Go to the first row - rs.next(); - // Go to the row just inserted - rs.relative(++currentRow); - assertEquals(expectedTimestampMillisPrecision(), rs.getTimestamp(1)); + try (ResultSet rs = ps.getResultSet()) { + // Go to the first row + rs.next(); + // Go to the row just inserted + rs.relative(++currentRow); + assertEquals(expectedTimestampMillisPrecision(), rs.getTimestamp(1)); + } } void verifyCSGetters(CallableStatement cs) throws Exception { @@ -695,9 +699,6 @@ private Timestamp expectedTimestamp() { private Timestamp expectedTimestampMillisPrecision() { Timestamp timestamp = new Timestamp(utcMillis); - // Cannot set the nanos to 0, as per doc " the fractional seconds are stored in the nanos field of the - // Timestamp object." - // timestamp.setNanos(0); return timestamp; } @@ -719,12 +720,9 @@ void verifyRSGetters(ResultSet rs) throws Exception { } void verifyRSUpdaters(ResultSet rs) throws Exception { - // Unlike PreparedStatement.setTimestamp(), there is no ResultSet.updateTimestamp() - // that takes a Calendar argument for passing in the time zone. ResultSet.updateTimestamp() - // always uses the VM default time zone. So we have to temporarily change it while doing - // the update. TimeZone tzDefault = TimeZone.getDefault(); try { + // temporary change default time zone while doing the update TimeZone.setDefault(tz); // Update the timestamp value with this value's time zone (set as the VM default above) @@ -738,26 +736,29 @@ void verifyRSUpdaters(ResultSet rs) throws Exception { TimeZone.setDefault(tzDefault); } - // Verify the update (after restoring the default time zone) using the getTimestamp - // variant that takes a time zone argument (as a Calendar) + /* + * Verify the update (after restoring the default time zone) using the getTimestamp variant that takes a + * time zone argument (as a Calendar) + */ assertEquals(expectedTimestamp(), rs.getTimestamp(1, Calendar.getInstance(tz))); } void verifySetters(PreparedStatement ps) throws Exception { - // Verify PreparedStatement.setTimestamp with default time zone first. - // Temporarily change the VM default time zone as in the ResultSet verifier. TimeZone tzDefault = TimeZone.getDefault(); try { + // temporary change default time zone TimeZone.setDefault(tz); ps.setTimestamp(1, expectedTimestamp()); ps.execute(); ps.getMoreResults(); - ResultSet rs = ps.getResultSet(); - rs.next(); + try (ResultSet rs = ps.getResultSet()) { + rs.next(); - assertEquals(expectedTimestamp(), rs.getTimestamp(1)); + assertEquals(expectedTimestamp(), rs.getTimestamp(1)); + } } finally { + // Restore the original default time zone TimeZone.setDefault(tzDefault); } @@ -765,10 +766,10 @@ void verifySetters(PreparedStatement ps) throws Exception { ps.setTimestamp(1, expectedTimestamp(), Calendar.getInstance(tz)); ps.execute(); ps.getMoreResults(); - ResultSet rs = ps.getResultSet(); - rs.next(); - - assertEquals(expectedTimestamp(), rs.getTimestamp(1, Calendar.getInstance(tz))); + try (ResultSet rs = ps.getResultSet()) { + rs.next(); + assertEquals(expectedTimestamp(), rs.getTimestamp(1, Calendar.getInstance(tz))); + } } void verifySettersUtilDate(PreparedStatement ps) throws Exception { @@ -776,55 +777,59 @@ void verifySettersUtilDate(PreparedStatement ps) throws Exception { ps.setObject(1, expectedUtilDate()); ps.execute(); ps.getMoreResults(); - ResultSet rs = ps.getResultSet(); - rs.next(); - assertEquals(expectedTimestampMillisPrecision(), rs.getTimestamp(1)); - assertEquals(expectedUtilDate(), (java.util.Date) rs.getObject(1)); + try (ResultSet rs = ps.getResultSet()) { + rs.next(); + assertEquals(expectedTimestampMillisPrecision(), rs.getTimestamp(1)); + assertEquals(expectedUtilDate(), (java.util.Date) rs.getObject(1)); + } - // Test the additional conversions introduced in JDBC41 for types setters // Test datetime2 column with target type TIMESTAMP ps.setObject(1, expectedUtilDate(), java.sql.Types.TIMESTAMP); ps.execute(); ps.getMoreResults(); - rs = ps.getResultSet(); - // Go to the next row - rs.next(); - rs.relative(++currentRow); - assertEquals(expectedTimestampMillisPrecision(), rs.getTimestamp(1)); + try (ResultSet rs = ps.getResultSet()) { + // Go to the next row + rs.next(); + rs.relative(++currentRow); + assertEquals(expectedTimestampMillisPrecision(), rs.getTimestamp(1)); + } // Test the setNull() methods for different data type conversions ps.setNull(1, java.sql.Types.TIME); ps.execute(); ps.getMoreResults(); - rs = ps.getResultSet(); - // Go to the next row - rs.next(); - rs.relative(++currentRow); - // Read the column, first before calling rs.wasNull() - rs.getTimestamp(1); - assertEquals(true, rs.wasNull()); + try (ResultSet rs = ps.getResultSet()) { + // Go to the next row + rs.next(); + rs.relative(++currentRow); + // Read the column, first before calling rs.wasNull() + rs.getTimestamp(1); + assertEquals(true, rs.wasNull()); + } ps.setNull(1, java.sql.Types.DATE); ps.execute(); ps.getMoreResults(); - rs = ps.getResultSet(); - // Go to the next row - rs.next(); - rs.relative(++currentRow); - // Read the column, first before calling rs.wasNull() - rs.getTimestamp(1); - assertEquals(true, rs.wasNull()); + try (ResultSet rs = ps.getResultSet()) { + // Go to the next row + rs.next(); + rs.relative(++currentRow); + // Read the column, first before calling rs.wasNull() + rs.getTimestamp(1); + assertEquals(true, rs.wasNull()); + } ps.setNull(1, java.sql.Types.TIMESTAMP); ps.execute(); ps.getMoreResults(); - rs = ps.getResultSet(); - // Go to the next row - rs.next(); - rs.relative(++currentRow); - // Read the column, first before calling rs.wasNull() - rs.getTimestamp(1); - assertEquals(true, rs.wasNull()); + try (ResultSet rs = ps.getResultSet()) { + // Go to the next row + rs.next(); + rs.relative(++currentRow); + // Read the column, first before calling rs.wasNull() + rs.getTimestamp(1); + assertEquals(true, rs.wasNull()); + } } void verifySettersCalendar(PreparedStatement ps) throws Exception { @@ -832,22 +837,21 @@ void verifySettersCalendar(PreparedStatement ps) throws Exception { ps.setObject(1, expectedCalendar()); ps.execute(); ps.getMoreResults(); - ResultSet rs = ps.getResultSet(); - rs.next(); - assertEquals(expectedTimestampMillisPrecision(), rs.getTimestamp(1)); - // Cannot test rs.getObject for the Calendar object type, as none of Time, Timestamp, Date - // or java.util.Date can be cast to a calendar + try (ResultSet rs = ps.getResultSet()) { + rs.next(); + assertEquals(expectedTimestampMillisPrecision(), rs.getTimestamp(1)); + } - // Test the additional conversions introduced in JDBC41 for types setters // Test datetime2 column with target type TIMESTAMP ps.setObject(1, expectedCalendar(), java.sql.Types.TIMESTAMP); ps.execute(); ps.getMoreResults(); - rs = ps.getResultSet(); - // Go to the next row - rs.next(); - rs.relative(++currentRow); - assertEquals(expectedTimestampMillisPrecision(), rs.getTimestamp(1)); + try (ResultSet rs = ps.getResultSet()) { + // Go to the next row + rs.next(); + rs.relative(++currentRow); + assertEquals(expectedTimestampMillisPrecision(), rs.getTimestamp(1)); + } } void verifyCSGetters(CallableStatement cs) throws Exception { @@ -946,33 +950,6 @@ private java.sql.Time expectedTime() { return new java.sql.Time(cal.getTimeInMillis()); } - private Timestamp expectedTimestamp() { - Calendar cal = Calendar.getInstance(new SimpleTimeZone(1000 * 60 * dto.getMinutesOffset(), ""), Locale.US); - cal.set(Calendar.ZONE_OFFSET, 1000 * 60 * dto.getMinutesOffset()); - cal.setTimeInMillis(dto.getTimestamp().getTime()); - if (dto.getTimestamp().getNanos() % 1000000 >= 500000) - cal.add(Calendar.MILLISECOND, 1); - return new Timestamp(cal.getTimeInMillis()); - } - - private java.util.Date expectedUtilDate() { - Calendar cal = Calendar.getInstance(new SimpleTimeZone(1000 * 60 * dto.getMinutesOffset(), ""), Locale.US); - cal.set(Calendar.ZONE_OFFSET, 1000 * 60 * dto.getMinutesOffset()); - cal.setTimeInMillis(dto.getTimestamp().getTime()); - if (dto.getTimestamp().getNanos() % 1000000 >= 500000) - cal.add(Calendar.MILLISECOND, 1); - return new java.util.Date(cal.getTimeInMillis()); - } - - private java.util.Calendar expectedCalendar() { - Calendar cal = Calendar.getInstance(new SimpleTimeZone(1000 * 60 * dto.getMinutesOffset(), ""), Locale.US); - cal.set(Calendar.ZONE_OFFSET, 1000 * 60 * dto.getMinutesOffset()); - cal.setTimeInMillis(dto.getTimestamp().getTime()); - if (dto.getTimestamp().getNanos() % 1000000 >= 500000) - cal.add(Calendar.MILLISECOND, 1); - return cal; - } - void verifyRSGetters(ResultSet rs) throws Exception { assertEquals(expectedDate(), rs.getDate(1)); assertEquals(expectedTime(), rs.getTime(1)); @@ -992,10 +969,10 @@ void verifySetters(PreparedStatement ps) throws Exception { ((SQLServerPreparedStatement) ps).setDateTimeOffset(1, dto); ps.execute(); ps.getMoreResults(); - ResultSet rs = ps.getResultSet(); - rs.next(); - - assertEquals(dto, ((SQLServerResultSet) rs).getDateTimeOffset(1)); + try (ResultSet rs = ps.getResultSet()) { + rs.next(); + assertEquals(dto, ((SQLServerResultSet) rs).getDateTimeOffset(1)); + } } void verifySettersUtilDate(PreparedStatement ps) throws Exception { @@ -1019,7 +996,6 @@ void verifyCSGetters(CallableStatement cs) throws Exception { cs.registerOutParameter(2, microsoft.sql.Types.DATETIMEOFFSET); cs.execute(); assertEquals(dto, ((SQLServerCallableStatement) cs).getDateTimeOffset(2)); - assertEquals(dto, cs.getObject(2)); } } @@ -1045,9 +1021,10 @@ enum TestValue { PRE_CUTOVER(new DateValue("1582-10-04")), - // Dates in the Gregorian cutover date range appear as 10 days later than what they should. - // This behavior is consistent with other JDBC drivers, such as IBM's: - // http://publib.boulder.ibm.com/infocenter/dzichelp/v2r2/index.jsp?topic=/com.ibm.db2.doc.java/com.ibm.db2.luw.apdv.java.doc/doc/r0053436.htm + /* + * Dates in the Gregorian cutover date range appear as 10 days later than what they should. This behavior is + * consistent with other JDBC drivers, such as IBM + */ CUTOVER_START(new DateValue("1582-10-05")), CUTOVER_END(new DateValue("1582-10-14")), @@ -1060,9 +1037,9 @@ enum TestValue { // First "fast path" date POST_CUTOVER_PLUS_2(new DateValue("1582-10-17")), - // VSTS 403522 - // Post-cutover date requiring preservation of "wall calendar" date - // in computing Calendar.DAY_OF_YEAR. + /* + * Post-cutover date requiring preservation of "wall calendar" date in computing Calendar.DAY_OF_YEAR + */ POST_CUTOVER_NOVEMBER(new DateTime2Value("1582-11-15 15:07:09.0810000")), A_RECENT_DATE(new DateValue("2009-10-20")), @@ -1124,9 +1101,8 @@ public void testParameterMetaData() throws Exception { } ; } - /** - * VSTS 411537 - CS.setObject(timestamp, TIME)/registerOutParam(TIME) on time backend type seems to ignore - * sendTimeAsDatetime knob. + /* + * test CS.setObject(timestamp, TIME)/registerOutParam(TIME) with sendTimeAsDatetime */ public void testSendTimestampAsTimeAsDatetime() throws Exception { try (Connection conn = DriverManager.getConnection(connectionString + ";sendTimeAsDatetime=true")) { @@ -1137,13 +1113,14 @@ public void testSendTimestampAsTimeAsDatetime() throws Exception { try (CallableStatement cs = conn.prepareCall("{call " + escapedProcName + "(?,?)}")) { - // Set up a timestamp with a time component that is the last millisecond of the day... + // Set up a timestamp with a time component that is the last millisecond of the day Timestamp ts = Timestamp.valueOf("2010-02-15 23:59:59.999"); - // ... and send that timestamp to the server using the TIME SQL type rather than TIMESTAMP. - // If the driver is doing the right thing, it strips the date portion and, because - // sendTimeAsDatetime=true, rounds the resulting time value to midnight because it should - // be sending a DATETIME which has only 1/300s accuracy. + /* + * send the timestamp to the server using the TIME SQL type rather than TIMESTAMP. The driver will + * strip the date portion and, because sendTimeAsDatetime=true, round the resulting time value to + * midnight because it should be sending a DATETIME which has only 1/300s accuracy + */ cs.setObject(1, ts, java.sql.Types.TIME); cs.registerOutParameter(2, java.sql.Types.TIME); cs.execute(); @@ -1152,7 +1129,6 @@ public void testSendTimestampAsTimeAsDatetime() throws Exception { java.sql.Time timeOut = cs.getTime(2); Timestamp tsOut = new Timestamp(timeOut.getTime()); assertEquals("1970-01-01 00:00:00.0", tsOut.toString()); - } } finally { try (Statement stmt = conn.createStatement()) { @@ -1162,8 +1138,10 @@ public void testSendTimestampAsTimeAsDatetime() throws Exception { } } - // 507919 - Sending Timestamp to the server via an updater does not result in the same behavior as a setter wrt - // double-rounding of fractional seconds + /* + * test sending Timestamp to the server via an updater does not result in the same behavior as a setter wrt + * double-rounding of fractional seconds + */ public void testDoubleRounding() throws Exception { try (Connection conn = DriverManager.getConnection(connectionString)) { @@ -1226,20 +1204,18 @@ public void testDoubleRounding() throws Exception { } } - /** + /* * Tests "fail fast" SQLException path when a Japanese imperial calendar is used with values representing the first - * year of an imperial era. - * - * See for more details: http://java.sun.com/javase/6/docs/technotes/guides/intl/calendar.doc.html + * year of an imperial era. See for more details: + * http://java.sun.com/javase/6/docs/technotes/guides/intl/calendar.doc.html */ public void testWithJapaneseImperialCalendar() throws Exception { - // From http://java.sun.com/javase/6/docs/api/java/util/Locale.html : - // "Note: When you ask for a resource for a particular locale, - // you get back the best available match, not necessarily precisely what you asked for. - // For more information, look at ResourceBundle." - // - // Japanese Imperial locale does not exist with some VMs. In these VMs, Locale.US is - // substituted as "best available match". + /* + * From http://java.sun.com/javase/6/docs/api/java/util/Locale.html : "Note: When you ask for a resource for a + * particular locale, you get back the best available match, not necessarily precisely what you asked for. For + * more information, look at ResourceBundle." Japanese Imperial locale does not exist with some VMs. In these + * VMs, Locale.US is substituted as "best available match". + */ Locale japaneseImperialLocale = new Locale("ja", "JP", "JP"); Calendar japaneseImperialCalendar = Calendar.getInstance(japaneseImperialLocale); @@ -1264,8 +1240,10 @@ public void testWithJapaneseImperialCalendar() throws Exception { try (PreparedStatement ps = conn.prepareStatement("SELECT CAST(? AS VARCHAR(40))")) { Timestamp ts; - // Set second day of first year of Taisho era (1912 Gregorian) - // Note: Taisho era began July 30, 1912 Gregorian; second day of that year was July 31. + /* + * Set second day of first year of Taisho era (1912 Gregorian) Note: Taisho era began July 30, 1912 + * Gregorian; second day of that year was July 31. + */ japaneseImperialCalendar.clear(); japaneseImperialCalendar.set(Calendar.ERA, 2); // Taisho -> ERA 2 japaneseImperialCalendar.set(Calendar.YEAR, 1); @@ -1365,21 +1343,26 @@ public void testWithThaiLocale() throws Exception { // Test setter conversions try (PreparedStatement ps = conn.prepareStatement("SELECT CAST(? AS VARCHAR(40))")) { - // Need to use the following constructor for running against IBM JVM. Here, year should be year-1900, - // month - // is from 0-11. + /* + * Need to use the following constructor for running against IBM JVM. Here, year should be year-1900, // + * month // is from 0-11. + */ Timestamp ts = new Timestamp(System.currentTimeMillis()); - // Test PreparedStatement with Timestamp - // Value sent as DATETIME2; result should have 7 digits of subsecond precision) + /* + * Test PreparedStatement with Timestamp Value sent as DATETIME2; result should have 7 digits of + * subsecond precision) + */ ps.setTimestamp(1, ts); try (ResultSet rs = ps.executeQuery()) { rs.next(); assertEquals(tsFormat.format(ts), rs.getString(1)); } - // Test PreparedStatement with Time - // Value sent as DATETIME w/Unix Epoch as base date when sendTimeAsDatetime=true + /* + * Test PreparedStatement with Time Value sent as DATETIME w/Unix Epoch as base date when + * sendTimeAsDatetime=true + */ Time time = new Time(ts.getTime()); ps.setTime(1, time); try (ResultSet rs = ps.executeQuery()) { @@ -1425,7 +1408,7 @@ public void testWithThaiLocale() throws Exception { } } - // DCR 393826 - DCR Need base date compatibility for Time to DATETIMEx conversions with 2.0 driver + // test base date compatibility for Time to DATETIMEx conversions @Test public void testBaseDate() throws Exception { Timestamp ts; @@ -1457,8 +1440,7 @@ public void testBaseDate() throws Exception { } } - // VSTS 393831 - setTimestamp to DATETIMEOFFSET must yield a value in local time with UTC time zone offset - // (+00:00) + // test setTimestamp to DATETIMEOFFSET yields a value in local time with UTC time zone offset (+00:00) @Test public void testTimestampToDateTimeOffset() throws Exception { try (Connection conn = DriverManager.getConnection(connectionString); @@ -1473,9 +1455,10 @@ public void testTimestampToDateTimeOffset() throws Exception { } } - // VSTS 400431 - PS.setObject() on a datetime2 with values on or after 0700-02-29 have a value one day ahead - // stored - // in the server + /* + * test PS.setObject() on a datetime2 with values on or after 0700-02-29 have a value one day ahead stored in the + * server + */ @Test public void testJulianLeapYear() throws Exception { try (Connection conn = DriverManager.getConnection(connectionString + ";sendTimeAsDatetime=true"); @@ -1538,12 +1521,12 @@ public void testGregorianCutoverDateTime2() throws Exception { } } - // VSTS 393831 - setTimestamp to DATETIMEOFFSET must yield a value in local time with UTC time zone offset - // (+00:00) - // - // In this case, verify that SELECT with a WHERE clause doesn't fail due to mapping the Timestamp value to a - // SQL Server type that does not compare equal. For example, a DATETIMEOFFSET and DATETIME only compare equal - // if the DATETIMEOFFSET offset is 0 (UTC). + /* + * setTimestamp to DATETIMEOFFSET yields a value in local time with UTC time zone offset (+00:00) In this case, + * verify that SELECT with a WHERE clause does not fail due to mapping the Timestamp value to a SQL Server type that + * does not compare equal. For example, a DATETIMEOFFSET and DATETIME only compare equal if the DATETIMEOFFSET + * offset is 0 (UTC). + */ @Test public void testTimestampToDateTime() throws Exception { try (Connection conn = DriverManager.getConnection(connectionString); PreparedStatement ps = conn @@ -1555,9 +1538,6 @@ public void testTimestampToDateTime() throws Exception { } } - // testUpdateMisc - // - // Haphazard collection of bugs that popped up during unit testing (i.e. regression tests) @Test public void testUpdateMisc() throws Exception { try (SQLServerConnection conn = (SQLServerConnection) DriverManager @@ -1656,8 +1636,7 @@ public void testUpdateMisc() throws Exception { rs.updateRow(); assertEquals("6289-04-22 05:13:57.677", rs.getTimestamp(2).toString()); - // Update datetime with rounding-induced overflow from Time - // (should roll date part to 1/2/1970) + // Update datetime with rounding-induced overflow from Time (should roll date part to 1/2/1970) ts = Timestamp.valueOf("2010-01-18 23:59:59.999"); rs.updateTime(2, new java.sql.Time(ts.getTime())); rs.updateRow(); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java index d8b9316b8b..a76ee6580c 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java @@ -267,8 +267,8 @@ public void testGetObjectAsLocalDateTime() throws SQLException { final String testValueTime = "02:00:00.1234567"; final String testValueDateTime = testValueDate + "T" + testValueTime; - stmt.executeUpdate("CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName) - + " (id INT, dt2 DATETIME2)"); + stmt.executeUpdate( + "CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName) + " (id INT, dt2 DATETIME2)"); stmt.executeUpdate("INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableName) + " (id, dt2) VALUES (1, '" + testValueDateTime + "')"); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java index f08ce00b21..3cf7fe82b8 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/serial/DTOSerialTest.java @@ -166,7 +166,7 @@ private static void verifyMessedSerializationHelper(byte[] svalue) throws Except } } - // This function is used to make sure the hydrated is equal to original string and the initial DTO + // This is used to make sure the hydrated is equal to original string and the initial DTO private static void verifyDTOEqual(DateTimeOffset initial, DateTimeOffset hydrated) throws Exception { String initialStr = initial.toString(); String hydratedStr = hydrated.toString(); From 33bbc8487e867782125c8fb092b7d692889838c3 Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Sat, 22 Dec 2018 16:08:54 +0900 Subject: [PATCH 41/51] Add support for OffsetDateTime to be passed as 'type' in ResultSet.getObject() (#830) * Add support for OffsetDateTime to be passed as 'type' in ResultSet.getObject() * Typo fix. * Added OffsetTime support. Allow retrieving OffsetTime out of DATETIMEOFFSET column. * Added assertions for more coverage. --- .../sqlserver/jdbc/SQLServerResultSet.java | 14 ++++++++ .../java/microsoft/sql/DateTimeOffset.java | 12 +++++++ .../jdbc/resultset/ResultSetTest.java | 36 +++++++++++++++++++ 3 files changed, 62 insertions(+) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java index 1aef808039..caa6b4e683 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java @@ -2397,6 +2397,20 @@ public T getObject(int columnIndex, Class type) throws SQLException { returnValue = ldt.toLocalTime(); } } + } else if (type == java.time.OffsetDateTime.class) { + microsoft.sql.DateTimeOffset dateTimeOffset = getDateTimeOffset(columnIndex); + if (dateTimeOffset == null) { + returnValue = null; + } else { + returnValue = dateTimeOffset.getOffsetDateTime(); + } + } else if (type == java.time.OffsetTime.class) { + microsoft.sql.DateTimeOffset dateTimeOffset = getDateTimeOffset(columnIndex); + if (dateTimeOffset == null) { + returnValue = null; + } else { + returnValue = dateTimeOffset.getOffsetDateTime().toOffsetTime(); + } } else if (type == microsoft.sql.DateTimeOffset.class) { returnValue = getDateTimeOffset(columnIndex); } else if (type == UUID.class) { diff --git a/src/main/java/microsoft/sql/DateTimeOffset.java b/src/main/java/microsoft/sql/DateTimeOffset.java index fc5135b43d..170ddce427 100644 --- a/src/main/java/microsoft/sql/DateTimeOffset.java +++ b/src/main/java/microsoft/sql/DateTimeOffset.java @@ -215,6 +215,18 @@ public java.sql.Timestamp getTimestamp() { return timestamp; } + /** + * Returns OffsetDateTime equivalent to this DateTimeOffset object. + * + * @return OffsetDateTime equivalent to this DateTimeOffset object. + */ + public java.time.OffsetDateTime getOffsetDateTime() { + java.time.ZoneOffset zoneOffset = java.time.ZoneOffset.ofTotalSeconds(60 * minutesOffset); + java.time.LocalDateTime localDateTime = java.time.LocalDateTime.ofEpochSecond(utcMillis / 1000, nanos, + zoneOffset); + return java.time.OffsetDateTime.of(localDateTime, zoneOffset); + } + /** * Returns this DateTimeOffset object's offset value. * diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java index 366ca00ada..7670458cad 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/resultset/ResultSetTest.java @@ -24,6 +24,8 @@ import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; +import java.time.OffsetDateTime; +import java.time.OffsetTime; import java.util.TimeZone; import java.util.UUID; @@ -293,6 +295,40 @@ public void testGetObjectAsLocalDateTime() throws SQLException { } } + /** + * Tests getObject(n, java.time.OffsetDateTime.class) and getObject(n, java.time.OffsetTime.class). + * + * @throws SQLException + */ + @Test + public void testGetObjectAsOffsetDateTime() throws SQLException { + try (Connection con = DriverManager.getConnection(connectionString); Statement stmt = con.createStatement()) { + final String testValue = "2018-01-02T11:22:33.123456700+12:34"; + + stmt.executeUpdate("CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName) + + " (id INT PRIMARY KEY, dto DATETIMEOFFSET, dto2 DATETIMEOFFSET)"); + stmt.executeUpdate("INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableName) + + " (id, dto, dto2) VALUES (1, '" + testValue + "', null)"); + + try (ResultSet rs = stmt.executeQuery( + "SELECT dto, dto2 FROM " + AbstractSQLGenerator.escapeIdentifier(tableName) + " WHERE id=1")) { + rs.next(); + + OffsetDateTime expected = OffsetDateTime.parse(testValue); + OffsetDateTime actual = rs.getObject(1, OffsetDateTime.class); + assertEquals(expected, actual); + assertNull(rs.getObject(2, OffsetDateTime.class)); + + OffsetTime expectedTime = OffsetTime.parse(testValue.split("T")[1]); + OffsetTime actualTime = rs.getObject(1, OffsetTime.class); + assertEquals(expectedTime, actualTime); + assertNull(rs.getObject(2, OffsetTime.class)); + } finally { + TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(tableName), stmt); + } + } + } + /** * Tests ResultSet#isWrapperFor and ResultSet#unwrap. * From 3f9635aa8f42a656db0feddba7afc0eb5d494d39 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Mon, 24 Dec 2018 16:02:06 -0800 Subject: [PATCH 42/51] Feature | Active Directory MSI Authentication support (#838) --- .../sqlserver/jdbc/AuthenticationJNI.java | 1 - .../microsoft/sqlserver/jdbc/IOBuffer.java | 1 + .../sqlserver/jdbc/ISQLServerDataSource.java | 15 + .../sqlserver/jdbc/SQLServerADAL4JUtils.java | 40 +-- .../sqlserver/jdbc/SQLServerConnection.java | 256 ++++++++++++++++-- .../sqlserver/jdbc/SQLServerDataSource.java | 11 + .../sqlserver/jdbc/SQLServerDriver.java | 10 +- .../jdbc/SQLServerPooledConnection.java | 55 ++-- .../sqlserver/jdbc/SQLServerResource.java | 15 +- .../sqlserver/jdbc/SqlFedAuthToken.java | 58 ++-- .../com/microsoft/sqlserver/jdbc/Util.java | 4 +- 11 files changed, 369 insertions(+), 97 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/AuthenticationJNI.java b/src/main/java/com/microsoft/sqlserver/jdbc/AuthenticationJNI.java index 0364a3a78d..bd4a04df98 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/AuthenticationJNI.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/AuthenticationJNI.java @@ -59,7 +59,6 @@ static boolean isDllLoaded() { enabled = true; } catch (UnsatisfiedLinkError e) { temp = e; - authLogger.warning("Failed to load the sqljdbc_auth.dll cause : " + e.getMessage()); // This is not re-thrown on purpose - the constructor will terminate the properly with the appropriate error // string } finally { diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java index 60e976f6f4..48f622bea6 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java @@ -103,6 +103,7 @@ final class TDS { static final int TDS_FEDAUTH_LIBRARY_RESERVED = 0x7F; static final byte ADALWORKFLOW_ACTIVEDIRECTORYPASSWORD = 0x01; static final byte ADALWORKFLOW_ACTIVEDIRECTORYINTEGRATED = 0x02; + static final byte ADALWORKFLOW_ACTIVEDIRECTORYMSI = 0x03; static final byte FEDAUTH_INFO_ID_STSURL = 0x01; // FedAuthInfoData is token endpoint URL from which to acquire fed // auth token static final byte FEDAUTH_INFO_ID_SPN = 0x02; // FedAuthInfoData is the SPN to use for acquiring fed auth token diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerDataSource.java b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerDataSource.java index a0aedb4a56..af603ef7a6 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerDataSource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerDataSource.java @@ -805,4 +805,19 @@ public interface ISQLServerDataSource extends javax.sql.CommonDataSource { * indicates whether Bulk Copy API should be used for Batch Insert operations. */ public void setUseBulkCopyForBatchInsert(boolean useBulkCopyForBatchInsert); + + /** + * Sets the client id to be used to retrieve access token from MSI EndPoint. + * + * @param msiClientId + * Client ID of User Assigned Managed Identity + */ + public void setMSIClientId(String msiClientId); + + /** + * Returns the value for the connection property 'msiClientId'. + * + * @return msiClientId property value + */ + public String getMSIClientId(); } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java index f0418a60c9..a94ca3cc5e 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java @@ -37,25 +37,26 @@ static SqlFedAuthToken getSqlFedAuthToken(SqlFedAuthInfo fedAuthInfo, String use ActiveDirectoryAuthentication.JDBC_FEDAUTH_CLIENT_ID, user, password, null); AuthenticationResult authenticationResult = future.get(); - SqlFedAuthToken fedAuthToken = new SqlFedAuthToken(authenticationResult.getAccessToken(), - authenticationResult.getExpiresOnDate()); - return fedAuthToken; + return new SqlFedAuthToken(authenticationResult.getAccessToken(), authenticationResult.getExpiresOnDate()); } catch (MalformedURLException | InterruptedException e) { throw new SQLServerException(e.getMessage(), e); } catch (ExecutionException e) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_ADALExecution")); Object[] msgArgs = {user, authenticationString}; - // the cause error message uses \\n\\r which does not give correct format - // change it to \r\n to provide correct format + /* + * the cause error message uses \\n\\r which does not give correct format change it to \r\n to provide + * correct format + */ String correctedErrorMessage = e.getCause().getMessage().replaceAll("\\\\r\\\\n", "\r\n"); AuthenticationException correctedAuthenticationException = new AuthenticationException( correctedErrorMessage); - // SQLServerException is caused by ExecutionException, which is caused by - // AuthenticationException - // to match the exception tree before error message correction + /* + * SQLServerException is caused by ExecutionException, which is caused by AuthenticationException to match + * the exception tree before error message correction + */ ExecutionException correctedExecutionException = new ExecutionException(correctedAuthenticationException); throw new SQLServerException(form.format(msgArgs), null, 0, correctedExecutionException); @@ -69,8 +70,10 @@ static SqlFedAuthToken getSqlFedAuthTokenIntegrated(SqlFedAuthInfo fedAuthInfo, ExecutorService executorService = Executors.newFixedThreadPool(1); try { - // principal name does not matter, what matters is the realm name - // it gets the username in principal_name@realm_name format + /* + * principal name does not matter, what matters is the realm name it gets the username in + * principal_name@realm_name format + */ KerberosPrincipal kerberosPrincipal = new KerberosPrincipal("username"); String username = kerberosPrincipal.getName(); @@ -83,10 +86,8 @@ static SqlFedAuthToken getSqlFedAuthTokenIntegrated(SqlFedAuthInfo fedAuthInfo, ActiveDirectoryAuthentication.JDBC_FEDAUTH_CLIENT_ID, username, null, null); AuthenticationResult authenticationResult = future.get(); - SqlFedAuthToken fedAuthToken = new SqlFedAuthToken(authenticationResult.getAccessToken(), - authenticationResult.getExpiresOnDate()); - return fedAuthToken; + return new SqlFedAuthToken(authenticationResult.getAccessToken(), authenticationResult.getExpiresOnDate()); } catch (InterruptedException | IOException e) { throw new SQLServerException(e.getMessage(), e); } catch (ExecutionException e) { @@ -97,15 +98,18 @@ static SqlFedAuthToken getSqlFedAuthTokenIntegrated(SqlFedAuthInfo fedAuthInfo, // the case when Future's outcome has no AuthenticationResult but exception throw new SQLServerException(form.format(msgArgs), null); } else { - // the cause error message uses \\n\\r which does not give correct format - // change it to \r\n to provide correct format + /* + * the cause error message uses \\n\\r which does not give correct format change it to \r\n to provide + * correct format + */ String correctedErrorMessage = e.getCause().getMessage().replaceAll("\\\\r\\\\n", "\r\n"); AuthenticationException correctedAuthenticationException = new AuthenticationException( correctedErrorMessage); - // SQLServerException is caused by ExecutionException, which is caused by - // AuthenticationException - // to match the exception tree before error message correction + /* + * SQLServerException is caused by ExecutionException, which is caused by AuthenticationException to + * match the exception tree before error message correction + */ ExecutionException correctedExecutionException = new ExecutionException( correctedAuthenticationException); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index dd152b9fcf..0807ae17dc 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -6,12 +6,18 @@ package com.microsoft.sqlserver.jdbc; import static java.nio.charset.StandardCharsets.UTF_16LE; +import static java.nio.charset.StandardCharsets.UTF_8; +import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.net.DatagramPacket; import java.net.DatagramSocket; +import java.net.HttpURLConnection; import java.net.InetAddress; import java.net.SocketException; +import java.net.URL; import java.net.UnknownHostException; import java.sql.CallableStatement; import java.sql.Connection; @@ -25,8 +31,13 @@ import java.sql.SQLXML; import java.sql.Savepoint; import java.sql.Statement; +import java.text.DateFormat; import java.text.MessageFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; import java.util.Enumeration; import java.util.HashMap; import java.util.LinkedList; @@ -37,6 +48,7 @@ import java.util.UUID; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.Executor; +import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; @@ -324,10 +336,6 @@ private static int[] locateParams(String sql) { return parameterPositions.stream().mapToInt(Integer::valueOf).toArray(); } - SqlFedAuthToken getAuthenticationResult() { - return fedAuthToken; - } - /** * Encapsulates the data to be sent to the server as part of Federated Authentication Feature Extension. */ @@ -342,13 +350,16 @@ class FederatedAuthenticationFeatureExtensionData { this.libraryType = libraryType; this.fedAuthRequiredPreLoginResponse = fedAuthRequiredPreLoginResponse; - switch (authenticationString.toUpperCase(Locale.ENGLISH).trim()) { + switch (authenticationString.toUpperCase(Locale.ENGLISH)) { case "ACTIVEDIRECTORYPASSWORD": this.authentication = SqlAuthentication.ActiveDirectoryPassword; break; case "ACTIVEDIRECTORYINTEGRATED": this.authentication = SqlAuthentication.ActiveDirectoryIntegrated; break; + case "ACTIVEDIRECTORYMSI": + this.authentication = SqlAuthentication.ActiveDirectoryMSI; + break; default: assert (false); MessageFormat form = new MessageFormat( @@ -378,7 +389,12 @@ public String toString() { class ActiveDirectoryAuthentication { static final String JDBC_FEDAUTH_CLIENT_ID = "7f98cb04-cd1e-40df-9140-3bf7e2cea4db"; + static final String AZURE_REST_MSI_URL = "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01"; static final String ADAL_GET_ACCESS_TOKEN_FUNCTION_NAME = "ADALGetAccessToken"; + static final String ACCESS_TOKEN_IDENTIFIER = "\"access_token\":\""; + static final String ACCESS_TOKEN_EXPIRES_IN_IDENTIFIER = "\"expires_in\":\""; + static final String ACCESS_TOKEN_EXPIRES_ON_IDENTIFIER = "\"expires_on\":\""; + static final String ACCESS_TOKEN_EXPIRES_ON_DATE_FORMAT = "M/d/yyyy h:mm:ss a X"; static final int GET_ACCESS_TOKEN_SUCCESS = 0; static final int GET_ACCESS_TOKEN_INVALID_GRANT = 1; static final int GET_ACCESS_TOKEN_TANSISENT_ERROR = 2; @@ -1051,12 +1067,17 @@ void checkClosed() throws SQLServerException { SQLServerException.makeFromDriverError(null, null, SQLServerException.getErrString("R_connectionIsClosed"), null, false); } + } + protected boolean needsReconnect() throws SQLServerException { + // Check if federated Authentication is in use if (null != fedAuthToken) { - if (Util.checkIfNeedNewAccessToken(this)) { - connect(this.activeConnectionProperties, null); + // Check if access token is about to expire soon + if (Util.checkIfNeedNewAccessToken(this, fedAuthToken.expiresOn)) { + return true; } } + return false; } /** @@ -1527,7 +1548,7 @@ Connection connectInternal(Properties propsIn, if (sPropValue == null) { sPropValue = SQLServerDriverStringProperty.AUTHENTICATION.getDefaultValue(); } - authenticationString = SqlAuthentication.valueOfString(sPropValue).toString(); + authenticationString = SqlAuthentication.valueOfString(sPropValue).toString().trim(); if (integratedSecurity && !authenticationString.equalsIgnoreCase(SqlAuthentication.NotSpecified.toString())) { @@ -1565,6 +1586,19 @@ Connection connectInternal(Properties propsIn, null); } + if (authenticationString.equalsIgnoreCase(SqlAuthentication.ActiveDirectoryMSI.toString()) + && ((!activeConnectionProperties.getProperty(SQLServerDriverStringProperty.USER.toString()) + .isEmpty()) + || (!activeConnectionProperties + .getProperty(SQLServerDriverStringProperty.PASSWORD.toString()).isEmpty()))) { + if (connectionlogger.isLoggable(Level.SEVERE)) { + connectionlogger.severe( + toString() + " " + SQLServerException.getErrString("R_MSIAuthenticationWithUserPassword")); + } + throw new SQLServerException(SQLServerException.getErrString("R_MSIAuthenticationWithUserPassword"), + null); + } + if (authenticationString.equalsIgnoreCase(SqlAuthentication.SqlPassword.toString()) && ((activeConnectionProperties.getProperty(SQLServerDriverStringProperty.USER.toString()) .isEmpty()) @@ -1840,6 +1874,12 @@ else if (0 == requestedPacketSize) activeConnectionProperties.setProperty(sPropKey, SSLProtocol.valueOfString(sPropValue).toString()); } + sPropKey = SQLServerDriverStringProperty.MSI_CLIENT_ID.toString(); + sPropValue = activeConnectionProperties.getProperty(sPropKey); + if (null != sPropValue) { + activeConnectionProperties.setProperty(sPropKey, sPropValue); + } + FailoverInfo fo = null; String databaseNameProperty = SQLServerDriverStringProperty.DATABASE_NAME.toString(); String serverNameProperty = SQLServerDriverStringProperty.SERVER_NAME.toString(); @@ -2400,7 +2440,7 @@ private void connectHelper(ServerPortPlaceHolder serverInfo, int timeOutsliceInM */ void Prelogin(String serverName, int portNumber) throws SQLServerException { // Build a TDS Pre-Login packet to send to the server. - if ((!authenticationString.trim().equalsIgnoreCase(SqlAuthentication.NotSpecified.toString())) + if ((!authenticationString.equalsIgnoreCase(SqlAuthentication.NotSpecified.toString())) || (null != accessTokenInByte)) { fedAuthRequiredByUser = true; } @@ -3490,6 +3530,9 @@ int writeFedAuthFeatureRequest(boolean write, TDSWriter tdsWriter, case ActiveDirectoryIntegrated: workflow = TDS.ADALWORKFLOW_ACTIVEDIRECTORYINTEGRATED; break; + case ActiveDirectoryMSI: + workflow = TDS.ADALWORKFLOW_ACTIVEDIRECTORYMSI; + break; default: assert (false); // Unrecognized Authentication type for fedauth ADAL request break; @@ -3562,8 +3605,9 @@ private void logon(LogonCommand command) throws SQLServerException { // for FEDAUTHREQUIRED option indicates Federated Authentication is required, we have to insert FedAuth Feature // Extension // in Login7, indicating the intent to use Active Directory Authentication Library for SQL Server. - if (authenticationString.trim().equalsIgnoreCase(SqlAuthentication.ActiveDirectoryPassword.toString()) - || (authenticationString.trim().equalsIgnoreCase(SqlAuthentication.ActiveDirectoryIntegrated.toString()) + if (authenticationString.equalsIgnoreCase(SqlAuthentication.ActiveDirectoryPassword.toString()) + || ((authenticationString.equalsIgnoreCase(SqlAuthentication.ActiveDirectoryIntegrated.toString()) + || authenticationString.equalsIgnoreCase(SqlAuthentication.ActiveDirectoryMSI.toString())) && fedAuthRequiredPreLoginResponse)) { federatedAuthenticationInfoRequested = true; fedAuthFeatureExtensionData = new FederatedAuthenticationFeatureExtensionData(TDS.TDS_FEDAUTH_LIBRARY_ADAL, @@ -3982,16 +4026,16 @@ final void processFedAuthInfo(TDSReader tdsReader, TDSTokenHandler tdsTokenHandl final class FedAuthTokenCommand extends UninterruptableTDSCommand { TDSTokenHandler tdsTokenHandler = null; - SqlFedAuthToken fedAuthToken = null; + SqlFedAuthToken sqlFedAuthToken = null; - FedAuthTokenCommand(SqlFedAuthToken fedAuthToken, TDSTokenHandler tdsTokenHandler) { + FedAuthTokenCommand(SqlFedAuthToken sqlFedAuthToken, TDSTokenHandler tdsTokenHandler) { super("FedAuth"); this.tdsTokenHandler = tdsTokenHandler; - this.fedAuthToken = fedAuthToken; + this.sqlFedAuthToken = sqlFedAuthToken; } final boolean doExecute() throws SQLServerException { - sendFedAuthToken(this, fedAuthToken, tdsTokenHandler); + sendFedAuthToken(this, sqlFedAuthToken, tdsTokenHandler); return true; } } @@ -4003,8 +4047,10 @@ final boolean doExecute() throws SQLServerException { void onFedAuthInfo(SqlFedAuthInfo fedAuthInfo, TDSTokenHandler tdsTokenHandler) throws SQLServerException { assert (null != activeConnectionProperties.getProperty(SQLServerDriverStringProperty.USER.toString()) && null != activeConnectionProperties.getProperty(SQLServerDriverStringProperty.PASSWORD.toString())) - || ((authenticationString.trim().equalsIgnoreCase( - SqlAuthentication.ActiveDirectoryIntegrated.toString()) && fedAuthRequiredPreLoginResponse)); + || (authenticationString.equalsIgnoreCase(SqlAuthentication.ActiveDirectoryIntegrated.toString()) + || authenticationString.equalsIgnoreCase(SqlAuthentication.ActiveDirectoryMSI.toString()) + && fedAuthRequiredPreLoginResponse); + assert null != fedAuthInfo; attemptRefreshTokenLocked = true; @@ -4031,14 +4077,20 @@ private SqlFedAuthToken getFedAuthToken(SqlFedAuthInfo fedAuthInfo) throws SQLSe int sleepInterval = 100; while (true) { - if (authenticationString.trim().equalsIgnoreCase(SqlAuthentication.ActiveDirectoryPassword.toString())) { + if (authenticationString.equalsIgnoreCase(SqlAuthentication.ActiveDirectoryPassword.toString())) { + validateAdalLibrary("R_ADALMissing"); fedAuthToken = SQLServerADAL4JUtils.getSqlFedAuthToken(fedAuthInfo, user, password, authenticationString); // Break out of the retry loop in successful case. break; - } else if (authenticationString.trim() - .equalsIgnoreCase(SqlAuthentication.ActiveDirectoryIntegrated.toString())) { + } else if (authenticationString.equalsIgnoreCase(SqlAuthentication.ActiveDirectoryMSI.toString())) { + fedAuthToken = getMSIAuthToken(fedAuthInfo.spn, + activeConnectionProperties.getProperty(SQLServerDriverStringProperty.MSI_CLIENT_ID.toString())); + + // Break out of the retry loop in successful case. + break; + } else if (authenticationString.equalsIgnoreCase(SqlAuthentication.ActiveDirectoryIntegrated.toString())) { // If operating system is windows and sqljdbc_auth is loaded then choose the DLL authentication. if (System.getProperty("os.name").toLowerCase(Locale.ENGLISH).startsWith("windows") @@ -4051,11 +4103,9 @@ private SqlFedAuthToken getFedAuthToken(SqlFedAuthInfo fedAuthInfo) throws SQLSe // AccessToken should not be null. assert null != dllInfo.accessTokenBytes; - byte[] accessTokenFromDLL = dllInfo.accessTokenBytes; String accessToken = new String(accessTokenFromDLL, UTF_16LE); - fedAuthToken = new SqlFedAuthToken(accessToken, dllInfo.expiresIn); // Break out of the retry loop in successful case. @@ -4115,6 +4165,8 @@ private SqlFedAuthToken getFedAuthToken(SqlFedAuthInfo fedAuthInfo) throws SQLSe // so we don't need to check the // OS version here. else { + // Check if ADAL4J library is available + validateAdalLibrary("R_DLLandADALMissing"); fedAuthToken = SQLServerADAL4JUtils.getSqlFedAuthTokenIntegrated(fedAuthInfo, authenticationString); } // Break out of the retry loop in successful case. @@ -4125,6 +4177,166 @@ private SqlFedAuthToken getFedAuthToken(SqlFedAuthInfo fedAuthInfo) throws SQLSe return fedAuthToken; } + private void validateAdalLibrary(String errorMessage) throws SQLServerException { + try { + Class.forName("com.microsoft.aad.adal4j.AuthenticationContext"); + } catch (ClassNotFoundException e) { + // throw Exception for missing libraries + MessageFormat form = new MessageFormat(SQLServerException.getErrString(errorMessage)); + throw new SQLServerException(form.format(new Object[] {authenticationString}), null, 0, null); + } + } + + private SqlFedAuthToken getMSIAuthToken(String resource, String msiClientId) throws SQLServerException { + // IMDS upgrade time can take up to 70s + final int imdsUpgradeTimeInMs = 70 * 1000; + final List retrySlots = new ArrayList<>(); + final String msiEndpoint = System.getenv("MSI_ENDPOINT"); + final String msiSecret = System.getenv("MSI_SECRET"); + + StringBuilder urlString = new StringBuilder(); + int retry = 1, maxRetry = 1; + + /* + * isAzureFunction is used for identifying if the current client application is running in a Virtual Machine + * (without MSI environment variables) or App Service/Function (with MSI environment variables) as the APIs to + * be called for acquiring MSI Token are different for both cases. + */ + boolean isAzureFunction = null != msiEndpoint && !msiEndpoint.isEmpty() && null != msiSecret + && !msiSecret.isEmpty(); + + if (isAzureFunction) { + urlString.append(msiEndpoint).append("?api-version=2017-09-01&resource=").append(resource); + } else { + urlString.append(ActiveDirectoryAuthentication.AZURE_REST_MSI_URL).append("&resource=").append(resource); + // Retry acquiring access token upto 20 times due to possible IMDS upgrade (Applies to VM only) + maxRetry = 20; + // Simplified variant of Exponential BackOff + for (int x = 0; x < maxRetry; x++) { + retrySlots.add(500 * ((2 << 1) - 1) / 1000); + } + } + + // Append Client Id if available + if (null != msiClientId && !msiClientId.isEmpty()) { + if (isAzureFunction) { + urlString.append("&clientid=").append(msiClientId); + } else { + urlString.append("&client_id=").append(msiClientId); + } + } + + // Loop while maxRetry reaches its limit + while (retry <= maxRetry) { + HttpURLConnection connection = null; + + try { + connection = (HttpURLConnection) new URL(urlString.toString()).openConnection(); + connection.setRequestMethod("GET"); + + if (isAzureFunction) { + connection.setRequestProperty("Secret", msiSecret); + if (connectionlogger.isLoggable(Level.FINER)) { + connectionlogger.finer(toString() + " Using Azure Function/App Service MSI auth: " + urlString); + } + } else { + connection.setRequestProperty("Metadata", "true"); + if (connectionlogger.isLoggable(Level.FINER)) { + connectionlogger.finer(toString() + " Using Azure MSI auth: " + urlString); + } + } + + connection.connect(); + + try (InputStream stream = connection.getInputStream()) { + + BufferedReader reader = new BufferedReader(new InputStreamReader(stream, UTF_8), 100); + String result = reader.readLine(); + + int startIndex_AT = result.indexOf(ActiveDirectoryAuthentication.ACCESS_TOKEN_IDENTIFIER) + + ActiveDirectoryAuthentication.ACCESS_TOKEN_IDENTIFIER.length(); + + String accessToken = result.substring(startIndex_AT, result.indexOf("\"", startIndex_AT + 1)); + + Calendar cal = new Calendar.Builder().setInstant(new Date()).build(); + + if (isAzureFunction) { + // Fetch expires_on + int startIndex_ATX = result + .indexOf(ActiveDirectoryAuthentication.ACCESS_TOKEN_EXPIRES_ON_IDENTIFIER) + + ActiveDirectoryAuthentication.ACCESS_TOKEN_EXPIRES_ON_IDENTIFIER.length(); + String accessTokenExpiry = result.substring(startIndex_ATX, + result.indexOf("\"", startIndex_ATX + 1)); + if (connectionlogger.isLoggable(Level.FINER)) { + connectionlogger.finer(toString() + " MSI auth token expires on: " + accessTokenExpiry); + } + + DateFormat df = new SimpleDateFormat( + ActiveDirectoryAuthentication.ACCESS_TOKEN_EXPIRES_ON_DATE_FORMAT); + cal = new Calendar.Builder().setInstant(df.parse(accessTokenExpiry)).build(); + } else { + // Fetch expires_in + int startIndex_ATX = result + .indexOf(ActiveDirectoryAuthentication.ACCESS_TOKEN_EXPIRES_IN_IDENTIFIER) + + ActiveDirectoryAuthentication.ACCESS_TOKEN_EXPIRES_IN_IDENTIFIER.length(); + String accessTokenExpiry = result.substring(startIndex_ATX, + result.indexOf("\"", startIndex_ATX + 1)); + cal.add(Calendar.SECOND, Integer.parseInt(accessTokenExpiry)); + } + + return new SqlFedAuthToken(accessToken, cal.getTime()); + } + } catch (Exception e) { + retry++; + // Below code applicable only when !isAzureFunctcion (VM) + if (retry > maxRetry) { + // Do not retry if maxRetry limit has been reached. + break; + } else { + try { + int responseCode = connection.getResponseCode(); + // Check Error Response Code from Connection + if (410 == responseCode || 429 == responseCode || 404 == responseCode + || (500 <= responseCode && 599 >= responseCode)) { + try { + int retryTimeoutInMs = retrySlots.get(ThreadLocalRandom.current().nextInt(retry - 1)); + // Error code 410 indicates IMDS upgrade is in progress, which can take up to 70s + retryTimeoutInMs = (responseCode == 410 + && retryTimeoutInMs < imdsUpgradeTimeInMs) ? imdsUpgradeTimeInMs + : retryTimeoutInMs; + Thread.sleep(retryTimeoutInMs); + } catch (InterruptedException ex) { + // Throw runtime exception as driver must not be interrupted here + throw new RuntimeException(ex); + } + } else { + if (null != msiClientId && !msiClientId.isEmpty()) { + SQLServerException.makeFromDriverError(this, null, + SQLServerException.getErrString("R_MSITokenFailureClientId"), null, true); + } else { + SQLServerException.makeFromDriverError(this, null, + SQLServerException.getErrString("R_MSITokenFailureImds"), null, true); + } + } + } catch (IOException io) { + // Throw error as unexpected if response code not available + SQLServerException.makeFromDriverError(this, null, + SQLServerException.getErrString("R_MSITokenFailureUnexpected"), null, true); + } + } + } finally { + if (connection != null) { + connection.disconnect(); + } + } + } + if (retry > maxRetry) { + SQLServerException.makeFromDriverError(this, null, SQLServerException + .getErrString(isAzureFunction ? "R_MSITokenFailureEndpoint" : "R_MSITokenFailureImds"), null, true); + } + return null; + } + /** * Send the access token to the server. */ diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java index e20b0a8e87..b234e51156 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java @@ -866,6 +866,17 @@ public String getJASSConfigurationName() { SQLServerDriverStringProperty.JAAS_CONFIG_NAME.getDefaultValue()); } + @Override + public void setMSIClientId(String msiClientId) { + setStringProperty(connectionProps, SQLServerDriverStringProperty.MSI_CLIENT_ID.toString(), msiClientId); + } + + @Override + public String getMSIClientId() { + return getStringProperty(connectionProps, SQLServerDriverStringProperty.MSI_CLIENT_ID.toString(), + SQLServerDriverStringProperty.MSI_CLIENT_ID.getDefaultValue()); + } + /** * Sets a property string value. * diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java index 30a0c4d761..222a897dc5 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java @@ -65,7 +65,8 @@ enum SqlAuthentication { NotSpecified, SqlPassword, ActiveDirectoryPassword, - ActiveDirectoryIntegrated; + ActiveDirectoryIntegrated, + ActiveDirectoryMSI; static SqlAuthentication valueOfString(String value) throws SQLServerException { SqlAuthentication method = null; @@ -80,6 +81,8 @@ static SqlAuthentication valueOfString(String value) throws SQLServerException { } else if (value.toLowerCase(Locale.US) .equalsIgnoreCase(SqlAuthentication.ActiveDirectoryIntegrated.toString())) { method = SqlAuthentication.ActiveDirectoryIntegrated; + } else if (value.toLowerCase(Locale.US).equalsIgnoreCase(SqlAuthentication.ActiveDirectoryMSI.toString())) { + method = SqlAuthentication.ActiveDirectoryMSI; } else { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_InvalidConnectionSetting")); Object[] msgArgs = {"authentication", value}; @@ -280,7 +283,8 @@ enum SQLServerDriverStringProperty { KEY_STORE_AUTHENTICATION("keyStoreAuthentication", ""), KEY_STORE_SECRET("keyStoreSecret", ""), KEY_STORE_LOCATION("keyStoreLocation", ""), - SSL_PROTOCOL("sslProtocol", SSLProtocol.TLS.toString()),; + SSL_PROTOCOL("sslProtocol", SSLProtocol.TLS.toString()), + MSI_CLIENT_ID("msiClientId", ""),; private final String name; private final String defaultValue; @@ -500,6 +504,8 @@ public final class SQLServerDriver implements java.sql.Driver { SQLServerDriverStringProperty.SSL_PROTOCOL.getDefaultValue(), false, new String[] {SSLProtocol.TLS.toString(), SSLProtocol.TLS_V10.toString(), SSLProtocol.TLS_V11.toString(), SSLProtocol.TLS_V12.toString()}), + new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.MSI_CLIENT_ID.toString(), + SQLServerDriverStringProperty.MSI_CLIENT_ID.getDefaultValue(), false, null), new SQLServerDriverPropertyInfo(SQLServerDriverIntProperty.CANCEL_QUERY_TIMEOUT.toString(), Integer.toString(SQLServerDriverIntProperty.CANCEL_QUERY_TIMEOUT.getDefaultValue()), false, null), new SQLServerDriverPropertyInfo(SQLServerDriverBooleanProperty.USE_BULK_COPY_FOR_BATCH_INSERT.toString(), diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPooledConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPooledConnection.java index 91b8a51442..31ca83aa8e 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPooledConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPooledConnection.java @@ -29,11 +29,11 @@ public class SQLServerPooledConnection implements PooledConnection { private SQLServerConnectionPoolProxy lastProxyConnection; private String factoryUser, factoryPassword; private java.util.logging.Logger pcLogger; - static private final AtomicInteger basePooledConnectionID = new AtomicInteger(0); // Unique id generator for each - // PooledConnection instance - // (used for logging). private final String traceID; + // Unique id generator for each PooledConnection instance (used for logging). + static private final AtomicInteger basePooledConnectionID = new AtomicInteger(0); + SQLServerPooledConnection(SQLServerDataSource ds, String user, String password) throws SQLException { listeners = new Vector<>(); // Piggyback SQLServerDataSource logger for now. @@ -65,7 +65,12 @@ public String toString() { return traceID; } - // Helper function to create a new connection for the pool. + /** + * Helper function to create a new connection for the pool. + * + * @return SQLServerConnection instance + * @throws SQLException + */ private SQLServerConnection createNewConnection() throws SQLException { return factoryDataSource.getConnectionInternal(factoryUser, factoryPassword, this); } @@ -88,28 +93,38 @@ public Connection getConnection() throws SQLException { SQLServerException.getErrString("R_physicalConnectionIsClosed"), "", true); } - // Check with security manager to insure caller has rights to connect. - // This will throw a SecurityException if the caller does not have proper rights. + /* + * Check with security manager to insure caller has rights to connect. This will throw a SecurityException + * if the caller does not have proper rights. + */ physicalConnection.doSecurityCheck(); if (pcLogger.isLoggable(Level.FINE)) pcLogger.fine(toString() + " Physical connection, " + safeCID()); - if (null != physicalConnection.getAuthenticationResult()) { - if (Util.checkIfNeedNewAccessToken(physicalConnection)) { - physicalConnection = createNewConnection(); - } + if (physicalConnection.needsReconnect()) { + physicalConnection.close(); + physicalConnection = createNewConnection(); } - // The last proxy connection handle returned will be invalidated (moved to closed state) - // when getConnection is called. + /* + * The last proxy connection handle returned will be invalidated (moved to closed state) when getConnection + * is called. + */ if (null != lastProxyConnection) { // if there was a last proxy connection send reset physicalConnection.resetPooledConnection(); - if (pcLogger.isLoggable(Level.FINE) && !lastProxyConnection.isClosed()) - pcLogger.fine(toString() + "proxy " + lastProxyConnection.toString() - + " is not closed before getting the connection."); - // use internal close so there wont be an event due to us closing the connection, if not closed already. - lastProxyConnection.internalClose(); + + if (!lastProxyConnection.isClosed()) { + if (pcLogger.isLoggable(Level.FINE)) { + pcLogger.fine(toString() + "proxy " + lastProxyConnection.toString() + + " is not closed before getting the connection."); + } + /* + * use internal close so there wont be an event due to us closing the connection, if not closed + * already. + */ + lastProxyConnection.internalClose(); + } } lastProxyConnection = new SQLServerConnectionPoolProxy(physicalConnection); @@ -222,8 +237,10 @@ private static int nextPooledConnectionID() { return basePooledConnectionID.incrementAndGet(); } - // Helper function to return connectionID of the physicalConnection in a safe manner for logging. - // Returns (null) if physicalConnection is null, otherwise returns connectionID. + /** + * Helper function to return connectionID of the physicalConnection in a safe manner for logging. Returns (null) if + * physicalConnection is null, otherwise returns connectionID. + **/ private String safeCID() { if (null == physicalConnection) return " ConnectionID:(null)"; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java index 7f30ca791e..d3a6e7303c 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java @@ -245,6 +245,8 @@ protected Object[][] getContents() { {"R_statementPoolingCacheSizePropertyDescription", "This setting specifies the size of the prepared statement cache for a connection. A value less than 1 means no cache."}, {"R_gsscredentialPropertyDescription", "Impersonated GSS Credential to access SQL Server."}, + {"R_msiClientIdPropertyDescription", + "Client Id of User Assigned Managed Identity to be used for generating access token for Azure AD MSI Authentication"}, {"R_noParserSupport", "An error occurred while instantiating the required parser. Error: \"{0}\""}, {"R_writeOnlyXML", "Cannot read from this SQLXML instance. This instance is for writing data only."}, {"R_dataHasBeenReadXML", "Cannot read from this SQLXML instance. The data has already been read."}, @@ -386,6 +388,8 @@ protected Object[][] getContents() { "Cannot set the AccessToken property if the \"IntegratedSecurity\" connection string keyword has been set to \"true\"."}, {"R_IntegratedAuthenticationWithUserPassword", "Cannot use \"Authentication=ActiveDirectoryIntegrated\" with \"User\", \"UserName\" or \"Password\" connection string keywords."}, + {"R_MSIAuthenticationWithUserPassword", + "Cannot use \"Authentication=ActiveDirectoryMSI\" with \"User\", \"UserName\" or \"Password\" connection string keywords."}, {"R_AccessTokenWithUserPassword", "Cannot set the AccessToken property if \"User\", \"UserName\" or \"Password\" has been specified in the connection string."}, {"R_AccessTokenCannotBeEmpty", "AccesToken cannot be empty."}, @@ -534,5 +538,14 @@ protected Object[][] getContents() { {"R_unknownUTF8SupportValue", "Unknown value for UTF8 support."}, {"R_illegalWKT", "Illegal Well-Known text. Please make sure Well-Known text is valid."}, {"R_illegalTypeForGeometry", "{0} is not supported for Geometry."}, - {"R_illegalWKTposition", "Illegal character in Well-Known text at position {0}."},}; + {"R_illegalWKTposition", "Illegal character in Well-Known text at position {0}."}, + {"R_ADALMissing", "Failed to load ADAL4J Java library for performing {0} authentication."}, + {"R_DLLandADALMissing", + "Failed to load both sqljdbc_auth.dll and ADAL4J Java library for performing {0} authentication. Please install one of them to proceed."}, + {"R_MSITokenFailureImds", "MSI Token failure: Failed to acquire access token from IMDS"}, + {"R_MSITokenFailureImdsClientId", + "MSI Token failure: Failed to acquire access token from IMDS, verify your clientId."}, + {"R_MSITokenFailureUnexpected", + "MSI Token failure: Failed to acquire access token from IMDS, unexpected error occurred."}, + {"R_MSITokenFailureEndpoint", "MSI Token failure: Failed to acquire token from MSI Endpoint"}}; } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SqlFedAuthToken.java b/src/main/java/com/microsoft/sqlserver/jdbc/SqlFedAuthToken.java index 01adb70a10..21aedbb8ec 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SqlFedAuthToken.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SqlFedAuthToken.java @@ -1,31 +1,27 @@ -/* - * 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.jdbc; - -import java.util.Date; - - -class SqlFedAuthToken { - final Date expiresOn; - final String accessToken; - - SqlFedAuthToken(final String accessToken, final long expiresIn) { - this.accessToken = accessToken; - - Date now = new Date(); - now.setTime(now.getTime() + (expiresIn * 1000)); - this.expiresOn = now; - } - - SqlFedAuthToken(final String accessToken, final Date expiresOn) { - this.accessToken = accessToken; - this.expiresOn = expiresOn; - } - - Date getExpiresOnDate() { - return expiresOn; - } -} +/* + * 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.jdbc; + +import java.util.Date; + + +class SqlFedAuthToken { + final Date expiresOn; + final String accessToken; + + SqlFedAuthToken(String accessToken, long expiresIn) { + this.accessToken = accessToken; + + Date now = new Date(); + now.setTime(now.getTime() + (expiresIn * 1000)); + this.expiresOn = now; + } + + SqlFedAuthToken(String accessToken, Date expiresOn) { + this.accessToken = accessToken; + this.expiresOn = expiresOn; + } +} diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/Util.java b/src/main/java/com/microsoft/sqlserver/jdbc/Util.java index 76617d31a0..0f08dbce90 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/Util.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/Util.java @@ -936,8 +936,7 @@ else if (("" + value).contains("E")) { // If the token is expiring within the next 45 mins, try to fetch a new token if there is no thread already doing // it. // If a thread is already doing the refresh, just use the existing token and proceed. - static synchronized boolean checkIfNeedNewAccessToken(SQLServerConnection connection) { - Date accessTokenExpireDate = connection.getAuthenticationResult().getExpiresOnDate(); + static synchronized boolean checkIfNeedNewAccessToken(SQLServerConnection connection, Date accessTokenExpireDate) { Date now = new Date(); // if the token's expiration is within the next 45 mins @@ -957,7 +956,6 @@ static synchronized boolean checkIfNeedNewAccessToken(SQLServerConnection connec } } } - return false; } From 850675e3fb92062fd9c3f5c9d50f388baf68091b Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Thu, 27 Dec 2018 13:14:36 -0800 Subject: [PATCH 43/51] Imrpovement | MSI support minor alignments and adjustments (#915) --- .../com/microsoft/sqlserver/jdbc/SQLServerDriver.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java index 222a897dc5..5679943727 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java @@ -480,7 +480,8 @@ public final class SQLServerDriver implements java.sql.Driver { SQLServerDriverStringProperty.AUTHENTICATION.getDefaultValue(), false, new String[] {SqlAuthentication.NotSpecified.toString(), SqlAuthentication.SqlPassword.toString(), SqlAuthentication.ActiveDirectoryPassword.toString(), - SqlAuthentication.ActiveDirectoryIntegrated.toString()}), + SqlAuthentication.ActiveDirectoryIntegrated.toString(), + SqlAuthentication.ActiveDirectoryMSI.toString()}), new SQLServerDriverPropertyInfo(SQLServerDriverIntProperty.SOCKET_TIMEOUT.toString(), Integer.toString(SQLServerDriverIntProperty.SOCKET_TIMEOUT.getDefaultValue()), false, null), new SQLServerDriverPropertyInfo(SQLServerDriverBooleanProperty.FIPS.toString(), @@ -504,13 +505,13 @@ public final class SQLServerDriver implements java.sql.Driver { SQLServerDriverStringProperty.SSL_PROTOCOL.getDefaultValue(), false, new String[] {SSLProtocol.TLS.toString(), SSLProtocol.TLS_V10.toString(), SSLProtocol.TLS_V11.toString(), SSLProtocol.TLS_V12.toString()}), - new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.MSI_CLIENT_ID.toString(), - SQLServerDriverStringProperty.MSI_CLIENT_ID.getDefaultValue(), false, null), new SQLServerDriverPropertyInfo(SQLServerDriverIntProperty.CANCEL_QUERY_TIMEOUT.toString(), Integer.toString(SQLServerDriverIntProperty.CANCEL_QUERY_TIMEOUT.getDefaultValue()), false, null), new SQLServerDriverPropertyInfo(SQLServerDriverBooleanProperty.USE_BULK_COPY_FOR_BATCH_INSERT.toString(), Boolean.toString(SQLServerDriverBooleanProperty.USE_BULK_COPY_FOR_BATCH_INSERT.getDefaultValue()), - false, TRUE_FALSE),}; + false, TRUE_FALSE), + new SQLServerDriverPropertyInfo(SQLServerDriverStringProperty.MSI_CLIENT_ID.toString(), + SQLServerDriverStringProperty.MSI_CLIENT_ID.getDefaultValue(), false, null),}; /** * Properties that can only be set by using Properties. Cannot set in connection string From 10fd845486ac38446352f4f528e3fdfca7827a99 Mon Sep 17 00:00:00 2001 From: Iwao AVE! Date: Sat, 29 Dec 2018 03:26:06 +0900 Subject: [PATCH 44/51] Fix | Send java.time.OffsetDateTime value to the server unaffected by default time zone. (#831) * Added a test case: If an instance of java.time.OffsetDateTime which has a different time zone than the default time zone is passed to `PreparedStatement#setObject()`, the driver sends an incorrect value. * Use OffsetDateTime#toEpochSecond() instead of java.time.Timestamp#getTime(). `java.sql.Timestamp#getTime()` subsequently invokes `java.util.Date#normalize()` which takes the default TimeZone into account. * Applied the previous change to the code handling OFFSETTIME. * Removed an unused import. --- .../com/microsoft/sqlserver/jdbc/dtv.java | 17 +-- .../jdbc/preparedStatement/SetObjectTest.java | 134 ++++++++++++++++++ 2 files changed, 137 insertions(+), 14 deletions(-) create mode 100644 src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/SetObjectTest.java diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java b/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java index 657cb7e798..cc67a6da23 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java @@ -23,7 +23,6 @@ import java.sql.Blob; import java.sql.Clob; import java.sql.SQLException; -import java.sql.Timestamp; import java.text.MessageFormat; import java.time.LocalDate; import java.time.LocalDateTime; @@ -630,11 +629,8 @@ private void sendTemporal(DTV dtv, JavaType javaType, Object value) throws SQLSe * 1000, ""); - // The behavior is similar to microsoft.sql.DateTimeOffset - // In Timestamp format, leading zeros for the fields can be omitted. - String offsetTimeStr = conn.baseYear() + "-01-01" + ' ' + offsetTimeValue.getHour() + ':' - + offsetTimeValue.getMinute() + ':' + offsetTimeValue.getSecond(); - utcMillis = Timestamp.valueOf(offsetTimeStr).getTime(); + LocalDate baseDate = LocalDate.of(conn.baseYear(), 1, 1); + utcMillis = offsetTimeValue.atDate(baseDate).toEpochSecond() * 1000; break; case OFFSETDATETIME: @@ -679,14 +675,7 @@ private void sendTemporal(DTV dtv, JavaType javaType, Object value) throws SQLSe * 1000, ""); - // The behavior is similar to microsoft.sql.DateTimeOffset - // In Timestamp format, only YEAR needs to have 4 digits. The leading zeros for the rest of the - // fields can be omitted. - String offDateTimeStr = String.format("%04d", offsetDateTimeValue.getYear()) + '-' - + offsetDateTimeValue.getMonthValue() + '-' + offsetDateTimeValue.getDayOfMonth() + ' ' - + offsetDateTimeValue.getHour() + ':' + offsetDateTimeValue.getMinute() + ':' - + offsetDateTimeValue.getSecond(); - utcMillis = Timestamp.valueOf(offDateTimeStr).getTime(); + utcMillis = offsetDateTimeValue.toEpochSecond() * 1000; break; case DATETIMEOFFSET: { diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/SetObjectTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/SetObjectTest.java new file mode 100644 index 0000000000..1ac70723a7 --- /dev/null +++ b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/SetObjectTest.java @@ -0,0 +1,134 @@ +/* + * 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.jdbc.preparedStatement; + +import static org.junit.jupiter.api.Assertions.*; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.time.OffsetDateTime; +import java.time.OffsetTime; + +import org.junit.jupiter.api.Test; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; + +import com.microsoft.sqlserver.jdbc.RandomUtil; +import com.microsoft.sqlserver.jdbc.SQLServerConnection; +import com.microsoft.sqlserver.jdbc.TestUtils; +import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; +import com.microsoft.sqlserver.testframework.AbstractTest; + + +@RunWith(JUnitPlatform.class) +public class SetObjectTest extends AbstractTest { + private static final String tableName = RandomUtil.getIdentifier("SetObjectTestTable"); + + /** + * Tests setObject(n, java.time.OffsetDateTime.class). + * + * @throws SQLException + */ + @Test + public void testSetObjectWithOffsetDateTime() throws SQLException { + try (Connection con = DriverManager.getConnection(connectionString)) { + final String testValue = "2018-01-02T11:22:33.123456700+12:34"; + try (Statement stmt = con.createStatement()) { + stmt.executeUpdate("CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName) + + " (id INT PRIMARY KEY, dto DATETIMEOFFSET)"); + try { + try (PreparedStatement pstmt = con.prepareStatement("INSERT INTO " + + AbstractSQLGenerator.escapeIdentifier(tableName) + " (id, dto) VALUES (?, ?)")) { + pstmt.setInt(1, 1); + pstmt.setObject(2, OffsetDateTime.parse(testValue)); + pstmt.executeUpdate(); + } + + try (ResultSet rs = stmt + .executeQuery("SELECT COUNT(*) FROM " + AbstractSQLGenerator.escapeIdentifier(tableName) + + " WHERE id = 1 AND dto = '" + testValue + "'")) { + rs.next(); + assertEquals(1, rs.getInt(1)); + } + } finally { + TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(tableName), stmt); + } + } + } + } + + /** + * Tests setObject(n, java.time.OffsetTime.class). + * + * @throws SQLException + */ + @Test + public void testSetObjectWithOffsetTime() throws SQLException { + try (Connection con = DriverManager.getConnection(connectionString)) { + final String testValue = "11:22:33.123456700+12:34"; + final String expectedDto = "1970-01-01T" + testValue; + try (Statement stmt = con.createStatement()) { + stmt.executeUpdate("CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName) + + " (id INT PRIMARY KEY, dto DATETIMEOFFSET)"); + try { + try (PreparedStatement pstmt = con.prepareStatement("INSERT INTO " + + AbstractSQLGenerator.escapeIdentifier(tableName) + " (id, dto) VALUES (?, ?)")) { + pstmt.setInt(1, 1); + pstmt.setObject(2, OffsetTime.parse(testValue)); + pstmt.executeUpdate(); + } + + try (ResultSet rs = stmt + .executeQuery("SELECT COUNT(*) FROM " + AbstractSQLGenerator.escapeIdentifier(tableName) + + " WHERE id = 1 AND dto = '" + expectedDto + "'")) { + rs.next(); + assertEquals(1, rs.getInt(1)); + } + } finally { + TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(tableName), stmt); + } + } + } + } + + /** + * Tests setObject(n, java.time.OffsetTime.class) when 'setSendTimeAsDatetime' connection property is false. + * + * @throws SQLException + */ + @Test + public void testSetObjectWithOffsetTime_sendTimeAsDatetimeDisabled() throws SQLException { + try (Connection con = DriverManager.getConnection(connectionString)) { + ((SQLServerConnection) con).setSendTimeAsDatetime(false); + final String testValue = "11:22:33.123456700+12:34"; + final String expectedDto = "1900-01-01T" + testValue; + try (Statement stmt = con.createStatement()) { + stmt.executeUpdate("CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName) + + " (id INT PRIMARY KEY, dto DATETIMEOFFSET)"); + try { + try (PreparedStatement pstmt = con.prepareStatement("INSERT INTO " + + AbstractSQLGenerator.escapeIdentifier(tableName) + " (id, dto) VALUES (?, ?)")) { + pstmt.setInt(1, 1); + pstmt.setObject(2, OffsetTime.parse(testValue)); + pstmt.executeUpdate(); + } + + try (ResultSet rs = stmt + .executeQuery("SELECT COUNT(*) FROM " + AbstractSQLGenerator.escapeIdentifier(tableName) + + " WHERE id = 1 AND dto = '" + expectedDto + "'")) { + rs.next(); + assertEquals(1, rs.getInt(1)); + } + } finally { + TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(tableName), stmt); + } + } + } + } +} From 4a1486171387e1532e799bf4e4e52b4a82800785 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20L=C3=A4ubrich?= Date: Wed, 2 Jan 2019 19:22:49 +0100 Subject: [PATCH 45/51] Add | DataSourceFactory and OSGI Framework APIs (#700) --- pom.xml | 24 +++ .../sqlserver/jdbc/SQLServerDriver.java | 30 +++- .../sqlserver/jdbc/SQLServerResource.java | 4 +- .../sqlserver/jdbc/osgi/Activator.java | 44 +++++ .../jdbc/osgi/SQLServerDataSourceFactory.java | 129 +++++++++++++++ .../sqlserver/jdbc/osgi/DataFactoryTest.java | 154 ++++++++++++++++++ .../sqlserver/testframework/AbstractTest.java | 6 + 7 files changed, 389 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/microsoft/sqlserver/jdbc/osgi/Activator.java create mode 100644 src/main/java/com/microsoft/sqlserver/jdbc/osgi/SQLServerDataSourceFactory.java create mode 100644 src/test/java/com/microsoft/sqlserver/jdbc/osgi/DataFactoryTest.java diff --git a/pom.xml b/pom.xml index 7289aed62b..ccc932a716 100644 --- a/pom.xml +++ b/pom.xml @@ -42,6 +42,7 @@ 1.3.1 5.3.1 + 4.3.1 @@ -63,6 +64,22 @@ 1.6.2 true + + + + org.osgi + org.osgi.core + ${osgi.version} + provided + + + org.osgi + org.osgi.compendium + ${osgi.version} + provided + + + com.microsoft.rest client-runtime @@ -137,6 +154,12 @@ 1.7.25 test + + org.eclipse.gemini.blueprint + gemini-blueprint-mock + 2.0.0.RELEASE + test + @@ -358,6 +381,7 @@ <_exportcontents>export com.microsoft.sqlserver.jdbc.dataclassification !microsoft.sql,* + com.microsoft.sqlserver.jdbc.osgi.Activator diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java index 5679943727..760f3f0828 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDriver.java @@ -555,10 +555,11 @@ String getClassNameLogging() { private final static java.util.logging.Logger drLogger = java.util.logging.Logger .getLogger("com.microsoft.sqlserver.jdbc.internals.SQLServerDriver"); + private static java.sql.Driver mssqlDriver = null; // Register with the DriverManager static { try { - java.sql.DriverManager.registerDriver(new SQLServerDriver()); + register(); } catch (SQLException e) { if (drLogger.isLoggable(Level.FINER) && Util.IsActivityTraceOn()) { drLogger.finer("Error registering driver: " + e); @@ -566,6 +567,33 @@ String getClassNameLogging() { } } + /* + * Registers the driver with DriverManager. No-op if driver is already registered. + */ + public static void register() throws SQLException { + if (!isRegistered()) { + mssqlDriver = new SQLServerDriver(); + DriverManager.registerDriver(mssqlDriver); + } + } + + /* + * De-registers the driver with the DriverManager. No-op if the driver is not registered. + */ + public static void deregister() throws SQLException { + if (isRegistered()) { + DriverManager.deregisterDriver(mssqlDriver); + mssqlDriver = null; + } + } + + /* + * Checks whether the driver has been registered with the driver manager. + */ + public static boolean isRegistered() { + return mssqlDriver != null; + } + public SQLServerDriver() { instanceID = nextInstanceID(); traceID = "SQLServerDriver:" + instanceID; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java index d3a6e7303c..266f8131ab 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java @@ -547,5 +547,7 @@ protected Object[][] getContents() { "MSI Token failure: Failed to acquire access token from IMDS, verify your clientId."}, {"R_MSITokenFailureUnexpected", "MSI Token failure: Failed to acquire access token from IMDS, unexpected error occurred."}, - {"R_MSITokenFailureEndpoint", "MSI Token failure: Failed to acquire token from MSI Endpoint"}}; + {"R_MSITokenFailureEndpoint", "MSI Token failure: Failed to acquire token from MSI Endpoint"}, + {"R_propertyNotSupported", + "Microsoft JDBC Driver for SQL Server currently does not support the property: {0}"}}; } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/osgi/Activator.java b/src/main/java/com/microsoft/sqlserver/jdbc/osgi/Activator.java new file mode 100644 index 0000000000..bc6d8e15a7 --- /dev/null +++ b/src/main/java/com/microsoft/sqlserver/jdbc/osgi/Activator.java @@ -0,0 +1,44 @@ +/* + * 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.jdbc.osgi; + +import java.util.Dictionary; +import java.util.Hashtable; + +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.jdbc.DataSourceFactory; + +import com.microsoft.sqlserver.jdbc.SQLServerDriver; + + +/** + * Allows plugins to register the driver as an OSGI Framework service. + */ +public class Activator implements BundleActivator { + + private ServiceRegistration service; + + @Override + public void start(BundleContext context) throws Exception { + Dictionary properties = new Hashtable<>(); + SQLServerDriver driver = new SQLServerDriver(); + properties.put(DataSourceFactory.OSGI_JDBC_DRIVER_CLASS, driver.getClass().getName()); + properties.put(DataSourceFactory.OSGI_JDBC_DRIVER_NAME, "Microsoft JDBC Driver for SQL Server"); + properties.put(DataSourceFactory.OSGI_JDBC_DRIVER_VERSION, + driver.getMajorVersion() + "." + driver.getMinorVersion()); + service = context.registerService(DataSourceFactory.class, new SQLServerDataSourceFactory(), properties); + SQLServerDriver.register(); + } + + @Override + public void stop(BundleContext context) throws Exception { + if (service != null) { + service.unregister(); + } + SQLServerDriver.deregister(); + } +} diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/osgi/SQLServerDataSourceFactory.java b/src/main/java/com/microsoft/sqlserver/jdbc/osgi/SQLServerDataSourceFactory.java new file mode 100644 index 0000000000..a5a3a72a04 --- /dev/null +++ b/src/main/java/com/microsoft/sqlserver/jdbc/osgi/SQLServerDataSourceFactory.java @@ -0,0 +1,129 @@ +/* + * 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.jdbc.osgi; + +import java.sql.Driver; +import java.sql.SQLException; +import java.util.Locale; +import java.util.Properties; +import java.util.ResourceBundle; +import java.util.logging.Level; + +import javax.activation.DataSource; +import javax.sql.ConnectionPoolDataSource; +import javax.sql.XADataSource; + +import org.osgi.service.jdbc.DataSourceFactory; + +import com.microsoft.sqlserver.jdbc.SQLServerConnectionPoolDataSource; +import com.microsoft.sqlserver.jdbc.SQLServerDataSource; +import com.microsoft.sqlserver.jdbc.SQLServerDriver; +import com.microsoft.sqlserver.jdbc.SQLServerXADataSource; + + +/** + * Implementation of the Data Service Specification for JDBC™ Technology. Refer to the OSGI 7.0.0 specifications for + * more details. + */ +public class SQLServerDataSourceFactory implements DataSourceFactory { + + private static java.util.logging.Logger osgiLogger = java.util.logging.Logger + .getLogger("com.microsoft.sqlserver.jdbc.osgi.SQLServerDataSourceFactory"); + private static final String NOT_SUPPORTED_MSG = ResourceBundle + .getBundle("com.microsoft.sqlserver.jdbc.SQLServerResource", Locale.getDefault()) + .getString("R_propertyNotSupported"); + + @Override + public javax.sql.DataSource createDataSource(Properties props) throws SQLException { + SQLServerDataSource source = new SQLServerDataSource(); + setup(source, props); + return source; + } + + @Override + public ConnectionPoolDataSource createConnectionPoolDataSource(Properties props) throws SQLException { + SQLServerConnectionPoolDataSource poolDataSource = new SQLServerConnectionPoolDataSource(); + setupXSource(poolDataSource, props); + return poolDataSource; + } + + @Override + public XADataSource createXADataSource(Properties props) throws SQLException { + SQLServerXADataSource xaDataSource = new SQLServerXADataSource(); + setupXSource(xaDataSource, props); + return xaDataSource; + } + + @Override + public Driver createDriver(Properties props) throws SQLException { + SQLServerDriver driver = new SQLServerDriver(); + return driver; + } + + /** + * Sets up the basic properties for {@link DataSource}s + */ + private void setup(SQLServerDataSource source, Properties props) { + if (props == null) { + return; + } + if (props.containsKey(JDBC_DATABASE_NAME)) { + source.setDatabaseName(props.getProperty(JDBC_DATABASE_NAME)); + } + if (props.containsKey(JDBC_DATASOURCE_NAME)) { + osgiLogger.log(Level.WARNING, NOT_SUPPORTED_MSG, JDBC_DATASOURCE_NAME); + } + if (props.containsKey(JDBC_DESCRIPTION)) { + source.setDescription(props.getProperty(JDBC_DESCRIPTION)); + } + if (props.containsKey(JDBC_NETWORK_PROTOCOL)) { + osgiLogger.log(Level.WARNING, NOT_SUPPORTED_MSG, JDBC_NETWORK_PROTOCOL); + } + if (props.containsKey(JDBC_PASSWORD)) { + source.setPassword(props.getProperty(JDBC_PASSWORD)); + } + if (props.containsKey(JDBC_PORT_NUMBER)) { + source.setPortNumber(Integer.parseInt(props.getProperty(JDBC_PORT_NUMBER))); + } + if (props.containsKey(JDBC_ROLE_NAME)) { + osgiLogger.log(Level.WARNING, NOT_SUPPORTED_MSG, JDBC_ROLE_NAME); + } + if (props.containsKey(JDBC_SERVER_NAME)) { + source.setServerName(props.getProperty(JDBC_SERVER_NAME)); + } + if (props.containsKey(JDBC_URL)) { + source.setURL(props.getProperty(JDBC_URL)); + } + if (props.containsKey(JDBC_USER)) { + source.setUser(props.getProperty(JDBC_USER)); + } + } + + /** + * Sets up the basic and extended properties for {@link XADataSource}s and {@link ConnectionPoolDataSource}s + */ + private void setupXSource(SQLServerConnectionPoolDataSource source, Properties props) { + if (props == null) { + return; + } + setup(source, props); + if (props.containsKey(JDBC_INITIAL_POOL_SIZE)) { + osgiLogger.log(Level.WARNING, NOT_SUPPORTED_MSG, JDBC_INITIAL_POOL_SIZE); + } + if (props.containsKey(JDBC_MAX_IDLE_TIME)) { + osgiLogger.log(Level.WARNING, NOT_SUPPORTED_MSG, JDBC_MAX_IDLE_TIME); + } + if (props.containsKey(JDBC_MAX_STATEMENTS)) { + osgiLogger.log(Level.WARNING, NOT_SUPPORTED_MSG, JDBC_MAX_STATEMENTS); + } + if (props.containsKey(JDBC_MAX_POOL_SIZE)) { + osgiLogger.log(Level.WARNING, NOT_SUPPORTED_MSG, JDBC_MAX_POOL_SIZE); + } + if (props.containsKey(JDBC_MIN_POOL_SIZE)) { + osgiLogger.log(Level.WARNING, NOT_SUPPORTED_MSG, JDBC_MIN_POOL_SIZE); + } + } + +} diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/osgi/DataFactoryTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/osgi/DataFactoryTest.java new file mode 100644 index 0000000000..092edda023 --- /dev/null +++ b/src/test/java/com/microsoft/sqlserver/jdbc/osgi/DataFactoryTest.java @@ -0,0 +1,154 @@ +/* + * 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.jdbc.osgi; + +import static org.junit.Assert.assertTrue; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Dictionary; +import java.util.Properties; + +import javax.sql.ConnectionPoolDataSource; +import javax.sql.DataSource; +import javax.sql.PooledConnection; +import javax.sql.XAConnection; +import javax.sql.XADataSource; + +import org.junit.jupiter.api.Test; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; +import org.osgi.framework.BundleContext; +import org.osgi.framework.ServiceReference; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.jdbc.DataSourceFactory; + +import com.microsoft.sqlserver.jdbc.SQLServerDriver; +import com.microsoft.sqlserver.testframework.AbstractTest; + +import org.eclipse.gemini.blueprint.mock.MockBundleContext; +import org.eclipse.gemini.blueprint.mock.MockServiceRegistration; + + +@RunWith(JUnitPlatform.class) +public class DataFactoryTest extends AbstractTest { + + @Test + public void testDataFactory() throws SQLException { + DataSourceFactory dsf = new SQLServerDataSourceFactory(); + verifyFactoryNormalConnection(dsf); + verifyFactoryPooledConnection(dsf); + verifyFactoryXAConnection(dsf); + } + + @Test + public void testActivator() throws Exception { + BundleContext bc = new MockBundleContext() { + private ServiceReference sr; + private DataSourceFactory dsf; + + @Override + public ServiceReference getServiceReference(String clazz) { + return sr; + } + + @Override + public ServiceRegistration registerService(String[] clazzes, Object service, Dictionary properties) { + MockServiceRegistration reg = new MockServiceRegistration(properties); + sr = reg.getReference(); + if (service instanceof DataSourceFactory) { + dsf = (DataSourceFactory) service; + } + return reg; + } + + @SuppressWarnings("unchecked") + @Override + public S getService(ServiceReference reference) { + return (S) dsf; + } + }; + Activator a = new com.microsoft.sqlserver.jdbc.osgi.Activator(); + a.start(bc); + + ServiceReference sr = bc.getServiceReference(DataSourceFactory.class); + String[] propertyKeys = sr.getPropertyKeys(); + boolean correctClass = false; + boolean correctName = false; + boolean correctVersion = false; + SQLServerDriver driver = new SQLServerDriver(); + + for (String key : propertyKeys) { + if (key.equals(DataSourceFactory.OSGI_JDBC_DRIVER_CLASS)) { + String bundleClassName = (String) sr.getProperty(key); + String actualClassName = driver.getClass().getName(); + assertTrue("Driver class name mismatch. Expected: " + bundleClassName + ", Actual: " + actualClassName + + ".", bundleClassName.equals(actualClassName)); + correctClass = true; + } else if (key.equals(DataSourceFactory.OSGI_JDBC_DRIVER_NAME)) { + String bundleDriverName = (String) sr.getProperty(key); + String actualDriverName = "Microsoft JDBC Driver for SQL Server"; + assertTrue( + "Driver name mismatch. Expected: " + bundleDriverName + ", Actual: " + actualDriverName + ".", + bundleDriverName.equals(actualDriverName)); + correctName = true; + } else if (key.equals(DataSourceFactory.OSGI_JDBC_DRIVER_VERSION)) { + String bundleDriverVer = (String) sr.getProperty(key); + String actualDriverVer = driver.getMajorVersion() + "." + driver.getMinorVersion(); + assertTrue( + "Driver version mismatch. Expected: " + bundleDriverVer + ", Actual: " + actualDriverVer + ".", + bundleDriverVer.equals(actualDriverVer)); + correctVersion = true; + } + } + assertTrue("Not all properties were checked.", correctClass && correctName && correctVersion); + DataSourceFactory dsf = bc.getService(sr); + verifyFactoryNormalConnection(dsf); + verifyFactoryPooledConnection(dsf); + verifyFactoryXAConnection(dsf); + a.stop(bc); + } + + private void verifyFactoryNormalConnection(DataSourceFactory dsFactory) throws SQLException { + Properties props = new Properties(); + props.setProperty(DataSourceFactory.JDBC_URL, connectionString); + DataSource ds = dsFactory.createDataSource(props); + try (Connection c = ds.getConnection(); Statement s = c.createStatement()) { + try (ResultSet rs = s.executeQuery("SELECT 1")) { + assertTrue("Resultset is empty.", rs.next()); + } + } + } + + private void verifyFactoryPooledConnection(DataSourceFactory dsFactory) throws SQLException { + Properties props = new Properties(); + props.setProperty(DataSourceFactory.JDBC_URL, connectionString); + ConnectionPoolDataSource ds = dsFactory.createConnectionPoolDataSource(props); + PooledConnection c = ds.getPooledConnection(); + try (Statement s = c.getConnection().createStatement()) { + try (ResultSet rs = s.executeQuery("SELECT 1")) { + assertTrue("Resultset is empty.", rs.next()); + } + } finally { + c.close(); + } + } + + private void verifyFactoryXAConnection(DataSourceFactory dsFactory) throws SQLException { + Properties props = new Properties(); + props.setProperty(DataSourceFactory.JDBC_URL, connectionString); + XADataSource ds = dsFactory.createXADataSource(props); + XAConnection c = ds.getXAConnection(); + try (Statement s = c.getConnection().createStatement()) { + try (ResultSet rs = s.executeQuery("SELECT 1")) { + assertTrue("Resultset is empty.", rs.next()); + } + } finally { + c.close(); + } + } +} diff --git a/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java b/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java index 0d2e6f37cf..eb7c3a7c93 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/AbstractTest.java @@ -22,6 +22,7 @@ import org.junit.jupiter.api.BeforeAll; import com.microsoft.sqlserver.jdbc.SQLServerConnection; +import com.microsoft.sqlserver.jdbc.SQLServerDriver; import com.microsoft.sqlserver.jdbc.TestUtils; @@ -125,6 +126,11 @@ public static void teardown() throws Exception { } } + @BeforeAll + public static void registerDriver() throws Exception { + SQLServerDriver.register(); + } + /** * Read variable from property files if found null try to read from env. * From a1e56f2ba8c2ccc34a97db3109e76b032cf3a9cf Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Wed, 2 Jan 2019 15:25:42 -0800 Subject: [PATCH 46/51] Make SSL certificate validation respect wildcards (#836) Fix | Make SSL certificate validation respect wildcards (#836) --- .../microsoft/sqlserver/jdbc/IOBuffer.java | 61 +++++-- .../jdbc/SSLCertificateValidation.java | 172 ++++++++++++++++++ 2 files changed, 222 insertions(+), 11 deletions(-) create mode 100644 src/test/java/com/microsoft/sqlserver/jdbc/SSLCertificateValidation.java diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java index 48f622bea6..027cc5eae0 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java @@ -1400,27 +1400,66 @@ private String parseCommonName(String distinguishedName) { return commonName; } - private boolean validateServerName(String nameInCert) throws CertificateException { + private boolean validateServerName(String nameInCert) { // Failed to get the common name from DN or empty CN if (null == nameInCert) { - if (logger.isLoggable(Level.FINER)) + if (logger.isLoggable(Level.FINER)) { logger.finer(logContext + " Failed to parse the name from the certificate or name is empty."); + } return false; } - + // We do not allow wildcards in IDNs (xn--). + if (!nameInCert.startsWith("xn--") && nameInCert.contains("*")) { + int hostIndex = 0, certIndex = 0, match = 0, startIndex = -1, periodCount = 0; + while (hostIndex < hostName.length()) { + if ('.' == hostName.charAt(hostIndex)) { + periodCount++; + } + if (certIndex < nameInCert.length() && hostName.charAt(hostIndex) == nameInCert.charAt(certIndex)) { + hostIndex++; + certIndex++; + } else if (certIndex < nameInCert.length() && '*' == nameInCert.charAt(certIndex)) { + startIndex = certIndex; + match = hostIndex; + certIndex++; + } else if (startIndex != -1 && 0 == periodCount) { + certIndex = startIndex + 1; + match++; + hostIndex = match; + } else { + logFailMessage(nameInCert); + return false; + } + } + if (nameInCert.length() == certIndex && periodCount > 1) { + logSuccessMessage(nameInCert); + return true; + } else { + logFailMessage(nameInCert); + return false; + } + } // Verify that the name in certificate matches exactly with the host name if (!nameInCert.equals(hostName)) { - if (logger.isLoggable(Level.FINER)) - logger.finer(logContext + " The name in certificate " + nameInCert - + " does not match with the server name " + hostName + "."); + logFailMessage(nameInCert); return false; } + logSuccessMessage(nameInCert); + return true; + } - if (logger.isLoggable(Level.FINER)) + private void logFailMessage(String nameInCert) { + if (logger.isLoggable(Level.FINER)) { + logger.finer(logContext + " The name in certificate " + nameInCert + + " does not match with the server name " + hostName + "."); + } + } + + private void logSuccessMessage(String nameInCert) { + if (logger.isLoggable(Level.FINER)) { logger.finer(logContext + " The name in certificate:" + nameInCert + " validated against server name " + hostName + "."); - - return true; + } } public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { @@ -4591,8 +4630,8 @@ void writeTVPRows(TVP value) throws SQLServerException { SQLServerError databaseError = new SQLServerError(); databaseError.setFromTDS(tdsReader); - SQLServerException.makeFromDatabaseError(con, null, databaseError.getErrorMessage(), databaseError, - false); + SQLServerException.makeFromDatabaseError(con, null, databaseError.getErrorMessage(), + databaseError, false); } command.setInterruptsEnabled(true); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/SSLCertificateValidation.java b/src/test/java/com/microsoft/sqlserver/jdbc/SSLCertificateValidation.java new file mode 100644 index 0000000000..af17708eb0 --- /dev/null +++ b/src/test/java/com/microsoft/sqlserver/jdbc/SSLCertificateValidation.java @@ -0,0 +1,172 @@ +/* + * 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.jdbc; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; + +import org.junit.jupiter.api.Test; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; + + +@RunWith(JUnitPlatform.class) +public class SSLCertificateValidation { + + /** + * Tests our internal method, validateServerName() against different possible names in SSL certificate. + * + * @throws Exception + */ + @Test + public void testValidateServerName() throws Exception { + + String serverName = "msjdbc.database.windows.net"; + String serverName2 = "bbbbuuzzuzzzzzz.example.net"; + String serverName3 = "xn--ms.database.windows.net"; + + // Set up the HostNameOverrideX509TrustManager object using reflection + TDSChannel tdsc = new TDSChannel(new SQLServerConnection("someConnectionProperty")); + Class hsoClass = Class.forName("com.microsoft.sqlserver.jdbc.TDSChannel$HostNameOverrideX509TrustManager"); + Constructor constructor = hsoClass.getDeclaredConstructors()[0]; + constructor.setAccessible(true); + Object hsoObject = constructor.newInstance(null, tdsc, null, serverName); + Method method = hsoObject.getClass().getDeclaredMethod("validateServerName", String.class); + method.setAccessible(true); + + /* + * Server Name = msjdbc.database.windows.net + * SAN = msjdbc.database.windows.net + * Expected result: true + */ + assertTrue((boolean) method.invoke(hsoObject, "msjdbc.database.windows.net")); + + /* + * Server Name = msjdbc.database.windows.net + * SAN = msjdbc***.database.windows.net + * Expected result: true + */ + assertTrue((boolean) method.invoke(hsoObject, "msjdbc***.database.windows.net")); + + /* + * Server Name = msjdbc.database.windows.net + * SAN = ms*bc.database.windows.net + * Expected result: true + */ + assertTrue((boolean) method.invoke(hsoObject, "ms*bc.database.windows.net")); + + /* + * Server Name = msjdbc.database.windows.net + * SAN = *bc.database.windows.net + * Expected result: true + */ + assertTrue((boolean) method.invoke(hsoObject, "*bc.database.windows.net")); + + /* + * Server Name = msjdbc.database.windows.net + * SAN = ms*.database.windows.net + * Expected result: true + */ + assertTrue((boolean) method.invoke(hsoObject, "ms*.database.windows.net")); + + /* + * Server Name = msjdbc.database.windows.net + * SAN = *jd*.database.windows.net + * Expected result: true + */ + assertTrue((boolean) method.invoke(hsoObject, "*jd*.database.windows.net")); + + /* + * Server Name = msjdbc.database.windows.net + * SAN = ms.*.net + * Expected result: false + */ + assertFalse((boolean) method.invoke(hsoObject, "ms.*.net")); + + /* + * Server Name = msjdbc.database.windows.net + * SAN = msjdbc.asd*dsa.windows.net + * Expected result: false + */ + assertFalse((boolean) method.invoke(hsoObject, "msjdbc.asd*dsa.windows.net")); + + /* + * Server Name = msjdbc.database.windows.net + * SAN = *.*.windows.net + * Expected result: false + */ + assertFalse((boolean) method.invoke(hsoObject, ".*.windows.net")); + + /* + * Server Name = msjdbc.database.windows.net + * SAN = msjdbc.*.windows.net + * Expected result: false + */ + assertFalse((boolean) method.invoke(hsoObject, "msjdbc.*.windows.net")); + + /* + * Server Name = msjdbc.database.windows.net + * SAN = *.*.windows.net + * Expected result: false + * Note: multiple wildcards are not allowed, so this case shouldn't happen, but we still make sure to fail this. + */ + assertFalse((boolean) method.invoke(hsoObject, "*.*.windows.net")); + + /* + * Server Name = msjdbc.database.windows.net + * SAN = *.com + * Expected result: false + * A cert with * plus a top-level domain is not allowed. + */ + assertFalse((boolean) method.invoke(hsoObject, "*.com")); + + /* + * Server Name = msjdbc.database.windows.net + * SAN = xn--ms*.database.windows.net + * Expected result: false + */ + assertFalse((boolean) method.invoke(hsoObject, "xn--ms*.database.windows.net")); + + /* + * Server Name = msjdbc.database.windows.net + * SAN = * + * Expected result: false + */ + assertFalse((boolean) method.invoke(hsoObject, "*")); + + /* + * Server Name = msjdbc.database.windows.net + * SAN = ms*atabase.windows.net + * Expected result: false + */ + assertFalse((boolean) method.invoke(hsoObject, "ms*atabase.windows.net")); + + hsoObject = constructor.newInstance(null, tdsc, null, serverName2); + method = hsoObject.getClass().getDeclaredMethod("validateServerName", String.class); + method.setAccessible(true); + + /* + * Server Name = bbbbuuzzuzzzzzz.example.net + * SAN = b*zzz.example.net + * Expected result: true + */ + assertTrue((boolean) method.invoke(hsoObject, "b*zzz.example.net")); + + hsoObject = constructor.newInstance(null, tdsc, null, serverName3); + method = hsoObject.getClass().getDeclaredMethod("validateServerName", String.class); + method.setAccessible(true); + + /* + * Server Name = xn--ms.database.windows.net + * SAN = xn--ms.database.windows.net + * Expected result: true + */ + assertTrue((boolean) method.invoke(hsoObject, "xn--ms.database.windows.net")); + } +} From b18c44e3316adec6555a9abad1c65e7078588b84 Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Wed, 2 Jan 2019 16:58:36 -0800 Subject: [PATCH 47/51] Enable all tests in newly added datatype test (#916) --- .../jdbc/datatypes/KatmaiDataTypesTest.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java index cd256d0564..dc3f0bee1c 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java @@ -57,7 +57,7 @@ public class KatmaiDataTypesTest extends AbstractTest { final static String escapedProcName = AbstractSQLGenerator.escapeIdentifier(procName); enum SQLType { - date("yyyy-mm-dd", 0, java.sql.Types.DATE, "Date"), + date("yyyy-mm-dd", 0, java.sql.Types.DATE, "java.sql.Date"), time("hh:mm:ss", 7, java.sql.Types.TIME, "java.sql.Time"), @@ -1059,6 +1059,7 @@ enum TestValue { } }; + @Test public void testResultSetGetters() throws Exception { try (Connection conn = DriverManager.getConnection(connectionString)) { for (TestValue value : TestValue.values()) @@ -1066,6 +1067,7 @@ public void testResultSetGetters() throws Exception { } } + @Test public void testResultSetUpdaters() throws Exception { try (Connection conn = DriverManager.getConnection(connectionString)) { for (TestValue value : TestValue.values()) @@ -1073,6 +1075,7 @@ public void testResultSetUpdaters() throws Exception { } } + @Test public void testSetters() throws Exception { try (Connection conn = DriverManager.getConnection(connectionString + ";sendTimeAsDateTime=true")) { for (TestValue value : TestValue.values()) @@ -1080,6 +1083,7 @@ public void testSetters() throws Exception { } } + @Test public void testCallableStatementGetters() throws Exception { try (Connection conn = DriverManager.getConnection(connectionString)) { for (TestValue value : TestValue.values()) @@ -1087,13 +1091,16 @@ public void testCallableStatementGetters() throws Exception { } } + @Test public void testResultSetMetaData() throws Exception { try (Connection conn = DriverManager.getConnection(connectionString)) { + TestValue v[] = TestValue.values(); for (TestValue value : TestValue.values()) value.sqlValue.verifyResultSetMetaData(conn); } } + @Test public void testParameterMetaData() throws Exception { try (Connection conn = DriverManager.getConnection(connectionString)) { for (TestValue value : TestValue.values()) @@ -1104,6 +1111,7 @@ public void testParameterMetaData() throws Exception { /* * test CS.setObject(timestamp, TIME)/registerOutParam(TIME) with sendTimeAsDatetime */ + @Test public void testSendTimestampAsTimeAsDatetime() throws Exception { try (Connection conn = DriverManager.getConnection(connectionString + ";sendTimeAsDatetime=true")) { try (Statement stmt = conn.createStatement()) { @@ -1142,6 +1150,7 @@ public void testSendTimestampAsTimeAsDatetime() throws Exception { * test sending Timestamp to the server via an updater does not result in the same behavior as a setter wrt * double-rounding of fractional seconds */ + @Test public void testDoubleRounding() throws Exception { try (Connection conn = DriverManager.getConnection(connectionString)) { @@ -1209,6 +1218,7 @@ public void testDoubleRounding() throws Exception { * year of an imperial era. See for more details: * http://java.sun.com/javase/6/docs/technotes/guides/intl/calendar.doc.html */ + @Test public void testWithJapaneseImperialCalendar() throws Exception { /* * From http://java.sun.com/javase/6/docs/api/java/util/Locale.html : "Note: When you ask for a resource for a @@ -1218,10 +1228,9 @@ public void testWithJapaneseImperialCalendar() throws Exception { */ Locale japaneseImperialLocale = new Locale("ja", "JP", "JP"); Calendar japaneseImperialCalendar = Calendar.getInstance(japaneseImperialLocale); - MessageFormat cal = new MessageFormat(TestResource.getResource("R_noJRESupport")); - assumeTrue(GregorianCalendar.class.isInstance(japaneseImperialCalendar), - cal.format(japaneseImperialLocale.toString())); + Object[] msgsArgs = {japaneseImperialLocale.toString()}; + assumeTrue(GregorianCalendar.class.isInstance(japaneseImperialCalendar), cal.format(msgsArgs)); Locale defaultLocale = Locale.getDefault(); Locale.setDefault(japaneseImperialLocale); From 0a5e216be0fad33ae8ad69758383a00c720b5ec3 Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Thu, 3 Jan 2019 10:01:11 -0800 Subject: [PATCH 48/51] Tests | Rename test file name (#917) --- .../datatypes/{KatmaiDataTypesTest.java => DataTypesTest.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/test/java/com/microsoft/sqlserver/jdbc/datatypes/{KatmaiDataTypesTest.java => DataTypesTest.java} (97%) diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/DataTypesTest.java similarity index 97% rename from src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java rename to src/test/java/com/microsoft/sqlserver/jdbc/datatypes/DataTypesTest.java index dc3f0bee1c..4038abc814 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/KatmaiDataTypesTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/DataTypesTest.java @@ -48,7 +48,7 @@ * date/time/datetime2/datetimeoffset data types. Also includes tests for data type mappings. */ @RunWith(JUnitPlatform.class) -public class KatmaiDataTypesTest extends AbstractTest { +public class DataTypesTest extends AbstractTest { final static String tableName = RandomUtil.getIdentifier("KatmaiDataTypesTable"); final static String escapedTableName = AbstractSQLGenerator.escapeIdentifier(tableName); From fc2428423408c31db90b7708ee9c2c4f932abd54 Mon Sep 17 00:00:00 2001 From: Cheena Malhotra Date: Fri, 4 Jan 2019 14:55:21 -0800 Subject: [PATCH 49/51] Updates | Test Improvements + Readme changes + Dependency version updates (#919) --- README.md | 36 +++-- build.gradle | 24 ++-- pom.xml | 26 ++-- .../microsoft/sqlserver/jdbc/JDBC43Test.java | 44 +++--- .../jdbc/SSLCertificateValidation.java | 84 ++++------- .../sqlserver/jdbc/TestResource.java | 7 +- .../jdbc/bulkCopy/BulkCopyAllTypesTest.java | 2 +- .../bulkCopy/BulkCopyResultSetCursorTest.java | 22 +-- .../jdbc/connection/ConnectionDriverTest.java | 15 +- .../jdbc/connection/PoolingTest.java | 4 + .../SQLServerSpatialDatatypeTest.java | 8 +- .../jdbc/datatypes/TVPWithSqlVariantTest.java | 19 +-- .../sqlserver/jdbc/osgi/DataFactoryTest.java | 5 +- .../jdbc/preparedStatement/SetObjectTest.java | 2 +- .../sqlserver/jdbc/tvp/TVPAllTypesTest.java | 4 +- .../sqlserver/jdbc/tvp/TVPIssuesTest.java | 4 - .../unit/statement/PreparedStatementTest.java | 133 +++++++----------- .../jdbc/unit/statement/StatementTest.java | 11 +- .../sqlserver/testframework/DBConnection.java | 7 + 19 files changed, 209 insertions(+), 248 deletions(-) diff --git a/README.md b/README.md index 8fb19d3f95..c934ecda21 100644 --- a/README.md +++ b/README.md @@ -102,8 +102,10 @@ Starting from version 7.0.0, the driver Jars (jre10 and above) will expose 'Auto This project has following dependencies: Compile Time: - - `azure-keyvault` : Azure Key Vault Provider for Always Encrypted Azure Key Vault feature (optional) - - `adal4j` : Azure ActiveDirectory Library for Java for Azure Active Directory Authentication feature and Azure Key Vault feature (optional) + - `com.microsoft.azure:azure-keyvault` : Azure Key Vault Provider for Always Encrypted Azure Key Vault feature (optional) + - `com.microsoft.azure:azure-keyvault-webkey` : Azure Key Vault Provider for Always Encrypted Azure Key Vault feature (optional) + - `com.microsoft.azure:adal4j` : Azure Active Directory Library for Java for Azure Active Directory Authentication feature and Azure Key Vault feature (optional) + - `com.microsoft.rest:client-runtime` : Azure Active Directory Library for Java for Azure Active Directory Authentication feature and Azure Key Vault feature (optional) Test Time: - `junit:jar` : For Unit Test cases. @@ -117,7 +119,7 @@ mvn dependency:tree ### Azure Key Vault and Azure Active Directory Authentication Dependencies Projects that require either of the two features need to explicitly declare the dependency in their pom file. -***For Example:*** If you are using *Azure Active Directory Authentication feature* then you need to redeclare *adal4j* dependency in your project's pom file. Please see the following snippet: +***For Example:*** If you are using *Azure Active Directory Authentication feature* then you need to redeclare *adal4j* and *client-runtime* dependency in your project's pom file. Please see the following snippet: ```xml @@ -130,11 +132,17 @@ Projects that require either of the two features need to explicitly declare the com.microsoft.azure adal4j - 1.6.0 + 1.6.3 + + + + com.microsoft.rest + client-runtime + 1.6.5 ``` -***For Example:*** If you are using *Azure Key Vault feature* then you need to redeclare *azure-keyvault* dependency and *adal4j* dependency in your project's pom file. Please see the following snippet: +***For Example:*** If you are using *Azure Key Vault feature* then you need to redeclare *azure-keyvault*, *azure-keyvault-webkey* dependency and *adal4j*, *client-runtime* dependency in your project's pom file. Please see the following snippet: ```xml @@ -147,13 +155,25 @@ Projects that require either of the two features need to explicitly declare the com.microsoft.azure adal4j - 1.6.0 + 1.6.3 + + + + com.microsoft.rest + client-runtime + 1.6.5 com.microsoft.azure azure-keyvault - 1.0.0 + 1.2.0 + + + + com.microsoft.azure + zure-keyvault-webkey + 1.2.0 ``` @@ -189,7 +209,7 @@ Preview releases happen approximately monthly between stable releases. This give You can see what is going into a future release by monitoring [Milestones](https://github.com/Microsoft/mssql-jdbc/milestones) in the repository. ### Versioning convention -Starting with 6.0, stable versions have an even minor version. For example, 6.0, 6.2, 6.4, 7.0. Preview versions have an odd minor version. For example, 6.1, 6.3, 6.5, 7.1. +Starting with 6.0, stable versions have an even minor version. For example, 6.0, 6.2, 6.4, 7.0, 7.2. Preview versions have an odd minor version. For example, 6.1, 6.3, 6.5, 7.1. ## Contributors Special thanks to everyone who has contributed to the project. diff --git a/build.gradle b/build.gradle index 4f099b60d1..abf1126b27 100644 --- a/build.gradle +++ b/build.gradle @@ -82,18 +82,18 @@ repositories { } dependencies { - compileOnly 'com.microsoft.azure:azure-keyvault:1.1.1', - 'com.microsoft.azure:azure-keyvault-webkey:1.1.1', - 'com.microsoft.rest:client-runtime:1.6.2', - 'com.microsoft.azure:adal4j:1.6.2' - testCompile 'org.junit.platform:junit-platform-console:1.3.1', - 'org.junit.platform:junit-platform-commons:1.3.1', - 'org.junit.platform:junit-platform-engine:1.3.1', - 'org.junit.platform:junit-platform-launcher:1.3.1', - 'org.junit.platform:junit-platform-runner:1.3.1', - 'org.junit.platform:junit-platform-surefire-provider:1.3.1', - 'org.junit.jupiter:junit-jupiter-api:5.3.1', - 'org.junit.jupiter:junit-jupiter-engine:5.3.1', + compileOnly 'com.microsoft.azure:azure-keyvault:1.2.0', + 'com.microsoft.azure:azure-keyvault-webkey:1.2.0', + 'com.microsoft.rest:client-runtime:1.6.5', + 'com.microsoft.azure:adal4j:1.6.3' + testCompile 'org.junit.platform:junit-platform-console:1.3.2', + 'org.junit.platform:junit-platform-commons:1.3.2', + 'org.junit.platform:junit-platform-engine:1.3.2', + 'org.junit.platform:junit-platform-launcher:1.3.2', + 'org.junit.platform:junit-platform-runner:1.3.2', + 'org.junit.platform:junit-platform-surefire-provider:1.3.2', + 'org.junit.jupiter:junit-jupiter-api:5.3.2', + 'org.junit.jupiter:junit-jupiter-engine:5.3.2', 'com.zaxxer:HikariCP:3.2.0', 'org.apache.commons:commons-dbcp2:2.5.0', 'org.slf4j:slf4j-nop:1.7.25' diff --git a/pom.xml b/pom.xml index ccc932a716..6c53a6e8ad 100644 --- a/pom.xml +++ b/pom.xml @@ -39,8 +39,8 @@ UTF-8 - 1.3.1 - 5.3.1 + 1.3.2 + 5.3.2 4.3.1 @@ -49,23 +49,29 @@ com.microsoft.azure azure-keyvault - 1.1.1 + 1.2.0 true com.microsoft.azure azure-keyvault-webkey - 1.1.1 + 1.2.0 true com.microsoft.azure adal4j - 1.6.2 + 1.6.3 + true + + + com.microsoft.rest + client-runtime + 1.6.5 true - + org.osgi org.osgi.core @@ -79,14 +85,6 @@ provided - - - com.microsoft.rest - client-runtime - 1.6.2 - true - - org.junit.platform diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/JDBC43Test.java b/src/test/java/com/microsoft/sqlserver/jdbc/JDBC43Test.java index b688db2024..44388eea1b 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/JDBC43Test.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/JDBC43Test.java @@ -141,18 +141,18 @@ public void connectionPoolDataSourceTest() throws TestAbortedException, SQLExcep @Test public void setShardingKeyIfValidTest() throws TestAbortedException, SQLException { assumeTrue(TestUtils.supportJDBC43(connection)); - SQLServerConnection connection43 = (SQLServerConnection43) DriverManager.getConnection(connectionString); - try { - connection43.setShardingKeyIfValid(shardingKey, 10); - } catch (SQLException e) { - assert (e.getMessage().contains(TestResource.getResource("R_operationNotSupported"))); - } - try { - connection43.setShardingKeyIfValid(shardingKey, superShardingKey, 10); - } catch (SQLException e) { - assert (e.getMessage().contains(TestResource.getResource("R_operationNotSupported"))); + try (SQLServerConnection connection43 = (SQLServerConnection43) DriverManager.getConnection(connectionString)) { + try { + connection43.setShardingKeyIfValid(shardingKey, 10); + } catch (SQLException e) { + assert (e.getMessage().contains(TestResource.getResource("R_operationNotSupported"))); + } + try { + connection43.setShardingKeyIfValid(shardingKey, superShardingKey, 10); + } catch (SQLException e) { + assert (e.getMessage().contains(TestResource.getResource("R_operationNotSupported"))); + } } - } /** @@ -165,18 +165,18 @@ public void setShardingKeyIfValidTest() throws TestAbortedException, SQLExceptio @Test public void setShardingKeyTest() throws TestAbortedException, SQLException { assumeTrue(TestUtils.supportJDBC43(connection)); - SQLServerConnection connection43 = (SQLServerConnection43) DriverManager.getConnection(connectionString); - try { - connection43.setShardingKey(shardingKey); - } catch (SQLException e) { - assert (e.getMessage().contains(TestResource.getResource("R_operationNotSupported"))); - } - try { - connection43.setShardingKey(shardingKey, superShardingKey); - } catch (SQLException e) { - assert (e.getMessage().contains(TestResource.getResource("R_operationNotSupported"))); + try (SQLServerConnection connection43 = (SQLServerConnection43) DriverManager.getConnection(connectionString)) { + try { + connection43.setShardingKey(shardingKey); + } catch (SQLException e) { + assert (e.getMessage().contains(TestResource.getResource("R_operationNotSupported"))); + } + try { + connection43.setShardingKey(shardingKey, superShardingKey); + } catch (SQLException e) { + assert (e.getMessage().contains(TestResource.getResource("R_operationNotSupported"))); + } } - } /** diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/SSLCertificateValidation.java b/src/test/java/com/microsoft/sqlserver/jdbc/SSLCertificateValidation.java index af17708eb0..101cfb4b2e 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/SSLCertificateValidation.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/SSLCertificateValidation.java @@ -39,133 +39,99 @@ public void testValidateServerName() throws Exception { Object hsoObject = constructor.newInstance(null, tdsc, null, serverName); Method method = hsoObject.getClass().getDeclaredMethod("validateServerName", String.class); method.setAccessible(true); - + /* - * Server Name = msjdbc.database.windows.net - * SAN = msjdbc.database.windows.net - * Expected result: true + * Server Name = msjdbc.database.windows.net SAN = msjdbc.database.windows.net Expected result: true */ assertTrue((boolean) method.invoke(hsoObject, "msjdbc.database.windows.net")); /* - * Server Name = msjdbc.database.windows.net - * SAN = msjdbc***.database.windows.net - * Expected result: true + * Server Name = msjdbc.database.windows.net SAN = msjdbc***.database.windows.net Expected result: true */ assertTrue((boolean) method.invoke(hsoObject, "msjdbc***.database.windows.net")); /* - * Server Name = msjdbc.database.windows.net - * SAN = ms*bc.database.windows.net - * Expected result: true + * Server Name = msjdbc.database.windows.net SAN = ms*bc.database.windows.net Expected result: true */ assertTrue((boolean) method.invoke(hsoObject, "ms*bc.database.windows.net")); /* - * Server Name = msjdbc.database.windows.net - * SAN = *bc.database.windows.net - * Expected result: true + * Server Name = msjdbc.database.windows.net SAN = *bc.database.windows.net Expected result: true */ assertTrue((boolean) method.invoke(hsoObject, "*bc.database.windows.net")); /* - * Server Name = msjdbc.database.windows.net - * SAN = ms*.database.windows.net - * Expected result: true + * Server Name = msjdbc.database.windows.net SAN = ms*.database.windows.net Expected result: true */ assertTrue((boolean) method.invoke(hsoObject, "ms*.database.windows.net")); /* - * Server Name = msjdbc.database.windows.net - * SAN = *jd*.database.windows.net - * Expected result: true + * Server Name = msjdbc.database.windows.net SAN = *jd*.database.windows.net Expected result: true */ assertTrue((boolean) method.invoke(hsoObject, "*jd*.database.windows.net")); /* - * Server Name = msjdbc.database.windows.net - * SAN = ms.*.net - * Expected result: false + * Server Name = msjdbc.database.windows.net SAN = ms.*.net Expected result: false */ assertFalse((boolean) method.invoke(hsoObject, "ms.*.net")); /* - * Server Name = msjdbc.database.windows.net - * SAN = msjdbc.asd*dsa.windows.net - * Expected result: false + * Server Name = msjdbc.database.windows.net SAN = msjdbc.asd*dsa.windows.net Expected result: false */ assertFalse((boolean) method.invoke(hsoObject, "msjdbc.asd*dsa.windows.net")); /* - * Server Name = msjdbc.database.windows.net - * SAN = *.*.windows.net - * Expected result: false + * Server Name = msjdbc.database.windows.net SAN = *.*.windows.net Expected result: false */ assertFalse((boolean) method.invoke(hsoObject, ".*.windows.net")); /* - * Server Name = msjdbc.database.windows.net - * SAN = msjdbc.*.windows.net - * Expected result: false + * Server Name = msjdbc.database.windows.net SAN = msjdbc.*.windows.net Expected result: false */ assertFalse((boolean) method.invoke(hsoObject, "msjdbc.*.windows.net")); /* - * Server Name = msjdbc.database.windows.net - * SAN = *.*.windows.net - * Expected result: false - * Note: multiple wildcards are not allowed, so this case shouldn't happen, but we still make sure to fail this. + * Server Name = msjdbc.database.windows.net SAN = *.*.windows.net Expected result: false Note: multiple + * wildcards are not allowed, so this case shouldn't happen, but we still make sure to fail this. */ assertFalse((boolean) method.invoke(hsoObject, "*.*.windows.net")); /* - * Server Name = msjdbc.database.windows.net - * SAN = *.com - * Expected result: false - * A cert with * plus a top-level domain is not allowed. + * Server Name = msjdbc.database.windows.net SAN = *.com Expected result: false A cert with * plus a top-level + * domain is not allowed. */ assertFalse((boolean) method.invoke(hsoObject, "*.com")); /* - * Server Name = msjdbc.database.windows.net - * SAN = xn--ms*.database.windows.net - * Expected result: false + * Server Name = msjdbc.database.windows.net SAN = xn--ms*.database.windows.net Expected result: false */ assertFalse((boolean) method.invoke(hsoObject, "xn--ms*.database.windows.net")); /* - * Server Name = msjdbc.database.windows.net - * SAN = * - * Expected result: false + * Server Name = msjdbc.database.windows.net SAN = * Expected result: false */ assertFalse((boolean) method.invoke(hsoObject, "*")); - + /* - * Server Name = msjdbc.database.windows.net - * SAN = ms*atabase.windows.net - * Expected result: false + * Server Name = msjdbc.database.windows.net SAN = ms*atabase.windows.net Expected result: false */ assertFalse((boolean) method.invoke(hsoObject, "ms*atabase.windows.net")); - + hsoObject = constructor.newInstance(null, tdsc, null, serverName2); method = hsoObject.getClass().getDeclaredMethod("validateServerName", String.class); method.setAccessible(true); - + /* - * Server Name = bbbbuuzzuzzzzzz.example.net - * SAN = b*zzz.example.net - * Expected result: true + * Server Name = bbbbuuzzuzzzzzz.example.net SAN = b*zzz.example.net Expected result: true */ assertTrue((boolean) method.invoke(hsoObject, "b*zzz.example.net")); - + hsoObject = constructor.newInstance(null, tdsc, null, serverName3); method = hsoObject.getClass().getDeclaredMethod("validateServerName", String.class); method.setAccessible(true); - + /* - * Server Name = xn--ms.database.windows.net - * SAN = xn--ms.database.windows.net - * Expected result: true + * Server Name = xn--ms.database.windows.net SAN = xn--ms.database.windows.net Expected result: true */ assertTrue((boolean) method.invoke(hsoObject, "xn--ms.database.windows.net")); } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java b/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java index cdfc565b10..421ba36ec2 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java @@ -28,7 +28,8 @@ protected Object[][] getContents() { {"R_fipsPropertyNotSet", "Aborting test case as FIPS_ENV property is not set."}, {"R_invalidTrustCert", "Invalid TrustServerCertificate value."}, {"R_invalidEncrypt", "Invalid encrypt value."}, {"R_notImplemented", "not implemented"}, - {"R_resultsetClosed", "The result set is closed."}, {"R_resultsetNull", "The result set is null."}, + {"R_resultsetClosed", "The result set is closed."}, {"R_statementClosed", "The statement is closed."}, + {"R_resultsetNull", "The result set is null."}, {"R_invalidFipsConfig", "Unable to verify FIPS mode settings."}, {"R_shouldBeEnabled", "should be enabled."}, {"R_StoredProcedureNotFound", "Could not find stored procedure"}, @@ -144,6 +145,10 @@ protected Object[][] getContents() { {"R_paramNotRecognized", "Not all parameters are recognized by driver."}, {"R_invalidGetPreparedStatementHandle", "Invalid use of getPreparedStatementHandle() after statement close expected."}, + {"R_invalidserverPreparedStatementDiscardThreshold", + "The serverPreparedStatementDiscardThreshold %s is not valid."}, + {"R_invalidenablePrepareOnFirstPreparedStatementCall", + "The property enablePrepareOnFirstPreparedStatementCall does not contain a valid boolean value. Only true or false can be used."}, {"R_cancellationFailed", "Cancellation failed."}, {"R_executionNotTimeout", "Execution did not timeout."}, {"R_executionTooLong", "Execution took too long."}, {"R_executionNotLong", "Execution did not take long enough."}, diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyAllTypesTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyAllTypesTest.java index 7b518741f5..ea9ad5103f 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyAllTypesTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyAllTypesTest.java @@ -64,7 +64,7 @@ private void testBulkCopyResultSet(boolean setSelectMethod, Integer resultSetTyp bcOperation.writeToServer(rs); bcOperation.close(); - ComparisonUtil.compareSrcTableAndDestTableIgnoreRowOrder(new DBConnection(connectionString), tableSrc, + ComparisonUtil.compareSrcTableAndDestTableIgnoreRowOrder(new DBConnection(connection), tableSrc, tableDest); } finally { terminateVariation(); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyResultSetCursorTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyResultSetCursorTest.java index 3ed177217e..e7a6b00dee 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyResultSetCursorTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyResultSetCursorTest.java @@ -69,8 +69,8 @@ private void serverCursorsTest(int resultSetType, int resultSetConcurrency) thro populateSourceTable(); try (Statement stmt2 = conn.createStatement(resultSetType, resultSetConcurrency); - ResultSet rs = stmt2 - .executeQuery("select * from " + AbstractSQLGenerator.escapeIdentifier(srcTable) + " ORDER BY id ASC"); + ResultSet rs = stmt2.executeQuery( + "select * from " + AbstractSQLGenerator.escapeIdentifier(srcTable) + " ORDER BY id ASC"); SQLServerBulkCopy bulkCopy = new SQLServerBulkCopy(conn)) { bulkCopy.setDestinationTableName(AbstractSQLGenerator.escapeIdentifier(desTable)); bulkCopy.writeToServer(rs); @@ -96,7 +96,8 @@ public void testSelectMethodSetToCursor() throws SQLException { createTables(stmt); populateSourceTable(); - try (ResultSet rs = stmt.executeQuery("select * from " + AbstractSQLGenerator.escapeIdentifier(srcTable) + " ORDER BY id ASC"); + try (ResultSet rs = stmt.executeQuery( + "select * from " + AbstractSQLGenerator.escapeIdentifier(srcTable) + " ORDER BY id ASC"); SQLServerBulkCopy bulkCopy = new SQLServerBulkCopy(conn)) { bulkCopy.setDestinationTableName(AbstractSQLGenerator.escapeIdentifier(desTable)); @@ -121,8 +122,8 @@ public void testMultiplePreparedStatementAndResultSet() throws SQLException { populateSourceTable(); try (Statement stmt1 = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); - ResultSet rs = stmt1 - .executeQuery("select * from " + AbstractSQLGenerator.escapeIdentifier(srcTable) + " ORDER BY id ASC")) { + ResultSet rs = stmt1.executeQuery( + "select * from " + AbstractSQLGenerator.escapeIdentifier(srcTable) + " ORDER BY id ASC")) { try (SQLServerBulkCopy bulkCopy = new SQLServerBulkCopy(conn)) { bulkCopy.setDestinationTableName(AbstractSQLGenerator.escapeIdentifier(desTable)); bulkCopy.writeToServer(rs); @@ -157,8 +158,8 @@ public void testMultiplePreparedStatementAndResultSet() throws SQLException { } try (Statement stmt2 = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE); - ResultSet rs2 = stmt2 - .executeQuery("select * from " + AbstractSQLGenerator.escapeIdentifier(srcTable) + " ORDER BY id ASC"); + ResultSet rs2 = stmt2.executeQuery("select * from " + + AbstractSQLGenerator.escapeIdentifier(srcTable) + " ORDER BY id ASC"); SQLServerBulkCopy bulkCopy3 = new SQLServerBulkCopy(conn)) { bulkCopy3.setDestinationTableName(AbstractSQLGenerator.escapeIdentifier(desTable)); bulkCopy3.writeToServer(rs2); @@ -169,8 +170,9 @@ public void testMultiplePreparedStatementAndResultSet() throws SQLException { } private static void verifyDestinationTableData(int expectedNumberOfRows) throws SQLException { - try (Connection conn = DriverManager.getConnection(connectionString); ResultSet rs = conn.createStatement() - .executeQuery("select * from " + AbstractSQLGenerator.escapeIdentifier(desTable) + " ORDER BY id ASC")) { + try (Connection conn = DriverManager.getConnection(connectionString); + ResultSet rs = conn.createStatement().executeQuery( + "select * from " + AbstractSQLGenerator.escapeIdentifier(desTable) + " ORDER BY id ASC")) { int expectedArrayLength = expectedBigDecimals.length; @@ -196,7 +198,7 @@ private static void populateSourceTable() throws SQLException { Calendar calGMT = Calendar.getInstance(TimeZone.getTimeZone("GMT")); try (Connection conn = DriverManager.getConnection(connectionString); - SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) conn.prepareStatement(sql)) { + SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) conn.prepareStatement(sql)) { for (int i = 0; i < expectedBigDecimals.length; i++) { pstmt.setBigDecimal(1, expectedBigDecimals[i]); pstmt.setString(2, expectedStrings[i]); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/connection/ConnectionDriverTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/connection/ConnectionDriverTest.java index 2e256f7b1a..7c3cf72bdd 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/connection/ConnectionDriverTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/ConnectionDriverTest.java @@ -190,8 +190,11 @@ public void testConnectionEvents() throws SQLException { // Check to see if error occurred. assertTrue(myE.errorOccurred, TestResource.getResource("R_errorNotCalled")); + } finally { + // make sure that connection is closed. + if (null != pooledConnection) + pooledConnection.close(); } - // make sure that connection is closed. } @Test @@ -218,6 +221,10 @@ public void testConnectionPoolGetTwice() throws SQLException { con.close(); // check to make sure that connection is closed. assertTrue(con.isClosed(), TestResource.getResource("R_connectionIsNotClosed")); + } finally { + // make sure that connection is closed. + if (null != pooledConnection) + pooledConnection.close(); } } @@ -254,7 +261,7 @@ public void testIsWrapperFor() throws SQLException, ClassNotFoundException { Object[] msgArgs1 = {"SQLServerConnection"}; assertTrue(isWrapper, form.format(msgArgs1)); - assertEquals(ssconn.TRANSACTION_SNAPSHOT, ssconn.TRANSACTION_SNAPSHOT, + assertEquals(ISQLServerConnection.TRANSACTION_SNAPSHOT, ISQLServerConnection.TRANSACTION_SNAPSHOT, TestResource.getResource("R_cantAccessSnapshot")); isWrapper = ssconn.isWrapperFor(Class.forName("com.microsoft.sqlserver.jdbc.ISQLServerConnection")); @@ -262,7 +269,7 @@ public void testIsWrapperFor() throws SQLException, ClassNotFoundException { assertTrue(isWrapper, form.format(msgArgs2)); ISQLServerConnection iSql = (ISQLServerConnection) ssconn .unwrap(Class.forName("com.microsoft.sqlserver.jdbc.ISQLServerConnection")); - assertEquals(iSql.TRANSACTION_SNAPSHOT, iSql.TRANSACTION_SNAPSHOT, + assertEquals(ISQLServerConnection.TRANSACTION_SNAPSHOT, ISQLServerConnection.TRANSACTION_SNAPSHOT, TestResource.getResource("R_cantAccessSnapshot")); ssconn.unwrap(Class.forName("java.sql.Connection")); @@ -462,7 +469,6 @@ public void testInvalidCombination() throws SQLException { ds.setFailoverPartner(RandomUtil.getIdentifier("FailoverPartner")); timerStart = System.currentTimeMillis(); try (Connection con = ds.getConnection()) { - long timeDiff = timerEnd - timerStart; assertTrue(con == null, TestResource.getResource("R_shouldNotConnect")); MessageFormat form = new MessageFormat(TestResource.getResource("R_exitedMoreSeconds")); @@ -488,7 +494,6 @@ public void testIncorrectDatabaseWithFailoverPartner() throws SQLException { ds.setFailoverPartner(RandomUtil.getIdentifier("FailoverPartner")); timerStart = System.currentTimeMillis(); try (Connection con = ds.getConnection()) { - long timeDiff = timerEnd - timerStart; assertTrue(con == null, TestResource.getResource("R_shouldNotConnect")); MessageFormat form = new MessageFormat(TestResource.getResource("R_exitedLessSeconds")); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/connection/PoolingTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/connection/PoolingTest.java index cc56b26810..401cc459a3 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/connection/PoolingTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/PoolingTest.java @@ -74,6 +74,10 @@ public void testPooling() throws SQLException { if (e.getMessage().startsWith(TestResource.getResource("R_invalidObjectName"))) { tempTableFileRemoved = true; } + } finally { + if (null != pc) { + pc.close(); + } } assertTrue(tempTableFileRemoved, TestResource.getResource("R_tempTAbleNotRemoved")); } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SQLServerSpatialDatatypeTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SQLServerSpatialDatatypeTest.java index cce188515e..a2cde12b76 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SQLServerSpatialDatatypeTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SQLServerSpatialDatatypeTest.java @@ -1042,7 +1042,7 @@ public void testNull() throws SQLException { } } } - + @Test public void testWrongtype() throws SQLException { beforeEachSetup(); @@ -1059,7 +1059,8 @@ public void testWrongtype() throws SQLException { pstmt.execute(); try { - SQLServerResultSet rs = (SQLServerResultSet) stmt.executeQuery("select * from " + AbstractSQLGenerator.escapeIdentifier(geomTableName)); + SQLServerResultSet rs = (SQLServerResultSet) stmt + .executeQuery("select * from " + AbstractSQLGenerator.escapeIdentifier(geomTableName)); rs.next(); rs.getGeography(1); // should fail fail(); @@ -1074,7 +1075,8 @@ public void testWrongtype() throws SQLException { pstmt.execute(); try { - SQLServerResultSet rs = (SQLServerResultSet) stmt.executeQuery("select * from " + AbstractSQLGenerator.escapeIdentifier(geogTableName)); + SQLServerResultSet rs = (SQLServerResultSet) stmt + .executeQuery("select * from " + AbstractSQLGenerator.escapeIdentifier(geogTableName)); rs.next(); rs.getGeometry(1); // should fail fail(); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/TVPWithSqlVariantTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/TVPWithSqlVariantTest.java index 307bbdcdf9..135816a2ef 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/TVPWithSqlVariantTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/TVPWithSqlVariantTest.java @@ -14,7 +14,6 @@ import java.sql.SQLException; import java.sql.SQLTimeoutException; -import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -322,7 +321,7 @@ public void testLongVarChar() throws SQLException { } /** - * Test ith datetime + * Test with datetime * * @throws SQLException * @throws SQLTimeoutException @@ -353,10 +352,10 @@ public void testDateTime() throws SQLException { * * @throws SQLException * @throws SQLTimeoutException - * https://msdn.microsoft.com/en-ca/library/dd303302.aspx?f=255&MSPPError=-2147217396 - * Data types cannot be NULL when inside a sql_variant + * https://msdn.microsoft.com/en-ca/library/dd303302.aspx?f=255&MSPPError=-2147217396 Data types cannot be + * NULL when inside a sql_variant */ - @Test + @Test public void testNull() throws SQLException { tvp = new SQLServerDataTable(); tvp.addColumnMetadata("c1", microsoft.sql.Types.SQL_VARIANT); @@ -509,19 +508,9 @@ public void terminateVariation() throws SQLException { TestUtils.dropProcedureIfExists(AbstractSQLGenerator.escapeIdentifier(procedureName), stmt); TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(destTable), stmt); dropTVPS(); - } - - /** - * drop the tables - * - * @throws SQLException - */ - @AfterAll - public static void afterAll() throws SQLException { if (null != stmt) { stmt.close(); } - if (null != conn) { conn.close(); } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/osgi/DataFactoryTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/osgi/DataFactoryTest.java index 092edda023..c283b9d53f 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/osgi/DataFactoryTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/osgi/DataFactoryTest.java @@ -19,6 +19,8 @@ import javax.sql.XAConnection; import javax.sql.XADataSource; +import org.eclipse.gemini.blueprint.mock.MockBundleContext; +import org.eclipse.gemini.blueprint.mock.MockServiceRegistration; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; @@ -30,9 +32,6 @@ import com.microsoft.sqlserver.jdbc.SQLServerDriver; import com.microsoft.sqlserver.testframework.AbstractTest; -import org.eclipse.gemini.blueprint.mock.MockBundleContext; -import org.eclipse.gemini.blueprint.mock.MockServiceRegistration; - @RunWith(JUnitPlatform.class) public class DataFactoryTest extends AbstractTest { diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/SetObjectTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/SetObjectTest.java index 1ac70723a7..4d13267f8b 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/SetObjectTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/SetObjectTest.java @@ -4,7 +4,7 @@ */ package com.microsoft.sqlserver.jdbc.preparedStatement; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.sql.Connection; import java.sql.DriverManager; diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/tvp/TVPAllTypesTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/tvp/TVPAllTypesTest.java index d5c7e2f9cf..a2124deb86 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/tvp/TVPAllTypesTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/tvp/TVPAllTypesTest.java @@ -79,7 +79,7 @@ private void testTVPResultSet(boolean setSelectMethod, Integer resultSetType, "INSERT INTO " + tableDest.getEscapedTableName() + " select * from ? ;")) { pstmt.setStructured(1, tvpName, rs); pstmt.execute(); - ComparisonUtil.compareSrcTableAndDestTableIgnoreRowOrder(new DBConnection(connectionString), tableSrc, + ComparisonUtil.compareSrcTableAndDestTableIgnoreRowOrder(new DBConnection(conn), tableSrc, tableDest); } catch (Exception e) { fail(TestResource.getResource("R_unexpectedErrorMessage") + e.toString()); @@ -130,7 +130,7 @@ private void testTVPStoredProcedureResultSet(boolean setSelectMethod, Integer re .prepareCall("{call " + AbstractSQLGenerator.escapeIdentifier(procedureName) + "(?)}")) { Cstmt.setStructured(1, tvpName, rs); Cstmt.execute(); - ComparisonUtil.compareSrcTableAndDestTableIgnoreRowOrder(new DBConnection(connectionString), tableSrc, + ComparisonUtil.compareSrcTableAndDestTableIgnoreRowOrder(new DBConnection(conn), tableSrc, tableDest); } finally { stmt.close(); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/tvp/TVPIssuesTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/tvp/TVPIssuesTest.java index 25c6261026..badc77b110 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/tvp/TVPIssuesTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/tvp/TVPIssuesTest.java @@ -46,8 +46,6 @@ public class TVPIssuesTest extends AbstractTest { @Test public void tryTVPRSvarcharMax4000Issue() throws Exception { - setup(); - try (SQLServerStatement st = (SQLServerStatement) connection.createStatement(); ResultSet rs = st .executeQuery("select * from " + AbstractSQLGenerator.escapeIdentifier(srcTable_varcharMax)); @@ -98,8 +96,6 @@ public void testExceptionWithInvalidStoredProcedureName() throws Exception { */ @Test public void tryTVPPrecisionmissedissue315() throws Exception { - setup(); - try (Connection connection = DriverManager.getConnection(connectionString); Statement stmt = connection.createStatement(); ResultSet rs = stmt diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PreparedStatementTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PreparedStatementTest.java index e2ff2b7038..f1cfa0bb0e 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PreparedStatementTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/PreparedStatementTest.java @@ -4,7 +4,6 @@ */ package com.microsoft.sqlserver.jdbc.unit.statement; -import static java.util.concurrent.TimeUnit.SECONDS; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; @@ -19,11 +18,11 @@ import java.sql.Statement; import java.util.Random; import java.util.UUID; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicReference; -import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; @@ -44,8 +43,9 @@ public class PreparedStatementTest extends AbstractTest { final String tableName2 = RandomUtil.getIdentifier("#update2"); private void executeSQL(SQLServerConnection conn, String sql) throws SQLException { - Statement stmt = conn.createStatement(); - stmt.execute(sql); + try (Statement stmt = conn.createStatement()) { + stmt.execute(sql); + } } private int executeSQLReturnFirstInt(SQLServerConnection conn, String sql) throws SQLException { @@ -80,10 +80,8 @@ public void testBatchedUnprepare() throws SQLException { String lookupUniqueifier = UUID.randomUUID().toString(); - String queryCacheLookup = String.format("%%/*unpreparetest_%s%%*/SELECT * FROM sys.tables;", - lookupUniqueifier); - String query = String.format("/*unpreparetest_%s only sp_executesql*/SELECT * FROM sys.tables;", - lookupUniqueifier); + String queryCacheLookup = String.format("%%/*unpreparetest_%s%%*/SELECT 1;", lookupUniqueifier); + String query = String.format("/*unpreparetest_%s only sp_executesql*/SELECT 1;", lookupUniqueifier); // Verify nothing in cache. String verifyTotalCacheUsesQuery = String.format( @@ -95,7 +93,7 @@ public void testBatchedUnprepare() throws SQLException { int iterations = 25; query = String.format( - "/*unpreparetest_%s, sp_executesql->sp_prepexec->sp_execute- batched sp_unprepare*/SELECT * FROM sys.tables;", + "/*unpreparetest_%s, sp_executesql->sp_prepexec->sp_execute- batched sp_unprepare*/SELECT 1;", lookupUniqueifier); int prevDiscardActionCount = 0; @@ -146,7 +144,6 @@ public void testBatchedUnprepare() throws SQLException { * @throws SQLException */ @Test - @Tag("slow") public void testStatementPooling() throws Exception { testStatementPoolingInternal("batchInsert"); } @@ -161,7 +158,6 @@ public void testStatementPooling() throws Exception { * @throws IllegalArgumentException */ @Test - @Tag("slow") public void testStatementPoolingUseBulkCopyAPI() throws Exception { testStatementPoolingInternal("BulkCopy"); } @@ -185,8 +181,7 @@ public void testStatementPoolingEviction() throws SQLException { con.setServerPreparedStatementDiscardThreshold(discardedStatementCount); String lookupUniqueifier = UUID.randomUUID().toString(); - String query = String.format("/*statementpoolingevictiontest_%s*/SELECT * FROM sys.tables; -- ", - lookupUniqueifier); + String query = String.format("/*statementpoolingevictiontest_%s*/SELECT 1; -- ", lookupUniqueifier); // Add new statements to fill up the statement pool. for (int i = 0; i < cacheSize; ++i) { @@ -260,50 +255,34 @@ public void testStatementPoolingEviction() throws SQLException { } } - final class TestPrepareRace implements Runnable { - - SQLServerConnection con; - String[] queries; - AtomicReference exception; - - TestPrepareRace(SQLServerConnection con, String[] queries, AtomicReference exception) { - this.con = con; - this.queries = queries; - this.exception = exception; - } - - @Override - public void run() { - for (int j = 0; j < 500000; j++) { - try (SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) con - .prepareStatement(queries[j % 3])) { - pstmt.execute(); - } catch (SQLException e) { - exception.set(e); - break; - } - } - } - } - @Test public void testPrepareRace() throws Exception { String[] queries = new String[3]; - queries[0] = String.format("SELECT * FROM sys.tables -- %s", UUID.randomUUID()); - queries[1] = String.format("SELECT * FROM sys.tables -- %s", UUID.randomUUID()); - queries[2] = String.format("SELECT * FROM sys.tables -- %s", UUID.randomUUID()); + queries[0] = String.format("SELECT 1 -- %s", UUID.randomUUID()); + queries[1] = String.format("SELECT 1 -- %s", UUID.randomUUID()); + queries[2] = String.format("SELECT 1 -- %s", UUID.randomUUID()); - ExecutorService threadPool = Executors.newFixedThreadPool(4); + ExecutorService execServiceThread = Executors.newFixedThreadPool(10); + CountDownLatch latch = new CountDownLatch(3); AtomicReference exception = new AtomicReference<>(); - try (SQLServerConnection con = (SQLServerConnection) DriverManager.getConnection(connectionString)) { - for (int i = 0; i < 4; i++) { - threadPool.execute(new TestPrepareRace(con, queries, exception)); + try (SQLServerConnection con = (SQLServerConnection) DriverManager.getConnection(connectionString)) { + for (int i = 0; i < 3; i++) { + execServiceThread.submit(() -> { + for (int j = 0; j < 500; j++) { + try (SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) con + .prepareStatement(queries[j % 3])) { + pstmt.execute(); + } catch (SQLException e) { + exception.set(e); + break; + } + } + latch.countDown(); + }); } - - threadPool.shutdown(); - threadPool.awaitTermination(10, SECONDS); + latch.await(); assertNull(exception.get()); @@ -345,7 +324,7 @@ public void testStatementPoolingPreparedStatementExecAndUnprepareConfig() throws } // Test connection string properties. - // Test disableStatementPooling + // Test disableStatementPooling=true String connectionStringDisableStatementPooling = connectionString + ";disableStatementPooling=true;"; try (SQLServerConnection connectionDisableStatementPooling = (SQLServerConnection) DriverManager .getConnection(connectionStringDisableStatementPooling)) { @@ -354,9 +333,12 @@ public void testStatementPoolingPreparedStatementExecAndUnprepareConfig() throws connectionDisableStatementPooling.setStatementPoolingCacheSize(10); assertSame(10, connectionDisableStatementPooling.getStatementPoolingCacheSize()); assertTrue(!connectionDisableStatementPooling.isStatementPoolingEnabled()); - String connectionStringEnableStatementPooling = connectionString + ";disableStatementPooling=false;"; - SQLServerConnection connectionEnableStatementPooling = (SQLServerConnection) DriverManager - .getConnection(connectionStringEnableStatementPooling); + } + + // Test disableStatementPooling=false + String connectionStringEnableStatementPooling = connectionString + ";disableStatementPooling=false;"; + try (SQLServerConnection connectionEnableStatementPooling = (SQLServerConnection) DriverManager + .getConnection(connectionStringEnableStatementPooling)) { connectionEnableStatementPooling.setStatementPoolingCacheSize(10); // to turn on caching. // for now, it won't affect if disable is false or true. Since statementPoolingCacheSize is set to 0 as @@ -445,22 +427,26 @@ public void testStatementPoolingPreparedStatementExecAndUnprepareConfig() throws assertSame(3, connectionThresholdAndNoExecuteSQL.getServerPreparedStatementDiscardThreshold()); } + String invalidValue = "hello"; // Test that an error is thrown for invalid connection string property values (non int/bool). - String connectionStringThresholdError = connectionString + ";ServerPreparedStatementDiscardThreshold=hej;"; + String connectionStringThresholdError = connectionString + ";ServerPreparedStatementDiscardThreshold=" + + invalidValue; try (SQLServerConnection con = (SQLServerConnection) DriverManager .getConnection(connectionStringThresholdError)) { fail("Error for invalid ServerPreparedStatementDiscardThresholdexpected."); } catch (SQLException e) { - // Good! + assert (e.getMessage().equalsIgnoreCase(String.format( + TestResource.getResource("R_invalidserverPreparedStatementDiscardThreshold"), invalidValue))); } - String connectionStringNoExecuteSQLError = connectionString - + ";enablePrepareOnFirstPreparedStatementCall=dobidoo;"; + String connectionStringNoExecuteSQLError = connectionString + ";enablePrepareOnFirstPreparedStatementCall=" + + invalidValue; try (SQLServerConnection con = (SQLServerConnection) DriverManager .getConnection(connectionStringNoExecuteSQLError)) { fail("Error for invalid enablePrepareOnFirstPreparedStatementCall expected."); } catch (SQLException e) { - // Good! + assert (e.getMessage() + .equalsIgnoreCase(TestResource.getResource("R_invalidenablePrepareOnFirstPreparedStatementCall"))); } // Verify instance setting is followed. @@ -469,7 +455,7 @@ public void testStatementPoolingPreparedStatementExecAndUnprepareConfig() throws // Turn off use of prepared statement cache. con.setStatementPoolingCacheSize(0); - String query = "/*unprepSettingsTest*/SELECT * FROM sys.objects;"; + String query = "/*unprepSettingsTest*/SELECT 1;"; // Verify initial default is not serial: assertTrue(1 < con.getServerPreparedStatementDiscardThreshold()); @@ -506,8 +492,7 @@ private void testStatementPoolingInternal(String mode) throws Exception { if (mode.equalsIgnoreCase("bulkcopy")) { modifyConnectionForBulkCopyAPI(con); } - String query = String.format("/*statementpoolingtest_re-use_%s*/SELECT TOP(1) * FROM sys.tables;", - UUID.randomUUID().toString()); + String query = String.format("/*statementpoolingtest_re-use_%s*/SELECT 1;", UUID.randomUUID().toString()); con.setStatementPoolingCacheSize(10); @@ -524,7 +509,6 @@ private void testStatementPoolingInternal(String mode) throws Exception { queries[i] = String.format("%s--%s--%s--%s", query, i, queryCount, prepOnFirstCall); } - int testsWithHandleReuse = 0; final int testCount = 500; for (int i = 0; i < testCount; ++i) { Random random = new Random(); @@ -532,11 +516,6 @@ private void testStatementPoolingInternal(String mode) throws Exception { try (SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) con .prepareStatement(queries[queryNumber])) { pstmt.execute(); - - // Grab handle-reuse before it would be populated if initially created. - if (0 < pstmt.getPreparedStatementHandle()) - testsWithHandleReuse++; - pstmt.getMoreResults(); // Make sure handle is updated. } } @@ -548,7 +527,7 @@ private void testStatementPoolingInternal(String mode) throws Exception { if (mode.equalsIgnoreCase("bulkcopy")) { modifyConnectionForBulkCopyAPI(con); } - // Test behvaior with statement pooling. + // Test behavior with statement pooling. con.setStatementPoolingCacheSize(10); this.executeSQL(con, "IF NOT EXISTS (SELECT * FROM sys.messages WHERE message_id = 99586) EXEC sp_addmessage 99586, 16, 'Prepared handle GAH!';"); @@ -576,7 +555,7 @@ private void testStatementPoolingInternal(String mode) throws Exception { // test updated value, should be 1 + 100 = 101 // although executeUpdate() throws exception, update operation should be executed successfully. - try (ResultSet rs = con.createStatement() + try (Statement stmt = con.createStatement(); ResultSet rs = stmt .executeQuery("select * from " + AbstractSQLGenerator.escapeIdentifier(tableName) + "")) { rs.next(); assertSame(101, rs.getInt(1)); @@ -613,24 +592,18 @@ private void testStatementPoolingInternal(String mode) throws Exception { // test updated value, should be 1 + 100 = 101 // although executeBatch() throws exception, update operation should be executed successfully. - try (ResultSet rs = con.createStatement() + try (Statement stmt = con.createStatement(); ResultSet rs = stmt .executeQuery("select * from " + AbstractSQLGenerator.escapeIdentifier(tableName2) + "")) { rs.next(); assertSame(101, rs.getInt(1)); } } - } - try (SQLServerConnection con = (SQLServerConnection) DriverManager.getConnection(connectionString)) { - if (mode.equalsIgnoreCase("bulkcopy")) { - modifyConnectionForBulkCopyAPI(con); - } - // Test behvaior with statement pooling. + // Test behavior with statement pooling enabled con.setDisableStatementPooling(false); - con.setStatementPoolingCacheSize(10); String lookupUniqueifier = UUID.randomUUID().toString(); - String query = String.format("/*statementpoolingtest_%s*/SELECT * FROM sys.tables;", lookupUniqueifier); + String query = String.format("/*statementpoolingtest_%s*/SELECT 1;", lookupUniqueifier); // Execute statement first, should create cache entry WITHOUT handle (since sp_executesql was used). try (SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) con.prepareStatement(query)) { @@ -671,10 +644,10 @@ private void testStatementPoolingInternal(String mode) throws Exception { assertNotSame(handle, pstmt.getPreparedStatementHandle()); } try { - System.out.println(outer.getPreparedStatementHandle()); + outer.getPreparedStatementHandle(); fail(TestResource.getResource("R_invalidGetPreparedStatementHandle")); } catch (Exception e) { - // Good! + assert (e.getMessage().equalsIgnoreCase(TestResource.getResource("R_statementClosed"))); } finally { if (null != outer) { outer.close(); diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java index 2603cd8116..240ce60388 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/unit/statement/StatementTest.java @@ -29,7 +29,6 @@ import java.util.UUID; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; @@ -686,7 +685,6 @@ class Hammer { int numCancelSuccesses = 0; int numCancelExceptions = 0; int numCancellations = 0; - int numExecuteTries = 0; int numExecuteSuccesses = 0; int numExecuteExceptions = 0; int numCloseExceptions = 0; @@ -717,8 +715,6 @@ void start(final Connection con) { final Runnable runner = new Runnable() { public void run() { - ++numExecuteTries; - try (ResultSet rs = stmt.executeQuery( "SELECT * FROM " + AbstractSQLGenerator.escapeIdentifier(tableName))) { @@ -751,11 +747,10 @@ public void run() { } }; - final ScheduledFuture runnerHandle = executionScheduler.scheduleAtFixedRate(runner, startDelay, - 1, TimeUnit.MILLISECONDS); + executionScheduler.scheduleAtFixedRate(runner, startDelay, 1, TimeUnit.MILLISECONDS); - final ScheduledFuture cancelHandle = cancelScheduler.scheduleAtFixedRate(canceller, - cancelInterval, cancelInterval, TimeUnit.MILLISECONDS); + cancelScheduler.scheduleAtFixedRate(canceller, cancelInterval, cancelInterval, + TimeUnit.MILLISECONDS); } void stop() { diff --git a/src/test/java/com/microsoft/sqlserver/testframework/DBConnection.java b/src/test/java/com/microsoft/sqlserver/testframework/DBConnection.java index e3631238f4..7764029628 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/DBConnection.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/DBConnection.java @@ -7,6 +7,7 @@ import static org.junit.jupiter.api.Assertions.fail; +import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.ResultSet; import java.sql.SQLException; @@ -35,6 +36,12 @@ public DBConnection(String connectionString) { super(null, null, "connection"); getConnection(connectionString); } + + public DBConnection(Connection connection) { + super(null, null, "connection"); + this.connection = (SQLServerConnection) connection; + setInternal(connection); + } /** * establish connection From d639870444d74ca117c0a96625f6b9b35751a69a Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Fri, 4 Jan 2019 14:58:44 -0800 Subject: [PATCH 50/51] Workaround Bulk Copy for batch insert operation against specific datatypes. (#912) Fix | Workaround Bulk Copy for batch insert operation against specific datatypes. (#912) --- .../jdbc/SQLServerBulkBatchInsertRecord.java | 80 ++--- .../jdbc/SQLServerPreparedStatement.java | 52 +-- .../sqlserver/jdbc/SQLServerResource.java | 1 + .../microsoft/sqlserver/jdbc/RandomData.java | 28 +- .../BatchExecutionWithBulkCopyTest.java | 307 +++++++++++------- 5 files changed, 265 insertions(+), 203 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java index 3203f07f10..3c662b267e 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkBatchInsertRecord.java @@ -160,12 +160,20 @@ private Object convertValue(ColumnMetadata cm, Object data) throws SQLServerExce case Types.VARBINARY: case Types.LONGVARBINARY: case Types.BLOB: { - // Strip off 0x if present. - String binData = data.toString().trim(); - if (binData.startsWith("0x") || binData.startsWith("0X")) { - return binData.substring(2); + if (data instanceof byte[]) { + /* + * if the binary data comes in as a byte array through setBytes through Bulk Copy for Batch Insert + * API, don't turn the binary array into a string. + */ + return data; } else { - return binData; + // Strip off 0x if present. + String binData = data.toString().trim(); + if (binData.startsWith("0x") || binData.startsWith("0X")) { + return binData.substring(2); + } else { + return binData; + } } } @@ -229,8 +237,9 @@ public Object[] getRowData() throws SQLServerException { Object rowData; int columnListIndex = 0; - // check if the size of the list of values = size of the list of columns - // (which is optional) + /* + * check if the size of the list of values = size of the list of columns (which is optional) + */ if (null != columnList && columnList.size() != valueList.size()) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_DataSchemaMismatch")); Object[] msgArgs = {}; @@ -240,50 +249,43 @@ public Object[] getRowData() throws SQLServerException { for (Entry pair : columnMetadata.entrySet()) { int index = pair.getKey() - 1; - // To explain what each variable represents: - // columnMetadata = map containing the ENTIRE list of columns in the - // table. - // columnList = the *optional* list of columns the user can provide. - // For example, the (c1, c3) part of this query: INSERT into t1 (c1, - // c3) values (?, ?) - // valueList = the *mandatory* list of columns the user needs - // provide. This is the (?, ?) part of the previous query. The size - // of this valueList will always equal the number of - // the entire columns in the table IF columnList has NOT been - // provided. If columnList HAS been provided, then this valueList - // may be smaller than the list of all columns (which is - // columnMetadata). - - // case when the user has not provided the optional list of column - // names. + /* + * To explain what each variable represents: columnMetadata = map containing the ENTIRE list of columns in + * the table. columnList = the *optional* list of columns the user can provide. For example, the (c1, c3) + * part of this query: INSERT into t1 (c1, c3) values (?, ?) valueList = the *mandatory* list of columns the + * user needs provide. This is the (?, ?) part of the previous query. The size of this valueList will always + * equal the number of the entire columns in the table IF columnList has NOT been provided. If columnList + * HAS been provided, then this valueList may be smaller than the list of all columns (which is + * columnMetadata). + */ + // case when the user has not provided the optional list of column names. if (null == columnList || columnList.size() == 0) { valueData = valueList.get(index); - // if the user has provided a wildcard for this column, fetch - // the set value from the batchParam. + /* + * if the user has provided a wildcard for this column, fetch the set value from the batchParam. + */ if (valueData.equalsIgnoreCase("?")) { rowData = batchParam.get(batchParamIndex)[valueIndex++].getSetterValue(); } else if (valueData.equalsIgnoreCase("null")) { rowData = null; } - // if the user has provided a hardcoded value for this column, - // rowData is simply set to the hardcoded value. + /* + * if the user has provided a hardcoded value for this column, rowData is simply set to the hardcoded + * value. + */ else { rowData = removeSingleQuote(valueData); } } - // case when the user has provided the optional list of column - // names. + // case when the user has provided the optional list of column names. else { - // columnListIndex is a separate counter we need to keep track - // of for each time we've processed a column - // that the user provided. - // for example, if the user provided an optional columnList of - // (c1, c3, c5, c7) in a table that has 8 columns (c1~c8), - // then the columnListIndex would increment only when we're - // dealing with the four columns inside columnMetadata. - // compare the list of the optional list of column names to the - // table's metadata, and match each other, so we assign the - // correct value to each column. + /* + * columnListIndex is a separate counter we need to keep track of for each time we've processed a column + * that the user provided. for example, if the user provided an optional columnList of (c1, c3, c5, c7) + * in a table that has 8 columns (c1~c8), then the columnListIndex would increment only when we're + * dealing with the four columns inside columnMetadata. compare the list of the optional list of column + * names to the table's metadata, and match each other, so we assign the correct value to each column. + */ if (columnList.size() > columnListIndex && columnList.get(columnListIndex).equalsIgnoreCase(columnMetadata.get(index + 1).columnName)) { valueData = valueList.get(columnListIndex); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java index fea6c22e36..acce3058bc 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerPreparedStatement.java @@ -1936,6 +1936,12 @@ public int[] executeBatch() throws SQLServerException, BatchUpdateException, SQL try { if (this.useBulkCopyForBatchInsert && connection.isAzureDW() && isInsert(localUserSQL)) { + if (null == batchParamValues) { + updateCounts = new int[0]; + loggerExternal.exiting(getClassNameLogging(), "executeBatch", updateCounts); + return updateCounts; + } + // From the JDBC spec, section 9.1.4 - Making Batch Updates: // The CallableStatement.executeBatch method (inherited from PreparedStatement) will // throw a BatchUpdateException if the stored procedure returns anything other than an @@ -1955,12 +1961,6 @@ public int[] executeBatch() throws SQLServerException, BatchUpdateException, SQL } } - if (batchParamValues == null) { - updateCounts = new int[0]; - loggerExternal.exiting(getClassNameLogging(), "executeBatch", updateCounts); - return updateCounts; - } - String tableName = parseUserSQLForTableNameDW(false, false, false, false); ArrayList columnList = parseUserSQLForColumnListDW(); ArrayList valueList = parseUserSQLForValueListDW(false); @@ -2032,7 +2032,7 @@ public int[] executeBatch() throws SQLServerException, BatchUpdateException, SQL } } - if (batchParamValues == null) + if (null == batchParamValues) updateCounts = new int[0]; else try { @@ -2093,6 +2093,12 @@ public long[] executeLargeBatch() throws SQLServerException, BatchUpdateExceptio try { if (this.useBulkCopyForBatchInsert && connection.isAzureDW() && isInsert(localUserSQL)) { + if (null == batchParamValues) { + updateCounts = new long[0]; + loggerExternal.exiting(getClassNameLogging(), "executeLargeBatch", updateCounts); + return updateCounts; + } + // From the JDBC spec, section 9.1.4 - Making Batch Updates: // The CallableStatement.executeBatch method (inherited from PreparedStatement) will // throw a BatchUpdateException if the stored procedure returns anything other than an @@ -2112,12 +2118,6 @@ public long[] executeLargeBatch() throws SQLServerException, BatchUpdateExceptio } } - if (batchParamValues == null) { - updateCounts = new long[0]; - loggerExternal.exiting(getClassNameLogging(), "executeLargeBatch", updateCounts); - return updateCounts; - } - String tableName = parseUserSQLForTableNameDW(false, false, false, false); ArrayList columnList = parseUserSQLForColumnListDW(); ArrayList valueList = parseUserSQLForValueListDW(false); @@ -2189,7 +2189,7 @@ public long[] executeLargeBatch() throws SQLServerException, BatchUpdateExceptio } } - if (batchParamValues == null) + if (null == batchParamValues) updateCounts = new long[0]; else try { @@ -2234,8 +2234,19 @@ public long[] executeLargeBatch() throws SQLServerException, BatchUpdateExceptio private void checkValidColumns(TypeInfo ti) throws SQLServerException { int jdbctype = ti.getSSType().getJDBCType().getIntValue(); - // currently, we do not support: geometry, geography, datetime and smalldatetime + String typeName; + MessageFormat form; switch (jdbctype) { + case microsoft.sql.Types.MONEY: + case microsoft.sql.Types.SMALLMONEY: + case java.sql.Types.DATE: + case microsoft.sql.Types.DATETIME: + case microsoft.sql.Types.DATETIMEOFFSET: + case microsoft.sql.Types.SMALLDATETIME: + case java.sql.Types.TIME: + typeName = ti.getSSTypeName(); + form = new MessageFormat(SQLServerException.getErrString("R_BulkTypeNotSupportedDW")); + throw new IllegalArgumentException(form.format(new Object[] {typeName})); case java.sql.Types.INTEGER: case java.sql.Types.SMALLINT: case java.sql.Types.BIGINT: @@ -2243,8 +2254,6 @@ private void checkValidColumns(TypeInfo ti) throws SQLServerException { case java.sql.Types.TINYINT: case java.sql.Types.DOUBLE: case java.sql.Types.REAL: - case microsoft.sql.Types.MONEY: - case microsoft.sql.Types.SMALLMONEY: case java.sql.Types.DECIMAL: case java.sql.Types.NUMERIC: case microsoft.sql.Types.GUID: @@ -2258,21 +2267,18 @@ private void checkValidColumns(TypeInfo ti) throws SQLServerException { case java.sql.Types.LONGVARBINARY: case java.sql.Types.VARBINARY: // Spatial datatypes fall under Varbinary, check if the UDT is geometry/geography. - String typeName = ti.getSSTypeName(); + typeName = ti.getSSTypeName(); if (typeName.equalsIgnoreCase("geometry") || typeName.equalsIgnoreCase("geography")) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_BulkTypeNotSupported")); + form = new MessageFormat(SQLServerException.getErrString("R_BulkTypeNotSupported")); throw new IllegalArgumentException(form.format(new Object[] {typeName})); } case java.sql.Types.TIMESTAMP: - case java.sql.Types.DATE: - case java.sql.Types.TIME: case 2013: // java.sql.Types.TIME_WITH_TIMEZONE case 2014: // java.sql.Types.TIMESTAMP_WITH_TIMEZONE - case microsoft.sql.Types.DATETIMEOFFSET: case microsoft.sql.Types.SQL_VARIANT: return; default: { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_BulkTypeNotSupported")); + form = new MessageFormat(SQLServerException.getErrString("R_BulkTypeNotSupported")); String unsupportedDataType = JDBCType.of(jdbctype).toString(); throw new IllegalArgumentException(form.format(new Object[] {unsupportedDataType})); } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java index 266f8131ab..1deca52cc8 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResource.java @@ -279,6 +279,7 @@ protected Object[][] getContents() { {"R_unableRetrieveSourceData", "Unable to retrieve data from the source."}, {"R_ParsingError", "Failed to parse data for the {0} type."}, {"R_BulkTypeNotSupported", "Data type {0} is not supported in bulk copy."}, + {"R_BulkTypeNotSupportedDW", "Data type {0} is not supported in bulk copy against Azure Data Warehouse."}, {"R_invalidTransactionOption", "UseInternalTransaction option cannot be set to TRUE when used with a Connection object."}, {"R_invalidNegativeArg", "The {0} argument cannot be negative."}, diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/RandomData.java b/src/test/java/com/microsoft/sqlserver/jdbc/RandomData.java index 2e2b2e8a0c..f091f8f005 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/RandomData.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/RandomData.java @@ -25,8 +25,6 @@ public class RandomData { private static Random r = new Random(); public static boolean returnNull = (0 == r.nextInt(5)); // 20% chance of return null - public static boolean returnFullLength = (0 == r.nextInt(2)); // 50% chance of return full length for char/nchar and - // binary types public static boolean returnMinMax = (0 == r.nextInt(5)); // 20% chance of return Min/Max value public static boolean returnZero = (0 == r.nextInt(10)); // 10% chance of return zero @@ -357,8 +355,8 @@ public static byte[] generateBinaryTypes(String columnLength, boolean nullable, minimumLength = 1; } - int length; if (columnLength.toLowerCase().equals("max")) { + int length; // 50% chance of return value longer than 8000/4000 if (r.nextBoolean()) { length = r.nextInt(100000) + maxBound; @@ -373,17 +371,9 @@ public static byte[] generateBinaryTypes(String columnLength, boolean nullable, } } else { int columnLengthInt = Integer.parseInt(columnLength); - if (returnFullLength) { - length = columnLengthInt; - byte[] bytes = new byte[length]; - r.nextBytes(bytes); - return bytes; - } else { - length = r.nextInt(columnLengthInt - minimumLength) + minimumLength; - byte[] bytes = new byte[length]; - r.nextBytes(bytes); - return bytes; - } + byte[] bytes = new byte[columnLengthInt]; + r.nextBytes(bytes); + return bytes; } } @@ -699,8 +689,8 @@ private static String buildCharOrNChar(String columnLength, boolean nullable, bo minimumLength = 1; } - int length; if (columnLength.toLowerCase().equals("max")) { + int length; // 50% chance of return value longer than 8000/4000 if (r.nextBoolean()) { length = r.nextInt(100000) + maxBound; @@ -711,13 +701,7 @@ private static String buildCharOrNChar(String columnLength, boolean nullable, bo } } else { int columnLengthInt = Integer.parseInt(columnLength); - if (returnFullLength) { - length = columnLengthInt; - return buildRandomString(length, charSet); - } else { - length = r.nextInt(columnLengthInt - minimumLength) + minimumLength; - return buildRandomString(length, charSet); - } + return buildRandomString(columnLengthInt, charSet); } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java index e3fbaa8b0c..c684dab001 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/preparedStatement/BatchExecutionWithBulkCopyTest.java @@ -7,6 +7,8 @@ import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.math.BigDecimal; +import java.math.RoundingMode; import java.sql.BatchUpdateException; import java.sql.Connection; import java.sql.Date; @@ -15,8 +17,13 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; +import java.sql.Time; import java.sql.Timestamp; +import java.time.LocalDateTime; +import java.time.ZoneId; import java.util.ArrayList; +import java.util.Arrays; +import java.util.concurrent.ThreadLocalRandom; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeEach; @@ -28,6 +35,7 @@ import com.microsoft.sqlserver.jdbc.Geography; import com.microsoft.sqlserver.jdbc.Geometry; +import com.microsoft.sqlserver.jdbc.RandomData; import com.microsoft.sqlserver.jdbc.RandomUtil; import com.microsoft.sqlserver.jdbc.SQLServerConnection; import com.microsoft.sqlserver.jdbc.SQLServerPreparedStatement; @@ -37,6 +45,8 @@ import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; import com.microsoft.sqlserver.testframework.AbstractTest; +import microsoft.sql.DateTimeOffset; + @RunWith(JUnitPlatform.class) @Tag("AzureDWTest") @@ -49,15 +59,68 @@ public class BatchExecutionWithBulkCopyTest extends AbstractTest { static String doubleQuoteTableName = RandomUtil.getIdentifier("\"BulkCopy\"\"\"\"test\""); static String schemaTableName = "\"dbo\" . /*some comment */ " + squareBracketTableName; + private Object[] generateExpectedValues() { + float randomFloat = RandomData.generateReal(false); + int ramdonNum = RandomData.generateInt(false); + short randomShort = RandomData.generateTinyint(false); + String randomString = RandomData.generateCharTypes("6", false, false); + String randomChar = RandomData.generateCharTypes("1", false, false); + byte[] randomBinary = RandomData.generateBinaryTypes("5", false, false); + BigDecimal randomBigDecimal = new BigDecimal(ramdonNum); + BigDecimal randomMoney = RandomData.generateMoney(false); + BigDecimal randomSmallMoney = RandomData.generateSmallMoney(false); + + // Temporal datatypes + Date randomDate = Date.valueOf(LocalDateTime.now().toLocalDate()); + Time randomTime = new Time(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); + Timestamp randomTimestamp = new Timestamp( + LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); + + // Datetime can only end in 0,3,7 and will be rounded to those numbers on the server. Manually set nanos + Timestamp roundedDatetime = randomTimestamp; + roundedDatetime.setNanos(0); + // Smalldatetime does not have seconds. Manually set nanos and seconds. + Timestamp smallTimestamp = randomTimestamp; + smallTimestamp.setNanos(0); + smallTimestamp.setSeconds(0); + + Object[] expected = new Object[23]; + expected[0] = ThreadLocalRandom.current().nextLong(); + expected[1] = randomBinary; + expected[2] = true; + expected[3] = randomChar; + expected[4] = randomDate; + expected[5] = roundedDatetime; + expected[6] = randomTimestamp; + expected[7] = microsoft.sql.DateTimeOffset.valueOf(randomTimestamp, 0); + expected[8] = randomBigDecimal.setScale(0, RoundingMode.HALF_UP); + expected[9] = (double) ramdonNum; + expected[10] = ramdonNum; + expected[11] = randomMoney; + expected[12] = randomChar; + expected[13] = BigDecimal.valueOf(ThreadLocalRandom.current().nextInt()); + expected[14] = randomString; + expected[15] = randomFloat; + expected[16] = smallTimestamp; + expected[17] = randomShort; + expected[18] = randomSmallMoney; + expected[19] = randomTime; + expected[20] = randomShort; + expected[21] = randomBinary; + expected[22] = randomString; + + return expected; + } + @Test public void testIsInsert() throws Exception { try (Connection connection = DriverManager.getConnection(connectionString + ";useBulkCopyForBatchInsert=true;"); Statement stmt = (SQLServerStatement) connection.createStatement()) { - String valid1 = "INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableNameBulk) + " values (1, 2)"; - String valid2 = " INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableNameBulk) + " values (1, 2)"; - String valid3 = "/* asdf */ INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableNameBulk) + String valid1 = "insert into " + AbstractSQLGenerator.escapeIdentifier(tableNameBulk) + " values (1, 2)"; + String valid2 = " insert into " + AbstractSQLGenerator.escapeIdentifier(tableNameBulk) + " values (1, 2)"; + String valid3 = "/* asdf */ insert into " + AbstractSQLGenerator.escapeIdentifier(tableNameBulk) + " values (1, 2)"; - String invalid = "Select * from " + AbstractSQLGenerator.escapeIdentifier(tableNameBulk); + String invalid = "select * from " + AbstractSQLGenerator.escapeIdentifier(tableNameBulk); Method method = stmt.getClass().getDeclaredMethod("isInsert", String.class); method.setAccessible(true); @@ -164,9 +227,9 @@ public void testAll() throws Exception { @Test public void testAllcolumns() throws Exception { - assumeFalse(isSqlAzureDW(), TestResource.getResource("R_issueAzureDW")); - String valid = "INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableName) + " values " + "(" + "?, " - + "?, " + "?, " + "?, " + "?, " + "?, " + "?, " + "?, " + "? " + ")"; + String valid = "insert into " + AbstractSQLGenerator.escapeIdentifier(tableName) + " values " + "(" + "?, " + + "?, " + "?, " + "?, " + "?, " + "?, " + "?, " + "?, " + "?, " + "?, " + "?, " + "?, " + "?, " + "?, " + + "?, " + "?, " + "?, " + "?, " + "?, " + "?, " + "?, " + "?, " + "?" + ")"; try (Connection connection = DriverManager.getConnection(connectionString + ";useBulkCopyForBatchInsert=true;"); SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) connection.prepareStatement(valid); @@ -175,41 +238,44 @@ public void testAllcolumns() throws Exception { f1.setAccessible(true); f1.set(connection, true); - Timestamp myTimestamp = new Timestamp(114550L); - - Date d = new Date(114550L); + Object[] expected = generateExpectedValues(); + + pstmt.setLong(1, (long) expected[0]); // bigint + pstmt.setBytes(2, (byte[]) expected[1]); // binary(5) + pstmt.setBoolean(3, (boolean) expected[2]); // bit + pstmt.setString(4, (String) expected[3]); // char + pstmt.setDate(5, (Date) expected[4]); // date + pstmt.setDateTime(6, (Timestamp) expected[5]);// datetime + pstmt.setDateTime(7, (Timestamp) expected[6]); // datetime2 + pstmt.setDateTimeOffset(8, (DateTimeOffset) expected[7]); // datetimeoffset + pstmt.setBigDecimal(9, (BigDecimal) expected[8]); // decimal + pstmt.setDouble(10, (double) expected[9]); // float + pstmt.setInt(11, (int) expected[10]); // int + pstmt.setMoney(12, (BigDecimal) expected[11]); // money + pstmt.setString(13, (String) expected[12]); // nchar + pstmt.setBigDecimal(14, (BigDecimal) expected[13]); // numeric + pstmt.setString(15, (String) expected[14]); // nvarchar(20) + pstmt.setFloat(16, (float) expected[15]); // real + pstmt.setSmallDateTime(17, (Timestamp) expected[16]); // smalldatetime + pstmt.setShort(18, (short) expected[17]); // smallint + pstmt.setSmallMoney(19, (BigDecimal) expected[18]); // smallmoney + pstmt.setTime(20, (Time) expected[19]); // time + pstmt.setShort(21, (short) expected[20]); // tinyint + pstmt.setBytes(22, (byte[]) expected[21]); // varbinary(5) + pstmt.setString(23, (String) expected[22]); // varchar(20) - pstmt.setInt(1, 1234); - pstmt.setBoolean(2, false); - pstmt.setString(3, "a"); - pstmt.setDate(4, d); - pstmt.setDateTime(5, myTimestamp); - pstmt.setFloat(6, (float) 123.45); - pstmt.setString(7, "b"); - pstmt.setString(8, "varc"); - pstmt.setString(9, "''"); pstmt.addBatch(); - pstmt.executeBatch(); try (ResultSet rs = stmt - .executeQuery("SELECT * FROM " + AbstractSQLGenerator.escapeIdentifier(tableName))) { - - Object[] expected = new Object[9]; - - expected[0] = 1234; - expected[1] = false; - expected[2] = "a"; - expected[3] = d; - expected[4] = myTimestamp; - expected[5] = 123.45; - expected[6] = "b"; - expected[7] = "varc"; - expected[8] = "''"; - + .executeQuery("select * from " + AbstractSQLGenerator.escapeIdentifier(tableName))) { rs.next(); for (int i = 0; i < expected.length; i++) { - assertEquals(expected[i].toString(), rs.getObject(i + 1).toString()); + if (rs.getObject(i + 1) instanceof byte[]) { + assertTrue(Arrays.equals((byte[]) expected[i], (byte[]) rs.getObject(i + 1))); + } else { + assertEquals(expected[i].toString(), rs.getObject(i + 1).toString()); + } } } } @@ -217,8 +283,7 @@ public void testAllcolumns() throws Exception { @Test public void testMixColumns() throws Exception { - assumeFalse(isSqlAzureDW(), TestResource.getResource("R_issueAzureDW")); - String valid = "INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableName) + " (c1, c3, c5, c8) values " + String valid = "insert into " + AbstractSQLGenerator.escapeIdentifier(tableName) + " (c1, c3, c5, c8) values " + "(" + "?, " + "?, " + "?, " + "? " + ")"; try (Connection connection = DriverManager.getConnection(connectionString + ";useBulkCopyForBatchInsert=true;"); @@ -228,34 +293,29 @@ public void testMixColumns() throws Exception { f1.setAccessible(true); f1.set(connection, true); - Timestamp myTimestamp = new Timestamp(114550L); - - Date d = new Date(114550L); - - pstmt.setInt(1, 1234); - pstmt.setString(2, "a"); - pstmt.setDateTime(3, myTimestamp); - pstmt.setString(4, "varc"); + Timestamp randomTimestamp = new Timestamp(LocalDateTime.now().atZone(ZoneId.systemDefault()).toInstant().toEpochMilli()); + Date randomDate = Date.valueOf(LocalDateTime.now().toLocalDate()); + long randomLong = ThreadLocalRandom.current().nextLong(); + + pstmt.setLong(1, randomLong); // bigint + pstmt.setBoolean(2, true); // bit + pstmt.setDate(3, randomDate); // date + pstmt.setDateTimeOffset(4, microsoft.sql.DateTimeOffset.valueOf(randomTimestamp, 0)); // datetimeoffset pstmt.addBatch(); pstmt.executeBatch(); try (ResultSet rs = stmt - .executeQuery("SELECT * FROM " + AbstractSQLGenerator.escapeIdentifier(tableName))) { - - Object[] expected = new Object[9]; + .executeQuery("select c1, c3, c5, c8 from " + AbstractSQLGenerator.escapeIdentifier(tableName))) { - expected[0] = 1234; - expected[1] = false; - expected[2] = "a"; - expected[3] = d; - expected[4] = myTimestamp; - expected[5] = 123.45; - expected[6] = "b"; - expected[7] = "varc"; - expected[8] = "varcmax"; + Object[] expected = new Object[4]; + expected[0] = randomLong; + expected[1] = true; + expected[2] = randomDate; + expected[3] = microsoft.sql.DateTimeOffset.valueOf(randomTimestamp, 0); rs.next(); + for (int i = 0; i < expected.length; i++) { if (null != rs.getObject(i + 1)) { assertEquals(expected[i].toString(), rs.getObject(i + 1).toString()); @@ -267,8 +327,7 @@ public void testMixColumns() throws Exception { @Test public void testNullOrEmptyColumns() throws Exception { - assumeFalse(isSqlAzureDW(), TestResource.getResource("R_issueAzureDW")); - String valid = "INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableName) + String valid = "insert into " + AbstractSQLGenerator.escapeIdentifier(tableName) + " (c1, c2, c3, c4, c5, c6, c7) values " + "(" + "?, " + "?, " + "?, " + "?, " + "?, " + "?, " + "? " + ")"; @@ -279,29 +338,32 @@ public void testNullOrEmptyColumns() throws Exception { f1.setAccessible(true); f1.set(connection, true); - pstmt.setInt(1, 1234); - pstmt.setBoolean(2, false); - pstmt.setString(3, null); - pstmt.setDate(4, null); - pstmt.setDateTime(5, null); - pstmt.setFloat(6, (float) 123.45); - pstmt.setString(7, ""); + long randomLong = ThreadLocalRandom.current().nextLong(); + String randomChar = RandomData.generateCharTypes("1", false, false); + + pstmt.setLong(1, randomLong); // bigint + pstmt.setBytes(2, null); // binary(5) + pstmt.setBoolean(3, true); // bit + pstmt.setString(4, randomChar); // char + pstmt.setDate(5, null); // date + pstmt.setDateTime(6, null);// datetime + pstmt.setDateTime(7, null); // datetime2 pstmt.addBatch(); pstmt.executeBatch(); try (ResultSet rs = stmt - .executeQuery("SELECT * FROM " + AbstractSQLGenerator.escapeIdentifier(tableName))) { + .executeQuery("select * from " + AbstractSQLGenerator.escapeIdentifier(tableName))) { - Object[] expected = new Object[9]; + Object[] expected = new Object[7]; - expected[0] = 1234; - expected[1] = false; - expected[2] = null; - expected[3] = null; + expected[0] = randomLong; + expected[1] = null; + expected[2] = true; + expected[3] = randomChar; expected[4] = null; - expected[5] = 123.45; - expected[6] = " "; + expected[5] = null; + expected[6] = null; rs.next(); for (int i = 0; i < expected.length; i++) { @@ -335,7 +397,7 @@ public void testSquareBracketAgainstDB() throws Exception { pstmt.executeBatch(); try (ResultSet rs = stmt - .executeQuery("SELECT * FROM " + AbstractSQLGenerator.escapeIdentifier(squareBracketTableName))) { + .executeQuery("select * from " + AbstractSQLGenerator.escapeIdentifier(squareBracketTableName))) { rs.next(); assertEquals(1, rs.getObject(1)); @@ -365,7 +427,7 @@ public void testDoubleQuoteAgainstDB() throws Exception { pstmt.executeBatch(); try (ResultSet rs = stmt - .executeQuery("SELECT * FROM " + AbstractSQLGenerator.escapeIdentifier(doubleQuoteTableName))) { + .executeQuery("select * from " + AbstractSQLGenerator.escapeIdentifier(doubleQuoteTableName))) { rs.next(); assertEquals(1, rs.getObject(1)); @@ -394,7 +456,7 @@ public void testSchemaAgainstDB() throws Exception { pstmt.executeBatch(); try (ResultSet rs = stmt - .executeQuery("SELECT * FROM " + AbstractSQLGenerator.escapeIdentifier(schemaTableName))) { + .executeQuery("select * from " + AbstractSQLGenerator.escapeIdentifier(schemaTableName))) { rs.next(); assertEquals(1, rs.getObject(1)); @@ -425,7 +487,7 @@ public void testColumnNameMixAgainstDB() throws Exception { pstmt.executeBatch(); try (ResultSet rs = stmt - .executeQuery("SELECT * FROM " + AbstractSQLGenerator.escapeIdentifier(squareBracketTableName))) { + .executeQuery("select * from " + AbstractSQLGenerator.escapeIdentifier(squareBracketTableName))) { rs.next(); assertEquals(1, rs.getObject(1)); @@ -435,9 +497,9 @@ public void testColumnNameMixAgainstDB() throws Exception { @Test public void testAllColumnsLargeBatch() throws Exception { - assumeFalse(isSqlAzureDW(), TestResource.getResource("R_issueAzureDW")); - String valid = "INSERT INTO " + AbstractSQLGenerator.escapeIdentifier(tableName) + " values " + "(" + "?, " - + "?, " + "?, " + "?, " + "?, " + "?, " + "?, " + "?, " + "? " + ")"; + String valid = "insert into " + AbstractSQLGenerator.escapeIdentifier(tableName) + " values " + "(" + "?, " + + "?, " + "?, " + "?, " + "?, " + "?, " + "?, " + "?, " + "?, " + "?, " + "?, " + "?, " + "?, " + "?, " + + "?, " + "?, " + "?, " + "?, " + "?, " + "?, " + "?, " + "?, " + "?" + ")"; try (Connection connection = DriverManager.getConnection(connectionString + ";useBulkCopyForBatchInsert=true;"); SQLServerPreparedStatement pstmt = (SQLServerPreparedStatement) connection.prepareStatement(valid); @@ -446,41 +508,44 @@ public void testAllColumnsLargeBatch() throws Exception { f1.setAccessible(true); f1.set(connection, true); - Timestamp myTimestamp = new Timestamp(114550L); - - Date d = new Date(114550L); + Object[] expected = generateExpectedValues(); + + pstmt.setLong(1, (long) expected[0]); // bigint + pstmt.setBytes(2, (byte[]) expected[1]); // binary(5) + pstmt.setBoolean(3, (boolean) expected[2]); // bit + pstmt.setString(4, (String) expected[3]); // char + pstmt.setDate(5, (Date) expected[4]); // date + pstmt.setDateTime(6, (Timestamp) expected[5]);// datetime + pstmt.setDateTime(7, (Timestamp) expected[6]); // datetime2 + pstmt.setDateTimeOffset(8, (DateTimeOffset) expected[7]); // datetimeoffset + pstmt.setBigDecimal(9, (BigDecimal) expected[8]); // decimal + pstmt.setDouble(10, (double) expected[9]); // float + pstmt.setInt(11, (int) expected[10]); // int + pstmt.setMoney(12, (BigDecimal) expected[11]); // money + pstmt.setString(13, (String) expected[12]); // nchar + pstmt.setBigDecimal(14, (BigDecimal) expected[13]); // numeric + pstmt.setString(15, (String) expected[14]); // nvarchar(20) + pstmt.setFloat(16, (float) expected[15]); // real + pstmt.setSmallDateTime(17, (Timestamp) expected[16]); // smalldatetime + pstmt.setShort(18, (short) expected[17]); // smallint + pstmt.setSmallMoney(19, (BigDecimal) expected[18]); // smallmoney + pstmt.setTime(20, (Time) expected[19]); // time + pstmt.setShort(21, (short) expected[20]); // tinyint + pstmt.setBytes(22, (byte[]) expected[21]); // varbinary(5) + pstmt.setString(23, (String) expected[22]); // varchar(20) - pstmt.setInt(1, 1234); - pstmt.setBoolean(2, false); - pstmt.setString(3, "a"); - pstmt.setDate(4, d); - pstmt.setDateTime(5, myTimestamp); - pstmt.setFloat(6, (float) 123.45); - pstmt.setString(7, "b"); - pstmt.setString(8, "varc"); - pstmt.setString(9, "''"); pstmt.addBatch(); - pstmt.executeLargeBatch(); try (ResultSet rs = stmt - .executeQuery("SELECT * FROM " + AbstractSQLGenerator.escapeIdentifier(tableName))) { - - Object[] expected = new Object[9]; - - expected[0] = 1234; - expected[1] = false; - expected[2] = "a"; - expected[3] = d; - expected[4] = myTimestamp; - expected[5] = 123.45; - expected[6] = "b"; - expected[7] = "varc"; - expected[8] = "''"; - + .executeQuery("select * from " + AbstractSQLGenerator.escapeIdentifier(tableName))) { rs.next(); for (int i = 0; i < expected.length; i++) { - assertEquals(expected[i].toString(), rs.getObject(i + 1).toString()); + if (rs.getObject(i + 1) instanceof byte[]) { + assertTrue(Arrays.equals((byte[]) expected[i], (byte[]) rs.getObject(i + 1))); + } else { + assertEquals(expected[i].toString(), rs.getObject(i + 1).toString()); + } } } } @@ -538,7 +603,6 @@ public void testIllegalNumberOfArgNoColumnList() throws Exception { @Test public void testNonParameterizedQuery() throws Exception { - assumeFalse(isSqlAzureDW(), TestResource.getResource("R_issueAzureDW")); String invalid = "insert into " + AbstractSQLGenerator.escapeIdentifier(tableName) + " values ((SELECT * from table where c1=?), ?,? ,?) "; @@ -619,7 +683,7 @@ public void testNonSupportedColumns() throws Exception { pstmt.executeBatch(); try (ResultSet rs = stmt - .executeQuery("SELECT * FROM " + AbstractSQLGenerator.escapeIdentifier(unsupportedTableName))) { + .executeQuery("select * from " + AbstractSQLGenerator.escapeIdentifier(unsupportedTableName))) { rs.next(); assertEquals(g1.toString(), Geometry.STGeomFromWKB((byte[]) rs.getObject(1)).toString()); assertEquals(g2.toString(), Geography.STGeomFromWKB((byte[]) rs.getObject(2)).toString()); @@ -631,14 +695,19 @@ public void testNonSupportedColumns() throws Exception { @BeforeEach public void testSetup() throws TestAbortedException, Exception { - try (Connection connection = DriverManager.getConnection(connectionString + ";useBulkCopyForBatchInsert=true;"); - Statement stmt = (SQLServerStatement) connection.createStatement()) { - TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(tableName), stmt); - String sql1 = "create table " + AbstractSQLGenerator.escapeIdentifier(tableName) + " " + "(" - + "c1 int DEFAULT 1234, " + "c2 bit, " + "c3 char DEFAULT NULL, " + "c4 date, " + "c5 datetime2, " - + "c6 float, " + "c7 nchar, " + "c8 varchar(20), " + "c9 varchar(8000)" + ")"; - - stmt.execute(sql1); + try (Connection connection = DriverManager + .getConnection(connectionString + ";useBulkCopyForBatchInsert=true;")) { + try (Statement stmt = (SQLServerStatement) connection.createStatement()) { + TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(tableName), stmt); + String sql1 = "create table " + AbstractSQLGenerator.escapeIdentifier(tableName) + " " + "(" + + "c1 bigint, " + "c2 binary(5), " + "c3 bit, " + "c4 char, " + "c5 date, " + "c6 datetime, " + + "c7 datetime2, " + "c8 datetimeoffset, " + "c9 decimal, " + "c10 float, " + "c11 int, " + + "c12 money, " + "c13 nchar, " + "c14 numeric, " + "c15 nvarchar(20), " + "c16 real, " + + "c17 smalldatetime, " + "c18 smallint, " + "c19 smallmoney, " + "c20 time, " + "c21 tinyint, " + + "c22 varbinary(5), " + "c23 varchar(20) " + ")"; + + stmt.execute(sql1); + } } } From 9e9fe830ac532792100c2eda3d049567d30d13a2 Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Tue, 8 Jan 2019 10:24:08 -0800 Subject: [PATCH 51/51] Release | 7.1.4 preview release changes (#922) --- CHANGELOG.md | 20 ++++++++++++++++++++ README.md | 6 +++--- build.gradle | 2 +- pom.xml | 2 +- 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 081cb8281e..031a60c5b7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,26 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) +## [7.1.4] Preview Release +### Added +- Added APIs for DataSourceFactory and OSGI Framework [#700](https://github.com/Microsoft/mssql-jdbc/pull/700) +- Added support for OffsetDateTime to be passed as 'type' in ResultSet.getObject() [#830](https://github.com/Microsoft/mssql-jdbc/pull/830) +- Added support for Active Directory MSI Authentication [#838](https://github.com/Microsoft/mssql-jdbc/pull/838) +- Added more datatype tests to JUnit test suite [#878](https://github.com/Microsoft/mssql-jdbc/pull/878) [#916](https://github.com/Microsoft/mssql-jdbc/pull/916) +- Added an option to perform JUnit testing against Azure Data Warehouse [#903](https://github.com/Microsoft/mssql-jdbc/pull/903) +- Added new APIs to retrieve SQL Server error information received with SQLServerException [#905](https://github.com/Microsoft/mssql-jdbc/pull/905) + +### Fixed Issues +- Fixed issue with java.time.OffsetDateTime value sent to the server being affected by the default timezone [#831](https://github.com/Microsoft/mssql-jdbc/pull/831) +- Fixed SSL certificate validation to respect wildcards [#836](https://github.com/Microsoft/mssql-jdbc/pull/836) +- Fixed Bulk Copy for batch insert operation to not error out against specific datatypes [#912](https://github.com/Microsoft/mssql-jdbc/pull/912) + +### Changed +- Fixed synchronization on a non-final field [#860](https://github.com/Microsoft/mssql-jdbc/pull/860) +- Removed hardcoded error messages from test file [#904](https://github.com/Microsoft/mssql-jdbc/pull/904) +- Updated Issue and Pull Request templates [#906](https://github.com/Microsoft/mssql-jdbc/pull/906) +- Updated JUnit tests by closing all resources consistently and updated Maven dependency versions to latest [#919](https://github.com/Microsoft/mssql-jdbc/pull/919) + ## [7.1.3] Preview Release ### Added - Added a new SQLServerMetaData constructor for string values of length greater than 4000 [#876](https://github.com/Microsoft/mssql-jdbc/pull/876) diff --git a/README.md b/README.md index c934ecda21..4fb106b126 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ To get the latest preview version of the driver, add the following to your POM f com.microsoft.sqlserver mssql-jdbc - 7.1.3.jre11-preview + 7.1.4.jre11-preview ``` @@ -125,7 +125,7 @@ Projects that require either of the two features need to explicitly declare the com.microsoft.sqlserver mssql-jdbc - 7.1.3.jre11-preview + 7.1.4.jre11-preview compile @@ -148,7 +148,7 @@ Projects that require either of the two features need to explicitly declare the com.microsoft.sqlserver mssql-jdbc - 7.1.3.jre11-preview + 7.1.4.jre11-preview compile diff --git a/build.gradle b/build.gradle index abf1126b27..d7cf97c95c 100644 --- a/build.gradle +++ b/build.gradle @@ -10,7 +10,7 @@ apply plugin: 'java' -version = '7.1.4-SNAPSHOT' +version = '7.1.4' def jreVersion = "" def testOutputDir = file("build/classes/java/test") def archivesBaseName = 'mssql-jdbc' diff --git a/pom.xml b/pom.xml index 6c53a6e8ad..279417c642 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.microsoft.sqlserver mssql-jdbc - 7.1.4-SNAPSHOT + 7.1.4 jar Microsoft JDBC Driver for SQL Server