From 66ee80888d7ab79a844424ae8c3454f39c13fbb9 Mon Sep 17 00:00:00 2001 From: Peter Bae Date: Fri, 29 Jan 2021 14:24:33 -0800 Subject: [PATCH 1/5] Merge dev to master for 9.2.0 release (#1506) Merge dev to master for 9.2.0 release (#1506) --- CHANGELOG.md | 7 +++ README.md | 10 ++-- azure-pipelines.yml | 2 +- build.gradle | 4 +- mssql-jdbc_auth_LICENSE | 2 +- pom.xml | 4 +- .../microsoft/sqlserver/jdbc/IOBuffer.java | 21 +++---- .../jdbc/KeyVaultTokenCredential.java | 12 ++-- .../sqlserver/jdbc/SQLJdbcVersion.java | 6 +- .../sqlserver/jdbc/SQLServerBulkCopy.java | 54 ++++++++---------- ...ColumnEncryptionAzureKeyVaultProvider.java | 14 +++-- .../sqlserver/jdbc/SQLServerConnection.java | 56 +++++++++---------- .../sqlserver/jdbc/SQLServerMSAL4JUtils.java | 17 +++--- src/samples/adaptive/pom.xml | 8 +-- src/samples/alwaysencrypted/pom.xml | 8 +-- .../pom.xml | 8 +-- src/samples/connections/pom.xml | 8 +-- src/samples/constrained/pom.xml | 8 +-- src/samples/dataclassification/pom.xml | 8 +-- src/samples/datatypes/pom.xml | 8 +-- src/samples/resultsets/pom.xml | 8 +-- src/samples/sparse/pom.xml | 8 +-- .../sqlserver/jdbc/MaxResultBufferTest.java | 4 ++ .../sqlserver/jdbc/TestResource.java | 2 +- .../microsoft/sqlserver/jdbc/TestUtils.java | 19 +++++++ .../bulkCopy/BulkCopyColumnMappingTest.java | 39 +++++++++++++ .../DatabaseMetaDataTest.java | 40 ++++++++++--- .../jdbc/fedauth/ErrorMessageTest.java | 9 +-- 28 files changed, 240 insertions(+), 154 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 217f2d7a8b..869b9de15b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) +## [9.2.0] Stable Release +### Added +- Added logic to handle multi-factor authentication timeouts during ActiveDirectoryInteractive authentication [#1488](https://github.com/microsoft/mssql-jdbc/pull/1488) + +### Fixed issues +- Fixed an issue with high memory allocation during bulk copy [#1475](https://github.com/microsoft/mssql-jdbc/pull/1475) + ## [9.1.1] Preview Release ### Added - Added maxResultBuffer connection property [1431](https://github.com/microsoft/mssql-jdbc/pull/1431) diff --git a/README.md b/README.md index c08f6b5c7c..73f21b934e 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ We're now on the Maven Central Repository. Add the following to your POM file to com.microsoft.sqlserver mssql-jdbc - 8.4.1.jre14 + 9.2.0.jre15 ``` The driver can be downloaded from the [Microsoft Download Center](https://go.microsoft.com/fwlink/?linkid=868287). @@ -127,7 +127,7 @@ Projects that require either of the two features need to explicitly declare the com.microsoft.sqlserver mssql-jdbc - 9.1.1.jre15-preview + 9.2.0.jre15-preview compile @@ -145,7 +145,7 @@ Projects that require either of the two features need to explicitly declare the com.microsoft.sqlserver mssql-jdbc - 9.1.1.jre15-preview + 9.2.0.jre15-preview compile @@ -172,7 +172,7 @@ When setting 'useFmtOnly' property to 'true' for establishing a connection or cr com.microsoft.sqlserver mssql-jdbc - 9.1.1.jre15-preview + 9.2.0.jre15-preview @@ -212,7 +212,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. ### Version conventions -Starting with 6.0, stable versions have an even minor version. For example, 6.0, 6.2, 6.4, 7.0, 7.2, 7.4, 8.2, 8.4. Preview versions have an odd minor version. For example, 6.1, 6.3, 6.5, 7.1, 7.3, 8.1 and so on +Starting with 6.0, stable versions have an even minor version. For example, 6.0, 6.2, 6.4, 7.0, 7.2, 7.4, 8.2, 8.4, 9.2. Preview versions have an odd minor version. For example, 6.1, 6.3, 6.5, 7.1, 7.3, 8.1 and so on ## Contributors Special thanks to everyone who has contributed to the project. diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 977851c76d..4cb42ff519 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -38,7 +38,7 @@ jobs: name: authDLL displayName: 'Download mssql-jdbc_auth DLL' inputs: - secureFile: 'mssql-jdbc_auth-9.1.1.x64-preview.dll' + secureFile: 'mssql-jdbc_auth-9.2.0.x64.dll' - task: Maven@3 displayName: 'Maven build jre15' inputs: diff --git a/build.gradle b/build.gradle index dc223da6ba..02fa0916d0 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,7 @@ apply plugin: 'java' -version = '9.1.1' +version = '9.2.0' def jreVersion = "" def testOutputDir = file("build/classes/java/test") def archivesBaseName = 'mssql-jdbc' @@ -75,7 +75,7 @@ if(hasProperty('buildProfile') && buildProfile == "jre8") { } } -jar.archiveFileName = "${archivesBaseName}-${version}.${jreVersion}-preview.jar" +jar.archiveFileName = "${archivesBaseName}-${version}.${jreVersion}.jar" jar { manifest { attributes 'Title': "Microsoft JDBC Driver ${version} for SQL Server", diff --git a/mssql-jdbc_auth_LICENSE b/mssql-jdbc_auth_LICENSE index f35f9d80e1..29dd404a39 100644 --- a/mssql-jdbc_auth_LICENSE +++ b/mssql-jdbc_auth_LICENSE @@ -1,5 +1,5 @@ MICROSOFT SOFTWARE LICENSE TERMS -MICROSOFT JDBC DRIVER 9.1 FOR SQL SERVER +MICROSOFT JDBC DRIVER 9.2 FOR SQL SERVER These license terms are an agreement between you and Microsoft Corporation (or one of its affiliates). They apply to the software named above and any Microsoft services or software updates (except to the extent such services or updates are accompanied by new or additional terms, in which case those different terms apply prospectively and do not alter your or Microsoft’s rights relating to pre-updated software or services). IF YOU COMPLY WITH THESE LICENSE TERMS, YOU HAVE THE RIGHTS BELOW. BY USING THE SOFTWARE, YOU ACCEPT THESE TERMS. diff --git a/pom.xml b/pom.xml index a93874f604..0eed1fe2ad 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.microsoft.sqlserver mssql-jdbc - 9.1.1 + 9.2.0 jar Microsoft JDBC Driver for SQL Server @@ -57,7 +57,7 @@ xSQLv12,xSQLv15,NTLM,MSI,reqExternalSetup,clientCertAuth,fedAuth - -preview + 4.2.1 diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java index 4e0752aeb2..87bd5e532e 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java @@ -3151,6 +3151,11 @@ boolean isEOMSent() { private ByteBuffer socketBuffer; private ByteBuffer logBuffer; + // Intermediate arrays + // It is assumed, startMessage is called before use, to alloc arrays + private char[] streamCharBuffer; + private byte[] streamByteBuffer; + private CryptoMetadata cryptoMeta = null; TDSWriter(TDSChannel tdsChannel, SQLServerConnection con) { @@ -3247,6 +3252,8 @@ void startMessage(TDSCommand command, byte tdsMessageType) throws SQLServerExcep stagingBuffer = ByteBuffer.allocate(negotiatedPacketSize).order(ByteOrder.LITTLE_ENDIAN); logBuffer = ByteBuffer.allocate(negotiatedPacketSize).order(ByteOrder.LITTLE_ENDIAN); currentPacketSize = negotiatedPacketSize; + streamCharBuffer = new char[2 * currentPacketSize]; + streamByteBuffer = new byte[4 * currentPacketSize]; } ((Buffer) socketBuffer).position(((Buffer) socketBuffer).limit()); @@ -4015,10 +4022,6 @@ void writeNonUnicodeReader(Reader reader, long advertisedLength, boolean isDestB assert DataTypes.UNKNOWN_STREAM_LENGTH == advertisedLength || advertisedLength >= 0; long actualLength = 0; - char[] streamCharBuffer = new char[currentPacketSize]; - // The unicode version, writeReader() allocates a byte buffer that is 4 times the currentPacketSize, not sure - // why. - byte[] streamByteBuffer = new byte[currentPacketSize]; int charsRead = 0; int charsToWrite; int bytesToWrite; @@ -4026,10 +4029,10 @@ void writeNonUnicodeReader(Reader reader, long advertisedLength, boolean isDestB do { // Read in next chunk - for (charsToWrite = 0; -1 != charsRead && charsToWrite < streamCharBuffer.length; + for (charsToWrite = 0; -1 != charsRead && charsToWrite < currentPacketSize; charsToWrite += charsRead) { try { - charsRead = reader.read(streamCharBuffer, charsToWrite, streamCharBuffer.length - charsToWrite); + charsRead = reader.read(streamCharBuffer, charsToWrite, currentPacketSize - charsToWrite); } catch (IOException e) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_errorReadingStream")); Object[] msgArgs = {e.toString()}; @@ -4040,7 +4043,7 @@ void writeNonUnicodeReader(Reader reader, long advertisedLength, boolean isDestB break; // Check for invalid bytesRead returned from Reader.read - if (charsRead < 0 || charsRead > streamCharBuffer.length - charsToWrite) { + if (charsRead < 0 || charsRead > currentPacketSize - charsToWrite) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_errorReadingStream")); Object[] msgArgs = {SQLServerException.getErrString("R_streamReadReturnedInvalidValue")}; error(form.format(msgArgs), SQLState.DATA_EXCEPTION_NOT_SPECIFIC, DriverError.NOT_SET); @@ -4070,7 +4073,7 @@ void writeNonUnicodeReader(Reader reader, long advertisedLength, boolean isDestB if (0 != charsToWrite) bytesToWrite = charsToWrite / 2; - streamString = new String(streamCharBuffer); + streamString = new String(streamCharBuffer, 0, currentPacketSize); byte[] bytes = ParameterUtils.HexToBin(streamString.trim()); writeInt(bytesToWrite); writeBytes(bytes, 0, bytesToWrite); @@ -4096,8 +4099,6 @@ void writeReader(Reader reader, long advertisedLength, boolean writeChunkSizes) assert DataTypes.UNKNOWN_STREAM_LENGTH == advertisedLength || advertisedLength >= 0; long actualLength = 0; - char[] streamCharBuffer = new char[2 * currentPacketSize]; - byte[] streamByteBuffer = new byte[4 * currentPacketSize]; int charsRead = 0; int charsToWrite; do { diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/KeyVaultTokenCredential.java b/src/main/java/com/microsoft/sqlserver/jdbc/KeyVaultTokenCredential.java index 06c09be463..3bedc589e0 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/KeyVaultTokenCredential.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/KeyVaultTokenCredential.java @@ -29,6 +29,8 @@ */ @Immutable class KeyVaultTokenCredential implements TokenCredential { + private static final String NULL_VALUE = "R_NullValue"; + private final String clientId; private final String clientSecret; private final SQLServerKeyVaultAuthenticationCallback authenticationCallback; @@ -48,13 +50,13 @@ class KeyVaultTokenCredential implements TokenCredential { */ KeyVaultTokenCredential(String clientId, String clientSecret) throws SQLServerException { if (null == clientId || clientId.isEmpty()) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_NullValue")); + MessageFormat form = new MessageFormat(SQLServerException.getErrString(NULL_VALUE)); Object[] msgArgs1 = {"Client ID"}; throw new SQLServerException(form.format(msgArgs1), null); } if (null == clientSecret || clientSecret.isEmpty()) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_NullValue")); + MessageFormat form = new MessageFormat(SQLServerException.getErrString(NULL_VALUE)); Object[] msgArgs1 = {"Client Secret"}; throw new SQLServerException(form.format(msgArgs1), null); } @@ -116,19 +118,19 @@ KeyVaultTokenCredential setAuthorization(String authorization) { */ private ConfidentialClientApplication getConfidentialClientApplication() { if (null == clientId) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_NullValue")); + MessageFormat form = new MessageFormat(SQLServerException.getErrString(NULL_VALUE)); Object[] msgArgs1 = {"Client ID"}; throw new IllegalArgumentException(form.format(msgArgs1), null); } if (null == authorization) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_NullValue")); + MessageFormat form = new MessageFormat(SQLServerException.getErrString(NULL_VALUE)); Object[] msgArgs1 = {"Authorization"}; throw new IllegalArgumentException(form.format(msgArgs1), null); } if (null == clientSecret) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_NullValue")); + MessageFormat form = new MessageFormat(SQLServerException.getErrString(NULL_VALUE)); Object[] msgArgs1 = {"Client Secret"}; throw new IllegalArgumentException(form.format(msgArgs1), null); } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLJdbcVersion.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLJdbcVersion.java index 207f24bd92..11edf4db30 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLJdbcVersion.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLJdbcVersion.java @@ -7,13 +7,13 @@ final class SQLJdbcVersion { static final int major = 9; - static final int minor = 1; - static final int patch = 1; + static final int minor = 2; + static final int patch = 0; static final int build = 0; /* * Used to load mssql-jdbc_auth DLL. * 1. Set to "-preview" for preview release. * 2. Set to "" (empty String) for official release. */ - static final String releaseExt = "-preview"; + static final String releaseExt = ""; } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java index 53f6c27c05..13d14465b2 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java @@ -369,7 +369,7 @@ public void addColumnMapping(int sourceColumn, String destinationColumn) throws } else if (null == destinationColumn || destinationColumn.isEmpty()) { throwInvalidArgument("destinationColumn"); } - columnMappings.add(new ColumnMapping(sourceColumn, destinationColumn.trim())); + columnMappings.add(new ColumnMapping(sourceColumn, destinationColumn)); loggerExternal.exiting(loggerClassName, "addColumnMapping"); } @@ -396,7 +396,7 @@ public void addColumnMapping(String sourceColumn, int destinationColumn) throws } else if (null == sourceColumn || sourceColumn.isEmpty()) { throwInvalidArgument("sourceColumn"); } - columnMappings.add(new ColumnMapping(sourceColumn.trim(), destinationColumn)); + columnMappings.add(new ColumnMapping(sourceColumn, destinationColumn)); loggerExternal.exiting(loggerClassName, "addColumnMapping"); } @@ -422,7 +422,7 @@ public void addColumnMapping(String sourceColumn, String destinationColumn) thro } else if (null == destinationColumn || destinationColumn.isEmpty()) { throwInvalidArgument("destinationColumn"); } - columnMappings.add(new ColumnMapping(sourceColumn.trim(), destinationColumn.trim())); + columnMappings.add(new ColumnMapping(sourceColumn, destinationColumn)); loggerExternal.exiting(loggerClassName, "addColumnMapping"); } @@ -929,11 +929,10 @@ private void writeTypeInfo(TDSWriter tdsWriter, int srcJdbcType, int srcScale, i case java.sql.Types.NUMERIC: case java.sql.Types.DECIMAL: /* - * SQL Server allows the insertion of decimal and numeric into a money (and smallmoney) column, - * but Azure DW only accepts money types for money column. - * To make the code compatible against both SQL Server and Azure DW, always send decimal and - * numeric as money/smallmoney if the destination column is money/smallmoney - * and the source is decimal/numeric. + * SQL Server allows the insertion of decimal and numeric into a money (and smallmoney) column, but + * Azure DW only accepts money types for money column. To make the code compatible against both SQL + * Server and Azure DW, always send decimal and numeric as money/smallmoney if the destination column is + * money/smallmoney and the source is decimal/numeric. */ if (destSSType == SSType.MONEY) { tdsWriter.writeByte(TDSType.MONEYN.byteValue()); @@ -944,7 +943,8 @@ private void writeTypeInfo(TDSWriter tdsWriter, int srcJdbcType, int srcScale, i tdsWriter.writeByte((byte) 4); break; } - byte byteType = (java.sql.Types.DECIMAL == srcJdbcType) ? TDSType.DECIMALN.byteValue() : TDSType.NUMERICN.byteValue(); + byte byteType = (java.sql.Types.DECIMAL == srcJdbcType) ? TDSType.DECIMALN.byteValue() + : TDSType.NUMERICN.byteValue(); tdsWriter.writeByte(byteType); tdsWriter.writeByte((byte) TDSWriter.BIGDECIMAL_MAX_LENGTH); // maximum length tdsWriter.writeByte((byte) srcPrecision); // unsigned byte @@ -1296,11 +1296,10 @@ private String getDestTypeFromSrcType(int srcColIndx, int destColIndx, return "smallmoney"; case java.sql.Types.DECIMAL: /* - * SQL Server allows the insertion of decimal and numeric into a money (and smallmoney) column, - * but Azure DW only accepts money types for money column. - * To make the code compatible against both SQL Server and Azure DW, always send decimal and - * numeric as money/smallmoney if the destination column is money/smallmoney - * and the source is decimal/numeric. + * SQL Server allows the insertion of decimal and numeric into a money (and smallmoney) column, but + * Azure DW only accepts money types for money column. To make the code compatible against both SQL + * Server and Azure DW, always send decimal and numeric as money/smallmoney if the destination column is + * money/smallmoney and the source is decimal/numeric. */ if (destSSType == SSType.MONEY) { return "money"; @@ -1842,8 +1841,7 @@ private void validateColumnMappings() throws SQLServerException { Set columnOrdinals = serverBulkData.getColumnOrdinals(); Iterator columnsIterator = columnOrdinals.iterator(); int j = 1; - outerWhileLoop: - while (columnsIterator.hasNext()) { + outerWhileLoop : while (columnsIterator.hasNext()) { int currentOrdinal = columnsIterator.next(); if (j != currentOrdinal) { /* @@ -2067,8 +2065,7 @@ else if (null != sourceCryptoMeta) { } else if (null != serverBulkData && connection.getSendTemporalDataTypesAsStringForBulkCopy()) { /* * Bulk copy from CSV and destination is not encrypted. In this case, we send the temporal types as varchar - * and - * SQL Server does the conversion. If destination is encrypted, then temporal types can not be sent as + * and SQL Server does the conversion. If destination is encrypted, then temporal types can not be sent as * varchar. */ switch (bulkJdbcType) { @@ -2219,10 +2216,9 @@ else if (null != sourceCryptoMeta) { } /* * SQL Server allows the insertion of decimal and numeric into a money (and smallmoney) column, - * but Azure DW only accepts money types for money column. - * To make the code compatible against both SQL Server and Azure DW, always send decimal and - * numeric as money/smallmoney if the destination column is money/smallmoney - * and the source is decimal/numeric. + * but Azure DW only accepts money types for money column. To make the code compatible against + * both SQL Server and Azure DW, always send decimal and numeric as money/smallmoney if the + * destination column is money/smallmoney and the source is decimal/numeric. */ if (destSSType == SSType.MONEY) { tdsWriter.writeMoney((BigDecimal) colValue, microsoft.sql.Types.MONEY); @@ -2490,14 +2486,12 @@ else if (4 >= bulkScale) tdsWriter.writeByte((byte) 0x05); if (colValue instanceof String) { /* - * if colValue is an instance of String, this means the data is coming from a CSV file. - * Time string is expected to come in with this pattern: hh:mm:ss[.nnnnnnn] - * First, look for the '.' character to determine if the String has the optional nanoseconds - * component. - * Next, create a java.sql.Time instance with the hh:mm:ss part we extracted, then set that - * time as the timestamp's time. - * Then, add the nanoseconds (optional, 0 if not provided) to the timestamp value. - * Finally, provide the timestamp value to writeTime method. + * if colValue is an instance of String, this means the data is coming from a CSV file. Time + * string is expected to come in with this pattern: hh:mm:ss[.nnnnnnn] First, look for the + * '.' character to determine if the String has the optional nanoseconds component. Next, + * create a java.sql.Time instance with the hh:mm:ss part we extracted, then set that time + * as the timestamp's time. Then, add the nanoseconds (optional, 0 if not provided) to the + * timestamp value. Finally, provide the timestamp value to writeTime method. */ java.sql.Timestamp ts = new java.sql.Timestamp(0); int nanos = 0; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java index d5f0f237ab..3b31812ac4 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java @@ -58,6 +58,8 @@ public class SQLServerColumnEncryptionAzureKeyVaultProvider extends SQLServerCol private static final int KEY_NAME_INDEX = 4; private static final int KEY_URL_SPLIT_LENGTH_WITH_VERSION = 6; private static final String KEY_URL_DELIMITER = "/"; + private static final String NULL_VALUE = "R_NullValue"; + private HttpPipeline keyVaultPipeline; private KeyVaultTokenCredential keyVaultTokenCredential; @@ -102,12 +104,12 @@ public String getName() { */ public SQLServerColumnEncryptionAzureKeyVaultProvider(String clientId, String clientKey) throws SQLServerException { if (null == clientId || clientId.isEmpty()) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_NullValue")); + MessageFormat form = new MessageFormat(SQLServerException.getErrString(NULL_VALUE)); Object[] msgArgs1 = {"Client ID"}; throw new SQLServerException(form.format(msgArgs1), null); } if (null == clientKey || clientKey.isEmpty()) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_NullValue")); + MessageFormat form = new MessageFormat(SQLServerException.getErrString(NULL_VALUE)); Object[] msgArgs1 = {"Client Key"}; throw new SQLServerException(form.format(msgArgs1), null); } @@ -141,7 +143,7 @@ public SQLServerColumnEncryptionAzureKeyVaultProvider(String clientId, String cl */ SQLServerColumnEncryptionAzureKeyVaultProvider(String clientId) throws SQLServerException { if (null == clientId || clientId.isEmpty()) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_NullValue")); + MessageFormat form = new MessageFormat(SQLServerException.getErrString(NULL_VALUE)); Object[] msgArgs1 = {"Client ID"}; throw new SQLServerException(form.format(msgArgs1), null); } @@ -160,7 +162,7 @@ public SQLServerColumnEncryptionAzureKeyVaultProvider(String clientId, String cl */ public SQLServerColumnEncryptionAzureKeyVaultProvider(TokenCredential tokenCredential) throws SQLServerException { if (null == tokenCredential) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_NullValue")); + MessageFormat form = new MessageFormat(SQLServerException.getErrString(NULL_VALUE)); Object[] msgArgs1 = {"Token Credential"}; throw new SQLServerException(form.format(msgArgs1), null); } @@ -183,7 +185,7 @@ public SQLServerColumnEncryptionAzureKeyVaultProvider(TokenCredential tokenCrede public SQLServerColumnEncryptionAzureKeyVaultProvider( SQLServerKeyVaultAuthenticationCallback authenticationCallback) throws SQLServerException { if (null == authenticationCallback) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_NullValue")); + MessageFormat form = new MessageFormat(SQLServerException.getErrString(NULL_VALUE)); Object[] msgArgs1 = {"SQLServerKeyVaultAuthenticationCallback"}; throw new SQLServerException(form.format(msgArgs1), null); } @@ -202,7 +204,7 @@ public SQLServerColumnEncryptionAzureKeyVaultProvider( */ private void setCredential(TokenCredential credential) throws SQLServerException { if (null == credential) { - MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_NullValue")); + MessageFormat form = new MessageFormat(SQLServerException.getErrString(NULL_VALUE)); Object[] msgArgs1 = {"Credential"}; throw new SQLServerException(form.format(msgArgs1), null); } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 0b65ff8a55..cca90a0a42 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -2366,6 +2366,10 @@ private void login(String primary, String primaryInstanceName, int primaryPortNu + " Timeout Unit Interval: " + timeoutUnitInterval); } + // Returns false if authenticationString is null + boolean isInteractive = SqlAuthentication.ActiveDirectoryInteractive.toString() + .equalsIgnoreCase(authenticationString); + // Initialize loop variables int attemptNumber = 0; @@ -2477,33 +2481,21 @@ private void login(String primary, String primaryInstanceName, int primaryPortNu } else break; // leave the while loop -- we've successfully connected } catch (SQLServerException sqlex) { - if ((SQLServerException.LOGON_FAILED == sqlex.getErrorCode()) // actual logon failed, i.e. bad password - || (SQLServerException.PASSWORD_EXPIRED == sqlex.getErrorCode()) // actual logon failed, i.e. - // password isExpired - || (SQLServerException.USER_ACCOUNT_LOCKED == sqlex.getErrorCode()) // actual logon failed, i.e. - // user account locked - || (SQLServerException.DRIVER_ERROR_INVALID_TDS == sqlex.getDriverErrorCode()) // invalid TDS - // received from - // server - || (SQLServerException.DRIVER_ERROR_SSL_FAILED == sqlex.getDriverErrorCode()) // failure - // negotiating SSL - || (SQLServerException.DRIVER_ERROR_INTERMITTENT_TLS_FAILED == sqlex.getDriverErrorCode()) // failure - // TLS1.2 - || (SQLServerException.DRIVER_ERROR_UNSUPPORTED_CONFIG == sqlex.getDriverErrorCode()) // unsupported - // configuration - // (e.g. - // Sphinx, - // invalid - // packet - // size, - // etc.) - || (SQLServerException.ERROR_SOCKET_TIMEOUT == sqlex.getDriverErrorCode()) // socket timeout - // ocurred - || timerHasExpired(timerExpire)// no more time to try again - || (state.equals(State.Connected) && !isDBMirroring) - // for non-dbmirroring cases, do not retry after tcp socket connection succeeds - ) { - + int errorCode = sqlex.getErrorCode(); + int driverErrorCode = sqlex.getDriverErrorCode(); + if (SQLServerException.LOGON_FAILED == errorCode // logon failed, ie bad password + || SQLServerException.PASSWORD_EXPIRED == errorCode // password expired + || SQLServerException.USER_ACCOUNT_LOCKED == errorCode // user account locked + || SQLServerException.DRIVER_ERROR_INVALID_TDS == driverErrorCode // invalid TDS + || SQLServerException.DRIVER_ERROR_SSL_FAILED == driverErrorCode // SSL failure + || SQLServerException.DRIVER_ERROR_INTERMITTENT_TLS_FAILED == driverErrorCode // TLS1.2 failure + || SQLServerException.DRIVER_ERROR_UNSUPPORTED_CONFIG == driverErrorCode // unsupported config + // (eg Sphinx, invalid + // packetsize, etc) + || SQLServerException.ERROR_SOCKET_TIMEOUT == driverErrorCode // socket timeout + || (timerHasExpired(timerExpire) && !isInteractive) // no time to try again and not interactive + // for non-dbmirroring cases, do not retry after tcp socket connection succeeds + || (state.equals(State.Connected) && !isDBMirroring)) { // close the connection and throw the error back close(); throw sqlex; @@ -2521,7 +2513,7 @@ private void login(String primary, String primaryInstanceName, int primaryPortNu // Check sleep interval to make sure we won't exceed the timeout // Do this in the catch block so we can re-throw the current exception long remainingMilliseconds = timerRemaining(timerExpire); - if (remainingMilliseconds <= sleepInterval) { + if (remainingMilliseconds <= sleepInterval && !isInteractive) { throw sqlex; } } @@ -2552,6 +2544,14 @@ private void login(String primary, String primaryInstanceName, int primaryPortNu intervalExpire = System.currentTimeMillis() + (timeoutUnitInterval * (attemptNumber + 1)); } else if (isDBMirroring) { intervalExpire = System.currentTimeMillis() + (timeoutUnitInterval * ((attemptNumber / 2) + 1)); + } else if (isInteractive) { + // Interactive auth may involve MFA which will take longer and timeout. Reset timeout and retry silently + timerStart = System.currentTimeMillis(); + timeout = SQLServerDriverIntProperty.LOGIN_TIMEOUT.getDefaultValue(); + timerTimeout = timeout * 1000L; // ConnectTimeout is in seconds, we need timer millis + timerExpire = timerStart + timerTimeout; + intervalExpire = timerStart + timeoutUnitInterval; + intervalExpireFullTimeout = timerStart + timerTimeout; } else if (useTnir) { long timeSlice = timeoutUnitInterval * (1 << attemptNumber); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerMSAL4JUtils.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerMSAL4JUtils.java index b1bd17ebbe..2f7933e936 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerMSAL4JUtils.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerMSAL4JUtils.java @@ -40,8 +40,9 @@ class SQLServerMSAL4JUtils { static final String REDIRECTURI = "http://localhost"; + private static final String SLASH_DEFAULT = "/.default"; - static final private java.util.logging.Logger logger = java.util.logging.Logger + private static final java.util.logging.Logger logger = java.util.logging.Logger .getLogger("com.microsoft.sqlserver.jdbc.SQLServerMSAL4JUtils"); static SqlFedAuthToken getSqlFedAuthToken(SqlFedAuthInfo fedAuthInfo, String user, String password, @@ -53,7 +54,7 @@ static SqlFedAuthToken getSqlFedAuthToken(SqlFedAuthInfo fedAuthInfo, String use .builder(ActiveDirectoryAuthentication.JDBC_FEDAUTH_CLIENT_ID).executorService(executorService) .authority(fedAuthInfo.stsurl).build(); final CompletableFuture future = pca.acquireToken(UserNamePasswordParameters - .builder(Collections.singleton(fedAuthInfo.spn + "/.default"), user, password.toCharArray()) + .builder(Collections.singleton(fedAuthInfo.spn + SLASH_DEFAULT), user, password.toCharArray()) .build()); final IAuthenticationResult authenticationResult = future.get(); @@ -71,10 +72,10 @@ static SqlFedAuthToken getSqlFedAuthTokenPrincipal(SqlFedAuthInfo fedAuthInfo, S String aadPrincipalSecret, String authenticationString) throws SQLServerException { ExecutorService executorService = Executors.newSingleThreadExecutor(); try { - String defaultScopeSuffix = "/.default"; + String defaultScopeSuffix = SLASH_DEFAULT; String scope = fedAuthInfo.spn.endsWith(defaultScopeSuffix) ? fedAuthInfo.spn : fedAuthInfo.spn + defaultScopeSuffix; - Set scopes = new HashSet(); + Set scopes = new HashSet<>(); scopes.add(scope); IClientCredential credential = ClientCredentialFactory.createFromSecret(aadPrincipalSecret); ConfidentialClientApplication clientApplication = ConfidentialClientApplication @@ -114,7 +115,7 @@ static SqlFedAuthToken getSqlFedAuthTokenIntegrated(SqlFedAuthInfo fedAuthInfo, .authority(fedAuthInfo.stsurl).build(); final CompletableFuture future = pca .acquireToken(IntegratedWindowsAuthenticationParameters - .builder(Collections.singleton(fedAuthInfo.spn + "/.default"), user).build()); + .builder(Collections.singleton(fedAuthInfo.spn + SLASH_DEFAULT), user).build()); final IAuthenticationResult authenticationResult = future.get(); return new SqlFedAuthToken(authenticationResult.accessToken(), authenticationResult.expiresOnDate()); @@ -135,7 +136,7 @@ static SqlFedAuthToken getSqlFedAuthTokenInteractive(SqlFedAuthInfo fedAuthInfo, PublicClientApplication pca = PublicClientApplication .builder(ActiveDirectoryAuthentication.JDBC_FEDAUTH_CLIENT_ID).executorService(executorService) .setTokenCacheAccessAspect(PersistentTokenCacheAccessAspect.getInstance()) - .authority(fedAuthInfo.stsurl).logPii((logger.isLoggable(Level.FINE)) ? true : false).build(); + .authority(fedAuthInfo.stsurl).logPii((logger.isLoggable(Level.FINE))).build(); CompletableFuture future = null; IAuthenticationResult authenticationResult = null; @@ -150,7 +151,7 @@ static SqlFedAuthToken getSqlFedAuthTokenInteractive(SqlFedAuthInfo fedAuthInfo, logger.fine(logger.toString() + "Silent authentication for user:" + user); } SilentParameters silentParameters = SilentParameters - .builder(Collections.singleton(fedAuthInfo.spn + "/.default"), account).build(); + .builder(Collections.singleton(fedAuthInfo.spn + SLASH_DEFAULT), account).build(); future = pca.acquireTokenSilently(silentParameters); } @@ -169,7 +170,7 @@ static SqlFedAuthToken getSqlFedAuthTokenInteractive(SqlFedAuthInfo fedAuthInfo, InteractiveRequestParameters parameters = InteractiveRequestParameters.builder(new URI(REDIRECTURI)) .systemBrowserOptions(SystemBrowserOptions.builder() .htmlMessageSuccess(SQLServerResource.getResource("R_MSALAuthComplete")).build()) - .loginHint(user).scopes(Collections.singleton(fedAuthInfo.spn + "/.default")).build(); + .loginHint(user).scopes(Collections.singleton(fedAuthInfo.spn + SLASH_DEFAULT)).build(); future = pca.acquireToken(parameters); authenticationResult = future.get(); diff --git a/src/samples/adaptive/pom.xml b/src/samples/adaptive/pom.xml index dc9450b10b..c998e5e492 100644 --- a/src/samples/adaptive/pom.xml +++ b/src/samples/adaptive/pom.xml @@ -15,7 +15,7 @@ com.microsoft.sqlserver mssql-jdbc - 7.0.0.jre10 + 9.2.0.jre15 @@ -74,14 +74,14 @@ org.apache.maven.plugins maven-compiler-plugin - 10 - 10 + 15 + 15 org.apache.maven.plugins maven-resources-plugin - 2.5 + 3.1.0 diff --git a/src/samples/alwaysencrypted/pom.xml b/src/samples/alwaysencrypted/pom.xml index aa764957d8..26bb8ddf7d 100644 --- a/src/samples/alwaysencrypted/pom.xml +++ b/src/samples/alwaysencrypted/pom.xml @@ -15,7 +15,7 @@ com.microsoft.sqlserver mssql-jdbc - 7.0.0.jre10 + 9.2.0.jre15 @@ -42,14 +42,14 @@ org.apache.maven.plugins maven-compiler-plugin - 10 - 10 + 15 + 15 org.apache.maven.plugins maven-resources-plugin - 2.5 + 3.1.0 diff --git a/src/samples/azureactivedirectoryauthentication/pom.xml b/src/samples/azureactivedirectoryauthentication/pom.xml index c51324ef6d..d25a988843 100644 --- a/src/samples/azureactivedirectoryauthentication/pom.xml +++ b/src/samples/azureactivedirectoryauthentication/pom.xml @@ -14,7 +14,7 @@ com.microsoft.sqlserver mssql-jdbc - 7.0.0.jre10 + 9.2.0.jre15 @@ -41,14 +41,14 @@ org.apache.maven.plugins maven-compiler-plugin - 10 - 10 + 15 + 15 org.apache.maven.plugins maven-resources-plugin - 2.5 + 3.1.0 diff --git a/src/samples/connections/pom.xml b/src/samples/connections/pom.xml index e80665f3ad..f9191e5587 100644 --- a/src/samples/connections/pom.xml +++ b/src/samples/connections/pom.xml @@ -14,7 +14,7 @@ com.microsoft.sqlserver mssql-jdbc - 7.0.0.jre10 + 9.2.0.jre15 @@ -57,14 +57,14 @@ org.apache.maven.plugins maven-compiler-plugin - 10 - 10 + 15 + 15 org.apache.maven.plugins maven-resources-plugin - 2.5 + 3.1.0 diff --git a/src/samples/constrained/pom.xml b/src/samples/constrained/pom.xml index 2c374ec1c9..d810c540d6 100644 --- a/src/samples/constrained/pom.xml +++ b/src/samples/constrained/pom.xml @@ -16,7 +16,7 @@ com.microsoft.sqlserver mssql-jdbc - 7.0.0.jre10 + 9.2.0.jre15 @@ -44,14 +44,14 @@ maven-compiler-plugin 3.6.0 - 10 - 10 + 15 + 15 org.apache.maven.plugins maven-resources-plugin - 3.0.2 + 3.1.0 diff --git a/src/samples/dataclassification/pom.xml b/src/samples/dataclassification/pom.xml index 5ca6680065..00c34bff07 100644 --- a/src/samples/dataclassification/pom.xml +++ b/src/samples/dataclassification/pom.xml @@ -16,7 +16,7 @@ com.microsoft.sqlserver mssql-jdbc - 7.0.0.jre10 + 9.2.0.jre15 @@ -44,14 +44,14 @@ maven-compiler-plugin 3.6.0 - 10 - 10 + 15 + 15 org.apache.maven.plugins maven-resources-plugin - 3.0.2 + 3.1.0 diff --git a/src/samples/datatypes/pom.xml b/src/samples/datatypes/pom.xml index f364cbec30..e19ee77530 100644 --- a/src/samples/datatypes/pom.xml +++ b/src/samples/datatypes/pom.xml @@ -15,7 +15,7 @@ com.microsoft.sqlserver mssql-jdbc - 7.0.0.jre10 + 9.2.0.jre15 @@ -74,14 +74,14 @@ org.apache.maven.plugins maven-compiler-plugin - 10 - 10 + 15 + 15 org.apache.maven.plugins maven-resources-plugin - 2.5 + 3.1.0 diff --git a/src/samples/resultsets/pom.xml b/src/samples/resultsets/pom.xml index a2960b72ee..b53bb5ace2 100644 --- a/src/samples/resultsets/pom.xml +++ b/src/samples/resultsets/pom.xml @@ -14,7 +14,7 @@ com.microsoft.sqlserver mssql-jdbc - 7.0.0.jre10 + 9.2.0.jre15 @@ -73,14 +73,14 @@ org.apache.maven.plugins maven-compiler-plugin - 10 - 10 + 15 + 15 org.apache.maven.plugins maven-resources-plugin - 2.5 + 3.1.0 diff --git a/src/samples/sparse/pom.xml b/src/samples/sparse/pom.xml index 4d557cf059..d10d2dc7ef 100644 --- a/src/samples/sparse/pom.xml +++ b/src/samples/sparse/pom.xml @@ -14,7 +14,7 @@ com.microsoft.sqlserver mssql-jdbc - 7.0.0.jre10 + 9.2.0.jre15 @@ -41,14 +41,14 @@ org.apache.maven.plugins maven-compiler-plugin - 10 - 10 + 15 + 15 org.apache.maven.plugins maven-resources-plugin - 2.5 + 3.1.0 diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/MaxResultBufferTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/MaxResultBufferTest.java index 594e4f709d..5fed5e24b9 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/MaxResultBufferTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/MaxResultBufferTest.java @@ -7,11 +7,14 @@ import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; import com.microsoft.sqlserver.testframework.AbstractTest; +import com.microsoft.sqlserver.testframework.Constants; + import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -104,6 +107,7 @@ static void teardownTestTable() throws SQLException { * @param concurrencyMode * type of ResultSet's concurrency provided by source method */ + @Tag(Constants.xAzureSQLDW) @ParameterizedTest( name = "[{index}] maxResultBuffer = {0}, responseBuffering = {1}, resultSetType = {2}, concurrencyMode = {3}") @MethodSource("linearResultSetData") diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java b/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java index 6d131ca2e9..f0198d85b9 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/TestResource.java @@ -195,5 +195,5 @@ protected Object[][] getContents() { {"R_socketClosed", "Socket closed"}, {"R_aeStreamReadError", "The multi-part identifier"}, {"R_dataClassificationNotSupported", "Data Classification is not supported on this server."}, {"R_maxResultBufferExceeded", "MaxResultBuffer exceeded {0}."}, - {"R_noAuthorizationCode", "No Authorization code was returned from the server"}}; + {"R_databaseNotFound", "Database {0} not found."}}; } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/TestUtils.java b/src/test/java/com/microsoft/sqlserver/jdbc/TestUtils.java index b293b5a873..054c08e77c 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/TestUtils.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/TestUtils.java @@ -840,6 +840,25 @@ public static String removeProperty(String connectionString, String property) { return connectionString.replace(propertyStr, ""); } + /** + * Get the given connection property in the connection string + * + * @param connectionString + * connection string + * @param property + * name of the property + * @return The the value of the connection property or null if not found + */ + public static String getProperty(String connectionString, String property) { + int start = connectionString.indexOf(property); + if (-1 == start) { + return null; + } + start = connectionString.indexOf("=", start) + 1; + int end = connectionString.indexOf(";", start); + return connectionString.substring(start, -1 != end ? end : connectionString.length()); + } + /** * Creates a truststore and returns the path of it. * 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 1ec4de537c..493eff7515 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyColumnMappingTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyColumnMappingTest.java @@ -26,6 +26,7 @@ import com.microsoft.sqlserver.jdbc.ComparisonUtil; import com.microsoft.sqlserver.jdbc.RandomUtil; import com.microsoft.sqlserver.jdbc.SQLServerBulkCopy; +import com.microsoft.sqlserver.jdbc.SQLServerBulkCopyOptions; import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.jdbc.TestUtils; import com.microsoft.sqlserver.testframework.AbstractSQLGenerator; @@ -377,6 +378,44 @@ public void testUnicodeCharToNchar() throws SQLException, ClassNotFoundException validateMapping("VARCHAR(5)", "NVARCHAR(max)", "фщыab"); } + @Tag(Constants.xAzureSQLDW) + @Test + @DisplayName("BulkCopy:test column name with trailing space") + public void testTrailingSpace() throws SQLException, ClassNotFoundException { + String sourceTable = TestUtils + .escapeSingleQuotes(AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("sourceTable"))); + String destTable = TestUtils + .escapeSingleQuotes(AbstractSQLGenerator.escapeIdentifier(RandomUtil.getIdentifier("destTable"))); + + try (Connection conn = DriverManager.getConnection(connectionString);) { + try (Statement sourceStmt = conn.createStatement(); Statement destStmt = conn.createStatement(); + SQLServerBulkCopy bulkCopy = new SQLServerBulkCopy(conn)) { + + sourceStmt.executeUpdate("CREATE TABLE " + sourceTable + " ([colTrailingSpace ] char(5) null);"); + sourceStmt.executeUpdate("INSERT INTO " + sourceTable + " VALUES('" + "data" + "');"); + destStmt.executeUpdate("CREATE TABLE " + destTable + " ([colTrailingSpace ] char(5) null);"); + ResultSet sourceRs = sourceStmt.executeQuery("SELECT * FROM " + sourceTable); + + ResultSet destRs = destStmt.executeQuery("SELECT * FROM " + destTable); + destRs.next(); + + ResultSetMetaData rsmd = sourceRs.getMetaData(); + String name = rsmd.getColumnName(1); + bulkCopy.addColumnMapping(name, name); + + bulkCopy.setDestinationTableName(destTable); + bulkCopy.writeToServer(sourceRs); + } catch (Exception e) { + fail(e.getMessage()); + } finally { + try (Statement stmt = conn.createStatement();) { + TestUtils.dropTableIfExists(destTable, stmt); + TestUtils.dropTableIfExists(sourceTable, stmt); + } + } + } + } + /** * Validate if same values are in both source and destination table taking into account 1 extra column in * destination which should be a copy of first column of source. 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 a16fc8152b..644087b87e 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/databasemetadata/DatabaseMetaDataTest.java @@ -317,19 +317,41 @@ public void testDBTables() throws SQLException { try (Connection con = getConnection()) { DatabaseMetaData databaseMetaData = con.getMetaData(); try (ResultSet rsCatalog = databaseMetaData.getCatalogs()) { - - MessageFormat form1 = new MessageFormat(TestResource.getResource("R_atLeastOneFound")); Object[] msgArgs1 = {"catalog"}; - assertTrue(rsCatalog.next(), form1.format(msgArgs1)); + assertTrue(rsCatalog.next(), + (new MessageFormat(TestResource.getResource("R_atLeastOneFound"))).format(msgArgs1)); + String dbNameFromCatalog = rsCatalog.getString("TABLE_CAT"); String[] types = {"TABLE"}; - try (ResultSet rs = databaseMetaData.getTables(rsCatalog.getString("TABLE_CAT"), null, "%", types)) { - - MessageFormat form2 = new MessageFormat(TestResource.getResource("R_nameEmpty")); - Object[] msgArgs2 = {"Table"}; - while (rs.next()) { - assertTrue(!StringUtils.isEmpty(rs.getString("TABLE_NAME")), form2.format(msgArgs2)); + Object[] msgArgs2 = {"Table"}; + String dbNameFromConnectionString = TestUtils.getProperty(connectionString, "databaseName"); + if (null == dbNameFromConnectionString || (null != dbNameFromConnectionString + && dbNameFromConnectionString.equals(dbNameFromCatalog))) { + try (ResultSet rs = databaseMetaData.getTables(dbNameFromCatalog, null, "%", types)) { + while (rs.next()) { + assertTrue(!StringUtils.isEmpty(rs.getString("TABLE_NAME")), + (new MessageFormat(TestResource.getResource("R_nameEmpty"))).format(msgArgs2)); + } } + } else { + // try to find the databaseName specified + while (rsCatalog.next()) { + dbNameFromCatalog = rsCatalog.getString("TABLE_CAT"); + if (null != dbNameFromCatalog && !dbNameFromCatalog.isEmpty() + && dbNameFromConnectionString.equals(dbNameFromCatalog)) { + try (ResultSet rs = databaseMetaData.getTables(dbNameFromCatalog, null, "%", types)) { + while (rs.next()) { + assertTrue(!StringUtils.isEmpty(rs.getString("TABLE_NAME")), + (new MessageFormat(TestResource.getResource("R_nameEmpty"))) + .format(msgArgs2)); + } + return; + } + } + } + + Object[] msgArgs3 = {dbNameFromConnectionString}; + fail((new MessageFormat(TestResource.getResource("R_databaseNotFound"))).format(msgArgs3)); } } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/fedauth/ErrorMessageTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/fedauth/ErrorMessageTest.java index ce92d69164..0f39c17f7a 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/fedauth/ErrorMessageTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/fedauth/ErrorMessageTest.java @@ -17,10 +17,8 @@ import org.junit.platform.runner.JUnitPlatform; import org.junit.runner.RunWith; -import com.microsoft.sqlserver.jdbc.SQLServerConnectionTest; import com.microsoft.sqlserver.jdbc.SQLServerDataSource; import com.microsoft.sqlserver.jdbc.SQLServerException; -import com.microsoft.sqlserver.jdbc.TestResource; import com.microsoft.sqlserver.testframework.Constants; @@ -837,11 +835,8 @@ public void testInteractiveAuthTimeout() throws SQLException { fail(EXPECTED_EXCEPTION_NOT_THROWN); } assertTrue(INVALID_EXCEPTION_MSG + ": " + e.getMessage() + "," + e.getCause(), - e.getMessage() - .contains(ERR_MSG_FAILED_AUTHENTICATE + " the user " + azureUserName - + " in Active Directory (Authentication=ActiveDirectoryInteractive).") - && (isWindows ? e.getCause().getMessage() - .contains(TestResource.getResource("R_noAuthorizationCode")) : true)); + e.getMessage().contains(ERR_MSG_FAILED_AUTHENTICATE + " the user " + azureUserName + + " in Active Directory (Authentication=ActiveDirectoryInteractive).")); } } } From 7bb59ed166cf542c7fdbe7a61012d9e09e40ae7d Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Thu, 6 May 2021 17:39:22 -0700 Subject: [PATCH 2/5] update to jre16 --- azure-pipelines.yml | 6 +++--- build.gradle | 12 ++++++------ pom.xml | 8 ++++---- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index f75d832b8d..df48e0d1bc 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -40,13 +40,13 @@ jobs: inputs: secureFile: 'mssql-jdbc_auth-9.3.1.x64-preview.dll' - task: Maven@3 - displayName: 'Maven build jre15' + displayName: 'Maven build jre16' inputs: mavenPomFile: 'pom.xml' - goals: 'clean dependency:purge-local-repository -DdllPath=$(Agent.TempDirectory) -Dmssql_jdbc_test_connection_properties=jdbc:sqlserver://$(Target_SQL)$(server_domain);$(database);$(user);$(password); install -Pjre15 -DuserNTLM=$(userNTLM) -DpasswordNTLM=$(passwordNTLM) -DdomainNTLM=$(domainNTLM) -DexcludedGroups=$(Ex_Groups) -Dpkcs12_truststore_password=$(pkcs12_truststore_password) -Dpkcs12_truststore=$(pkcs12_truststore.secureFilePath) + goals: 'clean dependency:purge-local-repository -DdllPath=$(Agent.TempDirectory) -Dmssql_jdbc_test_connection_properties=jdbc:sqlserver://$(Target_SQL)$(server_domain);$(database);$(user);$(password); install -Pjre16 -DuserNTLM=$(userNTLM) -DpasswordNTLM=$(passwordNTLM) -DdomainNTLM=$(domainNTLM) -DexcludedGroups=$(Ex_Groups) -Dpkcs12_truststore_password=$(pkcs12_truststore_password) -Dpkcs12_truststore=$(pkcs12_truststore.secureFilePath) -DapplicationClientID=$(applicationClientID) -DapplicationKey=$(applicationKey) -DtenantID=$(tenantID) -DkeyID=$(keyID) -DwindowsKeyPath=$(windowsKeyPath) -DenclaveAttestationUrl=$(enclaveAttestationUrl) -DenclaveAttestationProtocol=$(enclaveAttestationProtocol) -DenclaveServer=$(enclaveServer)' testResultsFiles: '**/TEST-*.xml' - testRunTitle: 'Maven build jre15' + testRunTitle: 'Maven build jre16' javaHomeOption: Path jdkDirectory: $(JDK15) - task: Maven@3 diff --git a/build.gradle b/build.gradle index 2354fefef0..f8f1b0006d 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: - * jre15 - - PS> gradle build - PS> gradle build -PbuildProfile=jre15 + * jre16 - - PS> gradle build + PS> gradle build -PbuildProfile=jre16 * jre11 - - PS> gradle build -PbuildProfile=jre11 * jre8 - - PS> gradle build -PbuildProfile=jre8 * @@ -35,17 +35,17 @@ test { } } -if (!hasProperty('buildProfile') || (hasProperty('buildProfile') && buildProfile == "jre15")){ +if (!hasProperty('buildProfile') || (hasProperty('buildProfile') && buildProfile == "jre16")){ - jreVersion = "jre15" + jreVersion = "jre16" excludedFile = 'com/microsoft/sqlserver/jdbc/SQLServerJdbc42.java' jar { manifest { attributes 'Automatic-Module-Name': 'com.microsoft.sqlserver.jdbc' } } - sourceCompatibility = 15 - targetCompatibility = 15 + sourceCompatibility = 16 + targetCompatibility = 16 } if (hasProperty('buildProfile') && buildProfile == "jre11"){ diff --git a/pom.xml b/pom.xml index 0215573f6b..879823a92f 100644 --- a/pom.xml +++ b/pom.xml @@ -322,12 +322,12 @@ - jre15 + jre16 true - ${project.artifactId}-${project.version}.jre15${releaseExt} + ${project.artifactId}-${project.version}.jre16${releaseExt} org.apache.maven.plugins @@ -337,8 +337,8 @@ **/com/microsoft/sqlserver/jdbc/SQLServerJdbc42.java - 15 - 15 + 16 + 16 From 40242957041c564de40b0515a5cea25ec4cb9a90 Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Mon, 10 May 2021 19:45:35 -0700 Subject: [PATCH 3/5] JDK16 --- azure-pipelines.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index df48e0d1bc..916cac5a4a 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -48,7 +48,7 @@ jobs: testResultsFiles: '**/TEST-*.xml' testRunTitle: 'Maven build jre16' javaHomeOption: Path - jdkDirectory: $(JDK15) + jdkDirectory: $(JDK16) - task: Maven@3 displayName: 'Maven build jre11' inputs: @@ -58,7 +58,7 @@ jobs: testResultsFiles: '**/TEST-*.xml' testRunTitle: 'Maven build jre11' javaHomeOption: Path - jdkDirectory: $(JDK15) + jdkDirectory: $(JDK16) - task: Maven@3 displayName: 'Maven build jre8' inputs: @@ -68,4 +68,4 @@ jobs: testResultsFiles: '**/TEST-*.xml' testRunTitle: 'Maven build jre8' javaHomeOption: Path - jdkDirectory: $(JDK15) + jdkDirectory: $(JDK16) From e7f42374afe754b17926f36f8ea391ad3a2f1ea7 Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Tue, 11 May 2021 20:19:52 -0700 Subject: [PATCH 4/5] update maven surefire plugin --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 879823a92f..050e58abec 100644 --- a/pom.xml +++ b/pom.xml @@ -277,7 +277,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M1 + 3.0.0-M5 ${excludedGroups}, xJDBC42 @@ -418,7 +418,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M1 + 3.0.0-M5 3 true From 60c36dd76b93727fcfe3092ae01220785c49bc04 Mon Sep 17 00:00:00 2001 From: lilgreenbird Date: Wed, 12 May 2021 11:07:25 -0700 Subject: [PATCH 5/5] surefire plugin back to 3.0.0-M1 --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 050e58abec..879823a92f 100644 --- a/pom.xml +++ b/pom.xml @@ -277,7 +277,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M5 + 3.0.0-M1 ${excludedGroups}, xJDBC42 @@ -418,7 +418,7 @@ org.apache.maven.plugins maven-surefire-plugin - 3.0.0-M5 + 3.0.0-M1 3 true