diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java index 507e65cfa..435de47ac 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/IOBuffer.java @@ -1987,7 +1987,7 @@ final int read(byte[] data, con.terminate(SQLServerException.ERROR_SOCKET_TIMEOUT, e.getMessage(), e); } else { - con.terminate(SQLServerException.DRIVER_ERROR_IO_FAILED, e.getMessage()); + con.terminate(SQLServerException.DRIVER_ERROR_IO_FAILED, e.getMessage(), e); } return 0; // Keep the compiler happy. @@ -2004,7 +2004,7 @@ final void write(byte[] data, if (logger.isLoggable(Level.FINER)) logger.finer(toString() + " write failed:" + e.getMessage()); - con.terminate(SQLServerException.DRIVER_ERROR_IO_FAILED, e.getMessage()); + con.terminate(SQLServerException.DRIVER_ERROR_IO_FAILED, e.getMessage(), e); } } @@ -2016,7 +2016,7 @@ final void flush() throws SQLServerException { if (logger.isLoggable(Level.FINER)) logger.finer(toString() + " flush failed:" + e.getMessage()); - con.terminate(SQLServerException.DRIVER_ERROR_IO_FAILED, e.getMessage()); + con.terminate(SQLServerException.DRIVER_ERROR_IO_FAILED, e.getMessage(), e); } } @@ -3537,7 +3537,7 @@ void writeOffsetDateTimeWithTimezone(OffsetDateTime offsetDateTimeValue, throw new SQLServerException(SQLServerException.getErrString("R_zoneOffsetError"), null, // SQLState is null as this error is generated in // the driver 0, // Use 0 instead of DriverError.NOT_SET to use the correct constructor - null); + e); } subSecondNanos = offsetDateTimeValue.getNano(); @@ -3593,7 +3593,7 @@ void writeOffsetTimeWithTimezone(OffsetTime offsetTimeValue, throw new SQLServerException(SQLServerException.getErrString("R_zoneOffsetError"), null, // SQLState is null as this error is generated in // the driver 0, // Use 0 instead of DriverError.NOT_SET to use the correct constructor - null); + e); } subSecondNanos = offsetTimeValue.getNano(); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/KeyStoreProviderCommon.java b/src/main/java/com/microsoft/sqlserver/jdbc/KeyStoreProviderCommon.java index 324b1232e..51de7fe7e 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/KeyStoreProviderCommon.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/KeyStoreProviderCommon.java @@ -134,7 +134,7 @@ private static byte[] decryptRSAOAEP(byte[] cipherText, catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException | BadPaddingException e) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_CEKDecryptionFailed")); Object[] msgArgs = {e.getMessage()}; - throw new SQLServerException(form.format(msgArgs), null); + throw new SQLServerException(form.format(msgArgs), e); } return plainCEK; @@ -156,7 +156,7 @@ private static boolean verifyRSASignature(byte[] hash, catch (InvalidKeyException | NoSuchAlgorithmException | SignatureException e) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_InvalidCertificateSignature")); Object[] msgArgs = {masterKeyPath}; - throw new SQLServerException(form.format(msgArgs), null); + throw new SQLServerException(form.format(msgArgs), e); } return verificationSucess; diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java index f7338122f..e3e10dd06 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerADAL4JUtils.java @@ -31,7 +31,7 @@ static SqlFedAuthToken getSqlFedAuthToken(SqlFedAuthInfo fedAuthInfo, return fedAuthToken; } catch (MalformedURLException | InterruptedException e) { - throw new SQLServerException(e.getMessage(), null); + throw new SQLServerException(e.getMessage(), e); } catch (ExecutionException e) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_ADALExecution")); diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java index 56190a1d7..288ba694d 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCSVFileRecord.java @@ -152,7 +152,7 @@ else if (null == delimiter) { } catch (UnsupportedEncodingException unsupportedEncoding) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_unsupportedEncoding")); - throw new SQLServerException(form.format(new Object[] {encoding}), null, 0, null); + throw new SQLServerException(form.format(new Object[] {encoding}), null, 0, unsupportedEncoding); } catch (Exception e) { throw new SQLServerException(null, e.getMessage(), null, 0, false); @@ -523,7 +523,7 @@ public Object[] getRowData() throws SQLServerException { catch (ArithmeticException ex) { String value = "'" + data[pair.getKey() - 1] + "'"; MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_errorConvertingValue")); - throw new SQLServerException(form.format(new Object[] {value, JDBCType.of(cm.columnType)}), null, 0, null); + throw new SQLServerException(form.format(new Object[] {value, JDBCType.of(cm.columnType)}), null, 0, ex); } break; } @@ -648,10 +648,10 @@ else if (dateTimeFormatter != null) catch (IllegalArgumentException e) { String value = "'" + data[pair.getKey() - 1] + "'"; MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_errorConvertingValue")); - throw new SQLServerException(form.format(new Object[] {value, JDBCType.of(cm.columnType)}), null, 0, null); + throw new SQLServerException(form.format(new Object[] {value, JDBCType.of(cm.columnType)}), null, 0, e); } catch (ArrayIndexOutOfBoundsException e) { - throw new SQLServerException(SQLServerException.getErrString("R_BulkCSVDataSchemaMismatch"), null); + throw new SQLServerException(SQLServerException.getErrString("R_BulkCSVDataSchemaMismatch"), e); } } @@ -665,7 +665,7 @@ public boolean next() throws SQLServerException { currentLine = fileReader.readLine(); } catch (IOException e) { - throw new SQLServerException(null, e.getMessage(), null, 0, false); + throw new SQLServerException(e.getMessage(), null, 0, e); } return (null != currentLine); } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java index a5c225a8c..93a6ef268 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerBulkCopy.java @@ -1209,7 +1209,7 @@ private void checkForTimeoutException(SQLException e, connection.rollback(); } - throw new SQLServerException(SQLServerException.getErrString("R_queryTimedOut"), SQLState.STATEMENT_CANCELED, DriverError.NOT_SET, null); + throw new SQLServerException(SQLServerException.getErrString("R_queryTimedOut"), SQLState.STATEMENT_CANCELED, DriverError.NOT_SET, e); } } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java index 191729b59..767c07c07 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerColumnEncryptionAzureKeyVaultProvider.java @@ -184,7 +184,7 @@ public byte[] decryptColumnEncryptionKey(String masterKeyPath, md = MessageDigest.getInstance("SHA-256"); } catch (NoSuchAlgorithmException e) { - throw new SQLServerException(SQLServerException.getErrString("R_NoSHA256Algorithm"), null); + throw new SQLServerException(SQLServerException.getErrString("R_NoSHA256Algorithm"), e); } md.update(hash); byte dataToVerify[] = md.digest(); @@ -302,7 +302,7 @@ public byte[] encryptColumnEncryptionKey(String masterKeyPath, md = MessageDigest.getInstance("SHA-256"); } catch (NoSuchAlgorithmException e) { - throw new SQLServerException(SQLServerException.getErrString("R_NoSHA256Algorithm"), null); + throw new SQLServerException(SQLServerException.getErrString("R_NoSHA256Algorithm"), e); } md.update(dataToHash); byte dataToSign[] = md.digest(); @@ -401,7 +401,7 @@ private void ValidateNonEmptyAKVPath(String masterKeyPath) throws SQLServerExcep catch (URISyntaxException e) { MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_AKVURLInvalid")); Object[] msgArgs = {masterKeyPath}; - throw new SQLServerException(null, form.format(msgArgs), null, 0, false); + throw new SQLServerException(form.format(msgArgs), null, 0, e); } // A valid URI. @@ -439,7 +439,7 @@ private byte[] AzureKeyVaultWrap(String masterKeyPath, wrappedKey = keyVaultClient.wrapKeyAsync(masterKeyPath, encryptionAlgorithm, columnEncryptionKey).get(); } catch (InterruptedException | ExecutionException e) { - throw new SQLServerException(SQLServerException.getErrString("R_EncryptCEKError"), null); + throw new SQLServerException(SQLServerException.getErrString("R_EncryptCEKError"), e); } return wrappedKey.getResult(); } @@ -472,7 +472,7 @@ private byte[] AzureKeyVaultUnWrap(String masterKeyPath, unwrappedKey = keyVaultClient.unwrapKeyAsync(masterKeyPath, encryptionAlgorithm, encryptedColumnEncryptionKey).get(); } catch (InterruptedException | ExecutionException e) { - throw new SQLServerException(SQLServerException.getErrString("R_DecryptCEKError"), null); + throw new SQLServerException(SQLServerException.getErrString("R_DecryptCEKError"), e); } return unwrappedKey.getResult(); } @@ -496,7 +496,7 @@ private byte[] AzureKeyVaultSignHashedData(byte[] dataToSign, signedData = keyVaultClient.signAsync(masterKeyPath, JsonWebKeySignatureAlgorithm.RS256, dataToSign).get(); } catch (InterruptedException | ExecutionException e) { - throw new SQLServerException(SQLServerException.getErrString("R_GenerateSignature"), null); + throw new SQLServerException(SQLServerException.getErrString("R_GenerateSignature"), e); } return signedData.getResult(); } @@ -522,7 +522,7 @@ private boolean AzureKeyVaultVerifySignature(byte[] dataToVerify, valid = keyVaultClient.verifyAsync(masterKeyPath, JsonWebKeySignatureAlgorithm.RS256, dataToVerify, signature).get(); } catch (InterruptedException | ExecutionException e) { - throw new SQLServerException(SQLServerException.getErrString("R_VerifySignature"), null); + throw new SQLServerException(SQLServerException.getErrString("R_VerifySignature"), e); } return valid; @@ -544,7 +544,7 @@ private int getAKVKeySize(String masterKeyPath) throws SQLServerException { retrievedKey = keyVaultClient.getKeyAsync(masterKeyPath).get(); } catch (InterruptedException | ExecutionException e) { - throw new SQLServerException(SQLServerException.getErrString("R_GetAKVKeySize"), null); + throw new SQLServerException(SQLServerException.getErrString("R_GetAKVKeySize"), e); } if (!retrievedKey.getKey().getKty().equalsIgnoreCase("RSA") && !retrievedKey.getKey().getKty().equalsIgnoreCase("RSA-HSM")) { diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java index 7e1e5e15e..700347dfa 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerConnection.java @@ -3412,7 +3412,7 @@ final void processFedAuthInfo(TDSReader tdsReader, } catch (Exception e) { connectionlogger.severe(toString() + "Failed to read FedAuthInfoData."); - throw new SQLServerException(SQLServerException.getErrString("R_FedAuthInfoFailedToReadData"), null); + throw new SQLServerException(SQLServerException.getErrString("R_FedAuthInfoFailedToReadData"), e); } if (connectionlogger.isLoggable(Level.FINER)) { diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java index 396563971..d49d72221 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/SQLServerStatement.java @@ -2132,7 +2132,7 @@ public final ResultSet getGeneratedKeys() throws SQLServerException { rsPrevious.close(); } catch (SQLException e) { - throw new SQLServerException(null, e.getMessage(), null, 0, false); + throw new SQLServerException(e.getMessage(), null, 0, e); } } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/Util.java b/src/main/java/com/microsoft/sqlserver/jdbc/Util.java index a4a580d41..0e3093cd8 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/Util.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/Util.java @@ -580,7 +580,7 @@ static String readUnicodeString(byte[] b, MessageFormat form = new MessageFormat(txtMsg); Object[] msgArgs = {new Integer(offset)}; // Re-throw SQLServerException if conversion fails. - throw new SQLServerException(null, form.format(msgArgs), null, 0, true); + throw new SQLServerException(form.format(msgArgs), null, 0, ex); } } diff --git a/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java b/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java index cee390b2e..7c18c3fb8 100644 --- a/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java +++ b/src/main/java/com/microsoft/sqlserver/jdbc/dtv.java @@ -656,7 +656,7 @@ private void sendTemporal(DTV dtv, throw new SQLServerException(SQLServerException.getErrString("R_zoneOffsetError"), null, // SQLState is null as this error // is generated in the driver 0, // Use 0 instead of DriverError.NOT_SET to use the correct constructor - null); + e); } subSecondNanos = offsetTimeValue.getNano(); @@ -688,7 +688,7 @@ private void sendTemporal(DTV dtv, throw new SQLServerException(SQLServerException.getErrString("R_zoneOffsetError"), null, // SQLState is null as this error // is generated in the driver 0, // Use 0 instead of DriverError.NOT_SET to use the correct constructor - null); + e); } subSecondNanos = offsetDateTimeValue.getNano(); @@ -2230,7 +2230,7 @@ void execute(DTV dtv, readerValue = new InputStreamReader(inputStreamValue, "US-ASCII"); } catch (UnsupportedEncodingException ex) { - throw new SQLServerException(null, ex.getMessage(), null, 0, true); + throw new SQLServerException(ex.getMessage(), null, 0, ex); } dtv.setValue(readerValue, JavaType.READER); @@ -3556,7 +3556,7 @@ Object denormalizedValue(byte[] decryptedValue, catch (UnsupportedEncodingException e) { // Important: we should not pass the exception here as it displays the data. MessageFormat form = new MessageFormat(SQLServerException.getErrString("R_unsupportedEncoding")); - throw new SQLServerException(form.format(new Object[] {baseTypeInfo.getCharset()}), null, 0, null); + throw new SQLServerException(form.format(new Object[] {baseTypeInfo.getCharset()}), null, 0, e); } } diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyCSVTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyCSVTest.java index 324748fc0..e46d61a19 100644 --- a/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyCSVTest.java +++ b/src/test/java/com/microsoft/sqlserver/jdbc/bulkCopy/BulkCopyCSVTest.java @@ -12,7 +12,6 @@ import java.io.BufferedReader; import java.io.FileInputStream; import java.io.InputStreamReader; -import java.net.URI; import java.sql.Connection; import java.sql.ResultSet; import java.sql.ResultSetMetaData; @@ -34,6 +33,7 @@ import com.microsoft.sqlserver.testframework.DBResultSet; import com.microsoft.sqlserver.testframework.DBStatement; import com.microsoft.sqlserver.testframework.DBTable; +import com.microsoft.sqlserver.testframework.Utils; import com.microsoft.sqlserver.testframework.sqlType.SqlType; /** @@ -63,7 +63,7 @@ public class BulkCopyCSVTest extends AbstractTest { static void setUpConnection() { con = new DBConnection(connectionString); stmt = con.createStatement(); - filePath = getCurrentClassPath(); + filePath = Utils.getCurrentClassPath(); } /** @@ -133,25 +133,6 @@ void testCSV() { } } - /** - * - * @return location of resource file - */ - static String getCurrentClassPath() { - - try { - String className = new Object() { - }.getClass().getEnclosingClass().getName(); - String location = Class.forName(className).getProtectionDomain().getCodeSource().getLocation().getPath()+ "/"; - URI uri = new URI(location.toString()); - return uri.getPath(); - } - catch (Exception e) { - fail("Failed to get CSV file path. " + e.getMessage()); - } - return null; - } - /** * validate value in csv and in destination table as string * diff --git a/src/test/java/com/microsoft/sqlserver/jdbc/exception/ExceptionTest.java b/src/test/java/com/microsoft/sqlserver/jdbc/exception/ExceptionTest.java new file mode 100644 index 000000000..7ba6cbc36 --- /dev/null +++ b/src/test/java/com/microsoft/sqlserver/jdbc/exception/ExceptionTest.java @@ -0,0 +1,97 @@ +/* + * 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.exception; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.UnsupportedEncodingException; +import java.net.SocketTimeoutException; +import java.sql.DriverManager; +import java.sql.SQLException; + +import org.junit.jupiter.api.Test; +import org.junit.platform.runner.JUnitPlatform; +import org.junit.runner.RunWith; + +import com.microsoft.sqlserver.jdbc.SQLServerBulkCSVFileRecord; +import com.microsoft.sqlserver.jdbc.SQLServerConnection; +import com.microsoft.sqlserver.jdbc.SQLServerException; +import com.microsoft.sqlserver.testframework.AbstractTest; +import com.microsoft.sqlserver.testframework.Utils; + +@RunWith(JUnitPlatform.class) +public class ExceptionTest extends AbstractTest { + static String inputFile = "BulkCopyCSVTestInput.csv"; + + /** + * Test the SQLServerException has the proper cause when encoding is not supported. + * + * @throws Exception + */ + @Test + public void testBulkCSVFileRecordExceptionCause() throws Exception { + String filePath = Utils.getCurrentClassPath(); + + try { + SQLServerBulkCSVFileRecord scvFileRecord = new SQLServerBulkCSVFileRecord(filePath + inputFile, "invalid_encoding", true); + } + catch (Exception e) { + if (!(e instanceof SQLServerException)) { + throw e; + } + + assertTrue(null != e.getCause(), "Cause should not be null."); + assertTrue(e.getCause() instanceof UnsupportedEncodingException, "Cause should be instance of UnsupportedEncodingException."); + } + } + + String waitForDelaySPName = "waitForDelaySP"; + final int waitForDelaySeconds = 10; + + /** + * Test the SQLServerException has the proper cause when socket timeout occurs. + * + * @throws Exception + * + */ + @Test + public void testSocketTimeoutExceptionCause() throws Exception { + SQLServerConnection conn = null; + try { + conn = (SQLServerConnection) DriverManager.getConnection(connectionString); + + Utils.dropProcedureIfExists(waitForDelaySPName, conn.createStatement()); + createWaitForDelayPreocedure(conn); + + conn = (SQLServerConnection) DriverManager.getConnection(connectionString + ";socketTimeout=" + (waitForDelaySeconds * 1000 / 2) + ";"); + + try { + conn.createStatement().execute("exec " + waitForDelaySPName); + throw new Exception("Exception for socketTimeout is not thrown."); + } + catch (Exception e) { + if (!(e instanceof SQLServerException)) { + throw e; + } + + assertTrue(null != e.getCause(), "Cause should not be null."); + assertTrue(e.getCause() instanceof SocketTimeoutException, "Cause should be instance of SocketTimeoutException."); + } + } + finally { + if (null != conn) { + conn.close(); + } + } + } + + private void createWaitForDelayPreocedure(SQLServerConnection conn) throws SQLException { + String sql = "CREATE PROCEDURE " + waitForDelaySPName + " AS" + " BEGIN" + " WAITFOR DELAY '00:00:" + waitForDelaySeconds + "';" + " END"; + conn.createStatement().execute(sql); + } +} \ No newline at end of file diff --git a/src/test/java/com/microsoft/sqlserver/testframework/Utils.java b/src/test/java/com/microsoft/sqlserver/testframework/Utils.java index 45aa08780..1f959b8eb 100644 --- a/src/test/java/com/microsoft/sqlserver/testframework/Utils.java +++ b/src/test/java/com/microsoft/sqlserver/testframework/Utils.java @@ -8,8 +8,11 @@ package com.microsoft.sqlserver.testframework; +import static org.junit.Assert.fail; + import java.io.ByteArrayInputStream; import java.io.CharArrayReader; +import java.net.URI; import java.sql.SQLException; import java.util.ArrayList; import java.util.logging.Level; @@ -248,6 +251,24 @@ public DBNCharacterStream(String value) { } } + /** + * + * @return location of resource file + */ + public static String getCurrentClassPath() { + try { + String className = new Object() { + }.getClass().getEnclosingClass().getName(); + String location = Class.forName(className).getProtectionDomain().getCodeSource().getLocation().getPath() + "/"; + URI uri = new URI(location.toString()); + return uri.getPath(); + } + catch (Exception e) { + fail("Failed to get CSV file path. " + e.getMessage()); + } + return null; + } + /** * mimic "DROP TABLE IF EXISTS ..." for older versions of SQL Server */