From 8a5fca2207dd9a5f4f22bfbfb45652992a53cb3a Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Fri, 23 Aug 2019 11:46:33 -0700 Subject: [PATCH 01/11] Fix | Add list of trusted endpoints for AKV (#1130) * added list of trusted endpoints * rename --- ...ColumnEncryptionAzureKeyVaultProvider.java | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java index 5b3e25eab..1ad15e4f7 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java @@ -50,7 +50,14 @@ public class SQLServerColumnEncryptionAzureKeyVaultProvider extends SQLServerCol private final String baseUrl = "https://{vaultBaseUrl}"; - private final String azureKeyVaultDomainName = "vault.azure.net"; + /** + * List of Azure trusted endpoints https://docs.microsoft.com/en-us/azure/key-vault/key-vault-secure-your-key-vault + */ + private final String azureTrustedEndpoints[] = {"vault.azure.net", // default + "vault.azure.cn", // Azure China + "vault.usgovcloudapi.net", // US Government + "vault.microsoftazure.de" // Azure Germany + }; private final String rsaEncryptionAlgorithmWithOAEPForAKV = "RSA-OAEP"; @@ -448,13 +455,15 @@ private void ValidateNonEmptyAKVPath(String masterKeyPath) throws SQLServerExcep } // A valid URI. - // Check if it is pointing to AKV. - if (!parsedUri.getHost().toLowerCase(Locale.ENGLISH).endsWith(azureKeyVaultDomainName)) { - // Return an error indicating that the AKV url is invalid. - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_AKVMasterKeyPathInvalid")); - Object[] msgArgs = {masterKeyPath}; - throw new SQLServerException(null, form.format(msgArgs), null, 0, false); + // Check if it is pointing to a trusted endpoint. + for (final String endpoint : azureTrustedEndpoints) { + if (parsedUri.getHost().toLowerCase(Locale.ENGLISH).endsWith(endpoint)) { + return; + } } + MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_AKVMasterKeyPathInvalid")); + Object[] msgArgs = {masterKeyPath}; + throw new SQLServerException(null, form.format(msgArgs), null, 0, false); } } From 557ac93d98b50a8b2976c2e6b4d6a339730af115 Mon Sep 17 00:00:00 2001 From: ulvii Date: Wed, 18 Sep 2019 16:58:07 -0700 Subject: [PATCH 02/11] Update issue templates (#1148) --- .../ISSUE_TEMPLATE/{bug_report.md => incident-report.md} | 8 ++++---- .github/ISSUE_TEMPLATE/question.md | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) rename .github/ISSUE_TEMPLATE/{bug_report.md => incident-report.md} (91%) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/incident-report.md similarity index 91% rename from .github/ISSUE_TEMPLATE/bug_report.md rename to .github/ISSUE_TEMPLATE/incident-report.md index a43efd7d3..2eb2e3a78 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/incident-report.md @@ -1,8 +1,8 @@ --- -name: Bug report -about: Report a bug to help us improve -title: "[BUG]" -labels: bug +name: Incident report +about: Report an incident +title: '' +labels: '' assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/question.md b/.github/ISSUE_TEMPLATE/question.md index 1131f35db..9681d198d 100644 --- a/.github/ISSUE_TEMPLATE/question.md +++ b/.github/ISSUE_TEMPLATE/question.md @@ -1,6 +1,6 @@ --- name: Question -about: Discuss an idea to see if it would be an appropriate Issue. +about: Ask a question title: "[QUESTION]" labels: question assignees: '' From 40fb4fce893710c4687090850c652ffc1d8f8bf8 Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Thu, 19 Sep 2019 14:12:20 -0700 Subject: [PATCH 03/11] Fix getImportedKeys() returning duplicate rows if multiple FKs have the same name (#1092) * fixed for github #1091 dup rows --- .../jdbc/SQLServerDatabaseMetaData.java | 2 +- .../DatabaseMetaDataForeignKeyTest.java | 79 ++++++++++++++++--- 2 files changed, 68 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java index 6bbf9995c..04030878d 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java @@ -1011,7 +1011,7 @@ private ResultSet executeSPFkeys(String[] procParams) throws SQLException, SQLTi "END as UPDATE_RULE, " + "CASE s.delete_referential_action " + "WHEN 1 THEN 0 " + "WHEN 0 THEN 3 " + "WHEN 2 THEN 2 " + "WHEN 3 THEN 4 " + "END as DELETE_RULE, " + "t.FK_NAME, " + "t.PK_NAME, " + "t.DEFERRABILITY " + "FROM " + tempTableName + " t " - + "LEFT JOIN sys.foreign_keys s ON t.FK_NAME = s.name collate database_default;"; + + "LEFT JOIN sys.foreign_keys s ON t.FK_NAME = s.name COLLATE database_default AND schema_id(t.FKTABLE_OWNER) = s.schema_id"; SQLServerCallableStatement cstmt = (SQLServerCallableStatement) connection.prepareCall(sql); cstmt.closeOnCompletion(); for (int i = 0; i < 6; i++) { diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataForeignKeyTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataForeignKeyTest.java index fbe3f195f..1c5f48bb3 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataForeignKeyTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataForeignKeyTest.java @@ -42,8 +42,13 @@ public class DatabaseMetaDataForeignKeyTest extends AbstractTest { private static String table3 = RandomUtil.getIdentifier("DatabaseMetaDataForeignKeyTest_table_3"); private static String table4 = RandomUtil.getIdentifier("DatabaseMetaDataForeignKeyTest_table_4"); private static String table5 = RandomUtil.getIdentifier("DatabaseMetaDataForeignKeyTest_table_5"); + private static String PKTable1 = RandomUtil.getIdentifier("DatabaseMetaDataForeignKeyTest_PKTable1"); + private static String FKTable1 = RandomUtil.getIdentifier("DatabaseMetaDataForeignKeyTest_FKTable1"); + private static String PKTable2 = RandomUtil.getIdentifier("DatabaseMetaDataForeignKeyTest_PKTable2"); + private static String FKTable2 = RandomUtil.getIdentifier("DatabaseMetaDataForeignKeyTest_FKTable2"); private static String schema = null; + private static String anotherSchema = RandomUtil.getIdentifier("anotherSchema"); private static String catalog = null; @BeforeAll @@ -52,30 +57,24 @@ public static void setupVariation() throws SQLException { catalog = conn.getCatalog(); schema = conn.getSchema(); - stmt.executeUpdate("if object_id('" + TestUtils.escapeSingleQuotes(table1) - + "','U') is not null drop table " + AbstractSQLGenerator.escapeIdentifier(table1)); - stmt.executeUpdate("if object_id('" + TestUtils.escapeSingleQuotes(table2) - + "','U') is not null drop table " + AbstractSQLGenerator.escapeIdentifier(table2)); + TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(table1), stmt); + TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(table2), stmt); + TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(table3), stmt); + TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(table4), stmt); + TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(table5), stmt); + stmt.execute("Create table " + AbstractSQLGenerator.escapeIdentifier(table2) + " (c21 int NOT NULL PRIMARY KEY)"); - stmt.executeUpdate("if object_id('" + TestUtils.escapeSingleQuotes(table3) - + "','U') is not null drop table " + AbstractSQLGenerator.escapeIdentifier(table3)); stmt.execute("Create table " + AbstractSQLGenerator.escapeIdentifier(table3) + " (c31 int NOT NULL PRIMARY KEY)"); - stmt.executeUpdate("if object_id('" + TestUtils.escapeSingleQuotes(table4) - + "','U') is not null drop table " + AbstractSQLGenerator.escapeIdentifier(table4)); stmt.execute("Create table " + AbstractSQLGenerator.escapeIdentifier(table4) + " (c41 int NOT NULL PRIMARY KEY)"); - stmt.executeUpdate("if object_id('" + TestUtils.escapeSingleQuotes(table5) - + "','U') is not null drop table " + AbstractSQLGenerator.escapeIdentifier(table5)); stmt.execute("Create table " + AbstractSQLGenerator.escapeIdentifier(table5) + " (c51 int NOT NULL PRIMARY KEY)"); - stmt.executeUpdate("if object_id('" + TestUtils.escapeSingleQuotes(table1) - + "','U') is not null drop table " + AbstractSQLGenerator.escapeIdentifier(table1)); stmt.execute("Create table " + AbstractSQLGenerator.escapeIdentifier(table1) + " (c11 int primary key," + " c12 int FOREIGN KEY REFERENCES " + AbstractSQLGenerator.escapeIdentifier(table2) + "(c21) ON DELETE no action ON UPDATE set default," + " c13 int FOREIGN KEY REFERENCES " @@ -84,6 +83,27 @@ public static void setupVariation() throws SQLException { + "(c41) ON DELETE set null ON UPDATE cascade," + " c15 int FOREIGN KEY REFERENCES " + AbstractSQLGenerator.escapeIdentifier(table5) + "(c51) ON DELETE set default ON UPDATE no action," + ")"); + + stmt.execute("CREATE SCHEMA " + AbstractSQLGenerator.escapeIdentifier(anotherSchema)); + + stmt.execute("Create table " + AbstractSQLGenerator.escapeIdentifier(PKTable1) + + " (col int NOT NULL PRIMARY KEY)"); + + stmt.execute("Create table " + AbstractSQLGenerator.escapeIdentifier(anotherSchema) + "." + + AbstractSQLGenerator.escapeIdentifier(PKTable2) + " (col int NOT NULL PRIMARY KEY)"); + + stmt.execute("Create table " + AbstractSQLGenerator.escapeIdentifier(schema) + "." + + AbstractSQLGenerator.escapeIdentifier(FKTable1) + + " (col int, CONSTRAINT fk_DuplicateName FOREIGN KEY ([col]) REFERENCES " + + AbstractSQLGenerator.escapeIdentifier(schema) + "." + + AbstractSQLGenerator.escapeIdentifier(PKTable1) + "([col])" + ")"); + + stmt.execute("Create table " + AbstractSQLGenerator.escapeIdentifier(anotherSchema) + "." + + AbstractSQLGenerator.escapeIdentifier(FKTable2) + + " (col int, CONSTRAINT fk_DuplicateName FOREIGN KEY ([col]) REFERENCES " + + AbstractSQLGenerator.escapeIdentifier(anotherSchema) + "." + + AbstractSQLGenerator.escapeIdentifier(PKTable2) + "([col])" + ")"); + } catch (Exception e) { fail(TestResource.getResource("R_unexpectedErrorMessage") + e.getMessage()); } @@ -97,6 +117,16 @@ public static void terminateVariation() throws SQLException { TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(table3), stmt); TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(table4), stmt); TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(table5), stmt); + + TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(FKTable1), stmt); + TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(anotherSchema) + "." + + AbstractSQLGenerator.escapeIdentifier(FKTable2), stmt); + TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(anotherSchema) + "." + + AbstractSQLGenerator.escapeIdentifier(PKTable2), stmt); + TestUtils.dropTableIfExists(AbstractSQLGenerator.escapeIdentifier(PKTable1), stmt); + + TestUtils.dropSchemaIfExists(anotherSchema, stmt); + } catch (Exception e) { fail(TestResource.getResource("R_unexpectedErrorMessage") + e.getMessage()); } @@ -133,6 +163,31 @@ public void testGetImportedKeys() throws SQLException { } } + @Test + /** + * test getImportedKeys does not return duplicate row if multiple FKs have same name + * + * @throws SQLException + * @throws SQLTimeoutException + */ + public void validateDuplicateForeignKeys() throws SQLException { + int expectedRowCount = 1; + int rowCount = 0; + + try (Connection conn = getConnection()) { + DatabaseMetaData dmd = conn.getMetaData(); + + try (ResultSet rs = dmd.getImportedKeys(null, null, FKTable1)) { + while (rs.next()) { + rowCount++; + } + } + if (expectedRowCount != rowCount) { + assertEquals(expectedRowCount, rowCount, TestResource.getResource("R_numKeysIncorrect")); + } + } + } + private void validateGetImportedKeysResults(ResultSet rs) throws SQLException { int expectedRowCount = 4; int rowCount = 0; From 7dd953ef6ae3f3a131061fea0ed2df721e9fa2ab Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Thu, 19 Sep 2019 14:37:36 -0700 Subject: [PATCH 04/11] Fix STAsBinary returning null for a single point (#1074) * populate wkb for point * fix stasbinary issue * change variable name * apply same variable name change --- .../jdbc/SQLServerSpatialDatatype.java | 58 +++++++++++++------ .../SQLServerSpatialDatatypeTest.java | 5 +- 2 files changed, 42 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSpatialDatatype.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSpatialDatatype.java index f7c722e6b..29fc21e8e 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSpatialDatatype.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerSpatialDatatype.java @@ -94,19 +94,31 @@ abstract class SQLServerSpatialDatatype { /** * Serializes the Geogemetry/Geography instance to WKB. * - * @param noZM - * flag to indicate if Z and M coordinates should be included + * @param excludeZMFromWKB + * flag to indicate if Z and M coordinates should be excluded from the WKB representation * @param type * Type of Spatial Datatype (Geometry/Geography) */ - protected void serializeToWkb(boolean noZM, SQLServerSpatialDatatype type) { - ByteBuffer buf = ByteBuffer.allocate(determineWkbCapacity()); + protected void serializeToWkb(boolean excludeZMFromWKB, SQLServerSpatialDatatype type) { + ByteBuffer buf = ByteBuffer.allocate(determineWkbCapacity(excludeZMFromWKB)); createSerializationProperties(); buf.order(ByteOrder.LITTLE_ENDIAN); buf.putInt(srid); buf.put(version); - buf.put(serializationProperties); + if (excludeZMFromWKB) { + byte serializationPropertiesNoZM = serializationProperties; + if (hasZvalues) { + serializationPropertiesNoZM -= hasZvaluesMask; + } + + if (hasMvalues) { + serializationPropertiesNoZM -= hasMvaluesMask; + } + buf.put(serializationPropertiesNoZM); + } else { + buf.put(serializationProperties); + } if (!isSinglePoint && !isSingleLineSegment) { buf.putInt(numberOfPoints); @@ -124,7 +136,7 @@ protected void serializeToWkb(boolean noZM, SQLServerSpatialDatatype type) { } } - if (!noZM) { + if (!excludeZMFromWKB) { if (hasZvalues) { for (int i = 0; i < numberOfPoints; i++) { buf.putDouble(zValues[i]); @@ -139,7 +151,11 @@ protected void serializeToWkb(boolean noZM, SQLServerSpatialDatatype type) { } if (isSinglePoint || isSingleLineSegment) { - wkb = buf.array(); + if (excludeZMFromWKB) { + wkbNoZM = buf.array(); + } else { + wkb = buf.array(); + } return; } @@ -163,7 +179,7 @@ protected void serializeToWkb(boolean noZM, SQLServerSpatialDatatype type) { } } - if (noZM) { + if (excludeZMFromWKB) { wkbNoZM = buf.array(); } else { wkb = buf.array(); @@ -1282,7 +1298,7 @@ protected void createSerializationProperties() { } } - protected int determineWkbCapacity() { + protected int determineWkbCapacity(boolean excludeZMFromWKB) { int totalSize = 0; totalSize += 6; // SRID + version + SerializationPropertiesByte @@ -1290,24 +1306,28 @@ protected int determineWkbCapacity() { if (isSinglePoint || isSingleLineSegment) { totalSize += 16 * numberOfPoints; - if (hasZvalues) { - totalSize += 8 * numberOfPoints; - } + if (!excludeZMFromWKB) { + if (hasZvalues) { + totalSize += 8 * numberOfPoints; + } - if (hasMvalues) { - totalSize += 8 * numberOfPoints; + if (hasMvalues) { + totalSize += 8 * numberOfPoints; + } } return totalSize; } int pointSize = 16; - if (hasZvalues) { - pointSize += 8; - } + if (!excludeZMFromWKB) { + if (hasZvalues) { + pointSize += 8; + } - if (hasMvalues) { - pointSize += 8; + if (hasMvalues) { + pointSize += 8; + } } totalSize += 12; // 4 bytes for 3 ints, each representing the number of points, shapes and figures 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 d17b5a17d..af7b42d81 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SQLServerSpatialDatatypeTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/datatypes/SQLServerSpatialDatatypeTest.java @@ -5,6 +5,7 @@ package com.microsoft.sqlserver.jdbc.datatypes; import static org.junit.Assert.fail; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; import java.io.IOException; @@ -957,8 +958,8 @@ public void testSTAsBinary() throws SQLException { byte[] geomWKB2 = geomWKT2.STAsBinary(); byte[] geogWKB2 = geogWKT2.STAsBinary(); - assertEquals(geomWKB, geomWKB2); - assertEquals(geogWKB, geogWKB2); + assertArrayEquals(geomWKB, geomWKB2); + assertArrayEquals(geogWKB, geogWKB2); } @Test From b424b77fd11b2c883dbbd4abf8c3c2d5d1cb34e5 Mon Sep 17 00:00:00 2001 From: v-reye Date: Tue, 24 Sep 2019 16:03:24 -0700 Subject: [PATCH 05/11] Performance | Improved performance of column name string lookups (#1066) * Fix | Made column name lookup more performant * Fix | Spacing * Fix | Changed arraylist to maps * Fix | Add newline * Add | Caching for previously retrieved columns * Fix | Formatting * Fix | Variable issue * Fix | null order * Fix | part 2 * Revert | Integer changes * Fix | Trim retrieved column name * Fix | Clear column names whenever ResultSet cursor is moved * Revert "Fix | Clear column names whenever ResultSet cursor is moved" This reverts commit db42d447ef37ea545eefc82f3e500584153b4777. --- .../jdbc/SQLServerCallableStatement.java | 66 +++++++------------ .../sqlserver/jdbc/SQLServerResultSet.java | 29 +++++--- 2 files changed, 42 insertions(+), 53 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java index 76eabac63..c773c9f80 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerCallableStatement.java @@ -24,8 +24,9 @@ import java.sql.Time; import java.sql.Timestamp; import java.text.MessageFormat; -import java.util.ArrayList; import java.util.Calendar; +import java.util.HashMap; +import java.util.TreeMap; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -49,7 +50,8 @@ public class SQLServerCallableStatement extends SQLServerPreparedStatement imple private static final long serialVersionUID = 5044984771674532350L; /** the call param names */ - private ArrayList parameterNames; + private HashMap parameterNames; + private TreeMap insensitiveParameterNames; /** Number of registered OUT parameters */ int nOutParams = 0; @@ -702,7 +704,7 @@ public Object getObject(int index) throws SQLServerException { loggerExternal.entering(getClassNameLogging(), "getObject", index); checkClosed(); Object value = getValue(index, - getterGetParam(index).getJdbcTypeSetByUser() != null ? getterGetParam(index).getJdbcTypeSetByUser() + null != getterGetParam(index).getJdbcTypeSetByUser() ? getterGetParam(index).getJdbcTypeSetByUser() : getterGetParam(index).getJdbcType()); loggerExternal.exiting(getClassNameLogging(), "getObject", value); return value; @@ -743,7 +745,7 @@ public T getObject(int index, Class type) throws SQLException { } else if (type == UUID.class) { // read binary, avoid string allocation and parsing byte[] guid = getBytes(index); - returnValue = guid != null ? Util.readGUIDtoUUID(guid) : null; + returnValue = null != guid ? Util.readGUIDtoUUID(guid) : null; } else if (type == SQLXML.class) { returnValue = getSQLXML(index); } else if (type == Blob.class) { @@ -778,7 +780,7 @@ public Object getObject(String parameterName) throws SQLServerException { checkClosed(); int parameterIndex = findColumn(parameterName); Object value = getValue(parameterIndex, - getterGetParam(parameterIndex).getJdbcTypeSetByUser() != null ? getterGetParam(parameterIndex) + null != getterGetParam(parameterIndex).getJdbcTypeSetByUser() ? getterGetParam(parameterIndex) .getJdbcTypeSetByUser() : getterGetParam(parameterIndex).getJdbcType()); loggerExternal.exiting(getClassNameLogging(), "getObject", value); return value; @@ -1253,7 +1255,7 @@ public java.sql.Array getArray(String parameterName) throws SQLException { * @return the index */ private int findColumn(String columnName) throws SQLServerException { - if (parameterNames == null) { + if (null == parameterNames) { try (SQLServerStatement s = (SQLServerStatement) connection.createStatement()) { // Note we are concatenating the information from the passed in sql, not any arguments provided by the // user @@ -1287,11 +1289,15 @@ private int findColumn(String columnName) throws SQLServerException { SQLServerException.makeFromDriverError(connection, this, form.format(msgArgs), "07009", false); } - ResultSet rs = s.executeQueryInternal(metaQuery.toString()); - parameterNames = new ArrayList<>(); - while (rs.next()) { - String parameterName = rs.getString(4); - parameterNames.add(parameterName.trim()); + try (ResultSet rs = s.executeQueryInternal(metaQuery.toString())) { + parameterNames = new HashMap<>(); + insensitiveParameterNames = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + int columnIndex = 0; + while (rs.next()) { + String p = rs.getString(4).trim(); + parameterNames.put(p, columnIndex); + insensitiveParameterNames.put(p, columnIndex++); + } } } catch (SQLException e) { SQLServerException.makeFromDriverError(connection, this, e.toString(), null, false); @@ -1310,12 +1316,7 @@ private int findColumn(String columnName) throws SQLServerException { // handle `@name` as well as `name`, since `@name` is what's returned // by DatabaseMetaData#getProcedureColumns - String columnNameWithoutAtSign = null; - if (columnName.startsWith("@")) { - columnNameWithoutAtSign = columnName.substring(1, columnName.length()); - } else { - columnNameWithoutAtSign = columnName; - } + String columnNameWithSign = columnName.startsWith("@") ? columnName : "@" + columnName; // In order to be as accurate as possible when locating parameter name // indexes, as well as be deterministic when running on various client @@ -1323,34 +1324,11 @@ private int findColumn(String columnName) throws SQLServerException { // 1. Search using case-sensitive non-locale specific (binary) compare first. // 2. Search using case-insensitive, non-locale specific (binary) compare last. - - int i; - int matchPos = -1; - // Search using case-sensitive, non-locale specific (binary) compare. - // If the user supplies a true match for the parameter name, we will find it here. - for (i = 0; i < l; i++) { - String sParam = parameterNames.get(i); - sParam = sParam.substring(1, sParam.length()); - if (sParam.equals(columnNameWithoutAtSign)) { - matchPos = i; - break; - } + Integer matchPos = parameterNames.get(columnNameWithSign); + if (null == matchPos) { + matchPos = insensitiveParameterNames.get(columnNameWithSign); } - - if (-1 == matchPos) { - // Check for case-insensitive match using a non-locale aware method. - // Use VM supplied String.equalsIgnoreCase to do the "case-insensitive search". - for (i = 0; i < l; i++) { - String sParam = parameterNames.get(i); - sParam = sParam.substring(1, sParam.length()); - if (sParam.equalsIgnoreCase(columnNameWithoutAtSign)) { - matchPos = i; - break; - } - } - } - - if (-1 == matchPos) { + if (null == matchPos) { MessageFormat form = new MessageFormat( SQLServerException.getErrString("R_parameterNotDefinedForProcedure")); Object[] msgArgs = {columnName, procedureName}; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java index ca043a8bc..a6560b975 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerResultSet.java @@ -24,6 +24,8 @@ import java.sql.SQLXML; import java.text.MessageFormat; import java.util.Calendar; +import java.util.HashMap; +import java.util.Map; import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; @@ -153,6 +155,9 @@ final void setCurrentRowType(RowType rowType) { /** Flag set to true if the current row was updated through this ResultSet object */ private boolean updatedCurrentRow = false; + // Column name hash map for caching. + private final Map columnNames = new HashMap<>(); + final boolean getUpdatedCurrentRow() { return updatedCurrentRow; } @@ -632,17 +637,22 @@ public void close() throws SQLServerException { /** * Finds a column index given a column name. * - * @param columnName + * @param userProvidedColumnName * the name of the column * @throws SQLServerException * If any errors occur. * @return the column index */ @Override - public int findColumn(String columnName) throws SQLServerException { - loggerExternal.entering(getClassNameLogging(), "findColumn", columnName); + public int findColumn(String userProvidedColumnName) throws SQLServerException { + loggerExternal.entering(getClassNameLogging(), "findColumn", userProvidedColumnName); checkClosed(); + Integer value = columnNames.get(userProvidedColumnName); + if (null != value) { + return value; + } + // In order to be as accurate as possible when locating column name // indexes, as well as be deterministic when running on various client // locales, we search for column names using the following scheme: @@ -663,9 +673,9 @@ public int findColumn(String columnName) throws SQLServerException { // Search using case-sensitive, non-locale specific (binary) compare. // If the user supplies a true match for the column name, we will find it here. - int i; - for (i = 0; i < columns.length; i++) { - if (columns[i].getColumnName().equals(columnName)) { + for (int i = 0; i < columns.length; i++) { + if (columns[i].getColumnName().equals(userProvidedColumnName)) { + columnNames.put(userProvidedColumnName, i + 1); loggerExternal.exiting(getClassNameLogging(), "findColumn", i + 1); return i + 1; } @@ -675,14 +685,15 @@ public int findColumn(String columnName) throws SQLServerException { // Per JDBC spec, 27.3 "The driver will do a case-insensitive search for // columnName in it's attempt to map it to the column's index". // Use VM supplied String.equalsIgnoreCase to do the "case-insensitive search". - for (i = 0; i < columns.length; i++) { - if (columns[i].getColumnName().equalsIgnoreCase(columnName)) { + for (int i = 0; i < columns.length; i++) { + if (columns[i].getColumnName().equalsIgnoreCase(userProvidedColumnName)) { + columnNames.put(userProvidedColumnName, i + 1); loggerExternal.exiting(getClassNameLogging(), "findColumn", i + 1); return i + 1; } } MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_invalidColumnName")); - Object[] msgArgs = {columnName}; + Object[] msgArgs = {userProvidedColumnName}; SQLServerException.makeFromDriverError(stmt.connection, stmt, form.format(msgArgs), "07009", false); return 0; From 2deb6fa3daee88aa386f73f4721f1bf4db33eaad Mon Sep 17 00:00:00 2001 From: ulvii Date: Thu, 26 Sep 2019 13:39:58 -0700 Subject: [PATCH 06/11] Add | Implement hashCode() and equals() APIs for SQLServerDataTable and SQLServerDataColumn (#1146) * Add | Add hashCode()/equals() methods to SQLServerDataColumn and SQLServerDataTable --- .../sqlserver/jdbc/SQLServerDataColumn.java | 31 ++++++ .../sqlserver/jdbc/SQLServerDataTable.java | 76 +++++++++++++ .../jdbc/tvp/SQLServerDataTableTest.java | 103 ++++++++++++++++++ 3 files changed, 210 insertions(+) create mode 100644 src/test/java/com/microsoft/sqlserver/jdbc/tvp/SQLServerDataTableTest.java diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataColumn.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataColumn.java index 218f58dbb..1cb840b3c 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataColumn.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataColumn.java @@ -45,4 +45,35 @@ public String getColumnName() { public int getColumnType() { return javaSqlType; } + + @Override + public int hashCode() { + int hash = 7; + hash = 31 * hash + javaSqlType; + hash = 31 * hash + precision; + hash = 31 * hash + scale; + hash = 31 * hash + numberOfDigitsIntegerPart; + hash = 31 * hash + (null != columnName ? columnName.hashCode() : 0); + return hash; + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + + if (null != object && object.getClass() == SQLServerDataColumn.class) { + SQLServerDataColumn aSQLServerDataColumn = (SQLServerDataColumn) object; + if (hashCode() == aSQLServerDataColumn.hashCode()) { + // Compare objects to avoid collision + return ((null == columnName && null == aSQLServerDataColumn.columnName + || columnName.equals(aSQLServerDataColumn.columnName)) + && javaSqlType == aSQLServerDataColumn.javaSqlType + && numberOfDigitsIntegerPart == aSQLServerDataColumn.numberOfDigitsIntegerPart + && precision == aSQLServerDataColumn.precision && scale == aSQLServerDataColumn.scale); + } + } + return false; + } } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataTable.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataTable.java index 8d59c0463..9f718d400 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataTable.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataTable.java @@ -9,6 +9,7 @@ import java.text.MessageFormat; import java.time.OffsetDateTime; import java.time.OffsetTime; +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -334,4 +335,79 @@ public String getTvpName() { public void setTvpName(String tvpName) { this.tvpName = tvpName; } + + @Override + public int hashCode() { + int hash = 7; + hash = 31 * hash + rowCount; + hash = 31 * hash + columnCount; + hash = 31 * hash + (null != columnMetadata ? columnMetadata.hashCode() : 0); + hash = 31 * hash + (null != columnNames ? columnNames.hashCode() : 0); + hash = 31 * hash + getRowsHashCode(); + hash = 31 * hash + (null != tvpName ? tvpName.hashCode() : 0); + return hash; + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + + if (null != object && object.getClass() == SQLServerDataTable.class) { + SQLServerDataTable aSQLServerDataTable = (SQLServerDataTable) object; + if (hashCode() == aSQLServerDataTable.hashCode()) { + + // Compare objects to avoid collision + boolean equalColumnMetadata = columnMetadata.equals(aSQLServerDataTable.columnMetadata); + boolean equalColumnNames = columnNames.equals(aSQLServerDataTable.columnNames); + boolean equalRowData = compareRows(aSQLServerDataTable.rows); + + return (rowCount == aSQLServerDataTable.rowCount && columnCount == aSQLServerDataTable.columnCount + && tvpName == aSQLServerDataTable.tvpName && equalColumnMetadata && equalColumnNames + && equalRowData); + } + } + return false; + } + + private int getRowsHashCode() { + if (null == rows) { + return 0; + } + int h = 0; + for (Entry entry : rows.entrySet()) { + h += entry.getKey() ^ Arrays.hashCode(entry.getValue()); + } + return h; + } + + private boolean compareRows(Map otherRows) { + if (rows == otherRows) { + return true; + } + if (rows.size() != otherRows.size()) { + return false; + } + try { + for (Entry e : rows.entrySet()) { + Integer key = e.getKey(); + Object[] value = e.getValue(); + if (null == value) { + if (!(null == otherRows.get(key) && otherRows.containsKey(key))) { + return false; + } + } else { + if (!Arrays.equals(value, otherRows.get(key))) { + return false; + } + } + } + } catch (ClassCastException unused) { + return false; + } catch (NullPointerException unused) { + return false; + } + return true; + } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/tvp/SQLServerDataTableTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/tvp/SQLServerDataTableTest.java new file mode 100644 index 000000000..cea36fcbd --- /dev/null +++ b/src/test/java/com/microsoft/sqlserver/jdbc/tvp/SQLServerDataTableTest.java @@ -0,0 +1,103 @@ +/* + * 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.tvp; + +import static org.junit.Assert.assertEquals; + +import java.math.BigDecimal; +import java.sql.Types; +import org.junit.jupiter.api.Test; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; + +import com.microsoft.sqlserver.jdbc.SQLServerDataColumn; +import com.microsoft.sqlserver.jdbc.SQLServerDataTable; +import com.microsoft.sqlserver.jdbc.SQLServerException; + + +@RunWith(JUnitPlatform.class) +public class SQLServerDataTableTest { + + @Test + public void testClear() throws SQLServerException { + SQLServerDataTable table = new SQLServerDataTable(); + SQLServerDataColumn a = new SQLServerDataColumn("foo", Types.VARCHAR); + SQLServerDataColumn b = new SQLServerDataColumn("bar", Types.INTEGER); + + table.addColumnMetadata(a); + table.addColumnMetadata(b); + assertEquals(2, table.getColumnMetadata().size()); + + table.clear(); + assertEquals(0, table.getColumnMetadata().size()); + + table.addColumnMetadata(a); + table.addColumnMetadata(b); + assertEquals(2, table.getColumnMetadata().size()); + } + + @Test + public void testHashCodes() throws SQLServerException { + // Test Null field values for SQLServerDataColumn + SQLServerDataColumn nullDataColumn1 = new SQLServerDataColumn(null, 0); + SQLServerDataColumn nullDataColumn2 = new SQLServerDataColumn(null, 0); + assert (nullDataColumn1.hashCode() == nullDataColumn2.hashCode()); + assert (nullDataColumn1.equals(nullDataColumn2)); + + // Test Null field values for SQLServerDataTable + SQLServerDataTable nullDataTable1 = new SQLServerDataTable(); + SQLServerDataTable nullDataTable2 = new SQLServerDataTable(); + assert (nullDataTable1.hashCode() == nullDataTable2.hashCode()); + assert (nullDataTable1.equals(nullDataTable2)); + + SQLServerDataColumn a = new SQLServerDataColumn("foo", Types.VARCHAR); + + // Test consistent generation of hashCode + assert (a.hashCode() == a.hashCode()); + assert (a.equals(a)); + + SQLServerDataColumn aClone = new SQLServerDataColumn("foo", Types.VARCHAR); + + // Test for different instances generating same hashCode for same data + assert (a.hashCode() == aClone.hashCode()); + assert (a.equals(aClone)); + + SQLServerDataColumn b = new SQLServerDataColumn("bar", Types.DECIMAL); + SQLServerDataTable table = createTable(a, b); + + // Test consistent generation of hashCode + assert (table.hashCode() == table.hashCode()); + assert (table.equals(table)); + + SQLServerDataTable tableClone = createTable(aClone, b); + + // Test for different instances generating same hashCode for same data + assert (table.hashCode() == tableClone.hashCode()); + assert (table.equals(tableClone)); + + // Test for non equal hashCodes + assert (a.hashCode() != b.hashCode()); + assert (!a.equals(b)); + + SQLServerDataColumn c = new SQLServerDataColumn("bar", Types.FLOAT); + table.clear(); + table = createTable(a, c); + + // Test for non equal hashCodes + assert (table.hashCode() != tableClone.hashCode()); + assert (!table.equals(tableClone)); + } + + private SQLServerDataTable createTable(SQLServerDataColumn a, SQLServerDataColumn b) throws SQLServerException { + SQLServerDataTable table = new SQLServerDataTable(); + table.addColumnMetadata(a); + table.addColumnMetadata(b); + table.addRow("Hello", new BigDecimal(1.5)); + table.addRow("World", new BigDecimal(5.5)); + table.setTvpName("TVP_HashCode"); + return table; + } +} From 8e615009c77a55e4af5669d6bdf130c385bdfbda Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Fri, 27 Sep 2019 15:36:46 -0700 Subject: [PATCH 07/11] Fix issue with truststore password being removed too early for XA transaction (#1133) Fix | Fix issue with truststore password being removed too early for XA transaction --- azure-pipelines.yml | 11 +++- build.gradle | 4 +- pom.xml | 5 +- .../sqlserver/jdbc/SQLServerDataSource.java | 11 ++-- .../sqlserver/jdbc/SQLServerXAConnection.java | 16 ++++++ .../microsoft/sqlserver/jdbc/TestUtils.java | 55 +++++++++++++++++++ .../jdbc/connection/XADataSourceTest.java | 44 +++++++++++++++ .../sqlserver/testframework/Constants.java | 2 + 8 files changed, 137 insertions(+), 11 deletions(-) create mode 100644 src/test/java/com/microsoft/sqlserver/jdbc/connection/XADataSourceTest.java diff --git a/azure-pipelines.yml b/azure-pipelines.yml index e80e73dae..45647c1e6 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -29,11 +29,16 @@ jobs: Get-Content .\JavaKeyStoreBase.txt | Set-Content -Encoding utf8 JavaKeyStore.txt Remove-Item –path .\JavaKeyStoreBase.txt displayName: 'PowerShell Script' + - task: DownloadSecureFile@1 + name: pkcs12_truststore + displayName: 'Download PKCS12 truststore file' + inputs: + secureFile: 'pkcs12_truststore' - task: Maven@3 displayName: 'Maven build jre12' inputs: mavenPomFile: 'pom.xml' - goals: 'clean -Dmssql_jdbc_test_connection_properties=jdbc:sqlserver://$(Target_SQL)$(server_domain);$(database);$(user);$(password); install -Pjre12 -DuserNTLM=$(userNTLM) -DpasswordNTLM=$(passwordNTLM) -DdomainNTLM=$(domainNTLM) -DexcludedGroups=$(Ex_Groups)' + goals: 'clean -Dmssql_jdbc_test_connection_properties=jdbc:sqlserver://$(Target_SQL)$(server_domain);$(database);$(user);$(password); install -Pjre12 -DuserNTLM=$(userNTLM) -DpasswordNTLM=$(passwordNTLM) -DdomainNTLM=$(domainNTLM) -DexcludedGroups=$(Ex_Groups) -Dpkcs12_truststore_password=$(pkcs12_truststore_password) -Dpkcs12_truststore=$(pkcs12_truststore.secureFilePath)' testResultsFiles: '**/TEST-*.xml' testRunTitle: 'Maven build jre12' javaHomeOption: Path @@ -42,7 +47,7 @@ jobs: displayName: 'Maven build jre11' inputs: mavenPomFile: 'pom.xml' - goals: 'clean -Dmssql_jdbc_test_connection_properties=jdbc:sqlserver://$(Target_SQL)$(server_domain);$(database);$(user);$(password); install -Pjre11 -DuserNTLM=$(userNTLM) -DpasswordNTLM=$(passwordNTLM) -DdomainNTLM=$(domainNTLM) -DexcludedGroups=$(Ex_Groups)' + goals: 'clean -Dmssql_jdbc_test_connection_properties=jdbc:sqlserver://$(Target_SQL)$(server_domain);$(database);$(user);$(password); install -Pjre11 -DuserNTLM=$(userNTLM) -DpasswordNTLM=$(passwordNTLM) -DdomainNTLM=$(domainNTLM) -DexcludedGroups=$(Ex_Groups) -Dpkcs12_truststore_password=$(pkcs12_truststore_password) -Dpkcs12_truststore=$(pkcs12_truststore.secureFilePath)' testResultsFiles: '**/TEST-*.xml' testRunTitle: 'Maven build jre11' javaHomeOption: Path @@ -51,7 +56,7 @@ jobs: displayName: 'Maven build jre8' inputs: mavenPomFile: 'pom.xml' - goals: 'clean -Dmssql_jdbc_test_connection_properties=jdbc:sqlserver://$(Target_SQL)$(server_domain);$(database);$(user);$(password); install -Pjre8 -DuserNTLM=$(userNTLM) -DpasswordNTLM=$(passwordNTLM) -DdomainNTLM=$(domainNTLM) -DexcludedGroups=$(Ex_Groups)' + goals: 'clean -Dmssql_jdbc_test_connection_properties=jdbc:sqlserver://$(Target_SQL)$(server_domain);$(database);$(user);$(password); install -Pjre8 -DuserNTLM=$(userNTLM) -DpasswordNTLM=$(passwordNTLM) -DdomainNTLM=$(domainNTLM) -DexcludedGroups=$(Ex_Groups) -Dpkcs12_truststore_password=$(pkcs12_truststore_password) -Dpkcs12_truststore=$(pkcs12_truststore.secureFilePath)' testResultsFiles: '**/TEST-*.xml' testRunTitle: 'Maven build jre8' javaHomeOption: Path diff --git a/build.gradle b/build.gradle index e3adfde93..2deb2a598 100644 --- a/build.gradle +++ b/build.gradle @@ -31,7 +31,7 @@ allprojects { test { useJUnitPlatform { - excludeTags (hasProperty('excludedGroups') ? excludedGroups : 'xSQLv15','xGradle','NTLM') + excludeTags (hasProperty('excludedGroups') ? excludedGroups : 'xSQLv15','xGradle','reqExternalSetup','NTLM') } } @@ -70,7 +70,7 @@ if(hasProperty('buildProfile') && buildProfile == "jre8") { targetCompatibility = 1.8 test { useJUnitPlatform { - excludeTags (hasProperty('excludedGroups') ? excludedGroups : 'xSQLv15','xGradle','NTLM','xJDBC42') + excludeTags (hasProperty('excludedGroups') ? excludedGroups : 'xSQLv15','xGradle','NTLM','reqExternalSetup','xJDBC42') } } } diff --git a/pom.xml b/pom.xml index 1b3eb944b..0c4c4a729 100644 --- a/pom.xml +++ b/pom.xml @@ -49,10 +49,11 @@ xAzureSQLDB - - - - For tests not compatible with Azure SQL Database - - xAzureSQLDW - - - - For tests not compatible with Azure Data Warehouse - xAzureSQLMI - - - - For tests not compatible with Azure SQL Managed Instance - NTLM - - - - - - For tests using NTLM Authentication mode (excluded by default) + NTLM - - - - - - - For tests using NTLM Authentication mode (excluded by default) + reqExternalSetup - For tests requiring external setup (excluded by default) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Default testing enabled with SQL Server 2019 (SQLv14) --> - xSQLv15, NTLM + xSQLv15, NTLM, reqExternalSetup 1.2.1 diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java index 64c523fd7..046dd7313 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDataSource.java @@ -358,6 +358,10 @@ public void setTrustStorePassword(String trustStorePassword) { trustStorePassword); } + String getTrustStorePassword() { + return getStringProperty(connectionProps, SQLServerDriverStringProperty.TRUST_STORE_PASSWORD.toString(), null); + } + @Override public void setHostNameInCertificate(String hostName) { setStringProperty(connectionProps, SQLServerDriverStringProperty.HOSTNAME_IN_CERTIFICATE.toString(), hostName); @@ -478,11 +482,10 @@ public boolean getSendTimeAsDatetime() { return getBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.SEND_TIME_AS_DATETIME.toString(), SQLServerDriverBooleanProperty.SEND_TIME_AS_DATETIME.getDefaultValue()); } - + @Override public void setUseFmtOnly(boolean useFmtOnly) { - setBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.USE_FMT_ONLY.toString(), - useFmtOnly); + setBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.USE_FMT_ONLY.toString(), useFmtOnly); } @Override @@ -490,7 +493,7 @@ public boolean getUseFmtOnly() { return getBooleanProperty(connectionProps, SQLServerDriverBooleanProperty.USE_FMT_ONLY.toString(), SQLServerDriverBooleanProperty.USE_FMT_ONLY.getDefaultValue()); } - + /** * Sets whether string parameters are sent to the server in UNICODE format. * diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerXAConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerXAConnection.java index 958ffa365..fe1067775 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerXAConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerXAConnection.java @@ -52,6 +52,22 @@ public final class SQLServerXAConnection extends SQLServerPooledConnection imple controlConnectionProperties.setProperty(SQLServerDriverStringProperty.PASSWORD.toString(), pwd); } + // Add truststore password property for creating the control connection. This will be removed again + String trustStorePassword = ds.getTrustStorePassword(); + if (null == trustStorePassword) { + // trustStorePassword can either come from the connection string or added via + // SQLServerXADataSource::setTrustStorePassword. + // if trustStorePassword is null at this point, then check the connection string. + Properties urlProps = Util.parseUrl(ds.getURL(), xaLogger); + trustStorePassword = urlProps.getProperty(SQLServerDriverStringProperty.TRUST_STORE_PASSWORD.toString()); + } + + // if trustStorePassword is still null, it wasn't provided. Do not set the property as null to avoid NPE. + if (null != trustStorePassword) { + controlConnectionProperties.setProperty(SQLServerDriverStringProperty.TRUST_STORE_PASSWORD.toString(), + trustStorePassword); + } + if (xaLogger.isLoggable(Level.FINER)) xaLogger.finer("Creating an internal control connection for" + toString()); physicalControlConnection = null; diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/TestUtils.java b/src/test/java/com/microsoft/sqlserver/jdbc/TestUtils.java index 1e257dec3..791b83a72 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/TestUtils.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/TestUtils.java @@ -10,7 +10,12 @@ import java.io.ByteArrayInputStream; import java.io.CharArrayReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.net.URI; +import java.security.KeyStore; +import java.security.cert.CertificateFactory; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.PreparedStatement; @@ -21,6 +26,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; +import java.util.List; import java.util.Locale; import java.util.ResourceBundle; @@ -827,4 +833,53 @@ public static String formatErrorMsg(String s) { public static String addOrOverrideProperty(String connectionString, String property, String value) { return connectionString + ";" + property + "=" + value + ";"; } + + /** + * Creates a truststore and returns the path of it. + * + * @param certificates + * String list of certificates + * @param tsName + * name of truststore to create + * @param tsPwd + * password of truststore to set + * @param ksType + * type of Keystore e.g PKCS12 or JKS + * @return Path of truststore that was created + * @throws Exception + */ + public static String createTrustStore(List certificates, String tsName, String tsPwd, + String ksType) throws Exception { + return (new TrustStore(certificates, tsName, tsPwd, ksType)).getFileName(); + } + + private static class TrustStore { + private File trustStoreFile; + + TrustStore(List certificateNames, String tsName, String tsPwd, String ksType) throws Exception { + trustStoreFile = File.createTempFile(tsName, null, new File(".")); + trustStoreFile.deleteOnExit(); + KeyStore ks = KeyStore.getInstance(ksType); + ks.load(null, null); + + for (String certificateName : certificateNames) { + ks.setCertificateEntry(certificateName, getCertificate(certificateName)); + } + + FileOutputStream os = new FileOutputStream(trustStoreFile); + ks.store(os, tsPwd.toCharArray()); + os.flush(); + os.close(); + } + + final String getFileName() throws Exception { + return trustStoreFile.getCanonicalPath(); + } + + private static java.security.cert.Certificate getCertificate(String certname) throws Exception { + FileInputStream is = new FileInputStream(certname); + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + return cf.generateCertificate(is); + } + } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/connection/XADataSourceTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/connection/XADataSourceTest.java new file mode 100644 index 000000000..9d5958a0c --- /dev/null +++ b/src/test/java/com/microsoft/sqlserver/jdbc/connection/XADataSourceTest.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.connection; + +import javax.sql.XAConnection; + +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.SQLServerXADataSource; +import com.microsoft.sqlserver.testframework.AbstractTest; +import com.microsoft.sqlserver.testframework.Constants; + + +@RunWith(JUnitPlatform.class) +@Tag(Constants.reqExternalSetup) +public class XADataSourceTest extends AbstractTest { + private static String connectionUrlSSL = connectionString + ";encrypt=true;trustServerCertificate=false;"; + + /** + * Tests XA connection with PKCS12 truststore that is password protected. + * + * Only re-populate the truststore if need arises in the future. + * TestUtils.createTrustStore() can be used to create the truststore. + * + * @throws Exception + */ + @Test + public void testPKCS12() throws Exception { + SQLServerXADataSource ds = new SQLServerXADataSource(); + + String trustStore = System.getProperty("pkcs12_truststore"); + String url = connectionUrlSSL + "trustStore=" + trustStore + ";"; + ds.setURL(url); + ds.setTrustStorePassword(System.getProperty("pkcs12_truststore_password")); + XAConnection connection = ds.getXAConnection(); + connection.close(); + } +} diff --git a/src/test/java/com/microsoft/sqlserver/testframework/Constants.java b/src/test/java/com/microsoft/sqlserver/testframework/Constants.java index b1ff0d894..add919439 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/Constants.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/Constants.java @@ -25,6 +25,7 @@ private Constants() {} * xAzureSQLDW - - - - For tests not compatible with Azure Data Warehouse * xAzureSQLMI - - - - For tests not compatible with Azure SQL Managed Instance * NTLM - - - - - - - For NTLM tests + * reqExternalSetup - For tests requiring external setup * */ public static final String xJDBC42 = "xJDBC42"; @@ -36,6 +37,7 @@ private Constants() {} public static final String xAzureSQLDW = "xAzureSQLDW"; public static final String xAzureSQLMI = "xAzureSQLMI"; public static final String NTLM = "NTLM"; + public static final String reqExternalSetup = "reqExternalSetup"; public static final ThreadLocalRandom RANDOM = ThreadLocalRandom.current(); public static final Logger LOGGER = Logger.getLogger("AbstractTest"); From aed8307bbb06fb0c8759f051fbed2d1a198bcd79 Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Tue, 1 Oct 2019 17:24:19 -0700 Subject: [PATCH 08/11] Fix | SQLServerDatabaseMetada.getColumns not escaping wildcard (#1138) --- .../jdbc/SQLServerDatabaseMetaData.java | 12 ++--- .../sqlserver/jdbc/TestResource.java | 3 +- .../DatabaseMetaDataTest.java | 54 +++++++++++++------ 3 files changed, 45 insertions(+), 24 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java index 04030878d..65880bdd2 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerDatabaseMetaData.java @@ -638,10 +638,10 @@ public java.sql.ResultSet getColumns(String catalog, String schema, String table PreparedStatement pstmt = (SQLServerPreparedStatement) this.connection.prepareStatement(spColumnsSql); pstmt.closeOnCompletion(); try { - pstmt.setString(1, (null != table && !table.isEmpty()) ? table : "%"); - pstmt.setString(2, (null != schema && !schema.isEmpty()) ? schema : "%"); + pstmt.setString(1, (null != table && !table.isEmpty()) ? EscapeIDName(table) : "%"); + pstmt.setString(2, (null != schema && !schema.isEmpty()) ? EscapeIDName(schema) : "%"); pstmt.setString(3, (null != catalog && !catalog.isEmpty()) ? catalog : this.connection.getCatalog()); - pstmt.setString(4, (null != col && !col.isEmpty()) ? col : "%"); + pstmt.setString(4, (null != col && !col.isEmpty()) ? EscapeIDName(col) : "%"); pstmt.setInt(5, 2);// show sparse columns pstmt.setInt(6, 3);// odbc version @@ -677,11 +677,11 @@ public java.sql.ResultSet getColumns(String catalog, String schema, String table */ try (PreparedStatement storedProcPstmt = this.connection .prepareStatement("EXEC sp_columns_100 ?,?,?,?,?,?;")) { - storedProcPstmt.setString(1, (null != table && !table.isEmpty()) ? table : "%"); - storedProcPstmt.setString(2, (null != schema && !schema.isEmpty()) ? schema : "%"); + storedProcPstmt.setString(1, (null != table && !table.isEmpty()) ? EscapeIDName(table) : "%"); + storedProcPstmt.setString(2, (null != schema && !schema.isEmpty()) ? EscapeIDName(schema) : "%"); storedProcPstmt.setString(3, (null != catalog && !catalog.isEmpty()) ? catalog : this.connection.getCatalog()); - storedProcPstmt.setString(4, (null != col && !col.isEmpty()) ? col : "%"); + storedProcPstmt.setString(4, (null != col && !col.isEmpty()) ? EscapeIDName(col) : "%"); storedProcPstmt.setInt(5, 2);// show sparse columns storedProcPstmt.setInt(6, 3);// odbc version diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java b/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java index b5cd04128..1ab69af97 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java @@ -179,5 +179,6 @@ protected Object[][] getContents() { {"R_incorrectSyntaxTableDW", "Incorrect syntax near 'table'."}, {"R_ConnectionStringNull", "Connection String should not be null"}, {"R_OperandTypeClash", "Operand type clash"}, - {"R_NoPrivilege", "The EXECUTE permission was denied on the object {0}"}}; + {"R_NoPrivilege", "The EXECUTE permission was denied on the object {0}"}, + {"R_resultSetEmpty", "Result set is empty."}}; } 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 d3a7e2a98..a6a5f9ac9 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java @@ -353,22 +353,19 @@ public void testGetDBColumn() throws SQLException { fail(form1.format(msgArgs1)); } else { try (ResultSet rs1 = databaseMetaData.getColumns(null, null, tableName, "%")) { - MessageFormat form2 = new MessageFormat(TestResource.getResource("R_nameEmpty")); - Object[][] msgArgs2 = {{"Category"}, {"SCHEMA"}, {"Table"}, {"COLUMN"}, {"Data Type"}, {"Type"}, - {"Column Size"}, {"Nullable value"}, {"IS_NULLABLE"}, {"IS_AUTOINCREMENT"}}; - while (rs1.next()) { - assertTrue(!StringUtils.isEmpty(rs1.getString("TABLE_CAT")), form2.format(msgArgs2[0])); - assertTrue(!StringUtils.isEmpty(rs1.getString("TABLE_SCHEM")), form2.format(msgArgs2[1])); - assertTrue(!StringUtils.isEmpty(rs1.getString("TABLE_NAME")), form2.format(msgArgs2[2])); - assertTrue(!StringUtils.isEmpty(rs1.getString("COLUMN_NAME")), form2.format(msgArgs2[3])); - assertTrue(!StringUtils.isEmpty(rs1.getString("DATA_TYPE")), form2.format(msgArgs2[4])); - assertTrue(!StringUtils.isEmpty(rs1.getString("TYPE_NAME")), form2.format(msgArgs2[5])); - assertTrue(!StringUtils.isEmpty(rs1.getString("COLUMN_SIZE")), form2.format(msgArgs2[6])); - assertTrue(!StringUtils.isEmpty(rs1.getString("NULLABLE")), form2.format(msgArgs2[7])); // 11 - assertTrue(!StringUtils.isEmpty(rs1.getString("IS_NULLABLE")), form2.format(msgArgs2[8])); // 18 - assertTrue(!StringUtils.isEmpty(rs1.getString("IS_AUTOINCREMENT")), - form2.format(msgArgs2[9])); // 22 - } + testGetDBColumnInternal(rs1, databaseMetaData); + } + + try (ResultSet rs1 = databaseMetaData.getColumns(null, null, tableName, "col\\_1")) { + testGetDBColumnInternal(rs1, databaseMetaData); + } + + try (ResultSet rs1 = databaseMetaData.getColumns(null, null, tableName, "col\\%2")) { + testGetDBColumnInternal(rs1, databaseMetaData); + } + + try (ResultSet rs1 = databaseMetaData.getColumns(null, null, tableName, "col\\[3")) { + testGetDBColumnInternal(rs1, databaseMetaData); } } } @@ -377,6 +374,29 @@ public void testGetDBColumn() throws SQLException { } } + private void testGetDBColumnInternal(ResultSet rs1, DatabaseMetaData databaseMetaData) throws SQLException { + MessageFormat form2 = new MessageFormat(TestResource.getResource("R_nameEmpty")); + Object[][] msgArgs2 = {{"Category"}, {"SCHEMA"}, {"Table"}, {"COLUMN"}, {"Data Type"}, {"Type"}, + {"Column Size"}, {"Nullable value"}, {"IS_NULLABLE"}, {"IS_AUTOINCREMENT"}}; + if (!rs1.next()) { + fail(TestResource.getResource("R_resultSetEmpty")); + } else { + do { + assertTrue(!StringUtils.isEmpty(rs1.getString("TABLE_CAT")), form2.format(msgArgs2[0])); + assertTrue(!StringUtils.isEmpty(rs1.getString("TABLE_SCHEM")), form2.format(msgArgs2[1])); + assertTrue(!StringUtils.isEmpty(rs1.getString("TABLE_NAME")), form2.format(msgArgs2[2])); + assertTrue(!StringUtils.isEmpty(rs1.getString("COLUMN_NAME")), form2.format(msgArgs2[3])); + assertTrue(!StringUtils.isEmpty(rs1.getString("DATA_TYPE")), form2.format(msgArgs2[4])); + assertTrue(!StringUtils.isEmpty(rs1.getString("TYPE_NAME")), form2.format(msgArgs2[5])); + assertTrue(!StringUtils.isEmpty(rs1.getString("COLUMN_SIZE")), form2.format(msgArgs2[6])); + assertTrue(!StringUtils.isEmpty(rs1.getString("NULLABLE")), form2.format(msgArgs2[7])); // 11 + assertTrue(!StringUtils.isEmpty(rs1.getString("IS_NULLABLE")), form2.format(msgArgs2[8])); // 18 + assertTrue(!StringUtils.isEmpty(rs1.getString("IS_AUTOINCREMENT")), + form2.format(msgArgs2[9])); // 22 + } while (rs1.next()); + } + } + /** * We can improve this test case by following manner: *
    @@ -592,7 +612,7 @@ public void testGetMaxConnections() throws SQLException { public static void setupTable() throws SQLException { try (Statement stmt = connection.createStatement()) { stmt.execute("CREATE TABLE " + AbstractSQLGenerator.escapeIdentifier(tableName) - + " (col1 int NOT NULL, col2 varchar(200), col3 decimal(15,2))"); + + " ([col_1] int NOT NULL, [col%2] varchar(200), [col[3] decimal(15,2))"); stmt.execute("CREATE FUNCTION " + AbstractSQLGenerator.escapeIdentifier(functionName) + " (@p1 INT, @p2 INT) RETURNS INT AS BEGIN DECLARE @result INT; SET @result = @p1 + @p2; RETURN @result; END"); } From 86914b9c2417f19ed58d757ca4cc4d994eff4104 Mon Sep 17 00:00:00 2001 From: ulvii Date: Wed, 2 Oct 2019 15:09:04 -0700 Subject: [PATCH 09/11] Feature | Introduce JAVA 13 Support (#1151) * JDK 13 | Remove jre12 from pom file * JDK 13 | Update Gradle build file * JDK 13 | Update Azure-pipelines --- azure-pipelines.yml | 12 ++++++------ build.gradle | 12 ++++++------ pom.xml | 8 ++++---- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 45647c1e6..69983369f 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -35,14 +35,14 @@ jobs: inputs: secureFile: 'pkcs12_truststore' - task: Maven@3 - displayName: 'Maven build jre12' + displayName: 'Maven build jre13' inputs: mavenPomFile: 'pom.xml' - goals: 'clean -Dmssql_jdbc_test_connection_properties=jdbc:sqlserver://$(Target_SQL)$(server_domain);$(database);$(user);$(password); install -Pjre12 -DuserNTLM=$(userNTLM) -DpasswordNTLM=$(passwordNTLM) -DdomainNTLM=$(domainNTLM) -DexcludedGroups=$(Ex_Groups) -Dpkcs12_truststore_password=$(pkcs12_truststore_password) -Dpkcs12_truststore=$(pkcs12_truststore.secureFilePath)' + goals: 'clean -Dmssql_jdbc_test_connection_properties=jdbc:sqlserver://$(Target_SQL)$(server_domain);$(database);$(user);$(password); install -Pjre13 -DuserNTLM=$(userNTLM) -DpasswordNTLM=$(passwordNTLM) -DdomainNTLM=$(domainNTLM) -DexcludedGroups=$(Ex_Groups) -Dpkcs12_truststore_password=$(pkcs12_truststore_password) -Dpkcs12_truststore=$(pkcs12_truststore.secureFilePath)' testResultsFiles: '**/TEST-*.xml' - testRunTitle: 'Maven build jre12' + testRunTitle: 'Maven build jre13' javaHomeOption: Path - jdkDirectory: $(JDK12) + jdkDirectory: $(JDK13) - task: Maven@3 displayName: 'Maven build jre11' inputs: @@ -51,7 +51,7 @@ jobs: testResultsFiles: '**/TEST-*.xml' testRunTitle: 'Maven build jre11' javaHomeOption: Path - jdkDirectory: $(JDK12) + jdkDirectory: $(JDK13) - task: Maven@3 displayName: 'Maven build jre8' inputs: @@ -60,4 +60,4 @@ jobs: testResultsFiles: '**/TEST-*.xml' testRunTitle: 'Maven build jre8' javaHomeOption: Path - jdkDirectory: $(JDK12) + jdkDirectory: $(JDK13) diff --git a/build.gradle b/build.gradle index 2deb2a598..de92ea8a7 100644 --- a/build.gradle +++ b/build.gradle @@ -3,8 +3,8 @@ **************************************************************** * Instruction for Building JDBC Driver: * For building particular version of the driver, use commands: - * jre12 - - PS> gradle build - PS> gradle build -PbuildProfile=jre12 + * jre13 - - PS> gradle build + PS> gradle build -PbuildProfile=jre13 * jre11 - - PS> gradle build -PbuildProfile=jre11 * jre8 - - PS> gradle build -PbuildProfile=jre8 * @@ -35,17 +35,17 @@ test { } } -if (!hasProperty('buildProfile') || (hasProperty('buildProfile') && buildProfile == "jre12")){ +if (!hasProperty('buildProfile') || (hasProperty('buildProfile') && buildProfile == "jre13")){ - jreVersion = "jre12" + jreVersion = "jre13" excludedFile = 'com/microsoft/sqlserver/jdbc/SQLServerJdbc42.java' jar { manifest { attributes 'Automatic-Module-Name': 'com.microsoft.sqlserver.jdbc' } } - sourceCompatibility = 12 - targetCompatibility = 12 + sourceCompatibility = 13 + targetCompatibility = 13 } if (hasProperty('buildProfile') && buildProfile == "jre11"){ diff --git a/pom.xml b/pom.xml index 0c4c4a729..9bc532a13 100644 --- a/pom.xml +++ b/pom.xml @@ -274,12 +274,12 @@ - jre12 + jre13 true - ${project.artifactId}-${project.version}.jre12-preview + ${project.artifactId}-${project.version}.jre13-preview org.apache.maven.plugins @@ -289,8 +289,8 @@ **/com/microsoft/sqlserver/jdbc/SQLServerJdbc42.java - 12 - 12 + 13 + 13 From e441f291e4263e2e52a0a39f10d5c18ccf5340ec Mon Sep 17 00:00:00 2001 From: ulvii Date: Thu, 3 Oct 2019 11:07:42 -0700 Subject: [PATCH 10/11] Fix | Formatted files --- .../jdbc/ISQLServerCallableStatement.java | 24 +++++++------------ .../jdbc/ISQLServerPreparedStatement.java | 9 +++---- .../sqlserver/jdbc/ISQLServerResultSet.java | 21 ++++++---------- .../sqlserver/jdbc/SQLServerBulkCopy.java | 4 ++-- 4 files changed, 20 insertions(+), 38 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerCallableStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerCallableStatement.java index be5de1e61..9ad83fc86 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerCallableStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerCallableStatement.java @@ -559,8 +559,7 @@ void setDateTimeOffset(String parameterName, microsoft.sql.DateTimeOffset value, * method is called on a closed CallableStatement * @see #getTime */ - void setTime(String parameterName, java.sql.Time value, int scale, - boolean forceEncrypt) throws SQLServerException; + void setTime(String parameterName, java.sql.Time value, int scale, boolean forceEncrypt) throws SQLServerException; /** * Sets the designated parameter to the given java.sql.Timestamp value. The driver converts this to an @@ -592,8 +591,7 @@ void setTime(String parameterName, java.sql.Time value, int scale, * if parameterName does not correspond to a named parameter; if a database access error occurs or this * method is called on a closed CallableStatement */ - void setDateTime(String parameterName, java.sql.Timestamp value, - boolean forceEncrypt) throws SQLServerException; + void setDateTime(String parameterName, java.sql.Timestamp value, boolean forceEncrypt) throws SQLServerException; /** * Sets the designated parameter to the given java.sql.Timestamp value. The driver converts this to an @@ -796,8 +794,7 @@ void setSmallDateTime(String parameterName, java.sql.Timestamp value, * if parameterName does not correspond to a named parameter; if a database access error occurs or this * method is called on a closed CallableStatement */ - void setBigDecimal(String parameterName, BigDecimal value, int precision, - int scale) throws SQLServerException; + void setBigDecimal(String parameterName, BigDecimal value, int precision, int scale) throws SQLServerException; /** * Sets the designated parameter to the given java.math.BigDecimal value. The driver converts this to @@ -942,8 +939,7 @@ void setBigDecimal(String parameterName, BigDecimal value, int precision, int sc * @throws SQLServerException * when an error occurs */ - void setStructured(String parameterName, String tvpName, - SQLServerDataTable tvpDataTable) throws SQLServerException; + void setStructured(String parameterName, String tvpName, SQLServerDataTable tvpDataTable) throws SQLServerException; /** * Populates a table valued parameter passed to a stored procedure with a ResultSet retrieved from another table @@ -957,8 +953,7 @@ void setStructured(String parameterName, String tvpName, * @throws SQLServerException * when an error occurs */ - void setStructured(String parameterName, String tvpName, - java.sql.ResultSet tvpResultSet) throws SQLServerException; + void setStructured(String parameterName, String tvpName, java.sql.ResultSet tvpResultSet) throws SQLServerException; /** * Populates a table valued parameter passed to a stored procedure with an ISQLServerDataRecord object. @@ -1017,8 +1012,7 @@ void registerOutParameter(String parameterName, SQLType sqlType, int precision, * @throws SQLServerException * If any errors occur. */ - void registerOutParameter(int parameterIndex, SQLType sqlType, int precision, - int scale) throws SQLServerException; + void registerOutParameter(int parameterIndex, SQLType sqlType, int precision, int scale) throws SQLServerException; /** * Registers the parameter in ordinal position index to be of JDBC type sqlType. All OUT parameters must be @@ -1039,8 +1033,7 @@ void registerOutParameter(int parameterIndex, SQLType sqlType, int precision, * @throws SQLServerException * If any errors occur. */ - void registerOutParameter(int parameterIndex, int sqlType, int precision, - int scale) throws SQLServerException; + void registerOutParameter(int parameterIndex, int sqlType, int precision, int scale) throws SQLServerException; /** * Registers the parameter in ordinal position index to be of JDBC type sqlType. All OUT parameters must be @@ -1061,8 +1054,7 @@ void registerOutParameter(int parameterIndex, int sqlType, int precision, * @throws SQLServerException * If any errors occur. */ - void registerOutParameter(String parameterName, int sqlType, int precision, - int scale) throws SQLServerException; + void registerOutParameter(String parameterName, int sqlType, int precision, int scale) throws SQLServerException; /** * Sets the value of the designated parameter with the given object. diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerPreparedStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerPreparedStatement.java index 3460cc714..647185163 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerPreparedStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerPreparedStatement.java @@ -564,8 +564,7 @@ void setTimestamp(int parameterIndex, java.sql.Timestamp x, int scale, * @throws SQLServerException * when an error occurs */ - void setDateTimeOffset(int parameterIndex, microsoft.sql.DateTimeOffset x, - int scale) throws SQLServerException; + void setDateTimeOffset(int parameterIndex, microsoft.sql.DateTimeOffset x, int scale) throws SQLServerException; /** * Sets the designated parameter to the given microsoft.sql.DatetimeOffset value. @@ -640,8 +639,7 @@ void setDateTimeOffset(int parameterIndex, microsoft.sql.DateTimeOffset x, int s * @throws SQLServerException * when an error occurs */ - void setSmallDateTime(int parameterIndex, java.sql.Timestamp x, - boolean forceEncrypt) throws SQLServerException; + void setSmallDateTime(int parameterIndex, java.sql.Timestamp x, boolean forceEncrypt) throws SQLServerException; /** * Sets the data table to populates a table valued parameter. @@ -655,8 +653,7 @@ void setSmallDateTime(int parameterIndex, java.sql.Timestamp x, * @throws SQLServerException * when an error occurs */ - void setStructured(int parameterIndex, String tvpName, - SQLServerDataTable tvpDataTable) throws SQLServerException; + void setStructured(int parameterIndex, String tvpName, SQLServerDataTable tvpDataTable) throws SQLServerException; /** * Sets the result set to populate a table-valued parameter. diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerResultSet.java b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerResultSet.java index da26859ff..fe2c9edf0 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerResultSet.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerResultSet.java @@ -836,8 +836,7 @@ void updateBigDecimal(int index, BigDecimal x, Integer precision, Integer scale, * @throws SQLServerException * when an error occurs */ - void updateTimestamp(int index, java.sql.Timestamp x, int scale, - boolean forceEncrypt) throws SQLServerException; + void updateTimestamp(int index, java.sql.Timestamp x, int scale, boolean forceEncrypt) throws SQLServerException; /** * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to @@ -887,8 +886,7 @@ void updateTimestamp(int index, java.sql.Timestamp x, int scale, * @throws SQLServerException * when an error occurs */ - void updateDateTime(int index, java.sql.Timestamp x, Integer scale, - boolean forceEncrypt) throws SQLServerException; + void updateDateTime(int index, java.sql.Timestamp x, Integer scale, boolean forceEncrypt) throws SQLServerException; /** * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to @@ -953,8 +951,7 @@ void updateSmallDateTime(int index, java.sql.Timestamp x, Integer scale, * @throws SQLServerException * when an error occurs */ - void updateDateTimeOffset(int index, microsoft.sql.DateTimeOffset x, - Integer scale) throws SQLServerException; + void updateDateTimeOffset(int index, microsoft.sql.DateTimeOffset x, Integer scale) throws SQLServerException; /** * Updates the value of the column specified to the DateTimeOffset Class value, given a zero-based column ordinal. @@ -1029,8 +1026,7 @@ void updateDateTimeOffset(int index, microsoft.sql.DateTimeOffset x, Integer sca * @throws SQLServerException * when an error occurs */ - void updateObject(int index, Object x, int precision, int scale, - boolean forceEncrypt) throws SQLServerException; + void updateObject(int index, Object x, int precision, int scale, boolean forceEncrypt) throws SQLServerException; /** * Updates the designated column with a boolean value. The updater methods are used to update column @@ -1194,8 +1190,7 @@ void updateObject(int index, Object x, int precision, int scale, * @throws SQLServerException * If any errors occur. */ - void updateBigDecimal(String columnName, BigDecimal x, Integer precision, - Integer scale) throws SQLServerException; + void updateBigDecimal(String columnName, BigDecimal x, Integer precision, Integer scale) throws SQLServerException; /** * Updates the designated column with a java.sql.BigDecimal value. The updater methods are used to @@ -1311,8 +1306,7 @@ void updateBigDecimal(String columnName, BigDecimal x, Integer precision, Intege * @throws SQLServerException * If any errors occur. */ - void updateTime(String columnName, java.sql.Time x, int scale, - boolean forceEncrypt) throws SQLServerException; + void updateTime(String columnName, java.sql.Time x, int scale, boolean forceEncrypt) throws SQLServerException; /** * Updates the designated column with a java.sql.Timestamp value. The updater methods are used to @@ -1465,8 +1459,7 @@ void updateSmallDateTime(String columnName, java.sql.Timestamp x, int scale, * @throws SQLServerException * If any errors occur. */ - void updateDateTimeOffset(String columnName, microsoft.sql.DateTimeOffset x, - int scale) throws SQLServerException; + void updateDateTimeOffset(String columnName, microsoft.sql.DateTimeOffset x, int scale) throws SQLServerException; /** * Updates the value of the column specified to the DateTimeOffset Class value, given a column name. diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java index 900487e64..0f736c240 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java @@ -2074,7 +2074,7 @@ else if (null != sourceCryptoMeta) { tdsWriter.writeDouble((float) colValue); } break; - + case java.sql.Types.DOUBLE: if (null == colValue) { writeNullToTdsWriter(tdsWriter, bulkJdbcType, isStreaming); @@ -3362,7 +3362,7 @@ private byte[] normalizedValue(JDBCType destJdbcType, Object value, JDBCType src Float floatValue = (value instanceof String) ? Float.parseFloat((String) value) : (Float) value; return ByteBuffer.allocate((Float.SIZE / Byte.SIZE)).order(ByteOrder.LITTLE_ENDIAN) .putFloat(floatValue).array(); - + case FLOAT: case DOUBLE: Double doubleValue = (value instanceof String) ? Double.parseDouble((String) value) From 5bab43e99454fa530dcb3a05b8c0e64d29593e19 Mon Sep 17 00:00:00 2001 From: ulvii Date: Thu, 3 Oct 2019 12:52:55 -0700 Subject: [PATCH 11/11] Add | Adding @deprecated tag to ISQLServerBulkRecord --- .../sqlserver/jdbc/ISQLServerBulkRecord.java | 4 ++++ .../sqlserver/jdbc/ISQLServerConnection.java | 2 +- .../sqlserver/jdbc/ISQLServerResultSet.java | 16 ++++++++-------- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerBulkRecord.java b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerBulkRecord.java index 01582cc74..7767cf868 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerBulkRecord.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerBulkRecord.java @@ -13,7 +13,11 @@ * SQLServerBulkCopy class to write the data to SQL Server tables. * * This interface is implemented by {@link SQLServerBulkRecord} Class + * + * @deprecated as of 7.5.0, because the interface contains methods which are not called as part of actual bulk copy + * process. Use {@link ISQLServerBulkData}} instead. */ +@Deprecated public interface ISQLServerBulkRecord extends ISQLServerBulkData { /** * Returns whether the column represents an identity column. diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection.java index 0a02624e5..58e5e5f75 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerConnection.java @@ -18,7 +18,7 @@ public interface ISQLServerConnection extends java.sql.Connection { // Transaction types. // TRANSACTION_SNAPSHOT corresponds to -> SET TRANSACTION ISOLATION LEVEL SNAPSHOT - final static int TRANSACTION_SNAPSHOT = 0x1000; + int TRANSACTION_SNAPSHOT = 0x1000; /** * Returns the connection ID of the most recent connection attempt, regardless of whether the attempt succeeded or diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerResultSet.java b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerResultSet.java index fe2c9edf0..068d3cb5f 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerResultSet.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/ISQLServerResultSet.java @@ -17,16 +17,16 @@ */ public interface ISQLServerResultSet extends java.sql.ResultSet { - static final int TYPE_SS_DIRECT_FORWARD_ONLY = 2003; // TYPE_FORWARD_ONLY + 1000 - static final int TYPE_SS_SERVER_CURSOR_FORWARD_ONLY = 2004; // TYPE_FORWARD_ONLY + 1001 - static final int TYPE_SS_SCROLL_STATIC = 1004; // TYPE_SCROLL_INSENSITIVE - static final int TYPE_SS_SCROLL_KEYSET = 1005; // TYPE_SCROLL_SENSITIVE - static final int TYPE_SS_SCROLL_DYNAMIC = 1006; // TYPE_SCROLL_SENSITIVE + 1 + int TYPE_SS_DIRECT_FORWARD_ONLY = 2003; // TYPE_FORWARD_ONLY + 1000 + int TYPE_SS_SERVER_CURSOR_FORWARD_ONLY = 2004; // TYPE_FORWARD_ONLY + 1001 + int TYPE_SS_SCROLL_STATIC = 1004; // TYPE_SCROLL_INSENSITIVE + int TYPE_SS_SCROLL_KEYSET = 1005; // TYPE_SCROLL_SENSITIVE + int TYPE_SS_SCROLL_DYNAMIC = 1006; // TYPE_SCROLL_SENSITIVE + 1 /* SQL Server concurrency values */ - static final int CONCUR_SS_OPTIMISTIC_CC = 1008; // CONCUR_UPDATABLE - static final int CONCUR_SS_SCROLL_LOCKS = 1009; // CONCUR_UPDATABLE + 1 - static final int CONCUR_SS_OPTIMISTIC_CCVAL = 1010; // CONCUR_UPDATABLE + 2 + int CONCUR_SS_OPTIMISTIC_CC = 1008; // CONCUR_UPDATABLE + int CONCUR_SS_SCROLL_LOCKS = 1009; // CONCUR_UPDATABLE + 1 + int CONCUR_SS_OPTIMISTIC_CCVAL = 1010; // CONCUR_UPDATABLE + 2 /** * Returns the value of the designated column in the current row of this ResultSet object as a